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 update_primary_hops(&mut self, hops: u8) -> bool {
106 let Some(primary) = self.paths.first_mut() else {
107 return false;
108 };
109 primary.hops = hops;
110 self.sort();
111 true
112 }
113
114 pub fn is_empty(&self) -> bool {
116 self.paths.is_empty()
117 }
118
119 pub fn len(&self) -> usize {
121 self.paths.len()
122 }
123
124 pub fn iter(&self) -> impl Iterator<Item = &PathEntry> {
126 self.paths.iter()
127 }
128
129 pub fn upsert(&mut self, entry: PathEntry) {
137 if let Some(pos) = self.paths.iter().position(|p| p.next_hop == entry.next_hop) {
139 self.paths[pos] = entry;
140 } else if self.paths.len() < self.capacity {
141 self.paths.push(entry);
142 } else {
143 self.paths.push(entry);
147 }
148 self.sort();
149 self.paths.truncate(self.capacity);
150 }
151
152 pub fn upsert_primary(&mut self, entry: PathEntry) {
154 if let Some(pos) = self
155 .paths
156 .iter()
157 .position(|path| path.next_hop == entry.next_hop)
158 {
159 self.paths.remove(pos);
160 }
161 self.paths.insert(0, entry);
162 self.sort();
163 self.paths.truncate(self.capacity);
164 }
165
166 pub fn failover(&mut self, remove: bool) {
172 if self.paths.len() <= 1 {
173 return;
174 }
175 if remove {
176 self.paths.remove(0);
177 } else {
178 let old_primary = self.paths.remove(0);
179 self.paths.push(old_primary);
180 }
181 }
182
183 pub fn cull(&mut self, now: f64, interface_exists: impl Fn(&InterfaceId) -> bool) {
188 self.paths
189 .retain(|p| now <= p.expires && interface_exists(&p.receiving_interface));
190 }
191
192 pub fn retain(&mut self, predicate: impl Fn(&PathEntry) -> bool) {
194 self.paths.retain(predicate);
195 }
196
197 pub fn expire_all(&mut self) {
199 for p in &mut self.paths {
200 p.timestamp = 0.0;
201 p.expires = 0.0;
202 }
203 }
204
205 pub fn all_random_blobs(&self) -> Vec<[u8; 10]> {
207 let mut blobs = Vec::new();
208 for p in &self.paths {
209 blobs.extend_from_slice(&p.random_blobs);
210 }
211 blobs
212 }
213
214 pub fn find_by_next_hop(&self, next_hop: &[u8; 16]) -> Option<&PathEntry> {
216 self.paths.iter().find(|p| &p.next_hop == next_hop)
217 }
218
219 fn sort(&mut self) {
221 self.paths.sort_by(|a, b| {
222 a.hops.cmp(&b.hops).then_with(|| {
223 b.timestamp
224 .partial_cmp(&a.timestamp)
225 .unwrap_or(core::cmp::Ordering::Equal)
226 })
227 });
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_path_entry_creation() {
237 let entry = PathEntry {
238 timestamp: 1000.0,
239 next_hop: [0xAA; 16],
240 hops: 3,
241 expires: 2000.0,
242 random_blobs: Vec::new(),
243 receiving_interface: InterfaceId(1),
244 packet_hash: [0xBB; 32],
245 announce_raw: None,
246 };
247 assert_eq!(entry.hops, 3);
248 assert_eq!(entry.receiving_interface, InterfaceId(1));
249 }
250
251 #[test]
252 fn upsert_primary_preserves_alternative_and_wins_equal_rank_tie() {
253 let first = PathEntry {
254 timestamp: 1000.0,
255 next_hop: [0xAA; 16],
256 hops: 3,
257 expires: 2000.0,
258 random_blobs: vec![[1; 10]],
259 receiving_interface: InterfaceId(1),
260 packet_hash: [0x11; 32],
261 announce_raw: None,
262 };
263 let preferred = PathEntry {
264 next_hop: [0xBB; 16],
265 receiving_interface: InterfaceId(2),
266 packet_hash: [0x22; 32],
267 ..first.clone()
268 };
269 let mut paths = PathSet::from_single(first, 3);
270
271 paths.upsert_primary(preferred);
272
273 assert_eq!(paths.len(), 2);
274 assert_eq!(paths.primary().unwrap().receiving_interface, InterfaceId(2));
275 }
276
277 #[test]
278 fn test_link_entry_creation() {
279 let entry = LinkEntry {
280 timestamp: 100.0,
281 next_hop_transport_id: [0x11; 16],
282 next_hop_interface: InterfaceId(2),
283 remaining_hops: 5,
284 received_interface: InterfaceId(3),
285 taken_hops: 2,
286 destination_hash: [0x22; 16],
287 validated: false,
288 proof_timeout: 200.0,
289 };
290 assert!(!entry.validated);
291 assert_eq!(entry.remaining_hops, 5);
292 }
293
294 #[test]
295 fn test_rate_entry_creation() {
296 let entry = RateEntry {
297 last: 50.0,
298 rate_violations: 0,
299 blocked_until: 0.0,
300 timestamps: Vec::new(),
301 };
302 assert_eq!(entry.rate_violations, 0);
303 }
304
305 fn make_path(next_hop: [u8; 16], hops: u8, timestamp: f64, expires: f64) -> PathEntry {
310 PathEntry {
311 timestamp,
312 next_hop,
313 hops,
314 expires,
315 random_blobs: Vec::new(),
316 receiving_interface: InterfaceId(1),
317 packet_hash: [0; 32],
318 announce_raw: None,
319 }
320 }
321
322 #[test]
323 fn test_pathset_from_single() {
324 let ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
325 assert_eq!(ps.len(), 1);
326 assert_eq!(ps.primary().unwrap().hops, 3);
327 }
328
329 #[test]
330 fn test_pathset_upsert_same_nexthop_replaces() {
331 let mut ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
332 ps.upsert(make_path([1; 16], 2, 200.0, 9999.0));
333 assert_eq!(ps.len(), 1);
334 assert_eq!(ps.primary().unwrap().hops, 2);
335 assert_eq!(ps.primary().unwrap().timestamp, 200.0);
336 }
337
338 #[test]
339 fn test_pathset_upsert_new_nexthop_adds_alternative() {
340 let mut ps = PathSet::from_single(make_path([1; 16], 3, 100.0, 9999.0), 3);
341 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
342 assert_eq!(ps.len(), 2);
343 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
345 }
346
347 #[test]
348 fn test_pathset_capacity_eviction() {
349 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 2);
350 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
351 ps.upsert(make_path([3; 16], 3, 300.0, 9999.0));
352 assert_eq!(ps.len(), 2);
354 assert_eq!(ps.primary().unwrap().next_hop, [1; 16]);
355 assert!(ps.find_by_next_hop(&[3; 16]).is_none());
356 }
357
358 #[test]
359 fn test_pathset_failover_promotes_second() {
360 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
361 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
362
363 ps.failover(false); assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
365 assert_eq!(ps.len(), 2); }
367
368 #[test]
369 fn test_pathset_failover_with_remove() {
370 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
371 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
372
373 ps.failover(true); assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
375 assert_eq!(ps.len(), 1);
376 }
377
378 #[test]
379 fn test_pathset_sort_ordering() {
380 let mut ps = PathSet::from_single(make_path([1; 16], 5, 300.0, 9999.0), 4);
381 ps.upsert(make_path([2; 16], 2, 100.0, 9999.0));
382 ps.upsert(make_path([3; 16], 2, 200.0, 9999.0));
383 ps.upsert(make_path([4; 16], 3, 400.0, 9999.0));
384
385 let hops: Vec<u8> = ps.iter().map(|p| p.hops).collect();
386 assert_eq!(hops, vec![2, 2, 3, 5]);
388 assert_eq!(ps.paths[0].next_hop, [3; 16]); assert_eq!(ps.paths[1].next_hop, [2; 16]); }
392
393 #[test]
394 fn test_pathset_cull_removes_expired() {
395 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 500.0), 3);
396 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
397
398 ps.cull(600.0, |_| true); assert_eq!(ps.len(), 1);
400 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
401 }
402
403 #[test]
404 fn test_pathset_cull_removes_orphaned_interface() {
405 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
406 ps.cull(200.0, |id| id.0 != 1); assert!(ps.is_empty());
408 }
409
410 #[test]
411 fn test_pathset_retain_filters() {
412 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
413 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
414
415 ps.retain(|p| p.next_hop != [1; 16]);
416 assert_eq!(ps.len(), 1);
417 assert_eq!(ps.primary().unwrap().next_hop, [2; 16]);
418 }
419
420 #[test]
421 fn test_pathset_expire_all() {
422 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
423 ps.upsert(make_path([2; 16], 2, 200.0, 9999.0));
424
425 ps.expire_all();
426 for p in ps.iter() {
427 assert_eq!(p.timestamp, 0.0);
428 assert_eq!(p.expires, 0.0);
429 }
430 }
431
432 #[test]
433 fn test_pathset_all_random_blobs() {
434 let mut e1 = make_path([1; 16], 1, 100.0, 9999.0);
435 e1.random_blobs = alloc::vec![[0xAA; 10]];
436 let mut e2 = make_path([2; 16], 2, 200.0, 9999.0);
437 e2.random_blobs = alloc::vec![[0xBB; 10], [0xCC; 10]];
438
439 let mut ps = PathSet::from_single(e1, 3);
440 ps.upsert(e2);
441
442 let blobs = ps.all_random_blobs();
443 assert_eq!(blobs.len(), 3);
444 }
445
446 #[test]
447 fn test_pathset_failover_single_path_noop() {
448 let mut ps = PathSet::from_single(make_path([1; 16], 1, 100.0, 9999.0), 3);
449 ps.failover(false);
450 assert_eq!(ps.len(), 1);
451 assert_eq!(ps.primary().unwrap().next_hop, [1; 16]);
452 }
453}