예제 #1
0
        public void Miner_PosNetwork_CreatePowTransaction_AheadOfFutureDrift_ShouldNotBeIncludedInBlock()
        {
            var network = KnownNetworks.StratisRegTest;

            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisMiner = builder.CreateStratisPosNode(network).WithWallet().Start();

                int maturity = (int)network.Consensus.CoinbaseMaturity;
                TestHelper.MineBlocks(stratisMiner, maturity + 5);

                // Send coins to the receiver
                var context = WalletTests.CreateContext(network, new WalletAccountReference(WalletName, Account), Password, new Key().PubKey.GetAddress(network).ScriptPubKey, Money.COIN * 100, FeeType.Medium, 1);

                Transaction trx = stratisMiner.FullNode.WalletTransactionHandler().BuildTransaction(context);

                // This should make the mempool reject a POS trx.
                trx.Time = Utils.DateTimeToUnixTime(Utils.UnixTimeToDateTime(trx.Time).AddMinutes(5));

                // Sign trx again after changing the time property.
                trx = context.TransactionBuilder.SignTransaction(trx);

                var broadcaster = stratisMiner.FullNode.NodeService <IBroadcasterManager>();

                broadcaster.BroadcastTransactionAsync(trx).GetAwaiter().GetResult();
                var entry = broadcaster.GetTransaction(trx.GetHash());

                Assert.Equal(State.ToBroadcast, entry.State);

                Assert.NotNull(stratisMiner.FullNode.MempoolManager().GetTransaction(trx.GetHash()).Result);

                TestHelper.MineBlocks(stratisMiner, 1);

                Assert.NotNull(stratisMiner.FullNode.MempoolManager().GetTransaction(trx.GetHash()).Result);
            }
        }
        public void LegacyNodesConnectsToProvenHeaderEnabledNode_AndLastOneIsDisconnectedToReserveSlot()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this).WithLogsEnabled())
            {
                // Create separate network parameters for this test.
                CoreNode phEnabledNode = this.CreateNode(builder, "ph-enabled", ProtocolVersion.PROVEN_HEADER_VERSION, new NodeConfigParameters {
                    { "maxoutboundconnections", "3" }
                }).Start();
                CoreNode legacyNode1 = this.CreateNode(builder, "legacy1", ProtocolVersion.ALT_PROTOCOL_VERSION).Start();
                CoreNode legacyNode2 = this.CreateNode(builder, "legacy2", ProtocolVersion.ALT_PROTOCOL_VERSION).Start();
                CoreNode legacyNode3 = this.CreateNode(builder, "legacy3", ProtocolVersion.ALT_PROTOCOL_VERSION).Start();

                TestHelper.Connect(phEnabledNode, legacyNode1);
                TestHelper.Connect(phEnabledNode, legacyNode2);
                TestHelper.Connect(phEnabledNode, legacyNode3);

                // TODO: ProvenHeadersReservedSlotsBehavior kicks in only during peer discovery, so it doesn't trigger when we have an inbound connection or
                // when we are using addnode/connect.
                // We need to configure a peers.json file or mock the PeerDiscovery to let phEnabledNode try to connect to legacyNode1, legacyNode2 and legacyNode3
                // With a maxoutboundconnections = 3, we expect the 3rd peer being disconnected to reserve a slot for a ph enabled node.

                // Assert.Equal(phEnabledNode.FullNode.ConnectionManager.ConnectedPeers.Count() == 2);
            }
        }
예제 #3
0
        public void CanOverrideAllPorts()
        {
            var extraParams = new NodeConfigParameters
            {
                { "port", "123" },
                { "rpcport", "456" },
                { "apiport", "567" }
            };

            using (var nodeBuilder = NodeBuilder.Create(this))
            {
                var buildAction = new Action <IFullNodeBuilder>(builder =>
                                                                builder.UseBlockStore()
                                                                .UsePowConsensus()
                                                                .UseMempool()
                                                                .AddMining()
                                                                .UseWallet()
                                                                .AddRPC()
                                                                .UseApi()
                                                                .MockIBD());

                var coreNode = nodeBuilder.CreateCustomNode(buildAction, this.network,
                                                            ProtocolVersion.PROVEN_HEADER_VERSION, configParameters: extraParams);

                coreNode.Start();

                coreNode.ApiPort.Should().Be(567);
                coreNode.FullNode.NodeService <ApiSettings>().ApiPort.Should().Be(567);

                coreNode.RpcPort.Should().Be(456);
                coreNode.FullNode.NodeService <RpcSettings>().RPCPort.Should().Be(456);

                coreNode.ProtocolPort.Should().Be(123);
                coreNode.FullNode.ConnectionManager.ConnectionSettings.ExternalEndpoint.Port.Should().Be(123);
            }
        }
예제 #4
0
        public void NodesCanConnectToEachOthers()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode node1 = builder.CreateStratisPowNode(this.powNetwork);
                CoreNode node2 = builder.CreateStratisPowNode(this.powNetwork);
                builder.StartAll();
                node1.NotInIBD();
                node2.NotInIBD();
                Assert.Empty(node1.FullNode.ConnectionManager.ConnectedPeers);
                Assert.Empty(node2.FullNode.ConnectionManager.ConnectedPeers);
                RPCClient rpc1 = node1.CreateRPCClient();
                rpc1.AddNode(node2.Endpoint, true);
                Assert.Single(node1.FullNode.ConnectionManager.ConnectedPeers);
                Assert.Single(node2.FullNode.ConnectionManager.ConnectedPeers);

                var behavior = node1.FullNode.ConnectionManager.ConnectedPeers.First().Behaviors.Find <IConnectionManagerBehavior>();
                Assert.False(behavior.Inbound);
                Assert.True(behavior.OneTry);
                behavior = node2.FullNode.ConnectionManager.ConnectedPeers.First().Behaviors.Find <IConnectionManagerBehavior>();
                Assert.True(behavior.Inbound);
                Assert.False(behavior.OneTry);
            }
        }
예제 #5
0
        public void CanHandleReorgs()
        {
            using (NodeBuilder builder = NodeBuilder.Create())
            {
                var stratisNode = builder.CreateStratisNode();
                var coreNode1   = builder.CreateNode();
                var coreNode2   = builder.CreateNode();
                builder.StartAll();

                //Core1 discovers 10 blocks, sends to stratis
                var tip = coreNode1.FindBlock(10).Last();
                stratisNode.CreateRPCClient().AddNode(coreNode1.Endpoint, true);
                Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode1.CreateRPCClient().GetBestBlockHash());
                stratisNode.CreateRPCClient().RemoveNode(coreNode1.Endpoint);

                //Core2 discovers 20 blocks, sends to stratis
                tip = coreNode2.FindBlock(20).Last();
                stratisNode.CreateRPCClient().AddNode(coreNode2.Endpoint, true);
                Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode2.CreateRPCClient().GetBestBlockHash());
                stratisNode.CreateRPCClient().RemoveNode(coreNode2.Endpoint);
                ((CachedCoinView)stratisNode.FullNode.CoinView).FlushAsync().Wait();

                //Core1 discovers 30 blocks, sends to stratis
                tip = coreNode1.FindBlock(30).Last();
                stratisNode.CreateRPCClient().AddNode(coreNode1.Endpoint, true);
                Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode1.CreateRPCClient().GetBestBlockHash());
                stratisNode.CreateRPCClient().RemoveNode(coreNode1.Endpoint);

                //Core2 discovers 50 blocks, sends to stratis
                tip = coreNode2.FindBlock(50).Last();
                stratisNode.CreateRPCClient().AddNode(coreNode2.Endpoint, true);
                Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode2.CreateRPCClient().GetBestBlockHash());
                stratisNode.CreateRPCClient().RemoveNode(coreNode2.Endpoint);
                ((CachedCoinView)stratisNode.FullNode.CoinView).FlushAsync().Wait();
            }
        }
예제 #6
0
        public void TxMempoolMapOrphans()
        {
            var            rand     = new Random();
            var            randByte = new byte[32];
            Func <uint256> randHash = () =>
            {
                rand.NextBytes(randByte);
                return(new uint256(randByte));
            };

            using (NodeBuilder builder = NodeBuilder.Create())
            {
                var stratisNode = builder.CreateStratisPowNode();
                builder.StartAll();

                stratisNode.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNode.FullNode.Network));

                // 50 orphan transactions:
                for (ulong i = 0; i < 50; i++)
                {
                    Transaction tx = new Transaction();
                    tx.AddInput(new TxIn(new OutPoint(randHash(), 0), new Script(OpcodeType.OP_1)));
                    tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey));

                    stratisNode.FullNode.MempoolManager().Orphans.AddOrphanTx(i, tx).Wait();
                }

                Assert.Equal(50, stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count);

                // ... and 50 that depend on other orphans:
                for (ulong i = 0; i < 50; i++)
                {
                    var txPrev = stratisNode.FullNode.MempoolManager().Orphans.OrphansList().ElementAt(rand.Next(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count));

                    Transaction tx = new Transaction();
                    tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), 0), new Script(OpcodeType.OP_1)));
                    tx.AddOutput(new TxOut(new Money((1 + i + 100) * Money.CENT), stratisNode.MinerSecret.ScriptPubKey));
                    stratisNode.FullNode.MempoolManager().Orphans.AddOrphanTx(i, tx).Wait();
                }

                Assert.Equal(100, stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count);

                // This really-big orphan should be ignored:
                for (ulong i = 0; i < 10; i++)
                {
                    var         txPrev = stratisNode.FullNode.MempoolManager().Orphans.OrphansList().ElementAt(rand.Next(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count));
                    Transaction tx     = new Transaction();
                    tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey));
                    foreach (var index in Enumerable.Range(0, 2777))
                    {
                        tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), index), new Script(OpcodeType.OP_1)));
                    }

                    Assert.False(stratisNode.FullNode.MempoolManager().Orphans.AddOrphanTx(i, tx).Result);
                }

                Assert.Equal(100, stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count);

                // Test EraseOrphansFor:
                for (ulong i = 0; i < 3; i++)
                {
                    var sizeBefore = stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count;
                    stratisNode.FullNode.MempoolManager().Orphans.EraseOrphansFor(i).Wait();
                    Assert.True(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count < sizeBefore);
                }

                // Test LimitOrphanTxSize() function:
                stratisNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(40).Wait();
                Assert.True(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count <= 40);
                stratisNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(10).Wait();
                Assert.True(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count <= 10);
                stratisNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(0).Wait();
                Assert.True(!stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Any());
            }
        }
예제 #7
0
        public void TxMempoolBlockDoublespend()
        {
            using (NodeBuilder builder = NodeBuilder.Create())
            {
                var stratisNodeSync = builder.CreateStratisPowNode();
                builder.StartAll();
                stratisNodeSync.NotInIBD();
                stratisNodeSync.FullNode.Settings.RequireStandard = true; // make sure to test standard tx

                stratisNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network));
                stratisNodeSync.GenerateStratis(100); // coinbase maturity = 100
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock);
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.HighestPersistedBlock().HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock);

                // Make sure skipping validation of transctions that were
                // validated going into the memory pool does not allow
                // double-spends in blocks to pass validation when they should not.

                var scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey);
                var genBlock     = stratisNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(stratisNodeSync.FullNode.Chain.GetBlock(1).HashBlock).Result;

                // Create a double-spend of mature coinbase txn:
                List <Transaction> spends = new List <Transaction>(2);
                foreach (var index in Enumerable.Range(1, 2))
                {
                    var trx = new Transaction();
                    trx.AddInput(new TxIn(new OutPoint(genBlock.Transactions[0].GetHash(), 0), scriptPubKey));
                    trx.AddOutput(Money.Cents(11), new Key().PubKey.Hash);
                    // Sign:
                    trx.Sign(stratisNodeSync.MinerSecret, false);
                    spends.Add(trx);
                }

                // Test 1: block with both of those transactions should be rejected.
                var block = stratisNodeSync.GenerateStratis(1, spends).Single();
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock);
                Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash());

                // Test 2: ... and should be rejected if spend1 is in the memory pool
                Assert.True(stratisNodeSync.AddToStratisMempool(spends[0]));
                block = stratisNodeSync.GenerateStratis(1, spends).Single();
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock);
                Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash());
                stratisNodeSync.FullNode.MempoolManager().Clear().Wait();

                // Test 3: ... and should be rejected if spend2 is in the memory pool
                Assert.True(stratisNodeSync.AddToStratisMempool(spends[1]));
                block = stratisNodeSync.GenerateStratis(1, spends).Single();
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock);
                Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash());
                stratisNodeSync.FullNode.MempoolManager().Clear().Wait();

                // Final sanity test: first spend in mempool, second in block, that's OK:
                List <Transaction> oneSpend = new List <Transaction>();
                oneSpend.Add(spends[0]);
                Assert.True(stratisNodeSync.AddToStratisMempool(spends[1]));
                block = stratisNodeSync.GenerateStratis(1, oneSpend).Single();
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock);
                Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == block.GetHash());

                // spends[1] should have been removed from the mempool when the
                // block with spends[0] is accepted:
                TestHelper.WaitLoop(() => stratisNodeSync.FullNode.MempoolManager().MempoolSize().Result == 0);
            }
        }
 private void CleanFolder()
 {
     NodeBuilder.CleanupTestFolder(this.folder);
 }
        public void ConsensusManager_Fork_Occurs_Node_Gets_Disconnected_Due_To_InvalidStakeDepth()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                var minerA = builder.CreateStratisPosNode(this.posNetwork);
                var minerB = builder.CreateStratisPosNode(this.posNetwork);
                var syncer = builder.CreateStratisPosNode(this.posNetwork);

                builder.StartAll();

                minerA.NotInIBD().WithWallet();
                minerB.NotInIBD();
                syncer.NotInIBD();

                // MinerA mines to height 15.
                TestHelper.MineBlocks(minerA, 15);

                // Sync the network to height 15.
                TestHelper.Connect(syncer, minerA);
                TestHelper.Connect(syncer, minerB);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerA));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB));

                // Disconnect Miner A and B.
                TestHelper.Disconnect(syncer, minerA);
                TestHelper.Disconnect(syncer, minerB);

                // Ensure syncer does not have any connections.
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(syncer));

                // Miner A stakes a coin that increases the network height to 16.
                var minter = minerA.FullNode.NodeService <IPosMinting>();
                minter.Stake(new WalletSecret()
                {
                    WalletName = "mywallet", WalletPassword = "******"
                });

                TestHelper.WaitLoop(() =>
                {
                    return(minerA.FullNode.ConsensusManager().Tip.Height == 16);
                });

                minter.StopStake();

                // Update the network consensus options so that the GetStakeMinConfirmations returns a higher value
                // to ensure that the InvalidStakeDepth exception can be thrown.
                minerA.FullNode.Network.Consensus.Options = new ConsensusOptionsTest();
                minerB.FullNode.Network.Consensus.Options = new ConsensusOptionsTest();
                syncer.FullNode.Network.Consensus.Options = new ConsensusOptionsTest();

                // Syncer now connects to both miners causing a InvalidStakeDepth exception to be thrown
                // on Miner A.
                TestHelper.Connect(syncer, minerA);
                TestHelper.Connect(syncer, minerB);

                // Ensure that Syncer is synced with MinerB.
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB));

                // Ensure that Syncer is not connected to MinerA.
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(syncer, minerA));

                Assert.True(syncer.FullNode.ConsensusManager().Tip.Height == 15);
                Assert.True(minerB.FullNode.ConsensusManager().Tip.Height == 15);
            }
        }
예제 #10
0
 public ProofOfStakeSteps(string displayName)
 {
     this.nodeBuilder = NodeBuilder.Create(Path.Combine(this.GetType().Name, displayName));
 }
예제 #11
0
        public void Given__NodesAreSynced__When__ABigReorgHappens__Then__TheReorgIsIgnored()
        {
            // Temporary fix so the Network static initialize will not break.
            var m = Network.Main;

            Transaction.TimeStamp = true;
            Block.BlockSignature  = true;
            try
            {
                using (NodeBuilder builder = NodeBuilder.Create())
                {
                    var stratisMiner  = builder.CreateStratisPosNode();
                    var stratisSyncer = builder.CreateStratisPosNode();
                    var stratisReorg  = builder.CreateStratisPosNode();

                    builder.StartAll();
                    stratisMiner.NotInIBD();
                    stratisSyncer.NotInIBD();
                    stratisReorg.NotInIBD();

                    // TODO: set the max allowed reorg threshold here
                    // assume a reorg of 10 blocks is not allowed.
                    stratisMiner.FullNode.ChainBehaviorState.MaxReorgLength  = 10;
                    stratisSyncer.FullNode.ChainBehaviorState.MaxReorgLength = 10;
                    stratisReorg.FullNode.ChainBehaviorState.MaxReorgLength  = 10;

                    stratisMiner.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMiner.FullNode.Network));
                    stratisReorg.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisReorg.FullNode.Network));

                    stratisMiner.GenerateStratisWithMiner(1);

                    // wait for block repo for block sync to work
                    TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner));
                    stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                    stratisMiner.CreateRPCClient().AddNode(stratisSyncer.Endpoint, true);

                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisReorg));

                    // create a reorg by mining on two different chains
                    // ================================================

                    stratisMiner.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                    stratisSyncer.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);

                    var t1 = Task.Run(() => stratisMiner.GenerateStratisWithMiner(11));
                    var t2 = Task.Delay(1000).ContinueWith(t => stratisReorg.GenerateStratisWithMiner(12));
                    Task.WaitAll(t1, t2);
                    TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner));
                    TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg));

                    // make sure the nodes are actually on different chains.
                    Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock);

                    TestHelper.TriggerSync(stratisSyncer);
                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));

                    // The hash before the reorg node is connected.
                    var hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock;

                    // connect the reorg chain
                    stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                    stratisSyncer.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);

                    // trigger nodes to sync
                    TestHelper.TriggerSync(stratisMiner);
                    TestHelper.TriggerSync(stratisReorg);
                    TestHelper.TriggerSync(stratisSyncer);

                    // wait for the synced chain to get headers updated.
                    TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedNodes.Any());

                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false);
                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false);

                    // check that a reorg did not happen.
                    Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock);
                }
            }
            finally
            {
                Transaction.TimeStamp = false;
                Block.BlockSignature  = false;
            }
        }
예제 #12
0
        public void MempoolSyncTransactions()
        {
            using (NodeBuilder builder = NodeBuilder.Create())
            {
                var stratisNodeSync = builder.CreateStratisPowNode();
                var stratisNode1    = builder.CreateStratisPowNode();
                var stratisNode2    = builder.CreateStratisPowNode();
                builder.StartAll();

                stratisNodeSync.NotInIBD();
                stratisNode1.NotInIBD();
                stratisNode2.NotInIBD();

                // generate blocks and wait for the downloader to pickup
                stratisNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network));
                stratisNodeSync.GenerateStratisWithMiner(105); // coinbase maturity = 100
                // wait for block repo for block sync to work
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisNodeSync));

                // sync both nodes
                stratisNode1.CreateRPCClient().AddNode(stratisNodeSync.Endpoint, true);
                stratisNode2.CreateRPCClient().AddNode(stratisNodeSync.Endpoint, true);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, stratisNodeSync));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode2, stratisNodeSync));

                // create some transactions and push them to the pool
                var trxs = new List <Transaction>();
                foreach (var index in Enumerable.Range(1, 5))
                {
                    var block   = stratisNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(stratisNodeSync.FullNode.Chain.GetBlock(index).HashBlock).Result;
                    var prevTrx = block.Transactions.First();
                    var dest    = new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network);

                    Transaction tx = new Transaction();
                    tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey)));
                    tx.AddOutput(new TxOut("25", dest.PubKey.Hash));
                    tx.AddOutput(new TxOut("24", new Key().PubKey.Hash)); // 1 btc fee
                    tx.Sign(stratisNodeSync.MinerSecret, false);
                    trxs.Add(tx);
                }
                var options = new ParallelOptions {
                    MaxDegreeOfParallelism = 5
                };
                Parallel.ForEach(trxs, options, transaction =>
                {
                    stratisNodeSync.Broadcast(transaction);
                });

                // wait for all nodes to have all trx
                TestHelper.WaitLoop(() => stratisNodeSync.CreateRPCClient().GetRawMempool().Length == 5);

                // the full node should be connected to both nodes
                Assert.True(stratisNodeSync.FullNode.ConnectionManager.ConnectedPeers.Count() >= 2);

                // reset the trickle timer on the full node that has the transactions in the pool
                foreach (var node in stratisNodeSync.FullNode.ConnectionManager.ConnectedPeers)
                {
                    node.Behavior <MempoolBehavior>().NextInvSend = 0;
                }

                TestHelper.WaitLoop(() => stratisNode1.CreateRPCClient().GetRawMempool().Length == 5);
                TestHelper.WaitLoop(() => stratisNode2.CreateRPCClient().GetRawMempool().Length == 5);

                // mine the transactions in the mempool
                stratisNodeSync.GenerateStratisWithMiner(1);
                TestHelper.WaitLoop(() => stratisNodeSync.CreateRPCClient().GetRawMempool().Length == 0);

                // wait for block and mempool to change
                TestHelper.WaitLoop(() => stratisNode1.CreateRPCClient().GetBestBlockHash() == stratisNodeSync.CreateRPCClient().GetBestBlockHash());
                TestHelper.WaitLoop(() => stratisNode2.CreateRPCClient().GetBestBlockHash() == stratisNodeSync.CreateRPCClient().GetBestBlockHash());
                TestHelper.WaitLoop(() => stratisNode1.CreateRPCClient().GetRawMempool().Length == 0);
                TestHelper.WaitLoop(() => stratisNode2.CreateRPCClient().GetRawMempool().Length == 0);
            }
        }
        public void WalletCanReceiveAndSendCorrectly()
        {
            using (NodeBuilder builder = NodeBuilder.Create())
            {
                var stratisSender   = builder.CreateStratisPowNode();
                var stratisReceiver = builder.CreateStratisPowNode();

                builder.StartAll();
                stratisSender.NotInIBD();
                stratisReceiver.NotInIBD();

                // get a key from the wallet
                var mnemonic1 = stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet");
                var mnemonic2 = stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet");
                Assert.Equal(12, mnemonic1.Words.Length);
                Assert.Equal(12, mnemonic2.Words.Length);
                var addr   = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0"));
                var wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet");
                var key    = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey;

                stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network));
                var maturity = (int)stratisSender.FullNode.Network.Consensus.Option <PowConsensusOptions>().CoinbaseMaturity;
                stratisSender.GenerateStratis(maturity + 5);
                // wait for block repo for block sync to work

                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));

                // the mining should add coins to the wallet
                var total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                Assert.Equal(Money.COIN * 105 * 50, total);

                // sync both nodes
                stratisSender.CreateRPCClient().AddNode(stratisReceiver.Endpoint, true);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));

                // send coins to the receiver
                var sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0"));
                var trx    = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101));

                // [SENDTRANSACTION TODO] Conceptual changes had been introduced to tx sending
                // These tests don't make sense anymore
                // It must be either removed or refactored
                //// broadcast to the other node
                //stratisSender.FullNode.WalletManager().SendTransaction(trx.ToHex());

                //// wait for the trx to arrive
                //TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0);
                //TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any());

                //var receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                //Assert.Equal(Money.COIN * 100, receivetotal);
                //Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight);

                //// generate two new blocks do the trx is confirmed
                //stratisSender.GenerateStratis(1, new List<Transaction>(new[] {trx.Clone()}));
                //stratisSender.GenerateStratis(1);

                //// wait for block repo for block sync to work
                //TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));

                //TestHelper.WaitLoop(() => maturity + 6 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight);
            }
        }
        public void WalletCanReorg()
        {
            // this test has 4 parts:
            // send first transaction from one wallet to another and wait for it to be confirmed
            // send a second transaction and wait for it to be confirmed
            // connected to a longer chain that couse a reorg back so the second trasnaction is undone
            // mine the second transaction back in to the main chain

            using (NodeBuilder builder = NodeBuilder.Create())
            {
                var stratisSender   = builder.CreateStratisPowNode();
                var stratisReceiver = builder.CreateStratisPowNode();
                var stratisReorg    = builder.CreateStratisPowNode();

                builder.StartAll();
                stratisSender.NotInIBD();
                stratisReceiver.NotInIBD();
                stratisReorg.NotInIBD();

                // get a key from the wallet
                var mnemonic1 = stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet");
                var mnemonic2 = stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet");
                Assert.Equal(12, mnemonic1.Words.Length);
                Assert.Equal(12, mnemonic2.Words.Length);
                var addr   = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0"));
                var wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet");
                var key    = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey;

                stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network));
                stratisReorg.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network));

                var maturity = (int)stratisSender.FullNode.Network.Consensus.Option <PowConsensusOptions>().CoinbaseMaturity;
                stratisSender.GenerateStratisWithMiner(maturity + 15);

                var currentBestHeight = maturity + 15;

                // wait for block repo for block sync to work
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));

                // the mining should add coins to the wallet
                var total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                Assert.Equal(Money.COIN * currentBestHeight * 50, total);

                // sync all nodes
                stratisReceiver.CreateRPCClient().AddNode(stratisSender.Endpoint, true);
                stratisReceiver.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                stratisSender.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg));

                // Build Transaction 1
                // ====================
                // send coins to the receiver
                var sendto       = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0"));
                var transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101));

                // [SENDTRANSACTION TODO] Conceptual changes had been introduced to tx sending
                // These tests don't make sense anymore
                // It must be either removed or refactored
                //// broadcast to the other node
                //stratisSender.FullNode.WalletManager().SendTransaction(transaction1.ToHex());

                //// wait for the trx to arrive
                //TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0);
                //Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), false));
                //TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any());

                //var receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                //Assert.Equal(Money.COIN * 100, receivetotal);
                //Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight);

                //// generate two new blocks so the trx is confirmed
                //stratisSender.GenerateStratisWithMiner(1);
                //var transaction1MinedHeight = currentBestHeight + 1;
                //stratisSender.GenerateStratisWithMiner(1);
                //currentBestHeight = currentBestHeight + 2;

                //// wait for block repo for block sync to work
                //TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg));
                //Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height);
                //TestHelper.WaitLoop(() => transaction1MinedHeight == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight);

                //// Build Transaction 2
                //// ====================
                //// remove the reorg node
                //stratisReceiver.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                //stratisSender.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                //var forkblock = stratisReceiver.FullNode.Chain.Tip;

                //// send more coins to the wallet
                //sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0"));
                //var transaction2 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 101));
                //stratisSender.FullNode.WalletManager().SendTransaction(transaction2.ToHex());
                //// wait for the trx to arrive
                //TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0);
                //Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction2.GetHash(), false));
                //TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any());
                //var newamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                //Assert.Equal(Money.COIN * 110, newamount);
                //Assert.True(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == null));

                //// mine more blocks so its included in the chain

                //stratisSender.GenerateStratisWithMiner(1);
                //var transaction2MinedHeight = currentBestHeight + 1;
                //stratisSender.GenerateStratisWithMiner(1);
                //currentBestHeight = currentBestHeight + 2;
                //TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));
                //Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height);
                //TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == transaction2MinedHeight));

                //// create a reorg by mining on two different chains
                //// ================================================
                //// advance both chains, one chin is longer
                //stratisSender.GenerateStratisWithMiner(2);
                //stratisReorg.GenerateStratisWithMiner(10);
                //currentBestHeight = forkblock.Height + 10;
                //TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg));

                //// connect the reorg chain
                //stratisReceiver.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                //stratisSender.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                //// wait for the chains to catch up
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg));
                //Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height);

                //// ensure wallet reorg complete
                //TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().WalletTipHash == stratisReorg.CreateRPCClient().GetBestBlockHash());
                //// check the wallet amount was rolled back
                //var newtotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                //Assert.Equal(receivetotal, newtotal);
                //TestHelper.WaitLoop(() => maturity + 16 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight);

                //// ReBuild Transaction 2
                //// ====================
                //// TODO: Investigate when a reorg happens the store needs to put the rolled back transactions back to the mempool
                //stratisSender.FullNode.WalletManager().SendTransaction(transaction2.ToHex());
                //TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0);

                //// mine the transaction again
                //stratisSender.GenerateStratisWithMiner(1);
                //transaction2MinedHeight = currentBestHeight + 1;
                //stratisSender.GenerateStratisWithMiner(1);
                //currentBestHeight = currentBestHeight + 2;

                //TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender));
                //TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg));
                //Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height);
                //var newsecondamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount);
                //Assert.Equal(newamount, newsecondamount);
                //TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == transaction2MinedHeight));
            }
        }
        public async Task ReorgChain_FailsFullValidation_Reconnect_OldChain_Nodes_ConnectedAsync()
        {
            using (var builder = NodeBuilder.Create(this))
            {
                var bitcoinNoValidationRulesNetwork = new BitcoinRegTestNoValidationRules();

                var minerA = builder.CreateStratisPowNode(this.powNetwork).WithDummyWallet().WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner);
                var minerB = builder.CreateStratisPowNode(bitcoinNoValidationRulesNetwork).NoValidation().WithDummyWallet().Start();

                ChainedHeader minerBChainTip      = null;
                bool          interceptorsEnabled = false;
                bool          minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain = false;
                bool          minerA_IsConnecting_To_MinerBChain = false;
                bool          minerA_Disconnected_MinerBsChain   = false;
                bool          minerA_Reconnected_Its_OwnChain    = false;

                // Configure the interceptor to intercept when Miner A connects Miner B's chain.
                void interceptorConnect(ChainedHeaderBlock chainedHeaderBlock)
                {
                    if (!interceptorsEnabled)
                    {
                        return;
                    }

                    if (!minerA_IsConnecting_To_MinerBChain)
                    {
                        if (chainedHeaderBlock.ChainedHeader.Height == 12)
                        {
                            minerA_IsConnecting_To_MinerBChain = minerA.FullNode.ConsensusManager().Tip.HashBlock == minerBChainTip.GetAncestor(12).HashBlock;
                        }

                        return;
                    }

                    if (!minerA_Reconnected_Its_OwnChain)
                    {
                        if (chainedHeaderBlock.ChainedHeader.Height == 14)
                        {
                            minerA_Reconnected_Its_OwnChain = true;
                        }

                        return;
                    }
                }

                // Configure the interceptor to intercept when Miner A disconnects Miner B's chain after the reorg.
                void interceptorDisconnect(ChainedHeaderBlock chainedHeaderBlock)
                {
                    if (!interceptorsEnabled)
                    {
                        return;
                    }

                    if (!minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain)
                    {
                        if (minerA.FullNode.ConsensusManager().Tip.Height == 10)
                        {
                            minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain = true;
                        }

                        return;
                    }

                    if (!minerA_Disconnected_MinerBsChain)
                    {
                        if (minerA.FullNode.ConsensusManager().Tip.Height == 10)
                        {
                            minerA_Disconnected_MinerBsChain = true;
                        }

                        return;
                    }
                }

                minerA.SetConnectInterceptor(interceptorConnect);
                minerA.SetDisconnectInterceptor(interceptorDisconnect);
                minerA.Start();

                // Miner B syncs with Miner A
                TestHelper.ConnectAndSync(minerB, minerA);

                // Disable Miner A from sending blocks to Miner B
                TestHelper.DisableBlockPropagation(minerA, minerB);

                // Miner A continues to mine to height 14
                TestHelper.MineBlocks(minerA, 4);
                TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14);
                Assert.Equal(10, minerB.FullNode.ConsensusManager().Tip.Height);

                // Enable the interceptors so that they are active during the reorg.
                interceptorsEnabled = true;

                // Miner B mines 5 more blocks:
                // Block 6,7,9,10 = valid
                // Block 8 = invalid
                minerBChainTip = await TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(13, (coreNode, block) => BlockBuilder.InvalidCoinbaseReward(coreNode, block)).BuildAsync();

                Assert.Equal(15, minerBChainTip.Height);
                Assert.Equal(15, minerB.FullNode.ConsensusManager().Tip.Height);

                // Wait until Miner A disconnected its own chain so that it can connect to
                // Miner B's longer chain.
                TestHelper.WaitLoop(() => minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain);

                // Wait until Miner A has connected Miner B's chain (but failed)
                TestHelper.WaitLoop(() => minerA_IsConnecting_To_MinerBChain);

                // Wait until Miner A has disconnected Miner B's invalid chain.
                TestHelper.WaitLoop(() => minerA_Disconnected_MinerBsChain);

                // Wait until Miner A has reconnected its own chain.
                TestHelper.WaitLoop(() => minerA_Reconnected_Its_OwnChain);
            }
        }
예제 #16
0
        public void Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisMiner  = builder.CreateStratisPosNode(this.posNetwork);
                CoreNode stratisSyncer = builder.CreateStratisPosNode(this.posNetwork);
                CoreNode stratisReorg  = builder.CreateStratisPosNode(this.posNetwork);

                builder.StartAll();
                stratisMiner.NotInIBD().WithWallet();
                stratisSyncer.NotInIBD().WithWallet();
                stratisReorg.NotInIBD().WithWallet();

                // TODO: set the max allowed reorg threshold here
                // assume a reorg of 10 blocks is not allowed.
                stratisMiner.FullNode.ChainBehaviorState.MaxReorgLength  = 10;
                stratisSyncer.FullNode.ChainBehaviorState.MaxReorgLength = 10;
                stratisReorg.FullNode.ChainBehaviorState.MaxReorgLength  = 10;

                stratisMiner.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMiner.FullNode.Network));
                stratisReorg.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisReorg.FullNode.Network));

                stratisMiner.GenerateStratisWithMiner(1);

                // wait for block repo for block sync to work
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner));
                stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                stratisMiner.CreateRPCClient().AddNode(stratisSyncer.Endpoint, true);

                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisReorg));

                // create a reorg by mining on two different chains
                // ================================================

                stratisMiner.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                stratisSyncer.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(stratisReorg));

                Task <List <uint256> > t1 = Task.Run(() => stratisMiner.GenerateStratisWithMiner(11));
                Task <List <uint256> > t2 = Task.Delay(1000).ContinueWith(t => stratisReorg.GenerateStratisWithMiner(12));
                Task.WaitAll(t1, t2);
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner));
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg));

                // make sure the nodes are actually on different chains.
                Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock);

                TestHelper.TriggerSync(stratisSyncer);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));

                // The hash before the reorg node is connected.
                uint256 hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock;

                // connect the reorg chain
                stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                stratisSyncer.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);

                // trigger nodes to sync
                TestHelper.TriggerSync(stratisMiner);
                TestHelper.TriggerSync(stratisReorg);
                TestHelper.TriggerSync(stratisSyncer);

                // wait for the synced chain to get headers updated.
                TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedPeers.Any());

                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false);

                // check that a reorg did not happen.
                Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock);
            }
        }
예제 #17
0
        public void SegwitWalletTransactionBuildingTest_SendToBech32AndNormalDestination()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode node     = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start();
                CoreNode listener = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start();

                TestHelper.Connect(listener, node);

                var mineAddress = node.FullNode.WalletManager().GetUnusedAddress();

                int maturity = (int)node.FullNode.Network.Consensus.CoinbaseMaturity;

                var miner = node.FullNode.NodeService <IPowMining>() as PowMining;
                miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), (ulong)(maturity + 1), int.MaxValue);

                TestHelper.WaitForNodeToSync(node, listener);

                var destinationAddress = node.FullNode.WalletManager().GetUnusedAddress();
                var witAddress         = destinationAddress.Bech32Address;
                var nonWitAddress      = destinationAddress.Address;

                IActionResult transactionResult = node.FullNode.NodeController <WalletController>()
                                                  .BuildTransaction(new BuildTransactionRequest
                {
                    AccountName      = "account 0",
                    AllowUnconfirmed = true,
                    Recipients       = new List <RecipientModel> {
                        new RecipientModel {
                            DestinationAddress = witAddress, Amount = Money.Coins(1).ToString()
                        }
                    },
                    Password   = node.WalletPassword,
                    WalletName = node.WalletName,
                    FeeAmount  = Money.Coins(0.001m).ToString()
                }).GetAwaiter().GetResult();

                var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value;

                node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex));

                Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex);
                uint        witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N;

                TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= (maturity + 1), cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue);

                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                // Make sure wallet is synced.
                TestBase.WaitLoop(() => node.CreateRPCClient().GetBlockCount() == node.FullNode.WalletManager().LastBlockHeight(), cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                // By sending more than the size of the P2WPKH UTXO, we guarantee that at least one non-P2WPKH UTXO gets included
                transactionResult = node.FullNode.NodeController <WalletController>()
                                    .BuildTransaction(new BuildTransactionRequest
                {
                    AccountName      = "account 0",
                    AllowUnconfirmed = true,
                    Outpoints        = new List <OutpointRequest>()
                    {
                        new OutpointRequest()
                        {
                            Index = (int)witIndex, TransactionId = witFunds.GetHash().ToString()
                        }
                    },
                    Recipients = new List <RecipientModel>
                    {
                        new RecipientModel {
                            DestinationAddress = witAddress, Amount = Money.Coins(0.4m).ToString()
                        },
                        new RecipientModel {
                            DestinationAddress = nonWitAddress, Amount = Money.Coins(0.4m).ToString()
                        }
                    },
                    Password   = node.WalletPassword,
                    WalletName = node.WalletName,
                    FeeAmount  = Money.Coins(0.001m).ToString()
                }).GetAwaiter().GetResult();

                walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value;

                node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex));

                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);

                miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue);

                TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token);
            }
        }
예제 #18
0
        public void CheckSegwitP2PSerialisationForNonWitnessNode()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                // We have to name the networks differently because the NBitcoin network registration won't allow two identical networks to coexist otherwise.
                var network = new StraxRegTest();
                network.SetPrivatePropertyValue("Name", "StraxRegTestWithDeployments");
                Assert.NotNull(network.Consensus.BIP9Deployments[2]);

                var networkNoBIP9 = new StraxRegTest();
                networkNoBIP9.SetPrivatePropertyValue("Name", "StraxRegTestWithoutDeployments");
                Assert.NotNull(networkNoBIP9.Consensus.BIP9Deployments[2]);

                // Remove BIP9 deployments (i.e. segwit).
                for (int i = 0; i < networkNoBIP9.Consensus.BIP9Deployments.Length; i++)
                {
                    networkNoBIP9.Consensus.BIP9Deployments[i] = null;
                }

                // Ensure the workaround had the desired effect.
                Assert.Null(networkNoBIP9.Consensus.BIP9Deployments[2]);
                Assert.NotNull(network.Consensus.BIP9Deployments[2]);

                // Explicitly use new & separate instances of StratisRegTest because we modified the BIP9 deployments on one instance.
                CoreNode node     = builder.CreateStratisPosNode(network).Start();
                CoreNode listener = builder.CreateStratisPosNode(networkNoBIP9).Start();

                // Sanity check.
                Assert.Null(listener.FullNode.Network.Consensus.BIP9Deployments[2]);
                Assert.NotNull(node.FullNode.Network.Consensus.BIP9Deployments[2]);

                // By disabling Segwit on the listener node we also prevent the WitnessCommitments rule from rejecting the mining node's blocks once we modify the listener's peer services.

                IConnectionManager listenerConnMan = listener.FullNode.NodeService <IConnectionManager>();
                listenerConnMan.Parameters.TemplateBehaviors.Add(new TestBehavior());

                // Override the listener node's default settings, so that it will not ask for witness data in P2P messages.
                listenerConnMan.Parameters.Services &= ~NetworkPeerServices.NODE_WITNESS;

                TestHelper.Connect(listener, node);

                // Mine a Segwit block on the first node. It should have commitment data as its settings have not been modified.
                var            script     = new Key().PubKey.WitHash.ScriptPubKey;
                var            miner      = node.FullNode.NodeService <IPowMining>() as PowMining;
                List <uint256> res        = miner.GenerateBlocks(new ReserveScript(script), 1, int.MaxValue);
                Block          block      = node.FullNode.ChainIndexer.GetHeader(res.First()).Block;
                Script         commitment = WitnessCommitmentsRule.GetWitnessCommitment(node.FullNode.Network, block);
                Assert.NotNull(commitment);

                // The listener should sync the mined block without validation failures.
                var cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token;
                TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= 1, cancellationToken: cancellationToken);

                // We need to capture a message on the non-witness-enabled destination node and see that it contains a block serialised without witness data.
                INetworkPeer connectedPeer = listenerConnMan.ConnectedPeers.FindByEndpoint(node.Endpoint);
                TestBehavior testBehavior  = connectedPeer.Behavior <TestBehavior>();

                var blockMessages = testBehavior.receivedMessageTracker["block"];
                var blockReceived = blockMessages.First();

                var receivedBlock = blockReceived.Message.Payload as BlockPayload;
                var parsedBlock   = receivedBlock.Obj;

                // The block mined on the mining node (witness) should be bigger than the one received by the listener (no witness).
                Assert.True(block.GetSerializedSize() > parsedBlock.GetSerializedSize());

                // Reserialise the received block without witness data (this should have no effect on its size).
                var nonWitnessBlock = parsedBlock.WithOptions(listener.FullNode.Network.Consensus.ConsensusFactory, TransactionOptions.None);

                // We received a block without witness data in the first place.
                Assert.True(parsedBlock.GetSerializedSize() == nonWitnessBlock.GetSerializedSize());
            }
        }
        public void MiningNodeWithOneConnectionAlwaysSynced()
        {
            string testFolderPath = Path.Combine(this.GetType().Name, nameof(MiningNodeWithOneConnectionAlwaysSynced));

            using (NodeBuilder nodeBuilder = NodeBuilder.Create(testFolderPath))
            {
                CoreNode minerNode = nodeBuilder.CreateStratisPowNode(this.powNetwork).NotInIBD();
                minerNode.Start();
                minerNode.WithWallet();

                CoreNode connectorNode = nodeBuilder.CreateStratisPowNode(this.powNetwork).NotInIBD();
                connectorNode.Start();
                connectorNode.WithWallet();

                CoreNode firstNode = nodeBuilder.CreateStratisPowNode(this.powNetwork).NotInIBD();
                firstNode.Start();
                firstNode.WithWallet();

                CoreNode secondNode = nodeBuilder.CreateStratisPowNode(this.powNetwork);
                secondNode.Start();
                secondNode.WithWallet();

                TestHelper.Connect(minerNode, connectorNode);
                TestHelper.Connect(connectorNode, firstNode);
                TestHelper.Connect(connectorNode, secondNode);
                TestHelper.Connect(firstNode, secondNode);

                List <CoreNode> nodes = new List <CoreNode> {
                    minerNode, connectorNode, firstNode, secondNode
                };

                nodes.ForEach(n =>
                {
                    TestHelper.MineBlocks(n, 1);
                    TestHelper.WaitForNodeToSync(nodes.ToArray());
                });

                int networkHeight = minerNode.FullNode.Chain.Height;
                Assert.Equal(networkHeight, nodes.Count);

                // Random node on network generates a block.
                TestHelper.MineBlocks(firstNode, 1);

                // Wait until connector get the hash of network's block.
                while ((connectorNode.FullNode.ChainBehaviorState.ConsensusTip.HashBlock != firstNode.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) ||
                       (firstNode.FullNode.ChainBehaviorState.ConsensusTip.Height == networkHeight))
                {
                    Thread.Sleep(1);
                }

                Assert.Equal(connectorNode.FullNode.Chain.Tip.HashBlock, firstNode.FullNode.Chain.Tip.HashBlock);
                Assert.Equal(minerNode.FullNode.Chain.Tip.Height, networkHeight);
                Assert.Equal(connectorNode.FullNode.Chain.Tip.Height, networkHeight + 1);

                // Miner mines the block.
                TestHelper.MineBlocks(minerNode, 1);

                networkHeight++;

                Assert.Equal(connectorNode.FullNode.Chain.Tip.HashBlock, firstNode.FullNode.Chain.Tip.HashBlock);
                Assert.Equal(minerNode.FullNode.Chain.Tip.Height, networkHeight);
                Assert.Equal(connectorNode.FullNode.Chain.Tip.Height, networkHeight);

                TestHelper.MineBlocks(connectorNode, 1);
                networkHeight++;

                TestHelper.WaitForNodeToSync(nodes.ToArray());

                nodes.All(n => n.FullNode.Chain.Height == networkHeight).Should()
                .BeTrue(because: "all nodes have synced to chain height");

                Assert.Equal(firstNode.FullNode.Chain.Tip.HashBlock, minerNode.FullNode.Chain.Tip.HashBlock);
            }
        }
        public void ReorgChain_AfterInitialRewind_ChainB_MinerB_Disconnects()
        {
            using (var builder = NodeBuilder.Create(this))
            {
                var network = new BitcoinRegTest();

                var minerA = builder.CreateStratisPowNode(network).WithDummyWallet();
                var minerB = builder.CreateStratisPowNode(network).WithDummyWallet();
                var syncer = builder.CreateStratisPowNode(network).WithDummyWallet();

                bool minerADisconnectedFromMinerB = false;

                // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound).
                void interceptor(ChainedHeaderBlock chainedHeaderBlock)
                {
                    if (minerADisconnectedFromMinerB)
                    {
                        return;
                    }

                    if (chainedHeaderBlock.ChainedHeader.Previous.Height == 5)
                    {
                        // Ensure that minerA's tips has rewound to 5.
                        TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 5));
                        TestHelper.Disconnect(minerA, minerB);
                        minerADisconnectedFromMinerB = true;

                        return;
                    }
                }

                minerA.SetDisconnectInterceptor(interceptor);

                // Start the nodes.
                minerA.Start();
                minerB.Start();
                syncer.Start();

                // MinerA mines 5 blocks.
                TestHelper.MineBlocks(minerA, 5);

                // MinerB/Syncer syncs with MinerA.
                TestHelper.ConnectAndSync(minerA, minerB, syncer);

                // Disable block propagation from MinerA to MinerB so that it can mine on its own and create a fork.
                TestHelper.DisableBlockPropagation(minerA, minerB);

                // MinerA continues to mine to height 9.
                TestHelper.MineBlocks(minerA, 4);
                TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 9);
                TestHelper.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 5);
                TestHelper.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 9);

                // MinerB mines 5 more blocks so that a reorg is triggered.
                TestHelper.MineBlocks(minerB, 5);
                TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 10));

                // MinerA and Syncer should have reorged to the longer chain.
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(minerA, minerB));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB));
            }
        }
예제 #21
0
        public void PullerVsMinerRaceCondition()
        {
            // Temporary fix so the Network static initialize will not break.
            var m = Network.Main;

            Transaction.TimeStamp = true;
            Block.BlockSignature  = true;
            try
            {
                using (NodeBuilder builder = NodeBuilder.Create())
                {
                    // This represents local node.
                    var stratisMinerLocal = builder.CreateStratisPosNode();

                    // This represents remote, which blocks are received by local node using its puller.
                    var stratisMinerRemote = builder.CreateStratisPosNode();

                    builder.StartAll();
                    stratisMinerLocal.NotInIBD();
                    stratisMinerRemote.NotInIBD();

                    stratisMinerLocal.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMinerLocal.FullNode.Network));
                    stratisMinerRemote.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMinerRemote.FullNode.Network));

                    // Let's mine block Ap and Bp.
                    stratisMinerRemote.GenerateStratisWithMiner(2);

                    // Wait for block repository for block sync to work.
                    TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMinerRemote));
                    stratisMinerLocal.CreateRPCClient().AddNode(stratisMinerRemote.Endpoint, true);

                    TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMinerLocal, stratisMinerRemote));

                    // Now disconnect the peers and mine block C2p on remote.
                    stratisMinerLocal.CreateRPCClient().RemoveNode(stratisMinerRemote.Endpoint);

                    // Mine block C2p.
                    stratisMinerRemote.GenerateStratisWithMiner(1);
                    Thread.Sleep(2000);

                    // Now reconnect nodes and mine block C1s before C2p arrives.
                    stratisMinerLocal.CreateRPCClient().AddNode(stratisMinerRemote.Endpoint, true);
                    stratisMinerLocal.GenerateStratisWithMiner(1);

                    // Mine block Dp.
                    uint256 dpHash = stratisMinerRemote.GenerateStratisWithMiner(1)[0];

                    // Now we wait until the local node's chain tip has correct hash of Dp.
                    TestHelper.WaitLoop(() => stratisMinerLocal.FullNode.Chain.Tip.HashBlock.Equals(dpHash));

                    // Then give it time to receive the block from the puller.
                    Thread.Sleep(2500);

                    // Check that local node accepted the Dp as consensus tip.
                    Assert.Equal(stratisMinerLocal.FullNode.ChainBehaviorState.ConsensusTip.HashBlock, dpHash);
                }
            }
            finally
            {
                Transaction.TimeStamp = false;
                Block.BlockSignature  = false;
            }
        }
예제 #22
0
        public void ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_Disconnects()
        {
            using (var builder = NodeBuilder.Create(this))
            {
                var network = new BitcoinRegTest();

                var minerA = builder.CreateStratisPowNode(network).WithDummyWallet();
                var minerB = builder.CreateStratisPowNode(network).NoValidation().WithDummyWallet();
                var syncer = builder.CreateStratisPowNode(network).WithDummyWallet();

                // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound).
                bool interceptor(ChainedHeaderBlock chainedHeaderBlock)
                {
                    if (chainedHeaderBlock.ChainedHeader.Previous.Height == 5)
                    {
                        // Ensure that minerA's tip has rewound to 5.
                        TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 5));
                        TestHelper.Disconnect(minerA, syncer);
                        return(true);
                    }

                    return(false);
                }

                minerA.BlockDisconnectInterceptor(interceptor);

                // Start the nodes.
                minerA.Start();
                minerB.Start();
                syncer.Start();

                // MinerA mines 5 blocks.
                TestHelper.MineBlocks(minerA, 5);

                // minerB and syncer syncs with minerA.
                TestHelper.ConnectAndSync(minerA, minerB, syncer);

                // Disconnect minerB from miner so that it can mine on its own and create a fork.
                TestHelper.Disconnect(minerA, minerB);

                // MinerA continues to mine to height 9.
                TestHelper.MineBlocks(minerA, 4);
                TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 9);
                TestHelper.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 5);
                TestHelper.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 9);

                // minerB mines 5 more blocks:
                // Block 6,7,9,10 = valid
                // Block 8 = invalid
                Assert.False(TestHelper.IsNodeConnected(minerB));
                TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(8, (node, block) => BlockBuilder.InvalidCoinbaseReward(node, block)).BuildAsync();

                // Reconnect minerA to minerB.
                TestHelper.Connect(minerA, minerB);

                // minerB should be disconnected from minerA.
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, minerB));

                // syncer should be disconnected from minerA (via interceptor).
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, syncer));

                // The reorg will fail at block 8 and roll back any changes.
                TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 9));
                TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 10));
                TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(syncer, 9));
            }
        }
        public async Task ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_DisconnectsAsync()
        {
            using (var builder = NodeBuilder.Create(this))
            {
                var minerA = builder.CreateStratisPowNode(new BitcoinRegTest(), "cmi-1-minerA").WithDummyWallet();
                var minerB = builder.CreateStratisPowNode(new BitcoinRegTestNoValidationRules(), "cmi-1-minerB").NoValidation().WithDummyWallet();
                var syncer = builder.CreateStratisPowNode(new BitcoinRegTest(), "cmi-1-syncer").WithDummyWallet();

                bool minerADisconnectedFromSyncer = false;

                // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound).
                void interceptor(ChainedHeaderBlock chainedHeaderBlock)
                {
                    if (minerADisconnectedFromSyncer)
                    {
                        return;
                    }

                    if (chainedHeaderBlock.ChainedHeader.Previous.Height == 10)
                    {
                        // Ensure that minerA's tip has rewound to 10.
                        TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 10));
                        TestHelper.Disconnect(minerA, syncer);
                        minerADisconnectedFromSyncer = true;

                        return;
                    }
                }

                // Start minerA and mine 10 blocks. We cannot use a premade chain as it adversely affects the max tip age calculation, causing sporadic sync errors.
                minerA.Start();
                TestHelper.MineBlocks(minerA, 10);
                TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 10);

                // Start the nodes.
                minerB.Start();
                syncer.Start();

                minerA.SetDisconnectInterceptor(interceptor);

                // minerB and syncer syncs with minerA.
                TestHelper.ConnectAndSync(minerA, minerB, syncer);

                // Disconnect minerB from miner so that it can mine on its own and create a fork.
                TestHelper.Disconnect(minerA, minerB);

                // MinerA continues to mine to height 14.
                TestHelper.MineBlocks(minerA, 4);
                TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14);
                TestBase.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 10);
                TestBase.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 14);

                // minerB mines 5 more blocks:
                // Block 6,7,9,10 = valid
                // Block 8 = invalid
                Assert.False(TestHelper.IsNodeConnected(minerB));
                await TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(13, (node, block) => BlockBuilder.InvalidCoinbaseReward(node, block)).BuildAsync();

                // Reconnect minerA to minerB.
                TestHelper.ConnectNoCheck(minerA, minerB);

                // minerB should be disconnected from minerA.
                TestBase.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, minerB));

                // syncer should be disconnected from minerA (via interceptor).
                TestBase.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, syncer));

                // The reorg will fail at block 8 and roll back any changes.
                TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 14));
                TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 15));
                TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(syncer, 14));
            }
        }
예제 #24
0
        public void Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisMiner  = builder.CreateStratisPosNode(this.posNetwork);
                CoreNode stratisSyncer = builder.CreateStratisPosNode(this.posNetwork);
                CoreNode stratisReorg  = builder.CreateStratisPosNode(this.posNetwork);

                builder.StartAll();
                stratisMiner.NotInIBD().WithWallet(walletPassword, walletName, walletPassphrase);
                stratisSyncer.NotInIBD().WithWallet(walletPassword, walletName, walletPassphrase);
                stratisReorg.NotInIBD().WithWallet(walletPassword, walletName, walletPassphrase);

                TestHelper.MineBlocks(stratisMiner, walletName, walletPassword, walletAccount, 1);

                // wait for block repo for block sync to work
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner));
                stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                stratisMiner.CreateRPCClient().AddNode(stratisSyncer.Endpoint, true);

                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisReorg));

                // create a reorg by mining on two different chains
                // ================================================

                stratisMiner.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                stratisSyncer.CreateRPCClient().RemoveNode(stratisReorg.Endpoint);
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(stratisReorg));

                TestHelper.MineBlocks(stratisMiner, walletName, walletPassword, walletAccount, 11);
                TestHelper.MineBlocks(stratisReorg, walletName, walletPassword, walletAccount, 12);

                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner));
                TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg));

                // make sure the nodes are actually on different chains.
                Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock);

                TestHelper.TriggerSync(stratisSyncer);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));

                // The hash before the reorg node is connected.
                uint256 hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock;

                // connect the reorg chain
                stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);
                stratisSyncer.CreateRPCClient().AddNode(stratisReorg.Endpoint, true);

                // trigger nodes to sync
                TestHelper.TriggerSync(stratisMiner);
                TestHelper.TriggerSync(stratisReorg);
                TestHelper.TriggerSync(stratisSyncer);

                // wait for the synced chain to get headers updated.
                TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedPeers.Any());

                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false);
                TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false);

                // check that a reorg did not happen.
                Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock);
            }
        }