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 % 899 0
Test Date: 2026-05-27 07:43:50 Functions: 0.0 % 41 0
Branches: 0.0 % 2646 0

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

Generated by: LCOV version 2.0-1