From 4db6033ba7ef7329322711d67857fd6154b5021b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Thu, 10 Jul 2025 16:55:51 +0200 Subject: [PATCH 1/4] docs: Update the transfer example to iroh 0.90 --- examples/transfer.rs | 113 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 examples/transfer.rs diff --git a/examples/transfer.rs b/examples/transfer.rs new file mode 100644 index 00000000..74e2a02b --- /dev/null +++ b/examples/transfer.rs @@ -0,0 +1,113 @@ +use std::path::PathBuf; + +use anyhow::Result; +use iroh::{protocol::Router, Endpoint}; +use iroh_blobs::{ + api::blobs::{AddPathOptions, ExportMode, ExportOptions, ImportMode}, + net_protocol::Blobs, + store::mem::MemStore, + ticket::BlobTicket, + BlobFormat, +}; + +#[tokio::main] +async fn main() -> Result<()> { + // Create an endpoint, it allows creating and accepting + // connections in the iroh p2p world + let endpoint = Endpoint::builder().discovery_n0().bind().await?; + // We initialize an in-memory backing store for iroh-blobs + let store = MemStore::new(); + // Then we initialize a struct that can accept blobs requests over iroh connections + let blobs = Blobs::new(&store, endpoint.clone(), None); + + // Grab all passed in arguments, the first one is the binary itself, so we skip it. + let args: Vec = std::env::args().skip(1).collect(); + // Convert to &str, so we can pattern-match easily: + let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect(); + + match arg_refs.as_slice() { + ["send", filename] => { + // For sending files we build a router that accepts blobs connections & routes them + // to the blobs protocol. + let router = Router::builder(endpoint) + .accept(iroh_blobs::ALPN, blobs) + .spawn(); + + let filename: PathBuf = filename.parse()?; + let abs_path = std::path::absolute(&filename)?; + + println!("Hashing file."); + + // When we import a blob, we get back a tag that refers to said blob in the store + // and allows us to control when/if it gets garbage-collected + let tag = store + .blobs() + .add_path_with_opts(AddPathOptions { + path: abs_path, + format: BlobFormat::Raw, + mode: ImportMode::TryReference, + }) + .await?; + + let node_id = router.endpoint().node_id(); + let ticket = BlobTicket::new(node_id.into(), tag.hash, tag.format); + + println!("File hashed. Fetch this file by running:"); + println!( + "cargo run --example transfer -- receive {ticket} {}", + filename.display() + ); + + tokio::signal::ctrl_c().await?; + + // Gracefully shut down the node + println!("Shutting down."); + router.shutdown().await?; + } + ["receive", ticket, filename] => { + // For receiving files, we create a "downloader" that allows us to fetch files + // from other nodes via iroh connections + let downloader = store.downloader(&endpoint); + + let filename: PathBuf = filename.parse()?; + let abs_path = std::path::absolute(filename)?; + let ticket: BlobTicket = ticket.parse()?; + + println!("Starting download."); + + downloader + .download(ticket.hash(), Some(ticket.node_addr().node_id)) + .await?; + + println!("Finished download."); + println!("Copying to destination."); + + store + .blobs() + .export_with_opts(ExportOptions { + hash: ticket.hash(), + mode: ExportMode::TryReference, + target: abs_path, + }) + .await?; + + println!("Finished copying."); + + // Gracefully shut down the node + println!("Shutting down."); + endpoint.close().await; + } + _ => { + println!("Couldn't parse command line arguments: {args:?}"); + println!("Usage:"); + println!(" # to send:"); + println!(" cargo run --example transfer -- send [FILE]"); + println!(" # this will print a ticket."); + println!(); + println!(" # to receive:"); + println!(" cargo run --example transfer -- receive [TICKET] [FILE]"); + } + } + + Ok(()) +} From 870e415a4d84b0838c17789f6dfca7da2f1f19b5 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Thu, 10 Jul 2025 18:06:36 +0300 Subject: [PATCH 2/4] fix: Make the default split strategy for the downloader None This avoids https://github.com/n0-computer/iroh-blobs/issues/106 until it is fixed. --- src/api/downloader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/downloader.rs b/src/api/downloader.rs index c86d8899..4ea34120 100644 --- a/src/api/downloader.rs +++ b/src/api/downloader.rs @@ -364,7 +364,7 @@ impl Downloader { self.download_with_opts(DownloadOptions { request, providers, - strategy: SplitStrategy::Split, + strategy: SplitStrategy::None, }) } From ac51f3dbe7723b40291aabb689a918ac4a427996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Thu, 10 Jul 2025 19:25:42 +0200 Subject: [PATCH 3/4] Simplify --- examples/transfer.rs | 55 ++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/examples/transfer.rs b/examples/transfer.rs index 74e2a02b..7c2c92d7 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -1,20 +1,14 @@ use std::path::PathBuf; -use anyhow::Result; use iroh::{protocol::Router, Endpoint}; -use iroh_blobs::{ - api::blobs::{AddPathOptions, ExportMode, ExportOptions, ImportMode}, - net_protocol::Blobs, - store::mem::MemStore, - ticket::BlobTicket, - BlobFormat, -}; +use iroh_blobs::{net_protocol::Blobs, store::mem::MemStore, ticket::BlobTicket}; #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> anyhow::Result<()> { // Create an endpoint, it allows creating and accepting // connections in the iroh p2p world let endpoint = Endpoint::builder().discovery_n0().bind().await?; + // We initialize an in-memory backing store for iroh-blobs let store = MemStore::new(); // Then we initialize a struct that can accept blobs requests over iroh connections @@ -27,29 +21,16 @@ async fn main() -> Result<()> { match arg_refs.as_slice() { ["send", filename] => { - // For sending files we build a router that accepts blobs connections & routes them - // to the blobs protocol. - let router = Router::builder(endpoint) - .accept(iroh_blobs::ALPN, blobs) - .spawn(); - let filename: PathBuf = filename.parse()?; let abs_path = std::path::absolute(&filename)?; println!("Hashing file."); - // When we import a blob, we get back a tag that refers to said blob in the store + // When we import a blob, we get back a "tag" that refers to said blob in the store // and allows us to control when/if it gets garbage-collected - let tag = store - .blobs() - .add_path_with_opts(AddPathOptions { - path: abs_path, - format: BlobFormat::Raw, - mode: ImportMode::TryReference, - }) - .await?; + let tag = store.blobs().add_path(abs_path).await?; - let node_id = router.endpoint().node_id(); + let node_id = endpoint.node_id(); let ticket = BlobTicket::new(node_id.into(), tag.hash, tag.format); println!("File hashed. Fetch this file by running:"); @@ -58,6 +39,12 @@ async fn main() -> Result<()> { filename.display() ); + // For sending files we build a router that accepts blobs connections & routes them + // to the blobs protocol. + let router = Router::builder(endpoint) + .accept(iroh_blobs::ALPN, blobs) + .spawn(); + tokio::signal::ctrl_c().await?; // Gracefully shut down the node @@ -65,31 +52,23 @@ async fn main() -> Result<()> { router.shutdown().await?; } ["receive", ticket, filename] => { - // For receiving files, we create a "downloader" that allows us to fetch files - // from other nodes via iroh connections - let downloader = store.downloader(&endpoint); - let filename: PathBuf = filename.parse()?; let abs_path = std::path::absolute(filename)?; let ticket: BlobTicket = ticket.parse()?; println!("Starting download."); - downloader + // For receiving files, we create a "downloader" that allows us to fetch files + // from other nodes via iroh connections + store + .downloader(&endpoint) .download(ticket.hash(), Some(ticket.node_addr().node_id)) .await?; println!("Finished download."); println!("Copying to destination."); - store - .blobs() - .export_with_opts(ExportOptions { - hash: ticket.hash(), - mode: ExportMode::TryReference, - target: abs_path, - }) - .await?; + store.blobs().export(ticket.hash(), abs_path).await?; println!("Finished copying."); From 23c2d58de4560bab878ddc2e2463e6a449ba07de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 11 Jul 2025 09:38:14 +0200 Subject: [PATCH 4/4] Store downloader in a variable --- examples/transfer.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/transfer.rs b/examples/transfer.rs index 7c2c92d7..b3146d64 100644 --- a/examples/transfer.rs +++ b/examples/transfer.rs @@ -56,12 +56,13 @@ async fn main() -> anyhow::Result<()> { let abs_path = std::path::absolute(filename)?; let ticket: BlobTicket = ticket.parse()?; - println!("Starting download."); - // For receiving files, we create a "downloader" that allows us to fetch files // from other nodes via iroh connections - store - .downloader(&endpoint) + let downloader = store.downloader(&endpoint); + + println!("Starting download."); + + downloader .download(ticket.hash(), Some(ticket.node_addr().node_id)) .await?;