Skip to main content

ipfrs_network/
bitswap.rs

1//! Bitswap protocol implementation for block exchange
2//!
3//! Bitswap is a data exchange protocol for sharing blocks of content-addressed data.
4//! It allows peers to request blocks they need and provide blocks they have.
5
6use cid::Cid;
7use ipfrs_core::error::Result;
8use libp2p::PeerId;
9use std::collections::{HashMap, HashSet};
10use std::sync::Arc;
11use tokio::sync::{mpsc, RwLock};
12use tracing::{debug, info};
13
14/// Bitswap protocol handler
15pub struct Bitswap {
16    /// Blocks we want (want list)
17    want_list: Arc<RwLock<HashSet<Cid>>>,
18    /// Blocks we have (have list)
19    have_list: Arc<RwLock<HashSet<Cid>>>,
20    /// Pending block requests per peer
21    pending_requests: Arc<RwLock<HashMap<PeerId, HashSet<Cid>>>>,
22    /// Block exchange events
23    event_tx: mpsc::Sender<BitswapEvent>,
24    event_rx: Option<mpsc::Receiver<BitswapEvent>>,
25}
26
27/// Bitswap events
28#[derive(Debug, Clone)]
29pub enum BitswapEvent {
30    /// Block received from peer
31    BlockReceived {
32        cid: Cid,
33        data: Vec<u8>,
34        from: PeerId,
35    },
36    /// Block sent to peer
37    BlockSent { cid: Cid, to: PeerId },
38    /// Block request received
39    BlockRequested { cid: Cid, from: PeerId },
40    /// Peer doesn't have requested block
41    BlockNotFound { cid: Cid, peer: PeerId },
42}
43
44/// Bitswap message types
45#[derive(Debug, Clone)]
46pub enum BitswapMessage {
47    /// Want a block
48    Want(Cid),
49    /// Have a block
50    Have(Cid),
51    /// Provide block data
52    Block { cid: Cid, data: Vec<u8> },
53    /// Don't have requested block
54    DontHave(Cid),
55}
56
57impl Bitswap {
58    /// Create a new Bitswap instance
59    pub fn new() -> Self {
60        let (event_tx, event_rx) = mpsc::channel(1024);
61
62        Self {
63            want_list: Arc::new(RwLock::new(HashSet::new())),
64            have_list: Arc::new(RwLock::new(HashSet::new())),
65            pending_requests: Arc::new(RwLock::new(HashMap::new())),
66            event_tx,
67            event_rx: Some(event_rx),
68        }
69    }
70
71    /// Add a CID to our want list
72    pub async fn want(&self, cid: Cid) -> Result<()> {
73        let mut want_list = self.want_list.write().await;
74        want_list.insert(cid);
75        debug!("Added to want list: {}", cid);
76        Ok(())
77    }
78
79    /// Remove a CID from our want list
80    pub async fn cancel_want(&self, cid: &Cid) -> Result<()> {
81        let mut want_list = self.want_list.write().await;
82        want_list.remove(cid);
83        debug!("Removed from want list: {}", cid);
84        Ok(())
85    }
86
87    /// Add a CID to our have list
88    pub async fn have(&self, cid: Cid) -> Result<()> {
89        let mut have_list = self.have_list.write().await;
90        have_list.insert(cid);
91        debug!("Added to have list: {}", cid);
92        Ok(())
93    }
94
95    /// Check if we want a CID
96    pub async fn wants(&self, cid: &Cid) -> bool {
97        let want_list = self.want_list.read().await;
98        want_list.contains(cid)
99    }
100
101    /// Check if we have a CID
102    pub async fn has(&self, cid: &Cid) -> bool {
103        let have_list = self.have_list.read().await;
104        have_list.contains(cid)
105    }
106
107    /// Get all CIDs we want
108    pub async fn get_want_list(&self) -> HashSet<Cid> {
109        let want_list = self.want_list.read().await;
110        want_list.clone()
111    }
112
113    /// Get all CIDs we have
114    pub async fn get_have_list(&self) -> HashSet<Cid> {
115        let have_list = self.have_list.read().await;
116        have_list.clone()
117    }
118
119    /// Request a block from a specific peer
120    pub async fn request_block(&self, cid: Cid, peer: PeerId) -> Result<()> {
121        let mut pending = self.pending_requests.write().await;
122        pending.entry(peer).or_insert_with(HashSet::new).insert(cid);
123        debug!("Requesting block {} from peer {}", cid, peer);
124        Ok(())
125    }
126
127    /// Handle incoming Bitswap message
128    pub async fn handle_message(
129        &self,
130        message: BitswapMessage,
131        from: PeerId,
132    ) -> Result<Option<BitswapMessage>> {
133        match message {
134            BitswapMessage::Want(cid) => {
135                // Peer wants a block
136                debug!("Peer {} wants block {}", from, cid);
137                let _ = self
138                    .event_tx
139                    .send(BitswapEvent::BlockRequested { cid, from })
140                    .await;
141
142                // Check if we have it
143                if self.has(&cid).await {
144                    // We'll send the block data through the event system
145                    // The actual block retrieval happens outside Bitswap
146                    Ok(Some(BitswapMessage::Have(cid)))
147                } else {
148                    Ok(Some(BitswapMessage::DontHave(cid)))
149                }
150            }
151            BitswapMessage::Have(cid) => {
152                // Peer has a block we might want
153                debug!("Peer {} has block {}", from, cid);
154
155                // If we want it, request it
156                if self.wants(&cid).await {
157                    self.request_block(cid, from).await?;
158                }
159                Ok(None)
160            }
161            BitswapMessage::Block { cid, data } => {
162                // Received block data
163                info!(
164                    "Received block {} ({} bytes) from peer {}",
165                    cid,
166                    data.len(),
167                    from
168                );
169
170                // Remove from want list
171                self.cancel_want(&cid).await?;
172
173                // Remove from pending requests
174                let mut pending = self.pending_requests.write().await;
175                if let Some(peer_requests) = pending.get_mut(&from) {
176                    peer_requests.remove(&cid);
177                }
178
179                // Emit event
180                let _ = self
181                    .event_tx
182                    .send(BitswapEvent::BlockReceived { cid, data, from })
183                    .await;
184
185                Ok(None)
186            }
187            BitswapMessage::DontHave(cid) => {
188                // Peer doesn't have the block
189                debug!("Peer {} doesn't have block {}", from, cid);
190
191                let _ = self
192                    .event_tx
193                    .send(BitswapEvent::BlockNotFound { cid, peer: from })
194                    .await;
195
196                Ok(None)
197            }
198        }
199    }
200
201    /// Send a block to a peer
202    pub async fn send_block(&self, cid: Cid, data: Vec<u8>, to: PeerId) -> Result<BitswapMessage> {
203        debug!(
204            "Sending block {} ({} bytes) to peer {}",
205            cid,
206            data.len(),
207            to
208        );
209
210        let _ = self
211            .event_tx
212            .send(BitswapEvent::BlockSent { cid, to })
213            .await;
214
215        Ok(BitswapMessage::Block { cid, data })
216    }
217
218    /// Get pending requests for a peer
219    pub async fn get_pending_requests(&self, peer: &PeerId) -> HashSet<Cid> {
220        let pending = self.pending_requests.read().await;
221        pending.get(peer).cloned().unwrap_or_default()
222    }
223
224    /// Get Bitswap statistics
225    pub async fn stats(&self) -> BitswapStats {
226        let want_list = self.want_list.read().await;
227        let have_list = self.have_list.read().await;
228        let pending = self.pending_requests.read().await;
229
230        BitswapStats {
231            want_list_size: want_list.len(),
232            have_list_size: have_list.len(),
233            pending_requests: pending.values().map(|s| s.len()).sum(),
234            peers_with_pending_requests: pending.len(),
235        }
236    }
237
238    /// Take the event receiver
239    pub fn take_event_receiver(&mut self) -> Option<mpsc::Receiver<BitswapEvent>> {
240        self.event_rx.take()
241    }
242}
243
244impl Default for Bitswap {
245    fn default() -> Self {
246        Self::new()
247    }
248}
249
250/// Bitswap statistics
251#[derive(Debug, Clone, Default, serde::Serialize)]
252pub struct BitswapStats {
253    /// Number of blocks we want
254    pub want_list_size: usize,
255    /// Number of blocks we have
256    pub have_list_size: usize,
257    /// Total pending block requests
258    pub pending_requests: usize,
259    /// Number of peers with pending requests
260    pub peers_with_pending_requests: usize,
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266    use multihash_codetable::{Code, MultihashDigest};
267
268    fn test_cid() -> Cid {
269        let hash = Code::Sha2_256.digest(b"test data");
270        Cid::new_v1(0x55, hash)
271    }
272
273    fn test_peer_id() -> PeerId {
274        PeerId::random()
275    }
276
277    #[tokio::test]
278    async fn test_bitswap_creation() {
279        let bitswap = Bitswap::new();
280        let stats = bitswap.stats().await;
281
282        assert_eq!(stats.want_list_size, 0);
283        assert_eq!(stats.have_list_size, 0);
284        assert_eq!(stats.pending_requests, 0);
285    }
286
287    #[tokio::test]
288    async fn test_want_list() {
289        let bitswap = Bitswap::new();
290        let cid = test_cid();
291
292        // Add to want list
293        bitswap
294            .want(cid)
295            .await
296            .expect("test: want() should succeed");
297        assert!(bitswap.wants(&cid).await);
298
299        let want_list = bitswap.get_want_list().await;
300        assert_eq!(want_list.len(), 1);
301        assert!(want_list.contains(&cid));
302
303        // Cancel want
304        bitswap
305            .cancel_want(&cid)
306            .await
307            .expect("test: cancel_want() should succeed");
308        assert!(!bitswap.wants(&cid).await);
309    }
310
311    #[tokio::test]
312    async fn test_have_list() {
313        let bitswap = Bitswap::new();
314        let cid = test_cid();
315
316        // Add to have list
317        bitswap
318            .have(cid)
319            .await
320            .expect("test: have() should succeed");
321        assert!(bitswap.has(&cid).await);
322
323        let have_list = bitswap.get_have_list().await;
324        assert_eq!(have_list.len(), 1);
325        assert!(have_list.contains(&cid));
326    }
327
328    #[tokio::test]
329    async fn test_request_block() {
330        let bitswap = Bitswap::new();
331        let cid = test_cid();
332        let peer = test_peer_id();
333
334        bitswap
335            .request_block(cid, peer)
336            .await
337            .expect("test: request_block() should succeed");
338
339        let pending = bitswap.get_pending_requests(&peer).await;
340        assert_eq!(pending.len(), 1);
341        assert!(pending.contains(&cid));
342    }
343
344    #[tokio::test]
345    async fn test_handle_want_message_have_block() {
346        let bitswap = Bitswap::new();
347        let cid = test_cid();
348        let peer = test_peer_id();
349
350        // Add to have list
351        bitswap
352            .have(cid)
353            .await
354            .expect("test: have() should succeed");
355
356        // Handle want message
357        let response = bitswap
358            .handle_message(BitswapMessage::Want(cid), peer)
359            .await
360            .expect("test: handle_message(Want) should succeed when block is in have list");
361
362        assert!(response.is_some());
363        match response
364            .expect("test: handle_message should return Some(Have) when block is in have list")
365        {
366            BitswapMessage::Have(received_cid) => assert_eq!(received_cid, cid),
367            _ => panic!("Expected Have message"),
368        }
369    }
370
371    #[tokio::test]
372    async fn test_handle_want_message_dont_have() {
373        let bitswap = Bitswap::new();
374        let cid = test_cid();
375        let peer = test_peer_id();
376
377        // Don't add to have list
378        let response = bitswap
379            .handle_message(BitswapMessage::Want(cid), peer)
380            .await
381            .expect("test: handle_message(Want) should succeed when block is not in have list");
382
383        assert!(response.is_some());
384        match response
385            .expect("test: handle_message should return Some(DontHave) when block is absent")
386        {
387            BitswapMessage::DontHave(received_cid) => assert_eq!(received_cid, cid),
388            _ => panic!("Expected DontHave message"),
389        }
390    }
391
392    #[tokio::test]
393    async fn test_handle_have_message() {
394        let bitswap = Bitswap::new();
395        let cid = test_cid();
396        let peer = test_peer_id();
397
398        // Add to want list
399        bitswap
400            .want(cid)
401            .await
402            .expect("test: want() should succeed");
403
404        // Handle have message
405        let response = bitswap
406            .handle_message(BitswapMessage::Have(cid), peer)
407            .await
408            .expect("test: handle_message(Have) should succeed");
409
410        assert!(response.is_none());
411
412        // Should have created a pending request
413        let pending = bitswap.get_pending_requests(&peer).await;
414        assert_eq!(pending.len(), 1);
415        assert!(pending.contains(&cid));
416    }
417
418    #[tokio::test]
419    async fn test_handle_block_message() {
420        let bitswap = Bitswap::new();
421        let cid = test_cid();
422        let peer = test_peer_id();
423        let data = b"test block data".to_vec();
424
425        // Add to want list and pending requests
426        bitswap
427            .want(cid)
428            .await
429            .expect("test: want() should succeed");
430        bitswap
431            .request_block(cid, peer)
432            .await
433            .expect("test: request_block() should succeed");
434
435        // Handle block message
436        let response = bitswap
437            .handle_message(
438                BitswapMessage::Block {
439                    cid,
440                    data: data.clone(),
441                },
442                peer,
443            )
444            .await
445            .expect("test: handle_message(Block) should succeed");
446
447        assert!(response.is_none());
448
449        // Should have removed from want list
450        assert!(!bitswap.wants(&cid).await);
451
452        // Should have removed from pending requests
453        let pending = bitswap.get_pending_requests(&peer).await;
454        assert_eq!(pending.len(), 0);
455    }
456
457    #[tokio::test]
458    async fn test_send_block() {
459        let bitswap = Bitswap::new();
460        let cid = test_cid();
461        let peer = test_peer_id();
462        let data = b"test block data".to_vec();
463
464        let message = bitswap
465            .send_block(cid, data.clone(), peer)
466            .await
467            .expect("test: send_block() should succeed");
468
469        match message {
470            BitswapMessage::Block {
471                cid: received_cid,
472                data: received_data,
473            } => {
474                assert_eq!(received_cid, cid);
475                assert_eq!(received_data, data);
476            }
477            _ => panic!("Expected Block message"),
478        }
479    }
480
481    #[tokio::test]
482    async fn test_bitswap_stats() {
483        let bitswap = Bitswap::new();
484        let cid1 = test_cid();
485        let cid2 = test_cid();
486        let peer = test_peer_id();
487
488        bitswap
489            .want(cid1)
490            .await
491            .expect("test: want() should succeed");
492        bitswap
493            .have(cid2)
494            .await
495            .expect("test: have() should succeed");
496        bitswap
497            .request_block(cid1, peer)
498            .await
499            .expect("test: request_block() should succeed");
500
501        let stats = bitswap.stats().await;
502
503        assert_eq!(stats.want_list_size, 1);
504        assert_eq!(stats.have_list_size, 1);
505        assert_eq!(stats.pending_requests, 1);
506        assert_eq!(stats.peers_with_pending_requests, 1);
507    }
508}