public void Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode stratisMiner  = builder.CreateStratisPosNode(this.posNetwork).NotInIBD();
                CoreNode stratisSyncer = builder.CreateStratisPosNode(this.posNetwork).NotInIBD();
                CoreNode stratisReorg  = builder.CreateStratisPosNode(this.posNetwork).NotInIBD();

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

                TestHelper.MineBlocks(stratisMiner, 1);

                // wait for block repo for block sync to work
                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, 11);
                TestHelper.MineBlocks(stratisReorg, 12);

                // 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);
            }
        }
        public void PullerVsMinerRaceCondition()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                // This represents local node.
                CoreNode stratisMinerLocal = builder.CreateStratisPosNode(this.posNetwork).NotInIBD();

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

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

                // Let's mine block Ap and Bp.
                TestHelper.MineBlocks(stratisMinerRemote, 2);

                // Wait for block repository for block sync to work.
                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);
                TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(stratisMinerRemote));

                // Mine block C2p.
                TestHelper.MineBlocks(stratisMinerRemote, 1);
                Thread.Sleep(2000);

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

                // Mine block Dp.
                uint256 dpHash = TestHelper.MineBlocks(stratisMinerRemote, 1).BlockHashes[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);
            }
        }
        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);
            }
        }