Skip to content

Commit

Permalink
Support entrypoints with different number of params
Browse files Browse the repository at this point in the history
  • Loading branch information
fridrik01 committed Sep 8, 2023
1 parent ffba463 commit 22242b1
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 45 deletions.
120 changes: 80 additions & 40 deletions fvm/src/call_manager/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand All @@ -434,11 +435,11 @@ where
),
);

// run the upgrade endpoint
// run the upgrade entrypoint
let result = self.send::<K>(
actor_id,
Address::new_id(actor_id),
Entrypoint::Upgrade,
Entrypoint::Upgrade(UpgradeInfo { old_code_cid: code }),
params,
&TokenAmount::zero(),
None,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -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) => (
Expand All @@ -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,
Expand All @@ -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(),
Expand Down Expand Up @@ -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<wasmtime::Val>,
}

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<wasmtime::Val> {
&self.params
}
}
5 changes: 3 additions & 2 deletions fvm/src/call_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"),
}
}
}
3 changes: 3 additions & 0 deletions fvm/src/syscalls/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion shared/src/upgrade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 14 additions & 2 deletions testing/test_actors/actors/fil-upgrade-actor/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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::<SomeStruct>().unwrap();
let ui = ui_params.deserialize::<UpgradeInfo>().unwrap();

sdk::debug::log(format!(
"[upgrade] value: {}, old_code_cid: {}",
p.value, ui.old_code_cid
));

match params.deserialize::<SomeStruct>().unwrap().value {
match p.value {
1 => {
let block_id = sdk::ipld::put_block(CBOR, &to_vec(&666).unwrap()).unwrap();
sdk::debug::log(format!(
Expand Down

0 comments on commit 22242b1

Please sign in to comment.