1use crate::PeerId;
20use serde::{Deserialize, Serialize};
21use std::collections::HashMap;
22use std::net::SocketAddr;
23use std::time::Duration;
24
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub struct QuicContactInfo {
28 pub direct_addresses: Vec<SocketAddr>,
30 pub quic_quality: QuicQualityMetrics,
32 pub last_quic_connection: chrono::DateTime<chrono::Utc>,
34 pub successful_connection_types: Vec<QuicConnectionType>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40pub struct QuicQualityMetrics {
41 pub avg_response_time_ms: f64,
43 pub avg_throughput_mbps: f64,
45 pub connection_success_rate: f64,
47 pub avg_connection_setup_time_ms: f64,
49 pub connection_type_success_rates: HashMap<QuicConnectionType, f64>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
55pub enum QuicConnectionType {
56 DirectIPv4,
58 DirectIPv6,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
64pub struct ContactEntry {
65 pub peer_id: PeerId,
67 pub addresses: Vec<SocketAddr>,
69 pub last_seen: chrono::DateTime<chrono::Utc>,
71 pub quality_metrics: QualityMetrics,
73 pub capabilities: Vec<String>,
75 pub ipv6_identity_verified: bool,
77 pub reputation_score: f64,
79 pub connection_history: ConnectionHistory,
81
82 pub quic_contact: Option<QuicContactInfo>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
89pub struct QualityMetrics {
90 pub success_rate: f64,
92 pub avg_latency_ms: f64,
94 pub quality_score: f64,
96 pub last_connection_attempt: chrono::DateTime<chrono::Utc>,
98 pub last_successful_connection: chrono::DateTime<chrono::Utc>,
100 pub uptime_score: f64,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
106pub struct ConnectionHistory {
107 pub total_attempts: u64,
109 pub successful_connections: u64,
111 pub failed_connections: u64,
113 pub total_session_time: Duration,
115 pub recent_latencies: Vec<u64>,
117 pub connection_failures: HashMap<String, u64>,
119}
120
121impl ContactEntry {
122 pub fn new(peer_id: PeerId, addresses: Vec<SocketAddr>) -> Self {
124 let now = chrono::Utc::now();
125
126 Self {
127 peer_id,
128 addresses,
129 last_seen: now,
130 quality_metrics: QualityMetrics::new(),
131 capabilities: Vec::new(),
132 ipv6_identity_verified: false,
133 reputation_score: 0.5, connection_history: ConnectionHistory::new(),
135 quic_contact: None, }
137 }
138
139 pub fn new_with_quic(
141 peer_id: PeerId,
142 addresses: Vec<SocketAddr>,
143 quic_info: QuicContactInfo,
144 ) -> Self {
145 let now = chrono::Utc::now();
146
147 Self {
148 peer_id,
149 addresses,
150 last_seen: now,
151 quality_metrics: QualityMetrics::new(),
152 capabilities: Vec::new(),
153 ipv6_identity_verified: false,
154 reputation_score: 0.5, connection_history: ConnectionHistory::new(),
156 quic_contact: Some(quic_info),
157 }
158 }
159
160 pub fn update_connection_result(
162 &mut self,
163 success: bool,
164 latency_ms: Option<u64>,
165 error: Option<String>,
166 ) {
167 let now = chrono::Utc::now();
168 self.last_seen = now;
169 self.quality_metrics.last_connection_attempt = now;
170 self.connection_history.total_attempts += 1;
171
172 if success {
173 self.connection_history.successful_connections += 1;
174 self.quality_metrics.last_successful_connection = now;
175
176 if let Some(latency) = latency_ms {
177 self.add_latency_measurement(latency);
178 }
179 } else {
180 self.connection_history.failed_connections += 1;
181
182 if let Some(err) = error {
183 *self
184 .connection_history
185 .connection_failures
186 .entry(err)
187 .or_insert(0) += 1;
188 }
189 }
190
191 self.update_success_rate();
192 self.update_latency_average();
193 self.recalculate_quality_score();
194 }
195
196 fn add_latency_measurement(&mut self, latency_ms: u64) {
198 self.connection_history.recent_latencies.push(latency_ms);
199
200 if self.connection_history.recent_latencies.len() > 10 {
202 self.connection_history.recent_latencies.remove(0);
203 }
204 }
205
206 pub fn update_success_rate(&mut self) {
208 if self.connection_history.total_attempts > 0 {
209 self.quality_metrics.success_rate = self.connection_history.successful_connections
210 as f64
211 / self.connection_history.total_attempts as f64;
212 }
213 }
214
215 fn update_latency_average(&mut self) {
217 if !self.connection_history.recent_latencies.is_empty() {
218 let sum: u64 = self.connection_history.recent_latencies.iter().sum();
219 self.quality_metrics.avg_latency_ms =
220 sum as f64 / self.connection_history.recent_latencies.len() as f64;
221 }
222 }
223
224 pub fn recalculate_quality_score(&mut self) {
226 let quality_calculator = QualityCalculator::new();
227 self.quality_metrics.quality_score = quality_calculator.calculate_quality(self);
228 }
229
230 pub fn update_capabilities(&mut self, capabilities: Vec<String>) {
232 self.capabilities = capabilities;
233 self.recalculate_quality_score();
234 }
235
236 pub fn update_reputation(&mut self, reputation: f64) {
238 self.reputation_score = reputation.clamp(0.0, 1.0);
239 self.recalculate_quality_score();
240 }
241
242 pub fn mark_ipv6_verified(&mut self) {
244 self.ipv6_identity_verified = true;
245 self.recalculate_quality_score();
246 }
247
248 pub fn is_stale(&self, max_age: Duration) -> bool {
250 let now = chrono::Utc::now();
251 let age = now.signed_duration_since(self.last_seen);
252 age.to_std().unwrap_or(Duration::MAX) > max_age
253 }
254
255 pub fn age_seconds(&self) -> u64 {
257 let now = chrono::Utc::now();
258 let age = now.signed_duration_since(self.last_seen);
259 age.num_seconds().max(0) as u64
260 }
261
262 pub fn has_capability(&self, capability: &str) -> bool {
264 self.capabilities.contains(&capability.to_string())
265 }
266
267 pub fn summary(&self) -> String {
269 let quic_info = if let Some(ref quic_contact) = self.quic_contact {
270 format!(
271 " QUIC: Setup:{:.0}ms",
272 quic_contact.quic_quality.avg_connection_setup_time_ms
273 )
274 } else {
275 " QUIC: None".to_string()
276 };
277
278 format!(
279 "Peer {} (Quality: {:.2}, Success: {:.1}%, Latency: {:.0}ms, Verified: {}{})",
280 self.peer_id.to_string().chars().take(8).collect::<String>(),
281 self.quality_metrics.quality_score,
282 self.quality_metrics.success_rate * 100.0,
283 self.quality_metrics.avg_latency_ms,
284 self.ipv6_identity_verified,
285 quic_info
286 )
287 }
288
289 pub fn update_quic_contact(&mut self, quic_info: QuicContactInfo) {
291 self.quic_contact = Some(quic_info);
292 self.recalculate_quality_score(); }
294
295 pub fn update_quic_connection_result(
297 &mut self,
298 connection_type: QuicConnectionType,
299 success: bool,
300 setup_time_ms: Option<u64>,
301 ) {
302 if let Some(ref mut quic_contact) = self.quic_contact {
303 let now = chrono::Utc::now();
304
305 if success {
306 quic_contact.last_quic_connection = now;
307
308 if !quic_contact
310 .successful_connection_types
311 .contains(&connection_type)
312 {
313 quic_contact
314 .successful_connection_types
315 .push(connection_type.clone());
316 }
317
318 if let Some(setup_time) = setup_time_ms {
320 let current_avg = quic_contact.quic_quality.avg_connection_setup_time_ms;
321 quic_contact.quic_quality.avg_connection_setup_time_ms = if current_avg == 0.0 {
322 setup_time as f64
323 } else {
324 (current_avg + setup_time as f64) / 2.0 };
326 }
327 }
328
329 let current_rate = quic_contact
331 .quic_quality
332 .connection_type_success_rates
333 .get(&connection_type)
334 .copied()
335 .unwrap_or(0.0);
336 let new_rate = if current_rate == 0.0 {
337 if success { 1.0 } else { 0.0 }
338 } else {
339 (current_rate + if success { 1.0 } else { 0.0 }) / 2.0
340 };
341 quic_contact
342 .quic_quality
343 .connection_type_success_rates
344 .insert(connection_type.clone(), new_rate);
345
346 self.recalculate_quality_score();
347 }
348 }
349
350 pub fn quic_direct_addresses(&self) -> Option<&Vec<SocketAddr>> {
352 self.quic_contact
353 .as_ref()
354 .map(|contact| &contact.direct_addresses)
355 }
356
357 pub fn supports_quic_connection_type(&self, connection_type: &QuicConnectionType) -> bool {
359 self.quic_contact
360 .as_ref()
361 .map(|contact| {
362 contact
363 .successful_connection_types
364 .contains(connection_type)
365 })
366 .unwrap_or(false)
367 }
368
369 pub fn quic_quality_score(&self) -> f64 {
371 if let Some(ref quic_contact) = self.quic_contact {
372 let setup_score = if quic_contact.quic_quality.avg_connection_setup_time_ms > 0.0 {
373 (5000.0 / (quic_contact.quic_quality.avg_connection_setup_time_ms + 1000.0))
375 .min(1.0)
376 } else {
377 0.5 };
379 let type_diversity_score = quic_contact.successful_connection_types.len() as f64 / 2.0; let success_score = quic_contact.quic_quality.connection_success_rate;
381
382 (setup_score * 0.4 + type_diversity_score * 0.3 + success_score * 0.3).clamp(0.0, 1.0)
384 } else {
385 0.0 }
387 }
388}
389
390impl Default for QualityMetrics {
391 fn default() -> Self {
392 Self::new()
393 }
394}
395
396impl QualityMetrics {
397 pub fn new() -> Self {
399 let now = chrono::Utc::now();
400
401 Self {
402 success_rate: 0.0,
403 avg_latency_ms: 0.0,
404 quality_score: 0.0,
405 last_connection_attempt: now,
406 last_successful_connection: now,
407 uptime_score: 0.5, }
409 }
410
411 pub fn apply_age_decay(&mut self, decay_factor: f64) {
413 self.quality_score *= decay_factor;
415 self.uptime_score *= decay_factor;
416 }
417}
418
419impl Default for ConnectionHistory {
420 fn default() -> Self {
421 Self::new()
422 }
423}
424
425impl ConnectionHistory {
426 pub fn new() -> Self {
428 Self {
429 total_attempts: 0,
430 successful_connections: 0,
431 failed_connections: 0,
432 total_session_time: Duration::from_secs(0),
433 recent_latencies: Vec::new(),
434 connection_failures: HashMap::new(),
435 }
436 }
437
438 pub fn add_session_time(&mut self, duration: Duration) {
440 self.total_session_time = self.total_session_time.saturating_add(duration);
441 }
442
443 pub fn get_failure_rate(&self, error_type: &str) -> f64 {
445 let failures = self
446 .connection_failures
447 .get(error_type)
448 .copied()
449 .unwrap_or(0);
450 if self.total_attempts > 0 {
451 failures as f64 / self.total_attempts as f64
452 } else {
453 0.0
454 }
455 }
456}
457
458pub struct QualityCalculator {
460 success_weight: f64,
461 latency_weight: f64,
462 recency_weight: f64,
463 reputation_weight: f64,
464 verification_bonus: f64,
465 capability_bonus: f64,
466}
467
468impl QualityCalculator {
469 pub fn new() -> Self {
471 Self {
472 success_weight: 0.40, latency_weight: 0.30, recency_weight: 0.20, reputation_weight: 0.10, verification_bonus: 0.05, capability_bonus: 0.02, }
479 }
480
481 pub fn calculate_quality(&self, contact: &ContactEntry) -> f64 {
483 let mut score = 0.0;
484
485 let success_component = contact.quality_metrics.success_rate * self.success_weight;
487 score += success_component;
488
489 let latency_component = if contact.quality_metrics.avg_latency_ms > 0.0 {
491 let normalized_latency =
492 (1000.0 / (contact.quality_metrics.avg_latency_ms + 100.0)).min(1.0);
493 normalized_latency * self.latency_weight
494 } else {
495 0.0
496 };
497 score += latency_component;
498
499 let age_seconds = contact.age_seconds() as f64;
501 let recency_component = (-age_seconds / 86400.0).exp() * self.recency_weight; score += recency_component;
503
504 let reputation_component = contact.reputation_score * self.reputation_weight;
506 score += reputation_component;
507
508 if contact.ipv6_identity_verified {
510 score += self.verification_bonus;
511 }
512
513 let important_capabilities = ["dht", "relay"];
515 let capability_count = important_capabilities
516 .iter()
517 .filter(|&cap| contact.has_capability(cap))
518 .count();
519 score += capability_count as f64 * self.capability_bonus;
520
521 if let Some(ref quic_contact) = contact.quic_contact {
523 let quic_score = contact.quic_quality_score();
524 let quic_bonus = 0.10 * quic_score;
526 score += quic_bonus;
527
528 let diversity_bonus =
530 (quic_contact.successful_connection_types.len() as f64 / 2.0) * 0.03;
531 score += diversity_bonus;
532 }
533
534 score.clamp(0.0, 1.0)
536 }
537
538 pub fn calculate_with_weights(
540 &self,
541 contact: &ContactEntry,
542 success_weight: f64,
543 latency_weight: f64,
544 recency_weight: f64,
545 reputation_weight: f64,
546 ) -> f64 {
547 let mut calculator = self.clone();
548 calculator.success_weight = success_weight;
549 calculator.latency_weight = latency_weight;
550 calculator.recency_weight = recency_weight;
551 calculator.reputation_weight = reputation_weight;
552
553 calculator.calculate_quality(contact)
554 }
555}
556
557impl Clone for QualityCalculator {
558 fn clone(&self) -> Self {
559 Self {
560 success_weight: self.success_weight,
561 latency_weight: self.latency_weight,
562 recency_weight: self.recency_weight,
563 reputation_weight: self.reputation_weight,
564 verification_bonus: self.verification_bonus,
565 capability_bonus: self.capability_bonus,
566 }
567 }
568}
569
570impl Default for QualityCalculator {
571 fn default() -> Self {
572 Self::new()
573 }
574}
575
576impl QuicContactInfo {
577 pub fn new(direct_addresses: Vec<std::net::SocketAddr>) -> Self {
579 let now = chrono::Utc::now();
580
581 Self {
582 direct_addresses,
583 quic_quality: QuicQualityMetrics::new(),
584 last_quic_connection: now,
585 successful_connection_types: Vec::new(),
586 }
587 }
588
589 pub fn update_direct_addresses(&mut self, addresses: Vec<std::net::SocketAddr>) {
591 self.direct_addresses = addresses;
592 }
593
594 pub fn has_connectivity_options(&self) -> bool {
596 !self.direct_addresses.is_empty()
597 }
598}
599
600impl QuicQualityMetrics {
601 pub fn new() -> Self {
603 Self {
604 avg_response_time_ms: 0.0,
605 avg_throughput_mbps: 0.0,
606 connection_success_rate: 0.0,
607 avg_connection_setup_time_ms: 0.0,
608 connection_type_success_rates: HashMap::new(),
609 }
610 }
611
612 pub fn overall_score(&self) -> f64 {
614 let speed_score = if self.avg_connection_setup_time_ms > 0.0 {
615 (5000.0 / (self.avg_connection_setup_time_ms + 1000.0)).min(1.0)
616 } else {
617 0.5
618 };
619 let reliability_score = if !self.connection_type_success_rates.is_empty() {
620 self.connection_type_success_rates.values().sum::<f64>()
621 / self.connection_type_success_rates.len() as f64
622 } else {
623 0.0
624 };
625 let success_score = self.connection_success_rate;
626
627 (speed_score * 0.4 + reliability_score * 0.3 + success_score * 0.3).clamp(0.0, 1.0)
629 }
630}
631
632impl Default for QuicQualityMetrics {
633 fn default() -> Self {
634 Self::new()
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use super::*;
641
642 #[test]
643 fn test_contact_entry_creation() {
644 let peer_id = PeerId::from("test-peer");
645 let addresses = vec!["127.0.0.1:9000".parse().unwrap()];
646
647 let contact = ContactEntry::new(peer_id.clone(), addresses.clone());
648
649 assert_eq!(contact.peer_id, peer_id);
650 assert_eq!(contact.addresses, addresses);
651 assert_eq!(contact.quality_metrics.success_rate, 0.0);
652 assert!(!contact.ipv6_identity_verified);
653 }
654
655 #[test]
656 fn test_quality_calculation() {
657 let mut contact = ContactEntry::new(
658 PeerId::from("test-peer"),
659 vec!["127.0.0.1:9000".parse().unwrap()],
660 );
661
662 contact.update_connection_result(true, Some(50), None);
664 contact.update_connection_result(true, Some(60), None);
665 contact.update_connection_result(false, None, Some("timeout".to_string()));
666
667 assert!(contact.quality_metrics.success_rate > 0.5);
668 assert!(contact.quality_metrics.avg_latency_ms > 0.0);
669 assert!(contact.quality_metrics.quality_score > 0.0);
670 }
671
672 #[test]
673 fn test_capability_bonus() {
674 let mut contact = ContactEntry::new(
675 PeerId::from("test-peer"),
676 vec!["127.0.0.1:9000".parse().unwrap()],
677 );
678
679 let initial_score = contact.quality_metrics.quality_score;
680
681 contact.update_capabilities(vec!["dht".to_string()]);
682
683 assert!(contact.quality_metrics.quality_score > initial_score);
684 }
685
686 #[test]
687 fn test_stale_detection() {
688 let mut contact = ContactEntry::new(
689 PeerId::from("test-peer"),
690 vec!["127.0.0.1:9000".parse().unwrap()],
691 );
692
693 contact.last_seen = chrono::Utc::now() - chrono::Duration::hours(2);
695
696 assert!(contact.is_stale(Duration::from_secs(3600))); assert!(!contact.is_stale(Duration::from_secs(10800))); }
699
700 #[test]
701 fn test_quality_decay() {
702 let mut metrics = QualityMetrics::new();
703 metrics.quality_score = 0.8;
704 metrics.uptime_score = 0.9;
705
706 metrics.apply_age_decay(0.9);
707
708 assert!(metrics.quality_score < 0.8);
709 assert!(metrics.uptime_score < 0.9);
710 }
711
712 #[test]
713 fn test_quic_contact_creation() {
714 let addresses = vec!["127.0.0.1:9000".parse().unwrap()];
715
716 let quic_contact = QuicContactInfo::new(addresses.clone());
717
718 assert_eq!(quic_contact.direct_addresses, addresses);
719 assert!(quic_contact.has_connectivity_options());
720 }
721
722 #[test]
723 fn test_contact_with_quic_info() {
724 let addresses = vec!["127.0.0.1:9000".parse().unwrap()];
725 let quic_info = QuicContactInfo::new(addresses);
726
727 let contact = ContactEntry::new_with_quic(
728 PeerId::from("test-peer"),
729 vec!["127.0.0.1:9000".parse().unwrap()],
730 quic_info,
731 );
732
733 assert!(contact.quic_contact.is_some());
734 assert!(contact.quic_direct_addresses().is_some());
735 assert!(has_connectivity_options(&contact));
736 }
737
738 #[test]
739 fn test_quic_connection_result_update() {
740 let addresses = vec!["127.0.0.1:9000".parse().unwrap()];
741 let quic_info = QuicContactInfo::new(addresses);
742
743 let mut contact = ContactEntry::new_with_quic(
744 PeerId::from("test-peer"),
745 vec!["127.0.0.1:9000".parse().unwrap()],
746 quic_info,
747 );
748
749 contact.update_quic_connection_result(
751 QuicConnectionType::DirectIPv4,
752 true,
753 Some(250), );
755
756 assert_eq!(
757 contact
758 .quic_contact
759 .as_ref()
760 .unwrap()
761 .quic_quality
762 .avg_connection_setup_time_ms,
763 250.0
764 );
765 assert!(contact.supports_quic_connection_type(&QuicConnectionType::DirectIPv4));
766
767 let quic_quality = contact.quic_quality_score();
768 assert!(quic_quality > 0.0);
769 }
770
771 #[test]
772 fn test_quic_quality_affects_overall_score() {
773 let addresses = vec!["127.0.0.1:9000".parse().unwrap()];
774 let quic_info = QuicContactInfo::new(addresses);
775
776 let mut contact_no_quic = ContactEntry::new(
778 PeerId::from("test-peer-no-quic"),
779 vec!["127.0.0.1:9000".parse().unwrap()],
780 );
781 contact_no_quic.update_connection_result(true, Some(100), None);
782
783 let mut contact_with_quic = ContactEntry::new_with_quic(
785 PeerId::from("test-peer-with-quic"),
786 vec!["127.0.0.1:9000".parse().unwrap()],
787 quic_info,
788 );
789 contact_with_quic.update_connection_result(true, Some(100), None);
790 contact_with_quic.update_quic_connection_result(
791 QuicConnectionType::DirectIPv4,
792 true,
793 Some(200),
794 );
795
796 assert!(
798 contact_with_quic.quality_metrics.quality_score
799 > contact_no_quic.quality_metrics.quality_score
800 );
801 }
802
803 fn has_connectivity_options(contact: &ContactEntry) -> bool {
804 contact
805 .quic_contact
806 .as_ref()
807 .map(|quic| !quic.direct_addresses.is_empty())
808 .unwrap_or(false)
809 }
810}