car_mirror/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)]
3#![deny(unreachable_pub)]
4
5//! # Car Mirror
6//!
7//! This crate provides the "no-io" protocol pieces to run the car mirror protocol.
8//!
9//! For more information, see the `push` and `pull` modules for further documentation
10//! or take a look at the [specification].
11//!
12//! [specification]: https://github.com/wnfs-wg/car-mirror-spec
13
14/// Test utilities. Enabled with the `test_utils` feature flag.
15#[cfg(any(test, feature = "test_utils"))]
16#[cfg_attr(docsrs, doc(cfg(feature = "test_utils")))]
17pub mod test_utils;
18
19/// Module with local caching strategies and mechanisms that greatly enhance CAR mirror performance
20pub mod cache;
21/// Code that's common among the push and pull protocol sides (most of the code).
22///
23/// This code is less concerened about the "client" and "server" ends of the protocol, but
24/// more about the "block sending" and "block receiving" end of the protocol. I.e. which
25/// direction do blocks go?
26/// When going from "push" to "pull" protocol, the client and server swap the "block sending"
27/// and "block receiving" roles.
28///
29/// Consider the functions in here mostly internal, and refer to the `push` and `pull` modules instead.
30pub mod common;
31/// Algorithms for walking IPLD directed acyclic graphs
32pub mod dag_walk;
33/// Error types
34mod error;
35/// Algorithms for doing incremental verification of IPLD DAGs against a root hash on the receiving end.
36pub mod incremental_verification;
37/// Data types that are sent over-the-wire and relevant serialization code.
38pub mod messages;
39/// The CAR mirror pull protocol. Meant to be used qualified, i.e. `pull::request` and `pull::response`.
40///
41/// This library exposes both streaming and non-streaming variants. It's recommended to use
42/// the streaming variants if possible.
43///
44/// ## Examples
45///
46/// ### Test Data
47///
48/// We'll set up some test data to simulate the protocol like this:
49///
50/// ```no_run
51/// use car_mirror::cache::InMemoryCache;
52/// use wnfs_common::MemoryBlockStore;
53/// use wnfs_unixfs_file::builder::FileBuilder;
54///
55/// # #[async_std::main]
56/// # async fn main() -> anyhow::Result<()> {
57/// // We simulate peers having separate data stores
58/// let client_store = MemoryBlockStore::new();
59/// let server_store = MemoryBlockStore::new();
60///
61/// // Give both peers ~10MB of cache space for speeding up computations.
62/// // These are available under the `quick_cache` feature.
63/// // (You can also implement your own, or disable caches using `NoCache`)
64/// let client_cache = InMemoryCache::new(100_000);
65/// let server_cache = InMemoryCache::new(100_000);
66///
67/// // At time of writing, Cargo.lock is 86KB, so this ends u ~8MB
68/// let file_bytes = async_std::fs::read("../Cargo.lock").await?.repeat(100);
69///
70/// // Load some data onto the client
71/// let root = FileBuilder::new()
72/// .content_bytes(file_bytes.clone())
73/// .build()?
74/// .store(&client_store)
75/// .await?;
76///
77/// // The server may already have a subset of the data
78/// FileBuilder::new()
79/// .content_bytes(file_bytes[0..1_000_000].to_vec())
80/// .build()?
81/// .store(&server_store)
82/// .await?;
83/// # Ok(())
84/// # }
85/// ```
86///
87/// ### With Streaming
88///
89/// This simulates a pull protocol run between two peers locally:
90///
91/// ```
92/// use car_mirror::{pull, common::Config};
93/// use futures::TryStreamExt;
94/// use tokio_util::io::StreamReader;
95/// # use car_mirror::cache::InMemoryCache;
96/// # use wnfs_common::MemoryBlockStore;
97/// # use wnfs_unixfs_file::builder::FileBuilder;
98/// #
99/// # #[async_std::main]
100/// # async fn main() -> anyhow::Result<()> {
101/// # let client_store = MemoryBlockStore::new();
102/// # let server_store = MemoryBlockStore::new();
103/// #
104/// # let client_cache = InMemoryCache::new(100_000);
105/// # let server_cache = InMemoryCache::new(100_000);
106/// #
107/// # let file_bytes = async_std::fs::read("../Cargo.lock").await?.repeat(100);
108/// #
109/// # let root = FileBuilder::new()
110/// # .content_bytes(file_bytes.clone())
111/// # .build()?
112/// # .store(&client_store)
113/// # .await?;
114/// #
115/// # FileBuilder::new()
116/// # .content_bytes(file_bytes[0..1_000_000].to_vec())
117/// # .build()?
118/// # .store(&server_store)
119/// # .await?;
120///
121/// // We set up some protocol configurations (allowed maximum block sizes etc.)
122/// let config = &Config::default();
123///
124/// // The client generates a request of what data still needs to be fetched
125/// let mut request =
126/// pull::request(root, None, config, &client_store, &client_cache).await?;
127///
128/// // The request contains information about which blocks still need to be
129/// // fetched, so we can use it to find out whether we need to to fetch any
130/// // blocks at all.
131/// while !request.indicates_finished() {
132/// // The server answers with a stream of data
133/// let chunk_stream = pull::response_streaming(
134/// root,
135/// request,
136/// &server_store,
137/// &server_cache
138/// ).await?;
139///
140/// let byte_stream = StreamReader::new(
141/// chunk_stream.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
142/// );
143///
144/// // The client verifies & stores the streamed data and possibly
145/// // interrupts the stream to produce a new request with more precise
146/// // information on what to pull.
147/// request = pull::handle_response_streaming(
148/// root,
149/// byte_stream,
150/// config,
151/// &client_store,
152/// &client_cache,
153/// ).await?;
154/// }
155/// # Ok(())
156/// # }
157/// ```
158///
159///
160/// ### Without Streaming
161///
162/// This simulates a pull protocol run between two peers locally, without streaming:
163///
164/// ```
165/// use car_mirror::{pull, common::Config};
166/// # use car_mirror::cache::InMemoryCache;
167/// # use wnfs_common::MemoryBlockStore;
168/// # use wnfs_unixfs_file::builder::FileBuilder;
169/// #
170/// # #[async_std::main]
171/// # async fn main() -> anyhow::Result<()> {
172/// # let client_store = MemoryBlockStore::new();
173/// # let server_store = MemoryBlockStore::new();
174/// #
175/// # let client_cache = InMemoryCache::new(100_000);
176/// # let server_cache = InMemoryCache::new(100_000);
177/// #
178/// # let file_bytes = async_std::fs::read("../Cargo.lock").await?.repeat(100);
179/// #
180/// # let root = FileBuilder::new()
181/// # .content_bytes(file_bytes.clone())
182/// # .build()?
183/// # .store(&client_store)
184/// # .await?;
185/// #
186/// # FileBuilder::new()
187/// # .content_bytes(file_bytes[0..1_000_000].to_vec())
188/// # .build()?
189/// # .store(&server_store)
190/// # .await?;
191///
192/// // We set up some protocol configurations (allowed maximum block sizes etc.)
193/// let config = &Config::default();
194///
195/// let mut last_car = None;
196/// loop {
197/// // The client handles a possible previous response and produces a request
198/// let request = pull::request(
199/// root,
200/// last_car,
201/// config,
202/// &client_store,
203/// &client_cache
204/// ).await?;
205///
206/// if request.indicates_finished() {
207/// break; // No need to fetch more, we already have all data
208/// }
209///
210/// // The server consumes the car file and provides information about
211/// // further blocks needed
212/// last_car = Some(pull::response(
213/// root,
214/// request,
215/// config,
216/// &server_store,
217/// &server_cache
218/// ).await?);
219/// }
220/// # Ok(())
221/// # }
222/// ```
223pub mod pull;
224/// The CAR mirror push protocol. Meant to be used qualified, i.e. `push::request` and `push::response`.
225///
226/// This library exposes both streaming and non-streaming variants. It's recommended to use
227/// the streaming variants if possible.
228///
229/// ## Examples
230///
231/// ### Test Data
232///
233/// We'll set up some test data to simulate the protocol like this:
234///
235/// ```no_run
236/// use car_mirror::cache::InMemoryCache;
237/// use wnfs_common::MemoryBlockStore;
238/// use wnfs_unixfs_file::builder::FileBuilder;
239///
240/// # #[async_std::main]
241/// # async fn main() -> anyhow::Result<()> {
242/// // We simulate peers having separate data stores
243/// let client_store = MemoryBlockStore::new();
244/// let server_store = MemoryBlockStore::new();
245///
246/// // Give both peers ~10MB of cache space for speeding up computations.
247/// // These are available under the `quick_cache` feature.
248/// // (You can also implement your own, or disable caches using `NoCache`)
249/// let client_cache = InMemoryCache::new(100_000);
250/// let server_cache = InMemoryCache::new(100_000);
251///
252/// let file_bytes = async_std::fs::read("../Cargo.lock").await?.repeat(100);
253///
254/// // Load some data onto the client
255/// let root = FileBuilder::new()
256/// .content_bytes(file_bytes.clone())
257/// .build()?
258/// .store(&client_store)
259/// .await?;
260///
261/// // The server may already have a subset of the data
262/// FileBuilder::new()
263/// .content_bytes(file_bytes[0..1_000_000].to_vec())
264/// .build()?
265/// .store(&server_store)
266/// .await?;
267/// # Ok(())
268/// # }
269/// ```
270///
271/// ### With Streaming
272///
273/// This simulates a push protocol run between two peers locally:
274///
275/// ```
276/// use car_mirror::{push, common::Config};
277/// use futures::TryStreamExt;
278/// use tokio_util::io::StreamReader;
279/// # use car_mirror::cache::InMemoryCache;
280/// # use wnfs_common::MemoryBlockStore;
281/// # use wnfs_unixfs_file::builder::FileBuilder;
282/// #
283/// # #[async_std::main]
284/// # async fn main() -> anyhow::Result<()> {
285/// # let client_store = MemoryBlockStore::new();
286/// # let server_store = MemoryBlockStore::new();
287/// #
288/// # let client_cache = InMemoryCache::new(100_000);
289/// # let server_cache = InMemoryCache::new(100_000);
290/// #
291/// # let file_bytes = async_std::fs::read("../Cargo.lock").await?.repeat(100);
292/// #
293/// # let root = FileBuilder::new()
294/// # .content_bytes(file_bytes.clone())
295/// # .build()?
296/// # .store(&client_store)
297/// # .await?;
298/// #
299/// # FileBuilder::new()
300/// # .content_bytes(file_bytes[0..1_000_000].to_vec())
301/// # .build()?
302/// # .store(&server_store)
303/// # .await?;
304///
305/// // We set up some protocol configurations (allowed maximum block sizes etc.)
306/// let config = &Config::default();
307///
308/// let mut last_response = None;
309/// loop {
310/// // The client generates a request that streams the data to the server
311/// let chunk_stream = push::request_streaming(
312/// root,
313/// last_response,
314/// &client_store,
315/// &client_cache
316/// ).await?;
317///
318/// let byte_stream = StreamReader::new(
319/// chunk_stream.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
320/// );
321///
322/// // The server consumes the streaming request & interrupts with new
323/// // information about what blocks it already has or in case the client
324/// // can stop sending altogether.
325/// let response = push::response_streaming(
326/// root,
327/// byte_stream,
328/// config,
329/// &server_store,
330/// &server_cache
331/// ).await?;
332///
333/// if response.indicates_finished() {
334/// break; // we're done!
335/// }
336///
337/// last_response = Some(response);
338/// }
339/// # Ok(())
340/// # }
341/// ```
342///
343///
344/// ### Without Streaming
345///
346/// This simulates a push protocol run between two peers locally, without streaming:
347///
348/// ```
349/// use car_mirror::{push, common::Config};
350/// # use car_mirror::cache::InMemoryCache;
351/// # use wnfs_common::MemoryBlockStore;
352/// # use wnfs_unixfs_file::builder::FileBuilder;
353/// #
354/// # #[async_std::main]
355/// # async fn main() -> anyhow::Result<()> {
356/// # let client_store = MemoryBlockStore::new();
357/// # let server_store = MemoryBlockStore::new();
358/// #
359/// # let client_cache = InMemoryCache::new(100_000);
360/// # let server_cache = InMemoryCache::new(100_000);
361/// #
362/// # let file_bytes = async_std::fs::read("../Cargo.lock").await?.repeat(100);
363/// #
364/// # let root = FileBuilder::new()
365/// # .content_bytes(file_bytes.clone())
366/// # .build()?
367/// # .store(&client_store)
368/// # .await?;
369/// #
370/// # FileBuilder::new()
371/// # .content_bytes(file_bytes[0..1_000_000].to_vec())
372/// # .build()?
373/// # .store(&server_store)
374/// # .await?;
375///
376/// // We set up some protocol configurations (allowed maximum block sizes etc.)
377/// let config = &Config::default();
378///
379/// let mut last_response = None;
380/// loop {
381/// // The client creates a CAR file for the request
382/// let car_file = push::request(
383/// root,
384/// last_response,
385/// config,
386/// &client_store,
387/// &client_cache
388/// ).await?;
389///
390/// // The server consumes the car file and provides information about
391/// // further blocks needed
392/// let response = push::response(
393/// root,
394/// car_file,
395/// config,
396/// &server_store,
397/// &server_cache
398/// ).await?;
399///
400/// if response.indicates_finished() {
401/// break; // we're done!
402/// }
403///
404/// last_response = Some(response);
405/// }
406/// # Ok(())
407/// # }
408/// ```
409pub mod push;
410
411pub use error::*;
412
413pub(crate) mod serde_bloom_bytes;
414pub(crate) mod serde_cid_vec;