/// <inheritdoc />
        public async Task ExecuteAsync(BlockValidationContext blockValidationContext)
        {
            Guard.NotNull(blockValidationContext, nameof(blockValidationContext));
            Guard.NotNull(blockValidationContext.RuleContext, nameof(blockValidationContext.RuleContext));

            try
            {
                var context = blockValidationContext.RuleContext;

                foreach (var consensusRule in this.consensusRules)
                {
                    var rule = consensusRule.Value;

                    if (context.SkipValidation && rule.ValidationOnlyRule)
                    {
                        this.logger.LogTrace("Rule {0} skipped for block at height {1}.", nameof(rule), blockValidationContext.ChainedBlock.Height);
                    }
                    else
                    {
                        await rule.RunAsync(context).ConfigureAwait(false);
                    }
                }
            }
            catch (ConsensusErrorException ex)
            {
                blockValidationContext.Error = ex.ConsensusError;
            }

            if (blockValidationContext.Error != null)
            {
                // TODO invoke the error handler rule.
            }
        }
        public void GenerateBlocks_SingleBlock_MaxTriesReached_StopsGeneratingBlocks_ReturnsEmptyList()
        {
            this.ExecuteUsingNonProofOfStakeSettings(() =>
            {
                BlockValidationContext callbackBlockValidationContext = null;
                this.consensusLoop.Setup(c => c.AcceptBlockAsync(It.IsAny <BlockValidationContext>())).Callback <BlockValidationContext>((context) =>
                {
                    context.ChainedHeader = new ChainedHeader(context.Block.Header, context.Block.GetHash(), this.chain.Tip);
                    this.chain.SetTip(context.ChainedHeader);
                    callbackBlockValidationContext = context;
                }).Returns(Task.CompletedTask);

                BlockTemplate blockTemplate      = this.CreateBlockTemplate(this.fixture.Block1);
                blockTemplate.Block.Header.Nonce = 0;
                blockTemplate.Block.Header.Bits  = Network.TestNet.GetGenesis().Header.Bits; // make the difficulty harder.

                this.chain.SetTip(this.chain.GetBlock(0));

                var blockBuilder = this.CreateProofOfWorkBlockBuilder();
                blockBuilder.Setup(b => b.Build(It.IsAny <ChainedHeader>(), It.Is <Script>(r => r == this.fixture.ReserveScript.ReserveFullNodeScript))).Returns(blockTemplate);

                var miner       = CreateProofOfWorkMiner(blockBuilder.Object);
                var blockHashes = miner.GenerateBlocks(this.fixture.ReserveScript, 1, 15);

                Assert.Empty(blockHashes);
            });
        }
        public void GenerateBlocks_SingleBlock_ValidationContextError_ReturnsEmptyList()
        {
            this.ExecuteUsingNonProofOfStakeSettings(() =>
            {
                BlockValidationContext callbackBlockValidationContext = null;

                this.consensusLoop.Setup(c => c.AcceptBlockAsync(It.IsAny <BlockValidationContext>()))
                .Callback <BlockValidationContext>((context) =>
                {
                    context.ChainedHeader = new ChainedHeader(context.Block.Header, context.Block.GetHash(), this.chain.Tip);
                    this.chain.SetTip(context.ChainedHeader);
                    context.Error = ConsensusErrors.BadMerkleRoot;
                    callbackBlockValidationContext = context;
                })
                .Returns(Task.CompletedTask);

                BlockTemplate blockTemplate = this.CreateBlockTemplate(this.fixture.Block1);

                this.chain.SetTip(this.chain.GetBlock(0));

                var blockBuilder = this.CreateProofOfWorkBlockBuilder();
                blockBuilder.Setup(b => b.Build(It.IsAny <ChainedHeader>(), It.Is <Script>(r => r == this.fixture.ReserveScript.ReserveFullNodeScript))).Returns(blockTemplate);

                var miner       = this.CreateProofOfWorkMiner(blockBuilder.Object);
                var blockHashes = miner.GenerateBlocks(this.fixture.ReserveScript, 1, uint.MaxValue);

                Assert.Empty(blockHashes);
            });
        }
        public async Task ExecuteAsync_RuleCanSkipValidation_ContextCanSkipValidation_DoesNotRunRuleAsync()
        {
            var rule = new ConsensusRuleWithSkipValidationAttribute();

            this.ruleRegistrations = new List <ConsensusRule> {
                rule
            };

            var blockValidationContext = new BlockValidationContext()
            {
                ChainedBlock = this.concurrentChain.Tip,
                RuleContext  = new RuleContext()
                {
                    SkipValidation = true
                }
            };
            var consensusRules = InitializeConsensusRules();

            consensusRules.Register(this.ruleRegistration.Object);

            await consensusRules.ExecuteAsync(blockValidationContext);

            Assert.False(rule.RunCalled);
            Assert.Null(blockValidationContext.Error);
        }
        public async Task ExecuteAsync_ConsensusErrorException_SetsConsensusErrorOnBlockValidationContextAsync()
        {
            var consensusError = ConsensusErrors.BadBlockLength;
            var rule           = new Mock <ConsensusRule>();

            rule.Setup(r => r.RunAsync(It.Is <RuleContext>(c => c.SkipValidation == false)))
            .Throws(new ConsensusErrorException(consensusError))
            .Verifiable();

            this.ruleRegistrations = new List <ConsensusRule> {
                rule.Object
            };
            var blockValidationContext = new BlockValidationContext()
            {
                RuleContext = new RuleContext()
                {
                    SkipValidation = false
                }
            };
            var consensusRules = InitializeConsensusRules();

            consensusRules.Register(this.ruleRegistration.Object);

            await consensusRules.ExecuteAsync(blockValidationContext);

            Assert.NotNull(blockValidationContext.Error);
            Assert.Equal(consensusError.Message, blockValidationContext.Error.Message);
            Assert.Equal(consensusError.Code, blockValidationContext.Error.Code);
        }
Beispiel #6
0
        public void GenerateBlocks_SingleBlock_BlockValidationContextErrorInvalidPrevTip_ContinuesExecution_ReturnsGeneratedBlock()
        {
            this.ExecuteUsingNonProofOfStakeSettings(() =>
            {
                BlockValidationContext callbackBlockValidationContext = null;
                ConsensusError lastError = null;
                this.consensusLoop.Setup(c => c.AcceptBlockAsync(It.IsAny <BlockValidationContext>()))
                .Callback <BlockValidationContext>((context) =>
                {
                    context.ChainedBlock = new ChainedBlock(context.Block.Header, context.Block.GetHash(), this.chain.Tip);
                    if (lastError == null)
                    {
                        context.Error = ConsensusErrors.InvalidPrevTip;
                        lastError     = context.Error;
                    }
                    else if (lastError != null)
                    {
                        this.chain.SetTip(context.ChainedBlock);
                    }
                    callbackBlockValidationContext = context;
                })
                .Returns(Task.CompletedTask);

                BlockTemplate blockTemplate = CreateBlockTemplate(this.fixture.Block1);
                this.blockAssembler.Setup(b => b.CreateNewBlock(It.Is <Script>(r => r == this.fixture.ReserveScript.ReserveFullNodeScript), true))
                .Returns(blockTemplate);

                var blockHashes = this.powMining.GenerateBlocks(this.fixture.ReserveScript, 1, uint.MaxValue);

                Assert.NotEmpty(blockHashes);
                Assert.True(blockHashes.Count == 1);
                Assert.Equal(callbackBlockValidationContext.Block.GetHash(), blockHashes[0]);
            });
        }
Beispiel #7
0
        /// <inheritdoc />
        public async Task ExectueAsync(BlockValidationContext blockValidationContext)
        {
            try
            {
                var context = new ContextInformation(blockValidationContext, this.ConsensusParams);
                blockValidationContext.Context = context;

                foreach (var consensusRule in this.consensusRules)
                {
                    var rule = consensusRule.Value;

                    if (context.BlockValidationContext.SkipValidation && rule.CanSkipValidation)
                    {
                        this.logger.LogTrace("Rule {0} skipped for block at height {1}.", nameof(rule), context.BlockValidationContext.ChainedBlock.Height);
                    }
                    else
                    {
                        await rule.RunAsync(context).ConfigureAwait(false);
                    }
                }
            }
            catch (ConsensusErrorException ex)
            {
                blockValidationContext.Error = ex.ConsensusError;
            }

            if (blockValidationContext.Error != null)
            {
                // TODO invoke the error handler rule.
            }
        }
        private static async Task ValidateBlock(TestChainContext testChainContext, BlockTemplate newBlock)
        {
            var context = new BlockValidationContext {
                Block = newBlock.Block
            };
            await testChainContext.Consensus.AcceptBlockAsync(context);

            Assert.Null(context.Error);
        }
        /// <summary>
        /// Mine new blocks in to the consensus database and the chain.
        /// </summary>
        public static async Task <List <Block> > MineBlocksAsync(TestChainContext testChainContext, int count, Script scriptPubKey)
        {
            BlockPolicyEstimator blockPolicyEstimator = new BlockPolicyEstimator(new MempoolSettings(testChainContext.NodeSettings), testChainContext.LoggerFactory, testChainContext.NodeSettings);
            TxMempool            mempool     = new TxMempool(testChainContext.DateTimeProvider, blockPolicyEstimator, testChainContext.LoggerFactory, testChainContext.NodeSettings);
            MempoolSchedulerLock mempoolLock = new MempoolSchedulerLock();

            // Simple block creation, nothing special yet:

            List <Block> blocks = new List <Block>();

            for (int i = 0; i < count; ++i)
            {
                PowBlockAssembler blockAssembler = CreatePowBlockAssembler(testChainContext.Network, testChainContext.Consensus, testChainContext.Chain, mempoolLock, mempool, testChainContext.DateTimeProvider, testChainContext.LoggerFactory as LoggerFactory);

                BlockTemplate newBlock = blockAssembler.CreateNewBlock(scriptPubKey);

                int         nHeight    = testChainContext.Chain.Tip.Height + 1; // Height first in coinbase required for block.version=2
                Transaction txCoinbase = newBlock.Block.Transactions[0];
                txCoinbase.Inputs[0] = TxIn.CreateCoinbase(nHeight);
                newBlock.Block.UpdateMerkleRoot();

                var maxTries = int.MaxValue;

                while (maxTries > 0 && !newBlock.Block.CheckProofOfWork())
                {
                    ++newBlock.Block.Header.Nonce;
                    --maxTries;
                }

                if (maxTries == 0)
                {
                    throw new XunitException("Test failed no blocks found");
                }

                var context = new BlockValidationContext {
                    Block = newBlock.Block
                };
                await testChainContext.Consensus.AcceptBlockAsync(context);

                Assert.Null(context.Error);

                blocks.Add(newBlock.Block);
            }

            return(blocks);
        }
        private async Task NodeIsSynced_PeerSendsABadBlockAndErrorIsNotBanError_ThePeerIsNotBanned_Async(Func <TestChainContext, Task <Block> > createBadBlock)
        {
            var(context, peerEndPoint) = await this.InitialiseContextAndPeerEndpointAsync();

            MockPeerConnection(context, false);
            var badBlock = await createBadBlock(context);

            var blockValidationContext = new BlockValidationContext
            {
                Block = badBlock,
                Peer  = peerEndPoint,
                BanDurationSeconds = BlockValidationContext.BanDurationNoBan
            };

            await context.Consensus.AcceptBlockAsync(blockValidationContext);

            Assert.False(context.PeerBanning.IsBanned(peerEndPoint));
        }
Beispiel #11
0
        /// <inheritdoc/>
        public async Task AcceptBlockAsync(BlockValidationContext blockValidationContext)
        {
            Guard.NotNull(blockValidationContext, nameof(blockValidationContext));
            Guard.NotNull(blockValidationContext.RuleContext, nameof(blockValidationContext.RuleContext));

            try
            {
                await this.ValidateAndExecuteAsync(blockValidationContext.RuleContext);
            }
            catch (ConsensusErrorException ex)
            {
                blockValidationContext.Error = ex.ConsensusError;
            }

            if (blockValidationContext.Error != null)
            {
                // TODO invoke the error handler rule.
            }
        }
        private async Task NodeIsSynced_PeerSendsABadBlockAndPeerIsBannedAndBanIsExpired_ThePeerIsNotBanned_Async(Func <TestChainContext, Task <Block> > createBadBlock)
        {
            var(context, peerEndPoint) = await this.InitialiseContextAndPeerEndpointAsync();

            MockPeerConnection(context, false);
            var badBlock = await createBadBlock(context);

            var blockValidationContext = new BlockValidationContext
            {
                Block = badBlock,
                Peer  = peerEndPoint,
                BanDurationSeconds = 1,
            };

            await context.Consensus.AcceptBlockAsync(blockValidationContext);

            // wait 1 sec for ban to expire.
            Thread.Sleep(1000);

            Assert.False(context.PeerBanning.IsBanned(peerEndPoint));
        }
Beispiel #13
0
        public void GenerateBlocks_SingleBlock_ChainedBlockNotPresentInBlockValidationContext_ReturnsEmptyList()
        {
            this.ExecuteUsingNonProofOfStakeSettings(() =>
            {
                BlockValidationContext callbackBlockValidationContext = null;
                this.consensusLoop.Setup(c => c.AcceptBlockAsync(It.IsAny <BlockValidationContext>()))
                .Callback <BlockValidationContext>((context) =>
                {
                    context.ChainedBlock           = null;
                    callbackBlockValidationContext = context;
                })
                .Returns(Task.CompletedTask);

                BlockTemplate blockTemplate = CreateBlockTemplate(this.fixture.Block1);
                this.blockAssembler.Setup(b => b.CreateNewBlock(It.Is <Script>(r => r == this.fixture.ReserveScript.ReserveFullNodeScript), true))
                .Returns(blockTemplate);

                var blockHashes = this.powMining.GenerateBlocks(this.fixture.ReserveScript, 1, uint.MaxValue);

                Assert.Empty(blockHashes);
            });
        }
        public async Task ExecuteAsync_RuleCannotSkipValidation_ContextCannotSkipValidation_RunsRuleAsync()
        {
            var rule = new ConsensusRuleWithValidationAttribute();

            this.ruleRegistrations = new List <ConsensusRule> {
                rule
            };
            var blockValidationContext = new BlockValidationContext()
            {
                RuleContext = new RuleContext()
                {
                    SkipValidation = false
                }
            };
            var consensusRules = InitializeConsensusRules();

            consensusRules.Register(this.ruleRegistration.Object);

            await consensusRules.ExecuteAsync(blockValidationContext);

            Assert.True(rule.RunCalled);
            Assert.Null(blockValidationContext.Error);
        }
Beispiel #15
0
        public void GenerateBlocks_SingleBlock_MaxTriesReached_StopsGeneratingBlocks_ReturnsEmptyList()
        {
            this.ExecuteUsingNonProofOfStakeSettings(() =>
            {
                BlockValidationContext callbackBlockValidationContext = null;
                this.consensusLoop.Setup(c => c.AcceptBlockAsync(It.IsAny <BlockValidationContext>()))
                .Callback <BlockValidationContext>((context) =>
                {
                    context.ChainedBlock = new ChainedBlock(context.Block.Header, context.Block.GetHash(), this.chain.Tip);
                    this.chain.SetTip(context.ChainedBlock);
                    callbackBlockValidationContext = context;
                })
                .Returns(Task.CompletedTask);

                BlockTemplate blockTemplate      = CreateBlockTemplate(this.fixture.Block1);
                blockTemplate.Block.Header.Nonce = 0;
                this.blockAssembler.Setup(b => b.CreateNewBlock(It.Is <Script>(r => r == this.fixture.ReserveScript.ReserveFullNodeScript), true))
                .Returns(blockTemplate);

                var blockHashes = this.powMining.GenerateBlocks(this.fixture.ReserveScript, 1, 15);

                Assert.Empty(blockHashes);
            });
        }
        /// <summary>
        /// Generates up to a specified number of blocks with a limited number of attempts.
        /// </summary>
        /// <param name="reserveScript"></param>
        /// <param name="generate">Number of blocks to generate. It is possible that less than the required number of blocks will be mined.</param>
        /// <param name="maxTries">Maximum number of attempts the miner will calculate PoW hash in order to find suitable ones to generate specified amount of blocks.</param>
        /// <returns>List with generated block's hashes</returns>
        public List <uint256> GenerateBlocks(ReserveScript reserveScript, ulong generate, ulong maxTries)
        {
            ulong nHeightStart = 0;
            ulong nHeightEnd   = 0;
            ulong nHeight      = 0;

            nHeightStart = (ulong)this.chain.Height;
            nHeight      = nHeightStart;
            nHeightEnd   = nHeightStart + generate;
            int nExtraNonce = 0;
            var blocks      = new List <uint256>();

            while (nHeight < nHeightEnd)
            {
                this.nodeLifetime.ApplicationStopping.ThrowIfCancellationRequested();

                ChainedBlock chainTip = this.consensusLoop.Tip;
                if (this.chain.Tip != chainTip)
                {
                    Task.Delay(TimeSpan.FromMinutes(1), this.nodeLifetime.ApplicationStopping).GetAwaiter().GetResult();
                    continue;
                }

                BlockTemplate pblockTemplate = this.blockAssemblerFactory.Create(chainTip).CreateNewBlock(reserveScript.reserveSfullNodecript);

                if (Block.BlockSignature)
                {
                    // Make sure the POS consensus rules are valid. This is required for generation of blocks inside tests,
                    // where it is possible to generate multiple blocks within one second.
                    if (pblockTemplate.Block.Header.Time <= chainTip.Header.Time)
                    {
                        continue;
                    }
                }

                this.IncrementExtraNonce(pblockTemplate.Block, chainTip, nExtraNonce);
                Block pblock = pblockTemplate.Block;

                while ((maxTries > 0) && (pblock.Header.Nonce < InnerLoopCount) && !pblock.CheckProofOfWork())
                {
                    this.nodeLifetime.ApplicationStopping.ThrowIfCancellationRequested();

                    ++pblock.Header.Nonce;
                    --maxTries;
                }

                if (maxTries == 0)
                {
                    break;
                }

                if (pblock.Header.Nonce == InnerLoopCount)
                {
                    continue;
                }

                var newChain = new ChainedBlock(pblock.Header, pblock.GetHash(), chainTip);

                if (newChain.ChainWork <= chainTip.ChainWork)
                {
                    continue;
                }

                var blockValidationContext = new BlockValidationContext {
                    Block = pblock
                };

                this.consensusLoop.AcceptBlockAsync(blockValidationContext).GetAwaiter().GetResult();

                if (blockValidationContext.ChainedBlock == null)
                {
                    this.logger.LogTrace("(-)[REORG-2]");
                    return(blocks);
                }

                if (blockValidationContext.Error != null)
                {
                    if (blockValidationContext.Error == ConsensusErrors.InvalidPrevTip)
                    {
                        continue;
                    }

                    this.logger.LogTrace("(-)[ACCEPT_BLOCK_ERROR]");
                    return(blocks);
                }

                this.logger.LogInformation("Mined new {0} block: '{1}'.", BlockStake.IsProofOfStake(blockValidationContext.Block) ? "POS" : "POW", blockValidationContext.ChainedBlock);

                nHeight++;
                blocks.Add(pblock.GetHash());

                pblockTemplate = null;
            }

            return(blocks);
        }
Beispiel #17
0
        ///<inheritdoc/>
        public List <uint256> GenerateBlocks(ReserveScript reserveScript, ulong generate, ulong maxTries)
        {
            ulong nHeightStart = 0;
            ulong nHeightEnd   = 0;
            ulong nHeight      = 0;

            nHeightStart = (ulong)this.chain.Height;
            nHeight      = nHeightStart;
            nHeightEnd   = nHeightStart + generate;
            int nExtraNonce = 0;
            var blocks      = new List <uint256>();

            while (nHeight < nHeightEnd)
            {
                this.nodeLifetime.ApplicationStopping.ThrowIfCancellationRequested();

                ChainedBlock chainTip = this.consensusLoop.Tip;
                if (this.chain.Tip != chainTip)
                {
                    Task.Delay(TimeSpan.FromMinutes(1), this.nodeLifetime.ApplicationStopping).GetAwaiter().GetResult();
                    continue;
                }

                BlockTemplate pblockTemplate = this.blockAssemblerFactory.Create(chainTip).CreateNewBlock(reserveScript.reserveSfullNodecript);

                if (this.network.NetworkOptions.IsProofOfStake)
                {
                    // Make sure the POS consensus rules are valid. This is required for generation of blocks inside tests,
                    // where it is possible to generate multiple blocks within one second.
                    if (pblockTemplate.Block.Header.Time <= chainTip.Header.Time)
                    {
                        continue;
                    }
                }

                this.IncrementExtraNonce(pblockTemplate.Block, chainTip, nExtraNonce);
                Block pblock = pblockTemplate.Block;

                var maxTries1 = (int)maxTries;

                var options = new ParallelOptions
                {
                    MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * this.minerSettings.MineCpuPercentage) * 1.0))
                };

                try
                {
                    Parallel.ForEach(Enumerable.Range(0, InnerLoopCount), options, (i, state) =>
                    {
                        if (state.IsStopped || state.ShouldExitCurrentIteration)
                        {
                            return;
                        }

                        if (this.nodeLifetime.ApplicationStopping.IsCancellationRequested)
                        {
                            state.Break();
                            throw new OperationCanceledException();
                        }

                        ulong tries = (ulong)maxTries1;
                        if (tries == 0)
                        {
                            maxTries = (uint)tries;
                            state.Break();
                        }

                        if (i == InnerLoopCount)
                        {
                            pblock.Header.Nonce = InnerLoopCount;
                            state.Break();
                        }

                        Interlocked.Decrement(ref maxTries1);

                        BlockHeader header = pblock.Header.Clone();
                        header.Nonce       = (uint)i;

                        if (!header.CheckProofOfWork(this.network.Consensus))
                        {
                            return;
                        }

                        pblock.Header.Nonce = (uint)i;
                        maxTries            = tries;
                        state.Break();
                    });
                }
                catch (AggregateException ex)
                {
                    if (ex.InnerExceptions.All(e => e is OperationCanceledException))
                    {
                        throw ex.InnerExceptions.First();
                    }

                    throw ex;
                }

                if (maxTries == 0)
                {
                    break;
                }

                if (pblock.Header.Nonce == InnerLoopCount)
                {
                    continue;
                }

                var newChain = new ChainedBlock(pblock.Header, pblock.GetHash(), chainTip);

                if (newChain.ChainWork <= chainTip.ChainWork)
                {
                    continue;
                }

                var blockValidationContext = new BlockValidationContext {
                    Block = pblock
                };

                this.consensusLoop.AcceptBlockAsync(blockValidationContext).GetAwaiter().GetResult();

                if (blockValidationContext.ChainedBlock == null)
                {
                    this.logger.LogTrace("(-)[REORG-2]");
                    return(blocks);
                }

                if (blockValidationContext.Error != null)
                {
                    if (blockValidationContext.Error == ConsensusErrors.InvalidPrevTip)
                    {
                        continue;
                    }

                    this.logger.LogTrace("(-)[ACCEPT_BLOCK_ERROR]");
                    return(blocks);
                }

                this.logger.LogInformation("Mined new {0} block: '{1}'.", BlockStake.IsProofOfStake(blockValidationContext.Block) ? "POS" : "POW", blockValidationContext.ChainedBlock);

                nHeight++;
                blocks.Add(pblock.GetHash());

                pblockTemplate = null;
            }

            return(blocks);
        }