Skip to content

Commit

Permalink
chore: Add doc requirement to CI, add missing docs
Browse files Browse the repository at this point in the history
Signed-off-by: Mikołaj Florkiewicz <[email protected]>
  • Loading branch information
fl0rek committed Mar 12, 2024
1 parent d582adc commit 46d7e5b
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 6 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Run clippy
run: cargo clippy --all --all-targets -- -D warnings
run: cargo clippy --all --all-targets -- -D warnings -D missing-docs

docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Run rustdoc check
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc

fmt:
runs-on: ubuntu-latest
Expand Down
173 changes: 173 additions & 0 deletions examples/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//! Example bitswap node implementation allowing with basic interaction over cli
//!
//! It shows off example way to setup beetswap behaviour with a libp2p swarm and then either
//! connecting to another node and/or listening for incoming connections. In both cases both
//! the server and the client parts of the bitswap are running.
//!
//! Example invocations:
//!
//! Listen on port `9898` and serve single block with contents "12345"
//! ```sh
//! cargo run --example=node -- -l 9898 --preload-blockstore-string 12345
//! ```
//!
//! Connect to `10.0.0.101` on port `9898` and ask for CID `bafkreic...` (it's a CID of "12345" string above). You can specify multiple CIDs at once.
//! ```sh
//! cargo run --example=node -- -p /ip4/10.0.0.101/tcp/9898 bafkreiczsrdrvoybcevpzqmblh3my5fu6ui3tgag3jm3hsxvvhaxhswpyu
//! ```
//!
//! Listen on port `9898` and requests provided CID from them until it gets correct response with
//! data
//! ```sh
//! cargo run --example=node -- -l 9898 bafkreiczsrdrvoybcevpzqmblh3my5fu6ui3tgag3jm3hsxvvhaxhswpyu
//! ```
use std::collections::HashMap;
use std::time::Duration;

use anyhow::Result;
use blockstore::{
block::{Block, CidError},
Blockstore, InMemoryBlockstore,
};
use cid::Cid;
use clap::Parser;
use libp2p::{
futures::StreamExt, identify, swarm::NetworkBehaviour, swarm::SwarmEvent, tcp, Multiaddr,
SwarmBuilder,
};
use multihash_codetable::{Code, MultihashDigest};
use tracing::{debug, info};

const MAX_MULTIHASH_LENGHT: usize = 64;
const RAW_CODEC: u64 = 0x55;

#[derive(Debug, Parser)]
struct Args {
/// Peers to connect to
#[arg(short, long = "peer")]
pub(crate) peers: Vec<Multiaddr>,

/// CIDs to request
pub(crate) cids: Vec<Cid>,

/// Listen on provided port
#[arg(short, long = "listen")]
pub(crate) listen_port: Option<u16>,

/// Load provided string into blockstore on start
#[arg(long)]
pub(crate) preload_blockstore_string: Vec<String>,
}

#[derive(NetworkBehaviour)]
struct Behaviour {
identify: identify::Behaviour,
bitswap: beetswap::Behaviour<MAX_MULTIHASH_LENGHT, InMemoryBlockstore<MAX_MULTIHASH_LENGHT>>,
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let args = Args::parse();

let _guard = init_tracing();

let store = InMemoryBlockstore::new();
for preload_string in args.preload_blockstore_string {
let block = StringBlock(preload_string);
let cid = block.cid()?;
info!("inserted {cid} with content '{}'", block.0);
store.put_keyed(&cid, block.data()).await?;
}

let mut swarm = SwarmBuilder::with_new_identity()
.with_tokio()
.with_tcp(
tcp::Config::default(),
libp2p_noise::Config::new,
libp2p_yamux::Config::default,
)?
.with_behaviour(|key| Behaviour {
identify: identify::Behaviour::new(identify::Config::new(
"/ipfs/id/1.0.0".to_string(),
key.public(),
)),
bitswap: beetswap::Behaviour::new(store),
})?
.with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60)))
.build();

if let Some(port) = args.listen_port {
swarm.listen_on(format!("/ip4/0.0.0.0/tcp/{port}").parse()?)?;
}

for peer in args.peers {
swarm.dial(peer)?;
}

let mut queries = HashMap::new();
for cid in args.cids {
let query_id = swarm.behaviour_mut().bitswap.get(&cid);
queries.insert(query_id, cid);
info!("requested cid {cid}: {query_id:?}");
}

loop {
match swarm.select_next_some().await {
SwarmEvent::NewListenAddr { address, .. } => debug!("Listening on {address:?}"),
// Prints peer id identify info is being sent to.
SwarmEvent::Behaviour(BehaviourEvent::Identify(identify)) => match identify {
identify::Event::Sent { peer_id, .. } => {
info!("Sent identify info to {peer_id:?}");
}
identify::Event::Received { info, .. } => {
info!("Received {info:?}")
}
_ => (),
},
SwarmEvent::Behaviour(BehaviourEvent::Bitswap(bitswap)) => match bitswap {
beetswap::Event::GetQueryResponse { query_id, data } => {
let cid = queries.get(&query_id).expect("unknown cid received");
info!("received response for {cid:?}: {data:?}");
}
beetswap::Event::GetQueryError { query_id, error } => {
let cid = queries.get(&query_id).expect("unknown cid received");
info!("received error for {cid:?}: {error}");
}
},
_ => (),
}
}
}

struct StringBlock(pub String);

impl Block<64> for StringBlock {
fn cid(&self) -> Result<Cid, CidError> {
let hash = Code::Sha2_256.digest(self.0.as_ref());
Ok(Cid::new_v1(RAW_CODEC, hash))
}

fn data(&self) -> &[u8] {
self.0.as_ref()
}
}

fn init_tracing() -> tracing_appender::non_blocking::WorkerGuard {
let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());

let filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy();

tracing_subscriber::fmt()
.event_format(
tracing_subscriber::fmt::format()
.with_file(true)
.with_line_number(true),
)
.with_env_filter(filter)
.with_writer(non_blocking)
.init();

guard
}
24 changes: 22 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#![cfg_attr(docs_rs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]

use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};

Expand Down Expand Up @@ -52,19 +55,35 @@ where
/// Event produced by [`Behaviour`].
#[derive(Debug)]
pub enum Event {
GetQueryResponse { query_id: QueryId, data: Vec<u8> },
GetQueryError { query_id: QueryId, error: Error },
/// Requested block has been successfuly retrieved
GetQueryResponse {
/// Id of the query, returned by [`Behaviour::get`]
query_id: QueryId,
/// Data of the requested block
data: Vec<u8>,
},
/// Error occurred while fetching block
GetQueryError {
/// Id of the query, returned by [`Behaviour::get`]
query_id: QueryId,
/// Error that occurred when getting the data
error: Error,
},
}

/// Representation of all the errors that can occur when interacting with this crate.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Encountered CID with multihash longer than max set when creating the [`Behaviour`]
#[error("Invalid multihash size")]
InvalidMultihashSize,

/// Invalid protocol prefix provided when building `Behaviour`, see
/// [`BehaviourBuilder::protocol_prefix`]
#[error("Invalid protocol prefix: {0}")]
InvalidProtocolPrefix(String),

/// Error received when interacting with blockstore
#[error("Blockstore error: {0}")]
Blockstore(#[from] BlockstoreError),
}
Expand Down Expand Up @@ -212,6 +231,7 @@ pub enum ToHandlerEvent {
QueueOutgoingMessages(Vec<(Vec<u8>, Vec<u8>)>),
}

#[doc(hidden)]
pub enum StreamRequester {
Client,
Server,
Expand Down
3 changes: 0 additions & 3 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ pub(crate) const MAX_MESSAGE_SIZE: usize = 4 * 1024 * 1024;

pub(crate) struct Codec;

#[derive(Debug, thiserror::Error)]
pub(crate) enum CodecError {}

impl Encoder for Codec {
type Item<'a> = &'a Message;
type Error = io::Error;
Expand Down
13 changes: 13 additions & 0 deletions src/multihasher.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
//! Module responsible for calculating hashes for data received
//!
//! For interoperability `StandardMultihasher` is registered by default, which uses hashes
//! provided by [`multihash_codetable::Code`]. If you need to register your own multihashes,
//! you can implement [`Multihasher`] trait and then register the struct with
//! [`BehaviourBuilder::register_multihasher`] when creating the behaviour.
//!
//! [`BehaviourBuilder::register_multihasher`]:
//! crate::builder::BehaviourBuilder::register_multihasher
use std::collections::VecDeque;
use std::fmt::{self, Display};

Expand Down Expand Up @@ -40,10 +50,13 @@ pub enum MultihasherError {
}

impl MultihasherError {
/// Custom error, causes block to be ignored
pub fn custom(e: impl Display) -> MultihasherError {
MultihasherError::Custom(e.to_string())
}

/// Custom fatal error, causes block to be ignored and stream from which it was received to
/// close
pub fn custom_fatal(e: impl Display) -> MultihasherError {
MultihasherError::CustomFatal(e.to_string())
}
Expand Down
2 changes: 2 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Helpers used in and provided by the crate
use cid::CidGeneric;
use libp2p_core::multihash::Multihash;
use libp2p_swarm::StreamProtocol;
Expand Down

0 comments on commit 46d7e5b

Please sign in to comment.