diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 32a31ea653..07147694c3 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -321,3 +321,50 @@ int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost, un { return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost, bytes_per_sigop); } + +std::pair GetScriptForTransactionInput(CScript prev_script, const CTxIn& txin) +{ + bool p2sh = false; + if (prev_script.IsPayToScriptHash()) { + std::vector > stack; + if (!EvalScript(stack, txin.scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE)) { + return std::make_pair(CScript(), 0); + } + if (stack.empty()) { + return std::make_pair(CScript(), 0); + } + prev_script = CScript(stack.back().begin(), stack.back().end()); + p2sh = true; + } + + int witnessversion = 0; + std::vector witnessprogram; + + if (!prev_script.IsWitnessProgram(witnessversion, witnessprogram)) { + // For P2SH, scriptSig is always push-only, so the actual script is only the last stack item + // For non-P2SH, prevScript is likely the real script, but not part of this transaction, and scriptSig could very well be executable, so return the latter instead + return std::make_pair(p2sh ? prev_script : txin.scriptSig, WITNESS_SCALE_FACTOR); + } + + auto stack = std::span{txin.scriptWitness.stack}; + + if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { + if (stack.empty()) return std::make_pair(CScript(), 0); // invalid + auto& script_data = stack.back(); + prev_script = CScript(script_data.begin(), script_data.end()); + return std::make_pair(prev_script, 1); + } + + if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) { + if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) { + SpanPopBack(stack); + } + if (stack.size() >= 2) { + SpanPopBack(stack); // Ignore control block + prev_script = CScript(stack.back().begin(), stack.back().end()); + return std::make_pair(prev_script, 1); + } + } + + return std::make_pair(CScript(), 0); +} diff --git a/src/policy/policy.h b/src/policy/policy.h index 2151ec13dd..411cf6ca09 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -14,6 +14,7 @@ #include #include +#include class CCoinsViewCache; class CFeeRate; @@ -181,4 +182,6 @@ static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx) return GetVirtualTransactionInputSize(tx, 0, 0); } +std::pair GetScriptForTransactionInput(CScript prev_script, const CTxIn&); + #endif // BITCOIN_POLICY_POLICY_H