Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
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#include <config/bitcoin-config.h> // IWYU pragma: keep
7
8#include <chainparamsbase.h>
9#include <clientversion.h>
10#include <common/args.h>
11#include <common/system.h>
12#include <compat/compat.h>
13#include <compat/stdin.h>
14#include <policy/feerate.h>
15#include <rpc/client.h>
16#include <rpc/mining.h>
17#include <rpc/protocol.h>
18#include <rpc/request.h>
19#include <tinyformat.h>
20#include <univalue.h>
21#include <util/chaintype.h>
22#include <util/exception.h>
23#include <util/strencodings.h>
24#include <util/time.h>
25#include <util/translation.h>
26
27#include <algorithm>
28#include <chrono>
29#include <cmath>
30#include <cstdio>
31#include <functional>
32#include <memory>
33#include <optional>
34#include <string>
35#include <tuple>
36
37#ifndef WIN32
38#include <unistd.h>
39#endif
40
41#include <event2/buffer.h>
42#include <event2/keyvalq_struct.h>
43#include <support/events.h>
44
45using util::Join;
46using util::ToString;
47
48// The server returns time values from a mockable system clock, but it is not
49// trivial to get the mocked time from the server, nor is it needed for now, so
50// just use a plain system_clock.
51using CliClock = std::chrono::system_clock;
52
53const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
54
55static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
56static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
57static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
58static const bool DEFAULT_NAMED=false;
59static const int CONTINUE_EXECUTION=-1;
60static constexpr int8_t UNKNOWN_NETWORK{-1};
61// See GetNetworkName() in netbase.cpp
62static constexpr std::array NETWORKS{"not_publicly_routable", "ipv4", "ipv6", "onion", "i2p", "cjdns", "internal"};
63static constexpr std::array NETWORK_SHORT_NAMES{"npr", "ipv4", "ipv6", "onion", "i2p", "cjdns", "int"};
64static constexpr std::array UNREACHABLE_NETWORK_IDS{/*not_publicly_routable*/0, /*internal*/6};
65
67static const std::string DEFAULT_NBLOCKS = "1";
68
70static const std::string DEFAULT_COLOR_SETTING{"auto"};
71
72static void SetupCliArgs(ArgsManager& argsman)
73{
74 SetupHelpOptions(argsman);
75
76 const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN);
77 const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET);
78 const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4);
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, testnet4: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->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
113static 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//
125class CConnectionFailed : public std::runtime_error
126{
127public:
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//
139static 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 }
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
189{
190 HTTPReply() = default;
191
192 int status{0};
193 int error{-1};
194 std::string body;
195};
196
197static 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
217static 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
242static 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{
253public:
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{
262private:
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
271public:
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", std::move(addresses));
305 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
306 }
307};
308
311{
312public:
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", std::move(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(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
375 }
376};
377
380{
381private:
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};
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;
414 int64_t last_blck;
415 int64_t last_recv;
416 int64_t last_send;
417 int64_t last_trxn;
418 int id;
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()) {
433 return " testnet4";
435 return " testnet";
437 return " signet";
439 return " regtest";
440 case ChainType::MAIN:
441 return "";
442 }
443 assert(false);
444 }
445 std::string PingTimeToString(double seconds) const
446 {
447 if (seconds < 0) return "";
448 const double milliseconds{round(1000 * seconds)};
449 return milliseconds > 999999 ? "-" : ToString(milliseconds);
450 }
451 std::string ConnectionTypeForNetinfo(const std::string& conn_type) const
452 {
453 if (conn_type == "outbound-full-relay") return "full";
454 if (conn_type == "block-relay-only") return "block";
455 if (conn_type == "manual" || conn_type == "feeler") return conn_type;
456 if (conn_type == "addr-fetch") return "addr";
457 return "";
458 }
459
460public:
461 static constexpr int ID_PEERINFO = 0;
462 static constexpr int ID_NETWORKINFO = 1;
463
464 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
465 {
466 if (!args.empty()) {
467 uint8_t n{0};
468 if (ParseUInt8(args.at(0), &n)) {
469 m_details_level = std::min(n, MAX_DETAIL_LEVEL);
470 } else {
471 throw std::runtime_error(strprintf("invalid -netinfo argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));
472 }
473 }
474 UniValue result(UniValue::VARR);
475 result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
476 result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
477 return result;
478 }
479
480 UniValue ProcessReply(const UniValue& batch_in) override
481 {
482 const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
483 if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
484 if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
485
486 const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
487 if (networkinfo["version"].getInt<int>() < 209900) {
488 throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
489 }
490 const int64_t time_now{TicksSinceEpoch<std::chrono::seconds>(CliClock::now())};
491
492 // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
493 for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
494 const std::string network{peer["network"].get_str()};
495 const int8_t network_id{NetworkStringToId(network)};
496 if (network_id == UNKNOWN_NETWORK) continue;
497 const bool is_outbound{!peer["inbound"].get_bool()};
498 const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
499 const std::string conn_type{peer["connection_type"].get_str()};
500 ++m_counts.at(is_outbound).at(network_id); // in/out by network
501 ++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
502 ++m_counts.at(2).at(network_id); // total by network
503 ++m_counts.at(2).at(NETWORKS.size()); // total overall
504 if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
505 if (conn_type == "manual") ++m_manual_peers_count;
506 if (DetailsRequested()) {
507 // Push data for this peer to the peers vector.
508 const int peer_id{peer["id"].getInt<int>()};
509 const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()};
510 const int version{peer["version"].getInt<int>()};
511 const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()};
512 const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()};
513 const int64_t conn_time{peer["conntime"].getInt<int64_t>()};
514 const int64_t last_blck{peer["last_block"].getInt<int64_t>()};
515 const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()};
516 const int64_t last_send{peer["lastsend"].getInt<int64_t>()};
517 const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()};
518 const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
519 const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
520 const std::string addr{peer["addr"].get_str()};
521 const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)};
522 const std::string sub_version{peer["subver"].get_str()};
523 const std::string transport{peer["transport_protocol_type"].isNull() ? "v1" : peer["transport_protocol_type"].get_str()};
524 const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
525 const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
526 const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
527 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});
528 m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
529 m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
530 m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
531 m_max_age_length = std::max(age.length(), m_max_age_length);
532 m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
533 m_is_asmap_on |= (mapped_as != 0);
534 }
535 }
536
537 // Generate report header.
538 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())};
539
540 // Report detailed peer connections list sorted by direction and minimum ping time.
541 if (DetailsRequested() && !m_peers.empty()) {
542 std::sort(m_peers.begin(), m_peers.end());
543 result += strprintf("<-> type net v mping ping send recv txn blk hb %*s%*s%*s ",
546 m_max_age_length, "age");
547 if (m_is_asmap_on) result += " asmap ";
548 result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
549 for (const Peer& peer : m_peers) {
550 std::string version{ToString(peer.version) + peer.sub_version};
551 result += strprintf(
552 "%3s %6s %5s %2s%7s%7s%5s%5s%5s%5s %2s %*s%*s%*s%*i %*s %-*s%s\n",
553 peer.is_outbound ? "out" : "in",
554 ConnectionTypeForNetinfo(peer.conn_type),
555 peer.network,
556 (peer.transport_protocol_type.size() == 2 && peer.transport_protocol_type[0] == 'v') ? peer.transport_protocol_type[1] : ' ',
557 PingTimeToString(peer.min_ping),
558 PingTimeToString(peer.ping),
559 peer.last_send ? ToString(time_now - peer.last_send) : "",
560 peer.last_recv ? ToString(time_now - peer.last_recv) : "",
561 peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
562 peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
563 strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
564 m_max_addr_processed_length, // variable spacing
565 peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
566 m_max_addr_rate_limited_length, // variable spacing
567 peer.addr_rate_limited ? ToString(peer.addr_rate_limited) : "",
568 m_max_age_length, // variable spacing
569 peer.age,
570 m_is_asmap_on ? 7 : 0, // variable spacing
571 m_is_asmap_on && peer.mapped_as ? ToString(peer.mapped_as) : "",
572 m_max_id_length, // variable spacing
573 peer.id,
574 IsAddressSelected() ? m_max_addr_length : 0, // variable spacing
575 IsAddressSelected() ? peer.addr : "",
576 IsVersionSelected() && version != "0" ? version : "");
577 }
578 result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
579 }
580
581 // Report peer connection totals by type.
582 result += " ";
583 std::vector<int8_t> reachable_networks;
584 for (const UniValue& network : networkinfo["networks"].getValues()) {
585 if (network["reachable"].get_bool()) {
586 const std::string& network_name{network["name"].get_str()};
587 const int8_t network_id{NetworkStringToId(network_name)};
588 if (network_id == UNKNOWN_NETWORK) continue;
589 result += strprintf("%8s", network_name); // column header
590 reachable_networks.push_back(network_id);
591 }
592 };
593
594 for (const size_t network_id : UNREACHABLE_NETWORK_IDS) {
595 if (m_counts.at(2).at(network_id) == 0) continue;
596 result += strprintf("%8s", NETWORK_SHORT_NAMES.at(network_id)); // column header
597 reachable_networks.push_back(network_id);
598 }
599
600 result += " total block";
601 if (m_manual_peers_count) result += " manual";
602
603 const std::array rows{"in", "out", "total"};
604 for (size_t i = 0; i < rows.size(); ++i) {
605 result += strprintf("\n%-5s", rows[i]); // row header
606 for (int8_t n : reachable_networks) {
607 result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
608 }
609 result += strprintf(" %5i", m_counts.at(i).at(NETWORKS.size())); // total peers count
610 if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
611 result += strprintf(" %5i", m_block_relay_peers_count);
613 }
614 }
615
616 // Report local addresses, ports, and scores.
617 result += "\n\nLocal addresses";
618 const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()};
619 if (local_addrs.empty()) {
620 result += ": n/a\n";
621 } else {
622 size_t max_addr_size{0};
623 for (const UniValue& addr : local_addrs) {
624 max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size);
625 }
626 for (const UniValue& addr : local_addrs) {
627 result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>());
628 }
629 }
630
631 return JSONRPCReplyObj(UniValue{result}, NullUniValue, /*id=*/1, JSONRPCVersion::V2);
632 }
633
634 const std::string m_help_doc{
635 "-netinfo level|\"help\" \n\n"
636 "Returns a network peer connections dashboard with information from the remote server.\n"
637 "This human-readable interface will change regularly and is not intended to be a stable API.\n"
638 "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
639 + 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) +
640 "Pass \"help\" to see this detailed help documentation.\n"
641 "If more than one argument is passed, only the first one is read and parsed.\n"
642 "Suggestion: use with the Linux watch(1) command for a live dashboard; see example below.\n\n"
643 "Arguments:\n"
644 + strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", MAX_DETAIL_LEVEL) +
645 " 0 - Peer counts for each reachable network as well as for block relay peers\n"
646 " and manual peers, and the list of local addresses and ports\n"
647 " 1 - Like 0 but preceded by a peers listing (without address and version columns)\n"
648 " 2 - Like 1 but with an address column\n"
649 " 3 - Like 1 but with a version column\n"
650 " 4 - Like 1 but with both address and version columns\n"
651 "2. help (string \"help\", optional) Print this help documentation instead of the dashboard.\n\n"
652 "Result:\n\n"
653 + 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) +
654 " Column Description\n"
655 " ------ -----------\n"
656 " <-> Direction\n"
657 " \"in\" - inbound connections are those initiated by the peer\n"
658 " \"out\" - outbound connections are those initiated by us\n"
659 " type Type of peer connection\n"
660 " \"full\" - full relay, the default\n"
661 " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
662 " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
663 " \"feeler\" - short-lived connection for testing addresses\n"
664 " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
665 " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n"
666 " v Version of transport protocol used for the connection\n"
667 " mping Minimum observed ping time, in milliseconds (ms)\n"
668 " ping Last observed ping time, in milliseconds (ms)\n"
669 " send Time since last message sent to the peer, in seconds\n"
670 " recv Time since last message received from the peer, in seconds\n"
671 " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
672 " \"*\" - we do not relay transactions to this peer (relaytxes is false)\n"
673 " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
674 " hb High-bandwidth BIP152 compact block relay\n"
675 " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
676 " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
677 " addrp Total number of addresses processed, excluding those dropped due to rate limiting\n"
678 " \".\" - we do not relay addresses to this peer (addr_relay_enabled is false)\n"
679 " addrl Total number of addresses dropped due to rate limiting\n"
680 " age Duration of connection to the peer, in minutes\n"
681 " asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
682 " peer selection (only displayed if the -asmap config option is set)\n"
683 " id Peer index, in increasing order of peer connections since node startup\n"
684 " address IP address and port of the peer\n"
685 " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
686 "* The peer counts table displays the number of peers for each reachable network as well as\n"
687 " the number of block relay peers and manual peers.\n\n"
688 "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
689 "Examples:\n\n"
690 "Peer counts table of reachable networks and list of local addresses\n"
691 "> bitcoin-cli -netinfo\n\n"
692 "The same, preceded by a peers listing without address and version columns\n"
693 "> bitcoin-cli -netinfo 1\n\n"
694 "Full dashboard\n"
695 + strprintf("> bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
696 "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
697 + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", MAX_DETAIL_LEVEL) +
698 "See this help\n"
699 "> bitcoin-cli -netinfo help\n"};
700};
701
704{
705public:
706 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
707 {
708 address_str = args.at(1);
709 UniValue params{RPCConvertValues("generatetoaddress", args)};
710 return JSONRPCRequestObj("generatetoaddress", params, 1);
711 }
712
713 UniValue ProcessReply(const UniValue &reply) override
714 {
715 UniValue result(UniValue::VOBJ);
716 result.pushKV("address", address_str);
717 result.pushKV("blocks", reply.get_obj()["result"]);
718 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
719 }
720protected:
721 std::string address_str;
722};
723
726public:
727 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
728 {
729 UniValue params;
730 if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
731 params = RPCConvertNamedValues(method, args);
732 } else {
733 params = RPCConvertValues(method, args);
734 }
735 return JSONRPCRequestObj(method, params, 1);
736 }
737
738 UniValue ProcessReply(const UniValue &reply) override
739 {
740 return reply.get_obj();
741 }
742};
743
744static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
745{
746 std::string host;
747 // In preference order, we choose the following for the port:
748 // 1. -rpcport
749 // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
750 // 3. default port for chain
751 uint16_t port{BaseParams().RPCPort()};
752 {
753 uint16_t rpcconnect_port{0};
754 const std::string rpcconnect_str = gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
755 if (!SplitHostPort(rpcconnect_str, rpcconnect_port, host)) {
756 // Uses argument provided as-is
757 // (rather than value parsed)
758 // to aid the user in troubleshooting
759 throw std::runtime_error(strprintf("Invalid port provided in -rpcconnect: %s", rpcconnect_str));
760 } else {
761 if (rpcconnect_port != 0) {
762 // Use the valid port provided in rpcconnect
763 port = rpcconnect_port;
764 } // else, no port was provided in rpcconnect (continue using default one)
765 }
766
767 if (std::optional<std::string> rpcport_arg = gArgs.GetArg("-rpcport")) {
768 // -rpcport was specified
769 const uint16_t rpcport_int{ToIntegral<uint16_t>(rpcport_arg.value()).value_or(0)};
770 if (rpcport_int == 0) {
771 // Uses argument provided as-is
772 // (rather than value parsed)
773 // to aid the user in troubleshooting
774 throw std::runtime_error(strprintf("Invalid port provided in -rpcport: %s", rpcport_arg.value()));
775 }
776
777 // Use the valid port provided
778 port = rpcport_int;
779
780 // If there was a valid port provided in rpcconnect,
781 // rpcconnect_port is non-zero.
782 if (rpcconnect_port != 0) {
783 tfm::format(std::cerr, "Warning: Port specified in both -rpcconnect and -rpcport. Using -rpcport %u\n", port);
784 }
785 }
786 }
787
788 // Obtain event base
789 raii_event_base base = obtain_event_base();
790
791 // Synchronously look up hostname
792 raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
793
794 // Set connection timeout
795 {
796 const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
797 if (timeout > 0) {
798 evhttp_connection_set_timeout(evcon.get(), timeout);
799 } else {
800 // Indefinite request timeouts are not possible in libevent-http, so we
801 // set the timeout to a very long time period instead.
802
803 constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
804 evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS);
805 }
806 }
807
808 HTTPReply response;
809 raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
810 if (req == nullptr) {
811 throw std::runtime_error("create http request failed");
812 }
813
814 evhttp_request_set_error_cb(req.get(), http_error_cb);
815
816 // Get credentials
817 std::string strRPCUserColonPass;
818 bool failedToGetAuthCookie = false;
819 if (gArgs.GetArg("-rpcpassword", "") == "") {
820 // Try fall back to cookie-based authentication if no password is provided
822 failedToGetAuthCookie = true;
823 }
824 } else {
825 strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
826 }
827
828 struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
829 assert(output_headers);
830 evhttp_add_header(output_headers, "Host", host.c_str());
831 evhttp_add_header(output_headers, "Connection", "close");
832 evhttp_add_header(output_headers, "Content-Type", "application/json");
833 evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
834
835 // Attach request data
836 std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
837 struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
838 assert(output_buffer);
839 evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
840
841 // check if we should use a special wallet endpoint
842 std::string endpoint = "/";
843 if (rpcwallet) {
844 char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
845 if (encodedURI) {
846 endpoint = "/wallet/" + std::string(encodedURI);
847 free(encodedURI);
848 } else {
849 throw CConnectionFailed("uri-encode failed");
850 }
851 }
852 int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
853 req.release(); // ownership moved to evcon in above call
854 if (r != 0) {
855 throw CConnectionFailed("send http request failed");
856 }
857
858 event_base_dispatch(base.get());
859
860 if (response.status == 0) {
861 std::string responseErrorMessage;
862 if (response.error != -1) {
863 responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error));
864 }
865 throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\n"
866 "Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n"
867 "Use \"bitcoin-cli -help\" for more info.",
868 host, port, responseErrorMessage));
869 } else if (response.status == HTTP_UNAUTHORIZED) {
870 if (failedToGetAuthCookie) {
871 throw std::runtime_error(strprintf(
872 "Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)",
874 } else {
875 throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
876 }
877 } else if (response.status == HTTP_SERVICE_UNAVAILABLE) {
878 throw std::runtime_error(strprintf("Server response: %s", response.body));
879 } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
880 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
881 else if (response.body.empty())
882 throw std::runtime_error("no response from server");
883
884 // Parse reply
885 UniValue valReply(UniValue::VSTR);
886 if (!valReply.read(response.body))
887 throw std::runtime_error("couldn't parse reply from server");
888 UniValue reply = rh->ProcessReply(valReply);
889 if (reply.empty())
890 throw std::runtime_error("expected reply to have result, error and id properties");
891
892 return reply;
893}
894
904static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
905{
906 UniValue response(UniValue::VOBJ);
907 // Execute and handle connection failures with -rpcwait.
908 const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
909 const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
910 const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
911
912 do {
913 try {
914 response = CallRPC(rh, strMethod, args, rpcwallet);
915 if (fWait) {
916 const UniValue& error = response.find_value("error");
917 if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
918 throw CConnectionFailed("server in warmup");
919 }
920 }
921 break; // Connection succeeded, no need to retry.
922 } catch (const CConnectionFailed& e) {
923 if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
925 } else {
926 throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
927 }
928 }
929 } while (fWait);
930 return response;
931}
932
934static void ParseResult(const UniValue& result, std::string& strPrint)
935{
936 if (result.isNull()) return;
937 strPrint = result.isStr() ? result.get_str() : result.write(2);
938}
939
941static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
942{
943 if (error.isObject()) {
944 const UniValue& err_code = error.find_value("code");
945 const UniValue& err_msg = error.find_value("message");
946 if (!err_code.isNull()) {
947 strPrint = "error code: " + err_code.getValStr() + "\n";
948 }
949 if (err_msg.isStr()) {
950 strPrint += ("error message:\n" + err_msg.get_str());
951 }
952 if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) {
953 strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
954 }
955 } else {
956 strPrint = "error: " + error.write();
957 }
958 nRet = abs(error["code"].getInt<int>());
959}
960
967static void GetWalletBalances(UniValue& result)
968{
970 const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{});
971 if (!listwallets.find_value("error").isNull()) return;
972 const UniValue& wallets = listwallets.find_value("result");
973 if (wallets.size() <= 1) return;
974
975 UniValue balances(UniValue::VOBJ);
976 for (const UniValue& wallet : wallets.getValues()) {
977 const std::string& wallet_name = wallet.get_str();
978 const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
979 const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"];
980 balances.pushKV(wallet_name, balance);
981 }
982 result.pushKV("balances", std::move(balances));
983}
984
991static void GetProgressBar(double progress, std::string& progress_bar)
992{
993 if (progress < 0 || progress > 1) return;
994
995 static constexpr double INCREMENT{0.05};
996 static const std::string COMPLETE_BAR{"\u2592"};
997 static const std::string INCOMPLETE_BAR{"\u2591"};
998
999 for (int i = 0; i < progress / INCREMENT; ++i) {
1000 progress_bar += COMPLETE_BAR;
1001 }
1002
1003 for (int i = 0; i < (1 - progress) / INCREMENT; ++i) {
1004 progress_bar += INCOMPLETE_BAR;
1005 }
1006}
1007
1013static void ParseGetInfoResult(UniValue& result)
1014{
1015 if (!result.find_value("error").isNull()) return;
1016
1017 std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN;
1018 bool should_colorize = false;
1019
1020#ifndef WIN32
1021 if (isatty(fileno(stdout))) {
1022 // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal.
1023 should_colorize = true;
1024 }
1025#endif
1026
1027 if (gArgs.IsArgSet("-color")) {
1028 const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)};
1029 if (color == "always") {
1030 should_colorize = true;
1031 } else if (color == "never") {
1032 should_colorize = false;
1033 } else if (color != "auto") {
1034 throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never.");
1035 }
1036 }
1037
1038 if (should_colorize) {
1039 RESET = "\x1B[0m";
1040 GREEN = "\x1B[32m";
1041 BLUE = "\x1B[34m";
1042 YELLOW = "\x1B[33m";
1043 MAGENTA = "\x1B[35m";
1044 CYAN = "\x1B[36m";
1045 }
1046
1047 std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
1048 result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
1049 result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
1050
1051 const double ibd_progress{result["verificationprogress"].get_real()};
1052 std::string ibd_progress_bar;
1053 // Display the progress bar only if IBD progress is less than 99%
1054 if (ibd_progress < 0.99) {
1055 GetProgressBar(ibd_progress, ibd_progress_bar);
1056 // Add padding between progress bar and IBD progress
1057 ibd_progress_bar += " ";
1058 }
1059
1060 result_string += strprintf("Verification progress: %s%.4f%%\n", ibd_progress_bar, ibd_progress * 100);
1061 result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
1062
1063 result_string += strprintf(
1064 "%sNetwork: in %s, out %s, total %s%s\n",
1065 GREEN,
1066 result["connections"]["in"].getValStr(),
1067 result["connections"]["out"].getValStr(),
1068 result["connections"]["total"].getValStr(),
1069 RESET);
1070 result_string += strprintf("Version: %s\n", result["version"].getValStr());
1071 result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
1072
1073 // proxies
1074 std::map<std::string, std::vector<std::string>> proxy_networks;
1075 std::vector<std::string> ordered_proxies;
1076
1077 for (const UniValue& network : result["networks"].getValues()) {
1078 const std::string proxy = network["proxy"].getValStr();
1079 if (proxy.empty()) continue;
1080 // Add proxy to ordered_proxy if has not been processed
1081 if (proxy_networks.find(proxy) == proxy_networks.end()) ordered_proxies.push_back(proxy);
1082
1083 proxy_networks[proxy].push_back(network["name"].getValStr());
1084 }
1085
1086 std::vector<std::string> formatted_proxies;
1087 formatted_proxies.reserve(ordered_proxies.size());
1088 for (const std::string& proxy : ordered_proxies) {
1089 formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", ")));
1090 }
1091 result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", "));
1092
1093 result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
1094
1095 if (!result["has_wallet"].isNull()) {
1096 const std::string walletname = result["walletname"].getValStr();
1097 result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET);
1098
1099 result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr());
1100 if (!result["unlocked_until"].isNull()) {
1101 result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr());
1102 }
1103 result_string += strprintf("Transaction fee rate (-paytxfee) (%s/kvB): %s\n\n", CURRENCY_UNIT, result["paytxfee"].getValStr());
1104 }
1105 if (!result["balance"].isNull()) {
1106 result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr());
1107 }
1108
1109 if (!result["balances"].isNull()) {
1110 result_string += strprintf("%sBalances%s\n", CYAN, RESET);
1111
1112 size_t max_balance_length{10};
1113
1114 for (const std::string& wallet : result["balances"].getKeys()) {
1115 max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length);
1116 }
1117
1118 for (const std::string& wallet : result["balances"].getKeys()) {
1119 result_string += strprintf("%*s %s\n",
1120 max_balance_length,
1121 result["balances"][wallet].getValStr(),
1122 wallet.empty() ? "\"\"" : wallet);
1123 }
1124 result_string += "\n";
1125 }
1126
1127 const std::string warnings{result["warnings"].getValStr()};
1128 result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings);
1129
1130 result.setStr(result_string);
1131}
1132
1138{
1139 std::optional<std::string> wallet_name{};
1140 if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
1142 return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name);
1143}
1144
1150static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
1151{
1152 if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
1153 if (args.size() == 0) {
1154 args.emplace_back(DEFAULT_NBLOCKS);
1155 } else if (args.at(0) == "0") {
1156 throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
1157 }
1158 args.emplace(args.begin() + 1, address);
1159}
1160
1161static int CommandLineRPC(int argc, char *argv[])
1162{
1163 std::string strPrint;
1164 int nRet = 0;
1165 try {
1166 // Skip switches
1167 while (argc > 1 && IsSwitchChar(argv[1][0])) {
1168 argc--;
1169 argv++;
1170 }
1171 std::string rpcPass;
1172 if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
1173 NO_STDIN_ECHO();
1174 if (!StdinReady()) {
1175 fputs("RPC password> ", stderr);
1176 fflush(stderr);
1177 }
1178 if (!std::getline(std::cin, rpcPass)) {
1179 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
1180 }
1181 if (StdinTerminal()) {
1182 fputc('\n', stdout);
1183 }
1184 gArgs.ForceSetArg("-rpcpassword", rpcPass);
1185 }
1186 std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
1187 if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
1188 NO_STDIN_ECHO();
1189 std::string walletPass;
1190 if (args.size() < 1 || args[0].substr(0, 16) != "walletpassphrase") {
1191 throw std::runtime_error("-stdinwalletpassphrase is only applicable for walletpassphrase(change)");
1192 }
1193 if (!StdinReady()) {
1194 fputs("Wallet passphrase> ", stderr);
1195 fflush(stderr);
1196 }
1197 if (!std::getline(std::cin, walletPass)) {
1198 throw std::runtime_error("-stdinwalletpassphrase specified but failed to read from standard input");
1199 }
1200 if (StdinTerminal()) {
1201 fputc('\n', stdout);
1202 }
1203 args.insert(args.begin() + 1, walletPass);
1204 }
1205 if (gArgs.GetBoolArg("-stdin", false)) {
1206 // Read one arg per line from stdin and append
1207 std::string line;
1208 while (std::getline(std::cin, line)) {
1209 args.push_back(line);
1210 }
1211 if (StdinTerminal()) {
1212 fputc('\n', stdout);
1213 }
1214 }
1215 std::unique_ptr<BaseRequestHandler> rh;
1216 std::string method;
1217 if (gArgs.IsArgSet("-getinfo")) {
1218 rh.reset(new GetinfoRequestHandler());
1219 } else if (gArgs.GetBoolArg("-netinfo", false)) {
1220 if (!args.empty() && args.at(0) == "help") {
1221 tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc);
1222 return 0;
1223 }
1224 rh.reset(new NetinfoRequestHandler());
1225 } else if (gArgs.GetBoolArg("-generate", false)) {
1226 const UniValue getnewaddress{GetNewAddress()};
1227 const UniValue& error{getnewaddress.find_value("error")};
1228 if (error.isNull()) {
1229 SetGenerateToAddressArgs(getnewaddress.find_value("result").get_str(), args);
1230 rh.reset(new GenerateToAddressRequestHandler());
1231 } else {
1232 ParseError(error, strPrint, nRet);
1233 }
1234 } else if (gArgs.GetBoolArg("-addrinfo", false)) {
1235 rh.reset(new AddrinfoRequestHandler());
1236 } else {
1237 rh.reset(new DefaultRequestHandler());
1238 if (args.size() < 1) {
1239 throw std::runtime_error("too few parameters (need at least command)");
1240 }
1241 method = args[0];
1242 args.erase(args.begin()); // Remove trailing method name from arguments vector
1243 }
1244 if (nRet == 0) {
1245 // Perform RPC call
1246 std::optional<std::string> wallet_name{};
1247 if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
1248 const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
1249
1250 // Parse reply
1251 UniValue result = reply.find_value("result");
1252 const UniValue& error = reply.find_value("error");
1253 if (error.isNull()) {
1254 if (gArgs.GetBoolArg("-getinfo", false)) {
1255 if (!gArgs.IsArgSet("-rpcwallet")) {
1256 GetWalletBalances(result); // fetch multiwallet balances and append to result
1257 }
1258 ParseGetInfoResult(result);
1259 }
1260
1261 ParseResult(result, strPrint);
1262 } else {
1263 ParseError(error, strPrint, nRet);
1264 }
1265 }
1266 } catch (const std::exception& e) {
1267 strPrint = std::string("error: ") + e.what();
1268 nRet = EXIT_FAILURE;
1269 } catch (...) {
1270 PrintExceptionContinue(nullptr, "CommandLineRPC()");
1271 throw;
1272 }
1273
1274 if (strPrint != "") {
1275 tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
1276 }
1277 return nRet;
1278}
1279
1281{
1282#ifdef WIN32
1283 common::WinCmdLineArgs winArgs;
1284 std::tie(argc, argv) = winArgs.get();
1285#endif
1288 tfm::format(std::cerr, "Error: Initializing networking failed\n");
1289 return EXIT_FAILURE;
1290 }
1291 event_set_log_callback(&libevent_log_cb);
1292
1293 try {
1294 int ret = AppInitRPC(argc, argv);
1295 if (ret != CONTINUE_EXECUTION)
1296 return ret;
1297 }
1298 catch (const std::exception& e) {
1299 PrintExceptionContinue(&e, "AppInitRPC()");
1300 return EXIT_FAILURE;
1301 } catch (...) {
1302 PrintExceptionContinue(nullptr, "AppInitRPC()");
1303 return EXIT_FAILURE;
1304 }
1305
1306 int ret = EXIT_FAILURE;
1307 try {
1308 ret = CommandLineRPC(argc, argv);
1309 }
1310 catch (const std::exception& e) {
1311 PrintExceptionContinue(&e, "CommandLineRPC()");
1312 } catch (...) {
1313 PrintExceptionContinue(nullptr, "CommandLineRPC()");
1314 }
1315 return ret;
1316}
bool HelpRequested(const ArgsManager &args)
Definition args.cpp:660
void SetupHelpOptions(ArgsManager &args)
Add help options to the args manager.
Definition args.cpp:665
bool CheckDataDirOption(const ArgsManager &args)
Definition args.cpp:730
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[]
static constexpr int8_t UNKNOWN_NETWORK
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT
static const int CONTINUE_EXECUTION
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
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
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.
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.
static constexpr std::array NETWORKS
static void SetupCliArgs(ArgsManager &argsman)
static const std::string DEFAULT_COLOR_SETTING
Default -color setting.
std::chrono::system_clock CliClock
static constexpr std::array NETWORK_SHORT_NAMES
static std::string http_errorstring(int code)
static const bool DEFAULT_NAMED
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:270
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
std::unique_ptr< CBaseChainParams > CreateBaseChainParams(const ChainType chain)
Port numbers for incoming Tor connections (8334, 18334, 38334, 48334, 18445) have been chosen arbitra...
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.
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:749
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition args.cpp:545
bool ParseParameters(int argc, const char *const argv[], std::string &error)
Definition args.cpp:178
std::string GetHelpMessage() const
Get the help string.
Definition args.cpp:591
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition args.cpp:370
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition args.cpp:481
fs::path GetConfigFilePath() const
Return config file path (read-only)
Definition args.cpp:736
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition args.cpp:456
bool ReadConfigFiles(std::string &error, bool ignore_invalid_keys=false)
Definition config.cpp:121
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition args.cpp:506
void AddArg(const std::string &name, const std::string &help, unsigned int flags, const OptionsCategory &cat)
Add argument.
Definition args.cpp:563
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.
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
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
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
bool isNull() const
Definition univalue.h:79
const std::string & getValStr() const
Definition univalue.h:68
const UniValue & get_obj() const
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:360
UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert named arguments to command-specific RPC representation.
Definition client.cpp:372
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:73
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition fs.h:151
void format(std::ostream &out, const char *fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition string.h:156
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition string.h:115
std::vector< UniValue > JSONRPCProcessBatchReply(const UniValue &in)
Parse JSON-RPC batch reply into a vector.
Definition request.cpp:168
bool GetAuthCookie(std::string *cookie_out)
Read the RPC authentication cookie from disk.
Definition request.cpp:140
UniValue JSONRPCRequestObj(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition request.cpp:41
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
Definition request.cpp:51
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:79
@ HTTP_BAD_REQUEST
Definition protocol.h:14
@ HTTP_SERVICE_UNAVAILABLE
Definition protocol.h:20
@ HTTP_UNAUTHORIZED
Definition protocol.h:15
@ HTTP_NOT_FOUND
Definition protocol.h:17
@ HTTP_INTERNAL_SERVER_ERROR
Definition protocol.h:19
@ 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:50
bool StdinReady()
Definition stdin.cpp:52
bool StdinTerminal()
Definition stdin.cpp:43
#define NO_STDIN_ECHO()
Definition stdin.h:13
std::optional< T > ToIntegral(std::string_view str)
Convert string to integral type T.
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
constexpr auto TicksSinceEpoch(Timepoint t)
Definition time.h:50
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
const UniValue NullUniValue
Definition univalue.cpp:16
std::string EncodeBase64(Span< const unsigned char > input)
bool ParseUInt8(std::string_view str, uint8_t *out)
Convert decimal string to unsigned 8-bit integer with strict parse error feedback.
std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
Format a paragraph of text to a fixed width, adding spaces for indentation to any added line.
bool SplitHostPort(std::string_view in, uint16_t &portOut, std::string &hostOut)
Splits socket address string into host string and port value.
assert(!tx.IsCoinBase())