diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/script/miniscript.cpp | 2 | ||||
-rw-r--r-- | src/script/miniscript.h | 43 |
2 files changed, 40 insertions, 5 deletions
diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp index c133f88709..03158c5d8a 100644 --- a/src/script/miniscript.cpp +++ b/src/script/miniscript.cpp @@ -6,11 +6,11 @@ #include <vector> #include <script/script.h> #include <script/miniscript.h> +#include <serialize.h> #include <assert.h> namespace miniscript { - namespace internal { Type SanitizeType(Type e) { diff --git a/src/script/miniscript.h b/src/script/miniscript.h index cd656444b6..54d86bf5b0 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -248,6 +248,34 @@ constexpr bool IsTapscript(MiniscriptContext ms_ctx) namespace internal { +//! The maximum size of a witness item for a Miniscript under Tapscript context. (A BIP340 signature with a sighash type byte.) +static constexpr uint32_t MAX_TAPMINISCRIPT_STACK_ELEM_SIZE{65}; + +//! nVersion + nLockTime +constexpr uint32_t TX_OVERHEAD{4 + 4}; +//! prevout + nSequence + scriptSig +constexpr uint32_t TXIN_BYTES_NO_WITNESS{36 + 4 + 1}; +//! nValue + script len + OP_0 + pushdata 32. +constexpr uint32_t P2WSH_TXOUT_BYTES{8 + 1 + 1 + 33}; +//! Data other than the witness in a transaction. Overhead + vin count + one vin + vout count + one vout + segwit marker +constexpr uint32_t TX_BODY_LEEWAY_WEIGHT{(TX_OVERHEAD + GetSizeOfCompactSize(1) + TXIN_BYTES_NO_WITNESS + GetSizeOfCompactSize(1) + P2WSH_TXOUT_BYTES) * WITNESS_SCALE_FACTOR + 2}; +//! Maximum possible stack size to spend a Taproot output (excluding the script itself). +constexpr uint32_t MAX_TAPSCRIPT_SAT_SIZE{GetSizeOfCompactSize(MAX_STACK_SIZE) + (GetSizeOfCompactSize(MAX_TAPMINISCRIPT_STACK_ELEM_SIZE) + MAX_TAPMINISCRIPT_STACK_ELEM_SIZE) * MAX_STACK_SIZE + GetSizeOfCompactSize(TAPROOT_CONTROL_MAX_SIZE) + TAPROOT_CONTROL_MAX_SIZE}; +/** The maximum size of a script depending on the context. */ +constexpr uint32_t MaxScriptSize(MiniscriptContext ms_ctx) +{ + if (IsTapscript(ms_ctx)) { + // Leaf scripts under Tapscript are not explicitly limited in size. They are only implicitly + // bounded by the maximum standard size of a spending transaction. Let the maximum script + // size conservatively be small enough such that even a maximum sized witness and a reasonably + // sized spending transaction can spend an output paying to this script without running into + // the maximum standard tx size limit. + constexpr auto max_size{MAX_STANDARD_TX_WEIGHT - TX_BODY_LEEWAY_WEIGHT - MAX_TAPSCRIPT_SAT_SIZE}; + return max_size - GetSizeOfCompactSize(max_size); + } + return MAX_STANDARD_P2WSH_SCRIPT_SIZE; +} + //! Helper function for Node::CalcType. Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Type>& sub_types, uint32_t k, size_t data_size, size_t n_subs, size_t n_keys, MiniscriptContext ms_ctx); @@ -1277,6 +1305,7 @@ public: //! Check the ops limit of this script against the consensus limit. bool CheckOpsLimit() const { + if (IsTapscript(m_script_ctx)) return true; if (const auto ops = GetOps()) return *ops <= MAX_OPS_PER_SCRIPT; return true; } @@ -1290,6 +1319,8 @@ public: //! Check the maximum stack size for this script against the policy limit. bool CheckStackSize() const { + // TODO: MAX_STACK_SIZE during script execution under Tapscript. + if (IsTapscript(m_script_ctx)) return true; if (const auto ss = GetStackSize()) return *ss <= MAX_STANDARD_P2WSH_STACK_ITEMS; return true; } @@ -1359,7 +1390,10 @@ public: } //! Check whether this node is valid at all. - bool IsValid() const { return !(GetType() == ""_mst) && ScriptSize() <= MAX_STANDARD_P2WSH_SCRIPT_SIZE; } + bool IsValid() const { + if (GetType() == ""_mst) return false; + return ScriptSize() <= internal::MaxScriptSize(m_script_ctx); + } //! Check whether this node is valid as a script on its own. bool IsValidTopLevel() const { return IsValid() && GetType() << "B"_mst; } @@ -1546,6 +1580,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) // (instead transforming another opcode into its VERIFY form). However, the v: wrapper has // to be interleaved with other fragments to be valid, so this is not a concern. size_t script_size{1}; + size_t max_size{internal::MaxScriptSize(ctx.MsContext())}; // The two integers are used to hold state for thresh() std::vector<std::tuple<ParseContext, int64_t, int64_t>> to_parse; @@ -1589,7 +1624,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) }; while (!to_parse.empty()) { - if (script_size > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (script_size > max_size) return {}; // Get the current context we are decoding within auto [cur_context, n, k] = to_parse.back(); @@ -1608,7 +1643,7 @@ inline NodeRef<Key> Parse(Span<const char> in, const Ctx& ctx) // If there is no colon, this loop won't execute bool last_was_v{false}; for (size_t j = 0; colon_index && j < *colon_index; ++j) { - if (script_size > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (script_size > max_size) return {}; if (in[j] == 'a') { script_size += 2; to_parse.emplace_back(ParseContext::ALT, -1, -1); @@ -2386,7 +2421,7 @@ template<typename Ctx> inline NodeRef<typename Ctx::Key> FromScript(const CScript& script, const Ctx& ctx) { using namespace internal; // A too large Script is necessarily invalid, don't bother parsing it. - if (script.size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {}; + if (script.size() > MaxScriptSize(ctx.MsContext())) return {}; auto decomposed = DecomposeScript(script); if (!decomposed) return {}; auto it = decomposed->begin(); |