/Users/eugenesiegel/btc/bitcoin/src/rpc/output_script.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2010 Satoshi Nakamoto | 
| 2 |  | // Copyright (c) 2009-2022 The Bitcoin Core developers | 
| 3 |  | // Distributed under the MIT software license, see the accompanying | 
| 4 |  | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | 
| 5 |  |  | 
| 6 |  | #include <key_io.h> | 
| 7 |  | #include <outputtype.h> | 
| 8 |  | #include <pubkey.h> | 
| 9 |  | #include <rpc/protocol.h> | 
| 10 |  | #include <rpc/request.h> | 
| 11 |  | #include <rpc/server.h> | 
| 12 |  | #include <rpc/util.h> | 
| 13 |  | #include <script/descriptor.h> | 
| 14 |  | #include <script/script.h> | 
| 15 |  | #include <script/signingprovider.h> | 
| 16 |  | #include <tinyformat.h> | 
| 17 |  | #include <univalue.h> | 
| 18 |  | #include <util/check.h> | 
| 19 |  | #include <util/strencodings.h> | 
| 20 |  |  | 
| 21 |  | #include <cstdint> | 
| 22 |  | #include <memory> | 
| 23 |  | #include <optional> | 
| 24 |  | #include <string> | 
| 25 |  | #include <tuple> | 
| 26 |  | #include <vector> | 
| 27 |  |  | 
| 28 |  | static RPCHelpMan validateaddress() | 
| 29 | 0 | { | 
| 30 | 0 |     return RPCHelpMan{ | 
| 31 | 0 |         "validateaddress", | 
| 32 | 0 |         "Return information about the given bitcoin address.\n", | 
| 33 | 0 |         { | 
| 34 | 0 |             {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, | 
| 35 | 0 |         }, | 
| 36 | 0 |         RPCResult{ | 
| 37 | 0 |             RPCResult::Type::OBJ, "", "", | 
| 38 | 0 |             { | 
| 39 | 0 |                 {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, | 
| 40 | 0 |                 {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"}, | 
| 41 | 0 |                 {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded output script generated by the address"}, | 
| 42 | 0 |                 {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"}, | 
| 43 | 0 |                 {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"}, | 
| 44 | 0 |                 {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program"}, | 
| 45 | 0 |                 {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program"}, | 
| 46 | 0 |                 {RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"}, | 
| 47 | 0 |                 {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)", | 
| 48 | 0 |                     { | 
| 49 | 0 |                         {RPCResult::Type::NUM, "index", "index of a potential error"}, | 
| 50 | 0 |                     }}, | 
| 51 | 0 |             } | 
| 52 | 0 |         }, | 
| 53 | 0 |         RPCExamples{ | 
| 54 | 0 |             HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + | 
| 55 | 0 |             HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") | 
| 56 | 0 |         }, | 
| 57 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 58 | 0 |         { | 
| 59 | 0 |             std::string error_msg; | 
| 60 | 0 |             std::vector<int> error_locations; | 
| 61 | 0 |             CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations); | 
| 62 | 0 |             const bool isValid = IsValidDestination(dest); | 
| 63 | 0 |             CHECK_NONFATAL(isValid == error_msg.empty()); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 64 |  | 
 | 
| 65 | 0 |             UniValue ret(UniValue::VOBJ); | 
| 66 | 0 |             ret.pushKV("isvalid", isValid); | 
| 67 | 0 |             if (isValid) { | 
| 68 | 0 |                 std::string currentAddress = EncodeDestination(dest); | 
| 69 | 0 |                 ret.pushKV("address", currentAddress); | 
| 70 |  | 
 | 
| 71 | 0 |                 CScript scriptPubKey = GetScriptForDestination(dest); | 
| 72 | 0 |                 ret.pushKV("scriptPubKey", HexStr(scriptPubKey)); | 
| 73 |  | 
 | 
| 74 | 0 |                 UniValue detail = DescribeAddress(dest); | 
| 75 | 0 |                 ret.pushKVs(std::move(detail)); | 
| 76 | 0 |             } else { | 
| 77 | 0 |                 UniValue error_indices(UniValue::VARR); | 
| 78 | 0 |                 for (int i : error_locations) error_indices.push_back(i); | 
| 79 | 0 |                 ret.pushKV("error_locations", std::move(error_indices)); | 
| 80 | 0 |                 ret.pushKV("error", error_msg); | 
| 81 | 0 |             } | 
| 82 |  | 
 | 
| 83 | 0 |             return ret; | 
| 84 | 0 |         }, | 
| 85 | 0 |     }; | 
| 86 | 0 | } | 
| 87 |  |  | 
| 88 |  | static RPCHelpMan createmultisig() | 
| 89 | 0 | { | 
| 90 | 0 |     return RPCHelpMan{ | 
| 91 | 0 |         "createmultisig", | 
| 92 | 0 |         "Creates a multi-signature address with n signatures of m keys required.\n" | 
| 93 | 0 |         "It returns a json object with the address and redeemScript.\n", | 
| 94 | 0 |         { | 
| 95 | 0 |             {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the m keys."}, | 
| 96 | 0 |             {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex-encoded public keys.", | 
| 97 | 0 |                 { | 
| 98 | 0 |                     {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"}, | 
| 99 | 0 |                 }}, | 
| 100 | 0 |             {"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, | 
| 101 | 0 |         }, | 
| 102 | 0 |         RPCResult{ | 
| 103 | 0 |             RPCResult::Type::OBJ, "", "", | 
| 104 | 0 |             { | 
| 105 | 0 |                 {RPCResult::Type::STR, "address", "The value of the new multisig address."}, | 
| 106 | 0 |                 {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."}, | 
| 107 | 0 |                 {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, | 
| 108 | 0 |                 {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig", | 
| 109 | 0 |                 { | 
| 110 | 0 |                     {RPCResult::Type::STR, "", ""}, | 
| 111 | 0 |                 }}, | 
| 112 | 0 |             } | 
| 113 | 0 |         }, | 
| 114 | 0 |         RPCExamples{ | 
| 115 | 0 |             "\nCreate a multisig address from 2 public keys\n" | 
| 116 | 0 |             + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") + | 
| 117 | 0 |             "\nAs a JSON-RPC call\n" | 
| 118 | 0 |             + HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]") | 
| 119 | 0 |                 }, | 
| 120 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 121 | 0 |         { | 
| 122 | 0 |             int required = request.params[0].getInt<int>(); | 
| 123 |  |  | 
| 124 |  |             // Get the public keys | 
| 125 | 0 |             const UniValue& keys = request.params[1].get_array(); | 
| 126 | 0 |             std::vector<CPubKey> pubkeys; | 
| 127 | 0 |             pubkeys.reserve(keys.size()); | 
| 128 | 0 |             for (unsigned int i = 0; i < keys.size(); ++i) { | 
| 129 | 0 |                 pubkeys.push_back(HexToPubKey(keys[i].get_str())); | 
| 130 | 0 |             } | 
| 131 |  |  | 
| 132 |  |             // Get the output type | 
| 133 | 0 |             OutputType output_type = OutputType::LEGACY; | 
| 134 | 0 |             if (!request.params[2].isNull()) { | 
| 135 | 0 |                 std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str()); | 
| 136 | 0 |                 if (!parsed) { | 
| 137 | 0 |                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 138 | 0 |                 } else if (parsed.value() == OutputType::BECH32M) { | 
| 139 | 0 |                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses"); | 
| 140 | 0 |                 } | 
| 141 | 0 |                 output_type = parsed.value(); | 
| 142 | 0 |             } | 
| 143 |  |  | 
| 144 | 0 |             FlatSigningProvider keystore; | 
| 145 | 0 |             CScript inner; | 
| 146 | 0 |             const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner); | 
| 147 |  |  | 
| 148 |  |             // Make the descriptor | 
| 149 | 0 |             std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore); | 
| 150 |  | 
 | 
| 151 | 0 |             UniValue result(UniValue::VOBJ); | 
| 152 | 0 |             result.pushKV("address", EncodeDestination(dest)); | 
| 153 | 0 |             result.pushKV("redeemScript", HexStr(inner)); | 
| 154 | 0 |             result.pushKV("descriptor", descriptor->ToString()); | 
| 155 |  | 
 | 
| 156 | 0 |             UniValue warnings(UniValue::VARR); | 
| 157 | 0 |             if (descriptor->GetOutputType() != output_type) { | 
| 158 |  |                 // Only warns if the user has explicitly chosen an address type we cannot generate | 
| 159 | 0 |                 warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present."); | 
| 160 | 0 |             } | 
| 161 | 0 |             PushWarnings(warnings, result); | 
| 162 |  | 
 | 
| 163 | 0 |             return result; | 
| 164 | 0 |         }, | 
| 165 | 0 |     }; | 
| 166 | 0 | } | 
| 167 |  |  | 
| 168 |  | static RPCHelpMan getdescriptorinfo() | 
| 169 | 0 | { | 
| 170 | 0 |     const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)"; | 
| 171 |  | 
 | 
| 172 | 0 |     return RPCHelpMan{ | 
| 173 | 0 |         "getdescriptorinfo", | 
| 174 | 0 |         "Analyses a descriptor.\n", | 
| 175 | 0 |         { | 
| 176 | 0 |             {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."}, | 
| 177 | 0 |         }, | 
| 178 | 0 |         RPCResult{ | 
| 179 | 0 |             RPCResult::Type::OBJ, "", "", | 
| 180 | 0 |             { | 
| 181 | 0 |                 {RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys. For a multipath descriptor, only the first will be returned."}, | 
| 182 | 0 |                 {RPCResult::Type::ARR, "multipath_expansion", /*optional=*/true, "All descriptors produced by expanding multipath derivation elements. Only if the provided descriptor specifies multipath derivation elements.", | 
| 183 | 0 |                 { | 
| 184 | 0 |                     {RPCResult::Type::STR, "", ""}, | 
| 185 | 0 |                 }}, | 
| 186 | 0 |                 {RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"}, | 
| 187 | 0 |                 {RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"}, | 
| 188 | 0 |                 {RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"}, | 
| 189 | 0 |                 {RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"}, | 
| 190 | 0 |             } | 
| 191 | 0 |         }, | 
| 192 | 0 |         RPCExamples{ | 
| 193 | 0 |             "Analyse a descriptor\n" + | 
| 194 | 0 |             HelpExampleCli("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") + | 
| 195 | 0 |             HelpExampleRpc("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") | 
| 196 | 0 |         }, | 
| 197 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 198 | 0 |         { | 
| 199 | 0 |             FlatSigningProvider provider; | 
| 200 | 0 |             std::string error; | 
| 201 | 0 |             auto descs = Parse(request.params[0].get_str(), provider, error); | 
| 202 | 0 |             if (descs.empty()) { | 
| 203 | 0 |                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); | 
| 204 | 0 |             } | 
| 205 |  |  | 
| 206 | 0 |             UniValue result(UniValue::VOBJ); | 
| 207 | 0 |             result.pushKV("descriptor", descs.at(0)->ToString()); | 
| 208 |  | 
 | 
| 209 | 0 |             if (descs.size() > 1) { | 
| 210 | 0 |                 UniValue multipath_descs(UniValue::VARR); | 
| 211 | 0 |                 for (const auto& d : descs) { | 
| 212 | 0 |                     multipath_descs.push_back(d->ToString()); | 
| 213 | 0 |                 } | 
| 214 | 0 |                 result.pushKV("multipath_expansion", multipath_descs); | 
| 215 | 0 |             } | 
| 216 |  | 
 | 
| 217 | 0 |             result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str())); | 
| 218 | 0 |             result.pushKV("isrange", descs.at(0)->IsRange()); | 
| 219 | 0 |             result.pushKV("issolvable", descs.at(0)->IsSolvable()); | 
| 220 | 0 |             result.pushKV("hasprivatekeys", provider.keys.size() > 0); | 
| 221 | 0 |             return result; | 
| 222 | 0 |         }, | 
| 223 | 0 |     }; | 
| 224 | 0 | } | 
| 225 |  |  | 
| 226 |  | static UniValue DeriveAddresses(const Descriptor* desc, int64_t range_begin, int64_t range_end, FlatSigningProvider& key_provider) | 
| 227 | 0 | { | 
| 228 | 0 |     UniValue addresses(UniValue::VARR); | 
| 229 |  | 
 | 
| 230 | 0 |     for (int64_t i = range_begin; i <= range_end; ++i) { | 
| 231 | 0 |         FlatSigningProvider provider; | 
| 232 | 0 |         std::vector<CScript> scripts; | 
| 233 | 0 |         if (!desc->Expand(i, key_provider, scripts, provider)) { | 
| 234 | 0 |             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys"); | 
| 235 | 0 |         } | 
| 236 |  |  | 
| 237 | 0 |         for (const CScript& script : scripts) { | 
| 238 | 0 |             CTxDestination dest; | 
| 239 | 0 |             if (!ExtractDestination(script, dest)) { | 
| 240 |  |                 // ExtractDestination no longer returns true for P2PK since it doesn't have a corresponding address | 
| 241 |  |                 // However combo will output P2PK and should just ignore that script | 
| 242 | 0 |                 if (scripts.size() > 1 && std::get_if<PubKeyDestination>(&dest)) { | 
| 243 | 0 |                     continue; | 
| 244 | 0 |                 } | 
| 245 | 0 |                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address"); | 
| 246 | 0 |             } | 
| 247 |  |  | 
| 248 | 0 |             addresses.push_back(EncodeDestination(dest)); | 
| 249 | 0 |         } | 
| 250 | 0 |     } | 
| 251 |  |  | 
| 252 |  |     // This should not be possible, but an assert seems overkill: | 
| 253 | 0 |     if (addresses.empty()) { | 
| 254 | 0 |         throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result"); | 
| 255 | 0 |     } | 
| 256 |  |  | 
| 257 | 0 |     return addresses; | 
| 258 | 0 | } | 
| 259 |  |  | 
| 260 |  | static RPCHelpMan deriveaddresses() | 
| 261 | 0 | { | 
| 262 | 0 |     const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu"; | 
| 263 |  | 
 | 
| 264 | 0 |     return RPCHelpMan{ | 
| 265 | 0 |         "deriveaddresses", | 
| 266 | 0 |         "Derives one or more addresses corresponding to an output descriptor.\n" | 
| 267 | 0 |          "Examples of output descriptors are:\n" | 
| 268 | 0 |          "    pkh(<pubkey>)                                     P2PKH outputs for the given pubkey\n" | 
| 269 | 0 |          "    wpkh(<pubkey>)                                    Native segwit P2PKH outputs for the given pubkey\n" | 
| 270 | 0 |          "    sh(multi(<n>,<pubkey>,<pubkey>,...))              P2SH-multisig outputs for the given threshold and pubkeys\n" | 
| 271 | 0 |          "    raw(<hex script>)                                 Outputs whose output script equals the specified hex-encoded bytes\n" | 
| 272 | 0 |          "    tr(<pubkey>,multi_a(<n>,<pubkey>,<pubkey>,...))   P2TR-multisig outputs for the given threshold and pubkeys\n" | 
| 273 | 0 |          "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" | 
| 274 | 0 |          "or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n" | 
| 275 | 0 |         "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n", | 
| 276 | 0 |         { | 
| 277 | 0 |             {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."}, | 
| 278 | 0 |             {"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."}, | 
| 279 | 0 |         }, | 
| 280 | 0 |         { | 
| 281 | 0 |             RPCResult{"for single derivation descriptors", | 
| 282 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 283 | 0 |                 { | 
| 284 | 0 |                     {RPCResult::Type::STR, "address", "the derived addresses"}, | 
| 285 | 0 |                 } | 
| 286 | 0 |             }, | 
| 287 | 0 |             RPCResult{"for multipath descriptors", | 
| 288 | 0 |                 RPCResult::Type::ARR, "", "The derived addresses for each of the multipath expansions of the descriptor, in multipath specifier order", | 
| 289 | 0 |                 { | 
| 290 | 0 |                     { | 
| 291 | 0 |                         RPCResult::Type::ARR, "", "The derived addresses for a multipath descriptor expansion", | 
| 292 | 0 |                         { | 
| 293 | 0 |                             {RPCResult::Type::STR, "address", "the derived address"}, | 
| 294 | 0 |                         }, | 
| 295 | 0 |                     }, | 
| 296 | 0 |                 }, | 
| 297 | 0 |             }, | 
| 298 | 0 |         }, | 
| 299 | 0 |         RPCExamples{ | 
| 300 | 0 |             "First three native segwit receive addresses\n" + | 
| 301 | 0 |             HelpExampleCli("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\" \"[0,2]\"") + | 
| 302 | 0 |             HelpExampleRpc("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\", \"[0,2]\"") | 
| 303 | 0 |         }, | 
| 304 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 305 | 0 |         { | 
| 306 | 0 |             const std::string desc_str = request.params[0].get_str(); | 
| 307 |  | 
 | 
| 308 | 0 |             int64_t range_begin = 0; | 
| 309 | 0 |             int64_t range_end = 0; | 
| 310 |  | 
 | 
| 311 | 0 |             if (request.params.size() >= 2 && !request.params[1].isNull()) { | 
| 312 | 0 |                 std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]); | 
| 313 | 0 |             } | 
| 314 |  | 
 | 
| 315 | 0 |             FlatSigningProvider key_provider; | 
| 316 | 0 |             std::string error; | 
| 317 | 0 |             auto descs = Parse(desc_str, key_provider, error, /* require_checksum = */ true); | 
| 318 | 0 |             if (descs.empty()) { | 
| 319 | 0 |                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); | 
| 320 | 0 |             } | 
| 321 | 0 |             auto& desc = descs.at(0); | 
| 322 | 0 |             if (!desc->IsRange() && request.params.size() > 1) { | 
| 323 | 0 |                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor"); | 
| 324 | 0 |             } | 
| 325 |  |  | 
| 326 | 0 |             if (desc->IsRange() && request.params.size() == 1) { | 
| 327 | 0 |                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor"); | 
| 328 | 0 |             } | 
| 329 |  |  | 
| 330 | 0 |             UniValue addresses = DeriveAddresses(desc.get(), range_begin, range_end, key_provider); | 
| 331 |  | 
 | 
| 332 | 0 |             if (descs.size() == 1) { | 
| 333 | 0 |                 return addresses; | 
| 334 | 0 |             } | 
| 335 |  |  | 
| 336 | 0 |             UniValue ret(UniValue::VARR); | 
| 337 | 0 |             ret.push_back(addresses); | 
| 338 | 0 |             for (size_t i = 1; i < descs.size(); ++i) { | 
| 339 | 0 |                 ret.push_back(DeriveAddresses(descs.at(i).get(), range_begin, range_end, key_provider)); | 
| 340 | 0 |             } | 
| 341 | 0 |             return ret; | 
| 342 | 0 |         }, | 
| 343 | 0 |     }; | 
| 344 | 0 | } | 
| 345 |  |  | 
| 346 |  | void RegisterOutputScriptRPCCommands(CRPCTable& t) | 
| 347 | 51.2k | { | 
| 348 | 51.2k |     static const CRPCCommand commands[]{ | 
| 349 | 51.2k |         {"util", &validateaddress}, | 
| 350 | 51.2k |         {"util", &createmultisig}, | 
| 351 | 51.2k |         {"util", &deriveaddresses}, | 
| 352 | 51.2k |         {"util", &getdescriptorinfo}, | 
| 353 | 51.2k |     }; | 
| 354 | 205k |     for (const auto& c : commands) { | 
| 355 | 205k |         t.appendCommand(c.name, &c); | 
| 356 | 205k |     } | 
| 357 | 51.2k | } |