use super::Control;
use crate::core::utils::{U256_ZERO, U64_MAX, USIZE_MAX};
use crate::prelude::*;
use crate::{
CallScheme, Capture, Context, CreateScheme, ExitError, ExitSucceed, Handler, Runtime, Transfer,
};
use core::cmp::max;
use primitive_types::{H256, U256};
use sha3::{Digest, Keccak256};
pub fn sha3<H: Handler>(runtime: &mut Runtime) -> Control<H> {
pop_u256!(runtime, from, len);
let from = if len == U256_ZERO {
usize::MAX
} else {
as_usize_or_fail!(from)
};
let len = as_usize_or_fail!(len);
try_or_fail!(runtime.machine.memory_mut().resize_offset(from, len));
let data = if len == 0 {
Vec::new()
} else {
runtime.machine.memory_mut().get(from, len)
};
let ret = Keccak256::digest(data.as_slice());
push_h256!(runtime, H256::from_slice(<[u8; 32]>::from(ret).as_slice()));
Control::Continue
}
pub fn chainid<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.chain_id());
Control::Continue
}
pub fn address<H: Handler>(runtime: &mut Runtime) -> Control<H> {
let ret = H256::from(runtime.context.address);
push_h256!(runtime, ret);
Control::Continue
}
pub fn balance<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
pop_h256!(runtime, address);
push_u256!(runtime, handler.balance(address.into()));
Control::Continue
}
pub fn selfbalance<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.balance(runtime.context.address));
Control::Continue
}
pub fn origin<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
let ret = H256::from(handler.origin());
push_h256!(runtime, ret);
Control::Continue
}
pub fn caller<H: Handler>(runtime: &mut Runtime) -> Control<H> {
let ret = H256::from(runtime.context.caller);
push_h256!(runtime, ret);
Control::Continue
}
pub fn callvalue<H: Handler>(runtime: &mut Runtime) -> Control<H> {
let ret = H256(runtime.context.apparent_value.to_big_endian());
push_h256!(runtime, ret);
Control::Continue
}
pub fn gasprice<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
let ret = H256(handler.gas_price().to_big_endian());
push_h256!(runtime, ret);
Control::Continue
}
pub fn base_fee<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.block_base_fee_per_gas());
Control::Continue
}
pub fn blob_base_fee<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
let blob_base_fee = U256::from(handler.blob_base_fee().unwrap_or_default());
push_u256!(runtime, blob_base_fee);
Control::Continue
}
pub fn blob_hash<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
let raw_index = match runtime.machine.stack().peek(0) {
Ok(value) => value,
Err(e) => return Control::Exit(e.into()),
};
let index = if raw_index > USIZE_MAX {
usize::MAX
} else {
raw_index.as_usize()
};
let blob_hash = handler.get_blob_hash(index).unwrap_or(U256_ZERO);
if let Err(e) = runtime.machine.stack_mut().set(0, blob_hash) {
return Control::Exit(e.into());
}
Control::Continue
}
pub fn extcodesize<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop_h256!(runtime, address);
push_u256!(runtime, handler.code_size(address.into()));
Control::Continue
}
pub fn extcodehash<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop_h256!(runtime, address);
push_h256!(runtime, handler.code_hash(address.into()));
Control::Continue
}
pub fn extcodecopy<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
pop_h256!(runtime, address);
pop_u256!(runtime, memory_offset, code_offset, len);
if len == U256_ZERO {
return Control::Continue;
}
let len = as_usize_or_fail!(len);
let memory_offset = as_usize_or_fail!(memory_offset);
try_or_fail!(runtime
.machine
.memory_mut()
.resize_offset(memory_offset, len));
match runtime.machine.memory_mut().copy_data(
memory_offset,
code_offset,
len,
&handler.code(address.into()),
) {
Ok(()) => (),
Err(e) => return Control::Exit(e.into()),
}
Control::Continue
}
pub fn returndatasize<H: Handler>(runtime: &mut Runtime) -> Control<H> {
let size = U256::from(runtime.return_data_buffer.len());
push_u256!(runtime, size);
Control::Continue
}
pub fn returndatacopy<H: Handler>(runtime: &mut Runtime) -> Control<H> {
pop_u256!(runtime, memory_offset, data_offset, len);
let memory_offset = if len == U256_ZERO {
0
} else {
as_usize_or_fail!(memory_offset)
};
let len = as_usize_or_fail!(len);
try_or_fail!(runtime
.machine
.memory_mut()
.resize_offset(memory_offset, len));
if data_offset
.checked_add(len.into())
.is_none_or(|l| l > U256::from(runtime.return_data_buffer.len()))
{
return Control::Exit(ExitError::OutOfOffset.into());
}
match runtime.machine.memory_mut().copy_data(
memory_offset,
data_offset,
len,
&runtime.return_data_buffer,
) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e.into()),
}
}
pub fn blockhash<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
pop_u256!(runtime, number);
push_h256!(runtime, handler.block_hash(number));
Control::Continue
}
pub fn coinbase<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_h256!(runtime, handler.block_coinbase());
Control::Continue
}
pub fn timestamp<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.block_timestamp());
Control::Continue
}
pub fn number<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.block_number());
Control::Continue
}
pub fn difficulty<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.block_difficulty());
Control::Continue
}
pub fn prevrandao<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
if let Some(rand) = handler.block_randomness() {
push_h256!(runtime, rand);
Control::Continue
} else {
difficulty(runtime, handler)
}
}
pub fn gaslimit<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.block_gas_limit());
Control::Continue
}
pub fn sload<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
pop_h256!(runtime, index);
let value = handler.storage(runtime.context.address, index);
push_h256!(runtime, value);
event!(SLoad {
address: runtime.context.address,
index,
value
});
Control::Continue
}
pub fn sstore<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop_h256!(runtime, index, value);
event!(SStore {
address: runtime.context.address,
index,
value
});
match handler.set_storage(runtime.context.address, index, value) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e.into()),
}
}
pub fn tload<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
let index = match runtime.machine.stack().peek(0) {
Ok(value) => H256(value.to_big_endian()),
Err(e) => return Control::Exit(e.into()),
};
let value = match handler.tload(runtime.context.address, index) {
Ok(value) => value,
Err(e) => return Control::Exit(e.into()),
};
match runtime.machine.stack_mut().set(0, value) {
Ok(()) => (),
Err(e) => return Control::Exit(e.into()),
}
Control::Continue
}
pub fn tstore<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop_h256!(runtime, index);
pop_u256!(runtime, value);
match handler.tstore(runtime.context.address, index, value) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e.into()),
}
}
pub fn mcopy<H: Handler>(runtime: &mut Runtime, _handler: &mut H) -> Control<H> {
pop_u256!(runtime, dst, src, len);
if len == U256_ZERO {
return Control::Continue;
}
let len = as_usize_or_fail!(len, ExitError::OutOfGas);
let dst = as_usize_or_fail!(dst, ExitError::OutOfGas);
let src = as_usize_or_fail!(src, ExitError::OutOfGas);
try_or_fail!(runtime
.machine
.memory_mut()
.resize_offset(max(src, dst), len));
match runtime.machine.memory_mut().copy(src, dst, len) {
Ok(()) => (),
Err(e) => return Control::Exit(e.into()),
}
Control::Continue
}
pub fn gas<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
push_u256!(runtime, handler.gas_left());
Control::Continue
}
pub fn log<H: Handler>(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control<H> {
pop_u256!(runtime, offset, len);
let offset = if len == U256_ZERO {
usize::MAX
} else {
as_usize_or_fail!(offset)
};
let len = as_usize_or_fail!(len);
try_or_fail!(runtime.machine.memory_mut().resize_offset(offset, len));
let data = if len == 0 {
Vec::new()
} else {
runtime.machine.memory().get(offset, len)
};
let mut topics = Vec::new();
for _ in 0..n {
match runtime.machine.stack_mut().pop_h256() {
Ok(value) => {
topics.push(value);
}
Err(e) => return Control::Exit(e.into()),
}
}
match handler.log(runtime.context.address, topics, data) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e.into()),
}
}
pub fn selfdestruct<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop_h256!(runtime, target);
match handler.mark_delete(runtime.context.address, target.into()) {
Ok(()) => (),
Err(e) => return Control::Exit(e.into()),
}
Control::Exit(ExitSucceed::Suicided.into())
}
pub fn create<H: Handler>(runtime: &mut Runtime, is_create2: bool, handler: &mut H) -> Control<H> {
runtime.return_data_buffer = Vec::new();
pop_u256!(runtime, value, code_offset, len);
let code_offset = if len == U256_ZERO {
usize::MAX
} else {
as_usize_or_fail!(code_offset)
};
let len = as_usize_or_fail!(len);
try_or_fail!(runtime.machine.memory_mut().resize_offset(code_offset, len));
let code = if len == 0 {
Vec::new()
} else {
runtime.machine.memory().get(code_offset, len)
};
let scheme = if is_create2 {
pop_h256!(runtime, salt);
let code_hash = H256::from_slice(<[u8; 32]>::from(Keccak256::digest(&code)).as_slice());
CreateScheme::Create2 {
caller: runtime.context.address,
salt,
code_hash,
}
} else {
CreateScheme::Legacy {
caller: runtime.context.address,
}
};
match handler.create(runtime.context.address, scheme, value, code, None) {
Capture::Exit((reason, return_data)) => {
debug_assert!(
reason.is_error(),
"ExitReason for finish_create should be only ExitError"
);
match super::finish_create(runtime, reason, None, return_data) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e),
}
}
Capture::Trap(interrupt) => Control::CreateInterrupt(interrupt),
}
}
pub fn call<H: Handler>(runtime: &mut Runtime, scheme: CallScheme, handler: &mut H) -> Control<H> {
runtime.return_data_buffer = Vec::new();
pop_u256!(runtime, gas);
pop_h256!(runtime, to);
let gas = if gas > U64_MAX {
None
} else {
Some(gas.as_u64())
};
let value = match scheme {
CallScheme::Call | CallScheme::CallCode => {
pop_u256!(runtime, value);
value
}
CallScheme::DelegateCall | CallScheme::StaticCall => U256_ZERO,
};
pop_u256!(runtime, in_offset, in_len);
pop_u256!(runtime, out_offset, out_len);
let in_offset = if in_len == U256_ZERO {
usize::MAX
} else {
as_usize_or_fail!(in_offset)
};
let in_len = as_usize_or_fail!(in_len);
let out_offset = if out_len == U256_ZERO {
usize::MAX
} else {
as_usize_or_fail!(out_offset)
};
let out_len = as_usize_or_fail!(out_len);
try_or_fail!(runtime
.machine
.memory_mut()
.resize_offset(in_offset, in_len));
try_or_fail!(runtime
.machine
.memory_mut()
.resize_offset(out_offset, out_len));
let input = if in_len == 0 {
Vec::new()
} else {
runtime.machine.memory().get(in_offset, in_len)
};
let context = match scheme {
CallScheme::Call | CallScheme::StaticCall => Context {
address: to.into(),
caller: runtime.context.address,
apparent_value: value,
},
CallScheme::CallCode => Context {
address: runtime.context.address,
caller: runtime.context.address,
apparent_value: value,
},
CallScheme::DelegateCall => Context {
address: runtime.context.address,
caller: runtime.context.caller,
apparent_value: runtime.context.apparent_value,
},
};
let transfer = if scheme == CallScheme::Call {
Some(Transfer {
source: runtime.context.address,
target: to.into(),
value,
})
} else if scheme == CallScheme::CallCode {
Some(Transfer {
source: runtime.context.address,
target: runtime.context.address,
value,
})
} else {
None
};
match handler.call(
to.into(),
transfer,
input,
gas,
scheme == CallScheme::StaticCall,
context,
) {
Capture::Exit((reason, return_data)) => {
match super::finish_call(runtime, out_len, out_offset, reason, return_data) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e),
}
}
Capture::Trap(interrupt) => {
runtime.return_data_len = out_len;
runtime.return_data_offset = out_offset;
Control::CallInterrupt(interrupt)
}
}
}