util.cpp
1 // Copyright (c) 2017-2022 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #if defined(HAVE_CONFIG_H) 6 #include <config/bitcoin-config.h> 7 #endif 8 9 #include <clientversion.h> 10 #include <core_io.h> 11 #include <common/args.h> 12 #include <consensus/amount.h> 13 #include <script/interpreter.h> 14 #include <key_io.h> 15 #include <outputtype.h> 16 #include <rpc/util.h> 17 #include <script/descriptor.h> 18 #include <script/signingprovider.h> 19 #include <script/solver.h> 20 #include <tinyformat.h> 21 #include <util/check.h> 22 #include <util/result.h> 23 #include <util/strencodings.h> 24 #include <util/string.h> 25 #include <util/translation.h> 26 27 #include <string_view> 28 #include <tuple> 29 30 const std::string UNIX_EPOCH_TIME = "UNIX epoch time"; 31 const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"}; 32 33 std::string GetAllOutputTypes() 34 { 35 std::vector<std::string> ret; 36 using U = std::underlying_type<TxoutType>::type; 37 for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) { 38 ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i))); 39 } 40 return Join(ret, ", "); 41 } 42 43 void RPCTypeCheckObj(const UniValue& o, 44 const std::map<std::string, UniValueType>& typesExpected, 45 bool fAllowNull, 46 bool fStrict) 47 { 48 for (const auto& t : typesExpected) { 49 const UniValue& v = o.find_value(t.first); 50 if (!fAllowNull && v.isNull()) 51 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); 52 53 if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) 54 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("JSON value of type %s for field %s is not of expected type %s", uvTypeName(v.type()), t.first, uvTypeName(t.second.type))); 55 } 56 57 if (fStrict) 58 { 59 for (const std::string& k : o.getKeys()) 60 { 61 if (typesExpected.count(k) == 0) 62 { 63 std::string err = strprintf("Unexpected key %s", k); 64 throw JSONRPCError(RPC_TYPE_ERROR, err); 65 } 66 } 67 } 68 } 69 70 CAmount AmountFromValue(const UniValue& value, int decimals) 71 { 72 if (!value.isNum() && !value.isStr()) 73 throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); 74 CAmount amount; 75 if (!ParseFixedPoint(value.getValStr(), decimals, &amount)) 76 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); 77 if (!MoneyRange(amount)) 78 throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); 79 return amount; 80 } 81 82 CFeeRate ParseFeeRate(const UniValue& json) 83 { 84 CAmount val{AmountFromValue(json)}; 85 if (val >= COIN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee rates larger than or equal to 1BTC/kvB are not accepted"); 86 return CFeeRate{val}; 87 } 88 89 uint256 ParseHashV(const UniValue& v, std::string_view name) 90 { 91 const std::string& strHex(v.get_str()); 92 if (64 != strHex.length()) 93 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", name, 64, strHex.length(), strHex)); 94 if (!IsHex(strHex)) // Note: IsHex("") is false 95 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be hexadecimal string (not '%s')", name, strHex)); 96 return uint256S(strHex); 97 } 98 uint256 ParseHashO(const UniValue& o, std::string_view strKey) 99 { 100 return ParseHashV(o.find_value(strKey), strKey); 101 } 102 std::vector<unsigned char> ParseHexV(const UniValue& v, std::string_view name) 103 { 104 std::string strHex; 105 if (v.isStr()) 106 strHex = v.get_str(); 107 if (!IsHex(strHex)) 108 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be hexadecimal string (not '%s')", name, strHex)); 109 return ParseHex(strHex); 110 } 111 std::vector<unsigned char> ParseHexO(const UniValue& o, std::string_view strKey) 112 { 113 return ParseHexV(o.find_value(strKey), strKey); 114 } 115 116 namespace { 117 118 /** 119 * Quote an argument for shell. 120 * 121 * @note This is intended for help, not for security-sensitive purposes. 122 */ 123 std::string ShellQuote(const std::string& s) 124 { 125 std::string result; 126 result.reserve(s.size() * 2); 127 for (const char ch: s) { 128 if (ch == '\'') { 129 result += "'\''"; 130 } else { 131 result += ch; 132 } 133 } 134 return "'" + result + "'"; 135 } 136 137 /** 138 * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing. 139 * 140 * @note This is intended for help, not for security-sensitive purposes. 141 */ 142 std::string ShellQuoteIfNeeded(const std::string& s) 143 { 144 for (const char ch: s) { 145 if (ch == ' ' || ch == '\'' || ch == '"') { 146 return ShellQuote(s); 147 } 148 } 149 150 return s; 151 } 152 153 } 154 155 std::string HelpExampleCli(const std::string& methodname, const std::string& args) 156 { 157 return "> bitcoin-cli " + methodname + " " + args + "\n"; 158 } 159 160 std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args) 161 { 162 std::string result = "> bitcoin-cli -named " + methodname; 163 for (const auto& argpair: args) { 164 const auto& value = argpair.second.isStr() 165 ? argpair.second.get_str() 166 : argpair.second.write(); 167 result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value); 168 } 169 result += "\n"; 170 return result; 171 } 172 173 std::string HelpExampleRpc(const std::string& methodname, const std::string& args) 174 { 175 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", " 176 "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; 177 } 178 179 std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args) 180 { 181 UniValue params(UniValue::VOBJ); 182 for (const auto& param: args) { 183 params.pushKV(param.first, param.second); 184 } 185 186 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", " 187 "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; 188 } 189 190 // Converts a hex string to a public key if possible 191 CPubKey HexToPubKey(const std::string& hex_in) 192 { 193 if (!IsHex(hex_in)) { 194 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in); 195 } 196 CPubKey vchPubKey(ParseHex(hex_in)); 197 if (!vchPubKey.IsFullyValid()) { 198 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in); 199 } 200 return vchPubKey; 201 } 202 203 // Retrieves a public key for an address from the given FillableSigningProvider 204 CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in) 205 { 206 CTxDestination dest = DecodeDestination(addr_in); 207 if (!IsValidDestination(dest)) { 208 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + addr_in); 209 } 210 CKeyID key = GetKeyForDestination(keystore, dest); 211 if (key.IsNull()) { 212 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' does not refer to a key", addr_in)); 213 } 214 CPubKey vchPubKey; 215 if (!keystore.GetPubKey(key, vchPubKey)) { 216 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("no full public key for address %s", addr_in)); 217 } 218 if (!vchPubKey.IsFullyValid()) { 219 throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet contains an invalid public key"); 220 } 221 return vchPubKey; 222 } 223 224 // Creates a multisig address from a given list of public keys, number of signatures required, and the address type 225 CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out) 226 { 227 // Gather public keys 228 if (required < 1) { 229 throw JSONRPCError(RPC_INVALID_PARAMETER, "a multisignature address must require at least one key to redeem"); 230 } 231 if ((int)pubkeys.size() < required) { 232 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("not enough keys supplied (got %u keys, but need at least %d to redeem)", pubkeys.size(), required)); 233 } 234 if (pubkeys.size() > MAX_PUBKEYS_PER_MULTISIG) { 235 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of keys involved in the multisignature address creation > %d\nReduce the number", MAX_PUBKEYS_PER_MULTISIG)); 236 } 237 238 script_out = GetScriptForMultisig(required, pubkeys); 239 240 // Check if any keys are uncompressed. If so, the type is legacy 241 for (const CPubKey& pk : pubkeys) { 242 if (!pk.IsCompressed()) { 243 type = OutputType::LEGACY; 244 break; 245 } 246 } 247 248 if (type == OutputType::LEGACY && script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) { 249 throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE))); 250 } 251 252 // Make the address 253 CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type); 254 255 return dest; 256 } 257 258 class DescribeAddressVisitor 259 { 260 public: 261 explicit DescribeAddressVisitor() = default; 262 263 UniValue operator()(const CNoDestination& dest) const 264 { 265 return UniValue(UniValue::VOBJ); 266 } 267 268 UniValue operator()(const PubKeyDestination& dest) const 269 { 270 return UniValue(UniValue::VOBJ); 271 } 272 273 UniValue operator()(const PKHash& keyID) const 274 { 275 UniValue obj(UniValue::VOBJ); 276 obj.pushKV("isscript", false); 277 obj.pushKV("iswitness", false); 278 return obj; 279 } 280 281 UniValue operator()(const ScriptHash& scriptID) const 282 { 283 UniValue obj(UniValue::VOBJ); 284 obj.pushKV("isscript", true); 285 obj.pushKV("iswitness", false); 286 return obj; 287 } 288 289 UniValue operator()(const WitnessV0KeyHash& id) const 290 { 291 UniValue obj(UniValue::VOBJ); 292 obj.pushKV("isscript", false); 293 obj.pushKV("iswitness", true); 294 obj.pushKV("witness_version", 0); 295 obj.pushKV("witness_program", HexStr(id)); 296 return obj; 297 } 298 299 UniValue operator()(const WitnessV0ScriptHash& id) const 300 { 301 UniValue obj(UniValue::VOBJ); 302 obj.pushKV("isscript", true); 303 obj.pushKV("iswitness", true); 304 obj.pushKV("witness_version", 0); 305 obj.pushKV("witness_program", HexStr(id)); 306 return obj; 307 } 308 309 UniValue operator()(const WitnessV1Taproot& tap) const 310 { 311 UniValue obj(UniValue::VOBJ); 312 obj.pushKV("isscript", true); 313 obj.pushKV("iswitness", true); 314 obj.pushKV("witness_version", 1); 315 obj.pushKV("witness_program", HexStr(tap)); 316 return obj; 317 } 318 319 UniValue operator()(const WitnessUnknown& id) const 320 { 321 UniValue obj(UniValue::VOBJ); 322 obj.pushKV("iswitness", true); 323 obj.pushKV("witness_version", id.GetWitnessVersion()); 324 obj.pushKV("witness_program", HexStr(id.GetWitnessProgram())); 325 return obj; 326 } 327 }; 328 329 UniValue DescribeAddress(const CTxDestination& dest) 330 { 331 return std::visit(DescribeAddressVisitor(), dest); 332 } 333 334 /** 335 * Returns a sighash value corresponding to the passed in argument. 336 * 337 * @pre The sighash argument should be string or null. 338 */ 339 int ParseSighashString(const UniValue& sighash) 340 { 341 if (sighash.isNull()) { 342 return SIGHASH_DEFAULT; 343 } 344 const auto result{SighashFromStr(sighash.get_str())}; 345 if (!result) { 346 throw JSONRPCError(RPC_INVALID_PARAMETER, util::ErrorString(result).original); 347 } 348 return result.value(); 349 } 350 351 unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target) 352 { 353 const int target{value.getInt<int>()}; 354 const unsigned int unsigned_target{static_cast<unsigned int>(target)}; 355 if (target < 1 || unsigned_target > max_target) { 356 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target)); 357 } 358 return unsigned_target; 359 } 360 361 RPCErrorCode RPCErrorFromTransactionError(TransactionError terr) 362 { 363 switch (terr) { 364 case TransactionError::MEMPOOL_REJECTED: 365 return RPC_TRANSACTION_REJECTED; 366 case TransactionError::ALREADY_IN_CHAIN: 367 return RPC_TRANSACTION_ALREADY_IN_CHAIN; 368 case TransactionError::P2P_DISABLED: 369 return RPC_CLIENT_P2P_DISABLED; 370 case TransactionError::INVALID_PSBT: 371 case TransactionError::PSBT_MISMATCH: 372 return RPC_INVALID_PARAMETER; 373 case TransactionError::SIGHASH_MISMATCH: 374 return RPC_DESERIALIZATION_ERROR; 375 default: break; 376 } 377 return RPC_TRANSACTION_ERROR; 378 } 379 380 UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string) 381 { 382 if (err_string.length() > 0) { 383 return JSONRPCError(RPCErrorFromTransactionError(terr), err_string); 384 } else { 385 return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original); 386 } 387 } 388 389 /** 390 * A pair of strings that can be aligned (through padding) with other Sections 391 * later on 392 */ 393 struct Section { 394 Section(const std::string& left, const std::string& right) 395 : m_left{left}, m_right{right} {} 396 std::string m_left; 397 const std::string m_right; 398 }; 399 400 /** 401 * Keeps track of RPCArgs by transforming them into sections for the purpose 402 * of serializing everything to a single string 403 */ 404 struct Sections { 405 std::vector<Section> m_sections; 406 size_t m_max_pad{0}; 407 408 void PushSection(const Section& s) 409 { 410 m_max_pad = std::max(m_max_pad, s.m_left.size()); 411 m_sections.push_back(s); 412 } 413 414 /** 415 * Recursive helper to translate an RPCArg into sections 416 */ 417 void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE) 418 { 419 const auto indent = std::string(current_indent, ' '); 420 const auto indent_next = std::string(current_indent + 2, ' '); 421 const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name 422 const bool is_top_level_arg{outer_type == OuterType::NONE}; // True on the first recursion 423 424 switch (arg.m_type) { 425 case RPCArg::Type::STR_HEX: 426 case RPCArg::Type::STR: 427 case RPCArg::Type::NUM: 428 case RPCArg::Type::AMOUNT: 429 case RPCArg::Type::RANGE: 430 case RPCArg::Type::BOOL: 431 case RPCArg::Type::OBJ_NAMED_PARAMS: { 432 if (is_top_level_arg) return; // Nothing more to do for non-recursive types on first recursion 433 auto left = indent; 434 if (arg.m_opts.type_str.size() != 0 && push_name) { 435 left += "\"" + arg.GetName() + "\": " + arg.m_opts.type_str.at(0); 436 } else { 437 left += push_name ? arg.ToStringObj(/*oneline=*/false) : arg.ToString(/*oneline=*/false); 438 } 439 left += ","; 440 PushSection({left, arg.ToDescriptionString(/*is_named_arg=*/push_name)}); 441 break; 442 } 443 case RPCArg::Type::OBJ: 444 case RPCArg::Type::OBJ_USER_KEYS: { 445 const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name); 446 PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right}); 447 for (const auto& arg_inner : arg.m_inner) { 448 Push(arg_inner, current_indent + 2, OuterType::OBJ); 449 } 450 if (arg.m_type != RPCArg::Type::OBJ) { 451 PushSection({indent_next + "...", ""}); 452 } 453 PushSection({indent + "}" + (is_top_level_arg ? "" : ","), ""}); 454 break; 455 } 456 case RPCArg::Type::ARR: { 457 auto left = indent; 458 left += push_name ? "\"" + arg.GetName() + "\": " : ""; 459 left += "["; 460 const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name); 461 PushSection({left, right}); 462 for (const auto& arg_inner : arg.m_inner) { 463 Push(arg_inner, current_indent + 2, OuterType::ARR); 464 } 465 PushSection({indent_next + "...", ""}); 466 PushSection({indent + "]" + (is_top_level_arg ? "" : ","), ""}); 467 break; 468 } 469 } // no default case, so the compiler can warn about missing cases 470 } 471 472 /** 473 * Concatenate all sections with proper padding 474 */ 475 std::string ToString() const 476 { 477 std::string ret; 478 const size_t pad = m_max_pad + 4; 479 for (const auto& s : m_sections) { 480 // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a 481 // brace like {, }, [, or ] 482 CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos); 483 if (s.m_right.empty()) { 484 ret += s.m_left; 485 ret += "\n"; 486 continue; 487 } 488 489 std::string left = s.m_left; 490 left.resize(pad, ' '); 491 ret += left; 492 493 // Properly pad after newlines 494 std::string right; 495 size_t begin = 0; 496 size_t new_line_pos = s.m_right.find_first_of('\n'); 497 while (true) { 498 right += s.m_right.substr(begin, new_line_pos - begin); 499 if (new_line_pos == std::string::npos) { 500 break; //No new line 501 } 502 right += "\n" + std::string(pad, ' '); 503 begin = s.m_right.find_first_not_of(' ', new_line_pos + 1); 504 if (begin == std::string::npos) { 505 break; // Empty line 506 } 507 new_line_pos = s.m_right.find_first_of('\n', begin + 1); 508 } 509 ret += right; 510 ret += "\n"; 511 } 512 return ret; 513 } 514 }; 515 516 RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples) 517 : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {} 518 519 RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun) 520 : m_name{std::move(name)}, 521 m_fun{std::move(fun)}, 522 m_description{std::move(description)}, 523 m_args{std::move(args)}, 524 m_results{std::move(results)}, 525 m_examples{std::move(examples)} 526 { 527 // Map of parameter names and types just used to check whether the names are 528 // unique. Parameter names always need to be unique, with the exception that 529 // there can be pairs of POSITIONAL and NAMED parameters with the same name. 530 enum ParamType { POSITIONAL = 1, NAMED = 2, NAMED_ONLY = 4 }; 531 std::map<std::string, int> param_names; 532 533 for (const auto& arg : m_args) { 534 std::vector<std::string> names = SplitString(arg.m_names, '|'); 535 // Should have unique named arguments 536 for (const std::string& name : names) { 537 auto& param_type = param_names[name]; 538 CHECK_NONFATAL(!(param_type & POSITIONAL)); 539 CHECK_NONFATAL(!(param_type & NAMED_ONLY)); 540 param_type |= POSITIONAL; 541 } 542 if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) { 543 for (const auto& inner : arg.m_inner) { 544 std::vector<std::string> inner_names = SplitString(inner.m_names, '|'); 545 for (const std::string& inner_name : inner_names) { 546 auto& param_type = param_names[inner_name]; 547 CHECK_NONFATAL(!(param_type & POSITIONAL) || inner.m_opts.also_positional); 548 CHECK_NONFATAL(!(param_type & NAMED)); 549 CHECK_NONFATAL(!(param_type & NAMED_ONLY)); 550 param_type |= inner.m_opts.also_positional ? NAMED : NAMED_ONLY; 551 } 552 } 553 } 554 // Default value type should match argument type only when defined 555 if (arg.m_fallback.index() == 2) { 556 const RPCArg::Type type = arg.m_type; 557 switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) { 558 case UniValue::VOBJ: 559 CHECK_NONFATAL(type == RPCArg::Type::OBJ); 560 break; 561 case UniValue::VARR: 562 CHECK_NONFATAL(type == RPCArg::Type::ARR); 563 break; 564 case UniValue::VSTR: 565 CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT); 566 break; 567 case UniValue::VNUM: 568 CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE); 569 break; 570 case UniValue::VBOOL: 571 CHECK_NONFATAL(type == RPCArg::Type::BOOL); 572 break; 573 case UniValue::VNULL: 574 // Null values are accepted in all arguments 575 break; 576 default: 577 NONFATAL_UNREACHABLE(); 578 break; 579 } 580 } 581 } 582 } 583 584 std::string RPCResults::ToDescriptionString() const 585 { 586 std::string result; 587 for (const auto& r : m_results) { 588 if (r.m_type == RPCResult::Type::ANY) continue; // for testing only 589 if (r.m_cond.empty()) { 590 result += "\nResult:\n"; 591 } else { 592 result += "\nResult (" + r.m_cond + "):\n"; 593 } 594 Sections sections; 595 r.ToSections(sections); 596 result += sections.ToString(); 597 } 598 return result; 599 } 600 601 std::string RPCExamples::ToDescriptionString() const 602 { 603 return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples; 604 } 605 606 UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const 607 { 608 if (request.mode == JSONRPCRequest::GET_ARGS) { 609 return GetArgMap(); 610 } 611 /* 612 * Check if the given request is valid according to this command or if 613 * the user is asking for help information, and throw help when appropriate. 614 */ 615 if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) { 616 throw std::runtime_error(ToString()); 617 } 618 UniValue arg_mismatch{UniValue::VOBJ}; 619 for (size_t i{0}; i < m_args.size(); ++i) { 620 const auto& arg{m_args.at(i)}; 621 UniValue match{arg.MatchesType(request.params[i])}; 622 if (!match.isTrue()) { 623 arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match)); 624 } 625 } 626 if (!arg_mismatch.empty()) { 627 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Wrong type passed:\n%s", arg_mismatch.write(4))); 628 } 629 CHECK_NONFATAL(m_req == nullptr); 630 m_req = &request; 631 UniValue ret = m_fun(*this, request); 632 m_req = nullptr; 633 if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) { 634 UniValue mismatch{UniValue::VARR}; 635 for (const auto& res : m_results.m_results) { 636 UniValue match{res.MatchesType(ret)}; 637 if (match.isTrue()) { 638 mismatch.setNull(); 639 break; 640 } 641 mismatch.push_back(match); 642 } 643 if (!mismatch.isNull()) { 644 std::string explain{ 645 mismatch.empty() ? "no possible results defined" : 646 mismatch.size() == 1 ? mismatch[0].write(4) : 647 mismatch.write(4)}; 648 throw std::runtime_error{ 649 strprintf("Internal bug detected: RPC call \"%s\" returned incorrect type:\n%s\n%s %s\nPlease report this issue here: %s\n", 650 m_name, explain, 651 PACKAGE_NAME, FormatFullVersion(), 652 PACKAGE_BUGREPORT)}; 653 } 654 } 655 return ret; 656 } 657 658 using CheckFn = void(const RPCArg&); 659 static const UniValue* DetailMaybeArg(CheckFn* check, const std::vector<RPCArg>& params, const JSONRPCRequest* req, size_t i) 660 { 661 CHECK_NONFATAL(i < params.size()); 662 const UniValue& arg{CHECK_NONFATAL(req)->params[i]}; 663 const RPCArg& param{params.at(i)}; 664 if (check) check(param); 665 666 if (!arg.isNull()) return &arg; 667 if (!std::holds_alternative<RPCArg::Default>(param.m_fallback)) return nullptr; 668 return &std::get<RPCArg::Default>(param.m_fallback); 669 } 670 671 static void CheckRequiredOrDefault(const RPCArg& param) 672 { 673 // Must use `Arg<Type>(i)` to get the argument or its default value. 674 const bool required{ 675 std::holds_alternative<RPCArg::Optional>(param.m_fallback) && RPCArg::Optional::NO == std::get<RPCArg::Optional>(param.m_fallback), 676 }; 677 CHECK_NONFATAL(required || std::holds_alternative<RPCArg::Default>(param.m_fallback)); 678 } 679 680 #define TMPL_INST(check_param, ret_type, return_code) \ 681 template <> \ 682 ret_type RPCHelpMan::ArgValue<ret_type>(size_t i) const \ 683 { \ 684 const UniValue* maybe_arg{ \ 685 DetailMaybeArg(check_param, m_args, m_req, i), \ 686 }; \ 687 return return_code \ 688 } \ 689 void force_semicolon(ret_type) 690 691 // Optional arg (without default). Can also be called on required args, if needed. 692 TMPL_INST(nullptr, const UniValue*, maybe_arg;); 693 TMPL_INST(nullptr, std::optional<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;); 694 TMPL_INST(nullptr, std::optional<bool>, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;); 695 TMPL_INST(nullptr, const std::string*, maybe_arg ? &maybe_arg->get_str() : nullptr;); 696 697 // Required arg or optional arg with default value. 698 TMPL_INST(CheckRequiredOrDefault, const UniValue&, *CHECK_NONFATAL(maybe_arg);); 699 TMPL_INST(CheckRequiredOrDefault, bool, CHECK_NONFATAL(maybe_arg)->get_bool();); 700 TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>();); 701 TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>();); 702 TMPL_INST(CheckRequiredOrDefault, const std::string&, CHECK_NONFATAL(maybe_arg)->get_str();); 703 704 bool RPCHelpMan::IsValidNumArgs(size_t num_args) const 705 { 706 size_t num_required_args = 0; 707 for (size_t n = m_args.size(); n > 0; --n) { 708 if (!m_args.at(n - 1).IsOptional()) { 709 num_required_args = n; 710 break; 711 } 712 } 713 return num_required_args <= num_args && num_args <= m_args.size(); 714 } 715 716 std::vector<std::pair<std::string, bool>> RPCHelpMan::GetArgNames() const 717 { 718 std::vector<std::pair<std::string, bool>> ret; 719 ret.reserve(m_args.size()); 720 for (const auto& arg : m_args) { 721 if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) { 722 for (const auto& inner : arg.m_inner) { 723 ret.emplace_back(inner.m_names, /*named_only=*/true); 724 } 725 } 726 ret.emplace_back(arg.m_names, /*named_only=*/false); 727 } 728 return ret; 729 } 730 731 std::string RPCHelpMan::ToString() const 732 { 733 std::string ret; 734 735 // Oneline summary 736 ret += m_name; 737 bool was_optional{false}; 738 for (const auto& arg : m_args) { 739 if (arg.m_opts.hidden) break; // Any arg that follows is also hidden 740 const bool optional = arg.IsOptional(); 741 ret += " "; 742 if (optional) { 743 if (!was_optional) ret += "( "; 744 was_optional = true; 745 } else { 746 if (was_optional) ret += ") "; 747 was_optional = false; 748 } 749 ret += arg.ToString(/*oneline=*/true); 750 } 751 if (was_optional) ret += " )"; 752 753 // Description 754 ret += "\n\n" + TrimString(m_description) + "\n"; 755 756 // Arguments 757 Sections sections; 758 Sections named_only_sections; 759 for (size_t i{0}; i < m_args.size(); ++i) { 760 const auto& arg = m_args.at(i); 761 if (arg.m_opts.hidden) break; // Any arg that follows is also hidden 762 763 // Push named argument name and description 764 sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString(/*is_named_arg=*/true)); 765 sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size()); 766 767 // Recursively push nested args 768 sections.Push(arg); 769 770 // Push named-only argument sections 771 if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) { 772 for (const auto& arg_inner : arg.m_inner) { 773 named_only_sections.PushSection({arg_inner.GetFirstName(), arg_inner.ToDescriptionString(/*is_named_arg=*/true)}); 774 named_only_sections.Push(arg_inner); 775 } 776 } 777 } 778 779 if (!sections.m_sections.empty()) ret += "\nArguments:\n"; 780 ret += sections.ToString(); 781 if (!named_only_sections.m_sections.empty()) ret += "\nNamed Arguments:\n"; 782 ret += named_only_sections.ToString(); 783 784 // Result 785 ret += m_results.ToDescriptionString(); 786 787 // Examples 788 ret += m_examples.ToDescriptionString(); 789 790 return ret; 791 } 792 793 UniValue RPCHelpMan::GetArgMap() const 794 { 795 UniValue arr{UniValue::VARR}; 796 797 auto push_back_arg_info = [&arr](const std::string& rpc_name, int pos, const std::string& arg_name, const RPCArg::Type& type) { 798 UniValue map{UniValue::VARR}; 799 map.push_back(rpc_name); 800 map.push_back(pos); 801 map.push_back(arg_name); 802 map.push_back(type == RPCArg::Type::STR || 803 type == RPCArg::Type::STR_HEX); 804 arr.push_back(map); 805 }; 806 807 for (int i{0}; i < int(m_args.size()); ++i) { 808 const auto& arg = m_args.at(i); 809 std::vector<std::string> arg_names = SplitString(arg.m_names, '|'); 810 for (const auto& arg_name : arg_names) { 811 push_back_arg_info(m_name, i, arg_name, arg.m_type); 812 if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) { 813 for (const auto& inner : arg.m_inner) { 814 std::vector<std::string> inner_names = SplitString(inner.m_names, '|'); 815 for (const std::string& inner_name : inner_names) { 816 push_back_arg_info(m_name, i, inner_name, inner.m_type); 817 } 818 } 819 } 820 } 821 } 822 return arr; 823 } 824 825 static std::optional<UniValue::VType> ExpectedType(RPCArg::Type type) 826 { 827 using Type = RPCArg::Type; 828 switch (type) { 829 case Type::STR_HEX: 830 case Type::STR: { 831 return UniValue::VSTR; 832 } 833 case Type::NUM: { 834 return UniValue::VNUM; 835 } 836 case Type::AMOUNT: { 837 // VNUM or VSTR, checked inside AmountFromValue() 838 return std::nullopt; 839 } 840 case Type::RANGE: { 841 // VNUM or VARR, checked inside ParseRange() 842 return std::nullopt; 843 } 844 case Type::BOOL: { 845 return UniValue::VBOOL; 846 } 847 case Type::OBJ: 848 case Type::OBJ_NAMED_PARAMS: 849 case Type::OBJ_USER_KEYS: { 850 return UniValue::VOBJ; 851 } 852 case Type::ARR: { 853 return UniValue::VARR; 854 } 855 } // no default case, so the compiler can warn about missing cases 856 NONFATAL_UNREACHABLE(); 857 } 858 859 UniValue RPCArg::MatchesType(const UniValue& request) const 860 { 861 if (m_opts.skip_type_check) return true; 862 if (IsOptional() && request.isNull()) return true; 863 const auto exp_type{ExpectedType(m_type)}; 864 if (!exp_type) return true; // nothing to check 865 866 if (*exp_type != request.getType()) { 867 return strprintf("JSON value of type %s is not of expected type %s", uvTypeName(request.getType()), uvTypeName(*exp_type)); 868 } 869 return true; 870 } 871 872 std::string RPCArg::GetFirstName() const 873 { 874 return m_names.substr(0, m_names.find('|')); 875 } 876 877 std::string RPCArg::GetName() const 878 { 879 CHECK_NONFATAL(std::string::npos == m_names.find('|')); 880 return m_names; 881 } 882 883 bool RPCArg::IsOptional() const 884 { 885 if (m_fallback.index() != 0) { 886 return true; 887 } else { 888 return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback); 889 } 890 } 891 892 std::string RPCArg::ToDescriptionString(bool is_named_arg) const 893 { 894 std::string ret; 895 ret += "("; 896 if (m_opts.type_str.size() != 0) { 897 ret += m_opts.type_str.at(1); 898 } else { 899 switch (m_type) { 900 case Type::STR_HEX: 901 case Type::STR: { 902 ret += "string"; 903 break; 904 } 905 case Type::NUM: { 906 ret += "numeric"; 907 break; 908 } 909 case Type::AMOUNT: { 910 ret += "numeric or string"; 911 break; 912 } 913 case Type::RANGE: { 914 ret += "numeric or array"; 915 break; 916 } 917 case Type::BOOL: { 918 ret += "boolean"; 919 break; 920 } 921 case Type::OBJ: 922 case Type::OBJ_NAMED_PARAMS: 923 case Type::OBJ_USER_KEYS: { 924 ret += "json object"; 925 break; 926 } 927 case Type::ARR: { 928 ret += "json array"; 929 break; 930 } 931 } // no default case, so the compiler can warn about missing cases 932 } 933 if (m_fallback.index() == 1) { 934 ret += ", optional, default=" + std::get<RPCArg::DefaultHint>(m_fallback); 935 } else if (m_fallback.index() == 2) { 936 ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write(); 937 } else { 938 switch (std::get<RPCArg::Optional>(m_fallback)) { 939 case RPCArg::Optional::OMITTED: { 940 if (is_named_arg) ret += ", optional"; // Default value is "null" in dicts. Otherwise, 941 // nothing to do. Element is treated as if not present and has no default value 942 break; 943 } 944 case RPCArg::Optional::NO: { 945 ret += ", required"; 946 break; 947 } 948 } // no default case, so the compiler can warn about missing cases 949 } 950 ret += ")"; 951 if (m_type == Type::OBJ_NAMED_PARAMS) ret += " Options object that can be used to pass named arguments, listed below."; 952 ret += m_description.empty() ? "" : " " + m_description; 953 return ret; 954 } 955 956 void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const 957 { 958 // Indentation 959 const std::string indent(current_indent, ' '); 960 const std::string indent_next(current_indent + 2, ' '); 961 962 // Elements in a JSON structure (dictionary or array) are separated by a comma 963 const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""}; 964 965 // The key name if recursed into a dictionary 966 const std::string maybe_key{ 967 outer_type == OuterType::OBJ ? 968 "\"" + this->m_key_name + "\" : " : 969 ""}; 970 971 // Format description with type 972 const auto Description = [&](const std::string& type) { 973 return "(" + type + (this->m_optional ? ", optional" : "") + ")" + 974 (this->m_description.empty() ? "" : " " + this->m_description); 975 }; 976 977 switch (m_type) { 978 case Type::ELISION: { 979 // If the inner result is empty, use three dots for elision 980 sections.PushSection({indent + "..." + maybe_separator, m_description}); 981 return; 982 } 983 case Type::ANY: { 984 NONFATAL_UNREACHABLE(); // Only for testing 985 } 986 case Type::NONE: { 987 sections.PushSection({indent + "null" + maybe_separator, Description("json null")}); 988 return; 989 } 990 case Type::STR: { 991 sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")}); 992 return; 993 } 994 case Type::STR_AMOUNT: { 995 sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")}); 996 return; 997 } 998 case Type::STR_HEX: { 999 sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")}); 1000 return; 1001 } 1002 case Type::NUM: { 1003 sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")}); 1004 return; 1005 } 1006 case Type::NUM_TIME: { 1007 sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")}); 1008 return; 1009 } 1010 case Type::BOOL: { 1011 sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")}); 1012 return; 1013 } 1014 case Type::ARR_FIXED: 1015 case Type::ARR: { 1016 sections.PushSection({indent + maybe_key + "[", Description("json array")}); 1017 for (const auto& i : m_inner) { 1018 i.ToSections(sections, OuterType::ARR, current_indent + 2); 1019 } 1020 CHECK_NONFATAL(!m_inner.empty()); 1021 if (m_type == Type::ARR && m_inner.back().m_type != Type::ELISION) { 1022 sections.PushSection({indent_next + "...", ""}); 1023 } else { 1024 // Remove final comma, which would be invalid JSON 1025 sections.m_sections.back().m_left.pop_back(); 1026 } 1027 sections.PushSection({indent + "]" + maybe_separator, ""}); 1028 return; 1029 } 1030 case Type::OBJ_DYN: 1031 case Type::OBJ: { 1032 if (m_inner.empty()) { 1033 sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")}); 1034 return; 1035 } 1036 sections.PushSection({indent + maybe_key + "{", Description("json object")}); 1037 for (const auto& i : m_inner) { 1038 i.ToSections(sections, OuterType::OBJ, current_indent + 2); 1039 } 1040 if (m_type == Type::OBJ_DYN && m_inner.back().m_type != Type::ELISION) { 1041 // If the dictionary keys are dynamic, use three dots for continuation 1042 sections.PushSection({indent_next + "...", ""}); 1043 } else { 1044 // Remove final comma, which would be invalid JSON 1045 sections.m_sections.back().m_left.pop_back(); 1046 } 1047 sections.PushSection({indent + "}" + maybe_separator, ""}); 1048 return; 1049 } 1050 } // no default case, so the compiler can warn about missing cases 1051 NONFATAL_UNREACHABLE(); 1052 } 1053 1054 static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type) 1055 { 1056 using Type = RPCResult::Type; 1057 switch (type) { 1058 case Type::ELISION: 1059 case Type::ANY: { 1060 return std::nullopt; 1061 } 1062 case Type::NONE: { 1063 return UniValue::VNULL; 1064 } 1065 case Type::STR: 1066 case Type::STR_HEX: { 1067 return UniValue::VSTR; 1068 } 1069 case Type::NUM: 1070 case Type::STR_AMOUNT: 1071 case Type::NUM_TIME: { 1072 return UniValue::VNUM; 1073 } 1074 case Type::BOOL: { 1075 return UniValue::VBOOL; 1076 } 1077 case Type::ARR_FIXED: 1078 case Type::ARR: { 1079 return UniValue::VARR; 1080 } 1081 case Type::OBJ_DYN: 1082 case Type::OBJ: { 1083 return UniValue::VOBJ; 1084 } 1085 } // no default case, so the compiler can warn about missing cases 1086 NONFATAL_UNREACHABLE(); 1087 } 1088 1089 UniValue RPCResult::MatchesType(const UniValue& result) const 1090 { 1091 if (m_skip_type_check) { 1092 return true; 1093 } 1094 1095 const auto exp_type = ExpectedType(m_type); 1096 if (!exp_type) return true; // can be any type, so nothing to check 1097 1098 if (*exp_type != result.getType()) { 1099 return strprintf("returned type is %s, but declared as %s in doc", uvTypeName(result.getType()), uvTypeName(*exp_type)); 1100 } 1101 1102 if (UniValue::VARR == result.getType()) { 1103 UniValue errors(UniValue::VOBJ); 1104 for (size_t i{0}; i < result.get_array().size(); ++i) { 1105 // If there are more results than documented, reuse the last doc_inner. 1106 const RPCResult& doc_inner{m_inner.at(std::min(m_inner.size() - 1, i))}; 1107 UniValue match{doc_inner.MatchesType(result.get_array()[i])}; 1108 if (!match.isTrue()) errors.pushKV(strprintf("%d", i), match); 1109 } 1110 if (errors.empty()) return true; // empty result array is valid 1111 return errors; 1112 } 1113 1114 if (UniValue::VOBJ == result.getType()) { 1115 if (!m_inner.empty() && m_inner.at(0).m_type == Type::ELISION) return true; 1116 UniValue errors(UniValue::VOBJ); 1117 if (m_type == Type::OBJ_DYN) { 1118 const RPCResult& doc_inner{m_inner.at(0)}; // Assume all types are the same, randomly pick the first 1119 for (size_t i{0}; i < result.get_obj().size(); ++i) { 1120 UniValue match{doc_inner.MatchesType(result.get_obj()[i])}; 1121 if (!match.isTrue()) errors.pushKV(result.getKeys()[i], match); 1122 } 1123 if (errors.empty()) return true; // empty result obj is valid 1124 return errors; 1125 } 1126 std::set<std::string> doc_keys; 1127 for (const auto& doc_entry : m_inner) { 1128 doc_keys.insert(doc_entry.m_key_name); 1129 } 1130 std::map<std::string, UniValue> result_obj; 1131 result.getObjMap(result_obj); 1132 for (const auto& result_entry : result_obj) { 1133 if (doc_keys.find(result_entry.first) == doc_keys.end()) { 1134 errors.pushKV(result_entry.first, "key returned that was not in doc"); 1135 } 1136 } 1137 1138 for (const auto& doc_entry : m_inner) { 1139 const auto result_it{result_obj.find(doc_entry.m_key_name)}; 1140 if (result_it == result_obj.end()) { 1141 if (!doc_entry.m_optional) { 1142 errors.pushKV(doc_entry.m_key_name, "key missing, despite not being optional in doc"); 1143 } 1144 continue; 1145 } 1146 UniValue match{doc_entry.MatchesType(result_it->second)}; 1147 if (!match.isTrue()) errors.pushKV(doc_entry.m_key_name, match); 1148 } 1149 if (errors.empty()) return true; 1150 return errors; 1151 } 1152 1153 return true; 1154 } 1155 1156 void RPCResult::CheckInnerDoc() const 1157 { 1158 if (m_type == Type::OBJ) { 1159 // May or may not be empty 1160 return; 1161 } 1162 // Everything else must either be empty or not 1163 const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN}; 1164 CHECK_NONFATAL(inner_needed != m_inner.empty()); 1165 } 1166 1167 std::string RPCArg::ToStringObj(const bool oneline) const 1168 { 1169 std::string res; 1170 res += "\""; 1171 res += GetFirstName(); 1172 if (oneline) { 1173 res += "\":"; 1174 } else { 1175 res += "\": "; 1176 } 1177 switch (m_type) { 1178 case Type::STR: 1179 return res + "\"str\""; 1180 case Type::STR_HEX: 1181 return res + "\"hex\""; 1182 case Type::NUM: 1183 return res + "n"; 1184 case Type::RANGE: 1185 return res + "n or [n,n]"; 1186 case Type::AMOUNT: 1187 return res + "amount"; 1188 case Type::BOOL: 1189 return res + "bool"; 1190 case Type::ARR: 1191 res += "["; 1192 for (const auto& i : m_inner) { 1193 res += i.ToString(oneline) + ","; 1194 } 1195 return res + "...]"; 1196 case Type::OBJ: 1197 case Type::OBJ_NAMED_PARAMS: 1198 case Type::OBJ_USER_KEYS: 1199 // Currently unused, so avoid writing dead code 1200 NONFATAL_UNREACHABLE(); 1201 } // no default case, so the compiler can warn about missing cases 1202 NONFATAL_UNREACHABLE(); 1203 } 1204 1205 std::string RPCArg::ToString(const bool oneline) const 1206 { 1207 if (oneline && !m_opts.oneline_description.empty()) { 1208 if (m_opts.oneline_description[0] == '\"' && m_type != Type::STR_HEX && m_type != Type::STR && gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) { 1209 throw std::runtime_error{ 1210 STR_INTERNAL_BUG(strprintf("non-string RPC arg \"%s\" quotes oneline_description:\n%s", 1211 m_names, m_opts.oneline_description) 1212 )}; 1213 } 1214 return m_opts.oneline_description; 1215 } 1216 1217 switch (m_type) { 1218 case Type::STR_HEX: 1219 case Type::STR: { 1220 return "\"" + GetFirstName() + "\""; 1221 } 1222 case Type::NUM: 1223 case Type::RANGE: 1224 case Type::AMOUNT: 1225 case Type::BOOL: { 1226 return GetFirstName(); 1227 } 1228 case Type::OBJ: 1229 case Type::OBJ_NAMED_PARAMS: 1230 case Type::OBJ_USER_KEYS: { 1231 const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); }); 1232 if (m_type == Type::OBJ) { 1233 return "{" + res + "}"; 1234 } else { 1235 return "{" + res + ",...}"; 1236 } 1237 } 1238 case Type::ARR: { 1239 std::string res; 1240 for (const auto& i : m_inner) { 1241 res += i.ToString(oneline) + ","; 1242 } 1243 return "[" + res + "...]"; 1244 } 1245 } // no default case, so the compiler can warn about missing cases 1246 NONFATAL_UNREACHABLE(); 1247 } 1248 1249 static std::pair<int64_t, int64_t> ParseRange(const UniValue& value) 1250 { 1251 if (value.isNum()) { 1252 return {0, value.getInt<int64_t>()}; 1253 } 1254 if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) { 1255 int64_t low = value[0].getInt<int64_t>(); 1256 int64_t high = value[1].getInt<int64_t>(); 1257 if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end"); 1258 return {low, high}; 1259 } 1260 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]"); 1261 } 1262 1263 std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value) 1264 { 1265 int64_t low, high; 1266 std::tie(low, high) = ParseRange(value); 1267 if (low < 0) { 1268 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0"); 1269 } 1270 if ((high >> 31) != 0) { 1271 throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high"); 1272 } 1273 if (high >= low + 1000000) { 1274 throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large"); 1275 } 1276 return {low, high}; 1277 } 1278 1279 std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider, const bool expand_priv) 1280 { 1281 std::string desc_str; 1282 std::pair<int64_t, int64_t> range = {0, 1000}; 1283 if (scanobject.isStr()) { 1284 desc_str = scanobject.get_str(); 1285 } else if (scanobject.isObject()) { 1286 const UniValue& desc_uni{scanobject.find_value("desc")}; 1287 if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object"); 1288 desc_str = desc_uni.get_str(); 1289 const UniValue& range_uni{scanobject.find_value("range")}; 1290 if (!range_uni.isNull()) { 1291 range = ParseDescriptorRange(range_uni); 1292 } 1293 } else { 1294 throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object"); 1295 } 1296 1297 std::string error; 1298 auto desc = Parse(desc_str, provider, error); 1299 if (!desc) { 1300 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); 1301 } 1302 if (!desc->IsRange()) { 1303 range.first = 0; 1304 range.second = 0; 1305 } 1306 std::vector<CScript> ret; 1307 for (int i = range.first; i <= range.second; ++i) { 1308 std::vector<CScript> scripts; 1309 if (!desc->Expand(i, provider, scripts, provider)) { 1310 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str)); 1311 } 1312 if (expand_priv) { 1313 desc->ExpandPrivate(/*pos=*/i, provider, /*out=*/provider); 1314 } 1315 std::move(scripts.begin(), scripts.end(), std::back_inserter(ret)); 1316 } 1317 return ret; 1318 } 1319 1320 /** Convert a vector of bilingual strings to a UniValue::VARR containing their original untranslated values. */ 1321 [[nodiscard]] static UniValue BilingualStringsToUniValue(const std::vector<bilingual_str>& bilingual_strings) 1322 { 1323 CHECK_NONFATAL(!bilingual_strings.empty()); 1324 UniValue result{UniValue::VARR}; 1325 for (const auto& s : bilingual_strings) { 1326 result.push_back(s.original); 1327 } 1328 return result; 1329 } 1330 1331 void PushWarnings(const UniValue& warnings, UniValue& obj) 1332 { 1333 if (warnings.empty()) return; 1334 obj.pushKV("warnings", warnings); 1335 } 1336 1337 void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj) 1338 { 1339 if (warnings.empty()) return; 1340 obj.pushKV("warnings", BilingualStringsToUniValue(warnings)); 1341 }