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
36pub type ReverseTable = hashbrown::HashMap<[u8; 16], ReverseEntry, foldhash::fast::FixedState>;
43
44#[derive(Debug, Clone)]
46pub struct ReverseEntry {
47 pub receiving_interface: InterfaceId,
48 pub outbound_interface: InterfaceId,
49 pub timestamp: f64,
50}
51
52#[derive(Debug, Clone)]
54pub struct LinkEntry {
55 pub timestamp: f64,
56 pub next_hop_transport_id: [u8; 16],
57 pub next_hop_interface: InterfaceId,
58 pub remaining_hops: u8,
59 pub received_interface: InterfaceId,
60 pub taken_hops: u8,
61 pub destination_hash: [u8; 16],
62 pub validated: bool,
63 pub proof_timeout: f64,
64}
65
66#[derive(Debug, Clone)]
69pub struct DiscoveryPathRequest {
70 pub timestamp: f64,
71 pub requesting_interfaces: Vec<InterfaceId>,
72 pub engaged: bool,
73}
74
75#[derive(Debug, Clone)]
77pub struct RateEntry {
78 pub last: f64,
79 pub rate_violations: u32,
80 pub blocked_until: f64,
81 pub timestamps: Vec<f64>,
82}
83
84#[derive(Debug, Clone)]
89pub struct PathSet {
90 paths: Vec<PathEntry>,
91 capacity: usize,
92}
93
94impl PathSet {
95 pub fn from_single(entry: PathEntry, capacity: usize) -> Self {
97 PathSet {
98 paths: alloc::vec![entry],
99 capacity: capacity.max(1),
100 }
101 }
102
103 pub fn primary(&self) -> Option<&PathEntry> {
105 self.paths.first()
106 }
107
108 pub fn primary_mut(&mut self) -> Option<&mut PathEntry> {
110 self.paths.first_mut()
111 }
112
113 pub fn update_primary_hops(&mut self, hops: u8) -> bool {
115 let Some(primary) = self.paths.first_mut() else {
116 return false;
117 };
118 primary.hops = hops;
119 self.sort();
120 true
121 }
122
123 pub fn is_empty(&self) -> bool {
125 self.paths.is_empty()
126 }
127
128 pub fn len(&self) -> usize {
130 self.paths.len()
131 }
132
133 pub fn iter(&self) -> impl Iterator<Item = &PathEntry> {
135 self.paths.iter()
136 }
137
138 pub fn upsert(&mut self, entry: PathEntry) {
146 if let Some(pos) = self.paths.iter().position(|p| p.next_hop == entry.next_hop) {
148 self.paths[pos] = entry;
149 } else if self.paths.len() < self.capacity {
150 self.paths.push(entry);
151 } else {
152 self.paths.push(entry);
156 }
157 self.sort();
158 self.paths.truncate(self.capacity);
159 }
160
161 pub fn upsert_primary(&mut self, entry: PathEntry) {
163 if let Some(pos) = self
164 .paths
165 .iter()
166 .position(|path| path.next_hop == entry.next_hop)
167 {
168 self.paths.remove(pos);
169 }
170 self.paths.insert(0, entry);
171 self.sort();
172 self.paths.truncate(self.capacity);
173 }
174
175 pub fn failover(&mut self, remove: bool) {
181 if self.paths.len() <= 1 {
182 return;
183 }
184 if remove {
185 self.paths.remove(0);
186 } else {
187 let old_primary = self.paths.remove(0);
188 self.paths.push(old_primary);
189 }
190 }
191
192 pub fn cull(&mut self, now: f64, interface_exists: impl Fn(&InterfaceId) -> bool) {
197 self.paths
198 .retain(|p| now <= p.expires && interface_exists(&p.receiving_interface));
199 }
200
201 pub fn retain(&mut self, predicate: impl Fn(&PathEntry) -> bool) {
203 self.paths.retain(predicate);
204 }
205
206 pub fn expire_all(&mut self) {
208 for p in &mut self.paths {
209 p.timestamp = 0.0;
210 p.expires = 0.0;
211 }
212 }
213
214 pub fn all_random_blobs(&self) -> Vec<[u8; 10]> {
216 let mut blobs = Vec::new();
217 for p in &self.paths {
218 blobs.extend_from_slice(&p.random_blobs);
219 }
220 blobs
221 }
222
223 pub fn find_by_next_hop(&self, next_hop: &[u8; 16]) -> Option<&PathEntry> {
225 self.paths.iter().find(|p| &p.next_hop == next_hop)
226 }
227
228 fn sort(&mut self) {
230 self.paths.sort_by(|a, b| {
231 a.hops.cmp(&b.hops).then_with(|| {
232 b.timestamp
233 .partial_cmp(&a.timestamp)
234 .unwrap_or(core::cmp::Ordering::Equal)
235 })
236 });
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_path_entry_creation() {
246 let entry = PathEntry {
247 timestamp: 1000.0,
248 next_hop: [0xAA; 16],
249 hops: 3,
250 expires: 2000.0,
251 random_blobs: Vec::new(),
252 receiving_interface: InterfaceId(1),
253 packet_hash: [0xBB; 32],
254 announce_raw: None,
255 };
256 assert_eq!(entry.hops, 3);
257 assert_eq!(entry.receiving_interface, InterfaceId(1));
258 }
259
260 #[test]
261 fn upsert_primary_preserves_alternative_and_wins_equal_rank_tie() {
262 let first = PathEntry {
263 timestamp: 1000.0,
264 next_hop: [0xAA; 16],
265 hops: 3,
266 expires: 2000.0,
267 random_blobs: vec![[1; 10]],
268 receiving_interface: InterfaceId(1),
269 packet_hash: [0x11; 32],
270 announce_raw: None,
271 };
272 let preferred = PathEntry {
273 next_hop: [0xBB; 16],
274 receiving_interface: InterfaceId(2),
275 packet_hash: [0x22; 32],
276 ..first.clone()
277 };
278 let mut paths = PathSet::from_single(first, 3);
279
280 paths.upsert_primary(preferred);
281
282 assert_eq!(paths.len(), 2);
283 assert_eq!(paths.primary().unwrap().receiving_interface, InterfaceId(2));
284 }
285
286 #[test]
287 fn test_link_entry_creation() {
288 let entry = LinkEntry {
289 timestamp: 100.0,
290 next_hop_transport_id: [0x11; 16],
291 next_hop_interface: InterfaceId(2),
292 remaining_hops: 5,
293 received_interface: InterfaceId(3),
294 taken_hops: 2,
295 destination_hash: [0x22; 16],
296 validated: false,
297 proof_timeout: 200.0,
298 };
299 assert!(!entry.validated);
300 assert_eq!(entry.remaining_hops, 5);
301 }
302
303 #[test]
304 fn test_rate_entry_creation() {
305 let entry = RateEntry {
306 last: 50.0,
307 rate_violations: 0,
308 blocked_until: 0.0,
309 timestamps: Vec::new(),
310 };
311 assert_eq!(entry.rate_violations, 0);
312 }
313
314 fn make_path(next_hop: [u8; 16], hops: u8, timestamp: f64, expires: f64) -> PathEntry {
319 PathEntry {
320 timestamp,
321 next_hop,
322 hops,
323 expires,
324 random_blobs: Vec::new(),
325 receiving_interface: InterfaceId(1),
326 packet_hash: [0; 32],
327 announce_raw: None,
328 }
329 }
330
331 #[test]
332 fn test_pathset_from_single() {
333 let ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
334 assert_eq!(ps.len(), 1);
335 assert_eq!(ps.primary().unwrap().hops, 3);
336 }
337
338 #[test]
339 fn test_pathset_upsert_same_nexthop_replaces() {
340 let mut ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
341 ps.upsert(make_path([1; 16], 2, 200.0, 9999.0));
342 assert_eq!(ps.len(), 1);
343 assert_eq!(ps.primary().unwrap().hops, 2);
344 assert_eq!(ps.primary().unwrap().timestamp, 200.0);
345 }
346
347 #[test]
348 fn test_pathset_upsert_new_nexthop_adds_alternative() {
349 let mut ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
350 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
351 assert_eq!(ps.len(), 2);
352 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
354 }
355
356 #[test]
357 fn test_pathset_capacity_eviction() {
358 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 2);
359 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
360 ps.upsert(make_path([3; 16], 3, 300.0, 9999.0));
361 assert_eq!(ps.len(), 2);
363 assert_eq!(ps.primary().unwrap().next_hop, [1; 16]);
364 assert!(ps.find_by_next_hop(&[3; 16]).is_none());
365 }
366
367 #[test]
368 fn test_pathset_failover_promotes_second() {
369 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
370 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
371
372 ps.failover(false); assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
374 assert_eq!(ps.len(), 2); }
376
377 #[test]
378 fn test_pathset_failover_with_remove() {
379 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
380 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
381
382 ps.failover(true); assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
384 assert_eq!(ps.len(), 1);
385 }
386
387 #[test]
388 fn test_pathset_sort_ordering() {
389 let mut ps = PathSet::from_single(make_path([1; 16], 5, 300.0, 9999.0), 4);
390 ps.upsert(make_path([2; 16], 2, 100.0, 9999.0));
391 ps.upsert(make_path([3; 16], 2, 200.0, 9999.0));
392 ps.upsert(make_path([4; 16], 3, 400.0, 9999.0));
393
394 let hops: Vec<u8> = ps.iter().map(|p| p.hops).collect();
395 assert_eq!(hops, vec![2, 2, 3, 5]);
397 assert_eq!(ps.paths[0].next_hop, [3; 16]); assert_eq!(ps.paths[1].next_hop, [2; 16]); }
401
402 #[test]
403 fn test_pathset_cull_removes_expired() {
404 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 500.0), 3);
405 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
406
407 ps.cull(600.0, |_| true); assert_eq!(ps.len(), 1);
409 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
410 }
411
412 #[test]
413 fn test_pathset_cull_removes_orphaned_interface() {
414 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
415 ps.cull(200.0, |id| id.0 != 1); assert!(ps.is_empty());
417 }
418
419 #[test]
420 fn test_pathset_retain_filters() {
421 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
422 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
423
424 ps.retain(|p| p.next_hop != [1; 16]);
425 assert_eq!(ps.len(), 1);
426 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
427 }
428
429 #[test]
430 fn test_pathset_expire_all() {
431 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
432 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
433
434 ps.expire_all();
435 for p in ps.iter() {
436 assert_eq!(p.timestamp, 0.0);
437 assert_eq!(p.expires, 0.0);
438 }
439 }
440
441 #[test]
442 fn test_pathset_all_random_blobs() {
443 let mut e1 = make_path([1; 16], 1, 100.0, 9999.0);
444 e1.random_blobs = alloc::vec![[0xAA; 10]];
445 let mut e2 = make_path([2; 16], 2, 200.0, 9999.0);
446 e2.random_blobs = alloc::vec![[0xBB; 10], [0xCC; 10]];
447
448 let mut ps = PathSet::from_single(e1, 3);
449 ps.upsert(e2);
450
451 let blobs = ps.all_random_blobs();
452 assert_eq!(blobs.len(), 3);
453 }
454
455 #[test]
456 fn test_pathset_failover_single_path_noop() {
457 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
458 ps.failover(false);
459 assert_eq!(ps.len(), 1);
460 assert_eq!(ps.primary().unwrap().next_hop, [1; 16]);
461 }
462}