bb_runtime/framework/inbound_dedup.rs
1//! `InboundDedup` - sliding-window seen-message tracker per
2//! ENGINE.md §10.4.
3//!
4//! Recv-flavoured wire ops consult this set to discard envelopes
5//! that repeat a recently-seen message hash. Storage is bounded -
6//! when the window fills, the oldest entry is evicted.
7//!
8//! lands a simple LRU-by-insertion-order implementation;
9use std::collections::{HashSet, VecDeque};
10
11/// Default rolling window (8192 distinct message hashes). Picked to
12/// be roughly 2× the default ingress capacity so duplicates from
13/// concurrent peers don't collide with recent legit traffic.
14pub const DEFAULT_WINDOW_SIZE: usize = 8192;
15
16/// Sliding-window seen-message tracker.
17pub struct InboundDedup {
18 seen: HashSet<u64>,
19 order: VecDeque<u64>,
20 capacity: usize,
21}
22
23impl Default for InboundDedup {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl InboundDedup {
30 /// Construct with the default window size.
31 pub fn new() -> Self {
32 Self::with_capacity(DEFAULT_WINDOW_SIZE)
33 }
34
35 /// Construct with a custom window size. Sizes below 1 are
36 /// clamped to 1.
37 pub fn with_capacity(capacity: usize) -> Self {
38 let capacity = capacity.max(1);
39 Self {
40 seen: HashSet::with_capacity(capacity),
41 order: VecDeque::with_capacity(capacity),
42 capacity,
43 }
44 }
45
46 /// Has this hash been seen within the current window?
47 pub fn contains(&self, hash: u64) -> bool {
48 self.seen.contains(&hash)
49 }
50
51 /// Mark `hash` as seen. Returns `true` if the entry was
52 /// already present (a duplicate). On insertion the oldest
53 /// entry is evicted if the window is full.
54 pub fn record(&mut self, hash: u64) -> bool {
55 if !self.seen.insert(hash) {
56 return true;
57 }
58 self.order.push_back(hash);
59 if self.order.len() > self.capacity {
60 if let Some(evicted) = self.order.pop_front() {
61 self.seen.remove(&evicted);
62 }
63 }
64 false
65 }
66
67 /// Clear the entire window.
68 pub fn clear(&mut self) {
69 self.seen.clear();
70 self.order.clear();
71 }
72
73 /// Current window size (number of distinct entries).
74 pub fn len(&self) -> usize {
75 self.order.len()
76 }
77
78 /// `true` when no messages have been recorded yet.
79 pub fn is_empty(&self) -> bool {
80 self.order.is_empty()
81 }
82
83 /// Configured window capacity.
84 pub fn capacity(&self) -> usize {
85 self.capacity
86 }
87}
88