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.machine.builtin(
57        &code_address,
58        frame_local.env.number,
59        frame_local.env.epoch_height,
60    ) {
61        trace!("CallBuiltin");
62        return Box::new(BuiltinExec { builtin, params });
63    }
64
65    if let Some(internal) =
66        internal_contract_map.contract(&code_address, &frame_local.spec)
67    {
68        trace!(
69            "CallInternalContract: address={:?} data={:?}",
70            code_address,
71            params.data
72        );
73        return Box::new(InternalContractExec { internal, params });
74    }
75
76    if is_create || params.code.is_some() {
77        trace!("CallCreate");
78
79        // call the initialize_interp hook to log gas_limit
80        tracer.initialize_interp(params.gas.clone());
81
82        let factory = frame_local.machine.vm_factory_ref();
83        Box::new(factory.create(params, frame_local.spec, frame_local.depth))
84    } else {
85        trace!("Transfer");
86        Box::new(NoopExec { gas: params.gas })
87    }
88}
89
90impl Executable for Box<dyn Exec> {
91    fn execute(
92        self: Box<Self>, mut context: Context,
93    ) -> DbResult<ExecutableOutcome> {
94        Ok(match self.exec(&mut context) {
95            TrapResult::Return(result) => {
96                let result = separate_out_db_error(result)?;
97                // Backward compatible for a strange behaviour. If the contract
98                // creation process do not generate any code and the contract is
99                // not inited, it should also be put in the contract creation
100                // receipt.
101                if matches!(result, Ok(GasLeft::Known(_))) {
102                    context.insert_create_address_to_substate();
103                }
104                Return(result.finalize(context))
105            }
106            TrapResult::SubCallCreate(TrapError::Call(params, resume)) => {
107                Invoke(params, Box::new(resume))
108            }
109            TrapResult::SubCallCreate(TrapError::Create(params, resume)) => {
110                Invoke(params, Box::new(resume))
111            }
112        })
113    }
114}
115
116pub struct NoopExec {
117    pub gas: U256,
118}
119
120impl Executable for NoopExec {
121    fn execute(self: Box<Self>, _: Context) -> DbResult<ExecutableOutcome> {
122        let result = FinalizationResult {
123            gas_left: self.gas,
124            apply_state: true,
125            return_data: ReturnData::empty(),
126        };
127        Ok(Return(Ok(result)))
128    }
129}