From 597706ae36be5e2c4ee34fc5b7f0bbebbbad3f94 Mon Sep 17 00:00:00 2001 From: Alex Su Date: Fri, 28 Jul 2023 14:53:34 +1000 Subject: [PATCH] update to pull in VM trait from external location --- Cargo.lock | 148 ++++++++++++---- Cargo.toml | 10 ++ api/Cargo.toml | 7 +- api/src/lib.rs | 24 ++- api/src/trace.rs | 149 +--------------- api/src/vm/mod.rs | 224 ------------------------ api/src/vm/test_utils.rs | 53 ------ api/src/wrangler.rs | 58 ++++-- builtin/Cargo.toml | 16 +- builtin/tests/batch_onboarding_deals.rs | 9 +- builtin/tests/util/deals.rs | 4 +- builtin/tests/util/hookup.rs | 7 +- builtin/tests/util/mod.rs | 2 +- builtin/tests/util/workflows.rs | 2 +- vm/Cargo.toml | 10 +- vm/src/bench/mod.rs | 8 +- vm/src/lib.rs | 1 + vm/src/primitives.rs | 93 ++++++++++ 18 files changed, 312 insertions(+), 513 deletions(-) delete mode 100644 api/src/vm/mod.rs delete mode 100644 api/src/vm/test_utils.rs create mode 100644 vm/src/primitives.rs diff --git a/Cargo.lock b/Cargo.lock index 1d82538..1c9539b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,12 +280,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bimap" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" - [[package]] name = "bincode" version = "1.3.3" @@ -1006,6 +1000,19 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.8.4" @@ -1013,7 +1020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "atty", - "humantime", + "humantime 2.1.0", "log", "regex", "termcolor", @@ -1142,7 +1149,7 @@ version = "12.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8e82ef71a1858d0530e74e778056dc1b67981587" dependencies = [ "anyhow", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_blockstore 0.2.0", @@ -1177,7 +1184,7 @@ name = "fil_actor_cron" version = "12.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8e82ef71a1858d0530e74e778056dc1b67981587" dependencies = [ - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", "fvm_shared", @@ -1193,7 +1200,7 @@ version = "12.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8e82ef71a1858d0530e74e778056dc1b67981587" dependencies = [ "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "frc46_token", "fvm_actor_utils", @@ -1216,7 +1223,7 @@ dependencies = [ "anyhow", "cid 0.10.1", "fil_actors_evm_shared", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", "fvm_shared", @@ -1235,7 +1242,7 @@ name = "fil_actor_ethaccount" version = "12.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8e82ef71a1858d0530e74e778056dc1b67981587" dependencies = [ - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_encoding 0.4.0", @@ -1254,7 +1261,7 @@ dependencies = [ "anyhow", "cid 0.10.1", "fil_actors_evm_shared", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", @@ -1278,7 +1285,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", @@ -1297,7 +1304,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "frc46_token", "fvm_ipld_bitfield", @@ -1321,7 +1328,7 @@ dependencies = [ "anyhow", "byteorder", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_ipld_amt", "fvm_ipld_bitfield", @@ -1345,7 +1352,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_actor_utils", "fvm_ipld_blockstore 0.2.0", @@ -1366,7 +1373,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", @@ -1388,7 +1395,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", @@ -1408,7 +1415,7 @@ name = "fil_actor_reward" version = "12.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8e82ef71a1858d0530e74e778056dc1b67981587" dependencies = [ - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", "fvm_shared", @@ -1426,7 +1433,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", "fvm_shared", @@ -1442,7 +1449,7 @@ source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8 dependencies = [ "anyhow", "cid 0.10.1", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "frc42_dispatch", "frc46_token", "fvm_actor_utils", @@ -1462,7 +1469,7 @@ name = "fil_actors_evm_shared" version = "12.0.0" source = "git+https://github.com/filecoin-project/builtin-actors?branch=master#8e82ef71a1858d0530e74e778056dc1b67981587" dependencies = [ - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "fvm_ipld_encoding 0.4.0", "fvm_shared", "hex", @@ -1470,6 +1477,42 @@ dependencies = [ "uint", ] +[[package]] +name = "fil_actors_runtime" +version = "12.0.0" +source = "git+https://github.com/filecoin-project/builtin-actors?branch=alexytsu/export-test-vm#27b175f4ca849396bd85589f2a432dc7d0dda722" +dependencies = [ + "anyhow", + "blake2b_simd", + "byteorder", + "castaway", + "cid 0.10.1", + "fvm_ipld_amt", + "fvm_ipld_bitfield", + "fvm_ipld_blockstore 0.2.0", + "fvm_ipld_encoding 0.4.0", + "fvm_ipld_hamt", + "fvm_shared", + "hex", + "itertools 0.10.5", + "lazy_static", + "libsecp256k1", + "log", + "multihash 0.18.1", + "num", + "num-derive", + "num-traits", + "paste", + "pretty_env_logger", + "rand", + "regex", + "serde", + "serde_repr", + "sha2 0.10.7", + "thiserror", + "unsigned-varint", +] + [[package]] name = "fil_actors_runtime" version = "12.0.0" @@ -1525,7 +1568,7 @@ dependencies = [ "fil_actor_reward", "fil_actor_system", "fil_actor_verifreg", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "num-traits", ] @@ -1894,18 +1937,13 @@ name = "fvm-workbench-api" version = "0.1.0" dependencies = [ "anyhow", - "bimap", - "blake2b_simd", "cid 0.10.1", "fvm_ipld_blockstore 0.2.0", "fvm_ipld_encoding 0.4.0", - "fvm_ipld_hamt", "fvm_shared", "itertools 0.10.5", - "libsecp256k1", - "multihash 0.18.1", "num-format", - "thiserror", + "vm_api", ] [[package]] @@ -1916,7 +1954,7 @@ dependencies = [ "blake2b_simd", "bls-signatures", "cid 0.10.1", - "env_logger", + "env_logger 0.8.4", "fil_actor_account", "fil_actor_cron", "fil_actor_datacap", @@ -1929,7 +1967,7 @@ dependencies = [ "fil_actor_reward", "fil_actor_system", "fil_actor_verifreg", - "fil_actors_runtime", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=master)", "fil_builtin_actors_bundle", "frc46_token", "fvm", @@ -1947,6 +1985,7 @@ dependencies = [ "num-traits", "rand_chacha", "thiserror", + "vm_api", ] [[package]] @@ -1954,6 +1993,7 @@ name = "fvm-workbench-vm" version = "0.1.0" dependencies = [ "anyhow", + "blake2b_simd", "cid 0.10.1", "futures", "fvm", @@ -1962,9 +2002,11 @@ dependencies = [ "fvm_ipld_car 0.7.0", "fvm_ipld_encoding 0.4.0", "fvm_shared", + "libsecp256k1", "multihash 0.18.1", "replace_with", "thiserror", + "vm_api", ] [[package]] @@ -2344,6 +2386,15 @@ dependencies = [ "hmac", ] +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "humantime" version = "2.1.0" @@ -3033,6 +3084,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger 0.7.1", + "log", +] + [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -3085,6 +3146,12 @@ dependencies = [ "cc", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.32" @@ -3907,6 +3974,23 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vm_api" +version = "1.0.0" +source = "git+https://github.com/filecoin-project/builtin-actors?branch=alexytsu/export-test-vm#27b175f4ca849396bd85589f2a432dc7d0dda722" +dependencies = [ + "anyhow", + "cid 0.10.1", + "fil_actors_runtime 12.0.0 (git+https://github.com/filecoin-project/builtin-actors?branch=alexytsu/export-test-vm)", + "fvm_ipld_blockstore 0.2.0", + "fvm_ipld_encoding 0.4.0", + "fvm_ipld_hamt", + "fvm_shared", + "rand", + "rand_chacha", + "serde", +] + [[package]] name = "waker-fn" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index b2c59bb..7c0d5f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,17 @@ fvm_ipld_car = { version = "0.7.0"} fvm_ipld_encoding = { version = "0.4.0" } fvm_ipld_hamt = { version = "0.7.0" } fvm_shared = { version = "~3.4.0", default-features = false } +vm_api = { version = "1.0.0", git="https://github.com/filecoin-project/builtin-actors", branch="alexytsu/export-test-vm" } +anyhow = { version = "~1.0.47 } +blake2b_simd = { version = "1.0" } +bls-signatures = { version = "0.13", default-features = false } +futures = { version = "~0.3.19" } +libsecp256k1 = { version = "0.7.1"} +multihash = { version = "0.18.1", default-features = false } +num-traits = "0.2.14" +rand_chacha = "~0.3" +replace_with = "0.1.7" [patch.crates-io] # fvm = {git="https://github.com/helix-onchain/ref-fvm", branch="master"} diff --git a/api/Cargo.toml b/api/Cargo.toml index 76ebf54..f3e2b31 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -9,15 +9,10 @@ edition = "2021" [dependencies] fvm_ipld_blockstore = { workspace = true } fvm_ipld_encoding = { workspace = true } -fvm_ipld_hamt = { workspace = true } fvm_shared = { workspace = true } +vm_api = { workspace = true } anyhow = "~1.0.47" -blake2b_simd = { version = "1.0" } -bimap = "0.6.2" cid = { workspace = true } itertools = "~0.10.5" -libsecp256k1 = { version = "0.7.1"} -multihash = { version = "0.18.1", default-features = false, features=["sha2", "derive"] } num-format = "0.4.4" -thiserror = "~1.0.30" diff --git a/api/src/lib.rs b/api/src/lib.rs index 03ddd75..d58b59d 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -7,16 +7,16 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::message::Message; use fvm_shared::receipt::Receipt; use fvm_shared::ActorID; - -use crate::trace::ExecutionTrace; +use vm_api::{ActorState, MessageResult}; pub mod analysis; pub mod bench; pub mod blockstore; pub mod trace; -pub mod vm; pub mod wrangler; +use trace::ExecutionTrace; + /// A factory for workbench instances. /// Built-in actors must be installed before the workbench can be created. // TODO: Configuration of default circulating supply, base fee etc. @@ -100,14 +100,12 @@ pub struct ExecutionResult { pub message: String, } -/// An actor root state object. -pub struct ActorState { - /// Link to code for the actor. - pub code: Cid, - /// Link to the state of the actor. - pub state: Cid, - /// Sequence of the actor. - pub sequence: u64, - /// Tokens available to the actor. - pub balance: TokenAmount, +impl From for MessageResult { + fn from(execution_res: ExecutionResult) -> MessageResult { + MessageResult { + code: execution_res.receipt.exit_code, + ret: execution_res.receipt.return_data.into(), + message: execution_res.message, + } + } } diff --git a/api/src/trace.rs b/api/src/trace.rs index b2573db..7361ea6 100644 --- a/api/src/trace.rs +++ b/api/src/trace.rs @@ -8,8 +8,9 @@ use fvm_shared::error::{ErrorNumber, ExitCode}; use fvm_shared::{ActorID, MethodNum}; use itertools::Itertools; -/// A trace of a single message execution. -/// A trace is a sequence of events. +/// A trace of a single message execution comprising a series of events. +/// An execution trace is easily produced by any abstract VM and can be used for low-level analysis +/// of gas costs and other call-events. #[derive(Clone, Debug)] pub struct ExecutionTrace { events: Vec, @@ -62,147 +63,3 @@ pub enum ExecutionEvent { msg: String, }, } - -// NOTE(alexytsu): the below invocation traces are copied fromthe builtin-actors repo. -// these are used in tests to make assertions that certain calls were made -// they differ from the above ExecutionTraces (which are low-level traces produced by the VM account for gas etc.) -// TODO: derive invocation trace from execution trace. move invocation trace to a separate utility module -// https://github.com/anorth/fvm-workbench/issues/19 - -/// A trace of an actor method invocation. -#[derive(Clone, Debug)] -pub struct InvocationTrace { - pub from: Address, - pub to: Address, - pub value: TokenAmount, - pub method: MethodNum, - pub params: Option, - pub code: ExitCode, - pub ret: Option, - pub subinvocations: Vec, -} - -/// An expectation for a method invocation trace. -/// Non-optional fields must always be specified, and are always checked against any trace. -/// Optional fields are ignored when checking the expectation against a trace. -// Future work: -// - Add mutator or factory methods to allow builder-style customisation of expectations. -// - Add a capture() option on value, params, ret etc to enable extraction of internal values -// while matching with an invocation trace. -// - Make value mandatory (requires specifying the currently unknown ones). -// - Return a top-level ExpectInvocation from helpers like util::apply_ok to save caller -// constructing it. -#[derive(Clone, Debug)] -pub struct ExpectInvocation { - pub from: Address, - pub to: Address, - pub method: MethodNum, - pub value: Option, - pub params: Option>, - pub code: ExitCode, - pub ret: Option>, - pub subinvocs: Option>, -} - -impl ExpectInvocation { - /// Asserts that a trace matches this expectation, including subinvocations. - pub fn matches(&self, invoc: &InvocationTrace) { - let id = format!("[{}→{}:{}]", invoc.from, invoc.to, invoc.method); - self.quick_match(invoc, String::new()); - assert_eq!( - self.code, invoc.code, - "{} unexpected code expected: {}, was: {}", - id, self.code, invoc.code - ); - if let Some(v) = &self.value { - assert_eq!( - v, &invoc.value, - "{} unexpected value: expected: {}, was: {} ", - id, v, invoc.value - ); - } - if let Some(p) = &self.params { - assert_eq!( - p, &invoc.params, - "{} unexpected params: expected: {:x?}, was: {:x?}", - id, p, invoc.params - ); - } - if let Some(r) = &self.ret { - assert_eq!( - r, &invoc.ret, - "{} unexpected ret: expected: {:x?}, was: {:x?}", - id, r, invoc.ret - ); - } - if let Some(expect_subinvocs) = &self.subinvocs { - let subinvocs = &invoc.subinvocations; - - let panic_str = format!( - "unexpected subinvocs:\n expected: \n[\n{}]\n was:\n[\n{}]\n", - self.fmt_expect_invocs(expect_subinvocs), - self.fmt_invocs(subinvocs) - ); - assert_eq!(subinvocs.len(), expect_subinvocs.len(), "{} {}", id, panic_str); - - for (i, invoc) in subinvocs.iter().enumerate() { - let expect_invoc = expect_subinvocs.get(i).unwrap(); - // only try to match if required fields match - expect_invoc.quick_match(invoc, panic_str.clone()); - expect_invoc.matches(invoc); - } - } - } - - pub fn fmt_invocs(&self, invocs: &[InvocationTrace]) -> String { - invocs - .iter() - .enumerate() - .map(|(i, invoc)| format!("{}: [{}:{}],\n", i, invoc.to, invoc.method)) - .collect() - } - - pub fn fmt_expect_invocs(&self, exs: &[ExpectInvocation]) -> String { - exs.iter() - .enumerate() - .map(|(i, ex)| format!("{}: [{}:{}],\n", i, ex.to, ex.method)) - .collect() - } - - pub fn quick_match(&self, invoc: &InvocationTrace, extra_msg: String) { - let id = format!("[{}→{}:{}]", invoc.from, invoc.to, invoc.method); - assert_eq!( - self.from, invoc.from, - "{} unexpected from addr: expected: {}, was: {} \n{}", - id, self.from, invoc.from, extra_msg - ); - assert_eq!( - self.to, invoc.to, - "{} unexpected to addr: expected: {}, was: {} \n{}", - id, self.to, invoc.to, extra_msg - ); - assert_eq!( - self.method, invoc.method, - "{} unexpected method: expected: {}, was: {} \n{}", - id, self.method, invoc.from, extra_msg - ); - } -} - -impl Default for ExpectInvocation { - // Defaults are mainly useful for ignoring optional fields with a ..Default::default() clause. - // The addresses must generally be provided explicitly. - // Defaults include successful exit code. - fn default() -> Self { - Self { - from: Address::new_id(0), - to: Address::new_id(0), - method: 0, - value: None, - params: None, - code: ExitCode::OK, - ret: None, - subinvocs: None, - } - } -} diff --git a/api/src/vm/mod.rs b/api/src/vm/mod.rs deleted file mode 100644 index 8157deb..0000000 --- a/api/src/vm/mod.rs +++ /dev/null @@ -1,224 +0,0 @@ -/*! - * The VM module is replicated in this code tree temporarily. This is the high-level abstract interface - * for a virtual-machine that can execute Filecoin WASM actors. It defines the high-level virtual-machine - * interface, associated error and trace types and an interface to inject/override the behaviour of - * certain primitives for the purpose of running tests. - * - * TODO(alexytsu): It should eventually be moved to an external location so that it can be shared - * with the builtin-actors integration tests' implementation - */ - -use std::error::Error; -use std::fmt; - -use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_shared::address::Address; -use fvm_shared::clock::ChainEpoch; -use fvm_shared::crypto::hash::SupportedHashes; -use fvm_shared::crypto::signature::{ - Signature, SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE, -}; -use fvm_shared::econ::TokenAmount; -use fvm_shared::error::ExitCode; -use fvm_shared::piece::PieceInfo; -use fvm_shared::sector::RegisteredSealProof; -use fvm_shared::MethodNum; - -use anyhow::anyhow; -use cid::multihash::MultihashDigest; -use cid::Cid; -use multihash::Code; - -use self::test_utils::{make_piece_cid, recover_secp_public_key}; -use crate::trace::InvocationTrace; -use crate::{ActorState, ExecutionResult}; - -pub mod test_utils; - -/// An abstract VM that is injected into integration tests -pub trait VM { - /// Returns the underlying blockstore of the VM - fn blockstore(&self) -> &dyn Blockstore; - - /// Get the current chain epoch - fn epoch(&self) -> ChainEpoch; - - /// Get the balance of the specified actor - fn balance(&self, address: &Address) -> TokenAmount; - - /// Get the ID for the specified address - fn resolve_id_address(&self, address: &Address) -> Option
; - - /// Send a message between the two specified actors - fn execute_message( - &self, - from: &Address, - to: &Address, - value: &TokenAmount, - method: MethodNum, - params: Option, - ) -> Result; - - /// Send a message without charging gas - fn execute_message_implicit( - &self, - from: &Address, - to: &Address, - value: &TokenAmount, - method: MethodNum, - params: Option, - ) -> Result; - - /// Sets the epoch to the specified value - fn set_epoch(&self, epoch: ChainEpoch); - - /// Take all the invocations that have been made since the last call to this method - fn take_invocations(&self) -> Vec; - - /// Get information about an actor - fn actor(&self, address: &Address) -> Option; - - /// Provides access to VM primitives - fn primitives(&self) -> &dyn Primitives; -} - -impl From for MessageResult { - fn from(value: ExecutionResult) -> Self { - Self { - code: value.receipt.exit_code, - message: value.message, - ret: value.receipt.return_data.into(), - } - } -} - -/// Pure functions implemented as primitives by the runtime. -pub trait Primitives { - /// Hashes input data using blake2b with 256 bit output. - fn hash_blake2b(&self, data: &[u8]) -> [u8; 32]; - - /// Hashes input data using a supported hash function. - fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec; - - /// Hashes input into a 64 byte buffer - fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize); - - /// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes. - fn compute_unsealed_sector_cid( - &self, - proof_type: RegisteredSealProof, - pieces: &[PieceInfo], - ) -> Result; - - /// Verifies that a signature is valid for an address and plaintext. - fn verify_signature( - &self, - signature: &Signature, - signer: &Address, - plaintext: &[u8], - ) -> Result<(), anyhow::Error>; - - fn recover_secp_public_key( - &self, - hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], - signature: &[u8; SECP_SIG_LEN], - ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error>; -} - -// Fake implementation of runtime primitives. -// Struct members can be added here to provide configurable functionality. -pub struct FakePrimitives {} - -impl Primitives for FakePrimitives { - fn hash_blake2b(&self, data: &[u8]) -> [u8; 32] { - blake2b_simd::Params::new() - .hash_length(32) - .to_state() - .update(data) - .finalize() - .as_bytes() - .try_into() - .unwrap() - } - - fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { - let hasher = Code::try_from(hasher as u64).unwrap(); // supported hashes are all implemented in multihash - hasher.digest(data).digest().to_owned() - } - - fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { - let hasher = Code::try_from(hasher as u64).unwrap(); - let (len, buf, ..) = hasher.digest(data).into_inner(); - (buf, len as usize) - } - - fn compute_unsealed_sector_cid( - &self, - _proof_type: RegisteredSealProof, - _pieces: &[PieceInfo], - ) -> Result { - Ok(make_piece_cid(b"test data")) - } - - fn verify_signature( - &self, - signature: &Signature, - _signer: &Address, - plaintext: &[u8], - ) -> Result<(), anyhow::Error> { - if signature.bytes != plaintext { - return Err(anyhow::format_err!( - "invalid signature (mock sig validation expects siggy bytes to be equal to plaintext)" - )); - } - Ok(()) - } - - fn recover_secp_public_key( - &self, - hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], - signature: &[u8; SECP_SIG_LEN], - ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { - recover_secp_public_key(hash, signature) - .map_err(|_| anyhow!("failed to recover secp public key")) - } -} - -#[derive(Debug)] -pub struct VMError { - pub msg: String, -} - -impl fmt::Display for VMError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -impl Error for VMError { - fn description(&self) -> &str { - &self.msg - } -} - -impl From for VMError { - fn from(h_err: fvm_ipld_hamt::Error) -> Self { - vm_err(h_err.to_string().as_str()) - } -} - -pub fn vm_err(msg: &str) -> VMError { - VMError { msg: msg.to_string() } -} - -pub fn actor(code: Cid, state: Cid, sequence: u64, balance: TokenAmount) -> ActorState { - ActorState { code, state, sequence, balance } -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct MessageResult { - pub code: ExitCode, - pub message: String, - pub ret: Option, -} diff --git a/api/src/vm/test_utils.rs b/api/src/vm/test_utils.rs deleted file mode 100644 index f2fc594..0000000 --- a/api/src/vm/test_utils.rs +++ /dev/null @@ -1,53 +0,0 @@ -use cid::Cid; -use fvm_shared::{ - commcid::FIL_COMMITMENT_UNSEALED, - crypto::signature::{SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, -}; -use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; - -use multihash::{derive::Multihash, MultihashDigest}; - -#[allow(clippy::result_unit_err)] -pub fn recover_secp_public_key( - hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], - signature: &[u8; SECP_SIG_LEN], -) -> Result<[u8; SECP_PUB_LEN], ()> { - // generate types to recover key from - let rec_id = RecoveryId::parse(signature[64]).map_err(|_| ())?; - let message = Message::parse(hash); - - // Signature value without recovery byte - let mut s = [0u8; 64]; - s.copy_from_slice(signature[..64].as_ref()); - - // generate Signature - let sig = EcsdaSignature::parse_standard(&s).map_err(|_| ())?; - Ok(recover(&message, &sig, &rec_id).map_err(|_| ())?.serialize()) -} - -// multihash library doesn't support poseidon hashing, so we fake it -#[derive(Clone, Copy, Debug, PartialEq, Eq, Multihash)] -#[mh(alloc_size = 64)] -enum MhCode { - #[mh(code = 0xb401, hasher = multihash::Sha2_256)] - PoseidonFake, - #[mh(code = 0x1012, hasher = multihash::Sha2_256)] - Sha256TruncPaddedFake, -} - -fn make_cid(input: &[u8], prefix: u64, hash: MhCode) -> Cid { - let hash = hash.digest(input); - Cid::new_v1(prefix, hash) -} - -pub fn make_cid_sha(input: &[u8], prefix: u64) -> Cid { - make_cid(input, prefix, MhCode::Sha256TruncPaddedFake) -} - -pub fn make_cid_poseidon(input: &[u8], prefix: u64) -> Cid { - make_cid(input, prefix, MhCode::PoseidonFake) -} - -pub fn make_piece_cid(input: &[u8]) -> Cid { - make_cid_sha(input, FIL_COMMITMENT_UNSEALED) -} diff --git a/api/src/wrangler.rs b/api/src/wrangler.rs index 019c469..8c0439d 100644 --- a/api/src/wrangler.rs +++ b/api/src/wrangler.rs @@ -2,9 +2,8 @@ use std::cell::RefCell; use std::collections::HashMap; use anyhow::anyhow; -use fvm_ipld_encoding::de; - use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::de; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{from_slice, RawBytes}; use fvm_shared::address::Address; @@ -13,15 +12,11 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::message::Message; use fvm_shared::{ActorID, MethodNum, BLOCK_GAS_LIMIT}; +use vm_api::trace::InvocationTrace; +use vm_api::{vm_err, ActorState, MessageResult, Primitives, VMError, VM}; -use crate::trace::InvocationTrace; -use crate::ActorState; pub use crate::{Bench, ExecutionResult}; -// TODO: import these from an external location -// https://github.com/anorth/fvm-workbench/issues/20 -pub use crate::vm::{FakePrimitives, MessageResult, Primitives, VMError, VM}; - pub struct ExecutionWrangler { bench: RefCell>, store: Box, @@ -29,6 +24,7 @@ pub struct ExecutionWrangler { gas_limit: u64, gas_fee_cap: TokenAmount, gas_premium: TokenAmount, + circulating_supply: RefCell, sequences: RefCell>, msg_length: usize, compute_msg_length: bool, @@ -38,33 +34,53 @@ pub struct ExecutionWrangler { impl ExecutionWrangler { /// Creates a new wrangler wrapping a given Bench. The store passed here must be a handle that /// operates on the same underlying storage as the bench + #[allow(clippy::too_many_arguments)] pub fn new( bench: Box, store: Box, + primitives: Box, version: u64, gas_limit: u64, gas_fee_cap: TokenAmount, gas_premium: TokenAmount, + circulating_supply: TokenAmount, compute_msg_length: bool, ) -> Self { Self { bench: RefCell::new(bench), store, + primitives, version, gas_limit, gas_fee_cap, gas_premium, + circulating_supply: RefCell::new(circulating_supply), sequences: RefCell::new(HashMap::new()), msg_length: 0, compute_msg_length, - primitives: Box::new(FakePrimitives {}), } } /// Creates a new wrangler wrapping a given Bench. The store passed here must be a handle that /// operates on the same underlying storage as the bench - pub fn new_default(bench: Box, store: Box) -> Self { - Self::new(bench, store, 0, BLOCK_GAS_LIMIT, TokenAmount::zero(), TokenAmount::zero(), true) + pub fn new_default( + bench: Box, + store: Box, + primitives: Box, + ) -> Self { + let reward_total = TokenAmount::from_whole(1_100_000_000i64); + let faucet_total = TokenAmount::from_whole(1_000_000_000i64); + Self::new( + bench, + store, + primitives, + 0, + BLOCK_GAS_LIMIT, + TokenAmount::zero(), + TokenAmount::zero(), + reward_total + faucet_total, + true, + ) } pub fn execute( @@ -226,7 +242,7 @@ impl VM for ExecutionWrangler { let raw_params = params.map_or(RawBytes::default(), |block| RawBytes::from(block.data)); match self.execute(*from, *to, method, raw_params, value.clone()) { Ok(res) => Ok(res.into()), - Err(e) => Err(VMError { msg: e.to_string() }), + Err(e) => Err(vm_err(&e.to_string())), } } @@ -241,7 +257,7 @@ impl VM for ExecutionWrangler { let raw_params = params.map_or(RawBytes::default(), |block| RawBytes::from(block.data)); match self.execute_implicit(*from, *to, method, raw_params, value.clone()) { Ok(res) => Ok(res.into()), - Err(e) => Err(VMError { msg: e.to_string() }), + Err(e) => Err(vm_err(&e.to_string())), } } @@ -262,4 +278,20 @@ impl VM for ExecutionWrangler { fn primitives(&self) -> &dyn Primitives { self.primitives.as_ref() } + + fn set_circulating_supply(&self, supply: TokenAmount) { + self.circulating_supply.replace(supply); + } + + fn circulating_supply(&self) -> TokenAmount { + self.circulating_supply.borrow().clone() + } + + fn actor_manifest(&self) -> std::collections::BTreeMap { + todo!() + } + + fn state_root(&self) -> cid::Cid { + todo!() + } } diff --git a/builtin/Cargo.toml b/builtin/Cargo.toml index fdf72b8..18c73f0 100644 --- a/builtin/Cargo.toml +++ b/builtin/Cargo.toml @@ -26,7 +26,6 @@ fil_actor_system = { version = "12.0.0", git = "https://github.com/filecoin-proj fil_actor_verifreg = { version = "12.0.0", git = "https://github.com/filecoin-project/builtin-actors", branch = "master", features = [] } fil_actors_runtime = { version = "12.0.0", git = "https://github.com/filecoin-project/builtin-actors", branch = "master", features = [] } - fvm = { workspace = true } fvm_actor_utils = { workspace = true } fvm_ipld_bitfield = { workspace = true } @@ -34,19 +33,18 @@ fvm_ipld_blockstore = { workspace = true } fvm_ipld_encoding = { workspace = true } fvm_ipld_hamt = { workspace = true } fvm_shared = { workspace = true } +vm_api = { workspace = true } # Used directly in some tests frc46_token = { workspace = true } -anyhow = "~1.0.47" -blake2b_simd = "1.0.0" -bls-signatures = { version = "0.13", default-features = false } +anyhow = { workspace = true } +blake2b_simd = { workspace = true } +bls-signatures = { workspace = true } cid = { workspace = true } -libsecp256k1 = { version = "0.7" } -multihash = { version = "~0.16.1", default-features = false } -rand_chacha = "~0.3" -thiserror = "~1.0.30" -num-traits = "0.2.14" +libsecp256k1 = { workspace = true } +rand_chacha = { workspace = true } +num-traits = { workspace = true } [dev-dependencies] log = "0.4" diff --git a/builtin/tests/batch_onboarding_deals.rs b/builtin/tests/batch_onboarding_deals.rs index 1ebc2cb..548f05d 100644 --- a/builtin/tests/batch_onboarding_deals.rs +++ b/builtin/tests/batch_onboarding_deals.rs @@ -10,14 +10,13 @@ use fvm_shared::sector::{RegisteredSealProof, StoragePower}; use fvm_shared::state::StateTreeVersion; use fvm_shared::version::NetworkVersion; use fvm_workbench_api::blockstore::DynBlockstore; -use fvm_workbench_api::{ - wrangler::{ExecutionWrangler, VM}, - WorkbenchBuilder, -}; +use fvm_workbench_api::{wrangler::ExecutionWrangler, WorkbenchBuilder}; use fvm_workbench_builtin_actors::genesis::{create_genesis_actors, GenesisSpec}; use fvm_workbench_vm::builder::FvmBenchBuilder; use fvm_workbench_vm::externs::FakeExterns; +use fvm_workbench_vm::primitives::FakePrimitives; use num_traits::Zero; +use vm_api::VM; use fil_actor_market::{deal_id_key, DealProposal}; use fil_actor_miner::{ @@ -53,7 +52,7 @@ fn batch_onboarding_deals() { let spec = GenesisSpec::default(manifest_data_cid); let _genesis = create_genesis_actors(&mut builder, &spec).unwrap(); let bench = builder.build().unwrap(); - let w = ExecutionWrangler::new_default(bench, Box::new(store)); + let w = ExecutionWrangler::new_default(bench, Box::new(store), Box::new(FakePrimitives {})); batch_onboarding_deals_test(&w); } diff --git a/builtin/tests/util/deals.rs b/builtin/tests/util/deals.rs index 40b0fcc..da1adaf 100644 --- a/builtin/tests/util/deals.rs +++ b/builtin/tests/util/deals.rs @@ -11,9 +11,9 @@ use fvm_shared::crypto::signature::{Signature, SignatureType}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::piece::PaddedPieceSize; -use fvm_workbench_api::vm::test_utils::make_piece_cid; -use fvm_workbench_api::wrangler::VM; +use fvm_workbench_vm::bench::kernel::make_piece_cid; use num_traits::Zero; +use vm_api::VM; use crate::util::{apply_ok, serialize_ok}; diff --git a/builtin/tests/util/hookup.rs b/builtin/tests/util/hookup.rs index 6c37b05..9be1b13 100644 --- a/builtin/tests/util/hookup.rs +++ b/builtin/tests/util/hookup.rs @@ -7,11 +7,13 @@ use fvm_shared::state::StateTreeVersion; use fvm_shared::version::NetworkVersion; use fvm_shared::METHOD_SEND; // use fvm_workbench_api::analysis::TraceAnalysis; -use fvm_workbench_api::wrangler::{ExecutionWrangler, VM}; +use fvm_workbench_api::wrangler::ExecutionWrangler; use fvm_workbench_api::WorkbenchBuilder; use fvm_workbench_builtin_actors::genesis::{create_genesis_actors, GenesisSpec}; use fvm_workbench_vm::builder::FvmBenchBuilder; use fvm_workbench_vm::externs::FakeExterns; +use fvm_workbench_vm::primitives::FakePrimitives; +use vm_api::VM; #[test] fn test_hookup() { @@ -28,7 +30,8 @@ fn test_hookup() { let spec = GenesisSpec::default(manifest_data_cid); let genesis = create_genesis_actors(&mut builder, &spec).unwrap(); let bench = builder.build().unwrap(); - let wrangler = ExecutionWrangler::new_default(bench, Box::new(store)); + let wrangler = + ExecutionWrangler::new_default(bench, Box::new(store), Box::new(FakePrimitives {})); let result = wrangler .execute_message( diff --git a/builtin/tests/util/mod.rs b/builtin/tests/util/mod.rs index ec81397..c688253 100644 --- a/builtin/tests/util/mod.rs +++ b/builtin/tests/util/mod.rs @@ -29,10 +29,10 @@ use fvm_shared::sector::{RegisteredSealProof, SectorNumber, StoragePower}; use fvm_shared::smooth::FilterEstimate; use fvm_shared::{ActorID, MethodNum}; use fvm_workbench_api::blockstore::DynBlockstore; -use fvm_workbench_api::wrangler::VM; use fvm_workbench_vm::bench::kernel::{make_cid, MhCode}; use rand_chacha::rand_core::SeedableRng; use rand_chacha::ChaCha8Rng; +use vm_api::VM; pub mod deals; pub mod hookup; diff --git a/builtin/tests/util/workflows.rs b/builtin/tests/util/workflows.rs index a33445c..6e8b9a5 100644 --- a/builtin/tests/util/workflows.rs +++ b/builtin/tests/util/workflows.rs @@ -21,9 +21,9 @@ use fvm_shared::sector::{ PoStProof, RegisteredPoStProof, RegisteredSealProof, SectorNumber, StoragePower, }; use fvm_shared::METHOD_SEND; -use fvm_workbench_api::wrangler::VM; use fvm_workbench_vm::bench::kernel::TEST_VM_RAND_ARRAY; use rand_chacha::rand_core::RngCore; +use vm_api::VM; use super::*; diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 1268b57..a137aa6 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -14,10 +14,12 @@ fvm_ipld_blockstore = { workspace = true } fvm_ipld_car = { workspace = true } fvm_ipld_encoding = { workspace = true } fvm_shared = { workspace = true } +vm_api = { workspace = true } anyhow = "~1.0.47" +blake2b_simd = { version = "1.0" } cid = { workspace = true } -futures = "~0.3.19" -multihash = { version = "0.18.1", default-features = false } -replace_with = "0.1.7" -thiserror = "~1.0.30" +futures = { workspace = true } +libsecp256k1 = { workspace = true } +multihash = { workspace = true } +replace_with = { workspace = true } diff --git a/vm/src/bench/mod.rs b/vm/src/bench/mod.rs index d250ffa..f3d5ab6 100644 --- a/vm/src/bench/mod.rs +++ b/vm/src/bench/mod.rs @@ -13,7 +13,8 @@ use fvm_shared::message::Message; use fvm_shared::ActorID; use fvm_workbench_api::trace::ExecutionEvent::{Call, CallError, CallReturn, GasCharge}; use fvm_workbench_api::trace::ExecutionTrace; -use fvm_workbench_api::{ActorState, Bench, ExecutionResult}; +use fvm_workbench_api::{Bench, ExecutionResult}; +use vm_api::ActorState; use crate::externs::FakeExterns; @@ -74,8 +75,11 @@ where Ok(raw.map(|a| ActorState { code: a.code, state: a.state, - sequence: a.sequence, + call_seq: a.sequence, balance: a.balance, + // TODO: possibly rename predictable address if these are the same concept + // in ref-fvm predictable address is assigned to delegated address in some instances + predictable_address: a.delegated_address, })) } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 6ed42b9..77f3cc8 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -1,3 +1,4 @@ pub mod bench; pub mod builder; pub mod externs; +pub mod primitives; diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs new file mode 100644 index 0000000..33934ef --- /dev/null +++ b/vm/src/primitives.rs @@ -0,0 +1,93 @@ +use anyhow::anyhow; +use cid::Cid; +use fvm_shared::{ + address::Address, + crypto::{ + hash::SupportedHashes, + signature::{Signature, SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE}, + }, + piece::PieceInfo, + sector::RegisteredSealProof, +}; +use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature}; +use multihash::{Code, MultihashDigest}; +use vm_api::Primitives; + +use crate::bench::kernel::make_piece_cid; + +// Fake implementation of runtime primitives. +// Struct members can be added here to provide configurable functionality. +pub struct FakePrimitives {} + +impl Primitives for FakePrimitives { + fn hash_blake2b(&self, data: &[u8]) -> [u8; 32] { + blake2b_simd::Params::new() + .hash_length(32) + .to_state() + .update(data) + .finalize() + .as_bytes() + .try_into() + .unwrap() + } + + fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec { + let hasher = Code::try_from(hasher as u64).unwrap(); // supported hashes are all implemented in multihash + hasher.digest(data).digest().to_owned() + } + + fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) { + let hasher = Code::try_from(hasher as u64).unwrap(); + let (len, buf, ..) = hasher.digest(data).into_inner(); + (buf, len as usize) + } + + fn compute_unsealed_sector_cid( + &self, + _proof_type: RegisteredSealProof, + _pieces: &[PieceInfo], + ) -> Result { + Ok(make_piece_cid(b"test data")) + } + + fn verify_signature( + &self, + signature: &Signature, + _signer: &Address, + plaintext: &[u8], + ) -> Result<(), anyhow::Error> { + if signature.bytes != plaintext { + return Err(anyhow::format_err!( + "invalid signature (mock sig validation expects siggy bytes to be equal to plaintext)" + )); + } + Ok(()) + } + + fn recover_secp_public_key( + &self, + hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + signature: &[u8; SECP_SIG_LEN], + ) -> Result<[u8; SECP_PUB_LEN], anyhow::Error> { + recover_secp_public_key(hash, signature) + .map_err(|_| anyhow!("failed to recover secp public key")) + } +} + +#[allow(clippy::result_unit_err)] +pub fn recover_secp_public_key( + hash: &[u8; SECP_SIG_MESSAGE_HASH_SIZE], + signature: &[u8; SECP_SIG_LEN], +) -> Result<[u8; SECP_PUB_LEN], ()> { + // generate types to recover key from + let rec_id = RecoveryId::parse(signature[64]).map_err(|_| ())?; + let message = Message::parse(hash); + + // Signature value without recovery byte + let mut s = [0u8; 64]; + s.copy_from_slice(signature[..64].as_ref()); + + // generate Signature + let sig = EcsdaSignature::parse_standard(&s).map_err(|_| ())?; + Ok(recover(&message, &sig, &rec_id).map_err(|_| ())?.serialize()) +}