beetswap 0.5.0

Implementation of bitswap protocol for libp2p
Documentation
use blockstore::{Blockstore, InMemoryBlockstore};
use futures_util::future::FutureExt;
use futures_util::poll;
use tokio::time::{sleep, Duration};

use crate::utils::{cid, spawn_node};

mod utils;

#[tokio::test]
async fn test_client_request() {
    let data_with_cid = ["foo", "bar", "baz"].map(|d| (cid(d.as_bytes()), d));
    let store = InMemoryBlockstore::new();
    for (cid, data) in data_with_cid {
        store.put_keyed(&cid, data.as_bytes()).await.unwrap()
    }
    let server = spawn_node(Some(store)).await;
    let mut client = spawn_node(None).await;

    let received0 = client.request_cid(data_with_cid[0].0);

    drop(client.connect(&server));
    let received1 = client
        .request_cid(data_with_cid[1].0)
        .await
        .expect("could not get CID");
    let received0 = received0.await.expect("could not get CID");

    assert_eq!(&received0[..], data_with_cid[0].1.as_bytes());
    assert_eq!(&received1[..], data_with_cid[1].1.as_bytes());
}

#[tokio::test]
async fn test_server_request() {
    let data_with_cid = ["foo", "bar", "baz"].map(|d| (cid(d.as_bytes()), d));
    let store = InMemoryBlockstore::new();
    for (cid, data) in data_with_cid {
        store.put_keyed(&cid, data.as_bytes()).await.unwrap()
    }

    let mut client = spawn_node(Some(store)).await;
    let mut server = spawn_node(None).await;

    let received0 = client.request_cid(data_with_cid[0].0);

    drop(client.connect(&server));
    let received1 = server
        .request_cid(data_with_cid[1].0)
        .await
        .expect("could not get CID");
    let received0 = received0.await.expect("Could not get CID");

    assert_eq!(&received0[..], data_with_cid[0].1.as_bytes());
    assert_eq!(&received1[..], data_with_cid[1].1.as_bytes());
}

#[tokio::test]
async fn test_chain_of_nodes() {
    let data = "foo";
    let cid = cid(data.as_bytes());
    let store = InMemoryBlockstore::new();
    store.put_keyed(&cid, data.as_ref()).await.unwrap();

    let mut node_with_data = spawn_node(Some(store)).await;
    let mut node0 = spawn_node(None).await;
    let mut node1 = spawn_node(None).await;
    let mut node2 = spawn_node(None).await;

    drop(node_with_data.connect(&node0));
    drop(node0.connect(&node1));
    drop(node1.connect(&node2));

    let mut node2_request = node2.request_cid(cid);
    sleep(Duration::from_millis(300)).await;
    assert!(poll!(&mut node2_request).is_pending());

    let mut node1_request = node1.request_cid(cid);
    sleep(Duration::from_millis(300)).await;
    assert!(poll!(&mut node1_request).is_pending());
    assert!(poll!(&mut node2_request).is_pending());

    let node0_request = node0.request_cid(cid);
    sleep(Duration::from_millis(300)).await;
    let data0 = node0_request
        .now_or_never()
        .expect("data should be ready")
        .unwrap();
    let data1 = node1_request
        .now_or_never()
        .expect("data should be ready")
        .unwrap();
    let data2 = node2_request
        .now_or_never()
        .expect("data should be ready")
        .unwrap();

    assert_eq!(data0, data.as_bytes());
    assert_eq!(data1, data.as_bytes());
    assert_eq!(data2, data.as_bytes());
}

#[tokio::test]
async fn test_node_with_data_coming_online() {
    let data = "foo";
    let cid = cid(data.as_bytes());
    let store = InMemoryBlockstore::new();
    store.put_keyed(&cid, data.as_ref()).await.unwrap();

    let node_with_data = spawn_node(Some(store)).await;

    let mut node0 = spawn_node(None).await;
    let mut node1 = spawn_node(None).await;
    drop(node0.connect(&node1));

    let mut node0_request = node0.request_cid(cid);
    let mut node1_request = node1.request_cid(cid);
    sleep(Duration::from_millis(300)).await;
    assert!(poll!(&mut node0_request).is_pending());
    assert!(poll!(&mut node1_request).is_pending());

    node1
        .connect(&node_with_data)
        .await
        .expect("could not connect");
    sleep(Duration::from_millis(300)).await;

    let data0 = node0_request
        .now_or_never()
        .expect("data should be ready")
        .unwrap();
    let data1 = node1_request
        .now_or_never()
        .expect("data should be ready")
        .unwrap();
    assert_eq!(data0, data.as_bytes());
    assert_eq!(data1, data.as_bytes());
}

#[tokio::test]
async fn test_node_with_invalid_data() {
    let data = "foo";
    let cid = cid(data.as_bytes());
    let store = InMemoryBlockstore::new();
    store.put_keyed(&cid, data.as_ref()).await.unwrap();

    let invalid_data = "bar";
    let invalid_store = InMemoryBlockstore::new();
    invalid_store
        .put_keyed(&cid, invalid_data.as_ref())
        .await
        .unwrap();

    let mut client = spawn_node(None).await;
    let node = spawn_node(Some(store)).await;
    let malicious_node = spawn_node(Some(invalid_store)).await;

    client
        .connect(&malicious_node)
        .await
        .expect("could not connect");
    let mut request = client.request_cid(cid);
    sleep(Duration::from_millis(200)).await;
    assert!(poll!(&mut request).is_pending());

    drop(client.connect(&node));
    let received = request.await.expect("could not get CID");
    assert_eq!(received, data.as_bytes());
}

#[tokio::test]
async fn test_node_wishlist_updates() {
    let data_with_cid = ["foo", "bar", "baz"].map(|d| (cid(d.as_bytes()), d));
    let store = InMemoryBlockstore::new();
    for (cid, data) in data_with_cid {
        store.put_keyed(&cid, data.as_bytes()).await.unwrap()
    }

    let server = spawn_node(Some(store)).await;
    let mut client = spawn_node(None).await;

    let request0 = client.request_cid(data_with_cid[0].0);
    client.connect(&server).await.unwrap();

    let request1 = client.request_cid(data_with_cid[1].0);

    let data0 = request0.await.unwrap();
    assert_eq!(data0, data_with_cid[0].1.as_bytes());

    let request2 = client.request_cid(data_with_cid[2].0);

    let data1 = request1.await.unwrap();
    let data2 = request2.await.unwrap();

    assert_eq!(data1, data_with_cid[1].1.as_bytes());
    assert_eq!(data2, data_with_cid[2].1.as_bytes());
}