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;
            }
        }