public ConsensusFlags GetFlags(ChainedBlock block = null) { block = block ?? Tip; lock (bip9) { var states = bip9.GetStates(block.Previous); var flags = new ConsensusFlags(block, states, Validator.ConsensusParams); return(flags); } }
private Task TryPrefetchAsync(ConsensusFlags flags) { Task prefetching = Task.FromResult <bool>(true); if (UTXOSet is CachedCoinView) { var nextBlock = this.Puller.TryGetLookahead(0); if (nextBlock != null) { prefetching = UTXOSet.FetchCoinsAsync(GetIdsToFetch(nextBlock, flags.EnforceBIP30)); } } return(prefetching); }
public long GetTransactionSigOpCost(Transaction tx, UnspentOutputSet inputs, ConsensusFlags flags) { long nSigOps = GetLegacySigOpCount(tx) * this.consensusOptions.WITNESS_SCALE_FACTOR; if (tx.IsCoinBase) { return(nSigOps); } if (flags.ScriptFlags.HasFlag(ScriptVerify.P2SH)) { nSigOps += GetP2SHSigOpCount(tx, inputs) * this.consensusOptions.WITNESS_SCALE_FACTOR; } for (var i = 0; i < tx.Inputs.Count; i++) { var prevout = inputs.GetOutputFor(tx.Inputs[i]); nSigOps += CountWitnessSigOps(tx.Inputs[i].ScriptSig, prevout.ScriptPubKey, tx.Inputs[i].WitScript, flags); } return(nSigOps); }
private long WitnessSigOps(WitProgramParameters witParams, WitScript witScript, ConsensusFlags flags) { if (witParams.Version == 0) { if (witParams.Program.Length == 20) { return(1); } if (witParams.Program.Length == 32 && witScript.PushCount > 0) { Script subscript = Script.FromBytesUnsafe(witScript.GetUnsafePush(witScript.PushCount - 1)); return(subscript.GetSigOpCount(true)); } } // Future flags may be implemented here. return(0); }
private long CountWitnessSigOps(Script scriptSig, Script scriptPubKey, WitScript witness, ConsensusFlags flags) { witness = witness ?? WitScript.Empty; if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { return(0); } var witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams != null) { return(WitnessSigOps(witParams, witness, flags)); } if (scriptPubKey.IsPayToScriptHash && scriptSig.IsPushOnly) { var data = scriptSig.ToOps().Select(o => o.PushData).LastOrDefault() ?? new byte[0]; var subScript = Script.FromBytesUnsafe(data); witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams != null) { return(WitnessSigOps(witParams, witness, flags)); } } return(0); }
public virtual void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { Block block = context.BlockResult.Block; ChainedBlock index = context.BlockResult.ChainedBlock; ConsensusFlags flags = context.Flags; UnspentOutputSet view = context.Set; PerformanceCounter.AddProcessedBlocks(1); taskScheduler = taskScheduler ?? TaskScheduler.Default; if (flags.EnforceBIP30) { foreach (var tx in block.Transactions) { var coins = view.AccessCoins(tx.GetHash()); if (coins != null && !coins.IsPrunable) { ConsensusErrors.BadTransactionBIP30.Throw(); } } } long nSigOpsCost = 0; Money nFees = Money.Zero; List <Task <bool> > checkInputs = new List <Task <bool> >(); for (int i = 0; i < block.Transactions.Count; i++) { PerformanceCounter.AddProcessedTransactions(1); var tx = block.Transactions[i]; if (!tx.IsCoinBase && !tx.IsCoinStake) { int[] prevheights; if (!view.HaveInputs(tx)) { ConsensusErrors.BadTransactionMissingInput.Throw(); } prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set for (var j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSigOpCost counts 3 types of sigops: // * legacy (always) // * p2sh (when P2SH enabled in flags and excludes coinbase) // * witness (when witness enabled in flags and excludes coinbase) nSigOpsCost += GetTransactionSigOpCost(tx, view, flags); if (nSigOpsCost > this.consensusOptions.MAX_BLOCK_SIGOPS_COST) { ConsensusErrors.BadBlockSigOps.Throw(); } if (!tx.IsCoinBase && !tx.IsCoinStake) { CheckInputs(tx, view, index.Height); nFees += view.GetValueIn(tx) - tx.TotalOut; int ii = i; var localTx = tx; PrecomputedTransactionData txData = new PrecomputedTransactionData(tx); for (int iInput = 0; iInput < tx.Inputs.Count; iInput++) { PerformanceCounter.AddProcessedInputs(1); var input = tx.Inputs[iInput]; int iiIntput = iInput; var txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { if (UseConsensusLib) { Script.BitcoinConsensusError error; return(Script.VerifyScriptConsensus(txout.ScriptPubKey, tx, (uint)iiIntput, flags.ScriptFlags, out error)); } else { var checker = new TransactionChecker(tx, iiIntput, txout.Value, txData); var ctx = new ScriptEvaluationContext(); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); } }); checkInput.Start(taskScheduler); checkInputs.Add(checkInput); } } if (tx.IsCoinStake) { context.Stake.TotalCoinStakeValueIn = view.GetValueIn(tx); } view.Update(tx, index.Height); } this.CheckBlockReward(context, nFees, index, block); var passed = checkInputs.All(c => c.GetAwaiter().GetResult()); if (!passed) { ConsensusErrors.BadTransactionScriptError.Throw(); } }
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(); } }