/// <summary> /// Calculates signature operation cost for single transaction input. /// </summary> /// <param name="scriptSig">Signature script.</param> /// <param name="scriptPubKey">Script public key.</param> /// <param name="witness">Witness script.</param> /// <param name="flags">Script verification flags.</param> /// <returns>Signature operation cost for single transaction input.</returns> private long CountWitnessSignatureOperation(Script scriptSig, Script scriptPubKey, WitScript witness, DeploymentFlags flags) { witness = witness ?? WitScript.Empty; if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { return(0); } WitProgramParameters witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams?.Version == 0) { if (witParams.Program.Length == 20) { return(1); } if (witParams.Program.Length == 32 && witness.PushCount > 0) { Script subscript = Script.FromBytesUnsafe(witness.GetUnsafePush(witness.PushCount - 1)); return(subscript.GetSigOpCount(true)); } } return(0); }
private long CountWitnessSigOps(Script scriptSig, Script scriptPubKey, WitScript witness, DeploymentFlags flags) { witness = witness ?? WitScript.Empty; if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { return(0); } WitProgramParameters witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams != null) { return(this.WitnessSigOps(witParams, witness, flags)); } if (scriptPubKey.IsPayToScriptHash && scriptSig.IsPushOnly) { byte[] data = scriptSig.ToOps().Select(o => o.PushData).LastOrDefault() ?? new byte[0]; Script subScript = Script.FromBytesUnsafe(data); witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams != null) { return(this.WitnessSigOps(witParams, witness, flags)); } } return(0); }
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); }
/// <summary> /// Whether transaction is witness standard. /// <seealso cref="https://github.com/bitcoin/bitcoin/blob/aa624b61c928295c27ffbb4d27be582f5aa31b56/src/policy/policy.cpp#L196"/> /// </summary> /// <param name="tx">Transaction to verify.</param> /// <param name="mapInputs">Map of previous transactions that have outputs we're spending.</param> /// <returns>Whether transaction is witness standard.</returns> private bool IsWitnessStandard(Transaction tx, MempoolCoinView mapInputs) { if (tx.IsCoinBase) { this.logger.LogTrace("(-)[IS_COINBASE]:true"); return(true); // Coinbases are skipped. } foreach (TxIn input in tx.Inputs) { // We don't care if witness for this input is empty, since it must not be bloated. // If the script is invalid without witness, it would be caught sooner or later during validation. if (input.WitScriptEmpty) { continue; } TxOut prev = mapInputs.GetOutputFor(input); // Get the scriptPubKey corresponding to this input. Script prevScript = prev.ScriptPubKey; if (prevScript.IsPayToScriptHash(this.network)) { // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway. // If the check fails at this stage, we know that this txid must be a bad one. PayToScriptHashSigParameters sigParams = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.network, input.ScriptSig); if (sigParams == null || sigParams.RedeemScript == null) { this.logger.LogTrace("(-)[BAD_TXID]:false"); return(false); } prevScript = sigParams.RedeemScript; } // Non-witness program must not be associated with any witness. if (!prevScript.IsWitness(this.network)) { this.logger.LogTrace("(-)[WITNESS_MISMATCH]:false"); return(false); } // Check P2WSH standard limits. WitProgramParameters wit = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(this.chainIndexer.Network, prevScript); if (wit == null) { this.logger.LogTrace("(-)[BAD_WITNESS_PARAMS]:false"); return(false); } // Version 0 segregated witness program validation. if (wit.Version == 0 && wit.Program.Length == WitnessV0ScriptHashSize) { WitScript witness = input.WitScript; // Get P2WSH script from top of stack. Script scriptPubKey = Script.FromBytesUnsafe(witness.GetUnsafePush(witness.PushCount - 1)); // Stack items are remainder of stack. int sizeWitnessStack = witness.PushCount - 1; // Get the witness stack items. var stack = new List <byte[]>(); for (int i = 0; i < sizeWitnessStack; i++) { stack.Add(witness.GetUnsafePush(i)); } // Validate P2WSH script isn't larger than max length. if (scriptPubKey.ToBytes(true).Length > MaxStandardP2wshScriptSize) { this.logger.LogTrace("(-)[P2WSH_SCRIPT_SIZE]:false"); return(false); } // Validate number items in witness stack isn't larger than max. if (sizeWitnessStack > MaxStandardP2wshStackItems) { this.logger.LogTrace("(-)[P2WSH_STACK_ITEMS]:false"); return(false); } // Validate size of each of the witness stack items. for (int j = 0; j < sizeWitnessStack; j++) { if (stack[j].Length > MaxStandardP2wshStackItemSize) { this.logger.LogTrace("(-)[P2WSH_STACK_ITEM_SIZE]:false"); return(false); } } } } return(true); }