Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TODO] Remove ChainState: (2) Move ChainState's BlockStoreTip to BSQ #920

Open
wants to merge 4 commits into
base: release/1.6.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/Stratis.Bitcoin.Features.Api/NodeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ public NodeController(
public IActionResult Status([FromQuery] bool publish)
{
// Output has been merged with RPC's GetInfo() since they provided similar functionality.
this.consensusManager.IsAtBestChainTip(out ChainedHeader bestPeerTip);

var model = new StatusModel
{
Version = this.fullNode.Version?.ToString() ?? "0",
Expand All @@ -157,7 +159,7 @@ public IActionResult Status([FromQuery] bool publish)
RunningTime = this.dateTimeProvider.GetUtcNow() - this.fullNode.StartTime,
CoinTicker = this.network.CoinTicker,
State = this.fullNode.State.ToString(),
BestPeerHeight = this.chainState.BestPeerTip?.Height,
BestPeerHeight = bestPeerTip?.Height,
InIbd = this.initialBlockDownloadState.IsInitialBlockDownload()
};

Expand Down Expand Up @@ -185,8 +187,8 @@ public IActionResult Status([FromQuery] bool publish)
}

// Include BlockStore Height if enabled
if (this.chainState.BlockStoreTip != null)
model.BlockStoreHeight = this.chainState.BlockStoreTip.Height;
if (this.blockStore is IBlockStoreQueue blockStoreQueue && blockStoreQueue.StoreTip != null)
model.BlockStoreHeight = blockStoreQueue.StoreTip.Height;

// Add the details of connected nodes.
foreach (INetworkPeer peer in this.connectionManager.ConnectedPeers)
Expand Down
59 changes: 37 additions & 22 deletions src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq;
Expand All @@ -9,6 +10,7 @@
using Stratis.Bitcoin.AsyncWork;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.Networks;
using Stratis.Bitcoin.Primitives;
Expand Down Expand Up @@ -99,14 +101,15 @@ public BlockStoreTests()
this.chainState = new ChainState();
this.initialBlockDownloadState = new Mock<IInitialBlockDownloadState>();

var blockStoreFlushCondition = new BlockStoreQueueFlushCondition(this.chainState, this.initialBlockDownloadState.Object);
var blockStoreFlushCondition = new BlockStoreQueueFlushCondition(this.initialBlockDownloadState.Object);

this.loggerFactory = new LoggerFactory();
this.signals = new Signals.Signals(this.loggerFactory, null);
this.asyncProvider = new AsyncProvider(this.loggerFactory, this.signals);

this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushCondition, this.storeSettings,
this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider, new Mock<IInitialBlockDownloadState>().Object);
this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider,
new Mock<IInitialBlockDownloadState>().Object);
}

private ChainIndexer CreateChain(int blocksCount)
Expand Down Expand Up @@ -158,7 +161,7 @@ public void BlockStoreInitializesTipAtHashOfLastSavedBlock()
this.repositoryTipHashAndHeight = new HashHeightPair(initializationHeader);

this.blockStoreQueue.Initialize();
Assert.Equal(initializationHeader, this.chainState.BlockStoreTip);
Assert.Equal(initializationHeader, this.blockStoreQueue.StoreTip);
}

[Fact]
Expand All @@ -168,26 +171,29 @@ public void BlockStoreRecoversToLastCommonBlockOnInitialization()

this.blockStoreQueue.Initialize();

Assert.Equal(this.chainIndexer.Genesis, this.chainState.BlockStoreTip);
Assert.Equal(this.chainIndexer.Genesis, this.blockStoreQueue.StoreTip);
}

[Fact]
public async Task BatchIsSavedAfterSizeThresholdReachedAsync()
{
Block block = Block.Load(Encoders.Hex.DecodeData(this.testBlockHex), this.network.Consensus.ConsensusFactory);
int blockSize = block.GetSerializedSize();
this.chainState.ConsensusTip = null;

int count = 5 * 1024 * 1024 / blockSize + 2;

ChainIndexer longChainIndexer = this.CreateChain(count);
this.repositoryTipHashAndHeight = new HashHeightPair(longChainIndexer.Genesis.HashBlock, 0);

var blockStoreFlushCondition = new BlockStoreQueueFlushCondition(this.chainState, this.initialBlockDownloadState.Object);
var blockStoreFlushCondition = new BlockStoreQueueFlushCondition(this.initialBlockDownloadState.Object);

this.blockStoreQueue = new BlockStoreQueue(longChainIndexer, this.chainState, blockStoreFlushCondition, this.storeSettings,
this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider, new Mock<IInitialBlockDownloadState>().Object);

// Simulate construction of ConsensusManager.
var consensusManager = new Mock<IConsensusManager>();
this.blockStoreQueue.SetConsensusManager(consensusManager.Object);

this.blockStoreQueue.Initialize();
this.chainState.ConsensusTip = longChainIndexer.Tip;

Expand All @@ -200,17 +206,26 @@ public async Task BatchIsSavedAfterSizeThresholdReachedAsync()
}

await WaitUntilQueueIsEmptyAsync().ConfigureAwait(false);
Assert.Equal(longChainIndexer.GetHeader(count - 1), this.chainState.BlockStoreTip);
Assert.Equal(longChainIndexer.GetHeader(count - 1), this.blockStoreQueue.StoreTip);
Assert.True(this.repositorySavesCount > 0);
}

private ChainedHeader blockStoreTip
{
get
{
FieldInfo blockStoreTipField = this.blockStoreQueue.GetType().GetField("blockStoreTip", BindingFlags.NonPublic | BindingFlags.Instance);
return (ChainedHeader)blockStoreTipField.GetValue(this.blockStoreQueue);
}
}

[Fact]
public async Task BatchIsSavedOnShutdownAsync()
{
this.repositoryTipHashAndHeight = new HashHeightPair(this.chainIndexer.Genesis.HashBlock, 0);

var blockStoreFlushConditionMock = new Mock<IBlockStoreQueueFlushCondition>();
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush).Returns(false);
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(false);
this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushConditionMock.Object, this.storeSettings, this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider, new Mock<IInitialBlockDownloadState>().Object);

this.blockStoreQueue.Initialize();
Expand All @@ -228,23 +243,23 @@ public async Task BatchIsSavedOnShutdownAsync()

await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false);

Assert.Equal(this.chainState.BlockStoreTip, this.chainIndexer.Genesis);
Assert.Equal(this.chainIndexer.Genesis, this.blockStoreTip);
Assert.Equal(0, this.repositorySavesCount);

this.nodeLifetime.StopApplication();
this.blockStoreQueue.Dispose();

Assert.Equal(this.chainState.BlockStoreTip, lastHeader);
Assert.Equal(lastHeader, this.blockStoreTip);
Assert.Equal(1, this.repositorySavesCount);
}

[Fact]
public async Task BatchIsSavedWhenAtConsensusTipAsync()
{
this.repositoryTipHashAndHeight = new HashHeightPair(this.chainIndexer.Genesis.HashBlock, 0);

var blockStoreFlushConditionMock = new Mock<IBlockStoreQueueFlushCondition>();
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush).Returns(false);
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(false);
this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushConditionMock.Object, this.storeSettings,
this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider, new Mock<IInitialBlockDownloadState>().Object);

Expand All @@ -260,7 +275,7 @@ public async Task BatchIsSavedWhenAtConsensusTipAsync()
if (i == this.chainIndexer.Height)
{
await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false);
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush).Returns(true);
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(true);
}

this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, lastHeader));
Expand All @@ -270,14 +285,14 @@ public async Task BatchIsSavedWhenAtConsensusTipAsync()

// Wait for store tip to finish saving
int counter = 0;
if (this.chainState.BlockStoreTip != this.chainIndexer.Tip)
if (this.blockStoreQueue.StoreTip != this.chainIndexer.Tip)
{
Assert.True(counter < 10);
counter++;
await Task.Delay(500);
}

Assert.Equal(this.chainState.BlockStoreTip, this.chainIndexer.Tip);
Assert.Equal(this.blockStoreQueue.StoreTip, this.chainIndexer.Tip);
Assert.Equal(1, this.repositorySavesCount);
}

Expand All @@ -287,7 +302,7 @@ public async Task ReorgedBlocksAreNotSavedAsync()
this.repositoryTipHashAndHeight = new HashHeightPair(this.chainIndexer.Genesis.HashBlock, 0);

var blockStoreFlushConditionMock = new Mock<IBlockStoreQueueFlushCondition>();
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush).Returns(false);
blockStoreFlushConditionMock.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(false);
this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushConditionMock.Object, this.storeSettings,
this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider, new Mock<IInitialBlockDownloadState>().Object);

Expand Down Expand Up @@ -317,15 +332,15 @@ public async Task ReorgedBlocksAreNotSavedAsync()

await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false);

Assert.Equal(this.chainState.BlockStoreTip, this.chainIndexer.Genesis);
Assert.Equal(this.chainIndexer.Genesis, this.blockStoreTip);
Assert.Equal(0, this.repositorySavesCount);

// Dispose block store to trigger save.
this.nodeLifetime.StopApplication();
this.blockStoreQueue.Dispose();

// Make sure that blocks only from 2nd chain were saved.
Assert.Equal(this.chainIndexer.GetHeader(realChainLenght - 1), this.chainState.BlockStoreTip);
Assert.Equal(this.chainIndexer.GetHeader(realChainLenght - 1), this.blockStoreTip);
Assert.Equal(1, this.repositorySavesCount);
Assert.Equal(realChainLenght - 1, this.repositoryTotalBlocksSaved);
}
Expand All @@ -342,7 +357,7 @@ public async Task ReorgedBlocksAreDeletedFromRepositoryIfReorgDetectedAsync()
this.repositoryTipHashAndHeight = new HashHeightPair(this.chainIndexer.Genesis.HashBlock, 0);

var blockStoreFlushCondition = new Mock<IBlockStoreQueueFlushCondition>();
blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(false);
blockStoreFlushCondition.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(false);

this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushCondition.Object, this.storeSettings,
this.blockRepositoryMock.Object, this.loggerFactory, new Mock<INodeStats>().Object, this.asyncProvider, new Mock<IInitialBlockDownloadState>().Object);
Expand Down Expand Up @@ -386,14 +401,14 @@ public async Task ReorgedBlocksAreDeletedFromRepositoryIfReorgDetectedAsync()
block.GetSerializedSize();

if (header == alternativeBlocks.Last())
blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(true);
blockStoreFlushCondition.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(true);

this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, header));
}

await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false);

blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(false);
blockStoreFlushCondition.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(false);

// Make sure only longest chain is saved.
Assert.Equal(this.chainIndexer.Tip.Height, this.repositoryTotalBlocksSaved);
Expand All @@ -408,7 +423,7 @@ public async Task ReorgedBlocksAreDeletedFromRepositoryIfReorgDetectedAsync()
block.GetSerializedSize();

if (i == this.chainIndexer.Height)
blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(true);
blockStoreFlushCondition.Setup(s => s.ShouldFlush(It.IsAny<IConsensusManager>(), It.IsAny<IBlockStoreQueue>())).Returns(true);

this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, this.chainIndexer.GetHeader(i)));
}
Expand Down
Loading