Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: data format snapshot tests #314

Merged
merged 16 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions wnfs-bench/hamt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use criterion::{
};
use proptest::{arbitrary::any, collection::vec, test_runner::TestRunner};
use std::{cmp, rc::Rc, sync::Arc};
use wnfs_common::{dagcbor, utils::Sampleable, BlockStore, Link, MemoryBlockStore};
use wnfs_common::{
async_encode, decode, libipld::cbor::DagCborCodec, utils::Sampleable, BlockStore, Link,
MemoryBlockStore,
};
use wnfs_hamt::{
diff, merge,
strategies::{generate_kvs, node_from_kvs, node_from_operations, operations},
Expand Down Expand Up @@ -77,7 +80,7 @@ fn node_load_get(c: &mut Criterion) {
node.set(i.to_string(), i, &store).await.unwrap();
}

let encoded_hamt = dagcbor::async_encode(&Hamt::with_root(node), &store)
let encoded_hamt = async_encode(&Hamt::with_root(node), &store, DagCborCodec)
.await
.unwrap();

Expand All @@ -87,7 +90,7 @@ fn node_load_get(c: &mut Criterion) {
c.bench_function("node load and get", |b| {
b.to_async(AsyncStdExecutor).iter(|| async {
let encoded_hamt = store.get_deserializable::<Vec<u8>>(&cid).await.unwrap();
let hamt: Hamt<String, i32> = dagcbor::decode(encoded_hamt.as_ref()).unwrap();
let hamt: Hamt<String, i32> = decode(encoded_hamt.as_ref(), DagCborCodec).unwrap();

for i in 0..50 {
assert!(hamt
Expand All @@ -109,7 +112,7 @@ fn node_load_remove(c: &mut Criterion) {
node.set(i.to_string(), i, &store).await.unwrap();
}

let encoded_hamt = dagcbor::async_encode(&Hamt::with_root(node), &store)
let encoded_hamt = async_encode(&Hamt::with_root(node), &store, DagCborCodec)
.await
.unwrap();

Expand All @@ -120,7 +123,7 @@ fn node_load_remove(c: &mut Criterion) {
b.to_async(AsyncStdExecutor).iter(|| async {
let encoded_hamt = store.get_deserializable::<Vec<u8>>(&cid).await.unwrap();
let mut hamt: Hamt<String, i32> =
black_box(dagcbor::decode(encoded_hamt.as_ref()).unwrap());
black_box(decode(encoded_hamt.as_ref(), DagCborCodec).unwrap());

for i in 0..50 {
let value = hamt.root.remove(&i.to_string(), &store).await.unwrap();
Expand All @@ -138,7 +141,7 @@ fn hamt_load_decode(c: &mut Criterion) {
node.set(i.to_string(), i, &store).await.unwrap();
}

let encoded_hamt = dagcbor::async_encode(&Hamt::with_root(node), &store)
let encoded_hamt = async_encode(&Hamt::with_root(node), &store, DagCborCodec)
.await
.unwrap();

Expand All @@ -152,7 +155,8 @@ fn hamt_load_decode(c: &mut Criterion) {
group.bench_function("0", |b| {
b.to_async(AsyncStdExecutor).iter(|| async {
let encoded_hamt = store.get_deserializable::<Vec<u8>>(&cid).await.unwrap();
let _: Hamt<String, i32> = black_box(dagcbor::decode(encoded_hamt.as_ref()).unwrap());
let _: Hamt<String, i32> =
black_box(decode(encoded_hamt.as_ref(), DagCborCodec).unwrap());
})
});
group.finish();
Expand All @@ -174,7 +178,7 @@ fn hamt_set_encode(c: &mut Criterion) {

let hamt = Hamt::with_root(node);

let _ = black_box(dagcbor::async_encode(&hamt, &store).await.unwrap());
let _ = black_box(async_encode(&hamt, &store, DagCborCodec).await.unwrap());
},
BatchSize::SmallInput,
)
Expand Down
12 changes: 9 additions & 3 deletions wnfs-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,27 @@ authors = ["The Fission Authors"]
anyhow = "1.0"
async-once-cell = "0.4"
async-trait = "0.1"
bytes = { version = "1.4.0", features = ["serde"] }
chrono = { version = "0.4.23", default-features = false, features = ["clock", "std"] }
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"] }
futures = "0.3"
libipld = { version = "0.16", features = ["dag-cbor", "derive", "serde-codec"] }
multihash = "0.18"
once_cell = "1.16"
proptest = { version = "1.1", optional = true }
rand_core = "0.6"
serde = { version = "1.0", features = ["rc"] }
serde_json = { version = "1.0", optional = true }
thiserror = "1.0"

[dev-dependencies]
async-std = { version = "1.11", features = ["attributes"] }
base64 = "0.21"
base64-serde = "0.7"
proptest = "1.1"
rand = "0.8"
serde_json = "1.0"

[features]
test_utils = ["proptest"]
test_utils = ["dep:proptest", "dep:base64-serde", "dep:base64", "dep:serde_json"]
23 changes: 14 additions & 9 deletions wnfs-common/src/blockstore.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::{dagcbor, AsyncSerialize, BlockStoreError, MAX_BLOCK_SIZE};
use crate::{decode, encode, AsyncSerialize, BlockStoreError, MAX_BLOCK_SIZE};
use anyhow::{bail, Result};
use async_trait::async_trait;
use bytes::Bytes;
use libipld::{
cbor::DagCborCodec,
cid::Version,
multihash::{Code, MultihashDigest},
serde as ipld_serde, Cid,
Expand Down Expand Up @@ -50,18 +51,18 @@ pub trait BlockStore: Sized {

async fn get_deserializable<V: DeserializeOwned>(&self, cid: &Cid) -> Result<V> {
let bytes = self.get_block(cid).await?;
let ipld = dagcbor::decode(bytes.as_ref())?;
let ipld = decode(bytes.as_ref(), DagCborCodec)?;
Ok(ipld_serde::from_ipld::<V>(ipld)?)
}

async fn put_serializable<V: Serialize>(&self, value: &V) -> Result<Cid> {
let bytes = dagcbor::encode(&ipld_serde::to_ipld(value)?)?;
let bytes = encode(&ipld_serde::to_ipld(value)?, DagCborCodec)?;
self.put_block(bytes, CODEC_DAG_CBOR).await
}

async fn put_async_serializable<V: AsyncSerialize>(&self, value: &V) -> Result<Cid> {
let ipld = value.async_serialize_ipld(self).await?;
let bytes = dagcbor::encode(&ipld)?;
let bytes = encode(&ipld, DagCborCodec)?;
self.put_block(bytes, CODEC_DAG_CBOR).await
}

Expand Down Expand Up @@ -90,7 +91,11 @@ pub trait BlockStore: Sized {
///
/// IPFS is basically a glorified HashMap.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MemoryBlockStore(RefCell<HashMap<String, Bytes>>);
pub struct MemoryBlockStore(
#[serde(serialize_with = "crate::utils::serialize_cid_map")]
#[serde(deserialize_with = "crate::utils::deserialize_cid_map")]
pub(crate) RefCell<HashMap<Cid, Bytes>>,
);

impl MemoryBlockStore {
/// Creates a new in-memory block store.
Expand All @@ -106,7 +111,7 @@ impl BlockStore for MemoryBlockStore {
let bytes = self
.0
.borrow()
.get(&cid.to_string())
.get(cid)
.ok_or(BlockStoreError::CIDNotFound(*cid))?
.clone();

Expand All @@ -122,7 +127,7 @@ impl BlockStore for MemoryBlockStore {
let cid = self.create_cid(&bytes, codec)?;

// Insert the bytes into the HashMap using the CID as the key
self.0.borrow_mut().insert(cid.to_string(), bytes);
self.0.borrow_mut().insert(cid, bytes);

// Return Ok status with the generated CID
Ok(cid)
Expand Down Expand Up @@ -192,9 +197,9 @@ pub async fn bs_serialization_test<
// Insert the object into the blockstore
let cid = store.put_serializable(&bytes).await?;
// Serialize the BlockStore
let serial_store: Vec<u8> = dagcbor::encode(&store)?;
let serial_store: Vec<u8> = encode(&store, DagCborCodec)?;
// Construct a new BlockStore from the Serialized object
let deserial_store: T = dagcbor::decode(&serial_store)?;
let deserial_store: T = decode(&serial_store, DagCborCodec)?;
// Retrieve the object from the blockstore
let loaded: Vec<u8> = deserial_store.get_deserializable(&cid).await?;
// Assert that the objects are the same as the ones we inserted
Expand Down
75 changes: 42 additions & 33 deletions wnfs-common/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
/// Helper methods for decoding and encoding values into DagCbor.
pub mod dagcbor {
use crate::{AsyncSerialize, BlockStore};
use anyhow::Result;
use libipld::{
cbor::DagCborCodec,
codec::{Decode, Encode},
serde as ipld_serde, Ipld,
};
use serde::{de::DeserializeOwned, Serialize};
use std::io::Cursor;
use crate::{AsyncSerialize, BlockStore};
use anyhow::Result;
use libipld::{
codec::{Decode, Encode},
prelude::Codec,
serde as ipld_serde, Ipld,
};
use serde::{de::DeserializeOwned, Serialize};
use std::io::Cursor;

/// Encodes a serializable value into DagCbor bytes.
pub fn encode<S: Serialize>(value: &S) -> Result<Vec<u8>> {
let ipld = ipld_serde::to_ipld(value)?;
let mut bytes = Vec::new();
ipld.encode(DagCborCodec, &mut bytes)?;
Ok(bytes)
}
/// Encodes a serializable value into DagCbor bytes.
pub fn encode<S, C>(value: &S, codec: C) -> Result<Vec<u8>>
where
S: Serialize,
C: Codec,
Ipld: Encode<C>,
{
let ipld = ipld_serde::to_ipld(value)?;
let mut bytes = Vec::new();
<Ipld as Encode<C>>::encode(&ipld, codec, &mut bytes)?;
Ok(bytes)
}

/// Encodes an async serializable value into DagCbor bytes.
pub async fn async_encode<V: AsyncSerialize>(
value: &V,
store: &impl BlockStore,
) -> Result<Vec<u8>> {
let ipld = value.async_serialize_ipld(store).await?;
let mut bytes = Vec::new();
ipld.encode(DagCborCodec, &mut bytes)?;
Ok(bytes)
}
/// Encodes an async serializable value into DagCbor bytes.
pub async fn async_encode<V, C>(value: &V, store: &impl BlockStore, codec: C) -> Result<Vec<u8>>
where
V: AsyncSerialize,
C: Codec,
Ipld: Encode<C>,
{
let ipld = value.async_serialize_ipld(store).await?;
let mut bytes = Vec::new();
<Ipld as Encode<C>>::encode(&ipld, codec, &mut bytes)?;
Ok(bytes)
}

/// Decodes recieved DagCbor bytes into a deserializable value.
pub fn decode<D: DeserializeOwned>(bytes: &[u8]) -> Result<D> {
let ipld = Ipld::decode(DagCborCodec, &mut Cursor::new(bytes))?;
Ok(ipld_serde::from_ipld::<_>(ipld)?)
}
/// Decodes recieved DagCbor bytes into a deserializable value.
pub fn decode<D, C>(bytes: &[u8], codec: C) -> Result<D>
where
D: DeserializeOwned,
C: Codec,
Ipld: Decode<C>,
{
let ipld = <Ipld as Decode<C>>::decode(codec, &mut Cursor::new(bytes))?;
Ok(ipld_serde::from_ipld::<_>(ipld)?)
}
3 changes: 3 additions & 0 deletions wnfs-common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +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("Lock poisoned")]
LockPoisoned,
}
8 changes: 8 additions & 0 deletions wnfs-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ pub const MAX_BLOCK_SIZE: usize = usize::pow(2, 18);

/// The general size of digests in WNFS.
pub type HashOutput = [u8; HASH_BYTE_SIZE];

//--------------------------------------------------------------------------------------------------
// Re-exports
//--------------------------------------------------------------------------------------------------

pub mod libipld {
pub use libipld::*;
}
7 changes: 4 additions & 3 deletions wnfs-common/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,16 @@ impl<'de> Deserialize<'de> for NodeType {

#[cfg(test)]
mod tests {
use crate::{dagcbor, Metadata};
use crate::{decode, encode, Metadata};
use chrono::Utc;
use libipld::cbor::DagCborCodec;

#[async_std::test]
async fn metadata_can_encode_decode_as_cbor() {
let metadata = Metadata::new(Utc::now());

let encoded_metadata = dagcbor::encode(&metadata).unwrap();
let decoded_metadata = dagcbor::decode::<Metadata>(encoded_metadata.as_ref()).unwrap();
let encoded_metadata = encode(&metadata, DagCborCodec).unwrap();
let decoded_metadata: Metadata = decode(encoded_metadata.as_ref(), DagCborCodec).unwrap();

assert_eq!(metadata, decoded_metadata);
}
Expand Down
Loading