Skip to main content

fvm/call_manager/
mod.rs

1// Copyright 2021-2023 Protocol Labs
2// SPDX-License-Identifier: Apache-2.0, MIT
3use cid::Cid;
4use fvm_ipld_encoding::{CBOR, to_vec};
5use fvm_shared::address::Address;
6use fvm_shared::econ::TokenAmount;
7use fvm_shared::error::ExitCode;
8use fvm_shared::upgrade::UpgradeInfo;
9use fvm_shared::{ActorID, METHOD_CONSTRUCTOR, MethodNum};
10
11use crate::Kernel;
12use crate::engine::Engine;
13use crate::gas::{Gas, GasCharge, GasTimer, GasTracker, PriceList};
14use crate::kernel::{self, BlockRegistry, ClassifyResult, Context, Result};
15use crate::machine::{Machine, MachineContext};
16use crate::state_tree::ActorState;
17
18pub mod backtrace;
19mod state_access_tracker;
20pub use backtrace::Backtrace;
21
22mod default;
23
24pub use default::DefaultCallManager;
25use fvm_shared::event::StampedEvent;
26
27use crate::trace::{ExecutionTrace, IpldOperation};
28
29/// BlockID representing nil parameters or return data.
30pub const NO_DATA_BLOCK_ID: u32 = 0;
31
32/// The `CallManager` manages a single call stack.
33///
34/// When a top-level message is executed:
35///
36/// 1. The [`crate::executor::Executor`] creates a [`CallManager`] for that message, giving itself
37///    to the [`CallManager`].
38/// 2. The [`crate::executor::Executor`] calls the specified actor/entrypoint using
39///    [`CallManager::call_actor()`].
40/// 3. The [`CallManager`] then constructs a [`Kernel`] and executes the actual actor code on that
41///    kernel.
42/// 4. If an actor calls another actor, the [`Kernel`] will:
43///    1. Detach the [`CallManager`] from itself.
44///    2. Call [`CallManager::call_actor()`] to execute the new message.
45///    3. Re-attach the [`CallManager`].
46///    4. Return.
47pub trait CallManager: 'static {
48    /// The underlying [`Machine`] on top of which this [`CallManager`] executes.
49    type Machine: Machine;
50
51    /// Construct a new call manager.
52    #[allow(clippy::too_many_arguments)]
53    fn new(
54        machine: Self::Machine,
55        engine: Engine,
56        gas_limit: u64,
57        origin: ActorID,
58        origin_address: Address,
59        receiver: Option<ActorID>,
60        receiver_address: Address,
61        nonce: u64,
62        gas_premium: TokenAmount,
63    ) -> Self;
64
65    /// Calls an actor at the given address and entrypoint. The type parameter `K` specifies the the _kernel_ on top of which the target
66    /// actor should execute.
67    #[allow(clippy::too_many_arguments)]
68    fn call_actor<K: Kernel<CallManager = Self>>(
69        &mut self,
70        from: ActorID,
71        to: Address,
72        entrypoint: Entrypoint,
73        params: Option<kernel::Block>,
74        value: &TokenAmount,
75        gas_limit: Option<Gas>,
76        read_only: bool,
77    ) -> Result<InvocationResult>;
78
79    /// Execute some operation (usually a call_actor) within a transaction.
80    fn with_transaction(
81        &mut self,
82        f: impl FnOnce(&mut Self) -> Result<InvocationResult>,
83    ) -> Result<InvocationResult>;
84
85    /// Finishes execution, returning the gas used, machine, and exec trace if requested.
86    fn finish(self) -> (Result<FinishRet>, Self::Machine);
87
88    /// Returns a reference to the machine.
89    fn machine(&self) -> &Self::Machine;
90
91    /// Returns a mutable reference to the machine.
92    fn machine_mut(&mut self) -> &mut Self::Machine;
93
94    /// Returns a reference to the engine
95    fn engine(&self) -> &Engine;
96
97    /// Returns a reference to the gas tracker.
98    fn gas_tracker(&self) -> &GasTracker;
99
100    /// Returns the gas premium paid by the currently executing message.
101    fn gas_premium(&self) -> &TokenAmount;
102
103    /// Getter for origin actor.
104    fn origin(&self) -> ActorID;
105
106    /// Get the actor address (f2) that will should be assigned to the next actor created.
107    ///
108    /// This method doesn't have any side-effects and will continue to return the same address until
109    /// `create_actor` is called next.
110    fn next_actor_address(&self) -> Address;
111
112    /// Create a new actor with the given code CID, actor ID, and delegated address. This method
113    /// does not register the actor with the init actor. It just creates it in the state-tree.
114    ///
115    /// It handles all appropriate gas charging for creating new actors.
116    fn create_actor(
117        &mut self,
118        code_id: Cid,
119        actor_id: ActorID,
120        delegated_address: Option<Address>,
121    ) -> Result<()>;
122
123    // returns the actor call stack
124    fn get_call_stack(&self) -> &[(ActorID, &'static str)];
125
126    /// Resolve an address into an actor ID, charging gas as appropriate.
127    fn resolve_address(&self, address: &Address) -> Result<Option<ActorID>>;
128
129    /// Sets an actor in the state-tree, charging gas as appropriate. Use `create_actor` if you want
130    /// to create a new actor.
131    fn set_actor(&mut self, id: ActorID, state: ActorState) -> Result<()>;
132
133    /// Looks up an actor in the state-tree, charging gas as appropriate.
134    fn get_actor(&self, id: ActorID) -> Result<Option<ActorState>>;
135
136    /// Deletes an actor from the state-tree, charging gas as appropriate.
137    fn delete_actor(&mut self, id: ActorID) -> Result<()>;
138
139    /// Transfers tokens from one actor to another, charging gas as appropriate.
140    fn transfer(&mut self, from: ActorID, to: ActorID, value: &TokenAmount) -> Result<()>;
141
142    /// Getter for message nonce.
143    fn nonce(&self) -> u64;
144
145    /// Gets the total invocations done on this call stack.
146    fn invocation_count(&self) -> u64;
147
148    /// Returns the current price list.
149    fn price_list(&self) -> &PriceList {
150        self.machine().context().price_list
151    }
152
153    /// Returns the machine context.
154    fn context(&self) -> &MachineContext {
155        self.machine().context()
156    }
157
158    /// Returns the blockstore.
159    fn blockstore(&self) -> &<Self::Machine as Machine>::Blockstore {
160        self.machine().blockstore()
161    }
162
163    /// Returns the externs.
164    fn externs(&self) -> &<Self::Machine as Machine>::Externs {
165        self.machine().externs()
166    }
167
168    /// Charge gas.
169    fn charge_gas(&self, charge: GasCharge) -> Result<GasTimer> {
170        self.gas_tracker().apply_charge(charge)
171    }
172
173    /// Limit memory usage throughout a message execution.
174    fn limiter_mut(&mut self) -> &mut <Self::Machine as Machine>::Limiter;
175
176    /// Appends an event to the event accumulator.
177    fn append_event(&mut self, evt: StampedEvent);
178
179    /// log
180    fn log(&mut self, msg: String);
181
182    fn trace_ipld(&mut self, op: IpldOperation, cid: Cid, size: usize);
183}
184
185/// The result of calling actor's entrypoint
186#[derive(Clone, Debug)]
187pub struct InvocationResult {
188    /// The exit code (0 for success).
189    pub exit_code: ExitCode,
190    /// The return value, if any.
191    pub value: Option<kernel::Block>,
192}
193
194impl Default for InvocationResult {
195    fn default() -> Self {
196        Self {
197            value: None,
198            exit_code: ExitCode::OK,
199        }
200    }
201}
202
203/// The returned values upon finishing a call manager.
204pub struct FinishRet {
205    pub gas_used: u64,
206    pub backtrace: Backtrace,
207    pub exec_trace: ExecutionTrace,
208    pub events: Vec<StampedEvent>,
209    pub events_root: Option<Cid>,
210}
211
212#[derive(Clone, Debug, Copy)]
213pub enum Entrypoint {
214    /// Implicitly invoke a constructor. We keep this separate for better tracing.
215    ImplicitConstructor,
216    /// Invoke a method.
217    Invoke(MethodNum),
218    /// Upgrade to a new actor code CID.
219    Upgrade(UpgradeInfo),
220}
221
222pub static INVOKE_FUNC_NAME: &str = "invoke";
223pub static UPGRADE_FUNC_NAME: &str = "upgrade";
224
225const METHOD_UPGRADE: MethodNum = 932083;
226
227impl std::fmt::Display for Entrypoint {
228    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229        match self {
230            Entrypoint::ImplicitConstructor => write!(f, "implicit_constructor"),
231            Entrypoint::Invoke(method) => write!(f, "invoke({})", method),
232            Entrypoint::Upgrade(_) => write!(f, "upgrade"),
233        }
234    }
235}
236
237impl Entrypoint {
238    fn method_num(&self) -> MethodNum {
239        match self {
240            Entrypoint::ImplicitConstructor => METHOD_CONSTRUCTOR,
241            Entrypoint::Invoke(num) => *num,
242            Entrypoint::Upgrade(_) => METHOD_UPGRADE,
243        }
244    }
245
246    fn func_name(&self) -> &'static str {
247        match self {
248            Entrypoint::ImplicitConstructor | Entrypoint::Invoke(_) => INVOKE_FUNC_NAME,
249            Entrypoint::Upgrade(_) => UPGRADE_FUNC_NAME,
250        }
251    }
252
253    fn invokes(&self, method: MethodNum) -> bool {
254        match self {
255            Entrypoint::ImplicitConstructor => method == METHOD_CONSTRUCTOR,
256            Entrypoint::Invoke(num) => *num == method,
257            Entrypoint::Upgrade(_) => false,
258        }
259    }
260
261    fn into_params(self, br: &mut BlockRegistry) -> Result<Vec<wasmtime::Val>> {
262        match self {
263            Entrypoint::ImplicitConstructor | Entrypoint::Invoke(_) => Ok(Vec::new()),
264            Entrypoint::Upgrade(ui) => {
265                let ui_params = to_vec(&ui)
266                    .or_fatal()
267                    .context("failed to serialize upgrade params")?;
268                // This is CBOR instead of DAG_CBOR because these params are not reachable
269                let block_id = br.put_reachable(kernel::Block::new(CBOR, ui_params, Vec::new()))?;
270                Ok(vec![wasmtime::Val::I32(block_id as i32)])
271            }
272        }
273    }
274}