1use alloc::vec::Vec;
2
3use super::types::InterfaceId;
4
5#[derive(Debug, Clone)]
7pub struct PathEntry {
8 pub timestamp: f64,
9 pub next_hop: [u8; 16],
10 pub hops: u8,
11 pub expires: f64,
12 pub random_blobs: Vec<[u8; 10]>,
13 pub receiving_interface: InterfaceId,
14 pub packet_hash: [u8; 32],
15 pub announce_raw: Option<Vec<u8>>,
17}
18
19#[derive(Debug, Clone)]
21pub struct AnnounceEntry {
22 pub timestamp: f64,
23 pub retransmit_timeout: f64,
24 pub retries: u8,
25 pub received_from: [u8; 16],
26 pub hops: u8,
27 pub packet_raw: Vec<u8>,
28 pub packet_data: Vec<u8>,
29 pub destination_hash: [u8; 16],
30 pub context_flag: u8,
31 pub local_rebroadcasts: u8,
32 pub block_rebroadcasts: bool,
33 pub attached_interface: Option<InterfaceId>,
34}
35
36#[derive(Debug, Clone)]
38pub struct ReverseEntry {
39 pub receiving_interface: InterfaceId,
40 pub outbound_interface: InterfaceId,
41 pub timestamp: f64,
42}
43
44#[derive(Debug, Clone)]
46pub struct LinkEntry {
47 pub timestamp: f64,
48 pub next_hop_transport_id: [u8; 16],
49 pub next_hop_interface: InterfaceId,
50 pub remaining_hops: u8,
51 pub received_interface: InterfaceId,
52 pub taken_hops: u8,
53 pub destination_hash: [u8; 16],
54 pub validated: bool,
55 pub proof_timeout: f64,
56}
57
58#[derive(Debug, Clone)]
61pub struct DiscoveryPathRequest {
62 pub timestamp: f64,
63 pub requesting_interface: InterfaceId,
64}
65
66#[derive(Debug, Clone)]
68pub struct RateEntry {
69 pub last: f64,
70 pub rate_violations: u32,
71 pub blocked_until: f64,
72 pub timestamps: Vec<f64>,
73}
74
75#[derive(Debug, Clone)]
80pub struct PathSet {
81 paths: Vec<PathEntry>,
82 capacity: usize,
83}
84
85impl PathSet {
86 pub fn from_single(entry: PathEntry, capacity: usize) -> Self {
88 PathSet {
89 paths: alloc::vec![entry],
90 capacity: capacity.max(1),
91 }
92 }
93
94 pub fn primary(&self) -> Option<&PathEntry> {
96 self.paths.first()
97 }
98
99 pub fn primary_mut(&mut self) -> Option<&mut PathEntry> {
101 self.paths.first_mut()
102 }
103
104 pub fn is_empty(&self) -> bool {
106 self.paths.is_empty()
107 }
108
109 pub fn len(&self) -> usize {
111 self.paths.len()
112 }
113
114 pub fn iter(&self) -> impl Iterator<Item = &PathEntry> {
116 self.paths.iter()
117 }
118
119 pub fn upsert(&mut self, entry: PathEntry) {
127 if let Some(pos) = self.paths.iter().position(|p| p.next_hop == entry.next_hop) {
129 self.paths[pos] = entry;
130 } else if self.paths.len() < self.capacity {
131 self.paths.push(entry);
132 } else {
133 self.paths.push(entry);
137 }
138 self.sort();
139 self.paths.truncate(self.capacity);
140 }
141
142 pub fn failover(&mut self, remove: bool) {
148 if self.paths.len() <= 1 {
149 return;
150 }
151 if remove {
152 self.paths.remove(0);
153 } else {
154 let old_primary = self.paths.remove(0);
155 self.paths.push(old_primary);
156 }
157 }
158
159 pub fn cull(&mut self, now: f64, interface_exists: impl Fn(&InterfaceId) -> bool) {
164 self.paths
165 .retain(|p| now <= p.expires && interface_exists(&p.receiving_interface));
166 }
167
168 pub fn retain(&mut self, predicate: impl Fn(&PathEntry) -> bool) {
170 self.paths.retain(predicate);
171 }
172
173 pub fn expire_all(&mut self) {
175 for p in &mut self.paths {
176 p.timestamp = 0.0;
177 p.expires = 0.0;
178 }
179 }
180
181 pub fn all_random_blobs(&self) -> Vec<[u8; 10]> {
183 let mut blobs = Vec::new();
184 for p in &self.paths {
185 blobs.extend_from_slice(&p.random_blobs);
186 }
187 blobs
188 }
189
190 pub fn find_by_next_hop(&self, next_hop: &[u8; 16]) -> Option<&PathEntry> {
192 self.paths.iter().find(|p| &p.next_hop == next_hop)
193 }
194
195 fn sort(&mut self) {
197 self.paths.sort_by(|a, b| {
198 a.hops.cmp(&b.hops).then_with(|| {
199 b.timestamp
200 .partial_cmp(&a.timestamp)
201 .unwrap_or(core::cmp::Ordering::Equal)
202 })
203 });
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_path_entry_creation() {
213 let entry = PathEntry {
214 timestamp: 1000.0,
215 next_hop: [0xAA; 16],
216 hops: 3,
217 expires: 2000.0,
218 random_blobs: Vec::new(),
219 receiving_interface: InterfaceId(1),
220 packet_hash: [0xBB; 32],
221 announce_raw: None,
222 };
223 assert_eq!(entry.hops, 3);
224 assert_eq!(entry.receiving_interface, InterfaceId(1));
225 }
226
227 #[test]
228 fn test_link_entry_creation() {
229 let entry = LinkEntry {
230 timestamp: 100.0,
231 next_hop_transport_id: [0x11; 16],
232 next_hop_interface: InterfaceId(2),
233 remaining_hops: 5,
234 received_interface: InterfaceId(3),
235 taken_hops: 2,
236 destination_hash: [0x22; 16],
237 validated: false,
238 proof_timeout: 200.0,
239 };
240 assert!(!entry.validated);
241 assert_eq!(entry.remaining_hops, 5);
242 }
243
244 #[test]
245 fn test_rate_entry_creation() {
246 let entry = RateEntry {
247 last: 50.0,
248 rate_violations: 0,
249 blocked_until: 0.0,
250 timestamps: Vec::new(),
251 };
252 assert_eq!(entry.rate_violations, 0);
253 }
254
255 fn make_path(next_hop: [u8; 16], hops: u8, timestamp: f64, expires: f64) -> PathEntry {
260 PathEntry {
261 timestamp,
262 next_hop,
263 hops,
264 expires,
265 random_blobs: Vec::new(),
266 receiving_interface: InterfaceId(1),
267 packet_hash: [0; 32],
268 announce_raw: None,
269 }
270 }
271
272 #[test]
273 fn test_pathset_from_single() {
274 let ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
275 assert_eq!(ps.len(), 1);
276 assert_eq!(ps.primary().unwrap().hops, 3);
277 }
278
279 #[test]
280 fn test_pathset_upsert_same_nexthop_replaces() {
281 let mut ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
282 ps.upsert(make_path([1; 16], 2, 200.0, 9999.0));
283 assert_eq!(ps.len(), 1);
284 assert_eq!(ps.primary().unwrap().hops, 2);
285 assert_eq!(ps.primary().unwrap().timestamp, 200.0);
286 }
287
288 #[test]
289 fn test_pathset_upsert_new_nexthop_adds_alternative() {
290 let mut ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
291 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
292 assert_eq!(ps.len(), 2);
293 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
295 }
296
297 #[test]
298 fn test_pathset_capacity_eviction() {
299 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 2);
300 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
301 ps.upsert(make_path([3; 16], 3, 300.0, 9999.0));
302 assert_eq!(ps.len(), 2);
304 assert_eq!(ps.primary().unwrap().next_hop, [1; 16]);
305 assert!(ps.find_by_next_hop(&[3; 16]).is_none());
306 }
307
308 #[test]
309 fn test_pathset_failover_promotes_second() {
310 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
311 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
312
313 ps.failover(false); assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
315 assert_eq!(ps.len(), 2); }
317
318 #[test]
319 fn test_pathset_failover_with_remove() {
320 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
321 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
322
323 ps.failover(true); assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
325 assert_eq!(ps.len(), 1);
326 }
327
328 #[test]
329 fn test_pathset_sort_ordering() {
330 let mut ps = PathSet::from_single(make_path([1; 16], 5, 300.0, 9999.0), 4);
331 ps.upsert(make_path([2; 16], 2, 100.0, 9999.0));
332 ps.upsert(make_path([3; 16], 2, 200.0, 9999.0));
333 ps.upsert(make_path([4; 16], 3, 400.0, 9999.0));
334
335 let hops: Vec<u8> = ps.iter().map(|p| p.hops).collect();
336 assert_eq!(hops, vec![2, 2, 3, 5]);
338 assert_eq!(ps.paths[0].next_hop, [3; 16]); assert_eq!(ps.paths[1].next_hop, [2; 16]); }
342
343 #[test]
344 fn test_pathset_cull_removes_expired() {
345 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 500.0), 3);
346 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
347
348 ps.cull(600.0, |_| true); assert_eq!(ps.len(), 1);
350 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
351 }
352
353 #[test]
354 fn test_pathset_cull_removes_orphaned_interface() {
355 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
356 ps.cull(200.0, |id| id.0 != 1); assert!(ps.is_empty());
358 }
359
360 #[test]
361 fn test_pathset_retain_filters() {
362 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
363 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
364
365 ps.retain(|p| p.next_hop != [1; 16]);
366 assert_eq!(ps.len(), 1);
367 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
368 }
369
370 #[test]
371 fn test_pathset_expire_all() {
372 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
373 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
374
375 ps.expire_all();
376 for p in ps.iter() {
377 assert_eq!(p.timestamp, 0.0);
378 assert_eq!(p.expires, 0.0);
379 }
380 }
381
382 #[test]
383 fn test_pathset_all_random_blobs() {
384 let mut e1 = make_path([1; 16], 1, 100.0, 9999.0);
385 e1.random_blobs = alloc::vec![[0xAA; 10]];
386 let mut e2 = make_path([2; 16], 2, 200.0, 9999.0);
387 e2.random_blobs = alloc::vec![[0xBB; 10], [0xCC; 10]];
388
389 let mut ps = PathSet::from_single(e1, 3);
390 ps.upsert(e2);
391
392 let blobs = ps.all_random_blobs();
393 assert_eq!(blobs.len(), 3);
394 }
395
396 #[test]
397 fn test_pathset_failover_single_path_noop() {
398 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
399 ps.failover(false);
400 assert_eq!(ps.len(), 1);
401 assert_eq!(ps.primary().unwrap().next_hop, [1; 16]);
402 }
403}