fix(network): reject outbound connections to non-serving peers#11071
fix(network): reject outbound connections to non-serving peers#11071upbqdn wants to merge 3 commits into
Conversation
A large share of reachable mainnet listeners currently advertise no services, and Zebra kept them in outbound slots and routed block requests to them, which can stall a fresh sync at genesis. Reject outbound handshakes without NODE_NETWORK and record the peer's advertised services in the address book, so the crawler stops re-dialing and gossiping it. Inbound and isolated connections are unaffected.
|
So these are coming from communicating to Zakura pruned nodes on the network, as the v1 p2p stack does not yet enable communicating which block heights you know to peers. The impact of rejecting connections to them would be effectively kicking them off the p2p network, when they are very capable (if not more capable) of communicating recent blocks faster (they spend less delays in DB time :) ) We are aware of exchanges using pruned functionality. Are there other ways we can achieve the desired goal? (Perhaps setting a cap on number of pruned peers you talk to, or rejecting connecting to these during checkpoint sync?) |
I believe they would still be able to connect to Zebra nodes, it's just that Zebra nodes won't connect to them. So they won't be kicked out off the p2p network. But we're looking into if the behaviour can be improved. |
…cing Review feedback: the services=0 peers are pruned nodes that can serve recent blocks, so an unconditional rejection would stop Zebra from ever peering with them. Gate the rejection on the node being away from the network tip (mirroring Bitcoin Core's IBD service requirements), and stop recording the peer's advertised services on rejection - stored non-serving services exclude a peer from outbound connections permanently, which would defeat the gate. Rejected peers now get the standard retryable failure from the crawler instead.
Motivation
Closes #11061.
Solution
While the node is away from the network tip, reject outbound handshakes whose
versionmessagedoesn't advertise
NODE_NETWORK(newHandshakeError::MissingRequiredServices, mirroring theobsolete-version rejection). This keeps a fresh sync's outbound slots free for block-serving
peers, matching Bitcoin Core's policy of requiring block-serving peers during initial block
download.
At or near the tip the requirement is dropped, so Zebra keeps peering with non-serving peers —
the
services=0listeners on mainnet are pruned Zakura nodes that serve recent blocks well(review feedback below). For the same reason, the rejection records a plain retryable failure
rather than the peer's advertised services: stored non-serving services would exclude the peer
from outbound connections permanently, so it could never be dialed once the node reaches the
tip. Inbound and isolated connections are always exempt, so light clients can still connect.
Tests
direct and proxy rejected without
NODE_NETWORKwhile syncing; accepted at the network tip;inbound, isolated, and serving outbound always accepted), plus an end-to-end
Handshakeservice test asserting the rejected peer is not recorded with poisoning services.
-D warnings, check--locked, docs, feature powerset,nextest ci profile, udeps, deny, MSRV build).
Follow-up Work
PeerSet(route block requests only toNODE_NETWORKpeers), as discussed in the review thread — useful on top of this gate, but insufficient
alone, because non-serving peers would still occupy outbound slots during a sync.
(like Bitcoin's
NODE_NETWORK_LIMITED, or the served-range advertising in Zakura'sexperimental v2 stack), which needs a ZIP.
AI Disclosure
reviewed by the author.
PR Checklist
type(scope): description