Skip to content

Commit 2105390

Browse files
committed
wallet, rpc: Allow importdescriptors to import multipath descriptors
Multipath descriptors will be imported as two separate descriptors with the first for receiving addresses and the second for change. This mirrors importmulti.
1 parent 6be554f commit 2105390

File tree

1 file changed

+70
-54
lines changed

1 file changed

+70
-54
lines changed

src/wallet/rpc/backup.cpp

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,22 +1456,30 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
14561456

14571457
const std::string& descriptor = data["desc"].get_str();
14581458
const bool active = data.exists("active") ? data["active"].get_bool() : false;
1459-
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
14601459
const std::string& label = data.exists("label") ? data["label"].get_str() : "";
14611460

14621461
// Parse descriptor string
14631462
FlatSigningProvider keys;
14641463
std::string error;
1465-
auto parsed_desc = Parse(descriptor, keys, error, /* require_checksum = */ true).first;
1466-
if (!parsed_desc) {
1464+
auto parsed_descs = Parse(descriptor, keys, error, /* require_checksum = */ true);
1465+
if (!parsed_descs.first) {
14671466
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
14681467
}
14691468

1469+
std::optional<bool> internal;
1470+
bool multipath = parsed_descs.second != nullptr;
1471+
if (data.exists("internal")) {
1472+
if (multipath) {
1473+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot have multipath descriptor while also specifying \'internal\'");
1474+
}
1475+
internal = data["internal"].get_bool();
1476+
}
1477+
14701478
// Range check
14711479
int64_t range_start = 0, range_end = 1, next_index = 0;
1472-
if (!parsed_desc->IsRange() && data.exists("range")) {
1480+
if (!parsed_descs.first->IsRange() && data.exists("range")) {
14731481
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
1474-
} else if (parsed_desc->IsRange()) {
1482+
} else if (parsed_descs.first->IsRange()) {
14751483
if (data.exists("range")) {
14761484
auto range = ParseDescriptorRange(data["range"]);
14771485
range_start = range.first;
@@ -1493,10 +1501,15 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
14931501
}
14941502

14951503
// Active descriptors must be ranged
1496-
if (active && !parsed_desc->IsRange()) {
1504+
if (active && !parsed_descs.first->IsRange()) {
14971505
throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptors must be ranged");
14981506
}
14991507

1508+
// Multipath descriptors should not have a label
1509+
if (multipath && data.exists("label")) {
1510+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Multipath descriptors should not have a label");
1511+
}
1512+
15001513
// Ranged descriptors should not have a label
15011514
if (data.exists("range") && data.exists("label")) {
15021515
throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
@@ -1508,7 +1521,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
15081521
}
15091522

15101523
// Combo descriptor check
1511-
if (active && !parsed_desc->IsSingleType()) {
1524+
if (active && !parsed_descs.first->IsSingleType()) {
15121525
throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");
15131526
}
15141527

@@ -1517,61 +1530,64 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
15171530
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
15181531
}
15191532

1520-
// Need to ExpandPrivate to check if private keys are available for all pubkeys
1521-
FlatSigningProvider expand_keys;
1522-
std::vector<CScript> scripts;
1523-
if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
1524-
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
1525-
}
1526-
parsed_desc->ExpandPrivate(0, keys, expand_keys);
1527-
1528-
// Check if all private keys are provided
1529-
bool have_all_privkeys = !expand_keys.keys.empty();
1530-
for (const auto& entry : expand_keys.origins) {
1531-
const CKeyID& key_id = entry.first;
1532-
CKey key;
1533-
if (!expand_keys.GetKey(key_id, key)) {
1534-
have_all_privkeys = false;
1535-
break;
1533+
for (int j = 0; j < (multipath ? 2 : 1); ++j) {
1534+
auto parsed_desc = j ? std::move(parsed_descs.second) : std::move(parsed_descs.first);
1535+
// Need to ExpandPrivate to check if private keys are available for all pubkeys
1536+
FlatSigningProvider expand_keys;
1537+
std::vector<CScript> scripts;
1538+
if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
1539+
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
1540+
}
1541+
parsed_desc->ExpandPrivate(0, keys, expand_keys);
1542+
1543+
// Check if all private keys are provided
1544+
bool have_all_privkeys = !expand_keys.keys.empty();
1545+
for (const auto& entry : expand_keys.origins) {
1546+
const CKeyID& key_id = entry.first;
1547+
CKey key;
1548+
if (!expand_keys.GetKey(key_id, key)) {
1549+
have_all_privkeys = false;
1550+
break;
1551+
}
15361552
}
1537-
}
15381553

1539-
// If private keys are enabled, check some things.
1540-
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1541-
if (keys.keys.empty()) {
1542-
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
1543-
}
1544-
if (!have_all_privkeys) {
1545-
warnings.push_back("Not all private keys provided. Some wallet functionality may return unexpected errors");
1546-
}
1547-
}
1554+
// If private keys are enabled, check some things.
1555+
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1556+
if (keys.keys.empty()) {
1557+
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
1558+
}
1559+
if (!have_all_privkeys) {
1560+
warnings.push_back("Not all private keys provided. Some wallet functionality may return unexpected errors");
1561+
}
1562+
}
15481563

1549-
WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
1564+
WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
15501565

1551-
// Check if the wallet already contains the descriptor
1552-
auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
1553-
if (existing_spk_manager) {
1554-
if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
1555-
throw JSONRPCError(RPC_INVALID_PARAMETER, error);
1566+
// Check if the wallet already contains the descriptor
1567+
auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
1568+
if (existing_spk_manager) {
1569+
if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
1570+
throw JSONRPCError(RPC_INVALID_PARAMETER, error);
1571+
}
15561572
}
1557-
}
15581573

1559-
// Add descriptor to the wallet
1560-
auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
1561-
if (spk_manager == nullptr) {
1562-
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
1563-
}
1574+
// Add descriptor to the wallet
1575+
auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, (internal.has_value() ? internal.value() : j));
1576+
if (spk_manager == nullptr) {
1577+
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
1578+
}
15641579

1565-
// Set descriptor as active if necessary
1566-
if (active) {
1567-
if (!w_desc.descriptor->GetOutputType()) {
1568-
warnings.push_back("Unknown output type, cannot set descriptor to active.");
1580+
// Set descriptor as active if necessary
1581+
if (active) {
1582+
if (!w_desc.descriptor->GetOutputType()) {
1583+
warnings.push_back("Unknown output type, cannot set descriptor to active.");
1584+
} else {
1585+
wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), (internal.has_value() ? internal.value() : j));
1586+
}
15691587
} else {
1570-
wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
1571-
}
1572-
} else {
1573-
if (w_desc.descriptor->GetOutputType()) {
1574-
wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
1588+
if (w_desc.descriptor->GetOutputType()) {
1589+
wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), (internal.has_value() ? internal.value() : j));
1590+
}
15751591
}
15761592
}
15771593

0 commit comments

Comments
 (0)