/// <summary> /// Validates a block using the consensus rules. /// </summary> public void ValidateBlock(ContextInformation context) { this.logger.LogTrace("()"); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { // Check that the current block has not been reorged. // Catching a reorg at this point will not require a rewind. if (context.BlockValidationContext.Block.Header.HashPrevBlock != this.Tip.HashBlock) { this.logger.LogTrace("Reorganization detected."); ConsensusErrors.InvalidPrevTip.Throw(); } this.logger.LogTrace("Validating new block."); // Build the next block in the chain of headers. The chain header is most likely already created by // one of the peers so after we create a new chained block (mainly for validation) // we ask the chain headers for its version (also to prevent memory leaks). context.BlockValidationContext.ChainedBlock = new ChainedBlock(context.BlockValidationContext.Block.Header, context.BlockValidationContext.Block.Header.GetHash(), this.Tip); // Liberate from memory the block created above if possible. context.BlockValidationContext.ChainedBlock = this.Chain.GetBlock(context.BlockValidationContext.ChainedBlock.HashBlock) ?? context.BlockValidationContext.ChainedBlock; context.SetBestBlock(this.dateTimeProvider.GetTimeOffset()); // == Validation flow == // Check the block header is correct. this.Validator.CheckBlockHeader(context); this.Validator.ContextualCheckBlockHeader(context); // Calculate the consensus flags and check they are valid. context.Flags = this.NodeDeployments.GetFlags(context.BlockValidationContext.ChainedBlock); int lastCheckpointHeight = this.checkpoints.GetLastCheckpointHeight(); if (context.BlockValidationContext.ChainedBlock.Height > lastCheckpointHeight) { this.Validator.ContextualCheckBlock(context); // Check the block itself. this.Validator.CheckBlock(context); } else { this.logger.LogTrace("Block validation partially skipped because block height {0} is not greater than last checkpointed block height {1}.", context.BlockValidationContext.ChainedBlock.Height, lastCheckpointHeight); } } this.logger.LogTrace("(-)[OK]"); }
public void AcceptBlock(ContextInformation context) { this.logger.LogTrace("()"); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { // Check that the current block has not been reorged. // Catching a reorg at this point will not require a rewind. if (context.BlockResult.Block.Header.HashPrevBlock != this.Tip.HashBlock) { this.logger.LogTrace("Reorganization detected."); ConsensusErrors.InvalidPrevTip.Throw(); // reorg } this.logger.LogTrace("Validating new block."); // Build the next block in the chain of headers. The chain header is most likely already created by // one of the peers so after we create a new chained block (mainly for validation) // we ask the chain headers for its version (also to prevent memory leaks). context.BlockResult.ChainedBlock = new ChainedBlock(context.BlockResult.Block.Header, context.BlockResult.Block.Header.GetHash(), this.Tip); // Liberate from memory the block created above if possible. context.BlockResult.ChainedBlock = this.Chain.GetBlock(context.BlockResult.ChainedBlock.HashBlock) ?? context.BlockResult.ChainedBlock; context.SetBestBlock(); // == validation flow == // Check the block header is correct. this.Validator.CheckBlockHeader(context); this.Validator.ContextualCheckBlockHeader(context); // Calculate the consensus flags and check they are valid. context.Flags = this.NodeDeployments.GetFlags(context.BlockResult.ChainedBlock); this.Validator.ContextualCheckBlock(context); // check the block itself this.Validator.CheckBlock(context); } if (context.OnlyCheck) { this.logger.LogTrace("(-)[CHECK_ONLY]"); return; } // Load the UTXO set of the current block. UTXO may be loaded from cache or from disk. // The UTXO set is stored in the context. this.logger.LogTrace("Loading UTXO set of the new block."); context.Set = new UnspentOutputSet(); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddUTXOFetchingTime(o))) { uint256[] ids = GetIdsToFetch(context.BlockResult.Block, context.Flags.EnforceBIP30); FetchCoinsResponse coins = this.UTXOSet.FetchCoinsAsync(ids).GetAwaiter().GetResult(); context.Set.SetCoins(coins.UnspentOutputs); } // Attempt to load into the cache the next set of UTXO to be validated. // The task is not awaited so will not stall main validation process. this.TryPrefetchAsync(context.Flags); // Validate the UTXO set is correctly spent. this.logger.LogTrace("Executing block."); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { this.Validator.ExecuteBlock(context, null); } // Persist the changes to the coinview. This will likely only be stored in memory, // unless the coinview treashold is reached. this.logger.LogTrace("Saving coinview changes."); this.UTXOSet.SaveChangesAsync(context.Set.GetCoins(this.UTXOSet), null, this.Tip.HashBlock, context.BlockResult.ChainedBlock.HashBlock).GetAwaiter().GetResult(); // Set the new tip. this.Tip = context.BlockResult.ChainedBlock; this.logger.LogTrace("(-)[OK]"); }
public void AcceptBlock(ContextInformation context) { using (this.watch.Start(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { // check that the current block has not been reorged // catching a reorg at this point will not require a rewind if (context.BlockResult.Block.Header.HashPrevBlock != this.Tip.HashBlock) { ConsensusErrors.InvalidPrevTip.Throw(); // reorg } // build the next block in the chain of headers // the chain header is most likely already created by // one of the peers so after we create a new chained block (mainly for validation) // we ask the chain headers for its version (also to prevent mempry leaks) context.BlockResult.ChainedBlock = new ChainedBlock(context.BlockResult.Block.Header, context.BlockResult.Block.Header.GetHash(), this.Tip); //Liberate from memory the block created above if possible context.BlockResult.ChainedBlock = this.Chain.GetBlock(context.BlockResult.ChainedBlock.HashBlock) ?? context.BlockResult.ChainedBlock; context.SetBestBlock(); // == validation flow == // check the block hedaer is correct this.Validator.CheckBlockHeader(context); this.Validator.ContextualCheckBlockHeader(context); // calculate the consensus flags // and check they are valid context.Flags = this.NodeDeployments.GetFlags(context.BlockResult.ChainedBlock); this.Validator.ContextualCheckBlock(context); // check the block itself this.Validator.CheckBlock(context); } if (context.OnlyCheck) { return; } // load the UTXO set of the current block // UTXO may be loaded form cache or from disk // the UTXO set are stored in the context context.Set = new UnspentOutputSet(); using (this.watch.Start(o => this.Validator.PerformanceCounter.AddUTXOFetchingTime(o))) { var ids = GetIdsToFetch(context.BlockResult.Block, context.Flags.EnforceBIP30); var coins = this.UTXOSet.FetchCoinsAsync(ids).GetAwaiter().GetResult(); context.Set.SetCoins(coins); } // attempt to load in to cach the // next set of UTXO to be validated // the task is not awaited so will not // stall main validation process this.TryPrefetchAsync(context.Flags); // validate the UTXO set are correctly spent using (this.watch.Start(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { this.Validator.ExecuteBlock(context, null); } // persist the changes to the coinview // this will likely only be sotred in mempry // unless the coinview trashold is reached this.UTXOSet.SaveChangesAsync(context.Set.GetCoins(this.UTXOSet), null, this.Tip.HashBlock, context.BlockResult.ChainedBlock.HashBlock); // set the new tip. this.Tip = context.BlockResult.ChainedBlock; }