From 1bbf46e2dae4599d04c79aaacf7c5db00b2e707f Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Wed, 7 Aug 2024 16:29:06 -0400 Subject: [PATCH] descriptors: Change Parse to return vector of descriptors When given a descriptor which contins a multipath derivation specifier, a vector of descriptors will be returned. --- src/bench/descriptors.cpp | 4 +- src/bench/wallet_ismine.cpp | 4 +- src/qt/test/wallettests.cpp | 6 ++- src/rpc/mining.cpp | 49 ++++++++++++------------ src/rpc/output_script.cpp | 37 +++++++++++++----- src/rpc/util.cpp | 22 ++++++----- src/script/descriptor.cpp | 15 ++++++-- src/script/descriptor.h | 4 +- src/test/descriptor_tests.cpp | 19 +++++---- src/test/fuzz/descriptor_parse.cpp | 30 ++++++++++++--- src/wallet/rpc/backup.cpp | 10 +++-- src/wallet/rpc/spend.cpp | 8 ++-- src/wallet/scriptpubkeyman.cpp | 10 +++-- src/wallet/test/fuzz/notifications.cpp | 2 +- src/wallet/test/fuzz/scriptpubkeyman.cpp | 6 +-- src/wallet/test/ismine_tests.cpp | 7 ++-- src/wallet/test/psbt_wallet_tests.cpp | 5 ++- src/wallet/test/util.cpp | 5 ++- src/wallet/test/wallet_tests.cpp | 5 ++- src/wallet/wallet.cpp | 21 +++++----- src/wallet/walletutil.cpp | 4 +- src/wallet/walletutil.h | 8 +++- 22 files changed, 174 insertions(+), 107 deletions(-) diff --git a/src/bench/descriptors.cpp b/src/bench/descriptors.cpp index 5d6bcb8ce8729..26976fa6693fd 100644 --- a/src/bench/descriptors.cpp +++ b/src/bench/descriptors.cpp @@ -18,12 +18,12 @@ static void ExpandDescriptor(benchmark::Bench& bench) const std::pair range = {0, 1000}; FlatSigningProvider provider; std::string error; - auto desc = Parse(desc_str, provider, error); + auto descs = Parse(desc_str, provider, error); bench.run([&] { for (int i = range.first; i <= range.second; ++i) { std::vector scripts; - bool success = desc->Expand(i, provider, scripts, provider); + bool success = descs[0]->Expand(i, provider, scripts, provider); assert(success); } }); diff --git a/src/bench/wallet_ismine.cpp b/src/bench/wallet_ismine.cpp index 753404b526193..4189c51db06dc 100644 --- a/src/bench/wallet_ismine.cpp +++ b/src/bench/wallet_ismine.cpp @@ -43,8 +43,8 @@ static void WalletIsMine(benchmark::Bench& bench, bool legacy_wallet, int num_co key.MakeNewKey(/*fCompressed=*/true); FlatSigningProvider keys; std::string error; - std::unique_ptr desc = Parse("combo(" + EncodeSecret(key) + ")", keys, error, /*require_checksum=*/false); - WalletDescriptor w_desc(std::move(desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/0, /*next_index=*/0); + std::vector> desc = Parse("combo(" + EncodeSecret(key) + ")", keys, error, /*require_checksum=*/false); + WalletDescriptor w_desc(std::move(desc.at(0)), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/0, /*next_index=*/0); auto spkm = wallet->AddWalletDescriptor(w_desc, keys, /*label=*/"", /*internal=*/false); assert(spkm); } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 603df0b15f2c7..8fdb56dd7eaf8 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -218,8 +218,10 @@ std::shared_ptr SetupDescriptorsWallet(interfaces::Node& node, TestChai // Add the coinbase key FlatSigningProvider provider; std::string error; - std::unique_ptr desc = Parse("combo(" + EncodeSecret(test.coinbaseKey) + ")", provider, error, /* require_checksum=*/ false); - assert(desc); + auto descs = Parse("combo(" + EncodeSecret(test.coinbaseKey) + ")", provider, error, /* require_checksum=*/ false); + assert(!descs.empty()); + assert(descs.size() == 1); + auto& desc = descs.at(0); WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1); if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false); CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 7e420dcd9b509..1ad7ee36db575 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -180,35 +180,36 @@ static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const static bool getScriptFromDescriptor(const std::string& descriptor, CScript& script, std::string& error) { FlatSigningProvider key_provider; - const auto desc = Parse(descriptor, key_provider, error, /* require_checksum = */ false); - if (desc) { - if (desc->IsRange()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptor not accepted. Maybe pass through deriveaddresses first?"); - } - - FlatSigningProvider provider; - std::vector scripts; - if (!desc->Expand(0, key_provider, scripts, provider)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys"); - } + const auto descs = Parse(descriptor, key_provider, error, /* require_checksum = */ false); + if (descs.empty()) return false; + if (descs.size() > 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Multipath descriptor not accepted"); + } + const auto& desc = descs.at(0); + if (desc->IsRange()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptor not accepted. Maybe pass through deriveaddresses first?"); + } - // Combo descriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1 - CHECK_NONFATAL(scripts.size() > 0 && scripts.size() <= 4); + FlatSigningProvider provider; + std::vector scripts; + if (!desc->Expand(0, key_provider, scripts, provider)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys"); + } - if (scripts.size() == 1) { - script = scripts.at(0); - } else if (scripts.size() == 4) { - // For uncompressed keys, take the 3rd script, since it is p2wpkh - script = scripts.at(2); - } else { - // Else take the 2nd script, since it is p2pkh - script = scripts.at(1); - } + // Combo descriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1 + CHECK_NONFATAL(scripts.size() > 0 && scripts.size() <= 4); - return true; + if (scripts.size() == 1) { + script = scripts.at(0); + } else if (scripts.size() == 4) { + // For uncompressed keys, take the 3rd script, since it is p2wpkh + script = scripts.at(2); } else { - return false; + // Else take the 2nd script, since it is p2pkh + script = scripts.at(1); } + + return true; } static RPCHelpMan generatetodescriptor() diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp index 65a9be2762fc8..551a7f780728e 100644 --- a/src/rpc/output_script.cpp +++ b/src/rpc/output_script.cpp @@ -175,7 +175,11 @@ static RPCHelpMan getdescriptorinfo() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"}, + {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys. For a multipath descriptor, only the first will be returned."}, + {RPCResult::Type::ARR, "multipath_expansion", /*optional=*/true, "All descriptors produced by expanding multipath derivation elements. Only if the provided descriptor specifies multipath derivation elements.", + { + {RPCResult::Type::STR, "", ""}, + }}, {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"}, {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"}, {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"}, @@ -191,16 +195,25 @@ static RPCHelpMan getdescriptorinfo() { FlatSigningProvider provider; std::string error; - auto desc = Parse(request.params[0].get_str(), provider, error); - if (!desc) { + auto descs = Parse(request.params[0].get_str(), provider, error); + if (descs.empty()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } UniValue result(UniValue::VOBJ); - result.pushKV("descriptor", desc->ToString()); + result.pushKV("descriptor", descs.at(0)->ToString()); + + if (descs.size() > 1) { + UniValue multipath_descs(UniValue::VARR); + for (const auto& d : descs) { + multipath_descs.push_back(d->ToString()); + } + result.pushKV("multipath_expansion", multipath_descs); + } + result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str())); - result.pushKV("isrange", desc->IsRange()); - result.pushKV("issolvable", desc->IsSolvable()); + result.pushKV("isrange", descs.at(0)->IsRange()); + result.pushKV("issolvable", descs.at(0)->IsSolvable()); result.pushKV("hasprivatekeys", provider.keys.size() > 0); return result; }, @@ -221,7 +234,8 @@ static RPCHelpMan deriveaddresses() " tr(,multi_a(,,,...)) P2TR-multisig outputs for the given threshold and pubkeys\n" "\nIn the above, either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n" - "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"}, + "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n" + "Note that only descriptors that specify a single derivation path can be derived.\n"}, { {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."}, {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."}, @@ -250,11 +264,14 @@ static RPCHelpMan deriveaddresses() FlatSigningProvider key_provider; std::string error; - auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true); - if (!desc) { + auto descs = Parse(desc_str, key_provider, error, /* require_checksum = */ true); + if (descs.empty()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } - + if (descs.size() > 1) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor with multipath derivation path specifiers are not allowed"); + } + auto& desc = descs.at(0); if (!desc->IsRange() && request.params.size() > 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor"); } diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 4df4466c4940d..ee3dfa45ccfbc 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -1337,24 +1337,26 @@ std::vector EvalDescriptorStringOrObject(const UniValue& scanobject, Fl } std::string error; - auto desc = Parse(desc_str, provider, error); - if (!desc) { + auto descs = Parse(desc_str, provider, error); + if (descs.empty()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } - if (!desc->IsRange()) { + if (!descs.at(0)->IsRange()) { range.first = 0; range.second = 0; } std::vector ret; for (int i = range.first; i <= range.second; ++i) { - std::vector scripts; - if (!desc->Expand(i, provider, scripts, provider)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str)); - } - if (expand_priv) { - desc->ExpandPrivate(/*pos=*/i, provider, /*out=*/provider); + for (const auto& desc : descs) { + std::vector scripts; + if (!desc->Expand(i, provider, scripts, provider)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str)); + } + if (expand_priv) { + desc->ExpandPrivate(/*pos=*/i, provider, /*out=*/provider); + } + std::move(scripts.begin(), scripts.end(), std::back_inserter(ret)); } - std::move(scripts.begin(), scripts.end(), std::back_inserter(ret)); } return ret; } diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index ab77bb6384131..ac46f9d29e8ae 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -2364,14 +2364,21 @@ bool CheckChecksum(Span& sp, bool require_checksum, std::string& err return true; } -std::unique_ptr Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum) +std::vector> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum) { Span sp{descriptor}; - if (!CheckChecksum(sp, require_checksum, error)) return nullptr; + if (!CheckChecksum(sp, require_checksum, error)) return {}; uint32_t key_exp_index = 0; auto ret = ParseScript(key_exp_index, sp, ParseScriptContext::TOP, out, error); - if (sp.size() == 0 && !ret.empty()) return std::unique_ptr(std::move(ret.at(0))); - return nullptr; + if (sp.size() == 0 && !ret.empty()) { + std::vector> descs; + descs.reserve(ret.size()); + for (auto& r : ret) { + descs.emplace_back(std::unique_ptr(std::move(r))); + } + return descs; + } + return {}; } std::string GetDescriptorChecksum(const std::string& descriptor) diff --git a/src/script/descriptor.h b/src/script/descriptor.h index e78a775330270..473649a314491 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -173,9 +173,9 @@ struct Descriptor { * is set, the checksum is mandatory - otherwise it is optional. * * If a parse error occurs, or the checksum is missing/invalid, or anything - * else is wrong, `nullptr` is returned. + * else is wrong, an empty vector is returned. */ -std::unique_ptr Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum = false); +std::vector> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum = false); /** Get the checksum for a `descriptor`. * diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index e6821dd321884..f9fbbf8cabeb0 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -25,8 +25,8 @@ void CheckUnparsable(const std::string& prv, const std::string& pub, const std:: std::string error; auto parse_priv = Parse(prv, keys_priv, error); auto parse_pub = Parse(pub, keys_pub, error); - BOOST_CHECK_MESSAGE(!parse_priv, prv); - BOOST_CHECK_MESSAGE(!parse_pub, pub); + BOOST_CHECK_MESSAGE(parse_priv.empty(), prv); + BOOST_CHECK_MESSAGE(parse_pub.empty(), pub); BOOST_CHECK_EQUAL(error, expected_error); } @@ -139,19 +139,22 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int std::set> left_paths = paths; std::string error; - std::unique_ptr parse_priv; - std::unique_ptr parse_pub; + std::vector> parse_privs; + std::vector> parse_pubs; // Check that parsing succeeds. if (replace_apostrophe_with_h_in_prv) { prv = UseHInsteadOfApostrophe(prv); } - parse_priv = Parse(prv, keys_priv, error); - BOOST_CHECK_MESSAGE(parse_priv, error); + parse_privs = Parse(prv, keys_priv, error); + BOOST_CHECK_MESSAGE(!parse_privs.empty(), error); if (replace_apostrophe_with_h_in_pub) { pub = UseHInsteadOfApostrophe(pub); } - parse_pub = Parse(pub, keys_pub, error); - BOOST_CHECK_MESSAGE(parse_pub, error); + parse_pubs = Parse(pub, keys_pub, error); + BOOST_CHECK_MESSAGE(!parse_pubs.empty(), error); + + auto& parse_priv = parse_privs.at(0); + auto& parse_pub = parse_pubs.at(0); // 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()}; diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index b9a5560ffb905..14322effbefaa 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -15,14 +15,24 @@ MockedDescriptorConverter MOCKED_DESC_CONVERTER; /** Test a successfully parsed descriptor. */ -static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy) +static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy, std::optional& is_ranged, std::optional& is_solvable) { // Trivial helpers. (void)desc.IsRange(); - const bool is_solvable{desc.IsSolvable()}; (void)desc.IsSingleType(); (void)desc.GetOutputType(); + if (is_ranged.has_value()) { + assert(desc.IsRange() == *is_ranged); + } else { + is_ranged = desc.IsRange(); + } + if (is_solvable.has_value()) { + assert(desc.IsSolvable() == *is_solvable); + } else { + is_solvable = desc.IsSolvable(); + } + // Serialization to string representation. (void)desc.ToString(); (void)desc.ToPrivateString(sig_provider, dummy); @@ -48,7 +58,7 @@ static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_prov const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)}; const auto max_elems{desc.MaxSatisfactionElems()}; // 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_nontop_or_nonsolvable{!*is_solvable || !desc.GetOutputType()}; const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems}; assert(is_input_size_info_set || is_nontop_or_nonsolvable); } @@ -77,7 +87,12 @@ FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse) FlatSigningProvider signing_provider; std::string error; const auto desc = Parse(*descriptor, signing_provider, error); - if (desc) TestDescriptor(*desc, signing_provider, error); + std::optional is_ranged; + std::optional is_solvable; + for (const auto& d : desc) { + assert(d); + TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable); + } } } @@ -91,6 +106,11 @@ FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) std::string error; for (const bool require_checksum : {true, false}) { const auto desc = Parse(descriptor, signing_provider, error, require_checksum); - if (desc) TestDescriptor(*desc, signing_provider, error); + std::optional is_ranged; + std::optional is_solvable; + for (const auto& d : desc) { + assert(d); + TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable); + } } } diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index 8cddb8b099048..d92c2b6770ae5 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -1061,10 +1061,11 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::mapGetOutputType() == OutputType::BECH32M) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets"); } @@ -1452,10 +1453,11 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c // Parse descriptor string FlatSigningProvider keys; std::string error; - auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true); - if (!parsed_desc) { + auto parsed_descs = Parse(descriptor, keys, error, /* require_checksum = */ true); + if (parsed_descs.empty()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } + auto& parsed_desc = parsed_descs.at(0); // Range check int64_t range_start = 0, range_end = 1, next_index = 0; diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index ac2a4826f05d9..ba03804270e4b 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -660,11 +660,13 @@ CreatedTransactionResult FundTransaction(CWallet& wallet, const CMutableTransact FlatSigningProvider desc_out; std::string error; std::vector scripts_temp; - std::unique_ptr desc = Parse(desc_str, desc_out, error, true); - if (!desc) { + auto descs = Parse(desc_str, desc_out, error, true); + if (descs.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unable to parse descriptor '%s': %s", desc_str, error)); } - desc->Expand(0, desc_out, scripts_temp, desc_out); + for (auto& desc : descs) { + desc->Expand(0, desc_out, scripts_temp, desc_out); + } coinControl.m_external_provider.Merge(std::move(desc_out)); } } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index c64aff5fe25e8..6b57f8c3477cd 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1812,8 +1812,9 @@ std::optional LegacyScriptPubKeyMan::MigrateToDescriptor() std::string desc_str = "combo(" + origin_str + HexStr(key.GetPubKey()) + ")"; FlatSigningProvider keys; std::string error; - std::unique_ptr desc = Parse(desc_str, keys, error, false); - WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); + std::vector> descs = Parse(desc_str, keys, error, false); + CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor + WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys auto desc_spk_man = std::unique_ptr(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size)); @@ -1856,9 +1857,10 @@ std::optional LegacyScriptPubKeyMan::MigrateToDescriptor() std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)"; FlatSigningProvider keys; std::string error; - std::unique_ptr desc = Parse(desc_str, keys, error, false); + std::vector> descs = Parse(desc_str, keys, error, false); + CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor uint32_t chain_counter = std::max((i == 1 ? chain.nInternalChainCounter : chain.nExternalChainCounter), (uint32_t)0); - WalletDescriptor w_desc(std::move(desc), 0, 0, chain_counter, 0); + WalletDescriptor w_desc(std::move(descs.at(0)), 0, 0, chain_counter, 0); // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys auto desc_spk_man = std::unique_ptr(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size)); diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp index 792079e6c6569..a7015f6685b27 100644 --- a/src/wallet/test/fuzz/notifications.cpp +++ b/src/wallet/test/fuzz/notifications.cpp @@ -68,7 +68,7 @@ void ImportDescriptors(CWallet& wallet, const std::string& seed_insecure) FlatSigningProvider keys; std::string error; - auto parsed_desc = Parse(descriptor, keys, error, /*require_checksum=*/false); + auto parsed_desc = std::move(Parse(descriptor, keys, error, /*require_checksum=*/false).at(0)); assert(parsed_desc); assert(error.empty()); assert(parsed_desc->IsRange()); diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp index 835470aeae99a..2cc54b171708f 100644 --- a/src/wallet/test/fuzz/scriptpubkeyman.cpp +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -69,10 +69,10 @@ static std::optional> CreateWal FlatSigningProvider keys; std::string error; - std::unique_ptr parsed_desc{Parse(desc_str.value(), keys, error, false)}; - if (!parsed_desc) return std::nullopt; + std::vector> parsed_descs = Parse(desc_str.value(), keys, error, false); + if (parsed_descs.empty()) return std::nullopt; - WalletDescriptor w_desc{std::move(parsed_desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1}; + WalletDescriptor w_desc{std::move(parsed_descs.at(0)), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1}; return std::make_pair(w_desc, keys); } diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp index dfad0e2126973..c446c0f8d851c 100644 --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -25,13 +25,14 @@ wallet::ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& FlatSigningProvider keys; std::string error; - std::unique_ptr parsed_desc = Parse(desc_str, keys, error, false); - BOOST_CHECK(success == (parsed_desc != nullptr)); + auto parsed_descs = Parse(desc_str, keys, error, false); + BOOST_CHECK(success == (!parsed_descs.empty())); if (!success) return nullptr; + auto& desc = parsed_descs.at(0); const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1; - WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index); + WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index); LOCK(keystore.cs_wallet); diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index b5a3b22c5446d..817c4f5feaaca 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -21,8 +21,9 @@ static void import_descriptor(CWallet& wallet, const std::string& descriptor) AssertLockHeld(wallet.cs_wallet); FlatSigningProvider provider; std::string error; - std::unique_ptr desc = Parse(descriptor, provider, error, /* require_checksum=*/ false); - assert(desc); + auto descs = Parse(descriptor, provider, error, /* require_checksum=*/ false); + assert(descs.size() == 1); + auto& desc = descs.at(0); WalletDescriptor w_desc(std::move(desc), 0, 0, 10, 0); wallet.AddWalletDescriptor(w_desc, provider, "", false); } diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp index b21a9a601d1c2..23d8eaf09ccb4 100644 --- a/src/wallet/test/util.cpp +++ b/src/wallet/test/util.cpp @@ -31,8 +31,9 @@ std::unique_ptr CreateSyncedWallet(interfaces::Chain& chain, CChain& cc FlatSigningProvider provider; std::string error; - std::unique_ptr desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false); - assert(desc); + auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false); + assert(descs.size() == 1); + auto& desc = descs.at(0); WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1); if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false); } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 53f3bcc421750..7cf90edea5f3d 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -65,8 +65,9 @@ static void AddKey(CWallet& wallet, const CKey& key) LOCK(wallet.cs_wallet); FlatSigningProvider provider; std::string error; - std::unique_ptr desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false); - assert(desc); + auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false); + assert(descs.size() == 1); + auto& desc = descs.at(0); WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1); if (!wallet.AddWalletDescriptor(w_desc, provider, "", false)) assert(false); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d569c64b4335d..2ff5a529841c9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3743,10 +3743,11 @@ void CWallet::SetupDescriptorScriptPubKeyMans() const std::string& desc_str = desc_val.getValStr(); FlatSigningProvider keys; std::string desc_error; - std::unique_ptr desc = Parse(desc_str, keys, desc_error, false); - if (desc == nullptr) { + auto descs = Parse(desc_str, keys, desc_error, false); + if (descs.empty()) { throw std::runtime_error(std::string(__func__) + ": Invalid descriptor \"" + desc_str + "\" (" + desc_error + ")"); } + auto& desc = descs.at(0); if (!desc->GetOutputType()) { continue; } @@ -4285,12 +4286,12 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, // Parse the descriptor FlatSigningProvider keys; std::string parse_err; - std::unique_ptr desc = Parse(desc_str, keys, parse_err, /* require_checksum */ true); - assert(desc); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor - assert(!desc->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor + std::vector> descs = Parse(desc_str, keys, parse_err, /* require_checksum */ true); + assert(descs.size() == 1); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor or a multipath descriptors + assert(!descs.at(0)->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor // Add to the wallet - WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); + WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); data->watchonly_wallet->AddWalletDescriptor(w_desc, keys, "", false); } @@ -4322,12 +4323,12 @@ bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, // Parse the descriptor FlatSigningProvider keys; std::string parse_err; - std::unique_ptr desc = Parse(desc_str, keys, parse_err, /* require_checksum */ true); - assert(desc); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor - assert(!desc->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor + std::vector> descs = Parse(desc_str, keys, parse_err, /* require_checksum */ true); + assert(descs.size() == 1); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor or a multipath descriptors + assert(!descs.at(0)->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor // Add to the wallet - WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); + WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); data->solvable_wallet->AddWalletDescriptor(w_desc, keys, "", false); } diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index 0de2617d45681..53e65d0194a58 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -94,8 +94,8 @@ WalletDescriptor GenerateWalletDescriptor(const CExtPubKey& master_key, const Ou // Make the descriptor FlatSigningProvider keys; std::string error; - std::unique_ptr desc = Parse(desc_str, keys, error, false); - WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); + std::vector> desc = Parse(desc_str, keys, error, false); + WalletDescriptor w_desc(std::move(desc.at(0)), creation_time, 0, 0, 0); return w_desc; } diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index 38926c1eb81d5..3416f1dcd05df 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -96,10 +96,14 @@ class WalletDescriptor { std::string error; FlatSigningProvider keys; - descriptor = Parse(str, keys, error, true); - if (!descriptor) { + auto descs = Parse(str, keys, error, true); + if (descs.empty()) { throw std::ios_base::failure("Invalid descriptor: " + error); } + if (descs.size() > 1) { + throw std::ios_base::failure("Can't load a multipath descriptor from databases"); + } + descriptor = std::move(descs.at(0)); id = DescriptorID(*descriptor); }