From 12242b17a52c9833e6504f3f0a5b247a6e2fc5f9 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Mon, 27 Jul 2020 08:20:26 +0200 Subject: [PATCH 01/13] cli: create initial -netinfo option, NetinfoRequestHandler class --- src/bitcoin-cli.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index cf52b710cb..b7634a7c84 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -56,6 +56,8 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result 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); + argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional boolean argument can be passed for a detailed peers listing (default: false).", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS); + SetupChainParamsBaseOptions(argsman); argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcclienttimeout=", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -291,6 +293,37 @@ class GetinfoRequestHandler: public BaseRequestHandler } }; +/** Process netinfo requests */ +class NetinfoRequestHandler : public BaseRequestHandler +{ +private: + bool m_verbose{false}; //!< Whether user requested verbose -netinfo report +public: + const int ID_PEERINFO = 0; + const int ID_NETWORKINFO = 1; + + UniValue PrepareRequest(const std::string& method, const std::vector& args) override + { + if (!args.empty()) { + const std::string arg{ToLower(args.at(0))}; + m_verbose = (arg == "true" || arg == "t"); + } + UniValue result(UniValue::VARR); + result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO)); + result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); + return result; + } + + UniValue ProcessReply(const UniValue& batch_in) override + { + const std::vector batch{JSONRPCProcessBatchReply(batch_in)}; + if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO]; + if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; + std::string result; + return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1); + } +}; + /** Process RPC generatetoaddress request. */ class GenerateToAddressRequestHandler : public BaseRequestHandler { @@ -618,6 +651,8 @@ static int CommandLineRPC(int argc, char *argv[]) std::string method; if (gArgs.IsArgSet("-getinfo")) { rh.reset(new GetinfoRequestHandler()); + } else if (gArgs.GetBoolArg("-netinfo", false)) { + rh.reset(new NetinfoRequestHandler()); } else if (gArgs.GetBoolArg("-generate", false)) { const UniValue getnewaddress{GetNewAddress()}; const UniValue& error{find_value(getnewaddress, "error")}; From 54799b66b466c0d015e6fe2f820663cc5d8e7998 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:16:36 +0200 Subject: [PATCH 02/13] cli: add ipv6 and onion address type detection helpers --- src/bitcoin-cli.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index b7634a7c84..1fb02b1a49 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -39,6 +39,8 @@ static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; static const bool DEFAULT_NAMED=false; static const int CONTINUE_EXECUTION=-1; +static const std::string ONION{".onion"}; +static const size_t ONION_LEN{ONION.size()}; /** Default number of blocks to generate for RPC generatetoaddress. */ static const std::string DEFAULT_NBLOCKS = "1"; @@ -297,6 +299,21 @@ class GetinfoRequestHandler: public BaseRequestHandler class NetinfoRequestHandler : public BaseRequestHandler { private: + bool IsAddrIPv6(const std::string& addr) const + { + return !addr.empty() && addr.front() == '['; + } + bool IsInboundOnion(const std::string& addr_local, int mapped_as) const + { + return mapped_as == 0 && addr_local.find(ONION) != std::string::npos; + } + bool IsOutboundOnion(const std::string& addr, int mapped_as) const + { + const size_t addr_len{addr.size()}; + const size_t onion_pos{addr.rfind(ONION)}; + return mapped_as == 0 && onion_pos != std::string::npos && addr_len > ONION_LEN && + (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN); + } bool m_verbose{false}; //!< Whether user requested verbose -netinfo report public: const int ID_PEERINFO = 0; From a3653c159e4f5c887eec9ea608e474eaa299fc07 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:19:27 +0200 Subject: [PATCH 03/13] cli: tally peer connections by type --- src/bitcoin-cli.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1fb02b1a49..037b5fd06d 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -336,6 +336,39 @@ class NetinfoRequestHandler : public BaseRequestHandler const std::vector batch{JSONRPCProcessBatchReply(batch_in)}; if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO]; if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; + + // Count peer connection totals. + int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}; // inbound conn counters + int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}; // outbound conn counters + const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]}; + + for (const UniValue& peer : getpeerinfo.getValues()) { + const std::string addr{peer["addr"].get_str()}; + const std::string addr_local{peer["addrlocal"].isNull() ? "" : peer["addrlocal"].get_str()}; + const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()}; + const bool is_block_relay{!peer["relaytxes"].get_bool()}; + const bool is_inbound{peer["inbound"].get_bool()}; + if (is_inbound) { + if (IsAddrIPv6(addr)) { + ++ipv6_i; + } else if (IsInboundOnion(addr_local, mapped_as)) { + ++onion_i; + } else { + ++ipv4_i; + } + if (is_block_relay) ++block_relay_i; + } else { + if (IsAddrIPv6(addr)) { + ++ipv6_o; + } else if (IsOutboundOnion(addr, mapped_as)) { + ++onion_o; + } else { + ++ipv4_o; + } + if (is_block_relay) ++block_relay_o; + } + } + std::string result; return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1); } From 19377b2fd24704bff6c805946575b116f07d5e0d Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:29:10 +0200 Subject: [PATCH 04/13] cli: start dashboard report with chain and version header --- src/bitcoin-cli.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 037b5fd06d..a2670ed1a6 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -315,6 +315,12 @@ class NetinfoRequestHandler : public BaseRequestHandler (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN); } bool m_verbose{false}; //!< Whether user requested verbose -netinfo report + std::string ChainToString() const + { + if (gArgs.GetChainName() == CBaseChainParams::TESTNET) return " testnet"; + if (gArgs.GetChainName() == CBaseChainParams::REGTEST) return " regtest"; + return ""; + } public: const int ID_PEERINFO = 0; const int ID_NETWORKINFO = 1; @@ -369,7 +375,10 @@ class NetinfoRequestHandler : public BaseRequestHandler } } - std::string result; + // Generate report header. + const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]}; + std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())}; + return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1); } }; From d3f77b736e43b187771b901a6a3452f83c116918 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:25:04 +0200 Subject: [PATCH 05/13] cli: create inbound/outbound peer connections report --- src/bitcoin-cli.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index a2670ed1a6..7833aa946c 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -344,8 +344,8 @@ class NetinfoRequestHandler : public BaseRequestHandler if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; // Count peer connection totals. - int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}; // inbound conn counters - int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}; // outbound conn counters + int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters + int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]}; for (const UniValue& peer : getpeerinfo.getValues()) { @@ -379,6 +379,14 @@ class NetinfoRequestHandler : public BaseRequestHandler const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]}; std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())}; + // Report peer connection totals by type. + total_i = ipv4_i + ipv6_i + onion_i; + total_o = ipv4_o + ipv6_o + onion_o; + result += " ipv4 ipv6 onion total block-relay\n"; + result += strprintf("in %5i %5i %5i %5i %5i\n", ipv4_i, ipv6_i, onion_i, total_i, block_relay_i); + result += strprintf("out %5i %5i %5i %5i %5i\n", ipv4_o, ipv6_o, onion_o, total_o, block_relay_o); + result += strprintf("total %5i %5i %5i %5i %5i\n", ipv4_i + ipv4_o, ipv6_i + ipv6_o, onion_i + onion_o, total_i + total_o, block_relay_i + block_relay_o); + return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1); } }; From c227100919dd2422b29eb3bca9c0f1a7983cc3a8 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:51:22 +0200 Subject: [PATCH 06/13] cli: create local addresses, ports, and scores report --- src/bitcoin-cli.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 7833aa946c..ac59665c12 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -387,6 +387,17 @@ class NetinfoRequestHandler : public BaseRequestHandler result += strprintf("out %5i %5i %5i %5i %5i\n", ipv4_o, ipv6_o, onion_o, total_o, block_relay_o); result += strprintf("total %5i %5i %5i %5i %5i\n", ipv4_i + ipv4_o, ipv6_i + ipv6_o, onion_i + onion_o, total_i + total_o, block_relay_i + block_relay_o); + // Report local addresses, ports, and scores. + result += "\nLocal addresses"; + const UniValue& local_addrs{networkinfo["localaddresses"]}; + if (local_addrs.empty()) { + result += ": n/a\n"; + } else { + for (const UniValue& addr : local_addrs.getValues()) { + result += strprintf("\n%-40i port %5i score %6i", addr["address"].get_str(), addr["port"].get_int(), addr["score"].get_int()); + } + } + return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1); } }; From 3a0ab93e1ce8d91235a6d46a57c6cb110fc5bf03 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:49:39 +0200 Subject: [PATCH 07/13] cli: add NetType enum struct and NetTypeEnumToString() --- src/bitcoin-cli.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index ac59665c12..b9877f5126 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -315,6 +315,20 @@ class NetinfoRequestHandler : public BaseRequestHandler (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN); } bool m_verbose{false}; //!< Whether user requested verbose -netinfo report + enum struct NetType { + ipv4, + ipv6, + onion, + }; + std::string NetTypeEnumToString(NetType t) + { + switch (t) { + case NetType::ipv4: return "ipv4"; + case NetType::ipv6: return "ipv6"; + case NetType::onion: return "onion"; + } // no default case, so the compiler can warn about missing cases + assert(false); + } std::string ChainToString() const { if (gArgs.GetChainName() == CBaseChainParams::TESTNET) return " testnet"; @@ -354,10 +368,13 @@ class NetinfoRequestHandler : public BaseRequestHandler const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()}; const bool is_block_relay{!peer["relaytxes"].get_bool()}; const bool is_inbound{peer["inbound"].get_bool()}; + NetType net_type{NetType::ipv4}; if (is_inbound) { if (IsAddrIPv6(addr)) { + net_type = NetType::ipv6; ++ipv6_i; } else if (IsInboundOnion(addr_local, mapped_as)) { + net_type = NetType::onion; ++onion_i; } else { ++ipv4_i; @@ -365,8 +382,10 @@ class NetinfoRequestHandler : public BaseRequestHandler if (is_block_relay) ++block_relay_i; } else { if (IsAddrIPv6(addr)) { + net_type = NetType::ipv6; ++ipv6_o; } else if (IsOutboundOnion(addr, mapped_as)) { + net_type = NetType::onion; ++onion_o; } else { ++ipv4_o; From f5edd66e5d136b229c805af9e6ea73218f6cedde Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 16:59:57 +0200 Subject: [PATCH 08/13] cli: create vector of Peer structs for peers data --- src/bitcoin-cli.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index b9877f5126..e50d9c43fa 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -320,6 +320,22 @@ class NetinfoRequestHandler : public BaseRequestHandler ipv6, onion, }; + struct Peer { + int id; + int mapped_as; + int version; + int64_t conn_time; + int64_t last_recv; + int64_t last_send; + double min_ping; + double ping; + std::string addr; + std::string sub_version; + NetType net_type; + bool is_block_relay; + bool is_outbound; + bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); } + }; std::string NetTypeEnumToString(NetType t) { switch (t) { @@ -357,9 +373,10 @@ class NetinfoRequestHandler : public BaseRequestHandler if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO]; if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; - // Count peer connection totals. + // Count peer connection totals, and if m_verbose is true, store peer data in a vector of structs. int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters + std::vector peers; const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]}; for (const UniValue& peer : getpeerinfo.getValues()) { @@ -392,6 +409,18 @@ class NetinfoRequestHandler : public BaseRequestHandler } if (is_block_relay) ++block_relay_o; } + if (m_verbose) { + // Push data for this peer to the peers vector. + const int peer_id{peer["id"].get_int()}; + const int version{peer["version"].get_int()}; + const std::string sub_version{peer["subver"].get_str()}; + const int64_t conn_time{peer["conntime"].get_int64()}; + const int64_t last_recv{peer["lastrecv"].get_int64()}; + const int64_t last_send{peer["lastsend"].get_int64()}; + const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()}; + const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()}; + peers.push_back({peer_id, mapped_as, version, conn_time, last_recv, last_send, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound}); + } } // Generate report header. From ce57bf6cc0cdaf8233fd8a20e0d1c5b69d17d2a3 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 13 Aug 2020 17:13:14 +0200 Subject: [PATCH 09/13] cli: create peer connections report sorted by dir, minping --- src/bitcoin-cli.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index e50d9c43fa..ad514e7939 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -374,8 +374,11 @@ class NetinfoRequestHandler : public BaseRequestHandler if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; // Count peer connection totals, and if m_verbose is true, store peer data in a vector of structs. + const int64_t time_now{GetSystemTimeInSeconds()}; int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters + size_t max_peer_id_length{2}, max_version_length{1}; + bool is_asmap_on{false}; std::vector peers; const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]}; @@ -420,6 +423,9 @@ class NetinfoRequestHandler : public BaseRequestHandler const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()}; const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()}; peers.push_back({peer_id, mapped_as, version, conn_time, last_recv, last_send, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound}); + max_peer_id_length = std::max(ToString(peer_id).length(), max_peer_id_length); + max_version_length = std::max((ToString(version) + sub_version).length(), max_version_length); + is_asmap_on |= (mapped_as != 0); } } @@ -427,6 +433,35 @@ class NetinfoRequestHandler : public BaseRequestHandler const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]}; std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())}; + // Report detailed peer connections list sorted by direction and minimum ping time. + if (m_verbose && !peers.empty()) { + std::sort(peers.begin(), peers.end()); + result += "Peer connections sorted by direction and min ping\n<-> relay net minping ping lastsend lastrecv uptime "; + if (is_asmap_on) result += " asmap "; + result += strprintf("%*s %-*s address\n", max_peer_id_length, "id", max_version_length, "version"); + for (const Peer& peer : peers) { + std::string version{ToString(peer.version) + peer.sub_version}; + result += strprintf( + "%3s %5s %5s%8s%7s %8s %8s%7s%*i %*s %-*s %s\n", + peer.is_outbound ? "out" : "in", + peer.is_block_relay ? "block" : "full", + NetTypeEnumToString(peer.net_type), + peer.min_ping == -1 ? "" : ToString(round(1000 * peer.min_ping)), + peer.ping == -1 ? "" : ToString(round(1000 * peer.ping)), + peer.last_send == 0 ? "" : ToString(time_now - peer.last_send), + peer.last_recv == 0 ? "" : ToString(time_now - peer.last_recv), + peer.conn_time == 0 ? "" : ToString((time_now - peer.conn_time) / 60), + is_asmap_on ? 7 : 0, // variable spacing + is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "", + max_peer_id_length, // variable spacing + peer.id, + max_version_length, // variable spacing + version == "0" ? "" : version, + peer.addr); + } + result += " ms ms sec sec min\n\n"; + } + // Report peer connection totals by type. total_i = ipv4_i + ipv6_i + onion_i; total_o = ipv4_o + ipv6_o + onion_o; From 644be659abfcf7c638e17795bafedad73bc55b27 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 30 Aug 2020 12:38:40 +0200 Subject: [PATCH 10/13] cli: add -netinfo server version check and error message --- src/bitcoin-cli.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index ad514e7939..f895d90feb 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -373,6 +373,11 @@ class NetinfoRequestHandler : public BaseRequestHandler if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO]; if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; + const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]}; + if (networkinfo["version"].get_int() < 209900) { + throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up"); + } + // Count peer connection totals, and if m_verbose is true, store peer data in a vector of structs. const int64_t time_now{GetSystemTimeInSeconds()}; int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters @@ -430,7 +435,6 @@ class NetinfoRequestHandler : public BaseRequestHandler } // Generate report header. - const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]}; std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())}; // Report detailed peer connections list sorted by direction and minimum ping time. From 4e2f2ddd6444d93dd7e60ec67cc393dfc2a29b9d Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Fri, 28 Aug 2020 14:03:06 +0200 Subject: [PATCH 11/13] cli: add getpeerinfo last_{block,transaction} to -netinfo --- src/bitcoin-cli.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index f895d90feb..d63a796a16 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -325,8 +325,10 @@ class NetinfoRequestHandler : public BaseRequestHandler int mapped_as; int version; int64_t conn_time; + int64_t last_blck; int64_t last_recv; int64_t last_send; + int64_t last_trxn; double min_ping; double ping; std::string addr; @@ -423,11 +425,13 @@ class NetinfoRequestHandler : public BaseRequestHandler const int version{peer["version"].get_int()}; const std::string sub_version{peer["subver"].get_str()}; const int64_t conn_time{peer["conntime"].get_int64()}; + const int64_t last_blck{peer["last_block"].get_int64()}; const int64_t last_recv{peer["lastrecv"].get_int64()}; const int64_t last_send{peer["lastsend"].get_int64()}; + const int64_t last_trxn{peer["last_transaction"].get_int64()}; const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()}; const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()}; - peers.push_back({peer_id, mapped_as, version, conn_time, last_recv, last_send, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound}); + peers.push_back({peer_id, mapped_as, version, conn_time, last_blck, last_recv, last_send, last_trxn, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound}); max_peer_id_length = std::max(ToString(peer_id).length(), max_peer_id_length); max_version_length = std::max((ToString(version) + sub_version).length(), max_version_length); is_asmap_on |= (mapped_as != 0); @@ -440,13 +444,13 @@ class NetinfoRequestHandler : public BaseRequestHandler // Report detailed peer connections list sorted by direction and minimum ping time. if (m_verbose && !peers.empty()) { std::sort(peers.begin(), peers.end()); - result += "Peer connections sorted by direction and min ping\n<-> relay net minping ping lastsend lastrecv uptime "; + result += "Peer connections sorted by direction and min ping\n<-> relay net mping ping send recv txn blk uptime "; if (is_asmap_on) result += " asmap "; result += strprintf("%*s %-*s address\n", max_peer_id_length, "id", max_version_length, "version"); for (const Peer& peer : peers) { std::string version{ToString(peer.version) + peer.sub_version}; result += strprintf( - "%3s %5s %5s%8s%7s %8s %8s%7s%*i %*s %-*s %s\n", + "%3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s %s\n", peer.is_outbound ? "out" : "in", peer.is_block_relay ? "block" : "full", NetTypeEnumToString(peer.net_type), @@ -454,6 +458,8 @@ class NetinfoRequestHandler : public BaseRequestHandler peer.ping == -1 ? "" : ToString(round(1000 * peer.ping)), peer.last_send == 0 ? "" : ToString(time_now - peer.last_send), peer.last_recv == 0 ? "" : ToString(time_now - peer.last_recv), + peer.last_trxn == 0 ? "" : ToString((time_now - peer.last_trxn) / 60), + peer.last_blck == 0 ? "" : ToString((time_now - peer.last_blck) / 60), peer.conn_time == 0 ? "" : ToString((time_now - peer.conn_time) / 60), is_asmap_on ? 7 : 0, // variable spacing is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "", @@ -463,7 +469,7 @@ class NetinfoRequestHandler : public BaseRequestHandler version == "0" ? "" : version, peer.addr); } - result += " ms ms sec sec min\n\n"; + result += " ms ms sec sec min min min\n\n"; } // Report peer connection totals by type. From 077b3ac9284a90f33e31c64c49062ceaf815af60 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sat, 29 Aug 2020 11:28:48 +0200 Subject: [PATCH 12/13] cli: change -netinfo optional arg from bool to int --- src/bitcoin-cli.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index d63a796a16..e15e7d1d8e 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -58,7 +58,7 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result 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); - argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional boolean argument can be passed for a detailed peers listing (default: false).", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS); + argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument can be passed for a detailed peers listing (default: 0).", ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS); SetupChainParamsBaseOptions(argsman); argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -314,7 +314,8 @@ class NetinfoRequestHandler : public BaseRequestHandler return mapped_as == 0 && onion_pos != std::string::npos && addr_len > ONION_LEN && (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN); } - bool m_verbose{false}; //!< Whether user requested verbose -netinfo report + uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level + bool DetailsRequested() const { return m_details_level != 0; } enum struct NetType { ipv4, ipv6, @@ -360,8 +361,10 @@ class NetinfoRequestHandler : public BaseRequestHandler UniValue PrepareRequest(const std::string& method, const std::vector& args) override { if (!args.empty()) { - const std::string arg{ToLower(args.at(0))}; - m_verbose = (arg == "true" || arg == "t"); + uint8_t n{0}; + if (ParseUInt8(args.at(0), &n)) { + m_details_level = n; + } } UniValue result(UniValue::VARR); result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO)); @@ -380,7 +383,7 @@ class NetinfoRequestHandler : public BaseRequestHandler throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up"); } - // Count peer connection totals, and if m_verbose is true, store peer data in a vector of structs. + // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs. const int64_t time_now{GetSystemTimeInSeconds()}; int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters @@ -419,7 +422,7 @@ class NetinfoRequestHandler : public BaseRequestHandler } if (is_block_relay) ++block_relay_o; } - if (m_verbose) { + if (DetailsRequested()) { // Push data for this peer to the peers vector. const int peer_id{peer["id"].get_int()}; const int version{peer["version"].get_int()}; @@ -442,7 +445,7 @@ class NetinfoRequestHandler : public BaseRequestHandler std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())}; // Report detailed peer connections list sorted by direction and minimum ping time. - if (m_verbose && !peers.empty()) { + if (DetailsRequested() && !peers.empty()) { std::sort(peers.begin(), peers.end()); result += "Peer connections sorted by direction and min ping\n<-> relay net mping ping send recv txn blk uptime "; if (is_asmap_on) result += " asmap "; From bf1f913c4405cba35c8f99ec07b407940eb955b6 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sat, 29 Aug 2020 12:20:37 +0200 Subject: [PATCH 13/13] cli -netinfo: display multiple levels of details --- src/bitcoin-cli.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index e15e7d1d8e..abbb949001 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -58,7 +58,7 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result 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); - argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument can be passed for a detailed peers listing (default: 0).", ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS); + 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).", ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS); SetupChainParamsBaseOptions(argsman); argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -315,7 +315,9 @@ class NetinfoRequestHandler : public BaseRequestHandler (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN); } uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level - bool DetailsRequested() const { return m_details_level != 0; } + bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; } + bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; } + bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; } enum struct NetType { ipv4, ipv6, @@ -387,7 +389,7 @@ class NetinfoRequestHandler : public BaseRequestHandler const int64_t time_now{GetSystemTimeInSeconds()}; int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters - size_t max_peer_id_length{2}, max_version_length{1}; + size_t max_peer_id_length{2}, max_addr_length{0}; bool is_asmap_on{false}; std::vector peers; const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]}; @@ -436,7 +438,7 @@ class NetinfoRequestHandler : public BaseRequestHandler const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()}; peers.push_back({peer_id, mapped_as, version, conn_time, last_blck, last_recv, last_send, last_trxn, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound}); max_peer_id_length = std::max(ToString(peer_id).length(), max_peer_id_length); - max_version_length = std::max((ToString(version) + sub_version).length(), max_version_length); + max_addr_length = std::max(addr.length() + 1, max_addr_length); is_asmap_on |= (mapped_as != 0); } } @@ -449,11 +451,11 @@ class NetinfoRequestHandler : public BaseRequestHandler std::sort(peers.begin(), peers.end()); result += "Peer connections sorted by direction and min ping\n<-> relay net mping ping send recv txn blk uptime "; if (is_asmap_on) result += " asmap "; - result += strprintf("%*s %-*s address\n", max_peer_id_length, "id", max_version_length, "version"); + result += strprintf("%*s %-*s%s\n", max_peer_id_length, "id", IsAddressSelected() ? max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : ""); for (const Peer& peer : peers) { std::string version{ToString(peer.version) + peer.sub_version}; result += strprintf( - "%3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s %s\n", + "%3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s%s\n", peer.is_outbound ? "out" : "in", peer.is_block_relay ? "block" : "full", NetTypeEnumToString(peer.net_type), @@ -468,9 +470,9 @@ class NetinfoRequestHandler : public BaseRequestHandler is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "", max_peer_id_length, // variable spacing peer.id, - max_version_length, // variable spacing - version == "0" ? "" : version, - peer.addr); + IsAddressSelected() ? max_addr_length : 0, // variable spacing + IsAddressSelected() ? peer.addr : "", + IsVersionSelected() && version != "0" ? version : ""); } result += " ms ms sec sec min min min\n\n"; }