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 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()); }