Skip to content

Commit cddc0ba

Browse files
committed
rpc: Have deriveaddresses derive receiving and change
When given a multipath descriptor, derive all of the descriptors. The derived addresses will be returned in an object consisting of multiple arrays. For compatibility, when given a single path descriptor, the addresses are provided in a single array as before.
1 parent 360456c commit cddc0ba

File tree

1 file changed

+61
-36
lines changed

1 file changed

+61
-36
lines changed

src/rpc/output_script.cpp

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,40 @@ static RPCHelpMan getdescriptorinfo()
220220
};
221221
}
222222

223+
static UniValue DeriveAddresses(const Descriptor* desc, int64_t range_begin, int64_t range_end, FlatSigningProvider& key_provider)
224+
{
225+
UniValue addresses(UniValue::VARR);
226+
227+
for (int64_t i = range_begin; i <= range_end; ++i) {
228+
FlatSigningProvider provider;
229+
std::vector<CScript> scripts;
230+
if (!desc->Expand(i, key_provider, scripts, provider)) {
231+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
232+
}
233+
234+
for (const CScript& script : scripts) {
235+
CTxDestination dest;
236+
if (!ExtractDestination(script, dest)) {
237+
// ExtractDestination no longer returns true for P2PK since it doesn't have a corresponding address
238+
// However combo will output P2PK and should just ignore that script
239+
if (scripts.size() > 1 && std::get_if<PubKeyDestination>(&dest)) {
240+
continue;
241+
}
242+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
243+
}
244+
245+
addresses.push_back(EncodeDestination(dest));
246+
}
247+
}
248+
249+
// This should not be possible, but an assert seems overkill:
250+
if (addresses.empty()) {
251+
throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
252+
}
253+
254+
return addresses;
255+
}
256+
223257
static RPCHelpMan deriveaddresses()
224258
{
225259
const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
@@ -234,17 +268,29 @@ static RPCHelpMan deriveaddresses()
234268
" tr(<pubkey>,multi_a(<n>,<pubkey>,<pubkey>,...)) P2TR-multisig outputs for the given threshold and pubkeys\n"
235269
"\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
236270
"or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
237-
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"
238-
"Note that only descriptors that specify a single derivation path can be derived.\n"},
271+
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
239272
{
240273
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
241274
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
242275
},
243-
RPCResult{
244-
RPCResult::Type::ARR, "", "",
245-
{
246-
{RPCResult::Type::STR, "address", "the derived addresses"},
247-
}
276+
{
277+
RPCResult{"for single derivation descriptors",
278+
RPCResult::Type::ARR, "", "",
279+
{
280+
{RPCResult::Type::STR, "address", "the derived addresses"},
281+
}
282+
},
283+
RPCResult{"for multipath descriptors",
284+
RPCResult::Type::ARR, "", "The derived addresses for each of the multipath expansions of the descriptor, in multipath specifier order",
285+
{
286+
{
287+
RPCResult::Type::ARR, "", "The derived addresses for a multipath descriptor expansion",
288+
{
289+
{RPCResult::Type::STR, "address", "the derived address"},
290+
},
291+
},
292+
},
293+
},
248294
},
249295
RPCExamples{
250296
"First three native segwit receive addresses\n" +
@@ -268,9 +314,6 @@ static RPCHelpMan deriveaddresses()
268314
if (descs.empty()) {
269315
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
270316
}
271-
if (descs.size() > 1) {
272-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor with multipath derivation path specifiers are not allowed");
273-
}
274317
auto& desc = descs.at(0);
275318
if (!desc->IsRange() && request.params.size() > 1) {
276319
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
@@ -280,36 +323,18 @@ static RPCHelpMan deriveaddresses()
280323
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
281324
}
282325

283-
UniValue addresses(UniValue::VARR);
284-
285-
for (int64_t i = range_begin; i <= range_end; ++i) {
286-
FlatSigningProvider provider;
287-
std::vector<CScript> scripts;
288-
if (!desc->Expand(i, key_provider, scripts, provider)) {
289-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
290-
}
326+
UniValue addresses = DeriveAddresses(desc.get(), range_begin, range_end, key_provider);
291327

292-
for (const CScript& script : scripts) {
293-
CTxDestination dest;
294-
if (!ExtractDestination(script, dest)) {
295-
// ExtractDestination no longer returns true for P2PK since it doesn't have a corresponding address
296-
// However combo will output P2PK and should just ignore that script
297-
if (scripts.size() > 1 && std::get_if<PubKeyDestination>(&dest)) {
298-
continue;
299-
}
300-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
301-
}
302-
303-
addresses.push_back(EncodeDestination(dest));
304-
}
328+
if (descs.size() == 1) {
329+
return addresses;
305330
}
306331

307-
// This should not be possible, but an assert seems overkill:
308-
if (addresses.empty()) {
309-
throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
332+
UniValue ret(UniValue::VARR);
333+
ret.push_back(addresses);
334+
for (size_t i = 1; i < descs.size(); ++i) {
335+
ret.push_back(DeriveAddresses(descs.at(i).get(), range_begin, range_end, key_provider));
310336
}
311-
312-
return addresses;
337+
return ret;
313338
},
314339
};
315340
}

0 commit comments

Comments
 (0)