Skip to content

Commit 6be554f

Browse files
committed
rpc: Allow importmulti to import multipath descriptors correctly
Multipath descriptors will be imported as two separate descriptors with the first one for receiving addreses, and the second for change addresses. When importing a multipath descriptor, 'internal' cannot be specified because of the implicit receive and change descriptors.
1 parent b005cff commit 6be554f

File tree

1 file changed

+34
-24
lines changed

1 file changed

+34
-24
lines changed

src/wallet/rpc/backup.cpp

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,28 +1063,35 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
10631063

10641064
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)
10651065
{
1066-
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1067-
10681066
UniValue warnings(UniValue::VARR);
10691067

10701068
const std::string& descriptor = data["desc"].get_str();
10711069
FlatSigningProvider keys;
10721070
std::string error;
1073-
auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true).first;
1074-
if (!parsed_desc) {
1071+
auto parsed_descs = Parse(descriptor, keys, error, /* require_checksum = */ true);
1072+
if (!parsed_descs.first) {
10751073
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
10761074
}
1077-
if (parsed_desc->GetOutputType() == OutputType::BECH32M) {
1075+
if (parsed_descs.first->GetOutputType() == OutputType::BECH32M) {
10781076
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m descriptors cannot be imported into legacy wallets");
10791077
}
10801078

1081-
have_solving_data = parsed_desc->IsSolvable();
1079+
std::optional<bool> internal;
1080+
bool multipath = parsed_descs.second != nullptr;
1081+
if (data.exists("internal")) {
1082+
if (multipath) {
1083+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot have multipath descriptor while also specifying \'internal\'");
1084+
}
1085+
internal = data["internal"].get_bool();
1086+
}
1087+
1088+
have_solving_data = parsed_descs.first->IsSolvable();
10821089
const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
10831090

10841091
int64_t range_start = 0, range_end = 0;
1085-
if (!parsed_desc->IsRange() && data.exists("range")) {
1092+
if (!parsed_descs.first->IsRange() && data.exists("range")) {
10861093
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1087-
} else if (parsed_desc->IsRange()) {
1094+
} else if (parsed_descs.first->IsRange()) {
10881095
if (!data.exists("range")) {
10891096
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
10901097
}
@@ -1093,25 +1100,28 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
10931100

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

1096-
// Expand all descriptors to get public keys and scripts, and private keys if available.
1097-
for (int i = range_start; i <= range_end; ++i) {
1098-
FlatSigningProvider out_keys;
1099-
std::vector<CScript> scripts_temp;
1100-
parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1101-
std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1102-
for (const auto& key_pair : out_keys.pubkeys) {
1103-
ordered_pubkeys.push_back({key_pair.first, internal});
1104-
}
1103+
for (int j = 0; j < (multipath ? 2 : 1); ++j) {
1104+
const auto& parsed_desc = j ? parsed_descs.second : parsed_descs.first;
1105+
// Expand all descriptors to get public keys and scripts, and private keys if available.
1106+
for (int i = range_start; i <= range_end; ++i) {
1107+
FlatSigningProvider out_keys;
1108+
std::vector<CScript> scripts_temp;
1109+
parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1110+
std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1111+
for (const auto& key_pair : out_keys.pubkeys) {
1112+
ordered_pubkeys.push_back({key_pair.first, (internal.has_value() ? internal.value() : j)});
1113+
}
11051114

1106-
for (const auto& x : out_keys.scripts) {
1107-
import_data.import_scripts.emplace(x.second);
1108-
}
1115+
for (const auto& x : out_keys.scripts) {
1116+
import_data.import_scripts.emplace(x.second);
1117+
}
11091118

1110-
parsed_desc->ExpandPrivate(i, keys, out_keys);
1119+
parsed_desc->ExpandPrivate(i, keys, out_keys);
11111120

1112-
std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1113-
std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
1114-
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
1121+
std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1122+
std::copy(out_keys.keys.begin(), out_keys.keys.end(), std::inserter(privkey_map, privkey_map.end()));
1123+
import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
1124+
}
11151125
}
11161126

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

0 commit comments

Comments
 (0)