use alloc::vec::Vec;
use evm_interpreter::uint::H160;
use evm_interpreter::{
ExitError, ExitException, Interpreter, Opcode,
runtime::{
RuntimeBackend, RuntimeEnvironment, RuntimeState, SetCodeOrigin, TouchKind, Transfer,
},
trap::{CallScheme, CallTrap, CreateTrap},
};
use crate::{
backend::TransactionalBackend,
invoker::{InvokerControl, InvokerExit},
standard::{Config, InvokerState, Resolver, ResolverOrigin, SubstackInvoke},
};
#[allow(clippy::too_many_arguments)]
pub fn make_enter_call_machine<H, R>(
origin: ResolverOrigin,
resolver: &R,
scheme: CallScheme,
code_address: H160,
input: Vec<u8>,
transfer: Option<Transfer>,
state: <R::Interpreter as Interpreter<H>>::State,
handler: &mut H,
) -> Result<InvokerControl<R::Interpreter, R::State>, ExitError>
where
R::State: AsRef<RuntimeState>,
H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend,
R: Resolver<H>,
{
handler.mark_hot(state.as_ref().context.address, TouchKind::StateChange);
if let Some(transfer) = transfer {
match handler.transfer(transfer) {
Ok(()) => (),
Err(err) => {
return Ok(InvokerControl::DirectExit(InvokerExit {
result: Err(err),
substate: Some(state),
retval: Vec::new(),
}));
}
}
}
resolver.resolve_call(origin, scheme, code_address, input, state, handler)
}
#[allow(clippy::too_many_arguments)]
pub fn make_enter_create_machine<H, R>(
origin: ResolverOrigin,
resolver: &R,
_caller: H160,
init_code: Vec<u8>,
transfer: Transfer,
state: <R::Interpreter as Interpreter<H>>::State,
handler: &mut H,
) -> Result<InvokerControl<R::Interpreter, R::State>, ExitError>
where
R::State: AsRef<RuntimeState> + AsRef<Config>,
H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend,
R: Resolver<H>,
{
handler.mark_hot(
AsRef::<RuntimeState>::as_ref(&state).context.address,
TouchKind::StateChange,
);
match handler.transfer(transfer) {
Ok(()) => (),
Err(err) => {
return Ok(InvokerControl::DirectExit(InvokerExit {
result: Err(err),
substate: Some(state),
retval: Vec::new(),
}));
}
}
if !handler.can_create(AsRef::<RuntimeState>::as_ref(&state).context.address) {
return Ok(InvokerControl::DirectExit(InvokerExit {
result: Err(ExitException::CreateCollision.into()),
substate: Some(state),
retval: Vec::new(),
}));
}
if AsRef::<Config>::as_ref(&state).eip161_create_increase_nonce {
match handler.inc_nonce(AsRef::<RuntimeState>::as_ref(&state).context.address) {
Ok(()) => (),
Err(err) => {
return Ok(InvokerControl::DirectExit(InvokerExit {
result: Err(err),
substate: Some(state),
retval: Vec::new(),
}));
}
}
}
handler.reset_storage(AsRef::<RuntimeState>::as_ref(&state).context.address);
handler.mark_create(AsRef::<RuntimeState>::as_ref(&state).context.address);
resolver.resolve_create(origin, init_code, state, handler)
}
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub fn enter_call_substack<H, R>(
resolver: &R,
trap_data: CallTrap,
code_address: H160,
state: R::State,
handler: &mut H,
) -> Result<(SubstackInvoke, InvokerControl<R::Interpreter, R::State>), ExitError>
where
R::State: AsRef<RuntimeState>,
H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend,
R: Resolver<H>,
{
handler.push_substate();
let work = || -> Result<_, ExitError> {
let machine = make_enter_call_machine(
ResolverOrigin::Substack,
resolver,
trap_data.scheme,
code_address,
trap_data.input.clone(),
trap_data.transfer.clone(),
state,
handler,
)?;
Ok(machine)
};
let res = work();
let invoke = SubstackInvoke::Call { trap: trap_data };
match res {
Ok(machine) => Ok((invoke, machine)),
Err(err) => Ok((
invoke,
InvokerControl::DirectExit(InvokerExit {
result: Err(err),
substate: None,
retval: Vec::new(),
}),
)),
}
}
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub fn enter_create_substack<H, R>(
resolver: &R,
code: Vec<u8>,
trap_data: CreateTrap,
address: H160,
state: R::State,
handler: &mut H,
) -> Result<(SubstackInvoke, InvokerControl<R::Interpreter, R::State>), ExitError>
where
R::State: AsRef<RuntimeState> + AsRef<Config>,
H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend,
R: Resolver<H>,
{
handler.push_substate();
let scheme = trap_data.scheme;
let value = trap_data.value;
let caller = scheme.caller();
let transfer = Transfer {
source: caller,
target: address,
value,
};
let invoke = SubstackInvoke::Create {
address,
trap: trap_data,
};
let machine = make_enter_create_machine(
ResolverOrigin::Substack,
resolver,
caller,
code,
transfer,
state,
handler,
)?;
Ok((invoke, machine))
}
fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> {
if config.eip3541_disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first()
{
return Err(ExitException::InvalidOpcode(Opcode::EOFMAGIC).into());
}
Ok(())
}
pub fn deploy_create_code<S, H>(
address: H160,
retbuf: Vec<u8>,
state: &mut S,
handler: &mut H,
origin: SetCodeOrigin,
) -> Result<(), ExitError>
where
S: InvokerState + AsRef<Config>,
H: RuntimeEnvironment + RuntimeBackend + TransactionalBackend,
{
check_first_byte(state.as_ref(), &retbuf[..])?;
if let Some(limit) = AsRef::<Config>::as_ref(state).create_contract_limit()
&& retbuf.len() > limit
{
return Err(ExitException::CreateContractLimit.into());
}
state.record_codedeposit(retbuf.len())?;
handler.set_code(address, retbuf, origin)?;
Ok(())
}