/// <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); }
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); }