/// <summary> /// Creates and store a <see cref="ProvenBlockHeader" /> generated by the signaled <see cref="ChainedHeaderBlock" />. /// </summary> /// <param name="blockHeight">Height of the block used to generate its Proven Header.</param> /// <param name="chainedHeaderBlock">Block used to generate its Proven Header.</param> /// <param name="isIBD">Is node in IBD.</param> void CreateAndStoreProvenHeader(int blockHeight, ChainedHeaderBlock chainedHeaderBlock, bool isIBD) { var block = (PosBlock)chainedHeaderBlock.Block; var newProvenHeader = ((PosConsensusFactory)this.network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block); var provenHeaderHash = newProvenHeader.GetHash(); this.provenBlockHeaderStore.AddToPendingBatch(newProvenHeader, new HashHeightPair(provenHeaderHash, blockHeight)); this.logger.LogDebug( "Created Proven Header at height {0} with hash {1} and adding to the pending batch to be stored.", blockHeight, provenHeaderHash); // If our node is in IBD the block will not be announced to peers. // If not in IBD the signaler may expect the block header to be of type PH. // TODO: Memory foot print: // This design will cause memory to grow over time (depending on how long the node is running) // based on the size of the Proven Headers (a proven header can be up to 1000 bytes). // This is also correct for regular header (which are 80 bytes in size). // If we want to be able to control the size of PH we will need to change the logic // in ProvenHeadersBlockStoreBehavior and load the PH from the PH store instead if (!isIBD) { chainedHeaderBlock.SetHeader(newProvenHeader); } }
private async Task OnPartialValidationCompletedCallbackAsync(ChainedHeaderBlock chainedHeaderBlock, bool success) { this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(chainedHeaderBlock), chainedHeaderBlock, nameof(success), success); if (success) { await this.OnPartialValidationSucceededAsync(chainedHeaderBlock).ConfigureAwait(false); } else { List <int> peersToBan; lock (this.peerLock) { peersToBan = this.chainedHeaderTree.PartialOrFullValidationFailed(chainedHeaderBlock.ChainedHeader); } this.logger.LogDebug("Validation of block '{0}' failed, banning and disconnecting {1} peers.", chainedHeaderBlock, peersToBan.Count); foreach (int peerId in peersToBan) { // TODO: ban and disconnect those peers } } this.logger.LogTrace("(-)"); }
private void OnBlockConnected(BlockConnected blockConnected) { ChainedHeaderBlock blockPair = blockConnected.ConnectedBlock; ChainedHeader chainedHeader = blockPair.ChainedHeader; if (chainedHeader == null) { this.logger.LogTrace("(-)[REORG]"); return; } this.logger.LogTrace("Block hash is '{0}'.", chainedHeader.HashBlock); bool isIBD = this.initialBlockDownloadState.IsInitialBlockDownload(); // Ensure the block is written to disk before relaying. this.AddBlockToQueue(blockPair, isIBD); if (isIBD) { this.logger.LogTrace("(-)[IBD]"); return; } if (this.storeSettings.PruningEnabled) { this.logger.LogTrace("(-)[PRUNE]"); return; } this.logger.LogTrace("Block header '{0}' added to the announce queue.", chainedHeader); this.blocksToAnnounce.Enqueue(chainedHeader); }
public void OnNextCore_WhenTransactionsMissingInLongestChain_ReturnsThemToTheMempool() { var mempoolValidatorMock = new Mock <IMempoolValidator>(); var mempoolMock = new Mock <ITxMempool>(); var loggerFactoryMock = new Mock <ILoggerFactory>(); loggerFactoryMock.Setup(i => i.CreateLogger(It.IsAny <string>())).Returns(new Mock <ILogger>().Object); Signals.Signals signals = new Signals.Signals(loggerFactoryMock.Object, null); var subject = new BlocksDisconnectedSignaled(mempoolMock.Object, mempoolValidatorMock.Object, new MempoolSchedulerLock(), loggerFactoryMock.Object, signals); subject.Initialize(); var block = new Block(); var genesisChainedHeaderBlock = new ChainedHeaderBlock(block, ChainedHeadersHelper.CreateGenesisChainedHeader()); var transaction1 = new Transaction(); var transaction2 = new Transaction(); block.Transactions = new List <Transaction> { transaction1, transaction2 }; signals.Publish(new BlockDisconnected(genesisChainedHeaderBlock)); mempoolValidatorMock.Verify(x => x.AcceptToMemoryPool(It.IsAny <MempoolValidationState>(), transaction1)); mempoolValidatorMock.Verify(x => x.AcceptToMemoryPool(It.IsAny <MempoolValidationState>(), transaction2)); }
/// <summary> /// When a block is received it is passed to the monitor. /// </summary> /// <param name="chainedHeaderBlock">The new block.</param> public void OnBlockReceived(ChainedHeaderBlock chainedHeaderBlock) { this.walletSyncManager.ProcessBlock(chainedHeaderBlock.Block); // Cancel previous sending to avoid first sending new tip and then sending older tip. if ((this.pushBlockTipTask != null) && !this.pushBlockTipTask.IsCompleted) { this.cancellationSource.Cancel(); this.pushBlockTipTask.GetAwaiter().GetResult(); this.pushBlockTipTask = null; this.cancellationSource = null; } var blockTipModel = new BlockTipModel(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock.ChainedHeader.Height, (int)this.depositExtractor.MinimumDepositConfirmations); // There is no reason to wait for the message to be sent. // Awaiting REST API call will only slow this callback. // Callbacks never supposed to do any IO calls or web requests. // Instead we start sending the message and if next block was connected faster than the message was sent we // are canceling it and sending the next tip. // Receiver of this message doesn't care if we are not providing tips for every block we connect, // it just requires to know about out latest state. this.cancellationSource = new CancellationTokenSource(); this.pushBlockTipTask = Task.Run(async() => await this.federationGatewayClient.PushCurrentBlockTipAsync(blockTipModel, this.cancellationSource.Token).ConfigureAwait(false)); IReadOnlyList <IWithdrawal> withdrawals = this.withdrawalExtractor.ExtractWithdrawalsFromBlock( chainedHeaderBlock.Block, chainedHeaderBlock.ChainedHeader.Height); this.withdrawalReceiver.ReceiveWithdrawals(withdrawals); }
/// <inheritdoc /> public void AddToPending(ChainedHeaderBlock chainedHeaderBlock) { // Throw any error encountered by the asynchronous loop. if (this.saveAsyncLoopException != null) { throw this.saveAsyncLoopException; } lock (this.blocksCacheLock) { if (this.pendingBlocksCache.TryAdd(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock)) { this.logger.LogDebug("Block '{0}' was added to pending.", chainedHeaderBlock.ChainedHeader); } else { // If the chained header block already exists, we need to remove it and add to the back of the collection. this.pendingBlocksCache.Remove(chainedHeaderBlock.ChainedHeader.HashBlock); this.pendingBlocksCache.Add(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock); this.logger.LogDebug("Block '{0}' was re-added to pending.", chainedHeaderBlock.ChainedHeader); } this.BlockStoreCacheTip = chainedHeaderBlock.ChainedHeader; } this.blocksQueue.Enqueue(chainedHeaderBlock); this.blocksQueueSizeBytes += chainedHeaderBlock.Block.BlockSize.Value; }
/// <summary> /// Cleans the batch in a way that all headers from the latest one are consecutive. /// Those that violate consecutiveness are removed. /// </summary> /// <returns>List of consecutive blocks.</returns> private List <ChainedHeaderBlock> GetBatchWithoutReorgedBlocks() { // Initialize current with highest block from the batch. ChainedHeaderBlock current = this.batch.Last(); // List of consecutive blocks. It's a cleaned out version of batch that doesn't have blocks that were reorged. var batchCleared = new List <ChainedHeaderBlock>(this.batch.Count) { current }; // Select only those blocks that were not reorged away. for (int i = this.batch.Count - 2; i >= 0; i--) { if (this.batch[i].ChainedHeader.HashBlock != current.ChainedHeader.Previous.HashBlock) { this.logger.LogDebug("Block '{0}' removed from the batch because it was reorged.", this.batch[i].ChainedHeader); continue; } batchCleared.Add(this.batch[i]); current = this.batch[i]; } batchCleared.Reverse(); return(batchCleared); }
private void OnBlockDisconnected(BlockDisconnected blockDisconnected) { ChainedHeaderBlock chBlock = blockDisconnected.DisconnectedBlock; lock (this.locker) { foreach (Poll poll in this.polls.Where(x => !x.IsPending && x.PollExecutedBlockData?.Hash == chBlock.ChainedHeader.HashBlock).ToList()) { this.logger.LogDebug("Reverting poll execution '{0}'.", poll); this.pollResultExecutor.RevertChange(poll.VotingData); poll.PollExecutedBlockData = null; this.pollsRepository.UpdatePoll(poll); } } byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]); if (rawVotingData == null) { this.logger.LogTrace("(-)[NO_VOTING_DATA]"); return; } List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData); votingDataList.Reverse(); lock (this.locker) { foreach (VotingData votingData in votingDataList) { // Poll that was finished in the block being disconnected. Poll targetPoll = this.polls.Single(x => x.VotingData == votingData); this.logger.LogDebug("Reverting poll voting in favor: '{0}'.", targetPoll); if (targetPoll.PollVotedInFavorBlockData == new HashHeightPair(chBlock.ChainedHeader)) { targetPoll.PollVotedInFavorBlockData = null; this.pollsRepository.UpdatePoll(targetPoll); } // Pub key of a fed member that created voting data. string fedMemberKeyHex = this.slotsManager.GetPubKeyForTimestamp(chBlock.Block.Header.Time).ToHex(); targetPoll.PubKeysHexVotedInFavor.Remove(fedMemberKeyHex); if (targetPoll.PubKeysHexVotedInFavor.Count == 0) { this.polls.Remove(targetPoll); this.pollsRepository.RemovePolls(targetPoll.Id); this.logger.LogDebug("Poll with Id {0} was removed.", targetPoll.Id); } } } }
public BlockDisconnected(ChainedHeaderBlock disconnectedBlock) { this.DisconnectedBlock = disconnectedBlock; this.Hash = this.DisconnectedBlock.ChainedHeader.HashBlock; this.Height = this.DisconnectedBlock.ChainedHeader.Height; }
public void BlockObserverShouldSendExtractedWithdrawalsToWithdrawalReceiver() { ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock; this.blockObserver.OnBlockReceived(chainedHeaderBlock); this.withdrawalReceiver.ReceivedWithAnyArgs(1).ReceiveWithdrawals(Arg.Is(this.extractedWithdrawals)); }
public BlockConnected(ChainedHeaderBlock connectedBlock) { this.ConnectedBlock = connectedBlock; this.Hash = this.ConnectedBlock.ChainedHeader.HashBlock; this.Height = this.ConnectedBlock.ChainedHeader.Height; }
public void BlockObserver_Should_Send_Block_Tip() { ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock; this.blockObserver.OnNext(chainedHeaderBlock); this.blockTipSender.ReceivedWithAnyArgs(1).SendBlockTipAsync(null); }
public void BlockObserver_Should_Extract_Withdrawals() { ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock; this.blockObserver.OnNext(chainedHeaderBlock); this.withdrawalExtractor.ReceivedWithAnyArgs(1).ExtractWithdrawalsFromBlock(null, 0); }
/// <summary> /// Adds a block to the saving queue. /// </summary> /// <param name="chainedHeaderBlock">The block and its chained header pair to be added to pending storage.</param> public void AddToPending(ChainedHeaderBlock chainedHeaderBlock) { this.logger.LogTrace("({0}:'{1}')", nameof(chainedHeaderBlock), chainedHeaderBlock.ChainedHeader); this.blocksQueue.Enqueue(chainedHeaderBlock); this.logger.LogTrace("(-)"); }
public void BlockObserver_Should_Send_Extracted_Withdrawals_To_WithdrawalReceiver() { ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock; this.blockObserver.OnNext(chainedHeaderBlock); this.withdrawalReceiver.ReceivedWithAnyArgs(1).ReceiveWithdrawals(Arg.Is(this.extractedWithdrawals)); }
/// <inheritdoc /> public void AddToPending(ChainedHeaderBlock chainedHeaderBlock) { lock (this.blocksCacheLock) { this.pendingBlocksCache.TryAdd(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock); } this.blocksQueue.Enqueue(chainedHeaderBlock); }
private Task ProcessBlockInternal(ChainedHeaderBlock chainedHeaderBlock, CancellationToken cancellationToken) { lock (this.txLock) { // No work to do if (this.ConsolidationTransactions == null) { return(Task.CompletedTask); } // If a consolidation transaction comes through, set it to SeenInBlock foreach (Transaction transaction in chainedHeaderBlock.Block.Transactions) { if (this.IsConsolidatingTransaction(transaction)) { ConsolidationTransaction inMemoryTransaction = this.GetInMemoryConsolidationTransaction(transaction); if (inMemoryTransaction != null) { this.logger.Debug("Saw condensing transaction {0}, updating status to SeenInBlock", transaction.GetHash()); inMemoryTransaction.Status = ConsolidationTransactionStatus.SeenInBlock; } } } // Need to check all the transactions that are partial are still valid in case of a reorg. List <ConsolidationTransaction> partials = this.ConsolidationTransactions .Where(x => x.Status == ConsolidationTransactionStatus.Partial || x.Status == ConsolidationTransactionStatus.FullySigned) .Take(5) // We don't actually need to validate all of them - just the next potential ones. .ToList(); foreach (ConsolidationTransaction cTransaction in partials) { if (!this.walletManager.ValidateConsolidatingTransaction(cTransaction.PartialTransaction)) { // If we find an invalid one, everything will need redoing! this.logger.Debug( "Consolidation transaction {0} failed validation, resetting InputConsolidator", cTransaction.PartialTransaction.GetHash()); this.ConsolidationTransactions = null; return(Task.CompletedTask); } } // If all of our consolidation inputs are SeenInBlock, we can move on! Yay if (this.ConsolidationTransactions.All(x => x.Status == ConsolidationTransactionStatus.SeenInBlock)) { this.ConsolidationTransactions = null; } } return(Task.CompletedTask); }
// This test requires retries because we are asserting result of a background thread that calls API. // This will work in real life but in tests it produces a race condition and therefore requires retries. public async Task BlockObserverShouldSendBlockTipAsync() { ChainedHeaderBlock chainedHeaderBlock = this.ChainHeaderBlockBuilder().chainedHeaderBlock; this.blockObserver.OnBlockReceived(chainedHeaderBlock); await Task.Delay(5000).ConfigureAwait(false); await this.federationGatewayClient.ReceivedWithAnyArgs(1).PushCurrentBlockTipAsync(null); }
public void SignalBlockDisconnectedToBlockSignaler() { Block block = KnownNetworks.StratisMain.CreateBlock(); ChainedHeader header = ChainedHeadersHelper.CreateGenesisChainedHeader(); var chainedHeaderBlock = new ChainedHeaderBlock(block, header); this.signals.SignalBlockDisconnected(chainedHeaderBlock); this.blockDisconnectedSignaler.Verify(b => b.Broadcast(chainedHeaderBlock), Times.Exactly(1)); }
private void OnBlockConnected(ChainedHeaderBlock chainedHeaderBlock) { ChainedHeader blockHeader = chainedHeaderBlock.ChainedHeader; Task task = this.RemoveForBlock(chainedHeaderBlock.Block, blockHeader?.Height ?? -1); // wait for the mempool code to complete // until the signaler becomes async task.GetAwaiter().GetResult(); }
/// <summary> /// Provides block data for the given block hashes. /// </summary> /// <remarks> /// First we check if the block exists in chained header tree, then it check the block store and if it wasn't found there the block will be scheduled for download. /// Given callback is called when the block is obtained. If obtaining the block fails the callback will be called with <c>null</c>. /// </remarks> /// <param name="blockHashes">The block hashes to download.</param> /// <param name="onBlockDownloadedCallback">The callback that will be called for each downloaded block.</param> public async Task GetOrDownloadBlocksAsync(List <uint256> blockHashes, OnBlockDownloadedCallback onBlockDownloadedCallback) { this.logger.LogTrace("({0}.{1}:{2})", nameof(blockHashes), nameof(blockHashes.Count), blockHashes.Count); var blocksToDownload = new List <ChainedHeader>(); for (int i = 0; i < blockHashes.Count; i++) { uint256 blockHash = blockHashes[i]; ChainedHeaderBlock chainedHeaderBlock = null; lock (this.peerLock) { chainedHeaderBlock = this.chainedHeaderTree.GetChainedHeaderBlock(blockHash); } if (chainedHeaderBlock == null) { this.logger.LogTrace("Block hash '{0}' is not part of the tree.", blockHash); onBlockDownloadedCallback(null); continue; } if (chainedHeaderBlock.Block != null) { this.logger.LogTrace("Block pair '{0}' was found in memory.", chainedHeaderBlock); onBlockDownloadedCallback(chainedHeaderBlock); continue; } if (this.blockStore != null) { Block block = await this.blockStore.GetBlockAsync(blockHash).ConfigureAwait(false); if (block != null) { var newBlockPair = new ChainedHeaderBlock(block, chainedHeaderBlock.ChainedHeader); this.logger.LogTrace("Chained header block '{0}' was found in store.", newBlockPair); onBlockDownloadedCallback(newBlockPair); continue; } } blocksToDownload.Add(chainedHeaderBlock.ChainedHeader); this.logger.LogTrace("Block hash '{0}' is queued for download.", blockHash); } if (blocksToDownload.Count != 0) { this.logger.LogTrace("Asking block puller for {0} blocks.", blocksToDownload.Count); this.DownloadBlocks(blocksToDownload.ToArray(), this.ProcessDownloadedBlock); } this.logger.LogTrace("(-)"); }
public Task <MerkleBlock> GetTxOutProofAsync(string[] txids, string blockhash = "") { List <uint256> transactionIds = txids.Select(txString => uint256.Parse(txString)).ToList(); ChainedHeaderBlock block = null; if (!string.IsNullOrEmpty(blockhash)) { // We presume that all the transactions are supposed to be contained in the same specified block, so we only retrieve it once. uint256 hashBlock = uint256.Parse(blockhash); block = this.consensusManager?.GetBlockData(hashBlock); } else { if (!(this.storeSettings?.TxIndex ?? false)) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "Cannot find block containing specified transactions if hash is not specified and -txindex is disabled"); } // Loop through txids and try to find which block they're in. Exit loop once a block is found, as they are supposed to all be in the same block. foreach (uint256 transactionId in transactionIds) { ChainedHeader chainedHeader = this.GetTransactionBlock(transactionId); if (chainedHeader?.BlockDataAvailability != null && chainedHeader.BlockDataAvailability == BlockDataAvailabilityState.BlockAvailable) { block = this.consensusManager?.GetBlockData(chainedHeader.HashBlock); break; } } } if (block == null) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "Block not found"); } // Need to be able to lookup the transactions within the block efficiently. HashSet <uint256> transactionMap = block.Block.Transactions.Select(t => t.GetHash()).ToHashSet(); // Loop through txids and verify that every transaction is in the block. foreach (uint256 transactionId in transactionIds) { if (!transactionMap.Contains(transactionId)) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_PARAMETER, "Not all transactions found in specified or retrieved block"); } } var result = new MerkleBlock(block.Block, transactionIds.ToArray()); return(Task.FromResult(result)); }
private ChainedHeaderBlock[] GetBlocks(int count, Func <int, ChainedHeaderBlock> block) { ChainedHeader previous = null; return(Enumerable.Range(0, count).Select(i => { ChainedHeaderBlock chainedHeaderBlock = block(i); chainedHeaderBlock.ChainedHeader.SetPrivatePropertyValue("Previous", previous); previous = chainedHeaderBlock.ChainedHeader; return chainedHeaderBlock; }).ToArray()); }
/// <inheritdoc /> public void StartPartialValidation(ChainedHeaderBlock chainedHeaderBlock, OnPartialValidationCompletedAsyncCallback onPartialValidationCompletedAsyncCallback) { this.logger.LogTrace("({0}:'{1}')", nameof(chainedHeaderBlock), chainedHeaderBlock); this.asyncQueue.Enqueue(new PartialValidationItem() { ChainedHeaderBlock = chainedHeaderBlock, PartialValidationCompletedAsyncCallback = onPartialValidationCompletedAsyncCallback }); this.logger.LogTrace("(-)"); }
public void OnNextCoreProcessesOnTheWalletSyncManager() { var walletSyncManager = new Mock <IWalletSyncManager>(); var observer = new BlockObserver(walletSyncManager.Object); Block block = KnownNetworks.StratisMain.CreateBlock(); ChainedHeader header = ChainedHeadersHelper.CreateGenesisChainedHeader(); var chainedHeaderBlock = new ChainedHeaderBlock(block, header); observer.OnNext(chainedHeaderBlock); walletSyncManager.Verify(w => w.ProcessBlock(block), Times.Exactly(1)); }
public void BlockDownloaded_NullBlock_CallsCallbacks() { TestContext builder = GetBuildTestContext(10, useCheckpoints: false, initializeWithChainTip: true); var additionalHeaders = builder.ExtendAChain(2, builder.InitialChainTip); var headerTree = builder.ChainedHeaderToList(additionalHeaders, 2); var peer = builder.GetNetworkPeerWithConnection(); var result = builder.TestConsensusManager.ConsensusManager.HeadersPresented(peer.Object, headerTree, true); Assert.NotNull(builder.blockPullerBlockDownloadCallback); builder.TestConsensusManager.SetExpectedBlockDataBytes(1000); var callback1Called = false; var callback2Called = false; var callback3Called = false; ChainedHeaderBlock calledWith1 = null; ChainedHeaderBlock calledWith2 = null; ChainedHeaderBlock calledWith3 = null; var callback1 = new Bitcoin.Consensus.OnBlockDownloadedCallback(d => { callback1Called = true; calledWith1 = d; }); var callback2 = new Bitcoin.Consensus.OnBlockDownloadedCallback(d => { callback2Called = true; calledWith2 = d; }); var callback3 = new Bitcoin.Consensus.OnBlockDownloadedCallback(d => { callback3Called = true; calledWith3 = d; }); builder.TestConsensusManager.SetupCallbackByBlocksRequestedHash(additionalHeaders.HashBlock, callback1, callback2); builder.TestConsensusManager.SetupCallbackByBlocksRequestedHash(additionalHeaders.Previous.HashBlock, callback3); // call for both blocks. var block = new Block(); block.ToBytes(); builder.blockPullerBlockDownloadCallback(additionalHeaders.Previous.HashBlock, block, peer.Object.Connection.Id); builder.blockPullerBlockDownloadCallback(additionalHeaders.HashBlock, block, peer.Object.Connection.Id); Assert.False(builder.TestConsensusManager.CallbacksByBlocksRequestedHashContainsKeyForHash(additionalHeaders.HashBlock)); Assert.False(builder.TestConsensusManager.CallbacksByBlocksRequestedHashContainsKeyForHash(additionalHeaders.Previous.HashBlock)); builder.AssertExpectedBlockSizesEmpty(); Assert.True(callback1Called); Assert.True(callback2Called); Assert.True(callback3Called); Assert.NotNull(calledWith1); Assert.Equal(additionalHeaders, calledWith1.ChainedHeader); Assert.NotNull(calledWith2); Assert.Equal(additionalHeaders, calledWith2.ChainedHeader); Assert.NotNull(calledWith3); Assert.Equal(additionalHeaders.Previous, calledWith3.ChainedHeader); }
/// <summary> Reconstructs voting and poll data from a given height.</summary> /// <param name="height">The height to start reconstructing from.</param> public void ReconstructVotingDataFromHeightLocked(int height) { try { this.isBusyReconstructing = true; var currentHeight = height; var progress = $"Reconstructing voting poll data from height {currentHeight}."; this.logger.Info(progress); this.signals.Publish(new RecontructFederationProgressEvent() { Progress = progress }); do { ChainedHeader chainedHeader = this.chainIndexer.GetHeader(currentHeight); if (chainedHeader == null) { break; } Block block = this.blockRepository.GetBlock(chainedHeader.HashBlock); if (block == null) { break; } var chainedHeaderBlock = new ChainedHeaderBlock(block, chainedHeader); this.idleFederationMembersKicker.UpdateFederationMembersLastActiveTime(chainedHeaderBlock, false); OnBlockConnected(new BlockConnected(chainedHeaderBlock)); currentHeight++; if (currentHeight % 10000 == 0) { progress = $"Reconstructing voting data at height {currentHeight}"; this.logger.Info(progress); this.signals.Publish(new RecontructFederationProgressEvent() { Progress = progress }); } } while (true); } finally { this.isBusyReconstructing = false; } }
/// <inheritdoc /> public void AddToPending(ChainedHeaderBlock chainedHeaderBlock) { this.logger.LogTrace("({0}:'{1}')", nameof(chainedHeaderBlock), chainedHeaderBlock.ChainedHeader); lock (this.getBlockLock) { this.pendingBlocks.TryAdd(chainedHeaderBlock.ChainedHeader.HashBlock, chainedHeaderBlock); } this.blocksQueue.Enqueue(chainedHeaderBlock); this.logger.LogTrace("(-)"); }
/// <inheritdoc /> /// <remarks>When a block is signaled, we check if its header is a Proven Header, if not, we need to generate and store it.</remarks> protected override void AddBlockToQueue(ChainedHeaderBlock blockPair, bool isIBD) { var blockHeight = blockPair.ChainedHeader.Height; if (blockPair.ChainedHeader.Header is ProvenBlockHeader phHeader) { this.logger.LogDebug("Current header is already a Proven Header."); // Add to the store, to be sure we actually store it anyway. // It's ProvenBlockHeaderStore responsibility to prevent us to store it twice. this.provenBlockHeaderStore.AddToPendingBatch(phHeader, new HashHeightPair(phHeader.GetHash(), blockHeight)); } else { // Ensure we doesn't have already the ProvenHeader in the store. var provenHeader = this.provenBlockHeaderStore.GetAsync(blockPair.ChainedHeader.Height).GetAwaiter() .GetResult(); // Proven Header not found? create it now. if (provenHeader == null) { this.logger.LogDebug("Proven Header at height {0} NOT found.", blockHeight); CreateAndStoreProvenHeader(blockHeight, blockPair, isIBD); } else { var signaledHeaderHash = blockPair.Block.Header.GetHash(); // If the Proven Header is the right one, then it's OK and we can return without doing anything. var provenHeaderHash = provenHeader.GetHash(); if (provenHeaderHash == signaledHeaderHash) { this.logger.LogDebug("Proven Header {0} found.", signaledHeaderHash); } else { this.logger.LogDebug( "Found a proven header with a different hash, recreating PH. Expected Hash: {0}, found Hash: {1}.", signaledHeaderHash, provenHeaderHash); // A reorg happened so we recreate a new Proven Header to replace the wrong one. CreateAndStoreProvenHeader(blockHeight, blockPair, isIBD); } } } // At the end, if no exception happened, control is passed back to base AddBlockToQueue. base.AddBlockToQueue(blockPair, isIBD); }
/// <summary> /// Ensures that any blocks queued in <see cref="blocksQueue"/> gets added to <see cref="batch"/> /// so that it can be persisted on dispose. /// </summary> private void FlushAllCollections() { ChainedHeaderBlock chainedHeaderBlock = null; while (this.blocksQueue.TryDequeue(out chainedHeaderBlock)) { this.batch.Add(chainedHeaderBlock); } if (this.batch.Count != 0) { this.SaveBatch(); } }