Skip to content

Commit

Permalink
feat: make changes to BlockStore trait based on feedback (#286)
Browse files Browse the repository at this point in the history
* Replace IpldCodec with u64

* Change `Vec<u8>` to `Bytes` on BlockStore trait
  • Loading branch information
appcypher authored Jun 23, 2023
1 parent 3fb5392 commit 085242d
Show file tree
Hide file tree
Showing 41 changed files with 275 additions and 272 deletions.
1 change: 1 addition & 0 deletions wnfs-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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"] }
futures = "0.3"
libipld = { version = "0.16", features = ["dag-cbor", "derive", "serde-codec"] }
Expand Down
75 changes: 56 additions & 19 deletions wnfs-common/src/blockstore.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
use crate::{dagcbor, AsyncSerialize, BlockStoreError, MAX_BLOCK_SIZE};
use anyhow::{bail, Result};
use async_trait::async_trait;
use bytes::Bytes;
use libipld::{
cid::Version,
multihash::{Code, MultihashDigest},
serde as ipld_serde, Cid, IpldCodec,
serde as ipld_serde, Cid,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{borrow::Cow, cell::RefCell, collections::HashMap};
use std::{cell::RefCell, collections::HashMap};

//--------------------------------------------------------------------------------------------------
// Constants
//--------------------------------------------------------------------------------------------------

/// The value representing the DAG-JSON codec.
///
/// - https://ipld.io/docs/codecs/#known-codecs
/// - https://github.com/multiformats/multicodec/blob/master/table.csv
pub const CODEC_DAG_JSON: u64 = 0x0129;

/// The value representing the DAG-CBOR codec.
///
/// - https://ipld.io/docs/codecs/#known-codecs
/// - https://github.com/multiformats/multicodec/blob/master/table.csv
pub const CODEC_DAG_CBOR: u64 = 0x71;

/// The value representing the DAG-Protobuf codec.
///
/// - https://ipld.io/docs/codecs/#known-codecs
/// - https://github.com/multiformats/multicodec/blob/master/table.csv
pub const CODEC_DAG_PB: u64 = 0x70;

/// The value representing the raw codec.
///
/// - https://ipld.io/docs/codecs/#known-codecs
/// - https://github.com/multiformats/multicodec/blob/master/table.csv
pub const CODEC_RAW: u64 = 0x55;

//--------------------------------------------------------------------------------------------------
// Type Definitions
Expand All @@ -16,8 +45,8 @@ use std::{borrow::Cow, cell::RefCell, collections::HashMap};
/// For types that implement block store operations like adding, getting content from the store.
#[async_trait(?Send)]
pub trait BlockStore: Sized {
async fn get_block(&self, cid: &Cid) -> Result<Cow<Vec<u8>>>;
async fn put_block(&self, bytes: Vec<u8>, codec: IpldCodec) -> Result<Cid>;
async fn get_block(&self, cid: &Cid) -> Result<Bytes>;
async fn put_block(&self, bytes: impl Into<Bytes>, codec: u64) -> Result<Cid>;

async fn get_deserializable<V: DeserializeOwned>(&self, cid: &Cid) -> Result<V> {
let bytes = self.get_block(cid).await?;
Expand All @@ -27,26 +56,28 @@ pub trait BlockStore: Sized {

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

// This should be the same in all implementations of BlockStore
fn create_cid(&self, bytes: &Vec<u8>, codec: IpldCodec) -> Result<Cid> {
fn create_cid(&self, bytes: &[u8], codec: u64) -> Result<Cid> {
// If there are too many bytes, abandon this task
if bytes.len() > MAX_BLOCK_SIZE {
bail!(BlockStoreError::MaximumBlockSizeExceeded(bytes.len()))
}

// Compute the SHA256 hash of the bytes
let hash = Code::Sha2_256.digest(bytes);

// Represent the hash as a V1 CID
let cid = Cid::new(Version::V1, codec.into(), hash)?;
// Return Ok with the CID
let cid = Cid::new(Version::V1, codec, hash)?;

Ok(cid)
}
}
Expand All @@ -59,7 +90,7 @@ pub trait BlockStore: Sized {
///
/// IPFS is basically a glorified HashMap.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MemoryBlockStore(RefCell<HashMap<String, Vec<u8>>>);
pub struct MemoryBlockStore(RefCell<HashMap<String, Bytes>>);

impl MemoryBlockStore {
/// Creates a new in-memory block store.
Expand All @@ -71,22 +102,28 @@ impl MemoryBlockStore {
#[async_trait(?Send)]
impl BlockStore for MemoryBlockStore {
/// Retrieves an array of bytes from the block store with given CID.
async fn get_block(&self, cid: &Cid) -> Result<Cow<Vec<u8>>> {
Ok(Cow::Owned(
self.0
.borrow()
.get(&cid.to_string())
.ok_or(BlockStoreError::CIDNotFound(*cid))?
.clone(),
))
async fn get_block(&self, cid: &Cid) -> Result<Bytes> {
let bytes = self
.0
.borrow()
.get(&cid.to_string())
.ok_or(BlockStoreError::CIDNotFound(*cid))?
.clone();

Ok(bytes)
}

/// Stores an array of bytes in the block store.
async fn put_block(&self, bytes: Vec<u8>, codec: IpldCodec) -> Result<Cid> {
async fn put_block(&self, bytes: impl Into<Bytes>, codec: u64) -> Result<Cid> {
// Convert the bytes into a Bytes object
let bytes: Bytes = bytes.into();

// Try to build the CID from the bytes and codec
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);

// Return Ok status with the generated CID
Ok(cid)
}
Expand Down
6 changes: 6 additions & 0 deletions wnfs-common/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::HashOutput;
use anyhow::Result;
use futures::{AsyncRead, AsyncReadExt};
use libipld::IpldCodec;
#[cfg(any(test, feature = "test_utils"))]
use proptest::{
strategy::{Strategy, ValueTree},
Expand Down Expand Up @@ -126,3 +127,8 @@ pub fn to_hash_output(bytes: &[u8]) -> HashOutput {
nibbles[..bytes.len()].copy_from_slice(bytes);
nibbles
}

/// Tries to convert a u64 value to IPLD codec.
pub fn u64_to_ipld(value: u64) -> Result<IpldCodec> {
Ok(value.try_into()?)
}
2 changes: 1 addition & 1 deletion wnfs-hamt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The implementation is based on [fvm_ipld_hamt](https://github.com/filecoin-proje
use wnfs_hamt::Node;
use wnfs_common::MemoryBlockStore;

let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();
let scores: Node<String, usize> = Rc::new(Node::default());

scores.set("Mandy", 30, store).await?;
Expand Down
14 changes: 7 additions & 7 deletions wnfs-hamt/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub struct KeyValueChange<K, V> {
///
/// #[async_std::main]
/// async fn main() {
/// let store = &mut MemoryBlockStore::new();
/// let store = &MemoryBlockStore::new();
/// let main_node = &mut Rc::new(Node::<[u8; 4], String>::default());
/// for i in 0u32..3 {
/// main_node
Expand Down Expand Up @@ -333,7 +333,7 @@ mod tests {

#[async_std::test]
async fn can_diff_main_node_with_added_removed_pairs() {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let main_node = &mut Rc::new(Node::<[u8; 4], String>::default());
for i in 0u32..3 {
Expand Down Expand Up @@ -404,7 +404,7 @@ mod tests {

#[async_std::test]
async fn can_diff_main_node_with_no_changes() {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let main_node = &mut Rc::new(Node::<_, _>::default());
for i in 0_u32..3 {
Expand Down Expand Up @@ -435,7 +435,7 @@ mod tests {

#[async_std::test]
async fn can_diff_nodes_with_different_structure_and_modified_changes() {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

// A node that adds the first 3 pairs of HASH_KV_PAIRS.
let other_node = &mut Rc::new(Node::<_, _, MockHasher>::default());
Expand Down Expand Up @@ -582,7 +582,7 @@ mod proptests {
),
) {
task::block_on(async {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();
let (ops, strategy_changes) = ops_changes;

let other_node = &mut strategies::node_from_operations(&ops, store).await.unwrap();
Expand Down Expand Up @@ -624,7 +624,7 @@ mod proptests {
#[strategy(generate_kvs("[a-z0-9]{1,3}", 0u64..1000, 0..100))] kvs2: Vec<(String, u64)>,
) {
task::block_on(async {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let node1 = strategies::node_from_kvs(kvs1, store).await.unwrap();
let node2 = strategies::node_from_kvs(kvs2, store).await.unwrap();
Expand All @@ -648,7 +648,7 @@ mod proptests {
#[strategy(generate_kvs("[a-z0-9]{1,3}", 0u64..1000, 0..100))] kvs2: Vec<(String, u64)>,
) {
task::block_on(async {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let node1 = strategies::node_from_kvs(kvs1, store).await.unwrap();
let node2 = strategies::node_from_kvs(kvs2, store).await.unwrap();
Expand Down
4 changes: 2 additions & 2 deletions wnfs-hamt/src/hamt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl<K, V, H: Hasher> Hamt<K, V, H> {
///
/// #[async_std::main]
/// async fn main() {
/// let store = &mut MemoryBlockStore::default();
/// let store = &MemoryBlockStore::default();
///
/// let main_hamt = Hamt::<String, usize>::with_root({
/// let mut node = Rc::new(Node::default());
Expand Down Expand Up @@ -228,7 +228,7 @@ mod tests {

#[async_std::test]
async fn hamt_can_encode_decode_as_cbor() {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();
let root = Rc::new(Node::default());
let hamt: Hamt<String, i32> = Hamt::with_root(root);

Expand Down
6 changes: 3 additions & 3 deletions wnfs-hamt/src/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ mod proptests {
#[strategy(generate_kvs("[a-z0-9]{1,3}", 0u64..1000, 0..100))] kvs3: Vec<(String, u64)>,
) {
task::block_on(async {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let node1 = strategies::node_from_kvs(kvs1, store).await.unwrap();
let node2 = strategies::node_from_kvs(kvs2, store).await.unwrap();
Expand Down Expand Up @@ -132,7 +132,7 @@ mod proptests {
#[strategy(generate_kvs("[a-z0-9]{1,3}", 0u64..1000, 0..100))] kvs2: Vec<(String, u64)>,
) {
task::block_on(async {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let node1 = strategies::node_from_kvs(kvs1, store).await.unwrap();
let node2 = strategies::node_from_kvs(kvs2, store).await.unwrap();
Expand Down Expand Up @@ -165,7 +165,7 @@ mod proptests {
#[strategy(generate_kvs("[a-z0-9]{1,3}", 0u64..1000, 0..100))] kvs2: Vec<(String, u64)>,
) {
task::block_on(async {
let store = &mut MemoryBlockStore::default();
let store = &MemoryBlockStore::default();

let node1 = strategies::node_from_kvs(kvs1, store).await.unwrap();
let node2 = strategies::node_from_kvs(kvs2, store).await.unwrap();
Expand Down
Loading

0 comments on commit 085242d

Please sign in to comment.