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