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