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