diff --git a/fvm/src/blockstore/buffered.rs b/fvm/src/blockstore/buffered.rs index 17107b98b..606d35637 100644 --- a/fvm/src/blockstore/buffered.rs +++ b/fvm/src/blockstore/buffered.rs @@ -5,23 +5,25 @@ use std::cell::RefCell; use std::collections::HashMap; use std::io::{Cursor, Read, Seek}; +use std::marker::PhantomData; use anyhow::{anyhow, Result}; use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; use cid::Cid; -use fvm_ipld_blockstore::{Blockstore, Buffered}; +use fvm_ipld_blockstore::{Block, Blockstore, Buffered}; use fvm_ipld_encoding::{CBOR, DAG_CBOR}; use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED}; /// Wrapper around `Blockstore` to limit and have control over when values are written. /// This type is not threadsafe and can only be used in synchronous contexts. #[derive(Debug)] -pub struct BufferedBlockstore { +pub struct BufferedBlockstore { base: BS, write: RefCell>>, + _marker: PhantomData C>, } -impl BufferedBlockstore +impl BufferedBlockstore where BS: Blockstore, { @@ -29,6 +31,7 @@ where Self { base, write: Default::default(), + _marker: Default::default(), } } @@ -37,6 +40,20 @@ where } } +impl BufferedBlockstore +where + C: multihash::MultihashDigest<64>, + anyhow::Error: From, +{ + fn cid_of(&self, mh_code: u64, block: &dyn Block) -> Result { + let mh_code = C::try_from(mh_code)?; + let data = block.data(); + let codec = block.codec(); + let digest = mh_code.digest(data); + Ok(Cid::new_v1(codec, digest)) + } +} + impl Buffered for BufferedBlockstore where BS: Blockstore, @@ -229,9 +246,11 @@ fn copy_rec<'a>( Ok(()) } -impl Blockstore for BufferedBlockstore +impl Blockstore for BufferedBlockstore where BS: Blockstore, + C: multihash::MultihashDigest<64>, + anyhow::Error: From, { fn get(&self, cid: &Cid) -> Result>> { Ok(if let Some(data) = self.write.borrow().get(cid) { @@ -241,6 +260,12 @@ where }) } + fn put(&self, mh_code: u64, block: &dyn fvm_ipld_blockstore::Block) -> Result { + let k = self.cid_of(mh_code, block)?; + self.put_keyed(&k, block.data())?; + Ok(k) + } + fn put_keyed(&self, cid: &Cid, buf: &[u8]) -> Result<()> { self.write.borrow_mut().insert(*cid, Vec::from(buf)); Ok(()) diff --git a/fvm/src/blockstore/discard.rs b/fvm/src/blockstore/discard.rs index 9021c72d6..4e4e1a479 100644 --- a/fvm/src/blockstore/discard.rs +++ b/fvm/src/blockstore/discard.rs @@ -1,22 +1,67 @@ // Copyright 2021-2023 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT +use std::marker::PhantomData; + use cid::Cid; use fvm_ipld_blockstore::Blockstore; // A blockstore that accepts but discards all insertions, and returns errors on reads. // Useful for when the FVM needs to stage ephemeral data structures without persisting them, // like the events AMT. -pub struct DiscardBlockstore; +#[derive(Copy, Clone)] +pub struct DiscardBlockstore(PhantomData C>); + +impl Default for DiscardBlockstore { + fn default() -> Self { + DiscardBlockstore(Default::default()) + } +} -impl Blockstore for DiscardBlockstore { +impl Blockstore for DiscardBlockstore +where + C: multihash::MultihashDigest<64>, + anyhow::Error: From, +{ fn get(&self, _: &Cid) -> anyhow::Result>> { Err(anyhow::anyhow!( - "Blockstore#get not supported with DiscardBlockstore" + "Blockstore::get not supported with DiscardBlockstore" )) } fn put_keyed(&self, _: &Cid, _: &[u8]) -> anyhow::Result<()> { Ok(()) } + + fn put(&self, mh_code: u64, block: &dyn fvm_ipld_blockstore::Block) -> anyhow::Result { + let mh_code = C::try_from(mh_code)?; + let data = block.data(); + let codec = block.codec(); + let digest = mh_code.digest(data); + Ok(Cid::new_v1(codec, digest)) + } + + fn has(&self, _: &Cid) -> anyhow::Result { + Err(anyhow::anyhow!( + "Blockstore::has not supported with DiscardBlockstore" + )) + } + + fn put_many(&self, _: I) -> anyhow::Result<()> + where + Self: Sized, + B: fvm_ipld_blockstore::Block, + I: IntoIterator, + { + Ok(()) + } + + fn put_many_keyed(&self, _: I) -> anyhow::Result<()> + where + Self: Sized, + D: AsRef<[u8]>, + I: IntoIterator, + { + Ok(()) + } } diff --git a/fvm/src/call_manager/default.rs b/fvm/src/call_manager/default.rs index dfb35d237..453f3b5ee 100644 --- a/fvm/src/call_manager/default.rs +++ b/fvm/src/call_manager/default.rs @@ -928,7 +928,7 @@ impl EventsAccumulator { let root = if !self.events.is_empty() { const EVENTS_AMT_BITWIDTH: u32 = 5; let root = Amt::new_from_iter_with_bit_width( - DiscardBlockstore, + DiscardBlockstore::default(), EVENTS_AMT_BITWIDTH, self.events.iter().cloned(), ) diff --git a/fvm/src/machine/default.rs b/fvm/src/machine/default.rs index 023ec84b6..33efd6025 100644 --- a/fvm/src/machine/default.rs +++ b/fvm/src/machine/default.rs @@ -4,7 +4,8 @@ use std::ops::RangeInclusive; use anyhow::{anyhow, Context as _}; use cid::Cid; -use fvm_ipld_blockstore::{Block, Blockstore, Buffered}; +use fvm_ipld_blockstore::{Blockstore, Buffered}; +use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{to_vec, CborStore, DAG_CBOR}; use fvm_shared::version::NetworkVersion; use log::debug; @@ -24,8 +25,9 @@ use crate::EMPTY_ARR_CID; lazy_static::lazy_static! { /// Pre-serialized block containing the empty array - pub static ref EMPTY_ARRAY_BLOCK: Block> = { - Block::new(DAG_CBOR, to_vec::<[(); 0]>(&[]).unwrap()) + pub static ref EMPTY_ARRAY_BLOCK: IpldBlock = IpldBlock { + codec: DAG_CBOR, + data: to_vec::<[(); 0]>(&[]).unwrap(), }; } @@ -194,7 +196,7 @@ where // Helper method that puts certain "empty" types in the blockstore. // These types are privileged by some parts of the system (eg. as the default actor state). fn put_empty_blocks(blockstore: B) -> anyhow::Result<()> { - let empty_arr_cid = blockstore.put(Blake2b256, &EMPTY_ARRAY_BLOCK)?; + let empty_arr_cid = blockstore.put(Blake2b256.into(), &*EMPTY_ARRAY_BLOCK)?; debug_assert!( empty_arr_cid == *EMPTY_ARR_CID, diff --git a/fvm/tests/default_kernel/mod.rs b/fvm/tests/default_kernel/mod.rs index 3827e9f45..5e167c2ce 100644 --- a/fvm/tests/default_kernel/mod.rs +++ b/fvm/tests/default_kernel/mod.rs @@ -5,7 +5,7 @@ use std::rc::Rc; // test target use fvm::kernel::default::DefaultKernel; -use fvm::kernel::{Block, BlockRegistry}; +use fvm::kernel::BlockRegistry; use fvm::Kernel; use multihash::Code; use num_traits::Zero; diff --git a/fvm/tests/default_kernel/ops.rs b/fvm/tests/default_kernel/ops.rs index 381a00757..cb044febe 100644 --- a/fvm/tests/default_kernel/ops.rs +++ b/fvm/tests/default_kernel/ops.rs @@ -177,7 +177,6 @@ mod ipld { "charge_gas should only be called exactly once per block_link" ); - let expected_block = Block::new(cid.codec(), block); let expected_create_price = call_manager .machine .context() @@ -190,7 +189,7 @@ mod ipld { .price_list .on_block_link( SupportedHashes::try_from(cid.hash().code()).unwrap(), - expected_block.size() as usize, + block.len(), ) .total(); diff --git a/ipld/blockstore/Cargo.toml b/ipld/blockstore/Cargo.toml index 0bf608581..03863bca9 100644 --- a/ipld/blockstore/Cargo.toml +++ b/ipld/blockstore/Cargo.toml @@ -14,5 +14,10 @@ anyhow = "1.0.51" # depdendency is needed to enable the features of the re-export. multihash = { version = "0.16.1", default-features = false, features = ["multihash-impl"] } +[dev-dependencies.multihash] +version = "*" +default-features = false +features = ["multihash-impl", "blake2b"] + [features] default = [] diff --git a/ipld/blockstore/src/block.rs b/ipld/blockstore/src/block.rs deleted file mode 100644 index 19d96e484..000000000 --- a/ipld/blockstore/src/block.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2021-2023 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT -use cid::multihash::{self, MultihashDigest}; -use cid::Cid; - -/// Block represents a typed (i.e., with codec) IPLD block. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Block -where - D: AsRef<[u8]> + ?Sized, -{ - pub codec: u64, - pub data: D, -} - -impl Block -where - D: AsRef<[u8]> + ?Sized, -{ - pub fn new(codec: u64, data: D) -> Self - where - Self: Sized, - D: Sized, - { - Self { codec, data } - } - - pub fn cid(&self, mh_code: multihash::Code) -> Cid { - Cid::new_v1(self.codec, mh_code.digest(self.data.as_ref())) - } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.data.as_ref().len() - } -} - -impl AsRef<[u8]> for Block -where - D: AsRef<[u8]>, -{ - fn as_ref(&self) -> &[u8] { - self.data.as_ref() - } -} - -impl<'a, D> From<&'a Block> for Block<&'a [u8]> -where - D: AsRef<[u8]>, -{ - fn from(b: &'a Block) -> Self { - Block { - codec: b.codec, - data: b.data.as_ref(), - } - } -} diff --git a/ipld/blockstore/src/lib.rs b/ipld/blockstore/src/lib.rs index dfff2ad73..c7679f4bd 100644 --- a/ipld/blockstore/src/lib.rs +++ b/ipld/blockstore/src/lib.rs @@ -3,15 +3,57 @@ use std::rc::Rc; use anyhow::Result; -use cid::{multihash, Cid}; +use cid::Cid; pub mod tracking; mod memory; pub use memory::MemoryBlockstore; -mod block; -pub use block::*; +pub trait Block { + fn codec(&self) -> u64; + fn data(&self) -> &[u8]; + fn len(&self) -> usize { + self.data().len() + } +} + +impl Block for &T { + fn codec(&self) -> u64 { + (**self).codec() + } + fn data(&self) -> &[u8] { + (**self).data() + } +} + +impl Block for Box { + fn codec(&self) -> u64 { + (**self).codec() + } + fn data(&self) -> &[u8] { + (**self).data() + } +} + +impl Block for Rc { + fn codec(&self) -> u64 { + (**self).codec() + } + fn data(&self) -> &[u8] { + (**self).data() + } +} + +impl> Block for (u64, D) { + fn codec(&self) -> u64 { + self.0 + } + + fn data(&self) -> &[u8] { + self.1.as_ref() + } +} /// An IPLD blockstore suitable for injection into the FVM. /// @@ -34,17 +76,7 @@ pub trait Blockstore { } /// Puts the block into the blockstore, computing the hash with the specified multicodec. - /// - /// By default, this defers to put. - fn put(&self, mh_code: multihash::Code, block: &Block) -> Result - where - Self: Sized, - D: AsRef<[u8]>, - { - let k = block.cid(mh_code); - self.put_keyed(&k, block.as_ref())?; - Ok(k) - } + fn put(&self, mh_code: u64, block: &dyn Block) -> Result; /// Bulk put blocks into the blockstore. /// @@ -54,16 +86,18 @@ pub trait Blockstore { /// use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore, Block}; /// /// let bs = MemoryBlockstore::default(); - /// let blocks = vec![Block::new(0x55, vec![0, 1, 2])]; - /// bs.put_many(blocks.iter().map(|b| (Blake2b256, b.into()))).unwrap(); + /// let blocks = vec![(0x55, vec![0, 1, 2])]; + /// bs.put_many(blocks.iter().map(|b| (Blake2b256.into(), b))).unwrap(); /// ``` - fn put_many(&self, blocks: I) -> Result<()> + fn put_many(&self, blocks: I) -> Result<()> where Self: Sized, - D: AsRef<[u8]>, - I: IntoIterator)>, + B: Block, + I: IntoIterator, { - self.put_many_keyed(blocks.into_iter().map(|(mc, b)| (b.cid(mc), b)))?; + for (code, block) in blocks { + self.put(code, &block)?; + } Ok(()) } @@ -103,19 +137,15 @@ where (*self).has(k) } - fn put(&self, mh_code: multihash::Code, block: &Block) -> Result - where - Self: Sized, - D: AsRef<[u8]>, - { + fn put(&self, mh_code: u64, block: &dyn Block) -> Result { (*self).put(mh_code, block) } - fn put_many(&self, blocks: I) -> Result<()> + fn put_many(&self, blocks: I) -> Result<()> where Self: Sized, - D: AsRef<[u8]>, - I: IntoIterator)>, + B: Block, + I: IntoIterator, { (*self).put_many(blocks) } @@ -146,19 +176,15 @@ where (**self).has(k) } - fn put(&self, mh_code: multihash::Code, block: &Block) -> Result - where - Self: Sized, - D: AsRef<[u8]>, - { + fn put(&self, mh_code: u64, block: &dyn Block) -> Result { (**self).put(mh_code, block) } - fn put_many(&self, blocks: I) -> Result<()> + fn put_many(&self, blocks: I) -> Result<()> where Self: Sized, - D: AsRef<[u8]>, - I: IntoIterator)>, + B: Block, + I: IntoIterator, { (**self).put_many(blocks) } diff --git a/ipld/blockstore/src/memory.rs b/ipld/blockstore/src/memory.rs index 087c43bd0..bcd1f428d 100644 --- a/ipld/blockstore/src/memory.rs +++ b/ipld/blockstore/src/memory.rs @@ -2,24 +2,40 @@ // SPDX-License-Identifier: Apache-2.0, MIT use std::cell::RefCell; use std::collections::HashMap; +use std::marker::PhantomData; use anyhow::Result; use cid::Cid; +use multihash::MultihashDigest; use super::Blockstore; -#[derive(Debug, Default, Clone)] -pub struct MemoryBlockstore { +#[derive(Debug, Clone)] +pub struct MemoryBlockstore { blocks: RefCell>>, + _marker: PhantomData C>, } -impl MemoryBlockstore { +impl Default for MemoryBlockstore { + fn default() -> Self { + Self::new() + } +} + +impl MemoryBlockstore { pub fn new() -> Self { - Self::default() + Self { + blocks: Default::default(), + _marker: Default::default(), + } } } -impl Blockstore for MemoryBlockstore { +impl Blockstore for MemoryBlockstore +where + C: MultihashDigest<64>, + anyhow::Error: From, +{ fn has(&self, k: &Cid) -> Result { Ok(self.blocks.borrow().contains_key(k)) } @@ -32,4 +48,22 @@ impl Blockstore for MemoryBlockstore { self.blocks.borrow_mut().insert(*k, block.into()); Ok(()) } + + fn put(&self, mh_code: u64, block: &dyn crate::Block) -> Result { + let mhcode = C::try_from(mh_code)?; + let data = block.data(); + let codec = block.codec(); + + let mh = mhcode.digest(data); + let k = Cid::new_v1(codec, mh); + self.put_keyed(&k, data)?; + Ok(k) + } +} + +#[test] +fn basic_test() { + let bs = MemoryBlockstore::default(); + bs.put(multihash::Code::Blake2b256.into(), &(0x55, b"foobar")) + .unwrap(); } diff --git a/ipld/blockstore/src/tracking.rs b/ipld/blockstore/src/tracking.rs index 8ddc1cff5..195b5fe22 100644 --- a/ipld/blockstore/src/tracking.rs +++ b/ipld/blockstore/src/tracking.rs @@ -5,7 +5,6 @@ use std::cell::RefCell; use anyhow::Result; -use cid::multihash::{self, Code}; use cid::Cid; use super::{Block, Blockstore}; @@ -62,13 +61,10 @@ where self.base.has(cid) } - fn put(&self, code: Code, block: &Block) -> Result - where - D: AsRef<[u8]>, - { + fn put(&self, code: u64, block: &dyn Block) -> Result { let mut stats = self.stats.borrow_mut(); stats.w += 1; - stats.bw += block.as_ref().len(); + stats.bw += block.data().len(); self.base.put(code, block) } @@ -79,16 +75,16 @@ where self.base.put_keyed(k, block) } - fn put_many(&self, blocks: I) -> Result<()> + fn put_many(&self, blocks: I) -> Result<()> where Self: Sized, - D: AsRef<[u8]>, - I: IntoIterator)>, + B: Block, + I: IntoIterator, { let mut stats = self.stats.borrow_mut(); self.base.put_many(blocks.into_iter().inspect(|(_, b)| { stats.w += 1; - stats.bw += b.as_ref().len(); + stats.bw += b.data().len(); }))?; Ok(()) } @@ -111,6 +107,8 @@ where #[cfg(test)] mod tests { + use cid::multihash::{Code, MultihashDigest}; + use super::*; use crate::{Block, MemoryBlockstore}; @@ -120,8 +118,10 @@ mod tests { let tr_store = TrackingBlockstore::new(&mem); assert_eq!(*tr_store.stats.borrow(), BSStats::default()); - let block = Block::new(0x55, &b"foobar"[..]); - tr_store.get(&block.cid(Code::Blake2b256)).unwrap(); + let block = (0x55, &b"foobar"[..]); + let mh = Code::Blake2b256.digest(block.data()); + let k = Cid::new_v1(block.codec(), mh); + tr_store.get(&k).unwrap(); assert_eq!( *tr_store.stats.borrow(), BSStats { @@ -130,8 +130,11 @@ mod tests { } ); - let put_cid = tr_store.put(Code::Blake2b256, &block).unwrap(); - assert_eq!(tr_store.get(&put_cid).unwrap().as_deref(), Some(block.data)); + let put_cid = tr_store.put(Code::Blake2b256.into(), &block).unwrap(); + assert_eq!( + tr_store.get(&put_cid).unwrap().as_deref(), + Some(block.data()) + ); assert_eq!( *tr_store.stats.borrow(), BSStats { diff --git a/ipld/encoding/src/cbor_store.rs b/ipld/encoding/src/cbor_store.rs index 5a740ffed..fd9c06555 100644 --- a/ipld/encoding/src/cbor_store.rs +++ b/ipld/encoding/src/cbor_store.rs @@ -1,7 +1,7 @@ // Copyright 2021-2023 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT use cid::{multihash, Cid}; -use fvm_ipld_blockstore::{Block, Blockstore}; +use fvm_ipld_blockstore::Blockstore; use serde::{de, ser}; use crate::DAG_CBOR; @@ -28,13 +28,7 @@ pub trait CborStore: Blockstore + Sized { S: ser::Serialize, { let bytes = crate::to_vec(obj)?; - self.put( - code, - &Block { - codec: DAG_CBOR, - data: &bytes, - }, - ) + self.put(code.into(), &(DAG_CBOR, bytes)) } } diff --git a/ipld/encoding/src/ipld_block.rs b/ipld/encoding/src/ipld_block.rs index 066bb65e5..70336b86c 100644 --- a/ipld/encoding/src/ipld_block.rs +++ b/ipld/encoding/src/ipld_block.rs @@ -1,5 +1,6 @@ use std::fmt::{Debug, Formatter}; +use fvm_ipld_blockstore::Block; // Copyright 2021-2023 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT use serde::de::value; @@ -33,6 +34,16 @@ impl Debug for IpldBlock { } } +impl Block for IpldBlock { + fn codec(&self) -> u64 { + self.codec + } + + fn data(&self) -> &[u8] { + &self.data + } +} + impl IpldBlock { pub fn deserialize<'de, T>(&'de self) -> Result where diff --git a/testing/integration/src/tester.rs b/testing/integration/src/tester.rs index 23fedf922..2b68dcc3d 100644 --- a/testing/integration/src/tester.rs +++ b/testing/integration/src/tester.rs @@ -9,7 +9,7 @@ use fvm::externs::Externs; use fvm::machine::{DefaultMachine, Machine, MachineContext, NetworkConfig}; use fvm::state_tree::{ActorState, StateTree}; use fvm::{init_actor, system_actor, DefaultKernel}; -use fvm_ipld_blockstore::{Block, Blockstore, MemoryBlockstore}; +use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore}; use fvm_ipld_encoding::{ser, CborStore}; use fvm_shared::address::{Address, Protocol}; use fvm_shared::econ::TokenAmount; @@ -421,12 +421,6 @@ impl BasicTester { /// Inserts the WASM code for the actor into the blockstore. fn put_wasm_code(blockstore: &impl Blockstore, wasm_binary: &[u8]) -> Result { - let cid = blockstore.put( - Code::Blake2b256, - &Block { - codec: IPLD_RAW, - data: wasm_binary, - }, - )?; + let cid = blockstore.put(Code::Blake2b256.into(), &(IPLD_RAW, wasm_binary))?; Ok(cid) } diff --git a/testing/integration/tests/fil-integer-overflow-actor/src/actor/blockstore.rs b/testing/integration/tests/fil-integer-overflow-actor/src/actor/blockstore.rs index d74565ef3..5d2b0264e 100644 --- a/testing/integration/tests/fil-integer-overflow-actor/src/actor/blockstore.rs +++ b/testing/integration/tests/fil-integer-overflow-actor/src/actor/blockstore.rs @@ -1,9 +1,6 @@ // Copyright 2021-2023 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use std::convert::TryFrom; - use anyhow::{anyhow, Result}; -use cid::multihash::Code; use cid::Cid; use fvm_ipld_blockstore::Block; use fvm_sdk as sdk; @@ -20,22 +17,18 @@ impl fvm_ipld_blockstore::Blockstore for Blockstore { } fn put_keyed(&self, k: &Cid, block: &[u8]) -> Result<()> { - let code = Code::try_from(k.hash().code()).map_err(|e| anyhow!(e.to_string()))?; - let k2 = self.put(code, &Block::new(k.codec(), block))?; + let k2 = self.put(k.hash().code(), &(k.codec(), block))?; if k != &k2 { return Err(anyhow!("put block with cid {} but has cid {}", k, k2)); } Ok(()) } - fn put(&self, code: Code, block: &Block) -> Result - where - D: AsRef<[u8]>, - { + fn put(&self, mh_code: u64, block: &dyn Block) -> Result { // TODO: Don't hard-code the size. Unfortunately, there's no good way to get it from the // codec at the moment. const SIZE: u32 = 32; - let k = sdk::ipld::put(code.into(), SIZE, block.codec, block.data.as_ref()) + let k = sdk::ipld::put(mh_code, SIZE, block.codec(), block.data()) .map_err(|e| anyhow!("put failed with {:?}", e))?; Ok(k) } diff --git a/testing/integration/tests/main.rs b/testing/integration/tests/main.rs index 290bf532e..2efa02614 100644 --- a/testing/integration/tests/main.rs +++ b/testing/integration/tests/main.rs @@ -961,4 +961,8 @@ impl Blockstore for FailingBlockstore { fn put_keyed(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()> { self.target.put_keyed(k, block) } + + fn put(&self, mh_code: u64, block: &dyn fvm_ipld_blockstore::Block) -> anyhow::Result { + self.target.put(mh_code, block) + } }