public void ContextualCheckBlockHeader(BlockHeader header, ContextInformation context) { if (context.BestBlock == null) { throw new ArgumentException("context.BestBlock should not be null"); } int nHeight = context.BestBlock.Height + 1; // Check proof of work if (header.Bits != context.NextWorkRequired) { ConsensusErrors.BadDiffBits.Throw(); } // Check timestamp against prev if (header.BlockTime <= context.BestBlock.MedianTimePast) { ConsensusErrors.TimeTooOld.Throw(); } // Check timestamp if (header.BlockTime > context.Time + TimeSpan.FromHours(2)) { ConsensusErrors.TimeTooNew.Throw(); } // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: // check for version 2, 3 and 4 upgrades if ((header.Version < 2 && nHeight >= _ConsensusParams.BuriedDeployments[BuriedDeployments.BIP34]) || (header.Version < 3 && nHeight >= _ConsensusParams.BuriedDeployments[BuriedDeployments.BIP66]) || (header.Version < 4 && nHeight >= _ConsensusParams.BuriedDeployments[BuriedDeployments.BIP65])) { ConsensusErrors.BadVersion.Throw(); } }
/// <summary> /// Pull blocks, validate them and update the UTXO Set /// </summary> /// <param name="utxo">UTXO Set</param> /// <param name="puller">Block source</param> /// <returns>Stream of validated blocks</returns> public IEnumerable <Block> Run(CoinViewStack utxoStack, BlockPuller puller) { var utxo = utxoStack.Top; var cache = utxoStack.Find <CacheCoinView>(); var lookaheadPuller = puller as ILookaheadBlockPuller; puller.SetLocation(utxo.Tip); ThresholdConditionCache bip9 = new ThresholdConditionCache(_ConsensusParams); StopWatch watch = new StopWatch(); bool rejected = false; while (true) { Block block = null; while (true) { rejected = false; try { using (watch.Start(o => PerformanceCounter.AddBlockFetchingTime(o))) { block = puller.NextBlock(); } ChainedBlock next; ContextInformation context; ConsensusFlags flags; using (watch.Start(o => PerformanceCounter.AddBlockProcessingTime(o))) { CheckBlockHeader(block.Header); next = new ChainedBlock(block.Header, block.Header.GetHash(), utxo.Tip); context = new ContextInformation(next, this._ConsensusParams); ContextualCheckBlockHeader(block.Header, context); var states = bip9.GetStates(utxo.Tip); flags = new ConsensusFlags(next, states, _ConsensusParams); ContextualCheckBlock(block, flags, context); CheckBlock(block); } var commitable = new CommitableCoinView(next, utxo); using (watch.Start(o => PerformanceCounter.AddUTXOFetchingTime(o))) { commitable.FetchCoins(GetIdsToFetch(block, flags.EnforceBIP30)); } commitable.SetInner(NullCoinView.Instance); Task prefetching = GetPrefetchingTask(cache, lookaheadPuller, flags); using (watch.Start(o => PerformanceCounter.AddBlockProcessingTime(o))) { ExecuteBlock(block, next, flags, commitable, null); } using (watch.Start(o => PerformanceCounter.AddUTXOFetchingTime(o))) { prefetching.Wait(); commitable.Commit(utxo); } } catch (ConsensusErrorException ex) { rejected = true; if (ex.ConsensusError == ConsensusErrors.TimeTooNew) { puller.Reject(block, RejectionMode.Temporary); } else { puller.Reject(block, RejectionMode.Permanent); } } if (!rejected) { yield return(block); } } } }
public void ContextualCheckBlock(Block block, ConsensusFlags consensusFlags, ContextInformation context) { int nHeight = context.BestBlock == null ? 0 : context.BestBlock.Height + 1; // Start enforcing BIP113 (Median Time Past) using versionbits logic. var nLockTimeCutoff = consensusFlags.LockTimeFlags.HasFlag(LockTimeFlags.MedianTimePast) ? context.BestBlock.MedianTimePast : block.Header.BlockTime; // Check that all transactions are finalized foreach (var transaction in block.Transactions) { if (!transaction.IsFinal(nLockTimeCutoff, nHeight)) { ConsensusErrors.BadTransactionNonFinal.Throw(); } } // Enforce rule that the coinbase starts with serialized block height if (consensusFlags.EnforceBIP34) { Script expect = new Script(Op.GetPushOp(nHeight)); Script actual = block.Transactions[0].Inputs[0].ScriptSig; if (!StartWith(actual.ToBytes(true), expect.ToBytes(true))) { ConsensusErrors.BadCoinbaseHeight.Throw(); } } // Validation for witness commitments. // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the // coinbase (where 0x0000....0000 is used instead). // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are // multiple, the last one is used. bool fHaveWitness = false; if (consensusFlags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { int commitpos = GetWitnessCommitmentIndex(block); if (commitpos != -1) { bool malleated = false; uint256 hashWitness = BlockWitnessMerkleRoot(block, ref malleated); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. var witness = block.Transactions[0].Inputs[0].WitScript; if (witness.PushCount != 1 || witness.Pushes.First().Length != 32) { ConsensusErrors.BadWitnessNonceSize.Throw(); } byte[] hashed = new byte[64]; Buffer.BlockCopy(hashWitness.ToBytes(), 0, hashed, 0, 32); Buffer.BlockCopy(witness.Pushes.First(), 0, hashed, 32, 32); hashWitness = Hashes.Hash256(hashed); if (!EqualsArray(hashWitness.ToBytes(), block.Transactions[0].Outputs[commitpos].ScriptPubKey.ToBytes(true).Skip(6).ToArray(), 32)) { ConsensusErrors.BadWitnessMerkleMatch.Throw(); } fHaveWitness = true; } } if (!fHaveWitness) { for (var i = 0; i < block.Transactions.Count; i++) { if (block.Transactions[i].HasWitness) { ConsensusErrors.UnexpectedWitness.Throw(); } } } // After the coinbase witness nonce and commitment are verified, // we can check if the block weight passes (before we've checked the // coinbase witness, it would be possible for the weight to be too // large by filling up the coinbase witness, which doesn't change // the block hash, so we couldn't mark the block as permanently // failed). if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) { ConsensusErrors.BadCoinbaseHeight.Throw(); } }