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

Generated by: LCOV version 2.0-1