LCOV - code coverage report
Current view: top level - src/rpc - util.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 573 605 94.7 %
Date: 2026-06-25 07:23:43 Functions: 71 75 94.7 %

          Line data    Source code
       1             : // Copyright (c) 2017-2020 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             : #include <chainparamsbase.h>
       6             : #include <consensus/amount.h>
       7             : #include <key_io.h>
       8             : #include <outputtype.h>
       9             : #include <pubkey.h>
      10             : #include <rpc/util.h>
      11             : #include <script/descriptor.h>
      12             : #include <script/signingprovider.h>
      13             : #include <tinyformat.h>
      14             : #include <util/check.h>
      15             : #include <util/std23.h>
      16             : #include <util/strencodings.h>
      17             : #include <util/string.h>
      18             : #include <util/system.h>
      19             : #include <util/translation.h>
      20             : 
      21             : const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
      22        3516 : const std::string EXAMPLE_ADDRESS[2] = {"XunLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPw0", "XwQQkwA4FYkq2XERzMY2CiAZhJTEDAbtc0"};
      23             : 
      24        6180 : std::string GetAllOutputTypes()
      25             : {
      26        6180 :     std::vector<std::string> ret;
      27             :     using U = std::underlying_type<TxoutType>::type;
      28       43260 :     for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::NULL_DATA; ++i) {
      29       37080 :         ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
      30       37080 :     }
      31        6180 :     return Join(ret, ", ");
      32        6180 : }
      33             : 
      34      112798 : void RPCTypeCheck(const UniValue& params,
      35             :                   const std::list<UniValueType>& typesExpected,
      36             :                   bool fAllowNull)
      37             : {
      38      112798 :     unsigned int i = 0;
      39      243959 :     for (const UniValueType& t : typesExpected) {
      40      150939 :         if (params.size() <= i)
      41       19778 :             break;
      42             : 
      43      131161 :         const UniValue& v = params[i];
      44      131161 :         if (!(fAllowNull && v.isNull())) {
      45      129451 :             RPCTypeCheckArgument(v, t);
      46      129451 :         }
      47      131161 :         i++;
      48             :     }
      49      112798 : }
      50             : 
      51      129451 : void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
      52             : {
      53      129451 :     if (!typeExpected.typeAny && value.type() != typeExpected.type) {
      54          42 :         throw JSONRPCError(RPC_TYPE_ERROR,
      55          21 :                            strprintf("JSON value of type %s is not of expected type %s", uvTypeName(value.type()), uvTypeName(typeExpected.type)));
      56             :     }
      57      129451 : }
      58             : 
      59        3031 : void RPCTypeCheckObj(const UniValue& o,
      60             :     const std::map<std::string, UniValueType>& typesExpected,
      61             :     bool fAllowNull,
      62             :     bool fStrict)
      63             : {
      64       47893 :     for (const auto& t : typesExpected) {
      65       45016 :         const UniValue& v = o.find_value(t.first);
      66       45016 :         if (!fAllowNull && v.isNull())
      67         178 :             throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
      68             : 
      69       44992 :         if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull())))
      70         130 :             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)));
      71             :     }
      72             : 
      73        2877 :     if (fStrict)
      74             :     {
      75        6585 :         for (const std::string& k : o.getKeys())
      76             :         {
      77        4651 :             if (typesExpected.count(k) == 0)
      78             :             {
      79          24 :                 std::string err = strprintf("Unexpected key %s", k);
      80          24 :                 throw JSONRPCError(RPC_TYPE_ERROR, err);
      81          24 :             }
      82             :         }
      83        1934 :     }
      84        3031 : }
      85             : 
      86       48539 : CAmount AmountFromValue(const UniValue& value, int decimals)
      87             : {
      88       48539 :     if (!value.isNum() && !value.isStr())
      89         626 :         throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
      90             :     CAmount amount;
      91       48479 :     if (!ParseFixedPoint(value.getValStr(), decimals, &amount))
      92         514 :         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
      93       47965 :     if (!MoneyRange(amount))
      94          52 :         throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
      95       47913 :     return amount;
      96         626 : }
      97             : 
      98       73286 : uint256 ParseHashV(const UniValue& v, std::string strName)
      99             : {
     100       73286 :     const std::string& strHex(v.get_str());
     101       73286 :     if (64 != strHex.length())
     102          77 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
     103       73245 :     if (!IsHex(strHex)) // Note: IsHex("") is false
     104          36 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
     105       73209 :     return uint256S(strHex);
     106          77 : }
     107       17105 : uint256 ParseHashO(const UniValue& o, std::string strKey)
     108             : {
     109       17105 :     return ParseHashV(o.find_value(strKey), strKey);
     110          18 : }
     111         281 : std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
     112             : {
     113         281 :     std::string strHex;
     114         281 :     if (v.isStr())
     115         281 :         strHex = v.get_str();
     116         281 :     if (!IsHex(strHex))
     117           8 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
     118         273 :     return ParseHex(strHex);
     119         289 : }
     120          80 : std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
     121             : {
     122          80 :     return ParseHexV(o.find_value(strKey), strKey);
     123           0 : }
     124             : 
     125        1379 : bool ParseBoolV(const UniValue& v, const std::string &strName)
     126             : {
     127        1399 :     if (std23::ranges::contains(gArgs.GetArgs("-deprecatedrpc"), "permissive_bool")) {
     128          24 :         std::string strBool;
     129          24 :         if (v.isBool())
     130           4 :             return v.get_bool();
     131          20 :         else if (v.isNum())
     132           6 :             strBool = ToString(v.getInt<int>());
     133          14 :         else if (v.isStr())
     134          14 :             strBool = v.get_str();
     135             : 
     136          20 :         strBool = ToLower(strBool);
     137             : 
     138          20 :         if (strBool == "true" || strBool == "yes" || strBool == "1") {
     139           8 :             return true;
     140          12 :         } else if (strBool == "false" || strBool == "no" || strBool == "0") {
     141           8 :             return false;
     142             :         }
     143           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be true, false, yes, no, 1 or 0 (not '%s')", strName, strBool));
     144          24 :     }
     145             : 
     146        1355 :     if (!v.isBool()) {
     147          32 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be a JSON boolean. " /* Continued */
     148          16 :                            "Pass -deprecatedrpc=permissive_bool to allow legacy boolean parsing.", strName));
     149             :     }
     150        1339 :     return v.get_bool();
     151        1379 : }
     152             : 
     153             : namespace {
     154             : 
     155             : /**
     156             :  * Quote an argument for shell.
     157             :  *
     158             :  * @note This is intended for help, not for security-sensitive purposes.
     159             :  */
     160        2947 : std::string ShellQuote(const std::string& s)
     161             : {
     162        2947 :     std::string result;
     163        2947 :     result.reserve(s.size() * 2);
     164       88321 :     for (const char ch: s) {
     165       85374 :         if (ch == '\'') {
     166           1 :             result += "'\''";
     167           1 :         } else {
     168       85373 :             result += ch;
     169             :         }
     170             :     }
     171        2947 :     return "'" + result + "'";
     172        2947 : }
     173             : 
     174             : /**
     175             :  * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing.
     176             :  *
     177             :  * @note This is intended for help, not for security-sensitive purposes.
     178             :  */
     179       25212 : std::string ShellQuoteIfNeeded(const std::string& s)
     180             : {
     181      242959 :     for (const char ch: s) {
     182      220694 :         if (ch == ' ' || ch == '\'' || ch == '"') {
     183        2947 :             return ShellQuote(s);
     184             :         }
     185             :     }
     186             : 
     187       22265 :     return s;
     188       25212 : }
     189             : 
     190             : }
     191             : 
     192     1894493 : std::string HelpExampleCli(const std::string& methodname, const std::string& args)
     193             : {
     194     1894493 :     return "> dash-cli " + methodname + " " + args + "\n";
     195           0 : }
     196             : 
     197        7044 : std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
     198             : {
     199        7044 :     std::string result = "> dash-cli -named " + methodname;
     200       32256 :     for (const auto& argpair: args) {
     201       50424 :         const auto& value = argpair.second.isStr()
     202        9983 :                 ? argpair.second.get_str()
     203       15229 :                 : argpair.second.write();
     204       25212 :         result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value);
     205       25212 :     }
     206        7044 :     result += "\n";
     207        7044 :     return result;
     208        7044 : }
     209             : 
     210     1267306 : std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
     211             : {
     212     1267306 :     return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
     213     1267306 :         "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: application/json;'"
     214             :         " http://127.0.0.1:9998/\n";
     215           0 : }
     216             : 
     217        7041 : std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args)
     218             : {
     219        7041 :     UniValue params(UniValue::VOBJ);
     220       32250 :     for (const auto& param: args) {
     221       25209 :         params.pushKV(param.first, param.second);
     222             :     }
     223             : 
     224        7041 :     return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
     225        7041 :            "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: application/json;' http://127.0.0.1:8332/\n";
     226        7041 : }
     227             : 
     228             : // Converts a hex string to a public key if possible
     229         271 : CPubKey HexToPubKey(const std::string& hex_in)
     230             : {
     231         271 :     if (!IsHex(hex_in)) {
     232           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
     233             :     }
     234         271 :     CPubKey vchPubKey(ParseHex(hex_in));
     235         271 :     if (!vchPubKey.IsFullyValid()) {
     236           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
     237             :     }
     238         271 :     return vchPubKey;
     239           0 : }
     240             : 
     241             : // Retrieves a public key for an address from the given FillableSigningProvider
     242         171 : CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in)
     243             : {
     244         171 :     CTxDestination dest = DecodeDestination(addr_in);
     245         171 :     if (!IsValidDestination(dest)) {
     246           4 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + addr_in);
     247             :     }
     248         171 :     const PKHash *pkhash = std::get_if<PKHash>(&dest);
     249         171 :     if (!pkhash) {
     250           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' does not refer to a key", addr_in));
     251             :     }
     252         171 :     CPubKey vchPubKey;
     253         171 :     if (!keystore.GetPubKey(ToKeyID(*pkhash), vchPubKey)) {
     254           4 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("no full public key for address %s", addr_in));
     255             :     }
     256         167 :     if (!vchPubKey.IsFullyValid()) {
     257           0 :        throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet contains an invalid public key");
     258             :     }
     259         167 :     return vchPubKey;
     260           4 : }
     261             : 
     262             : // Creates a multisig address from a given list of public keys, number of signatures required
     263         114 : CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, FillableSigningProvider& keystore, CScript& script_out)
     264             : {
     265             :     // Gather public keys
     266         114 :     if (required < 1) {
     267           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "a multisignature address must require at least one key to redeem");
     268             :     }
     269         114 :     if ((int)pubkeys.size() < required) {
     270           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("not enough keys supplied (got %u keys, but need at least %d to redeem)", pubkeys.size(), required));
     271             :     }
     272         114 :     if (pubkeys.size() > MAX_PUBKEYS_PER_MULTISIG) {
     273           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of keys involved in the multisignature address creation > %d\nReduce the number", MAX_PUBKEYS_PER_MULTISIG));
     274             :     }
     275             : 
     276         114 :     script_out = GetScriptForMultisig(required, pubkeys);
     277             : 
     278         114 :     if (script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
     279           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
     280             :     }
     281             : 
     282         114 :     return AddAndGetDestinationForScript(keystore, script_out, OutputType::LEGACY);
     283           0 : }
     284             : 
     285             : class DescribeAddressVisitor
     286             : {
     287             : public:
     288             :     explicit DescribeAddressVisitor() = default;
     289             : 
     290           0 :     UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
     291             : 
     292        1868 :     UniValue operator()(const PKHash &pkhash) const {
     293        1868 :         UniValue obj(UniValue::VOBJ);
     294        1868 :         obj.pushKV("isscript", false);
     295        1868 :         return obj;
     296        1868 :     }
     297             : 
     298         211 :     UniValue operator()(const ScriptHash &scriptID) const {
     299         211 :         UniValue obj(UniValue::VOBJ);
     300         211 :         obj.pushKV("isscript", true);
     301         211 :         return obj;
     302         211 :     }
     303             : };
     304             : 
     305        2079 : UniValue DescribeAddress(const CTxDestination& dest)
     306             : {
     307        2079 :     return std::visit(DescribeAddressVisitor(), dest);
     308             : }
     309             : 
     310         890 : unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
     311             : {
     312         890 :     const int target{value.getInt<int>()};
     313         890 :     const unsigned int unsigned_target{static_cast<unsigned int>(target)};
     314         890 :     if (target < 1 || unsigned_target > max_target) {
     315         197 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target));
     316             :     }
     317         693 :     return unsigned_target;
     318         197 : }
     319             : 
     320             : /**
     321             :  * A pair of strings that can be aligned (through padding) with other Sections
     322             :  * later on
     323             :  */
     324       93512 : struct Section {
     325       89036 :     Section(const std::string& left, const std::string& right)
     326       89036 :         : m_left{left}, m_right{right} {}
     327             :     std::string m_left;
     328             :     const std::string m_right;
     329             : };
     330             : 
     331             : /**
     332             :  * Keeps track of RPCArgs by transforming them into sections for the purpose
     333             :  * of serializing everything to a single string
     334             :  */
     335        6169 : struct Sections {
     336             :     std::vector<Section> m_sections;
     337        6169 :     size_t m_max_pad{0};
     338             : 
     339       39282 :     void PushSection(const Section& s)
     340             :     {
     341       39282 :         m_max_pad = std::max(m_max_pad, s.m_left.size());
     342       39282 :         m_sections.push_back(s);
     343       39282 :     }
     344             : 
     345             :     /**
     346             :      * Recursive helper to translate an RPCArg into sections
     347             :      */
     348        8376 :     void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
     349             :     {
     350        8376 :         const auto indent = std::string(current_indent, ' ');
     351        8376 :         const auto indent_next = std::string(current_indent + 2, ' ');
     352        8376 :         const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
     353        8376 :         const bool is_top_level_arg{outer_type == OuterType::NONE}; // True on the first recursion
     354             : 
     355        8376 :         switch (arg.m_type) {
     356             :         case RPCArg::Type::STR_HEX:
     357             :         case RPCArg::Type::STR:
     358             :         case RPCArg::Type::NUM:
     359             :         case RPCArg::Type::AMOUNT:
     360             :         case RPCArg::Type::RANGE:
     361             :         case RPCArg::Type::BOOL: {
     362        6871 :             if (is_top_level_arg) return; // Nothing more to do for non-recursive types on first recursion
     363        2405 :             auto left = indent;
     364        2405 :             if (arg.m_type_str.size() != 0 && push_name) {
     365          42 :                 left += "\"" + arg.GetName() + "\": " + arg.m_type_str.at(0);
     366          42 :             } else {
     367        2363 :                 left += push_name ? arg.ToStringObj(/*oneline=*/false) : arg.ToString(/*oneline=*/false);
     368             :             }
     369        2405 :             left += ",";
     370        2405 :             PushSection({left, arg.ToDescriptionString(/*is_named_arg=*/push_name)});
     371             :             break;
     372        2405 :         }
     373             :         case RPCArg::Type::OBJ:
     374             :         case RPCArg::Type::OBJ_USER_KEYS: {
     375         567 :             const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name);
     376         567 :             PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right});
     377        2592 :             for (const auto& arg_inner : arg.m_inner) {
     378        2025 :                 Push(arg_inner, current_indent + 2, OuterType::OBJ);
     379             :             }
     380         567 :             if (arg.m_type != RPCArg::Type::OBJ) {
     381         109 :                 PushSection({indent_next + "...", ""});
     382         109 :             }
     383         567 :             PushSection({indent + "}" + (is_top_level_arg ? "" : ","), ""});
     384             :             break;
     385         567 :         }
     386             :         case RPCArg::Type::ARR: {
     387         938 :             auto left = indent;
     388         938 :             left += push_name ? "\"" + arg.GetName() + "\": " : "";
     389         938 :             left += "[";
     390         938 :             const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name);
     391         938 :             PushSection({left, right});
     392        2053 :             for (const auto& arg_inner : arg.m_inner) {
     393        1115 :                 Push(arg_inner, current_indent + 2, OuterType::ARR);
     394             :             }
     395         938 :             PushSection({indent_next + "...", ""});
     396         938 :             PushSection({indent + "]" + (is_top_level_arg ? "" : ","), ""});
     397             :             break;
     398         938 :         }
     399             :         } // no default case, so the compiler can warn about missing cases
     400        8376 :     }
     401             : 
     402             :     /**
     403             :      * Concatenate all sections with proper padding
     404             :      */
     405        6169 :     std::string ToString() const
     406             :     {
     407        6169 :         std::string ret;
     408        6169 :         const size_t pad = m_max_pad + 4;
     409       50687 :         for (const auto& s : m_sections) {
     410             :             // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
     411             :             // brace like {, }, [, or ]
     412       44518 :             CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
     413       44518 :             if (s.m_right.empty()) {
     414       11154 :                 ret += s.m_left;
     415       11154 :                 ret += "\n";
     416       11154 :                 continue;
     417             :             }
     418             : 
     419       33364 :             std::string left = s.m_left;
     420       33364 :             left.resize(pad, ' ');
     421       33364 :             ret += left;
     422             : 
     423             :             // Properly pad after newlines
     424       33364 :             std::string right;
     425       33364 :             size_t begin = 0;
     426       33364 :             size_t new_line_pos = s.m_right.find_first_of('\n');
     427       36798 :             while (true) {
     428       36798 :                 right += s.m_right.substr(begin, new_line_pos - begin);
     429       36798 :                 if (new_line_pos == std::string::npos) {
     430       33123 :                     break; //No new line
     431             :                 }
     432        3675 :                 right += "\n" + std::string(pad, ' ');
     433        3675 :                 begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);
     434        3675 :                 if (begin == std::string::npos) {
     435         241 :                     break; // Empty line
     436             :                 }
     437        3434 :                 new_line_pos = s.m_right.find_first_of('\n', begin + 1);
     438             :             }
     439       33364 :             ret += right;
     440       33364 :             ret += "\n";
     441       33364 :         }
     442        6169 :         return ret;
     443        6169 :     }
     444             : };
     445             : 
     446           0 : RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
     447           0 :     : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
     448             : 
     449     3726486 : RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
     450     1863243 :     : m_name{std::move(name)},
     451     1863243 :       m_fun{std::move(fun)},
     452     1863243 :       m_description{std::move(description)},
     453     1863243 :       m_args{std::move(args)},
     454     1863243 :       m_results{std::move(results)},
     455     1863243 :       m_examples{std::move(examples)}
     456     1863243 : {
     457             :     std::set<std::string> named_args;
     458             :     for (const auto& arg : m_args) {
     459             :         std::vector<std::string> names = SplitString(arg.m_names, '|');
     460             :         // Should have unique named arguments
     461             :         for (const std::string& name : names) {
     462             :             CHECK_NONFATAL(named_args.insert(name).second);
     463             :         }
     464             :         // Default value type should match argument type only when defined
     465             :         if (arg.m_fallback.index() == 2) {
     466             :             const RPCArg::Type type = arg.m_type;
     467             :             switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) {
     468             :             case UniValue::VOBJ:
     469             :                 CHECK_NONFATAL(type == RPCArg::Type::OBJ);
     470             :                 break;
     471             :             case UniValue::VARR:
     472             :                 CHECK_NONFATAL(type == RPCArg::Type::ARR);
     473             :                 break;
     474             :             case UniValue::VSTR:
     475             :                 CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT);
     476             :                 break;
     477             :             case UniValue::VNUM:
     478             :                 CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE);
     479             :                 break;
     480             :             case UniValue::VBOOL:
     481             :                 CHECK_NONFATAL(type == RPCArg::Type::BOOL);
     482             :                 break;
     483             :             case UniValue::VNULL:
     484             :                 // Null values are accepted in all arguments
     485             :                 break;
     486             :             default:
     487             :                 NONFATAL_UNREACHABLE();
     488             :                 break;
     489             :             }
     490             :         }
     491             :     }
     492     1863243 : }
     493             : 
     494        2885 : std::string RPCResults::ToDescriptionString() const
     495             : {
     496        2885 :     std::string result;
     497        6187 :     for (const auto& r : m_results) {
     498        3302 :         if (r.m_type == RPCResult::Type::ANY) continue; // for testing only
     499        3284 :         if (r.m_cond.empty()) {
     500        2594 :             result += "\nResult:\n";
     501        2594 :         } else {
     502         690 :             result += "\nResult (" + r.m_cond + "):\n";
     503             :         }
     504        3284 :         Sections sections;
     505        3284 :         r.ToSections(sections);
     506        3284 :         result += sections.ToString();
     507        3284 :     }
     508        2885 :     return result;
     509        2885 : }
     510             : 
     511        2885 : std::string RPCExamples::ToDescriptionString() const
     512             : {
     513        2885 :     return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
     514             : }
     515             : 
     516      501489 : UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
     517             : {
     518      501489 :     if (request.mode == JSONRPCRequest::GET_ARGS) {
     519         416 :         return GetArgMap();
     520             :     }
     521             :     /*
     522             :      * Check if the given request is valid according to this command or if
     523             :      * the user is asking for help information, and throw help when appropriate.
     524             :      */
     525      501073 :     if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
     526        2879 :         throw std::runtime_error(ToString());
     527             :     }
     528      498194 :     CHECK_NONFATAL(m_req == nullptr);
     529      498194 :     m_req = &request;
     530      498194 :     UniValue ret = m_fun(*this, request);
     531      498194 :     m_req = nullptr;
     532      498194 :     if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
     533     1014540 :         CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [&ret](const RPCResult& res) { return res.MatchesType(ret); }));
     534      491954 :     }
     535      498194 :     return ret;
     536      999683 : }
     537             : 
     538             : using CheckFn = void(const RPCArg&);
     539        6383 : static const UniValue* DetailMaybeArg(CheckFn* check, const std::vector<RPCArg>& params, const JSONRPCRequest* req, size_t i)
     540             : {
     541        6383 :     CHECK_NONFATAL(i < params.size());
     542        6383 :     const UniValue& arg{CHECK_NONFATAL(req)->params[i]};
     543        6383 :     const RPCArg& param{params.at(i)};
     544        6383 :     if (check) check(param);
     545             : 
     546        6383 :     if (!arg.isNull()) return &arg;
     547        3666 :     if (!std::holds_alternative<RPCArg::Default>(param.m_fallback)) return nullptr;
     548        1380 :     return &std::get<RPCArg::Default>(param.m_fallback);
     549        6383 : }
     550             : 
     551        4064 : static void CheckRequiredOrDefault(const RPCArg& param)
     552             : {
     553             :     // Must use `Arg<Type>(i)` to get the argument or its default value.
     554        4064 :     const bool required{
     555        4064 :         std::holds_alternative<RPCArg::Optional>(param.m_fallback) && RPCArg::Optional::NO == std::get<RPCArg::Optional>(param.m_fallback),
     556             :     };
     557        4064 :     CHECK_NONFATAL(required || std::holds_alternative<RPCArg::Default>(param.m_fallback));
     558        4064 : }
     559             : 
     560             : #define TMPL_INST(check_param, ret_type, return_code)       \
     561             :     template <>                                             \
     562             :     ret_type RPCHelpMan::ArgValue<ret_type>(size_t i) const \
     563             :     {                                                       \
     564             :         const UniValue* maybe_arg{                          \
     565             :             DetailMaybeArg(check_param, m_args, m_req, i),  \
     566             :         };                                                  \
     567             :         return return_code                                  \
     568             :     }                                                       \
     569             :     void force_semicolon(ret_type)
     570             : 
     571             : // Optional arg (without default). Can also be called on required args, if needed.
     572           0 : TMPL_INST(nullptr, std::optional<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;);
     573         364 : TMPL_INST(nullptr, std::optional<bool>, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;);
     574        1955 : TMPL_INST(nullptr, const std::string*, maybe_arg ? &maybe_arg->get_str() : nullptr;);
     575             : 
     576             : // Required arg or optional arg with default value.
     577        1392 : TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>(););
     578        1336 : TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>(););
     579        1336 : TMPL_INST(CheckRequiredOrDefault, const std::string&, CHECK_NONFATAL(maybe_arg)->get_str(););
     580             : 
     581      503111 : bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
     582             : {
     583      503111 :     size_t num_required_args = 0;
     584      870886 :     for (size_t n = m_args.size(); n > 0; --n) {
     585      649507 :         if (!m_args.at(n - 1).IsOptional()) {
     586      281732 :             num_required_args = n;
     587      281732 :             break;
     588             :         }
     589      367775 :     }
     590      503111 :     return num_required_args <= num_args && num_args <= m_args.size();
     591             : }
     592             : 
     593      678456 : std::vector<std::string> RPCHelpMan::GetArgNames() const
     594             : {
     595      678456 :     std::vector<std::string> ret;
     596     2084428 :     for (const auto& arg : m_args) {
     597     1405972 :         ret.emplace_back(arg.m_names);
     598             :     }
     599      678456 :     return ret;
     600      678456 : }
     601             : 
     602        2885 : std::string RPCHelpMan::ToString() const
     603             : {
     604        2885 :     std::string ret;
     605             : 
     606             :     // Oneline summary
     607        2885 :     ret += m_name;
     608        2885 :     bool was_optional{false};
     609        8121 :     for (const auto& arg : m_args) {
     610        5252 :         if (arg.m_hidden) break; // Any arg that follows is also hidden
     611        5236 :         const bool optional = arg.IsOptional();
     612        5236 :         ret += " ";
     613        5236 :         if (optional) {
     614        2890 :             if (!was_optional) ret += "( ";
     615        2890 :             was_optional = true;
     616        2890 :         } else {
     617        2346 :             if (was_optional) ret += ") ";
     618        2346 :             was_optional = false;
     619             :         }
     620        5236 :         ret += arg.ToString(/*oneline=*/true);
     621             :     }
     622        2885 :     if (was_optional) ret += " )";
     623             : 
     624             :     // Description
     625        2885 :     ret += "\n\n" + TrimString(m_description) + "\n";
     626             : 
     627             :     // Arguments
     628        2885 :     Sections sections;
     629        8121 :     for (size_t i{0}; i < m_args.size(); ++i) {
     630        5252 :         const auto& arg = m_args.at(i);
     631        5252 :         if (arg.m_hidden) break; // Any arg that follows is also hidden
     632             : 
     633        5236 :         if (i == 0) ret += "\nArguments:\n";
     634             : 
     635             :         // Push named argument name and description
     636        5236 :         sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString(/*is_named_arg=*/true));
     637        5236 :         sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
     638             : 
     639             :         // Recursively push nested args
     640        5236 :         sections.Push(arg);
     641        5236 :     }
     642        2885 :     ret += sections.ToString();
     643             : 
     644             :     // Result
     645        2885 :     ret += m_results.ToDescriptionString();
     646             : 
     647             :     // Examples
     648        2885 :     ret += m_examples.ToDescriptionString();
     649             : 
     650        2885 :     return ret;
     651        2885 : }
     652             : 
     653         416 : UniValue RPCHelpMan::GetArgMap() const
     654             : {
     655         416 :     UniValue arr{UniValue::VARR};
     656        1204 :     for (int i{0}; i < int(m_args.size()); ++i) {
     657         788 :         const auto& arg = m_args.at(i);
     658         788 :         std::vector<std::string> arg_names = SplitString(arg.m_names, '|');
     659        1580 :         for (const auto& arg_name : arg_names) {
     660         792 :             UniValue map{UniValue::VARR};
     661         792 :             map.push_back(m_name);
     662         792 :             map.push_back(i);
     663         792 :             map.push_back(arg_name);
     664         792 :             map.push_back(arg.m_type == RPCArg::Type::STR ||
     665         514 :                           arg.m_type == RPCArg::Type::STR_HEX);
     666         792 :             arr.push_back(map);
     667         792 :         }
     668         788 :     }
     669         416 :     return arr;
     670         416 : }
     671             : 
     672       13070 : std::string RPCArg::GetFirstName() const
     673             : {
     674       13070 :     return m_names.substr(0, m_names.find("|"));
     675             : }
     676             : 
     677         426 : std::string RPCArg::GetName() const
     678             : {
     679         426 :     CHECK_NONFATAL(std::string::npos == m_names.find("|"));
     680         426 :     return m_names;
     681             : }
     682             : 
     683      654743 : bool RPCArg::IsOptional() const
     684             : {
     685      654743 :     if (m_fallback.index() != 0) {
     686      322640 :         return true;
     687             :     } else {
     688      332103 :         return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback);
     689             :     }
     690      654743 : }
     691             : 
     692        9510 : std::string RPCArg::ToDescriptionString(bool is_named_arg) const
     693             : {
     694        9510 :     std::string ret;
     695        9510 :     ret += "(";
     696        8376 :     if (m_type_str.size() != 0) {
     697          78 :         ret += m_type_str.at(1);
     698          78 :     } else {
     699        8298 :         switch (m_type) {
     700             :         case Type::STR_HEX:
     701             :         case Type::STR: {
     702        4007 :             ret += "string";
     703        3440 :             break;
     704             :         }
     705             :         case Type::NUM: {
     706        1490 :             ret += "numeric";
     707        1490 :             break;
     708             :         }
     709             :         case Type::AMOUNT: {
     710         368 :             ret += "numeric or string";
     711         368 :             break;
     712             :         }
     713             :         case Type::RANGE: {
     714          78 :             ret += "numeric or array";
     715          78 :             break;
     716             :         }
     717             :         case Type::BOOL: {
     718        1417 :             ret += "boolean";
     719        1417 :             break;
     720             :         }
     721             :         case Type::OBJ:
     722             :         case Type::OBJ_USER_KEYS: {
     723           0 :             ret += "json object";
     724         567 :             break;
     725             :         }
     726             :         case Type::ARR: {
     727         938 :             ret += "json array";
     728         938 :             break;
     729             :         }
     730             :         } // no default case, so the compiler can warn about missing cases
     731             :     }
     732        8376 :     if (m_fallback.index() == 1) {
     733        1046 :         ret += ", optional, default=" + std::get<RPCArg::DefaultHint>(m_fallback);
     734        8376 :     } else if (m_fallback.index() == 2) {
     735        2531 :         ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write();
     736        2531 :     } else {
     737        4799 :         switch (std::get<RPCArg::Optional>(m_fallback)) {
     738             :         case RPCArg::Optional::OMITTED_NAMED_ARG: // Deprecated alias for OMITTED, can be removed
     739             :         case RPCArg::Optional::OMITTED: {
     740        1773 :             if (is_named_arg) ret += ", optional"; // Default value is "null" in dicts. Otherwise,
     741             :             // nothing to do. Element is treated as if not present and has no default value
     742        1773 :             break;
     743             :         }
     744             :         case RPCArg::Optional::NO: {
     745        3026 :             ret += ", required";
     746        3026 :             break;
     747             :         }
     748             :         } // no default case, so the compiler can warn about missing cases
     749             :     }
     750        8376 :     ret += ")";
     751        8376 :     ret += m_description.empty() ? "" : " " + m_description;
     752        8376 :     return ret;
     753       10644 : }
     754             : 
     755       25030 : void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
     756             : {
     757             :     // Indentation
     758       25030 :     const std::string indent(current_indent, ' ');
     759       25030 :     const std::string indent_next(current_indent + 2, ' ');
     760             : 
     761             :     // Elements in a JSON structure (dictionary or array) are separated by a comma
     762       25030 :     const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};
     763             : 
     764             :     // The key name if recursed into an dictionary
     765       25030 :     const std::string maybe_key{
     766       50060 :         outer_type == OuterType::OBJ ?
     767       19653 :             "\"" + this->m_key_name + "\" : " :
     768        5377 :             ""};
     769             : 
     770             :     // Format description with type
     771       49850 :     const auto Description = [&](const std::string& type) {
     772       49640 :         return "(" + type + (this->m_optional ? ", optional" : "") + ")" +
     773       24820 :                (this->m_description.empty() ? "" : " " + this->m_description);
     774           0 :     };
     775             : 
     776       25030 :     switch (m_type) {
     777             :     case Type::ELISION: {
     778             :         // If the inner result is empty, use three dots for elision
     779         210 :         sections.PushSection({indent + "..." + maybe_separator, m_description});
     780         210 :         return;
     781             :     }
     782             :     case Type::ANY: {
     783           0 :         NONFATAL_UNREACHABLE(); // Only for testing
     784             :     }
     785             :     case Type::NONE: {
     786         560 :         sections.PushSection({indent + "null" + maybe_separator, Description("json null")});
     787         560 :         return;
     788             :     }
     789             :     case Type::STR: {
     790        4250 :         sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});
     791        4250 :         return;
     792             :     }
     793             :     case Type::STR_AMOUNT: {
     794        1279 :         sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
     795        1279 :         return;
     796             :     }
     797             :     case Type::STR_HEX: {
     798        3833 :         sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});
     799        3833 :         return;
     800             :     }
     801             :     case Type::NUM: {
     802        6657 :         sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
     803        6657 :         return;
     804             :     }
     805             :     case Type::NUM_TIME: {
     806         730 :         sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});
     807         730 :         return;
     808             :     }
     809             :     case Type::BOOL: {
     810        1946 :         sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});
     811        1946 :         return;
     812             :     }
     813             :     case Type::ARR_FIXED:
     814             :     case Type::ARR: {
     815        1955 :         sections.PushSection({indent + maybe_key + "[", Description("json array")});
     816        4048 :         for (const auto& i : m_inner) {
     817        2093 :             i.ToSections(sections, OuterType::ARR, current_indent + 2);
     818             :         }
     819        1955 :         CHECK_NONFATAL(!m_inner.empty());
     820        1955 :         if (m_type == Type::ARR && m_inner.back().m_type != Type::ELISION) {
     821        1877 :             sections.PushSection({indent_next + "...", ""});
     822        1877 :         } else {
     823             :             // Remove final comma, which would be invalid JSON
     824          78 :             sections.m_sections.back().m_left.pop_back();
     825             :         }
     826        1955 :         sections.PushSection({indent + "]" + maybe_separator, ""});
     827        1955 :         return;
     828             :     }
     829             :     case Type::OBJ_DYN:
     830             :     case Type::OBJ: {
     831        3610 :         if (m_inner.empty()) {
     832          16 :             sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")});
     833          16 :             return;
     834             :         }
     835        3594 :         sections.PushSection({indent + maybe_key + "{", Description("json object")});
     836       23247 :         for (const auto& i : m_inner) {
     837       19653 :             i.ToSections(sections, OuterType::OBJ, current_indent + 2);
     838             :         }
     839        3594 :         if (m_type == Type::OBJ_DYN && m_inner.back().m_type != Type::ELISION) {
     840             :             // If the dictionary keys are dynamic, use three dots for continuation
     841         364 :             sections.PushSection({indent_next + "...", ""});
     842         364 :         } else {
     843             :             // Remove final comma, which would be invalid JSON
     844        3230 :             sections.m_sections.back().m_left.pop_back();
     845             :         }
     846        3594 :         sections.PushSection({indent + "}" + maybe_separator, ""});
     847        3594 :         return;
     848             :     }
     849             :     } // no default case, so the compiler can warn about missing cases
     850           0 :     NONFATAL_UNREACHABLE();
     851       25030 : }
     852             : 
     853      522586 : bool RPCResult::MatchesType(const UniValue& result) const
     854             : {
     855      522586 :     switch (m_type) {
     856             :     case Type::ELISION: {
     857           0 :         return false;
     858             :     }
     859             :     case Type::ANY: {
     860          26 :         return true;
     861             :     }
     862             :     case Type::NONE: {
     863      126911 :         return UniValue::VNULL == result.getType();
     864             :     }
     865             :     case Type::STR:
     866             :     case Type::STR_HEX: {
     867      114430 :         return UniValue::VSTR == result.getType();
     868             :     }
     869             :     case Type::NUM:
     870             :     case Type::STR_AMOUNT:
     871             :     case Type::NUM_TIME: {
     872       13918 :         return UniValue::VNUM == result.getType();
     873             :     }
     874             :     case Type::BOOL: {
     875       18621 :         return UniValue::VBOOL == result.getType();
     876             :     }
     877             :     case Type::ARR_FIXED:
     878             :     case Type::ARR: {
     879       68736 :         return UniValue::VARR == result.getType();
     880             :     }
     881             :     case Type::OBJ_DYN:
     882             :     case Type::OBJ: {
     883      179944 :         return UniValue::VOBJ == result.getType();
     884             :     }
     885             :     } // no default case, so the compiler can warn about missing cases
     886           0 :     NONFATAL_UNREACHABLE();
     887      522586 : }
     888             : 
     889    20883110 : void RPCResult::CheckInnerDoc() const
     890             : {
     891    20883110 :     if (m_type == Type::OBJ) {
     892             :         // May or may not be empty
     893     2749208 :         return;
     894             :     }
     895             :     // Everything else must either be empty or not
     896    18133902 :     const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN};
     897    18133902 :     CHECK_NONFATAL(inner_needed != m_inner.empty());
     898    20883110 : }
     899             : 
     900        2238 : std::string RPCArg::ToStringObj(const bool oneline) const
     901             : {
     902        2238 :     std::string res;
     903        2238 :     res += "\"";
     904        2238 :     res += GetFirstName();
     905        2238 :     if (oneline) {
     906         639 :         res += "\":";
     907         639 :     } else {
     908        1599 :         res += "\": ";
     909             :     }
     910        2238 :     switch (m_type) {
     911             :     case Type::STR:
     912         234 :         return res + "\"str\"";
     913             :     case Type::STR_HEX:
     914         540 :         return res + "\"hex\"";
     915             :     case Type::NUM:
     916         616 :         return res + "n";
     917             :     case Type::RANGE:
     918          78 :         return res + "n or [n,n]";
     919             :     case Type::AMOUNT:
     920         376 :         return res + "amount";
     921             :     case Type::BOOL:
     922         378 :         return res + "bool";
     923             :     case Type::ARR:
     924          16 :         res += "[";
     925          32 :         for (const auto& i : m_inner) {
     926          16 :             res += i.ToString(oneline) + ",";
     927             :         }
     928          16 :         return res + "...]";
     929             :     case Type::OBJ:
     930             :     case Type::OBJ_USER_KEYS:
     931             :         // Currently unused, so avoid writing dead code
     932           0 :         NONFATAL_UNREACHABLE();
     933             :     } // no default case, so the compiler can warn about missing cases
     934           0 :     NONFATAL_UNREACHABLE();
     935        2238 : }
     936             : 
     937        6657 : std::string RPCArg::ToString(const bool oneline) const
     938             : {
     939        6657 :     if (oneline && !m_oneline_description.empty()) return m_oneline_description;
     940             : 
     941        6477 :     switch (m_type) {
     942             :     case Type::STR_HEX:
     943             :     case Type::STR: {
     944        3288 :         return "\"" + GetFirstName() + "\"";
     945             :     }
     946             :     case Type::NUM:
     947             :     case Type::RANGE:
     948             :     case Type::AMOUNT:
     949             :     case Type::BOOL: {
     950        2308 :         return GetFirstName();
     951             :     }
     952             :     case Type::OBJ:
     953             :     case Type::OBJ_USER_KEYS: {
     954         976 :         const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
     955         337 :         if (m_type == Type::OBJ) {
     956         228 :             return "{" + res + "}";
     957             :         } else {
     958         109 :             return "{" + res + ",...}";
     959             :         }
     960         337 :     }
     961             :     case Type::ARR: {
     962         544 :         std::string res;
     963        1185 :         for (const auto& i : m_inner) {
     964         641 :             res += i.ToString(oneline) + ",";
     965             :         }
     966         544 :         return "[" + res + "...]";
     967         544 :     }
     968             :     } // no default case, so the compiler can warn about missing cases
     969           0 :     NONFATAL_UNREACHABLE();
     970        6657 : }
     971             : 
     972         227 : static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
     973             : {
     974         227 :     if (value.isNum()) {
     975          72 :         return {0, value.getInt<int64_t>()};
     976             :     }
     977         155 :     if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
     978         155 :         int64_t low = value[0].getInt<int64_t>();
     979         155 :         int64_t high = value[1].getInt<int64_t>();
     980         155 :         if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
     981         145 :         return {low, high};
     982             :     }
     983           0 :     throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
     984         227 : }
     985             : 
     986         217 : std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
     987             : {
     988             :     int64_t low, high;
     989         217 :     std::tie(low, high) = ParseRange(value);
     990         217 :     if (low < 0) {
     991          36 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
     992             :     }
     993         207 :     if ((high >> 31) != 0) {
     994          16 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
     995             :     }
     996         191 :     if (high >= low + 1000000) {
     997          10 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
     998             :     }
     999         181 :     return {low, high};
    1000          36 : }
    1001             : 
    1002        1145 : RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
    1003             : {
    1004        1145 :     switch (terr) {
    1005             :         case TransactionError::MEMPOOL_REJECTED:
    1006        1118 :             return RPC_TRANSACTION_REJECTED;
    1007             :         case TransactionError::ALREADY_IN_CHAIN:
    1008           9 :             return RPC_TRANSACTION_ALREADY_IN_CHAIN;
    1009             :         case TransactionError::P2P_DISABLED:
    1010           0 :             return RPC_CLIENT_P2P_DISABLED;
    1011             :         case TransactionError::INVALID_PSBT:
    1012             :         case TransactionError::PSBT_MISMATCH:
    1013           4 :             return RPC_INVALID_PARAMETER;
    1014             :         case TransactionError::SIGHASH_MISMATCH:
    1015           0 :             return RPC_DESERIALIZATION_ERROR;
    1016          14 :         default: break;
    1017             :     }
    1018          14 :     return RPC_TRANSACTION_ERROR;
    1019        1145 : }
    1020             : 
    1021        1145 : UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
    1022             : {
    1023        1145 :     if (err_string.length() > 0) {
    1024        1124 :         return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
    1025             :     } else {
    1026          21 :         return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original);
    1027             :     }
    1028        1145 : }
    1029             : 
    1030        1636 : std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider)
    1031             : {
    1032        1636 :     std::string desc_str;
    1033        1636 :     std::pair<int64_t, int64_t> range = {0, 1000};
    1034        1636 :     if (scanobject.isStr()) {
    1035        1600 :         desc_str = scanobject.get_str();
    1036        1636 :     } else if (scanobject.isObject()) {
    1037          36 :         const UniValue& desc_uni{scanobject.find_value("desc")};
    1038          36 :         if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
    1039          36 :         desc_str = desc_uni.get_str();
    1040          36 :         const UniValue& range_uni{scanobject.find_value("range")};
    1041          36 :         if (!range_uni.isNull()) {
    1042          36 :             range = ParseDescriptorRange(range_uni);
    1043          26 :         }
    1044          26 :     } else {
    1045           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
    1046             :     }
    1047             : 
    1048        1626 :     std::string error;
    1049        1626 :     auto desc = Parse(desc_str, provider, error);
    1050        1626 :     if (!desc) {
    1051           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
    1052             :     }
    1053        1626 :     if (!desc->IsRange()) {
    1054        1600 :         range.first = 0;
    1055        1600 :         range.second = 0;
    1056        1600 :     }
    1057        1626 :     std::vector<CScript> ret;
    1058       39240 :     for (int i = range.first; i <= range.second; ++i) {
    1059       37614 :         std::vector<CScript> scripts;
    1060       37614 :         if (!desc->Expand(i, provider, scripts, provider)) {
    1061           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
    1062             :         }
    1063       37614 :         std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
    1064       37614 :     }
    1065        1626 :     return ret;
    1066        1636 : }

Generated by: LCOV version 1.16