From 192375f1846f967859529135bc015429d7a9d7fe Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 16:05:55 +1100 Subject: [PATCH 01/13] Improve tracking of Consensus tip with ChainIndexer tip --- src/NBitcoin.Tests/ChainTests.cs | 2 +- src/NBitcoin/ChainIndexer.cs | 69 +++++++++---------- .../BlockStoreQueue.cs | 2 +- ...rovenHeaderConsenusManagerBehaviorTests.cs | 3 +- .../ConsensusChainIndexerExtensions.cs | 43 ------------ src/Stratis.Bitcoin/Base/BaseFeature.cs | 4 +- .../Consensus/ConsensusManager.cs | 6 +- .../FederationGatewayControllerTests.cs | 6 +- .../WalletRepositoryTests.cs | 3 +- .../ChainIndexerRangeQueryTests.cs | 8 +-- 10 files changed, 50 insertions(+), 96 deletions(-) diff --git a/src/NBitcoin.Tests/ChainTests.cs b/src/NBitcoin.Tests/ChainTests.cs index f729cbc794..b9c3ea93c7 100644 --- a/src/NBitcoin.Tests/ChainTests.cs +++ b/src/NBitcoin.Tests/ChainTests.cs @@ -207,7 +207,7 @@ public void CanEnumerateAfterChainedBlock() enumerator.MoveNext(); Assert.True(enumerator.Current == c); - chain.Initialize(b); + chain.SetTip(b); ChainedHeader cc = this.AppendBlock(chain); ChainedHeader dd = this.AppendBlock(chain); diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 91839a76ee..0e1bce1e9d 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace NBitcoin { @@ -28,55 +29,43 @@ public class ChainIndexer /// The tip height of the best known validated chain. /// public int Height => this.Tip.Height; + public ChainedHeader Genesis => this.GetHeader(0); - public ChainIndexer() + public ChainIndexer(Network network) { - this.blocksByHeight = new Dictionary(); - this.blocksById = new Dictionary(); - } - - public ChainIndexer(Network network) : this() - { this.Network = network; - this.Initialize(new ChainedHeader(network.GetGenesis().Header, network.GetGenesis().GetHash(), 0)); - } + this.blocksByHeight = new Dictionary(); + this.blocksById = new Dictionary(); - public ChainIndexer(Network network, ChainedHeader chainedHeader) : this() - { - this.Network = network; + var tip = new ChainedHeader(this.Network.GetGenesis().Header, this.Network.GetGenesis().GetHash(), 0); + this.AddInternal(tip); - this.Initialize(chainedHeader); + this.Tip = tip; } - public void Initialize(ChainedHeader chainedHeader) + public ChainedHeader SetTip(ChainedHeader chainedHeader) { lock (this.lockObject) { - this.blocksById.Clear(); - this.blocksByHeight.Clear(); + ChainedHeader fork = chainedHeader.FindFork(this.Tip); - ChainedHeader iterator = chainedHeader; + if (fork == null) + throw new InvalidOperationException("Wrong network"); - while (iterator != null) - { - this.blocksById.Add(iterator.HashBlock, iterator); - this.blocksByHeight.Add(iterator.Height, iterator); + foreach (ChainedHeader header in this.Tip.EnumerateToGenesis().TakeWhile(h => h.Height > fork.Height)) + RemoveInternal(header); - if (iterator.Height == 0) - { - if (this.Network.GenesisHash != iterator.HashBlock) - throw new InvalidOperationException("Wrong network"); - } - - iterator = iterator.Previous; - } + foreach (ChainedHeader header in chainedHeader.EnumerateToGenesis().TakeWhile(h => h.Height > fork.Height)) + AddInternal(header); this.Tip = chainedHeader; + + return fork; } } - + /// /// Returns the first chained block header that exists in the chain from the list of block hashes. /// @@ -185,16 +174,21 @@ public void Add(ChainedHeader addTip) { lock (this.lockObject) { - if(this.Tip.HashBlock != addTip.Previous.HashBlock) + if (this.Tip.HashBlock != addTip.Previous.HashBlock) throw new InvalidOperationException("New tip must be consecutive"); - this.blocksById.Add(addTip.HashBlock, addTip); - this.blocksByHeight.Add(addTip.Height, addTip); + this.AddInternal(addTip); this.Tip = addTip; } } + private void AddInternal(ChainedHeader addTip) + { + this.blocksById.Add(addTip.HashBlock, addTip); + this.blocksByHeight.Add(addTip.Height, addTip); + } + /// /// TODO: Make this internal when the component moves to Stratis.Bitcoin /// @@ -205,13 +199,18 @@ public void Remove(ChainedHeader removeTip) if (this.Tip.HashBlock != removeTip.HashBlock) throw new InvalidOperationException("Trying to remove item that is not the tip."); - this.blocksById.Remove(removeTip.HashBlock); - this.blocksByHeight.Remove(removeTip.Height); + RemoveInternal(removeTip); this.Tip = this.blocksById[removeTip.Previous.HashBlock]; } } + private void RemoveInternal(ChainedHeader removeTip) + { + this.blocksById.Remove(removeTip.HashBlock); + this.blocksByHeight.Remove(removeTip.Height); + } + /// /// Get a based on it's hash. /// diff --git a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs index ae245d367e..9764e65777 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs @@ -451,7 +451,7 @@ private ChainedHeader RecoverStoreTip() if (blockStoreResetList.Count != 0) this.blockRepository.Delete(new HashHeightPair(newTip), blockStoreResetList); - this.chainIndexer.Initialize(newTip); // we have to set chain store to be same as the store tip. + this.chainIndexer.SetTip(newTip); // we have to set chain store to be same as the store tip. this.logger.LogWarning("Block store tip recovered to block '{0}'.", newTip); diff --git a/src/Stratis.Bitcoin.Features.Consensus.Tests/ProvenBlockHeaders/ProvenHeaderConsenusManagerBehaviorTests.cs b/src/Stratis.Bitcoin.Features.Consensus.Tests/ProvenBlockHeaders/ProvenHeaderConsenusManagerBehaviorTests.cs index a91cfd65db..e3d557d089 100644 --- a/src/Stratis.Bitcoin.Features.Consensus.Tests/ProvenBlockHeaders/ProvenHeaderConsenusManagerBehaviorTests.cs +++ b/src/Stratis.Bitcoin.Features.Consensus.Tests/ProvenBlockHeaders/ProvenHeaderConsenusManagerBehaviorTests.cs @@ -87,7 +87,8 @@ public void ConstructProvenHeaderPayload_Consecutive_Headers() { var provenHeaderChain = BuildProvenHeaderChain(10); - var chain = new ChainIndexer(this.Network, provenHeaderChain); + var chain = new ChainIndexer(this.Network); + chain.SetTip(provenHeaderChain); var consensusManager = new Mock(); consensusManager.Setup(c => c.Tip).Returns(provenHeaderChain); diff --git a/src/Stratis.Bitcoin.Tests.Common/ConsensusChainIndexerExtensions.cs b/src/Stratis.Bitcoin.Tests.Common/ConsensusChainIndexerExtensions.cs index ef013e6566..cfadb337d0 100644 --- a/src/Stratis.Bitcoin.Tests.Common/ConsensusChainIndexerExtensions.cs +++ b/src/Stratis.Bitcoin.Tests.Common/ConsensusChainIndexerExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using NBitcoin; using Xunit; @@ -35,48 +34,6 @@ public static bool TrySetTip(this ChainIndexer chainIndexer, BlockHeader header, return true; } - public static ChainedHeader SetTip(this ChainIndexer chainIndexer, ChainedHeader block) - { - ChainedHeader fork = chainIndexer.Tip.FindFork(block); - - chainIndexer.Initialize(block); - - return fork; - } - - private static IEnumerable EnumerateThisToFork(this ChainIndexer chainIndexer, ChainedHeader block) - { - if (chainIndexer.Tip == null) - yield break; - - ChainedHeader tip = chainIndexer.Tip; - while (true) - { - if (ReferenceEquals(null, block) || ReferenceEquals(null, tip)) - throw new InvalidOperationException("No fork found between the two chains"); - - if (tip.Height > block.Height) - { - yield return tip; - tip = tip.Previous; - } - else if (tip.Height < block.Height) - { - block = block.Previous; - } - else if (tip.Height == block.Height) - { - if (tip.HashBlock == block.HashBlock) - break; - - yield return tip; - - block = block.Previous; - tip = tip.Previous; - } - } - } - public static ChainIndexer Load(this ChainIndexer chainIndexer, byte[] chain) { using (var ms = new MemoryStream(chain)) diff --git a/src/Stratis.Bitcoin/Base/BaseFeature.cs b/src/Stratis.Bitcoin/Base/BaseFeature.cs index 31f50d4ab6..0248278ee6 100644 --- a/src/Stratis.Bitcoin/Base/BaseFeature.cs +++ b/src/Stratis.Bitcoin/Base/BaseFeature.cs @@ -259,7 +259,7 @@ public override async Task InitializeAsync() } if (this.chainIndexer.Tip.Height != initializedAt.Height) - this.chainIndexer.Initialize(initializedAt); + this.chainIndexer.SetTip(initializedAt); NetworkPeerConnectionParameters connectionParameters = this.connectionManager.Parameters; connectionParameters.IsRelay = this.connectionManager.ConnectionSettings.RelayTxes; @@ -330,7 +330,7 @@ private async Task StartChainAsync() this.logger.LogInformation("Loading chain."); ChainedHeader chainTip = await this.chainRepository.LoadAsync(this.chainIndexer.Genesis).ConfigureAwait(false); - this.chainIndexer.Initialize(chainTip); + this.chainIndexer.SetTip(chainTip); this.logger.LogInformation("Chain loaded at height {0}.", this.chainIndexer.Height); diff --git a/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs b/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs index a0dc11b24d..350254881e 100644 --- a/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs +++ b/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs @@ -260,9 +260,6 @@ public async Task InitializeAsync(ChainedHeader chainTip) this.SetConsensusTip(pendingTip); - if (this.chainIndexer.Tip != pendingTip) - this.chainIndexer.Initialize(pendingTip); - this.logger.LogInformation("Consensus Manager initialized with tip '{0}'.", pendingTip); this.blockPuller.Initialize(this.BlockDownloaded); @@ -744,7 +741,6 @@ private async Task> RewindToForkPointAsync(ChainedHeade lock (this.peerLock) { this.SetConsensusTipInternalLocked(current.Previous); - this.chainIndexer.Remove(current); } var disconnectedBlock = new ChainedHeaderBlock(block, current); @@ -796,7 +792,6 @@ private async Task ConnectChainAsync(List SetConsensusTip(ChainedHeader newTip, bool blockMined = false) private void SetConsensusTipInternalLocked(ChainedHeader newTip) { this.Tip = newTip; + this.chainIndexer.SetTip(newTip); this.chainState.ConsensusTip = this.Tip; } diff --git a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs index 24edccbef2..ff41ddae46 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs @@ -75,7 +75,7 @@ private FederationGatewayController CreateController(IFederatedPegSettings feder { var controller = new FederationGatewayController( Substitute.For(), - new ChainIndexer(), + new ChainIndexer(this.network), Substitute.For(), this.crossChainTransferStore, this.GetMaturedBlocksProvider(federatedPegSettings), @@ -209,7 +209,7 @@ public void Call_Sidechain_Gateway_Get_Info() var controller = new FederationGatewayController( Substitute.For(), - new ChainIndexer(), + new ChainIndexer(this.network), Substitute.For(), this.crossChainTransferStore, this.GetMaturedBlocksProvider(federatedPegSettings), @@ -302,7 +302,7 @@ public void Call_Mainchain_Gateway_Get_Info() var controller = new FederationGatewayController( Substitute.For(), - new ChainIndexer(), + new ChainIndexer(this.network), Substitute.For(), this.crossChainTransferStore, this.GetMaturedBlocksProvider(settings), diff --git a/src/Stratis.Features.SQLiteWalletRepository.Tests/WalletRepositoryTests.cs b/src/Stratis.Features.SQLiteWalletRepository.Tests/WalletRepositoryTests.cs index b1a8409c91..e9380e6060 100644 --- a/src/Stratis.Features.SQLiteWalletRepository.Tests/WalletRepositoryTests.cs +++ b/src/Stratis.Features.SQLiteWalletRepository.Tests/WalletRepositoryTests.cs @@ -125,7 +125,8 @@ public BlockBase(Network network, string dataDir, int blockLimit = int.MaxValue) } // Build the chain indexer from the chain. - this.ChainIndexer = new ChainIndexer(network, chainTip); + this.ChainIndexer = new ChainIndexer(network); + this.ChainIndexer.SetTip(chainTip); this.TicksReading = 0; } diff --git a/src/Stratis.SmartContracts.Core.Tests/ChainIndexerRangeQueryTests.cs b/src/Stratis.SmartContracts.Core.Tests/ChainIndexerRangeQueryTests.cs index 28c565c110..3b9f263b8f 100644 --- a/src/Stratis.SmartContracts.Core.Tests/ChainIndexerRangeQueryTests.cs +++ b/src/Stratis.SmartContracts.Core.Tests/ChainIndexerRangeQueryTests.cs @@ -30,7 +30,7 @@ public void Query_Range_Success(int chainLength, int start, int? end) ChainedHeader[] chain = this.CreateChain(chainIndexer.Genesis, chainLength); - chainIndexer.Initialize(chain.Last()); + chainIndexer.SetTip(chain.Last()); var query = new ChainIndexerRangeQuery(chainIndexer); @@ -60,7 +60,7 @@ public void Query_Range_During_Reorg_Success(int chainLength, int start, int? en // Create a new reorg that removes 3 blocks and adds another 5. ChainedHeader[] chainAfterReorg = this.CreateChain(chainBeforeReorg[chainLength - 3], 5); - chainIndexer.Initialize(chainBeforeReorg.Last()); + chainIndexer.SetTip(chainBeforeReorg.Last()); var query = new ChainIndexerRangeQuery(chainIndexer); @@ -104,7 +104,7 @@ public void Query_Range_From_Header_Null_Empty() ChainedHeader[] chain = this.CreateChain(chainIndexer.Genesis, 1); - chainIndexer.Initialize(chain.Last()); + chainIndexer.SetTip(chain.Last()); var query = new ChainIndexerRangeQuery(chainIndexer); @@ -121,7 +121,7 @@ public void Query_Range_To_Header_Null() ChainedHeader[] chain = this.CreateChain(chainIndexer.Genesis, 5); - chainIndexer.Initialize(chain.Last()); + chainIndexer.SetTip(chain.Last()); var query = new ChainIndexerRangeQuery(chainIndexer); From fcd89f5da540210c62d909187f505c8afbf8a73f Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 18:47:47 +1100 Subject: [PATCH 02/13] Update TestContext --- .../Consensus/ConsensusTestContextBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stratis.Bitcoin.Tests/Consensus/ConsensusTestContextBuilder.cs b/src/Stratis.Bitcoin.Tests/Consensus/ConsensusTestContextBuilder.cs index 2897339552..5785f06734 100644 --- a/src/Stratis.Bitcoin.Tests/Consensus/ConsensusTestContextBuilder.cs +++ b/src/Stratis.Bitcoin.Tests/Consensus/ConsensusTestContextBuilder.cs @@ -33,7 +33,7 @@ internal TestContext Build() { this.testContext.coinView.UpdateTipHash(new HashHeightPair(this.testContext.InitialChainTip)); this.testContext.ChainedHeaderTree.Initialize(this.testContext.InitialChainTip); - this.testContext.chainIndexer.Initialize(this.testContext.InitialChainTip); + this.testContext.chainIndexer.SetTip(this.testContext.InitialChainTip); this.testContext.ChainState.Setup(c => c.BlockStoreTip) .Returns(this.testContext.InitialChainTip); } From 0aa880380212c47e64ec774188c3127d4a2115e7 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 19:03:27 +1100 Subject: [PATCH 03/13] Restore parameterless constructor for mocking --- src/NBitcoin/ChainIndexer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 0e1bce1e9d..61474f50b8 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -32,12 +32,15 @@ public class ChainIndexer public ChainedHeader Genesis => this.GetHeader(0); - public ChainIndexer(Network network) + public ChainIndexer() { - this.Network = network; - this.blocksByHeight = new Dictionary(); this.blocksById = new Dictionary(); + } + + public ChainIndexer(Network network) : base() + { + this.Network = network; var tip = new ChainedHeader(this.Network.GetGenesis().Header, this.Network.GetGenesis().GetHash(), 0); this.AddInternal(tip); From 705107cad75200bea369c8bb1c057f0888f0d104 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 19:09:40 +1100 Subject: [PATCH 04/13] Fix base -> this --- src/NBitcoin/ChainIndexer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 61474f50b8..45f80f4cef 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -38,7 +38,7 @@ public ChainIndexer() this.blocksById = new Dictionary(); } - public ChainIndexer(Network network) : base() + public ChainIndexer(Network network) : this() { this.Network = network; From 141798b9b0605d44b4ccc108b61b67173f4420e2 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 22:11:34 +1100 Subject: [PATCH 05/13] Refactor ChainIndexer --- src/NBitcoin/ChainIndexer.cs | 55 +++++++++--------------------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 45f80f4cef..ff79d40c17 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -1,6 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Stratis.Bitcoin.Tests.Common")] +[assembly: InternalsVisibleTo("Stratis.SmartContracts.Core.Tests")] namespace NBitcoin { @@ -103,21 +107,6 @@ public ChainedHeader FindFork(BlockLocator locator) return this.FindFork(locator.Blocks); } - /// - /// Enumerate chain block headers after given block hash to genesis block. - /// - /// Block hash to enumerate after. - /// Enumeration of chained block headers after given block hash. - public IEnumerable EnumerateAfter(uint256 blockHash) - { - ChainedHeader block = this.GetHeader(blockHash); - - if (block == null) - return new ChainedHeader[0]; - - return this.EnumerateAfter(block); - } - /// /// Enumerates chain block headers from the given chained block header to tip. /// @@ -138,14 +127,11 @@ public IEnumerable EnumerateToTip(ChainedHeader block) /// Enumeration of chained block headers from the given block hash to tip. public IEnumerable EnumerateToTip(uint256 blockHash) { - ChainedHeader block = this.GetHeader(blockHash); + ChainedHeader block = this[blockHash]; if (block == null) - yield break; - - yield return block; + return new ChainedHeader[0]; - foreach (ChainedHeader chainedBlock in this.EnumerateAfter(blockHash)) - yield return chainedBlock; + return this.Tip.EnumerateToGenesis().TakeWhile(c => c.Height >= block.Height).Reverse(); } /// @@ -153,27 +139,15 @@ public IEnumerable EnumerateToTip(uint256 blockHash) /// /// The chained block header to enumerate after. /// Enumeration of chained block headers after the given block. - public virtual IEnumerable EnumerateAfter(ChainedHeader block) + internal virtual IEnumerable EnumerateAfter(ChainedHeader block) { - int i = block.Height + 1; - ChainedHeader prev = block; - - while (true) - { - ChainedHeader b = this.GetHeader(i); - if ((b == null) || (b.Previous != prev)) - yield break; + if (this[block.HashBlock] == null) + return new ChainedHeader[0]; - yield return b; - i++; - prev = b; - } + return this.Tip.EnumerateToGenesis().TakeWhile(c => c.Height > block.Height).Reverse(); } - /// - /// TODO: Make this internal when the component moves to Stratis.Bitcoin - /// - public void Add(ChainedHeader addTip) + internal void Add(ChainedHeader addTip) { lock (this.lockObject) { @@ -192,10 +166,7 @@ private void AddInternal(ChainedHeader addTip) this.blocksByHeight.Add(addTip.Height, addTip); } - /// - /// TODO: Make this internal when the component moves to Stratis.Bitcoin - /// - public void Remove(ChainedHeader removeTip) + internal void Remove(ChainedHeader removeTip) { lock (this.lockObject) { From fcccf45999c5040e9d0c5644c2a0f0834580f29f Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 22:27:07 +1100 Subject: [PATCH 06/13] Upate visibility --- src/NBitcoin/ChainIndexer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index ff79d40c17..be1deda1f0 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -5,6 +5,7 @@ [assembly: InternalsVisibleTo("Stratis.Bitcoin.Tests.Common")] [assembly: InternalsVisibleTo("Stratis.SmartContracts.Core.Tests")] +[assembly: InternalsVisibleTo("Stratis.Bitcoin.Features.PoA.Tests")] namespace NBitcoin { From cce6ba2a8c3bc69a75981e467261cebd6f165377 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Sun, 6 Mar 2022 23:53:36 +1100 Subject: [PATCH 07/13] Refactor EnumerateAfter --- src/NBitcoin/ChainIndexer.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index be1deda1f0..b03d289dda 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -132,7 +132,7 @@ public IEnumerable EnumerateToTip(uint256 blockHash) if (block == null) return new ChainedHeader[0]; - return this.Tip.EnumerateToGenesis().TakeWhile(c => c.Height >= block.Height).Reverse(); + return EnumerateAfter(block).Prepend(block); } /// @@ -140,12 +140,18 @@ public IEnumerable EnumerateToTip(uint256 blockHash) /// /// The chained block header to enumerate after. /// Enumeration of chained block headers after the given block. - internal virtual IEnumerable EnumerateAfter(ChainedHeader block) + public virtual IEnumerable EnumerateAfter(ChainedHeader block) { - if (this[block.HashBlock] == null) - return new ChainedHeader[0]; + for (int i = block.Height + 1; i < this.Tip.Height; i++) + { + ChainedHeader nextBlock = this.blocksByHeight[i]; + if (nextBlock.Previous != block) + yield break; - return this.Tip.EnumerateToGenesis().TakeWhile(c => c.Height > block.Height).Reverse(); + block = nextBlock; + + yield return block; + } } internal void Add(ChainedHeader addTip) From 68c512a136833cebd7d01466d8895d5b0f4f85d8 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Mon, 7 Mar 2022 00:01:32 +1100 Subject: [PATCH 08/13] Include tip in enumeration --- src/NBitcoin/ChainIndexer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index b03d289dda..94f968f4e4 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -142,7 +142,7 @@ public IEnumerable EnumerateToTip(uint256 blockHash) /// Enumeration of chained block headers after the given block. public virtual IEnumerable EnumerateAfter(ChainedHeader block) { - for (int i = block.Height + 1; i < this.Tip.Height; i++) + for (int i = block.Height + 1; i <= this.Tip.Height; i++) { ChainedHeader nextBlock = this.blocksByHeight[i]; if (nextBlock.Previous != block) From 304b29b017c18b1f1c89e47f853c19b340bb1446 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Mon, 7 Mar 2022 02:06:19 +1100 Subject: [PATCH 09/13] Remove unused code --- src/NBitcoin.Tests/ChainTests.cs | 14 -------------- src/NBitcoin/ChainIndexer.cs | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/NBitcoin.Tests/ChainTests.cs b/src/NBitcoin.Tests/ChainTests.cs index b9c3ea93c7..90f9ddd238 100644 --- a/src/NBitcoin.Tests/ChainTests.cs +++ b/src/NBitcoin.Tests/ChainTests.cs @@ -375,19 +375,5 @@ private ChainedHeader AppendBlock(params ChainIndexer[] chainsIndexer) ChainedHeader index = null; return this.AppendBlock(index, chainsIndexer); } - - /// - /// Returns the first common chained block header between two chains. - /// - /// The source chain. - /// The other chain. - /// First common chained block header or null. - private ChainedHeader FindFork(ChainIndexer chainSrc, ChainIndexer otherChain) - { - if (otherChain == null) - throw new ArgumentNullException("otherChain"); - - return chainSrc.FindFork(otherChain.Tip.EnumerateToGenesis().Select(o => o.HashBlock)); - } } } \ No newline at end of file diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 94f968f4e4..65c04f6a02 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -79,7 +79,7 @@ public ChainedHeader SetTip(ChainedHeader chainedHeader) /// /// Hash to search for. /// First found chained block header or null if not found. - public ChainedHeader FindFork(IEnumerable hashes) + internal ChainedHeader FindFork(IEnumerable hashes) { if (hashes == null) throw new ArgumentNullException("hashes"); @@ -140,7 +140,7 @@ public IEnumerable EnumerateToTip(uint256 blockHash) /// /// The chained block header to enumerate after. /// Enumeration of chained block headers after the given block. - public virtual IEnumerable EnumerateAfter(ChainedHeader block) + internal virtual IEnumerable EnumerateAfter(ChainedHeader block) { for (int i = block.Height + 1; i <= this.Tip.Height; i++) { From 1a2f35ba3e3f81beabe0fd415cc9eb7614a73875 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Mon, 7 Mar 2022 11:05:32 +1100 Subject: [PATCH 10/13] Add comment and more locks --- src/NBitcoin/ChainIndexer.cs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 65c04f6a02..dcbdadb074 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -48,7 +48,7 @@ public ChainIndexer(Network network) : this() this.Network = network; var tip = new ChainedHeader(this.Network.GetGenesis().Header, this.Network.GetGenesis().GetHash(), 0); - this.AddInternal(tip); + AddInternal(tip); this.Tip = tip; } @@ -84,15 +84,18 @@ internal ChainedHeader FindFork(IEnumerable hashes) if (hashes == null) throw new ArgumentNullException("hashes"); - // Find the first block the caller has in the main chain. - foreach (uint256 hash in hashes) + lock (this.lockObject) { - ChainedHeader chainedHeader = this.GetHeader(hash); - if (chainedHeader != null) - return chainedHeader; + // Find the first block the caller has in the main chain. + foreach (uint256 hash in hashes) + { + ChainedHeader chainedHeader = this.GetHeader(hash); + if (chainedHeader != null) + return chainedHeader; + } + + return null; } - - return null; } /// @@ -140,15 +143,20 @@ public IEnumerable EnumerateToTip(uint256 blockHash) /// /// The chained block header to enumerate after. /// Enumeration of chained block headers after the given block. + /// The chain could re-org in which case the enumeration may exit early when encountering a block from a different chain. internal virtual IEnumerable EnumerateAfter(ChainedHeader block) { for (int i = block.Height + 1; i <= this.Tip.Height; i++) { - ChainedHeader nextBlock = this.blocksByHeight[i]; - if (nextBlock.Previous != block) - yield break; + lock (this.lockObject) + { + ChainedHeader nextBlock = this.blocksByHeight[i]; + + if (nextBlock.Previous != block) + yield break; - block = nextBlock; + block = nextBlock; + } yield return block; } From 4c1eee89f5fc1842c6b81d7e79e0feb2bd63cbf3 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Mon, 7 Mar 2022 11:07:54 +1100 Subject: [PATCH 11/13] Add comments --- src/NBitcoin/ChainIndexer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index dcbdadb074..f22844033b 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -116,6 +116,7 @@ public ChainedHeader FindFork(BlockLocator locator) /// /// Chained block header to enumerate from. /// Enumeration of chained block headers from given chained block header to tip. + /// The chain could re-org in which case the enumeration may exit early when encountering a block from a different chain. public IEnumerable EnumerateToTip(ChainedHeader block) { if (block == null) @@ -129,6 +130,7 @@ public IEnumerable EnumerateToTip(ChainedHeader block) /// /// Block hash to enumerate from. /// Enumeration of chained block headers from the given block hash to tip. + /// The chain could re-org in which case the enumeration may exit early when encountering a block from a different chain. public IEnumerable EnumerateToTip(uint256 blockHash) { ChainedHeader block = this[blockHash]; From a176d72a98a2121eceeb5445e6c9c2a1a2b8f742 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Mon, 7 Mar 2022 11:11:02 +1100 Subject: [PATCH 12/13] Remove this from private call --- src/NBitcoin/ChainIndexer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index f22844033b..77852e9a21 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -171,7 +171,7 @@ internal void Add(ChainedHeader addTip) if (this.Tip.HashBlock != addTip.Previous.HashBlock) throw new InvalidOperationException("New tip must be consecutive"); - this.AddInternal(addTip); + AddInternal(addTip); this.Tip = addTip; } From 68c2a8535748b1db5fb6c9de030826b185d2c536 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 31 May 2022 21:50:18 +1000 Subject: [PATCH 13/13] Fix test --- src/NBitcoin/ChainIndexer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NBitcoin/ChainIndexer.cs b/src/NBitcoin/ChainIndexer.cs index 77852e9a21..d9e3f1b3f7 100644 --- a/src/NBitcoin/ChainIndexer.cs +++ b/src/NBitcoin/ChainIndexer.cs @@ -79,7 +79,7 @@ public ChainedHeader SetTip(ChainedHeader chainedHeader) /// /// Hash to search for. /// First found chained block header or null if not found. - internal ChainedHeader FindFork(IEnumerable hashes) + public ChainedHeader FindFork(IEnumerable hashes) { if (hashes == null) throw new ArgumentNullException("hashes");