public BlockStoreTests() { this.network = KnownNetworks.StratisMain; this.repositoryTipHashAndHeight = new HashHeightPair(this.network.GenesisHash, 0); this.storeSettings = new StoreSettings(NodeSettings.Default(this.network)); this.random = new Random(); this.listOfSavedBlocks = new Dictionary <uint256, Block>(); this.listOfSavedBlocks.Add(uint256.One, Block.Parse(this.testBlockHex, KnownNetworks.StratisMain.Consensus.ConsensusFactory)); this.chainIndexer = CreateChain(10); this.nodeLifetime = new NodeLifetime(); this.blockRepositoryMock = new Mock <IBlockRepository>(); this.blockRepositoryMock.Setup(x => x.PutBlocks(It.IsAny <HashHeightPair>(), It.IsAny <List <Block> >())) .Callback((HashHeightPair newTip, List <Block> blocks) => { this.repositoryTipHashAndHeight = newTip; this.repositorySavesCount++; this.repositoryTotalBlocksSaved += blocks.Count; }); this.blockRepositoryMock.Setup(x => x.Delete(It.IsAny <HashHeightPair>(), It.IsAny <List <uint256> >())) .Callback((HashHeightPair newTip, List <uint256> blocks) => { this.repositoryTotalBlocksDeleted += blocks.Count; }); this.blockRepositoryMock.Setup(x => x.GetBlock(It.IsAny <uint256>())) .Returns((uint256 hash) => { Block block = null; if (this.listOfSavedBlocks.ContainsKey(hash)) { block = this.listOfSavedBlocks[hash]; } return(block); }); this.blockRepositoryMock.Setup(x => x.TipHashAndHeight).Returns(() => { return(this.repositoryTipHashAndHeight); }); this.chainState = new ChainState(); this.initialBlockDownloadState = new Mock <IInitialBlockDownloadState>(); var blockStoreFlushCondition = new BlockStoreQueueFlushCondition(this.chainState, this.initialBlockDownloadState.Object); this.loggerFactory = new LoggerFactory(); this.signals = new Signals.Signals(this.loggerFactory, null); this.asyncProvider = new AsyncProvider(this.loggerFactory, this.signals, this.nodeLifetime); this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushCondition, this.storeSettings, this.blockRepositoryMock.Object, this.loggerFactory, new Mock <INodeStats>().Object, this.asyncProvider); }
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); this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushConditionMock.Object, this.storeSettings, this.blockRepositoryMock.Object, this.loggerFactory, new Mock <INodeStats>().Object, this.asyncProvider); this.blockStoreQueue.Initialize(); ChainedHeader lastHeader = null; for (int i = 1; i < this.chainIndexer.Height - 1; i++) { lastHeader = this.chainIndexer.GetHeader(i); Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, lastHeader)); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); Assert.Equal(this.chainState.BlockStoreTip, this.chainIndexer.Genesis); Assert.Equal(0, this.repositorySavesCount); this.nodeLifetime.StopApplication(); this.blockStoreQueue.Dispose(); Assert.Equal(this.chainState.BlockStoreTip, lastHeader); Assert.Equal(1, this.repositorySavesCount); }
public async Task BatchIsSavedAfterSizeThresholdReachedAsync() { Block block = Block.Load(Encoders.Hex.DecodeData(this.testBlockHex), Network.StratisMain); int blockSize = block.GetSerializedSize(); this.consensusTip = null; int count = BlockStoreQueue.BatchThresholdSizeBytes / blockSize + 2; ConcurrentChain longChain = this.CreateChain(count); this.repositoryBlockHash = longChain.Genesis.HashBlock; this.blockStoreQueue = new BlockStoreQueue(this.repository, longChain, this.chainState, new StoreSettings(), new NodeLifetime(), new LoggerFactory()); await this.blockStoreQueue.InitializeAsync().ConfigureAwait(false); this.consensusTip = longChain.Tip; // Send all the blocks to the block store except for the last one because that will trigger batch saving because of reaching the tip. for (int i = 1; i < count; i++) { ChainedHeader header = longChain.GetBlock(i); this.blockStoreQueue.AddToPending(new BlockPair(block, header)); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); Assert.Equal(longChain.GetBlock(count - 1), this.chainState.BlockStoreTip); Assert.Equal(1, this.repositorySavesCount); }
public async Task BatchIsSavedAfterSizeThresholdReachedAsync() { Block block = Block.Load(Encoders.Hex.DecodeData(this.testBlockHex), KnownNetworks.StratisMain.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); this.blockStoreQueue = new BlockStoreQueue(longChainIndexer, this.chainState, blockStoreFlushCondition, this.storeSettings, this.blockRepositoryMock.Object, this.loggerFactory, new Mock <INodeStats>().Object, this.asyncProvider); this.blockStoreQueue.Initialize(); this.chainState.ConsensusTip = longChainIndexer.Tip; // Send all the blocks to the block store except for the last one because that will trigger batch saving because of reaching the tip. for (int i = 1; i < count; i++) { ChainedHeader header = longChainIndexer.GetHeader(i); this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, header)); } await WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); Assert.Equal(longChainIndexer.GetHeader(count - 1), this.chainState.BlockStoreTip); Assert.Equal(1, this.repositorySavesCount); }
public BlockStoreTests() { this.network = KnownNetworks.StratisMain; this.repositoryTipHashAndHeight = new HashHeightPair(this.network.GenesisHash, 0); var serializer = new DBreezeSerializer(); serializer.Initialize(new StratisMain()); this.random = new Random(); this.listOfSavedBlocks = new Dictionary <uint256, Block>(); this.listOfSavedBlocks.Add(uint256.One, Block.Parse(this.testBlockHex, KnownNetworks.StratisMain)); this.chain = CreateChain(10); this.nodeLifetime = new NodeLifetime(); this.blockRepositoryMock = new Mock <IBlockRepository>(); this.blockRepositoryMock.Setup(x => x.PutAsync(It.IsAny <HashHeightPair>(), It.IsAny <List <Block> >())) .Returns((HashHeightPair newTip, List <Block> blocks) => { this.repositoryTipHashAndHeight = newTip; this.repositorySavesCount++; this.repositoryTotalBlocksSaved += blocks.Count; return(Task.CompletedTask); }); this.blockRepositoryMock.Setup(x => x.DeleteAsync(It.IsAny <HashHeightPair>(), It.IsAny <List <uint256> >())) .Returns((HashHeightPair newTip, List <uint256> blocks) => { this.repositoryTotalBlocksDeleted += blocks.Count; return(Task.CompletedTask); }); this.blockRepositoryMock.Setup(x => x.GetBlockAsync(It.IsAny <uint256>())) .Returns((uint256 hash) => { Block block = null; if (this.listOfSavedBlocks.ContainsKey(hash)) { block = this.listOfSavedBlocks[hash]; } return(Task.FromResult(block)); }); this.blockRepositoryMock.Setup(x => x.TipHashAndHeight).Returns(() => { return(this.repositoryTipHashAndHeight); }); this.chainState = new ChainState(); var blockStoreFlushCondition = new BlockStoreQueueFlushCondition(this.chainState); this.blockStoreQueue = new BlockStoreQueue(this.chain, this.chainState, blockStoreFlushCondition, new StoreSettings(NodeSettings.Default(this.network)), this.blockRepositoryMock.Object, new LoggerFactory(), new Mock <INodeStats>().Object); }
public BlockStoreTests() { this.listOfSavedBlocks = new Dictionary <uint256, Block>(); this.listOfSavedBlocks.Add(uint256.One, Block.Parse(this.testBlockHex, KnownNetworks.StratisMain)); this.network = KnownNetworks.StratisMain; this.chain = this.CreateChain(10); this.consensusTip = null; this.nodeLifetime = new NodeLifetime(); var blockRepositoryMock = new Mock <IBlockRepository>(); blockRepositoryMock.Setup(x => x.PutAsync(It.IsAny <uint256>(), It.IsAny <List <Block> >())) .Returns((uint256 nextBlockHash, List <Block> blocks) => { this.repositoryBlockHash = nextBlockHash; this.repositorySavesCount++; this.repositoryTotalBlocksSaved += blocks.Count; return(Task.CompletedTask); }); blockRepositoryMock.Setup(x => x.DeleteAsync(It.IsAny <uint256>(), It.IsAny <List <uint256> >())) .Returns((uint256 nextBlockHash, List <uint256> blocks) => { this.repositoryTotalBlocksDeleted += blocks.Count; return(Task.CompletedTask); }); blockRepositoryMock.Setup(x => x.GetAsync(It.IsAny <uint256>())) .Returns((uint256 hash) => { Block block = null; if (this.listOfSavedBlocks.ContainsKey(hash)) { block = this.listOfSavedBlocks[hash]; } return(Task.FromResult(block)); }); blockRepositoryMock.Setup(x => x.BlockHash).Returns(() => { return(this.repositoryBlockHash); }); this.repository = blockRepositoryMock.Object; var chainStateMoq = new Mock <IChainState>(); chainStateMoq.Setup(x => x.ConsensusTip).Returns(() => this.consensusTip); chainStateMoq.SetupProperty(x => x.BlockStoreTip, null); this.chainState = chainStateMoq.Object; this.blockStoreQueue = new BlockStoreQueue(this.repository, this.chain, this.chainState, new StoreSettings(), this.nodeLifetime, new LoggerFactory()); }
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); this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushConditionMock.Object, this.storeSettings, this.blockRepositoryMock.Object, this.loggerFactory, new Mock <INodeStats>().Object, this.asyncProvider); this.blockStoreQueue.Initialize(); int reorgedChainLenght = 3; int realChainLenght = 6; // First present a short chain. ChainIndexer alternativeChainIndexer = CreateChain(reorgedChainLenght); for (int i = 1; i < alternativeChainIndexer.Height; i++) { Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, alternativeChainIndexer.GetHeader(i))); } // Present second chain which has more work and reorgs blocks from genesis. for (int i = 1; i < realChainLenght; i++) { Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, this.chainIndexer.GetHeader(i))); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); Assert.Equal(this.chainState.BlockStoreTip, this.chainIndexer.Genesis); 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(1, this.repositorySavesCount); Assert.Equal(realChainLenght - 1, this.repositoryTotalBlocksSaved); }
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); this.blockStoreQueue = new BlockStoreQueue(this.chainIndexer, this.chainState, blockStoreFlushConditionMock.Object, this.storeSettings, this.blockRepositoryMock.Object, this.loggerFactory, new Mock <INodeStats>().Object, this.asyncProvider); this.blockStoreQueue.Initialize(); this.chainState.ConsensusTip = this.chainIndexer.Tip; for (int i = 1; i <= this.chainIndexer.Height; i++) { ChainedHeader lastHeader = this.chainIndexer.GetHeader(i); Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); if (i == this.chainIndexer.Height) { await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); blockStoreFlushConditionMock.Setup(s => s.ShouldFlush).Returns(true); } this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, lastHeader)); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); // Wait for store tip to finish saving int counter = 0; if (this.chainState.BlockStoreTip != this.chainIndexer.Tip) { Assert.True(counter < 10); counter++; await Task.Delay(500); } Assert.Equal(this.chainState.BlockStoreTip, this.chainIndexer.Tip); Assert.Equal(1, this.repositorySavesCount); }
public async Task ReorgedBlocksAreDeletedFromRepositoryIfReorgDetectedAsync() { this.chainIndexer = CreateChain(1000); this.repositoryTipHashAndHeight = new HashHeightPair(this.chainIndexer.Genesis.HashBlock, 0); var blockStoreFlushCondition = new Mock <IBlockStoreQueueFlushCondition>(); blockStoreFlushCondition.Setup(s => s.ShouldFlush).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); this.blockStoreQueue.Initialize(); this.chainState.ConsensusTip = this.chainIndexer.Tip; // Sending 500 blocks to the queue. for (int i = 1; i < 500; i++) { Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, this.chainIndexer.GetHeader(i))); } // Create alternative chain with fork point at 450. ChainedHeader prevBlock = this.chainIndexer.GetHeader(450); var alternativeBlocks = new List <ChainedHeader>(); for (int i = 0; i < 100; i++) { BlockHeader header = this.network.Consensus.ConsensusFactory.CreateBlockHeader(); header.Nonce = RandomUtils.GetUInt32(); header.HashPrevBlock = prevBlock.HashBlock; header.Bits = Target.Difficulty1; var chainedHeader = new ChainedHeader(header, header.GetHash(), prevBlock); alternativeBlocks.Add(chainedHeader); prevBlock = chainedHeader; } ChainedHeader savedHeader = this.chainIndexer.Tip; this.chainIndexer.SetTip(alternativeBlocks.Last()); this.chainState.ConsensusTip = this.chainIndexer.Tip; // Present alternative chain and trigger save. foreach (ChainedHeader header in alternativeBlocks) { Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); if (header == alternativeBlocks.Last()) { blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(true); } this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, header)); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(false); // Make sure only longest chain is saved. Assert.Equal(this.chainIndexer.Tip.Height, this.repositoryTotalBlocksSaved); // Present a new longer chain that will reorg the repository. this.chainIndexer.SetTip(savedHeader); this.chainState.ConsensusTip = this.chainIndexer.Tip; for (int i = 451; i <= this.chainIndexer.Height; i++) { Block block = this.network.Consensus.ConsensusFactory.CreateBlock(); block.GetSerializedSize(); if (i == this.chainIndexer.Height) { blockStoreFlushCondition.Setup(s => s.ShouldFlush).Returns(true); } this.blockStoreQueue.AddToPending(new ChainedHeaderBlock(block, this.chainIndexer.GetHeader(i))); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); // Make sure chain is saved. Assert.Equal(this.chainIndexer.Tip.Height + alternativeBlocks.Count, this.repositoryTotalBlocksSaved); Assert.Equal(alternativeBlocks.Count, this.repositoryTotalBlocksDeleted); // Dispose block store. this.nodeLifetime.StopApplication(); this.blockStoreQueue.Dispose(); }
/// <summary> /// Creates test chain with a consensus loop. /// </summary> public static async Task <TestChainContext> CreateAsync(Network network, string dataDir, Mock <IPeerAddressManager> mockPeerAddressManager = null) { var testChainContext = new TestChainContext() { Network = network }; testChainContext.NodeSettings = new NodeSettings(network, args: new string[] { $"-datadir={dataDir}" }); testChainContext.ConnectionSettings = new ConnectionManagerSettings(testChainContext.NodeSettings); testChainContext.LoggerFactory = testChainContext.NodeSettings.LoggerFactory; testChainContext.DateTimeProvider = DateTimeProvider.Default; network.Consensus.Options = new ConsensusOptions(); new FullNodeBuilderConsensusExtension.PowConsensusRulesRegistration().RegisterRules(network.Consensus); var consensusSettings = new ConsensusSettings(testChainContext.NodeSettings); testChainContext.Checkpoints = new Checkpoints(); testChainContext.Chain = new ConcurrentChain(network); testChainContext.ChainState = new ChainState(); testChainContext.InitialBlockDownloadState = new InitialBlockDownloadState(testChainContext.ChainState, testChainContext.Network, consensusSettings, new Checkpoints()); var inMemoryCoinView = new InMemoryCoinView(testChainContext.Chain.Tip.HashBlock); var cachedCoinView = new CachedCoinView(inMemoryCoinView, DateTimeProvider.Default, testChainContext.LoggerFactory); var dataFolder = new DataFolder(TestBase.AssureEmptyDir(dataDir)); testChainContext.PeerAddressManager = mockPeerAddressManager == null ? new PeerAddressManager(DateTimeProvider.Default, dataFolder, testChainContext.LoggerFactory, new SelfEndpointTracker(testChainContext.LoggerFactory)) : mockPeerAddressManager.Object; testChainContext.MockConnectionManager = new Mock <IConnectionManager>(); testChainContext.MockReadOnlyNodesCollection = new Mock <IReadOnlyNetworkPeerCollection>(); testChainContext.MockConnectionManager.Setup(s => s.ConnectedPeers).Returns(testChainContext.MockReadOnlyNodesCollection.Object); testChainContext.MockConnectionManager.Setup(s => s.NodeSettings).Returns(testChainContext.NodeSettings); testChainContext.MockConnectionManager.Setup(s => s.ConnectionSettings).Returns(testChainContext.ConnectionSettings); testChainContext.ConnectionManager = testChainContext.MockConnectionManager.Object; testChainContext.PeerBanning = new PeerBanning(testChainContext.ConnectionManager, testChainContext.LoggerFactory, testChainContext.DateTimeProvider, testChainContext.PeerAddressManager); var deployments = new NodeDeployments(testChainContext.Network, testChainContext.Chain); testChainContext.ConsensusRules = new PowConsensusRuleEngine(testChainContext.Network, testChainContext.LoggerFactory, testChainContext.DateTimeProvider, testChainContext.Chain, deployments, consensusSettings, testChainContext.Checkpoints, cachedCoinView, testChainContext.ChainState, new InvalidBlockHashStore(new DateTimeProvider())) .Register(); testChainContext.HeaderValidator = new HeaderValidator(testChainContext.ConsensusRules, testChainContext.LoggerFactory); testChainContext.IntegrityValidator = new IntegrityValidator(testChainContext.ConsensusRules, testChainContext.LoggerFactory); testChainContext.PartialValidator = new PartialValidator(testChainContext.ConsensusRules, testChainContext.LoggerFactory); testChainContext.FullValidator = new FullValidator(testChainContext.ConsensusRules, testChainContext.LoggerFactory); var blockRepository = new BlockRepository(testChainContext.Network, dataFolder, testChainContext.DateTimeProvider, testChainContext.LoggerFactory); var blockStore = new BlockStoreQueue(testChainContext.Chain, testChainContext.ChainState, new Mock <StoreSettings>().Object, new Mock <INodeLifetime>().Object, blockRepository, testChainContext.LoggerFactory, new Mock <INodeStats>().Object); await blockStore.InitializeAsync(); testChainContext.Consensus = ConsensusManagerHelper.CreateConsensusManager(network, dataDir); await testChainContext.Consensus.InitializeAsync(testChainContext.Chain.Tip); return(testChainContext); }
public async Task ReorgedBlocksAreDeletedFromRepositoryIfReorgDetectedAsync() { this.chain = this.CreateChain(1000); this.repositoryBlockHash = this.chain.Genesis.HashBlock; this.blockStoreQueue = new BlockStoreQueue(this.repository, this.chain, this.chainState, new StoreSettings(), this.nodeLifetime, new LoggerFactory()); await this.blockStoreQueue.InitializeAsync().ConfigureAwait(false); this.consensusTip = this.chain.Tip; // Sending 500 blocks to the queue. for (int i = 1; i < 500; i++) { Block block = new Block(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new BlockPair(block, this.chain.GetBlock(i))); } // Create alternative chain with fork point at 450. ChainedHeader prevBlock = this.chain.GetBlock(450); var alternativeBlocks = new List <ChainedHeader>(); for (int i = 0; i < 100; i++) { var header = new BlockHeader() { Nonce = RandomUtils.GetUInt32(), HashPrevBlock = prevBlock.HashBlock, Bits = Target.Difficulty1 }; var chainedHeader = new ChainedHeader(header, header.GetHash(), prevBlock); alternativeBlocks.Add(chainedHeader); prevBlock = chainedHeader; } ChainedHeader savedHeader = this.chain.Tip; this.chain.SetTip(alternativeBlocks.Last()); this.consensusTip = this.chain.Tip; // Present alternative chain and trigger save. foreach (ChainedHeader header in alternativeBlocks) { Block block = new Block(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new BlockPair(block, header)); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); // Make sure only longest chain is saved. Assert.Equal(1, this.repositorySavesCount); Assert.Equal(this.chain.Tip.Height, this.repositoryTotalBlocksSaved); // Present a new longer chain that will reorg the repository. this.chain.SetTip(savedHeader); this.consensusTip = this.chain.Tip; for (int i = 451; i <= this.chain.Height; i++) { Block block = new Block(); block.GetSerializedSize(); this.blockStoreQueue.AddToPending(new BlockPair(block, this.chain.GetBlock(i))); } await this.WaitUntilQueueIsEmptyAsync().ConfigureAwait(false); // Make sure chain is saved. Assert.Equal(2, this.repositorySavesCount); Assert.Equal(this.chain.Tip.Height + alternativeBlocks.Count, this.repositoryTotalBlocksSaved); Assert.Equal(alternativeBlocks.Count, this.repositoryTotalBlocksDeleted); // Dispose block store. this.nodeLifetime.StopApplication(); this.blockStoreQueue.Dispose(); }
public BlockStoreManager(IBlockRepository blockRepository, BlockStoreQueue blockStoreQueue) { this.BlockRepository = blockRepository; this.BlockStoreQueue = blockStoreQueue; }