0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-03-05 14:06:27 -05:00

wallet, rpc: Allow importdescriptors to import multipath descriptors

Multipath descriptors will be imported as multiple separate descriptors.
When there are 2 multipath items, the first descriptor will be for receiving
addresses and the second for change. This mirrors importmulti.
This commit is contained in:
Ava Chow 2024-08-07 16:29:19 -04:00
parent 32dcbca3fb
commit f97d5c137d

View file

@ -1463,7 +1463,6 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
const std::string& descriptor = data["desc"].get_str();
const bool active = data.exists("active") ? data["active"].get_bool() : false;
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
const std::string label{LabelFromValue(data["label"])};
// Parse descriptor string
@ -1473,13 +1472,19 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
if (parsed_descs.empty()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
auto& parsed_desc = parsed_descs.at(0);
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();
}
// Range check
int64_t range_start = 0, range_end = 1, next_index = 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")) {
auto range = ParseDescriptorRange(data["range"]);
range_start = range.first;
@ -1501,10 +1506,15 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
// Active descriptors must be ranged
if (active && !parsed_desc->IsRange()) {
if (active && !parsed_descs.at(0)->IsRange()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Active descriptors must be ranged");
}
// Multipath descriptors should not have a label
if (parsed_descs.size() > 1 && data.exists("label")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Multipath descriptors should not have a label");
}
// Ranged descriptors should not have a label
if (data.exists("range") && data.exists("label")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Ranged descriptors should not have a label");
@ -1516,7 +1526,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
// Combo descriptor check
if (active && !parsed_desc->IsSingleType()) {
if (active && !parsed_descs.at(0)->IsSingleType()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Combo descriptors cannot be set to active");
}
@ -1525,6 +1535,14 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
for (size_t j = 0; j < parsed_descs.size(); ++j) {
auto parsed_desc = std::move(parsed_descs[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);
}
// Need to ExpandPrivate to check if private keys are available for all pubkeys
FlatSigningProvider expand_keys;
std::vector<CScript> scripts;
@ -1565,7 +1583,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
// Add descriptor to the wallet
auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, desc_internal);
if (spk_manager == nullptr) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
}
@ -1575,11 +1593,12 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
if (!w_desc.descriptor->GetOutputType()) {
warnings.push_back("Unknown output type, cannot set descriptor to active.");
} else {
wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), desc_internal);
}
} else {
if (w_desc.descriptor->GetOutputType()) {
wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), desc_internal);
}
}
}
@ -1596,6 +1615,7 @@ RPCHelpMan importdescriptors()
{
return RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"When importing descriptors with multipath key expressions, if the multipath specifier contains exactly two elements, the descriptor produced from the second elements will be imported as an internal descriptor.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
"The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",