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); }
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()); }
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())); } }
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); }
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); }
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); }
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()); }
public void FindHeaderAndVerifyBlockIntegrityCalledForBogusBlock_ExceptionShouldBeThrown() { // Chain header tree setup. Initial chain has 4 headers. // Example: h1=h2=h3=h4. const int initialChainSize = 4; 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. // Example: h1=h2=h3=h4=h5=h6. initialChainTip = ctx.ExtendAChain(extensionChainSize, initialChainTip); // Call FindHeaderAndVerifyBlockIntegrity on the block from header 6. // BlockDownloadedForMissingChainedHeaderException should be thrown. Action verificationAction = () => cht.FindHeaderAndVerifyBlockIntegrity(initialChainTip.Block); verificationAction.Should().Throw <BlockDownloadedForMissingChainedHeaderException>(); }
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); }