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 <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;
410  double min_ping;
411  double ping;
412  int64_t addr_processed;
414  int64_t last_blck;
415  int64_t last_recv;
416  int64_t last_send;
417  int64_t last_trxn;
418  int id;
420  int version;
426  bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
427  };
428  std::vector<Peer> m_peers;
429  std::string ChainToString() const
430  {
431  switch (gArgs.GetChainType()) {
432  case ChainType::TESTNET:
433  return " testnet";
434  case ChainType::SIGNET:
435  return " signet";
436  case ChainType::REGTEST:
437  return " regtest";
438  case ChainType::MAIN:
439  return "";
440  }
441  assert(false);
442  }
443  std::string PingTimeToString(double seconds) const
444  {
445  if (seconds < 0) return "";
446  const double milliseconds{round(1000 * seconds)};
447  return milliseconds > 999999 ? "-" : ToString(milliseconds);
448  }
449  std::string ConnectionTypeForNetinfo(const std::string& conn_type) const
450  {
451  if (conn_type == "outbound-full-relay") return "full";
452  if (conn_type == "block-relay-only") return "block";
453  if (conn_type == "manual" || conn_type == "feeler") return conn_type;
454  if (conn_type == "addr-fetch") return "addr";
455  return "";
456  }
457 
458 public:
459  static constexpr int ID_PEERINFO = 0;
460  static constexpr int ID_NETWORKINFO = 1;
461 
462  UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
463  {
464  if (!args.empty()) {
465  uint8_t n{0};
466  if (ParseUInt8(args.at(0), &n)) {
467  m_details_level = std::min(n, MAX_DETAIL_LEVEL);
468  } else {
469  throw std::runtime_error(strprintf("invalid -netinfo argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));
470  }
471  }
472  UniValue result(UniValue::VARR);
473  result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
474  result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
475  return result;
476  }
477 
478  UniValue ProcessReply(const UniValue& batch_in) override
479  {
480  const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
481  if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
482  if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
483 
484  const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
485  if (networkinfo["version"].getInt<int>() < 209900) {
486  throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
487  }
488  const int64_t time_now{TicksSinceEpoch<std::chrono::seconds>(CliClock::now())};
489 
490  // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
491  for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
492  const std::string network{peer["network"].get_str()};
493  const int8_t network_id{NetworkStringToId(network)};
494  if (network_id == UNKNOWN_NETWORK) continue;
495  const bool is_outbound{!peer["inbound"].get_bool()};
496  const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
497  const std::string conn_type{peer["connection_type"].get_str()};
498  ++m_counts.at(is_outbound).at(network_id); // in/out by network
499  ++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
500  ++m_counts.at(2).at(network_id); // total by network
501  ++m_counts.at(2).at(NETWORKS.size()); // total overall
502  if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
503  if (conn_type == "manual") ++m_manual_peers_count;
504  if (DetailsRequested()) {
505  // Push data for this peer to the peers vector.
506  const int peer_id{peer["id"].getInt<int>()};
507  const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()};
508  const int version{peer["version"].getInt<int>()};
509  const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()};
510  const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()};
511  const int64_t conn_time{peer["conntime"].getInt<int64_t>()};
512  const int64_t last_blck{peer["last_block"].getInt<int64_t>()};
513  const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()};
514  const int64_t last_send{peer["lastsend"].getInt<int64_t>()};
515  const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()};
516  const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
517  const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
518  const std::string addr{peer["addr"].get_str()};
519  const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)};
520  const std::string sub_version{peer["subver"].get_str()};
521  const std::string transport{peer["transport_protocol_type"].isNull() ? "v1" : peer["transport_protocol_type"].get_str()};
522  const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
523  const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
524  const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
525  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});
526  m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
527  m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
528  m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
529  m_max_age_length = std::max(age.length(), m_max_age_length);
530  m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
531  m_is_asmap_on |= (mapped_as != 0);
532  }
533  }
534 
535  // Generate report header.
536  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())};
537 
538  // Report detailed peer connections list sorted by direction and minimum ping time.
539  if (DetailsRequested() && !m_peers.empty()) {
540  std::sort(m_peers.begin(), m_peers.end());
541  result += strprintf("<-> type net v mping ping send recv txn blk hb %*s%*s%*s ",
544  m_max_age_length, "age");
545  if (m_is_asmap_on) result += " asmap ";
546  result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
547  for (const Peer& peer : m_peers) {
548  std::string version{ToString(peer.version) + peer.sub_version};
549  result += strprintf(
550  "%3s %6s %5s %2s%7s%7s%5s%5s%5s%5s %2s %*s%*s%*s%*i %*s %-*s%s\n",
551  peer.is_outbound ? "out" : "in",
552  ConnectionTypeForNetinfo(peer.conn_type),
553  peer.network,
554  (peer.transport_protocol_type.size() == 2 && peer.transport_protocol_type[0] == 'v') ? peer.transport_protocol_type[1] : ' ',
555  PingTimeToString(peer.min_ping),
556  PingTimeToString(peer.ping),
557  peer.last_send ? ToString(time_now - peer.last_send) : "",
558  peer.last_recv ? ToString(time_now - peer.last_recv) : "",
559  peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
560  peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
561  strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
562  m_max_addr_processed_length, // variable spacing
563  peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
564  m_max_addr_rate_limited_length, // variable spacing
565  peer.addr_rate_limited ? ToString(peer.addr_rate_limited) : "",
566  m_max_age_length, // variable spacing
567  peer.age,
568  m_is_asmap_on ? 7 : 0, // variable spacing
569  m_is_asmap_on && peer.mapped_as ? ToString(peer.mapped_as) : "",
570  m_max_id_length, // variable spacing
571  peer.id,
572  IsAddressSelected() ? m_max_addr_length : 0, // variable spacing
573  IsAddressSelected() ? peer.addr : "",
574  IsVersionSelected() && version != "0" ? version : "");
575  }
576  result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
577  }
578 
579  // Report peer connection totals by type.
580  result += " ";
581  std::vector<int8_t> reachable_networks;
582  for (const UniValue& network : networkinfo["networks"].getValues()) {
583  if (network["reachable"].get_bool()) {
584  const std::string& network_name{network["name"].get_str()};
585  const int8_t network_id{NetworkStringToId(network_name)};
586  if (network_id == UNKNOWN_NETWORK) continue;
587  result += strprintf("%8s", network_name); // column header
588  reachable_networks.push_back(network_id);
589  }
590  };
591 
592  for (const size_t network_id : UNREACHABLE_NETWORK_IDS) {
593  if (m_counts.at(2).at(network_id) == 0) continue;
594  result += strprintf("%8s", NETWORK_SHORT_NAMES.at(network_id)); // column header
595  reachable_networks.push_back(network_id);
596  }
597 
598  result += " total block";
599  if (m_manual_peers_count) result += " manual";
600 
601  const std::array rows{"in", "out", "total"};
602  for (size_t i = 0; i < rows.size(); ++i) {
603  result += strprintf("\n%-5s", rows[i]); // row header
604  for (int8_t n : reachable_networks) {
605  result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
606  }
607  result += strprintf(" %5i", m_counts.at(i).at(NETWORKS.size())); // total peers count
608  if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
609  result += strprintf(" %5i", m_block_relay_peers_count);
610  if (m_manual_peers_count) result += strprintf(" %5i", m_manual_peers_count);
611  }
612  }
613 
614  // Report local addresses, ports, and scores.
615  result += "\n\nLocal addresses";
616  const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()};
617  if (local_addrs.empty()) {
618  result += ": n/a\n";
619  } else {
620  size_t max_addr_size{0};
621  for (const UniValue& addr : local_addrs) {
622  max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size);
623  }
624  for (const UniValue& addr : local_addrs) {
625  result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>());
626  }
627  }
628 
629  return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
630  }
631 
632  const std::string m_help_doc{
633  "-netinfo level|\"help\" \n\n"
634  "Returns a network peer connections dashboard with information from the remote server.\n"
635  "This human-readable interface will change regularly and is not intended to be a stable API.\n"
636  "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
637  + 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) +
638  "Pass \"help\" to see this detailed help documentation.\n"
639  "If more than one argument is passed, only the first one is read and parsed.\n"
640  "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
641  "Arguments:\n"
642  + strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", MAX_DETAIL_LEVEL) +
643  " 0 - Peer counts for each reachable network as well as for block relay peers\n"
644  " and manual peers, and the list of local addresses and ports\n"
645  " 1 - Like 0 but preceded by a peers listing (without address and version columns)\n"
646  " 2 - Like 1 but with an address column\n"
647  " 3 - Like 1 but with a version column\n"
648  " 4 - Like 1 but with both address and version columns\n"
649  "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n"
650  "Result:\n\n"
651  + 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) +
652  " Column Description\n"
653  " ------ -----------\n"
654  " <-> Direction\n"
655  " \"in\" - inbound connections are those initiated by the peer\n"
656  " \"out\" - outbound connections are those initiated by us\n"
657  " type Type of peer connection\n"
658  " \"full\" - full relay, the default\n"
659  " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
660  " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
661  " \"feeler\" - short-lived connection for testing addresses\n"
662  " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
663  " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n"
664  " v Version of transport protocol used for the connection\n"
665  " mping Minimum observed ping time, in milliseconds (ms)\n"
666  " ping Last observed ping time, in milliseconds (ms)\n"
667  " send Time since last message sent to the peer, in seconds\n"
668  " recv Time since last message received from the peer, in seconds\n"
669  " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
670  " \"*\" - we do not relay transactions to this peer (relaytxes is false)\n"
671  " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
672  " hb High-bandwidth BIP152 compact block relay\n"
673  " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
674  " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
675  " addrp Total number of addresses processed, excluding those dropped due to rate limiting\n"
676  " \".\" - we do not relay addresses to this peer (addr_relay_enabled is false)\n"
677  " addrl Total number of addresses dropped due to rate limiting\n"
678  " age Duration of connection to the peer, in minutes\n"
679  " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
680  " peer selection (only displayed if the -asmap config option is set)\n"
681  " id Peer index, in increasing order of peer connections since node startup\n"
682  " address IP address and port of the peer\n"
683  " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
684  "* The peer counts table displays the number of peers for each reachable network as well as\n"
685  " the number of block relay peers and manual peers.\n\n"
686  "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
687  "Examples:\n\n"
688  "Peer counts table of reachable networks and list of local addresses\n"
689  "> bitcoin-cli -netinfo\n\n"
690  "The same, preceded by a peers listing without address and version columns\n"
691  "> bitcoin-cli -netinfo 1\n\n"
692  "Full dashboard\n"
693  + strprintf("> bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
694  "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
695  + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
696  "See this help\n"
697  "> bitcoin-cli -netinfo help\n"};
698 };
699 
702 {
703 public:
704  UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
705  {
706  address_str = args.at(1);
707  UniValue params{RPCConvertValues("generatetoaddress", args)};
708  return JSONRPCRequestObj("generatetoaddress", params, 1);
709  }
710 
711  UniValue ProcessReply(const UniValue &reply) override
712  {
713  UniValue result(UniValue::VOBJ);
714  result.pushKV("address", address_str);
715  result.pushKV("blocks", reply.get_obj()["result"]);
716  return JSONRPCReplyObj(result, NullUniValue, 1);
717  }
718 protected:
719  std::string address_str;
720 };
721 
724 public:
725  UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
726  {
727  UniValue params;
728  if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
729  params = RPCConvertNamedValues(method, args);
730  } else {
731  params = RPCConvertValues(method, args);
732  }
733  return JSONRPCRequestObj(method, params, 1);
734  }
735 
736  UniValue ProcessReply(const UniValue &reply) override
737  {
738  return reply.get_obj();
739  }
740 };
741 
742 static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
743 {
744  std::string host;
745  // In preference order, we choose the following for the port:
746  // 1. -rpcport
747  // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
748  // 3. default port for chain
749  uint16_t port{BaseParams().RPCPort()};
750  SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
751  port = static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", port));
752 
753  // Obtain event base
754  raii_event_base base = obtain_event_base();
755 
756  // Synchronously look up hostname
757  raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
758 
759  // Set connection timeout
760  {
761  const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
762  if (timeout > 0) {
763  evhttp_connection_set_timeout(evcon.get(), timeout);
764  } else {
765  // Indefinite request timeouts are not possible in libevent-http, so we
766  // set the timeout to a very long time period instead.
767 
768  constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
769  evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS);
770  }
771  }
772 
773  HTTPReply response;
774  raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
775  if (req == nullptr) {
776  throw std::runtime_error("create http request failed");
777  }
778 
779  evhttp_request_set_error_cb(req.get(), http_error_cb);
780 
781  // Get credentials
782  std::string strRPCUserColonPass;
783  bool failedToGetAuthCookie = false;
784  if (gArgs.GetArg("-rpcpassword", "") == "") {
785  // Try fall back to cookie-based authentication if no password is provided
787  failedToGetAuthCookie = true;
788  }
789  } else {
790  strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
791  }
792 
793  struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
794  assert(output_headers);
795  evhttp_add_header(output_headers, "Host", host.c_str());
796  evhttp_add_header(output_headers, "Connection", "close");
797  evhttp_add_header(output_headers, "Content-Type", "application/json");
798  evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
799 
800  // Attach request data
801  std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
802  struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
803  assert(output_buffer);
804  evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
805 
806  // check if we should use a special wallet endpoint
807  std::string endpoint = "/";
808  if (rpcwallet) {
809  char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
810  if (encodedURI) {
811  endpoint = "/wallet/" + std::string(encodedURI);
812  free(encodedURI);
813  } else {
814  throw CConnectionFailed("uri-encode failed");
815  }
816  }
817  int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
818  req.release(); // ownership moved to evcon in above call
819  if (r != 0) {
820  throw CConnectionFailed("send http request failed");
821  }
822 
823  event_base_dispatch(base.get());
824 
825  if (response.status == 0) {
826  std::string responseErrorMessage;
827  if (response.error != -1) {
828  responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error));
829  }
830  throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\n"
831  "Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n"
832  "Use \"bitcoin-cli -help\" for more info.",
833  host, port, responseErrorMessage));
834  } else if (response.status == HTTP_UNAUTHORIZED) {
835  if (failedToGetAuthCookie) {
836  throw std::runtime_error(strprintf(
837  "Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)",
839  } else {
840  throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
841  }
842  } else if (response.status == HTTP_SERVICE_UNAVAILABLE) {
843  throw std::runtime_error(strprintf("Server response: %s", response.body));
844  } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
845  throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
846  else if (response.body.empty())
847  throw std::runtime_error("no response from server");
848 
849  // Parse reply
850  UniValue valReply(UniValue::VSTR);
851  if (!valReply.read(response.body))
852  throw std::runtime_error("couldn't parse reply from server");
853  UniValue reply = rh->ProcessReply(valReply);
854  if (reply.empty())
855  throw std::runtime_error("expected reply to have result, error and id properties");
856 
857  return reply;
858 }
859 
869 static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
870 {
871  UniValue response(UniValue::VOBJ);
872  // Execute and handle connection failures with -rpcwait.
873  const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
874  const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
875  const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
876 
877  do {
878  try {
879  response = CallRPC(rh, strMethod, args, rpcwallet);
880  if (fWait) {
881  const UniValue& error = response.find_value("error");
882  if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
883  throw CConnectionFailed("server in warmup");
884  }
885  }
886  break; // Connection succeeded, no need to retry.
887  } catch (const CConnectionFailed& e) {
888  if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
890  } else {
891  throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
892  }
893  }
894  } while (fWait);
895  return response;
896 }
897 
899 static void ParseResult(const UniValue& result, std::string& strPrint)
900 {
901  if (result.isNull()) return;
902  strPrint = result.isStr() ? result.get_str() : result.write(2);
903 }
904 
906 static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
907 {
908  if (error.isObject()) {
909  const UniValue& err_code = error.find_value("code");
910  const UniValue& err_msg = error.find_value("message");
911  if (!err_code.isNull()) {
912  strPrint = "error code: " + err_code.getValStr() + "\n";
913  }
914  if (err_msg.isStr()) {
915  strPrint += ("error message:\n" + err_msg.get_str());
916  }
917  if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) {
918  strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
919  }
920  } else {
921  strPrint = "error: " + error.write();
922  }
923  nRet = abs(error["code"].getInt<int>());
924 }
925 
932 static void GetWalletBalances(UniValue& result)
933 {
935  const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{});
936  if (!listwallets.find_value("error").isNull()) return;
937  const UniValue& wallets = listwallets.find_value("result");
938  if (wallets.size() <= 1) return;
939 
940  UniValue balances(UniValue::VOBJ);
941  for (const UniValue& wallet : wallets.getValues()) {
942  const std::string& wallet_name = wallet.get_str();
943  const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
944  const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"];
945  balances.pushKV(wallet_name, balance);
946  }
947  result.pushKV("balances", balances);
948 }
949 
956 static void GetProgressBar(double progress, std::string& progress_bar)
957 {
958  if (progress < 0 || progress > 1) return;
959 
960  static constexpr double INCREMENT{0.05};
961  static const std::string COMPLETE_BAR{"\u2592"};
962  static const std::string INCOMPLETE_BAR{"\u2591"};
963 
964  for (int i = 0; i < progress / INCREMENT; ++i) {
965  progress_bar += COMPLETE_BAR;
966  }
967 
968  for (int i = 0; i < (1 - progress) / INCREMENT; ++i) {
969  progress_bar += INCOMPLETE_BAR;
970  }
971 }
972 
978 static void ParseGetInfoResult(UniValue& result)
979 {
980  if (!result.find_value("error").isNull()) return;
981 
982  std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN;
983  bool should_colorize = false;
984 
985 #ifndef WIN32
986  if (isatty(fileno(stdout))) {
987  // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal.
988  should_colorize = true;
989  }
990 #endif
991 
992  if (gArgs.IsArgSet("-color")) {
993  const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)};
994  if (color == "always") {
995  should_colorize = true;
996  } else if (color == "never") {
997  should_colorize = false;
998  } else if (color != "auto") {
999  throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never.");
1000  }
1001  }
1002 
1003  if (should_colorize) {
1004  RESET = "\x1B[0m";
1005  GREEN = "\x1B[32m";
1006  BLUE = "\x1B[34m";
1007  YELLOW = "\x1B[33m";
1008  MAGENTA = "\x1B[35m";
1009  CYAN = "\x1B[36m";
1010  }
1011 
1012  std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
1013  result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
1014  result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
1015 
1016  const double ibd_progress{result["verificationprogress"].get_real()};
1017  std::string ibd_progress_bar;
1018  // Display the progress bar only if IBD progress is less than 99%
1019  if (ibd_progress < 0.99) {
1020  GetProgressBar(ibd_progress, ibd_progress_bar);
1021  // Add padding between progress bar and IBD progress
1022  ibd_progress_bar += " ";
1023  }
1024 
1025  result_string += strprintf("Verification progress: %s%.4f%%\n", ibd_progress_bar, ibd_progress * 100);
1026  result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
1027 
1028  result_string += strprintf(
1029  "%sNetwork: in %s, out %s, total %s%s\n",
1030  GREEN,
1031  result["connections"]["in"].getValStr(),
1032  result["connections"]["out"].getValStr(),
1033  result["connections"]["total"].getValStr(),
1034  RESET);
1035  result_string += strprintf("Version: %s\n", result["version"].getValStr());
1036  result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
1037 
1038  // proxies
1039  std::map<std::string, std::vector<std::string>> proxy_networks;
1040  std::vector<std::string> ordered_proxies;
1041 
1042  for (const UniValue& network : result["networks"].getValues()) {
1043  const std::string proxy = network["proxy"].getValStr();
1044  if (proxy.empty()) continue;
1045  // Add proxy to ordered_proxy if has not been processed
1046  if (proxy_networks.find(proxy) == proxy_networks.end()) ordered_proxies.push_back(proxy);
1047 
1048  proxy_networks[proxy].push_back(network["name"].getValStr());
1049  }
1050 
1051  std::vector<std::string> formatted_proxies;
1052  formatted_proxies.reserve(ordered_proxies.size());
1053  for (const std::string& proxy : ordered_proxies) {
1054  formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", ")));
1055  }
1056  result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", "));
1057 
1058  result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
1059 
1060  if (!result["has_wallet"].isNull()) {
1061  const std::string walletname = result["walletname"].getValStr();
1062  result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET);
1063 
1064  result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr());
1065  if (!result["unlocked_until"].isNull()) {
1066  result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr());
1067  }
1068  result_string += strprintf("Transaction fee rate (-paytxfee) (%s/kvB): %s\n\n", CURRENCY_UNIT, result["paytxfee"].getValStr());
1069  }
1070  if (!result["balance"].isNull()) {
1071  result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr());
1072  }
1073 
1074  if (!result["balances"].isNull()) {
1075  result_string += strprintf("%sBalances%s\n", CYAN, RESET);
1076 
1077  size_t max_balance_length{10};
1078 
1079  for (const std::string& wallet : result["balances"].getKeys()) {
1080  max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length);
1081  }
1082 
1083  for (const std::string& wallet : result["balances"].getKeys()) {
1084  result_string += strprintf("%*s %s\n",
1085  max_balance_length,
1086  result["balances"][wallet].getValStr(),
1087  wallet.empty() ? "\"\"" : wallet);
1088  }
1089  result_string += "\n";
1090  }
1091 
1092  const std::string warnings{result["warnings"].getValStr()};
1093  result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings);
1094 
1095  result.setStr(result_string);
1096 }
1097 
1103 {
1104  std::optional<std::string> wallet_name{};
1105  if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
1107  return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name);
1108 }
1109 
1115 static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
1116 {
1117  if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
1118  if (args.size() == 0) {
1119  args.emplace_back(DEFAULT_NBLOCKS);
1120  } else if (args.at(0) == "0") {
1121  throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
1122  }
1123  args.emplace(args.begin() + 1, address);
1124 }
1125 
1126 static int CommandLineRPC(int argc, char *argv[])
1127 {
1128  std::string strPrint;
1129  int nRet = 0;
1130  try {
1131  // Skip switches
1132  while (argc > 1 && IsSwitchChar(argv[1][0])) {
1133  argc--;
1134  argv++;
1135  }
1136  std::string rpcPass;
1137  if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
1138  NO_STDIN_ECHO();
1139  if (!StdinReady()) {
1140  fputs("RPC password> ", stderr);
1141  fflush(stderr);
1142  }
1143  if (!std::getline(std::cin, rpcPass)) {
1144  throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
1145  }
1146  if (StdinTerminal()) {
1147  fputc('\n', stdout);
1148  }
1149  gArgs.ForceSetArg("-rpcpassword", rpcPass);
1150  }
1151  std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
1152  if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
1153  NO_STDIN_ECHO();
1154  std::string walletPass;
1155  if (args.size() < 1 || args[0].substr(0, 16) != "walletpassphrase") {
1156  throw std::runtime_error("-stdinwalletpassphrase is only applicable for walletpassphrase(change)");
1157  }
1158  if (!StdinReady()) {
1159  fputs("Wallet passphrase> ", stderr);
1160  fflush(stderr);
1161  }
1162  if (!std::getline(std::cin, walletPass)) {
1163  throw std::runtime_error("-stdinwalletpassphrase specified but failed to read from standard input");
1164  }
1165  if (StdinTerminal()) {
1166  fputc('\n', stdout);
1167  }
1168  args.insert(args.begin() + 1, walletPass);
1169  }
1170  if (gArgs.GetBoolArg("-stdin", false)) {
1171  // Read one arg per line from stdin and append
1172  std::string line;
1173  while (std::getline(std::cin, line)) {
1174  args.push_back(line);
1175  }
1176  if (StdinTerminal()) {
1177  fputc('\n', stdout);
1178  }
1179  }
1180  std::unique_ptr<BaseRequestHandler> rh;
1181  std::string method;
1182  if (gArgs.IsArgSet("-getinfo")) {
1183  rh.reset(new GetinfoRequestHandler());
1184  } else if (gArgs.GetBoolArg("-netinfo", false)) {
1185  if (!args.empty() && args.at(0) == "help") {
1186  tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc);
1187  return 0;
1188  }
1189  rh.reset(new NetinfoRequestHandler());
1190  } else if (gArgs.GetBoolArg("-generate", false)) {
1192  const UniValue& error{getnewaddress.find_value("error")};
1193  if (error.isNull()) {
1194  SetGenerateToAddressArgs(getnewaddress.find_value("result").get_str(), args);
1195  rh.reset(new GenerateToAddressRequestHandler());
1196  } else {
1197  ParseError(error, strPrint, nRet);
1198  }
1199  } else if (gArgs.GetBoolArg("-addrinfo", false)) {
1200  rh.reset(new AddrinfoRequestHandler());
1201  } else {
1202  rh.reset(new DefaultRequestHandler());
1203  if (args.size() < 1) {
1204  throw std::runtime_error("too few parameters (need at least command)");
1205  }
1206  method = args[0];
1207  args.erase(args.begin()); // Remove trailing method name from arguments vector
1208  }
1209  if (nRet == 0) {
1210  // Perform RPC call
1211  std::optional<std::string> wallet_name{};
1212  if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
1213  const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
1214 
1215  // Parse reply
1216  UniValue result = reply.find_value("result");
1217  const UniValue& error = reply.find_value("error");
1218  if (error.isNull()) {
1219  if (gArgs.GetBoolArg("-getinfo", false)) {
1220  if (!gArgs.IsArgSet("-rpcwallet")) {
1221  GetWalletBalances(result); // fetch multiwallet balances and append to result
1222  }
1223  ParseGetInfoResult(result);
1224  }
1225 
1226  ParseResult(result, strPrint);
1227  } else {
1228  ParseError(error, strPrint, nRet);
1229  }
1230  }
1231  } catch (const std::exception& e) {
1232  strPrint = std::string("error: ") + e.what();
1233  nRet = EXIT_FAILURE;
1234  } catch (...) {
1235  PrintExceptionContinue(nullptr, "CommandLineRPC()");
1236  throw;
1237  }
1238 
1239  if (strPrint != "") {
1240  tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
1241  }
1242  return nRet;
1243 }
1244 
1246 {
1247 #ifdef WIN32
1248  common::WinCmdLineArgs winArgs;
1249  std::tie(argc, argv) = winArgs.get();
1250 #endif
1251  SetupEnvironment();
1253  tfm::format(std::cerr, "Error: Initializing networking failed\n");
1254  return EXIT_FAILURE;
1255  }
1256  event_set_log_callback(&libevent_log_cb);
1257 
1258  try {
1259  int ret = AppInitRPC(argc, argv);
1260  if (ret != CONTINUE_EXECUTION)
1261  return ret;
1262  }
1263  catch (const std::exception& e) {
1264  PrintExceptionContinue(&e, "AppInitRPC()");
1265  return EXIT_FAILURE;
1266  } catch (...) {
1267  PrintExceptionContinue(nullptr, "AppInitRPC()");
1268  return EXIT_FAILURE;
1269  }
1270 
1271  int ret = EXIT_FAILURE;
1272  try {
1273  ret = CommandLineRPC(argc, argv);
1274  }
1275  catch (const std::exception& e) {
1276  PrintExceptionContinue(&e, "CommandLineRPC()");
1277  } catch (...) {
1278  PrintExceptionContinue(nullptr, "CommandLineRPC()");
1279  }
1280  return ret;
1281 }
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:56
static constexpr int8_t UNKNOWN_NETWORK
Definition: bitcoin-cli.cpp:61
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT
Definition: bitcoin-cli.cpp:58
UrlDecodeFn *const URL_DECODE
Definition: bitcoin-cli.cpp:54
static const int CONTINUE_EXECUTION
Definition: bitcoin-cli.cpp:60
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:65
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:57
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:68
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:53
static constexpr std::array NETWORKS
Definition: bitcoin-cli.cpp:63
static void SetupCliArgs(ArgsManager &argsman)
Definition: bitcoin-cli.cpp:73
static const std::string DEFAULT_COLOR_SETTING
Default -color setting.
Definition: bitcoin-cli.cpp:71
std::chrono::system_clock CliClock
Definition: bitcoin-cli.cpp:51
static constexpr std::array NETWORK_SHORT_NAMES
Definition: bitcoin-cli.cpp:64
static std::string http_errorstring(int code)
static const bool DEFAULT_NAMED
Definition: bitcoin-cli.cpp:59
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:268
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
UrlDecodeFn urlDecode
Definition: url.h:11
std::string(const std::string &url_encoded) UrlDecodeFn
Definition: url.h:10
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())