public ConsensusStats(
            CoinViewStack stack,
            CoinView coinView,
            ConsensusLoop consensusLoop,
            ChainState chainState,
            ConcurrentChain chain,
            IConnectionManager connectionManager,
            ILoggerFactory loggerFactory)
        {
            stack        = new CoinViewStack(coinView);
            this.cache   = stack.Find <CachedCoinView>();
            this.dbreeze = stack.Find <DBreezeCoinView>();
            this.bottom  = stack.Bottom;

            this.consensusLoop   = consensusLoop;
            this.lookaheadPuller = this.consensusLoop.Puller as LookaheadBlockPuller;

            this.lastSnapshot      = consensusLoop.Validator.PerformanceCounter.Snapshot();
            this.lastSnapshot2     = this.dbreeze?.PerformanceCounter.Snapshot();
            this.lastSnapshot3     = this.cache?.PerformanceCounter.Snapshot();
            this.chainState        = chainState;
            this.chain             = chain;
            this.connectionManager = connectionManager;
            this.logger            = loggerFactory.CreateLogger(this.GetType().FullName);
        }
        public ConsensusStats(
            CoinView coinView,
            IConsensusLoop consensusLoop,
            IInitialBlockDownloadState initialBlockDownloadState,
            ConcurrentChain chain,
            IConnectionManager connectionManager,
            IDateTimeProvider dateTimeProvider,
            ILoggerFactory loggerFactory)
        {
            CoinViewStack stack = new CoinViewStack(coinView);

            this.cache   = stack.Find <CachedCoinView>();
            this.dbreeze = stack.Find <DBreezeCoinView>();
            this.bottom  = stack.Bottom;

            this.consensusLoop   = consensusLoop;
            this.lookaheadPuller = this.consensusLoop.Puller as LookaheadBlockPuller;

            this.lastSnapshot              = consensusLoop.ConsensusRules.PerformanceCounter.Snapshot();
            this.lastSnapshot2             = this.dbreeze?.PerformanceCounter.Snapshot();
            this.lastSnapshot3             = this.cache?.PerformanceCounter.Snapshot();
            this.initialBlockDownloadState = initialBlockDownloadState;
            this.chain             = chain;
            this.connectionManager = connectionManager;
            this.dateTimeProvider  = dateTimeProvider;
            this.logger            = loggerFactory.CreateLogger(this.GetType().FullName);
        }
Example #3
0
        public ConsensusStats(
            ICoinView coinView,
            IConsensusManager consensusManager,
            IConsensusRuleEngine consensusRules,
            IInitialBlockDownloadState initialBlockDownloadState,
            ConcurrentChain chain,
            IConnectionManager connectionManager,
            IDateTimeProvider dateTimeProvider,
            IBlockPuller blockPuller,
            ILoggerFactory loggerFactory,
            INodeLifetime nodeLifetime)
        {
            var stack = new CoinViewStack(coinView);

            this.cache   = stack.Find <CachedCoinView>();
            this.dbreeze = stack.Find <DBreezeCoinView>();
            this.bottom  = stack.Bottom;

            this.consensusManager = consensusManager;
            this.consensusRules   = consensusRules;

            this.lastSnapshot              = consensusRules.PerformanceCounter.Snapshot();
            this.lastSnapshot2             = this.dbreeze?.PerformanceCounter.Snapshot();
            this.lastSnapshot3             = this.cache?.PerformanceCounter.Snapshot();
            this.initialBlockDownloadState = initialBlockDownloadState;
            this.chain             = chain;
            this.connectionManager = connectionManager;
            this.dateTimeProvider  = dateTimeProvider;
            this.blockPuller       = blockPuller;
            this.logger            = loggerFactory.CreateLogger(this.GetType().FullName);
            this.nodeLifetime      = nodeLifetime;
        }
Example #4
0
        public void ValidSomeBlocks()
        {
            using (NodeContext ctx = NodeContext.Create(network: Network.Main))
            {
                var network = ctx.Network;
                var chain   = new ConcurrentChain(network.GetGenesis().Header);
                if (network == NBitcoin.Network.Main)
                {
                    chain.Load(GetFile("main.data", "https://aois.blob.core.windows.net/public/main.data"));
                }
                else
                {
                    chain.Load(GetFile("test.data", "https://aois.blob.core.windows.net/public/test.data"));
                }

                var stack = new CoinViewStack(
                    new CacheCoinView(
                        new PrefetcherCoinView(
                            new BackgroundCommiterCoinView(
                                ctx.PersistentCoinView))));

                var cache = stack.Find <CacheCoinView>();
                var backgroundCommiter   = stack.Find <BackgroundCommiterCoinView>();
                ConsensusValidator valid = new ConsensusValidator(network.Consensus);
                valid.UseConsensusLib = false;
                Node node = Node.Connect(network, "yournode");
                node.VersionHandshake();
                var puller        = new CustomNodeBlockPuller(chain, node);
                var lastSnapshot  = valid.PerformanceCounter.Snapshot();
                var lastSnapshot2 = ctx.PersistentCoinView.PerformanceCounter.Snapshot();
                var lastSnapshot3 = cache.PerformanceCounter.Snapshot();
                foreach (var block in valid.Run(stack, puller))
                {
                    if ((DateTimeOffset.UtcNow - lastSnapshot.Taken) > TimeSpan.FromSeconds(5.0))
                    {
                        Console.WriteLine();

                        Console.WriteLine("ActualLookahead :\t" + puller.ActualLookahead + " blocks");
                        Console.WriteLine("Downloaded Count :\t" + puller.RollingAverageDownloadedCount + " blocks");
                        Console.WriteLine("CoinViewTip :\t" + backgroundCommiter.Tip.Height);
                        Console.WriteLine("CommitingTip :\t" + backgroundCommiter.CommitingTip.Height);
                        Console.WriteLine("InnerTip :\t" + backgroundCommiter.InnerTip.Height);
                        Console.WriteLine("Cache entries :\t" + cache.CacheEntryCount);

                        var snapshot = valid.PerformanceCounter.Snapshot();
                        Console.Write(snapshot - lastSnapshot);
                        lastSnapshot = snapshot;

                        var snapshot2 = ctx.PersistentCoinView.PerformanceCounter.Snapshot();
                        Console.Write(snapshot2 - lastSnapshot2);
                        lastSnapshot2 = snapshot2;

                        var snapshot3 = cache.PerformanceCounter.Snapshot();
                        Console.Write(snapshot3 - lastSnapshot3);
                        lastSnapshot3 = snapshot3;
                    }
                }
            }
        }
Example #5
0
        public void Constructor_CoinViewWithoutBackedCoinViews_SetsCoinViewAsTopAndBottom()
        {
            var coinView = new NonBackedCoinView();

            var stack = new CoinViewStack(coinView);

            Assert.True(stack.Top is NonBackedCoinView);
            Assert.True(stack.Bottom is NonBackedCoinView);
        }
Example #6
0
        public void Find_CoinViewNotFound_ReturnsNull()
        {
            var nonBackedCoinView = new NonBackedCoinView();

            var stack = new CoinViewStack(nonBackedCoinView);

            var coinView = stack.Find <BackedCoinView2>();

            Assert.Null(coinView);
        }
Example #7
0
        public void Constructor_CoinViewWithBackedCoinViews_SetsTopAndBottom()
        {
            var nonBackedCoinView = new NonBackedCoinView();
            var backedCoinView2   = new BackedCoinView2(nonBackedCoinView);
            var backedCoinView1   = new BackedCoinView1(backedCoinView2);

            var stack = new CoinViewStack(backedCoinView1);

            Assert.True(stack.Top is BackedCoinView1);
            Assert.True(stack.Bottom is NonBackedCoinView);
        }
Example #8
0
        public void Find_CoinViewWithinStack_ReturnsCoinView()
        {
            var nonBackedCoinView = new NonBackedCoinView();
            var backedCoinView2   = new BackedCoinView2(nonBackedCoinView, 3);
            var backedCoinView1   = new BackedCoinView1(backedCoinView2, 4);

            var stack = new CoinViewStack(backedCoinView1);

            var coinView = stack.Find <BackedCoinView2>();

            Assert.True(coinView is BackedCoinView2);
            Assert.Equal(3, coinView.OutputCount);
        }
Example #9
0
        public void GetElements_NullCoinViewWithinStack_ReturnsNonNullCoinViews()
        {
            var backedCoinView2 = new BackedCoinView2(null);
            var backedCoinView1 = new BackedCoinView1(backedCoinView2);

            var stack = new CoinViewStack(backedCoinView1);

            List <CoinView> coinViews = stack.GetElements().ToList();

            Assert.Equal(2, coinViews.Count);
            Assert.True(coinViews[0] is BackedCoinView1);
            Assert.True(coinViews[1] is BackedCoinView2);
        }
Example #10
0
        public void GetElements_CoinViewWithBackedCoinViews_ReturnsStack()
        {
            var nonBackedCoinView = new NonBackedCoinView();
            var backedCoinView2   = new BackedCoinView2(nonBackedCoinView);
            var backedCoinView1   = new BackedCoinView1(backedCoinView2);

            var stack = new CoinViewStack(backedCoinView1);

            List <CoinView> coinViews = stack.GetElements().ToList();

            Assert.Equal(3, coinViews.Count);
            Assert.True(coinViews[0] is BackedCoinView1);
            Assert.True(coinViews[1] is BackedCoinView2);
            Assert.True(coinViews[2] is NonBackedCoinView);
        }
            public ConsensusStats(FullNode fullNode, CoinViewStack stack)
            {
                this.fullNode = fullNode;

                stack   = new CoinViewStack(fullNode.CoinView);
                cache   = stack.Find <CachedCoinView>();
                dbreeze = stack.Find <DBreezeCoinView>();
                bottom  = stack.Bottom;

                lookaheadPuller = fullNode.ConsensusLoop.Puller as LookaheadBlockPuller;

                lastSnapshot  = fullNode.ConsensusLoop.Validator.PerformanceCounter.Snapshot();
                lastSnapshot2 = dbreeze?.PerformanceCounter.Snapshot();
                lastSnapshot3 = cache?.PerformanceCounter.Snapshot();
            }
        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;
            }
        }
Example #14
0
        public void ValidSomeBlocks()
        {
            using (NodeContext ctx = NodeContext.Create(network: Network.Main, clean: false))
            {
                var network = ctx.Network;
                var chain   = new ConcurrentChain(network.GetGenesis().Header);
                if (network == NBitcoin.Network.Main)
                {
                    chain.Load(GetFile("main.data", "https://aois.blob.core.windows.net/public/main.data"));
                }
                else
                {
                    chain.Load(GetFile("test.data", "https://aois.blob.core.windows.net/public/test.data"));
                }

                //var threads = new CustomThreadPoolTaskScheduler(10, 100, "Parallel Coin Fetcher");

                var stack = new CoinViewStack(
                    new CacheCoinView(
                        // PrefetcherCoinView(
                        //new ParallelCoinView(threads,
                        new BackgroundCommiterCoinView(
                            ctx.PersistentCoinView)));                    //);
                //new InMemoryCoinView(chain.Genesis));

                var bottom               = stack.Bottom;
                var cache                = stack.Find <CacheCoinView>();
                var backgroundCommiter   = stack.Find <BackgroundCommiterCoinView>();
                ConsensusValidator valid = new ConsensusValidator(network.Consensus);
                valid.UseConsensusLib = false;
                Node node = Node.Connect(network, "yournode");
                node.VersionHandshake();
                var puller        = new CustomNodeBlockPuller(chain, node);
                var lastSnapshot  = valid.PerformanceCounter.Snapshot();
                var lastSnapshot2 = ctx.PersistentCoinView.PerformanceCounter.Snapshot();
                var lastSnapshot3 = cache == null ? null : cache.PerformanceCounter.Snapshot();
                foreach (var block in valid.Run(stack, puller))
                {
                    if ((DateTimeOffset.UtcNow - lastSnapshot.Taken) > TimeSpan.FromSeconds(5.0))
                    {
                        Console.WriteLine();

                        Console.WriteLine("ActualLookahead :\t" + puller.ActualLookahead + " blocks");
                        Console.WriteLine("Median Downloaded :\t" + puller.MedianDownloadCount + " blocks");
                        if (backgroundCommiter != null)
                        {
                            Console.WriteLine("CoinViewTip :\t" + backgroundCommiter.Tip.Height);
                            Console.WriteLine("CommitingTip :\t" + backgroundCommiter.CommitingTip.Height);
                        }
                        Console.WriteLine("Bottom Tip :\t" + bottom.Tip.Height);
                        if (cache != null)
                        {
                            Console.WriteLine("Cache entries :\t" + cache.CacheEntryCount);
                        }

                        var snapshot = valid.PerformanceCounter.Snapshot();
                        Console.Write(snapshot - lastSnapshot);
                        lastSnapshot = snapshot;

                        var snapshot2 = ctx.PersistentCoinView.PerformanceCounter.Snapshot();
                        Console.Write(snapshot2 - lastSnapshot2);
                        lastSnapshot2 = snapshot2;
                        if (cache != null)
                        {
                            var snapshot3 = cache.PerformanceCounter.Snapshot();
                            Console.Write(snapshot3 - lastSnapshot3);
                            lastSnapshot3 = snapshot3;
                        }
                    }
                }
            }
        }
        void RunLoop()
        {
            try
            {
                var stack             = new CoinViewStack(CoinView);
                var cache             = stack.Find <CachedCoinView>();
                var stats             = new ConsensusStats(this, stack);
                var cancellationToken = this.GlobalCancellation.Cancellation.Token;

                ChainedBlock lastTip = ConsensusLoop.Tip;
                foreach (var block in ConsensusLoop.Execute(cancellationToken))
                {
                    bool reorg = false;
                    if (ConsensusLoop.Tip.FindFork(lastTip) != lastTip)
                    {
                        reorg = true;
                        Logs.FullNode.LogInformation("Reorg detected, rewinding from " + lastTip.Height + " (" + lastTip.HashBlock + ") to " + ConsensusLoop.Tip.Height + " (" + ConsensusLoop.Tip.HashBlock + ")");
                    }
                    lastTip = ConsensusLoop.Tip;
                    cancellationToken.ThrowIfCancellationRequested();
                    if (block.Error != null)
                    {
                        Logs.FullNode.LogError("Block rejected: " + block.Error.Message);

                        //Pull again
                        ConsensusLoop.Puller.SetLocation(ConsensusLoop.Tip);

                        if (block.Error == ConsensusErrors.BadWitnessNonceSize)
                        {
                            Logs.FullNode.LogInformation("You probably need witness information, activating witness requirement for peers.");
                            ConnectionManager.AddDiscoveredNodesRequirement(NodeServices.NODE_WITNESS);
                            ConsensusLoop.Puller.RequestOptions(TransactionOptions.Witness);
                            continue;
                        }

                        //Set the PoW chain back to ConsensusLoop.Tip
                        Chain.SetTip(ConsensusLoop.Tip);
                        //Since ChainBehavior check PoW, MarkBlockInvalid can't be spammed
                        Logs.FullNode.LogError("Marking block as invalid");
                        _ChainBehaviorState.MarkBlockInvalid(block.ChainedBlock.HashBlock);
                    }

                    if (!reorg && block.Error == null)
                    {
                        _ChainBehaviorState.HighestValidatedPoW = ConsensusLoop.Tip;
                        if (Chain.Tip.HashBlock == block.ChainedBlock?.HashBlock)
                        {
                            var unused = cache.FlushAsync();
                        }

                        this.Signals.Blocks.Broadcast(block.Block);
                    }

                    // TODO: replace this with a signalling object
                    if (stats.CanLog)
                    {
                        stats.Log();
                    }
                }
            }
            catch (Exception ex)             //TODO: Barbaric clean exit
            {
                if (ex is OperationCanceledException)
                {
                    if (this.GlobalCancellation.Cancellation.IsCancellationRequested)
                    {
                        return;
                    }
                }
                if (!IsDisposed)
                {
                    Logs.FullNode.LogCritical(new EventId(0), ex, "Consensus loop unhandled exception (Tip:" + ConsensusLoop.Tip?.Height + ")");
                    _UncatchedException = ex;
                    Dispose();
                }
            }
        }