Fix bloom filter cascade: gate re-announcements on content change

FilterAnnounce messages never settled in steady state because
handle_filter_announce unconditionally marked all peers for
re-announcement on every inbound filter, creating a perpetual
ping-pong at ~1 message/sec.

Added last_sent_filters tracking to BloomState. mark_changed_peers()
computes outgoing filters and compares against what was last sent,
only marking peers whose filter content actually differs.
This commit is contained in:
Johnathan Corgan
2026-02-14 23:36:39 +00:00
parent 57b2eef995
commit 89bc9cc4b0
3 changed files with 51 additions and 10 deletions
+8 -9
View File
@@ -61,6 +61,7 @@ impl Node {
// Build and encode
let announce = self.build_filter_announce(peer_addr);
let sent_filter = announce.filter.clone();
let encoded = announce.encode().map_err(|e| NodeError::SendFailed {
node_addr: *peer_addr,
reason: format!("FilterAnnounce encode failed: {}", e),
@@ -69,8 +70,9 @@ impl Node {
// Send
self.send_encrypted_link_message(peer_addr, &encoded).await?;
// Record send
// Record send and store the filter for change detection
self.bloom_state.record_update_sent(*peer_addr, now_ms);
self.bloom_state.record_sent_filter(*peer_addr, sent_filter);
if let Some(peer) = self.peers.get_mut(peer_addr) {
peer.clear_filter_update_needed();
}
@@ -165,14 +167,11 @@ impl Node {
"Received FilterAnnounce"
);
// Our outgoing filter changed — mark all other peers for update
let other_peers: Vec<NodeAddr> = self
.peers
.keys()
.filter(|addr| *addr != from)
.copied()
.collect();
self.bloom_state.mark_all_updates_needed(other_peers);
// Check which peers' outgoing filters actually changed
let peer_addrs: Vec<NodeAddr> = self.peers.keys().copied().collect();
let peer_filters = self.peer_inbound_filters();
self.bloom_state
.mark_changed_peers(from, &peer_addrs, &peer_filters);
}
/// Check bloom filter state on tick (called from event loop).
+2 -1
View File
@@ -107,7 +107,8 @@ impl Node {
}
}
// Bloom filter cleanup: our outgoing filter changed (lost a peer's filter)
// Bloom filter cleanup: clear state for removed peer, mark remaining
self.bloom_state.remove_peer_state(node_addr);
let remaining_peers: Vec<NodeAddr> = self.peers.keys().copied().collect();
self.bloom_state.mark_all_updates_needed(remaining_peers);