LCOV - code coverage report
Current view: top level - src - bitcoin-cli.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 56.6 % 728 412
Test Date: 2025-01-19 05:08:01 Functions: 55.9 % 34 19
Branches: 33.3 % 2289 762

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2                 :             : // Copyright (c) 2009-2022 The Bitcoin Core developers
       3                 :             : // Distributed under the MIT software license, see the accompanying
       4                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5                 :             : 
       6                 :             : #include <bitcoin-build-config.h> // IWYU pragma: keep
       7                 :             : 
       8                 :             : #include <chainparamsbase.h>
       9                 :             : #include <clientversion.h>
      10                 :             : #include <common/args.h>
      11                 :             : #include <common/system.h>
      12                 :             : #include <compat/compat.h>
      13                 :             : #include <compat/stdin.h>
      14                 :             : #include <policy/feerate.h>
      15                 :             : #include <rpc/client.h>
      16                 :             : #include <rpc/mining.h>
      17                 :             : #include <rpc/protocol.h>
      18                 :             : #include <rpc/request.h>
      19                 :             : #include <tinyformat.h>
      20                 :             : #include <univalue.h>
      21                 :             : #include <util/chaintype.h>
      22                 :             : #include <util/exception.h>
      23                 :             : #include <util/strencodings.h>
      24                 :             : #include <util/time.h>
      25                 :             : #include <util/translation.h>
      26                 :             : 
      27                 :             : #include <algorithm>
      28                 :             : #include <chrono>
      29                 :             : #include <cmath>
      30                 :             : #include <cstdio>
      31                 :             : #include <functional>
      32                 :             : #include <memory>
      33                 :             : #include <optional>
      34                 :             : #include <string>
      35                 :             : #include <tuple>
      36                 :             : 
      37                 :             : #ifndef WIN32
      38                 :             : #include <unistd.h>
      39                 :             : #endif
      40                 :             : 
      41                 :             : #include <event2/buffer.h>
      42                 :             : #include <event2/keyvalq_struct.h>
      43                 :             : #include <support/events.h>
      44                 :             : 
      45                 :             : using util::Join;
      46                 :             : using util::ToString;
      47                 :             : 
      48                 :             : // The server returns time values from a mockable system clock, but it is not
      49                 :             : // trivial to get the mocked time from the server, nor is it needed for now, so
      50                 :             : // just use a plain system_clock.
      51                 :             : using CliClock = std::chrono::system_clock;
      52                 :             : 
      53                 :             : const TranslateFn G_TRANSLATION_FUN{nullptr};
      54                 :             : 
      55                 :             : static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
      56                 :             : static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
      57                 :             : static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
      58                 :             : static const bool DEFAULT_NAMED=false;
      59                 :             : static const int CONTINUE_EXECUTION=-1;
      60                 :             : static constexpr uint8_t NETINFO_MAX_LEVEL{4};
      61                 :             : static constexpr int8_t UNKNOWN_NETWORK{-1};
      62                 :             : // See GetNetworkName() in netbase.cpp
      63                 :             : static constexpr std::array NETWORKS{"not_publicly_routable", "ipv4", "ipv6", "onion", "i2p", "cjdns", "internal"};
      64                 :             : static constexpr std::array NETWORK_SHORT_NAMES{"npr", "ipv4", "ipv6", "onion", "i2p", "cjdns", "int"};
      65                 :             : static constexpr std::array UNREACHABLE_NETWORK_IDS{/*not_publicly_routable*/0, /*internal*/6};
      66                 :             : 
      67                 :             : /** Default number of blocks to generate for RPC generatetoaddress. */
      68                 :             : static const std::string DEFAULT_NBLOCKS = "1";
      69                 :             : 
      70                 :             : /** Default -color setting. */
      71                 :             : static const std::string DEFAULT_COLOR_SETTING{"auto"};
      72                 :             : 
      73                 :         772 : static void SetupCliArgs(ArgsManager& argsman)
      74                 :             : {
      75                 :         772 :     SetupHelpOptions(argsman);
      76                 :             : 
      77                 :         772 :     const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN);
      78         [ +  - ]:         772 :     const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET);
      79         [ +  - ]:         772 :     const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4);
      80         [ +  - ]:         772 :     const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET);
      81         [ +  - ]:         772 :     const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST);
      82                 :             : 
      83   [ +  -  +  -  :        1544 :     argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
      84   [ +  -  +  -  :        1544 :     argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
      85   [ +  -  +  -  :        1544 :     argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
                   +  - ]
      86   [ +  -  +  - ]:        1544 :     argsman.AddArg("-generate",
      87                 :           0 :                    strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer "
      88                 :             :                              "arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to "
      89                 :             :                              "RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000",
      90                 :             :                              DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES),
      91         [ +  - ]:         772 :                    ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS);
      92   [ +  -  +  -  :        1544 :     argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total, after filtering for quality and recency. The total number of addresses known to the node may be higher.", ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS);
                   +  - ]
      93   [ +  -  +  -  :        1544 :     argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the output of -getinfo is the result of multiple non-atomic requests. Some entries in the output may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS);
                   +  - ]
      94   [ +  -  +  -  :        1544 :     argsman.AddArg("-netinfo", strprintf("Get network peer connection information from the remote server. An optional argument from 0 to %d can be passed for different peers listings (default: 0). If a non-zero value is passed, an additional \"outonly\" (or \"o\") argument can be passed to see outbound peers only. Pass \"help\" (or \"h\") for detailed help documentation.", NETINFO_MAX_LEVEL), ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS);
                   +  - ]
      95                 :             : 
      96         [ +  - ]:         772 :     SetupChainParamsBaseOptions(argsman);
      97   [ +  -  +  -  :        1544 :     argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never. Only applies to the output of -getinfo.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
                   +  - ]
      98   [ +  -  +  -  :        1544 :     argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
      99   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     100   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     101   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     102   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     103   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, testnet4: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
                   +  - ]
     104   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     105   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     106   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
                   +  - ]
     107   [ +  -  +  -  :        1544 :     argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     108   [ +  -  +  -  :        1544 :     argsman.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     109   [ +  -  +  -  :        1544 :     argsman.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     110   [ +  -  +  -  :        1544 :     argsman.AddArg("-stdinwalletpassphrase", "Read wallet passphrase from standard input as a single line. When combined with -stdin, the first line from standard input is used for the wallet passphrase.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
                   +  - ]
     111                 :         772 : }
     112                 :             : 
     113                 :             : /** libevent event log callback */
     114                 :           0 : static void libevent_log_cb(int severity, const char *msg)
     115                 :             : {
     116                 :             :     // Ignore everything other than errors
     117         [ #  # ]:           0 :     if (severity >= EVENT_LOG_ERR) {
     118   [ #  #  #  # ]:           0 :         throw std::runtime_error(strprintf("libevent error: %s", msg));
     119                 :             :     }
     120                 :           0 : }
     121                 :             : 
     122                 :             : //
     123                 :             : // Exception thrown on connection error.  This error is used to determine
     124                 :             : // when to wait if -rpcwait is given.
     125                 :             : //
     126                 :             : class CConnectionFailed : public std::runtime_error
     127                 :             : {
     128                 :             : public:
     129                 :             : 
     130                 :          23 :     explicit inline CConnectionFailed(const std::string& msg) :
     131   [ +  -  +  -  :          23 :         std::runtime_error(msg)
          -  -  -  -  +  
                      - ]
     132                 :             :     {}
     133                 :             : 
     134                 :             : };
     135                 :             : 
     136                 :             : //
     137                 :             : // This function returns either one of EXIT_ codes when it's expected to stop the process or
     138                 :             : // CONTINUE_EXECUTION when it's expected to continue further.
     139                 :             : //
     140                 :         772 : static int AppInitRPC(int argc, char* argv[])
     141                 :             : {
     142                 :         772 :     SetupCliArgs(gArgs);
     143         [ +  - ]:         772 :     std::string error;
     144   [ +  -  -  + ]:         772 :     if (!gArgs.ParseParameters(argc, argv, error)) {
     145         [ #  # ]:           0 :         tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
     146                 :             :         return EXIT_FAILURE;
     147                 :             :     }
     148   [ +  -  +  -  :        1544 :     if (argc < 2 || HelpRequested(gArgs) || gArgs.GetBoolArg("-version", false)) {
          +  -  +  -  +  
             -  +  +  +  
                      + ]
     149   [ +  -  +  - ]:           4 :         std::string strUsage = CLIENT_NAME " RPC client version " + FormatFullVersion() + "\n";
     150                 :             : 
     151   [ +  -  +  -  :           2 :         if (gArgs.GetBoolArg("-version", false)) {
                   +  - ]
     152   [ +  -  +  - ]:           4 :             strUsage += FormatParagraph(LicenseInfo());
     153                 :             :         } else {
     154         [ #  # ]:           0 :             strUsage += "\n"
     155                 :             :                 "The bitcoin-cli utility provides a command line interface to interact with a " CLIENT_NAME " RPC server.\n"
     156                 :             :                 "\nIt can be used to query network information, manage wallets, create or broadcast transactions, and control the " CLIENT_NAME " server.\n"
     157                 :             :                 "\nUse the \"help\" command to list all commands. Use \"help <command>\" to show help for that command.\n"
     158                 :             :                 "The -named option allows you to specify parameters using the key=value format, eliminating the need to pass unused positional parameters.\n"
     159                 :             :                 "\n"
     160                 :             :                 "Usage: bitcoin-cli [options] <command> [params]\n"
     161                 :             :                 "or:    bitcoin-cli [options] -named <command> [name=value]...\n"
     162                 :             :                 "or:    bitcoin-cli [options] help\n"
     163                 :             :                 "or:    bitcoin-cli [options] help <command>\n"
     164                 :           0 :                 "\n";
     165   [ #  #  #  # ]:           0 :             strUsage += "\n" + gArgs.GetHelpMessage();
     166                 :             :         }
     167                 :             : 
     168         [ +  - ]:           2 :         tfm::format(std::cout, "%s", strUsage);
     169         [ -  + ]:           2 :         if (argc < 2) {
     170         [ #  # ]:           0 :             tfm::format(std::cerr, "Error: too few parameters\n");
     171                 :             :             return EXIT_FAILURE;
     172                 :             :         }
     173                 :             :         return EXIT_SUCCESS;
     174                 :           2 :     }
     175   [ +  -  -  + ]:         770 :     if (!CheckDataDirOption(gArgs)) {
     176   [ #  #  #  #  :           0 :         tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
             #  #  #  # ]
     177                 :           0 :         return EXIT_FAILURE;
     178                 :             :     }
     179   [ +  -  -  + ]:         770 :     if (!gArgs.ReadConfigFiles(error, true)) {
     180         [ #  # ]:           0 :         tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
     181                 :             :         return EXIT_FAILURE;
     182                 :             :     }
     183                 :             :     // Check for chain settings (BaseParams() calls are only valid after this clause)
     184                 :         770 :     try {
     185   [ +  -  +  - ]:         770 :         SelectBaseParams(gArgs.GetChainType());
     186         [ -  - ]:           0 :     } catch (const std::exception& e) {
     187         [ -  - ]:           0 :         tfm::format(std::cerr, "Error: %s\n", e.what());
     188                 :           0 :         return EXIT_FAILURE;
     189                 :           0 :     }
     190                 :             :     return CONTINUE_EXECUTION;
     191                 :         772 : }
     192                 :             : 
     193                 :             : 
     194                 :             : /** Reply structure for request_done to fill in */
     195                 :         790 : struct HTTPReply
     196                 :             : {
     197                 :         790 :     HTTPReply() = default;
     198                 :             : 
     199                 :             :     int status{0};
     200                 :             :     int error{-1};
     201                 :             :     std::string body;
     202                 :             : };
     203                 :             : 
     204                 :           0 : static std::string http_errorstring(int code)
     205                 :             : {
     206   [ #  #  #  #  :           0 :     switch(code) {
                #  #  # ]
     207                 :           0 :     case EVREQ_HTTP_TIMEOUT:
     208                 :           0 :         return "timeout reached";
     209                 :           0 :     case EVREQ_HTTP_EOF:
     210                 :           0 :         return "EOF reached";
     211                 :           0 :     case EVREQ_HTTP_INVALID_HEADER:
     212                 :           0 :         return "error while reading header, or invalid header";
     213                 :           0 :     case EVREQ_HTTP_BUFFER_ERROR:
     214                 :           0 :         return "error encountered while reading or writing";
     215                 :           0 :     case EVREQ_HTTP_REQUEST_CANCEL:
     216                 :           0 :         return "request was canceled";
     217                 :           0 :     case EVREQ_HTTP_DATA_TOO_LONG:
     218                 :           0 :         return "response body is larger than allowed";
     219                 :           0 :     default:
     220                 :           0 :         return "unknown";
     221                 :             :     }
     222                 :             : }
     223                 :             : 
     224                 :         786 : static void http_request_done(struct evhttp_request *req, void *ctx)
     225                 :             : {
     226                 :         786 :     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
     227                 :             : 
     228         [ -  + ]:         786 :     if (req == nullptr) {
     229                 :             :         /* If req is nullptr, it means an error occurred while connecting: the
     230                 :             :          * error code will have been passed to http_error_cb.
     231                 :             :          */
     232                 :           0 :         reply->status = 0;
     233                 :           0 :         return;
     234                 :             :     }
     235                 :             : 
     236                 :         786 :     reply->status = evhttp_request_get_response_code(req);
     237                 :             : 
     238                 :         786 :     struct evbuffer *buf = evhttp_request_get_input_buffer(req);
     239         [ +  - ]:         786 :     if (buf)
     240                 :             :     {
     241                 :         786 :         size_t size = evbuffer_get_length(buf);
     242                 :         786 :         const char *data = (const char*)evbuffer_pullup(buf, size);
     243         [ +  + ]:         786 :         if (data)
     244                 :         764 :             reply->body = std::string(data, size);
     245                 :         786 :         evbuffer_drain(buf, size);
     246                 :             :     }
     247                 :             : }
     248                 :             : 
     249                 :           0 : static void http_error_cb(enum evhttp_request_error err, void *ctx)
     250                 :             : {
     251                 :           0 :     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
     252                 :           0 :     reply->error = err;
     253                 :           0 : }
     254                 :             : 
     255                 :             : /** Class that handles the conversion from a command-line to a JSON-RPC request,
     256                 :             :  * as well as converting back to a JSON object that can be shown as result.
     257                 :             :  */
     258                 :         755 : class BaseRequestHandler
     259                 :             : {
     260                 :             : public:
     261                 :          35 :     virtual ~BaseRequestHandler() = default;
     262                 :             :     virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
     263                 :             :     virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
     264                 :             : };
     265                 :             : 
     266                 :             : /** Process addrinfo requests */
     267                 :           0 : class AddrinfoRequestHandler : public BaseRequestHandler
     268                 :             : {
     269                 :             : private:
     270                 :           0 :     int8_t NetworkStringToId(const std::string& str) const
     271                 :             :     {
     272         [ #  # ]:           0 :         for (size_t i = 0; i < NETWORKS.size(); ++i) {
     273         [ #  # ]:           0 :             if (str == NETWORKS[i]) return i;
     274                 :             :         }
     275                 :             :         return UNKNOWN_NETWORK;
     276                 :             :     }
     277                 :             : 
     278                 :             : public:
     279                 :           0 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     280                 :             :     {
     281         [ #  # ]:           0 :         if (!args.empty()) {
     282         [ #  # ]:           0 :             throw std::runtime_error("-addrinfo takes no arguments");
     283                 :             :         }
     284   [ #  #  #  #  :           0 :         UniValue params{RPCConvertValues("getnodeaddresses", std::vector<std::string>{{"0"}})};
          #  #  #  #  #  
                      # ]
     285   [ #  #  #  #  :           0 :         return JSONRPCRequestObj("getnodeaddresses", params, 1);
                   #  # ]
     286                 :           0 :     }
     287                 :             : 
     288                 :           0 :     UniValue ProcessReply(const UniValue& reply) override
     289                 :             :     {
     290   [ #  #  #  # ]:           0 :         if (!reply["error"].isNull()) return reply;
     291   [ #  #  #  # ]:           0 :         const std::vector<UniValue>& nodes{reply["result"].getValues()};
     292   [ #  #  #  #  :           0 :         if (!nodes.empty() && nodes.at(0)["network"].isNull()) {
             #  #  #  # ]
     293         [ #  # ]:           0 :             throw std::runtime_error("-addrinfo requires bitcoind server to be running v22.0 and up");
     294                 :             :         }
     295                 :             :         // Count the number of peers known to our node, by network.
     296                 :           0 :         std::array<uint64_t, NETWORKS.size()> counts{{}};
     297         [ #  # ]:           0 :         for (const UniValue& node : nodes) {
     298   [ #  #  #  #  :           0 :             std::string network_name{node["network"].get_str()};
                   #  # ]
     299                 :           0 :             const int8_t network_id{NetworkStringToId(network_name)};
     300         [ #  # ]:           0 :             if (network_id == UNKNOWN_NETWORK) continue;
     301         [ #  # ]:           0 :             ++counts.at(network_id);
     302                 :           0 :         }
     303                 :             :         // Prepare result to return to user.
     304                 :           0 :         UniValue result{UniValue::VOBJ}, addresses{UniValue::VOBJ};
     305                 :           0 :         uint64_t total{0}; // Total address count
     306         [ #  # ]:           0 :         for (size_t i = 1; i < NETWORKS.size() - 1; ++i) {
     307   [ #  #  #  #  :           0 :             addresses.pushKV(NETWORKS[i], counts.at(i));
                   #  # ]
     308         [ #  # ]:           0 :             total += counts.at(i);
     309                 :             :         }
     310   [ #  #  #  #  :           0 :         addresses.pushKV("total", total);
                   #  # ]
     311   [ #  #  #  # ]:           0 :         result.pushKV("addresses_known", std::move(addresses));
     312   [ #  #  #  #  :           0 :         return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
                   #  # ]
     313                 :           0 :     }
     314                 :             : };
     315                 :             : 
     316                 :             : /** Process getinfo requests */
     317                 :          21 : class GetinfoRequestHandler: public BaseRequestHandler
     318                 :             : {
     319                 :             : public:
     320                 :             :     const int ID_NETWORKINFO = 0;
     321                 :             :     const int ID_BLOCKCHAININFO = 1;
     322                 :             :     const int ID_WALLETINFO = 2;
     323                 :             :     const int ID_BALANCES = 3;
     324                 :             : 
     325                 :             :     /** Create a simulated `getinfo` request. */
     326                 :          21 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     327                 :             :     {
     328         [ +  + ]:          21 :         if (!args.empty()) {
     329         [ +  - ]:           2 :             throw std::runtime_error("-getinfo takes no arguments");
     330                 :             :         }
     331                 :          19 :         UniValue result(UniValue::VARR);
     332   [ +  -  +  -  :          19 :         result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
             +  -  +  - ]
     333   [ +  -  +  -  :          19 :         result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO));
             +  -  +  - ]
     334   [ +  -  +  -  :          19 :         result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO));
             +  -  +  - ]
     335   [ +  -  +  -  :          19 :         result.push_back(JSONRPCRequestObj("getbalances", NullUniValue, ID_BALANCES));
             +  -  +  - ]
     336                 :          19 :         return result;
     337                 :           0 :     }
     338                 :             : 
     339                 :             :     /** Collect values from the batch and form a simulated `getinfo` reply. */
     340                 :          19 :     UniValue ProcessReply(const UniValue &batch_in) override
     341                 :             :     {
     342                 :          19 :         UniValue result(UniValue::VOBJ);
     343         [ +  - ]:          19 :         const std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in);
     344                 :             :         // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on;
     345                 :             :         // getwalletinfo() and getbalances() are allowed to fail if there is no wallet.
     346   [ +  -  +  -  :          19 :         if (!batch[ID_NETWORKINFO]["error"].isNull()) {
                   -  + ]
     347         [ #  # ]:           0 :             return batch[ID_NETWORKINFO];
     348                 :             :         }
     349   [ +  -  +  -  :          19 :         if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) {
                   -  + ]
     350         [ #  # ]:           0 :             return batch[ID_BLOCKCHAININFO];
     351                 :             :         }
     352   [ +  -  +  -  :          38 :         result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     353   [ +  -  +  -  :          38 :         result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     354   [ +  -  +  -  :          38 :         result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     355   [ +  -  +  -  :          38 :         result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     356   [ +  -  +  -  :          38 :         result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     357                 :             : 
     358                 :          19 :         UniValue connections(UniValue::VOBJ);
     359   [ +  -  +  -  :          38 :         connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     360   [ +  -  +  -  :          38 :         connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     361   [ +  -  +  -  :          38 :         connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     362   [ +  -  +  - ]:          38 :         result.pushKV("connections", std::move(connections));
     363                 :             : 
     364   [ +  -  +  -  :          38 :         result.pushKV("networks", batch[ID_NETWORKINFO]["result"]["networks"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     365   [ +  -  +  -  :          38 :         result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     366   [ +  -  +  -  :          38 :         result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     367   [ +  -  +  -  :          19 :         if (!batch[ID_WALLETINFO]["result"].isNull()) {
                   +  + ]
     368   [ +  -  +  -  :          14 :             result.pushKV("has_wallet", true);
                   +  - ]
     369   [ +  -  +  -  :          14 :             result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     370   [ +  -  +  -  :          14 :             result.pushKV("walletname", batch[ID_WALLETINFO]["result"]["walletname"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     371   [ +  -  +  -  :           7 :             if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
          +  -  +  -  +  
                      + ]
     372   [ +  -  +  -  :          12 :                 result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     373                 :             :             }
     374   [ +  -  +  -  :          14 :             result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     375                 :             :         }
     376   [ +  -  +  -  :          19 :         if (!batch[ID_BALANCES]["result"].isNull()) {
                   +  + ]
     377   [ +  -  +  -  :          14 :             result.pushKV("balance", batch[ID_BALANCES]["result"]["mine"]["trusted"]);
          +  -  +  -  +  
          -  +  -  +  -  
             +  -  +  - ]
     378                 :             :         }
     379   [ +  -  +  -  :          38 :         result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     380   [ +  -  +  -  :          38 :         result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     381   [ +  -  +  -  :          19 :         return JSONRPCReplyObj(std::move(result), NullUniValue,  /*id=*/1, JSONRPCVersion::V2);
                   +  - ]
     382                 :          19 :     }
     383                 :             : };
     384                 :             : 
     385                 :             : /** Process netinfo requests */
     386                 :             : class NetinfoRequestHandler : public BaseRequestHandler
     387                 :             : {
     388                 :             : private:
     389                 :             :     std::array<std::array<uint16_t, NETWORKS.size() + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total)
     390                 :             :     uint8_t m_block_relay_peers_count{0};
     391                 :             :     uint8_t m_manual_peers_count{0};
     392                 :           0 :     int8_t NetworkStringToId(const std::string& str) const
     393                 :             :     {
     394         [ #  # ]:           0 :         for (size_t i = 0; i < NETWORKS.size(); ++i) {
     395         [ #  # ]:           0 :             if (str == NETWORKS[i]) return i;
     396                 :             :         }
     397                 :             :         return UNKNOWN_NETWORK;
     398                 :             :     }
     399                 :             :     uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
     400                 :           0 :     bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; }
     401                 :           0 :     bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
     402                 :           0 :     bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
     403                 :             :     bool m_outbound_only_selected{false};
     404                 :             :     bool m_is_asmap_on{false};
     405                 :             :     size_t m_max_addr_length{0};
     406                 :             :     size_t m_max_addr_processed_length{5};
     407                 :             :     size_t m_max_addr_rate_limited_length{6};
     408                 :             :     size_t m_max_age_length{5};
     409                 :             :     size_t m_max_id_length{2};
     410                 :             :     size_t m_max_services_length{6};
     411                 :             :     struct Peer {
     412                 :             :         std::string addr;
     413                 :             :         std::string sub_version;
     414                 :             :         std::string conn_type;
     415                 :             :         std::string network;
     416                 :             :         std::string age;
     417                 :             :         std::string services;
     418                 :             :         std::string transport_protocol_type;
     419                 :             :         double min_ping;
     420                 :             :         double ping;
     421                 :             :         int64_t addr_processed;
     422                 :             :         int64_t addr_rate_limited;
     423                 :             :         int64_t last_blck;
     424                 :             :         int64_t last_recv;
     425                 :             :         int64_t last_send;
     426                 :             :         int64_t last_trxn;
     427                 :             :         int id;
     428                 :             :         int mapped_as;
     429                 :             :         int version;
     430                 :             :         bool is_addr_relay_enabled;
     431                 :             :         bool is_bip152_hb_from;
     432                 :             :         bool is_bip152_hb_to;
     433                 :             :         bool is_outbound;
     434                 :             :         bool is_tx_relay;
     435                 :           0 :         bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
     436                 :             :     };
     437                 :             :     std::vector<Peer> m_peers;
     438                 :           0 :     std::string ChainToString() const
     439                 :             :     {
     440   [ #  #  #  #  :           0 :         switch (gArgs.GetChainType()) {
                   #  # ]
     441                 :           0 :         case ChainType::TESTNET4:
     442                 :           0 :             return " testnet4";
     443                 :           0 :         case ChainType::TESTNET:
     444                 :           0 :             return " testnet";
     445                 :           0 :         case ChainType::SIGNET:
     446                 :           0 :             return " signet";
     447                 :           0 :         case ChainType::REGTEST:
     448                 :           0 :             return " regtest";
     449                 :           0 :         case ChainType::MAIN:
     450                 :           0 :             return "";
     451                 :             :         }
     452                 :           0 :         assert(false);
     453                 :             :     }
     454                 :           0 :     std::string PingTimeToString(double seconds) const
     455                 :             :     {
     456         [ #  # ]:           0 :         if (seconds < 0) return "";
     457                 :           0 :         const double milliseconds{round(1000 * seconds)};
     458         [ #  # ]:           0 :         return milliseconds > 999999 ? "-" : ToString(milliseconds);
     459                 :             :     }
     460                 :           0 :     std::string ConnectionTypeForNetinfo(const std::string& conn_type) const
     461                 :             :     {
     462         [ #  # ]:           0 :         if (conn_type == "outbound-full-relay") return "full";
     463         [ #  # ]:           0 :         if (conn_type == "block-relay-only") return "block";
     464   [ #  #  #  # ]:           0 :         if (conn_type == "manual" || conn_type == "feeler") return conn_type;
     465         [ #  # ]:           0 :         if (conn_type == "addr-fetch") return "addr";
     466                 :           0 :         return "";
     467                 :             :     }
     468                 :           0 :     std::string FormatServices(const UniValue& services)
     469                 :             :     {
     470                 :           0 :         std::string str;
     471         [ #  # ]:           0 :         for (size_t i = 0; i < services.size(); ++i) {
     472   [ #  #  #  #  :           0 :             const std::string s{services[i].get_str()};
                   #  # ]
     473   [ #  #  #  #  :           0 :             str += s == "NETWORK_LIMITED" ? 'l' : s == "P2P_V2" ? '2' : ToLower(s[0]);
                   #  # ]
     474                 :           0 :         }
     475                 :           0 :         return str;
     476                 :           0 :     }
     477                 :             : 
     478                 :             : public:
     479                 :             :     static constexpr int ID_PEERINFO = 0;
     480                 :             :     static constexpr int ID_NETWORKINFO = 1;
     481                 :             : 
     482                 :           0 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     483                 :             :     {
     484         [ #  # ]:           0 :         if (!args.empty()) {
     485                 :           0 :             uint8_t n{0};
     486         [ #  # ]:           0 :             if (ParseUInt8(args.at(0), &n)) {
     487         [ #  # ]:           0 :                 m_details_level = std::min(n, NETINFO_MAX_LEVEL);
     488                 :             :             } else {
     489   [ #  #  #  #  :           0 :                 throw std::runtime_error(strprintf("invalid -netinfo level argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));
                   #  # ]
     490                 :             :             }
     491         [ #  # ]:           0 :             if (args.size() > 1) {
     492   [ #  #  #  #  :           0 :                 if (std::string_view s{args.at(1)}; n && (s == "o" || s == "outonly")) {
                   #  # ]
     493                 :           0 :                     m_outbound_only_selected = true;
     494         [ #  # ]:           0 :                 } else if (n) {
     495   [ #  #  #  # ]:           0 :                     throw std::runtime_error(strprintf("invalid -netinfo outonly argument: %s\nFor more information, run: bitcoin-cli -netinfo help", s));
     496                 :             :                 } else {
     497   [ #  #  #  # ]:           0 :                     throw std::runtime_error(strprintf("invalid -netinfo outonly argument: %s\nThe outonly argument is only valid for a level greater than 0 (the first argument). For more information, run: bitcoin-cli -netinfo help", s));
     498                 :             :                 }
     499                 :             :             }
     500                 :             :         }
     501                 :           0 :         UniValue result(UniValue::VARR);
     502   [ #  #  #  #  :           0 :         result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
             #  #  #  # ]
     503   [ #  #  #  #  :           0 :         result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
             #  #  #  # ]
     504                 :           0 :         return result;
     505                 :           0 :     }
     506                 :             : 
     507                 :           0 :     UniValue ProcessReply(const UniValue& batch_in) override
     508                 :             :     {
     509                 :           0 :         const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
     510   [ #  #  #  #  :           0 :         if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
             #  #  #  # ]
     511   [ #  #  #  #  :           0 :         if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
             #  #  #  # ]
     512                 :             : 
     513   [ #  #  #  # ]:           0 :         const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
     514   [ #  #  #  #  :           0 :         if (networkinfo["version"].getInt<int>() < 209900) {
             #  #  #  # ]
     515         [ #  # ]:           0 :             throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
     516                 :             :         }
     517                 :           0 :         const int64_t time_now{TicksSinceEpoch<std::chrono::seconds>(CliClock::now())};
     518                 :             : 
     519                 :             :         // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
     520   [ #  #  #  #  :           0 :         for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
             #  #  #  # ]
     521   [ #  #  #  #  :           0 :             const std::string network{peer["network"].get_str()};
             #  #  #  # ]
     522                 :           0 :             const int8_t network_id{NetworkStringToId(network)};
     523         [ #  # ]:           0 :             if (network_id == UNKNOWN_NETWORK) continue;
     524   [ #  #  #  #  :           0 :             const bool is_outbound{!peer["inbound"].get_bool()};
                   #  # ]
     525   [ #  #  #  #  :           0 :             const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     526   [ #  #  #  #  :           0 :             const std::string conn_type{peer["connection_type"].get_str()};
             #  #  #  # ]
     527         [ #  # ]:           0 :             ++m_counts.at(is_outbound).at(network_id);      // in/out by network
     528         [ #  # ]:           0 :             ++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
     529                 :           0 :             ++m_counts.at(2).at(network_id);                // total by network
     530                 :           0 :             ++m_counts.at(2).at(NETWORKS.size());           // total overall
     531         [ #  # ]:           0 :             if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
     532         [ #  # ]:           0 :             if (conn_type == "manual") ++m_manual_peers_count;
     533   [ #  #  #  # ]:           0 :             if (m_outbound_only_selected && !is_outbound) continue;
     534         [ #  # ]:           0 :             if (DetailsRequested()) {
     535                 :             :                 // Push data for this peer to the peers vector.
     536   [ #  #  #  #  :           0 :                 const int peer_id{peer["id"].getInt<int>()};
                   #  # ]
     537   [ #  #  #  #  :           0 :                 const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     538   [ #  #  #  #  :           0 :                 const int version{peer["version"].getInt<int>()};
                   #  # ]
     539   [ #  #  #  #  :           0 :                 const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     540   [ #  #  #  #  :           0 :                 const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     541   [ #  #  #  #  :           0 :                 const int64_t conn_time{peer["conntime"].getInt<int64_t>()};
                   #  # ]
     542   [ #  #  #  #  :           0 :                 const int64_t last_blck{peer["last_block"].getInt<int64_t>()};
                   #  # ]
     543   [ #  #  #  #  :           0 :                 const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()};
                   #  # ]
     544   [ #  #  #  #  :           0 :                 const int64_t last_send{peer["lastsend"].getInt<int64_t>()};
                   #  # ]
     545   [ #  #  #  #  :           0 :                 const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()};
                   #  # ]
     546   [ #  #  #  #  :           0 :                 const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     547   [ #  #  #  #  :           0 :                 const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     548   [ #  #  #  #  :           0 :                 const std::string addr{peer["addr"].get_str()};
             #  #  #  # ]
     549   [ #  #  #  #  :           0 :                 const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)};
                   #  # ]
     550   [ #  #  #  #  :           0 :                 const std::string services{FormatServices(peer["servicesnames"])};
                   #  # ]
     551   [ #  #  #  #  :           0 :                 const std::string sub_version{peer["subver"].get_str()};
             #  #  #  # ]
     552   [ #  #  #  #  :           0 :                 const std::string transport{peer["transport_protocol_type"].isNull() ? "v1" : peer["transport_protocol_type"].get_str()};
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     553   [ #  #  #  #  :           0 :                 const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     554   [ #  #  #  #  :           0 :                 const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
                   #  # ]
     555   [ #  #  #  #  :           0 :                 const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
                   #  # ]
     556   [ #  #  #  # ]:           0 :                 m_peers.push_back({addr, sub_version, conn_type, NETWORK_SHORT_NAMES[network_id], age, services, transport, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_outbound, is_tx_relay});
     557         [ #  # ]:           0 :                 m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
     558   [ #  #  #  # ]:           0 :                 m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
     559   [ #  #  #  # ]:           0 :                 m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
     560         [ #  # ]:           0 :                 m_max_age_length = std::max(age.length(), m_max_age_length);
     561   [ #  #  #  # ]:           0 :                 m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
     562         [ #  # ]:           0 :                 m_max_services_length = std::max(services.length(), m_max_services_length);
     563                 :           0 :                 m_is_asmap_on |= (mapped_as != 0);
     564                 :           0 :             }
     565                 :           0 :         }
     566                 :             : 
     567                 :             :         // Generate report header.
     568   [ #  #  #  #  :           0 :         std::string result{strprintf("%s client %s%s - server %i%s\n\n", CLIENT_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt<int>(), networkinfo["subversion"].get_str())};
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     569                 :             : 
     570                 :             :         // Report detailed peer connections list sorted by direction and minimum ping time.
     571   [ #  #  #  # ]:           0 :         if (DetailsRequested() && !m_peers.empty()) {
     572                 :           0 :             std::sort(m_peers.begin(), m_peers.end());
     573                 :           0 :             result += strprintf("<->   type   net %*s  v  mping   ping send recv  txn  blk  hb %*s%*s%*s ",
     574                 :           0 :                                 m_max_services_length, "serv",
     575                 :           0 :                                 m_max_addr_processed_length, "addrp",
     576                 :           0 :                                 m_max_addr_rate_limited_length, "addrl",
     577         [ #  # ]:           0 :                                 m_max_age_length, "age");
     578   [ #  #  #  # ]:           0 :             if (m_is_asmap_on) result += " asmap ";
     579   [ #  #  #  #  :           0 :             result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
             #  #  #  # ]
     580         [ #  # ]:           0 :             for (const Peer& peer : m_peers) {
     581   [ #  #  #  # ]:           0 :                 std::string version{ToString(peer.version) + peer.sub_version};
     582                 :           0 :                 result += strprintf(
     583                 :             :                     "%3s %6s %5s %*s %2s%7s%7s%5s%5s%5s%5s  %2s %*s%*s%*s%*i %*s %-*s%s\n",
     584   [ #  #  #  # ]:           0 :                     peer.is_outbound ? "out" : "in",
     585                 :           0 :                     ConnectionTypeForNetinfo(peer.conn_type),
     586                 :           0 :                     peer.network,
     587                 :             :                     m_max_services_length, // variable spacing
     588                 :           0 :                     peer.services,
     589   [ #  #  #  #  :           0 :                     (peer.transport_protocol_type.size() == 2 && peer.transport_protocol_type[0] == 'v') ? peer.transport_protocol_type[1] : ' ',
                   #  # ]
     590         [ #  # ]:           0 :                     PingTimeToString(peer.min_ping),
     591         [ #  # ]:           0 :                     PingTimeToString(peer.ping),
     592   [ #  #  #  #  :           0 :                     peer.last_send ? ToString(time_now - peer.last_send) : "",
                   #  # ]
     593   [ #  #  #  #  :           0 :                     peer.last_recv ? ToString(time_now - peer.last_recv) : "",
                   #  # ]
     594   [ #  #  #  #  :           0 :                     peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
             #  #  #  # ]
     595   [ #  #  #  #  :           0 :                     peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
                   #  # ]
     596   [ #  #  #  #  :           0 :                     strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
                   #  # ]
     597                 :             :                     m_max_addr_processed_length, // variable spacing
     598   [ #  #  #  #  :           0 :                     peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
             #  #  #  # ]
     599                 :             :                     m_max_addr_rate_limited_length, // variable spacing
     600   [ #  #  #  # ]:           0 :                     peer.addr_rate_limited ? ToString(peer.addr_rate_limited) : "",
     601                 :             :                     m_max_age_length, // variable spacing
     602                 :           0 :                     peer.age,
     603   [ #  #  #  # ]:           0 :                     m_is_asmap_on ? 7 : 0, // variable spacing
     604   [ #  #  #  #  :           0 :                     m_is_asmap_on && peer.mapped_as ? ToString(peer.mapped_as) : "",
                   #  # ]
     605                 :             :                     m_max_id_length, // variable spacing
     606                 :           0 :                     peer.id,
     607   [ #  #  #  # ]:           0 :                     IsAddressSelected() ? m_max_addr_length : 0, // variable spacing
     608   [ #  #  #  #  :           0 :                     IsAddressSelected() ? peer.addr : "",
                   #  # ]
     609   [ #  #  #  #  :           0 :                     IsVersionSelected() && version != "0" ? version : "");
             #  #  #  # ]
     610                 :           0 :             }
     611         [ #  # ]:           0 :             result += strprintf("                %*s         ms     ms  sec  sec  min  min                %*s\n\n", m_max_services_length, "", m_max_age_length, "min");
     612                 :             :         }
     613                 :             : 
     614                 :             :         // Report peer connection totals by type.
     615         [ #  # ]:           0 :         result += "     ";
     616                 :           0 :         std::vector<int8_t> reachable_networks;
     617   [ #  #  #  #  :           0 :         for (const UniValue& network : networkinfo["networks"].getValues()) {
             #  #  #  # ]
     618   [ #  #  #  #  :           0 :             if (network["reachable"].get_bool()) {
             #  #  #  # ]
     619   [ #  #  #  #  :           0 :                 const std::string& network_name{network["name"].get_str()};
                   #  # ]
     620                 :           0 :                 const int8_t network_id{NetworkStringToId(network_name)};
     621         [ #  # ]:           0 :                 if (network_id == UNKNOWN_NETWORK) continue;
     622         [ #  # ]:           0 :                 result += strprintf("%8s", network_name); // column header
     623         [ #  # ]:           0 :                 reachable_networks.push_back(network_id);
     624                 :             :             }
     625                 :             :         };
     626                 :             : 
     627         [ #  # ]:           0 :         for (const size_t network_id : UNREACHABLE_NETWORK_IDS) {
     628   [ #  #  #  # ]:           0 :             if (m_counts.at(2).at(network_id) == 0) continue;
     629   [ #  #  #  # ]:           0 :             result += strprintf("%8s", NETWORK_SHORT_NAMES.at(network_id)); // column header
     630         [ #  # ]:           0 :             reachable_networks.push_back(network_id);
     631                 :             :         }
     632                 :             : 
     633         [ #  # ]:           0 :         result += "   total   block";
     634   [ #  #  #  # ]:           0 :         if (m_manual_peers_count) result += "  manual";
     635                 :             : 
     636                 :           0 :         const std::array rows{"in", "out", "total"};
     637         [ #  # ]:           0 :         for (size_t i = 0; i < rows.size(); ++i) {
     638         [ #  # ]:           0 :             result += strprintf("\n%-5s", rows[i]); // row header
     639         [ #  # ]:           0 :             for (int8_t n : reachable_networks) {
     640   [ #  #  #  # ]:           0 :                 result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
     641                 :             :             }
     642         [ #  # ]:           0 :             result += strprintf("   %5i", m_counts.at(i).at(NETWORKS.size())); // total peers count
     643         [ #  # ]:           0 :             if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
     644         [ #  # ]:           0 :                 result += strprintf("   %5i", m_block_relay_peers_count);
     645   [ #  #  #  # ]:           0 :                 if (m_manual_peers_count) result += strprintf("   %5i", m_manual_peers_count);
     646                 :             :             }
     647                 :             :         }
     648                 :             : 
     649                 :             :         // Report local addresses, ports, and scores.
     650         [ #  # ]:           0 :         result += "\n\nLocal addresses";
     651   [ #  #  #  #  :           0 :         const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()};
                   #  # ]
     652         [ #  # ]:           0 :         if (local_addrs.empty()) {
     653         [ #  # ]:           0 :             result += ": n/a\n";
     654                 :             :         } else {
     655                 :           0 :             size_t max_addr_size{0};
     656         [ #  # ]:           0 :             for (const UniValue& addr : local_addrs) {
     657   [ #  #  #  #  :           0 :                 max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size);
             #  #  #  # ]
     658                 :             :             }
     659         [ #  # ]:           0 :             for (const UniValue& addr : local_addrs) {
     660   [ #  #  #  #  :           0 :                 result += strprintf("\n%-*s    port %6i    score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>());
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     661                 :             :             }
     662                 :             :         }
     663                 :             : 
     664   [ #  #  #  #  :           0 :         return JSONRPCReplyObj(UniValue{result}, NullUniValue, /*id=*/1, JSONRPCVersion::V2);
             #  #  #  # ]
     665   [ #  #  #  #  :           0 :     }
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     666                 :             : 
     667                 :             :     const std::string m_help_doc{
     668                 :             :         "-netinfo (level [outonly]) | help\n\n"
     669                 :             :         "Returns a network peer connections dashboard with information from the remote server.\n"
     670                 :             :         "This human-readable interface will change regularly and is not intended to be a stable API.\n"
     671                 :             :         "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
     672                 :             :         + strprintf("An optional argument from 0 to %d can be passed for different peers listings; values above %d up to 255 are parsed as %d.\n", NETINFO_MAX_LEVEL, NETINFO_MAX_LEVEL, NETINFO_MAX_LEVEL) +
     673                 :             :         "If that argument is passed, an optional additional \"outonly\" argument may be passed to obtain the listing with outbound peers only.\n"
     674                 :             :         "Pass \"help\" or \"h\" to see this detailed help documentation.\n"
     675                 :             :         "If more than two arguments are passed, only the first two are read and parsed.\n"
     676                 :             :         "Suggestion: use -netinfo with the Linux watch(1) command for a live dashboard; see example below.\n\n"
     677                 :             :         "Arguments:\n"
     678                 :             :         + strprintf("1. level (integer 0-%d, optional)  Specify the info level of the peers dashboard (default 0):\n", NETINFO_MAX_LEVEL) +
     679                 :             :         "                                  0 - Peer counts for each reachable network as well as for block relay peers\n"
     680                 :             :         "                                      and manual peers, and the list of local addresses and ports\n"
     681                 :             :         "                                  1 - Like 0 but preceded by a peers listing (without address and version columns)\n"
     682                 :             :         "                                  2 - Like 1 but with an address column\n"
     683                 :             :         "                                  3 - Like 1 but with a version column\n"
     684                 :             :         "                                  4 - Like 1 but with both address and version columns\n"
     685                 :             :         "2. outonly (\"outonly\" or \"o\", optional) Return the peers listing with outbound peers only, i.e. to save screen space\n"
     686                 :             :         "                                        when a node has many inbound peers. Only valid if a level is passed.\n\n"
     687                 :             :         "help (\"help\" or \"h\", optional) Print this help documentation instead of the dashboard.\n\n"
     688                 :             :         "Result:\n\n"
     689                 :             :         + strprintf("* The peers listing in levels 1-%d displays all of the peers sorted by direction and minimum ping time:\n\n", NETINFO_MAX_LEVEL) +
     690                 :             :         "  Column   Description\n"
     691                 :             :         "  ------   -----------\n"
     692                 :             :         "  <->      Direction\n"
     693                 :             :         "           \"in\"  - inbound connections are those initiated by the peer\n"
     694                 :             :         "           \"out\" - outbound connections are those initiated by us\n"
     695                 :             :         "  type     Type of peer connection\n"
     696                 :             :         "           \"full\"   - full relay, the default\n"
     697                 :             :         "           \"block\"  - block relay; like full relay but does not relay transactions or addresses\n"
     698                 :             :         "           \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
     699                 :             :         "           \"feeler\" - short-lived connection for testing addresses\n"
     700                 :             :         "           \"addr\"   - address fetch; short-lived connection for requesting addresses\n"
     701                 :             :         "  net      Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n"
     702                 :             :         "  serv     Services offered by the peer\n"
     703                 :             :         "           \"n\" - NETWORK: peer can serve the full block chain\n"
     704                 :             :         "           \"b\" - BLOOM: peer can handle bloom-filtered connections (see BIP 111)\n"
     705                 :             :         "           \"w\" - WITNESS: peer can be asked for blocks and transactions with witness data (SegWit)\n"
     706                 :             :         "           \"c\" - COMPACT_FILTERS: peer can handle basic block filter requests (see BIPs 157 and 158)\n"
     707                 :             :         "           \"l\" - NETWORK_LIMITED: peer limited to serving only the last 288 blocks (~2 days)\n"
     708                 :             :         "           \"2\" - P2P_V2: peer supports version 2 P2P transport protocol, as defined in BIP 324\n"
     709                 :             :         "           \"u\" - UNKNOWN: unrecognized bit flag\n"
     710                 :             :         "  v        Version of transport protocol used for the connection\n"
     711                 :             :         "  mping    Minimum observed ping time, in milliseconds (ms)\n"
     712                 :             :         "  ping     Last observed ping time, in milliseconds (ms)\n"
     713                 :             :         "  send     Time since last message sent to the peer, in seconds\n"
     714                 :             :         "  recv     Time since last message received from the peer, in seconds\n"
     715                 :             :         "  txn      Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
     716                 :             :         "           \"*\" - we do not relay transactions to this peer (getpeerinfo \"relaytxes\" is false)\n"
     717                 :             :         "  blk      Time since last novel block passing initial validity checks received from the peer, in minutes\n"
     718                 :             :         "  hb       High-bandwidth BIP152 compact block relay\n"
     719                 :             :         "           \".\" (to)   - we selected the peer as a high-bandwidth peer\n"
     720                 :             :         "           \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
     721                 :             :         "  addrp    Total number of addresses processed, excluding those dropped due to rate limiting\n"
     722                 :             :         "           \".\" - we do not relay addresses to this peer (getpeerinfo \"addr_relay_enabled\" is false)\n"
     723                 :             :         "  addrl    Total number of addresses dropped due to rate limiting\n"
     724                 :             :         "  age      Duration of connection to the peer, in minutes\n"
     725                 :             :         "  asmap    Mapped AS (Autonomous System) number at the end of the BGP route to the peer, used for diversifying\n"
     726                 :             :         "           peer selection (only displayed if the -asmap config option is set)\n"
     727                 :             :         "  id       Peer index, in increasing order of peer connections since node startup\n"
     728                 :             :         "  address  IP address and port of the peer\n"
     729                 :             :         "  version  Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
     730                 :             :         "* The peer counts table displays the number of peers for each reachable network as well as\n"
     731                 :             :         "  the number of block relay peers and manual peers.\n\n"
     732                 :             :         "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
     733                 :             :         "Examples:\n\n"
     734                 :             :         "Peer counts table of reachable networks and list of local addresses\n"
     735                 :             :         "> bitcoin-cli -netinfo\n\n"
     736                 :             :         "The same, preceded by a peers listing without address and version columns\n"
     737                 :             :         "> bitcoin-cli -netinfo 1\n\n"
     738                 :             :         "Full dashboard\n"
     739                 :             :         + strprintf("> bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) +
     740                 :             :         "Full dashboard, but with outbound peers only\n"
     741                 :             :         + strprintf("> bitcoin-cli -netinfo %d outonly\n\n", NETINFO_MAX_LEVEL) +
     742                 :             :         "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
     743                 :             :         + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) +
     744                 :             :         "See this help\n"
     745                 :             :         "> bitcoin-cli -netinfo help\n"};
     746                 :             : };
     747                 :             : 
     748                 :             : /** Process RPC generatetoaddress request. */
     749                 :           9 : class GenerateToAddressRequestHandler : public BaseRequestHandler
     750                 :             : {
     751                 :             : public:
     752                 :           9 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     753                 :             :     {
     754                 :           9 :         address_str = args.at(1);
     755         [ +  + ]:          11 :         UniValue params{RPCConvertValues("generatetoaddress", args)};
     756   [ +  -  +  -  :          21 :         return JSONRPCRequestObj("generatetoaddress", params, 1);
                   +  - ]
     757                 :           7 :     }
     758                 :             : 
     759                 :           7 :     UniValue ProcessReply(const UniValue &reply) override
     760                 :             :     {
     761                 :           7 :         UniValue result(UniValue::VOBJ);
     762   [ +  -  +  -  :          14 :         result.pushKV("address", address_str);
                   +  - ]
     763   [ +  -  +  -  :          14 :         result.pushKV("blocks", reply.get_obj()["result"]);
          +  -  +  -  +  
                -  +  - ]
     764   [ +  -  +  -  :          14 :         return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
                   +  - ]
     765                 :           7 :     }
     766                 :             : protected:
     767                 :             :     std::string address_str;
     768                 :             : };
     769                 :             : 
     770                 :             : /** Process default single requests */
     771                 :         760 : class DefaultRequestHandler: public BaseRequestHandler {
     772                 :             : public:
     773                 :         760 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     774                 :             :     {
     775         [ +  - ]:         760 :         UniValue params;
     776   [ +  -  +  -  :         760 :         if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
                   +  + ]
     777         [ +  - ]:         333 :             params = RPCConvertNamedValues(method, args);
     778                 :             :         } else {
     779         [ +  - ]:         427 :             params = RPCConvertValues(method, args);
     780                 :             :         }
     781   [ +  -  +  - ]:        1520 :         return JSONRPCRequestObj(method, params, 1);
     782                 :         760 :     }
     783                 :             : 
     784                 :         737 :     UniValue ProcessReply(const UniValue &reply) override
     785                 :             :     {
     786                 :         737 :         return reply.get_obj();
     787                 :             :     }
     788                 :             : };
     789                 :             : 
     790                 :         806 : static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
     791                 :             : {
     792         [ +  - ]:         806 :     std::string host;
     793                 :             :     // In preference order, we choose the following for the port:
     794                 :             :     //     1. -rpcport
     795                 :             :     //     2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
     796                 :             :     //     3. default port for chain
     797   [ +  -  +  - ]:         806 :     uint16_t port{BaseParams().RPCPort()};
     798                 :         806 :     {
     799                 :         806 :         uint16_t rpcconnect_port{0};
     800   [ +  -  +  -  :        1612 :         const std::string rpcconnect_str = gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
                   +  - ]
     801   [ +  -  +  + ]:         806 :         if (!SplitHostPort(rpcconnect_str, rpcconnect_port, host)) {
     802                 :             :             // Uses argument provided as-is
     803                 :             :             // (rather than value parsed)
     804                 :             :             // to aid the user in troubleshooting
     805   [ +  -  +  - ]:          16 :             throw std::runtime_error(strprintf("Invalid port provided in -rpcconnect: %s", rpcconnect_str));
     806                 :             :         } else {
     807         [ +  + ]:         798 :             if (rpcconnect_port != 0) {
     808                 :             :                 // Use the valid port provided in rpcconnect
     809                 :           6 :                 port = rpcconnect_port;
     810                 :             :             } // else, no port was provided in rpcconnect (continue using default one)
     811                 :             :         }
     812                 :             : 
     813   [ +  -  +  -  :        1596 :         if (std::optional<std::string> rpcport_arg = gArgs.GetArg("-rpcport")) {
                   +  + ]
     814                 :             :             // -rpcport was specified
     815         [ +  + ]:         796 :             const uint16_t rpcport_int{ToIntegral<uint16_t>(rpcport_arg.value()).value_or(0)};
     816         [ +  + ]:         790 :             if (rpcport_int == 0) {
     817                 :             :                 // Uses argument provided as-is
     818                 :             :                 // (rather than value parsed)
     819                 :             :                 // to aid the user in troubleshooting
     820   [ +  -  +  -  :          16 :                 throw std::runtime_error(strprintf("Invalid port provided in -rpcport: %s", rpcport_arg.value()));
                   +  - ]
     821                 :             :             }
     822                 :             : 
     823                 :             :             // Use the valid port provided
     824                 :         788 :             port = rpcport_int;
     825                 :             : 
     826                 :             :             // If there was a valid port provided in rpcconnect,
     827                 :             :             // rpcconnect_port is non-zero.
     828         [ +  + ]:         788 :             if (rpcconnect_port != 0) {
     829         [ +  - ]:           4 :                 tfm::format(std::cerr, "Warning: Port specified in both -rpcconnect and -rpcport. Using -rpcport %u\n", port);
     830                 :             :             }
     831                 :         798 :         }
     832                 :          16 :     }
     833                 :             : 
     834                 :             :     // Obtain event base
     835         [ +  - ]:         790 :     raii_event_base base = obtain_event_base();
     836                 :             : 
     837                 :             :     // Synchronously look up hostname
     838   [ +  -  +  - ]:         790 :     raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
     839                 :             : 
     840                 :             :     // Set connection timeout
     841                 :         790 :     {
     842   [ +  -  +  - ]:         790 :         const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
     843         [ +  - ]:         790 :         if (timeout > 0) {
     844         [ +  - ]:         790 :             evhttp_connection_set_timeout(evcon.get(), timeout);
     845                 :             :         } else {
     846                 :             :             // Indefinite request timeouts are not possible in libevent-http, so we
     847                 :             :             // set the timeout to a very long time period instead.
     848                 :             : 
     849                 :           0 :             constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
     850         [ #  # ]:           0 :             evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS);
     851                 :             :         }
     852                 :             :     }
     853                 :             : 
     854         [ +  - ]:         790 :     HTTPReply response;
     855         [ +  - ]:         790 :     raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
     856         [ -  + ]:         790 :     if (req == nullptr) {
     857         [ #  # ]:           0 :         throw std::runtime_error("create http request failed");
     858                 :             :     }
     859                 :             : 
     860         [ +  - ]:         790 :     evhttp_request_set_error_cb(req.get(), http_error_cb);
     861                 :             : 
     862                 :             :     // Get credentials
     863         [ +  - ]:         790 :     std::string strRPCUserColonPass;
     864                 :         790 :     bool failedToGetAuthCookie = false;
     865   [ +  -  +  -  :         790 :     if (gArgs.GetArg("-rpcpassword", "") == "") {
             +  -  +  + ]
     866                 :             :         // Try fall back to cookie-based authentication if no password is provided
     867   [ +  -  +  + ]:         780 :         if (!GetAuthCookie(&strRPCUserColonPass)) {
     868                 :          14 :             failedToGetAuthCookie = true;
     869                 :             :         }
     870                 :             :     } else {
     871   [ +  -  +  -  :          20 :         strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     872                 :             :     }
     873                 :             : 
     874         [ +  - ]:         790 :     struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
     875         [ -  + ]:         790 :     assert(output_headers);
     876         [ +  - ]:         790 :     evhttp_add_header(output_headers, "Host", host.c_str());
     877         [ +  - ]:         790 :     evhttp_add_header(output_headers, "Connection", "close");
     878         [ +  - ]:         790 :     evhttp_add_header(output_headers, "Content-Type", "application/json");
     879   [ +  -  +  -  :        1580 :     evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
             +  -  +  - ]
     880                 :             : 
     881                 :             :     // Attach request data
     882   [ +  +  +  - ]:        1576 :     std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
     883         [ +  - ]:         786 :     struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
     884         [ -  + ]:         786 :     assert(output_buffer);
     885         [ +  - ]:         786 :     evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
     886                 :             : 
     887                 :             :     // check if we should use a special wallet endpoint
     888         [ +  - ]:         786 :     std::string endpoint = "/";
     889         [ +  + ]:         786 :     if (rpcwallet) {
     890         [ +  - ]:         164 :         char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
     891         [ +  - ]:         164 :         if (encodedURI) {
     892   [ +  -  +  - ]:         164 :             endpoint = "/wallet/" + std::string(encodedURI);
     893                 :         164 :             free(encodedURI);
     894                 :             :         } else {
     895         [ #  # ]:           0 :             throw CConnectionFailed("uri-encode failed");
     896                 :             :         }
     897                 :             :     }
     898         [ +  - ]:         786 :     int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
     899         [ -  + ]:         786 :     req.release(); // ownership moved to evcon in above call
     900         [ -  + ]:         786 :     if (r != 0) {
     901         [ #  # ]:           0 :         throw CConnectionFailed("send http request failed");
     902                 :             :     }
     903                 :             : 
     904         [ +  - ]:         786 :     event_base_dispatch(base.get());
     905                 :             : 
     906         [ +  + ]:         786 :     if (response.status == 0) {
     907         [ -  + ]:          16 :         std::string responseErrorMessage;
     908         [ -  + ]:          16 :         if (response.error != -1) {
     909   [ #  #  #  # ]:           0 :             responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error));
     910                 :             :         }
     911         [ +  - ]:          16 :         throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\n"
     912                 :             :                     "Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n"
     913                 :             :                     "Use \"bitcoin-cli -help\" for more info.",
     914                 :          32 :                     host, port, responseErrorMessage));
     915         [ +  + ]:         786 :     } else if (response.status == HTTP_UNAUTHORIZED) {
     916         [ +  + ]:           6 :         if (failedToGetAuthCookie) {
     917                 :           2 :             throw std::runtime_error(strprintf(
     918                 :             :                 "Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set.  See -rpcpassword and -stdinrpcpass.  Configuration file: (%s)",
     919   [ +  -  +  -  :           8 :                 fs::PathToString(gArgs.GetConfigFilePath())));
                   +  - ]
     920                 :             :         } else {
     921         [ +  - ]:           4 :             throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
     922                 :             :         }
     923         [ +  + ]:         764 :     } else if (response.status == HTTP_SERVICE_UNAVAILABLE) {
     924   [ +  -  +  - ]:           2 :         throw std::runtime_error(strprintf("Server response: %s", response.body));
     925   [ -  +  -  -  :         763 :     } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
                   -  - ]
     926   [ #  #  #  # ]:           0 :         throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
     927         [ -  + ]:         763 :     else if (response.body.empty())
     928         [ #  # ]:           0 :         throw std::runtime_error("no response from server");
     929                 :             : 
     930                 :             :     // Parse reply
     931                 :         763 :     UniValue valReply(UniValue::VSTR);
     932   [ +  -  -  + ]:         763 :     if (!valReply.read(response.body))
     933         [ #  # ]:           0 :         throw std::runtime_error("couldn't parse reply from server");
     934         [ +  - ]:         763 :     UniValue reply = rh->ProcessReply(valReply);
     935         [ -  + ]:         763 :     if (reply.empty())
     936         [ #  # ]:           0 :         throw std::runtime_error("expected reply to have result, error and id properties");
     937                 :             : 
     938                 :        1526 :     return reply;
     939   [ +  -  +  - ]:        2470 : }
     940                 :             : 
     941                 :             : /**
     942                 :             :  * ConnectAndCallRPC wraps CallRPC with -rpcwait and an exception handler.
     943                 :             :  *
     944                 :             :  * @param[in] rh         Pointer to RequestHandler.
     945                 :             :  * @param[in] strMethod  Reference to const string method to forward to CallRPC.
     946                 :             :  * @param[in] rpcwallet  Reference to const optional string wallet name to forward to CallRPC.
     947                 :             :  * @returns the RPC response as a UniValue object.
     948                 :             :  * @throws a CConnectionFailed std::runtime_error if connection failed or RPC server still in warmup.
     949                 :             :  */
     950                 :         795 : static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
     951                 :             : {
     952                 :         795 :     UniValue response(UniValue::VOBJ);
     953                 :             :     // Execute and handle connection failures with -rpcwait.
     954   [ +  -  +  - ]:         795 :     const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
     955   [ +  -  +  - ]:         795 :     const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
     956                 :         795 :     const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
     957                 :             : 
     958                 :         806 :     do {
     959                 :         806 :         try {
     960         [ +  + ]:         806 :             response = CallRPC(rh, strMethod, args, rpcwallet);
     961         [ +  + ]:         763 :             if (fWait) {
     962         [ +  - ]:           3 :                 const UniValue& error = response.find_value("error");
     963   [ +  +  +  -  :           4 :                 if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
          +  -  +  -  +  
                -  +  + ]
     964         [ +  - ]:           2 :                     throw CConnectionFailed("server in warmup");
     965                 :             :                 }
     966                 :             :             }
     967                 :             :             break; // Connection succeeded, no need to retry.
     968         [ +  + ]:          44 :         } catch (const CConnectionFailed& e) {
     969   [ +  +  +  +  :          17 :             if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
                   +  + ]
     970         [ +  - ]:          11 :                 UninterruptibleSleep(1s);
     971                 :             :             } else {
     972         [ +  - ]:          12 :                 throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
     973                 :             :             }
     974                 :           6 :         }
     975                 :          11 :     } while (fWait);
     976                 :         762 :     return response;
     977                 :          33 : }
     978                 :             : 
     979                 :             : /** Parse UniValue result to update the message to print to std::cout. */
     980                 :         659 : static void ParseResult(const UniValue& result, std::string& strPrint)
     981                 :             : {
     982         [ +  + ]:         659 :     if (result.isNull()) return;
     983         [ +  + ]:        1270 :     strPrint = result.isStr() ? result.get_str() : result.write(2);
     984                 :             : }
     985                 :             : 
     986                 :             : /** Parse UniValue error to update the message to print to std::cerr and the code to return. */
     987                 :          70 : static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
     988                 :             : {
     989         [ +  - ]:          70 :     if (error.isObject()) {
     990                 :          70 :         const UniValue& err_code = error.find_value("code");
     991                 :          70 :         const UniValue& err_msg = error.find_value("message");
     992         [ +  - ]:          70 :         if (!err_code.isNull()) {
     993         [ +  - ]:         140 :             strPrint = "error code: " + err_code.getValStr() + "\n";
     994                 :             :         }
     995         [ +  - ]:          70 :         if (err_msg.isStr()) {
     996         [ +  - ]:         140 :             strPrint += ("error message:\n" + err_msg.get_str());
     997                 :             :         }
     998   [ +  -  +  + ]:          70 :         if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) {
     999                 :           6 :             strPrint += " Or for the CLI, specify the \"-rpcwallet=<walletname>\" option before the command";
    1000                 :           6 :             strPrint += " (run \"bitcoin-cli -h\" for help or \"bitcoin-cli listwallets\" to see which wallets are currently loaded).";
    1001                 :             :         }
    1002                 :             :     } else {
    1003         [ #  # ]:           0 :         strPrint = "error: " + error.write();
    1004                 :             :     }
    1005   [ +  -  +  - ]:          70 :     nRet = abs(error["code"].getInt<int>());
    1006                 :          70 : }
    1007                 :             : 
    1008                 :             : /**
    1009                 :             :  * GetWalletBalances calls listwallets; if more than one wallet is loaded, it then
    1010                 :             :  * fetches mine.trusted balances for each loaded wallet and pushes them to `result`.
    1011                 :             :  *
    1012                 :             :  * @param result  Reference to UniValue object the wallet names and balances are pushed to.
    1013                 :             :  */
    1014                 :          13 : static void GetWalletBalances(UniValue& result)
    1015                 :             : {
    1016                 :          13 :     DefaultRequestHandler rh;
    1017   [ +  -  +  - ]:          26 :     const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{});
    1018   [ +  -  +  - ]:          13 :     if (!listwallets.find_value("error").isNull()) return;
    1019         [ +  - ]:          13 :     const UniValue& wallets = listwallets.find_value("result");
    1020         [ +  + ]:          13 :     if (wallets.size() <= 1) return;
    1021                 :             : 
    1022                 :           2 :     UniValue balances(UniValue::VOBJ);
    1023   [ +  -  +  + ]:           7 :     for (const UniValue& wallet : wallets.getValues()) {
    1024         [ +  - ]:           5 :         const std::string& wallet_name = wallet.get_str();
    1025   [ +  -  +  -  :          10 :         const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
                   +  - ]
    1026   [ +  -  +  -  :           5 :         const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"];
          +  -  +  -  +  
                      - ]
    1027   [ +  -  +  -  :          10 :         balances.pushKV(wallet_name, balance);
                   +  - ]
    1028                 :           5 :     }
    1029   [ +  -  +  - ]:           4 :     result.pushKV("balances", std::move(balances));
    1030                 :          13 : }
    1031                 :             : 
    1032                 :             : /**
    1033                 :             :  * GetProgressBar constructs a progress bar with 5% intervals.
    1034                 :             :  *
    1035                 :             :  * @param[in]   progress      The proportion of the progress bar to be filled between 0 and 1.
    1036                 :             :  * @param[out]  progress_bar  String representation of the progress bar.
    1037                 :             :  */
    1038                 :           0 : static void GetProgressBar(double progress, std::string& progress_bar)
    1039                 :             : {
    1040   [ #  #  #  # ]:           0 :     if (progress < 0 || progress > 1) return;
    1041                 :             : 
    1042                 :           0 :     static constexpr double INCREMENT{0.05};
    1043   [ #  #  #  # ]:           0 :     static const std::string COMPLETE_BAR{"\u2592"};
    1044   [ #  #  #  # ]:           0 :     static const std::string INCOMPLETE_BAR{"\u2591"};
    1045                 :             : 
    1046         [ #  # ]:           0 :     for (int i = 0; i < progress / INCREMENT; ++i) {
    1047                 :           0 :         progress_bar += COMPLETE_BAR;
    1048                 :             :     }
    1049                 :             : 
    1050         [ #  # ]:           0 :     for (int i = 0; i < (1 - progress) / INCREMENT; ++i) {
    1051                 :           0 :         progress_bar += INCOMPLETE_BAR;
    1052                 :             :     }
    1053                 :             : }
    1054                 :             : 
    1055                 :             : /**
    1056                 :             :  * ParseGetInfoResult takes in -getinfo result in UniValue object and parses it
    1057                 :             :  * into a user friendly UniValue string to be printed on the console.
    1058                 :             :  * @param[out] result  Reference to UniValue result containing the -getinfo output.
    1059                 :             :  */
    1060                 :          19 : static void ParseGetInfoResult(UniValue& result)
    1061                 :             : {
    1062         [ +  - ]:          19 :     if (!result.find_value("error").isNull()) return;
    1063                 :             : 
    1064                 :          19 :     std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN;
    1065                 :          19 :     bool should_colorize = false;
    1066                 :             : 
    1067                 :             : #ifndef WIN32
    1068         [ -  + ]:          19 :     if (isatty(fileno(stdout))) {
    1069                 :             :         // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal.
    1070                 :           0 :         should_colorize = true;
    1071                 :             :     }
    1072                 :             : #endif
    1073                 :             : 
    1074   [ +  -  +  -  :          19 :     if (gArgs.IsArgSet("-color")) {
                   +  + ]
    1075   [ +  -  +  - ]:           6 :         const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)};
    1076         [ +  + ]:           6 :         if (color == "always") {
    1077                 :             :             should_colorize = true;
    1078         [ +  + ]:           4 :         } else if (color == "never") {
    1079                 :             :             should_colorize = false;
    1080         [ +  - ]:           2 :         } else if (color != "auto") {
    1081         [ +  - ]:           2 :             throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never.");
    1082                 :             :         }
    1083                 :           6 :     }
    1084                 :             : 
    1085         [ +  + ]:          17 :     if (should_colorize) {
    1086         [ +  - ]:           2 :         RESET = "\x1B[0m";
    1087         [ +  - ]:           2 :         GREEN = "\x1B[32m";
    1088         [ +  - ]:           2 :         BLUE = "\x1B[34m";
    1089         [ +  - ]:           2 :         YELLOW = "\x1B[33m";
    1090         [ +  - ]:           2 :         MAGENTA = "\x1B[35m";
    1091         [ +  - ]:           2 :         CYAN = "\x1B[36m";
    1092                 :             :     }
    1093                 :             : 
    1094   [ +  -  +  -  :          17 :     std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
                   +  - ]
    1095   [ +  -  +  -  :          34 :     result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
                   +  - ]
    1096   [ +  -  +  -  :          34 :     result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
                   +  - ]
    1097                 :             : 
    1098   [ +  -  +  -  :          17 :     const double ibd_progress{result["verificationprogress"].get_real()};
                   +  - ]
    1099         [ -  + ]:          17 :     std::string ibd_progress_bar;
    1100                 :             :     // Display the progress bar only if IBD progress is less than 99%
    1101         [ -  + ]:          17 :     if (ibd_progress < 0.99) {
    1102         [ #  # ]:           0 :       GetProgressBar(ibd_progress, ibd_progress_bar);
    1103                 :             :       // Add padding between progress bar and IBD progress
    1104         [ #  # ]:           0 :       ibd_progress_bar += " ";
    1105                 :             :     }
    1106                 :             : 
    1107         [ +  - ]:          34 :     result_string += strprintf("Verification progress: %s%.4f%%\n", ibd_progress_bar, ibd_progress * 100);
    1108   [ +  -  +  -  :          34 :     result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
                   +  - ]
    1109                 :             : 
    1110   [ +  -  +  - ]:          51 :     result_string += strprintf(
    1111                 :             :         "%sNetwork: in %s, out %s, total %s%s\n",
    1112                 :             :         GREEN,
    1113   [ +  -  +  -  :          34 :         result["connections"]["in"].getValStr(),
          +  -  +  -  +  
                      - ]
    1114   [ +  -  +  -  :          34 :         result["connections"]["out"].getValStr(),
          +  -  +  -  +  
                      - ]
    1115   [ +  -  +  -  :          34 :         result["connections"]["total"].getValStr(),
          +  -  +  -  +  
                      - ]
    1116                 :          17 :         RESET);
    1117   [ +  -  +  -  :          34 :     result_string += strprintf("Version: %s\n", result["version"].getValStr());
                   +  - ]
    1118   [ +  -  +  -  :          34 :     result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
                   +  - ]
    1119                 :             : 
    1120                 :             :     // proxies
    1121         [ +  - ]:          17 :     std::map<std::string, std::vector<std::string>> proxy_networks;
    1122                 :          17 :     std::vector<std::string> ordered_proxies;
    1123                 :             : 
    1124   [ +  -  +  -  :         102 :     for (const UniValue& network : result["networks"].getValues()) {
             +  -  +  + ]
    1125   [ +  -  +  -  :          85 :         const std::string proxy = network["proxy"].getValStr();
                   +  - ]
    1126         [ +  + ]:          85 :         if (proxy.empty()) continue;
    1127                 :             :         // Add proxy to ordered_proxy if has not been processed
    1128   [ +  +  +  - ]:          10 :         if (proxy_networks.find(proxy) == proxy_networks.end()) ordered_proxies.push_back(proxy);
    1129                 :             : 
    1130   [ +  -  +  -  :          20 :         proxy_networks[proxy].push_back(network["name"].getValStr());
             +  -  +  - ]
    1131                 :          85 :     }
    1132                 :             : 
    1133                 :          17 :     std::vector<std::string> formatted_proxies;
    1134         [ +  - ]:          17 :     formatted_proxies.reserve(ordered_proxies.size());
    1135         [ +  + ]:          21 :     for (const std::string& proxy : ordered_proxies) {
    1136   [ +  -  +  -  :           8 :         formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", ")));
                   +  - ]
    1137                 :             :     }
    1138   [ +  +  +  -  :          34 :     result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", "));
             +  -  +  - ]
    1139                 :             : 
    1140   [ +  -  +  -  :          34 :     result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
                   +  - ]
    1141                 :             : 
    1142   [ +  -  +  -  :          17 :     if (!result["has_wallet"].isNull()) {
                   +  + ]
    1143   [ +  -  +  -  :           7 :         const std::string walletname = result["walletname"].getValStr();
                   +  - ]
    1144   [ -  +  -  -  :          14 :         result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET);
             +  -  +  - ]
    1145                 :             : 
    1146   [ +  -  +  -  :          14 :         result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr());
                   +  - ]
    1147   [ +  -  +  -  :           7 :         if (!result["unlocked_until"].isNull()) {
                   +  + ]
    1148   [ +  -  +  -  :          12 :             result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr());
                   +  - ]
    1149                 :             :         }
    1150   [ +  -  +  -  :          14 :         result_string += strprintf("Transaction fee rate (-paytxfee) (%s/kvB): %s\n\n", CURRENCY_UNIT, result["paytxfee"].getValStr());
                   +  - ]
    1151                 :           7 :     }
    1152   [ +  -  +  -  :          17 :     if (!result["balance"].isNull()) {
                   +  + ]
    1153   [ +  -  +  -  :          14 :         result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr());
                   +  - ]
    1154                 :             :     }
    1155                 :             : 
    1156   [ +  -  +  -  :          17 :     if (!result["balances"].isNull()) {
                   +  + ]
    1157         [ +  - ]:           4 :         result_string += strprintf("%sBalances%s\n", CYAN, RESET);
    1158                 :             : 
    1159                 :           2 :         size_t max_balance_length{10};
    1160                 :             : 
    1161   [ +  -  +  -  :           7 :         for (const std::string& wallet : result["balances"].getKeys()) {
             +  -  +  + ]
    1162   [ +  -  +  -  :           9 :             max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length);
             +  -  +  + ]
    1163                 :             :         }
    1164                 :             : 
    1165   [ +  -  +  -  :           7 :         for (const std::string& wallet : result["balances"].getKeys()) {
             +  -  +  + ]
    1166   [ -  +  +  - ]:          15 :             result_string += strprintf("%*s %s\n",
    1167                 :             :                                        max_balance_length,
    1168   [ +  -  +  -  :          10 :                                        result["balances"][wallet].getValStr(),
             +  -  +  - ]
    1169   [ -  +  -  -  :          15 :                                        wallet.empty() ? "\"\"" : wallet);
                   +  - ]
    1170                 :             :         }
    1171         [ +  - ]:           2 :         result_string += "\n";
    1172                 :             :     }
    1173                 :             : 
    1174   [ +  -  +  -  :          17 :     const std::string warnings{result["warnings"].getValStr()};
                   +  - ]
    1175   [ +  -  +  -  :          34 :     result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings);
             -  -  +  - ]
    1176                 :             : 
    1177   [ +  -  +  - ]:          34 :     result.setStr(result_string);
    1178                 :          27 : }
    1179                 :             : 
    1180                 :             : /**
    1181                 :             :  * Call RPC getnewaddress.
    1182                 :             :  * @returns getnewaddress response as a UniValue object.
    1183                 :             :  */
    1184                 :          22 : static UniValue GetNewAddress()
    1185                 :             : {
    1186                 :          22 :     std::optional<std::string> wallet_name{};
    1187   [ +  -  +  -  :          22 :     if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
          +  +  +  -  +  
                -  +  - ]
    1188                 :          22 :     DefaultRequestHandler rh;
    1189   [ +  -  +  - ]:          44 :     return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name);
    1190                 :          22 : }
    1191                 :             : 
    1192                 :             : /**
    1193                 :             :  * Check bounds and set up args for RPC generatetoaddress params: nblocks, address, maxtries.
    1194                 :             :  * @param[in] address  Reference to const string address to insert into the args.
    1195                 :             :  * @param     args     Reference to vector of string args to modify.
    1196                 :             :  */
    1197                 :          13 : static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
    1198                 :             : {
    1199   [ +  +  +  - ]:          13 :     if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
    1200         [ +  + ]:          11 :     if (args.size() == 0) {
    1201                 :           3 :         args.emplace_back(DEFAULT_NBLOCKS);
    1202         [ +  + ]:           8 :     } else if (args.at(0) == "0") {
    1203   [ +  -  +  - ]:           6 :         throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
    1204                 :             :     }
    1205                 :           9 :     args.emplace(args.begin() + 1, address);
    1206                 :           9 : }
    1207                 :             : 
    1208                 :         770 : static int CommandLineRPC(int argc, char *argv[])
    1209                 :             : {
    1210                 :         770 :     std::string strPrint;
    1211                 :         770 :     int nRet = 0;
    1212                 :         770 :     try {
    1213                 :             :         // Skip switches
    1214   [ +  +  +  + ]:        2155 :         while (argc > 1 && IsSwitchChar(argv[1][0])) {
    1215                 :        1385 :             argc--;
    1216                 :        1385 :             argv++;
    1217                 :             :         }
    1218         [ +  - ]:         770 :         std::string rpcPass;
    1219   [ +  -  +  -  :         770 :         if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
                   +  + ]
    1220         [ +  - ]:           8 :             NO_STDIN_ECHO();
    1221   [ +  -  -  + ]:           8 :             if (!StdinReady()) {
    1222         [ #  # ]:           0 :                 fputs("RPC password> ", stderr);
    1223         [ #  # ]:           0 :                 fflush(stderr);
    1224                 :             :             }
    1225   [ +  -  -  + ]:           8 :             if (!std::getline(std::cin, rpcPass)) {
    1226         [ #  # ]:           0 :                 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
    1227                 :             :             }
    1228   [ +  -  -  + ]:           8 :             if (StdinTerminal()) {
    1229         [ #  # ]:           0 :                 fputc('\n', stdout);
    1230                 :             :             }
    1231   [ +  -  +  - ]:           8 :             gArgs.ForceSetArg("-rpcpassword", rpcPass);
    1232                 :           8 :         }
    1233         [ +  - ]:         770 :         std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
    1234   [ +  -  +  -  :         770 :         if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
                   -  + ]
    1235         [ #  # ]:           0 :             NO_STDIN_ECHO();
    1236         [ #  # ]:           0 :             std::string walletPass;
    1237   [ #  #  #  #  :           0 :             if (args.size() < 1 || args[0].substr(0, 16) != "walletpassphrase") {
             #  #  #  # ]
    1238         [ #  # ]:           0 :                 throw std::runtime_error("-stdinwalletpassphrase is only applicable for walletpassphrase(change)");
    1239                 :             :             }
    1240   [ #  #  #  # ]:           0 :             if (!StdinReady()) {
    1241         [ #  # ]:           0 :                 fputs("Wallet passphrase> ", stderr);
    1242         [ #  # ]:           0 :                 fflush(stderr);
    1243                 :             :             }
    1244   [ #  #  #  # ]:           0 :             if (!std::getline(std::cin, walletPass)) {
    1245         [ #  # ]:           0 :                 throw std::runtime_error("-stdinwalletpassphrase specified but failed to read from standard input");
    1246                 :             :             }
    1247   [ #  #  #  # ]:           0 :             if (StdinTerminal()) {
    1248         [ #  # ]:           0 :                 fputc('\n', stdout);
    1249                 :             :             }
    1250         [ #  # ]:           0 :             args.insert(args.begin() + 1, walletPass);
    1251                 :           0 :         }
    1252   [ +  -  +  -  :         770 :         if (gArgs.GetBoolArg("-stdin", false)) {
                   +  + ]
    1253                 :             :             // Read one arg per line from stdin and append
    1254                 :           6 :             std::string line;
    1255   [ +  -  +  + ]:          14 :             while (std::getline(std::cin, line)) {
    1256         [ +  - ]:           8 :                 args.push_back(line);
    1257                 :             :             }
    1258   [ +  -  -  + ]:           6 :             if (StdinTerminal()) {
    1259         [ #  # ]:           0 :                 fputc('\n', stdout);
    1260                 :             :             }
    1261                 :           6 :         }
    1262         [ +  + ]:         770 :         gArgs.CheckMultipleCLIArgs();
    1263                 :         768 :         std::unique_ptr<BaseRequestHandler> rh;
    1264         [ +  - ]:         768 :         std::string method;
    1265   [ +  -  +  -  :         768 :         if (gArgs.IsArgSet("-getinfo")) {
                   +  + ]
    1266   [ +  -  -  + ]:          21 :             rh.reset(new GetinfoRequestHandler());
    1267   [ +  -  +  -  :         747 :         } else if (gArgs.GetBoolArg("-netinfo", false)) {
                   -  + ]
    1268   [ #  #  #  #  :           0 :             if (!args.empty() && (args.at(0) == "h" || args.at(0) == "help")) {
                   #  # ]
    1269   [ #  #  #  # ]:           0 :                 tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc);
    1270                 :           0 :                 return 0;
    1271                 :             :             }
    1272   [ #  #  #  #  :           0 :             rh.reset(new NetinfoRequestHandler());
                   #  # ]
    1273   [ +  -  +  -  :         747 :         } else if (gArgs.GetBoolArg("-generate", false)) {
                   +  + ]
    1274         [ +  - ]:          22 :             const UniValue getnewaddress{GetNewAddress()};
    1275         [ +  - ]:          22 :             const UniValue& error{getnewaddress.find_value("error")};
    1276         [ +  + ]:          22 :             if (error.isNull()) {
    1277   [ +  -  +  -  :          13 :                 SetGenerateToAddressArgs(getnewaddress.find_value("result").get_str(), args);
                   +  + ]
    1278   [ +  -  -  + ]:           9 :                 rh.reset(new GenerateToAddressRequestHandler());
    1279                 :             :             } else {
    1280         [ +  - ]:           9 :                 ParseError(error, strPrint, nRet);
    1281                 :             :             }
    1282   [ +  -  +  -  :         747 :         } else if (gArgs.GetBoolArg("-addrinfo", false)) {
                   -  + ]
    1283   [ #  #  #  # ]:           0 :             rh.reset(new AddrinfoRequestHandler());
    1284                 :             :         } else {
    1285   [ +  -  -  + ]:         725 :             rh.reset(new DefaultRequestHandler());
    1286         [ -  + ]:         725 :             if (args.size() < 1) {
    1287         [ #  # ]:           0 :                 throw std::runtime_error("too few parameters (need at least command)");
    1288                 :             :             }
    1289         [ +  - ]:         725 :             method = args[0];
    1290                 :         725 :             args.erase(args.begin()); // Remove trailing method name from arguments vector
    1291                 :             :         }
    1292         [ +  + ]:         764 :         if (nRet == 0) {
    1293                 :             :             // Perform RPC call
    1294                 :         755 :             std::optional<std::string> wallet_name{};
    1295   [ +  -  +  -  :         755 :             if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
          +  +  +  -  +  
                -  +  - ]
    1296         [ +  + ]:         755 :             const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
    1297                 :             : 
    1298                 :             :             // Parse reply
    1299   [ +  -  +  - ]:         722 :             UniValue result = reply.find_value("result");
    1300         [ +  - ]:         722 :             const UniValue& error = reply.find_value("error");
    1301         [ +  + ]:         722 :             if (error.isNull()) {
    1302   [ +  -  +  -  :         661 :                 if (gArgs.GetBoolArg("-getinfo", false)) {
                   +  + ]
    1303   [ +  -  +  -  :          19 :                     if (!gArgs.IsArgSet("-rpcwallet")) {
                   +  + ]
    1304         [ +  - ]:          13 :                         GetWalletBalances(result); // fetch multiwallet balances and append to result
    1305                 :             :                     }
    1306         [ +  + ]:          19 :                     ParseGetInfoResult(result);
    1307                 :             :                 }
    1308                 :             : 
    1309         [ +  - ]:         659 :                 ParseResult(result, strPrint);
    1310                 :             :             } else {
    1311         [ +  - ]:          61 :                 ParseError(error, strPrint, nRet);
    1312                 :             :             }
    1313                 :         759 :         }
    1314         [ +  - ]:         850 :     } catch (const std::exception& e) {
    1315         [ +  - ]:          82 :         strPrint = std::string("error: ") + e.what();
    1316                 :          41 :         nRet = EXIT_FAILURE;
    1317                 :          41 :     } catch (...) {
    1318         [ -  - ]:           0 :         PrintExceptionContinue(nullptr, "CommandLineRPC()");
    1319                 :           0 :         throw;
    1320                 :           0 :     }
    1321                 :             : 
    1322         [ +  + ]:         770 :     if (strPrint != "") {
    1323   [ +  +  +  - ]:         857 :         tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
    1324                 :             :     }
    1325                 :         770 :     return nRet;
    1326                 :         770 : }
    1327                 :             : 
    1328                 :         772 : MAIN_FUNCTION
    1329                 :             : {
    1330                 :             : #ifdef WIN32
    1331                 :             :     common::WinCmdLineArgs winArgs;
    1332                 :             :     std::tie(argc, argv) = winArgs.get();
    1333                 :             : #endif
    1334                 :         772 :     SetupEnvironment();
    1335         [ -  + ]:         772 :     if (!SetupNetworking()) {
    1336                 :           0 :         tfm::format(std::cerr, "Error: Initializing networking failed\n");
    1337                 :           0 :         return EXIT_FAILURE;
    1338                 :             :     }
    1339                 :         772 :     event_set_log_callback(&libevent_log_cb);
    1340                 :             : 
    1341                 :         772 :     try {
    1342         [ +  - ]:         772 :         int ret = AppInitRPC(argc, argv);
    1343         [ +  + ]:         772 :         if (ret != CONTINUE_EXECUTION)
    1344                 :             :             return ret;
    1345                 :             :     }
    1346         [ -  - ]:           0 :     catch (const std::exception& e) {
    1347         [ -  - ]:           0 :         PrintExceptionContinue(&e, "AppInitRPC()");
    1348                 :           0 :         return EXIT_FAILURE;
    1349                 :           0 :     } catch (...) {
    1350         [ -  - ]:           0 :         PrintExceptionContinue(nullptr, "AppInitRPC()");
    1351                 :           0 :         return EXIT_FAILURE;
    1352                 :           0 :     }
    1353                 :             : 
    1354                 :         770 :     int ret = EXIT_FAILURE;
    1355                 :         770 :     try {
    1356         [ +  - ]:         770 :         ret = CommandLineRPC(argc, argv);
    1357                 :             :     }
    1358         [ -  - ]:           0 :     catch (const std::exception& e) {
    1359         [ -  - ]:           0 :         PrintExceptionContinue(&e, "CommandLineRPC()");
    1360                 :           0 :     } catch (...) {
    1361         [ -  - ]:           0 :         PrintExceptionContinue(nullptr, "CommandLineRPC()");
    1362                 :           0 :     }
    1363                 :             :     return ret;
    1364                 :             : }
        

Generated by: LCOV version 2.0-1