/// <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);
        }
Exemplo n.º 2
0
        private static Transaction SignMultisigWithSinglePrivateKey(TxIn input, int inputIndex, Transaction[] previousTransactions, Transaction tx,
                                                                    BitcoinSecret secret, SigHash sigHash)
        {
            var outputTx        = tx.Clone();
            var prevTransaction = previousTransactions[inputIndex];
            var output          = prevTransaction.Outputs[input.PrevOut.N];

            if (PayToScriptHashTemplate.Instance.CheckScriptPubKey(output.ScriptPubKey))
            {
                var redeemScript       = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(input.ScriptSig)?.RedeemScript;
                var segwitRedeemScript = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(input.WitScript)?.RedeemScript;

                bool originalRedeem = false, segwitRedeem = false;
                if (redeemScript != null && PayToMultiSigTemplate.Instance.CheckScriptPubKey(redeemScript))
                {
                    originalRedeem = true;
                }
                if (segwitRedeemScript != null && PayToMultiSigTemplate.Instance.CheckScriptPubKey(segwitRedeemScript))
                {
                    segwitRedeem = true;
                }

                if (originalRedeem || segwitRedeem)
                {
                    PubKey[] pubkeys = null;

                    if (originalRedeem)
                    {
                        pubkeys = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(redeemScript).PubKeys;
                    }
                    if (segwitRedeem)
                    {
                        pubkeys = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(segwitRedeemScript).PubKeys;
                    }

                    for (int j = 0; j < pubkeys.Length; j++)
                    {
                        if (secret.PubKey.ToHex() == pubkeys[j].ToHex())
                        {
                            PayToScriptHashSigParameters scriptParams = null;
                            if (originalRedeem)
                            {
                                scriptParams = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(input.ScriptSig);
                            }
                            if (segwitRedeem)
                            {
                                scriptParams = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(input.WitScript);
                            }

                            uint256 hash = null;
                            hash = Script.SignatureHash(scriptParams.RedeemScript, tx, inputIndex, sigHash, output.Value,
                                                        segwitRedeem ? HashVersion.Witness : HashVersion.Original);

                            var signature = secret.PrivateKey.Sign(hash, sigHash);
                            scriptParams.Pushes[j + 1] = signature.Signature.ToDER().Concat(new byte[] { (byte)sigHash }).ToArray();

                            if (originalRedeem)
                            {
                                outputTx.Inputs[inputIndex].ScriptSig = PayToScriptHashTemplate.Instance.GenerateScriptSig(scriptParams);
                            }
                            if (segwitRedeem)
                            {
                                outputTx.Inputs[inputIndex].WitScript = PayToScriptHashTemplate.Instance.GenerateScriptSig(scriptParams);
                            }
                        }
                    }
                }
            }

            return(outputTx);
        }