mod call_helpers;
pub use call_helpers::{
get_memory_input_and_out_ranges, load_acc_and_calc_gas, load_account_delegated,
load_account_delegated_handle_error, resize_memory,
};
use crate::{
instructions::utility::IntoAddress,
interpreter_action::FrameInput,
interpreter_types::{InputsTr, InterpreterTypes, LoopControl, MemoryTr, RuntimeFlag, StackTr},
CallInput, CallInputs, CallScheme, CallValue, CreateInputs, Host, InstructionResult,
InterpreterAction,
};
use context_interface::CreateScheme;
use primitives::{hardfork::SpecId, Address, Bytes, B256, U256};
use std::boxed::Box;
use crate::InstructionContext;
pub fn create<WIRE: InterpreterTypes, const IS_CREATE2: bool, H: Host + ?Sized>(
context: InstructionContext<'_, H, WIRE>,
) {
require_non_staticcall!(context.interpreter);
if IS_CREATE2 {
check!(context.interpreter, PETERSBURG);
}
popn!([value, code_offset, len], context.interpreter);
let len = as_usize_or_fail!(context.interpreter, len);
let mut code = Bytes::new();
if len != 0 {
if context
.interpreter
.runtime_flag
.spec_id()
.is_enabled_in(SpecId::SHANGHAI)
{
if len > context.host.max_initcode_size() {
context
.interpreter
.halt(InstructionResult::CreateInitCodeSizeLimit);
return;
}
gas!(
context.interpreter,
context.host.gas_params().initcode_cost(len)
);
}
let code_offset = as_usize_or_fail!(context.interpreter, code_offset);
resize_memory!(
context.interpreter,
context.host.gas_params(),
code_offset,
len
);
code = Bytes::copy_from_slice(
context
.interpreter
.memory
.slice_len(code_offset, len)
.as_ref(),
);
}
let scheme = if IS_CREATE2 {
popn!([salt], context.interpreter);
gas!(
context.interpreter,
context.host.gas_params().create2_cost(len)
);
CreateScheme::Create2 { salt }
} else {
gas!(context.interpreter, context.host.gas_params().create_cost());
CreateScheme::Create
};
if context.host.is_amsterdam_eip8037_enabled() {
state_gas!(
context.interpreter,
context.host.gas_params().create_state_gas()
);
}
let mut gas_limit = context.interpreter.gas.remaining();
if context
.interpreter
.runtime_flag
.spec_id()
.is_enabled_in(SpecId::TANGERINE)
{
gas_limit = context.host.gas_params().call_stipend_reduction(gas_limit);
}
gas!(context.interpreter, gas_limit);
let mut create_inputs = CreateInputs::new(
context.interpreter.input.target_address(),
scheme,
value,
code,
gas_limit,
);
create_inputs.set_reservoir(context.interpreter.gas.reservoir());
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(
create_inputs,
))));
}
pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(
mut context: InstructionContext<'_, H, WIRE>,
) {
popn!([local_gas_limit, to, value], context.interpreter);
let to = to.into_address();
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
let has_transfer = !value.is_zero();
if context.interpreter.runtime_flag.is_static() && has_transfer {
context
.interpreter
.halt(InstructionResult::CallNotAllowedInsideStatic);
return;
}
let Some((input, return_memory_offset)) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())
else {
return;
};
let Some((gas_limit, bytecode, bytecode_hash)) =
load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit)
else {
return;
};
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: to,
caller: context.interpreter.input.target_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Transfer(value),
scheme: CallScheme::Call,
is_static: context.interpreter.runtime_flag.is_static(),
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
}
pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
mut context: InstructionContext<'_, H, WIRE>,
) {
popn!([local_gas_limit, to, value], context.interpreter);
let to = Address::from_word(B256::from(to));
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
let has_transfer = !value.is_zero();
let Some((input, return_memory_offset)) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())
else {
return;
};
let Some((gas_limit, bytecode, bytecode_hash)) =
load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit)
else {
return;
};
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: context.interpreter.input.target_address(),
caller: context.interpreter.input.target_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Transfer(value),
scheme: CallScheme::CallCode,
is_static: context.interpreter.runtime_flag.is_static(),
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
}
pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
mut context: InstructionContext<'_, H, WIRE>,
) {
check!(context.interpreter, HOMESTEAD);
popn!([local_gas_limit, to], context.interpreter);
let to = Address::from_word(B256::from(to));
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
let Some((input, return_memory_offset)) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())
else {
return;
};
let Some((gas_limit, bytecode, bytecode_hash)) =
load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
else {
return;
};
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: context.interpreter.input.target_address(),
caller: context.interpreter.input.caller_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Apparent(context.interpreter.input.call_value()),
scheme: CallScheme::DelegateCall,
is_static: context.interpreter.runtime_flag.is_static(),
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
}
pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
mut context: InstructionContext<'_, H, WIRE>,
) {
check!(context.interpreter, BYZANTIUM);
popn!([local_gas_limit, to], context.interpreter);
let to = Address::from_word(B256::from(to));
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
let Some((input, return_memory_offset)) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())
else {
return;
};
let Some((gas_limit, bytecode, bytecode_hash)) =
load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
else {
return;
};
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: to,
caller: context.interpreter.input.target_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Transfer(U256::ZERO),
scheme: CallScheme::StaticCall,
is_static: true,
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
}