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