aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/output_script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/output_script.cpp')
-rw-r--r--src/rpc/output_script.cpp126
1 files changed, 84 insertions, 42 deletions
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index 65a9be2762..49f3a81243 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -38,7 +38,7 @@ static RPCHelpMan validateaddress()
{
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"},
- {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded output script generated by the address"},
{RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"},
{RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"},
{RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program"},
@@ -175,7 +175,11 @@ static RPCHelpMan getdescriptorinfo()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"},
+ {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys. For a multipath descriptor, only the first will be returned."},
+ {RPCResult::Type::ARR, "multipath_expansion", /*optional=*/true, "All descriptors produced by expanding multipath derivation elements. Only if the provided descriptor specifies multipath derivation elements.",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
{RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"},
{RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"},
{RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"},
@@ -191,22 +195,65 @@ static RPCHelpMan getdescriptorinfo()
{
FlatSigningProvider provider;
std::string error;
- auto desc = Parse(request.params[0].get_str(), provider, error);
- if (!desc) {
+ auto descs = Parse(request.params[0].get_str(), provider, error);
+ if (descs.empty()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
UniValue result(UniValue::VOBJ);
- result.pushKV("descriptor", desc->ToString());
+ result.pushKV("descriptor", descs.at(0)->ToString());
+
+ if (descs.size() > 1) {
+ UniValue multipath_descs(UniValue::VARR);
+ for (const auto& d : descs) {
+ multipath_descs.push_back(d->ToString());
+ }
+ result.pushKV("multipath_expansion", multipath_descs);
+ }
+
result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
- result.pushKV("isrange", desc->IsRange());
- result.pushKV("issolvable", desc->IsSolvable());
+ result.pushKV("isrange", descs.at(0)->IsRange());
+ result.pushKV("issolvable", descs.at(0)->IsSolvable());
result.pushKV("hasprivatekeys", provider.keys.size() > 0);
return result;
},
};
}
+static UniValue DeriveAddresses(const Descriptor* desc, int64_t range_begin, int64_t range_end, FlatSigningProvider& key_provider)
+{
+ UniValue addresses(UniValue::VARR);
+
+ for (int64_t i = range_begin; i <= range_end; ++i) {
+ FlatSigningProvider provider;
+ std::vector<CScript> scripts;
+ if (!desc->Expand(i, key_provider, scripts, provider)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
+ }
+
+ for (const CScript& script : scripts) {
+ CTxDestination dest;
+ if (!ExtractDestination(script, dest)) {
+ // ExtractDestination no longer returns true for P2PK since it doesn't have a corresponding address
+ // However combo will output P2PK and should just ignore that script
+ if (scripts.size() > 1 && std::get_if<PubKeyDestination>(&dest)) {
+ continue;
+ }
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
+ }
+
+ addresses.push_back(EncodeDestination(dest));
+ }
+ }
+
+ // This should not be possible, but an assert seems overkill:
+ if (addresses.empty()) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
+ }
+
+ return addresses;
+}
+
static RPCHelpMan deriveaddresses()
{
const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
@@ -217,7 +264,7 @@ static RPCHelpMan deriveaddresses()
" pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
" wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n"
" sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
- " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ " raw(<hex script>) Outputs whose output script equals the specified hex-encoded bytes\n"
" tr(<pubkey>,multi_a(<n>,<pubkey>,<pubkey>,...)) P2TR-multisig outputs for the given threshold and pubkeys\n"
"\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
"or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
@@ -226,11 +273,24 @@ static RPCHelpMan deriveaddresses()
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
{"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."},
},
- RPCResult{
- RPCResult::Type::ARR, "", "",
- {
- {RPCResult::Type::STR, "address", "the derived addresses"},
- }
+ {
+ RPCResult{"for single derivation descriptors",
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "address", "the derived addresses"},
+ }
+ },
+ RPCResult{"for multipath descriptors",
+ RPCResult::Type::ARR, "", "The derived addresses for each of the multipath expansions of the descriptor, in multipath specifier order",
+ {
+ {
+ RPCResult::Type::ARR, "", "The derived addresses for a multipath descriptor expansion",
+ {
+ {RPCResult::Type::STR, "address", "the derived address"},
+ },
+ },
+ },
+ },
},
RPCExamples{
"First three native segwit receive addresses\n" +
@@ -250,11 +310,11 @@ static RPCHelpMan deriveaddresses()
FlatSigningProvider key_provider;
std::string error;
- auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
- if (!desc) {
+ auto descs = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
+ if (descs.empty()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
-
+ auto& desc = descs.at(0);
if (!desc->IsRange() && request.params.size() > 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
}
@@ -263,36 +323,18 @@ static RPCHelpMan deriveaddresses()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
}
- UniValue addresses(UniValue::VARR);
-
- for (int64_t i = range_begin; i <= range_end; ++i) {
- FlatSigningProvider provider;
- std::vector<CScript> scripts;
- if (!desc->Expand(i, key_provider, scripts, provider)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
- }
+ UniValue addresses = DeriveAddresses(desc.get(), range_begin, range_end, key_provider);
- for (const CScript& script : scripts) {
- CTxDestination dest;
- if (!ExtractDestination(script, dest)) {
- // ExtractDestination no longer returns true for P2PK since it doesn't have a corresponding address
- // However combo will output P2PK and should just ignore that script
- if (scripts.size() > 1 && std::get_if<PubKeyDestination>(&dest)) {
- continue;
- }
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
- }
-
- addresses.push_back(EncodeDestination(dest));
- }
+ if (descs.size() == 1) {
+ return addresses;
}
- // This should not be possible, but an assert seems overkill:
- if (addresses.empty()) {
- throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
+ UniValue ret(UniValue::VARR);
+ ret.push_back(addresses);
+ for (size_t i = 1; i < descs.size(); ++i) {
+ ret.push_back(DeriveAddresses(descs.at(i).get(), range_begin, range_end, key_provider));
}
-
- return addresses;
+ return ret;
},
};
}