From 332431ecf94f8fae96c0e43bbdf8b41c4490e5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Haf?= Date: Wed, 24 Jan 2024 17:34:32 +0100 Subject: [PATCH] add ordisrespector v1 --- src/policy/policy.cpp | 63 +++++++ src/policy/policy.h | 5 + src/script/script.cpp | 50 ++++++ src/script/script.h | 7 +- src/test/script_tests.cpp | 256 +++++++++++++++++++++++++++++ src/validation.cpp | 4 + test/functional/feature_taproot.py | 4 +- 7 files changed, 385 insertions(+), 4 deletions(-) diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index d08ec4fb7f..beabd580f2 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -306,3 +306,66 @@ int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost, un { return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost, bytes_per_sigop); } + +std::pair GetScriptForTransactionInput(CScript prevScript, const CTxIn& txin) +{ + bool p2sh = false; + if (prevScript.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); + } + prevScript = CScript(stack.back().begin(), stack.back().end()); + p2sh = true; + } + + int witnessversion = 0; + std::vector witnessprogram; + + if (!prevScript.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 ? prevScript : txin.scriptSig, WITNESS_SCALE_FACTOR); + } + + Span stack{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(); + prevScript = CScript(script_data.begin(), script_data.end()); + return std::make_pair(prevScript, 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 + prevScript = CScript(stack.back().begin(), stack.back().end()); + return std::make_pair(prevScript, 1); + } + } + + return std::make_pair(CScript(), 0); +} + +size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view) +{ + size_t ret{0}; + + for (const CTxIn& txin : tx.vin) { + const CTxOut &utxo = view.AccessCoin(txin.prevout).out; + auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin); + ret += script.DatacarrierBytes(); + } + for (const CTxOut& txout : tx.vout) { + ret += txout.scriptPubKey.DatacarrierBytes(); + } + + return ret; +} diff --git a/src/policy/policy.h b/src/policy/policy.h index d1c8148800..6eb376513d 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -14,6 +14,7 @@ #include #include +#include class CCoinsViewCache; class CFeeRate; @@ -168,4 +169,8 @@ static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx) return GetVirtualTransactionInputSize(tx, 0, 0); } +std::pair GetScriptForTransactionInput(CScript prevScript, const CTxIn&); + +size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view); + #endif // BITCOIN_POLICY_POLICY_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 1594d3cc79..09e777c2cf 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -283,6 +283,56 @@ bool CScript::HasValidOps() const return true; } +size_t CScript::DatacarrierBytes() const +{ + size_t counted{0}; + opcodetype opcode, last_opcode{OP_INVALIDOPCODE}; + std::vector push_data; + unsigned int inside_noop{0}, inside_conditional{0}; + CScript::const_iterator opcode_it = begin(), data_began = begin(); + for (CScript::const_iterator it = begin(); it < end(); last_opcode = opcode) { + opcode_it = it; + if (!GetOp(it, opcode, push_data)) { + // Invalid scripts are necessarily all data + return size(); + } + + if (opcode == OP_IF || opcode == OP_NOTIF) { + ++inside_conditional; + } else if (opcode == OP_ENDIF) { + if (!inside_conditional) return size(); // invalid + --inside_conditional; + } else if (opcode == OP_RETURN && !inside_conditional) { + // unconditional OP_RETURN is unspendable + return size(); + } + + // Match OP_FALSE OP_IF + if (inside_noop) { + switch (opcode) { + case OP_IF: case OP_NOTIF: + ++inside_noop; + break; + case OP_ENDIF: + if (0 == --inside_noop) { + counted += it - data_began + 1; + } + break; + default: /* do nothing */; + } + } else if (opcode == OP_IF && last_opcode == OP_FALSE) { + inside_noop = 1; + data_began = opcode_it; + // Match OP_DROP + } else if (opcode <= OP_PUSHDATA4) { + data_began = opcode_it; + } else if (opcode == OP_DROP && last_opcode <= OP_PUSHDATA4) { + counted += it - data_began; + } + } + return counted; +} + bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector* pvchRet) { opcodeRet = OP_INVALIDOPCODE; diff --git a/src/script/script.h b/src/script/script.h index c329a2afd6..5b9b8bf8cf 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -544,15 +544,18 @@ public: bool HasValidOps() const; /** - * Returns whether the script is guaranteed to fail at execution, + * Returns whether the scriptPubKey is guaranteed to fail at execution, * regardless of the initial stack. This allows outputs to be pruned - * instantly when entering the UTXO set. + * instantly when entering the UTXO set. Note that this is incorrect for + * witness scripts, which are not always limited by MAX_SCRIPT_SIZE. */ bool IsUnspendable() const { return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE); } + size_t DatacarrierBytes() const; + void clear() { // The default prevector::clear() does not release memory diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 624d0b2c12..9b131fc610 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include