From 4489ab526add168daf36684eb3d86957ff3388c1 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 16 Feb 2025 11:25:43 -0600 Subject: [PATCH 1/3] netinfo: return local services in the default report Credit to l0rinc for refactoring ServicesList(). Co-authored-by: l0rinc Co-authored-by: Daniela Brozzoni --- src/bitcoin-cli.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 14a0cb245f..1f4b512f42 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -463,6 +463,17 @@ private: } return str; } + static std::string ServicesList(const UniValue& services) + { + std::string str{services.size() ? services[0].get_str() : ""}; + for (size_t i{1}; i < services.size(); ++i) { + str += ", " + services[i].get_str(); + } + for (auto& c: str) { + c = (c == '_' ? ' ' : ToLower(c)); + } + return str; + } public: static constexpr int ID_PEERINFO = 0; @@ -636,7 +647,8 @@ public: } } - // Report local addresses, ports, and scores. + // Report local services, addresses, ports, and scores. + result += strprintf("\n\nLocal services: %s", ServicesList(networkinfo["localservicesnames"])); result += "\n\nLocal addresses"; const std::vector& local_addrs{networkinfo["localaddresses"].getValues()}; if (local_addrs.empty()) { From f7d2db28e90297664390d527881ebbbf2c6ce6f0 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Sun, 16 Feb 2025 09:22:46 -0600 Subject: [PATCH 2/3] netinfo: return shortened services, if peers list requested When the detailed peers list is requested, return the shortened services in the -netinfo header in the same format as the "serv" column, instead of the full names list in the report. --- src/bitcoin-cli.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1f4b512f42..56f2a906b5 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -566,7 +566,8 @@ public: } // Generate report header. - std::string result{strprintf("%s client %s%s - server %i%s\n\n", CLIENT_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt(), networkinfo["subversion"].get_str())}; + const std::string services{DetailsRequested() ? strprintf(" - services %s", FormatServices(networkinfo["localservicesnames"])) : ""}; + std::string result{strprintf("%s client %s%s - server %i%s%s\n\n", CLIENT_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt(), networkinfo["subversion"].get_str(), services)}; // Report detailed peer connections list sorted by direction and minimum ping time. if (DetailsRequested() && !m_peers.empty()) { @@ -648,7 +649,9 @@ public: } // Report local services, addresses, ports, and scores. - result += strprintf("\n\nLocal services: %s", ServicesList(networkinfo["localservicesnames"])); + if (!DetailsRequested()) { + result += strprintf("\n\nLocal services: %s", ServicesList(networkinfo["localservicesnames"])); + } result += "\n\nLocal addresses"; const std::vector& local_addrs{networkinfo["localaddresses"].getValues()}; if (local_addrs.empty()) { From 721a051320f2c10d2e9c89c985f180da81d64dca Mon Sep 17 00:00:00 2001 From: l0rinc Date: Thu, 24 Jul 2025 15:42:53 -0600 Subject: [PATCH 3/3] test: add coverage for -netinfo header and local services Co-authored-by: Jon Atack --- test/functional/interface_bitcoin_cli.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index 5b27f0b850..734f3110af 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -81,6 +81,19 @@ class TestBitcoinCli(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_cli() + def test_netinfo(self): + """Test -netinfo output format.""" + self.log.info("Test -netinfo header and separate local services line") + out = self.nodes[0].cli('-netinfo').send_cli().splitlines() + assert out[0].startswith(f"{self.config['environment']['CLIENT_NAME']} client ") + assert any(re.match(r"^Local services:.+network", line) for line in out) + + self.log.info("Test -netinfo local services are moved to header if details are requested") + det = self.nodes[0].cli('-netinfo', '1').send_cli().splitlines() + self.log.debug(f"Test -netinfo 1 header output: {det[0]}") + assert re.match(rf"^{re.escape(self.config['environment']['CLIENT_NAME'])} client.+services nwl2?$", det[0]) + assert not any(line.startswith("Local services:") for line in det) + def run_test(self): """Main test logic""" self.generate(self.nodes[0], BLOCKS) @@ -377,6 +390,8 @@ class TestBitcoinCli(BitcoinTestFramework): self.log.info("*** Wallet not compiled; cli getwalletinfo and -getinfo wallet tests skipped") self.generate(self.nodes[0], 25) # maintain block parity with the wallet_compiled conditional branch + self.test_netinfo() + self.log.info("Test -version with node stopped") self.stop_node(0) cli_response = self.nodes[0].cli('-version').send_cli()