Skip to content

Commit 32dcbca

Browse files
committed
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.
1 parent 64dfe3c commit 32dcbca

File tree

1 file changed

+37
-23
lines changed

1 file changed

+37
-23
lines changed

src/wallet/rpc/backup.cpp

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,8 +1056,6 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
10561056

10571057
static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<std::pair<CKeyID, bool>>& ordered_pubkeys)
10581058
{
1059-
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1060-
10611059
UniValue warnings(UniValue::VARR);
10621060

10631061
const std::string& descriptor = data["desc"].get_str();
@@ -1067,18 +1065,25 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
10671065
if (parsed_descs.empty()) {
10681066
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
10691067
}
1070-
const auto& parsed_desc = parsed_descs.at(0);
1071-
if (parsed_desc->GetOutputType() == OutputType::BECH32M) {
1068+
if (parsed_descs.at(0)->GetOutputType() == OutputType::BECH32M) {
10721069
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets");
10731070
}
10741071

1075-
have_solving_data = parsed_desc->IsSolvable();
1072+
std::optional<bool> internal;
1073+
if (data.exists("internal")) {
1074+
if (parsed_descs.size() > 1) {
1075+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot have multipath descriptor while also specifying \'internal\'");
1076+
}
1077+
internal = data["internal"].get_bool();
1078+
}
1079+
1080+
have_solving_data = parsed_descs.at(0)->IsSolvable();
10761081
const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
10771082

10781083
int64_t range_start = 0, range_end = 0;
1079-
if (!parsed_desc->IsRange() && data.exists("range")) {
1084+
if (!parsed_descs.at(0)->IsRange() && data.exists("range")) {
10801085
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1081-
} else if (parsed_desc->IsRange()) {
1086+
} else if (parsed_descs.at(0)->IsRange()) {
10821087
if (!data.exists("range")) {
10831088
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
10841089
}
@@ -1087,25 +1092,34 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
10871092

10881093
const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
10891094

1090-
// Expand all descriptors to get public keys and scripts, and private keys if available.
1091-
for (int i = range_start; i <= range_end; ++i) {
1092-
FlatSigningProvider out_keys;
1093-
std::vector<CScript> scripts_temp;
1094-
parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1095-
std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1096-
for (const auto& key_pair : out_keys.pubkeys) {
1097-
ordered_pubkeys.emplace_back(key_pair.first, internal);
1098-
}
1095+
for (size_t j = 0; j < parsed_descs.size(); ++j) {
1096+
const auto& parsed_desc = parsed_descs.at(j);
1097+
bool desc_internal = internal.has_value() && internal.value();
1098+
if (parsed_descs.size() == 2) {
1099+
desc_internal = j == 1;
1100+
} else if (parsed_descs.size() > 2) {
1101+
CHECK_NONFATAL(!desc_internal);
1102+
}
1103+
// Expand all descriptors to get public keys and scripts, and private keys if available.
1104+
for (int i = range_start; i <= range_end; ++i) {
1105+
FlatSigningProvider out_keys;
1106+
std::vector<CScript> scripts_temp;
1107+
parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1108+
std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1109+
for (const auto& key_pair : out_keys.pubkeys) {
1110+
ordered_pubkeys.emplace_back(key_pair.first, desc_internal);
1111+
}
10991112

1100-
for (const auto& x : out_keys.scripts) {
1101-
import_data.import_scripts.emplace(x.second);
1102-
}
1113+
for (const auto& x : out_keys.scripts) {
1114+
import_data.import_scripts.emplace(x.second);
1115+
}
11031116

1104-
parsed_desc->ExpandPrivate(i, keys, out_keys);
1117+
parsed_desc->ExpandPrivate(i, keys, out_keys);
11051118

1106-
std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1107-
std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
1108-
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
1119+
std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1120+
std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
1121+
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
1122+
}
11091123
}
11101124

11111125
for (size_t i = 0; i < priv_keys.size(); ++i) {

0 commit comments

Comments
 (0)