Skip to content

Commit

Permalink
feat(fs): Reimplement with different data model
Browse files Browse the repository at this point in the history
TODO:
- Write documentation.
- Implement announcing & querying replicas on mainline DHT.
    - After querying, implement node discovery and joining document swarms.
  • Loading branch information
emmyoh committed Apr 3, 2024
1 parent 5b27027 commit c685ac5
Show file tree
Hide file tree
Showing 7 changed files with 983 additions and 713 deletions.
9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ async-trait = "0.1.79"
bincode = "1.3.3"
bytes = "1.6.0"
chrono = "0.4.37"
derive_more = "0.99.17"
futures = "0.3.30"
hex = "0.4.3"
iroh = "0.13.0"
iroh-mainline-content-discovery = "0.5.0"
mainline = "1.4.0"
miette = { version = "7.2.0", features = ["fancy"] }
path-clean = "1.0.1"
quic-rpc = "0.7.0"
rand_core = "0.6.4"
serde = "1.0.197"
sha3 = "0.10.8"
thiserror = "1.0.58"
vfs = { version = "0.12.0", features = ["async-vfs"] }
wnfs = "0.2.2"
tokio = "1.37.0"
102 changes: 102 additions & 0 deletions src/discovery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::error::OkuDiscoveryError;
use futures::StreamExt;
use iroh::{
bytes::{Hash, HashAndFormat},
sync::NamespaceId,
ticket::BlobTicket,
};
use iroh_mainline_content_discovery::protocol::{Query, QueryFlags};
use iroh_mainline_content_discovery::to_infohash;
use iroh_mainline_content_discovery::UdpDiscovery;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::{error::Error, str::FromStr};

/*
The `ContentRequest` enum is derived from the `ContentArg` enum in the `iroh-examples` repository (https://github.com/n0-computer/iroh-examples/blob/6f184933efa72eec1d8cf2e8d07905650c0fdb46/content-discovery/iroh-mainline-content-discovery-cli/src/args.rs#L23).
*/
#[derive(Debug, Clone, derive_more::From)]
/// A request for content, which can be a raw hash, a hash and format pair, or a blob ticket.
pub enum ContentRequest {
/// A raw hash.
Hash(Hash),
/// A hash and format pair.
HashAndFormat(HashAndFormat),
/// A blob ticket.
Ticket(BlobTicket),
}

impl ContentRequest {
/// Get the hash and format pair for this content request.
pub fn hash_and_format(&self) -> HashAndFormat {
match self {
ContentRequest::Hash(hash) => HashAndFormat::raw(*hash),
ContentRequest::HashAndFormat(haf) => *haf,
ContentRequest::Ticket(ticket) => HashAndFormat {
hash: ticket.hash(),
format: ticket.format(),
},
}
}
}

impl FromStr for ContentRequest {
type Err = Box<dyn Error>;
fn from_str(s: &str) -> Result<Self, Box<dyn Error>> {
if let Ok(hash) = Hash::from_str(s) {
Ok(hash.into())
} else if let Ok(haf) = HashAndFormat::from_str(s) {
Ok(haf.into())
} else if let Ok(ticket) = BlobTicket::from_str(s) {
Ok(ticket.into())
} else {
Err(OkuDiscoveryError::InvalidHashAndFormat.into())
}
}
}

async fn query_dht(
content: ContentRequest,
partial: bool,
verified: bool,
udp_port: Option<u16>,
) -> Result<(), Box<dyn Error>> {
let bind_addr = SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::UNSPECIFIED,
udp_port.unwrap_or_default(),
));
let discovery = UdpDiscovery::new(bind_addr).await?;
let dht = mainline::Dht::default();
let q = Query {
content: content.hash_and_format(),
flags: QueryFlags {
complete: !partial,
verified: verified,
},
};
println!("content corresponds to infohash {}", to_infohash(q.content));

let mut stream = discovery.query_dht(dht, q).await?;
while let Some(announce) = stream.next().await {
if announce.verify().is_ok() {
println!("found verified provider {}", announce.host);
} else {
println!("got wrong signed announce!");
}
}
Ok(())
}

pub async fn query_fs_entry(
id: NamespaceId,
partial: bool,
verified: bool,
udp_port: Option<u16>,
) -> Result<(), Box<dyn Error>> {
query_dht(
ContentRequest::Hash(Hash::new(id)),
partial,
verified,
udp_port,
)
.await
}
7 changes: 7 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ pub enum OkuFsError {
/// File system root has not been loaded.
RootNotLoaded,
}

#[derive(Error, Debug, Diagnostic)]
pub enum OkuDiscoveryError {
#[error("Invalid hash and format.")]
#[diagnostic(code(discovery::invalid_hash_and_format), url(docsrs))]
InvalidHashAndFormat,
}
Loading

0 comments on commit c685ac5

Please sign in to comment.