use super::{FrameLocal, Resumable};
use crate::{
builtin::BuiltinExec, context::Context, executive_observer::TracerTrait,
internal_contract::InternalContractExec,
};
use cfx_statedb::Result as DbResult;
use cfx_types::{AddressSpaceUtil, U256};
use cfx_vm_interpreter::{FinalizationResult, Finalize};
use cfx_vm_types::{
self as vm, separate_out_db_error, ActionParams, Exec, GasLeft, ReturnData,
TrapError, TrapResult,
};
pub trait Executable: Send {
fn execute(
self: Box<Self>, context: Context,
) -> DbResult<ExecutableOutcome>;
}
pub enum ExecutableOutcome {
Return(vm::Result<FinalizationResult>),
Invoke(ActionParams, Box<dyn Resumable>),
}
use ExecutableOutcome::*;
pub fn make_executable<'a>(
frame_local: &FrameLocal<'a>, params: ActionParams,
tracer: &mut dyn TracerTrait,
) -> Box<dyn 'a + Executable> {
let is_create = frame_local.create_address.is_some();
let code_address = params.code_address.with_space(params.space);
let internal_contract_map = frame_local.machine.internal_contracts();
if let Some(builtin) = frame_local
.machine
.builtin(&code_address, frame_local.env.number)
{
trace!("CallBuiltin");
return Box::new(BuiltinExec { builtin, params });
}
if let Some(internal) =
internal_contract_map.contract(&code_address, &frame_local.spec)
{
trace!(
"CallInternalContract: address={:?} data={:?}",
code_address,
params.data
);
return Box::new(InternalContractExec { internal, params });
}
if is_create || params.code.is_some() {
trace!("CallCreate");
tracer.initialize_interp(params.gas.clone());
let factory = frame_local.machine.vm_factory_ref();
Box::new(factory.create(params, frame_local.spec, frame_local.depth))
} else {
trace!("Transfer");
Box::new(NoopExec { gas: params.gas })
}
}
impl Executable for Box<dyn Exec> {
fn execute(
self: Box<Self>, mut context: Context,
) -> DbResult<ExecutableOutcome> {
Ok(match self.exec(&mut context) {
TrapResult::Return(result) => {
let result = separate_out_db_error(result)?;
if matches!(result, Ok(GasLeft::Known(_))) {
context.insert_create_address_to_substate();
}
Return(result.finalize(context))
}
TrapResult::SubCallCreate(TrapError::Call(params, resume)) => {
Invoke(params, Box::new(resume))
}
TrapResult::SubCallCreate(TrapError::Create(params, resume)) => {
Invoke(params, Box::new(resume))
}
})
}
}
pub struct NoopExec {
pub gas: U256,
}
impl Executable for NoopExec {
fn execute(self: Box<Self>, _: Context) -> DbResult<ExecutableOutcome> {
let result = FinalizationResult {
gas_left: self.gas,
apply_state: true,
return_data: ReturnData::empty(),
};
Ok(Return(Ok(result)))
}
}