Exemple #1
0
        public void ChainHasAssumeValidHeaderAndMarkedForDownloadWhenPresented_SecondChainWithoutAssumeValidAlsoMarkedForDownload()
        {
            // Chain header tree setup with disabled checkpoints.
            // Initial chain has 2 headers.
            // Example: h1=h2.
            const int         initialChainSize = 2;
            TestContext       ctx             = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints(false).Build();
            ChainedHeaderTree cht             = ctx.ChainedHeaderTree;
            ChainedHeader     initialChainTip = ctx.InitialChainTip;

            // Setup two alternative chains A and B of the same length.
            const int          presentedChainSize       = 4;
            ChainedHeader      chainATip                = ctx.ExtendAChain(presentedChainSize, initialChainTip); // i.e. h1=h2=a1=a2=a3=a4
            ChainedHeader      chainBTip                = ctx.ExtendAChain(presentedChainSize, initialChainTip); // i.e. h1=h2=b1=b2=b3=b4
            List <BlockHeader> listOfChainABlockHeaders = ctx.ChainedHeaderToList(chainATip, initialChainSize + presentedChainSize);
            List <BlockHeader> listOfChainBBlockHeaders = ctx.ChainedHeaderToList(chainBTip, initialChainSize + presentedChainSize);

            // Set "Assume Valid" to the 4th block of the chain A.
            // Example h1=h2=a1=(a2)=a3=a4.
            ctx.ConsensusSettings.BlockAssumedValid = listOfChainABlockHeaders[3].GetHash();

            // Chain A is presented by peer 1. It meets "assume valid" hash and should
            // be marked for a download.
            ConnectNewHeadersResult connectNewHeadersResult = cht.ConnectNewHeaders(1, listOfChainABlockHeaders);
            ChainedHeader           chainedHeaderDownloadTo = connectNewHeadersResult.DownloadTo;

            chainedHeaderDownloadTo.HashBlock.Should().Be(chainATip.HashBlock);

            // Chain B is presented by peer 2. It doesn't meet "assume valid" hash but should still
            // be marked for a download.
            connectNewHeadersResult = cht.ConnectNewHeaders(2, listOfChainBBlockHeaders);
            chainedHeaderDownloadTo = connectNewHeadersResult.DownloadTo;
            chainedHeaderDownloadTo.HashBlock.Should().Be(chainBTip.HashBlock);
        }
Exemple #2
0
        public void ConnectHeaders_SupplyHeadersThenSupplyMore_Both_Tip_PeerId_Maps_ShouldBeUpdated()
        {
            TestContext       testContext = new TestContextBuilder().WithInitialChain(10).Build();
            ChainedHeaderTree cht         = testContext.ChainedHeaderTree;
            ChainedHeader     chainTip    = testContext.InitialChainTip;

            List <BlockHeader> listOfExistingHeaders = testContext.ChainedHeaderToList(chainTip, 10);

            cht.ConnectNewHeaders(1, listOfExistingHeaders);

            Dictionary <uint256, HashSet <int> > peerIdsByTipHashBefore = cht.GetPeerIdsByTipHash().ToDictionary(entry => entry.Key, entry => new HashSet <int>(entry.Value));
            Dictionary <int, uint256>            peerTipsByPeerIdBefore = cht.GetPeerTipsByPeerId().ToDictionary(entry => entry.Key, entry => new uint256(entry.Value));

            // (of 25 headers) supply last 5 existing and first 10 new
            ChainedHeader      newChainTip            = testContext.ExtendAChain(15, chainTip);
            List <BlockHeader> listOfNewAndOldHeaders = testContext.ChainedHeaderToList(newChainTip, 25).GetRange(5, 15);

            cht.ConnectNewHeaders(1, listOfNewAndOldHeaders);

            Dictionary <uint256, HashSet <int> > peerIdsByTipHashAfter = cht.GetPeerIdsByTipHash();
            Dictionary <int, uint256>            peerTipsByPeerIdAfter = cht.GetPeerTipsByPeerId();

            // Tip # -> peer id map has changed
            Assert.True(peerIdsByTipHashBefore.FirstOrDefault(x => x.Value.Contains(1)).Key !=
                        peerIdsByTipHashAfter.FirstOrDefault(x => x.Value.Contains(1)).Key);

            // Peer id -> tip # map has changed
            Assert.True(peerTipsByPeerIdBefore[1] != peerTipsByPeerIdAfter[1]);

            // reassigning # so amount of items the same
            Assert.True(peerTipsByPeerIdBefore.Values.Count == peerTipsByPeerIdAfter.Values.Count);
        }
Exemple #3
0
        public void ChainHasOneCheckPointAndAssumeValid_TwoAlternativeChainsArePresented_BothChainsAreMarkedForDownload()
        {
            // Chain header tree setup with disabled checkpoints.
            // Initial chain has 2 headers.
            // Example: h1=h2.
            const int         initialChainSize   = 2;
            const int         extensionChainSize = 2;
            TestContext       ctx             = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints().Build();
            ChainedHeaderTree cht             = ctx.ChainedHeaderTree;
            ChainedHeader     initialChainTip = ctx.InitialChainTip;

            // Extend chain with 2 more headers.
            initialChainTip = ctx.ExtendAChain(extensionChainSize, initialChainTip); // i.e. h1=h2=h3=h4
            List <BlockHeader> listOfCurrentChainHeaders = ctx.ChainedHeaderToList(initialChainTip, initialChainSize + extensionChainSize);

            // Setup a known checkpoint at header 4.
            // Example: h1=h2=h3=(h4).
            const int checkpointHeight = 4;
            var       checkpoint       = new CheckpointFixture(checkpointHeight, listOfCurrentChainHeaders.Last());

            ctx.SetupCheckpoints(checkpoint);

            // Extend chain and add "Assume valid" at block 6.
            // Example: h1=h2=h3=(h4)=h5=[h6].
            const int     chainExtension   = 2;
            ChainedHeader extendedChainTip = ctx.ExtendAChain(chainExtension, initialChainTip);

            ctx.ConsensusSettings.BlockAssumedValid = extendedChainTip.HashBlock;

            // Setup two alternative chains A and B. Chain A covers the last checkpoint (4) and "assume valid" (6).
            // Chain B only covers the last checkpoint (4).
            const int          chainAExtensionSize      = 2;
            const int          chainBExtensionSize      = 6;
            ChainedHeader      chainATip                = ctx.ExtendAChain(chainAExtensionSize, extendedChainTip); // i.e. h1=h2=h3=(h4)=h5=[h6]=a7=a8
            ChainedHeader      chainBTip                = ctx.ExtendAChain(chainBExtensionSize, initialChainTip);  // i.e. h1=h2=h3=(h4)=b5=b6=b7=b8=b9=b10
            List <BlockHeader> listOfChainABlockHeaders = ctx.ChainedHeaderToList(chainATip, initialChainSize + extensionChainSize + chainExtension + chainAExtensionSize);
            List <BlockHeader> listOfChainBBlockHeaders = ctx.ChainedHeaderToList(chainBTip, initialChainSize + extensionChainSize + chainBExtensionSize);

            // Chain A is presented by peer 1.
            // DownloadFrom should be set to header 3.
            // DownloadTo should be set to header 8.
            ConnectNewHeadersResult result = cht.ConnectNewHeaders(1, listOfChainABlockHeaders);

            result.DownloadFrom.HashBlock.Should().Be(listOfChainABlockHeaders.Skip(2).First().GetHash());
            result.DownloadTo.HashBlock.Should().Be(listOfChainABlockHeaders.Last().GetHash());

            // Chain B is presented by peer 2.
            // DownloadFrom should be set to header 5.
            // DownloadTo should be set to header 10.
            result = cht.ConnectNewHeaders(2, listOfChainBBlockHeaders);
            result.DownloadFrom.HashBlock.Should().Be(listOfChainBBlockHeaders[checkpointHeight].GetHash());
            result.DownloadTo.HashBlock.Should().Be(listOfChainBBlockHeaders.Last().GetHash());
        }
Exemple #4
0
        public void ChainHasTwoCheckPoints_ChainCoveringOnlyFirstCheckPointIsPresented_ChainIsDiscardedUpUntilFirstCheckpoint()
        {
            // Chain header tree setup.
            // Initial chain has 2 headers.
            // Example: h1=h2.
            const int         initialChainSize      = 2;
            const int         currentChainExtension = 6;
            TestContext       ctx             = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints().Build();
            ChainedHeaderTree cht             = ctx.ChainedHeaderTree;
            ChainedHeader     initialChainTip = ctx.InitialChainTip;

            ChainedHeader      extendedChainTip          = ctx.ExtendAChain(currentChainExtension, initialChainTip); // i.e. h1=h2=h3=h4=h5=h6=h7=h8
            List <BlockHeader> listOfCurrentChainHeaders = ctx.ChainedHeaderToList(extendedChainTip, initialChainSize + currentChainExtension);

            // Setup two known checkpoints at header 4 and 7.
            // Example: h1=h2=h3=(h4)=h5=h6=(h7)=h8.
            const int firstCheckpointHeight  = 4;
            const int secondCheckpointHeight = 7;
            var       checkpoint1            = new CheckpointFixture(firstCheckpointHeight, listOfCurrentChainHeaders[firstCheckpointHeight - 1]);
            var       checkpoint2            = new CheckpointFixture(secondCheckpointHeight, listOfCurrentChainHeaders[secondCheckpointHeight - 1]);

            ctx.SetupCheckpoints(checkpoint1, checkpoint2);

            // Setup new chain that only covers first checkpoint but doesn't cover second checkpoint.
            // Example: h1=h2=h3=(h4)=h5=h6=x7=x8=x9=x10.
            const int newChainExtension = 4;

            extendedChainTip = extendedChainTip.GetAncestor(6); // walk back to block 6
            extendedChainTip = ctx.ExtendAChain(newChainExtension, extendedChainTip);
            List <BlockHeader> listOfNewChainHeaders = ctx.ChainedHeaderToList(extendedChainTip, extendedChainTip.Height);

            // First 5 blocks are presented by peer 1.
            // DownloadTo should be set to a checkpoint 1.
            ConnectNewHeadersResult result = cht.ConnectNewHeaders(1, listOfNewChainHeaders.Take(5).ToList());

            result.DownloadTo.HashBlock.Should().Be(checkpoint1.Header.GetHash());

            // Remaining 5 blocks are presented by peer 1 which do not cover checkpoint 2.
            // InvalidHeaderException should be thrown.
            List <BlockHeader> violatingHeaders = listOfNewChainHeaders.Skip(5).ToList();
            Action             connectAction    = () =>
            {
                cht.ConnectNewHeaders(1, violatingHeaders);
            };

            connectAction.Should().Throw <CheckpointMismatchException>();

            // Make sure headers for violating chain don't exist.
            foreach (BlockHeader header in violatingHeaders)
            {
                Assert.False(cht.GetChainedHeadersByHash().ContainsKey(header.GetHash()));
            }
        }
Exemple #5
0
        public void PresentDifferentChains_AlternativeChainWithMoreChainWorkShouldAlwaysBeMarkedForDownload()
        {
            // Chain header tree setup.
            TestContext       ctx             = new TestContextBuilder().WithInitialChain(5).UseCheckpoints(false).Build();
            ChainedHeaderTree cht             = ctx.ChainedHeaderTree;
            ChainedHeader     initialChainTip = ctx.InitialChainTip;

            // Chains A and B setup.
            const int          commonChainSize          = 4;
            const int          chainAExtension          = 4;
            const int          chainBExtension          = 2;
            ChainedHeader      commonChainTip           = ctx.ExtendAChain(commonChainSize, initialChainTip); // i.e. h1=h2=h3=h4
            ChainedHeader      chainATip                = ctx.ExtendAChain(chainAExtension, commonChainTip);  // i.e. (h1=h2=h3=h4)=a5=a6=a7=a8
            ChainedHeader      chainBTip                = ctx.ExtendAChain(chainBExtension, commonChainTip);  // i.e. (h1=h2=h3=h4)=b5=b6
            List <BlockHeader> listOfChainABlockHeaders = ctx.ChainedHeaderToList(chainATip, commonChainSize + chainAExtension);
            List <BlockHeader> listOfChainBBlockHeaders = ctx.ChainedHeaderToList(chainBTip, commonChainSize + chainBExtension);

            // Chain A is presented by peer 1. DownloadTo should be chain A tip.
            ConnectNewHeadersResult connectNewHeadersResult = cht.ConnectNewHeaders(1, listOfChainABlockHeaders);
            ChainedHeader           chainedHeaderTo         = connectNewHeadersResult.DownloadTo;

            chainedHeaderTo.HashBlock.Should().Be(chainATip.HashBlock);

            // Set chain A tip as a consensus tip.
            cht.ConsensusTipChanged(chainATip);

            // Chain B is presented by peer 2. DownloadTo should be not set, as chain
            // B has less chain work.
            connectNewHeadersResult = cht.ConnectNewHeaders(2, listOfChainBBlockHeaders);
            connectNewHeadersResult.DownloadTo.Should().BeNull();

            // Add more chain work and blocks into chain B.
            const int chainBAdditionalBlocks = 4;

            chainBTip = ctx.ExtendAChain(chainBAdditionalBlocks, chainBTip); // i.e. (h1=h2=h3=h4)=b5=b6=b7=b8=b9=b10
            listOfChainBBlockHeaders = ctx.ChainedHeaderToList(chainBTip, commonChainSize + chainBExtension + chainBAdditionalBlocks);
            List <BlockHeader> listOfNewChainBBlockHeaders = listOfChainBBlockHeaders.TakeLast(chainBAdditionalBlocks).ToList();

            // Chain B is presented by peer 2 again.
            // DownloadTo should now be chain B as B has more chain work than chain A.
            // DownloadFrom should be the block where split occurred.
            // h1=h2=h3=h4=(b5)=b6=b7=b8=b9=(b10) - from b5 to b10.
            connectNewHeadersResult = cht.ConnectNewHeaders(2, listOfNewChainBBlockHeaders);

            ChainedHeader chainedHeaderFrom  = connectNewHeadersResult.DownloadFrom;
            BlockHeader   expectedHeaderFrom = listOfChainBBlockHeaders[commonChainSize];

            chainedHeaderFrom.Header.GetHash().Should().Be(expectedHeaderFrom.GetHash());

            chainedHeaderTo = connectNewHeadersResult.DownloadTo;
            chainedHeaderTo.HashBlock.Should().Be(chainBTip.HashBlock);
        }
Exemple #6
0
        public void ConnectHeaders_SupplyHeaders_ToDownloadArraySizeSameAsNumberOfHeaders()
        {
            // Setup
            TestContext       ctx      = new TestContextBuilder().WithInitialChain(5).UseCheckpoints(false).Build();
            ChainedHeaderTree cht      = ctx.ChainedHeaderTree;
            ChainedHeader     chainTip = ctx.InitialChainTip;

            // Checkpoints are off
            Assert.False(ctx.ConsensusSettings.UseCheckpoints);
            ChainedHeader      newChainTip           = ctx.ExtendAChain(7, chainTip);
            List <BlockHeader> listOfNewBlockHeaders = ctx.ChainedHeaderToList(newChainTip, 7);

            // Peer 1 supplies some headers
            List <BlockHeader> peer1Headers = listOfNewBlockHeaders.GetRange(0, 3);

            cht.ConnectNewHeaders(1, peer1Headers);

            // Peer 2 supplies some more headers
            List <BlockHeader>      peer2Headers            = listOfNewBlockHeaders.GetRange(3, 4);
            ConnectNewHeadersResult connectNewHeadersResult = cht.ConnectNewHeaders(2, peer2Headers);
            ChainedHeader           chainedHeaderFrom       = connectNewHeadersResult.DownloadFrom;
            ChainedHeader           chainedHeaderTo         = connectNewHeadersResult.DownloadTo;
            int headersToDownloadCount = chainedHeaderTo.Height - chainedHeaderFrom.Height + 1; // Inclusive

            // ToDownload array of the same size as the amount of headers
            Assert.Equal(headersToDownloadCount, peer2Headers.Count);
        }
Exemple #7
0
        public void ChainHasOneCheckPointAndAssumeValid_ChainsWithCheckpointButMissedAssumeValidIsPresented_BothChainsAreMarkedForDownload()
        {
            // Chain header tree setup with disabled checkpoints.
            // Initial chain has 2 headers.
            // Example: h1=h2.
            const int         initialChainSize   = 2;
            const int         extensionChainSize = 2;
            TestContext       ctx             = new TestContextBuilder().WithInitialChain(initialChainSize).UseCheckpoints().Build();
            ChainedHeaderTree cht             = ctx.ChainedHeaderTree;
            ChainedHeader     initialChainTip = ctx.InitialChainTip;

            // Extend chain with 2 more headers.
            initialChainTip = ctx.ExtendAChain(extensionChainSize, initialChainTip); // i.e. h1=h2=h3=h4
            List <BlockHeader> listOfCurrentChainHeaders = ctx.ChainedHeaderToList(initialChainTip, initialChainSize + extensionChainSize);

            // Setup a known checkpoint at header 4.
            // Example: h1=h2=h3=(h4).
            const int checkpointHeight = 4;
            var       checkpoint       = new CheckpointFixture(checkpointHeight, listOfCurrentChainHeaders.Last());

            ctx.SetupCheckpoints(checkpoint);

            // Extend chain and add "Assume valid" at block 6.
            // Example: h1=h2=h3=(h4)=h5=[h6].
            const int     chainExtension   = 2;
            ChainedHeader extendedChainTip = ctx.ExtendAChain(chainExtension, initialChainTip);

            ctx.ConsensusSettings.BlockAssumedValid = extendedChainTip.HashBlock;

            // Setup new chain, which covers the last checkpoint (4), but misses "assumed valid".
            const int     newChainExtensionSize = 6;
            ChainedHeader newChainTip           = ctx.ExtendAChain(newChainExtensionSize, initialChainTip); // i.e. h1=h2=h3=(h4)=b5=b6=b7=b8=b9=b10

            listOfCurrentChainHeaders = ctx.ChainedHeaderToList(newChainTip, initialChainSize + extensionChainSize + newChainExtensionSize);

            // Chain is presented by peer 2.
            // DownloadFrom should be set to header 3.
            // DownloadTo should be set to header 10.
            ConnectNewHeadersResult result = cht.ConnectNewHeaders(2, listOfCurrentChainHeaders);

            result.DownloadFrom.HashBlock.Should().Be(listOfCurrentChainHeaders.Skip(2).First().GetHash());
            result.DownloadTo.HashBlock.Should().Be(listOfCurrentChainHeaders.Last().GetHash());
        }
Exemple #8
0
        public void ConnectHeaders_NoNewHeadersToConnect_ShouldReturnNothingToDownload()
        {
            TestContext       testContext       = new TestContextBuilder().WithInitialChain(10).Build();
            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
            ChainedHeader     chainTip          = testContext.InitialChainTip;

            List <BlockHeader> listOfExistingHeaders = testContext.ChainedHeaderToList(chainTip, 4);

            ConnectNewHeadersResult connectNewHeadersResult = chainedHeaderTree.ConnectNewHeaders(1, listOfExistingHeaders);

            Assert.True(testContext.NoDownloadRequested(connectNewHeadersResult));
            Assert.Equal(11, chainedHeaderTree.GetChainedHeadersByHash().Count);
        }
Exemple #9
0
        public void ConnectHeaders_NewAndExistingHeaders_ShouldCreateNewHeaders()
        {
            TestContext       testContext       = new TestContextBuilder().WithInitialChain(10).Build();
            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
            ChainedHeader     chainTip          = testContext.InitialChainTip;

            chainTip.BlockDataAvailability = BlockDataAvailabilityState.BlockAvailable;
            ChainedHeader newChainTip = testContext.ExtendAChain(10, chainTip); // create 10 more headers

            List <BlockHeader> listOfExistingHeaders = testContext.ChainedHeaderToList(chainTip, 10);
            List <BlockHeader> listOfNewHeaders      = testContext.ChainedHeaderToList(newChainTip, 10);

            chainTip.BlockValidationState = ValidationState.FullyValidated;

            ConnectNewHeadersResult connectedHeadersResultOld = chainedHeaderTree.ConnectNewHeaders(2, listOfExistingHeaders);
            ConnectNewHeadersResult connectedHeadersResultNew = chainedHeaderTree.ConnectNewHeaders(1, listOfNewHeaders);

            Assert.Equal(21, chainedHeaderTree.GetChainedHeadersByHash().Count);
            Assert.Equal(10, listOfNewHeaders.Count);
            Assert.True(testContext.NoDownloadRequested(connectedHeadersResultOld));
            Assert.Equal(listOfNewHeaders.Last(), connectedHeadersResultNew.DownloadTo.Header);
            Assert.Equal(listOfNewHeaders.First(), connectedHeadersResultNew.DownloadFrom.Header);
        }
Exemple #10
0
        public void ConnectHeaders_HeadersFromTwoPeers_ShouldCreateTwoPeerTips()
        {
            TestContext       testContext       = new TestContextBuilder().WithInitialChain(10).Build();
            ChainedHeaderTree chainedHeaderTree = testContext.ChainedHeaderTree;
            ChainedHeader     chainTip          = testContext.InitialChainTip;

            List <BlockHeader> listOfExistingHeaders = testContext.ChainedHeaderToList(chainTip, 4);

            ConnectNewHeadersResult connectNewHeaders1 = chainedHeaderTree.ConnectNewHeaders(1, listOfExistingHeaders);
            ConnectNewHeadersResult connectNewHeaders2 = chainedHeaderTree.ConnectNewHeaders(2, listOfExistingHeaders);

            Assert.Single(chainedHeaderTree.GetPeerIdsByTipHash());
            Assert.Equal(11, chainedHeaderTree.GetChainedHeadersByHash().Count);

            Assert.Equal(3, chainedHeaderTree.GetPeerIdsByTipHash().First().Value.Count);

            Assert.Equal(ChainedHeaderTree.LocalPeerId, chainedHeaderTree.GetPeerIdsByTipHash().First().Value.ElementAt(0));
            Assert.Equal(1, chainedHeaderTree.GetPeerIdsByTipHash().First().Value.ElementAt(1));
            Assert.Equal(2, chainedHeaderTree.GetPeerIdsByTipHash().First().Value.ElementAt(2));

            Assert.True(testContext.NoDownloadRequested(connectNewHeaders1));
            Assert.True(testContext.NoDownloadRequested(connectNewHeaders2));
        }