diff --git a/src/NBitcoin.Tests/ChainTests.cs b/src/NBitcoin.Tests/ChainTests.cs
index 7fe5524395..cfab9ffb02 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);
@@ -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 91839a76ee..d9e3f1b3f7 100644
--- a/src/NBitcoin/ChainIndexer.cs
+++ b/src/NBitcoin/ChainIndexer.cs
@@ -1,5 +1,11 @@
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")]
+[assembly: InternalsVisibleTo("Stratis.Bitcoin.Features.PoA.Tests")]
namespace NBitcoin
{
@@ -28,6 +34,7 @@ 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()
@@ -37,46 +44,36 @@ public ChainIndexer()
}
public ChainIndexer(Network network) : this()
- {
- this.Network = network;
-
- this.Initialize(new ChainedHeader(network.GetGenesis().Header, network.GetGenesis().GetHash(), 0));
- }
-
- public ChainIndexer(Network network, ChainedHeader chainedHeader) : this()
{
this.Network = network;
- this.Initialize(chainedHeader);
+ var tip = new ChainedHeader(this.Network.GetGenesis().Header, this.Network.GetGenesis().GetHash(), 0);
+ AddInternal(tip);
+
+ 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.
///
@@ -87,15 +84,18 @@ public 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;
+ }
}
///
@@ -111,26 +111,12 @@ 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.
///
/// 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)
@@ -144,16 +130,14 @@ 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.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 EnumerateAfter(block).Prepend(block);
}
///
@@ -161,57 +145,63 @@ 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)
+ /// 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)
{
- int i = block.Height + 1;
- ChainedHeader prev = block;
-
- while (true)
+ for (int i = block.Height + 1; i <= this.Tip.Height; i++)
{
- ChainedHeader b = this.GetHeader(i);
- if ((b == null) || (b.Previous != prev))
- yield break;
+ lock (this.lockObject)
+ {
+ ChainedHeader nextBlock = this.blocksByHeight[i];
+
+ if (nextBlock.Previous != block)
+ yield break;
+
+ block = nextBlock;
+ }
- yield return b;
- i++;
- prev = b;
+ yield return block;
}
}
- ///
- /// TODO: Make this internal when the component moves to Stratis.Bitcoin
- ///
- public void Add(ChainedHeader addTip)
+ internal 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);
+ AddInternal(addTip);
this.Tip = addTip;
}
}
- ///
- /// TODO: Make this internal when the component moves to Stratis.Bitcoin
- ///
- public void Remove(ChainedHeader removeTip)
+ private void AddInternal(ChainedHeader addTip)
+ {
+ this.blocksById.Add(addTip.HashBlock, addTip);
+ this.blocksByHeight.Add(addTip.Height, addTip);
+ }
+
+ internal void Remove(ChainedHeader removeTip)
{
lock (this.lockObject)
{
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 60d1ac6285..a6f9a6064e 100644
--- a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs
+++ b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs
@@ -427,7 +427,7 @@ private ChainedHeader RecoverStoreTip()
ChainedHeader newTip = this.chainIndexer[firstNotFound - 1];
// Set chain store to be same as the store tip.
- this.chainIndexer.Initialize(newTip);
+ this.chainIndexer.SetTip(newTip);
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.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);
}
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 744ab85356..e18b40e342 100644
--- a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs
+++ b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs
@@ -78,7 +78,7 @@ private FederationGatewayController CreateController(IFederatedPegSettings feder
var controller = new FederationGatewayController(
Substitute.For(),
- new ChainIndexer(),
+ new ChainIndexer(this.network),
Substitute.For(),
this.crossChainTransferStore,
this.GetMaturedBlocksProvider(retrievalTypeConfirmations),
@@ -214,7 +214,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(retrievalTypeConfirmations),
@@ -308,7 +308,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(retrievalTypeConfirmations),
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);