From 378adb5d8023576dd8e4157a6add22f63e15c16b Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 15 Nov 2023 10:55:52 +0000 Subject: [PATCH] wip --- fvm/src/engine/mod.rs | 10 +- fvm/src/kernel/default.rs | 36 ++++-- fvm/src/kernel/mod.rs | 17 ++- fvm/src/syscalls/mod.rs | 231 +++++++++++++++++++--------------- testing/conformance/src/vm.rs | 16 +++ 5 files changed, 195 insertions(+), 115 deletions(-) diff --git a/fvm/src/engine/mod.rs b/fvm/src/engine/mod.rs index 9096cb8b8e..0abc335fe1 100644 --- a/fvm/src/engine/mod.rs +++ b/fvm/src/engine/mod.rs @@ -27,8 +27,7 @@ use crate::machine::limiter::MemoryLimiter; use crate::machine::{Machine, NetworkConfig}; use crate::syscalls::error::Abort; use crate::syscalls::{ - bind_syscalls, charge_for_exec, charge_for_init, record_init_time, update_gas_available, - InvocationData, + charge_for_exec, charge_for_init, record_init_time, update_gas_available, InvocationData, }; use crate::Kernel; @@ -517,7 +516,12 @@ impl Engine { let mut linker: Linker> = Linker::new(&self.inner.engine); linker.allow_shadowing(true); - bind_syscalls(&mut linker).map_err(Abort::Fatal)?; + store + .data() + .kernel + .bind_syscalls(&mut linker) + .map_err(Abort::Fatal)?; + Box::new(Cache { linker }) }) .downcast_mut() diff --git a/fvm/src/kernel/default.rs b/fvm/src/kernel/default.rs index 6e20873a00..0faa37dbda 100644 --- a/fvm/src/kernel/default.rs +++ b/fvm/src/kernel/default.rs @@ -53,24 +53,46 @@ const MAX_ARTIFACT_NAME_LEN: usize = 256; #[cfg(feature = "testing")] const TEST_ACTOR_ALLOWED_TO_CALL_CREATE_ACTOR: ActorID = 98; +pub struct DefaultFilecoinKernel(pub K) +where + K: Kernel; + +impl FilecoinKernel for DefaultFilecoinKernel> +where + C: CallManager, +{ + /// Verifies a window proof of spacetime. + fn verify_post(&self, verify_info: &WindowPoStVerifyInfo) -> Result { + let t = self + .0 + .call_manager + .charge_gas(self.0.call_manager.price_list().on_verify_post(verify_info))?; + + // This is especially important to catch as, otherwise, a bad "post" could be undisputable. + t.record(catch_and_log_panic("verifying post", || { + verify_post(verify_info) + })) + } +} + /// The "default" [`Kernel`] implementation. pub struct DefaultKernel { // Fields extracted from the message, except parameters, which have been // preloaded into the block registry. - caller: ActorID, - actor_id: ActorID, - method: MethodNum, - value_received: TokenAmount, - read_only: bool, + pub caller: ActorID, + pub actor_id: ActorID, + pub method: MethodNum, + pub value_received: TokenAmount, + pub read_only: bool, /// The call manager for this call stack. If this kernel calls another actor, it will /// temporarily "give" the call manager to the other kernel before re-attaching it. - call_manager: C, + pub call_manager: C, /// Tracks block data and organizes it through index handles so it can be /// referred to. /// /// This does not yet reason about reachability. - blocks: BlockRegistry, + pub blocks: BlockRegistry, } // Even though all children traits are implemented, Rust needs to know that the diff --git a/fvm/src/kernel/mod.rs b/fvm/src/kernel/mod.rs index 02a5f38ef6..13ccffc719 100644 --- a/fvm/src/kernel/mod.rs +++ b/fvm/src/kernel/mod.rs @@ -32,11 +32,13 @@ pub use error::{ClassifyResult, Context, ExecutionError, Result, SyscallError}; use fvm_shared::event::StampedEvent; pub use hash::SupportedHashes; use multihash::MultihashGeneric; +use wasmtime::Linker; use crate::call_manager::CallManager; use crate::gas::{Gas, GasTimer, PriceList}; use crate::machine::limiter::MemoryLimiter; use crate::machine::Machine; +use crate::syscalls::InvocationData; pub struct CallResult { pub block_id: BlockId, @@ -44,6 +46,11 @@ pub struct CallResult { pub exit_code: ExitCode, } +pub trait FilecoinKernel { + /// Verifies a window proof of spacetime. + fn verify_post(&self, verify_info: &WindowPoStVerifyInfo) -> Result; +} + /// The "kernel" implements the FVM interface as presented to the actors. It: /// /// - Manages the Actor's state. @@ -52,7 +59,8 @@ pub struct CallResult { /// Actors may call into the kernel via the syscalls defined in the [`syscalls`][crate::syscalls] /// module. pub trait Kernel: - ActorOps + SyscallHandler + + ActorOps + IpldBlockOps + CircSupplyOps + CryptoOps @@ -115,6 +123,13 @@ pub trait Kernel: ) -> Result; } +pub trait SyscallHandler { + fn bind_syscalls( + &self, + linker: &mut Linker>, + ) -> anyhow::Result<()>; +} + /// Network-related operations. pub trait NetworkOps { /// Network information (epoch, version, etc.). diff --git a/fvm/src/syscalls/mod.rs b/fvm/src/syscalls/mod.rs index 81490ccdf7..6aff7e7cd0 100644 --- a/fvm/src/syscalls/mod.rs +++ b/fvm/src/syscalls/mod.rs @@ -4,11 +4,12 @@ use anyhow::{anyhow, Context as _}; use num_traits::Zero; use wasmtime::{AsContextMut, ExternType, Global, Linker, Memory, Module, Val}; -use crate::call_manager::backtrace; +use crate::call_manager::{backtrace, CallManager}; use crate::gas::{Gas, GasInstant, GasTimer}; -use crate::kernel::ExecutionError; +use crate::kernel::default::DefaultFilecoinKernel; +use crate::kernel::{ExecutionError, SyscallHandler}; use crate::machine::limiter::MemoryLimiter; -use crate::Kernel; +use crate::{DefaultKernel, Kernel}; pub(crate) mod error; @@ -233,107 +234,129 @@ fn min_table_elements(module: &Module) -> Option { use self::bind::BindSyscall; use self::error::Abort; -// Binds the syscall handlers so they can handle invocations -// from the actor code. -pub fn bind_syscalls( - linker: &mut Linker>, -) -> anyhow::Result<()> { - linker.bind("vm", "exit", vm::exit)?; - linker.bind("vm", "message_context", vm::message_context)?; - - linker.bind( - "network", - "total_fil_circ_supply", - network::total_fil_circ_supply, - )?; - linker.bind("network", "context", network::context)?; - linker.bind("network", "tipset_cid", network::tipset_cid)?; - - linker.bind("ipld", "block_open", ipld::block_open)?; - linker.bind("ipld", "block_create", ipld::block_create)?; - linker.bind("ipld", "block_read", ipld::block_read)?; - linker.bind("ipld", "block_stat", ipld::block_stat)?; - linker.bind("ipld", "block_link", ipld::block_link)?; - - linker.bind("self", "root", sself::root)?; - linker.bind("self", "set_root", sself::set_root)?; - linker.bind("self", "current_balance", sself::current_balance)?; - linker.bind("self", "self_destruct", sself::self_destruct)?; - - linker.bind("actor", "resolve_address", actor::resolve_address)?; - linker.bind( - "actor", - "lookup_delegated_address", - actor::lookup_delegated_address, - )?; - linker.bind("actor", "get_actor_code_cid", actor::get_actor_code_cid)?; - linker.bind("actor", "next_actor_address", actor::next_actor_address)?; - linker.bind("actor", "create_actor", actor::create_actor)?; - if cfg!(feature = "upgrade-actor") { - // We disable/enable with the feature, but we always compile this code to ensure we don't - // accidentally break it. - linker.bind("actor", "upgrade_actor", actor::upgrade_actor)?; +impl SyscallHandler for DefaultKernel +where + C: CallManager, +{ + fn bind_syscalls( + &self, + linker: &mut Linker>, + ) -> anyhow::Result<()> { + linker.bind("vm", "exit", vm::exit)?; + linker.bind("vm", "message_context", vm::message_context)?; + + linker.bind( + "network", + "total_fil_circ_supply", + network::total_fil_circ_supply, + )?; + linker.bind("network", "context", network::context)?; + linker.bind("network", "tipset_cid", network::tipset_cid)?; + + linker.bind("ipld", "block_open", ipld::block_open)?; + linker.bind("ipld", "block_create", ipld::block_create)?; + linker.bind("ipld", "block_read", ipld::block_read)?; + linker.bind("ipld", "block_stat", ipld::block_stat)?; + linker.bind("ipld", "block_link", ipld::block_link)?; + + linker.bind("self", "root", sself::root)?; + linker.bind("self", "set_root", sself::set_root)?; + linker.bind("self", "current_balance", sself::current_balance)?; + linker.bind("self", "self_destruct", sself::self_destruct)?; + + linker.bind("actor", "resolve_address", actor::resolve_address)?; + linker.bind( + "actor", + "lookup_delegated_address", + actor::lookup_delegated_address, + )?; + linker.bind("actor", "get_actor_code_cid", actor::get_actor_code_cid)?; + linker.bind("actor", "next_actor_address", actor::next_actor_address)?; + linker.bind("actor", "create_actor", actor::create_actor)?; + if cfg!(feature = "upgrade-actor") { + // We disable/enable with the feature, but we always compile this code to ensure we don't + // accidentally break it. + linker.bind("actor", "upgrade_actor", actor::upgrade_actor)?; + } + linker.bind( + "actor", + "get_builtin_actor_type", + actor::get_builtin_actor_type, + )?; + linker.bind( + "actor", + "get_code_cid_for_type", + actor::get_code_cid_for_type, + )?; + linker.bind("actor", "balance_of", actor::balance_of)?; + + // Only wire this syscall when M2 native is enabled. + #[cfg(feature = "m2-native")] + linker.bind("actor", "install_actor", actor::install_actor)?; + + linker.bind("crypto", "verify_signature", crypto::verify_signature)?; + linker.bind( + "crypto", + "recover_secp_public_key", + crypto::recover_secp_public_key, + )?; + linker.bind("crypto", "hash", crypto::hash)?; + linker.bind("crypto", "verify_post", crypto::verify_post)?; + linker.bind( + "crypto", + "compute_unsealed_sector_cid", + crypto::compute_unsealed_sector_cid, + )?; + linker.bind( + "crypto", + "verify_consensus_fault", + crypto::verify_consensus_fault, + )?; + linker.bind( + "crypto", + "verify_aggregate_seals", + crypto::verify_aggregate_seals, + )?; + linker.bind( + "crypto", + "verify_replica_update", + crypto::verify_replica_update, + )?; + linker.bind("crypto", "batch_verify_seals", crypto::batch_verify_seals)?; + + linker.bind("event", "emit_event", event::emit_event)?; + + linker.bind("rand", "get_chain_randomness", rand::get_chain_randomness)?; + linker.bind("rand", "get_beacon_randomness", rand::get_beacon_randomness)?; + + linker.bind("gas", "charge", gas::charge_gas)?; + linker.bind("gas", "available", gas::available)?; + + // Ok, this singled-out syscall should probably be in another category. + linker.bind("send", "send", send::send)?; + + linker.bind("debug", "log", debug::log)?; + linker.bind("debug", "enabled", debug::enabled)?; + linker.bind("debug", "store_artifact", debug::store_artifact)?; + + Ok(()) } - linker.bind( - "actor", - "get_builtin_actor_type", - actor::get_builtin_actor_type, - )?; - linker.bind( - "actor", - "get_code_cid_for_type", - actor::get_code_cid_for_type, - )?; - linker.bind("actor", "balance_of", actor::balance_of)?; - - // Only wire this syscall when M2 native is enabled. - #[cfg(feature = "m2-native")] - linker.bind("actor", "install_actor", actor::install_actor)?; - - linker.bind("crypto", "verify_signature", crypto::verify_signature)?; - linker.bind( - "crypto", - "recover_secp_public_key", - crypto::recover_secp_public_key, - )?; - linker.bind("crypto", "hash", crypto::hash)?; - linker.bind("crypto", "verify_post", crypto::verify_post)?; - linker.bind( - "crypto", - "compute_unsealed_sector_cid", - crypto::compute_unsealed_sector_cid, - )?; - linker.bind( - "crypto", - "verify_consensus_fault", - crypto::verify_consensus_fault, - )?; - linker.bind( - "crypto", - "verify_aggregate_seals", - crypto::verify_aggregate_seals, - )?; - linker.bind( - "crypto", - "verify_replica_update", - crypto::verify_replica_update, - )?; - linker.bind("crypto", "batch_verify_seals", crypto::batch_verify_seals)?; - - linker.bind("event", "emit_event", event::emit_event)?; - - linker.bind("rand", "get_chain_randomness", rand::get_chain_randomness)?; - linker.bind("rand", "get_beacon_randomness", rand::get_beacon_randomness)?; - - linker.bind("gas", "charge", gas::charge_gas)?; - linker.bind("gas", "available", gas::available)?; - - // Ok, this singled-out syscall should probably be in another category. - linker.bind("send", "send", send::send)?; - - linker.bind("debug", "log", debug::log)?; - linker.bind("debug", "enabled", debug::enabled)?; - linker.bind("debug", "store_artifact", debug::store_artifact)?; +} - Ok(()) +impl SyscallHandler for DefaultFilecoinKernel +where + K: Kernel, +{ + fn bind_syscalls( + &self, + linker: &mut Linker>, + ) -> anyhow::Result<()> { + // Bind the default syscalls. + self.0.bind_syscalls(linker)?; + + // Now bind the crypto syscalls. + linker.bind("crypto", "verify_post", crypto::verify_post)?; + + Ok(()) + } } diff --git a/testing/conformance/src/vm.rs b/testing/conformance/src/vm.rs index 26ea2beb7c..4eb8b73f00 100644 --- a/testing/conformance/src/vm.rs +++ b/testing/conformance/src/vm.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex}; use anyhow::anyhow; use cid::Cid; +use fvm::syscalls::InvocationData; use multihash::MultihashGeneric; use fvm::call_manager::{CallManager, DefaultCallManager}; @@ -31,6 +32,7 @@ use fvm_shared::sector::{ use fvm_shared::sys::{EventEntry, SendFlags}; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum, TOTAL_FILECOIN}; +use wasmtime::Linker; use crate::externs::TestExterns; use crate::vector::{MessageVector, Variant}; @@ -251,6 +253,20 @@ where } } +impl SyscallHandler for TestKernel +where + M: Machine, + C: CallManager>, + K: Kernel, +{ + fn bind_syscalls( + &self, + _linker: &mut Linker>, + ) -> anyhow::Result<()> { + Ok(()) + } +} + impl ActorOps for TestKernel where M: Machine,