fix: make observed_peer_subnets fork-aware#820
Conversation
…scription During fork transitions, a peer may be subscribed to the same subnet via topics from different forks (e.g., ssv.v2.42 and /ssv/mainnet/boole/42). Previously, unsubscribing from one fork's topic would clear the subnet bit entirely, even though the peer was still subscribed via the other fork's topic. Track subscriptions per fork so each fork maintains its own bitmap. Query methods aggregate across forks using bitwise union when checking subnet coverage. Closes sigp#818 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This comment was marked as outdated.
This comment was marked as outdated.
Address review feedback: log a warning when a subnet ID exceeds the bitfield capacity instead of silently ignoring it, and add a test verifying that duplicate subscribes on the same fork+subnet don't break state.
|
Claude finished @diegomrsantos's task in 2m 6s —— View job Code Review Complete ✅SummaryThis PR successfully addresses issue #818 by making peer subnet tracking fork-aware. The implementation correctly handles fork transitions where peers may be subscribed to the same subnet via different fork-specific topics simultaneously (e.g., Strengths1. Correct Problem ModelingThe core data structure change from 2. Transparent AggregationThe fn aggregate_fork_bitmaps(fork_map: &HashMap<Fork, Bitfield<Fixed<U128>>>) -> Bitfield<Fixed<U128>> {
let mut result = Bitfield::default();
for bitfield in fork_map.values() {
result = result.union(bitfield);
}
result
}3. Proper Memory ManagementThe cleanup logic (connection.rs:150-157) prevents memory bloat by removing empty entries: if !subscribed {
if bitfield.is_zero() {
fork_map.remove(&fork);
}
if fork_map.is_empty() {
self.observed_peer_subnets.remove(&peer);
}
}4. Excellent Test CoverageThe test suite comprehensively validates the fix:
All tests follow AAA pattern with descriptive names. 5. Out-of-Bounds ProtectionCommit 19f675c adds proper bounds checking with a warning (connection.rs:142-147): if idx < bitfield.len() {
let _ = bitfield.set(idx, subscribed);
} else {
tracing::warn!(
subnet = idx,
max = bitfield.len(),
"Subnet ID exceeds bitfield capacity"
);
}This prevents silent failures when invalid subnet IDs are passed. Areas for Improvement1. Performance Consideration (Minor)The
Observation: This is likely not a bottleneck given:
Recommendation: Monitor in production. If profiling shows this as hot, consider caching the aggregated result and invalidating on subscription changes. Verdict: No action needed now. 2. Type Safety Consideration (Very Minor)The Concurrency & Thread SafetyThe Verdict: Thread-safe ✅ Security AssessmentNo security vulnerabilities identified:
Verdict: Secure ✅ Documentation Quality
Verdict: Documentation excellent ✅ Edge Cases Covered
Code Quality
Final Verdict✅ Approve This PR correctly solves the fork transition bug with a clean, well-tested implementation. The solution is minimal, focused, and production-ready. The minor performance observation is not a blocker - the implementation is already optimized for the common case (small number of forks, fast bitwise operations). The addition of the out-of-bounds warning in the second commit demonstrates attention to robustness. The comprehensive test suite ensures the fix works correctly and won't regress. Completed Tasks
|
317a350 to
1d13d0b
Compare
1d13d0b to
481f223
Compare
Add peer ID to the out-of-bounds subnet warning log and replace the imperative loop in aggregate_fork_bitmaps with a functional fold.
Merge Queue StatusRule:
This pull request spent 10 minutes 32 seconds in the queue, including 8 minutes 56 seconds running CI. Required conditions to merge
|
Issue Addressed
Closes #818
Proposed Changes
During fork transitions (e.g., Alan → Boole), peers may be subscribed to the same subnet via topics from different forks simultaneously — for example,
ssv.v2.42(Alan) and/ssv/mainnet/boole/42(Boole). The peer manager'sobserved_peer_subnetspreviously stored a single flat bitmap per peer, so unsubscribing from one fork's topic would incorrectly clear the subnet bit even though the peer was still subscribed via the other fork.This PR makes the subscription tracking fork-aware by maintaining a separate bitmap per fork per peer. Query methods (used for peer counting, disconnect decisions, and priority dialing) aggregate across forks transparently, so the rest of the peer management logic is unaffected.
Additional Info
topic::parse_topic()(which returns fork info) instead oftopic::parse_subnet_id()(which discarded it), threading the fork identity through to the connection manager.