private Task RunLoop(CancellationToken cancellationToken) { try { var stack = new CoinViewStack(this.coinView); CachedCoinView cache = stack.Find <CachedCoinView>(); var stats = new ConsensusStats(stack, this.coinView, this.consensusLoop, this.chainState, this.chain, this.connectionManager, this.loggerFactory); ChainedBlock lastTip = this.consensusLoop.Tip; foreach (BlockResult block in this.consensusLoop.Execute(cancellationToken)) { if (this.consensusLoop.Tip.FindFork(lastTip) != lastTip) { this.logger.LogInformation("Reorg detected, rewinding from '{0}' to '{1}'.", lastTip.HashBlock, this.consensusLoop.Tip); } lastTip = this.consensusLoop.Tip; cancellationToken.ThrowIfCancellationRequested(); if (block.Error != null) { this.logger.LogError("Block rejected: {0}", block.Error.Message); // Pull again. this.consensusLoop.Puller.SetLocation(this.consensusLoop.Tip); if (block.Error == ConsensusErrors.BadWitnessNonceSize) { this.logger.LogInformation("You probably need witness information, activating witness requirement for peers."); this.connectionManager.AddDiscoveredNodesRequirement(NodeServices.NODE_WITNESS); this.consensusLoop.Puller.RequestOptions(TransactionOptions.Witness); continue; } // Set the PoW chain back to ConsensusLoop.Tip. this.chain.SetTip(this.consensusLoop.Tip); // Since ChainHeadersBehavior check PoW, MarkBlockInvalid can't be spammed. this.logger.LogError("Marking block as invalid."); this.chainState.MarkBlockInvalid(block.Block.GetHash()); } if (block.Error == null) { this.chainState.HighestValidatedPoW = this.consensusLoop.Tip; // We really want to flush if we are at the top of the chain. // Otherwise, we just allow the flush to happen if it is needed. bool forceFlush = this.chain.Tip.HashBlock == block.ChainedBlock?.HashBlock; this.consensusLoop.FlushAsync(forceFlush).GetAwaiter().GetResult(); this.signals.SignalBlock(block.Block); } // TODO: Replace this with a signalling object. if (stats.CanLog) { stats.Log(); } } } catch (Exception ex) { if (ex is OperationCanceledException) { if (this.nodeLifetime.ApplicationStopping.IsCancellationRequested) { return(Task.FromException(ex)); } } // TODO Need to revisit unhandled exceptions in a way that any process can signal an exception has been // thrown so that the node and all the disposables can stop gracefully. this.logger.LogDebug("Exception occurred in consensus loop: {0}", ex.ToString()); this.logger.LogCritical(new EventId(0), ex, "Consensus loop at Tip:{0} unhandled exception {1}", this.consensusLoop.Tip?.Height, ex.ToString()); NLog.LogManager.Flush(); throw; } return(Task.CompletedTask); }
private void RunLoop() { try { var stack = new CoinViewStack(this.coinView); var cache = stack.Find <CachedCoinView>(); var stats = new ConsensusStats(stack, this.coinView, this.consensusLoop, this.chainState, this.chain, this.connectionManager, this.loggerFactory); var cancellationToken = this.nodeLifetime.ApplicationStopping; ChainedBlock lastTip = this.consensusLoop.Tip; foreach (var block in this.consensusLoop.Execute(cancellationToken)) { if (this.consensusLoop.Tip.FindFork(lastTip) != lastTip) { this.logger.LogInformation("Reorg detected, rewinding from " + lastTip.Height + " (" + lastTip.HashBlock + ") to " + this.consensusLoop.Tip.Height + " (" + this.consensusLoop.Tip.HashBlock + ")"); } lastTip = this.consensusLoop.Tip; cancellationToken.ThrowIfCancellationRequested(); if (block.Error != null) { this.logger.LogError("Block rejected: " + block.Error.Message); //Pull again this.consensusLoop.Puller.SetLocation(this.consensusLoop.Tip); if (block.Error == ConsensusErrors.BadWitnessNonceSize) { this.logger.LogInformation("You probably need witness information, activating witness requirement for peers."); this.connectionManager.AddDiscoveredNodesRequirement(NodeServices.NODE_WITNESS); this.consensusLoop.Puller.RequestOptions(TransactionOptions.Witness); continue; } //Set the PoW chain back to ConsensusLoop.Tip this.chain.SetTip(this.consensusLoop.Tip); //Since ChainHeadersBehavior check PoW, MarkBlockInvalid can't be spammed this.logger.LogError("Marking block as invalid"); this.chainState.MarkBlockInvalid(block.Block.GetHash()); } if (block.Error == null) { this.chainState.HighestValidatedPoW = this.consensusLoop.Tip; if (this.chain.Tip.HashBlock == block.ChainedBlock?.HashBlock) { this.consensusLoop.FlushAsync(); } this.signals.SignalBlock(block.Block); } // TODO: replace this with a signalling object if (stats.CanLog) { stats.Log(); } } } catch (Exception ex) { if (ex is OperationCanceledException) { if (this.nodeLifetime.ApplicationStopping.IsCancellationRequested) { return; } } // TODO Need to revisit unhandled exceptions in a way that any process can signal an exception has been // thrown so that the node and all the disposables can stop gracefully. this.logger.LogCritical(new EventId(0), ex, "Consensus loop unhandled exception (Tip:" + this.consensusLoop.Tip?.Height + ")"); throw; } }