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