Skip to content

Commit

Permalink
rpc: Allow importmulti to import multipath descriptors correctly
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
achow101 committed Oct 11, 2023
1 parent baa1523 commit bc6d185
Showing 1 changed file with 37 additions and 23 deletions.
60 changes: 37 additions & 23 deletions src/wallet/rpc/backup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1071,8 +1071,6 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP

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)
{
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;

UniValue warnings(UniValue::VARR);

const std::string& descriptor = data["desc"].get_str();
Expand All @@ -1082,18 +1080,25 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
if (parsed_descs.empty()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
const auto& parsed_desc = parsed_descs.at(0);
if (parsed_desc->GetOutputType() == 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<bool> 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");
}
Expand All @@ -1102,25 +1107,34 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID

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

// 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<CScript> 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.push_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<CScript> 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.push_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);
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());
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) {
Expand Down

0 comments on commit bc6d185

Please sign in to comment.