From 22242b1e1280aeb0880124dfa2e03338035c2b77 Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Fri, 8 Sep 2023 14:56:48 +0000 Subject: [PATCH] Support entrypoints with different number of params --- fvm/src/call_manager/default.rs | 120 ++++++++++++------ fvm/src/call_manager/mod.rs | 5 +- fvm/src/syscalls/error.rs | 3 + shared/src/lib.rs | 2 + shared/src/upgrade/mod.rs | 2 +- .../actors/fil-upgrade-actor/src/actor.rs | 16 ++- 6 files changed, 103 insertions(+), 45 deletions(-) diff --git a/fvm/src/call_manager/default.rs b/fvm/src/call_manager/default.rs index 9034473cde..447cc12bd6 100644 --- a/fvm/src/call_manager/default.rs +++ b/fvm/src/call_manager/default.rs @@ -12,6 +12,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::error::{ErrorNumber, ExitCode}; use fvm_shared::event::StampedEvent; use fvm_shared::sys::BlockId; +use fvm_shared::upgrade::UpgradeInfo; use fvm_shared::{ActorID, MethodNum, METHOD_SEND}; use num_traits::Zero; @@ -417,8 +418,8 @@ where .get_actor(actor_id)? .ok_or_else(|| syscall_error!(NotFound; "actor not found: {}", actor_id))?; - // store the code cid of the calling actor before running the upgrade endpoint - // in case it was changed (which could happen if the target upgrade endpoint + // store the code cid of the calling actor before running the upgrade entrypoint + // in case it was changed (which could happen if the target upgrade entrypoint // sent a message to this actor which in turn called upgrade) let code = state.code; @@ -434,11 +435,11 @@ where ), ); - // run the upgrade endpoint + // run the upgrade entrypoint let result = self.send::( actor_id, Address::new_id(actor_id), - Entrypoint::Upgrade, + Entrypoint::Upgrade(UpgradeInfo { old_code_cid: code }), params, &TokenAmount::zero(), None, @@ -735,6 +736,11 @@ where NO_DATA_BLOCK_ID }; + // additional_params takes care of adding entrypoint specific params to the block + // registry and passing them to wasmtime + let mut additional_params = EntrypointParams::new(entrypoint); + additional_params.maybe_put_registry(&mut block_registry)?; + // Increment invocation count self.invocation_count += 1; @@ -782,20 +788,21 @@ where store.data_mut().memory = memory; - // Lookup the invoke method. - let invoke: wasmtime::TypedFunc<(u32,), u32> = instance - .get_typed_func(&mut store, entrypoint.func_name()) - // All actors will have an invoke method. - .map_err(Abort::Fatal)?; + let func = match instance.get_func(&mut store, entrypoint.func_name()) { + Some(func) => func, + None => { + return Err(Abort::EntrypointNotFound); + } + }; + + let mut params = vec![wasmtime::Val::I32(params_id as i32)]; + params.extend_from_slice(additional_params.params().as_slice()); // Set the available gas. update_gas_available(&mut store)?; - // Invoke it. - let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - invoke.call(&mut store, (params_id,)) - })) - .map_err(|panic| Abort::Fatal(anyhow!("panic within actor: {:?}", panic)))?; + let mut out = [wasmtime::Val::I32(0)]; + func.call(&mut store, params.as_slice(), &mut out)?; // Charge for any remaining uncharged execution gas, returning an error if we run // out. @@ -805,35 +812,26 @@ where // detected it and returned OutOfGas above. Any other invocation failure is returned // here as an Abort - Ok(res?) + Ok(out[0].unwrap_i32() as u32) })(); let invocation_data = store.into_data(); let last_error = invocation_data.last_error; let (mut cm, block_registry) = invocation_data.kernel.into_inner(); - // Resolve the return block's ID into an actual block, converting to an abort if it - // doesn't exist. - let result = result.and_then(|ret_id| { - Ok(if ret_id == NO_DATA_BLOCK_ID { - None - } else { - Some(block_registry.get(ret_id).map_err(|_| { - Abort::Exit( - ExitCode::SYS_MISSING_RETURN, - String::from("returned block does not exist"), - NO_DATA_BLOCK_ID, - ) - })?) - }) - }); - // Process the result, updating the backtrace if necessary. let mut ret = match result { - Ok(ret) => Ok(InvocationResult { + Ok(NO_DATA_BLOCK_ID) => Ok(InvocationResult { exit_code: ExitCode::OK, - value: ret.cloned(), + value: None, }), + Ok(block_id) => match block_registry.get(block_id) { + Ok(blk) => Ok(InvocationResult { + exit_code: ExitCode::OK, + value: Some(blk.clone()), + }), + Err(e) => Err(ExecutionError::Fatal(anyhow!(e))), + }, Err(abort) => { let (code, message, res) = match abort { Abort::Exit(code, message, NO_DATA_BLOCK_ID) => ( @@ -845,11 +843,6 @@ where }), ), Abort::Exit(code, message, blk_id) => match block_registry.get(blk_id) { - Err(e) => ( - ExitCode::SYS_MISSING_RETURN, - "error getting exit data block".to_owned(), - Err(ExecutionError::Fatal(anyhow!(e))), - ), Ok(blk) => ( code, message, @@ -858,7 +851,20 @@ where value: Some(blk.clone()), }), ), + Err(e) => ( + ExitCode::SYS_MISSING_RETURN, + "error getting exit data block".to_owned(), + Err(ExecutionError::Fatal(anyhow!(e))), + ), }, + Abort::EntrypointNotFound => ( + ExitCode::USR_FORBIDDEN, + "entrypoint not found".to_owned(), + Err(ExecutionError::Syscall(SyscallError::new( + ErrorNumber::Forbidden, + "entrypoint not found", + ))), + ), Abort::OutOfGas => ( ExitCode::SYS_OUT_OF_GAS, "out of gas".to_owned(), @@ -1033,14 +1039,48 @@ impl Entrypoint { fn method_num(&self) -> MethodNum { match self { Entrypoint::Invoke(num) => *num, - Entrypoint::Upgrade => 191919, + Entrypoint::Upgrade(_) => fvm_shared::METHOD_UPGRADE, } } fn func_name(&self) -> &'static str { match self { Entrypoint::Invoke(_) => "invoke", - Entrypoint::Upgrade => "upgrade", + Entrypoint::Upgrade(_) => "upgrade", + } + } +} + +// EntrypointParams is a helper struct to init the registry with the entrypoint specific +// parameters and then forward them to wasmtime +struct EntrypointParams { + entrypoint: Entrypoint, + params: Vec, +} + +impl EntrypointParams { + fn new(entrypoint: Entrypoint) -> Self { + Self { + entrypoint, + params: Vec::new(), } } + + fn maybe_put_registry(&mut self, br: &mut BlockRegistry) -> Result<()> { + match self.entrypoint { + Entrypoint::Invoke(_) => Ok(()), + Entrypoint::Upgrade(ui) => { + let ui_params = to_vec(&ui).map_err( + |e| syscall_error!(IllegalArgument; "failed to serialize upgrade params: {}", e), + )?; + let block_id = br.put(Block::new(CBOR, ui_params))?; + self.params.push(wasmtime::Val::I32(block_id as i32)); + Ok(()) + } + } + } + + fn params(&self) -> &Vec { + &self.params + } } diff --git a/fvm/src/call_manager/mod.rs b/fvm/src/call_manager/mod.rs index 4706b8ee6b..74a367a23d 100644 --- a/fvm/src/call_manager/mod.rs +++ b/fvm/src/call_manager/mod.rs @@ -4,6 +4,7 @@ use cid::Cid; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::upgrade::UpgradeInfo; use fvm_shared::{ActorID, MethodNum}; use crate::engine::Engine; @@ -210,14 +211,14 @@ pub struct FinishRet { #[derive(Clone, Debug, Copy)] pub enum Entrypoint { Invoke(MethodNum), - Upgrade, + Upgrade(UpgradeInfo), } impl std::fmt::Display for Entrypoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Entrypoint::Invoke(method) => write!(f, "invoke({})", method), - Entrypoint::Upgrade => write!(f, "upgrade"), + Entrypoint::Upgrade(_) => write!(f, "upgrade"), } } } diff --git a/fvm/src/syscalls/error.rs b/fvm/src/syscalls/error.rs index ee545272d7..f786ccdede 100644 --- a/fvm/src/syscalls/error.rs +++ b/fvm/src/syscalls/error.rs @@ -17,6 +17,9 @@ pub enum Abort { /// The actor ran out of gas. #[error("out of gas")] OutOfGas, + /// The actor did not export the endpoint that was called. + #[error("entrypoint not found")] + EntrypointNotFound, /// The system failed with a fatal error. #[error("fatal error: {0}")] Fatal(anyhow::Error), diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 1b31f717a6..40ea2eb56d 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -109,6 +109,8 @@ pub type MethodNum = u64; pub const METHOD_SEND: MethodNum = 0; /// Base actor constructor method. pub const METHOD_CONSTRUCTOR: MethodNum = 1; +/// Upgrade actor method. +pub const METHOD_UPGRADE: MethodNum = 932083; /// The outcome of a `Send`, covering its ExitCode and optional return data #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/shared/src/upgrade/mod.rs b/shared/src/upgrade/mod.rs index 48da6a7605..557f45abd8 100644 --- a/shared/src/upgrade/mod.rs +++ b/shared/src/upgrade/mod.rs @@ -3,7 +3,7 @@ use cid::Cid; use fvm_ipld_encoding::tuple::*; -#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)] pub struct UpgradeInfo { // the old code cid we are upgrading from pub old_code_cid: Cid, diff --git a/testing/test_actors/actors/fil-upgrade-actor/src/actor.rs b/testing/test_actors/actors/fil-upgrade-actor/src/actor.rs index 48fff8c89f..e9dc464b1b 100644 --- a/testing/test_actors/actors/fil-upgrade-actor/src/actor.rs +++ b/testing/test_actors/actors/fil-upgrade-actor/src/actor.rs @@ -4,6 +4,7 @@ use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{to_vec, CBOR}; use fvm_sdk as sdk; use fvm_shared::address::Address; +use fvm_shared::upgrade::UpgradeInfo; use serde_tuple::*; #[derive(Serialize_tuple, Deserialize_tuple, PartialEq, Eq, Clone, Debug)] struct SomeStruct { @@ -13,13 +14,24 @@ struct SomeStruct { const UPGRADE_FAILED_EXIT_CODE: u32 = 19; #[no_mangle] -pub fn upgrade(params_id: u32) -> u32 { +pub fn upgrade(params_id: u32, upgrade_info_id: u32) -> u32 { sdk::initialize(); let params = sdk::message::params_raw(params_id).unwrap().unwrap(); + let ui_params = sdk::message::params_raw(upgrade_info_id).unwrap().unwrap(); + assert_eq!(params.codec, fvm_ipld_encoding::CBOR); + assert_eq!(ui_params.codec, fvm_ipld_encoding::CBOR); + + let p = params.deserialize::().unwrap(); + let ui = ui_params.deserialize::().unwrap(); + + sdk::debug::log(format!( + "[upgrade] value: {}, old_code_cid: {}", + p.value, ui.old_code_cid + )); - match params.deserialize::().unwrap().value { + match p.value { 1 => { let block_id = sdk::ipld::put_block(CBOR, &to_vec(&666).unwrap()).unwrap(); sdk::debug::log(format!(