LCOV - code coverage report
Current view: top level - src/rpc - util.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 773 855 90.4 %
Date: 2022-04-21 14:51:19 Functions: 55 58 94.8 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 456 514 88.7 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2017-2021 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 <consensus/amount.h>
#       6                 :            : #include <key_io.h>
#       7                 :            : #include <outputtype.h>
#       8                 :            : #include <rpc/util.h>
#       9                 :            : #include <script/descriptor.h>
#      10                 :            : #include <script/signingprovider.h>
#      11                 :            : #include <tinyformat.h>
#      12                 :            : #include <util/strencodings.h>
#      13                 :            : #include <util/string.h>
#      14                 :            : #include <util/translation.h>
#      15                 :            : 
#      16                 :            : #include <tuple>
#      17                 :            : 
#      18                 :            : #include <boost/algorithm/string/classification.hpp>
#      19                 :            : #include <boost/algorithm/string/split.hpp>
#      20                 :            : 
#      21                 :            : const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
#      22                 :            : const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
#      23                 :            : 
#      24                 :            : std::string GetAllOutputTypes()
#      25                 :       5703 : {
#      26                 :       5703 :     std::vector<std::string> ret;
#      27                 :       5703 :     using U = std::underlying_type<TxoutType>::type;
#      28         [ +  + ]:      62733 :     for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) {
#      29                 :      57030 :         ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
#      30                 :      57030 :     }
#      31                 :       5703 :     return Join(ret, ", ");
#      32                 :       5703 : }
#      33                 :            : 
#      34                 :            : void RPCTypeCheck(const UniValue& params,
#      35                 :            :                   const std::list<UniValueType>& typesExpected,
#      36                 :            :                   bool fAllowNull)
#      37                 :      24363 : {
#      38                 :      24363 :     unsigned int i = 0;
#      39         [ +  + ]:      51098 :     for (const UniValueType& t : typesExpected) {
#      40         [ +  + ]:      51098 :         if (params.size() <= i)
#      41                 :      12832 :             break;
#      42                 :            : 
#      43                 :      38266 :         const UniValue& v = params[i];
#      44 [ +  + ][ +  + ]:      38266 :         if (!(fAllowNull && v.isNull())) {
#      45                 :      37265 :             RPCTypeCheckArgument(v, t);
#      46                 :      37265 :         }
#      47                 :      38266 :         i++;
#      48                 :      38266 :     }
#      49                 :      24363 : }
#      50                 :            : 
#      51                 :            : void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
#      52                 :      38567 : {
#      53 [ +  + ][ +  + ]:      38567 :     if (!typeExpected.typeAny && value.type() != typeExpected.type) {
#      54                 :         14 :         throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
#      55                 :         14 :     }
#      56                 :      38567 : }
#      57                 :            : 
#      58                 :            : void RPCTypeCheckObj(const UniValue& o,
#      59                 :            :     const std::map<std::string, UniValueType>& typesExpected,
#      60                 :            :     bool fAllowNull,
#      61                 :            :     bool fStrict)
#      62                 :       1667 : {
#      63         [ +  + ]:      20056 :     for (const auto& t : typesExpected) {
#      64                 :      20056 :         const UniValue& v = find_value(o, t.first);
#      65 [ +  + ][ +  + ]:      20056 :         if (!fAllowNull && v.isNull())
#      66                 :         18 :             throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
#      67                 :            : 
#      68 [ +  + ][ +  + ]:      20038 :         if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
#         [ +  - ][ +  + ]
#      69                 :         48 :             std::string err = strprintf("Expected type %s for %s, got %s",
#      70                 :         48 :                 uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
#      71                 :         48 :             throw JSONRPCError(RPC_TYPE_ERROR, err);
#      72                 :         48 :         }
#      73                 :      20038 :     }
#      74                 :            : 
#      75         [ +  + ]:       1601 :     if (fStrict)
#      76                 :        936 :     {
#      77         [ +  + ]:        936 :         for (const std::string& k : o.getKeys())
#      78                 :       2063 :         {
#      79         [ +  + ]:       2063 :             if (typesExpected.count(k) == 0)
#      80                 :         10 :             {
#      81                 :         10 :                 std::string err = strprintf("Unexpected key %s", k);
#      82                 :         10 :                 throw JSONRPCError(RPC_TYPE_ERROR, err);
#      83                 :         10 :             }
#      84                 :       2063 :         }
#      85                 :        936 :     }
#      86                 :       1601 : }
#      87                 :            : 
#      88                 :            : CAmount AmountFromValue(const UniValue& value, int decimals)
#      89                 :      26796 : {
#      90 [ +  + ][ +  + ]:      26796 :     if (!value.isNum() && !value.isStr())
#      91                 :         26 :         throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
#      92                 :      26770 :     CAmount amount;
#      93         [ +  + ]:      26770 :     if (!ParseFixedPoint(value.getValStr(), decimals, &amount))
#      94                 :        224 :         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
#      95         [ +  + ]:      26546 :     if (!MoneyRange(amount))
#      96                 :         23 :         throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
#      97                 :      26523 :     return amount;
#      98                 :      26546 : }
#      99                 :            : 
#     100                 :            : uint256 ParseHashV(const UniValue& v, std::string strName)
#     101                 :      20996 : {
#     102                 :      20996 :     std::string strHex(v.get_str());
#     103         [ +  + ]:      20996 :     if (64 != strHex.length())
#     104                 :         22 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
#     105         [ +  + ]:      20974 :     if (!IsHex(strHex)) // Note: IsHex("") is false
#     106                 :         15 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
#     107                 :      20959 :     return uint256S(strHex);
#     108                 :      20974 : }
#     109                 :            : uint256 ParseHashO(const UniValue& o, std::string strKey)
#     110                 :       7657 : {
#     111                 :       7657 :     return ParseHashV(find_value(o, strKey), strKey);
#     112                 :       7657 : }
#     113                 :            : std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
#     114                 :        595 : {
#     115                 :        595 :     std::string strHex;
#     116         [ +  - ]:        595 :     if (v.isStr())
#     117                 :        595 :         strHex = v.get_str();
#     118         [ +  + ]:        595 :     if (!IsHex(strHex))
#     119                 :          8 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
#     120                 :        587 :     return ParseHex(strHex);
#     121                 :        595 : }
#     122                 :            : std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
#     123                 :        406 : {
#     124                 :        406 :     return ParseHexV(find_value(o, strKey), strKey);
#     125                 :        406 : }
#     126                 :            : 
#     127                 :            : namespace {
#     128                 :            : 
#     129                 :            : /**
#     130                 :            :  * Quote an argument for shell.
#     131                 :            :  *
#     132                 :            :  * @note This is intended for help, not for security-sensitive purposes.
#     133                 :            :  */
#     134                 :            : std::string ShellQuote(const std::string& s)
#     135                 :       1606 : {
#     136                 :       1606 :     std::string result;
#     137                 :       1606 :     result.reserve(s.size() * 2);
#     138         [ +  + ]:      46396 :     for (const char ch: s) {
#     139         [ +  + ]:      46396 :         if (ch == '\'') {
#     140                 :          2 :             result += "'\''";
#     141                 :      46394 :         } else {
#     142                 :      46394 :             result += ch;
#     143                 :      46394 :         }
#     144                 :      46396 :     }
#     145                 :       1606 :     return "'" + result + "'";
#     146                 :       1606 : }
#     147                 :            : 
#     148                 :            : /**
#     149                 :            :  * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing.
#     150                 :            :  *
#     151                 :            :  * @note This is intended for help, not for security-sensitive purposes.
#     152                 :            :  */
#     153                 :            : std::string ShellQuoteIfNeeded(const std::string& s)
#     154                 :      13024 : {
#     155         [ +  + ]:     115922 :     for (const char ch: s) {
#     156 [ +  + ][ +  + ]:     115922 :         if (ch == ' ' || ch == '\'' || ch == '"') {
#                 [ +  + ]
#     157                 :       1606 :             return ShellQuote(s);
#     158                 :       1606 :         }
#     159                 :     115922 :     }
#     160                 :            : 
#     161                 :      11418 :     return s;
#     162                 :      13024 : }
#     163                 :            : 
#     164                 :            : }
#     165                 :            : 
#     166                 :            : std::string HelpExampleCli(const std::string& methodname, const std::string& args)
#     167                 :     576896 : {
#     168                 :     576896 :     return "> bitcoin-cli " + methodname + " " + args + "\n";
#     169                 :     576896 : }
#     170                 :            : 
#     171                 :            : std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
#     172                 :       3666 : {
#     173                 :       3666 :     std::string result = "> bitcoin-cli -named " + methodname;
#     174         [ +  + ]:      13024 :     for (const auto& argpair: args) {
#     175         [ +  + ]:      13024 :         const auto& value = argpair.second.isStr()
#     176                 :      13024 :                 ? argpair.second.get_str()
#     177                 :      13024 :                 : argpair.second.write();
#     178                 :      13024 :         result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value);
#     179                 :      13024 :     }
#     180                 :       3666 :     result += "\n";
#     181                 :       3666 :     return result;
#     182                 :       3666 : }
#     183                 :            : 
#     184                 :            : std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
#     185                 :     349668 : {
#     186                 :     349668 :     return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
#     187                 :     349668 :         "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
#     188                 :     349668 : }
#     189                 :            : 
#     190                 :            : std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args)
#     191                 :       3660 : {
#     192                 :       3660 :     UniValue params(UniValue::VOBJ);
#     193         [ +  + ]:      13018 :     for (const auto& param: args) {
#     194                 :      13018 :         params.pushKV(param.first, param.second);
#     195                 :      13018 :     }
#     196                 :            : 
#     197                 :       3660 :     return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
#     198                 :       3660 :            "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
#     199                 :       3660 : }
#     200                 :            : 
#     201                 :            : // Converts a hex string to a public key if possible
#     202                 :            : CPubKey HexToPubKey(const std::string& hex_in)
#     203                 :        315 : {
#     204         [ -  + ]:        315 :     if (!IsHex(hex_in)) {
#     205                 :          0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
#     206                 :          0 :     }
#     207                 :        315 :     CPubKey vchPubKey(ParseHex(hex_in));
#     208         [ -  + ]:        315 :     if (!vchPubKey.IsFullyValid()) {
#     209                 :          0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
#     210                 :          0 :     }
#     211                 :        315 :     return vchPubKey;
#     212                 :        315 : }
#     213                 :            : 
#     214                 :            : // Retrieves a public key for an address from the given FillableSigningProvider
#     215                 :            : CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in)
#     216                 :        140 : {
#     217                 :        140 :     CTxDestination dest = DecodeDestination(addr_in);
#     218         [ -  + ]:        140 :     if (!IsValidDestination(dest)) {
#     219                 :          0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + addr_in);
#     220                 :          0 :     }
#     221                 :        140 :     CKeyID key = GetKeyForDestination(keystore, dest);
#     222         [ -  + ]:        140 :     if (key.IsNull()) {
#     223                 :          0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' does not refer to a key", addr_in));
#     224                 :          0 :     }
#     225                 :        140 :     CPubKey vchPubKey;
#     226         [ +  + ]:        140 :     if (!keystore.GetPubKey(key, vchPubKey)) {
#     227                 :          2 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("no full public key for address %s", addr_in));
#     228                 :          2 :     }
#     229         [ -  + ]:        138 :     if (!vchPubKey.IsFullyValid()) {
#     230                 :          0 :        throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet contains an invalid public key");
#     231                 :          0 :     }
#     232                 :        138 :     return vchPubKey;
#     233                 :        138 : }
#     234                 :            : 
#     235                 :            : // Creates a multisig address from a given list of public keys, number of signatures required, and the address type
#     236                 :            : CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out)
#     237                 :        162 : {
#     238                 :            :     // Gather public keys
#     239         [ -  + ]:        162 :     if (required < 1) {
#     240                 :          0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "a multisignature address must require at least one key to redeem");
#     241                 :          0 :     }
#     242         [ -  + ]:        162 :     if ((int)pubkeys.size() < required) {
#     243                 :          0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("not enough keys supplied (got %u keys, but need at least %d to redeem)", pubkeys.size(), required));
#     244                 :          0 :     }
#     245         [ -  + ]:        162 :     if (pubkeys.size() > MAX_PUBKEYS_PER_MULTISIG) {
#     246                 :          0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of keys involved in the multisignature address creation > %d\nReduce the number", MAX_PUBKEYS_PER_MULTISIG));
#     247                 :          0 :     }
#     248                 :            : 
#     249                 :        162 :     script_out = GetScriptForMultisig(required, pubkeys);
#     250                 :            : 
#     251                 :            :     // Check if any keys are uncompressed. If so, the type is legacy
#     252         [ +  + ]:        393 :     for (const CPubKey& pk : pubkeys) {
#     253         [ +  + ]:        393 :         if (!pk.IsCompressed()) {
#     254                 :         54 :             type = OutputType::LEGACY;
#     255                 :         54 :             break;
#     256                 :         54 :         }
#     257                 :        393 :     }
#     258                 :            : 
#     259 [ +  + ][ -  + ]:        162 :     if (type == OutputType::LEGACY && script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
#     260                 :          0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
#     261                 :          0 :     }
#     262                 :            : 
#     263                 :            :     // Make the address
#     264                 :        162 :     CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type);
#     265                 :            : 
#     266                 :        162 :     return dest;
#     267                 :        162 : }
#     268                 :            : 
#     269                 :            : class DescribeAddressVisitor
#     270                 :            : {
#     271                 :            : public:
#     272                 :       1867 :     explicit DescribeAddressVisitor() {}
#     273                 :            : 
#     274                 :            :     UniValue operator()(const CNoDestination& dest) const
#     275                 :          0 :     {
#     276                 :          0 :         return UniValue(UniValue::VOBJ);
#     277                 :          0 :     }
#     278                 :            : 
#     279                 :            :     UniValue operator()(const PKHash& keyID) const
#     280                 :        294 :     {
#     281                 :        294 :         UniValue obj(UniValue::VOBJ);
#     282                 :        294 :         obj.pushKV("isscript", false);
#     283                 :        294 :         obj.pushKV("iswitness", false);
#     284                 :        294 :         return obj;
#     285                 :        294 :     }
#     286                 :            : 
#     287                 :            :     UniValue operator()(const ScriptHash& scriptID) const
#     288                 :        452 :     {
#     289                 :        452 :         UniValue obj(UniValue::VOBJ);
#     290                 :        452 :         obj.pushKV("isscript", true);
#     291                 :        452 :         obj.pushKV("iswitness", false);
#     292                 :        452 :         return obj;
#     293                 :        452 :     }
#     294                 :            : 
#     295                 :            :     UniValue operator()(const WitnessV0KeyHash& id) const
#     296                 :        858 :     {
#     297                 :        858 :         UniValue obj(UniValue::VOBJ);
#     298                 :        858 :         obj.pushKV("isscript", false);
#     299                 :        858 :         obj.pushKV("iswitness", true);
#     300                 :        858 :         obj.pushKV("witness_version", 0);
#     301                 :        858 :         obj.pushKV("witness_program", HexStr(id));
#     302                 :        858 :         return obj;
#     303                 :        858 :     }
#     304                 :            : 
#     305                 :            :     UniValue operator()(const WitnessV0ScriptHash& id) const
#     306                 :        160 :     {
#     307                 :        160 :         UniValue obj(UniValue::VOBJ);
#     308                 :        160 :         obj.pushKV("isscript", true);
#     309                 :        160 :         obj.pushKV("iswitness", true);
#     310                 :        160 :         obj.pushKV("witness_version", 0);
#     311                 :        160 :         obj.pushKV("witness_program", HexStr(id));
#     312                 :        160 :         return obj;
#     313                 :        160 :     }
#     314                 :            : 
#     315                 :            :     UniValue operator()(const WitnessV1Taproot& tap) const
#     316                 :        103 :     {
#     317                 :        103 :         UniValue obj(UniValue::VOBJ);
#     318                 :        103 :         obj.pushKV("isscript", true);
#     319                 :        103 :         obj.pushKV("iswitness", true);
#     320                 :        103 :         obj.pushKV("witness_version", 1);
#     321                 :        103 :         obj.pushKV("witness_program", HexStr(tap));
#     322                 :        103 :         return obj;
#     323                 :        103 :     }
#     324                 :            : 
#     325                 :            :     UniValue operator()(const WitnessUnknown& id) const
#     326                 :          0 :     {
#     327                 :          0 :         UniValue obj(UniValue::VOBJ);
#     328                 :          0 :         obj.pushKV("iswitness", true);
#     329                 :          0 :         obj.pushKV("witness_version", (int)id.version);
#     330                 :          0 :         obj.pushKV("witness_program", HexStr({id.program, id.length}));
#     331                 :          0 :         return obj;
#     332                 :          0 :     }
#     333                 :            : };
#     334                 :            : 
#     335                 :            : UniValue DescribeAddress(const CTxDestination& dest)
#     336                 :       1867 : {
#     337                 :       1867 :     return std::visit(DescribeAddressVisitor(), dest);
#     338                 :       1867 : }
#     339                 :            : 
#     340                 :            : unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
#     341                 :        380 : {
#     342                 :        380 :     const int target{value.get_int()};
#     343                 :        380 :     const unsigned int unsigned_target{static_cast<unsigned int>(target)};
#     344 [ +  + ][ +  + ]:        380 :     if (target < 1 || unsigned_target > max_target) {
#     345                 :         66 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target));
#     346                 :         66 :     }
#     347                 :        314 :     return unsigned_target;
#     348                 :        380 : }
#     349                 :            : 
#     350                 :            : RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
#     351                 :       4223 : {
#     352                 :       4223 :     switch (terr) {
#     353         [ +  + ]:       4207 :         case TransactionError::MEMPOOL_REJECTED:
#     354                 :       4207 :             return RPC_TRANSACTION_REJECTED;
#     355         [ +  + ]:          8 :         case TransactionError::ALREADY_IN_CHAIN:
#     356                 :          8 :             return RPC_TRANSACTION_ALREADY_IN_CHAIN;
#     357         [ -  + ]:          0 :         case TransactionError::P2P_DISABLED:
#     358                 :          0 :             return RPC_CLIENT_P2P_DISABLED;
#     359         [ -  + ]:          0 :         case TransactionError::INVALID_PSBT:
#     360         [ -  + ]:          0 :         case TransactionError::PSBT_MISMATCH:
#     361                 :          0 :             return RPC_INVALID_PARAMETER;
#     362         [ -  + ]:          0 :         case TransactionError::SIGHASH_MISMATCH:
#     363                 :          0 :             return RPC_DESERIALIZATION_ERROR;
#     364         [ +  + ]:          8 :         default: break;
#     365                 :       4223 :     }
#     366                 :          8 :     return RPC_TRANSACTION_ERROR;
#     367                 :       4223 : }
#     368                 :            : 
#     369                 :            : UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
#     370                 :       4223 : {
#     371         [ +  + ]:       4223 :     if (err_string.length() > 0) {
#     372                 :       4209 :         return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
#     373                 :       4209 :     } else {
#     374                 :         14 :         return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original);
#     375                 :         14 :     }
#     376                 :       4223 : }
#     377                 :            : 
#     378                 :            : /**
#     379                 :            :  * A pair of strings that can be aligned (through padding) with other Sections
#     380                 :            :  * later on
#     381                 :            :  */
#     382                 :            : struct Section {
#     383                 :            :     Section(const std::string& left, const std::string& right)
#     384                 :      11880 :         : m_left{left}, m_right{right} {}
#     385                 :            :     std::string m_left;
#     386                 :            :     const std::string m_right;
#     387                 :            : };
#     388                 :            : 
#     389                 :            : /**
#     390                 :            :  * Keeps track of RPCArgs by transforming them into sections for the purpose
#     391                 :            :  * of serializing everything to a single string
#     392                 :            :  */
#     393                 :            : struct Sections {
#     394                 :            :     std::vector<Section> m_sections;
#     395                 :            :     size_t m_max_pad{0};
#     396                 :            : 
#     397                 :            :     void PushSection(const Section& s)
#     398                 :      10426 :     {
#     399                 :      10426 :         m_max_pad = std::max(m_max_pad, s.m_left.size());
#     400                 :      10426 :         m_sections.push_back(s);
#     401                 :      10426 :     }
#     402                 :            : 
#     403                 :            :     /**
#     404                 :            :      * Recursive helper to translate an RPCArg into sections
#     405                 :            :      */
#     406                 :            :     void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
#     407                 :       2569 :     {
#     408                 :       2569 :         const auto indent = std::string(current_indent, ' ');
#     409                 :       2569 :         const auto indent_next = std::string(current_indent + 2, ' ');
#     410                 :       2569 :         const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
#     411                 :            : 
#     412         [ -  + ]:       2569 :         switch (arg.m_type) {
#     413         [ +  + ]:        331 :         case RPCArg::Type::STR_HEX:
#     414         [ +  + ]:       1030 :         case RPCArg::Type::STR:
#     415         [ +  + ]:       1463 :         case RPCArg::Type::NUM:
#     416         [ +  + ]:       1608 :         case RPCArg::Type::AMOUNT:
#     417         [ +  + ]:       1633 :         case RPCArg::Type::RANGE:
#     418         [ +  + ]:       2110 :         case RPCArg::Type::BOOL: {
#     419         [ +  + ]:       2110 :             if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion
#     420                 :        866 :             auto left = indent;
#     421 [ +  + ][ +  - ]:        866 :             if (arg.m_type_str.size() != 0 && push_name) {
#     422                 :         15 :                 left += "\"" + arg.GetName() + "\": " + arg.m_type_str.at(0);
#     423                 :        851 :             } else {
#     424         [ +  + ]:        851 :                 left += push_name ? arg.ToStringObj(/*oneline=*/false) : arg.ToString(/*oneline=*/false);
#     425                 :        851 :             }
#     426                 :        866 :             left += ",";
#     427                 :        866 :             PushSection({left, arg.ToDescriptionString()});
#     428                 :        866 :             break;
#     429                 :       2110 :         }
#     430         [ +  + ]:        151 :         case RPCArg::Type::OBJ:
#     431         [ +  + ]:        189 :         case RPCArg::Type::OBJ_USER_KEYS: {
#     432         [ +  + ]:        189 :             const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
#     433         [ +  + ]:        189 :             PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right});
#     434         [ +  + ]:        755 :             for (const auto& arg_inner : arg.m_inner) {
#     435                 :        755 :                 Push(arg_inner, current_indent + 2, OuterType::OBJ);
#     436                 :        755 :             }
#     437         [ +  + ]:        189 :             if (arg.m_type != RPCArg::Type::OBJ) {
#     438                 :         38 :                 PushSection({indent_next + "...", ""});
#     439                 :         38 :             }
#     440         [ +  + ]:        189 :             PushSection({indent + "}" + (outer_type != OuterType::NONE ? "," : ""), ""});
#     441                 :        189 :             break;
#     442                 :        151 :         }
#     443         [ +  + ]:        270 :         case RPCArg::Type::ARR: {
#     444                 :        270 :             auto left = indent;
#     445         [ +  + ]:        270 :             left += push_name ? "\"" + arg.GetName() + "\": " : "";
#     446                 :        270 :             left += "[";
#     447         [ +  + ]:        270 :             const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
#     448                 :        270 :             PushSection({left, right});
#     449         [ +  + ]:        360 :             for (const auto& arg_inner : arg.m_inner) {
#     450                 :        360 :                 Push(arg_inner, current_indent + 2, OuterType::ARR);
#     451                 :        360 :             }
#     452                 :        270 :             PushSection({indent_next + "...", ""});
#     453         [ +  + ]:        270 :             PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
#     454                 :        270 :             break;
#     455                 :        151 :         }
#     456                 :       2569 :         } // no default case, so the compiler can warn about missing cases
#     457                 :       2569 :     }
#     458                 :            : 
#     459                 :            :     /**
#     460                 :            :      * Concatenate all sections with proper padding
#     461                 :            :      */
#     462                 :            :     std::string ToString() const
#     463                 :       1629 :     {
#     464                 :       1629 :         std::string ret;
#     465                 :       1629 :         const size_t pad = m_max_pad + 4;
#     466         [ +  + ]:      11880 :         for (const auto& s : m_sections) {
#     467                 :            :             // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
#     468                 :            :             // brace like {, }, [, or ]
#     469         [ -  + ]:      11880 :             CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
#     470         [ +  + ]:      11880 :             if (s.m_right.empty()) {
#     471                 :       2994 :                 ret += s.m_left;
#     472                 :       2994 :                 ret += "\n";
#     473                 :       2994 :                 continue;
#     474                 :       2994 :             }
#     475                 :            : 
#     476                 :       8886 :             std::string left = s.m_left;
#     477                 :       8886 :             left.resize(pad, ' ');
#     478                 :       8886 :             ret += left;
#     479                 :            : 
#     480                 :            :             // Properly pad after newlines
#     481                 :       8886 :             std::string right;
#     482                 :       8886 :             size_t begin = 0;
#     483                 :       8886 :             size_t new_line_pos = s.m_right.find_first_of('\n');
#     484                 :      10273 :             while (true) {
#     485                 :      10273 :                 right += s.m_right.substr(begin, new_line_pos - begin);
#     486         [ +  + ]:      10273 :                 if (new_line_pos == std::string::npos) {
#     487                 :       8823 :                     break; //No new line
#     488                 :       8823 :                 }
#     489                 :       1450 :                 right += "\n" + std::string(pad, ' ');
#     490                 :       1450 :                 begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);
#     491         [ +  + ]:       1450 :                 if (begin == std::string::npos) {
#     492                 :         63 :                     break; // Empty line
#     493                 :         63 :                 }
#     494                 :       1387 :                 new_line_pos = s.m_right.find_first_of('\n', begin + 1);
#     495                 :       1387 :             }
#     496                 :       8886 :             ret += right;
#     497                 :       8886 :             ret += "\n";
#     498                 :       8886 :         }
#     499                 :       1629 :         return ret;
#     500                 :       1629 :     }
#     501                 :            : };
#     502                 :            : 
#     503                 :            : RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
#     504                 :          0 :     : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
#     505                 :            : 
#     506                 :            : RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
#     507                 :            :     : m_name{std::move(name)},
#     508                 :            :       m_fun{std::move(fun)},
#     509                 :            :       m_description{std::move(description)},
#     510                 :            :       m_args{std::move(args)},
#     511                 :            :       m_results{std::move(results)},
#     512                 :            :       m_examples{std::move(examples)}
#     513                 :     393724 : {
#     514                 :     393724 :     std::set<std::string> named_args;
#     515         [ +  + ]:     757026 :     for (const auto& arg : m_args) {
#     516                 :     757026 :         std::vector<std::string> names;
#     517                 :     757026 :         boost::split(names, arg.m_names, boost::is_any_of("|"));
#     518                 :            :         // Should have unique named arguments
#     519         [ +  + ]:     763147 :         for (const std::string& name : names) {
#     520         [ -  + ]:     763147 :             CHECK_NONFATAL(named_args.insert(name).second);
#     521                 :     763147 :         }
#     522                 :            :         // Default value type should match argument type only when defined
#     523         [ +  + ]:     757026 :         if (arg.m_fallback.index() == 2) {
#     524                 :     253798 :             const RPCArg::Type type = arg.m_type;
#     525                 :     253798 :             switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) {
#     526         [ +  + ]:       1662 :             case UniValue::VOBJ:
#     527         [ -  + ]:       1662 :                 CHECK_NONFATAL(type == RPCArg::Type::OBJ);
#     528                 :       1662 :                 break;
#     529         [ +  + ]:       3776 :             case UniValue::VARR:
#     530         [ -  + ]:       3776 :                 CHECK_NONFATAL(type == RPCArg::Type::ARR);
#     531                 :       3776 :                 break;
#     532         [ +  + ]:      62868 :             case UniValue::VSTR:
#     533 [ +  - ][ +  + ]:      62868 :                 CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT);
#                 [ +  + ]
#     534                 :      62868 :                 break;
#     535         [ +  + ]:      62868 :             case UniValue::VNUM:
#     536 [ #  # ][ #  # ]:      50854 :                 CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE);
#                 [ +  - ]
#     537                 :      50854 :                 break;
#     538         [ +  + ]:     134638 :             case UniValue::VBOOL:
#     539         [ -  + ]:     134638 :                 CHECK_NONFATAL(type == RPCArg::Type::BOOL);
#     540                 :     134638 :                 break;
#     541         [ -  + ]:     134638 :             case UniValue::VNULL:
#     542                 :            :                 // Null values are accepted in all arguments
#     543                 :          0 :                 break;
#     544         [ -  + ]:          0 :             default:
#     545                 :          0 :                 CHECK_NONFATAL(false);
#     546                 :          0 :                 break;
#     547                 :     253798 :             }
#     548                 :     253798 :         }
#     549                 :     757026 :     }
#     550                 :     393724 : }
#     551                 :            : 
#     552                 :            : std::string RPCResults::ToDescriptionString() const
#     553                 :        767 : {
#     554                 :        767 :     std::string result;
#     555         [ +  + ]:        868 :     for (const auto& r : m_results) {
#     556         [ +  + ]:        868 :         if (r.m_type == RPCResult::Type::ANY) continue; // for testing only
#     557         [ +  + ]:        862 :         if (r.m_cond.empty()) {
#     558                 :        698 :             result += "\nResult:\n";
#     559                 :        698 :         } else {
#     560                 :        164 :             result += "\nResult (" + r.m_cond + "):\n";
#     561                 :        164 :         }
#     562                 :        862 :         Sections sections;
#     563                 :        862 :         r.ToSections(sections);
#     564                 :        862 :         result += sections.ToString();
#     565                 :        862 :     }
#     566                 :        767 :     return result;
#     567                 :        767 : }
#     568                 :            : 
#     569                 :            : std::string RPCExamples::ToDescriptionString() const
#     570                 :        767 : {
#     571         [ +  + ]:        767 :     return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
#     572                 :        767 : }
#     573                 :            : 
#     574                 :            : UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
#     575                 :     138432 : {
#     576         [ +  + ]:     138432 :     if (request.mode == JSONRPCRequest::GET_ARGS) {
#     577                 :        161 :         return GetArgMap();
#     578                 :        161 :     }
#     579                 :            :     /*
#     580                 :            :      * Check if the given request is valid according to this command or if
#     581                 :            :      * the user is asking for help information, and throw help when appropriate.
#     582                 :            :      */
#     583 [ +  + ][ +  + ]:     138271 :     if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
#     584                 :        766 :         throw std::runtime_error(ToString());
#     585                 :        766 :     }
#     586                 :     137505 :     const UniValue ret = m_fun(*this, request);
#     587         [ -  + ]:     137505 :     CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [ret](const RPCResult& res) { return res.MatchesType(ret); }));
#     588                 :     137505 :     return ret;
#     589                 :     137505 : }
#     590                 :            : 
#     591                 :            : bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
#     592                 :     137554 : {
#     593                 :     137554 :     size_t num_required_args = 0;
#     594         [ +  + ]:     288141 :     for (size_t n = m_args.size(); n > 0; --n) {
#     595         [ +  + ]:     225609 :         if (!m_args.at(n - 1).IsOptional()) {
#     596                 :      75022 :             num_required_args = n;
#     597                 :      75022 :             break;
#     598                 :      75022 :         }
#     599                 :     225609 :     }
#     600 [ +  + ][ +  + ]:     137554 :     return num_required_args <= num_args && num_args <= m_args.size();
#     601                 :     137554 : }
#     602                 :            : 
#     603                 :            : std::vector<std::string> RPCHelpMan::GetArgNames() const
#     604                 :     127646 : {
#     605                 :     127646 :     std::vector<std::string> ret;
#     606         [ +  + ]:     247024 :     for (const auto& arg : m_args) {
#     607                 :     247024 :         ret.emplace_back(arg.m_names);
#     608                 :     247024 :     }
#     609                 :     127646 :     return ret;
#     610                 :     127646 : }
#     611                 :            : 
#     612                 :            : std::string RPCHelpMan::ToString() const
#     613                 :        767 : {
#     614                 :        767 :     std::string ret;
#     615                 :            : 
#     616                 :            :     // Oneline summary
#     617                 :        767 :     ret += m_name;
#     618                 :        767 :     bool was_optional{false};
#     619         [ +  + ]:       1459 :     for (const auto& arg : m_args) {
#     620         [ +  + ]:       1459 :         if (arg.m_hidden) break; // Any arg that follows is also hidden
#     621                 :       1454 :         const bool optional = arg.IsOptional();
#     622                 :       1454 :         ret += " ";
#     623         [ +  + ]:       1454 :         if (optional) {
#     624         [ +  + ]:        851 :             if (!was_optional) ret += "( ";
#     625                 :        851 :             was_optional = true;
#     626                 :        851 :         } else {
#     627         [ +  + ]:        603 :             if (was_optional) ret += ") ";
#     628                 :        603 :             was_optional = false;
#     629                 :        603 :         }
#     630                 :       1454 :         ret += arg.ToString(/*oneline=*/true);
#     631                 :       1454 :     }
#     632         [ +  + ]:        767 :     if (was_optional) ret += " )";
#     633                 :            : 
#     634                 :            :     // Description
#     635                 :        767 :     ret += "\n\n" + TrimString(m_description) + "\n";
#     636                 :            : 
#     637                 :            :     // Arguments
#     638                 :        767 :     Sections sections;
#     639         [ +  + ]:       2221 :     for (size_t i{0}; i < m_args.size(); ++i) {
#     640                 :       1459 :         const auto& arg = m_args.at(i);
#     641         [ +  + ]:       1459 :         if (arg.m_hidden) break; // Any arg that follows is also hidden
#     642                 :            : 
#     643         [ +  + ]:       1454 :         if (i == 0) ret += "\nArguments:\n";
#     644                 :            : 
#     645                 :            :         // Push named argument name and description
#     646                 :       1454 :         sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString());
#     647                 :       1454 :         sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
#     648                 :            : 
#     649                 :            :         // Recursively push nested args
#     650                 :       1454 :         sections.Push(arg);
#     651                 :       1454 :     }
#     652                 :        767 :     ret += sections.ToString();
#     653                 :            : 
#     654                 :            :     // Result
#     655                 :        767 :     ret += m_results.ToDescriptionString();
#     656                 :            : 
#     657                 :            :     // Examples
#     658                 :        767 :     ret += m_examples.ToDescriptionString();
#     659                 :            : 
#     660                 :        767 :     return ret;
#     661                 :        767 : }
#     662                 :            : 
#     663                 :            : UniValue RPCHelpMan::GetArgMap() const
#     664                 :        161 : {
#     665                 :        161 :     UniValue arr{UniValue::VARR};
#     666         [ +  + ]:        473 :     for (int i{0}; i < int(m_args.size()); ++i) {
#     667                 :        312 :         const auto& arg = m_args.at(i);
#     668                 :        312 :         std::vector<std::string> arg_names;
#     669                 :        312 :         boost::split(arg_names, arg.m_names, boost::is_any_of("|"));
#     670         [ +  + ]:        314 :         for (const auto& arg_name : arg_names) {
#     671                 :        314 :             UniValue map{UniValue::VARR};
#     672                 :        314 :             map.push_back(m_name);
#     673                 :        314 :             map.push_back(i);
#     674                 :        314 :             map.push_back(arg_name);
#     675         [ +  + ]:        314 :             map.push_back(arg.m_type == RPCArg::Type::STR ||
#     676         [ +  + ]:        314 :                           arg.m_type == RPCArg::Type::STR_HEX);
#     677                 :        314 :             arr.push_back(map);
#     678                 :        314 :         }
#     679                 :        312 :     }
#     680                 :        161 :     return arr;
#     681                 :        161 : }
#     682                 :            : 
#     683                 :            : std::string RPCArg::GetFirstName() const
#     684                 :       3836 : {
#     685                 :       3836 :     return m_names.substr(0, m_names.find("|"));
#     686                 :       3836 : }
#     687                 :            : 
#     688                 :            : std::string RPCArg::GetName() const
#     689                 :        145 : {
#     690         [ -  + ]:        145 :     CHECK_NONFATAL(std::string::npos == m_names.find("|"));
#     691                 :        145 :     return m_names;
#     692                 :        145 : }
#     693                 :            : 
#     694                 :            : bool RPCArg::IsOptional() const
#     695                 :     227063 : {
#     696         [ +  + ]:     227063 :     if (m_fallback.index() != 0) {
#     697                 :     137622 :         return true;
#     698                 :     137622 :     } else {
#     699                 :      89441 :         return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback);
#     700                 :      89441 :     }
#     701                 :     227063 : }
#     702                 :            : 
#     703                 :            : std::string RPCArg::ToDescriptionString() const
#     704                 :       2569 : {
#     705                 :       2569 :     std::string ret;
#     706                 :       2569 :     ret += "(";
#     707         [ +  + ]:       2569 :     if (m_type_str.size() != 0) {
#     708                 :         27 :         ret += m_type_str.at(1);
#     709                 :       2542 :     } else {
#     710         [ -  + ]:       2542 :         switch (m_type) {
#     711         [ +  + ]:        331 :         case Type::STR_HEX:
#     712         [ +  + ]:       1025 :         case Type::STR: {
#     713                 :       1025 :             ret += "string";
#     714                 :       1025 :             break;
#     715                 :        331 :         }
#     716         [ +  + ]:        411 :         case Type::NUM: {
#     717                 :        411 :             ret += "numeric";
#     718                 :        411 :             break;
#     719                 :        331 :         }
#     720         [ +  + ]:        145 :         case Type::AMOUNT: {
#     721                 :        145 :             ret += "numeric or string";
#     722                 :        145 :             break;
#     723                 :        331 :         }
#     724         [ +  + ]:         25 :         case Type::RANGE: {
#     725                 :         25 :             ret += "numeric or array";
#     726                 :         25 :             break;
#     727                 :        331 :         }
#     728         [ +  + ]:        477 :         case Type::BOOL: {
#     729                 :        477 :             ret += "boolean";
#     730                 :        477 :             break;
#     731                 :        331 :         }
#     732         [ +  + ]:        151 :         case Type::OBJ:
#     733         [ +  + ]:        189 :         case Type::OBJ_USER_KEYS: {
#     734                 :        189 :             ret += "json object";
#     735                 :        189 :             break;
#     736                 :        151 :         }
#     737         [ +  + ]:        270 :         case Type::ARR: {
#     738                 :        270 :             ret += "json array";
#     739                 :        270 :             break;
#     740                 :        151 :         }
#     741                 :       2542 :         } // no default case, so the compiler can warn about missing cases
#     742                 :       2542 :     }
#     743         [ +  + ]:       2569 :     if (m_fallback.index() == 1) {
#     744                 :        410 :         ret += ", optional, default=" + std::get<RPCArg::DefaultHint>(m_fallback);
#     745         [ +  + ]:       2159 :     } else if (m_fallback.index() == 2) {
#     746                 :        721 :         ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write();
#     747                 :       1438 :     } else {
#     748         [ -  + ]:       1438 :         switch (std::get<RPCArg::Optional>(m_fallback)) {
#     749         [ +  + ]:        360 :         case RPCArg::Optional::OMITTED: {
#     750                 :            :             // nothing to do. Element is treated as if not present and has no default value
#     751                 :        360 :             break;
#     752                 :          0 :         }
#     753         [ +  + ]:        228 :         case RPCArg::Optional::OMITTED_NAMED_ARG: {
#     754                 :        228 :             ret += ", optional"; // Default value is "null"
#     755                 :        228 :             break;
#     756                 :          0 :         }
#     757         [ +  + ]:        850 :         case RPCArg::Optional::NO: {
#     758                 :        850 :             ret += ", required";
#     759                 :        850 :             break;
#     760                 :          0 :         }
#     761                 :       1438 :         } // no default case, so the compiler can warn about missing cases
#     762                 :       1438 :     }
#     763                 :       2569 :     ret += ")";
#     764         [ +  + ]:       2569 :     ret += m_description.empty() ? "" : " " + m_description;
#     765                 :       2569 :     return ret;
#     766                 :       2569 : }
#     767                 :            : 
#     768                 :            : void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
#     769                 :       6333 : {
#     770                 :            :     // Indentation
#     771                 :       6333 :     const std::string indent(current_indent, ' ');
#     772                 :       6333 :     const std::string indent_next(current_indent + 2, ' ');
#     773                 :            : 
#     774                 :            :     // Elements in a JSON structure (dictionary or array) are separated by a comma
#     775         [ +  + ]:       6333 :     const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};
#     776                 :            : 
#     777                 :            :     // The key name if recursed into a dictionary
#     778                 :       6333 :     const std::string maybe_key{
#     779         [ +  + ]:       6333 :         outer_type == OuterType::OBJ ?
#     780                 :       4927 :             "\"" + this->m_key_name + "\" : " :
#     781                 :       6333 :             ""};
#     782                 :            : 
#     783                 :            :     // Format description with type
#     784                 :       6333 :     const auto Description = [&](const std::string& type) {
#     785         [ +  + ]:       6272 :         return "(" + type + (this->m_optional ? ", optional" : "") + ")" +
#     786         [ +  + ]:       6272 :                (this->m_description.empty() ? "" : " " + this->m_description);
#     787                 :       6272 :     };
#     788                 :            : 
#     789         [ -  + ]:       6333 :     switch (m_type) {
#     790         [ +  + ]:         61 :     case Type::ELISION: {
#     791                 :            :         // If the inner result is empty, use three dots for elision
#     792                 :         61 :         sections.PushSection({indent + "..." + maybe_separator, m_description});
#     793                 :         61 :         return;
#     794                 :          0 :     }
#     795         [ -  + ]:          0 :     case Type::ANY: {
#     796                 :          0 :         CHECK_NONFATAL(false); // Only for testing
#     797                 :          0 :     }
#     798         [ +  + ]:        133 :     case Type::NONE: {
#     799                 :        133 :         sections.PushSection({indent + "null" + maybe_separator, Description("json null")});
#     800                 :        133 :         return;
#     801                 :          0 :     }
#     802         [ +  + ]:       1151 :     case Type::STR: {
#     803                 :       1151 :         sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});
#     804                 :       1151 :         return;
#     805                 :          0 :     }
#     806         [ +  + ]:        441 :     case Type::STR_AMOUNT: {
#     807                 :        441 :         sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
#     808                 :        441 :         return;
#     809                 :          0 :     }
#     810         [ +  + ]:        854 :     case Type::STR_HEX: {
#     811                 :        854 :         sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});
#     812                 :        854 :         return;
#     813                 :          0 :     }
#     814         [ +  + ]:       1531 :     case Type::NUM: {
#     815                 :       1531 :         sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
#     816                 :       1531 :         return;
#     817                 :          0 :     }
#     818         [ +  + ]:        241 :     case Type::NUM_TIME: {
#     819                 :        241 :         sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});
#     820                 :        241 :         return;
#     821                 :          0 :     }
#     822         [ +  + ]:        500 :     case Type::BOOL: {
#     823                 :        500 :         sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});
#     824                 :        500 :         return;
#     825                 :          0 :     }
#     826         [ +  + ]:         17 :     case Type::ARR_FIXED:
#     827         [ +  + ]:        501 :     case Type::ARR: {
#     828                 :        501 :         sections.PushSection({indent + maybe_key + "[", Description("json array")});
#     829         [ +  + ]:        544 :         for (const auto& i : m_inner) {
#     830                 :        544 :             i.ToSections(sections, OuterType::ARR, current_indent + 2);
#     831                 :        544 :         }
#     832         [ -  + ]:        501 :         CHECK_NONFATAL(!m_inner.empty());
#     833 [ +  + ][ +  + ]:        501 :         if (m_type == Type::ARR && m_inner.back().m_type != Type::ELISION) {
#     834                 :        479 :             sections.PushSection({indent_next + "...", ""});
#     835                 :        479 :         } else {
#     836                 :            :             // Remove final comma, which would be invalid JSON
#     837                 :         22 :             sections.m_sections.back().m_left.pop_back();
#     838                 :         22 :         }
#     839                 :        501 :         sections.PushSection({indent + "]" + maybe_separator, ""});
#     840                 :        501 :         return;
#     841                 :        501 :     }
#     842         [ +  + ]:        106 :     case Type::OBJ_DYN:
#     843         [ +  + ]:        920 :     case Type::OBJ: {
#     844         [ +  + ]:        920 :         if (m_inner.empty()) {
#     845                 :          5 :             sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")});
#     846                 :          5 :             return;
#     847                 :          5 :         }
#     848                 :        915 :         sections.PushSection({indent + maybe_key + "{", Description("json object")});
#     849         [ +  + ]:       4927 :         for (const auto& i : m_inner) {
#     850                 :       4927 :             i.ToSections(sections, OuterType::OBJ, current_indent + 2);
#     851                 :       4927 :         }
#     852 [ +  + ][ +  - ]:        915 :         if (m_type == Type::OBJ_DYN && m_inner.back().m_type != Type::ELISION) {
#     853                 :            :             // If the dictionary keys are dynamic, use three dots for continuation
#     854                 :        106 :             sections.PushSection({indent_next + "...", ""});
#     855                 :        809 :         } else {
#     856                 :            :             // Remove final comma, which would be invalid JSON
#     857                 :        809 :             sections.m_sections.back().m_left.pop_back();
#     858                 :        809 :         }
#     859                 :        915 :         sections.PushSection({indent + "}" + maybe_separator, ""});
#     860                 :        915 :         return;
#     861                 :        920 :     }
#     862                 :       6333 :     } // no default case, so the compiler can warn about missing cases
#     863                 :          0 :     CHECK_NONFATAL(false);
#     864                 :          0 : }
#     865                 :            : 
#     866                 :            : bool RPCResult::MatchesType(const UniValue& result) const
#     867                 :    2479747 : {
#     868         [ +  + ]:    2479747 :     if (m_skip_type_check) {
#     869                 :        991 :         return true;
#     870                 :        991 :     }
#     871         [ -  + ]:    2478756 :     switch (m_type) {
#     872         [ +  + ]:          4 :     case Type::ELISION:
#     873         [ +  + ]:         22 :     case Type::ANY: {
#     874                 :         22 :         return true;
#     875                 :          4 :     }
#     876         [ +  + ]:      16759 :     case Type::NONE: {
#     877                 :      16759 :         return UniValue::VNULL == result.getType();
#     878                 :          4 :     }
#     879         [ +  + ]:     390641 :     case Type::STR:
#     880         [ +  + ]:     993782 :     case Type::STR_HEX: {
#     881                 :     993782 :         return UniValue::VSTR == result.getType();
#     882                 :     390641 :     }
#     883         [ +  + ]:     776328 :     case Type::NUM:
#     884         [ +  + ]:     848873 :     case Type::STR_AMOUNT:
#     885         [ +  + ]:     962782 :     case Type::NUM_TIME: {
#     886                 :     962782 :         return UniValue::VNUM == result.getType();
#     887                 :     848873 :     }
#     888         [ +  + ]:     212377 :     case Type::BOOL: {
#     889                 :     212377 :         return UniValue::VBOOL == result.getType();
#     890                 :     848873 :     }
#     891         [ +  + ]:         54 :     case Type::ARR_FIXED:
#     892         [ +  + ]:      90362 :     case Type::ARR: {
#     893         [ +  + ]:      90362 :         if (UniValue::VARR != result.getType()) return false;
#     894         [ +  + ]:     670333 :         for (size_t i{0}; i < result.get_array().size(); ++i) {
#     895                 :            :             // If there are more results than documented, re-use the last doc_inner.
#     896                 :     581698 :             const RPCResult& doc_inner{m_inner.at(std::min(m_inner.size() - 1, i))};
#     897         [ +  + ]:     581698 :             if (!doc_inner.MatchesType(result.get_array()[i])) return false;
#     898                 :     581698 :         }
#     899                 :      88635 :         return true; // empty result array is valid
#     900                 :      90298 :     }
#     901         [ +  + ]:      30602 :     case Type::OBJ_DYN:
#     902         [ +  + ]:     202672 :     case Type::OBJ: {
#     903         [ +  + ]:     202672 :         if (UniValue::VOBJ != result.getType()) return false;
#     904 [ +  + ][ +  + ]:     202671 :         if (!m_inner.empty() && m_inner.at(0).m_type == Type::ELISION) return true;
#     905         [ +  + ]:     199872 :         if (m_type == Type::OBJ_DYN) {
#     906                 :      30602 :             const RPCResult& doc_inner{m_inner.at(0)}; // Assume all types are the same, randomly pick the first
#     907         [ +  + ]:     419730 :             for (size_t i{0}; i < result.get_obj().size(); ++i) {
#     908         [ -  + ]:     389128 :                 if (!doc_inner.MatchesType(result.get_obj()[i])) {
#     909                 :          0 :                     return false;
#     910                 :          0 :                 }
#     911                 :     389128 :             }
#     912                 :      30602 :             return true; // empty result obj is valid
#     913                 :      30602 :         }
#     914                 :     169270 :         std::set<std::string> doc_keys;
#     915         [ +  + ]:    1681728 :         for (const auto& doc_entry : m_inner) {
#     916                 :    1681728 :             doc_keys.insert(doc_entry.m_key_name);
#     917                 :    1681728 :         }
#     918                 :     169270 :         std::map<std::string, UniValue> result_obj;
#     919                 :     169270 :         result.getObjMap(result_obj);
#     920         [ +  + ]:    1379607 :         for (const auto& result_entry : result_obj) {
#     921         [ +  + ]:    1379607 :             if (doc_keys.find(result_entry.first) == doc_keys.end()) {
#     922                 :         53 :                 return false; // missing documentation
#     923                 :         53 :             }
#     924                 :    1379607 :         }
#     925                 :            : 
#     926         [ +  + ]:    1666708 :         for (const auto& doc_entry : m_inner) {
#     927                 :    1666708 :             const auto result_it{result_obj.find(doc_entry.m_key_name)};
#     928         [ +  + ]:    1666708 :             if (result_it == result_obj.end()) {
#     929         [ -  + ]:     301685 :                 if (!doc_entry.m_optional) {
#     930                 :          0 :                     return false; // result is missing a required key
#     931                 :          0 :                 }
#     932                 :     301685 :                 continue;
#     933                 :     301685 :             }
#     934         [ +  + ]:    1365023 :             if (!doc_entry.MatchesType(result_it->second)) {
#     935                 :       1663 :                 return false; // wrong type
#     936                 :       1663 :             }
#     937                 :    1365023 :         }
#     938                 :     167554 :         return true;
#     939                 :     169217 :     }
#     940                 :    2478756 :     } // no default case, so the compiler can warn about missing cases
#     941                 :          0 :     CHECK_NONFATAL(false);
#     942                 :          0 : }
#     943                 :            : 
#     944                 :            : void RPCResult::CheckInnerDoc() const
#     945                 :    3249006 : {
#     946         [ +  + ]:    3249006 :     if (m_type == Type::OBJ) {
#     947                 :            :         // May or may not be empty
#     948                 :     399006 :         return;
#     949                 :     399006 :     }
#     950                 :            :     // Everything else must either be empty or not
#     951 [ +  + ][ +  + ]:    2850000 :     const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN};
#                 [ +  + ]
#     952         [ -  + ]:    2850000 :     CHECK_NONFATAL(inner_needed != m_inner.empty());
#     953                 :    2850000 : }
#     954                 :            : 
#     955                 :            : std::string RPCArg::ToStringObj(const bool oneline) const
#     956                 :        830 : {
#     957                 :        830 :     std::string res;
#     958                 :        830 :     res += "\"";
#     959                 :        830 :     res += GetFirstName();
#     960         [ +  + ]:        830 :     if (oneline) {
#     961                 :        220 :         res += "\":";
#     962                 :        610 :     } else {
#     963                 :        610 :         res += "\": ";
#     964                 :        610 :     }
#     965         [ -  + ]:        830 :     switch (m_type) {
#     966         [ +  + ]:        110 :     case Type::STR:
#     967                 :        110 :         return res + "\"str\"";
#     968         [ +  + ]:        192 :     case Type::STR_HEX:
#     969                 :        192 :         return res + "\"hex\"";
#     970         [ +  + ]:        197 :     case Type::NUM:
#     971                 :        197 :         return res + "n";
#     972         [ +  + ]:         25 :     case Type::RANGE:
#     973                 :         25 :         return res + "n or [n,n]";
#     974         [ +  + ]:        151 :     case Type::AMOUNT:
#     975                 :        151 :         return res + "amount";
#     976         [ +  + ]:        155 :     case Type::BOOL:
#     977                 :        155 :         return res + "bool";
#     978         [ -  + ]:          0 :     case Type::ARR:
#     979                 :          0 :         res += "[";
#     980         [ #  # ]:          0 :         for (const auto& i : m_inner) {
#     981                 :          0 :             res += i.ToString(oneline) + ",";
#     982                 :          0 :         }
#     983                 :          0 :         return res + "...]";
#     984         [ -  + ]:          0 :     case Type::OBJ:
#     985         [ -  + ]:          0 :     case Type::OBJ_USER_KEYS:
#     986                 :            :         // Currently unused, so avoid writing dead code
#     987                 :          0 :         CHECK_NONFATAL(false);
#     988                 :        830 :     } // no default case, so the compiler can warn about missing cases
#     989                 :          0 :     CHECK_NONFATAL(false);
#     990                 :          0 : }
#     991                 :            : 
#     992                 :            : std::string RPCArg::ToString(const bool oneline) const
#     993                 :       1871 : {
#     994 [ +  + ][ +  + ]:       1871 :     if (oneline && !m_oneline_description.empty()) return m_oneline_description;
#     995                 :            : 
#     996         [ -  + ]:       1799 :     switch (m_type) {
#     997         [ +  + ]:        260 :     case Type::STR_HEX:
#     998         [ +  + ]:        891 :     case Type::STR: {
#     999                 :        891 :         return "\"" + GetFirstName() + "\"";
#    1000                 :        260 :     }
#    1001         [ +  + ]:        292 :     case Type::NUM:
#    1002         [ +  + ]:        297 :     case Type::RANGE:
#    1003         [ +  + ]:        339 :     case Type::AMOUNT:
#    1004         [ +  + ]:        661 :     case Type::BOOL: {
#    1005                 :        661 :         return GetFirstName();
#    1006                 :        339 :     }
#    1007         [ +  + ]:         71 :     case Type::OBJ:
#    1008         [ +  + ]:        109 :     case Type::OBJ_USER_KEYS: {
#    1009                 :        220 :         const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
#    1010         [ +  + ]:        109 :         if (m_type == Type::OBJ) {
#    1011                 :         71 :             return "{" + res + "}";
#    1012                 :         71 :         } else {
#    1013                 :         38 :             return "{" + res + ",...}";
#    1014                 :         38 :         }
#    1015                 :        109 :     }
#    1016         [ +  + ]:        138 :     case Type::ARR: {
#    1017                 :        138 :         std::string res;
#    1018         [ +  + ]:        176 :         for (const auto& i : m_inner) {
#    1019                 :        176 :             res += i.ToString(oneline) + ",";
#    1020                 :        176 :         }
#    1021                 :        138 :         return "[" + res + "...]";
#    1022                 :        109 :     }
#    1023                 :       1799 :     } // no default case, so the compiler can warn about missing cases
#    1024                 :          0 :     CHECK_NONFATAL(false);
#    1025                 :          0 : }
#    1026                 :            : 
#    1027                 :            : static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
#    1028                 :        107 : {
#    1029         [ +  + ]:        107 :     if (value.isNum()) {
#    1030                 :         38 :         return {0, value.get_int64()};
#    1031                 :         38 :     }
#    1032 [ +  - ][ +  - ]:         69 :     if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
#         [ +  - ][ +  - ]
#    1033                 :         69 :         int64_t low = value[0].get_int64();
#    1034                 :         69 :         int64_t high = value[1].get_int64();
#    1035         [ +  + ]:         69 :         if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
#    1036                 :         64 :         return {low, high};
#    1037                 :         69 :     }
#    1038                 :          0 :     throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
#    1039                 :         69 : }
#    1040                 :            : 
#    1041                 :            : std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
#    1042                 :        107 : {
#    1043                 :        107 :     int64_t low, high;
#    1044                 :        107 :     std::tie(low, high) = ParseRange(value);
#    1045         [ +  + ]:        107 :     if (low < 0) {
#    1046                 :          5 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
#    1047                 :          5 :     }
#    1048         [ +  + ]:        102 :     if ((high >> 31) != 0) {
#    1049                 :          8 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
#    1050                 :          8 :     }
#    1051         [ +  + ]:         94 :     if (high >= low + 1000000) {
#    1052                 :          5 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
#    1053                 :          5 :     }
#    1054                 :         89 :     return {low, high};
#    1055                 :         94 : }
#    1056                 :            : 
#    1057                 :            : std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider)
#    1058                 :         75 : {
#    1059                 :         75 :     std::string desc_str;
#    1060                 :         75 :     std::pair<int64_t, int64_t> range = {0, 1000};
#    1061         [ +  + ]:         75 :     if (scanobject.isStr()) {
#    1062                 :         57 :         desc_str = scanobject.get_str();
#    1063         [ +  - ]:         57 :     } else if (scanobject.isObject()) {
#    1064                 :         18 :         UniValue desc_uni = find_value(scanobject, "desc");
#    1065         [ -  + ]:         18 :         if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
#    1066                 :         18 :         desc_str = desc_uni.get_str();
#    1067                 :         18 :         UniValue range_uni = find_value(scanobject, "range");
#    1068         [ +  - ]:         18 :         if (!range_uni.isNull()) {
#    1069                 :         18 :             range = ParseDescriptorRange(range_uni);
#    1070                 :         18 :         }
#    1071                 :         18 :     } else {
#    1072                 :          0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
#    1073                 :          0 :     }
#    1074                 :            : 
#    1075                 :         75 :     std::string error;
#    1076                 :         75 :     auto desc = Parse(desc_str, provider, error);
#    1077         [ -  + ]:         75 :     if (!desc) {
#    1078                 :          0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
#    1079                 :          0 :     }
#    1080         [ +  + ]:         75 :     if (!desc->IsRange()) {
#    1081                 :         57 :         range.first = 0;
#    1082                 :         57 :         range.second = 0;
#    1083                 :         57 :     }
#    1084                 :         75 :     std::vector<CScript> ret;
#    1085         [ +  + ]:      18139 :     for (int i = range.first; i <= range.second; ++i) {
#    1086                 :      18064 :         std::vector<CScript> scripts;
#    1087         [ -  + ]:      18064 :         if (!desc->Expand(i, provider, scripts, provider)) {
#    1088                 :          0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
#    1089                 :          0 :         }
#    1090                 :      18064 :         std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
#    1091                 :      18064 :     }
#    1092                 :         75 :     return ret;
#    1093                 :         75 : }
#    1094                 :            : 
#    1095                 :            : UniValue GetServicesNames(ServiceFlags services)
#    1096                 :      14954 : {
#    1097                 :      14954 :     UniValue servicesNames(UniValue::VARR);
#    1098                 :            : 
#    1099         [ +  + ]:      41802 :     for (const auto& flag : serviceFlagsToStr(services)) {
#    1100                 :      41802 :         servicesNames.push_back(flag);
#    1101                 :      41802 :     }
#    1102                 :            : 
#    1103                 :      14954 :     return servicesNames;
#    1104                 :      14954 : }

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a