diff options
Diffstat (limited to 'src/rpc/output_script.cpp')
-rw-r--r-- | src/rpc/output_script.cpp | 126 |
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; }, }; } |