use cid::Cid;
use ipfrs_core::error::Result;
use libp2p::PeerId;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use tokio::sync::{mpsc, RwLock};
use tracing::{debug, info};
pub struct Bitswap {
want_list: Arc<RwLock<HashSet<Cid>>>,
have_list: Arc<RwLock<HashSet<Cid>>>,
pending_requests: Arc<RwLock<HashMap<PeerId, HashSet<Cid>>>>,
event_tx: mpsc::Sender<BitswapEvent>,
event_rx: Option<mpsc::Receiver<BitswapEvent>>,
}
#[derive(Debug, Clone)]
pub enum BitswapEvent {
BlockReceived {
cid: Cid,
data: Vec<u8>,
from: PeerId,
},
BlockSent { cid: Cid, to: PeerId },
BlockRequested { cid: Cid, from: PeerId },
BlockNotFound { cid: Cid, peer: PeerId },
}
#[derive(Debug, Clone)]
pub enum BitswapMessage {
Want(Cid),
Have(Cid),
Block { cid: Cid, data: Vec<u8> },
DontHave(Cid),
}
impl Bitswap {
pub fn new() -> Self {
let (event_tx, event_rx) = mpsc::channel(1024);
Self {
want_list: Arc::new(RwLock::new(HashSet::new())),
have_list: Arc::new(RwLock::new(HashSet::new())),
pending_requests: Arc::new(RwLock::new(HashMap::new())),
event_tx,
event_rx: Some(event_rx),
}
}
pub async fn want(&self, cid: Cid) -> Result<()> {
let mut want_list = self.want_list.write().await;
want_list.insert(cid);
debug!("Added to want list: {}", cid);
Ok(())
}
pub async fn cancel_want(&self, cid: &Cid) -> Result<()> {
let mut want_list = self.want_list.write().await;
want_list.remove(cid);
debug!("Removed from want list: {}", cid);
Ok(())
}
pub async fn have(&self, cid: Cid) -> Result<()> {
let mut have_list = self.have_list.write().await;
have_list.insert(cid);
debug!("Added to have list: {}", cid);
Ok(())
}
pub async fn wants(&self, cid: &Cid) -> bool {
let want_list = self.want_list.read().await;
want_list.contains(cid)
}
pub async fn has(&self, cid: &Cid) -> bool {
let have_list = self.have_list.read().await;
have_list.contains(cid)
}
pub async fn get_want_list(&self) -> HashSet<Cid> {
let want_list = self.want_list.read().await;
want_list.clone()
}
pub async fn get_have_list(&self) -> HashSet<Cid> {
let have_list = self.have_list.read().await;
have_list.clone()
}
pub async fn request_block(&self, cid: Cid, peer: PeerId) -> Result<()> {
let mut pending = self.pending_requests.write().await;
pending.entry(peer).or_insert_with(HashSet::new).insert(cid);
debug!("Requesting block {} from peer {}", cid, peer);
Ok(())
}
pub async fn handle_message(
&self,
message: BitswapMessage,
from: PeerId,
) -> Result<Option<BitswapMessage>> {
match message {
BitswapMessage::Want(cid) => {
debug!("Peer {} wants block {}", from, cid);
let _ = self
.event_tx
.send(BitswapEvent::BlockRequested { cid, from })
.await;
if self.has(&cid).await {
Ok(Some(BitswapMessage::Have(cid)))
} else {
Ok(Some(BitswapMessage::DontHave(cid)))
}
}
BitswapMessage::Have(cid) => {
debug!("Peer {} has block {}", from, cid);
if self.wants(&cid).await {
self.request_block(cid, from).await?;
}
Ok(None)
}
BitswapMessage::Block { cid, data } => {
info!(
"Received block {} ({} bytes) from peer {}",
cid,
data.len(),
from
);
self.cancel_want(&cid).await?;
let mut pending = self.pending_requests.write().await;
if let Some(peer_requests) = pending.get_mut(&from) {
peer_requests.remove(&cid);
}
let _ = self
.event_tx
.send(BitswapEvent::BlockReceived { cid, data, from })
.await;
Ok(None)
}
BitswapMessage::DontHave(cid) => {
debug!("Peer {} doesn't have block {}", from, cid);
let _ = self
.event_tx
.send(BitswapEvent::BlockNotFound { cid, peer: from })
.await;
Ok(None)
}
}
}
pub async fn send_block(&self, cid: Cid, data: Vec<u8>, to: PeerId) -> Result<BitswapMessage> {
debug!(
"Sending block {} ({} bytes) to peer {}",
cid,
data.len(),
to
);
let _ = self
.event_tx
.send(BitswapEvent::BlockSent { cid, to })
.await;
Ok(BitswapMessage::Block { cid, data })
}
pub async fn get_pending_requests(&self, peer: &PeerId) -> HashSet<Cid> {
let pending = self.pending_requests.read().await;
pending.get(peer).cloned().unwrap_or_default()
}
pub async fn stats(&self) -> BitswapStats {
let want_list = self.want_list.read().await;
let have_list = self.have_list.read().await;
let pending = self.pending_requests.read().await;
BitswapStats {
want_list_size: want_list.len(),
have_list_size: have_list.len(),
pending_requests: pending.values().map(|s| s.len()).sum(),
peers_with_pending_requests: pending.len(),
}
}
pub fn take_event_receiver(&mut self) -> Option<mpsc::Receiver<BitswapEvent>> {
self.event_rx.take()
}
}
impl Default for Bitswap {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct BitswapStats {
pub want_list_size: usize,
pub have_list_size: usize,
pub pending_requests: usize,
pub peers_with_pending_requests: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use multihash_codetable::{Code, MultihashDigest};
fn test_cid() -> Cid {
let hash = Code::Sha2_256.digest(b"test data");
Cid::new_v1(0x55, hash)
}
fn test_peer_id() -> PeerId {
PeerId::random()
}
#[tokio::test]
async fn test_bitswap_creation() {
let bitswap = Bitswap::new();
let stats = bitswap.stats().await;
assert_eq!(stats.want_list_size, 0);
assert_eq!(stats.have_list_size, 0);
assert_eq!(stats.pending_requests, 0);
}
#[tokio::test]
async fn test_want_list() {
let bitswap = Bitswap::new();
let cid = test_cid();
bitswap
.want(cid)
.await
.expect("test: want() should succeed");
assert!(bitswap.wants(&cid).await);
let want_list = bitswap.get_want_list().await;
assert_eq!(want_list.len(), 1);
assert!(want_list.contains(&cid));
bitswap
.cancel_want(&cid)
.await
.expect("test: cancel_want() should succeed");
assert!(!bitswap.wants(&cid).await);
}
#[tokio::test]
async fn test_have_list() {
let bitswap = Bitswap::new();
let cid = test_cid();
bitswap
.have(cid)
.await
.expect("test: have() should succeed");
assert!(bitswap.has(&cid).await);
let have_list = bitswap.get_have_list().await;
assert_eq!(have_list.len(), 1);
assert!(have_list.contains(&cid));
}
#[tokio::test]
async fn test_request_block() {
let bitswap = Bitswap::new();
let cid = test_cid();
let peer = test_peer_id();
bitswap
.request_block(cid, peer)
.await
.expect("test: request_block() should succeed");
let pending = bitswap.get_pending_requests(&peer).await;
assert_eq!(pending.len(), 1);
assert!(pending.contains(&cid));
}
#[tokio::test]
async fn test_handle_want_message_have_block() {
let bitswap = Bitswap::new();
let cid = test_cid();
let peer = test_peer_id();
bitswap
.have(cid)
.await
.expect("test: have() should succeed");
let response = bitswap
.handle_message(BitswapMessage::Want(cid), peer)
.await
.expect("test: handle_message(Want) should succeed when block is in have list");
assert!(response.is_some());
match response
.expect("test: handle_message should return Some(Have) when block is in have list")
{
BitswapMessage::Have(received_cid) => assert_eq!(received_cid, cid),
_ => panic!("Expected Have message"),
}
}
#[tokio::test]
async fn test_handle_want_message_dont_have() {
let bitswap = Bitswap::new();
let cid = test_cid();
let peer = test_peer_id();
let response = bitswap
.handle_message(BitswapMessage::Want(cid), peer)
.await
.expect("test: handle_message(Want) should succeed when block is not in have list");
assert!(response.is_some());
match response
.expect("test: handle_message should return Some(DontHave) when block is absent")
{
BitswapMessage::DontHave(received_cid) => assert_eq!(received_cid, cid),
_ => panic!("Expected DontHave message"),
}
}
#[tokio::test]
async fn test_handle_have_message() {
let bitswap = Bitswap::new();
let cid = test_cid();
let peer = test_peer_id();
bitswap
.want(cid)
.await
.expect("test: want() should succeed");
let response = bitswap
.handle_message(BitswapMessage::Have(cid), peer)
.await
.expect("test: handle_message(Have) should succeed");
assert!(response.is_none());
let pending = bitswap.get_pending_requests(&peer).await;
assert_eq!(pending.len(), 1);
assert!(pending.contains(&cid));
}
#[tokio::test]
async fn test_handle_block_message() {
let bitswap = Bitswap::new();
let cid = test_cid();
let peer = test_peer_id();
let data = b"test block data".to_vec();
bitswap
.want(cid)
.await
.expect("test: want() should succeed");
bitswap
.request_block(cid, peer)
.await
.expect("test: request_block() should succeed");
let response = bitswap
.handle_message(
BitswapMessage::Block {
cid,
data: data.clone(),
},
peer,
)
.await
.expect("test: handle_message(Block) should succeed");
assert!(response.is_none());
assert!(!bitswap.wants(&cid).await);
let pending = bitswap.get_pending_requests(&peer).await;
assert_eq!(pending.len(), 0);
}
#[tokio::test]
async fn test_send_block() {
let bitswap = Bitswap::new();
let cid = test_cid();
let peer = test_peer_id();
let data = b"test block data".to_vec();
let message = bitswap
.send_block(cid, data.clone(), peer)
.await
.expect("test: send_block() should succeed");
match message {
BitswapMessage::Block {
cid: received_cid,
data: received_data,
} => {
assert_eq!(received_cid, cid);
assert_eq!(received_data, data);
}
_ => panic!("Expected Block message"),
}
}
#[tokio::test]
async fn test_bitswap_stats() {
let bitswap = Bitswap::new();
let cid1 = test_cid();
let cid2 = test_cid();
let peer = test_peer_id();
bitswap
.want(cid1)
.await
.expect("test: want() should succeed");
bitswap
.have(cid2)
.await
.expect("test: have() should succeed");
bitswap
.request_block(cid1, peer)
.await
.expect("test: request_block() should succeed");
let stats = bitswap.stats().await;
assert_eq!(stats.want_list_size, 1);
assert_eq!(stats.have_list_size, 1);
assert_eq!(stats.pending_requests, 1);
assert_eq!(stats.peers_with_pending_requests, 1);
}
}