From 32dcbca3fb918bc899a0637f876db31c3419aafd Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Wed, 7 Aug 2024 16:29:17 -0400 Subject: [PATCH] rpc: Allow importmulti to import multipath descriptors correctly Multipath descriptors will be imported as multiple separate descriptors. When there are exactly 2 multipath items, the first descriptor will be for receiving addreses, and the second for change addresses. When importing a multipath descriptor, 'internal' cannot be specified. --- src/wallet/rpc/backup.cpp | 60 ++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index e7ce293da0..644a7b1081 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -1056,8 +1056,6 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map& pubkey_map, std::map& privkey_map, std::set& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector>& ordered_pubkeys) { - const bool internal = data.exists("internal") ? data["internal"].get_bool() : false; - UniValue warnings(UniValue::VARR); const std::string& descriptor = data["desc"].get_str(); @@ -1067,18 +1065,25 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::mapGetOutputType() == OutputType::BECH32M) { + if (parsed_descs.at(0)->GetOutputType() == OutputType::BECH32M) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets"); } - have_solving_data = parsed_desc->IsSolvable(); + std::optional internal; + if (data.exists("internal")) { + if (parsed_descs.size() > 1) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot have multipath descriptor while also specifying \'internal\'"); + } + internal = data["internal"].get_bool(); + } + + have_solving_data = parsed_descs.at(0)->IsSolvable(); const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false; int64_t range_start = 0, range_end = 0; - if (!parsed_desc->IsRange() && data.exists("range")) { + if (!parsed_descs.at(0)->IsRange() && data.exists("range")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor"); - } else if (parsed_desc->IsRange()) { + } else if (parsed_descs.at(0)->IsRange()) { if (!data.exists("range")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range"); } @@ -1087,25 +1092,34 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map scripts_temp; - parsed_desc->Expand(i, keys, scripts_temp, out_keys); - std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end())); - for (const auto& key_pair : out_keys.pubkeys) { - ordered_pubkeys.emplace_back(key_pair.first, internal); + for (size_t j = 0; j < parsed_descs.size(); ++j) { + const auto& parsed_desc = parsed_descs.at(j); + bool desc_internal = internal.has_value() && internal.value(); + if (parsed_descs.size() == 2) { + desc_internal = j == 1; + } else if (parsed_descs.size() > 2) { + CHECK_NONFATAL(!desc_internal); } + // Expand all descriptors to get public keys and scripts, and private keys if available. + for (int i = range_start; i <= range_end; ++i) { + FlatSigningProvider out_keys; + std::vector scripts_temp; + parsed_desc->Expand(i, keys, scripts_temp, out_keys); + std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end())); + for (const auto& key_pair : out_keys.pubkeys) { + ordered_pubkeys.emplace_back(key_pair.first, desc_internal); + } - for (const auto& x : out_keys.scripts) { - import_data.import_scripts.emplace(x.second); + for (const auto& x : out_keys.scripts) { + import_data.import_scripts.emplace(x.second); + } + + parsed_desc->ExpandPrivate(i, keys, out_keys); + + std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end())); + std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end())); + import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end()); } - - parsed_desc->ExpandPrivate(i, keys, out_keys); - - std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end())); - std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end())); - import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end()); } for (size_t i = 0; i < priv_keys.size(); ++i) {