ant_core/bootstrap/
mod.rs

1//! Bootstrap Cache System
2//!
3//! Provides decentralized peer discovery through local caching of known contacts.
4//! Eliminates dependency on central bootstrap servers by maintaining a high-quality
5//! cache of up to 30,000 peer contacts with automatic conflict resolution for
6//! multiple concurrent instances.
7
8pub mod cache;
9pub mod contact;
10pub mod discovery;
11pub mod merge;
12pub mod words;
13
14pub use cache::{BootstrapCache, CacheConfig, CacheError};
15pub use contact::{ContactEntry, QualityMetrics, QualityCalculator};
16pub use discovery::{BootstrapDiscovery, BootstrapConfig, ConfigurableBootstrapDiscovery};
17pub use merge::{MergeCoordinator, MergeResult};
18pub use words::{ThreeWordAddress, WordDictionary, WordEncoder};
19
20use crate::{Result, P2PError, PeerId};
21use std::path::PathBuf;
22use std::time::Duration;
23
24/// Default cache configuration
25pub const DEFAULT_MAX_CONTACTS: usize = 30_000;
26/// Default directory for storing bootstrap cache files
27pub const DEFAULT_CACHE_DIR: &str = ".cache/p2p_foundation";
28/// Default interval for merging instance cache files
29pub const DEFAULT_MERGE_INTERVAL: Duration = Duration::from_secs(30);
30/// Default interval for cleaning up stale contacts (1 hour)
31pub const DEFAULT_CLEANUP_INTERVAL: Duration = Duration::from_secs(3600);
32/// Default interval for updating contact quality scores (5 minutes)
33pub const DEFAULT_QUALITY_UPDATE_INTERVAL: Duration = Duration::from_secs(300);
34
35/// Bootstrap cache initialization and management
36pub struct BootstrapManager {
37    cache: BootstrapCache,
38    merge_coordinator: MergeCoordinator,
39    word_encoder: WordEncoder,
40}
41
42impl BootstrapManager {
43    /// Create a new bootstrap manager with default configuration
44    pub async fn new() -> Result<Self> {
45        let cache_dir = home_cache_dir()?;
46        let config = CacheConfig::default();
47        
48        let cache = BootstrapCache::new(cache_dir.clone(), config).await?;
49        let merge_coordinator = MergeCoordinator::new(cache_dir)?;
50        let word_encoder = WordEncoder::new();
51        
52        Ok(Self {
53            cache,
54            merge_coordinator,
55            word_encoder,
56        })
57    }
58    
59    /// Create a new bootstrap manager with custom configuration
60    pub async fn with_config(config: CacheConfig) -> Result<Self> {
61        let cache_dir = home_cache_dir()?;
62        
63        let cache = BootstrapCache::new(cache_dir.clone(), config).await?;
64        let merge_coordinator = MergeCoordinator::new(cache_dir)?;
65        let word_encoder = WordEncoder::new();
66        
67        Ok(Self {
68            cache,
69            merge_coordinator,
70            word_encoder,
71        })
72    }
73    
74    /// Get bootstrap peers for initial connection
75    pub async fn get_bootstrap_peers(&self, count: usize) -> Result<Vec<ContactEntry>> {
76        self.cache.get_bootstrap_peers(count).await
77    }
78    
79    /// Add a discovered peer to the cache
80    pub async fn add_contact(&mut self, contact: ContactEntry) -> Result<()> {
81        self.cache.add_contact(contact).await
82    }
83    
84    /// Update contact performance metrics
85    pub async fn update_contact_metrics(&mut self, peer_id: &PeerId, metrics: QualityMetrics) -> Result<()> {
86        self.cache.update_contact_metrics(peer_id, metrics).await
87    }
88    
89    /// Start background maintenance tasks
90    pub async fn start_background_tasks(&mut self) -> Result<()> {
91        // Start periodic merge of instance caches
92        let cache_clone = self.cache.clone();
93        let merge_coordinator = self.merge_coordinator.clone();
94        
95        tokio::spawn(async move {
96            let mut interval = tokio::time::interval(DEFAULT_MERGE_INTERVAL);
97            loop {
98                interval.tick().await;
99                if let Err(e) = merge_coordinator.merge_instance_caches(&cache_clone).await {
100                    tracing::warn!("Failed to merge instance caches: {}", e);
101                }
102            }
103        });
104        
105        // Start quality score updates
106        let cache_clone = self.cache.clone();
107        tokio::spawn(async move {
108            let mut interval = tokio::time::interval(DEFAULT_QUALITY_UPDATE_INTERVAL);
109            loop {
110                interval.tick().await;
111                if let Err(e) = cache_clone.update_quality_scores().await {
112                    tracing::warn!("Failed to update quality scores: {}", e);
113                }
114            }
115        });
116        
117        // Start cleanup task
118        let cache_clone = self.cache.clone();
119        tokio::spawn(async move {
120            let mut interval = tokio::time::interval(DEFAULT_CLEANUP_INTERVAL);
121            loop {
122                interval.tick().await;
123                if let Err(e) = cache_clone.cleanup_stale_entries().await {
124                    tracing::warn!("Failed to cleanup stale entries: {}", e);
125                }
126            }
127        });
128        
129        Ok(())
130    }
131    
132    /// Get cache statistics
133    pub async fn get_stats(&self) -> Result<CacheStats> {
134        self.cache.get_stats().await
135    }
136    
137    /// Force a cache merge operation
138    pub async fn force_merge(&self) -> Result<MergeResult> {
139        self.merge_coordinator.merge_instance_caches(&self.cache).await
140    }
141    
142    /// Convert multiaddr to three-word address
143    pub fn encode_address(&self, multiaddr: &crate::Multiaddr) -> Result<ThreeWordAddress> {
144        self.word_encoder.encode_multiaddr(multiaddr)
145    }
146    
147    /// Convert three-word address to multiaddr (requires registry lookup)
148    pub fn decode_address(&self, words: &ThreeWordAddress) -> Result<crate::Multiaddr> {
149        self.word_encoder.decode_to_multiaddr(words)
150    }
151    
152    /// Validate three-word address format
153    pub fn validate_words(&self, words: &ThreeWordAddress) -> Result<()> {
154        words.validate(&self.word_encoder)
155    }
156    
157    /// Get the word encoder for direct access
158    pub fn word_encoder(&self) -> &WordEncoder {
159        &self.word_encoder
160    }
161    
162    /// Get well-known bootstrap addresses as three-word addresses
163    pub fn get_well_known_word_addresses(&self) -> Vec<(ThreeWordAddress, crate::Multiaddr)> {
164        let well_known_addrs = vec![
165            // Primary bootstrap nodes with well-known addresses
166            "/ip6/2001:4860:4860::8888/udp/9000/quic", // Example - would be real bootstrap nodes
167            "/ip6/2001:4860:4860::8844/udp/9001/quic",
168            "/ip6/2606:4700:4700::1111/udp/9002/quic",
169        ];
170        
171        well_known_addrs
172            .into_iter()
173            .filter_map(|addr_str| {
174                if let Ok(multiaddr) = addr_str.parse() {
175                    if let Ok(words) = self.encode_address(&multiaddr) {
176                        Some((words, multiaddr))
177                    } else {
178                        None
179                    }
180                } else {
181                    None
182                }
183            })
184            .collect()
185    }
186}
187
188/// Cache statistics for monitoring
189#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
190pub struct CacheStats {
191    /// Total number of contacts in the cache
192    pub total_contacts: usize,
193    /// Number of contacts with high quality scores
194    pub high_quality_contacts: usize,
195    /// Number of contacts with verified IPv6 identity
196    pub verified_contacts: usize,
197    /// Timestamp of the last cache merge operation
198    pub last_merge: chrono::DateTime<chrono::Utc>,
199    /// Timestamp of the last cache cleanup operation
200    pub last_cleanup: chrono::DateTime<chrono::Utc>,
201    /// Cache hit rate for peer discovery operations
202    pub cache_hit_rate: f64,
203    /// Average quality score across all contacts
204    pub average_quality_score: f64,
205}
206
207/// Get the home cache directory
208fn home_cache_dir() -> Result<PathBuf> {
209    let home = std::env::var("HOME")
210        .or_else(|_| std::env::var("USERPROFILE"))
211        .map_err(|_| P2PError::Bootstrap("Unable to determine home directory".to_string()))?;
212    
213    let cache_dir = PathBuf::from(home).join(DEFAULT_CACHE_DIR);
214    
215    // Ensure cache directory exists
216    std::fs::create_dir_all(&cache_dir)
217        .map_err(|e| P2PError::Bootstrap(format!("Failed to create cache directory: {}", e)))?;
218    
219    Ok(cache_dir)
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use tempfile::TempDir;
226
227    #[tokio::test]
228    async fn test_bootstrap_manager_creation() {
229        let temp_dir = TempDir::new().unwrap();
230        let config = CacheConfig {
231            cache_dir: temp_dir.path().to_path_buf(),
232            max_contacts: 1000,
233            ..CacheConfig::default()
234        };
235        
236        let manager = BootstrapManager::with_config(config).await;
237        assert!(manager.is_ok());
238    }
239    
240    #[tokio::test]
241    async fn test_home_cache_dir() {
242        let result = home_cache_dir();
243        assert!(result.is_ok());
244        
245        let path = result.unwrap();
246        assert!(path.exists());
247        assert!(path.is_dir());
248    }
249}