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