From df5b83a30893b91c8670dea5b213dea2658459fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Thu, 15 Feb 2024 18:00:06 +0100 Subject: [PATCH] refactor: Use precise error type in `trait BlockStore` --- wnfs-common/Cargo.toml | 1 + wnfs-common/src/blockstore.rs | 71 +++++++++++++++++++---------- wnfs-common/src/error.rs | 8 ++-- wnfs-common/src/storable.rs | 2 +- wnfs-common/src/utils/test.rs | 18 ++++++-- wnfs-unixfs-file/src/types.rs | 4 +- wnfs-wasm/src/fs/blockstore.rs | 12 +++-- wnfs/examples/tiered_blockstores.rs | 18 ++++++-- wnfs/src/private/directory.rs | 2 +- wnfs/src/private/file.rs | 2 +- wnfs/src/private/node/header.rs | 2 +- wnfs/src/root_tree.rs | 6 ++- 12 files changed, 98 insertions(+), 48 deletions(-) diff --git a/wnfs-common/Cargo.toml b/wnfs-common/Cargo.toml index 88755ea7..d4e8904d 100644 --- a/wnfs-common/Cargo.toml +++ b/wnfs-common/Cargo.toml @@ -24,6 +24,7 @@ base64 = { version = "0.21", optional = true } base64-serde = { version = "0.7", optional = true } bytes = { version = "1.4", features = ["serde"] } chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +cid = "0.10" dashmap = "5.5.3" futures = "0.3" libipld = { version = "0.16", features = ["dag-cbor", "derive", "serde-codec"] } diff --git a/wnfs-common/src/blockstore.rs b/wnfs-common/src/blockstore.rs index 64adfbf9..f5dd08e1 100644 --- a/wnfs-common/src/blockstore.rs +++ b/wnfs-common/src/blockstore.rs @@ -3,7 +3,6 @@ use crate::{ utils::{Arc, CondSend, CondSync}, BlockStoreError, MAX_BLOCK_SIZE, }; -use anyhow::{bail, Result}; use bytes::Bytes; use futures::Future; use libipld::{ @@ -53,7 +52,10 @@ pub trait BlockStore: CondSync { /// Retrieve a block from this store via its hash (`Cid`). /// /// If this store can't find the block, it may raise an error like `BlockNotFound`. - fn get_block(&self, cid: &Cid) -> impl Future> + CondSend; + fn get_block( + &self, + cid: &Cid, + ) -> impl Future> + CondSend; /// Put some bytes into the blockstore. These bytes should be encoded with the given codec. /// @@ -72,7 +74,7 @@ pub trait BlockStore: CondSync { &self, bytes: impl Into + CondSend, codec: u64, - ) -> impl Future> + CondSend { + ) -> impl Future> + CondSend { let bytes = bytes.into(); async move { let cid = self.create_cid(&bytes, codec)?; @@ -94,19 +96,22 @@ pub trait BlockStore: CondSync { &self, cid: Cid, bytes: impl Into + CondSend, - ) -> impl Future> + CondSend; + ) -> impl Future> + CondSend; /// Find out whether a call to `get_block` would return with a result or not. /// /// This is useful for data exchange protocols to find out what needs to be fetched /// externally and what doesn't. - fn has_block(&self, cid: &Cid) -> impl Future> + CondSend; + fn has_block( + &self, + cid: &Cid, + ) -> impl Future> + CondSend; // This should be the same in all implementations of BlockStore - fn create_cid(&self, bytes: &[u8], codec: u64) -> Result { + fn create_cid(&self, bytes: &[u8], codec: u64) -> Result { // If there are too many bytes, abandon this task if bytes.len() > MAX_BLOCK_SIZE { - bail!(BlockStoreError::MaximumBlockSizeExceeded(bytes.len())) + return Err(BlockStoreError::MaximumBlockSizeExceeded(bytes.len())); } // Compute the Blake3 hash of the bytes @@ -124,45 +129,61 @@ pub trait BlockStore: CondSync { //-------------------------------------------------------------------------------------------------- impl BlockStore for &B { - async fn get_block(&self, cid: &Cid) -> Result { + async fn get_block(&self, cid: &Cid) -> Result { (**self).get_block(cid).await } - async fn put_block(&self, bytes: impl Into + CondSend, codec: u64) -> Result { + async fn put_block( + &self, + bytes: impl Into + CondSend, + codec: u64, + ) -> Result { (**self).put_block(bytes, codec).await } - async fn put_block_keyed(&self, cid: Cid, bytes: impl Into + CondSend) -> Result<()> { + async fn put_block_keyed( + &self, + cid: Cid, + bytes: impl Into + CondSend, + ) -> Result<(), BlockStoreError> { (**self).put_block_keyed(cid, bytes).await } - async fn has_block(&self, cid: &Cid) -> Result { + async fn has_block(&self, cid: &Cid) -> Result { (**self).has_block(cid).await } - fn create_cid(&self, bytes: &[u8], codec: u64) -> Result { + fn create_cid(&self, bytes: &[u8], codec: u64) -> Result { (**self).create_cid(bytes, codec) } } impl BlockStore for Box { - async fn get_block(&self, cid: &Cid) -> Result { + async fn get_block(&self, cid: &Cid) -> Result { (**self).get_block(cid).await } - async fn put_block(&self, bytes: impl Into + CondSend, codec: u64) -> Result { + async fn put_block( + &self, + bytes: impl Into + CondSend, + codec: u64, + ) -> Result { (**self).put_block(bytes, codec).await } - async fn put_block_keyed(&self, cid: Cid, bytes: impl Into + CondSend) -> Result<()> { + async fn put_block_keyed( + &self, + cid: Cid, + bytes: impl Into + CondSend, + ) -> Result<(), BlockStoreError> { (**self).put_block_keyed(cid, bytes).await } - async fn has_block(&self, cid: &Cid) -> Result { + async fn has_block(&self, cid: &Cid) -> Result { (**self).has_block(cid).await } - fn create_cid(&self, bytes: &[u8], codec: u64) -> Result { + fn create_cid(&self, bytes: &[u8], codec: u64) -> Result { (**self).create_cid(bytes, codec) } } @@ -186,7 +207,7 @@ impl MemoryBlockStore { } impl BlockStore for MemoryBlockStore { - async fn get_block(&self, cid: &Cid) -> Result { + async fn get_block(&self, cid: &Cid) -> Result { let bytes = self .0 .lock() @@ -197,13 +218,17 @@ impl BlockStore for MemoryBlockStore { Ok(bytes) } - async fn put_block_keyed(&self, cid: Cid, bytes: impl Into + CondSend) -> Result<()> { + async fn put_block_keyed( + &self, + cid: Cid, + bytes: impl Into + CondSend, + ) -> Result<(), BlockStoreError> { self.0.lock().insert(cid, bytes.into()); Ok(()) } - async fn has_block(&self, cid: &Cid) -> Result { + async fn has_block(&self, cid: &Cid) -> Result { Ok(self.0.lock().contains_key(cid)) } } @@ -213,7 +238,7 @@ impl BlockStore for MemoryBlockStore { //-------------------------------------------------------------------------------------------------- /// Tests the retrieval property of a BlockStore-conforming type. -pub async fn bs_retrieval_test(store: impl BlockStore) -> Result<()> { +pub async fn bs_retrieval_test(store: impl BlockStore) -> Result<(), BlockStoreError> { // Example objects to insert and remove from the blockstore let first_bytes = vec![1, 2, 3, 4, 5]; let second_bytes = b"hello world".to_vec(); @@ -234,7 +259,7 @@ pub async fn bs_retrieval_test(store: impl BlockStore) -> Result<()> { } /// Tests the duplication of a BlockStore-conforming type. -pub async fn bs_duplication_test(store: impl BlockStore) -> Result<()> { +pub async fn bs_duplication_test(store: impl BlockStore) -> Result<(), BlockStoreError> { // Example objects to insert and remove from the blockstore let first_bytes = vec![1, 2, 3, 4, 5]; let second_bytes = first_bytes.clone(); @@ -261,7 +286,7 @@ pub async fn bs_duplication_test(store: impl BlockStore) -> Result<()> { } /// Tests the serialization of a BlockStore-conforming type. -pub async fn bs_serialization_test(store: &T) -> Result<()> +pub async fn bs_serialization_test(store: &T) -> Result<(), BlockStoreError> where T: BlockStore + Serialize + for<'de> Deserialize<'de>, { diff --git a/wnfs-common/src/error.rs b/wnfs-common/src/error.rs index c75cf329..6db9b2e3 100644 --- a/wnfs-common/src/error.rs +++ b/wnfs-common/src/error.rs @@ -16,9 +16,9 @@ pub enum BlockStoreError { #[error("Cannot find specified CID in block store: {0}")] CIDNotFound(Cid), - #[error("Cannot find handler for block with CID: {0}")] - BlockHandlerNotFound(Cid), + #[error("CID error during blockstore operation: {0}")] + CIDError(#[from] cid::Error), - #[error("Lock poisoned")] - LockPoisoned, + #[error(transparent)] + Custom(#[from] anyhow::Error), } diff --git a/wnfs-common/src/storable.rs b/wnfs-common/src/storable.rs index 7ecb4141..53673936 100644 --- a/wnfs-common/src/storable.rs +++ b/wnfs-common/src/storable.rs @@ -92,7 +92,7 @@ pub trait Storable: Sized { { let store_future = async { let (bytes, codec) = self.to_serializable(store).await?.encode_ipld()?; - store.put_block(bytes, codec).await + Ok(store.put_block(bytes, codec).await?) }; async { diff --git a/wnfs-common/src/utils/test.rs b/wnfs-common/src/utils/test.rs index c173da73..3bbc44e3 100644 --- a/wnfs-common/src/utils/test.rs +++ b/wnfs-common/src/utils/test.rs @@ -1,5 +1,5 @@ use super::{Arc, CondSend, CondSync}; -use crate::{BlockStore, MemoryBlockStore, CODEC_DAG_CBOR, CODEC_RAW}; +use crate::{BlockStore, BlockStoreError, MemoryBlockStore, CODEC_DAG_CBOR, CODEC_RAW}; use anyhow::Result; use base64_serde::base64_serde_type; use bytes::Bytes; @@ -113,22 +113,30 @@ impl SnapshotBlockStore { impl BlockStore for SnapshotBlockStore { #[inline] - async fn get_block(&self, cid: &Cid) -> Result { + async fn get_block(&self, cid: &Cid) -> Result { self.inner.get_block(cid).await } #[inline] - async fn put_block(&self, bytes: impl Into + CondSend, codec: u64) -> Result { + async fn put_block( + &self, + bytes: impl Into + CondSend, + codec: u64, + ) -> Result { self.inner.put_block(bytes, codec).await } #[inline] - async fn put_block_keyed(&self, cid: Cid, bytes: impl Into + CondSend) -> Result<()> { + async fn put_block_keyed( + &self, + cid: Cid, + bytes: impl Into + CondSend, + ) -> Result<(), BlockStoreError> { self.inner.put_block_keyed(cid, bytes).await } #[inline] - async fn has_block(&self, cid: &Cid) -> Result { + async fn has_block(&self, cid: &Cid) -> Result { self.inner.has_block(cid).await } } diff --git a/wnfs-unixfs-file/src/types.rs b/wnfs-unixfs-file/src/types.rs index 79c89700..a669a0c5 100644 --- a/wnfs-unixfs-file/src/types.rs +++ b/wnfs-unixfs-file/src/types.rs @@ -38,7 +38,9 @@ impl Block { } pub async fn store(&self, store: &impl BlockStore) -> Result { - store.put_block(self.data.clone(), self.codec.into()).await + Ok(store + .put_block(self.data.clone(), self.codec.into()) + .await?) } /// Validate the block. Will return an error if the links are wrong. diff --git a/wnfs-wasm/src/fs/blockstore.rs b/wnfs-wasm/src/fs/blockstore.rs index bde0fe96..2e930a23 100644 --- a/wnfs-wasm/src/fs/blockstore.rs +++ b/wnfs-wasm/src/fs/blockstore.rs @@ -7,7 +7,7 @@ use js_sys::{Promise, Uint8Array}; use libipld_core::cid::Cid; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen_futures::JsFuture; -use wnfs::common::BlockStore as WnfsBlockStore; +use wnfs::common::{BlockStore as WnfsBlockStore, BlockStoreError}; //-------------------------------------------------------------------------------------------------- // Externs @@ -50,7 +50,11 @@ pub struct ForeignBlockStore(pub(crate) BlockStore); //-------------------------------------------------------------------------------------------------- impl WnfsBlockStore for ForeignBlockStore { - async fn put_block_keyed(&self, cid: Cid, bytes: impl Into) -> Result<()> { + async fn put_block_keyed( + &self, + cid: Cid, + bytes: impl Into, + ) -> Result<(), BlockStoreError> { let bytes: Bytes = bytes.into(); JsFuture::from(self.0.put_block_keyed(cid.to_bytes(), bytes.into())) @@ -60,7 +64,7 @@ impl WnfsBlockStore for ForeignBlockStore { Ok(()) } - async fn get_block(&self, cid: &Cid) -> Result { + async fn get_block(&self, cid: &Cid) -> Result { let value = JsFuture::from(self.0.get_block(cid.to_bytes())) .await .map_err(anyhow_error("Cannot get block: {:?}"))?; @@ -70,7 +74,7 @@ impl WnfsBlockStore for ForeignBlockStore { Ok(Bytes::from(bytes)) } - async fn has_block(&self, cid: &Cid) -> Result { + async fn has_block(&self, cid: &Cid) -> Result { let value = JsFuture::from(self.0.has_block(cid.to_bytes())) .await .map_err(anyhow_error("Cannot run has_block: {:?}"))?; diff --git a/wnfs/examples/tiered_blockstores.rs b/wnfs/examples/tiered_blockstores.rs index 078f42a3..4cfab8d4 100644 --- a/wnfs/examples/tiered_blockstores.rs +++ b/wnfs/examples/tiered_blockstores.rs @@ -16,7 +16,7 @@ use wnfs::{ PrivateDirectory, PrivateNode, }, }; -use wnfs_common::{utils::CondSend, Storable}; +use wnfs_common::{utils::CondSend, BlockStoreError, Storable}; #[async_std::main] async fn main() -> Result<()> { @@ -96,7 +96,7 @@ struct TieredBlockStore { } impl BlockStore for TieredBlockStore { - async fn get_block(&self, cid: &Cid) -> Result { + async fn get_block(&self, cid: &Cid) -> Result { if self.hot.has_block(cid).await? { self.hot.get_block(cid).await } else { @@ -104,15 +104,23 @@ impl BlockStore for TieredBlockStore { } } - async fn put_block(&self, bytes: impl Into + CondSend, codec: u64) -> Result { + async fn put_block( + &self, + bytes: impl Into + CondSend, + codec: u64, + ) -> Result { self.hot.put_block(bytes, codec).await } - async fn put_block_keyed(&self, cid: Cid, bytes: impl Into + CondSend) -> Result<()> { + async fn put_block_keyed( + &self, + cid: Cid, + bytes: impl Into + CondSend, + ) -> Result<(), BlockStoreError> { self.hot.put_block_keyed(cid, bytes).await } - async fn has_block(&self, cid: &Cid) -> Result { + async fn has_block(&self, cid: &Cid) -> Result { if self.hot.has_block(cid).await? { return Ok(true); } diff --git a/wnfs/src/private/directory.rs b/wnfs/src/private/directory.rs index 88c62d0c..f15a6b5a 100644 --- a/wnfs/src/private/directory.rs +++ b/wnfs/src/private/directory.rs @@ -1374,7 +1374,7 @@ impl PrivateDirectoryContent { let block = snapshot_key.encrypt(&bytes, rng)?; // Store content section in blockstore and get Cid. - store.put_block(block, CODEC_RAW).await + Ok(store.put_block(block, CODEC_RAW).await?) }) .await?) } diff --git a/wnfs/src/private/file.rs b/wnfs/src/private/file.rs index dfae4cb0..6920e5c8 100644 --- a/wnfs/src/private/file.rs +++ b/wnfs/src/private/file.rs @@ -826,7 +826,7 @@ impl PrivateFileContent { let block = snapshot_key.encrypt(&bytes, rng)?; // Store content section in blockstore and get Cid. - store.put_block(block, CODEC_RAW).await + Ok(store.put_block(block, CODEC_RAW).await?) }) .await?) } diff --git a/wnfs/src/private/node/header.rs b/wnfs/src/private/node/header.rs index f9dafe7a..fa850b02 100644 --- a/wnfs/src/private/node/header.rs +++ b/wnfs/src/private/node/header.rs @@ -165,7 +165,7 @@ impl PrivateNodeHeader { let temporal_key = self.derive_temporal_key(); let cbor_bytes = serde_ipld_dagcbor::to_vec(&self.to_serializable(forest))?; let ciphertext = temporal_key.key_wrap_encrypt(&cbor_bytes)?; - store.put_block(ciphertext, CODEC_RAW).await + Ok(store.put_block(ciphertext, CODEC_RAW).await?) } pub(crate) fn to_serializable( diff --git a/wnfs/src/root_tree.rs b/wnfs/src/root_tree.rs index 71972c88..4d959a6f 100644 --- a/wnfs/src/root_tree.rs +++ b/wnfs/src/root_tree.rs @@ -311,9 +311,11 @@ where version: WNFS_VERSION, }; - store + let cid = store .put_block(encode(&serializable, DagCborCodec)?, DagCborCodec.into()) - .await + .await?; + + Ok(cid) } pub async fn load(