descriptor: introduce a method to get the satisfaction size

In the wallet code, we are currently estimating the size of a signed
input by doing a dry run of the signing logic. This is unnecessary as
all outputs we are able to sign for can be represented by a descriptor,
and we can derive the size of a satisfaction ("signature") from the
descriptor itself directly.
In addition, this approach does not scale: getting the size of a
satisfaction through a dry run of the signing logic is only possible for
the most basic scripts.

This commit introduces the computation of the size of satisfaction per
descriptor. It's a bit intricate for 2 main reasons:
- We want to conserve the behaviour of the current dry-run logic used by
  the wallet that sometimes assumes ECDSA signatures will be low-r,
  sometimes not (when we don't create them).
- We need to account for the witness discount. A single descriptor may
  sometimes benefit of it, sometimes not (for instance `pk()` if used as
  top-level versus if used inside `wsh()`).
This commit is contained in:
Antoine Poinsot
2022-08-19 18:33:54 +02:00
parent bdba7667d2
commit fa7c46b503
5 changed files with 166 additions and 2 deletions

View File

@ -22,6 +22,7 @@
#include <util/vector.h>
#include <memory>
#include <numeric>
#include <optional>
#include <string>
#include <vector>
@ -706,6 +707,17 @@ 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 {}; }
};
/** A parsed addr(A) descriptor. */
@ -725,6 +737,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 +760,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 +782,19 @@ 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;
}
};
/** A parsed pkh(P) descriptor. */
@ -782,6 +811,17 @@ 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;
}
};
/** A parsed wpkh(P) descriptor. */
@ -798,6 +838,17 @@ 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);
}
};
/** A parsed combo(P) descriptor. */
@ -842,6 +893,22 @@ 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;
}
};
/** A parsed (sorted)multi_a(...) descriptor. Always uses x-only pubkeys. */
@ -867,6 +934,15 @@ 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);
}
};
/** A parsed sh(...) descriptor. */
@ -879,16 +955,34 @@ 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 {};
}
};
/** A parsed wsh(...) descriptor. */
@ -905,6 +999,21 @@ 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);
}
};
/** A parsed tr(...) descriptor. */
@ -958,6 +1067,13 @@ 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;
}
};
/* We instantiate Miniscript here with a simple integer as key type.
@ -1041,6 +1157,13 @@ 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();
}
};
/** A parsed rawtr(...) descriptor. */
@ -1059,6 +1182,13 @@ 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;
}
};
////////////////////////////////////////////////////////////////////////////

View File

@ -146,6 +146,15 @@ 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;
};
/** Parse a `descriptor` string. Included private keys are put in `out`.

View File

@ -150,6 +150,17 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int
parse_pub = Parse(pub, keys_pub, error);
BOOST_CHECK_MESSAGE(parse_pub, error);
// We must be able to estimate the max satisfaction size for any solvable descriptor top descriptor (but combo).
const bool is_nontop_or_nonsolvable{!parse_priv->IsSolvable() || !parse_priv->GetOutputType()};
for (const bool use_max_sig: {true, false}) {
BOOST_CHECK_MESSAGE(parse_priv->MaxSatisfactionWeight(use_max_sig) || is_nontop_or_nonsolvable, prv);
BOOST_CHECK_MESSAGE(parse_pub->MaxSatisfactionWeight(use_max_sig) || is_nontop_or_nonsolvable, pub);
}
// The ScriptSize() must match the size of the Script string. (ScriptSize() is set for all descs but 'combo()'.)
const bool is_combo{!parse_priv->IsSingleType()};
BOOST_CHECK_MESSAGE(is_combo || parse_priv->ScriptSize() == scripts[0][0].size() / 2, "Invalid ScriptSize() for " + prv);
// Check that the correct OutputType is inferred
BOOST_CHECK(parse_priv->GetOutputType() == type);
BOOST_CHECK(parse_pub->GetOutputType() == type);
@ -434,6 +445,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/std::nullopt);
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M, /*op_desc_id=*/uint256S("af482b44c10b737b678e1091584818372e169e2dc5219e2877fabe1b83ae467b"));
Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,multi_a(1,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,multi_a(1,669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,multi_a(1,669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"5120eb5bd3894327d75093891cc3a62506df7d58ec137fcd104cdd285d67816074f3"}}, OutputType::BECH32M);
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Multiple ']' characters found for a single pubkey"); // Double key origin descriptor
CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint

View File

@ -112,7 +112,7 @@ static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_prov
{
// Trivial helpers.
(void)desc.IsRange();
(void)desc.IsSolvable();
const bool is_solvable{desc.IsSolvable()};
(void)desc.IsSingleType();
(void)desc.GetOutputType();
@ -131,7 +131,18 @@ static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_prov
// If we could serialize to script we must be able to infer using the same provider.
if (!out_scripts.empty()) {
assert(InferDescriptor(out_scripts.back(), sig_provider));
// The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.)
const bool is_combo{!desc.IsSingleType()};
assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
}
const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
// We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()};
const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig};
assert(is_input_size_info_set || is_nontop_or_nonsolvable);
}
void initialize_descriptor_parse()

View File

@ -31,6 +31,8 @@ public:
bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const override { return false; };
bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override { return false; }
void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const override {}
std::optional<int64_t> ScriptSize() const override { return {}; }
std::optional<int64_t> MaxSatisfactionWeight(bool) const override { return {}; }
};
BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup)