internal Task Notify(CancellationToken token) { // Not syncing until the StartHash has been set. if (this.StartHash == null) { return(Task.CompletedTask); } // Not syncing until the chain is downloaded at least up to this block. ChainedHeader startBlock = this.Chain.GetBlock(this.StartHash); if (startBlock == null) { return(Task.CompletedTask); } // Sets the location of the puller to the block preceding the one we want to receive. ChainedHeader previousBlock = this.Chain.GetBlock(startBlock.Height > 0 ? startBlock.Height - 1 : 0); this.Puller.SetLocation(previousBlock); this.tip = previousBlock; this.logger.LogTrace("Puller location set to block: {0}.", previousBlock); // Send notifications for all the following blocks. while (!this.ReSync) { token.ThrowIfCancellationRequested(); LookaheadResult lookaheadResult = this.Puller.NextBlock(token); if (lookaheadResult.Block != null) { // Broadcast the block to the registered observers. this.signals.SignalBlockConnected(lookaheadResult.Block); this.tip = this.Chain.GetBlock(lookaheadResult.Block.GetHash()); continue; } // In reorg we reset the puller to the fork. // When a reorg happens the puller is pushed back and continues from the current fork. // Find the location of the fork. while (this.Chain.GetBlock(this.tip.HashBlock) == null) { this.tip = this.tip.Previous; } // Set the puller to the fork location. this.Puller.SetLocation(this.tip); } this.ReSync = false; return(Task.CompletedTask); }
/// <summary> /// A puller method that will continuously loop and ask for the next block in the chain from peers. /// The block will then be passed to the consensus validation. /// </summary> /// <remarks> /// If the <see cref="Block"/> returned from the puller is <c>null</c> that means the puller is signaling a reorg was detected. /// In this case a rewind of the <see cref="CoinView"/> db will be triggered to roll back consensus until a block is found that is in the best chain. /// </remarks> private async Task PullerLoopAsync() { this.logger.LogTrace("()"); while (!this.nodeLifetime.ApplicationStopping.IsCancellationRequested) { BlockValidationContext blockValidationContext = new BlockValidationContext(); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockFetchingTime(o))) { // Save the current consensus tip to later check if it changed. ChainedBlock consensusTip = this.Tip; this.logger.LogTrace("Asking block puller to deliver next block."); // This method will block until the next block is downloaded. LookaheadResult lookaheadResult = this.Puller.NextBlock(this.nodeLifetime.ApplicationStopping); if (lookaheadResult.Block == null) { using (await this.consensusLock.LockAsync(this.nodeLifetime.ApplicationStopping).ConfigureAwait(false)) { this.logger.LogTrace("No block received from puller due to reorganization."); if (!consensusTip.Equals(this.Tip)) { this.logger.LogTrace("Consensus tip changed from '{0}' to '{1}', no rewinding.", consensusTip, this.Tip); continue; } this.logger.LogTrace("Rewinding."); await this.RewindCoinViewLockedAsync().ConfigureAwait(false); continue; } } blockValidationContext.Block = lookaheadResult.Block; blockValidationContext.Peer = lookaheadResult.Peer; } this.logger.LogTrace("Block received from puller."); await this.AcceptBlockAsync(blockValidationContext).ConfigureAwait(false); } this.logger.LogTrace("(-)"); }