Ejemplo n.º 1
0
        /// <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);
        }
Ejemplo n.º 2
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);
        }
Ejemplo n.º 3
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);
        }