Adds an optional peer_ids parameter to getpeerinfo to filter results by peer IDs.
This is useful when using bitcoin-cli or the console to inspect specific peers referenced in debug.log.
Adds an optional peer_ids parameter to getpeerinfo to filter results by peer IDs.
This is useful when using bitcoin-cli or the console to inspect specific peers referenced in debug.log.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32741.
See the guideline for information on the review process.
| Type | Reviewers |
|---|---|
| ACK | danielabrozzoni, rkrux |
| Concept NACK | stickies-v |
If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.
No conflicts as of last run.
121@@ -122,7 +122,9 @@ static RPCHelpMan getpeerinfo()
122 return RPCHelpMan{
123 "getpeerinfo",
124 "Returns data about each connected network peer as a json array of objects.",
125- {},
126+ {
127+ {"nodeid", RPCArg::Type::NUM, RPCArg::Default{-1}, "Filter results to the peer with this nodeid. Specify -1 to return all peers."}
203 + HelpExampleRpc("getpeerinfo", "")
204+ + HelpExampleRpc("getpeerinfo", "7")
205 },
206 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
207 {
208+ const int filterPeerId = request.params[0].isNull() ? -1 : request.params[0].getInt<int>();
self.Arg to get the value.
Also, you can use the
self.Argto get the value.
Would this approach be preferable to avoid using a sentinel value like -1?
0const UniValue* nodeid_arg = self.MaybeArg<UniValue>("nodeid");
1const std::optional<NodeId> filter_id = nodeid_arg ? std::make_optional(nodeid_arg->getInt<NodeId>()) : std::nullopt;
0if (filter_id && stats.nodeid != *filter_id) continue;
1...
2if (filter_id && stats.nodeid == *filter_id) break;
Would it be acceptable to touch util.cpp for this to add a int64_t template for MaybeArg or stick to the sentinel approach?
0TMPL_INST(nullptr, std::optional<int64_t>, maybe_arg ? std::optional{maybe_arg->getInt<int64_t>()} : std::nullopt;);
Please squash your commits according to https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md#squashing-commits
Also, missing tests?
bitcoin-cli getpeerinfo | jq '[.[] | select(.id == 24430)]' for this. Doesn’t work in the GUI console though.
🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/44049534572
LLM reason (✨ experimental): The CI failure is caused by lint errors, specifically a trailing whitespace issue detected during the lint check.
Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
187+ assert_equal(filtered[0]["id"], peer["id"])
188+ self.log.debug("Filtered peer id {}: OK".format(peer["id"]))
189+
190+ self.log.info("Check getpeerinfo with nonexistent nodeid filter")
191+ nonexistent_id = max(p["id"] for p in all_peers) + 1000
192+ assert_equal(self.nodes[0].getpeerinfo(nonexistent_id), [])
Would it make sense to throw a JSONRPCError(RPC_MISC_ERROR,"Peer does not exist") if the peer does not exist, Similar to the getblockfrompeer instead of sending back an empty array?
where error is thrown in PeerManagerImpl::FetchBlock
getpeerbyid). The name getpeerinfo is arguably a bit misleading since it returns a list of peers, but given that it’s established, returning an empty array when filtering seems appropriate to me.
peer_id for consistency
Concept ACK, I think this is a good addition, as introducing a peer_id parameter to filter the getpeerinfo response is useful for debugging.
This needs a release note :)
307@@ -300,6 +308,9 @@ static RPCHelpMan getpeerinfo()
308 obj.pushKV("session_id", stats.m_session_id);
309
310 ret.push_back(std::move(obj));
311+ if (opt_peer_id && stats.nodeid == *opt_peer_id) {
opt_peer_id here, since we’d never get here if it’s set to something different
121@@ -122,7 +122,9 @@ static RPCHelpMan getpeerinfo()
122 return RPCHelpMan{
123 "getpeerinfo",
124 "Returns data about each connected network peer as a json array of objects.",
125- {},
126+ {
127+ {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Only return info for the specified peer."}
Since we have to iterate peers to find the right one, maybe this should be a list (unordered_set in C++)
I agree, the last push swapped to an unordered_set with an array argument
121@@ -122,7 +122,9 @@ static RPCHelpMan getpeerinfo()
122 return RPCHelpMan{
123 "getpeerinfo",
124 "Returns data about each connected network peer as a json array of objects.",
Can mention in the RPC help.
0- "Returns data about each connected network peer as a json array of objects.",
1+ "Returns data about each connected network peer as a json array of objects.\n"
2+ "Optionally filter by peer index.\n",
Concept ACK
Useful addition, I can see myself using the filtering here.
As mentioned in #32741#pullrequestreview-2953107634, a release note would be helpful as per this: https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#release-notes
🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/45364101888
LLM reason (✨ experimental): Lint check failed due to a coding style error in rpc_net.py caused by an unnecessary semicolon.
Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
peer_ids (array) instead of a single peer_id. This allows filtering by multiple peers in one call and aligns better with RPC design. Functional tests adjusted.
I would do peer_id|peer_ids and accept both a single number (most common use) or an array (efficient).
I don’t think RPCArg currently supports a single argument that can be either a num or an array (Maybe RPCArg::Type::ANY?). One option would be to add two separate optional arguments: one for a single ID and another for the list, then merge both into the filter set internally. That would allow getpeerinfo 7 for convenience and getpeerinfo -1 '[1,3,4,7]' for multiple.
Figured it out in the latest push, skipped type checking for peer_ids
I would do peer_id|peer_ids and accept both a single number (most common use) or an array (efficient).
Skipped type checking and added simple cases so getpeerinfo 7 and getpeerinfo '[1,2,3]' should work
🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/47411071680
LLM reason (✨ experimental): The CI failure caused by a lint check for a missing trailing newline in the release notes markdown file.
Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
130+ "A JSON array of peer IDs, or a single peer ID as a number.\n"
131+ "If omitted, returns all peers.\n",
132+ {
133+ {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Peer ID"},
134+ },
135+ {RPCArgOptions{.skip_type_check = true}}
0 {RPCArgOptions{.skip_type_check = true, .type_str = {"", "numeric or array"}}}
120@@ -121,8 +121,18 @@ static RPCHelpMan getpeerinfo()
121 {
122 return RPCHelpMan{
123 "getpeerinfo",
124- "Returns data about each connected network peer as a json array of objects.",
125- {},
126+ "Returns data about each connected network peer as a json array of objects.\n"
127+ "Optionally filter by peer ids.\n",
128+ {
129+ {"peer_ids", RPCArg::Type::ARR, RPCArg::Optional::OMITTED,
0 {"peer_id|peer_ids", RPCArg::Type::ARR, RPCArg::Optional::OMITTED,
198+
199+ self.log.info("Check getpeerinfo with nonexistent peer_id")
200+ nonexistent_id = max(p["id"] for p in all_peers) + 1000
201+ assert_equal(node.getpeerinfo(nonexistent_id), [])
202+ assert_raises_rpc_error(-8, "must be a number or an array of numbers", node.getpeerinfo, {})
203+
Nit if retouched:
0diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
1index 209e804021..47834c8375 100755
2--- a/test/functional/rpc_net.py
3+++ b/test/functional/rpc_net.py
4@@ -200,6 +200,7 @@ class NetTest(BitcoinTestFramework):
5 nonexistent_id = max(p["id"] for p in all_peers) + 1000
6 assert_equal(node.getpeerinfo(nonexistent_id), [])
7 assert_raises_rpc_error(-8, "must be a number or an array of numbers", node.getpeerinfo, {})
8+ assert_equal(len(node.getpeerinfo([ids[0], nonexistent_id])), 1)
9
10 no_version_peer.peer_disconnect()
11 self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 2)
the commit message is wrong?
Thanks. Must’ve replaced it by mistake when I rebased. Fixed now
120@@ -121,8 +121,18 @@ static RPCHelpMan getpeerinfo()
121 {
122 return RPCHelpMan{
123 "getpeerinfo",
124- "Returns data about each connected network peer as a json array of objects.",
125- {},
126+ "Returns data about each connected network peer as a json array of objects.\n"
127+ "Optionally filter by peer ids.\n",
tACK 9b6090d8d985f3ce61eaa587511bbeeeedf28cb9
I left a micro-nit, but I’d say avoid fixing it unless you already need to update, so you don’t invalidate the ack :)
Tested Ack
02026-03-08T22:42:05.369198Z TestFramework (INFO): Test getpeerinfo
12026-03-08T22:42:08.423239Z TestFramework (INFO): Check getpeerinfo output before a version message was sent
22026-03-08T22:42:08.476781Z TestFramework (INFO): Check getpeerinfo filtering: all, single, multiple
32026-03-08T22:42:08.479393Z TestFramework (INFO): Check getpeerinfo with nonexistent peer_id
jq as already suggested earlier. If there are use cases I’m missing, please document them in OP.
Concept NACK. This seems easy enough to address client-side such as with
jqas already suggested earlier. If there are use cases I’m missing, please document them in OP.
As mentioned jq and command line tools don’t work in the qt console. And generally it’s just bad to serialize and send the whole peer list when you’re only interested in specific ones. I’m sure in a lot of cases it’s easier to depend on command line tools but that doesn’t mean you shouldn’t have targeted fetches.
Anyway cheers thanks for the ‘review’
And generally it’s just bad to serialize and send the whole peer list when you’re only interested in specific ones
If/when serialization overhead becomes meaningful, sure, it can make sense to add server-side filters. Until we’re there, this is just unnecessary complexity.
As mentioned jq and command line tools don’t work in the qt console.
That sounds like a gui / console issue more than an rpc issue? If the rpc is not the blocker, we shouldn’t change the rpc.
Anyway cheers thanks for the ‘review’
I’m not sure being dismissive of people spending time on your PR is going to be helpful for your making progress.
And generally it’s just bad to serialize and send the whole peer list when you’re only interested in specific ones
If/when serialization overhead becomes meaningful, sure, it can make sense to add server-side filters. Until we’re there, this is just unnecessary complexity.
Serialization overhead is probably the only ‘meaningful’ bit out of all the get peer info functionality there is in the code. (Outside of being subjective) In both cpu time, memory and transmission if there is any.
As mentioned jq and command line tools don’t work in the qt console.
That sounds like a gui / console issue more than an rpc issue? If the rpc is not the blocker, we shouldn’t change the rpc.
Anyway cheers thanks for the ‘review’
I’m not sure being dismissive of people spending time on your PR is going to be helpful for your making progress.
I wasn’t being dismissive of you ‘spending time’ on ‘my pr’. I was being thankful for your suggestion of using jq and depend on command line utilities instead of having it filtered server side. I’m not sure being passive aggressive in a PR review is going to be helpful to me, muh progress, or yours.
Again thanks for spending the time to write a review and a thoughtful suggestion!
The GUI has a special peers tab, and the RPC console is only for expert users.
If the peers tab is lacking some feature, it may be better to submit the feature to the gui repo.
I don’t mind the changes here, but I am ~0 on them.
The GUI has a special peers tab, and the RPC console is only for expert users.
If the peers tab is lacking some feature, it may be better to submit the feature to the gui repo.
I don’t mind the changes here, but I am ~0 on them.
This is what I’m using and maintaining for my own builds. It covers my gui, cli, rpc, tracing uses. I don’t intend to write specialized gui coverage or submit this to the gui repo as it’s a rpc change.
If you don’t use or need any of the features it’s fine to be ~0 on them.
getpeerinfo, which returns at most 125 peers, the cost seems small to me.
That said, I am not strongly opposed. Though I’m not sure where the line is. If we add this, the same argument applies to other read RPCs, like listunspent, (filter with txid etc…)