LCOV - code coverage report
Current view: top level - src - bitcoin-cli.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 0.0 % 766 0
Test Date: 2026-04-23 06:31:55 Functions: 0.0 % 36 0
Branches: 0.0 % 2411 0

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

Generated by: LCOV version 2.0-1