fix(network): remove every address book entry for a banned peer IP - #11173
Merged
mergify[bot] merged 3 commits intoAug 5, 2026
Merged
Conversation
`by_addr` is ordered by reconnection order, not grouped by IP, so entries for one IP can be separated by entries for other IPs. The ban path scanned it with `skip_while(ip != banned).take_while(ip == banned)`, which stops at the first entry for a different IP, so an entry for the banned IP on another port could survive the ban. Such an entry could never leave the book: `reconnection_peers()` did not filter banned IPs, so it was selected as a candidate on every crawl, the ban check in `update()` rejected the resulting `UpdateAttempt`, and its state therefore never changed. It stayed at the front of the reconnection order until the node restarted, logging a warning on every cycle. Scan the whole book when removing a banned IP's entries, and skip banned IPs in `reconnection_peers()` so a surviving entry cannot be selected. Log a change rejected for a banned IP at `debug` instead of `warn`: remote peers can gossip a banned address at any time, so they were choosing the level of that message.
oxarbitrage
previously approved these changes
Aug 4, 2026
oxarbitrage
left a comment
Contributor
There was a problem hiding this comment.
Fix looks good, thanks!
Verbosity can be reduced a bit, i left some comments.
Co-authored-by: oxarbitrage <21685097+oxarbitrage@users.noreply.github.com>
oxarbitrage
approved these changes
Aug 5, 2026
Contributor
Merge Queue Status
This pull request spent 46 minutes 18 seconds in the queue, including 45 minutes 25 seconds running CI. Required conditions to merge
|
47 tasks
jvff
added a commit
that referenced
this pull request
Aug 6, 2026
Link the banned-peer behavior to its implementation PR #11173 so the release notes identify the reviewed change.
jvff
added a commit
that referenced
this pull request
Aug 6, 2026
Link the banned-peer logging change to its implementation PR #11173 so crate consumers can find its review context.
jvff
added a commit
that referenced
this pull request
Aug 6, 2026
Link the banned-peer behavior fix to its implementation PR #11173 so crate consumers can find the implementation and tests.
jvff
added a commit
that referenced
this pull request
Aug 10, 2026
Link the banned-peer behavior to its implementation PR #11173 so the release notes identify the reviewed change.
jvff
added a commit
that referenced
this pull request
Aug 10, 2026
Link the banned-peer logging change to its implementation PR #11173 so crate consumers can find its review context.
jvff
added a commit
that referenced
this pull request
Aug 10, 2026
Link the banned-peer behavior fix to its implementation PR #11173 so crate consumers can find the implementation and tests.
jvff
added a commit
that referenced
this pull request
Aug 10, 2026
Link the banned-peer behavior to its implementation PR #11173 so the release notes identify the reviewed change.
jvff
added a commit
that referenced
this pull request
Aug 10, 2026
Link the banned-peer logging change to its implementation PR #11173 so crate consumers can find its review context.
jvff
added a commit
that referenced
this pull request
Aug 10, 2026
Link the banned-peer behavior fix to its implementation PR #11173 so crate consumers can find the implementation and tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Closes #11134.
After a peer IP is banned, an address book entry for that same IP on a different port could
survive the ban and then stay at the front of the reconnection order for the lifetime of the
process, producing a burst of
attempted to add a banned peer addr to address bookwarnings onevery crawl cycle. This was observed on Mainnet shortly after NU6.3 activation, when the wave of
misbehavior bans made it easy to hit.
Solution
Three changes in
zebra-network, matching the three defects in the issue:AddressBook::update()scans the whole book when removing a banned IP's entries.by_addris anOrderedMapkeyed by reconnection order, not grouped by IP, so entries for oneIP can be separated by entries for other IPs. The previous
skip_while(ip != banned).take_while(ip == banned)scan stops at the first entry for adifferent IP, leaving any later same-IP entry behind. Replaced with a
filterover all keys.reconnection_peers()skips banned IPs. A surviving entry was selected as a candidate onevery crawl; the ban check at the top of
update()then rejected the resultingUpdateAttempt,so the entry was never marked
AttemptPendingand its position never changed. Filteringbans_by_iphere means a banned IP cannot be handed out even if an entry does reach the book.The rejected-change log moved from
warntodebug. Remote peers can gossip a bannedaddress at any time, so the volume of this message was effectively chosen by them.
Tests
One regression test in
zebra-network/src/address_book/tests/vectors.rs,ban_removes_every_entry_for_the_banned_ip. It builds a book where an unrelated IP sorts betweentwo entries that share the banned IP, and asserts that ordering before doing anything else —
with contiguous entries the test would pass against the old code and prove nothing. After the ban
it asserts the book holds exactly the unrelated entry, and that
reconnection_peers()yieldsexactly the unrelated address.
Negative controls, reverting the real implementation rather than reimplementing it, one change at a
time:
ban_removes_every_entry_for_the_banned_ipmain)Change 1 is pinned directly by the test. Change 2 is defence in depth: with change 1 in place the
surviving-entry state is not reachable through
update(), so no test can isolate it. That is whythe two earlier tests are now one — the second could only fail when the first also failed.
Gates run locally on this branch, all in debug:
cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets -- -D warnings— clean, for both thedefault-release-binariesand the
proptest-impl lightwalletd-grpc-tests zebra-checkpointsfeature setscargo clippy -p zebra-network --all-targets -- -D warningsandcargo build -p zebra-network,for each of the four feature combinations CI runs per crate — clean
cargo test -p zebra-network --lib— 216 passed, 0 failed (217 before the two tests became one).github/scripts/validate-pr-changelogs.sh <merge-base> HEAD true fix false— exit 0Specifications & References
attempted to add a banned peer addrWARNs every crawl cycle #11134 — the issue, including the Mainnet log signature and the three-defect breakdown.Follow-up Work
The issue's third defect also has a structural half that this PR does not touch: when
update()returns
None,CandidateSet::next()returns before theMIN_OUTBOUND_PEER_CONNECTION_INTERVALsleep, so queued crawler demand drains in a tight loop.With the two changes above, a ban no longer drives that loop. The general fix belongs with the
CandidateSet/AddressBookrefactor in #1976, as the issue notes.AI Disclosure
reviewed and verified by hand, including the negative-control runs in the table above.
PR Checklist
type(scope): description