cli: return local services in -netinfo #31886

pull jonatack wants to merge 3 commits into bitcoin:master from jonatack:2025-02-netinfo-services changing 2 files +32 −2
  1. jonatack commented at 6:31 pm on February 16, 2025: member

    Add local services info to -netinfo dashboard that already provides this info for each of the peer connections.

    • bitcoin-cli -netinfo with no args passed provides a nice easy-to-understand services list:
    0Bitcoin Core client v28.99.0 - server 70016/Satoshi:28.99.0/
    1
    2         ipv4    ipv6   onion     i2p   cjdns   total   block  manual
    3in          0       0      12       8       0      20
    4out         6       0       4       3       2      15       3       4
    5total       6       0      16      11       2      35
    6
    7Local services: network, bloom, witness, compact filters, network limited, p2p v2
    8
    9Local addresses
    
    • With a details level passed, e.g. -netinfo 3, print the services in the versions header instead (to avoid adding a line for more static information), in the same format as the peers list (see -netinfo help for info on the output of the serv column):
     0Bitcoin Core client v28.99.0 - server 70016/Satoshi:28.99.0/ - services nbwcl2
     1
     2<->   type   net   serv  v  mping   ping send recv  txn  blk  hb addrp addrl  age  asmap  id version
     3 in        onion         1    283    498   48   48    *              .         77        388 70016
     4 in        onion   nwl2  2    318    485    5  111                             79        372 70016/Satoshi:28.0.0/
     5 in        onion    nwl  1    342    344    4    1   53             96         84        344 70016/Satoshi:26.0.0/
     6 in        onion    nwl  1    411    601    4    1   35            124         85        339 70016/Satoshi:26.0.0/
     7 in        onion  nwcl2  2    436   4330    2    2    2             31         13        623 70016/Satoshi:28.0.0/
     8 in        onion    wl2  2    445    503    4    4    6            138         81        363 70016/Satoshi:28.0.0/
     9 in        onion    nwl  1    462    726    4    1   56             92         81        365 70016/Satoshi:23.0.0/
    10 in        onion    nwl  1    500    765    4    1   34             94         83        351 70016/Satoshi:25.0.0/
    11 in        onion   nwl2  2    578    684    4    0    1            134         87        327 70016/Satoshi:28.0.0/
    12 in          i2p   nwl2  2    712   1322    4    2   35            204     1   93        308 70016/Satoshi:27.2.0/
    13 in        onion   nwl2  2    727    873    5    5   56            162         85        342 70016/Satoshi:27.1.0/
    14 in          i2p   nwl2  2    749    976    4    2   25            120         72        408 70016/Satoshi:27.1.0/
    15 in          i2p   nwl2  2    776    954    4    1    0             72         68        426 70016/Satoshi:28.0.0/
    16 in          i2p   nbwl  1    883   1735    4    4                  53         34        551 70016/Satoshi:26.0.0/
    17 in          i2p  nwcl2  2    920   1044    2    0    0            131         83        350 70016/Satoshi:28.0.0/
    18 in        onion     wl  1   1021  20832   29   67                   3         49        501 70016/Satoshi:23.0.0/
    19 in          i2p  nwcl2  2   1830   1830    5    0                   3          3        668 70016/Satoshi:27.1.0/
    20 in        onion    nwl  1  41155  41155   87  204                              4        658 70016/Satoshi:25.0.0/
    21out   full  ipv4   nwl2  2     74     93    0    0    0           1028         85   1221 338 70016/Satoshi:27.1.0/
    22out   full  ipv4    nwl  1     82    104    0    2    0    5  .   1076         95  13536 301 70016/Satoshi:26.0.0/
    23out   full  ipv4    nwl  1    147    178    2    2    0   28  .   1104         95 395570 300 70016/Satoshi:25.0.0/
    24out  block  ipv4   nwl2  2    166    513    2    2    *              .         88  38001 324 70016/Satoshi:27.2.0/
    25out   full  ipv4     wl  1    193    201    0    4    0           1035         94  31376 307 70016/Satoshi:25.99.0/
    26out   full  ipv4   nwl2  2    199    796    1    1    0           1027         94   9723 304 70016/Satoshi:27.2.0/
    27out manual cjdns   nwl2  2    213    235    1    9    0           1109         83        353 70016/Satoshi:28.99.0/
    28out   full onion   nbwl  1    282    457    3    3    1           1130         73        404 70016/Satoshi:25.0.0/
    29out  block onion   nbwl  1    324    353   23   23    *              .         85        341 70016/Satoshi:26.0.0/
    30out manual cjdns   nwl2  2    340    445    1    1    7           1059         82        361 70016/Satoshi:27.0.0/
    31out manual onion    wl2  2    386    386    1    1    1           1048         84        345 70016/Satoshi:28.99.0/
    32out manual   i2p  nwcl2  2    697   1084    1    1    8           1113     3   93        310 70016/Satoshi:27.0.0/
    33out   full   i2p  nwcl2  2    730   1254    1    9    0           1128         89        318 70016/Satoshi:28.0.0/
    34out   full   i2p  nwcl2  2    765   1804    1    1    1           1132         72        409 70016/Satoshi:28.0.0/
    35                               ms     ms  sec  sec  min  min                  min
    36
    37         ipv4    ipv6   onion     i2p   cjdns   total   block  manual
    38in          0       0      12       6       0      18
    39out         6       0       3       3       2      14       2       4
    40total       6       0      15       9       2      32
    41
    42Local addresses
    
  2. DrahtBot commented at 6:31 pm on February 16, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31886.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK l0rinc, danielabrozzoni, 0xB10C
    Stale ACK zaidmstrr

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #32821 (rpc: Handle -named argument parsing where ‘=’ character is used by zaidmstrr)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  3. DrahtBot added the label Scripts and tools on Feb 16, 2025
  4. zaidmstrr commented at 6:18 pm on February 20, 2025: contributor
    Code ACK 724546e I tested the changes on Ubuntu 24.04.2, and it’s working fine. I also manually reviewed the changed code for any logical errors. Although it’s very beneficial to check the node advertising services in a quick way.
  5. luke-jr referenced this in commit 4ffdf3acae on Feb 26, 2025
  6. luke-jr referenced this in commit b8356b8502 on Feb 26, 2025
  7. laanwj commented at 2:18 pm on March 3, 2025: member
    Seems fine to add this, though it also takes up a line for information that is always the same for the same node, dynamic status information is the most interesting to show in -*info calls.
  8. jonatack commented at 8:26 pm on March 3, 2025: member

    Thanks for taking a look!

    Seems fine to add this, though it also takes up a line for information

    Right, when calling -netinfo without a details level (without the peers list).

    I began by adding the services in the versions line, in the same format as in the peers list:

    0Bitcoin Core client v28.99.0 - server 70016/Satoshi:28.99.0/ - services nbwcl2
    1
    2<->   type   net   serv  v  mping   ping send recv  txn  blk  hb addrp addrl  age  asmap  id version
    3 in        onion   nwl2  2    318    485    5  111                             79        372 70016/Satoshi:28.0.0/
    4 in        onion    nwl  1    342    344    4    1   53             96         84        344 70016/Satoshi:26.0.0/
    5 in        onion    nwl  1    411    601    4    1   35            124         85        339 70016/Satoshi:26.0.0
    

    Then, when calling -netinfo without a details level, I thought it lacked context or looked a bit cryptic, so for that case I replaced it with the extra line that prints the services in an easy-to-read manner, which seems nice. Nevertheless, I could revert that change if you prefer it remain appended to the versions header, WDYT?

  9. laanwj commented at 9:11 pm on March 3, 2025: member

    Nevertheless, I could revert that change if you prefer it remain appended to the versions header, WDYT?

    i agree an alphabet soup like “nbwcl2” does not say anything without a legend what every character means.

    To be clear, i’m not against adding it, it’s only one line, it was more of a general concern about the direction.

  10. jonatack commented at 9:15 pm on March 3, 2025: member
    I’ve updated the pull description to be clearer about it.
  11. danielabrozzoni commented at 10:25 am on April 7, 2025: contributor

    Code Review ACK 724546e28a5778e1c3f5100406ed7237f76aab55

    This PR adds the “Local services” line to -netinfo, when called without details:

    When -netinfo is called with details, services are print in the header line:

    Adding service info to -netinfo improves usability, and the code looks good to me.

  12. in src/bitcoin-cli.cpp:484 in 724546e28a outdated
    480@@ -481,6 +481,16 @@ class NetinfoRequestHandler : public BaseRequestHandler
    481         }
    482         return str;
    483     }
    484+    std::string ServicesList(const UniValue& services)
    


    l0rinc commented at 10:11 am on April 28, 2025:

    The new additions don’t seem to be covered by any tests https://corecheck.dev/bitcoin/bitcoin/pulls/31886

    Actually the whole file is a blood-bath: https://maflcko.github.io/b-c-cov/total.coverage/src/bitcoin-cli.cpp.gcov.html

    But since the following functional tests seem to cover this file:

    • interface_bitcoin_cli.py
    • interface_rpc.py
    • rpc_deriveaddresses.py
    • rpc_generate.py
    • tool_signet_miner.py
    • wallet_createwallet.py
    • wallet_descriptor.py
    • wallet_importdescriptors.py
    • wallet_listreceivedby.py
    • wallet_multiwallet.py

    And interface_bitcoin_cli.py seems the closest, we could test the two scenarios like:

     0diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
     1index 2618c12e9f..17b27855c3 100755
     2--- a/test/functional/interface_bitcoin_cli.py
     3+++ b/test/functional/interface_bitcoin_cli.py
     4@@ -83,6 +83,18 @@ class TestBitcoinCli(BitcoinTestFramework):
     5     def skip_test_if_missing_module(self):
     6         self.skip_if_no_cli()
     7 
     8+    def test_netinfo_and_version_cli(self):
     9+        """Test -netinfo output format."""
    10+        self.log.info("Test -netinfo with header + separate local services line")
    11+        out = self.nodes[0].cli('-netinfo').send_cli().splitlines()
    12+        assert out[0].startswith("Bitcoin Core client ")
    13+        assert any(re.match(r"^Local services:.+network", line) for line in out)
    14+
    15+        self.log.info("Test -netinfo header includes services line")
    16+        det = self.nodes[0].cli('-netinfo', '3').send_cli().splitlines()
    17+        assert re.match(r"^Bitcoin Core client.+services nwl$", det[0])
    18+        assert not any(line.startswith("Local services:") for line in det)
    19+
    20     def run_test(self):
    21         """Main test logic"""
    22         self.generate(self.nodes[0], BLOCKS)
    23@@ -379,6 +391,8 @@ class TestBitcoinCli(BitcoinTestFramework):
    24             self.log.info("*** Wallet not compiled; cli getwalletinfo and -getinfo wallet tests skipped")
    25             self.generate(self.nodes[0], 25)  # maintain block parity with the wallet_compiled conditional branch
    26 
    27+        self.test_netinfo_and_version_cli()
    28+
    29         self.log.info("Test -version with node stopped")
    30         self.stop_node(0)
    31         cli_response = self.nodes[0].cli('-version').send_cli()
    

    jonatack commented at 10:00 pm on July 24, 2025:
    I’ve pulled in your suggestion here.
  13. in src/bitcoin-cli.cpp:476 in 724546e28a outdated
    488+            std::string s{ToLower((services[i].get_str()))};
    489+            std::ranges::replace(s, '_', ' ');
    490+            v.push_back(s);
    491+        }
    492+        return Join(v, ", ");
    493+    }
    


    l0rinc commented at 11:13 am on April 28, 2025:

    We’re currently getting each string, recreating it as lowercase, iterating it again for potential replacement, adding it to a vector, which we’re recreating at the end again as a string.

    We could refactor it slightly to be similar to FormatServices, by doing the lowercase and replace operations once on the final joined string instead of processing each string individually:

    0    static std::string ServicesList(const UniValue& services)
    1    {
    2        std::string res{services.size() ? services[0].get_str() : ""};
    3        for (size_t i{1}; i < services.size(); ++i) res += ", " + services[i].get_str();
    4        for (auto& c: res) c = (c == '_' ? ' ' : ToLower(c));
    5        return res;
    6    }
    

    Which constructs the final string directly, sanitizing it in one separate step.

    Or if you prefer the sorted alternative I suggested above:

    0    static std::string ServicesList(const UniValue& services)
    1    {
    2        std::set<std::string> sorted;
    3        for (auto& s :  services.getValues()) sorted.emplace(s.get_str());
    4        auto res{Join(sorted, ", ")};
    5        for (auto& c : res) c = (c == '_' ? ' ' : ToLower(c));
    6        return res;
    7    }
    

    But if you decide to keep the original, consider the following nits:

    Nit - extra parenthesis:

    0std::string s{ToLower(services[i].get_str())};
    

    Nit - consistent brace init:

    0for (size_t i{0}; i < services.size(); ++i) {
    

    Nit - can be made static:

    0static std::string ServicesList(const UniValue& services)
    

    Nit - we can iterate the values directly:

    0for (auto& s : services.getValues())
    

    jonatack commented at 10:03 pm on July 24, 2025:
    Not sure if it makes a measurable difference in performance, but I’m happy to use your version. Updated.

    l0rinc commented at 11:12 pm on July 24, 2025:
    It’s not necessarily about performance, rather just doing less work
  14. in src/bitcoin-cli.cpp:487 in 724546e28a outdated
    480@@ -481,6 +481,16 @@ class NetinfoRequestHandler : public BaseRequestHandler
    481         }
    482         return str;
    483     }
    484+    std::string ServicesList(const UniValue& services)
    485+    {
    486+        std::vector<std::string> v;
    487+        for (size_t i = 0; i < services.size(); ++i) {
    


    l0rinc commented at 12:04 pm on April 28, 2025:

    should we maybe sort to avoid an order like:

    0Local services: network, witness, network limited, p2p v2
    

    it may make sense to have:

    0Local services: network, network limited, p2p v2, witness
    

    jonatack commented at 8:46 pm on July 24, 2025:
    I kept the order the same as in RPC getpeerinfo, the GUI, and the -netinfo peers list – If those are not sorted, I don’t think this should be, either.
  15. l0rinc changes_requested
  16. l0rinc commented at 12:33 pm on April 28, 2025: contributor

    Concept ACK

    I have tested it manually, seems to work correctly, but I would encourage the author to cover it with some simple functional tests and to consider sorting the values to make sure similar values end up closer together

  17. danielabrozzoni commented at 11:28 am on July 11, 2025: contributor
    Hey @jonatack, I noticed there are still a couple of comments open. Let me know if you’d like any help addressing them :)
  18. jonatack force-pushed on Jul 24, 2025
  19. jonatack commented at 10:09 pm on July 24, 2025: member
    Updated per review feedback (thanks!)
  20. jonatack force-pushed on Jul 24, 2025
  21. in src/bitcoin-cli.cpp:586 in fc37b64422 outdated
    582@@ -572,7 +583,8 @@ class NetinfoRequestHandler : public BaseRequestHandler
    583         }
    584 
    585         // Generate report header.
    586-        std::string result{strprintf("%s client %s%s - server %i%s\n\n", CLIENT_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt<int>(), networkinfo["subversion"].get_str())};
    587+        const std::string_view services{DetailsRequested() ? strprintf(" - services %s", FormatServices(networkinfo["localservicesnames"])) : ""};
    


    l0rinc commented at 11:11 pm on July 24, 2025:
    This should likely be an std::string instead

    jonatack commented at 3:08 pm on July 25, 2025:
    Done
  22. l0rinc approved
  23. in test/functional/interface_bitcoin_cli.py:93 in fc37b64422 outdated
    88+        self.log.info("Test -netinfo header and separate local services line")
    89+        out = self.nodes[0].cli('-netinfo').send_cli().splitlines()
    90+        assert out[0].startswith("Bitcoin Core client ")
    91+        assert any(re.match(r"^Local services:.+network", line) for line in out)
    92+
    93+        self.log.info("Test -netinfo with a details level passed appends local services to the header")
    


    l0rinc commented at 11:14 pm on July 24, 2025:

    nit: the log sounds a bit awkward (singular/plural & double verb) Edit:

    0        self.log.info("Test the local services are moved to header for higher details level")
    

    jonatack commented at 3:08 pm on July 25, 2025:
    Reworked it 👍
  24. DrahtBot added the label CI failed on Jul 25, 2025
  25. jonatack force-pushed on Jul 25, 2025
  26. in test/functional/interface_bitcoin_cli.py:90 in 8df2b0b230 outdated
    82@@ -83,6 +83,18 @@ def set_test_params(self):
    83     def skip_test_if_missing_module(self):
    84         self.skip_if_no_cli()
    85 
    86+    def test_netinfo(self):
    87+        """Test -netinfo output format."""
    88+        self.log.info("Test -netinfo header and separate local services line")
    89+        out = self.nodes[0].cli('-netinfo').send_cli().splitlines()
    90+        assert out[0].startswith("Bitcoin Core client ")
    


    luke-jr commented at 2:13 am on July 27, 2025:
    {self.config['environment']['CLIENT_NAME']}

    jonatack commented at 11:11 pm on July 27, 2025:
    done, thanks
  27. in test/functional/interface_bitcoin_cli.py:95 in 8df2b0b230 outdated
    90+        assert out[0].startswith("Bitcoin Core client ")
    91+        assert any(re.match(r"^Local services:.+network", line) for line in out)
    92+
    93+        self.log.info("Test -netinfo local services are moved to header if details are requested")
    94+        det = self.nodes[0].cli('-netinfo', '1').send_cli().splitlines()
    95+        assert re.match(r"^Bitcoin Core client.+services nwl$", det[0])
    


    luke-jr commented at 2:13 am on July 27, 2025:
    {self.config['environment']['CLIENT_NAME']}

    jonatack commented at 11:11 pm on July 27, 2025:
    done
  28. luke-jr changes_requested
  29. jonatack force-pushed on Jul 27, 2025
  30. jonatack force-pushed on Jul 27, 2025
  31. DrahtBot removed the label CI failed on Jul 28, 2025
  32. l0rinc commented at 1:42 am on July 28, 2025: contributor

    ACK 943ee6768d9f94da70aaf90d346d4cf2ea1fa39d

    Thanks for adding and tweaking the test (checked, it’s still passing locally as well)

  33. DrahtBot requested review from danielabrozzoni on Jul 28, 2025
  34. DrahtBot requested review from zaidmstrr on Jul 28, 2025
  35. in src/bitcoin-cli.cpp:667 in fb9b118cde outdated
    664@@ -654,6 +665,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
    665         }
    666 
    667         // Report local addresses, ports, and scores.
    


    danielabrozzoni commented at 10:24 am on July 29, 2025:
    nit: add local services to the comment

    jonatack commented at 4:14 pm on July 29, 2025:
    good eye, done
  36. danielabrozzoni commented at 11:04 am on July 29, 2025: contributor

    tACK 943ee6768d9f94da70aaf90d346d4cf2ea1fa39d

    I left a micro nit, feel free to ignore if you don’t need to retouch

  37. jonatack force-pushed on Jul 29, 2025
  38. l0rinc commented at 5:37 pm on July 29, 2025: contributor

    I don’t see how a comment change could cause the new CI failure

    reACK caea5f0bbf67984b3a187334ceef9e3023175848

  39. DrahtBot requested review from danielabrozzoni on Jul 29, 2025
  40. jonatack commented at 6:35 pm on July 29, 2025: member

    Yes, I opened an issue to report it: #33090

    I don’t see how to re-start that test.

  41. jonatack requested review from luke-jr on Jul 29, 2025
  42. luke-jr referenced this in commit f629f102db on Jul 29, 2025
  43. luke-jr referenced this in commit f419ecdd47 on Jul 29, 2025
  44. luke-jr referenced this in commit 1a7429244e on Jul 29, 2025
  45. netinfo: return local services in the default report
    Credit to l0rinc for refactoring ServicesList().
    
    Co-authored-by: l0rinc <pap.lorinc@gmail.com>
    Co-authored-by: Daniela Brozzoni <danielabrozzoni@protonmail.com>
    4489ab526a
  46. 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.
    f7d2db28e9
  47. test: add coverage for -netinfo header and local services
    Co-authored-by: Jon Atack <jon@atack.com>
    721a051320
  48. jonatack force-pushed on Aug 4, 2025
  49. jonatack commented at 9:36 pm on August 4, 2025: member
    Rebased to re-run the CI, no other change: git range-diff d1b5831 caea5f0 721a051
  50. l0rinc commented at 9:45 pm on August 4, 2025: contributor
    Redid the rebase, reran the test, reACK 721a051320f2c10d2e9c89c985f180da81d64dca
  51. danielabrozzoni commented at 9:54 am on August 5, 2025: contributor
    reACK 721a051320f2c10d2e9c89c985f180da81d64dca
  52. 0xB10C commented at 10:52 am on August 5, 2025: contributor

    ACK 721a051320f2c10d2e9c89c985f180da81d64dca

    light code-review and tested -netinfo

    (also wondered if maintaining this as an external tool would be easier and allowed for faster iteration on it, but that’s out of scope here)

  53. ryanofsky assigned ryanofsky on Aug 8, 2025
  54. ryanofsky merged this on Aug 8, 2025
  55. ryanofsky closed this on Aug 8, 2025

  56. jonatack deleted the branch on Aug 8, 2025

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-08-08 12:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me