Commit 5da6b37b authored by Sergei Zubov's avatar Sergei Zubov

Fix tests

parent 0e803d52
using System;
using System.IO;
using System.Linq;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore.LoopSteps;
using Stratis.Bitcoin.Utilities;
using Xunit;
using static Stratis.Bitcoin.BlockPulling.BlockPuller;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
public sealed class BlockStoreLoopIntegration : BlockStoreLoopStepBaseTest
{
/// <summary>
/// Tests the block store loop step with a concrete implementation of BlockRepository.
/// </summary>
[Fact]
public void CheckNextChainedBlockExists_WithNextChainedBlock_Exists_SetStoreTipAndBlockHash()
{
var blocks = this.CreateBlocks(5);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
fluent.WithConcreteRepository();
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks).GetAwaiter().GetResult();
// The chain has 4 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(3));
// Create the last chained block without appending to the chain
var block03 = chain.GetBlock(blocks[3].GetHash());
var block04 = new ChainedHeader(blocks[4].Header, blocks[4].Header.GetHash(), block03);
fluent.Create(chain);
Assert.Null(fluent.Loop.StoreTip);
var nextChainedBlock = block04;
var checkExistsStep = new CheckNextChainedBlockExistStep(fluent.Loop, this.LoggerFactory.Object);
checkExistsStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(fluent.Loop.StoreTip.Header.GetHash(), block04.Header.GetHash());
Assert.Equal(fluent.Loop.BlockRepository.BlockHash, block04.Header.GetHash());
}
}
[Fact]
public void ReorganiseBlockRepository_WithBlockRepositoryAndChainOutofSync_ReorganiseBlocks()
{
var blocks = this.CreateBlocks(15);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
fluent.WithConcreteRepository();
// Push 15 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks).GetAwaiter().GetResult();
// The chain has 10 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(9));
// Create the last 5 chained block headers without appending to the chain
var block9 = chain.GetBlock(blocks[9].Header.GetHash());
var block10 = new ChainedHeader(blocks[10].Header, blocks[10].Header.GetHash(), block9);
var block11 = new ChainedHeader(blocks[11].Header, blocks[11].Header.GetHash(), block10);
var block12 = new ChainedHeader(blocks[12].Header, blocks[12].Header.GetHash(), block11);
var block13 = new ChainedHeader(blocks[13].Header, blocks[13].Header.GetHash(), block12);
var block14 = new ChainedHeader(blocks[14].Header, blocks[14].Header.GetHash(), block13);
fluent.Create(chain);
fluent.Loop.SetStoreTip(block14);
Assert.Equal(fluent.Loop.StoreTip.Header.GetHash(), block14.Header.GetHash());
Assert.Equal(fluent.Loop.BlockRepository.BlockHash, block14.Header.GetHash());
var nextChainedBlock = block10;
var reorganiseStep = new ReorganiseBlockRepositoryStep(fluent.Loop, this.LoggerFactory.Object);
reorganiseStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(fluent.Loop.StoreTip.Header.GetHash(), block10.Previous.Header.GetHash());
Assert.Equal(fluent.Loop.BlockRepository.BlockHash, block10.Previous.Header.GetHash());
}
}
[Fact]
public void ProcessPendingStorage_PushToRepo_BeforeDownloadingNewBlocks()
{
var blocks = this.CreateBlocks(15);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)).AsIBD())
{
fluent.WithConcreteRepository();
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 15 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(14));
// Create block store loop
fluent.Create(chain);
//Set the store's tip
fluent.Loop.SetStoreTip(fluent.Loop.Chain.GetBlock(blocks.Take(5).Last().GetHash()));
// Add chained blocks 5 - 14 to PendingStorage
for (int i = 5; i <= 14; i++)
{
this.AddBlockToPendingStorage(fluent.Loop, blocks[i]);
}
//Start processing pending blocks from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var processPendingStorageStep = new ProcessPendingStorageStep(fluent.Loop, this.LoggerFactory.Object);
processPendingStorageStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(blocks[14].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[14].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
[Fact]
public void DownloadBlockStep_WithNewBlocksToDownload_DownloadBlocksAndPushToRepo()
{
var blocks = this.CreateBlocks(10);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
fluent.WithConcreteRepository();
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 10 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(9));
// Create block store loop
fluent.Create(chain);
// Push blocks 5 - 9 to the downloaded blocks collection
for (int i = 5; i <= 9; i++)
{
fluent.Loop.BlockPuller.InjectBlock(blocks[i].GetHash(), new DownloadedBlock { Length = blocks[i].GetSerializedSize(), Block = blocks[i] }, new CancellationToken());
}
// Start processing blocks to download from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var step = new DownloadBlockStep(fluent.Loop, this.LoggerFactory.Object, DateTimeProvider.Default);
step.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(blocks[9].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[9].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
}
}
using System;
using System.IO;
using Microsoft.Extensions.Logging;
using Moq;
using NBitcoin;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.BlockPulling;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Connection;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.P2P.Peer;
using Stratis.Bitcoin.Tests.Common;
using Stratis.Bitcoin.Tests.Common.Logging;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
/// <summary>
/// Base test class for all the BlockStoreLoop tests.
/// </summary>
public class BlockStoreLoopStepBaseTest : LogsTestBase
{
internal void AddBlockToPendingStorage(BlockStoreLoop blockStoreLoop, Block block)
{
var chainedBlock = blockStoreLoop.Chain.GetBlock(block.GetHash());
blockStoreLoop.PendingStorage.TryAdd(block.GetHash(), new BlockPair(block, chainedBlock));
}
}
internal class FluentBlockStoreLoop : IDisposable
{
private IAsyncLoopFactory asyncLoopFactory;
private StoreBlockPuller blockPuller;
internal IBlockRepository BlockRepository { get; private set; }
private Mock<ChainState> chainState;
private Mock<IConnectionManager> connectionManager;
private DataFolder dataFolder;
private Mock<INodeLifetime> nodeLifeTime;
private Mock<ILoggerFactory> loggerFactory;
private IInitialBlockDownloadState initialBlockDownloadState;
public BlockStoreLoop Loop { get; private set; }
internal FluentBlockStoreLoop(DataFolder dataFolder)
{
this.ConfigureLogger();
this.dataFolder = dataFolder;
this.BlockRepository = new BlockRepositoryInMemory();
this.ConfigureConnectionManager();
var fullNode = new Mock<FullNode>().Object;
fullNode.DateTimeProvider = new DateTimeProvider();
var mock = new Mock<IInitialBlockDownloadState>();
mock.Setup(x => x.IsInitialBlockDownload()).Returns(false);
this.initialBlockDownloadState = mock.Object;
this.chainState = new Mock<ChainState>(new InvalidBlockHashStore(fullNode.DateTimeProvider));
this.nodeLifeTime = new Mock<INodeLifetime>();
}
internal FluentBlockStoreLoop AsIBD()
{
var mock = new Mock<IInitialBlockDownloadState>();
mock.Setup(x => x.IsInitialBlockDownload()).Returns(true);
this.initialBlockDownloadState = mock.Object;
return this;
}
internal FluentBlockStoreLoop WithConcreteLoopFactory()
{
this.asyncLoopFactory = new AsyncLoopFactory(this.loggerFactory.Object);
return this;
}
internal FluentBlockStoreLoop WithConcreteRepository()
{
this.BlockRepository = new BlockRepository(Network.Main, this.dataFolder, DateTimeProvider.Default, this.loggerFactory.Object);
return this;
}
private void ConfigureLogger()
{
this.loggerFactory = new Mock<ILoggerFactory>();
this.loggerFactory.Setup(l => l.CreateLogger(It.IsAny<string>())).Returns(new Mock<ILogger>().Object);
}
private void ConfigureConnectionManager()
{
this.connectionManager = new Mock<IConnectionManager>();
this.connectionManager.Setup(c => c.ConnectedPeers).Returns(new NetworkPeerCollection());
this.connectionManager.Setup(c => c.NodeSettings).Returns(new NodeSettings(args:new string[] { $"-datadir={this.dataFolder.RootPath}" }));
this.connectionManager.Setup(c => c.Parameters).Returns(new NetworkPeerConnectionParameters());
}
internal void Create(ConcurrentChain chain)
{
this.blockPuller = new StoreBlockPuller(chain, this.connectionManager.Object, this.loggerFactory.Object);
if (this.asyncLoopFactory == null)
this.asyncLoopFactory = new Mock<IAsyncLoopFactory>().Object;
this.Loop = new BlockStoreLoop(
this.asyncLoopFactory,
this.blockPuller,
this.BlockRepository,
null,
chain,
this.chainState.Object,
new StoreSettings(new NodeSettings(args:new string[] { $"-datadir={this.dataFolder.RootPath}" })),
this.nodeLifeTime.Object,
this.loggerFactory.Object,
this.initialBlockDownloadState,
DateTimeProvider.Default);
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
this.BlockRepository.Dispose();
this.disposedValue = true;
}
}
public void Dispose()
{
this.Dispose(true);
}
#endregion IDisposable Support
}
}
using System.Linq;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore.LoopSteps;
using Xunit;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
public sealed class BlockStoreLoopStepCheckNextChainedBlockExistStepTest : BlockStoreLoopStepBaseTest
{
[Fact]
public void CheckNextChainedBlockExists_WithNextChainedBlock_Exists_SetStoreTipAndBlockHash_InMemory()
{
var blocks = this.CreateBlocks(5);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks).GetAwaiter().GetResult();
// The chain has 4 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(3));
// Create the last chained block header without appending to the chain
var block03 = chain.GetBlock(blocks[3].GetHash());
var block04 = new ChainedHeader(blocks[4].Header, blocks[4].Header.GetHash(), block03);
fluent.Create(chain);
Assert.Null(fluent.Loop.StoreTip);
var nextChainedBlock = block04;
var checkExistsStep = new CheckNextChainedBlockExistStep(fluent.Loop, this.LoggerFactory.Object);
checkExistsStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(fluent.Loop.StoreTip.Header.GetHash(), block04.Header.GetHash());
Assert.Equal(fluent.Loop.BlockRepository.BlockHash, block04.Header.GetHash());
}
}
}
}
using System.Linq;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore.LoopSteps;
using Stratis.Bitcoin.Utilities;
using Xunit;
using static Stratis.Bitcoin.BlockPulling.BlockPuller;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
public sealed class BlockStoreLoopStepFindBlocksTaskTest : BlockStoreLoopStepBaseTest
{
[Fact]
public void BlockStoreInnerStepFindBlocks_WithBlocksFound_AddToDownloadStack()
{
var blocks = this.CreateBlocks(10);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 10 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(9));
// Create block store loop
fluent.Create(chain);
// Push blocks[5] - [9] to the downloaded blocks collection
for (int i = 5; i <= 9; i++)
{
fluent.Loop.BlockPuller.InjectBlock(blocks[i].GetHash(), new DownloadedBlock { Length = blocks[i].GetSerializedSize(), Block = blocks[i] }, new CancellationToken());
}
// Start finding blocks from block[5]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
var task = new BlockStoreInnerStepFindBlocks(this.LoggerFactory.Object);
task.ExecuteAsync(context).GetAwaiter().GetResult();
// Block[5] through Block[9] should be in the DownloadStack
Assert.Equal(5, context.DownloadStack.Count());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[5].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[6].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[7].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[8].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[9].GetHash());
}
}
[Fact]
public void BlockStoreInnerStepFindBlocks_CanRemoveTaskFromRoutine_DownloadStackSizeReached()
{
var blocks = this.CreateBlocks(55);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 45 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(45).Last().GetHash(), blocks.Take(45).ToList()).GetAwaiter().GetResult();
// The chain has 55 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(54).ToList());
// Create block store loop
fluent.Create(chain);
// Start finding blocks from Block[45]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[45].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
var task = new BlockStoreInnerStepFindBlocks(this.LoggerFactory.Object);
task.ExecuteAsync(context).GetAwaiter().GetResult();
// Block[45] through Block[50] should be in the DownloadStack
Assert.Equal(10, context.DownloadStack.Count());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[45].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[46].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[47].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[48].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[49].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[50].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[51].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[52].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[53].GetHash());
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == blocks[54].GetHash());
// The FindBlocks() task should be removed from the routine
// as the batch download size is reached
Assert.Single(context.InnerSteps);
Assert.False(context.InnerSteps.OfType<BlockStoreInnerStepFindBlocks>().Any());
}
}
[Fact]
public void BlockStoreInnerStepFindBlocks_CanRemoveTaskFromRoutine_BlockExistsInPendingStorage()
{
var blocks = this.CreateBlocks(3);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 2 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(1).Last().GetHash(), blocks.Take(1).ToList()).GetAwaiter().GetResult();
// The chain has 3 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(2));
// Create block store loop
fluent.Create(chain);
// Add block[2] to Pending Storage
fluent.Loop.PendingStorage.TryAdd(blocks[2].GetHash(), new BlockPair(blocks[2], fluent.Loop.Chain.GetBlock(blocks[2].GetHash())));
// Start finding blocks from Block[1]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[1].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
var task = new BlockStoreInnerStepFindBlocks(this.LoggerFactory.Object);
task.ExecuteAsync(context).GetAwaiter().GetResult();
// DownloadStack should only contain Block[1]
Assert.Single(context.DownloadStack);
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == nextChainedBlock.HashBlock);
// The FindBlocks() task should be removed from the routine
// as the next chained block exists in PendingStorage
Assert.Single(context.InnerSteps);
Assert.False(context.InnerSteps.OfType<BlockStoreInnerStepFindBlocks>().Any());
}
}
[Fact]
public void BlockStoreInnerStepFindBlocks_CanRemoveTaskFromRoutine_BlockExistsInRepository()
{
var blocks = this.CreateBlocks(3);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 3 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks).GetAwaiter().GetResult();
// The chain has 3 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(2));
// Create block store loop
fluent.Create(chain);
// Start finding blocks from Block[1]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[1].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
var task = new BlockStoreInnerStepFindBlocks(this.LoggerFactory.Object);
task.ExecuteAsync(context).GetAwaiter().GetResult();
// DownloadStack should only contain Block[1]
Assert.Empty(context.DownloadStack);
// The FindBlocks() task should be removed from the routine
// as the next chained block exist in the BlockRepository
// causing a stop condition
Assert.Single(context.InnerSteps);
Assert.False(context.InnerSteps.OfType<BlockStoreInnerStepFindBlocks>().Any());
}
}
[Fact]
public void BlockStoreInnerStepFindBlocks_CanRemoveTaskFromRoutine_NextChainedBlockIsNull()
{
var blocks = this.CreateBlocks(3);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 2 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(2).Last().GetHash(), blocks.Take(2).ToList()).GetAwaiter().GetResult();
// The chain has 3 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(2));
// Create block store loop
fluent.Create(chain);
// Start finding blocks from Block[2]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[2].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
var task = new BlockStoreInnerStepFindBlocks(this.LoggerFactory.Object);
task.ExecuteAsync(context).GetAwaiter().GetResult();
// DownloadStack should only contain nextChainedHeader
Assert.Single(context.DownloadStack);
Assert.Contains(context.DownloadStack, cb => cb.HashBlock == nextChainedBlock.HashBlock);
// The FindBlocks() task should be removed from the routine
// as the next chained block is null
Assert.Single(context.InnerSteps);
Assert.False(context.InnerSteps.OfType<BlockStoreInnerStepFindBlocks>().Any());
}
}
}
}
using System.Linq;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore.LoopSteps;
using Stratis.Bitcoin.Utilities;
using Xunit;
using static Stratis.Bitcoin.BlockPulling.BlockPuller;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
public sealed class BlockStoreLoopStepDownloadBlocksTest : BlockStoreLoopStepBaseTest
{
/// <summary>
/// This test executes DownloadBlockStep the <see cref="BlockStoreInnerStepFindBlocks"/> and
/// <see cref="BlockStoreInnerStepReadBlocks"/> inner steps via <see cref="DownloadBlockStep"/>
/// </summary>
[Fact]
public void BlockStoreInnerStepReadBlocks_WithBlocksToAskAndRead_PushToRepository()
{
var blocks = this.CreateBlocks(10);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 10 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(9));
// Create block store loop
fluent.Create(chain);
// Push blocks 5 - 9 to the downloaded blocks collection
for (int i = 5; i <= 9; i++)
{
fluent.Loop.BlockPuller.InjectBlock(blocks[i].GetHash(), new DownloadedBlock { Length = blocks[i].GetSerializedSize(), Block = blocks[i] }, new CancellationToken());
}
// Start processing blocks to download from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var step = new DownloadBlockStep(fluent.Loop, this.LoggerFactory.Object, DateTimeProvider.Default);
step.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(blocks[9].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[9].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
/// <summary>
/// Test only BlockStoreInnerStepReadBlocks() inner step
/// </summary>
[Fact]
public void BlockStoreInnerStepReadBlocks_WithBlocksToAskAndRead_CanBreakExecutionOnStallCountReached()
{
var blocks = this.CreateBlocks(3);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 3 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks.Take(3).ToList()).GetAwaiter().GetResult();
// The chain has 3 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(2));
// Create block store loop
fluent.Create(chain);
//Start finding blocks from Block[1]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[1].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
context.DownloadStack.Enqueue(nextChainedBlock);
context.StallCount = 10001;
var task = new BlockStoreInnerStepReadBlocks(this.LoggerFactory.Object);
var result = task.ExecuteAsync(context).GetAwaiter().GetResult();
Assert.Equal(InnerStepResult.Stop, result);
}
}
[Fact]
public void BlockStoreInnerStepReadBlocks_CanBreakExecution_DownloadStackIsEmpty()
{
var blocks = this.CreateBlocks(2);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 2 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks).GetAwaiter().GetResult();
// The chain has 2 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(1));
// Create block store loop
fluent.Create(chain);
//Start finding blocks from Block[1]
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[1].GetHash());
// Create Task Context
var context = new BlockStoreInnerStepContext(new CancellationToken(), fluent.Loop, nextChainedBlock, this.LoggerFactory.Object, DateTimeProvider.Default);
context.StallCount = 10001;
var task = new BlockStoreInnerStepReadBlocks(this.LoggerFactory.Object);
var result = task.ExecuteAsync(context).GetAwaiter().GetResult();
Assert.Equal(InnerStepResult.Stop, result);
}
}
}
}
using System.Linq;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore.LoopSteps;
using Xunit;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
public sealed class BlockStoreLoopStepReorganiseTest : BlockStoreLoopStepBaseTest
{
[Fact]
public void ReorganiseBlockRepository_WithBlockRepositoryAndChainOutofSync_ReorganiseBlocks_InMemory()
{
var blocks = this.CreateBlocks(15);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 15 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Last().GetHash(), blocks).GetAwaiter().GetResult();
// The chain has 10 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(9));
// Create the last 5 chained block headers without appending to the chain
var block9 = chain.GetBlock(blocks[9].Header.GetHash());
var block10 = new ChainedHeader(blocks[10].Header, blocks[10].Header.GetHash(), block9);
var block11 = new ChainedHeader(blocks[11].Header, blocks[11].Header.GetHash(), block10);
var block12 = new ChainedHeader(blocks[12].Header, blocks[12].Header.GetHash(), block11);
var block13 = new ChainedHeader(blocks[13].Header, blocks[13].Header.GetHash(), block12);
var block14 = new ChainedHeader(blocks[14].Header, blocks[14].Header.GetHash(), block13);
fluent.Create(chain);
fluent.Loop.SetStoreTip(block14);
Assert.Equal(fluent.Loop.StoreTip.Header.GetHash(), block14.Header.GetHash());
Assert.Equal(fluent.Loop.BlockRepository.BlockHash, block14.Header.GetHash());
//Reorganise (delete) blocks from the block repository that is not found
var nextChainedBlock = block10;
var reorganiseStep = new ReorganiseBlockRepositoryStep(fluent.Loop, this.LoggerFactory.Object);
reorganiseStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(fluent.Loop.StoreTip.Header.GetHash(), block10.Previous.Header.GetHash());
Assert.Equal(fluent.Loop.BlockRepository.BlockHash, block10.Previous.Header.GetHash());
}
}
}
}
using System.Linq;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore.LoopSteps;
using Xunit;
namespace Stratis.Bitcoin.Features.BlockStore.Tests.LoopTests
{
public sealed class BlockStoreLoopStepTryPendingTest : BlockStoreLoopStepBaseTest
{
/// <summary>
/// If the store is not in IBD we need to immediately push any pending blocks
/// to the repository.
/// </summary>
[Fact]
public void ProcessPendingStorage_PushToRepo_NotIBD_InMemory()
{
var blocks = this.CreateBlocks(15);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)))
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 10 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(9));
// Create block store loop
fluent.Create(chain);
//Set the store's tip
fluent.Loop.SetStoreTip(fluent.Loop.Chain.GetBlock(blocks.Take(5).Last().GetHash()));
// Add chained blocks 5 - 9 to PendingStorage
for (int i = 5; i <= 9; i++)
{
this.AddBlockToPendingStorage(fluent.Loop, blocks[i]);
}
//Start processing pending blocks from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var processPendingStorageStep = new ProcessPendingStorageStep(fluent.Loop, this.LoggerFactory.Object);
processPendingStorageStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(blocks[9].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[9].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
/// <summary>
/// If the store IS in IBD we can process the blocks until:
/// <list>
/// <item>1: Pending storage count has reached <see cref="BlockStoreLoop.PendingStorageBatchThreshold"/>.</item>
/// <item>2: A break condition is found.</item>
/// <item>3: <see cref="BlockStoreLoop.MaxPendingInsertBlockSize"/> has been reached. </item>
/// </list>
/// </summary>
[Fact]
public void ProcessPendingStorage_PushToRepo_IBD_InMemory()
{
var blocks = this.CreateBlocks(15);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)).AsIBD())
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 15 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(14));
// Create block store loop
fluent.Create(chain);
//Set the store's tip
fluent.Loop.SetStoreTip(fluent.Loop.Chain.GetBlock(blocks.Take(5).Last().GetHash()));
// Add chained blocks 5 - 14 to PendingStorage
for (int i = 5; i <= 14; i++)
{
this.AddBlockToPendingStorage(fluent.Loop, blocks[i]);
}
//Start processing pending blocks from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var processPendingStorageStep = new ProcessPendingStorageStep(fluent.Loop, this.LoggerFactory.Object);
processPendingStorageStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(blocks[14].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[14].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
/// <summary>
/// If the store IS in IBD we can process the blocks until:
/// <list>
/// <item>1: Pending storage count has reached <see cref="BlockStoreLoop.PendingStorageBatchThreshold"/>.</item>
/// <item>2: A break condition is found.</item>
/// <item>3: <see cref="BlockStoreLoop.MaxPendingInsertBlockSize"/> has been reached. </item>
/// </list>
/// </summary>
[Fact]
public void ProcessPendingStorage_PushToRepo_IBD_MaxPendingInsertBlockSize_InMemory()
{
var blocks = this.CreateBlocks(2500, true);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)).AsIBD())
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 2500 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(2499));
// Create block store loop
fluent.Create(chain);
//Set the store's tip
fluent.Loop.SetStoreTip(fluent.Loop.Chain.GetBlock(blocks.Take(5).Last().GetHash()));
// Add chained blocks 5 - 2499 to PendingStorage
for (int i = 5; i <= 2499; i++)
{
this.AddBlockToPendingStorage(fluent.Loop, blocks[i]);
}
//Start processing pending blocks from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var processPendingStorageStep = new ProcessPendingStorageStep(fluent.Loop, this.LoggerFactory.Object);
processPendingStorageStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), false).GetAwaiter().GetResult();
Assert.Equal(blocks[2499].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[2499].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
/// <summary>
/// If the store is being disposed, process and push all the blocks in pending storage
/// </summary>
[Fact]
public void ProcessPendingStorage_PushToRepo_Disposing_InMemory()
{
var blocks = this.CreateBlocks(15);
using (var fluent = new FluentBlockStoreLoop(CreateDataFolder(this)).AsIBD())
{
// Push 5 blocks to the repository
fluent.BlockRepository.PutAsync(blocks.Take(5).Last().GetHash(), blocks.Take(5).ToList()).GetAwaiter().GetResult();
// The chain has 15 blocks appended
var chain = new ConcurrentChain(blocks[0].Header);
this.AppendBlocksToChain(chain, blocks.Skip(1).Take(14));
// Create block store loop
fluent.Create(chain);
//Set the store's tip
fluent.Loop.SetStoreTip(fluent.Loop.Chain.GetBlock(blocks.Take(5).Last().GetHash()));
// Add chained blocks 5 - 14 to PendingStorage
for (int i = 5; i <= 14; i++)
{
this.AddBlockToPendingStorage(fluent.Loop, blocks[i]);
}
//Start processing pending blocks from block 5
var nextChainedBlock = fluent.Loop.Chain.GetBlock(blocks[5].GetHash());
var processPendingStorageStep = new ProcessPendingStorageStep(fluent.Loop, this.LoggerFactory.Object);
processPendingStorageStep.ExecuteAsync(nextChainedBlock, new CancellationToken(), true).GetAwaiter().GetResult();
Assert.Equal(blocks[14].GetHash(), fluent.Loop.BlockRepository.BlockHash);
Assert.Equal(blocks[14].GetHash(), fluent.Loop.StoreTip.HashBlock);
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>Stratis.Bitcoin.Features.BlockStore.Tests</AssemblyName>
......@@ -15,21 +14,17 @@
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;IDE0008;</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Stratis.Bitcoin.Features.BlockStore.Tests\**" />
<EmbeddedResource Remove="Stratis.Bitcoin.Features.BlockStore.Tests\**" />
<None Remove="Stratis.Bitcoin.Features.BlockStore.Tests\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NBitcoin\NBitcoin.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin.Features.BlockStore\Stratis.Bitcoin.Features.BlockStore.csproj" />
......@@ -37,15 +32,12 @@
<ProjectReference Include="..\Stratis.Bitcoin.Tests.Wallet.Common\Stratis.Bitcoin.Tests.Wallet.Common.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Stratis Bitcoin Features BlockStore</Description>
<AssemblyTitle>Stratis.Bitcoin.Features.BlockStore</AssemblyTitle>
......@@ -14,7 +13,7 @@
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<Version>1.1.12-beta</Version>
<Version>1.1.12-beta</Version>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
......@@ -25,19 +24,16 @@
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NBitcoin\NBitcoin.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;IDE0008;</NoWarn>
<DocumentationFile></DocumentationFile>
<DocumentationFile>
</DocumentationFile>
</PropertyGroup>
</Project>
</Project>
\ No newline at end of file
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Configuration.Settings;
using Xunit;
namespace Stratis.Bitcoin.Features.Consensus.Tests
......@@ -13,7 +14,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Tests
uint256 validHexBlock = new uint256("00000000229d9fb87182d73870d53f9fdd9b76bfc02c059e6d9a6c7a3507031d");
Network network = Network.TestNet;
NodeSettings nodeSettings = new NodeSettings(network, args:new string[] { $"-assumevalid={validHexBlock.ToString()}" });
ConsensusSettings settings = new ConsensusSettings().Load(nodeSettings);
ConsensusSettings settings = new ConsensusSettings(nodeSettings);
Assert.Equal(validHexBlock, settings.BlockAssumedValid);
}
......@@ -23,7 +24,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Tests
LoggerFactory loggerFactory = new LoggerFactory();
Network network = Network.TestNet;
NodeSettings nodeSettings = new NodeSettings(network, args:new string[] { "-assumevalid=0" });
ConsensusSettings settings = new ConsensusSettings().Load(nodeSettings);
ConsensusSettings settings = new ConsensusSettings(nodeSettings);
Assert.Null(settings.BlockAssumedValid);
}
......@@ -33,26 +34,26 @@ namespace Stratis.Bitcoin.Features.Consensus.Tests
LoggerFactory loggerFactory = new LoggerFactory();
Network network = Network.TestNet;
NodeSettings nodeSettings = new NodeSettings(network, args:new string[] { "-assumevalid=xxx" });
Assert.Throws<ConfigurationException>(() => new ConsensusSettings().Load(nodeSettings));
Assert.Throws<ConfigurationException>(() => new ConsensusSettings(nodeSettings));
}
[Fact]
public void LoadConfigWithDefaultsSetsToNetworkDefault()
{
Network network = Network.StratisMain;
ConsensusSettings settings = new ConsensusSettings().Load(NodeSettings.Default(network));
ConsensusSettings settings = new ConsensusSettings(NodeSettings.Default(network));
Assert.Equal(network.Consensus.DefaultAssumeValid, settings.BlockAssumedValid);
network = Network.StratisTest;
settings = new ConsensusSettings().Load(NodeSettings.Default(network));
settings = new ConsensusSettings(NodeSettings.Default(network));
Assert.Equal(network.Consensus.DefaultAssumeValid, settings.BlockAssumedValid);
network = Network.Main;
settings = new ConsensusSettings().Load(NodeSettings.Default(network));
settings = new ConsensusSettings(NodeSettings.Default(network));
Assert.Equal(network.Consensus.DefaultAssumeValid, settings.BlockAssumedValid);
network = Network.TestNet;
settings = new ConsensusSettings().Load(NodeSettings.Default(network));
settings = new ConsensusSettings(NodeSettings.Default(network));
Assert.Equal(network.Consensus.DefaultAssumeValid, settings.BlockAssumedValid);
}
}
......
......@@ -2,6 +2,7 @@
using System.Linq;
using NBitcoin;
using Newtonsoft.Json.Linq;
using Stratis.Bitcoin.Controllers.Models;
using Stratis.Bitcoin.Features.RPC.Models;
using Xunit;
......@@ -23,9 +24,9 @@ namespace Stratis.Bitcoin.Features.RPC.Tests.Models
public TransactionModelsTest()
{
this.txBlock10CoinbaseModelBrief = new TransactionBriefModel(Transaction.Parse(TxBlock10Hex));
this.txBlock460373CoinbaseModelVerbose = new TransactionVerboseModel(Transaction.Parse(TxBlock460373CoinbaseHex), Network.Main);
this.txTwoInTwoOutModelVerbose = new TransactionVerboseModel(Transaction.Parse(TxTwoInTwoOutHex), Network.Main);
this.txBlock10CoinbaseModelBrief = new TransactionBriefModel(Transaction.Parse(TxBlock10Hex, RawFormat.BlockExplorer));
this.txBlock460373CoinbaseModelVerbose = new TransactionVerboseModel(Transaction.Parse(TxBlock460373CoinbaseHex, RawFormat.BlockExplorer), Network.Main);
this.txTwoInTwoOutModelVerbose = new TransactionVerboseModel(Transaction.Parse(TxTwoInTwoOutHex, RawFormat.BlockExplorer), Network.Main);
}
public void Dispose()
......
......@@ -2678,7 +2678,7 @@ namespace Stratis.Bitcoin.Tests.Consensus
Assert.True(connectNewHeadersResult.HaveBlockDataAvailabilityStateOf(BlockDataAvailabilityState.BlockRequired));
// 10 blocks are downloaded.
foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToHashArray())
foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
{
chainedHeaderTree.BlockDataDownloaded(chainedHeader, extendedChainTip.FindAncestorOrSelf(chainedHeader).Block);
chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
......@@ -2703,7 +2703,7 @@ namespace Stratis.Bitcoin.Tests.Consensus
Assert.Equal(connectNewHeadersResult.DownloadFrom.Header, tipOfFork.GetAncestor(heightOfFork + 1).Header);
Assert.Equal(connectNewHeadersResult.DownloadTo.Header, tipOfFork.Header);
foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToHashArray())
foreach (ChainedHeader chainedHeader in connectNewHeadersResult.ToArray())
{
chainedHeaderTree.BlockDataDownloaded(chainedHeader, tipOfFork.FindAncestorOrSelf(chainedHeader).Block);
chainedHeaderTree.PartialValidationSucceeded(chainedHeader, out bool fullValidationRequired);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment