Skip to content

Commit 97d501f

Browse files
committed
fix: make the empty array CID const and export it (#668)
Otherwise, we'll perform a hash in WASM when we first dereference (max once per call) which is just wasteful. This also fixes the integration tests to use the "correct" empty object. Previously, they were using `()` which mapped to null, not `[]`.
1 parent be69f54 commit 97d501f

File tree

5 files changed

+49
-19
lines changed

5 files changed

+49
-19
lines changed

runtime/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fvm_shared = { version = "3.0.0-alpha.1", default-features = false }
1414
num-traits = "0.2.14"
1515
num-derive = "0.3.3"
1616
serde = { version = "1.0.136", features = ["derive"] }
17-
lazy_static = "1.4.0"
17+
lazy_static = { version = "1.4.0", optional = true }
1818
unsigned-varint = "0.7.1"
1919
byteorder = "1.4.3"
2020
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
@@ -86,4 +86,4 @@ no-provider-deal-collateral = []
8686
# fake proofs (for testing)
8787
fake-proofs = []
8888

89-
test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng"]
89+
test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static"]

runtime/src/runtime/empty.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use std::mem;
2+
3+
use cid::multihash::Multihash;
4+
use cid::Cid;
5+
use fvm_ipld_encoding::DAG_CBOR;
6+
use fvm_shared::crypto::hash::SupportedHashes;
7+
8+
const fn const_unwrap<T: Copy, E>(r: Result<T, E>) -> T {
9+
let v = match r {
10+
Ok(r) => r,
11+
Err(_) => panic!(),
12+
};
13+
mem::forget(r);
14+
v
15+
}
16+
17+
// 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0
18+
const EMPTY_ARR_HASH_DIGEST: &[u8] = &[
19+
0x45, 0xb0, 0xcf, 0xc2, 0x20, 0xce, 0xec, 0x5b, 0x7c, 0x1c, 0x62, 0xc4, 0xd4, 0x19, 0x3d, 0x38,
20+
0xe4, 0xeb, 0xa4, 0x8e, 0x88, 0x15, 0x72, 0x9c, 0xe7, 0x5f, 0x9c, 0x0a, 0xb0, 0xe4, 0xc1, 0xc0,
21+
];
22+
23+
// bafy2bzacebc3bt6cedhoyw34drrmjvazhu4oj25er2ebk4u445pzycvq4ta4a
24+
pub const EMPTY_ARR_CID: Cid = Cid::new_v1(
25+
DAG_CBOR,
26+
const_unwrap(Multihash::wrap(SupportedHashes::Blake2b256 as u64, EMPTY_ARR_HASH_DIGEST)),
27+
);
28+
29+
#[test]
30+
fn test_empty_arr_cid() {
31+
use cid::multihash::{Code, MultihashDigest};
32+
use fvm_ipld_encoding::to_vec;
33+
34+
let empty = to_vec::<[(); 0]>(&[]).unwrap();
35+
let expected = Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(&empty));
36+
assert_eq!(EMPTY_ARR_CID, expected);
37+
}

runtime/src/runtime/fvm.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use anyhow::{anyhow, Error};
2-
use cid::multihash::{Code, MultihashDigest};
2+
use cid::multihash::Code;
33
use cid::Cid;
44
use fvm_ipld_blockstore::Blockstore;
5-
use fvm_ipld_encoding::{to_vec, Cbor, CborStore, RawBytes, DAG_CBOR};
5+
use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR};
66
use fvm_sdk as fvm;
77
use fvm_sdk::NO_DATA_BLOCK_ID;
88
use fvm_shared::address::Address;
@@ -32,13 +32,7 @@ use crate::runtime::{
3232
};
3333
use crate::{actor_error, ActorError, Runtime};
3434

35-
lazy_static::lazy_static! {
36-
/// Cid of the empty array Cbor bytes (`EMPTY_ARR_BYTES`).
37-
pub static ref EMPTY_ARR_CID: Cid = {
38-
let empty = to_vec::<[(); 0]>(&[]).unwrap();
39-
Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(&empty))
40-
};
41-
}
35+
use super::EMPTY_ARR_CID;
4236

4337
/// A runtime that bridges to the FVM environment through the FVM SDK.
4438
pub struct FvmRuntime<B = ActorBlockstore> {
@@ -239,7 +233,7 @@ where
239233

240234
fn create<C: Cbor>(&mut self, obj: &C) -> Result<(), ActorError> {
241235
let root = fvm::sself::root()?;
242-
if root != *EMPTY_ARR_CID {
236+
if root != EMPTY_ARR_CID {
243237
return Err(
244238
actor_error!(illegal_state; "failed to create state; expected empty array CID, got: {}", root),
245239
);

runtime/src/runtime/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ mod actor_blockstore;
3737
#[cfg(feature = "fil-actor")]
3838
pub mod fvm;
3939

40+
pub(crate) mod empty;
41+
pub use empty::EMPTY_ARR_CID;
42+
4043
/// Runtime is the VM's internal runtime object.
4144
/// this is everything that is accessible to actors, beyond parameters.
4245
pub trait Runtime<BS: Blockstore>: Primitives + Verifier + RuntimePolicy {

test_vm/src/lib.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use fil_actors_runtime::cbor::serialize;
1818
use fil_actors_runtime::runtime::builtins::Type;
1919
use fil_actors_runtime::runtime::{
2020
ActorCode, DomainSeparationTag, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy,
21-
Verifier,
21+
Verifier, EMPTY_ARR_CID,
2222
};
2323
use fil_actors_runtime::test_utils::*;
2424
use fil_actors_runtime::MessageAccumulator;
@@ -70,7 +70,6 @@ pub struct VM<'bs> {
7070
total_fil: TokenAmount,
7171
actors_dirty: RefCell<bool>,
7272
actors_cache: RefCell<HashMap<Address, Actor>>,
73-
empty_obj_cid: Cid,
7473
network_version: NetworkVersion,
7574
curr_epoch: ChainEpoch,
7675
invocations: RefCell<Vec<InvocationTrace>>,
@@ -115,14 +114,12 @@ pub const FIRST_TEST_USER_ADDR: ActorID = FIRST_NON_SINGLETON_ADDR + 3;
115114
impl<'bs> VM<'bs> {
116115
pub fn new(store: &'bs MemoryBlockstore) -> VM<'bs> {
117116
let mut actors = Hamt::<&'bs MemoryBlockstore, Actor, BytesKey, Sha256>::new(store);
118-
let empty = store.put_cbor(&(), Code::Blake2b256).unwrap();
119117
VM {
120118
store,
121119
state_root: RefCell::new(actors.flush().unwrap()),
122120
total_fil: TokenAmount::zero(),
123121
actors_dirty: RefCell::new(false),
124122
actors_cache: RefCell::new(HashMap::new()),
125-
empty_obj_cid: empty,
126123
network_version: NetworkVersion::V16,
127124
curr_epoch: ChainEpoch::zero(),
128125
invocations: RefCell::new(vec![]),
@@ -260,7 +257,6 @@ impl<'bs> VM<'bs> {
260257
total_fil: self.total_fil,
261258
actors_dirty: RefCell::new(false),
262259
actors_cache: RefCell::new(HashMap::new()),
263-
empty_obj_cid: self.empty_obj_cid,
264260
network_version: self.network_version,
265261
curr_epoch: epoch,
266262
invocations: RefCell::new(vec![]),
@@ -691,7 +687,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
691687
"attempt to create new actor at existing address".to_string(),
692688
));
693689
}
694-
let a = actor(code_id, self.v.empty_obj_cid, 0, TokenAmount::zero());
690+
let a = actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero());
695691
self.v.set_actor(addr, a);
696692
Ok(())
697693
}
@@ -846,7 +842,7 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
846842
"failed to create state".to_string(),
847843
)),
848844
Some(mut act) => {
849-
if act.head != self.v.empty_obj_cid {
845+
if act.head != EMPTY_ARR_CID {
850846
Err(ActorError::unchecked(
851847
ExitCode::SYS_ASSERTION_FAILED,
852848
"failed to construct state: already initialized".to_string(),

0 commit comments

Comments
 (0)