ant_quic/bootstrap_cache/
entry.rs1use crate::nat_traversal_api::PeerId;
11use serde::{Deserialize, Serialize};
12use std::collections::HashSet;
13use std::net::SocketAddr;
14use std::time::{Duration, SystemTime};
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct CachedPeer {
19 #[serde(with = "peer_id_serde")]
21 pub peer_id: PeerId,
22
23 pub addresses: Vec<SocketAddr>,
25
26 pub capabilities: PeerCapabilities,
28
29 pub first_seen: SystemTime,
31
32 pub last_seen: SystemTime,
34
35 pub last_attempt: Option<SystemTime>,
37
38 pub stats: ConnectionStats,
40
41 #[serde(default = "default_quality_score")]
43 pub quality_score: f64,
44
45 pub source: PeerSource,
47
48 #[serde(default)]
50 pub relay_paths: Vec<RelayPathHint>,
51
52 #[serde(default)]
54 pub token: Option<Vec<u8>>,
55}
56
57fn default_quality_score() -> f64 {
58 0.5
59}
60
61#[derive(Debug, Clone, Default, Serialize, Deserialize)]
63pub struct PeerCapabilities {
64 pub supports_relay: bool,
66
67 pub supports_coordination: bool,
69
70 #[serde(default)]
72 pub protocols: HashSet<String>,
73
74 pub nat_type: Option<NatType>,
76
77 #[serde(default)]
79 pub external_addresses: Vec<SocketAddr>,
80}
81
82impl PeerCapabilities {
83 pub fn has_ipv4(&self) -> bool {
85 self.external_addresses.iter().any(|addr| addr.is_ipv4())
86 }
87
88 pub fn has_ipv6(&self) -> bool {
90 self.external_addresses.iter().any(|addr| addr.is_ipv6())
91 }
92
93 pub fn supports_dual_stack(&self) -> bool {
98 self.has_ipv4() && self.has_ipv6()
99 }
100
101 pub fn addresses_by_version(&self, ipv4: bool) -> Vec<SocketAddr> {
103 self.external_addresses
104 .iter()
105 .filter(|addr| addr.is_ipv4() == ipv4)
106 .copied()
107 .collect()
108 }
109
110 pub fn can_bridge(&self, source: &SocketAddr, target: &SocketAddr) -> bool {
112 let source_v4 = source.is_ipv4();
113 let target_v4 = target.is_ipv4();
114
115 if source_v4 == target_v4 {
117 return true;
118 }
119
120 self.supports_dual_stack()
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
127pub enum NatType {
128 None,
130 FullCone,
132 AddressRestrictedCone,
134 PortRestrictedCone,
136 Symmetric,
138 Unknown,
140}
141
142#[derive(Debug, Clone, Default, Serialize, Deserialize)]
144pub struct ConnectionStats {
145 pub success_count: u32,
147
148 pub failure_count: u32,
150
151 pub avg_rtt_ms: u32,
153
154 pub min_rtt_ms: u32,
156
157 pub max_rtt_ms: u32,
159
160 pub bytes_relayed: u64,
162
163 pub coordinations_completed: u32,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
169pub enum PeerSource {
170 Seed,
172 Connection,
174 Relay,
176 Coordination,
178 Merge,
180 #[default]
182 Unknown,
183}
184
185#[derive(Debug, Clone)]
187pub struct ConnectionOutcome {
188 pub success: bool,
190 pub rtt_ms: Option<u32>,
192 pub capabilities_discovered: Option<PeerCapabilities>,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct RelayPathHint {
202 #[serde(with = "peer_id_serde")]
204 pub relay_endpoint_id: PeerId,
205
206 pub relay_locators: Vec<SocketAddr>,
208
209 pub observed_latency_ms: Option<u32>,
211
212 pub last_used: SystemTime,
214}
215
216impl CachedPeer {
217 pub fn new(peer_id: PeerId, addresses: Vec<SocketAddr>, source: PeerSource) -> Self {
219 let now = SystemTime::now();
220 Self {
221 peer_id,
222 addresses,
223 capabilities: PeerCapabilities::default(),
224 first_seen: now,
225 last_seen: now,
226 last_attempt: None,
227 stats: ConnectionStats::default(),
228 quality_score: 0.5, source,
230 relay_paths: Vec::new(),
231 token: None,
232 }
233 }
234
235 pub fn record_success(&mut self, rtt_ms: u32, caps: Option<PeerCapabilities>) {
237 self.last_seen = SystemTime::now();
238 self.last_attempt = Some(SystemTime::now());
239 self.stats.success_count = self.stats.success_count.saturating_add(1);
240
241 if self.stats.avg_rtt_ms == 0 {
243 self.stats.avg_rtt_ms = rtt_ms;
244 self.stats.min_rtt_ms = rtt_ms;
245 self.stats.max_rtt_ms = rtt_ms;
246 } else {
247 self.stats.avg_rtt_ms = (self.stats.avg_rtt_ms * 7 + rtt_ms) / 8;
248 self.stats.min_rtt_ms = self.stats.min_rtt_ms.min(rtt_ms);
249 self.stats.max_rtt_ms = self.stats.max_rtt_ms.max(rtt_ms);
250 }
251
252 if let Some(caps) = caps {
253 self.capabilities = caps;
254 }
255 }
256
257 pub fn record_failure(&mut self) {
259 self.last_attempt = Some(SystemTime::now());
260 self.stats.failure_count = self.stats.failure_count.saturating_add(1);
261 }
262
263 pub fn calculate_quality(&mut self, weights: &super::config::QualityWeights) {
265 let total_attempts = self.stats.success_count + self.stats.failure_count;
266
267 let success_rate = if total_attempts > 0 {
269 self.stats.success_count as f64 / total_attempts as f64
270 } else {
271 0.5 };
273
274 let rtt_score = if self.stats.avg_rtt_ms > 0 {
277 1.0 - (self.stats.avg_rtt_ms as f64 / 1000.0).min(1.0)
278 } else {
279 0.5 };
281
282 let age_secs = self
284 .last_seen
285 .duration_since(SystemTime::UNIX_EPOCH)
286 .ok()
287 .and_then(|last_seen_epoch| {
288 SystemTime::now()
289 .duration_since(SystemTime::UNIX_EPOCH)
290 .ok()
291 .map(|now_epoch| {
292 now_epoch
293 .as_secs()
294 .saturating_sub(last_seen_epoch.as_secs())
295 })
296 })
297 .unwrap_or(0) as f64;
298
299 let freshness = (-age_secs * 0.693 / 86400.0).exp();
301
302 let mut cap_bonus: f64 = 0.0;
304 if self.capabilities.supports_relay {
305 cap_bonus += 0.25;
306 }
307 if self.capabilities.supports_coordination {
308 cap_bonus += 0.25;
309 }
310 if self.capabilities.supports_dual_stack() {
311 cap_bonus += 0.2; }
313 if matches!(
314 self.capabilities.nat_type,
315 Some(NatType::None) | Some(NatType::FullCone)
316 ) {
317 cap_bonus += 0.3; }
319 let cap_score = cap_bonus.min(1.0);
320
321 self.quality_score = (success_rate * weights.success_rate
323 + rtt_score * weights.rtt
324 + freshness * weights.freshness
325 + cap_score * weights.capabilities)
326 .clamp(0.0, 1.0);
327 }
328
329 pub fn is_stale(&self, threshold: Duration) -> bool {
331 self.last_seen
332 .elapsed()
333 .map(|age| age > threshold)
334 .unwrap_or(true)
335 }
336
337 pub fn success_rate(&self) -> f64 {
339 let total = self.stats.success_count + self.stats.failure_count;
340 if total == 0 {
341 0.5
342 } else {
343 self.stats.success_count as f64 / total as f64
344 }
345 }
346
347 pub fn merge_addresses(&mut self, other: &CachedPeer) {
349 for addr in &other.addresses {
350 if !self.addresses.contains(addr) {
351 self.addresses.push(*addr);
352 }
353 }
354 if self.addresses.len() > 10 {
356 self.addresses.truncate(10);
357 }
358 }
359}
360
361mod peer_id_serde {
363 use super::PeerId;
364 use serde::{Deserialize, Deserializer, Serialize, Serializer};
365
366 pub fn serialize<S>(peer_id: &PeerId, serializer: S) -> Result<S::Ok, S::Error>
367 where
368 S: Serializer,
369 {
370 hex::encode(peer_id.0).serialize(serializer)
371 }
372
373 pub fn deserialize<'de, D>(deserializer: D) -> Result<PeerId, D::Error>
374 where
375 D: Deserializer<'de>,
376 {
377 let s = String::deserialize(deserializer)?;
378 let bytes = hex::decode(&s).map_err(serde::de::Error::custom)?;
379 if bytes.len() != 32 {
380 return Err(serde::de::Error::custom("PeerId must be 32 bytes"));
381 }
382 let mut arr = [0u8; 32];
383 arr.copy_from_slice(&bytes);
384 Ok(PeerId(arr))
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_cached_peer_new() {
394 let peer_id = PeerId([1u8; 32]);
395 let peer = CachedPeer::new(
396 peer_id,
397 vec!["127.0.0.1:9000".parse().unwrap()],
398 PeerSource::Seed,
399 );
400
401 assert_eq!(peer.peer_id, peer_id);
402 assert_eq!(peer.addresses.len(), 1);
403 assert_eq!(peer.source, PeerSource::Seed);
404 assert!((peer.quality_score - 0.5).abs() < f64::EPSILON);
405 }
406
407 #[test]
408 fn test_record_success() {
409 let mut peer = CachedPeer::new(
410 PeerId([1u8; 32]),
411 vec!["127.0.0.1:9000".parse().unwrap()],
412 PeerSource::Seed,
413 );
414
415 peer.record_success(100, None);
416 assert_eq!(peer.stats.success_count, 1);
417 assert_eq!(peer.stats.avg_rtt_ms, 100);
418 assert_eq!(peer.stats.min_rtt_ms, 100);
419 assert_eq!(peer.stats.max_rtt_ms, 100);
420
421 peer.record_success(200, None);
422 assert_eq!(peer.stats.success_count, 2);
423 assert_eq!(peer.stats.avg_rtt_ms, 112);
425 assert_eq!(peer.stats.min_rtt_ms, 100);
426 assert_eq!(peer.stats.max_rtt_ms, 200);
427 }
428
429 #[test]
430 fn test_record_failure() {
431 let mut peer = CachedPeer::new(
432 PeerId([1u8; 32]),
433 vec!["127.0.0.1:9000".parse().unwrap()],
434 PeerSource::Seed,
435 );
436
437 peer.record_failure();
438 assert_eq!(peer.stats.failure_count, 1);
439 assert!(peer.last_attempt.is_some());
440 }
441
442 #[test]
443 fn test_success_rate() {
444 let mut peer = CachedPeer::new(
445 PeerId([1u8; 32]),
446 vec!["127.0.0.1:9000".parse().unwrap()],
447 PeerSource::Seed,
448 );
449
450 assert!((peer.success_rate() - 0.5).abs() < f64::EPSILON);
452
453 peer.record_success(100, None);
454 assert!((peer.success_rate() - 1.0).abs() < f64::EPSILON);
455
456 peer.record_failure();
457 assert!((peer.success_rate() - 0.5).abs() < f64::EPSILON);
458 }
459
460 #[test]
461 fn test_quality_calculation() {
462 let weights = super::super::config::QualityWeights::default();
463 let mut peer = CachedPeer::new(
464 PeerId([1u8; 32]),
465 vec!["127.0.0.1:9000".parse().unwrap()],
466 PeerSource::Seed,
467 );
468
469 peer.calculate_quality(&weights);
471 assert!(peer.quality_score > 0.3 && peer.quality_score < 0.7);
472
473 for _ in 0..5 {
475 peer.record_success(50, None); }
477 peer.calculate_quality(&weights);
478 assert!(peer.quality_score > 0.6);
479 }
480
481 #[test]
482 fn test_peer_serialization() {
483 let peer = CachedPeer::new(
484 PeerId([0xab; 32]),
485 vec!["127.0.0.1:9000".parse().unwrap()],
486 PeerSource::Seed,
487 );
488
489 let json = serde_json::to_string(&peer).unwrap();
490 let deserialized: CachedPeer = serde_json::from_str(&json).unwrap();
491
492 assert_eq!(deserialized.peer_id, peer.peer_id);
493 assert_eq!(deserialized.addresses, peer.addresses);
494 assert_eq!(deserialized.source, peer.source);
495 }
496
497 #[test]
498 fn test_peer_capabilities_dual_stack() {
499 let mut caps = PeerCapabilities::default();
500
501 assert!(!caps.supports_dual_stack());
503 assert!(!caps.has_ipv4());
504 assert!(!caps.has_ipv6());
505
506 caps.external_addresses
508 .push("127.0.0.1:9000".parse().unwrap());
509 assert!(!caps.supports_dual_stack());
510 assert!(caps.has_ipv4());
511 assert!(!caps.has_ipv6());
512
513 caps.external_addresses.push("[::1]:9001".parse().unwrap());
515 assert!(caps.supports_dual_stack());
516 assert!(caps.has_ipv4());
517 assert!(caps.has_ipv6());
518 }
519
520 #[test]
521 fn test_peer_capabilities_ipv6_only() {
522 let mut caps = PeerCapabilities::default();
523 caps.external_addresses.push("[::1]:9000".parse().unwrap());
524 caps.external_addresses.push("[::1]:9001".parse().unwrap());
525
526 assert!(!caps.supports_dual_stack());
527 assert!(!caps.has_ipv4());
528 assert!(caps.has_ipv6());
529 }
530
531 #[test]
532 fn test_peer_capabilities_can_bridge() {
533 let mut caps = PeerCapabilities::default();
534 caps.external_addresses
535 .push("127.0.0.1:9000".parse().unwrap());
536 caps.external_addresses.push("[::1]:9001".parse().unwrap());
537
538 let v4_src: SocketAddr = "192.168.1.1:1000".parse().unwrap();
539 let v4_dst: SocketAddr = "192.168.1.2:2000".parse().unwrap();
540 let v6_src: SocketAddr = "[2001:db8::1]:1000".parse().unwrap();
541 let v6_dst: SocketAddr = "[2001:db8::2]:2000".parse().unwrap();
542
543 assert!(caps.can_bridge(&v4_src, &v4_dst));
545 assert!(caps.can_bridge(&v6_src, &v6_dst));
546
547 assert!(caps.can_bridge(&v4_src, &v6_dst));
549 assert!(caps.can_bridge(&v6_src, &v4_dst));
550 }
551
552 #[test]
553 fn test_peer_capabilities_cannot_bridge_ipv4_only() {
554 let mut caps = PeerCapabilities::default();
555 caps.external_addresses
556 .push("127.0.0.1:9000".parse().unwrap());
557
558 let v4_addr: SocketAddr = "192.168.1.1:1000".parse().unwrap();
559 let v6_addr: SocketAddr = "[2001:db8::1]:1000".parse().unwrap();
560
561 assert!(caps.can_bridge(&v4_addr, &v4_addr));
563
564 assert!(!caps.can_bridge(&v4_addr, &v6_addr));
566 assert!(!caps.can_bridge(&v6_addr, &v4_addr));
567 }
568
569 #[test]
570 fn test_addresses_by_version() {
571 let mut caps = PeerCapabilities::default();
572 caps.external_addresses
573 .push("127.0.0.1:9000".parse().unwrap());
574 caps.external_addresses
575 .push("10.0.0.1:9001".parse().unwrap());
576 caps.external_addresses.push("[::1]:9002".parse().unwrap());
577
578 let v4_addrs = caps.addresses_by_version(true);
579 assert_eq!(v4_addrs.len(), 2);
580
581 let v6_addrs = caps.addresses_by_version(false);
582 assert_eq!(v6_addrs.len(), 1);
583 }
584}