diff options
author | Antoine Poinsot <darosior@protonmail.com> | 2023-01-21 14:37:49 +0100 |
---|---|---|
committer | Antoine Poinsot <darosior@protonmail.com> | 2023-10-08 02:43:22 +0200 |
commit | 8571b89a7fce50229242ef3c6d9f807949f716a3 (patch) | |
tree | 35e21624afe992f6bc4fed3c98f55a3a02cc8c3e /src/script | |
parent | 8ff9489422009284967aeb9ff4232b135f5ddad8 (diff) |
descriptor: parse Miniscript expressions within Taproot descriptors
Diffstat (limited to 'src/script')
-rw-r--r-- | src/script/descriptor.cpp | 38 |
1 files changed, 26 insertions, 12 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 104990abb7..ba2334df49 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -1181,8 +1181,15 @@ protected: std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& provider) const override { - for (const auto& key : keys) provider.pubkeys.emplace(key.GetID(), key); - return Vector(m_node->ToScript(ScriptMaker(keys, m_node->GetMsCtx()))); + const auto script_ctx{m_node->GetMsCtx()}; + for (const auto& key : keys) { + if (miniscript::IsTapscript(script_ctx)) { + provider.pubkeys.emplace(Hash160(XOnlyPubKey{key}), key); + } else { + provider.pubkeys.emplace(key.GetID(), key); + } + } + return Vector(m_node->ToScript(ScriptMaker(keys, script_ctx))); } public: @@ -1443,9 +1450,12 @@ struct KeyParser { mutable std::string m_key_parsing_error; //! The script context we're operating within (Tapscript or P2WSH). const miniscript::MiniscriptContext m_script_ctx; + //! The number of keys that were parsed before starting to parse this Miniscript descriptor. + uint32_t m_offset; - KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND, miniscript::MiniscriptContext ctx) - : m_out(out), m_in(in), m_script_ctx(ctx) {} + KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND, + miniscript::MiniscriptContext ctx, uint32_t offset = 0) + : m_out(out), m_in(in), m_script_ctx(ctx), m_offset(offset) {} bool KeyCompare(const Key& a, const Key& b) const { return *m_keys.at(a) < *m_keys.at(b); @@ -1463,7 +1473,7 @@ struct KeyParser { { assert(m_out); Key key = m_keys.size(); - auto pk = ParsePubkey(key, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error); + auto pk = ParsePubkey(m_offset + key, {&*begin, &*end}, ParseContext(), *m_out, m_key_parsing_error); if (!pk) return {}; m_keys.push_back(std::move(pk)); return key; @@ -1537,8 +1547,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const } ++key_exp_index; return std::make_unique<PKHDescriptor>(std::move(pubkey)); - } else if (Func("pkh", expr)) { - error = "Can only have pkh at top level, in sh(), or in wsh()"; + } else if (ctx != ParseScriptContext::P2TR && Func("pkh", expr)) { + // Under Taproot, always the Miniscript parser deal with it. + error = "Can only have pkh at top level, in sh(), wsh(), or in tr()"; return nullptr; } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { @@ -1751,11 +1762,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const } // Process miniscript expressions. { - KeyParser parser(/*out = */&out, /* in = */nullptr, /* ctx = */miniscript::MiniscriptContext::P2WSH); + const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT}; + KeyParser parser(/*out = */&out, /* in = */nullptr, /* ctx = */script_ctx, key_exp_index); auto node = miniscript::FromString(std::string(expr.begin(), expr.end()), parser); if (node) { - if (ctx != ParseScriptContext::P2WSH) { - error = "Miniscript expressions can only be used in wsh"; + if (ctx != ParseScriptContext::P2WSH && ctx != ParseScriptContext::P2TR) { + error = "Miniscript expressions can only be used in wsh or tr."; return nullptr; } if (parser.m_key_parsing_error != "") { @@ -1790,6 +1802,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const // A signature check is required for a miniscript to be sane. Therefore no sane miniscript // may have an empty list of public keys. CHECK_NONFATAL(!parser.m_keys.empty()); + key_exp_index += parser.m_keys.size(); return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node)); } } @@ -1923,8 +1936,9 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo } } - if (ctx == ParseScriptContext::P2WSH) { - KeyParser parser(/* out = */nullptr, /* in = */&provider, /* ctx = */miniscript::MiniscriptContext::P2WSH); + if (ctx == ParseScriptContext::P2WSH || ctx == ParseScriptContext::P2TR) { + const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT}; + KeyParser parser(/* out = */nullptr, /* in = */&provider, /* ctx = */script_ctx); auto node = miniscript::FromScript(script, parser); if (node && node->IsSane()) { return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node)); |