diff --git a/wnfs-nameaccumulator/src/traits.rs b/wnfs-nameaccumulator/src/traits.rs index 8d80c984..262b26a4 100644 --- a/wnfs-nameaccumulator/src/traits.rs +++ b/wnfs-nameaccumulator/src/traits.rs @@ -1,6 +1,5 @@ #[cfg(feature = "num-bigint-dig")] use num_bigint_dig::{prime::probably_prime, BigUint, ModInverse, RandBigInt, RandPrime}; -#[cfg(feature = "num-bigint-dig")] use num_traits::{One, Zero}; use rand_core::CryptoRngCore; #[cfg(feature = "rug")] diff --git a/wnfs-wasm/src/fs/private/access_key.rs b/wnfs-wasm/src/fs/private/access_key.rs index 3a57e1f3..80af0fd9 100644 --- a/wnfs-wasm/src/fs/private/access_key.rs +++ b/wnfs-wasm/src/fs/private/access_key.rs @@ -1,3 +1,4 @@ +use crate::fs::{utils::error, JsResult}; use wasm_bindgen::prelude::wasm_bindgen; use wnfs::private::AccessKey as WnfsAccessKey; @@ -53,13 +54,18 @@ impl AccessKey { /// Make sure to keep safe or encrypt /// (e.g. using the WebCrypto and asymmetrically encrypting these bytes). #[wasm_bindgen(js_name = "toBytes")] - pub fn into_bytes(&self) -> Vec { - Vec::::from(&self.0) + pub fn into_bytes(&self) -> JsResult> { + let bytes = self + .0 + .to_bytes() + .map_err(error("Couldn't serialize access key"))?; + Ok(bytes) } /// Deserialize an AccessKey previously generated from `into_bytes`. #[wasm_bindgen(js_name = "fromBytes")] - pub fn from_bytes(bytes: &[u8]) -> Self { - Self(WnfsAccessKey::from(bytes).into()) + pub fn from_bytes(bytes: &[u8]) -> JsResult { + let access_key = WnfsAccessKey::parse(bytes).map_err(error("Couldn't parse access key"))?; + Ok(Self(access_key)) } } diff --git a/wnfs/Cargo.toml b/wnfs/Cargo.toml index d350c836..3a5918e0 100644 --- a/wnfs/Cargo.toml +++ b/wnfs/Cargo.toml @@ -34,6 +34,7 @@ multihash = "0.19" once_cell = "1.16" proptest = { version = "1.1", optional = true } quick_cache = "0.4.0" +rand_chacha = "0.3" rand_core = "0.6" semver = { version = "1.0", features = ["serde"] } serde = { version = "1.0", features = ["rc"] } @@ -58,7 +59,6 @@ fake = { version = "2.6.1", features = ["chrono"] } insta = "1.30" proptest = "1.1" rand = "0.8" -rand_chacha = "0.3" rsa = "0.9" serde_json = "1.0.103" sha2 = "0.10" diff --git a/wnfs/src/error.rs b/wnfs/src/error.rs index 300bb68c..49a94675 100644 --- a/wnfs/src/error.rs +++ b/wnfs/src/error.rs @@ -54,8 +54,8 @@ pub enum FsError { #[error("Mismatch between PrivateNode name {0} and its mountpoint {0}")] MountPointAndDeserializedNameMismatch(String, String), - #[error("Cannot find private ref with specified root path")] - PrivateRefNotFound, + #[error("Cannot find the partition with this name")] + PartitionNotFound, } /// Data sharing related errors diff --git a/wnfs/src/lib.rs b/wnfs/src/lib.rs index bef16225..98c6bebd 100644 --- a/wnfs/src/lib.rs +++ b/wnfs/src/lib.rs @@ -126,7 +126,8 @@ pub mod error; pub mod private; pub mod public; -pub(crate) mod root_tree; +#[doc(hidden)] // The API is in "prerelease" for now +pub mod root_tree; pub mod traits; mod utils; diff --git a/wnfs/src/private/keys/access.rs b/wnfs/src/private/keys/access.rs index 120490c6..67839f94 100644 --- a/wnfs/src/private/keys/access.rs +++ b/wnfs/src/private/keys/access.rs @@ -42,6 +42,14 @@ pub struct SnapshotAccessKey { //-------------------------------------------------------------------------------------------------- impl AccessKey { + pub fn parse(bytes: impl AsRef<[u8]>) -> Result { + Ok(serde_ipld_dagcbor::from_slice(bytes.as_ref())?) + } + + pub fn to_bytes(&self) -> Result> { + Ok(serde_ipld_dagcbor::to_vec(self)?) + } + pub fn get_label(&self) -> &HashOutput { match self { Self::Temporal(key) => &key.label, @@ -105,18 +113,6 @@ impl From<&PrivateRef> for SnapshotAccessKey { } } -impl From<&[u8]> for AccessKey { - fn from(bytes: &[u8]) -> Self { - serde_ipld_dagcbor::from_slice(bytes).unwrap() - } -} - -impl From<&AccessKey> for Vec { - fn from(key: &AccessKey) -> Self { - serde_ipld_dagcbor::to_vec(key).unwrap() - } -} - //-------------------------------------------------------------------------------------------------- // Tests //-------------------------------------------------------------------------------------------------- diff --git a/wnfs/src/private/node/keys.rs b/wnfs/src/private/node/keys.rs index 7c656caf..337600c7 100644 --- a/wnfs/src/private/node/keys.rs +++ b/wnfs/src/private/node/keys.rs @@ -222,7 +222,6 @@ impl SnapshotKey { #[cfg(test)] mod proptests { use super::*; - use crate::private::KEY_BYTE_SIZE; use proptest::{prelude::any, prop_assert_eq, prop_assert_ne}; use rand_chacha::ChaCha12Rng; use rand_core::SeedableRng; diff --git a/wnfs/src/private/previous.rs b/wnfs/src/private/previous.rs index d85240e9..6564e2c1 100644 --- a/wnfs/src/private/previous.rs +++ b/wnfs/src/private/previous.rs @@ -497,7 +497,7 @@ impl PrivateNodeOnPathHistory { #[cfg(test)] mod tests { use super::*; - use crate::private::{forest::hamt::HamtForest, PrivateDirectory}; + use crate::private::forest::hamt::HamtForest; use chrono::Utc; use rand_chacha::ChaCha12Rng; use rand_core::SeedableRng; diff --git a/wnfs/src/public/directory.rs b/wnfs/src/public/directory.rs index 46313398..69bfea37 100644 --- a/wnfs/src/public/directory.rs +++ b/wnfs/src/public/directory.rs @@ -167,6 +167,7 @@ impl PublicDirectory { path_segments: &[String], store: &impl BlockStore, ) -> Result> { + // TODO(matheus23) actually set the modification time of all these nodes let mut working_dir = self.prepare_next_revision(); for (depth, segment) in path_segments.iter().enumerate() { match working_dir.lookup_node(segment, store).await? { @@ -634,6 +635,7 @@ impl PublicDirectory { path_segments: &[String], store: &impl BlockStore, ) -> Result { + // TODO(matheus23) set modification time let (path, node_name) = utils::split_last(path_segments)?; let SearchResult::Found(dir) = self.get_leaf_dir_mut(path, store).await? else { @@ -885,7 +887,6 @@ impl Storable for PublicDirectory { #[cfg(test)] mod tests { use super::*; - use chrono::Utc; use libipld_core::ipld::Ipld; use testresult::TestResult; use wnfs_common::{decode, libipld::cbor::DagCborCodec, MemoryBlockStore}; diff --git a/wnfs/src/public/file.rs b/wnfs/src/public/file.rs index 97bf841e..ed4a3e6e 100644 --- a/wnfs/src/public/file.rs +++ b/wnfs/src/public/file.rs @@ -2,7 +2,7 @@ use super::{PublicFileSerializable, PublicNodeSerializable}; use crate::{error::FsError, is_readable_wnfs_version, traits::Id, WNFS_VERSION}; -use anyhow::{bail, Result}; +use anyhow::{anyhow, bail, Result}; use async_once_cell::OnceCell; use chrono::{DateTime, Utc}; use futures::{AsyncRead, AsyncReadExt}; @@ -297,8 +297,10 @@ impl PublicFile { len_limit: Option, store: &'a impl BlockStore, ) -> Result> { + let size = self.size(store).await?; let mut reader = self.stream_content(byte_offset, store).await?; if let Some(len) = len_limit { + let len = std::cmp::min(len as u64, size - byte_offset) as usize; let mut buffer = vec![0; len]; reader.read_exact(&mut buffer).await?; Ok(buffer) @@ -343,8 +345,11 @@ impl PublicFile { /// } /// ``` pub async fn size(&self, store: &impl BlockStore) -> Result { - let value = self.userland.resolve_value(store).await?; - Ok(value.filesize().unwrap_or(0)) + self.userland + .resolve_value(store) + .await? + .filesize() + .ok_or_else(|| anyhow!("Missing size on dag-pb node")) } /// Gets the entire content of a file. @@ -533,7 +538,6 @@ impl Clone for PublicFile { #[cfg(test)] mod tests { use super::*; - use chrono::Utc; use wnfs_common::MemoryBlockStore; #[async_std::test] diff --git a/wnfs/src/root_tree.rs b/wnfs/src/root_tree.rs index 4d959a6f..1fa4e746 100644 --- a/wnfs/src/root_tree.rs +++ b/wnfs/src/root_tree.rs @@ -1,12 +1,8 @@ -//! TODO(appcypher): Add private API for now, but remove it later. - -#![allow(dead_code)] - use crate::{ error::FsError, private::{ forest::{hamt::HamtForest, traits::PrivateForest}, - PrivateDirectory, + AccessKey, PrivateDirectory, PrivateNode, }, public::PublicDirectory, WNFS_VERSION, @@ -16,12 +12,11 @@ use anyhow::{bail, Result}; use chrono::TimeZone; use chrono::{DateTime, Utc}; use libipld_core::cid::Cid; -#[cfg(test)] -use rand_chacha::{rand_core::SeedableRng, ChaCha12Rng}; -use rand_core::CryptoRngCore; +use rand_chacha::ChaCha12Rng; +use rand_core::{CryptoRngCore, SeedableRng}; use semver::Version; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::BTreeMap; #[cfg(test)] use wnfs_common::MemoryBlockStore; use wnfs_common::{ @@ -30,21 +25,18 @@ use wnfs_common::{ utils::{Arc, CondSend}, BlockStore, Metadata, Storable, }; -#[cfg(test)] -use wnfs_nameaccumulator::AccumulatorSetup; //-------------------------------------------------------------------------------------------------- // Types //-------------------------------------------------------------------------------------------------- -#[derive(Debug)] -pub struct RootTree<'a, B: BlockStore, R: CryptoRngCore> { - pub store: &'a B, - pub rng: R, +#[derive(Debug, Clone)] +pub struct RootTree { + pub store: B, pub forest: Arc, pub public_root: Arc, pub exchange_root: Arc, - pub private_map: HashMap, Arc>, + pub private_map: BTreeMap, Arc>, } #[derive(Debug, Serialize, Deserialize)] @@ -55,25 +47,41 @@ pub struct RootTreeSerializable { pub version: Version, } +/// A directory from a particular WNFS partition +pub enum Partition { + Public(Arc), + Exchange(Arc), + Private(Vec, Arc), + // TODO(matheus23): Support mounting only singular files +} + //-------------------------------------------------------------------------------------------------- // Implementations //-------------------------------------------------------------------------------------------------- -impl<'a, B, R> RootTree<'a, B, R> -where - B: BlockStore, - R: CryptoRngCore + CondSend, -{ +impl RootTree { + pub fn empty_with(store: B, rng: &mut impl CryptoRngCore, time: DateTime) -> RootTree { + Self { + store, + forest: Arc::new(HamtForest::new_rsa_2048(rng)), + public_root: PublicDirectory::new_rc(time), + exchange_root: PublicDirectory::new_rc(time), + private_map: BTreeMap::new(), + } + } + + pub fn empty(store: B) -> RootTree { + Self::empty_with(store, &mut ChaCha12Rng::from_entropy(), Utc::now()) + } + pub async fn new( forest: Arc, - store: &'a B, - rng: R, + store: B, time: DateTime, - private_map: HashMap, Arc>, - ) -> RootTree<'a, B, R> { + private_map: BTreeMap, Arc>, + ) -> RootTree { Self { store, - rng, forest, public_root: PublicDirectory::new_rc(time), exchange_root: PublicDirectory::new_rc(time), @@ -81,284 +89,365 @@ where } } - pub async fn create_private_root(&mut self, name: &str, time: DateTime) -> Result<()> { + pub async fn create_private_root(&mut self, path: &[String]) -> Result { + self.create_private_root_with(path, Utc::now(), &mut ChaCha12Rng::from_entropy()) + .await + } + + pub async fn create_private_root_with( + &mut self, + path: &[String], + time: DateTime, + rng: &mut (impl CryptoRngCore + CondSend), + ) -> Result { + match path.first().map(|p| p.as_str()) { + Some("private") => {} + Some("public") | Some("exchange") => bail!(FsError::DirectoryAlreadyExists), + Some(_) => bail!(FsError::InvalidPath), + None => bail!(FsError::InvalidPath), + }; + + if self.private_map.contains_key(path) { + bail!(FsError::DirectoryAlreadyExists) + } + let root = PrivateDirectory::new_and_store( &self.forest.empty_name(), time, &mut self.forest, - self.store, - &mut self.rng, + &self.store, + rng, ) .await?; - self.private_map.insert(vec![name.to_string()], root); + let access_key = root + .as_node() + .store(&mut self.forest, &self.store, rng) + .await?; + + self.private_map.insert(path.to_vec(), root); + + Ok(access_key) + } + + pub async fn load_private_root( + &mut self, + path: &[String], + access_key: &AccessKey, + ) -> Result<()> { + let dir = PrivateNode::load(access_key, &self.forest, &self.store, None) + .await? + .as_dir()?; + + self.private_map.insert(path.to_vec(), dir); Ok(()) } - pub async fn ls( - &self, - root_segments: &[String], - path_segments: &[String], - ) -> Result> { - let Some(first) = root_segments.first() else { + pub async fn store_private_root(&mut self, path: &[String]) -> Result { + self.store_private_root_with(path, &mut ChaCha12Rng::from_entropy()) + .await + } + + pub async fn store_private_root_with( + &mut self, + path: &[String], + rng: &mut (impl CryptoRngCore + CondSend), + ) -> Result { + let mut forest = Arc::clone(&self.forest); + + let (path, Partition::Private(_, dir)) = self.get_partition(path)? else { + bail!("Path is not in the private partition"); + }; + + let node = dir + .get_node(path, true, &forest, &self.store) + .await? + .ok_or(FsError::NotFound)?; + let access_key = node.store(&mut forest, &self.store, rng).await?; + + Ok(access_key) + } + + pub fn get_partition<'p>(&self, path: &'p [String]) -> Result<(&'p [String], Partition)> { + let Some(first) = path.first() else { bail!(FsError::InvalidPath) }; match first.as_str() { - "public" => self.public_root.ls(path_segments, self.store).await, - "exchange" => self.exchange_root.ls(path_segments, self.store).await, + "public" => Ok((&path[1..], Partition::Public(Arc::clone(&self.public_root)))), + "exchange" => Ok(( + &path[1..], + Partition::Exchange(Arc::clone(&self.exchange_root)), + )), _ => { - let root = self - .private_map - .get(root_segments) - .ok_or(FsError::PrivateRefNotFound)?; + let (prefix, root) = self + .lookup_private_root(path) + .ok_or(FsError::PartitionNotFound)?; + + Ok((&path[prefix.len()..], Partition::Private(prefix, root))) + } + } + } - root.ls(path_segments, true, &self.forest, self.store).await + pub fn save_partition(&mut self, partition: Partition) { + match partition { + Partition::Public(public_root) => self.public_root = public_root, + Partition::Exchange(exchange_root) => self.exchange_root = exchange_root, + Partition::Private(prefix, private_root) => { + self.private_map.insert(prefix, private_root); } } } - pub async fn read( + fn find_private_root(&self, path: &[String]) -> Option> { + for i in 0..=path.len() { + let prefix = &path[..i]; + let item = self.private_map.get(prefix); + if item.is_some() { + return Some(prefix.to_vec()); + } + } + None + } + + pub fn lookup_private_root( &self, - root_segments: &[String], - path_segments: &[String], - ) -> Result> { - let Some(first) = root_segments.first() else { - bail!(FsError::InvalidPath) - }; + path: &[String], + ) -> Option<(Vec, Arc)> { + if let Some(prefix) = self.find_private_root(path) { + if let Some(item) = self.private_map.get(&prefix) { + return Some((prefix, Arc::clone(item))); + } + } + None + } - match first.as_str() { - "public" => self.public_root.read(path_segments, self.store).await, - "exchange" => self.exchange_root.read(path_segments, self.store).await, - _ => { - let root = self - .private_map - .get(root_segments) - .ok_or(FsError::PrivateRefNotFound)?; + pub fn lookup_private_root_mut( + &mut self, + path: &[String], + ) -> Option<(Vec, &mut Arc)> { + if let Some(prefix) = self.find_private_root(path) { + if let Some(item) = self.private_map.get_mut(&prefix) { + return Some((prefix, item)); + } + } + None + } - root.read(path_segments, true, &self.forest, self.store) + pub async fn ls(&self, path: &[String]) -> Result> { + match self.get_partition(path)? { + (path, Partition::Public(public_root)) => public_root.ls(path, &self.store).await, + (path, Partition::Exchange(exchange_root)) => exchange_root.ls(path, &self.store).await, + (path, Partition::Private(_, private_root)) => { + private_root.ls(path, true, &self.forest, &self.store).await + } + } + } + + pub async fn read(&self, path: &[String]) -> Result> { + match self.get_partition(path)? { + (path, Partition::Public(public_root)) => public_root.read(path, &self.store).await, + (path, Partition::Exchange(exchange_root)) => { + exchange_root.read(path, &self.store).await + } + (path, Partition::Private(_, private_root)) => { + private_root + .read(path, true, &self.forest, &self.store) .await } } } - pub async fn write( + pub async fn write(&mut self, path: &[String], content: Vec) -> Result<()> { + self.write_with(path, content, Utc::now(), &mut ChaCha12Rng::from_entropy()) + .await + } + + pub async fn write_with( &mut self, - root_segments: &[String], - path_segments: &[String], + path: &[String], content: Vec, time: DateTime, + rng: &mut (impl CryptoRngCore + CondSend), ) -> Result<()> { - let Some(first) = root_segments.first() else { - bail!(FsError::InvalidPath) - }; - - match first.as_str() { - "public" => { - self.public_root - .write(path_segments, content, time, self.store) - .await + let forest = &mut Arc::clone(&self.forest); + let partition = match self.get_partition(path)? { + (path, Partition::Public(mut public_root)) => { + public_root.write(path, content, time, &self.store).await?; + Partition::Public(public_root) } - "exchange" => { - self.exchange_root - .write(path_segments, content, time, self.store) - .await + (path, Partition::Exchange(mut exchange_root)) => { + exchange_root + .write(path, content, time, &self.store) + .await?; + Partition::Exchange(exchange_root) } - _ => { - let root = self - .private_map - .get_mut(root_segments) - .ok_or(FsError::PrivateRefNotFound)?; - - root.write( - path_segments, - true, - time, - content, - &mut self.forest, - self.store, - &mut self.rng, - ) - .await + (path, Partition::Private(prefix, mut private_root)) => { + private_root + .write(path, true, time, content, forest, &self.store, rng) + .await?; + Partition::Private(prefix, private_root) } - } + }; + + self.forest = Arc::clone(forest); + self.save_partition(partition); + + Ok(()) + } + + pub async fn mkdir(&mut self, path: &[String]) -> Result<()> { + self.mkdir_with(path, Utc::now(), &mut ChaCha12Rng::from_entropy()) + .await } - pub async fn mkdir( + pub async fn mkdir_with( &mut self, - root_segments: &[String], - path_segments: &[String], + path: &[String], time: DateTime, + rng: &mut (impl CryptoRngCore + CondSend), ) -> Result<()> { - let Some(first) = root_segments.first() else { - bail!(FsError::InvalidPath) - }; - - match first.as_str() { - "public" => { - self.public_root - .mkdir(path_segments, time, self.store) - .await + let forest = &mut Arc::clone(&self.forest); + let partition = match self.get_partition(path)? { + (path, Partition::Public(mut public_root)) => { + public_root.mkdir(path, time, &self.store).await?; + Partition::Public(public_root) } - "exchange" => { - self.exchange_root - .mkdir(path_segments, time, self.store) - .await + (path, Partition::Exchange(mut exchange_root)) => { + exchange_root.mkdir(path, time, &self.store).await?; + Partition::Exchange(exchange_root) } - _ => { - let root = self - .private_map - .get_mut(root_segments) - .ok_or(FsError::PrivateRefNotFound)?; - - root.mkdir( - path_segments, - true, - time, - &self.forest, - self.store, - &mut self.rng, - ) - .await + (path, Partition::Private(prefix, mut private_root)) => { + private_root + .mkdir(path, true, time, forest, &self.store, rng) + .await?; + Partition::Private(prefix, private_root) } - } + }; + + self.forest = Arc::clone(forest); + self.save_partition(partition); + + Ok(()) } - pub async fn rm(&mut self, root_segments: &[String], path_segments: &[String]) -> Result<()> { - let Some(first) = root_segments.first() else { - bail!(FsError::InvalidPath) + pub async fn rm(&mut self, path: &[String]) -> Result<()> { + let forest = &mut Arc::clone(&self.forest); + let partition = match self.get_partition(path)? { + (path, Partition::Public(mut public_root)) => { + public_root.rm(path, &self.store).await?; + Partition::Public(public_root) + } + (path, Partition::Exchange(mut exchange_root)) => { + exchange_root.rm(path, &self.store).await?; + Partition::Exchange(exchange_root) + } + (path, Partition::Private(prefix, mut private_root)) => { + private_root.rm(path, true, forest, &self.store).await?; + Partition::Private(prefix, private_root) + } }; - match first.as_str() { - "public" => self - .public_root - .rm(path_segments, self.store) - .await - .map(|_| ()), - "exchange" => self - .exchange_root - .rm(path_segments, self.store) - .await - .map(|_| ()), - _ => { - let root = self - .private_map - .get_mut(root_segments) - .ok_or(FsError::PrivateRefNotFound)?; + self.forest = Arc::clone(forest); + self.save_partition(partition); - let _ = root - .rm(path_segments, true, &self.forest, self.store) - .await?; + Ok(()) + } - Ok(()) - } - } + pub async fn basic_mv(&mut self, path_from: &[String], path_to: &[String]) -> Result<()> { + self.basic_mv_with( + path_from, + path_to, + Utc::now(), + &mut ChaCha12Rng::from_entropy(), + ) + .await } - pub async fn basic_mv( + pub async fn basic_mv_with( &mut self, - root_segments: &[String], - path_segments_from: &[String], - path_segments_to: &[String], + path_from: &[String], + path_to: &[String], time: DateTime, + rng: &mut (impl CryptoRngCore + CondSend), ) -> Result<()> { - let Some(first) = root_segments.first() else { - bail!(FsError::InvalidPath) - }; - - match first.as_str() { - "public" => { - self.public_root - .basic_mv(path_segments_from, path_segments_to, time, self.store) - .await + let forest = &mut Arc::clone(&self.forest); + let partition = match (self.get_partition(path_from)?, self.get_partition(path_to)?) { + ((path_from, Partition::Public(mut public_root)), (path_to, Partition::Public(_))) => { + public_root + .basic_mv(path_from, path_to, time, &self.store) + .await?; + Partition::Public(public_root) } - "exchange" => { - self.exchange_root - .basic_mv(path_segments_from, path_segments_to, time, self.store) - .await + ( + (path_from, Partition::Exchange(mut exchange_root)), + (path_to, Partition::Exchange(_)), + ) => { + exchange_root + .basic_mv(path_from, path_to, time, &self.store) + .await?; + Partition::Public(exchange_root) } - _ => { - let root = self - .private_map - .get_mut(root_segments) - .ok_or(FsError::PrivateRefNotFound)?; - - root.basic_mv( - path_segments_from, - path_segments_to, - true, - time, - &mut self.forest, - self.store, - &mut self.rng, - ) - .await + ( + (path_from, Partition::Private(prefix_from, mut private_root)), + (path_to, Partition::Private(prefix_to, _)), + ) if prefix_from == prefix_to => { + private_root + .basic_mv(path_from, path_to, true, time, forest, &self.store, rng) + .await?; + Partition::Private(prefix_from, private_root) } - } + _ => bail!("Moving files or directories across partitions is not yet supported."), + }; + + self.forest = Arc::clone(forest); + self.save_partition(partition); + + Ok(()) } - pub async fn store(&mut self, store: &B) -> Result { + pub async fn store(&mut self) -> Result { + self.store_with(&mut ChaCha12Rng::from_entropy()).await + } + + pub async fn store_with(&mut self, rng: &mut (impl CryptoRngCore + CondSend)) -> Result { for (_, root) in self.private_map.iter() { - root.store(&mut self.forest, self.store, &mut self.rng) - .await?; + root.store(&mut self.forest, &self.store, rng).await?; } let serializable = RootTreeSerializable { - public: self.public_root.store(store).await?, - exchange: self.exchange_root.store(store).await?, - forest: self.forest.store(store).await?, + public: self.public_root.store(&self.store).await?, + exchange: self.exchange_root.store(&self.store).await?, + forest: self.forest.store(&self.store).await?, version: WNFS_VERSION, }; - let cid = store + let cid = self + .store .put_block(encode(&serializable, DagCborCodec)?, DagCborCodec.into()) .await?; Ok(cid) } - pub async fn load( - cid: &Cid, - store: &'a B, - rng: R, - private_map: HashMap, Arc>, - ) -> Result> { + pub async fn load(cid: &Cid, store: B) -> Result> { let deserialized: RootTreeSerializable = decode(&store.get_block(cid).await?, DagCborCodec)?; - let forest = Arc::new(HamtForest::load(&deserialized.forest, store).await?); - let public_root = Arc::new(PublicDirectory::load(&deserialized.public, store).await?); - let exchange_root = Arc::new(PublicDirectory::load(&deserialized.exchange, store).await?); + let forest = Arc::new(HamtForest::load(&deserialized.forest, &store).await?); + let public_root = Arc::new(PublicDirectory::load(&deserialized.public, &store).await?); + let exchange_root = Arc::new(PublicDirectory::load(&deserialized.exchange, &store).await?); Ok(Self { store, - rng, forest, public_root, exchange_root, - private_map, + private_map: BTreeMap::new(), }) } } -#[cfg(test)] -impl<'a, B: BlockStore> RootTree<'a, B, ChaCha12Rng> { - pub fn with_store(store: &'a B) -> RootTree<'a, B, ChaCha12Rng> { - let mut rng = ChaCha12Rng::seed_from_u64(0); - let time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); - let forest = Arc::new(HamtForest::new(AccumulatorSetup::trusted(&mut rng))); - - Self { - store, - rng, - forest, - public_root: PublicDirectory::new_rc(time), - exchange_root: PublicDirectory::new_rc(time), - private_map: HashMap::default(), - } - } -} - //-------------------------------------------------------------------------------------------------- // Tests //-------------------------------------------------------------------------------------------------- @@ -370,9 +459,9 @@ mod tests { #[async_std::test] async fn test_roots_read_write() { let store = MemoryBlockStore::default(); - let mut root_tree = RootTree::with_store(&store); + let mut root_tree = RootTree::empty(store); root_tree - .create_private_root("private", Utc::now()) + .create_private_root(&["private".into()]) .await .unwrap(); @@ -380,16 +469,14 @@ mod tests { root_tree .write( - &["public".into()], - &["test".into(), "file".into()], + &["public".into(), "test".into(), "file".into()], b"hello world".to_vec(), - Utc::now(), ) .await .unwrap(); let content = root_tree - .read(&["public".into()], &["test".into(), "file".into()]) + .read(&["public".into(), "test".into(), "file".into()]) .await .unwrap(); @@ -399,16 +486,14 @@ mod tests { root_tree .write( - &["exchange".into()], - &["test".into(), "file".into()], + &["exchange".into(), "test".into(), "file".into()], b"hello world".to_vec(), - Utc::now(), ) .await .unwrap(); let content = root_tree - .read(&["exchange".into()], &["test".into(), "file".into()]) + .read(&["exchange".into(), "test".into(), "file".into()]) .await .unwrap(); @@ -418,16 +503,14 @@ mod tests { root_tree .write( - &["private".into()], - &["test".into(), "file".into()], + &["private".into(), "test".into(), "file".into()], b"hello world".to_vec(), - Utc::now(), ) .await .unwrap(); let content = root_tree - .read(&["private".into()], &["test".into(), "file".into()]) + .read(&["private".into(), "test".into(), "file".into()]) .await .unwrap(); @@ -439,46 +522,43 @@ mod tests { mod snapshot_tests { use super::*; use crate::utils; - use rand_chacha::ChaCha12Rng; - use rand_core::SeedableRng; use wnfs_common::utils::SnapshotBlockStore; #[async_std::test] async fn test_root_filesystems() { let rng = &mut ChaCha12Rng::seed_from_u64(0); - let store = &mut SnapshotBlockStore::default(); + let store = SnapshotBlockStore::default(); let time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); let paths = [ // (["public".into()], vec!["text.txt".into()]), // (["exchange".into()], vec!["music".into(), "jazz".into()]), - (["private".into()], vec!["videos".into()]), + (vec!["private".into(), "videos".into()]), ]; - let mut root_tree = RootTree::with_store(store); + let mut root_tree = RootTree::empty_with(store, rng, time); root_tree - .create_private_root("private", time) + .create_private_root_with(&["private".into()], time, rng) .await .unwrap(); - for (root, path) in paths.iter() { + println!("{:#?}", root_tree.private_map); + + for path in paths.iter() { root_tree - .write(root, path, b"hello world".to_vec(), time) + .write_with(path, b"hello world".to_vec(), time, rng) .await .unwrap(); } - let _ = root_tree.store(store).await.unwrap(); + let root_cid = root_tree.store_with(rng).await.unwrap(); let forest = &mut Arc::clone(&root_tree.forest); - let root_dir = root_tree - .private_map - .get(&vec!["private".to_string()]) - .unwrap(); - - let root_cid = forest.store(store).await.unwrap(); + let (_, root_dir) = root_tree.lookup_private_root(&["private".into()]).unwrap(); - utils::walk_dir(store, forest, root_dir, rng).await.unwrap(); + utils::walk_dir(&mut root_tree.store, forest, &root_dir, rng) + .await + .unwrap(); - let values = store.get_dag_snapshot(root_cid).await.unwrap(); + let values = root_tree.store.get_dag_snapshot(root_cid).await.unwrap(); insta::assert_json_snapshot!(values); } diff --git a/wnfs/src/snapshots/wnfs__root_tree__snapshot_tests__root_filesystems.snap b/wnfs/src/snapshots/wnfs__root_tree__snapshot_tests__root_filesystems.snap index 2d262127..9488d0f4 100644 --- a/wnfs/src/snapshots/wnfs__root_tree__snapshot_tests__root_filesystems.snap +++ b/wnfs/src/snapshots/wnfs__root_tree__snapshot_tests__root_filesystems.snap @@ -4,24 +4,40 @@ expression: values --- [ { - "cid": "bafyr4ia2wpujoivisxr2m6kidkzkfsa3rosxc3rd5wnfdvjpupfcfdsyay", + "cid": "bafyr4iheuuydjam7pbrh7f36nxwmgqmpz7yzym35mitk33gokp56ne4ivy", + "value": { + "exchange": { + "/": "bafyr4ih3k2ipwwqryrlmtmwtolma4t772gknmx7pxqu5opjwsrr2nc27qq" + }, + "forest": { + "/": "bafyr4ifm3iuhqtorqgsknwaa55kxlkylxly422so5tgbds7oixx6auclde" + }, + "public": { + "/": "bafyr4ih3k2ipwwqryrlmtmwtolma4t772gknmx7pxqu5opjwsrr2nc27qq" + }, + "version": "1.0.0" + }, + "bytes": "pGZmb3Jlc3TYKlglAAFxHiCs2ih4TdGBpKbYAO9VdasLuvHNak7szBHL7kXv4FBLGWZwdWJsaWPYKlglAAFxHiD7VpD7WhHEVsmy03LYDk//0ZTWX++8Kdc9NpRjpotfhGd2ZXJzaW9uZTEuMC4waGV4Y2hhbmdl2CpYJQABcR4g+1aQ+1oRxFbJstNy2A5P/9GU1l/vvCnXPTaUY6aLX4Q=" + }, + { + "cid": "bafyr4ifm3iuhqtorqgsknwaa55kxlkylxly422so5tgbds7oixx6auclde", "value": { "accumulator": { "generator": { "/": { - "bytes": "OiFUpIl3+9YLl6mb7DAncdAcsA4Es11CHSwuUrT3qLIOa01g8tx/05d13vLOdYGxlW0qJQM/pcg8gjI2RVQsXgxX5g+dNepL1LR5Sx28Zw1i1yL8T9HTDf+3H2DCAwvGcfmjd4VU39gUUzkBH1Wc8oHyuI02wgX63iRUNcj0l27f/qB2vX4XfVaLm+KXH6UXq632/EHP5pUPbbE06/EkkhPOGSalSCeCsARLNSvmtupatiJchfqwjpUQZ63zacDr1kDC7dQzChI/69V05dCrOF2YYU/1SmWZUvOcevMluzTsKoUQ4B4DxV3PK0nuXN07oP+Ns8wGLPUmHGIguZ5YSw" + "bytes": "DnwXmD/vORPcLzdf1evo7nGljNInOtYHpODrD3+82Nnpu0qfQPxFLKSB2dhylrLkh4UmTnLZtphq+jBbSjrWkqRf6C/qEToqzAhkAPtkF048lnLaI3UjBCXagQjJv8boXybWdaxiKxTFN2nu9B2vzNyUl1QqO9wXmNkzWM1P4T8lIm6FTjWSbHg/TVTXkpv8nTgQIUA1A0Kk7jEtjb/Vl8VdTZ2UgBywKE1Pvf8HbmXDc6Cjg0K5+XXC6SW1Bk71iLdLM174Mu8KWDjb3qotRpqrGGeSIv1KI1v1VWCI8ZuohkVe4zT5JvzyKIizsVDKcPTKGR60H6enSeQZgShFww" } }, "modulus": { "/": { - "bytes": "mWvJwnuqVAwDETG2orQL6iW4a/cF5KWD5HdA3iBMoU7m1nscxbalzePs54YZy9WIKLSJSFZAhCST1VIiLa03Q/VStRrKuJema4Ls/BCxOLT1edYYrjJPoDnb/zcEcqfS/Ft0Uk0R8ZZvxnstx8y/wT/PcCGdY81mwORO5J8iGLDvdFJzFaRrMhfXuhakr52KevCXztvUSSHjKp+U7b5mP2H5a6LzrduBVZzD86ifjTx9bFms9WvIfDwOTGQHci4oe27ihqs3LsuBAcV3IvlXYH4YEArKwUh4XxOJJ5/LJb2lrVNOZWV/sNuusMGxzb3DdwrR9gTVMZuguUzys8nz7Q" + "bytes": "x5cM7tzDsHVEkCAaeqYTzXOREIHHkPXxqHJvRjVQu1t/8NuOHqEYnscvk9FlABG9chrurMKs3jKgQQfwZIwoE6MfWwt3Zf+LRLS2/8kzhLZG6wnHz16FktQOozyAA581tPFKBLUfe/14G+TRZzFkuo65kcLE1zC7vjX1kr3vUkr36Nrv0mxm/ALEea+J1k03P0QnCUOd5mzrlV8+o31RWfYTWAn4UzS1yxgTrdyAzQVgnxCsapWtZYcskJUlva0yvHKVkmQpIPJMYdxbPDt5I+VrFqTZ03PYch8ko/wPGzEx9VYVFyhmvMww+VBUyCTnM6XraBf3vBY5nUjGNhzH5Q" } } }, "root": [ { "/": { - "bytes": "QDI" + "bytes": "DBg" } }, [ @@ -29,15 +45,15 @@ expression: values [ { "/": { - "bytes": "VEAJHjZxrRoB/8mTrX4UuMtnHM+itbCFH1vIWpXbbfAo7xBmlBZrzLJaCzkEQCCkbkj6rrU55B1q71h+sfq38SOllRERe8XmvC5jWngd4IPzzDQ1i57G7pnu7/MNbNU0UWfV74+a2XoS8eLoQv+3mH+1XuBGh14Z7sZgpz3uzMMu3JaAG/tJAK5vwgCM7I2M3a9rungzf0ssumEs1GK+bwDWPLt+Olby9LaR65VFjjXYlPx+wbZUWwohNeDAbLp12+pKBj/l3boO1Qn6HWfq3ntxoXhYZAB+h2GG0Q/1Y+eHdVRV5ZsEZ66em5HjdMdqKlapsTUGe80nV0ZhwyYdQw" + "bytes": "ksJLdMWDZFkSS3E3g7OaTZJVjaWiwATU3XWgr7mv8c9oBYDPNafx3PU76U8BtPYtJBKt2kOdWAicQyd5vRrb41khHrBtf+j0+SFOaWPhcFo0ucq2ZFMf2BC2TqbQ7H5b21SMnPMxLrw1HMER48e9Z/SDb0S5+RntXoOLUJtPAbn9g4CHGLeqLnYcyZevrzeX1ta0kuFvbcS4/xFsrzEuwfAFQ+1eeZOFOMU0DsiptAwSFKCnMwNMbGwdxmnFqmVtCHQf+0/9yv642eZSs2Q5+5GA9wp9BN7tINbrqWHNTcKZlTHzvtAtcMysmlULyovcySxy/zuOIki75xwSAnrlFA" } }, [ { - "/": "bafkr4iariqnpyd7e7cbyjnao6cumagfnogxpcnvvlmuean7ulvni76zmqm" + "/": "bafkr4ibhabqoafmrp6hwfgr4t2nhuaubcdmiyj5gabmmb4dfixikm5onyu" }, { - "/": "bafkr4ifolkmx6m5tgmadmeplquvuj6n5436zqwgh4jmrdpzzvcd3nfaynm" + "/": "bafkr4idllpieugl4biudvfi5qpw3g45pagbmifo5xzmrmhelvq6bm63cqa" } ] ] @@ -46,15 +62,15 @@ expression: values [ { "/": { - "bytes": "TRAkpJcR25M1t2wCygztw8j2AAbi1ofKdj16AKCNzPrzKPcUml/ImOwJ5vDYZfUylwuELjuC4BK+h6pz4q8eijGB6zGxrwz7e3mmVcsLup+2isXpajWIBIeYDEW9NhPxl6ZE2fuQTTT0lIlhDO/PCdk48nq8kCbkovBarG9my/605H046R6oEvE3tvxGIinEGDEmOJ9Z92bMFsf0Y31f2q8wcZncziecQN0scPYBgEQnbX4FUApdikWD2HLdLSBsq95+G3MCsRNxkVNl20tybP0GdByMkM2q1iaYXQo87AMXmcWWuN3OIEOm+LTdJOlDLb6aw2z7DpjY7siiFbvrYg" + "bytes": "oQFcAlCbAaqjLdfLoKs5ES5a+CNCfJz16DfVCFUsXAHhv1IFwEoWZrNxcm/e9ufBeH4YIGW05YN9aoy4iv6iHBeT8XgHay5LgBUKWAWl3NPMlt/N9Y7hk5kjynmAXNI8nkGwKfe0UamToMdLTpqpzeiTpjo2D/sIHDwZBpGAe/M496cLPZAXQe/nkqiePII8UfpIuoEJ0MaZWcOVIBJ+15YhJSW/f1ver2dr3TpWqJzZ80Ftc+oUJSZ3zqQwz6EJC1bqIfMufm1MjSZV4s09xe5QnI7WSjK/Z+jintpsAZOJ6znSrj+u12MKOKbxnykxRCm+8KmVqaeRqHzUmAY7Zg" } }, [ { - "/": "bafkr4ienirlkygapnwl3sghfweqvliryz3ljip7xsebi2ojowksg7hkr2a" + "/": "bafkr4iahin3hzsm2b73deeogau3shuwtrq2k5ygczipcdcnvf2xoopd64y" }, { - "/": "bafkr4igujtuwoy7dfdfwyv7izywttixyj5ca2lnzgbjjspx5sds6zs6w2m" + "/": "bafkr4ieeumiq23ogcl7447hxztp5wxh4a7biumjvzpft56vduqjusq5wsm" } ] ] @@ -63,12 +79,12 @@ expression: values [ { "/": { - "bytes": "JmHW6pYm05+aa5nfqqVFPsB+bIkFETkbH4jxFFDsz3v0qoaeoql1j7kVZMo+y5q9wr/980bbWTMPuxGOsHvGevz/etbHvT4wIjWYevyiJE4GQyhc+A9MfnzOXzsJYGCg8ykPx//RsE97Tk//uhyOV2DEl0hixOoyS1rqo/p/vQhHsgIMD7mttlALLIrn0hM15aRRJUlTzK7x7zhYeWgVW7SuF5yHkxm3o21oE3C5nKegsFoSdeal7++o+q2PVAgwxfHgwjhSizp7iikTPLDn7ZGkSdRM1BKPSIF17exSBw8rGb5k2QGA7fBrw1+JLmRE0sUd3p9lmT+YgDLPQutxmg" + "bytes": "PRRTWbJEdyP2vNQYaAM2E8jBbkHbdEvYlyQIrBxM4QIfU20qmFzVRgWvQ+V6kXmBymY1NS/zdD4Y3Y8HwWiwKeL/J1ngQMFyyyYa+e8AHzEe0m9L6bCIy/TS2mLndg9tjOYDgc/P9g7m03tna7y5RUwFnNexMyzqUz9PLB24GtvpMjFNAbXejG1DGL0BoNqQnA6C+YneurC1bMIzKXfkjf2CPtmdU0GGLY4wWXfaDMuIhobHxrGPUXhdLCIEGiuDweEzALy8B6Q/WxVJ6xf3r8dug5ke2/Qg6qaMJUhaDfnNTXKMhsmkPA38yGEcGKo8ZLYUlJdyG9VdHesNai1jHg" } }, [ { - "/": "bafkr4ia6aehlkv3fe3tyeqladznq66uaebxilra2jfgr2mai7cb3wimbbm" + "/": "bafkr4if5i2ranqh33wx2ztludcfuvkk2wak56dz4iuu23pi3hfna4aqboa" } ] ] @@ -77,15 +93,15 @@ expression: values [ { "/": { - "bytes": "ePgCFhgNtSx4LVwRtKr0BQZs2dHAt8c2b9BoCHyat3WmPGYJobH3IO4nesTX/RGMxAuCJBMYW4PkUyc9yFCwq1RlryfD96U9df4WpCY16auNJ8dYOSPCBvPK2B16RVrkKvkSr5M+17molPkeYpSJAmOJGt4HtWSuoiMiAReBnn4pwWcGr/pyGkR48CVM3u06xyYjWGzt2NG2NszHb7O5gdO7G77GiBwtKqsPY7B7TJWoRdUKTf5GBVpaba5z3fXVv+hONvFkN2zQ4GSv7PK7+YkRNt5G9C3LHdoXq7AE2Zsm4ALB8vSKm0tfps49aYbcvfDB4f/x/HWK67Y3NdSdnw" + "bytes": "LKRDbcOUq6sRIKQ3uduezN/gGUud1Otd+xAu1yJmGl4UkXqhRIIKCi7V5wP37+li32u8F25eg/5Per2y/kDMpANePEiooTECYlXWiLcux+6uOfdw8nFJBwFTQQmNAErs6jRpOcy3K9uamJwgB80GQa/XnieHMEo5d5giJJwUlcDBUO+27XL8RNn7m4WoCVjnaijjoGtVGVnF2pS/qsYAfHwBKCcP1wITdNd5ZwNVxSTqrEHu+vy0Z1rpjAvzHOZwgBZGiE6PYx0VcNI1MKakOzojvSJA2laqx5YAkDxQW3g/o1ihovjZHFWhaRoNKJzAkEbmWaGeHpIAFczEf3usAg" } }, [ { - "/": "bafkr4ib2p54jvv2bxpo3cp5zxbreqzinyz5ruhr2ssf5db74o27vvb6rwi" + "/": "bafkr4ibsfetuv2shlfs6lhpr3s3xzcczizrni25taxs6cf5ik2ul5nirpi" }, { - "/": "bafkr4idzdwor5tagoubrsjgmoqei45pl3gz77e47wegx47p6k4yeoefhyq" + "/": "bafkr4igxwg7sdlh7no6xlvgkyeal2h7nt572ztxz5keeckua3aadvhedr4" } ] ] @@ -95,88 +111,160 @@ expression: values "structure": "hamt", "version": "0.1.0" }, - "bytes": "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" - }, - { - "cid": "bafkr4iariqnpyd7e7cbyjnao6cumagfnogxpcnvvlmuean7ulvni76zmqm", - "value": { - "/": { - "bytes": "jY27HPNui8oCbUGk7drT74knk3kJkF7V8mxwpTZIjqCDXI19ejA+KWE0YwdqUJ0NavLJ6OLnuRVZiN2zVGUeQRUpPq1n1Y16g139Gwcr0/V2jGx0grUdiwZEbCSqFO8RblDZcZV35eaS8B4/6jiaa8I+3Mkp9AkY+fXwNMjjC5ICr4OoGN0VI7A+KAEenDpuP91v5R/WnleJ/qDAKWBAfSnBSuJdBO3zaw1AFVwaeDXnRz95SnMFU7hmE38Pa6gNzqFzxLe7BqPgML4lC5QVZPTPjQB7usys2DySyLqchF/X1EwbMXXriSZv6HEVDULQV85NzKDl9s7GTcDDZWtcY+Cbb5yatoxAYe+fkN24XNwQXe6D9i4KP//8gGhtysBnJaaozks+WDp6KMbglgmkq5ECDJviWHfJC/xTBkUhbg94N6yFQeZEQ2K5PfDer3p3S9TYO1IBK2wBnvQQvd8/dEOcHwplKb/dMkz95LgExhu5m7TAoXrBdwVIxJIBT7f8dj6I3l8RlSBVeDpgXMfY1mSjkS/eF26qb1AhD3/Ja5JrxBGVpAu2k1Yek9UZ42PDyUSIJYHr9V3ElLy5jSVf3V8VsZq9DnTbEoNOt5BiVYvyJ4HVozIVJTb4TI8VoPyzbMF5n0SlFPar0CcOru24QjXcKFGtY+P0S2rZzin2ec6Yi3hFSJRbyw" - } - }, - "bytes": "jY27HPNui8oCbUGk7drT74knk3kJkF7V8mxwpTZIjqCDXI19ejA+KWE0YwdqUJ0NavLJ6OLnuRVZiN2zVGUeQRUpPq1n1Y16g139Gwcr0/V2jGx0grUdiwZEbCSqFO8RblDZcZV35eaS8B4/6jiaa8I+3Mkp9AkY+fXwNMjjC5ICr4OoGN0VI7A+KAEenDpuP91v5R/WnleJ/qDAKWBAfSnBSuJdBO3zaw1AFVwaeDXnRz95SnMFU7hmE38Pa6gNzqFzxLe7BqPgML4lC5QVZPTPjQB7usys2DySyLqchF/X1EwbMXXriSZv6HEVDULQV85NzKDl9s7GTcDDZWtcY+Cbb5yatoxAYe+fkN24XNwQXe6D9i4KP//8gGhtysBnJaaozks+WDp6KMbglgmkq5ECDJviWHfJC/xTBkUhbg94N6yFQeZEQ2K5PfDer3p3S9TYO1IBK2wBnvQQvd8/dEOcHwplKb/dMkz95LgExhu5m7TAoXrBdwVIxJIBT7f8dj6I3l8RlSBVeDpgXMfY1mSjkS/eF26qb1AhD3/Ja5JrxBGVpAu2k1Yek9UZ42PDyUSIJYHr9V3ElLy5jSVf3V8VsZq9DnTbEoNOt5BiVYvyJ4HVozIVJTb4TI8VoPyzbMF5n0SlFPar0CcOru24QjXcKFGtY+P0S2rZzin2ec6Yi3hFSJRbyw==" + "bytes": "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" }, { - "cid": "bafkr4ifolkmx6m5tgmadmeplquvuj6n5436zqwgh4jmrdpzzvcd3nfaynm", + "cid": "bafyr4ih3k2ipwwqryrlmtmwtolma4t772gknmx7pxqu5opjwsrr2nc27qq", "value": { - "/": { - "bytes": "Y4zodSij8DA8epzsuk7V/vBVHzr9HHhlUjPVEMYeeYMPkA4GydTcDrhOV77MMg8wlAFxb2/jmRkqrgfkRhrjy531sHolOZ7o7EXrjya6ptXmXR0O5smhfnv4f63nCk9ZeJmlHyYhPuUVuLGp62OkyXzW7LAm+bYW/IXnDctui53Mn82Hck/cHHUXCklxoyQAFvPPHzun5krrWCtvf4pndWOMbEUs/sH8Vw" + "wnfs/pub/dir": { + "metadata": { + "created": 0, + "modified": 0 + }, + "previous": [], + "userland": {}, + "version": "1.0.0" } }, - "bytes": "Y4zodSij8DA8epzsuk7V/vBVHzr9HHhlUjPVEMYeeYMPkA4GydTcDrhOV77MMg8wlAFxb2/jmRkqrgfkRhrjy531sHolOZ7o7EXrjya6ptXmXR0O5smhfnv4f63nCk9ZeJmlHyYhPuUVuLGp62OkyXzW7LAm+bYW/IXnDctui53Mn82Hck/cHHUXCklxoyQAFvPPHzun5krrWCtvf4pndWOMbEUs/sH8Vw==" + "bytes": "oWx3bmZzL3B1Yi9kaXKkZ3ZlcnNpb25lMS4wLjBobWV0YWRhdGGiZ2NyZWF0ZWQAaG1vZGlmaWVkAGhwcmV2aW91c4BodXNlcmxhbmSg" }, { - "cid": "bafkr4ienirlkygapnwl3sghfweqvliryz3ljip7xsebi2ojowksg7hkr2a", + "cid": "bafkr4ibhabqoafmrp6hwfgr4t2nhuaubcdmiyj5gabmmb4dfixikm5onyu", "value": { "inumber": { "/": { - "bytes": "GFMkNG0zC5/viOjhdJIVS/w8ISMFLO90PAG33K8hPF8" + "bytes": "wTIY5Lj52cZAPjN2QxQb04Tvs5ben9hjXKIVD5UpCeE" } }, "name": { "/": { - "bytes": "YYged5yfTUvukA6Igm4LJvi1QdfAFL6rmt6OjbSnm0c9gIFQc+ayqL5ZKuXo8s/uka52sObCFFI39D1SexJKvM5Avt0YTgtRIB04JgQ+TJqe6KsgHXUXE+CIfvGWZq5ySlmCkn6WU4EZ7Nv8P8o0zUrpveFMM32zUFyBhZwMRDD0AUcGnI8UYTc7kqWODFo2a9FFWAc9imbI6ywZukHu7/u6Tt7PxOwF/Bp4wLHLfa90ZZ6UcCayz3DbJqqTXVVakObCheXVQ6MEKmpFlNYbXzkTm7TUhc3EJ1+WXLd96lvnnuhP+Zy6Vo64RnI6JjyQH7M4ln3yc2O92Qt6h17S1g" + "bytes": "WzUFTg2et93G6urhSL2Viqdf49sAgAv0vv8SE1SgzA2V80ukVHaZR6+PcbbkvJa7kqRzKbUJ8si3pckU/2L30Kmr+JVM38v6cQAT5N0y9KiTmXX6EyUIkQ5r+zfH8xybEhJ00mPJbRpB76OgZ24z9a1VLWQCcenR6GsrPWUkU94p3r8XBQtIBni/uHjKlpFvR/Csq+pmoBqK5qOYaDsgWn6nV8pgMIIRFifu6S7bw9SSMtRUrW/FFfcu1GjRHeq7UV71wyUGGB2gpC4Ekm8Y/tri1S3zHbLnh54ck+tjUZM0MMRSWnL+FvwXcYKaxfRmxooi0Jajw3zF+xGdW8hlnw" } }, "ratchet": { "large": { "/": { - "bytes": "DHT6+9qQ1z3H5YIAYJWRyiuWTJ2Fawa9cSd4WbJskqY" + "bytes": "0Xcz7RLjVmm+AzcWeY2i2QLcclCjoJktNy65o58rQDw" } }, "medium": { "/": { - "bytes": "oMjrCrGbBq9hrEhSpPa7wOaVR912bDWsgKYExTG50JE" + "bytes": "Nk4K4fSXXGSIL1hQ0gHHVGKjY/xSLm4fcLv3z0IPI5U" } }, - "mediumCounter": 124, + "mediumCounter": 187, "salt": { "/": { - "bytes": "07rvJ/l9/D/jZTCYmQDIgs36puSrnUzl7eSElSSdt4M" + "bytes": "ONa5zrgjyuV6QSBzqKdagXT6HqmDbOX6b0+TK6spVOw" } }, "small": { "/": { - "bytes": "gKe1aAXWaK7CiWvt1+O92sF+BqIN9Tz6xHYsKqt0x1k" + "bytes": "GW6XCdcS0jHPZikFzLxI+KfsQ7zW4fZDxUHoah+l5hU" } }, - "smallCounter": 153 + "smallCounter": 184 + } + }, + "bytes": "6bOExcGiQkZF4QyssM3SpoTIO8gh00SnOITC2x432WLvaLerZ0H3599c/bYp/fPktSx5qqFpyqXdee0/QXn7bU3f80Bi834OuBmeZThhTaziZUwJpBk6Iuo3eVCb7BEIl+3itdi4ZVI2wE29caepXpbeKkCAG2Keg0dediZNRE0+MJg/tt1yEMfmlZhrj1D8ZWAY/lzzzpvQcL4fIfXtpXZ4PmBa9nanhEVAdHomvipPsCTn64Sd6lJtfl/ZX+V95PIOJDltdS0plOSSRuG/MR4lLS4EavQU6dRoRiyQl+kzElS+1jr3HdnQwicFPRnBUYc0qtp6lACewo9wqK0cMKvd/ddU5P52tPCXYPOVFEYdcXI+O7ZhBxt5OAU9eeyFSZvtBp3dMSkk2+JLjuIcgmiYlZrkGMQ2Eweh2vwVou5mJ8PkC8gswKEKJNGQ50itgOHOkArlPV82pwjjhZRudZ5iK9O8iw6KqX4zA2By8LkkcYCPquIBD3iiUL3AQ3UlMzCNU9OwIykhFcHsw46fZ0UyODs3H9PWtIPyuIGask5gDaygOv/ydLzeki49dXyNXD6xTDMDZBn+fag8r3OseJyx829mk9uohNygPd9628JRUVpADJCQPabCPQkq2dIuLpJ7+1IHuePXlcp412BYf3xz2d2fRNEbJNQPE9KBT5fmqt11IzMcAw==" + }, + { + "cid": "bafkr4idllpieugl4biudvfi5qpw3g45pagbmifo5xzmrmhelvq6bm63cqa", + "value": { + "wnfs/priv/dir": { + "entries": { + "videos": { + "contentCid": { + "/": "bafkr4ibsfetuv2shlfs6lhpr3s3xzcczizrni25taxs6cf5ik2ul5nirpi" + }, + "label": { + "/": { + "bytes": "yQGatUO3pB+M+/GgYGbsHjG2uEBQiVWnu1/SkujoD0Y" + } + }, + "snapshotKey": { + "/": { + "bytes": "IG+kr1DmwFUNkhYAoCWczPt9zGIu9mTODMovJXO2JDg" + } + }, + "temporalKey": { + "/": { + "bytes": "BfXAsCDWBrqgeSWmPiIo2zxJ9nX5c6DnEJpzf1ouUXh+scPQemfBWQ" + } + } + } + }, + "headerCid": { + "/": "bafkr4ibhabqoafmrp6hwfgr4t2nhuaubcdmiyj5gabmmb4dfixikm5onyu" + }, + "metadata": { + "created": 0, + "modified": 0 + }, + "previous": [ + [ + 1, + { + "/": { + "bytes": "EuBCdAHvQg7ypzevZrDVzAC6sTERuq6QuEqwr//3CT6cGilB9LcnlV/QZvE35u92EBXxFvZrcIQ" + } + } + ] + ], + "version": "1.0.0" + } + }, + "bytes": "lGkOYRCu2AOX6pLROoK4EeP9HdsoYT+ezxC7LvMo7B3FY4atKGO3On6ZTil03oMpMZqhK3F/1qJiGPXAfzmFSfA0plB7xzbeN7LI1t9it5PNH6lGr4APeI1LhvSknOZUPvPqEU36MRgf1Nx4MTaqCAHkAytlrgWuMEec6Y+mCsgHVyFbK2j1oSonAujJaZqYqVzTXzBfULzK21NNus7D8qqzQboAJLq8HngCr9XtL6bap+7dDZKI34A+eXDP44JEeZEXQmQZ6rx5N4Milt3lp1aIVYRkFTaQb8dwKMjjAQY9LCEPClSowenhfj9FLgK8MDqxWLx9ugta6Zeacg/Xe+YJXT7SoY2csqoYbTlMvzLudK8B9UHFgDS9yKfjRg1Q3ixR5YOz/tBL/I2pqVfD2S8FN3D0EArCxGgG2kVU7b3QpHCuJj6zC92zf71SWvDRPiP2wY6jTlKqAbah/IqPKr4/ZgGIgYTDn9GehHaBXt3ZgYBgKYd0Lyet5vmq+nyFcN28EN47Sj2vfI54zwmxXYxpyRyQIDg75GrWb+1UI8PBA2Ua/WJxGUbf0reE" + }, + { + "cid": "bafkr4iahin3hzsm2b73deeogau3shuwtrq2k5ygczipcdcnvf2xoopd64y", + "value": { + "/": { + "bytes": "7hBOK8/9N8LTsZ07nmvXlVYeJuxJi6xLyf90IOlERtDf7KJmA5w7490h3we6S+3wcNH6mxurxELKkcNQSqhWlBZ20jssL4D9iUEtHg0s3AtSbcPjYvPv6uhNGHcF/aNMyokLO6n3NZ1hRdNy7dzxBmAmx8P1hL+2U5IywQCatHraSS8AiDZS12nktfSUbo7PTfW3IfbtN+rAR5Y5/ZwCbBjqzPvhzmdSjXDQ+7m/Ady9GS/AeOnpp9oH6MbdcWT1YwuJXO2e8+hKSmIEdPLpJmgdqfw9NpqUVHenABPjPiUtUScDRx1AiRgwxQKXo3yLyGHTk1GQRCQuvZc21kwBwwnP3RMqXiK8ZkUjiMM11RFeee71mHFcvgzC5RT0YkvDWalR6EoLzs6AV0OX8v4pFhmzVZyByiYcGkX+fb3qnuArbl3RpHLH+cO1iHScvCgqLT9M0TtIB+Y4UiOE6gwZJR4X0Pwdm1PxcYYcBe9Lo4Ln7A+7knWMp1j3SJaFGCu+WCAqwAv6RVwHR3RkJGVHHgFUgLKKCVQu/obEUyjh+RPw8AabldmG62lLzTvamKKpb0xUEG44zApJqlRlztQ0gZ7bSXSE6Wuy52RMB4geEvi6n9vByG/9WsGZZU7AW5JCCVj1BSDc95WZjb0xRw/o+l8tglC6YV1OQa54Dk+/LRK96xudFBHzAA" + } + }, + "bytes": "7hBOK8/9N8LTsZ07nmvXlVYeJuxJi6xLyf90IOlERtDf7KJmA5w7490h3we6S+3wcNH6mxurxELKkcNQSqhWlBZ20jssL4D9iUEtHg0s3AtSbcPjYvPv6uhNGHcF/aNMyokLO6n3NZ1hRdNy7dzxBmAmx8P1hL+2U5IywQCatHraSS8AiDZS12nktfSUbo7PTfW3IfbtN+rAR5Y5/ZwCbBjqzPvhzmdSjXDQ+7m/Ady9GS/AeOnpp9oH6MbdcWT1YwuJXO2e8+hKSmIEdPLpJmgdqfw9NpqUVHenABPjPiUtUScDRx1AiRgwxQKXo3yLyGHTk1GQRCQuvZc21kwBwwnP3RMqXiK8ZkUjiMM11RFeee71mHFcvgzC5RT0YkvDWalR6EoLzs6AV0OX8v4pFhmzVZyByiYcGkX+fb3qnuArbl3RpHLH+cO1iHScvCgqLT9M0TtIB+Y4UiOE6gwZJR4X0Pwdm1PxcYYcBe9Lo4Ln7A+7knWMp1j3SJaFGCu+WCAqwAv6RVwHR3RkJGVHHgFUgLKKCVQu/obEUyjh+RPw8AabldmG62lLzTvamKKpb0xUEG44zApJqlRlztQ0gZ7bSXSE6Wuy52RMB4geEvi6n9vByG/9WsGZZU7AW5JCCVj1BSDc95WZjb0xRw/o+l8tglC6YV1OQa54Dk+/LRK96xudFBHzAA==" + }, + { + "cid": "bafkr4ieeumiq23ogcl7447hxztp5wxh4a7biumjvzpft56vduqjusq5wsm", + "value": { + "/": { + "bytes": "e7jkHFUGlv3veP/28BO7NGJ7pQyi1jtux67NqxpxYiDeYXYnuRl8LKEZpuyZrhRoRJnuh0jdlPrw0dZbrVl70RBKYRKls6C3UiJBUqqqGYdJAfG8uxk83wEP7ZMsHel5qY8Ok3R3gCmqcnuQyNOIIbFfUVNrE5zIJLGepANp87acuaKX5QcelXWlRSIU8N4d45RIp6uMpT0eMgb6t8T8U8lZLuo02x1RrQ" + } + }, + "bytes": "e7jkHFUGlv3veP/28BO7NGJ7pQyi1jtux67NqxpxYiDeYXYnuRl8LKEZpuyZrhRoRJnuh0jdlPrw0dZbrVl70RBKYRKls6C3UiJBUqqqGYdJAfG8uxk83wEP7ZMsHel5qY8Ok3R3gCmqcnuQyNOIIbFfUVNrE5zIJLGepANp87acuaKX5QcelXWlRSIU8N4d45RIp6uMpT0eMgb6t8T8U8lZLuo02x1RrQ==" + }, + { + "cid": "bafkr4if5i2ranqh33wx2ztludcfuvkk2wak56dz4iuu23pi3hfna4aqboa", + "value": { + "/": { + "bytes": "aGVsbG8gd29ybGQ" } }, - "bytes": "MBCyzB+5fh+QOIE5bqmWzf/Net/g5PTKHatf0uABkBepce+7JmPMAK82OtiSvSYHYnECeLpFF3pNfl8Md3Ta1QtOszNBrf997XgYoMIA8a5Yq6oXKpKS2lKpoGnGXvkJT+7U817d40YHu5RgSW04PtVYvIPjWHepB7JJ5PJ3ynh2zx6kYh+4oW4qCi8IVpR0EAK/xCK2UK3J7RwAIBMaW7JchQm3w+FfxrTCFD5Q59uUduWTPaFs3nkSY8MijO24guC31k74GuNdr2yjXZhwbcZhHKxPTUSSriWABNEt85GhRMldLSFC/q/LS3/V9VqdlK8FV6qfeVFqpe2z+oMkDlOaz3zWc0tR4VQyQ92s7hh5RqBDs5x6fMvLczCQRAQfm7PoEyUU3ER52cuuC7PzLJSp1gOAelNapfWrMqZP/4JYB2K2IipJqqPXfXGtLF31r/MS8El3K9kl1CSbalRmRmdcbBoUmIJmnkLMTkGFiZSmMGrRWicZgGMY/UJPoJF0rKdXJL9+lXn0941i/9dM/dhaOiJI5vJKdv2C0OPWB38xbt3uwqOKn6IhZSDUkSzrlSvdh6svojvqxvgBTQANTn5hLC9+712fruLZ3xQE2G6luj7Z0OkUxyfdg1LMq3Q/bmfvtyCRQJv/M0nCcLRcGoGB6Ukaqb7b1LjaE3aHpZBo4dCmE39ivQ==" + "bytes": "VgFiuyjwLxAVo9zsONyk/HNTWymLC4A3EEuSozh0iYFkMP8FTIeeG/VSDYTUaa7kwqcP" }, { - "cid": "bafkr4igujtuwoy7dfdfwyv7izywttixyj5ca2lnzgbjjspx5sds6zs6w2m", + "cid": "bafkr4ibsfetuv2shlfs6lhpr3s3xzcczizrni25taxs6cf5ik2ul5nirpi", "value": { "wnfs/priv/file": { "content": { "external": { "baseName": { "/": { - "bytes": "hNDkU/HKn8pujw5HfKcBAuyx1r4wnvl0I9BgVr3hVQ+P69Cr4r8fIdMmGUId2cyVOWLZDh90HMkTeVWjbSypm8OhX+CEH4C9Ka/7G66hiZ87I6PxifVpZFwz1+ZpiS3YddG6aGwG32OxsnK+9FQd7Tdrp7orAQfSmgxC4ByUcpKGdjC/0RwUozX4S26qGl41cyXTZ/xzlkekdeAV2f6KWcp8Hw5UKtuMczYRsRhWSUvBAC2sT3A8uqy4INqoptDn4oCzjWGdzfszG12aFIKZuVE5ruXHOy2Zx0+zu7SqBbzYN3NLkog3orlPb+FRrXqIzUmiUqTA60Rphip3M0gkwA" + "bytes": "vQ54kY5pL8AvwnQs6JRG2n8jd/Z3tBSiBNlGvN0kafeMqx4FS9k+4nFNQJXIkZ/WhFRMS6GbkCQSVrPXYzdvjM5Q03Opb7UvzXIuk4QbGFkFBFoQaxmbHmOnVGbpwFLk9qpVos8IdeRuty21U7Q6Ls8YL56VZy4DSdqR3ZBthhrlzPvtK3mdvBeX649NvSatbTdfCa7RvLpaJ92q95Mb7GC0jq740HLINUdcLOI/gcn2rXV8dJvWAtp1kCvXXlZDbZv+3+ZNyu2ajXhIHsxDbUyPSDjpcWdQp8wqAR6Ul1ckOcNbqZ4FsJhxdAi31pQjAjcLFNNE2Mg5YHEtDBOJVw" } }, "blockContentSize": 262104, "blockCount": 1, "key": { "/": { - "bytes": "VgFiuyjwLxAVo9zsONyk/HNTWymLC4A3B37cb+IrIPo" + "bytes": "jU5e4dCLQ6PYBFe/CeCVei+SK1jnlkbgKiUpy3yZ494" } } } }, "headerCid": { - "/": "bafkr4ienirlkygapnwl3sghfweqvliryz3ljip7xsebi2ojowksg7hkr2a" + "/": "bafkr4igxwg7sdlh7no6xlvgkyeal2h7nt572ztxz5keeckua3aadvhedr4" }, "metadata": { "created": 0, @@ -186,103 +274,46 @@ expression: values "version": "1.0.0" } }, - "bytes": "l+qS0TqCuBHj/R3bKGE/nq2YabOS+h+dhF5XqTCHDolfBEFrOhsXkGHmPgt75jemuQIAoElYcMBBMA2Qb/0gaPgIXMzkw4lhBm1OivyvX4zl1XelII+QVrVk701SFnMO8fMlNiohneI5D4S0sW1utyKYeJRRSjCIefCx5hbRERoAZ6Op2Z5xO8reweGkpQlmYK6jg54DsEAiJDEU1C1uznMQdc/r0maNOvJMReha4UoA3MrYaZesz+CN+cTpLD6DWWiuaTSdzlwovNEq+nRHN4dAzdUi86lmRhc2JrqmqoGwStrzsRbQT3UsxAAZMLa8M8S5LyufyhKR3+ViNcFBFlHtCXJfaaO1lMZRSwLeVeXfycYBnVLQDOUoj4/ILwBFq6ot/tJgHyqIAST3NVYYhu4bKeezsOTgWkbOEWYwRV6Tke9BreSrJMYWTgkXqZ+73quknRVkdLfsbj7TZChfECqGUnyN0jH5c30VTv9vNj0u2zi3vd604eKl6uFgXwyEQadC0xCW5K+z3OoKiRB+XnmWJXEXNB5OI+2gPGfFbDuHU8gE7IIfP4pdgv1QP4BKQyBdXPxR+oEQk+ovhd43vm1mOISXNlWSUK/XdRNfX6lNRTivhaI8yqfdVraNYz/jIjLhvp+UOw5k3a4Cb5J0w8UxOlJOJQ/GK3LbJm492XofORvDgRd7dQ==" + "bytes": "B37cb+IrIPpyzAtM7pjd6qWgYmMRNV2tU+3ZCp6e/xGfkyaJ2+ocfd9mRBLoZthTe+8PoJbnPY0/1sTbTn0G8BDnG3YW3WnUW46X2RIyrEQwLv73cAEy/YUuW4tneY8QsHRQNILOdhhCilgnLQNPQ4xdiMpBE3kmvcu4C52bkvS/q73uDFflDNIjuuNZ4Tjj98NuLXdBD0PcJR+AKxFno17EaDl72EtFvX6r5ZblLsMDVVsxnRKIY4Q4v0bpUYJN5u4zG/LU5V6kDP05rsWUvfQZsGZ7s662dTr0EmD9iiMVBTTLMR7ociKVfRKwwklCg8DHXGO2UCNZHHnbTvTq63x0utR9dgUjE9q2kZ20svsA7epx63dWgOzXEXRrbP4PavG6uTYV7SRdy9AKoen9O9D3s90sPMPektSJqLTtGO66DaL2StuzIRj2l8C9JGj7Fw4xSLe9FJXKz9+3tPtrvlYkd2iMQpBt+4hBB4+TRlEFqcxjF67q3nr+oMl66VNkw0gae2Wzs6izmjMS+YrWEelK0LxscPy4siHcxDyvzPHAHQub3R8Ai9pEz7VUHFrGuvl84QyR1EdHqz7HvRsCghwXe5cgjOh9S7jptuid/kCf63U6S/nlC110u6u6PP/KPDrRZFn4k6dbqbv3hgPRGhezC1zoGUyBQUBvj44wVSO1gSAQV/rxyQ==" }, { - "cid": "bafkr4ia6aehlkv3fe3tyeqladznq66uaebxilra2jfgr2mai7cb3wimbbm", - "value": { - "/": { - "bytes": "aGVsbG8gd29ybGQ" - } - }, - "bytes": "cswLTO6Y3eqloGJjETVdrZRpDmEQrtgDhDjqmfEzoUGMuPgNRRziUmbll/NejAX9EL7r" - }, - { - "cid": "bafkr4ib2p54jvv2bxpo3cp5zxbreqzinyz5ruhr2ssf5db74o27vvb6rwi", + "cid": "bafkr4igxwg7sdlh7no6xlvgkyeal2h7nt572ztxz5keeckua3aadvhedr4", "value": { "inumber": { "/": { - "bytes": "BoN1qGYHdYlEkw+RiyTTdczVzcOJ4jysJ7ZKPATyUFs" + "bytes": "me0X91Is5qDx06gvyQPXcPK5uv4JFHhvz6uTH50unZ8" } }, "name": { "/": { - "bytes": "UxI9IKZpzhgA2jMAOdrLG64FHRlxy0YJdH7fX+SS13xpdzLQHftARaK5qAkxflHKHa0Ch/dGqmRwiDM5aK21nux6ha4vAlg6UpzbZpQWVS3mlBPA/eqBfjwHN5Uf0QOW66eSpnjV+guIE+wigxZJYllzSp65EeR0XlRmXn7pMbo87Q8pxGX+r8zM7x5BIcIA9FU28n2RBYpOMWeH6qwZ8K/CJ51j0FuE6eSyXW8SFSj5WDd38OMsM+W4Pg1X1PfVayXFzPBqhPUXF2LPU1NkjgeJPqzdPX3D7mL3RuGTFU2FE6ifn+cZRh8iby3l6bK7/vH5iw2C6sF+lO5fdgV77w" + "bytes": "j6YzwzpX6DUWE/zrM4KvY5WFhBOSjdry0gCan5UNBZ0hPAV1Oz6oiOfv8Kg2pAb6Y/NQSqtYfLSfNsFWb6gKK/GPklCVw5cOAd38HZh2E4dQopSFBfU9TJzbVfvYuLuoA+RsQDcXzuRQi52TLzaO4DC+z84JqNt0BlR7SSZE96ca4FQZCK3MKr7e1EEv2gQfla9UNenvCIUBQdJJZsxfPic6EUT+/2tg5VrjFCgiXMdtgrOnHZ4cSjHeG5L/BW6BNNjbPsg2sigB6KVjZp8fzLVZL3lk5/8e6Fgi3P9jW/SDVxJi+8EKfqz+qRzhRTTxWcpjRl2RcDHNIxsHr4Xh1w" } }, "ratchet": { "large": { "/": { - "bytes": "666g96bE4qyOoxxUywVa7Q/epb3Wp1aXaEo2kszzeaU" + "bytes": "Bt+ey5Boc0Fgx4R78mg9A5Q4DJeZYVzfhja2lwm97Bk" } }, "medium": { "/": { - "bytes": "bz3wwx+gSuhInYJgnwfQJIlzeJZ3Q1fm9CSlJtAUQtg" + "bytes": "3AkDRYPkuG+x+Mp/miOAJ1yS+9W8Iy+wcue3MK43D6g" } }, - "mediumCounter": 154, + "mediumCounter": 168, "salt": { "/": { - "bytes": "vEAU2QYIxP/ZnOkdA+IXGbGjAknK435hCXeEqKUiJgk" + "bytes": "0Ee3Zec2xWFCuo0RnICxnKIJBmcu20LvFKPh4lLjzoM" } }, "small": { "/": { - "bytes": "r7c1yehJQZypOvqs3sh4zGZvYPqoZCEud+jzi78rTxQ" + "bytes": "AVPlenVmZaC/go5ocjSdkW9R+KorleUn8NIzESoQMZE" } }, - "smallCounter": 51 - } - }, - "bytes": "LVULMQysvfjOGXf9UwRWPvIjKUGQP8uULdhZltI7eansOrY2KIgQbfJIFa5xv7bu6EDAZ3PHxKPIwZsjKqS9qXiECOmwPkGX/KLYffAJq9U2+IryKb7rSSieLhguHRfIcPjVZkgySK7TtJPXs83js9vsLeZaphSOGdSfBUp2rIVG+maHlrLaVh801ic6NpnSi4HwILb9D//yZFHLIM6xLaYH/oStyywk2FecY0YH2tHVg6MBzyJUe8RvfXHp7xoAIIZJ64JpvHvCyjh/sxnvGCaMCfJwXoJUHf3qkkw8EYHSdIRiA5sTQA3MLr+q3phWUZMKP9Wlzau7BhYZCpIGBjnzYa8Z4zO2y7YX7f/P0+wH5yMOx5uHl5rO1xCzyVJd1tRENjnzKQ9JqFRC1v4F3ONrUeO5ZJfnm3BiB9n7XE00K89DxyeV4tzIMKPu8H/L8LbHGJZ9hxY/XEjEA2JPIDL6tUCLozXCx7KiJFyS41HobN78kUcd1433nGgJHWEnJKpOmTVU0tkQAN8N2Qensy5ENfNHDdNDZsPn3Zly2pzLmXt6PbklFFh/ODc3tBDnEvT0FUGjH7j0GUvUuRjxBAk4s7ANeoguWv8Ac7G9twnMQBktbQoXMhQNif0IKMTXQdtsw1ax4e05wEtRGQcbu+vHikUw3A3e71Pd6gsFGfTAn/CkqNTswA==" - }, - { - "cid": "bafkr4idzdwor5tagoubrsjgmoqei45pl3gz77e47wegx47p6k4yeoefhyq", - "value": { - "wnfs/priv/dir": { - "entries": { - "videos": { - "contentCid": { - "/": "bafkr4igujtuwoy7dfdfwyv7izywttixyj5ca2lnzgbjjspx5sds6zs6w2m" - }, - "label": { - "/": { - "bytes": "mHNzb3ThL9fcezvNcPrUEAjmd9fmzy05clIrQ9qeQDc" - } - }, - "snapshotKey": { - "/": { - "bytes": "5mrHzS9XJ7JY789rEKALx03jXXWwLZKgiXWfCqIcG+0" - } - }, - "temporalKey": { - "/": { - "bytes": "fP0Fyq2RByxJiVBVWdRW9F9w6jTRyoAMUxO0x2lRXAu7g4m2eBH8NQ" - } - } - } - }, - "headerCid": { - "/": "bafkr4ib2p54jvv2bxpo3cp5zxbreqzinyz5ruhr2ssf5db74o27vvb6rwi" - }, - "metadata": { - "created": 0, - "modified": 0 - }, - "previous": [ - [ - 1, - { - "/": { - "bytes": "RplBHdBsKUICnZekEjRhbwkFd/CE58AZ53QWqhP2/fYlafOTs/DFXIvYjD+YEFV+T7/1F+gvctc" - } - } - ] - ], - "version": "1.0.0" + "smallCounter": 20 } }, - "bytes": "kb7xq2JWBcloxy9TEqx3qo6JWN3bqJsVZp6Wdos3zPRzgB2kG8mjI3LbwdRXxhc34/csiusA+GbCCTFaJOTnzJTDuda4OC/du+rn/GTg9oL1eXZqn7ldoS+KRxFVzcxFLJ0elDRbGqIUGyN8YXsChTwt7E3GLm3xg1L7QldPnCdUv9dNUkc9rTPknl7pLdIkw57KY2dDnKsPugNBLjlwgRbdCyHnyFs0hetZ28ZzSphvYFIViAHbjQ5x1I8/lIhSyPcFqftkp0KvahxW315UDVRZy72N+OZwEZni4/ne9IPeUQKX7r38ynXCrzDDa64f0A6YvR7aPNG7AfwcCvrTg2zKKXsPvGhEd/TXtFV3laSrt3kYnkLy2L4ZmL/qa+mjJU8ygNrAM+d7C4LzYbekbkFdb4GXbUwwmnkZOxFD0shwvvDblvV5pWuPpQy7SsVg8/N9luv6CgyBaObn+24avDGV2haoVHd5+ULuSkJfX004wBCPmJLVAFxpf46+yracTIRYAiLN5znbx8Xhgf9+msr/4f4hODMftNEV5XbjBxq7zMHzQSlL3rG4ERB8" + "bytes": "xj9M46nMXNbMdI8ZDIpAKPiaZnLxCiSO+Zj3CVEvd+FDQue8Dd1gPMOc5nXjGP4PBDmsZLILWmQ89g3o0pkyb23LWXhVJFg2CKcMS+MqLwnFB6WevOkZaQWdx/URS4P+CQvyRC5nqJGzUN8+SQCh/IHf/5mKqOyF4UtG3H/BLoX+orKybQW96S/Dv8DA553CXHjMG03qpo1Bm/Uull2HdSDsVbVbH8wTyV9cctG09qEfKFOmY8/65HoMm0Y/4Fo/ouFIKc4sPblJy3D+4mPNsx61/A7kM9LFu1jLLkVZPP8h5pmwRBCCsHn2zfZL/OYNlEADieYqt/dmsbwzVsTbgaBYU8aehY2hscquonyVN94MvUtG3WepaplabK0ANR9aYXoZtCHAd7ODwyPi9KwA+RKLTVcciD8Ocl3EH72IfBbw2baa2TPqUiiTs2lSgysH7AchtNPvO449p9tNxI4tffRkkHAXOyINXoHjw7/E/+sQT69nETnMi6KDiZWg/yPpxRmXjvcPhskVBveFZEY9u5rJCMqxDWIiiG3NeDP6ftS92ZgPC6n7830guk/oUMqgpn99qRR3mb0lWkwZuznwNxpAcTvyWxCuswj1nKWyWh8JCj7p3SkXv8ylKxQt3eSjdnGxiUz+D9qQZ1MoVPBM9AkHQPQgoYK4FTwpOpLSEcZPn7iaD4PmNg==" } ]