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