/// <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); }
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]); }); }
/// <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)); }
/// <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)); }
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); }
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); }
///<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); }