use crate::{
interpreter::{resize_memory, Interpreter},
interpreter_types::{
InputsTr, InterpreterTypes as ITy, LegacyBytecode, MemoryTr, ReturnData, RuntimeFlag,
StackTr,
},
CallInput, InstructionExecResult as Result, InstructionResult,
};
use context_interface::{cfg::GasParams, Host};
use core::ptr;
use primitives::{B256, KECCAK_EMPTY, U256};
use crate::InstructionContext as Ictx;
pub fn keccak256<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
popn_top!([offset], top, context.interpreter);
let len = as_usize_or_fail!(context.interpreter, top);
gas!(
context.interpreter,
context.host.gas_params().keccak256_cost(len)
);
let hash = if len == 0 {
KECCAK_EMPTY
} else {
let from = as_usize_or_fail!(context.interpreter, offset);
resize_memory(
&mut context.interpreter.gas,
&mut context.interpreter.memory,
context.host.gas_params(),
from,
len,
)?;
primitives::keccak256(context.interpreter.memory.slice_len(from, len).as_ref())
};
*top = hash.into();
Ok(())
}
pub fn address<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
push!(
context.interpreter,
context
.interpreter
.input
.target_address()
.into_word()
.into()
);
Ok(())
}
pub fn caller<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
push!(
context.interpreter,
context
.interpreter
.input
.caller_address()
.into_word()
.into()
);
Ok(())
}
pub fn codesize<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
push!(
context.interpreter,
U256::from(context.interpreter.bytecode.bytecode_len())
);
Ok(())
}
pub fn codecopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
popn!([memory_offset, code_offset, len], context.interpreter);
let len = as_usize_or_fail!(context.interpreter, len);
let Some(memory_offset) = copy_cost_and_memory_resize(
context.interpreter,
context.host.gas_params(),
memory_offset,
len,
)?
else {
return Ok(());
};
let code_offset = as_usize_saturated!(code_offset);
context.interpreter.memory.set_data(
memory_offset,
code_offset,
len,
context.interpreter.bytecode.bytecode_slice(),
);
Ok(())
}
pub fn calldataload<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
popn_top!([], offset_ptr, context.interpreter);
let mut word = B256::ZERO;
let offset = as_usize_saturated!(*offset_ptr);
let input = context.interpreter.input.input();
let input_len = input.len();
if offset < input_len {
let count = 32.min(input_len - offset);
let input = &*input.as_bytes_memory(&context.interpreter.memory);
unsafe { ptr::copy_nonoverlapping(input.as_ptr().add(offset), word.as_mut_ptr(), count) };
}
*offset_ptr = word.into();
Ok(())
}
pub fn calldatasize<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
push!(
context.interpreter,
U256::from(context.interpreter.input.input().len())
);
Ok(())
}
pub fn callvalue<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
push!(context.interpreter, context.interpreter.input.call_value());
Ok(())
}
pub fn calldatacopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
popn!([memory_offset, data_offset, len], context.interpreter);
let len = as_usize_or_fail!(context.interpreter, len);
let Some(memory_offset) = copy_cost_and_memory_resize(
context.interpreter,
context.host.gas_params(),
memory_offset,
len,
)?
else {
return Ok(());
};
let data_offset = as_usize_saturated!(data_offset);
match context.interpreter.input.input() {
CallInput::Bytes(bytes) => {
context
.interpreter
.memory
.set_data(memory_offset, data_offset, len, bytes.as_ref());
}
CallInput::SharedBuffer(range) => {
context.interpreter.memory.set_data_from_global(
memory_offset,
data_offset,
len,
range.clone(),
);
}
}
Ok(())
}
pub fn returndatasize<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
check!(context.interpreter, BYZANTIUM);
push!(
context.interpreter,
U256::from(context.interpreter.return_data.buffer().len())
);
Ok(())
}
pub fn returndatacopy<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
check!(context.interpreter, BYZANTIUM);
popn!([memory_offset, offset, len], context.interpreter);
let len = as_usize_or_fail!(context.interpreter, len);
let data_offset = as_usize_saturated!(offset);
let data_end = data_offset.saturating_add(len);
if data_end > context.interpreter.return_data.buffer().len() {
return Err(InstructionResult::OutOfOffset);
}
let Some(memory_offset) = copy_cost_and_memory_resize(
context.interpreter,
context.host.gas_params(),
memory_offset,
len,
)?
else {
return Ok(());
};
context.interpreter.memory.set_data(
memory_offset,
data_offset,
len,
context.interpreter.return_data.buffer(),
);
Ok(())
}
pub fn gas<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
let gas = &context.interpreter.gas;
push!(context.interpreter, U256::from(gas.remaining()));
Ok(())
}
pub fn copy_cost_and_memory_resize(
interpreter: &mut Interpreter<impl ITy>,
gas_params: &GasParams,
memory_offset: U256,
len: usize,
) -> Result<Option<usize>, InstructionResult> {
gas!(interpreter, gas_params.copy_cost(len));
if len == 0 {
return Ok(None);
}
let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
interpreter.resize_memory(gas_params, memory_offset, len)?;
Ok(Some(memory_offset))
}