public void PresentDifferentChains_AlternativeChainWithMoreChainWorkShouldAlwaysBeMarkedForDownload() { // Chain header tree setup. var ctx = new TestContext(); ChainedHeaderTree cht = ctx.CreateChainedHeaderTree(); ChainedHeader initialChainTip = ctx.ExtendAChain(5); cht.Initialize(initialChainTip, true); ctx.ConsensusSettings.UseCheckpoints = false; // Chains A and B setup. const int commonChainSize = 4; const int chainAExtension = 4; const int chainBExtension = 2; ChainedHeader commonChainTip = ctx.ExtendAChain(commonChainSize, initialChainTip); // ie. h1=h2=h3=h4 ChainedHeader chainATip = ctx.ExtendAChain(chainAExtension, commonChainTip); // ie. (h1=h2=h3=h4)=a5=a6=a7=a8 ChainedHeader chainBTip = ctx.ExtendAChain(chainBExtension, commonChainTip); // ie. (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); // ie. (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); }
internal void SwitchToChain(ChainedHeaderTree cht, ChainedHeader chainTip, ChainedHeader consumedHeader, int extensionSize) { ChainedHeader[] consumedHeaders = consumedHeader.ToArray(extensionSize); for (int i = 0; i < extensionSize; i++) { ChainedHeader currentConsumedCh = consumedHeaders[i]; cht.BlockDataDownloaded(currentConsumedCh, chainTip.GetAncestor(currentConsumedCh.Height).Block); cht.PartialValidationSucceeded(currentConsumedCh, out bool fullValidationRequired); cht.ConsensusTipChanged(currentConsumedCh); } }