diff options
author | Andrew Chow <github@achow101.com> | 2023-09-06 13:23:12 -0400 |
---|---|---|
committer | Andrew Chow <github@achow101.com> | 2023-09-06 13:31:03 -0400 |
commit | d2ccca253f4294b8f480e1d192913f4985a1af08 (patch) | |
tree | 2cdd4f310c9e808bc13e1aa10138cdf48413cc3c /src/script | |
parent | cf421820f50abcbd4f2709f200d3a78fb69fc698 (diff) | |
parent | 10546a569c6c96a5ec1b9708abf9ff5c8644f669 (diff) |
Merge bitcoin/bitcoin#26567: Wallet: estimate the size of signed inputs using descriptors
10546a569c6c96a5ec1b9708abf9ff5c8644f669 wallet: accurately account for the size of the witness stack (Antoine Poinsot)
9b7ec393b82ca9d7ada77d06e0835df0386a8b85 wallet: use descriptor satisfaction size to estimate inputs size (Antoine Poinsot)
8d870a98731e8db5ecc614bb5f7c064cbf30c7f4 script/signingprovider: introduce a MultiSigningProvider (Antoine Poinsot)
fa7c46b503f0b69630f55dc43021d2099e3515ba descriptor: introduce a method to get the satisfaction size (Antoine Poinsot)
bdba7667d2d65f31484760a8e8420c488fc5f801 miniscript: introduce a helper to get the maximum witness size (Antoine Poinsot)
4ab382c2cdb09fb4056711b4336807845cbe1ad5 miniscript: make GetStackSize independent of P2WSH context (Antoine Poinsot)
Pull request description:
The wallet currently estimates the size of a signed input by doing a dry run of the signing logic. This is unnecessary since all outputs we can sign for can be represented by a descriptor, and we can derive the size of a satisfaction ("signature") directly from the descriptor itself.
In addition, the current approach does not generalize well: dry runs of the signing logic are only possible for the most basic scripts. See for instance the discussion in #24149 around that.
This introduces a method to get the maximum size of a satisfaction from a descriptor, and makes the wallet use that instead of the dry-run.
ACKs for top commit:
sipa:
utACK 10546a569c6c96a5ec1b9708abf9ff5c8644f669
achow101:
re-ACK 10546a569c6c96a5ec1b9708abf9ff5c8644f669
Tree-SHA512: 43ed1529fbd30af709d903c8c5063235e8c6a03b500bc8f144273d6184e23a53edf0fea9ef898ed57d8a40d73208b5d935cc73b94a24fad3ad3c63b3b2027174
Diffstat (limited to 'src/script')
-rw-r--r-- | src/script/descriptor.cpp | 168 | ||||
-rw-r--r-- | src/script/descriptor.h | 12 | ||||
-rw-r--r-- | src/script/miniscript.h | 88 | ||||
-rw-r--r-- | src/script/signingprovider.cpp | 55 | ||||
-rw-r--r-- | src/script/signingprovider.h | 15 |
5 files changed, 327 insertions, 11 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 436ea9c093..2f3f2c7a1d 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -22,6 +22,7 @@ #include <util/vector.h> #include <memory> +#include <numeric> #include <optional> #include <string> #include <vector> @@ -706,6 +707,19 @@ public: } std::optional<OutputType> GetOutputType() const override { return std::nullopt; } + + std::optional<int64_t> ScriptSize() const override { return {}; } + + /** A helper for MaxSatisfactionWeight. + * + * @param use_max_sig Whether to assume ECDSA signatures will have a high-r. + * @return The maximum size of the satisfaction in raw bytes (with no witness meaning). + */ + virtual std::optional<int64_t> MaxSatSize(bool use_max_sig) const { return {}; } + + std::optional<int64_t> MaxSatisfactionWeight(bool) const override { return {}; } + + std::optional<int64_t> MaxSatisfactionElems() const override { return {}; } }; /** A parsed addr(A) descriptor. */ @@ -725,6 +739,8 @@ public: } bool IsSingleType() const final { return true; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; } + + std::optional<int64_t> ScriptSize() const override { return GetScriptForDestination(m_destination).size(); } }; /** A parsed raw(H) descriptor. */ @@ -746,6 +762,8 @@ public: } bool IsSingleType() const final { return true; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { return false; } + + std::optional<int64_t> ScriptSize() const override { return m_script.size(); } }; /** A parsed pk(P) descriptor. */ @@ -766,6 +784,21 @@ protected: public: PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly = false) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {} bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { + return 1 + (m_xonly ? 32 : m_pubkey_args[0]->GetSize()) + 1; + } + + std::optional<int64_t> MaxSatSize(bool use_max_sig) const override { + const auto ecdsa_sig_size = use_max_sig ? 72 : 71; + return 1 + (m_xonly ? 65 : ecdsa_sig_size); + } + + std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override { + return *MaxSatSize(use_max_sig) * WITNESS_SCALE_FACTOR; + } + + std::optional<int64_t> MaxSatisfactionElems() const override { return 1; } }; /** A parsed pkh(P) descriptor. */ @@ -782,6 +815,19 @@ public: PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pkh") {} std::optional<OutputType> GetOutputType() const override { return OutputType::LEGACY; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 1 + 20 + 1 + 1; } + + std::optional<int64_t> MaxSatSize(bool use_max_sig) const override { + const auto sig_size = use_max_sig ? 72 : 71; + return 1 + sig_size + 1 + m_pubkey_args[0]->GetSize(); + } + + std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override { + return *MaxSatSize(use_max_sig) * WITNESS_SCALE_FACTOR; + } + + std::optional<int64_t> MaxSatisfactionElems() const override { return 2; } }; /** A parsed wpkh(P) descriptor. */ @@ -798,6 +844,19 @@ public: WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "wpkh") {} std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 20; } + + std::optional<int64_t> MaxSatSize(bool use_max_sig) const override { + const auto sig_size = use_max_sig ? 72 : 71; + return (1 + sig_size + 1 + 33); + } + + std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override { + return MaxSatSize(use_max_sig); + } + + std::optional<int64_t> MaxSatisfactionElems() const override { return 2; } }; /** A parsed combo(P) descriptor. */ @@ -842,6 +901,24 @@ protected: public: MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti" : "multi"), m_threshold(threshold), m_sorted(sorted) {} bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { + const auto n_keys = m_pubkey_args.size(); + auto op = [](int64_t acc, const std::unique_ptr<PubkeyProvider>& pk) { return acc + 1 + pk->GetSize();}; + const auto pubkeys_size{std::accumulate(m_pubkey_args.begin(), m_pubkey_args.end(), int64_t{0}, op)}; + return 1 + BuildScript(n_keys).size() + BuildScript(m_threshold).size() + pubkeys_size; + } + + std::optional<int64_t> MaxSatSize(bool use_max_sig) const override { + const auto sig_size = use_max_sig ? 72 : 71; + return (1 + (1 + sig_size) * m_threshold); + } + + std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override { + return *MaxSatSize(use_max_sig) * WITNESS_SCALE_FACTOR; + } + + std::optional<int64_t> MaxSatisfactionElems() const override { return 1 + m_threshold; } }; /** A parsed (sorted)multi_a(...) descriptor. Always uses x-only pubkeys. */ @@ -867,6 +944,17 @@ protected: public: MultiADescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti_a" : "multi_a"), m_threshold(threshold), m_sorted(sorted) {} bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { + const auto n_keys = m_pubkey_args.size(); + return (1 + 32 + 1) * n_keys + BuildScript(m_threshold).size() + 1; + } + + std::optional<int64_t> MaxSatSize(bool use_max_sig) const override { + return (1 + 65) * m_threshold + (m_pubkey_args.size() - m_threshold); + } + + std::optional<int64_t> MaxSatisfactionElems() const override { return m_pubkey_args.size(); } }; /** A parsed sh(...) descriptor. */ @@ -879,16 +967,39 @@ protected: if (ret.size()) out.scripts.emplace(CScriptID(scripts[0]), scripts[0]); return ret; } + + bool IsSegwit() const { return m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32; } + public: SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {} std::optional<OutputType> GetOutputType() const override { assert(m_subdescriptor_args.size() == 1); - if (m_subdescriptor_args[0]->GetOutputType() == OutputType::BECH32) return OutputType::P2SH_SEGWIT; + if (IsSegwit()) return OutputType::P2SH_SEGWIT; return OutputType::LEGACY; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 20 + 1; } + + std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override { + if (const auto sat_size = m_subdescriptor_args[0]->MaxSatSize(use_max_sig)) { + if (const auto subscript_size = m_subdescriptor_args[0]->ScriptSize()) { + // The subscript is never witness data. + const auto subscript_weight = (1 + *subscript_size) * WITNESS_SCALE_FACTOR; + // The weight depends on whether the inner descriptor is satisfied using the witness stack. + if (IsSegwit()) return subscript_weight + *sat_size; + return subscript_weight + *sat_size * WITNESS_SCALE_FACTOR; + } + } + return {}; + } + + std::optional<int64_t> MaxSatisfactionElems() const override { + if (const auto sub_elems = m_subdescriptor_args[0]->MaxSatisfactionElems()) return 1 + *sub_elems; + return {}; + } }; /** A parsed wsh(...) descriptor. */ @@ -905,6 +1016,26 @@ public: WSHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "wsh") {} std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 32; } + + std::optional<int64_t> MaxSatSize(bool use_max_sig) const override { + if (const auto sat_size = m_subdescriptor_args[0]->MaxSatSize(use_max_sig)) { + if (const auto subscript_size = m_subdescriptor_args[0]->ScriptSize()) { + return GetSizeOfCompactSize(*subscript_size) + *subscript_size + *sat_size; + } + } + return {}; + } + + std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const override { + return MaxSatSize(use_max_sig); + } + + std::optional<int64_t> MaxSatisfactionElems() const override { + if (const auto sub_elems = m_subdescriptor_args[0]->MaxSatisfactionElems()) return 1 + *sub_elems; + return {}; + } }; /** A parsed tr(...) descriptor. */ @@ -958,6 +1089,18 @@ public: } std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 32; } + + std::optional<int64_t> MaxSatisfactionWeight(bool) const override { + // FIXME: We assume keypath spend, which can lead to very large underestimations. + return 1 + 65; + } + + std::optional<int64_t> MaxSatisfactionElems() const override { + // FIXME: See above, we assume keypath spend. + return 1; + } }; /* We instantiate Miniscript here with a simple integer as key type. @@ -1041,6 +1184,17 @@ public: bool IsSolvable() const override { return true; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return m_node->ScriptSize(); } + + std::optional<int64_t> MaxSatSize(bool) const override { + // For Miniscript we always assume high-R ECDSA signatures. + return m_node->GetWitnessSize(); + } + + std::optional<int64_t> MaxSatisfactionElems() const override { + return m_node->GetStackSize(); + } }; /** A parsed rawtr(...) descriptor. */ @@ -1059,6 +1213,18 @@ public: RawTRDescriptor(std::unique_ptr<PubkeyProvider> output_key) : DescriptorImpl(Vector(std::move(output_key)), "rawtr") {} std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32M; } bool IsSingleType() const final { return true; } + + std::optional<int64_t> ScriptSize() const override { return 1 + 1 + 32; } + + std::optional<int64_t> MaxSatisfactionWeight(bool) const override { + // We can't know whether there is a script path, so assume key path spend. + return 1 + 65; + } + + std::optional<int64_t> MaxSatisfactionElems() const override { + // See above, we assume keypath spend. + return 1; + } }; //////////////////////////////////////////////////////////////////////////// diff --git a/src/script/descriptor.h b/src/script/descriptor.h index c6860c5cf6..caa5d1608d 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -146,6 +146,18 @@ struct Descriptor { /** @return The OutputType of the scriptPubKey(s) produced by this descriptor. Or nullopt if indeterminate (multiple or none) */ virtual std::optional<OutputType> GetOutputType() const = 0; + + /** Get the size of the scriptPubKey for this descriptor. */ + virtual std::optional<int64_t> ScriptSize() const = 0; + + /** Get the maximum size of a satisfaction for this descriptor, in weight units. + * + * @param use_max_sig Whether to assume ECDSA signatures will have a high-r. + */ + virtual std::optional<int64_t> MaxSatisfactionWeight(bool use_max_sig) const = 0; + + /** Get the maximum size number of stack elements for satisfying this descriptor. */ + virtual std::optional<int64_t> MaxSatisfactionElems() const = 0; }; /** Parse a `descriptor` string. Included private keys are put in `out`. diff --git a/src/script/miniscript.h b/src/script/miniscript.h index b58740a125..4c6bd0bb1d 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -337,6 +337,15 @@ struct StackSize { StackSize(MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : sat(in_sat), dsat(in_dsat) {}; }; +struct WitnessSize { + //! Maximum witness size to satisfy; + MaxInt<uint32_t> sat; + //! Maximum witness size to dissatisfy; + MaxInt<uint32_t> dsat; + + WitnessSize(MaxInt<uint32_t> in_sat, MaxInt<uint32_t> in_dsat) : sat(in_sat), dsat(in_dsat) {}; +}; + struct NoDupCheck {}; } // namespace internal @@ -360,6 +369,8 @@ private: const internal::Ops ops; //! Cached stack size bounds. const internal::StackSize ss; + //! Cached witness size bounds. + const internal::WitnessSize ws; //! Cached expression type (computed by CalcType and fed through SanitizeType). const Type typ; //! Cached script length (computed by CalcScriptLen). @@ -846,6 +857,56 @@ private: assert(false); } + internal::WitnessSize CalcWitnessSize() const { + switch (fragment) { + case Fragment::JUST_0: return {{}, 0}; + case Fragment::JUST_1: + case Fragment::OLDER: + case Fragment::AFTER: return {0, {}}; + case Fragment::PK_K: return {1 + 72, 1}; + case Fragment::PK_H: return {1 + 72 + 1 + 33, 1 + 1 + 33}; + case Fragment::SHA256: + case Fragment::RIPEMD160: + case Fragment::HASH256: + case Fragment::HASH160: return {1 + 32, {}}; + case Fragment::ANDOR: { + const auto sat{(subs[0]->ws.sat + subs[1]->ws.sat) | (subs[0]->ws.dsat + subs[2]->ws.sat)}; + const auto dsat{subs[0]->ws.dsat + subs[2]->ws.dsat}; + return {sat, dsat}; + } + case Fragment::AND_V: return {subs[0]->ws.sat + subs[1]->ws.sat, {}}; + case Fragment::AND_B: return {subs[0]->ws.sat + subs[1]->ws.sat, subs[0]->ws.dsat + subs[1]->ws.dsat}; + case Fragment::OR_B: { + const auto sat{(subs[0]->ws.dsat + subs[1]->ws.sat) | (subs[0]->ws.sat + subs[1]->ws.dsat)}; + const auto dsat{subs[0]->ws.dsat + subs[1]->ws.dsat}; + return {sat, dsat}; + } + case Fragment::OR_C: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), {}}; + case Fragment::OR_D: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), subs[0]->ws.dsat + subs[1]->ws.dsat}; + case Fragment::OR_I: return {(subs[0]->ws.sat + 1 + 1) | (subs[1]->ws.sat + 1), (subs[0]->ws.dsat + 1 + 1) | (subs[1]->ws.dsat + 1)}; + case Fragment::MULTI: return {k * (1 + 72) + 1, k + 1}; + case Fragment::WRAP_A: + case Fragment::WRAP_N: + case Fragment::WRAP_S: + case Fragment::WRAP_C: return subs[0]->ws; + case Fragment::WRAP_D: return {1 + 1 + subs[0]->ws.sat, 1}; + case Fragment::WRAP_V: return {subs[0]->ws.sat, {}}; + case Fragment::WRAP_J: return {subs[0]->ws.sat, 1}; + case Fragment::THRESH: { + auto sats = Vector(internal::MaxInt<uint32_t>(0)); + for (const auto& sub : subs) { + auto next_sats = Vector(sats[0] + sub->ws.dsat); + for (size_t j = 1; j < sats.size(); ++j) next_sats.push_back((sats[j] + sub->ws.dsat) | (sats[j - 1] + sub->ws.sat)); + next_sats.push_back(sats[sats.size() - 1] + sub->ws.sat); + sats = std::move(next_sats); + } + assert(k <= sats.size()); + return {sats[k], sats[0]}; + } + } + assert(false); + } + template<typename Ctx> internal::InputResult ProduceInput(const Ctx& ctx) const { using namespace internal; @@ -1148,22 +1209,29 @@ public: return true; } - /** Return the maximum number of stack elements needed to satisfy this script non-malleably, including - * the script push. */ + /** Return the maximum number of stack elements needed to satisfy this script non-malleably. + * This does not account for the P2WSH script push. */ std::optional<uint32_t> GetStackSize() const { if (!ss.sat.valid) return {}; - return ss.sat.value + 1; + return ss.sat.value; } //! Check the maximum stack size for this script against the policy limit. bool CheckStackSize() const { - if (const auto ss = GetStackSize()) return *ss - 1 <= MAX_STANDARD_P2WSH_STACK_ITEMS; + if (const auto ss = GetStackSize()) return *ss <= MAX_STANDARD_P2WSH_STACK_ITEMS; return true; } //! Whether no satisfaction exists for this node. bool IsNotSatisfiable() const { return !GetStackSize(); } + /** Return the maximum size in bytes of a witness to satisfy this script non-malleably. Note this does + * not include the witness script push. */ + std::optional<uint32_t> GetWitnessSize() const { + if (!ws.sat.valid) return {}; + return ws.sat.value; + } + //! Return the expression type. Type GetType() const { return typ; } @@ -1260,12 +1328,12 @@ public: bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; } // Constructors with various argument combinations, which bypass the duplicate key check. - Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} - Node(internal::NoDupCheck, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0) : fragment(nt), k(val), data(std::move(arg)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, Fragment nt, std::vector<Key> key, uint32_t val = 0) : fragment(nt), k(val), keys(std::move(key)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, Fragment nt, std::vector<NodeRef<Key>> sub, uint32_t val = 0) : fragment(nt), k(val), subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} + Node(internal::NoDupCheck, Fragment nt, uint32_t val = 0) : fragment(nt), k(val), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {} // Constructors with various argument combinations, which do perform the duplicate key check. template <typename Ctx> Node(const Ctx& ctx, Fragment nt, std::vector<NodeRef<Key>> sub, std::vector<unsigned char> arg, uint32_t val = 0) : Node(internal::NoDupCheck{}, nt, std::move(sub), std::move(arg), val) { DuplicateKeyCheck(ctx); } diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index f3a69e5d21..168b3030cc 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -225,6 +225,61 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& } return CKeyID(); } + +void MultiSigningProvider::AddProvider(std::unique_ptr<SigningProvider> provider) +{ + m_providers.push_back(std::move(provider)); +} + +bool MultiSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const +{ + for (const auto& provider: m_providers) { + if (provider->GetCScript(scriptid, script)) return true; + } + return false; +} + +bool MultiSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const +{ + for (const auto& provider: m_providers) { + if (provider->GetPubKey(keyid, pubkey)) return true; + } + return false; +} + + +bool MultiSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const +{ + for (const auto& provider: m_providers) { + if (provider->GetKeyOrigin(keyid, info)) return true; + } + return false; +} + +bool MultiSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const +{ + for (const auto& provider: m_providers) { + if (provider->GetKey(keyid, key)) return true; + } + return false; +} + +bool MultiSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const +{ + for (const auto& provider: m_providers) { + if (provider->GetTaprootSpendData(output_key, spenddata)) return true; + } + return false; +} + +bool MultiSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const +{ + for (const auto& provider: m_providers) { + if (provider->GetTaprootBuilder(output_key, builder)) return true; + } + return false; +} + /*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b) { NodeInfo ret; diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index 712e2e73d1..3298376389 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -298,4 +298,19 @@ public: /** Return the CKeyID of the key involved in a script (if there is a unique one). */ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest); +/** A signing provider to be used to interface with multiple signing providers at once. */ +class MultiSigningProvider: public SigningProvider { + std::vector<std::unique_ptr<SigningProvider>> m_providers; + +public: + void AddProvider(std::unique_ptr<SigningProvider> provider); + + bool GetCScript(const CScriptID& scriptid, CScript& script) const override; + bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; + bool GetKey(const CKeyID& keyid, CKey& key) const override; + bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override; + bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override; +}; + #endif // BITCOIN_SCRIPT_SIGNINGPROVIDER_H |