cfx_executor/stack/
executable.rs

1use super::{FrameLocal, Resumable};
2use crate::{
3    builtin::BuiltinExec, context::Context, executive_observer::TracerTrait,
4    internal_contract::InternalContractExec,
5};
6use cfx_statedb::Result as DbResult;
7use cfx_types::{AddressSpaceUtil, U256};
8use cfx_vm_interpreter::{FinalizationResult, Finalize};
9use cfx_vm_types::{
10    self as vm, separate_out_db_error, ActionParams, Exec, GasLeft, ReturnData,
11    TrapError, TrapResult,
12};
13
14/// `Executable` is a trait representing an object that can be executed within a
15/// frame.
16///
17/// There are generally two ways to create an `Executable`:
18/// 1. In a new frame, an `Executable` is created by the `make_executable`
19/// function, which uses the frame's input parameters    (`ActionParams`) and
20/// local information (`FrameLocal`) to determine the appropriate executable
21/// action.
22/// 2. After the completion of a frame's execution, an `Executable` may
23/// be created by the its caller frame's `Resumer` (implementing the `Resumable`
24/// trait) based on the execution results.
25pub trait Executable: Send {
26    fn execute(
27        self: Box<Self>, context: Context,
28    ) -> DbResult<ExecutableOutcome>;
29}
30
31/// The possible outcomes of an `Executable`'s execution within a frame. It
32/// encapsulates either the final result of the frame's execution or the
33/// parameters needed for invoking the next level frame.
34
35pub enum ExecutableOutcome {
36    /// The result of the frame's execution.
37    Return(vm::Result<FinalizationResult>),
38    /// The parameters for invoking the next frame and a resumable object for
39    /// the current frame.
40    Invoke(ActionParams, Box<dyn Resumable>),
41}
42use ExecutableOutcome::*;
43
44/// Constructs an executable object from a frame's local information and input
45/// parameters. Possible executables include built-in functions, internal
46/// contracts, simple transfers, or the execution of EVM bytecode.
47pub fn make_executable<'a>(
48    frame_local: &FrameLocal<'a>, params: ActionParams,
49    tracer: &mut dyn TracerTrait,
50) -> Box<dyn 'a + Executable> {
51    let is_create = frame_local.create_address.is_some();
52    let code_address = params.code_address.with_space(params.space);
53    let internal_contract_map = frame_local.machine.internal_contracts();
54
55    // Builtin is located for both Conflux Space and EVM Space.
56    if let Some(builtin) = frame_local
57        .machine
58        .builtin(&code_address, frame_local.env.number)
59    {
60        trace!("CallBuiltin");
61        return Box::new(BuiltinExec { builtin, params });
62    }
63
64    if let Some(internal) =
65        internal_contract_map.contract(&code_address, &frame_local.spec)
66    {
67        trace!(
68            "CallInternalContract: address={:?} data={:?}",
69            code_address,
70            params.data
71        );
72        return Box::new(InternalContractExec { internal, params });
73    }
74
75    if is_create || params.code.is_some() {
76        trace!("CallCreate");
77
78        // call the initialize_interp hook to log gas_limit
79        tracer.initialize_interp(params.gas.clone());
80
81        let factory = frame_local.machine.vm_factory_ref();
82        Box::new(factory.create(params, frame_local.spec, frame_local.depth))
83    } else {
84        trace!("Transfer");
85        Box::new(NoopExec { gas: params.gas })
86    }
87}
88
89impl Executable for Box<dyn Exec> {
90    fn execute(
91        self: Box<Self>, mut context: Context,
92    ) -> DbResult<ExecutableOutcome> {
93        Ok(match self.exec(&mut context) {
94            TrapResult::Return(result) => {
95                let result = separate_out_db_error(result)?;
96                // Backward compatible for a strange behaviour. If the contract
97                // creation process do not generate any code and the contract is
98                // not inited, it should also be put in the contract creation
99                // receipt.
100                if matches!(result, Ok(GasLeft::Known(_))) {
101                    context.insert_create_address_to_substate();
102                }
103                Return(result.finalize(context))
104            }
105            TrapResult::SubCallCreate(TrapError::Call(params, resume)) => {
106                Invoke(params, Box::new(resume))
107            }
108            TrapResult::SubCallCreate(TrapError::Create(params, resume)) => {
109                Invoke(params, Box::new(resume))
110            }
111        })
112    }
113}
114
115pub struct NoopExec {
116    pub gas: U256,
117}
118
119impl Executable for NoopExec {
120    fn execute(self: Box<Self>, _: Context) -> DbResult<ExecutableOutcome> {
121        let result = FinalizationResult {
122            gas_left: self.gas,
123            apply_state: true,
124            return_data: ReturnData::empty(),
125        };
126        Ok(Return(Ok(result)))
127    }
128}