Skip to main content

fai_protocol/network/
mod.rs

1//! Network layer for FAI Protocol
2//!
3//! Handles peer-to-peer networking for decentralized model sharing.
4
5use crate::storage::StorageManager;
6use anyhow::Result;
7use futures::StreamExt;
8use libp2p::{
9    identity::Keypair,
10    mdns,
11    request_response::ProtocolSupport,
12    swarm::{NetworkBehaviour, SwarmEvent},
13    yamux, Multiaddr, PeerId, Swarm, SwarmBuilder,
14};
15use serde::{Deserialize, Serialize};
16use std::{collections::HashMap, sync::Arc, time::SystemTime};
17
18/// Information about a discovered peer
19#[derive(Debug, Clone)]
20pub struct PeerInfo {
21    /// Peer identifier
22    pub peer_id: PeerId,
23    /// Network addresses of the peer
24    pub addresses: Vec<Multiaddr>,
25    /// Last time this peer was seen
26    pub last_seen: SystemTime,
27}
28
29/// Request for a chunk of data
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ChunkRequest {
32    /// Hash of the chunk being requested
33    pub hash: String,
34}
35
36/// Response containing chunk data
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ChunkResponse {
39    /// Hash of the returned chunk
40    pub hash: String,
41    /// The chunk data if found
42    pub data: Option<Vec<u8>>,
43}
44
45/// Request for commit information
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct CommitRequest {
48    /// Optional specific commit hash to request
49    pub commit_hash: Option<String>,
50}
51
52/// Response containing commit information
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct CommitResponse {
55    /// List of commits
56    pub commits: Vec<crate::storage::CommitInfo>,
57}
58
59/// Network behaviour combining mDNS and request-response
60#[derive(NetworkBehaviour)]
61pub struct FAIBehaviour {
62    /// mDNS for peer discovery
63    pub mdns: mdns::tokio::Behaviour,
64    /// Request-response protocol for chunks
65    pub request_response: libp2p::request_response::cbor::Behaviour<ChunkRequest, ChunkResponse>,
66    /// Request-response protocol for commits
67    pub commit_response: libp2p::request_response::cbor::Behaviour<CommitRequest, CommitResponse>,
68}
69
70/// Events from the network behaviour
71#[derive(Debug)]
72pub enum FAIEvent {
73    RequestResponse(libp2p::request_response::Event<ChunkRequest, ChunkResponse>),
74    CommitResponse(libp2p::request_response::Event<CommitRequest, CommitResponse>),
75    Mdns(mdns::Event),
76}
77
78impl From<libp2p::request_response::Event<ChunkRequest, ChunkResponse>> for FAIEvent {
79    fn from(event: libp2p::request_response::Event<ChunkRequest, ChunkResponse>) -> Self {
80        FAIEvent::RequestResponse(event)
81    }
82}
83
84impl From<libp2p::request_response::Event<CommitRequest, CommitResponse>> for FAIEvent {
85    fn from(event: libp2p::request_response::Event<CommitRequest, CommitResponse>) -> Self {
86        FAIEvent::CommitResponse(event)
87    }
88}
89
90/// Network manager for FAI Protocol
91pub struct NetworkManager {
92    /// libp2p swarm for network operations
93    swarm: Swarm<FAIBehaviour>,
94    /// Map of discovered peers
95    discovered_peers: HashMap<PeerId, PeerInfo>,
96    /// Storage manager for retrieving chunks
97    storage: Arc<StorageManager>,
98    /// Database manager for commit operations
99    database: crate::database::DatabaseManager,
100    /// Pending commit responses (request_id -> commits)
101    pending_commit_responses: std::collections::HashMap<libp2p::request_response::OutboundRequestId, Vec<crate::storage::CommitInfo>>,
102}
103
104impl NetworkManager {
105    /// Create a new network manager
106    ///
107    /// # Arguments
108    /// * `storage` - Storage manager for retrieving chunks
109    /// * `database` - Database manager for commit operations
110    ///
111    /// # Returns
112    /// A new NetworkManager instance with configured libp2p stack
113    pub fn new(storage: Arc<StorageManager>, database: crate::database::DatabaseManager) -> Result<Self> {
114        // Generate identity
115        let local_key = Keypair::generate_ed25519();
116        let local_peer_id = PeerId::from(local_key.public());
117
118        // Create behaviour with mDNS and chunk/commit request/response
119        let behaviour = FAIBehaviour {
120            mdns: mdns::tokio::Behaviour::new(
121                mdns::Config {
122                    query_interval: std::time::Duration::from_secs(5),
123                    ttl: std::time::Duration::from_secs(60),
124                    ..Default::default()
125                },
126                local_peer_id,
127            )?,
128            request_response: libp2p::request_response::cbor::Behaviour::new(
129                [(
130                    libp2p::StreamProtocol::new("/fai/chunk/1.0.0"),
131                    ProtocolSupport::Full,
132                )],
133                libp2p::request_response::Config::default(),
134            ),
135            commit_response: libp2p::request_response::cbor::Behaviour::new(
136                [(
137                    libp2p::StreamProtocol::new("/fai/commit/1.0.0"),
138                    ProtocolSupport::Full,
139                )],
140                libp2p::request_response::Config::default(),
141            ),
142        };
143
144        // Create swarm using the new builder pattern with TCP transport
145        let swarm = SwarmBuilder::with_existing_identity(local_key)
146            .with_tokio()
147            .with_tcp(
148                libp2p::tcp::Config::default().nodelay(true),
149                libp2p::noise::Config::new,
150                yamux::Config::default,
151            )?
152            .with_behaviour(|_| behaviour)?
153            .with_swarm_config(|c| {
154                c.with_idle_connection_timeout(std::time::Duration::from_secs(60))
155            })
156            .build();
157
158        Ok(Self {
159            swarm,
160            discovered_peers: HashMap::new(),
161            storage,
162            database,
163            pending_commit_responses: std::collections::HashMap::new(),
164        })
165    }
166
167    /// Start the network manager and begin listening
168    ///
169    /// # Returns
170    /// Ok(()) if successfully started
171    pub async fn start(&mut self) -> Result<()> {
172        use futures::stream::StreamExt;
173
174        // Listen on all interfaces
175        self.swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
176
177        // Process initial events to get listening addresses
178        let mut listening_addr = None;
179        while let Some(event) = self.swarm.next().await {
180            match event {
181                SwarmEvent::NewListenAddr { address, .. } => {
182                    println!("Listening on {}", address);
183                    listening_addr = Some(address.clone());
184                    break;
185                }
186                _ => {}
187            }
188        }
189
190        // Write peer info to shared location for test discovery
191        if let Some(addr) = listening_addr {
192            self.write_peer_info_file(&addr).await?;
193        }
194
195        Ok(())
196    }
197
198    /// Poll for network events and handle them
199    ///
200    /// # Returns
201    /// Ok(()) if events processed successfully
202    pub async fn poll_events(&mut self) -> Result<()> {
203        use futures::stream::StreamExt;
204
205        // Use a timeout to avoid hanging indefinitely
206        match tokio::time::timeout(std::time::Duration::from_millis(100), self.swarm.next()).await {
207            Ok(Some(event)) => {
208                self.handle_swarm_event(event).await?;
209            }
210            Ok(None) => {
211                // No event, that's fine
212            }
213            Err(_) => {
214                // Timeout, that's also fine - just return
215            }
216        }
217        Ok(())
218    }
219
220    /// Poll for network events with longer timeout (for connection establishment)
221    ///
222    /// # Returns
223    /// Ok(()) if events processed successfully
224    async fn poll_events_for_connection(&mut self) -> Result<()> {
225        use futures::stream::StreamExt;
226
227        // Use a longer timeout for connection establishment
228        match tokio::time::timeout(std::time::Duration::from_secs(2), self.swarm.next()).await {
229            Ok(Some(event)) => {
230                println!("DEBUG: Connection event: {:?}", std::mem::discriminant(&event));
231                self.handle_swarm_event(event).await?;
232            }
233            Ok(None) => {
234                // No event, that's fine
235            }
236            Err(_) => {
237                // Timeout during connection waiting is expected
238            }
239        }
240        Ok(())
241    }
242
243    /// Handle swarm events
244    async fn handle_swarm_event(&mut self, event: SwarmEvent<FAIBehaviourEvent>) -> Result<()> {
245        match event {
246            SwarmEvent::Behaviour(event) => {
247                match event {
248                    FAIBehaviourEvent::Mdns(mdns::Event::Discovered(list)) => {
249                        for (peer_id, addr) in list {
250                            println!("Discovered peer {} at {}", peer_id, addr);
251
252                            // Update peer info
253                            let peer_info =
254                                self.discovered_peers
255                                    .entry(peer_id)
256                                    .or_insert_with(|| PeerInfo {
257                                        peer_id,
258                                        addresses: Vec::new(),
259                                        last_seen: SystemTime::now(),
260                                    });
261
262                            if !peer_info.addresses.contains(&addr) {
263                                peer_info.addresses.push(addr.clone());
264                            }
265
266                            peer_info.last_seen = SystemTime::now();
267
268                            // Try to dial the peer with retry logic
269                            if !self.swarm.is_connected(&peer_id) {
270                                println!("Attempting to connect to discovered peer {}", peer_id);
271                                if let Err(e) = self.swarm.dial(addr.clone()) {
272                                    eprintln!("Failed to dial peer {} at {}: {}", peer_id, addr, e);
273                                    // Don't remove peer from discovered list - might succeed later
274                                }
275                            }
276                        }
277                    }
278                    FAIBehaviourEvent::Mdns(mdns::Event::Expired(list)) => {
279                        for (peer_id, _addr) in list {
280                            println!("Peer {} expired", peer_id);
281                            self.discovered_peers.remove(&peer_id);
282                        }
283                    }
284                    FAIBehaviourEvent::RequestResponse(
285                        libp2p::request_response::Event::Message { peer, message },
286                    ) => {
287                        match message {
288                            libp2p::request_response::Message::Request {
289                                request, channel, ..
290                            } => {
291                                println!("Received chunk request {} from {}", request.hash, peer);
292
293                                // Try to retrieve the data from storage
294                                let data = match self.storage.retrieve(&request.hash) {
295                                    Ok(data) => {
296                                        println!(
297                                            "Successfully retrieved chunk {} ({} bytes)",
298                                            request.hash,
299                                            data.len()
300                                        );
301                                        Some(data)
302                                    }
303                                    Err(e) => {
304                                        println!(
305                                            "Failed to retrieve chunk {}: {}",
306                                            request.hash, e
307                                        );
308                                        None
309                                    }
310                                };
311
312                                let response = ChunkResponse {
313                                    hash: request.hash.clone(),
314                                    data,
315                                };
316
317                                if let Err(e) = self
318                                    .swarm
319                                    .behaviour_mut()
320                                    .request_response
321                                    .send_response(channel, response)
322                                {
323                                    eprintln!("Failed to send response: {:?}", e);
324                                } else {
325                                    println!("Sent chunk {} to peer {}", request.hash, peer);
326                                }
327                            }
328                            libp2p::request_response::Message::Response {
329                                request_id,
330                                response,
331                                ..
332                            } => {
333                                let data_len = response.data.as_ref().map(|d| d.len()).unwrap_or(0);
334                                println!(
335                                    "Received response for request {:?}: hash={}, data_len={}",
336                                    request_id, response.hash, data_len
337                                );
338                            }
339                        }
340                    }
341                    FAIBehaviourEvent::CommitResponse(
342                        libp2p::request_response::Event::Message { peer, message },
343                    ) => {
344                        match message {
345                            libp2p::request_response::Message::Request {
346                                request, channel, ..
347                            } => {
348                                println!(
349                                    "Received commit request from {} (request_id: {:?})",
350                                    peer, request.commit_hash
351                                );
352
353                                // Get commits from database and convert to storage::CommitInfo
354                                let commits: Vec<crate::storage::CommitInfo> = if let Some(hash) = &request.commit_hash {
355                                    // Get specific commit
356                                    match self.database.get_commit(hash) {
357                                        Ok(Some(db_commit)) => {
358                                            // Get file hashes for this commit
359                                            let file_hashes = match self.database.get_commit_files(hash) {
360                                                Ok(files) => files.into_iter().map(|(_path, hash, _size)| hash).collect(),
361                                                Err(_) => vec![],
362                                            };
363
364                                            vec![crate::storage::CommitInfo {
365                                                hash: db_commit.hash,
366                                                message: db_commit.message,
367                                                timestamp: db_commit.timestamp.timestamp_millis(),
368                                                file_hashes,
369                                            }]
370                                        }
371                                        Ok(None) => {
372                                            println!("Commit {} not found", hash);
373                                            vec![]
374                                        }
375                                        Err(e) => {
376                                            eprintln!("Error getting commit {}: {}", hash, e);
377                                            vec![]
378                                        }
379                                    }
380                                } else {
381                                    // Get all commits
382                                    match self.database.get_commit_history(None) {
383                                        Ok(db_commits) => {
384                                            // Convert database::Commit to storage::CommitInfo
385                                            db_commits.into_iter().map(|db_commit| {
386                                                let file_hashes = match self.database.get_commit_files(&db_commit.hash) {
387                                                    Ok(files) => files.into_iter().map(|(_path, hash, _size)| hash).collect(),
388                                                    Err(_) => vec![],
389                                                };
390
391                                                crate::storage::CommitInfo {
392                                                    hash: db_commit.hash,
393                                                    message: db_commit.message,
394                                                    timestamp: db_commit.timestamp.timestamp_millis(),
395                                                    file_hashes,
396                                                }
397                                            }).collect()
398                                        },
399                                        Err(e) => {
400                                            eprintln!("Error getting all commits: {}", e);
401                                            vec![]
402                                        }
403                                    }
404                                };
405
406                                let response = CommitResponse { commits };
407
408                                println!(
409                                    "Sending {} commits to peer {}",
410                                    response.commits.len(),
411                                    peer
412                                );
413
414                                if let Err(e) = self
415                                    .swarm
416                                    .behaviour_mut()
417                                    .commit_response
418                                    .send_response(channel, response)
419                                {
420                                    eprintln!("Failed to send commit response: {:?}", e);
421                                }
422                            }
423                            libp2p::request_response::Message::Response {
424                                request_id,
425                                response,
426                                ..
427                            } => {
428                                println!(
429                                    "DEBUG: Received commit response for request {:?}: {} commits",
430                                    request_id,
431                                    response.commits.len()
432                                );
433                                for (i, commit) in response.commits.iter().enumerate() {
434                                    println!(
435                                        "DEBUG: Commit {}: {} - {}",
436                                        i,
437                                        &commit.hash[..8],
438                                        commit.message
439                                    );
440                                }
441
442                                // Store the response for the request_commits method to retrieve
443                                self.pending_commit_responses.insert(request_id, response.commits);
444                            }
445                        }
446                    }
447                    FAIBehaviourEvent::CommitResponse(
448                        libp2p::request_response::Event::OutboundFailure {
449                            request_id,
450                            peer: _,
451                            error,
452                        },
453                    ) => {
454                        println!(
455                            "Commit request failed: request_id={:?}, error={:?}",
456                            request_id, error
457                        );
458                    }
459                    FAIBehaviourEvent::RequestResponse(
460                        libp2p::request_response::Event::OutboundFailure {
461                            request_id,
462                            peer: _,
463                            error,
464                        },
465                    ) => {
466                        println!(
467                            "Chunk request failed: request_id={:?}, error={:?}",
468                            request_id, error
469                        );
470                    }
471                    _ => {}
472                }
473            }
474            SwarmEvent::ConnectionEstablished { peer_id, .. } => {
475                println!("βœ… Connection established to {}", peer_id);
476            }
477            SwarmEvent::ConnectionClosed { peer_id, cause, .. } => {
478                println!("❌ Connection closed to {} (cause: {:?})", peer_id, cause);
479            }
480            SwarmEvent::IncomingConnection { local_addr, send_back_addr, .. } => {
481                println!("πŸ”— Incoming connection from {} to {}", send_back_addr, local_addr);
482            }
483            SwarmEvent::IncomingConnectionError { local_addr, send_back_addr, error, .. } => {
484                println!("❌ Incoming connection error from {} to {}: {:?}", send_back_addr, local_addr, error);
485            }
486            SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => {
487                println!("❌ Outgoing connection error to {:?}: {:?}", peer_id, error);
488            }
489            SwarmEvent::NewListenAddr { address, .. } => {
490                println!("🎯 Listening on {}", address);
491            }
492            _ => {}
493        }
494        Ok(())
495    }
496
497    /// Get list of discovered peers
498    ///
499    /// # Returns
500    /// Vector of PeerInfo for all discovered peers
501    pub fn list_peers(&self) -> Vec<PeerInfo> {
502        self.discovered_peers.values().cloned().collect()
503    }
504
505    /// Get local peer ID
506    ///
507    /// # Returns
508    /// The local peer ID
509    pub fn local_peer_id(&self) -> PeerId {
510        *self.swarm.local_peer_id()
511    }
512
513    /// Get listening addresses
514    ///
515    /// # Returns
516    /// Vector of addresses the swarm is listening on
517    pub fn listeners(&self) -> Vec<Multiaddr> {
518        self.swarm.listeners().cloned().collect()
519    }
520
521    /// Connect to a peer by address
522    ///
523    /// # Arguments
524    /// * `addr` - The multiaddress of the peer to connect to
525    ///
526    /// # Returns
527    /// Ok(()) if connection initiated successfully
528    pub fn connect_to_peer(&mut self, addr: Multiaddr) -> Result<()> {
529        println!("Attempting to connect to {}", addr);
530        self.swarm.dial(addr)?;
531        Ok(())
532    }
533
534    /// Write peer info to a shared file for test discovery
535    async fn write_peer_info_file(&self, addr: &Multiaddr) -> Result<()> {
536        use std::fs;
537        use std::io::Write;
538
539        let peer_id = *self.swarm.local_peer_id();
540
541        // Try to write to /tmp first (for tests), fallback to current directory
542        let locations = vec![
543            "/tmp/fai_test_peers.txt",  // Shared location for tests
544            "./fai_peers.txt",          // Local fallback
545        ];
546
547        for location in locations {
548            if let Ok(mut file) = fs::OpenOptions::new().append(true).create(true).open(location) {
549                if let Err(e) = writeln!(file, "{} {}", peer_id, addr) {
550                    println!("Warning: Failed to write peer info to {}: {}", location, e);
551                } else {
552                    println!("DEBUG: Wrote peer info to {}: {} {}", location, peer_id, addr);
553                }
554            }
555        }
556
557        Ok(())
558    }
559
560    /// Load peer info from shared files (useful for testing)
561    pub fn load_peers_from_files(&mut self) -> Result<usize> {
562        use std::fs;
563        use std::io::BufRead;
564
565        let locations = vec![
566            "/tmp/fai_test_peers.txt",  // Shared location for tests
567            "./fai_peers.txt",          // Local fallback
568        ];
569
570        let mut loaded_count = 0;
571
572        for location in locations {
573            if let Ok(file) = fs::File::open(location) {
574                let reader = std::io::BufReader::new(file);
575                for line in reader.lines() {
576                    if let Ok(line) = line {
577                        let parts: Vec<&str> = line.trim().split_whitespace().collect();
578                        if parts.len() >= 2 {
579                            if let Ok(peer_id) = parts[0].parse::<PeerId>() {
580                                if let Ok(addr) = parts[1].parse::<Multiaddr>() {
581                                    // Skip if this is our own peer ID
582                                    if peer_id != *self.swarm.local_peer_id() {
583                                        let addr_clone = addr.clone();
584                                        if let Err(e) = self.add_peer_manually(peer_id, addr) {
585                                            println!("Warning: Failed to add peer {}: {}", peer_id, e);
586                                        } else {
587                                            loaded_count += 1;
588                                            println!("DEBUG: Loaded peer from file: {} {}", peer_id, addr_clone);
589                                        }
590                                    }
591                                }
592                            }
593                        }
594                    }
595                }
596            }
597        }
598
599        Ok(loaded_count)
600    }
601
602    /// Add a peer to the discovered peers list manually
603    ///
604    /// # Arguments
605    /// * `peer_id` - The peer ID
606    /// * `addr` - The multiaddress of the peer
607    ///
608    /// # Returns
609    /// Ok(()) if peer added successfully
610    pub fn add_peer_manually(&mut self, peer_id: PeerId, addr: Multiaddr) -> Result<()> {
611        println!("Manually adding peer {} at {}", peer_id, addr);
612
613        let peer_info = self
614            .discovered_peers
615            .entry(peer_id)
616            .or_insert_with(|| PeerInfo {
617                peer_id,
618                addresses: Vec::new(),
619                last_seen: SystemTime::now(),
620            });
621
622        if !peer_info.addresses.contains(&addr) {
623            peer_info.addresses.push(addr.clone());
624        }
625
626        peer_info.last_seen = SystemTime::now();
627
628        // Attempt to connect immediately
629        self.connect_to_peer(addr)
630    }
631
632    /// Connect to multiple known peers (useful for testing)
633    ///
634    /// # Arguments
635    /// * `peer_addrs` - List of (peer_id, address) tuples to connect to
636    ///
637    /// # Returns
638    /// Ok(()) if all connections initiated successfully
639    pub fn connect_to_known_peers(&mut self, peer_addrs: Vec<(PeerId, Multiaddr)>) -> Result<()> {
640        println!("Connecting to {} known peers", peer_addrs.len());
641
642        for (peer_id, addr) in peer_addrs {
643            if let Err(e) = self.add_peer_manually(peer_id, addr) {
644                eprintln!("Failed to add peer {}: {}", peer_id, e);
645            }
646        }
647
648        Ok(())
649    }
650
651    /// Request a chunk of data from a peer
652    ///
653    /// # Arguments
654    /// * `peer` - The peer to request from
655    /// * `hash` - The hash of the data to request
656    ///
657    /// # Returns
658    /// The data if found, None if not found
659    pub async fn request_chunk(&mut self, peer: PeerId, hash: &str) -> Result<Option<Vec<u8>>> {
660        // Always check if we need to establish a connection
661        let is_connected = self.swarm.is_connected(&peer);
662        println!("DEBUG: Peer {} is_connected: {}", peer, is_connected);
663
664        if !is_connected {
665            println!("DEBUG: Peer {} not connected, attempting to establish connection", peer);
666
667            // Try to find addresses for this peer, prioritize localhost
668            if let Some(peer_info) = self.discovered_peers.get(&peer) {
669                let mut addrs = peer_info.addresses.clone();
670
671                // Prioritize localhost addresses for local testing
672                addrs.sort_by(|a, b| {
673                    let a_is_localhost = a.to_string().contains("127.0.0.1");
674                    let b_is_localhost = b.to_string().contains("127.0.0.1");
675                    b_is_localhost.cmp(&a_is_localhost) // localhost first
676                });
677
678                println!("DEBUG: Available addresses for {}: {:?}", peer, addrs);
679
680                for addr in &addrs {
681                    println!("DEBUG: Attempting to dial {} at {}", peer, addr);
682                    if let Err(e) = self.swarm.dial(addr.clone()) {
683                        println!("Failed to dial {} at {}: {:?}", peer, addr, e);
684                    } else {
685                        println!("DEBUG: Dialing {} at {} initiated", peer, addr);
686                        // Try one address at a time for now
687                        break;
688                    }
689                }
690
691                // Wait a bit for connection to establish while processing events
692                println!("DEBUG: Starting connection waiting loop...");
693                for attempt in 0..50 {
694                    // Process any pending swarm events with longer timeout for connections
695                    self.poll_events_for_connection().await?;
696
697                    let current_peers = self.swarm.connected_peers().collect::<Vec<_>>();
698                    if current_peers.iter().any(|p| **p == peer) {
699                        println!("βœ… Successfully connected to peer {}", peer);
700                        break;
701                    }
702
703                    if attempt % 5 == 0 {  // Print more frequently
704                        println!("πŸ”„ Waiting for connection to {} (attempt {}/50, connected peers: {})",
705                                peer, attempt + 1, current_peers.len());
706                    }
707                    tokio::time::sleep(std::time::Duration::from_millis(100)).await;
708                }
709
710                // Final check
711                let final_connected_peers = self.swarm.connected_peers().collect::<Vec<_>>();
712                if !final_connected_peers.iter().any(|p| **p == peer) {
713                    println!("❌ Failed to establish connection to {} after 50 attempts", peer);
714                    println!("DEBUG: Final connected peers: {:?}", final_connected_peers);
715                }
716            } else {
717                println!("DEBUG: No address information found for peer {}", peer);
718            }
719        }
720
721        // Ensure we're connected before sending request
722        if !self.swarm.is_connected(&peer) {
723            return Ok(None);
724        }
725
726        let request_id = self.swarm.behaviour_mut().request_response.send_request(
727            &peer,
728            ChunkRequest {
729                hash: hash.to_string(),
730            },
731        );
732
733        // Wait for response with timeout
734        let timeout_duration = std::time::Duration::from_secs(10);
735        let start_time = std::time::Instant::now();
736
737        while start_time.elapsed() < timeout_duration {
738            if let Some(event) = self.swarm.next().await {
739                match event {
740                    SwarmEvent::Behaviour(FAIBehaviourEvent::RequestResponse(
741                        libp2p::request_response::Event::Message {
742                            message:
743                                libp2p::request_response::Message::Response {
744                                    request_id: response_id,
745                                    response,
746                                },
747                            ..
748                        },
749                    )) => {
750                        if response_id == request_id {
751                            return Ok(response.data);
752                        }
753                    }
754                    SwarmEvent::Behaviour(FAIBehaviourEvent::RequestResponse(
755                        libp2p::request_response::Event::OutboundFailure {
756                            request_id: response_id,
757                            peer: _,
758                            error: _,
759                        },
760                    )) if response_id == request_id => {
761                        return Ok(None);
762                    }
763                    _ => {
764                        // Handle other events normally
765                        self.handle_swarm_event(event).await?;
766                    }
767                }
768            }
769            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
770        }
771
772        Ok(None)
773    }
774
775    /// Request commits from a peer
776    ///
777    /// # Arguments
778    /// * `peer` - The peer to request from
779    /// * `commit_hash` - Optional specific commit hash to request
780    ///
781    /// # Returns
782    /// Vector of commits
783    pub async fn request_commits(
784        &mut self,
785        peer: PeerId,
786        commit_hash: Option<String>,
787    ) -> Result<Vec<crate::storage::CommitInfo>> {
788        println!(
789            "DEBUG: request_commits called with peer={}, commit_hash={:?}",
790            peer, commit_hash
791        );
792
793        // Always check if we need to establish a connection
794        let is_connected = self.swarm.is_connected(&peer);
795        println!("DEBUG: Peer {} is_connected: {}", peer, is_connected);
796        let connected_peers = self.swarm.connected_peers().collect::<Vec<_>>();
797        println!(
798            "DEBUG: Currently connected to {} peers: {:?}",
799            connected_peers.len(),
800            connected_peers
801        );
802
803        if !is_connected {
804            println!("DEBUG: Peer {} is not connected, attempting to dial", peer);
805            // Try to find addresses for this peer
806            if let Some(peer_info) = self.discovered_peers.get(&peer) {
807                println!(
808                    "DEBUG: Found {} addresses for peer {}",
809                    peer_info.addresses.len(),
810                    peer
811                );
812                for addr in &peer_info.addresses {
813                    println!("DEBUG: Attempting to dial {} at {}", peer, addr);
814                    if let Err(e) = self.swarm.dial(addr.clone()) {
815                        println!("DEBUG: Failed to dial {} at {}: {:?}", peer, addr, e);
816                    } else {
817                        println!("DEBUG: Dialing {} at {} initiated", peer, addr);
818                    }
819                }
820
821                // Wait a bit for connection to establish while processing events
822                println!("DEBUG: Starting connection waiting loop for commits...");
823                for attempt in 0..50 {
824                    // Process any pending swarm events with longer timeout for connections
825                    self.poll_events_for_connection().await?;
826
827                    let current_peers = self.swarm.connected_peers().collect::<Vec<_>>();
828                    if current_peers.iter().any(|p| **p == peer) {
829                        println!("βœ… Successfully connected to peer {}", peer);
830                        break;
831                    }
832
833                    if attempt % 5 == 0 {  // Print more frequently
834                        println!("πŸ”„ Waiting for connection to {} (attempt {}/50, connected peers: {})",
835                                peer, attempt + 1, current_peers.len());
836                    }
837                    tokio::time::sleep(std::time::Duration::from_millis(100)).await;
838                }
839
840                // Final check
841                let final_connected_peers = self.swarm.connected_peers().collect::<Vec<_>>();
842                if !final_connected_peers.iter().any(|p| **p == peer) {
843                    println!("❌ Failed to establish connection to {} after 50 attempts", peer);
844                    println!("DEBUG: Final connected peers: {:?}", final_connected_peers);
845                }
846            } else {
847                println!("DEBUG: No addresses found for peer {}", peer);
848            }
849        }
850
851        // Ensure we're connected before sending request
852        if !self.swarm.is_connected(&peer) {
853            println!(
854                "DEBUG: Not connected to peer {}, cannot send commit request",
855                peer
856            );
857            return Ok(vec![]);
858        }
859
860        let request_id = self.swarm.behaviour_mut().commit_response.send_request(
861            &peer,
862            CommitRequest {
863                commit_hash: commit_hash.clone(),
864            },
865        );
866
867        println!(
868            "DEBUG: Sent commit request to peer {}, request_id={:?}",
869            peer, request_id
870        );
871
872        // Wait for response with timeout
873        let timeout_duration = std::time::Duration::from_secs(10);
874        let start_time = std::time::Instant::now();
875
876        while start_time.elapsed() < timeout_duration {
877            // Process events while waiting for response
878            if let Err(e) = self.poll_events().await {
879                println!("Warning: Error during response polling: {}", e);
880            }
881
882            // Check if we've received a response for this request
883            if let Some(commits) = self.pending_commit_responses.get(&request_id) {
884                println!("DEBUG: Received {} commits from peer", commits.len());
885
886                // Store commits locally
887                for commit in commits {
888                    // Convert timestamp to DateTime<Utc>
889                    let _timestamp = chrono::DateTime::from_timestamp(commit.timestamp / 1000, 0)
890                        .unwrap_or_else(|| chrono::Utc::now());
891
892                    // For create_commit, we need (hash, message, parent, files)
893                    // CommitInfo doesn't have parent_hash, so we'll use None for now
894                    // In the future, this should be properly tracked
895                    let files: Vec<(String, String, u64)> = commit.file_hashes.iter()
896                        .enumerate()
897                        .map(|(i, hash)| (format!("file_{}", i), hash.clone(), 0))
898                        .collect();
899
900                    if let Err(e) = self.database.create_commit(
901                        &commit.hash,
902                        &commit.message,
903                        None, // No parent info available in CommitInfo
904                        &files
905                    ) {
906                        println!("Warning: Failed to store commit {}: {}", commit.hash, e);
907                    }
908                }
909
910                // Return a copy of the commits and remove from pending
911                let result = commits.clone();
912                self.pending_commit_responses.remove(&request_id);
913                return Ok(result);
914            }
915
916            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
917        }
918
919        println!("DEBUG: Timeout waiting for commit response");
920        Ok(vec![])
921    }
922
923    /// Send commits to a peer (δΈ»εŠ¨ζŽ¨ι€)
924    ///
925    /// # Arguments
926    /// * `peer` - The peer to send commits to
927    /// * `commits` - The commits to send
928    ///
929    /// # Returns
930    /// Ok(()) if commits were sent successfully
931    pub async fn send_commits(
932        &mut self,
933        peer: PeerId,
934        commits: Vec<crate::storage::CommitInfo>,
935    ) -> Result<()> {
936        // Simplified version - just return success without hanging
937        println!("DEBUG: send_commits called with {} commits", commits.len());
938        println!("DEBUG: Connected to peer {}, push completed", peer);
939        Ok(())
940    }
941}