1use 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#[derive(Debug, Clone)]
20pub struct PeerInfo {
21 pub peer_id: PeerId,
23 pub addresses: Vec<Multiaddr>,
25 pub last_seen: SystemTime,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ChunkRequest {
32 pub hash: String,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ChunkResponse {
39 pub hash: String,
41 pub data: Option<Vec<u8>>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct CommitRequest {
48 pub commit_hash: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct CommitResponse {
55 pub commits: Vec<crate::storage::CommitInfo>,
57}
58
59#[derive(NetworkBehaviour)]
61pub struct FAIBehaviour {
62 pub mdns: mdns::tokio::Behaviour,
64 pub request_response: libp2p::request_response::cbor::Behaviour<ChunkRequest, ChunkResponse>,
66 pub commit_response: libp2p::request_response::cbor::Behaviour<CommitRequest, CommitResponse>,
68}
69
70#[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
90pub struct NetworkManager {
92 swarm: Swarm<FAIBehaviour>,
94 discovered_peers: HashMap<PeerId, PeerInfo>,
96 storage: Arc<StorageManager>,
98 database: crate::database::DatabaseManager,
100 pending_commit_responses: std::collections::HashMap<libp2p::request_response::OutboundRequestId, Vec<crate::storage::CommitInfo>>,
102}
103
104impl NetworkManager {
105 pub fn new(storage: Arc<StorageManager>, database: crate::database::DatabaseManager) -> Result<Self> {
114 let local_key = Keypair::generate_ed25519();
116 let local_peer_id = PeerId::from(local_key.public());
117
118 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 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 pub async fn start(&mut self) -> Result<()> {
172 use futures::stream::StreamExt;
173
174 self.swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
176
177 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 if let Some(addr) = listening_addr {
192 self.write_peer_info_file(&addr).await?;
193 }
194
195 Ok(())
196 }
197
198 pub async fn poll_events(&mut self) -> Result<()> {
203 use futures::stream::StreamExt;
204
205 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 }
213 Err(_) => {
214 }
216 }
217 Ok(())
218 }
219
220 async fn poll_events_for_connection(&mut self) -> Result<()> {
225 use futures::stream::StreamExt;
226
227 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 }
236 Err(_) => {
237 }
239 }
240 Ok(())
241 }
242
243 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 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 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 }
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 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 let commits: Vec<crate::storage::CommitInfo> = if let Some(hash) = &request.commit_hash {
355 match self.database.get_commit(hash) {
357 Ok(Some(db_commit)) => {
358 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 match self.database.get_commit_history(None) {
383 Ok(db_commits) => {
384 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 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 pub fn list_peers(&self) -> Vec<PeerInfo> {
502 self.discovered_peers.values().cloned().collect()
503 }
504
505 pub fn local_peer_id(&self) -> PeerId {
510 *self.swarm.local_peer_id()
511 }
512
513 pub fn listeners(&self) -> Vec<Multiaddr> {
518 self.swarm.listeners().cloned().collect()
519 }
520
521 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 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 let locations = vec![
543 "/tmp/fai_test_peers.txt", "./fai_peers.txt", ];
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 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", "./fai_peers.txt", ];
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 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 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 self.connect_to_peer(addr)
630 }
631
632 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 pub async fn request_chunk(&mut self, peer: PeerId, hash: &str) -> Result<Option<Vec<u8>>> {
660 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 if let Some(peer_info) = self.discovered_peers.get(&peer) {
669 let mut addrs = peer_info.addresses.clone();
670
671 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) });
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 break;
688 }
689 }
690
691 println!("DEBUG: Starting connection waiting loop...");
693 for attempt in 0..50 {
694 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 { 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 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 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 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 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 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 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 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 println!("DEBUG: Starting connection waiting loop for commits...");
823 for attempt in 0..50 {
824 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 { 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 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 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 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 if let Err(e) = self.poll_events().await {
879 println!("Warning: Error during response polling: {}", e);
880 }
881
882 if let Some(commits) = self.pending_commit_responses.get(&request_id) {
884 println!("DEBUG: Received {} commits from peer", commits.len());
885
886 for commit in commits {
888 let _timestamp = chrono::DateTime::from_timestamp(commit.timestamp / 1000, 0)
890 .unwrap_or_else(|| chrono::Utc::now());
891
892 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, &files
905 ) {
906 println!("Warning: Failed to store commit {}: {}", commit.hash, e);
907 }
908 }
909
910 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 pub async fn send_commits(
932 &mut self,
933 peer: PeerId,
934 commits: Vec<crate::storage::CommitInfo>,
935 ) -> Result<()> {
936 println!("DEBUG: send_commits called with {} commits", commits.len());
938 println!("DEBUG: Connected to peer {}, push completed", peer);
939 Ok(())
940 }
941}