diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -247,163 +247,141 @@ } }; -/** A parsed addr(A) descriptor. */ -class AddressDescriptor final : public Descriptor { - CTxDestination m_destination; - -public: - AddressDescriptor(CTxDestination destination) - : m_destination(std::move(destination)) {} - - bool IsRange() const override { return false; } - bool IsSolvable() const override { return false; } - std::string ToString() const override { - return "addr(" + EncodeDestination(m_destination, GetConfig()) + ")"; - } - bool ToPrivateString(const SigningProvider &arg, - std::string &out) const override { - out = ToString(); - return true; - } - bool Expand(int pos, const SigningProvider &arg, - std::vector &output_scripts, - FlatSigningProvider &out) const override { - output_scripts = - std::vector{GetScriptForDestination(m_destination)}; - return true; - } -}; - -/** A parsed raw(H) descriptor. */ -class RawDescriptor final : public Descriptor { - CScript m_script; - -public: - RawDescriptor(CScript script) : m_script(std::move(script)) {} - - bool IsRange() const override { return false; } - bool IsSolvable() const override { return false; } - std::string ToString() const override { - return "raw(" + HexStr(m_script.begin(), m_script.end()) + ")"; - } - bool ToPrivateString(const SigningProvider &arg, - std::string &out) const override { - out = ToString(); - return true; - } - bool Expand(int pos, const SigningProvider &arg, - std::vector &output_scripts, - FlatSigningProvider &out) const override { - output_scripts = std::vector{m_script}; - return true; - } -}; +/** Base class for all Descriptor implementations. */ +class DescriptorImpl : public Descriptor { + //! Public key arguments for this descriptor (size 1 for PK, PKH; any size + //! of Multisig). + const std::vector> m_pubkey_args; + //! The sub-descriptor argument (nullptr for everything but SH). + const std::unique_ptr m_script_arg; + //! The string name of the descriptor function. + const std::string m_name; + +protected: + //! Return a serialization of anything except pubkey and script arguments, + //! to be prepended to those. + virtual std::string ToStringExtra() const { return ""; } -/** A parsed pk(P), pkh(P) descriptor. */ -class SingleKeyDescriptor final : public Descriptor { - const std::function m_script_fn; - const std::string m_fn_name; - std::unique_ptr m_provider; + /** + * A helper function to construct the scripts for this descriptor. + * + * This function is invoked once for every CScript produced by evaluating + * m_script_arg, or just once in case m_script_arg is nullptr. + + * @param pubkeys The evaluations of the m_pubkey_args field. + * @param script The evaluation of m_script_arg (or nullptr when + m_script_arg is nullptr). + * @param out A FlatSigningProvider to put scripts or public keys in that + are necessary to the solver. + * The script and pubkeys argument to this function are + automatically added. + * @return A vector with scriptPubKeys for this descriptor. + */ + virtual std::vector + MakeScripts(const std::vector &pubkeys, const CScript *script, + FlatSigningProvider &out) const = 0; public: - SingleKeyDescriptor(std::unique_ptr prov, - const std::function &fn, - const std::string &name) - : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {} - - bool IsRange() const override { return m_provider->IsRange(); } - bool IsSolvable() const override { return true; } - std::string ToString() const override { - return m_fn_name + "(" + m_provider->ToString() + ")"; - } - bool ToPrivateString(const SigningProvider &arg, - std::string &out) const override { - std::string ret; - if (!m_provider->ToPrivateString(arg, ret)) { - return false; - } - out = m_fn_name + "(" + std::move(ret) + ")"; - return true; - } - bool Expand(int pos, const SigningProvider &arg, - std::vector &output_scripts, - FlatSigningProvider &out) const override { - CPubKey key; - KeyOriginInfo info; - if (!m_provider->GetPubKey(pos, arg, key, info)) { - return false; + DescriptorImpl(std::vector> pubkeys, + std::unique_ptr script, const std::string &name) + : m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)), + m_name(name) {} + + bool IsSolvable() const override { + if (m_script_arg) { + if (!m_script_arg->IsSolvable()) { + return false; + } } - output_scripts = std::vector{m_script_fn(key)}; - out.origins.emplace(key.GetID(), std::move(info)); - out.pubkeys.emplace(key.GetID(), key); return true; } -}; - -CScript P2PKHGetScript(const CPubKey &pubkey) { - return GetScriptForDestination(pubkey.GetID()); -} -CScript P2PKGetScript(const CPubKey &pubkey) { - return GetScriptForRawPubKey(pubkey); -} - -/** A parsed multi(...) descriptor. */ -class MultisigDescriptor : public Descriptor { - int m_threshold; - std::vector> m_providers; - -public: - MultisigDescriptor(int threshold, - std::vector> providers) - : m_threshold(threshold), m_providers(std::move(providers)) {} - bool IsRange() const override { - for (const auto &p : m_providers) { - if (p->IsRange()) { + bool IsRange() const final { + for (const auto &pubkey : m_pubkey_args) { + if (pubkey->IsRange()) { return true; } } - return false; - } - - bool IsSolvable() const override { return true; } - - std::string ToString() const override { - std::string ret = strprintf("multi(%i", m_threshold); - for (const auto &p : m_providers) { - ret += "," + p->ToString(); + if (m_script_arg) { + if (m_script_arg->IsRange()) { + return true; + } } - return std::move(ret) + ")"; + return false; } bool ToPrivateString(const SigningProvider &arg, - std::string &out) const override { - std::string ret = strprintf("multi(%i", m_threshold); - for (const auto &p : m_providers) { - std::string sub; - if (!p->ToPrivateString(arg, sub)) { + std::string &out) const final { + std::string extra = ToStringExtra(); + size_t pos = extra.size() > 0 ? 1 : 0; + std::string ret = m_name + "(" + extra; + for (const auto &pubkey : m_pubkey_args) { + if (pos++) { + ret += ","; + } + std::string tmp; + if (!pubkey->ToPrivateString(arg, tmp)) { + return false; + } + ret += std::move(tmp); + } + if (m_script_arg) { + if (pos++) { + ret += ","; + } + std::string tmp; + if (!m_script_arg->ToPrivateString(arg, tmp)) { return false; } - ret += "," + std::move(sub); + ret += std::move(tmp); } out = std::move(ret) + ")"; return true; } + std::string ToString() const final { + std::string extra = ToStringExtra(); + size_t pos = extra.size() > 0 ? 1 : 0; + std::string ret = m_name + "(" + extra; + for (const auto &pubkey : m_pubkey_args) { + if (pos++) { + ret += ","; + } + ret += pubkey->ToString(); + } + if (m_script_arg) { + if (pos++) { + ret += ","; + } + ret += m_script_arg->ToString(); + } + return std::move(ret) + ")"; + } + bool Expand(int pos, const SigningProvider &arg, std::vector &output_scripts, - FlatSigningProvider &out) const override { + FlatSigningProvider &out) const final { std::vector> entries; - entries.reserve(m_providers.size()); - // Construct temporary data in `entries`, to avoid producing output in - // case of failure. - for (const auto &p : m_providers) { + entries.reserve(m_pubkey_args.size()); + + // Construct temporary data in `entries` and `subscripts`, to avoid + // producing output in case of failure. + for (const auto &p : m_pubkey_args) { entries.emplace_back(); if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second)) { return false; } } + std::vector subscripts; + if (m_script_arg) { + FlatSigningProvider subprovider; + if (!m_script_arg->Expand(pos, arg, subscripts, subprovider)) { + return false; + } + out = Merge(out, subprovider); + } + std::vector pubkeys; pubkeys.reserve(entries.size()); for (auto &entry : entries) { @@ -411,101 +389,151 @@ out.origins.emplace(entry.first.GetID(), std::move(entry.second)); out.pubkeys.emplace(entry.first.GetID(), entry.first); } - output_scripts = - std::vector{GetScriptForMultisig(m_threshold, pubkeys)}; + if (m_script_arg) { + for (const auto &subscript : subscripts) { + out.scripts.emplace(CScriptID(subscript), subscript); + std::vector addscripts = + MakeScripts(pubkeys, &subscript, out); + for (auto &addscript : addscripts) { + output_scripts.push_back(std::move(addscript)); + } + } + } else { + output_scripts = MakeScripts(pubkeys, nullptr, out); + } return true; } }; -/** A parsed sh(S) descriptor. */ -class ConvertorDescriptor : public Descriptor { - const std::function m_convert_fn; - const std::string m_fn_name; - std::unique_ptr m_descriptor; +/** Construct a vector with one element, which is moved into it. */ +template std::vector Singleton(T elem) { + std::vector ret; + ret.emplace_back(std::move(elem)); + return ret; +} + +/** A parsed addr(A) descriptor. */ +class AddressDescriptor final : public DescriptorImpl { + const CTxDestination m_destination; + +protected: + std::string ToStringExtra() const override { + return EncodeDestination(m_destination, GetConfig()); + } + std::vector MakeScripts(const std::vector &, + const CScript *, + FlatSigningProvider &) const override { + return Singleton(GetScriptForDestination(m_destination)); + } public: - ConvertorDescriptor(std::unique_ptr descriptor, - const std::function &fn, - const std::string &name) - : m_convert_fn(fn), m_fn_name(name), - m_descriptor(std::move(descriptor)) {} - - bool IsRange() const override { return m_descriptor->IsRange(); } - bool IsSolvable() const override { return m_descriptor->IsSolvable(); } - std::string ToString() const override { - return m_fn_name + "(" + m_descriptor->ToString() + ")"; + AddressDescriptor(CTxDestination destination) + : DescriptorImpl({}, {}, "addr"), + m_destination(std::move(destination)) {} + bool IsSolvable() const final { return false; } +}; + +/** A parsed raw(H) descriptor. */ +class RawDescriptor final : public DescriptorImpl { + const CScript m_script; + +protected: + std::string ToStringExtra() const override { + return HexStr(m_script.begin(), m_script.end()); } - bool ToPrivateString(const SigningProvider &arg, - std::string &out) const override { - std::string ret; - if (!m_descriptor->ToPrivateString(arg, ret)) { - return false; - } - out = m_fn_name + "(" + std::move(ret) + ")"; - return true; + std::vector MakeScripts(const std::vector &, + const CScript *, + FlatSigningProvider &) const override { + return Singleton(m_script); } - bool Expand(int pos, const SigningProvider &arg, - std::vector &output_scripts, - FlatSigningProvider &out) const override { - std::vector sub; - if (!m_descriptor->Expand(pos, arg, sub, out)) { - return false; - } - output_scripts.clear(); - for (const auto &script : sub) { - CScriptID id(script); - out.scripts.emplace(CScriptID(script), script); - output_scripts.push_back(m_convert_fn(script)); - } - return true; + +public: + RawDescriptor(CScript script) + : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {} + bool IsSolvable() const final { return false; } +}; + +/** A parsed pk(P) descriptor. */ +class PKDescriptor final : public DescriptorImpl { +protected: + std::vector MakeScripts(const std::vector &keys, + const CScript *, + FlatSigningProvider &) const override { + return Singleton(GetScriptForRawPubKey(keys[0])); } + +public: + PKDescriptor(std::unique_ptr prov) + : DescriptorImpl(Singleton(std::move(prov)), {}, "pk") {} }; -CScript ConvertP2SH(const CScript &script) { - return GetScriptForDestination(CScriptID(script)); -} +/** A parsed pkh(P) descriptor. */ +class PKHDescriptor final : public DescriptorImpl { +protected: + std::vector MakeScripts(const std::vector &keys, + const CScript *, + FlatSigningProvider &) const override { + return Singleton(GetScriptForDestination(keys[0].GetID())); + } + +public: + PKHDescriptor(std::unique_ptr prov) + : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {} +}; /** A parsed combo(P) descriptor. */ -class ComboDescriptor final : public Descriptor { - std::unique_ptr m_provider; +class ComboDescriptor final : public DescriptorImpl { +protected: + std::vector MakeScripts(const std::vector &keys, + const CScript *, + FlatSigningProvider &out) const override { + std::vector ret; + CKeyID id = keys[0].GetID(); + // P2PK + ret.emplace_back(GetScriptForRawPubKey(keys[0])); + // P2PKH + ret.emplace_back(GetScriptForDestination(id)); + return ret; + } public: - ComboDescriptor(std::unique_ptr provider) - : m_provider(std::move(provider)) {} + ComboDescriptor(std::unique_ptr prov) + : DescriptorImpl(Singleton(std::move(prov)), {}, "combo") {} +}; - bool IsRange() const override { return m_provider->IsRange(); } - bool IsSolvable() const override { return true; } - std::string ToString() const override { - return "combo(" + m_provider->ToString() + ")"; +/** A parsed multi(...) descriptor. */ +class MultisigDescriptor final : public DescriptorImpl { + const int m_threshold; + +protected: + std::string ToStringExtra() const override { + return strprintf("%i", m_threshold); } - bool ToPrivateString(const SigningProvider &arg, - std::string &out) const override { - std::string ret; - if (!m_provider->ToPrivateString(arg, ret)) { - return false; - } - out = "combo(" + std::move(ret) + ")"; - return true; + std::vector MakeScripts(const std::vector &keys, + const CScript *, + FlatSigningProvider &) const override { + return Singleton(GetScriptForMultisig(m_threshold, keys)); } - bool Expand(int pos, const SigningProvider &arg, - std::vector &output_scripts, - FlatSigningProvider &out) const override { - CPubKey key; - KeyOriginInfo info; - if (!m_provider->GetPubKey(pos, arg, key, info)) { - return false; - } - CKeyID keyid = key.GetID(); - { - CScript p2pk = GetScriptForRawPubKey(key); - CScript p2pkh = GetScriptForDestination(keyid); - output_scripts = - std::vector{std::move(p2pk), std::move(p2pkh)}; - out.pubkeys.emplace(keyid, key); - out.origins.emplace(keyid, std::move(info)); - } - return true; + +public: + MultisigDescriptor(int threshold, + std::vector> providers) + : DescriptorImpl(std::move(providers), {}, "multi"), + m_threshold(threshold) {} +}; + +/** A parsed sh(...) descriptor. */ +class SHDescriptor final : public DescriptorImpl { +protected: + std::vector MakeScripts(const std::vector &, + const CScript *script, + FlatSigningProvider &) const override { + return Singleton(GetScriptForDestination(CScriptID(*script))); } + +public: + SHDescriptor(std::unique_ptr desc) + : DescriptorImpl({}, std::move(desc), "sh") {} }; //////////////////////////////////////////////////////////////////////////// @@ -603,9 +631,7 @@ return true; } -/** - * Parse a public key that excludes origin information. - */ +/** Parse a public key that excludes origin information. */ std::unique_ptr ParsePubkeyInner(const Span &sp, FlatSigningProvider &out) { auto split = Split(sp, '/'); @@ -651,9 +677,7 @@ type); } -/** - * Parse a public key including origin information (if enabled). - */ +/** Parse a public key including origin information (if enabled). */ std::unique_ptr ParsePubkey(const Span &sp, FlatSigningProvider &out) { auto origin_split = Split(sp, ']'); @@ -701,16 +725,14 @@ if (!pubkey) { return nullptr; } - return std::make_unique(std::move(pubkey), - P2PKGetScript, "pk"); + return std::make_unique(std::move(pubkey)); } if (Func("pkh", expr)) { auto pubkey = ParsePubkey(expr, out); if (!pubkey) { return nullptr; } - return std::make_unique(std::move(pubkey), - P2PKHGetScript, "pkh"); + return std::make_unique(std::move(pubkey)); } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { auto pubkey = ParsePubkey(expr, out); @@ -764,8 +786,7 @@ if (!desc || expr.size()) { return nullptr; } - return std::make_unique(std::move(desc), - ConvertP2SH, "sh"); + return std::make_unique(std::move(desc)); } if (ctx == ParseScriptContext::TOP && Func("addr", expr)) { CTxDestination dest = @@ -808,8 +829,8 @@ if (txntype == TX_PUBKEY) { CPubKey pubkey(data[0].begin(), data[0].end()); if (pubkey.IsValid()) { - return std::make_unique( - InferPubkey(pubkey, ctx, provider), P2PKGetScript, "pk"); + return std::make_unique( + InferPubkey(pubkey, ctx, provider)); } } if (txntype == TX_PUBKEYHASH) { @@ -817,8 +838,8 @@ CKeyID keyid(hash); CPubKey pubkey; if (provider.GetPubKey(keyid, pubkey)) { - return std::make_unique( - InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh"); + return std::make_unique( + InferPubkey(pubkey, ctx, provider)); } } if (txntype == TX_MULTISIG) { @@ -838,8 +859,7 @@ auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider); if (sub) { - return std::make_unique(std::move(sub), - ConvertP2SH, "sh"); + return std::make_unique(std::move(sub)); } } } diff --git a/src/script/sign.cpp b/src/script/sign.cpp --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -485,6 +485,8 @@ ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end()); ret.keys = a.keys; ret.keys.insert(b.keys.begin(), b.keys.end()); + ret.origins = a.origins; + ret.origins.insert(b.origins.begin(), b.origins.end()); return ret; }