|
16 | 16 | #include <rpc/blockchain.h>
|
17 | 17 | #include <rpc/server.h>
|
18 | 18 | #include <rpc/util.h>
|
| 19 | +#include <script/descriptor.h> |
19 | 20 | #include <timedata.h>
|
20 | 21 | #include <util/system.h>
|
21 | 22 | #include <util/strencodings.h>
|
@@ -142,6 +143,95 @@ static UniValue createmultisig(const JSONRPCRequest& request)
|
142 | 143 | return result;
|
143 | 144 | }
|
144 | 145 |
|
| 146 | +UniValue deriveaddresses(const JSONRPCRequest& request) |
| 147 | +{ |
| 148 | + if (request.fHelp || request.params.empty() || request.params.size() > 3) { |
| 149 | + throw std::runtime_error( |
| 150 | + RPCHelpMan{"deriveaddresses", |
| 151 | + {"\nDerives one or more addresses corresponding to an output descriptor.\n" |
| 152 | + "Examples of output descriptors are:\n" |
| 153 | + " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" |
| 154 | + " wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n" |
| 155 | + " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" |
| 156 | + " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n" |
| 157 | + "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" |
| 158 | + "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n" |
| 159 | + "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"}, |
| 160 | + { |
| 161 | + {"descriptor", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The descriptor."}, |
| 162 | + {"begin", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "If a ranged descriptor is used, this specifies the beginning of the range to import."}, |
| 163 | + {"end", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "", "If a ranged descriptor is used, this specifies the end of the range to import."} |
| 164 | + }, |
| 165 | + RPCResult{ |
| 166 | + "[ address ] (array) the derived addresses\n" |
| 167 | + }, |
| 168 | + RPCExamples{ |
| 169 | + "First three native segwit receive addresses\n" + |
| 170 | + HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)\" 0 2") |
| 171 | + }}.ToString() |
| 172 | + ); |
| 173 | + } |
| 174 | + |
| 175 | + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VNUM, UniValue::VNUM}); |
| 176 | + const std::string desc_str = request.params[0].get_str(); |
| 177 | + |
| 178 | + int range_begin = 0; |
| 179 | + int range_end = 0; |
| 180 | + |
| 181 | + if (request.params.size() >= 2) { |
| 182 | + if (request.params.size() == 2) { |
| 183 | + throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing range end parameter"); |
| 184 | + } |
| 185 | + range_begin = request.params[1].get_int(); |
| 186 | + range_end = request.params[2].get_int(); |
| 187 | + if (range_begin < 0) { |
| 188 | + throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0"); |
| 189 | + } |
| 190 | + if (range_begin > range_end) { |
| 191 | + throw JSONRPCError(RPC_INVALID_PARAMETER, "Range end should be equal to or greater than begin"); |
| 192 | + } |
| 193 | + } |
| 194 | + |
| 195 | + FlatSigningProvider provider; |
| 196 | + auto desc = Parse(desc_str, provider); |
| 197 | + if (!desc) { |
| 198 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor")); |
| 199 | + } |
| 200 | + |
| 201 | + if (!desc->IsRange() && request.params.size() > 1) { |
| 202 | + throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor"); |
| 203 | + } |
| 204 | + |
| 205 | + if (desc->IsRange() && request.params.size() == 1) { |
| 206 | + throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor"); |
| 207 | + } |
| 208 | + |
| 209 | + UniValue addresses(UniValue::VARR); |
| 210 | + |
| 211 | + for (int i = range_begin; i <= range_end; ++i) { |
| 212 | + std::vector<CScript> scripts; |
| 213 | + if (!desc->Expand(i, provider, scripts, provider)) { |
| 214 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys")); |
| 215 | + } |
| 216 | + |
| 217 | + for (const CScript &script : scripts) { |
| 218 | + CTxDestination dest; |
| 219 | + if (!ExtractDestination(script, dest)) { |
| 220 | + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Descriptor does not have a corresponding address")); |
| 221 | + } |
| 222 | + |
| 223 | + addresses.push_back(EncodeDestination(dest)); |
| 224 | + } |
| 225 | + } |
| 226 | + |
| 227 | + // This should not be possible, but an assert seems overkill: |
| 228 | + if (addresses.empty()) { |
| 229 | + throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result"); |
| 230 | + } |
| 231 | + |
| 232 | + return addresses; |
| 233 | +} |
| 234 | + |
145 | 235 | static UniValue verifymessage(const JSONRPCRequest& request)
|
146 | 236 | {
|
147 | 237 | if (request.fHelp || request.params.size() != 3)
|
@@ -473,6 +563,7 @@ static const CRPCCommand commands[] =
|
473 | 563 | { "control", "logging", &logging, {"include", "exclude"}},
|
474 | 564 | { "util", "validateaddress", &validateaddress, {"address"} },
|
475 | 565 | { "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
|
| 566 | + { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "begin", "end"} }, |
476 | 567 | { "util", "verifymessage", &verifymessage, {"address","signature","message"} },
|
477 | 568 | { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
|
478 | 569 |
|
|
0 commit comments