mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-13 00:54:53 +01:00
Merge bitcoin/bitcoin#26567: Wallet: estimate the size of signed inputs using descriptors
10546a569cwallet: accurately account for the size of the witness stack (Antoine Poinsot)9b7ec393b8wallet: use descriptor satisfaction size to estimate inputs size (Antoine Poinsot)8d870a9873script/signingprovider: introduce a MultiSigningProvider (Antoine Poinsot)fa7c46b503descriptor: introduce a method to get the satisfaction size (Antoine Poinsot)bdba7667d2miniscript: introduce a helper to get the maximum witness size (Antoine Poinsot)4ab382c2cdminiscript: 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: utACK10546a569cachow101: re-ACK10546a569cTree-SHA512: 43ed1529fbd30af709d903c8c5063235e8c6a03b500bc8f144273d6184e23a53edf0fea9ef898ed57d8a40d73208b5d935cc73b94a24fad3ad3c63b3b2027174
This commit is contained in:
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user