use cid::Cid;
use fvm_ipld_encoding::{CBOR, to_vec};
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use fvm_shared::upgrade::UpgradeInfo;
use fvm_shared::{ActorID, METHOD_CONSTRUCTOR, MethodNum};
use crate::Kernel;
use crate::engine::Engine;
use crate::gas::{Gas, GasCharge, GasTimer, GasTracker, PriceList};
use crate::kernel::{self, BlockRegistry, ClassifyResult, Context, Result};
use crate::machine::{Machine, MachineContext};
use crate::state_tree::ActorState;
pub mod backtrace;
mod state_access_tracker;
pub use backtrace::Backtrace;
mod default;
pub use default::DefaultCallManager;
use fvm_shared::event::StampedEvent;
use crate::trace::{ExecutionTrace, IpldOperation};
pub const NO_DATA_BLOCK_ID: u32 = 0;
pub trait CallManager: 'static {
type Machine: Machine;
#[allow(clippy::too_many_arguments)]
fn new(
machine: Self::Machine,
engine: Engine,
gas_limit: u64,
origin: ActorID,
origin_address: Address,
receiver: Option<ActorID>,
receiver_address: Address,
nonce: u64,
gas_premium: TokenAmount,
) -> Self;
#[allow(clippy::too_many_arguments)]
fn call_actor<K: Kernel<CallManager = Self>>(
&mut self,
from: ActorID,
to: Address,
entrypoint: Entrypoint,
params: Option<kernel::Block>,
value: &TokenAmount,
gas_limit: Option<Gas>,
read_only: bool,
) -> Result<InvocationResult>;
fn with_transaction(
&mut self,
f: impl FnOnce(&mut Self) -> Result<InvocationResult>,
) -> Result<InvocationResult>;
fn finish(self) -> (Result<FinishRet>, Self::Machine);
fn machine(&self) -> &Self::Machine;
fn machine_mut(&mut self) -> &mut Self::Machine;
fn engine(&self) -> &Engine;
fn gas_tracker(&self) -> &GasTracker;
fn gas_premium(&self) -> &TokenAmount;
fn origin(&self) -> ActorID;
fn next_actor_address(&self) -> Address;
fn create_actor(
&mut self,
code_id: Cid,
actor_id: ActorID,
delegated_address: Option<Address>,
) -> Result<()>;
fn get_call_stack(&self) -> &[(ActorID, &'static str)];
fn resolve_address(&self, address: &Address) -> Result<Option<ActorID>>;
fn set_actor(&mut self, id: ActorID, state: ActorState) -> Result<()>;
fn get_actor(&self, id: ActorID) -> Result<Option<ActorState>>;
fn delete_actor(&mut self, id: ActorID) -> Result<()>;
fn transfer(&mut self, from: ActorID, to: ActorID, value: &TokenAmount) -> Result<()>;
fn nonce(&self) -> u64;
fn invocation_count(&self) -> u64;
fn price_list(&self) -> &PriceList {
self.machine().context().price_list
}
fn context(&self) -> &MachineContext {
self.machine().context()
}
fn blockstore(&self) -> &<Self::Machine as Machine>::Blockstore {
self.machine().blockstore()
}
fn externs(&self) -> &<Self::Machine as Machine>::Externs {
self.machine().externs()
}
fn charge_gas(&self, charge: GasCharge) -> Result<GasTimer> {
self.gas_tracker().apply_charge(charge)
}
fn limiter_mut(&mut self) -> &mut <Self::Machine as Machine>::Limiter;
fn append_event(&mut self, evt: StampedEvent);
fn log(&mut self, msg: String);
fn trace_ipld(&mut self, op: IpldOperation, cid: Cid, size: usize);
}
#[derive(Clone, Debug)]
pub struct InvocationResult {
pub exit_code: ExitCode,
pub value: Option<kernel::Block>,
}
impl Default for InvocationResult {
fn default() -> Self {
Self {
value: None,
exit_code: ExitCode::OK,
}
}
}
pub struct FinishRet {
pub gas_used: u64,
pub backtrace: Backtrace,
pub exec_trace: ExecutionTrace,
pub events: Vec<StampedEvent>,
pub events_root: Option<Cid>,
}
#[derive(Clone, Debug, Copy)]
pub enum Entrypoint {
ImplicitConstructor,
Invoke(MethodNum),
Upgrade(UpgradeInfo),
}
pub static INVOKE_FUNC_NAME: &str = "invoke";
pub static UPGRADE_FUNC_NAME: &str = "upgrade";
const METHOD_UPGRADE: MethodNum = 932083;
impl std::fmt::Display for Entrypoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Entrypoint::ImplicitConstructor => write!(f, "implicit_constructor"),
Entrypoint::Invoke(method) => write!(f, "invoke({})", method),
Entrypoint::Upgrade(_) => write!(f, "upgrade"),
}
}
}
impl Entrypoint {
fn method_num(&self) -> MethodNum {
match self {
Entrypoint::ImplicitConstructor => METHOD_CONSTRUCTOR,
Entrypoint::Invoke(num) => *num,
Entrypoint::Upgrade(_) => METHOD_UPGRADE,
}
}
fn func_name(&self) -> &'static str {
match self {
Entrypoint::ImplicitConstructor | Entrypoint::Invoke(_) => INVOKE_FUNC_NAME,
Entrypoint::Upgrade(_) => UPGRADE_FUNC_NAME,
}
}
fn invokes(&self, method: MethodNum) -> bool {
match self {
Entrypoint::ImplicitConstructor => method == METHOD_CONSTRUCTOR,
Entrypoint::Invoke(num) => *num == method,
Entrypoint::Upgrade(_) => false,
}
}
fn into_params(self, br: &mut BlockRegistry) -> Result<Vec<wasmtime::Val>> {
match self {
Entrypoint::ImplicitConstructor | Entrypoint::Invoke(_) => Ok(Vec::new()),
Entrypoint::Upgrade(ui) => {
let ui_params = to_vec(&ui)
.or_fatal()
.context("failed to serialize upgrade params")?;
let block_id = br.put_reachable(kernel::Block::new(CBOR, ui_params, Vec::new()))?;
Ok(vec![wasmtime::Val::I32(block_id as i32)])
}
}
}
}