Skip to content

Instantly share code, notes, and snippets.

@Stebalien
Created October 31, 2022 16:31
Show Gist options
  • Save Stebalien/084ff93dde46527da14a8afa395c1cfb to your computer and use it in GitHub Desktop.
Save Stebalien/084ff93dde46527da14a8afa395c1cfb to your computer and use it in GitHub Desktop.
Diff between next & master, after merge
diff --git a/.github/actions/rust-cargo-run/action.yml b/.github/actions/rust-cargo-run/action.yml
index ee2aa8cf..643b872d 100644
--- a/.github/actions/rust-cargo-run/action.yml
+++ b/.github/actions/rust-cargo-run/action.yml
@@ -20,7 +20,7 @@ inputs:
cache_name:
description: The name of the cache to save/restore
required: true
- default: v2-test
+ default: test
runs:
using: composite
@@ -41,7 +41,7 @@ runs:
CACHE_SKIP_SAVE: ${{ inputs.save_cache == '' || inputs.save_cache == 'false' }}
with:
version: v0.2.15
- shared-key: ${{ inputs.cache_name }} # change this to invalidate sccache for this job
+ shared-key: v4-${{ inputs.cache_name }} # change this to invalidate sccache for this job
- name: Running ${{ inputs.command }}
uses: actions-rs/cargo@844f36862e911db73fe0815f00a4a2602c279505 # v1.0.3
env:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e36cb97a..f2774509 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- master
+ - next
pull_request:
env:
@@ -43,7 +44,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs' ]
+ network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ]
steps:
- name: Checking out
uses: actions/checkout@v2
@@ -57,6 +58,10 @@ jobs:
BUILD_FIL_NETWORK: ${{ matrix.network }}
run: |
cargo run --locked -- -o output/builtin-actors.car
+ - name: Checking no wasm-bindgen references
+ run: |
+ sudo apt-get update && sudo apt-get -y install wabt parallel
+ find ./target -name '*.wasm' -print0 | parallel -0 --halt now,fail=1 sh -c 'wasm2wat --enable-bulk-memory {} | grep bindgen; test $? -ne 0'
coverage:
runs-on: ubuntu-latest
env:
@@ -70,7 +75,7 @@ jobs:
command: version
components: llvm-tools-preview
github_token: ${{ secrets.GITHUB_TOKEN }}
- cache_name: v3-cov
+ cache_name: cov
save_cache: true
- name: Put LLVM tools into the PATH
run: echo "${HOME}/.rustup/toolchains/$(cat rust-toolchain)-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin" >> $GITHUB_PATH
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 36326fcc..9b007046 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,7 +16,7 @@ jobs:
CACHE_SKIP_SAVE: ${{ matrix.push == '' || matrix.push == 'false' }}
strategy:
matrix:
- network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs' ]
+ network: [ 'mainnet', 'caterpillarnet', 'butterflynet', 'calibrationnet', 'devnet', 'testing', 'testing-fake-proofs', 'wallaby', 'devnet-wasm' ]
steps:
- name: Checking out
uses: actions/checkout@v2
diff --git a/.gitignore b/.gitignore
index 609dd565..5b6079d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@ target
*.wasm
.idea/
**/.DS_Store
-output/builtin-actors.car
+output/*.car
diff --git a/Cargo.toml b/Cargo.toml
index 3824cc6b..b4ae4c41 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,8 @@ publish = false
[target.'cfg(target_arch = "wasm32")'.dependencies]
fil_actor_account = { version = "10.0.0-alpha.1", path = "./actors/account", features = ["fil-actor"] }
+fil_actor_embryo = { version = "10.0.0-alpha.1", path = "./actors/embryo", features = ["fil-actor"] }
+fil_actor_evm = { version = "10.0.0-alpha.1", path = "./actors/evm", features = ["fil-actor"] }
fil_actor_cron = { version = "10.0.0-alpha.1", path = "./actors/cron", features = ["fil-actor"] }
fil_actor_datacap = { version = "10.0.0-alpha.1", path = "./actors/datacap", features = ["fil-actor"] }
fil_actor_init = { version = "10.0.0-alpha.1", path = "./actors/init", features = ["fil-actor"] }
@@ -27,7 +29,7 @@ fil_actor_system = { version = "10.0.0-alpha.1", path = "./actors/system", featu
fil_actor_verifreg = { version = "10.0.0-alpha.1", path = "./actors/verifreg", features = ["fil-actor"] }
[build-dependencies]
-fil_actor_bundler = "4.0.0"
+fil_actor_bundler = "4.1.0"
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "runtime" }
num-traits = "0.2.15"
@@ -44,6 +46,7 @@ calibrationnet = []
devnet = []
testing = []
testing-fake-proofs = []
+m2-native = []
[workspace]
members = [
@@ -53,7 +56,11 @@ members = [
"test_vm",
]
-#[patch.crates-io]
+[patch.crates-io]
+frc42_dispatch = { git = "https://github.com/filecoin-project/filecoin-actor-utils", branch = "feat/fvm-m2" }
+frc46_token = { git = "https://github.com/filecoin-project/filecoin-actor-utils", branch = "feat/fvm-m2" }
+fvm_actor_utils = { git = "https://github.com/filecoin-project/filecoin-actor-utils", branch = "feat/fvm-m2" }
+
#fvm_shared = { git = "https://github.com/filecoin-project/ref-fvm" }
#fvm_sdk = { git = "https://github.com/filecoin-project/ref-fvm" }
#fvm_ipld_hamt = { git = "https://github.com/filecoin-project/ref-fvm" }
diff --git a/Makefile b/Makefile
index db5a24de..ee3907a1 100644
--- a/Makefile
+++ b/Makefile
@@ -62,6 +62,12 @@ bundle-calibrationnet: deps-build
bundle-devnet: deps-build
BUILD_FIL_NETWORK=devnet cargo run -- -o output/builtin-actors-devnet.car
+bundle-wallaby: deps-build
+ BUILD_FIL_NETWORK=wallaby cargo run -- -o output/builtin-actors-wallaby.car
+
+bundle-devnet-wasm: deps-build
+ BUILD_FIL_NETWORK=devnet-wasm cargo run -- -o output/builtin-actors-devnet-wasm.car
+
bundle-testing: deps-build
BUILD_FIL_NETWORK=testing cargo run -- -o output/builtin-actors-testing.car
BUILD_FIL_NETWORK=testing-fake-proofs cargo run -- -o output/builtin-actors-testing-fake-proofs.car
diff --git a/actors/account/Cargo.toml b/actors/account/Cargo.toml
index 6ae98064..8973fe58 100644
--- a/actors/account/Cargo.toml
+++ b/actors/account/Cargo.toml
@@ -15,12 +15,12 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
frc42_dispatch = "1.0.0"
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
serde = { version = "1.0.136", features = ["derive"] }
num-traits = "0.2.14"
num-derive = "0.3.3"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
anyhow = "1.0.65"
[dev-dependencies]
diff --git a/actors/cron/Cargo.toml b/actors/cron/Cargo.toml
index d1eb7b6d..4efe9455 100644
--- a/actors/cron/Cargo.toml
+++ b/actors/cron/Cargo.toml
@@ -15,13 +15,13 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
num-traits = "0.2.14"
num-derive = "0.3.3"
log = "0.4.14"
serde = { version = "1.0.136", features = ["derive"] }
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
[dev-dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] }
diff --git a/actors/datacap/Cargo.toml b/actors/datacap/Cargo.toml
index ea8f2202..cf1eb3ad 100644
--- a/actors/datacap/Cargo.toml
+++ b/actors/datacap/Cargo.toml
@@ -21,9 +21,9 @@ frc42_dispatch = "1.0.0"
frc46_token = "1.1.0"
fvm_actor_utils = "0.1.0"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
-fvm_ipld_hamt = "0.5.1"
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_ipld_encoding = "0.3.0"
+fvm_ipld_hamt = "0.6.0"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
lazy_static = "1.4.0"
num-derive = "0.3.3"
num-traits = "0.2.14"
@@ -34,4 +34,3 @@ log = "0.4.14"
fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] }
[features]
fil-actor = ["fil_actors_runtime/fil-actor"]
-
diff --git a/actors/embryo/Cargo.toml b/actors/embryo/Cargo.toml
new file mode 100644
index 00000000..374dc940
--- /dev/null
+++ b/actors/embryo/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "fil_actor_embryo"
+description = "Builtin embryo actor for Filecoin"
+version = "10.0.0-alpha.1"
+license = "MIT OR Apache-2.0"
+authors = ["Protocol Labs", "Filecoin Core Devs"]
+edition = "2021"
+keywords = ["filecoin", "web3", "wasm"]
+
+[lib]
+## lib is necessary for integration tests
+## cdylib is necessary for Wasm build
+crate-type = ["cdylib", "lib"]
+
+[dependencies]
+fvm_sdk = { version = "3.0.0-alpha.8", optional = true }
+fvm_shared = { version = "3.0.0-alpha.8", optional = true }
+
+[features]
+fil-actor = ["fvm_sdk", "fvm_shared"]
diff --git a/actors/embryo/src/lib.rs b/actors/embryo/src/lib.rs
new file mode 100644
index 00000000..d33f2bfd
--- /dev/null
+++ b/actors/embryo/src/lib.rs
@@ -0,0 +1,8 @@
+#[cfg(feature = "fil-actor")]
+#[no_mangle]
+pub extern "C" fn invoke(_: u32) -> u32 {
+ fvm_sdk::vm::abort(
+ fvm_shared::error::ExitCode::USR_UNHANDLED_MESSAGE.value(),
+ Some("embryo actors may only receive messages on method 0"),
+ )
+}
diff --git a/actors/init/Cargo.toml b/actors/init/Cargo.toml
index 5a316801..6a84116e 100644
--- a/actors/init/Cargo.toml
+++ b/actors/init/Cargo.toml
@@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
-fvm_ipld_hamt = "0.5.1"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
+fvm_ipld_hamt = "0.6.0"
serde = { version = "1.0.136", features = ["derive"] }
num-traits = "0.2.14"
num-derive = "0.3.3"
@@ -24,10 +24,11 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"]
anyhow = "1.0.65"
log = "0.4.14"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
[dev-dependencies]
-fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] }
+fil_actors_runtime = { path = "../../runtime", features = ["test_utils", "sector-default"] }
[features]
fil-actor = ["fil_actors_runtime/fil-actor"]
+m2-native = ["fil_actors_runtime/m2-native"]
diff --git a/actors/init/src/lib.rs b/actors/init/src/lib.rs
index 63dab273..b0cf0355 100644
--- a/actors/init/src/lib.rs
+++ b/actors/init/src/lib.rs
@@ -1,10 +1,13 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT
+use std::iter;
+
use cid::Cid;
-use fil_actors_runtime::runtime::builtins::Type;
use fil_actors_runtime::runtime::{ActorCode, Runtime};
-use fil_actors_runtime::{actor_error, cbor, ActorContext, ActorError, SYSTEM_ACTOR_ADDR};
+use fil_actors_runtime::{
+ actor_error, cbor, ActorContext, ActorError, EAM_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
+};
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
@@ -28,6 +31,9 @@ fil_actors_runtime::wasm_trampoline!(Actor);
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
Exec = 2,
+ Exec4 = 3,
+ #[cfg(feature = "m2-native")]
+ InstallCode = 4,
}
/// Init actor
@@ -80,14 +86,14 @@ impl Actor {
log::trace!("robust address: {:?}", &robust_address);
// Allocate an ID for this actor.
- // Store mapping of pubkey or actor address to actor ID
+ // Store mapping of actor addresses to the actor ID.
let id_address: ActorID = rt.transaction(|s: &mut State, rt| {
s.map_address_to_new_id(rt.store(), &robust_address)
.context("failed to allocate ID address")
})?;
// Create an empty actor
- rt.create_actor(params.code_cid, id_address)?;
+ rt.create_actor(params.code_cid, id_address, None)?;
// Invoke constructor
rt.send(
@@ -100,6 +106,99 @@ impl Actor {
Ok(ExecReturn { id_address: Address::new_id(id_address), robust_address })
}
+
+ /// Exec init actor
+ pub fn exec4<BS, RT>(rt: &mut RT, params: Exec4Params) -> Result<Exec4Return, ActorError>
+ where
+ BS: Blockstore,
+ RT: Runtime<BS>,
+ {
+ if cfg!(feature = "m2-native") {
+ rt.validate_immediate_caller_accept_any()?;
+ } else {
+ rt.validate_immediate_caller_is(iter::once(&EAM_ACTOR_ADDR))?;
+ }
+
+ // Compute the f4 address.
+ let caller_id = rt.message().caller().id().unwrap();
+ let delegated_address =
+ Address::new_delegated(caller_id, &params.subaddress).map_err(|e| {
+ ActorError::illegal_argument(format!("invalid delegated address: {}", e))
+ })?;
+
+ log::trace!("delegated address: {:?}", &delegated_address);
+
+ // Compute a re-org-stable address.
+ // This address exists for use by messages coming from outside the system, in order to
+ // stably address the newly created actor even if a chain re-org causes it to end up with
+ // a different ID.
+ let robust_address = rt.new_actor_address()?;
+
+ log::trace!("robust address: {:?}", &robust_address);
+
+ // Allocate an ID for this actor.
+ // Store mapping of actor addresses to the actor ID.
+ let id_address: ActorID = rt.transaction(|s: &mut State, rt| {
+ s.map_address_to_f4(rt.store(), &robust_address, &delegated_address)
+ .context("constructor failed")
+ })?;
+
+ // Create an empty actor
+ rt.create_actor(params.code_cid, id_address, Some(delegated_address))?;
+
+ // Invoke constructor
+ rt.send(
+ &Address::new_id(id_address),
+ METHOD_CONSTRUCTOR,
+ params.constructor_params,
+ rt.message().value_received(),
+ )
+ .context("constructor failed")?;
+
+ Ok(Exec4Return { id_address: Address::new_id(id_address), robust_address })
+ }
+
+ #[cfg(feature = "m2-native")]
+ pub fn install<BS, RT>(rt: &mut RT, params: InstallParams) -> Result<InstallReturn, ActorError>
+ where
+ BS: Blockstore,
+ RT: Runtime<BS>,
+ {
+ use cid::multihash::Code;
+ use fil_actors_runtime::AsActorError;
+ use fvm_ipld_blockstore::Block;
+ use fvm_shared::error::ExitCode;
+
+ rt.validate_immediate_caller_accept_any()?;
+
+ let (code_cid, installed) = rt.transaction(|st: &mut State, rt| {
+ let code = params.code.bytes();
+ let code_cid = rt.store().put(Code::Blake2b256, &Block::new(0x55, code)).context_code(
+ ExitCode::USR_SERIALIZATION,
+ "failed to put code into the bockstore",
+ )?;
+
+ if st.is_installed_actor(rt.store(), &code_cid).context_code(
+ ExitCode::USR_ILLEGAL_STATE,
+ "failed to check state for installed actor",
+ )? {
+ return Ok((code_cid, false));
+ }
+
+ rt.install_actor(&code_cid).context_code(
+ ExitCode::USR_ILLEGAL_ARGUMENT,
+ "failed to check state for installed actor",
+ )?;
+
+ st.add_installed_actor(rt.store(), code_cid).context_code(
+ ExitCode::USR_ILLEGAL_STATE,
+ "failed to add installed actor to state",
+ )?;
+ Ok((code_cid, true))
+ })?;
+
+ Ok(InstallReturn { code_cid, installed })
+ }
}
impl ActorCode for Actor {
@@ -121,16 +220,28 @@ impl ActorCode for Actor {
let res = Self::exec(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
+ Some(Method::Exec4) => {
+ let res = Self::exec4(rt, cbor::deserialize_params(params)?)?;
+ Ok(RawBytes::serialize(res)?)
+ }
+ #[cfg(feature = "m2-native")]
+ Some(Method::InstallCode) => {
+ let res = Self::install(rt, cbor::deserialize_params(params)?)?;
+ Ok(RawBytes::serialize(res)?)
+ }
None => Err(actor_error!(unhandled_message; "Invalid method")),
}
}
}
+#[cfg(not(feature = "m2-native"))]
fn can_exec<BS, RT>(rt: &RT, caller: &Cid, exec: &Cid) -> bool
where
BS: Blockstore,
RT: Runtime<BS>,
{
+ use fil_actors_runtime::runtime::builtins::Type;
+
rt.resolve_builtin_actor_type(exec)
.map(|typ| match typ {
Type::Multisig | Type::PaymentChannel => true,
@@ -139,3 +250,15 @@ where
})
.unwrap_or(false)
}
+
+#[cfg(feature = "m2-native")]
+fn can_exec<BS, RT>(_rt: &RT, _caller: &Cid, _exec: &Cid) -> bool
+where
+ BS: Blockstore,
+ RT: Runtime<BS>,
+{
+ // TODO figure out ACLs -- m2-native allows exec for everyone for now
+ // maybe we should leave this as is for production, but at least we should
+ // consider adding relevant ACLs.
+ true
+}
diff --git a/actors/init/src/state.rs b/actors/init/src/state.rs
index 3829ca91..c93f4f1b 100644
--- a/actors/init/src/state.rs
+++ b/actors/init/src/state.rs
@@ -1,6 +1,8 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT
+#[cfg(feature = "m2-native")]
+use cid::multihash::Code;
use cid::Cid;
use fil_actors_runtime::{
actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorError, AsActorError,
@@ -9,6 +11,8 @@ use fil_actors_runtime::{
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::tuple::*;
use fvm_ipld_encoding::Cbor;
+#[cfg(feature = "m2-native")]
+use fvm_ipld_encoding::CborStore;
use fvm_shared::address::{Address, Protocol};
use fvm_shared::error::ExitCode;
use fvm_shared::{ActorID, HAMT_BIT_WIDTH};
@@ -19,6 +23,8 @@ pub struct State {
pub address_map: Cid,
pub next_id: ActorID,
pub network_name: String,
+ #[cfg(feature = "m2-native")]
+ pub installed_actors: Cid,
}
impl State {
@@ -26,13 +32,25 @@ impl State {
let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH)
.flush()
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?;
- Ok(Self { address_map: empty_map, next_id: FIRST_NON_SINGLETON_ADDR, network_name })
+ #[cfg(feature = "m2-native")]
+ let installed_actors = store.put_cbor(&Vec::<Cid>::new(), Code::Blake2b256).context_code(
+ ExitCode::USR_ILLEGAL_STATE,
+ "failed to create installed actors object",
+ )?;
+ Ok(Self {
+ address_map: empty_map,
+ next_id: FIRST_NON_SINGLETON_ADDR,
+ network_name,
+ #[cfg(feature = "m2-native")]
+ installed_actors,
+ })
}
/// Allocates a new ID address and stores a mapping of the argument address to it.
/// Fails if the argument address is already present in the map to facilitate a tombstone
/// for when the predictable robust address generation is implemented.
- /// Returns the newly-allocated address.
+ ///
+ /// Returns the newly-allocated actor ID.
pub fn map_address_to_new_id<BS: Blockstore>(
&mut self,
store: &BS,
@@ -61,6 +79,56 @@ impl State {
Ok(id)
}
+ /// Allocates a new ID address and stores a mapping of the argument addresses to it.
+ /// Returns the newly-allocated actor ID.
+ pub fn map_address_to_f4<BS: Blockstore>(
+ &mut self,
+ store: &BS,
+ addr: &Address,
+ f4addr: &Address,
+ ) -> Result<ActorID, ActorError>
+ where
+ BS: Blockstore,
+ {
+ let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?;
+
+ // Assign a new ID address, or use the one currently mapped to the f4 address. We don't
+ // bother checking if the target actor is an embryo here, the FVM will check that when we go to create the actor.
+ let f4addr_key = f4addr.to_bytes().into();
+ let id: u64 = match map
+ .get(&f4addr_key)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lookup f4 address in map")?
+ {
+ Some(id) => *id,
+ None => {
+ let id = self.next_id;
+ self.next_id += 1;
+ map.set(f4addr_key, id)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set f4 address in map")?;
+ id
+ }
+ };
+
+ // Then go ahead and assign the f2 address.
+ let is_new = map
+ .set_if_absent(addr.to_bytes().into(), id)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to set map key")?;
+ if !is_new {
+ // this is impossible today as the robust address is a hash of unique inputs
+ // but in close future predictable address generation will make this possible
+ return Err(actor_error!(
+ forbidden,
+ "robust address {} is already allocated in the address map",
+ addr
+ ));
+ }
+ self.address_map =
+ map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?;
+
+ Ok(id)
+ }
+
/// ResolveAddress resolves an address to an ID-address, if possible.
/// If the provided address is an ID address, it is returned as-is.
/// This means that mapped ID-addresses (which should only appear as values, not keys) and
@@ -88,6 +156,44 @@ impl State {
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get address entry")?;
Ok(found.copied().map(Address::new_id))
}
+
+ /// Check to see if an actor is already installed
+ #[cfg(feature = "m2-native")]
+ pub fn is_installed_actor<BS: Blockstore>(
+ &self,
+ store: &BS,
+ cid: &Cid,
+ ) -> Result<bool, ActorError> {
+ let installed: Vec<Cid> = match store
+ .get_cbor(&self.installed_actors)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load installed actor list")?
+ {
+ Some(v) => v,
+ None => Vec::new(),
+ };
+ Ok(installed.contains(cid))
+ }
+
+ /// Adds a new code Cid to the list of installed actors.
+ #[cfg(feature = "m2-native")]
+ pub fn add_installed_actor<BS: Blockstore>(
+ &mut self,
+ store: &BS,
+ cid: Cid,
+ ) -> Result<(), ActorError> {
+ let mut installed: Vec<Cid> = match store
+ .get_cbor(&self.installed_actors)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load installed actor list")?
+ {
+ Some(v) => v,
+ None => Vec::new(),
+ };
+ installed.push(cid);
+ self.installed_actors = store
+ .put_cbor(&installed, Code::Blake2b256)
+ .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to save installed actor list")?;
+ Ok(())
+ }
}
impl Cbor for State {}
diff --git a/actors/init/src/testing.rs b/actors/init/src/testing.rs
index 88c09459..9fc069c6 100644
--- a/actors/init/src/testing.rs
+++ b/actors/init/src/testing.rs
@@ -29,7 +29,8 @@ pub fn check_state_invariants<BS: Blockstore>(
let mut init_summary = StateSummary { ids_by_address: HashMap::new(), next_id: state.next_id };
- let mut address_by_id = HashMap::<ActorID, Address>::new();
+ let mut stable_address_by_id = HashMap::<ActorID, Address>::new();
+ let mut delegated_address_by_id = HashMap::<ActorID, Address>::new();
match Map::<_, ActorID>::load(&state.address_map, store) {
Ok(address_map) => {
let ret = address_map.for_each(|key, actor_id| {
@@ -44,11 +45,29 @@ pub fn check_state_invariants<BS: Blockstore>(
format!("unexpected singleton ID value {actor_id}"),
);
- if let Some(duplicate) = address_by_id.insert(*actor_id, key_address) {
- acc.add(format!(
- "duplicate mapping to ID {actor_id}: {key_address} {duplicate}"
- ));
+ match key_address.protocol() {
+ Protocol::ID => {
+ acc.add(format!("key {key_address} is an ID address"));
+ }
+ Protocol::Delegated => {
+ if let Some(duplicate) =
+ delegated_address_by_id.insert(*actor_id, key_address)
+ {
+ acc.add(format!(
+ "duplicate mapping to ID {actor_id}: {key_address} {duplicate}"
+ ));
+ }
+ }
+ _ => {
+ if let Some(duplicate) = stable_address_by_id.insert(*actor_id, key_address)
+ {
+ acc.add(format!(
+ "duplicate mapping to ID {actor_id}: {key_address} {duplicate}"
+ ));
+ }
+ }
}
+
init_summary.ids_by_address.insert(key_address, *actor_id);
Ok(())
diff --git a/actors/init/src/types.rs b/actors/init/src/types.rs
index 2464d9e4..fe2afcb0 100644
--- a/actors/init/src/types.rs
+++ b/actors/init/src/types.rs
@@ -20,7 +20,7 @@ pub struct ExecParams {
}
/// Init actor Exec Return value
-#[derive(Serialize_tuple, Deserialize_tuple)]
+#[derive(Debug, Serialize_tuple, Deserialize_tuple)]
pub struct ExecReturn {
/// ID based address for created actor
pub id_address: Address,
@@ -28,5 +28,37 @@ pub struct ExecReturn {
pub robust_address: Address,
}
+/// Init actor Exec4 Params
+#[derive(Serialize_tuple, Deserialize_tuple)]
+pub struct Exec4Params {
+ pub code_cid: Cid,
+ pub constructor_params: RawBytes,
+ pub subaddress: RawBytes,
+}
+
+/// Init actor Exec4 Return value
+pub type Exec4Return = ExecReturn;
+
impl Cbor for ExecReturn {}
impl Cbor for ExecParams {}
+impl Cbor for Exec4Params {}
+
+/// Init actor Install Params
+#[cfg(feature = "m2-native")]
+#[derive(Serialize_tuple, Deserialize_tuple)]
+pub struct InstallParams {
+ pub code: RawBytes,
+}
+
+/// Init actor Install Return value
+#[cfg(feature = "m2-native")]
+#[derive(Serialize_tuple, Deserialize_tuple)]
+pub struct InstallReturn {
+ pub code_cid: Cid,
+ pub installed: bool,
+}
+
+#[cfg(feature = "m2-native")]
+impl Cbor for InstallParams {}
+#[cfg(feature = "m2-native")]
+impl Cbor for InstallReturn {}
diff --git a/actors/init/tests/init_actor_test.rs b/actors/init/tests/init_actor_test.rs
index b6999220..03e55809 100644
--- a/actors/init/tests/init_actor_test.rs
+++ b/actors/init/tests/init_actor_test.rs
@@ -4,10 +4,11 @@
use cid::Cid;
use fil_actor_init::testing::check_state_invariants;
use fil_actor_init::{
- Actor as InitActor, ConstructorParams, ExecParams, ExecReturn, Method, State,
+ Actor as InitActor, ConstructorParams, Exec4Params, Exec4Return, ExecParams, ExecReturn,
+ Method, State,
};
use fil_actors_runtime::runtime::Runtime;
-use fil_actors_runtime::test_utils::*;
+use fil_actors_runtime::{test_utils::*, EAM_ACTOR_ADDR, EAM_ACTOR_ID};
use fil_actors_runtime::{
ActorError, Multimap, FIRST_NON_SINGLETON_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
};
@@ -66,7 +67,7 @@ fn repeated_robust_address() {
// Next id
let expected_id = 100;
let expected_id_addr = Address::new_id(expected_id);
- rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id);
+ rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, None);
// Expect a send to the multisig actor constructor
rt.expect_send(
@@ -80,7 +81,6 @@ fn repeated_robust_address() {
// Return should have been successful. Check the returned addresses
let exec_ret = exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap();
- let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).unwrap();
assert_eq!(unique_address, exec_ret.robust_address, "Robust address does not macth");
assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match");
check_state(&rt);
@@ -125,12 +125,12 @@ fn create_2_payment_channels() {
let expected_id = 100 + n;
let expected_id_addr = Address::new_id(expected_id);
- rt.expect_create_actor(*PAYCH_ACTOR_CODE_ID, expected_id);
+ rt.expect_create_actor(*PAYCH_ACTOR_CODE_ID, expected_id, None);
let fake_params = ConstructorParams { network_name: String::from("fake_param") };
// expect anne creating a payment channel to trigger a send to the payment channels constructor
- let balance = TokenAmount::from_atto(100u8);
+ let balance = TokenAmount::from_atto(100);
rt.expect_send(
expected_id_addr,
@@ -142,7 +142,6 @@ fn create_2_payment_channels() {
);
let exec_ret = exec_and_verify(&mut rt, *PAYCH_ACTOR_CODE_ID, &fake_params).unwrap();
- let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).unwrap();
assert_eq!(unique_address, exec_ret.robust_address, "Robust Address does not match");
assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match");
@@ -170,7 +169,7 @@ fn create_storage_miner() {
let expected_id = 100;
let expected_id_addr = Address::new_id(expected_id);
- rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id);
+ rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id, None);
let fake_params = ConstructorParams { network_name: String::from("fake_param") };
@@ -184,8 +183,6 @@ fn create_storage_miner() {
);
let exec_ret = exec_and_verify(&mut rt, *MINER_ACTOR_CODE_ID, &fake_params).unwrap();
-
- let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).unwrap();
assert_eq!(unique_address, exec_ret.robust_address);
assert_eq!(expected_id_addr, exec_ret.id_address);
@@ -221,7 +218,7 @@ fn create_multisig_actor() {
// Next id
let expected_id = 100;
let expected_id_addr = Address::new_id(expected_id);
- rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id);
+ rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, None);
let fake_params = ConstructorParams { network_name: String::from("fake_param") };
// Expect a send to the multisig actor constructor
@@ -236,7 +233,6 @@ fn create_multisig_actor() {
// Return should have been successful. Check the returned addresses
let exec_ret = exec_and_verify(&mut rt, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap();
- let exec_ret: ExecReturn = RawBytes::deserialize(&exec_ret).unwrap();
assert_eq!(unique_address, exec_ret.robust_address, "Robust address does not macth");
assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match");
check_state(&rt);
@@ -257,7 +253,7 @@ fn sending_constructor_failure() {
// Create the next id address
let expected_id = 100;
let expected_id_addr = Address::new_id(expected_id);
- rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id);
+ rt.expect_create_actor(*MINER_ACTOR_CODE_ID, expected_id, None);
let fake_params = ConstructorParams { network_name: String::from("fake_param") };
rt.expect_send(
@@ -287,6 +283,52 @@ fn sending_constructor_failure() {
check_state(&rt);
}
+#[test]
+fn call_exec4() {
+ let mut rt = construct_runtime();
+ construct_and_verify(&mut rt);
+
+ // Assign addresses
+ let unique_address = Address::new_actor(b"test");
+ rt.new_actor_addr = Some(unique_address);
+
+ // Make the f4 addr
+ let subaddr = b"foobar";
+ let f4_addr = Address::new_delegated(EAM_ACTOR_ID, subaddr).unwrap();
+
+ // Next id
+ let expected_id = 100;
+ let expected_id_addr = Address::new_id(expected_id);
+ rt.expect_create_actor(*MULTISIG_ACTOR_CODE_ID, expected_id, Some(f4_addr));
+
+ let fake_params = ConstructorParams { network_name: String::from("fake_param") };
+ // Expect a send to the multisig actor constructor
+ rt.expect_send(
+ expected_id_addr,
+ METHOD_CONSTRUCTOR,
+ RawBytes::serialize(&fake_params).unwrap(),
+ TokenAmount::zero(),
+ RawBytes::default(),
+ ExitCode::OK,
+ );
+
+ // Return should have been successful. Check the returned addresses
+ let exec_ret =
+ exec4_and_verify(&mut rt, subaddr, *MULTISIG_ACTOR_CODE_ID, &fake_params).unwrap();
+
+ assert_eq!(unique_address, exec_ret.robust_address, "Robust address does not macth");
+ assert_eq!(expected_id_addr, exec_ret.id_address, "Id address does not match");
+
+ // Check that we assigned the right f4 address.
+ let init_state: State = rt.get_state();
+ let resolved_id = init_state
+ .resolve_address(rt.store(), &f4_addr)
+ .ok()
+ .flatten()
+ .expect("failed to lookup f4 address");
+ assert_eq!(expected_id_addr, resolved_id, "f4 address not assigned to the right actor");
+}
+
fn construct_and_verify(rt: &mut MockRuntime) {
rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]);
let params = ConstructorParams { network_name: "mock".to_string() };
@@ -312,7 +354,7 @@ fn exec_and_verify<S: Serialize>(
rt: &mut MockRuntime,
code_id: Cid,
params: &S,
-) -> Result<RawBytes, ActorError>
+) -> Result<ExecReturn, ActorError>
where
S: Serialize,
{
@@ -325,5 +367,34 @@ where
rt.verify();
check_state(rt);
- ret
+ ret.and_then(|v| v.deserialize().map_err(|e| e.into()))
+}
+
+fn exec4_and_verify<S: Serialize>(
+ rt: &mut MockRuntime,
+ subaddr: &[u8],
+ code_id: Cid,
+ params: &S,
+) -> Result<Exec4Return, ActorError>
+where
+ S: Serialize,
+{
+ rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, EAM_ACTOR_ADDR);
+ if cfg!(feature = "m2-native") {
+ rt.expect_validate_caller_any();
+ } else {
+ rt.expect_validate_caller_addr(vec![EAM_ACTOR_ADDR]);
+ }
+ let exec_params = Exec4Params {
+ code_cid: code_id,
+ constructor_params: RawBytes::serialize(params).unwrap(),
+ subaddress: subaddr.to_owned().into(),
+ };
+
+ let ret =
+ rt.call::<InitActor>(Method::Exec4 as u64, &RawBytes::serialize(&exec_params).unwrap());
+
+ rt.verify();
+ check_state(rt);
+ ret.and_then(|v| v.deserialize().map_err(|e| e.into()))
}
diff --git a/actors/market/Cargo.toml b/actors/market/Cargo.toml
index 055540fc..3e81b95c 100644
--- a/actors/market/Cargo.toml
+++ b/actors/market/Cargo.toml
@@ -19,11 +19,11 @@ fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime"}
anyhow = "1.0.65"
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
frc46_token = "1.1.0"
-fvm_ipld_bitfield = "0.5.2"
+fvm_ipld_bitfield = "0.5.4"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
-fvm_ipld_hamt = "0.5.1"
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_ipld_encoding = "0.3.0"
+fvm_ipld_hamt = "0.6.0"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
integer-encoding = { version = "3.0.3", default-features = false }
libipld-core = { version = "0.13.1", features = ["serde-codec"] }
log = "0.4.14"
@@ -36,7 +36,7 @@ fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", featu
fil_actor_power = { path = "../power" }
fil_actor_reward = { path = "../reward" }
fil_actor_verifreg = { path = "../verifreg" }
-fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] }
+fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] }
multihash = { version = "0.16.1", default-features = false }
regex = "1"
itertools = "0.10"
diff --git a/actors/miner/Cargo.toml b/actors/miner/Cargo.toml
index 0abd4b82..baac65d0 100644
--- a/actors/miner/Cargo.toml
+++ b/actors/miner/Cargo.toml
@@ -15,10 +15,10 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
-fvm_ipld_bitfield = "0.5.2"
-fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] }
-fvm_ipld_hamt = "0.5.1"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
+fvm_ipld_bitfield = "0.5.4"
+fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] }
+fvm_ipld_hamt = "0.6.0"
serde = { version = "1.0.136", features = ["derive"] }
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
num-traits = "0.2.14"
@@ -29,7 +29,7 @@ byteorder = "1.4.3"
anyhow = "1.0.65"
itertools = "0.10.3"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
multihash = { version = "0.16.2", default-features = false }
[dev-dependencies]
diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs
index 40179270..e59aead7 100644
--- a/actors/miner/src/state.rs
+++ b/actors/miner/src/state.rs
@@ -16,7 +16,7 @@ use fvm_ipld_amt::Error as AmtError;
use fvm_ipld_bitfield::BitField;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::tuple::*;
-use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor, CborStore};
+use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor, CborStore};
use fvm_ipld_hamt::Error as HamtError;
use fvm_shared::address::Address;
@@ -1208,7 +1208,7 @@ pub struct AdvanceDeadlineResult {
}
/// Static information about miner
-#[derive(Debug, PartialEq, Serialize_tuple, Deserialize_tuple)]
+#[derive(Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)]
pub struct MinerInfo {
/// Account that owns this miner
/// - Income and returned collateral are paid to this address
@@ -1228,7 +1228,7 @@ pub struct MinerInfo {
pub pending_worker_key: Option<WorkerKeyChange>,
/// Libp2p identity that should be used when connecting to this miner
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub peer_id: Vec<u8>,
/// Vector of byte arrays representing Libp2p multi-addresses used for establishing a connection with this miner.
diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs
index 41f4a7b1..e34eb3c7 100644
--- a/actors/miner/tests/miner_actor_test_construction.rs
+++ b/actors/miner/tests/miner_actor_test_construction.rs
@@ -46,7 +46,7 @@ fn prepare_env() -> TestEnv {
env.rt.actor_code_cids.insert(env.worker, *ACCOUNT_ACTOR_CODE_ID);
env.rt.actor_code_cids.insert(env.control_addrs[0], *ACCOUNT_ACTOR_CODE_ID);
env.rt.actor_code_cids.insert(env.control_addrs[1], *ACCOUNT_ACTOR_CODE_ID);
- env.rt.hash_func = Box::new(blake2b_256);
+ env.rt.hash_func = Box::new(hash);
env.rt.caller = INIT_ACTOR_ADDR;
env.rt.caller_type = *INIT_ACTOR_CODE_ID;
env
diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs
index e5929b95..48861b90 100644
--- a/actors/miner/tests/util.rs
+++ b/actors/miner/tests/util.rs
@@ -63,6 +63,7 @@ use fvm_shared::clock::QuantSpec;
use fvm_shared::clock::{ChainEpoch, NO_QUANTIZATION};
use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED};
use fvm_shared::consensus::ConsensusFault;
+use fvm_shared::crypto::hash::SupportedHashes;
use fvm_shared::deal::DealID;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
@@ -2877,14 +2878,14 @@ where
}
// Returns a fake hashing function that always arranges the first 8 bytes of the digest to be the binary
-// encoding of a target uint64.
-fn fixed_hasher(offset: ChainEpoch) -> Box<dyn Fn(&[u8]) -> [u8; 32]> {
- let hash = move |_: &[u8]| -> [u8; 32] {
- let mut result = [0u8; 32];
+// encoding of a target uint64 and ignores the hash function.
+fn fixed_hasher(offset: ChainEpoch) -> Box<dyn Fn(SupportedHashes, &[u8]) -> ([u8; 64], usize)> {
+ let hash = move |_: SupportedHashes, _: &[u8]| -> ([u8; 64], usize) {
+ let mut result = [0u8; 64];
for (i, item) in result.iter_mut().enumerate().take(8) {
*item = ((offset >> (8 * (7 - i))) & 0xff) as u8;
}
- result
+ (result, 32)
};
Box::new(hash)
}
diff --git a/actors/multisig/Cargo.toml b/actors/multisig/Cargo.toml
index 9e1c2c0a..90858e82 100644
--- a/actors/multisig/Cargo.toml
+++ b/actors/multisig/Cargo.toml
@@ -20,9 +20,9 @@ anyhow = "1.0.65"
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
frc42_dispatch = "1.0.0"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
-fvm_ipld_hamt = "0.5.1"
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_ipld_encoding = "0.3.0"
+fvm_ipld_hamt = "0.6.0"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
indexmap = { version = "1.8.0", features = ["serde-1"] }
integer-encoding = { version = "3.0.3", default-features = false }
num-derive = "0.3.3"
diff --git a/actors/multisig/src/types.rs b/actors/multisig/src/types.rs
index 51ed7899..a961de9f 100644
--- a/actors/multisig/src/types.rs
+++ b/actors/multisig/src/types.rs
@@ -4,7 +4,7 @@
use std::fmt::Display;
use fvm_ipld_encoding::tuple::*;
-use fvm_ipld_encoding::{serde_bytes, Cbor, RawBytes};
+use fvm_ipld_encoding::{strict_bytes, Cbor, RawBytes};
use fvm_ipld_hamt::BytesKey;
use fvm_shared::address::Address;
@@ -105,7 +105,7 @@ pub struct TxnIDParams {
pub id: TxnID,
/// Optional hash of proposal to ensure an operation can only apply to a
/// specific proposal.
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub proposal_hash: Vec<u8>,
}
diff --git a/actors/paych/Cargo.toml b/actors/paych/Cargo.toml
index 6cd02512..958f89e5 100644
--- a/actors/paych/Cargo.toml
+++ b/actors/paych/Cargo.toml
@@ -15,18 +15,18 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
num-traits = "0.2.14"
num-derive = "0.3.3"
serde = { version = "1.0.136", features = ["derive"] }
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
anyhow = "1.0.65"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
[dev-dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] }
-fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] }
+fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] }
derive_builder = "0.10.2"
[features]
diff --git a/actors/paych/src/types.rs b/actors/paych/src/types.rs
index f09d9e47..918468ee 100644
--- a/actors/paych/src/types.rs
+++ b/actors/paych/src/types.rs
@@ -3,7 +3,7 @@
use fil_actors_runtime::network::EPOCHS_IN_HOUR;
use fvm_ipld_encoding::tuple::*;
-use fvm_ipld_encoding::{serde_bytes, to_vec, Error, RawBytes};
+use fvm_ipld_encoding::{strict_bytes, to_vec, Error, RawBytes};
use fvm_shared::address::Address;
use fvm_shared::clock::ChainEpoch;
use fvm_shared::crypto::signature::Signature;
@@ -40,7 +40,7 @@ pub struct SignedVoucher {
/// set to 0 means no timeout
pub time_lock_max: ChainEpoch,
/// (optional) Used by `to` to validate
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub secret_pre_image: Vec<u8>,
/// (optional) Specified by `from` to add a verification method to the voucher
pub extra: Option<ModVerifyParams>,
@@ -68,7 +68,7 @@ impl SignedVoucher {
pub channel_addr: &'a Address,
pub time_lock_min: ChainEpoch,
pub time_lock_max: ChainEpoch,
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub secret_pre_image: &'a [u8],
pub extra: &'a Option<ModVerifyParams>,
pub lane: u64,
@@ -108,14 +108,14 @@ pub struct ModVerifyParams {
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct PaymentVerifyParams {
pub extra: RawBytes,
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub proof: Vec<u8>,
}
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct UpdateChannelStateParams {
pub sv: SignedVoucher,
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub secret: Vec<u8>,
// * proof removed in v2
}
diff --git a/actors/power/Cargo.toml b/actors/power/Cargo.toml
index 471bb44c..4307af3f 100644
--- a/actors/power/Cargo.toml
+++ b/actors/power/Cargo.toml
@@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
-fvm_ipld_hamt = "0.5.1"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
+fvm_ipld_hamt = "0.6.0"
num-traits = "0.2.14"
num-derive = "0.3.3"
log = "0.4.14"
@@ -27,7 +27,7 @@ lazy_static = "1.4.0"
serde = { version = "1.0.136", features = ["derive"] }
anyhow = "1.0.65"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
[dev-dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] }
diff --git a/actors/power/src/ext.rs b/actors/power/src/ext.rs
index 91ab19ec..f650ba27 100644
--- a/actors/power/src/ext.rs
+++ b/actors/power/src/ext.rs
@@ -1,6 +1,6 @@
use cid::Cid;
use fvm_ipld_encoding::tuple::*;
-use fvm_ipld_encoding::{serde_bytes, BytesDe, RawBytes};
+use fvm_ipld_encoding::{strict_bytes, BytesDe, RawBytes};
use fvm_shared::address::Address;
use fvm_shared::bigint::bigint_ser;
use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, StoragePower};
@@ -51,14 +51,14 @@ pub mod miner {
pub worker: Address,
pub control_addresses: Vec<Address>,
pub window_post_proof_type: RegisteredPoStProof,
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub peer_id: Vec<u8>,
pub multi_addresses: Vec<BytesDe>,
}
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct DeferredCronEventParams {
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub event_payload: Vec<u8>,
pub reward_smoothed: FilterEstimate,
pub quality_adj_power_smoothed: FilterEstimate,
diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs
index 521be8f8..74537f7a 100644
--- a/actors/power/src/types.rs
+++ b/actors/power/src/types.rs
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0, MIT
use fvm_ipld_encoding::tuple::*;
-use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor, RawBytes};
+use fvm_ipld_encoding::{strict_bytes, BytesDe, Cbor, RawBytes};
use fvm_shared::address::Address;
use fvm_shared::bigint::bigint_ser;
use fvm_shared::clock::ChainEpoch;
@@ -28,7 +28,7 @@ pub struct CreateMinerParams {
pub owner: Address,
pub worker: Address,
pub window_post_proof_type: RegisteredPoStProof,
- #[serde(with = "serde_bytes")]
+ #[serde(with = "strict_bytes")]
pub peer: Vec<u8>,
pub multiaddrs: Vec<BytesDe>,
}
diff --git a/actors/reward/Cargo.toml b/actors/reward/Cargo.toml
index 74d720db..5dd3d32c 100644
--- a/actors/reward/Cargo.toml
+++ b/actors/reward/Cargo.toml
@@ -15,14 +15,14 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
num-traits = "0.2.14"
num-derive = "0.3.3"
log = "0.4.14"
lazy_static = "1.4.0"
serde = { version = "1.0.136", features = ["derive"] }
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
+fvm_ipld_encoding = "0.3.0"
[dev-dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime", features = ["test_utils", "sector-default"] }
diff --git a/actors/system/Cargo.toml b/actors/system/Cargo.toml
index 0613ee2e..9584a264 100644
--- a/actors/system/Cargo.toml
+++ b/actors/system/Cargo.toml
@@ -15,8 +15,8 @@ crate-type = ["cdylib", "lib"]
[dependencies]
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../../runtime" }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
-fvm_ipld_encoding = "0.2.2"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
+fvm_ipld_encoding = "0.3.0"
fvm_ipld_blockstore = "0.1.1"
num-traits = "0.2.14"
anyhow = "1.0.65"
diff --git a/actors/verifreg/Cargo.toml b/actors/verifreg/Cargo.toml
index 6acde782..93e44d13 100644
--- a/actors/verifreg/Cargo.toml
+++ b/actors/verifreg/Cargo.toml
@@ -21,9 +21,9 @@ cid = { version = "0.8.3", default-features = false, features = ["serde-codec"]
frc42_dispatch = "1.0.0"
frc46_token = "1.1.0"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
-fvm_ipld_hamt = "0.5.1"
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_ipld_encoding = "0.3.0"
+fvm_ipld_hamt = "0.6.0"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
lazy_static = "1.4.0"
log = "0.4.14"
num-derive = "0.3.3"
diff --git a/build.rs b/build.rs
index 056a0beb..d3f46b66 100644
--- a/build.rs
+++ b/build.rs
@@ -26,6 +26,19 @@ const ACTORS: &[(&Package, &ID)] = &[
("reward", "reward"),
("verifreg", "verifiedregistry"),
("datacap", "datacap"),
+ ("embryo", "embryo"),
+ ("evm", "evm"),
+ ("eam", "eam"),
+];
+
+/// Default Cargo features to activate during the build.
+const DEFAULT_CARGO_FEATURES: &[&str] = &["fil-actor"];
+
+/// Extra Cargo-level features to enable per network.
+const EXTRA_CARGO_FEATURES: &[(&str, &[&str])] = &[
+ ("devnet-wasm", &["m2-native"]),
+ /*("devnet-evm", &["m2-fevm"]),*/
+ ("wallaby", &["m2-native"]),
];
const NETWORK_ENV: &str = "BUILD_FIL_NETWORK";
@@ -44,6 +57,10 @@ fn network_name() -> String {
Some("calibrationnet")
} else if cfg!(feature = "devnet") {
Some("devnet")
+ } else if cfg!(feature = "devnet-wasm") {
+ Some("devnet-wasm")
+ } else if cfg!(feature = "wallaby") {
+ Some("wallaby")
} else if cfg!(feature = "testing") {
Some("testing")
} else if cfg!(feature = "testing-fake-proofs") {
@@ -103,6 +120,16 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("cargo:rerun-if-changed={}", file);
}
+ // Compute Cargo features to apply.
+ let features = {
+ let extra = EXTRA_CARGO_FEATURES
+ .iter()
+ .find(|(k, _)| k == &network_name)
+ .map(|f| f.1)
+ .unwrap_or_default();
+ [DEFAULT_CARGO_FEATURES, extra].concat()
+ };
+
// Cargo build command for all actors at once.
let mut cmd = Command::new(&cargo);
cmd.arg("build")
@@ -110,7 +137,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.arg("--target=wasm32-unknown-unknown")
.arg("--profile=wasm")
.arg("--locked")
- .arg("--features=fil-actor")
+ .arg("--features=".to_owned() + &features.join(","))
.arg("--manifest-path=".to_owned() + manifest_path.to_str().unwrap())
.env(NETWORK_ENV, network_name)
.stdout(Stdio::piped())
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 47df4007..94b5405f 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -8,47 +8,54 @@ edition = "2021"
repository = "https://github.com/filecoin-project/builtin-actors"
[dependencies]
-fvm_ipld_hamt = "0.5.1"
-fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] }
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_ipld_hamt = "0.6.0"
+fvm_ipld_amt = { version = "0.5.0", features = ["go-interop"] }
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
num-traits = "0.2.14"
num-derive = "0.3.3"
serde = { version = "1.0.136", features = ["derive"] }
lazy_static = { version = "1.4.0", optional = true }
unsigned-varint = "0.7.1"
-integer-encoding = { version = "3.0.3", default-features = false }
byteorder = "1.4.3"
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
-base64 = "0.13.0"
log = "0.4.14"
-indexmap = { version = "1.8.0", features = ["serde-1"] }
thiserror = "1.0.30"
-# enforce wasm compat
-getrandom = { version = "0.2.5", features = ["js"] }
-hex = { version = "0.4.3", optional = true }
-anyhow = "1.0.65"
-fvm_sdk = { version = "2.0.0-alpha.3", optional = true }
-blake2b_simd = "1.0"
+anyhow = "1.0.56"
fvm_ipld_blockstore = "0.1.1"
-fvm_ipld_encoding = "0.2.2"
-fvm_ipld_bitfield = "0.5.2"
+fvm_ipld_encoding = "0.3.0"
multihash = { version = "0.16.1", default-features = false }
-rand = "0.8.5"
serde_repr = "0.1.8"
regex = "1"
itertools = "0.10"
paste = "1.0.9"
-[dependencies.sha2]
-version = "0.10"
+# A fake-proofs dependency but... we can't select on that feature here because we enable it from
+# build.rs.
+sha2 = "0.10"
+
+# fil-actor
+fvm_sdk = { version = "3.0.0-alpha.8", optional = true }
+
+# test_util
+rand = { version = "0.8.5", default-features = false, optional = true }
+hex = { version = "0.4.3", optional = true }
+blake2b_simd = { version = "1.0", optional = true }
+
+[dependencies.libsecp256k1]
+version = "0.7.1"
+default-features = false
+features = ["static-context", "std"]
+optional = true
[dev-dependencies]
derive_builder = "0.10.2"
hex = "0.4.3"
+rand = { version = "0.8.5" }
[features]
default = []
fil-actor = ["fvm_sdk"]
+m2-native = ["fvm_sdk/m2-native"]
# Enable 2k sectors
sector-2k = []
@@ -79,4 +86,4 @@ no-provider-deal-collateral = []
# fake proofs (for testing)
fake-proofs = []
-test_utils = ["hex", "multihash/sha2", "lazy_static"]
+test_utils = ["hex", "multihash/sha2", "libsecp256k1", "blake2b_simd", "rand", "rand/std_rng", "lazy_static"]
diff --git a/runtime/build.rs b/runtime/build.rs
index 767b810b..e2193242 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -1,7 +1,7 @@
-static NETWORKS: &[(&str, &[&str])] = &[
- ("mainnet", &["sector-32g", "sector-64g"]),
+static NETWORKS: &[(&[&str], &[&str])] = &[
+ (&["mainnet"], &["sector-32g", "sector-64g"]),
(
- "caterpillarnet",
+ &["caterpillarnet"],
&[
"sector-512m",
"sector-32g",
@@ -11,11 +11,18 @@ static NETWORKS: &[(&str, &[&str])] = &[
"min-power-2k",
],
),
- ("butterflynet", &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]),
- ("calibrationnet", &["sector-32g", "sector-64g", "min-power-32g"]),
- ("devnet", &["sector-2k", "sector-8m", "small-deals", "short-precommit", "min-power-2k"]),
+ (&["butterflynet"], &["sector-512m", "sector-32g", "sector-64g", "min-power-2g"]),
(
- "testing",
+ &["wallaby"],
+ &["sector-512m", "sector-32g", "sector-64g", "min-power-16g", "short-precommit"],
+ ),
+ (&["calibrationnet"], &["sector-32g", "sector-64g", "min-power-32g"]),
+ (
+ &["devnet", "devnet-wasm" /*devnet-fevm*/],
+ &["sector-2k", "sector-8m", "small-deals", "short-precommit", "min-power-2k"],
+ ),
+ (
+ &["testing"],
&[
"sector-2k",
"sector-8m",
@@ -29,7 +36,7 @@ static NETWORKS: &[(&str, &[&str])] = &[
],
),
(
- "testing-fake-proofs",
+ &["testing-fake-proofs"],
&[
"sector-2k",
"sector-8m",
@@ -46,12 +53,22 @@ static NETWORKS: &[(&str, &[&str])] = &[
];
const NETWORK_ENV: &str = "BUILD_FIL_NETWORK";
+/// This build script enables _local_ compile features. These features do not
+/// affect the dependency graph (they are not processed by Cargo). They are only
+/// in effect for conditional compilation _in this crate_.
+///
+/// The reason we can't set these features as Cargo features from the root build
+/// is that this crate is only ever used as a _transitive_ dependency from actor
+/// crates.
+///
+/// So the only two options are: (a) actors crates set the features when
+/// importing us as a dependency (super repetitive), or (b) this.
fn main() {
let network = std::env::var(NETWORK_ENV).ok();
println!("cargo:rerun-if-env-changed={}", NETWORK_ENV);
let network = network.as_deref().unwrap_or("mainnet");
- let features = NETWORKS.iter().find(|(k, _)| k == &network).expect("unknown network").1;
+ let features = NETWORKS.iter().find(|(k, _)| k.contains(&network)).expect("unknown network").1;
for feature in features {
println!("cargo:rustc-cfg=feature=\"{}\"", feature);
}
diff --git a/runtime/src/builtin/singletons.rs b/runtime/src/builtin/singletons.rs
index 458996b8..f02566ad 100644
--- a/runtime/src/builtin/singletons.rs
+++ b/runtime/src/builtin/singletons.rs
@@ -25,6 +25,7 @@ define_singletons! {
STORAGE_MARKET_ACTOR = 5,
VERIFIED_REGISTRY_ACTOR = 6,
DATACAP_TOKEN_ACTOR = 7,
+ EAM_ACTOR = 10,
CHAOS_ACTOR = 98,
BURNT_FUNDS_ACTOR = 99,
}
diff --git a/runtime/src/runtime/builtins.rs b/runtime/src/runtime/builtins.rs
index abc97cd0..6aab3bc7 100644
--- a/runtime/src/runtime/builtins.rs
+++ b/runtime/src/runtime/builtins.rs
@@ -20,6 +20,9 @@ pub enum Type {
Reward = 10,
VerifiedRegistry = 11,
DataCap = 12,
+ Embryo = 13,
+ EVM = 14,
+ EAM = 15,
}
impl Type {
@@ -37,6 +40,9 @@ impl Type {
Type::Reward => "reward",
Type::VerifiedRegistry => "verifiedregistry",
Type::DataCap => "datacap",
+ Type::Embryo => "embryo",
+ Type::EVM => "evm",
+ Type::EAM => "eam",
}
}
}
diff --git a/runtime/src/runtime/chainid.rs b/runtime/src/runtime/chainid.rs
new file mode 100644
index 00000000..2237366b
--- /dev/null
+++ b/runtime/src/runtime/chainid.rs
@@ -0,0 +1,37 @@
+#[cfg(feature = "mainnet")]
+pub const CHAINID: u64 = 314;
+
+// TODO buildernet
+// #[cfg(feature = "buildernet")]
+// pub const CHAINID: u64 = 3141;
+
+#[cfg(feature = "wallaby")]
+pub const CHAINID: u64 = 31415;
+
+#[cfg(feature = "calibrationnet")]
+pub const CHAINID: u64 = 314159;
+
+#[cfg(any(feature = "caterpillarnet", feature = "butterflynet"))]
+pub const CHAINID: u64 = 3141592;
+
+#[cfg(any(
+ feature = "devnet",
+ feature = "devnet-wasm",
+ feature = "testing",
+ feature = "testing-fake-proofs",
+))]
+pub const CHAINID: u64 = 31415926;
+
+// default build is same as a devnet
+#[cfg(not(any(
+ feature = "mainnet",
+ feature = "wallaby",
+ feature = "calibrationnet",
+ feature = "caterpillarnet",
+ feature = "butterflynet",
+ feature = "devnet",
+ feature = "devnet-wasm",
+ feature = "testing",
+ feature = "testing-fake-proofs",
+)))]
+pub const CHAINID: u64 = 31415926;
diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs
index 4ab78a40..d7b7dadf 100644
--- a/runtime/src/runtime/fvm.rs
+++ b/runtime/src/runtime/fvm.rs
@@ -5,9 +5,12 @@ use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::{Cbor, CborStore, RawBytes, DAG_CBOR};
use fvm_sdk as fvm;
use fvm_sdk::NO_DATA_BLOCK_ID;
-use fvm_shared::address::Address;
+use fvm_shared::address::{Address, Payload};
use fvm_shared::clock::ChainEpoch;
-use fvm_shared::crypto::signature::Signature;
+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::{ErrorNumber, ExitCode};
use fvm_shared::piece::PieceInfo;
@@ -30,8 +33,6 @@ use crate::runtime::{
};
use crate::{actor_error, ActorError, Runtime};
-use super::EMPTY_ARR_CID;
-
/// A runtime that bridges to the FVM environment through the FVM SDK.
pub struct FvmRuntime<B = ActorBlockstore> {
blockstore: B,
@@ -80,6 +81,10 @@ impl MessageInfo for FvmMessage {
Address::new_id(fvm::message::caller())
}
+ fn origin(&self) -> Address {
+ Address::new_id(fvm::message::origin())
+ }
+
fn receiver(&self) -> Address {
Address::new_id(fvm::message::receiver())
}
@@ -87,6 +92,14 @@ impl MessageInfo for FvmMessage {
fn value_received(&self) -> TokenAmount {
fvm::message::value_received()
}
+
+ fn gas_limit(&self) -> u64 {
+ fvm::message::gas_limit()
+ }
+
+ fn gas_premium(&self) -> TokenAmount {
+ fvm::message::gas_premium()
+ }
}
impl<B> Runtime<B> for FvmRuntime<B>
@@ -127,6 +140,26 @@ where
}
}
+ fn validate_immediate_caller_namespace<I>(&mut self, addresses: I) -> Result<(), ActorError>
+ where
+ I: IntoIterator<Item = u64>,
+ {
+ self.assert_not_validated()?;
+ let caller_addr = self.message().caller();
+ let caller_f4 = self.lookup_address(caller_addr.id().unwrap()).map(|a| *a.payload());
+ if addresses
+ .into_iter()
+ .any(|a| matches!(caller_f4, Some(Payload::Delegated(d)) if d.namespace() == a))
+ {
+ self.caller_validated = true;
+ Ok(())
+ } else {
+ Err(actor_error!(forbidden;
+ "caller's namespace {} is not one of supported", caller_addr
+ ))
+ }
+ }
+
fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError>
where
I: IntoIterator<Item = &'a Type>,
@@ -152,10 +185,18 @@ where
fvm::sself::current_balance()
}
+ fn actor_balance(&self, id: ActorID) -> Option<TokenAmount> {
+ fvm::actor::balance_of(id)
+ }
+
fn resolve_address(&self, address: &Address) -> Option<ActorID> {
fvm::actor::resolve_address(address)
}
+ fn lookup_address(&self, id: ActorID) -> Option<Address> {
+ fvm::actor::lookup_address(id)
+ }
+
fn get_actor_code_cid(&self, id: &ActorID) -> Option<Cid> {
fvm::actor::get_actor_code_cid(&Address::new_id(*id))
}
@@ -223,25 +264,12 @@ where
})
}
- fn create<C: Cbor>(&mut self, obj: &C) -> Result<(), ActorError> {
- let root = fvm::sself::root()?;
- if root != EMPTY_ARR_CID {
- return Err(
- actor_error!(illegal_state; "failed to create state; expected empty array CID, got: {}", root),
- );
- }
- let new_root = ActorBlockstore.put_cbor(obj, Code::Blake2b256)
- .map_err(|e| actor_error!(illegal_argument; "failed to write actor state during creation: {}", e.to_string()))?;
- fvm::sself::set_root(&new_root)?;
- Ok(())
+ fn get_state_root(&self) -> Result<Cid, ActorError> {
+ Ok(fvm::sself::root()?)
}
- fn state<C: Cbor>(&self) -> Result<C, ActorError> {
- let root = fvm::sself::root()?;
- Ok(ActorBlockstore
- .get_cbor(&root)
- .map_err(|_| actor_error!(illegal_argument; "failed to get actor for Readonly state"))?
- .expect("State does not exist for actor state root"))
+ fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError> {
+ Ok(fvm::sself::set_root(root)?)
}
fn transaction<C, RT, F>(&mut self, f: F) -> Result<RT, ActorError>
@@ -343,16 +371,22 @@ where
Ok(fvm::actor::new_actor_address())
}
- fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> {
+ fn create_actor(
+ &mut self,
+ code_id: Cid,
+ actor_id: ActorID,
+ predictable_address: Option<Address>,
+ ) -> Result<(), ActorError> {
if self.in_transaction {
return Err(
actor_error!(assertion_failed; "create_actor is not allowed during transaction"),
);
}
- fvm::actor::create_actor(actor_id, &code_id).map_err(|e| match e {
+ fvm::actor::create_actor(actor_id, &code_id, predictable_address).map_err(|e| match e {
ErrorNumber::IllegalArgument => {
ActorError::illegal_argument("failed to create actor".into())
}
+ ErrorNumber::Forbidden => ActorError::forbidden("actor already exists".into()),
_ => actor_error!(assertion_failed; "create failed with unknown error: {}", e),
})
}
@@ -377,6 +411,18 @@ where
fn base_fee(&self) -> TokenAmount {
fvm::network::base_fee()
}
+
+ fn gas_available(&self) -> u64 {
+ fvm::gas::available()
+ }
+
+ fn tipset_timestamp(&self) -> u64 {
+ fvm::network::tipset_timestamp()
+ }
+
+ fn tipset_cid(&self, epoch: i64) -> Option<Cid> {
+ fvm::network::tipset_cid(epoch).ok()
+ }
}
impl<B> Primitives for FvmRuntime<B>
@@ -409,6 +455,30 @@ where
fvm::crypto::compute_unsealed_sector_cid(proof_type, pieces)
.map_err(|e| anyhow!("failed to compute unsealed sector CID; exit code: {}", e))
}
+
+ #[cfg(feature = "m2-native")]
+ fn install_actor(&self, code_id: &Cid) -> Result<(), Error> {
+ fvm::actor::install_actor(code_id).map_err(|_| Error::msg("failed to install actor"))
+ }
+
+ fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec<u8> {
+ fvm::crypto::hash_owned(hasher, data)
+ }
+
+ fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) {
+ let mut buf = [0u8; 64];
+ let len = fvm::crypto::hash_into(hasher, data, &mut buf);
+ (buf, len)
+ }
+
+ 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> {
+ fvm::crypto::recover_secp_public_key(hash, signature)
+ .map_err(|e| anyhow!("failed to recover pubkey; exit code: {}", e))
+ }
}
#[cfg(not(feature = "fake-proofs"))]
diff --git a/runtime/src/runtime/hash_algorithm.rs b/runtime/src/runtime/hash_algorithm.rs
index 320938e0..2c1d8895 100644
--- a/runtime/src/runtime/hash_algorithm.rs
+++ b/runtime/src/runtime/hash_algorithm.rs
@@ -35,12 +35,7 @@ impl HashAlgorithm for FvmHashSha256 {
let mut hasher = RuntimeHasherWrapper::default();
key.hash(&mut hasher);
- // TODO :: Enable this when moving to
- // fvm_sdk 3.0.0-alpha.2,
- // looks like fvm_sdk 2.0.0-alpha.3 doesn't support `hash_into()` api
- // fvm::crypto::hash_into(SupportedHashes::Sha2_256, &hasher.0, &mut rval_digest);
-
- rval_digest.copy_from_slice(&fvm::crypto::hash(SupportedHashes::Sha2_256, &hasher.0));
+ fvm::crypto::hash_into(SupportedHashes::Sha2_256, &hasher.0, &mut rval_digest);
rval_digest
}
diff --git a/runtime/src/runtime/mod.rs b/runtime/src/runtime/mod.rs
index b69c0b93..76157ca4 100644
--- a/runtime/src/runtime/mod.rs
+++ b/runtime/src/runtime/mod.rs
@@ -3,11 +3,14 @@
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
-use fvm_ipld_encoding::{Cbor, RawBytes};
+use fvm_ipld_encoding::{Cbor, CborStore, RawBytes};
use fvm_shared::address::Address;
use fvm_shared::clock::ChainEpoch;
use fvm_shared::consensus::ConsensusFault;
-use fvm_shared::crypto::signature::Signature;
+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::piece::PieceInfo;
use fvm_shared::randomness::RANDOMNESS_LENGTH;
@@ -17,15 +20,17 @@ use fvm_shared::sector::{
};
use fvm_shared::version::NetworkVersion;
use fvm_shared::{ActorID, MethodNum};
+use multihash::Code;
pub use self::actor_code::*;
pub use self::policy::*;
pub use self::randomness::DomainSeparationTag;
use crate::runtime::builtins::Type;
-use crate::ActorError;
+use crate::{actor_error, ActorError};
mod actor_code;
pub mod builtins;
+pub mod chainid;
pub mod policy;
mod randomness;
@@ -57,6 +62,14 @@ pub trait Runtime<BS: Blockstore>: Primitives + Verifier + RuntimePolicy {
fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError>
where
I: IntoIterator<Item = &'a Address>;
+ /// Validates the caller is a member of a namespace.
+ /// Addresses must be of Protocol ID.
+ fn validate_immediate_caller_namespace<I>(
+ &mut self,
+ namespace_manager_addresses: I,
+ ) -> Result<(), ActorError>
+ where
+ I: IntoIterator<Item = u64>;
fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError>
where
I: IntoIterator<Item = &'a Type>;
@@ -64,11 +77,18 @@ pub trait Runtime<BS: Blockstore>: Primitives + Verifier + RuntimePolicy {
/// The balance of the receiver.
fn current_balance(&self) -> TokenAmount;
+ /// The balance of an actor.
+ fn actor_balance(&self, id: ActorID) -> Option<TokenAmount>;
+
/// Resolves an address of any protocol to an ID address (via the Init actor's table).
/// This allows resolution of externally-provided SECP, BLS, or actor addresses to the canonical form.
/// If the argument is an ID address it is returned directly.
fn resolve_address(&self, address: &Address) -> Option<ActorID>;
+ /// Looks-up the "predictable" address of an actor by ID, if any. Returns None if either the
+ /// target actor doesn't exist, or if the target actord doesn't have a predictable address.
+ fn lookup_address(&self, id: ActorID) -> Option<Address>;
+
/// Look up the code ID at an actor address.
fn get_actor_code_cid(&self, id: &ActorID) -> Option<Cid>;
@@ -95,10 +115,33 @@ pub trait Runtime<BS: Blockstore>: Primitives + Verifier + RuntimePolicy {
/// Initializes the state object.
/// This is only valid when the state has not yet been initialized.
/// NOTE: we should also limit this to being invoked during the constructor method
- fn create<C: Cbor>(&mut self, obj: &C) -> Result<(), ActorError>;
+ fn create<C: Cbor>(&mut self, obj: &C) -> Result<(), ActorError> {
+ let root = self.get_state_root()?;
+ if root != EMPTY_ARR_CID {
+ return Err(
+ actor_error!(illegal_state; "failed to create state; expected empty array CID, got: {}", root),
+ );
+ }
+ let new_root = self.store().put_cbor(obj, Code::Blake2b256)
+ .map_err(|e| actor_error!(illegal_argument; "failed to write actor state during creation: {}", e.to_string()))?;
+ self.set_state_root(&new_root)?;
+ Ok(())
+ }
/// Loads a readonly copy of the state of the receiver into the argument.
- fn state<C: Cbor>(&self) -> Result<C, ActorError>;
+ fn state<C: Cbor>(&self) -> Result<C, ActorError> {
+ Ok(self
+ .store()
+ .get_cbor(&self.get_state_root()?)
+ .map_err(|_| actor_error!(illegal_argument; "failed to get actor for Readonly state"))?
+ .expect("State does not exist for actor state root"))
+ }
+
+ /// Gets the state-root.
+ fn get_state_root(&self) -> Result<Cid, ActorError>;
+
+ /// Sets the state-root.
+ fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError>;
/// Loads a mutable copy of the state of the receiver, passes it to `f`,
/// and after `f` completes puts the state object back to the store and sets it as
@@ -135,9 +178,14 @@ pub trait Runtime<BS: Blockstore>: Primitives + Verifier + RuntimePolicy {
/// Always an ActorExec address.
fn new_actor_address(&mut self) -> Result<Address, ActorError>;
- /// Creates an actor with code `codeID` and address `address`, with empty state.
+ /// Creates an actor with code `codeID`, an empty state, id `actor_id`, and an optional predictable address.
/// May only be called by Init actor.
- fn create_actor(&mut self, code_id: Cid, address: ActorID) -> Result<(), ActorError>;
+ fn create_actor(
+ &mut self,
+ code_id: Cid,
+ actor_id: ActorID,
+ predictable_address: Option<Address>,
+ ) -> Result<(), ActorError>;
/// Deletes the executing actor from the state tree, transferring any balance to beneficiary.
/// Aborts if the beneficiary does not exist.
@@ -165,7 +213,17 @@ pub trait Runtime<BS: Blockstore>: Primitives + Verifier + RuntimePolicy {
/// `name` provides information about gas charging point
fn charge_gas(&mut self, name: &'static str, compute: i64);
+ /// The current network base fee
fn base_fee(&self) -> TokenAmount;
+
+ /// The gas still available for computation
+ fn gas_available(&self) -> u64;
+
+ /// The current tipset's timestamp, as UNIX seconds
+ fn tipset_timestamp(&self) -> u64;
+
+ /// The hash of on of the last 256 blocks
+ fn tipset_cid(&self, epoch: i64) -> Option<Cid>;
}
/// Message information available to the actor about executing message.
@@ -173,12 +231,21 @@ pub trait MessageInfo {
/// The address of the immediate calling actor. Always an ID-address.
fn caller(&self) -> Address;
+ /// The address of the origin of the current invocation. Always an ID-address
+ fn origin(&self) -> Address;
+
/// The address of the actor receiving the message. Always an ID-address.
fn receiver(&self) -> Address;
/// The value attached to the message being processed, implicitly
/// added to current_balance() before method invocation.
fn value_received(&self) -> TokenAmount;
+
+ /// The message gas limit
+ fn gas_limit(&self) -> u64;
+
+ /// The message gas premium
+ fn gas_premium(&self) -> TokenAmount;
}
/// Pure functions implemented as primitives by the runtime.
@@ -186,6 +253,12 @@ 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<u8>;
+
+ /// 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,
@@ -200,6 +273,15 @@ pub trait Primitives {
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>;
+
+ #[cfg(feature = "m2-native")]
+ fn install_actor(&self, code_cid: &Cid) -> Result<(), anyhow::Error>;
}
/// filcrypto verification primitives provided by the runtime
diff --git a/runtime/src/runtime/policy.rs b/runtime/src/runtime/policy.rs
index 5559433b..41c6561a 100644
--- a/runtime/src/runtime/policy.rs
+++ b/runtime/src/runtime/policy.rs
@@ -287,7 +287,8 @@ pub mod policy_constants {
/// The period over which all a miner's active sectors will be challenged.
pub const WPOST_PROVING_PERIOD: ChainEpoch = EPOCHS_IN_DAY;
/// The duration of a deadline's challenge window, the period before a deadline when the challenge is available.
- pub const WPOST_CHALLENGE_WINDOW: ChainEpoch = 30 * 60 / EPOCH_DURATION_SECONDS; // Half an hour (=48 per day)
+ pub const WPOST_CHALLENGE_WINDOW: ChainEpoch = 30 * 60 / EPOCH_DURATION_SECONDS;
+ // Half an hour (=48 per day)
/// The number of non-overlapping PoSt deadlines in each proving period.
pub const WPOST_PERIOD_DEADLINES: u64 = 48;
/// The maximum distance back that a valid Window PoSt must commit to the current chain.
@@ -411,11 +412,14 @@ pub mod policy_constants {
pub const MINIMUM_CONSENSUS_POWER: i64 = 2 << 10;
#[cfg(feature = "min-power-2g")]
pub const MINIMUM_CONSENSUS_POWER: i64 = 2 << 30;
+ #[cfg(feature = "min-power-16g")]
+ pub const MINIMUM_CONSENSUS_POWER: i64 = 16 << 30;
#[cfg(feature = "min-power-32g")]
pub const MINIMUM_CONSENSUS_POWER: i64 = 32 << 30;
#[cfg(not(any(
feature = "min-power-2k",
feature = "min-power-2g",
+ feature = "min-power-16g",
feature = "min-power-32g"
)))]
pub const MINIMUM_CONSENSUS_POWER: i64 = 10 << 40;
diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs
index d83912ce..dde00409 100644
--- a/runtime/src/test_utils.rs
+++ b/runtime/src/test_utils.rs
@@ -9,15 +9,17 @@ use std::rc::Rc;
use anyhow::anyhow;
use cid::multihash::{Code, Multihash as OtherMultihash};
use cid::Cid;
-use fvm_ipld_blockstore::MemoryBlockstore;
+use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore};
use fvm_ipld_encoding::de::DeserializeOwned;
use fvm_ipld_encoding::{Cbor, CborStore, RawBytes};
-use fvm_shared::address::Payload;
-use fvm_shared::address::{Address, Protocol};
+use fvm_shared::address::{Address, Payload, Protocol};
use fvm_shared::clock::ChainEpoch;
use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED};
use fvm_shared::consensus::ConsensusFault;
-use fvm_shared::crypto::signature::Signature;
+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;
@@ -37,9 +39,10 @@ use rand::prelude::*;
use crate::runtime::builtins::Type;
use crate::runtime::{
ActorCode, DomainSeparationTag, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy,
- Verifier,
+ Verifier, EMPTY_ARR_CID,
};
use crate::{actor_error, ActorError};
+use libsecp256k1::{recover, Message, RecoveryId, Signature as EcsdaSignature};
lazy_static::lazy_static! {
pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/system");
@@ -54,6 +57,9 @@ lazy_static::lazy_static! {
pub static ref REWARD_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/reward");
pub static ref VERIFREG_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/verifiedregistry");
pub static ref DATACAP_TOKEN_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/datacap");
+ pub static ref EMBRYO_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/embryo");
+ pub static ref EVM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/evm");
+ pub static ref EAM_ACTOR_CODE_ID: Cid = make_builtin(b"fil/test/eam");
pub static ref ACTOR_TYPES: BTreeMap<Cid, Type> = {
let mut map = BTreeMap::new();
map.insert(*SYSTEM_ACTOR_CODE_ID, Type::System);
@@ -68,6 +74,9 @@ lazy_static::lazy_static! {
map.insert(*REWARD_ACTOR_CODE_ID, Type::Reward);
map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry);
map.insert(*DATACAP_TOKEN_ACTOR_CODE_ID, Type::DataCap);
+ map.insert(*EMBRYO_ACTOR_CODE_ID, Type::Embryo);
+ map.insert(*EVM_ACTOR_CODE_ID, Type::EVM);
+ map.insert(*EAM_ACTOR_CODE_ID, Type::EAM);
map
};
pub static ref ACTOR_CODES: BTreeMap<Type, Cid> = [
@@ -83,6 +92,9 @@ lazy_static::lazy_static! {
(Type::Reward, *REWARD_ACTOR_CODE_ID),
(Type::VerifiedRegistry, *VERIFREG_ACTOR_CODE_ID),
(Type::DataCap, *DATACAP_TOKEN_ACTOR_CODE_ID),
+ (Type::Embryo, *EMBRYO_ACTOR_CODE_ID),
+ (Type::EVM, *EVM_ACTOR_CODE_ID),
+ (Type::EAM, *EAM_ACTOR_CODE_ID),
]
.into_iter()
.collect();
@@ -92,6 +104,8 @@ lazy_static::lazy_static! {
map.insert(*PAYCH_ACTOR_CODE_ID, ());
map.insert(*MULTISIG_ACTOR_CODE_ID, ());
map.insert(*MINER_ACTOR_CODE_ID, ());
+ map.insert(*EMBRYO_ACTOR_CODE_ID, ());
+ map.insert(*EVM_ACTOR_CODE_ID, ());
map
};
}
@@ -103,19 +117,29 @@ pub fn make_builtin(bz: &[u8]) -> Cid {
Cid::new_v1(IPLD_RAW, OtherMultihash::wrap(0, bz).expect("name too long"))
}
-pub struct MockRuntime {
+pub struct MockRuntime<BS = MemoryBlockstore> {
pub epoch: ChainEpoch,
pub miner: Address,
pub base_fee: TokenAmount,
pub id_addresses: HashMap<Address, Address>,
+ pub delegated_addresses: HashMap<Address, Address>,
+ pub delegated_addresses_source: HashMap<Address, Address>,
pub actor_code_cids: HashMap<Address, Cid>,
pub new_actor_addr: Option<Address>,
pub receiver: Address,
pub caller: Address,
pub caller_type: Cid,
+ pub origin: Address,
pub value_received: TokenAmount,
#[allow(clippy::type_complexity)]
- pub hash_func: Box<dyn Fn(&[u8]) -> [u8; 32]>,
+ pub hash_func: Box<dyn Fn(SupportedHashes, &[u8]) -> ([u8; 64], usize)>,
+ #[allow(clippy::type_complexity)]
+ pub recover_pubkey_fn: Box<
+ dyn Fn(
+ &[u8; SECP_SIG_MESSAGE_HASH_SIZE],
+ &[u8; SECP_SIG_LEN],
+ ) -> Result<[u8; SECP_PUB_LEN], ()>,
+ >,
pub network_version: NetworkVersion,
// Actor State
@@ -124,7 +148,7 @@ pub struct MockRuntime {
// VM Impl
pub in_call: bool,
- pub store: Rc<MemoryBlockstore>,
+ pub store: Rc<BS>,
pub in_transaction: bool,
// Expectations
@@ -134,12 +158,20 @@ pub struct MockRuntime {
pub policy: Policy,
pub circulating_supply: TokenAmount,
+
+ // iron
+ pub gas_limit: u64,
+ pub gas_premium: TokenAmount,
+ pub actor_balances: HashMap<ActorID, TokenAmount>,
+ pub tipset_timestamp: u64,
+ pub tipset_cids: Vec<Cid>,
}
#[derive(Default)]
pub struct Expectations {
pub expect_validate_caller_any: bool,
pub expect_validate_caller_addr: Option<Vec<Address>>,
+ pub expect_validate_caller_f4_namespace: Option<Vec<u64>>,
pub expect_validate_caller_type: Option<Vec<Type>>,
pub expect_sends: VecDeque<ExpectedMessage>,
pub expect_create_actor: Option<ExpectCreateActor>,
@@ -155,6 +187,7 @@ pub struct Expectations {
pub expect_aggregate_verify_seals: Option<ExpectAggregateVerifySeals>,
pub expect_replica_verify: Option<ExpectReplicaVerify>,
pub expect_gas_charge: VecDeque<i64>,
+ pub expect_gas_available: VecDeque<u64>,
}
impl Expectations {
@@ -169,6 +202,11 @@ impl Expectations {
"expected ValidateCallerAddr {:?}, not received",
self.expect_validate_caller_addr
);
+ assert!(
+ self.expect_validate_caller_f4_namespace.is_none(),
+ "expected ValidateNamespace {:?}, not received",
+ self.expect_validate_caller_f4_namespace
+ );
assert!(
self.expect_validate_caller_type.is_none(),
"expected ValidateCallerType {:?}, not received",
@@ -244,40 +282,61 @@ impl Expectations {
"expect_gas_charge {:?}, not received",
self.expect_gas_charge
);
+ assert!(
+ self.expect_gas_available.is_empty(),
+ "expect_gas_charge {:?}, not received",
+ self.expect_gas_available
+ );
}
}
impl Default for MockRuntime {
fn default() -> Self {
+ Self::new(Default::default())
+ }
+}
+
+impl<BS> MockRuntime<BS> {
+ pub fn new(store: BS) -> Self {
Self {
epoch: Default::default(),
miner: Address::new_id(0),
base_fee: Default::default(),
id_addresses: Default::default(),
+ delegated_addresses: Default::default(),
+ delegated_addresses_source: Default::default(),
actor_code_cids: Default::default(),
new_actor_addr: Default::default(),
receiver: Address::new_id(0),
caller: Address::new_id(0),
caller_type: Default::default(),
+ origin: Address::new_id(0),
value_received: Default::default(),
- hash_func: Box::new(blake2b_256),
+ hash_func: Box::new(hash),
+ recover_pubkey_fn: Box::new(recover_secp_public_key),
network_version: NetworkVersion::V0,
state: Default::default(),
balance: Default::default(),
in_call: Default::default(),
- store: Default::default(),
+ store: Rc::new(store),
in_transaction: Default::default(),
expectations: Default::default(),
policy: Default::default(),
circulating_supply: Default::default(),
+ gas_limit: 10_000_000_000u64,
+ gas_premium: Default::default(),
+ actor_balances: Default::default(),
+ tipset_timestamp: Default::default(),
+ tipset_cids: Default::default(),
}
}
}
-#[derive(Clone, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ExpectCreateActor {
pub code_id: Cid,
pub actor_id: ActorID,
+ pub predictable_address: Option<Address>,
}
#[derive(Clone, Debug)]
@@ -391,7 +450,7 @@ pub fn expect_abort<T: fmt::Debug>(exit_code: ExitCode, res: Result<T, ActorErro
expect_abort_contains_message(exit_code, "", res);
}
-impl MockRuntime {
+impl<BS: Blockstore> MockRuntime<BS> {
///// Runtime access for tests /////
pub fn get_state<T: Cbor>(&self) -> T {
@@ -424,6 +483,10 @@ impl MockRuntime {
self.actor_code_cids.insert(address, code_id);
}
+ pub fn set_origin(&mut self, address: Address) {
+ self.origin = address;
+ }
+
pub fn set_address_actor_type(&mut self, address: Address, actor_type: Cid) {
self.actor_code_cids.insert(address, actor_type);
}
@@ -440,6 +503,17 @@ impl MockRuntime {
self.id_addresses.insert(source, target);
}
+ pub fn add_delegated_address(&mut self, source: Address, target: Address) {
+ assert_eq!(
+ target.protocol(),
+ Protocol::Delegated,
+ "target must use Delegated address protocol"
+ );
+ assert_eq!(source.protocol(), Protocol::ID, "source must use ID address protocol");
+ self.delegated_addresses.insert(source, target);
+ self.delegated_addresses_source.insert(target, source);
+ }
+
pub fn call<A: ActorCode>(
&mut self,
method_num: MethodNum,
@@ -522,6 +596,12 @@ impl MockRuntime {
self.expectations.borrow_mut().expect_validate_caller_any = true;
}
+ #[allow(dead_code)]
+ pub fn expect_validate_caller_namespace(&self, namespaces: Vec<u64>) {
+ assert!(!namespaces.is_empty(), "f4 namespaces must be non-empty");
+ self.expectations.borrow_mut().expect_validate_caller_f4_namespace = Some(namespaces);
+ }
+
#[allow(dead_code)]
pub fn expect_delete_actor(&mut self, beneficiary: Address) {
self.expectations.borrow_mut().expect_delete_actor = Some(beneficiary);
@@ -548,8 +628,13 @@ impl MockRuntime {
}
#[allow(dead_code)]
- pub fn expect_create_actor(&mut self, code_id: Cid, actor_id: ActorID) {
- let a = ExpectCreateActor { code_id, actor_id };
+ pub fn expect_create_actor(
+ &mut self,
+ code_id: Cid,
+ actor_id: ActorID,
+ predictable_address: Option<Address>,
+ ) {
+ let a = ExpectCreateActor { code_id, actor_id, predictable_address };
self.expectations.borrow_mut().expect_create_actor = Some(a);
}
@@ -640,6 +725,11 @@ impl MockRuntime {
self.expectations.borrow_mut().expect_gas_charge.push_back(value);
}
+ #[allow(dead_code)]
+ pub fn expect_gas_available(&mut self, value: u64) {
+ self.expectations.borrow_mut().expect_gas_available.push_back(value);
+ }
+
///// Private helpers /////
fn require_in_call(&self) {
@@ -655,19 +745,28 @@ impl MockRuntime {
}
}
-impl MessageInfo for MockRuntime {
+impl<BS> MessageInfo for MockRuntime<BS> {
fn caller(&self) -> Address {
self.caller
}
+ fn origin(&self) -> Address {
+ self.origin
+ }
fn receiver(&self) -> Address {
self.receiver
}
fn value_received(&self) -> TokenAmount {
self.value_received.clone()
}
+ fn gas_limit(&self) -> u64 {
+ self.gas_limit
+ }
+ fn gas_premium(&self) -> TokenAmount {
+ self.gas_premium.clone()
+ }
}
-impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
+impl<BS: Blockstore> Runtime<Rc<BS>> for MockRuntime<BS> {
fn network_version(&self) -> NetworkVersion {
self.network_version
}
@@ -725,6 +824,51 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
self.message().caller(), &addrs
))
}
+
+ fn validate_immediate_caller_namespace<I>(&mut self, namespaces: I) -> Result<(), ActorError>
+ where
+ I: IntoIterator<Item = u64>,
+ {
+ self.require_in_call();
+
+ let namespaces: Vec<u64> = namespaces.into_iter().collect();
+
+ let mut expectations = self.expectations.borrow_mut();
+ assert!(
+ expectations.expect_validate_caller_f4_namespace.is_some(),
+ "unexpected validate caller namespace"
+ );
+
+ let expected_namespaces =
+ expectations.expect_validate_caller_f4_namespace.as_ref().unwrap();
+
+ assert_eq!(
+ &namespaces, expected_namespaces,
+ "unexpected validate caller namespace {:?}, expected {:?}",
+ namespaces, &expectations.expect_validate_caller_f4_namespace
+ );
+
+ let caller_f4 = self.lookup_address(self.caller().id().unwrap());
+
+ assert!(caller_f4.is_some(), "unexpected caller doesn't have a delegated address");
+
+ for id in namespaces.iter() {
+ let bound_address = match caller_f4.unwrap().payload() {
+ Payload::Delegated(d) => d.namespace(),
+ _ => unreachable!("lookup_address should always return a delegated address"),
+ };
+ if bound_address == *id {
+ expectations.expect_validate_caller_f4_namespace = None;
+ return Ok(());
+ }
+ }
+ expectations.expect_validate_caller_addr = None;
+ Err(actor_error!(forbidden;
+ "caller address {:?} forbidden, allowed: {:?}",
+ self.message().caller(), &namespaces
+ ))
+ }
+
fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError>
where
I: IntoIterator<Item = &'a Type>,
@@ -762,11 +906,22 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
self.balance.borrow().clone()
}
+ fn actor_balance(&self, id: ActorID) -> Option<TokenAmount> {
+ self.require_in_call();
+ self.actor_balances.get(&id).cloned()
+ }
+
fn resolve_address(&self, address: &Address) -> Option<ActorID> {
self.require_in_call();
if let &Payload::ID(id) = address.payload() {
return Some(id);
}
+ if Protocol::Delegated == address.protocol() {
+ return self
+ .delegated_addresses_source
+ .get(address)
+ .and_then(|a| self.resolve_address(a));
+ }
match self.get_id_address(address) {
None => None,
@@ -779,6 +934,11 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
}
}
+ fn lookup_address(&self, id: ActorID) -> Option<Address> {
+ self.require_in_call();
+ self.delegated_addresses.get(&Address::new_id(id)).copied()
+ }
+
fn get_actor_code_cid(&self, id: &ActorID) -> Option<Cid> {
self.require_in_call();
self.actor_code_cids.get(&Address::new_id(*id)).cloned()
@@ -862,6 +1022,15 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
Ok(self.store_get(self.state.as_ref().unwrap()))
}
+ fn get_state_root(&self) -> Result<Cid, ActorError> {
+ Ok(self.state.unwrap_or(EMPTY_ARR_CID))
+ }
+
+ fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError> {
+ self.state = Some(*root);
+ Ok(())
+ }
+
fn transaction<C, RT, F>(&mut self, f: F) -> Result<RT, ActorError>
where
C: Cbor,
@@ -880,7 +1049,7 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
ret
}
- fn store(&self) -> &Rc<MemoryBlockstore> {
+ fn store(&self) -> &Rc<BS> {
&self.store
}
@@ -949,7 +1118,12 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
Ok(ret)
}
- fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> {
+ fn create_actor(
+ &mut self,
+ code_id: Cid,
+ actor_id: ActorID,
+ predictable_address: Option<Address>,
+ ) -> Result<(), ActorError> {
self.require_in_call();
if self.in_transaction {
return Err(actor_error!(assertion_failed; "side-effect within transaction"));
@@ -961,7 +1135,11 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
.take()
.expect("unexpected call to create actor");
- assert!(expect_create_actor.code_id == code_id && expect_create_actor.actor_id == actor_id, "unexpected actor being created, expected code: {:?} address: {:?}, actual code: {:?} address: {:?}", expect_create_actor.code_id, expect_create_actor.actor_id, code_id, actor_id);
+ assert_eq!(
+ expect_create_actor,
+ ExpectCreateActor { code_id, actor_id, predictable_address },
+ "unexpected actor being created"
+ );
Ok(())
}
@@ -1008,9 +1186,26 @@ impl Runtime<Rc<MemoryBlockstore>> for MockRuntime {
fn base_fee(&self) -> TokenAmount {
self.base_fee.clone()
}
+
+ fn gas_available(&self) -> u64 {
+ let mut exs = self.expectations.borrow_mut();
+ assert!(!exs.expect_gas_available.is_empty(), "unexpected gas available call");
+ exs.expect_gas_available.pop_front().unwrap()
+ }
+
+ fn tipset_timestamp(&self) -> u64 {
+ self.tipset_timestamp
+ }
+
+ fn tipset_cid(&self, epoch: i64) -> Option<Cid> {
+ if epoch > self.epoch || epoch < 0 {
+ return None;
+ }
+ self.tipset_cids.get((self.epoch - epoch) as usize).copied()
+ }
}
-impl Primitives for MockRuntime {
+impl<BS> Primitives for MockRuntime<BS> {
fn verify_signature(
&self,
signature: &Signature,
@@ -1053,8 +1248,17 @@ impl Primitives for MockRuntime {
}
fn hash_blake2b(&self, data: &[u8]) -> [u8; 32] {
- (*self.hash_func)(data)
+ let (digest, _) = (*self.hash_func)(SupportedHashes::Blake2b256, data);
+ let mut ret = [0u8; 32];
+ ret.copy_from_slice(&digest[..32]);
+ ret
+ }
+
+ fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec<u8> {
+ let (digest, len) = (*self.hash_func)(hasher, data);
+ Vec::from(&digest[..len])
}
+
fn compute_unsealed_sector_cid(
&self,
reg: RegisteredSealProof,
@@ -1083,9 +1287,26 @@ impl Primitives for MockRuntime {
}
Ok(exp.cid)
}
+
+ #[cfg(feature = "m2-native")]
+ fn install_actor(&self, _code_cid: &Cid) -> anyhow::Result<(), anyhow::Error> {
+ 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> {
+ (*self.recover_pubkey_fn)(hash, signature).map_err(|_| anyhow!("failed to recover pubkey."))
+ }
+
+ fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) {
+ (*self.hash_func)(hasher, data)
+ }
}
-impl Verifier for MockRuntime {
+impl<BS> Verifier for MockRuntime<BS> {
fn verify_seal(&self, seal: &SealVerifyInfo) -> anyhow::Result<()> {
let exp = self
.expectations
@@ -1213,7 +1434,7 @@ impl Verifier for MockRuntime {
}
}
-impl RuntimePolicy for MockRuntime {
+impl<BS> RuntimePolicy for MockRuntime<BS> {
fn policy(&self) -> &Policy {
&self.policy
}
@@ -1230,6 +1451,30 @@ pub fn blake2b_256(data: &[u8]) -> [u8; 32] {
.unwrap()
}
+pub fn hash(hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) {
+ let hasher = Code::try_from(hasher as u64).unwrap();
+ let (_, digest, written) = hasher.digest(data).into_inner();
+ (digest, written as usize)
+}
+
+#[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)]
diff --git a/runtime/src/util/chaos/mod.rs b/runtime/src/util/chaos/mod.rs
index de8f4a80..335e119e 100644
--- a/runtime/src/util/chaos/mod.rs
+++ b/runtime/src/util/chaos/mod.rs
@@ -123,7 +123,7 @@ impl Actor {
let actor_address = arg.actor_id;
- rt.create_actor(actor_cid, actor_address)
+ rt.create_actor(actor_cid, actor_address, None)
}
/// Resolves address, and returns the resolved address (defaulting to 0 ID) and success boolean.
diff --git a/state/Cargo.toml b/state/Cargo.toml
index 15221012..1663fb15 100644
--- a/state/Cargo.toml
+++ b/state/Cargo.toml
@@ -27,17 +27,15 @@ fil_actor_reward = { version = "10.0.0-alpha.1", path = "../actors/reward"}
fil_actor_system = { version = "10.0.0-alpha.1", path = "../actors/system"}
fil_actor_init = { version = "10.0.0-alpha.1", path = "../actors/init"}
fil_actors_runtime = { version = "10.0.0-alpha.1", path = "../runtime"}
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
-fvm_ipld_encoding = "0.2.2"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
+fvm_ipld_encoding = "0.3.0"
fvm_ipld_blockstore = "0.1.1"
num-traits = "0.2.14"
-anyhow = "1.0.65"
bimap = { version = "0.6.2" }
+anyhow = "1.0.65"
num-derive = "0.3.3"
serde = { version = "1.0.136", features = ["derive"] }
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
-[dev-dependencies]
-
[features]
fil-actor = ["fil_actors_runtime/fil-actor"]
diff --git a/state/src/check.rs b/state/src/check.rs
index 00131bf5..483f5705 100644
--- a/state/src/check.rs
+++ b/state/src/check.rs
@@ -64,6 +64,10 @@ pub struct Actor {
pub call_seq_num: u64,
/// Token balance of the actor
pub balance: TokenAmount,
+ /// The actor's "predictable" address, if assigned.
+ ///
+ /// This field is set on actor creation and never modified.
+ pub address: Option<Address>,
}
/// A specialization of a map of ID-addresses to actor heads.
@@ -216,6 +220,9 @@ pub fn check_state_invariants<'a, BS: Blockstore + Debug>(
acc.with_prefix("datacap: ").add_all(&msgs);
datacap_summary = Some(summary);
}
+ Some(Type::Embryo) => {}
+ Some(Type::EVM) => {}
+ Some(Type::EAM) => {}
None => {
bail!("unexpected actor code CID {} for address {}", actor.code, key);
}
diff --git a/test_vm/Cargo.toml b/test_vm/Cargo.toml
index 3ad8eb4c..f25c20fb 100644
--- a/test_vm/Cargo.toml
+++ b/test_vm/Cargo.toml
@@ -25,17 +25,19 @@ fil_actor_market = { version = "10.0.0-alpha.1", path = "../actors/market" }
fil_actor_verifreg = { version = "10.0.0-alpha.1", path = "../actors/verifreg" }
fil_actor_miner = { version = "10.0.0-alpha.1", path = "../actors/miner" }
fil_actor_datacap = { version = "10.0.0-alpha.1", path = "../actors/datacap" }
+fil_actor_evm = { version = "10.0.0-alpha.1", path = "../actors/evm" }
+fil_actor_eam = { version = "10.0.0-alpha.1", path = "../actors/eam" }
anyhow = "1.0.65"
bimap = { version = "0.6.2" }
blake2b_simd = "1.0"
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
frc46_token = "1.1.0"
-fvm_ipld_bitfield = "0.5.2"
+fvm_ipld_bitfield = "0.5.4"
fvm_ipld_blockstore = { version = "0.1.1", default-features = false }
-fvm_ipld_encoding = { version = "0.2.2", default-features = false }
-fvm_ipld_hamt = "0.5.1"
-fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
+fvm_ipld_encoding = { version = "0.3.0", default-features = false }
+fvm_ipld_hamt = "0.6.0"
+fvm_shared = { version = "3.0.0-alpha.8", default-features = false }
indexmap = { version = "1.8.0", features = ["serde-1"] }
integer-encoding = { version = "3.0.3", default-features = false }
lazy_static = "1.4.0"
@@ -50,5 +52,10 @@ thiserror = "1.0.30"
[dev-dependencies]
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
-multihash = { version = "0.16.1", default-features = false }
+ethers = { version = "0.17.0", features = ["abigen"] }
+hex = "0.4.3"
test-case = "2.2.1"
+multihash = { version = "0.16.1", default-features = false }
+
+[features]
+m2-native = []
diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs
index ceda6a81..28c75bfb 100644
--- a/test_vm/src/lib.rs
+++ b/test_vm/src/lib.rs
@@ -1,10 +1,13 @@
use anyhow::anyhow;
use bimap::BiBTreeMap;
use cid::multihash::Code;
+use cid::multihash::MultihashDigest;
use cid::Cid;
use fil_actor_account::{Actor as AccountActor, State as AccountState};
use fil_actor_cron::{Actor as CronActor, Entry as CronEntry, State as CronState};
use fil_actor_datacap::{Actor as DataCapActor, State as DataCapState};
+use fil_actor_eam::EamActor;
+use fil_actor_evm::EvmContractActor;
use fil_actor_init::{Actor as InitActor, ExecReturn, State as InitState};
use fil_actor_market::{Actor as MarketActor, Method as MarketMethod, State as MarketState};
use fil_actor_miner::{Actor as MinerActor, MinerInfo, State as MinerState};
@@ -23,9 +26,9 @@ use fil_actors_runtime::runtime::{
};
use fil_actors_runtime::test_utils::*;
use fil_actors_runtime::{
- ActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, FIRST_NON_SINGLETON_ADDR, INIT_ACTOR_ADDR,
- REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
- VERIFIED_REGISTRY_ACTOR_ADDR,
+ ActorError, BURNT_FUNDS_ACTOR_ADDR, CRON_ACTOR_ADDR, EAM_ACTOR_ADDR, FIRST_NON_SINGLETON_ADDR,
+ INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR,
+ SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR,
};
use fil_actors_runtime::{MessageAccumulator, DATACAP_TOKEN_ACTOR_ADDR};
use fil_builtin_actors_state::check::check_state_invariants;
@@ -34,12 +37,14 @@ use fvm_ipld_blockstore::MemoryBlockstore;
use fvm_ipld_encoding::tuple::*;
use fvm_ipld_encoding::{Cbor, CborStore, RawBytes};
use fvm_ipld_hamt::{BytesKey, Hamt, Sha256};
-use fvm_shared::address::Payload;
-use fvm_shared::address::{Address, Protocol};
+use fvm_shared::address::{Address, Payload};
use fvm_shared::bigint::Zero;
use fvm_shared::clock::ChainEpoch;
use fvm_shared::consensus::ConsensusFault;
-use fvm_shared::crypto::signature::Signature;
+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;
@@ -51,7 +56,7 @@ use fvm_shared::sector::{
};
use fvm_shared::smooth::FilterEstimate;
use fvm_shared::version::NetworkVersion;
-use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND};
+use fvm_shared::{ActorID, MethodNum, IPLD_RAW, METHOD_CONSTRUCTOR, METHOD_SEND};
use regex::Regex;
use serde::ser;
use std::cell::{RefCell, RefMut};
@@ -138,17 +143,23 @@ impl<'bs> VM<'bs> {
let sys_st = SystemState::new(store).unwrap();
let sys_head = v.put_store(&sys_st);
let sys_value = faucet_total.clone(); // delegate faucet funds to system so we can construct faucet by sending to bls addr
- v.set_actor(SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value));
+ v.set_actor(SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value, None));
// init
let init_st = InitState::new(store, "integration-test".to_string()).unwrap();
let init_head = v.put_store(&init_st);
- v.set_actor(INIT_ACTOR_ADDR, actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero()));
+ v.set_actor(
+ INIT_ACTOR_ADDR,
+ actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero(), None),
+ );
// reward
let reward_head = v.put_store(&RewardState::new(StoragePower::zero()));
- v.set_actor(REWARD_ACTOR_ADDR, actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total));
+ v.set_actor(
+ REWARD_ACTOR_ADDR,
+ actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total, None),
+ );
// cron
let builtin_entries = vec![
@@ -162,20 +173,23 @@ impl<'bs> VM<'bs> {
},
];
let cron_head = v.put_store(&CronState { entries: builtin_entries });
- v.set_actor(CRON_ACTOR_ADDR, actor(*CRON_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero()));
+ v.set_actor(
+ CRON_ACTOR_ADDR,
+ actor(*CRON_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero(), None),
+ );
// power
let power_head = v.put_store(&PowerState::new(&v.store).unwrap());
v.set_actor(
STORAGE_POWER_ACTOR_ADDR,
- actor(*POWER_ACTOR_CODE_ID, power_head, 0, TokenAmount::zero()),
+ actor(*POWER_ACTOR_CODE_ID, power_head, 0, TokenAmount::zero(), None),
);
// market
let market_head = v.put_store(&MarketState::new(&v.store).unwrap());
v.set_actor(
STORAGE_MARKET_ACTOR_ADDR,
- actor(*MARKET_ACTOR_CODE_ID, market_head, 0, TokenAmount::zero()),
+ actor(*MARKET_ACTOR_CODE_ID, market_head, 0, TokenAmount::zero(), None),
);
// verifreg
@@ -223,22 +237,28 @@ impl<'bs> VM<'bs> {
let verifreg_head = v.put_store(&VerifRegState::new(&v.store, root_msig_addr).unwrap());
v.set_actor(
VERIFIED_REGISTRY_ACTOR_ADDR,
- actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero()),
+ actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero(), None),
);
- // datacap
- let datacap_head =
- v.put_store(&DataCapState::new(&v.store, VERIFIED_REGISTRY_ACTOR_ADDR).unwrap());
+ // Ethereum Address Manager
v.set_actor(
- DATACAP_TOKEN_ACTOR_ADDR,
- actor(*DATACAP_TOKEN_ACTOR_CODE_ID, datacap_head, 0, TokenAmount::zero()),
+ EAM_ACTOR_ADDR,
+ actor(*EAM_ACTOR_CODE_ID, EMPTY_ARR_CID, 0, TokenAmount::zero(), None),
);
// burnt funds
let burnt_funds_head = v.put_store(&AccountState { address: BURNT_FUNDS_ACTOR_ADDR });
v.set_actor(
BURNT_FUNDS_ACTOR_ADDR,
- actor(*ACCOUNT_ACTOR_CODE_ID, burnt_funds_head, 0, TokenAmount::zero()),
+ actor(*ACCOUNT_ACTOR_CODE_ID, burnt_funds_head, 0, TokenAmount::zero(), None),
+ );
+
+ // datacap
+ let datacap_head =
+ v.put_store(&DataCapState::new(&v.store, VERIFIED_REGISTRY_ACTOR_ADDR).unwrap());
+ v.set_actor(
+ DATACAP_TOKEN_ACTOR_ADDR,
+ actor(*DATACAP_TOKEN_ACTOR_CODE_ID, datacap_head, 0, TokenAmount::zero(), None),
);
// create a faucet with 1 billion FIL for setting up test accounts
@@ -541,12 +561,21 @@ impl MessageInfo for InvocationCtx<'_, '_> {
fn caller(&self) -> Address {
self.msg.from
}
+ fn origin(&self) -> Address {
+ Address::new_id(self.resolve_address(&self.top.originator_stable_addr).unwrap())
+ }
fn receiver(&self) -> Address {
self.to()
}
fn value_received(&self) -> TokenAmount {
self.msg.value.clone()
}
+ fn gas_limit(&self) -> u64 {
+ u32::MAX.into()
+ }
+ fn gas_premium(&self) -> TokenAmount {
+ TokenAmount::zero()
+ }
}
pub const TEST_VM_RAND_ARRAY: [u8; 32] = [
@@ -573,16 +602,22 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> {
}
};
// Address does not yet exist, create it
- let protocol = target.protocol();
- match protocol {
- Protocol::Actor | Protocol::ID => {
+ let is_account = match target.payload() {
+ Payload::Secp256k1(_) | Payload::BLS(_) => true,
+ Payload::Delegated(da)
+ // Validate that there's an actor at the target ID (we don't care what is there,
+ // just that something is there).
+ if self.v.get_actor(Address::new_id(da.namespace())).is_some() =>
+ {
+ false
+ }
+ _ => {
return Err(ActorError::unchecked(
ExitCode::SYS_INVALID_RECEIVER,
- format!("cannot create account for address {} type {}", target, protocol),
+ format!("cannot create account for address {} type {}", target, target.protocol()),
));
}
- _ => (),
- }
+ };
let mut st = self.v.get_state::<InitState>(INIT_ACTOR_ADDR).unwrap();
let target_id = st.map_address_to_new_id(self.v.store, target).unwrap();
let target_id_addr = Address::new_id(target_id);
@@ -607,13 +642,17 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> {
policy: self.policy,
subinvocations: RefCell::new(vec![]),
};
- new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id).unwrap();
- let res = new_ctx.invoke();
- let invoc = new_ctx.gather_trace(res);
- RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| {
- subinvocs.push(invoc);
- subinvocs
- });
+ if is_account {
+ new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id, Some(*target)).unwrap();
+ let res = new_ctx.invoke();
+ let invoc = new_ctx.gather_trace(res);
+ RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| {
+ subinvocs.push(invoc);
+ subinvocs
+ });
+ } else {
+ new_ctx.create_actor(*EMBRYO_ACTOR_CODE_ID, target_id, Some(*target)).unwrap();
+ }
}
Ok((self.v.get_actor(target_id_addr).unwrap(), target_id_addr))
@@ -685,8 +724,12 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> {
Type::Power => PowerActor::invoke_method(self, self.msg.method, &params),
Type::PaymentChannel => PaychActor::invoke_method(self, self.msg.method, &params),
Type::VerifiedRegistry => VerifregActor::invoke_method(self, self.msg.method, &params),
- // Type::EVM => panic!("no EVM"),
Type::DataCap => DataCapActor::invoke_method(self, self.msg.method, &params),
+ Type::Embryo => {
+ Err(ActorError::unhandled_message("embryo actors only handle method 0".into()))
+ }
+ Type::EVM => EvmContractActor::invoke_method(self, self.msg.method, &params),
+ Type::EAM => EamActor::invoke_method(self, self.msg.method, &params),
};
if res.is_ok() && !self.caller_validated {
res = Err(actor_error!(assertion_failed, "failed to validate caller"));
@@ -700,7 +743,12 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> {
}
impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocation, 'bs> {
- fn create_actor(&mut self, code_id: Cid, actor_id: ActorID) -> Result<(), ActorError> {
+ fn create_actor(
+ &mut self,
+ code_id: Cid,
+ actor_id: ActorID,
+ predictable_address: Option<Address>,
+ ) -> Result<(), ActorError> {
match NON_SINGLETON_CODES.get(&code_id) {
Some(_) => (),
None => {
@@ -711,14 +759,21 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
}
}
let addr = Address::new_id(actor_id);
- if self.v.get_actor(addr).is_some() {
- return Err(ActorError::unchecked(
- ExitCode::SYS_ASSERTION_FAILED,
- "attempt to create new actor at existing address".to_string(),
- ));
- }
- let a = actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero());
- self.v.set_actor(addr, a);
+ let actor = match self.v.get_actor(addr) {
+ Some(mut act) if act.code == *EMBRYO_ACTOR_CODE_ID => {
+ act.code = code_id;
+ act
+ }
+ None => actor(code_id, EMPTY_ARR_CID, 0, TokenAmount::zero(), predictable_address),
+ _ => {
+ // can happen if an actor is deployed to an f4 address.
+ return Err(ActorError::unchecked(
+ ExitCode::USR_FORBIDDEN,
+ "attempt to create new actor at existing address".to_string(),
+ ));
+ }
+ };
+ self.v.set_actor(addr, actor);
Ok(())
}
@@ -750,6 +805,43 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
}
}
+ fn validate_immediate_caller_namespace<I>(
+ &mut self,
+ namespace_manager_addresses: I,
+ ) -> Result<(), ActorError>
+ where
+ I: IntoIterator<Item = u64>,
+ {
+ if self.caller_validated {
+ return Err(ActorError::unchecked(
+ ExitCode::SYS_ASSERTION_FAILED,
+ "caller double validated".to_string(),
+ ));
+ }
+ let managers: Vec<_> = namespace_manager_addresses.into_iter().collect();
+
+ if let Some(delegated) = self.lookup_address(self.message().caller().id().unwrap()) {
+ for id in managers {
+ if match delegated.payload() {
+ Payload::Delegated(d) => d.namespace() == id,
+ _ => false,
+ } {
+ return Ok(());
+ }
+ }
+ } else {
+ return Err(ActorError::unchecked(
+ ExitCode::SYS_ASSERTION_FAILED,
+ "immediate caller actor expected to have namespace".to_string(),
+ ));
+ }
+
+ Err(ActorError::unchecked(
+ ExitCode::SYS_ASSERTION_FAILED,
+ "immediate caller actor namespace forbidden".to_string(),
+ ))
+ }
+
fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError>
where
I: IntoIterator<Item = &'a Address>,
@@ -814,6 +906,10 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
}
}
+ fn lookup_address(&self, id: ActorID) -> Option<Address> {
+ self.v.get_actor(Address::new_id(id)).and_then(|act| act.predictable_address)
+ }
+
fn send(
&self,
to: &Address,
@@ -866,32 +962,25 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
Ok(TEST_VM_RAND_ARRAY)
}
- fn create<C: Cbor>(&mut self, obj: &C) -> Result<(), ActorError> {
+ fn get_state_root(&self) -> Result<Cid, ActorError> {
+ Ok(self.v.get_actor(self.to()).unwrap().head)
+ }
+
+ fn set_state_root(&mut self, root: &Cid) -> Result<(), ActorError> {
let maybe_act = self.v.get_actor(self.to());
match maybe_act {
None => Err(ActorError::unchecked(
ExitCode::SYS_ASSERTION_FAILED,
- "failed to create state".to_string(),
+ "actor does not exist".to_string(),
)),
Some(mut act) => {
- if act.head != EMPTY_ARR_CID {
- Err(ActorError::unchecked(
- ExitCode::SYS_ASSERTION_FAILED,
- "failed to construct state: already initialized".to_string(),
- ))
- } else {
- act.head = self.v.store.put_cbor(obj, Code::Blake2b256).unwrap();
- self.v.set_actor(self.to(), act);
- Ok(())
- }
+ act.head = *root;
+ self.v.set_actor(self.to(), act);
+ Ok(())
}
}
}
- fn state<C: Cbor>(&self) -> Result<C, ActorError> {
- Ok(self.v.get_state::<C>(self.to()).unwrap())
- }
-
fn transaction<C, RT, F>(&mut self, f: F) -> Result<RT, ActorError>
where
C: Cbor,
@@ -938,6 +1027,22 @@ impl<'invocation, 'bs> Runtime<&'bs MemoryBlockstore> for InvocationCtx<'invocat
fn base_fee(&self) -> TokenAmount {
TokenAmount::zero()
}
+
+ fn actor_balance(&self, id: ActorID) -> Option<TokenAmount> {
+ self.v.get_actor(Address::new_id(id)).map(|act| act.balance)
+ }
+
+ fn gas_available(&self) -> u64 {
+ u32::MAX.into()
+ }
+
+ fn tipset_timestamp(&self) -> u64 {
+ 0
+ }
+
+ fn tipset_cid(&self, _epoch: i64) -> Option<Cid> {
+ Some(Cid::new_v1(IPLD_RAW, Multihash::wrap(0, b"faketipset").unwrap()))
+ }
}
impl Primitives for VM<'_> {
@@ -975,6 +1080,25 @@ impl Primitives for VM<'_> {
) -> Result<Cid, anyhow::Error> {
Ok(make_piece_cid(b"unsealed from itest vm"))
}
+
+ fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec<u8> {
+ let hasher = Code::try_from(hasher as u64).unwrap(); // supported hashes are all implemented in multihash
+ hasher.digest(data).to_bytes()
+ }
+
+ 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 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 pubkey"))
+ }
}
impl Primitives for InvocationCtx<'_, '_> {
@@ -998,6 +1122,27 @@ impl Primitives for InvocationCtx<'_, '_> {
) -> Result<Cid, anyhow::Error> {
self.v.compute_unsealed_sector_cid(proof_type, pieces)
}
+
+ #[cfg(feature = "m2-native")]
+ fn install_actor(&self, _: &Cid) -> Result<(), anyhow::Error> {
+ panic!("TODO implement me")
+ }
+
+ fn hash(&self, hasher: SupportedHashes, data: &[u8]) -> Vec<u8> {
+ self.v.hash(hasher, data)
+ }
+
+ fn hash_64(&self, hasher: SupportedHashes, data: &[u8]) -> ([u8; 64], usize) {
+ self.v.hash_64(hasher, data)
+ }
+
+ 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> {
+ self.v.recover_secp_public_key(hash, signature)
+ }
}
impl Verifier for InvocationCtx<'_, '_> {
@@ -1059,10 +1204,17 @@ pub struct Actor {
pub head: Cid,
pub call_seq_num: u64,
pub balance: TokenAmount,
+ pub predictable_address: Option<Address>,
}
-pub fn actor(code: Cid, head: Cid, seq: u64, bal: TokenAmount) -> Actor {
- Actor { code, head, call_seq_num: seq, balance: bal }
+pub fn actor(
+ code: Cid,
+ head: Cid,
+ call_seq_num: u64,
+ balance: TokenAmount,
+ predictable_address: Option<Address>,
+) -> Actor {
+ Actor { code, head, call_seq_num, balance, predictable_address }
}
#[derive(Clone)]
diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs
index 1e17fe49..8311d1fa 100644
--- a/test_vm/tests/test_vm_test.rs
+++ b/test_vm/tests/test_vm_test.rs
@@ -18,14 +18,25 @@ fn state_control() {
let addr2 = Address::new_id(2222);
// set actor
- let a1 =
- actor(*ACCOUNT_ACTOR_CODE_ID, make_builtin(b"a1-head"), 42, TokenAmount::from_atto(10u8));
+ let a1 = actor(
+ *ACCOUNT_ACTOR_CODE_ID,
+ make_builtin(b"a1-head"),
+ 42,
+ TokenAmount::from_atto(10u8),
+ None,
+ );
v.set_actor(addr1, a1.clone());
let out = v.get_actor(addr1).unwrap();
assert_eq!(out, a1);
let check = v.checkpoint();
- let a2 = actor(*PAYCH_ACTOR_CODE_ID, make_builtin(b"a2-head"), 88, TokenAmount::from_atto(1u8));
+ let a2 = actor(
+ *PAYCH_ACTOR_CODE_ID,
+ make_builtin(b"a2-head"),
+ 88,
+ TokenAmount::from_atto(1u8),
+ None,
+ );
v.set_actor(addr2, a2.clone());
assert_eq!(v.get_actor(addr2).unwrap(), a2);
// rollback removes a2 but not a1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment