use crate::{
limits,
metering::Token,
storage::WriteOutcome,
vec::Vec,
vm::{
evm::{
instructions::utility::IntoAddress, interpreter::Halt, util::as_usize_or_halt,
Interpreter,
},
Ext,
},
DispatchError, Error, Key, RuntimeCosts, LOG_TARGET, U256,
};
use core::ops::ControlFlow;
pub fn balance<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
interpreter.ext.charge_or_halt(RuntimeCosts::BalanceOf)?;
let ([], top) = interpreter.stack.popn_top()?;
*top = interpreter.ext.balance_of(&top.into_address());
ControlFlow::Continue(())
}
pub fn selfbalance<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
interpreter.ext.charge_or_halt(RuntimeCosts::Balance)?;
let balance = interpreter.ext.balance();
interpreter.stack.push(balance)
}
pub fn extcodesize<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let ([], top) = interpreter.stack.popn_top()?;
interpreter.ext.charge_or_halt(RuntimeCosts::CodeSize)?;
let code_size = interpreter.ext.code_size(&top.into_address());
*top = U256::from(code_size);
ControlFlow::Continue(())
}
pub fn extcodehash<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let ([], top) = interpreter.stack.popn_top()?;
interpreter.ext.charge_or_halt(RuntimeCosts::CodeHash)?;
let code_hash = interpreter.ext.code_hash(&top.into_address());
*top = U256::from_big_endian(&code_hash.0);
ControlFlow::Continue(())
}
pub fn extcodecopy<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let [address, memory_offset, code_offset, len] = interpreter.stack.popn()?;
let len = as_usize_or_halt::<E::T>(len)?;
interpreter.ext.charge_or_halt(RuntimeCosts::ExtCodeCopy(len as u32))?;
if len == 0 {
return ControlFlow::Continue(());
}
let address = address.into_address();
let memory_offset = as_usize_or_halt::<E::T>(memory_offset)?;
let code_offset = as_usize_or_halt::<E::T>(code_offset)?;
interpreter.memory.resize(memory_offset, len)?;
let mut buf = interpreter.memory.slice_mut(memory_offset, len);
interpreter.ext.copy_code_slice(&mut buf, &address, code_offset);
ControlFlow::Continue(())
}
pub fn blockhash<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
interpreter.ext.charge_or_halt(RuntimeCosts::BlockHash)?;
let ([], number) = interpreter.stack.popn_top()?;
if let Some(hash) = interpreter.ext.block_hash(*number) {
*number = U256::from_big_endian(&hash.0)
} else {
*number = U256::zero()
};
ControlFlow::Continue(())
}
pub fn sload<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let ([], index) = interpreter.stack.popn_top()?;
interpreter.ext.charge_or_halt(RuntimeCosts::GetStorage(32))?;
let key = Key::Fix(index.to_big_endian());
let value = interpreter.ext.get_storage(&key);
*index = if let Some(storage_value) = value {
let Ok::<[u8; 32], _>(bytes) = storage_value.try_into() else {
log::debug!(target: crate::LOG_TARGET, "sload read invalid storage value length. Expected 32.");
return ControlFlow::Break(Error::<E::T>::ContractTrapped.into());
};
U256::from_big_endian(&bytes)
} else {
U256::zero()
};
ControlFlow::Continue(())
}
fn store_helper<'ext, E: Ext>(
interpreter: &mut Interpreter<'ext, E>,
cost_before: RuntimeCosts,
set_function: fn(&mut E, &Key, Option<Vec<u8>>, bool) -> Result<WriteOutcome, DispatchError>,
adjust_cost: fn(new_bytes: u32, old_bytes: u32) -> RuntimeCosts,
) -> ControlFlow<Halt> {
if interpreter.ext.is_read_only() {
return ControlFlow::Break(Error::<E::T>::StateChangeDenied.into());
}
let [index, value] = interpreter.stack.popn()?;
let charged_amount = interpreter.ext.charge_or_halt(cost_before)?;
let key = Key::Fix(index.to_big_endian());
let take_old = false;
let value_to_store = if value.is_zero() { None } else { Some(value.to_big_endian().to_vec()) };
let Ok(write_outcome) = set_function(interpreter.ext, &key, value_to_store.clone(), take_old)
else {
return ControlFlow::Break(Error::<E::T>::ContractTrapped.into());
};
interpreter.ext.frame_meter_mut().adjust_weight(
charged_amount,
adjust_cost(value_to_store.unwrap_or_default().len() as u32, write_outcome.old_len()),
);
ControlFlow::Continue(())
}
pub fn sstore<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let old_bytes = limits::STORAGE_BYTES;
store_helper(
interpreter,
RuntimeCosts::SetStorage { new_bytes: 32, old_bytes },
|ext, key, value, take_old| ext.set_storage(key, value, take_old),
|new_bytes, old_bytes| RuntimeCosts::SetStorage { new_bytes, old_bytes },
)
}
pub fn tstore<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let old_bytes = limits::STORAGE_BYTES;
store_helper(
interpreter,
RuntimeCosts::SetTransientStorage { new_bytes: 32, old_bytes },
|ext, key, value, take_old| ext.set_transient_storage(key, value, take_old),
|new_bytes, old_bytes| RuntimeCosts::SetTransientStorage { new_bytes, old_bytes },
)
}
pub fn tload<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt> {
let ([], index) = interpreter.stack.popn_top()?;
interpreter.ext.charge_or_halt(RuntimeCosts::GetTransientStorage(32))?;
let key = Key::Fix(index.to_big_endian());
let bytes = interpreter.ext.get_transient_storage(&key);
*index = if let Some(storage_value) = bytes {
if storage_value.len() != 32 {
log::debug!(target: crate::LOG_TARGET, "tload read invalid storage value length. Expected 32.");
return ControlFlow::Break(Error::<E::T>::ContractTrapped.into());
}
let Ok::<[u8; 32], _>(bytes) = storage_value.try_into() else {
return ControlFlow::Break(Error::<E::T>::ContractTrapped.into());
};
U256::from_big_endian(&bytes)
} else {
U256::zero()
};
ControlFlow::Continue(())
}
pub fn log<'ext, const N: usize, E: Ext>(
interpreter: &mut Interpreter<'ext, E>,
) -> ControlFlow<Halt> {
if interpreter.ext.is_read_only() {
return ControlFlow::Break(Error::<E::T>::StateChangeDenied.into());
}
let [offset, len] = interpreter.stack.popn()?;
let len = as_usize_or_halt::<E::T>(len)?;
if len as u32 > limits::EVENT_BYTES {
return ControlFlow::Break(Error::<E::T>::OutOfGas.into());
}
let cost = RuntimeCosts::DepositEvent { num_topic: N as u32, len: len as u32 };
interpreter.ext.charge_or_halt(cost)?;
let data = if len == 0 {
Vec::new()
} else {
let offset = as_usize_or_halt::<E::T>(offset)?;
interpreter.memory.resize(offset, len)?;
interpreter.memory.slice(offset..offset + len).to_vec()
};
if interpreter.stack.len() < N {
return ControlFlow::Break(Error::<E::T>::StackUnderflow.into());
}
let topics = interpreter.stack.popn::<N>()?;
let topics = topics.into_iter().map(|v| sp_core::H256::from(v.to_big_endian())).collect();
interpreter.ext.deposit_event(topics, data.to_vec());
ControlFlow::Continue(())
}
pub fn selfdestruct<'ext, E: Ext>(interpreter: &mut Interpreter<'ext, E>) -> ControlFlow<Halt> {
if interpreter.ext.is_read_only() {
return ControlFlow::Break(Error::<E::T>::StateChangeDenied.into());
}
let [beneficiary] = interpreter.stack.popn()?;
let charged = interpreter.ext.charge_or_halt(RuntimeCosts::Terminate { code_removed: true })?;
let dispatch_result = interpreter.ext.terminate_if_same_tx(&beneficiary.into_address());
match dispatch_result {
Ok(code_removed) => {
if matches!(code_removed, crate::CodeRemoved::No) {
let actual_cost = RuntimeCosts::Terminate { code_removed: false };
interpreter
.ext
.adjust_gas(charged, <RuntimeCosts as Token<E::T>>::weight(&actual_cost));
}
ControlFlow::Break(Halt::Return(Vec::default()))
},
Err(e) => {
log::debug!(target: LOG_TARGET, "Selfdestruct failed: {:?}", e);
ControlFlow::Break(Halt::Err(e))
},
}
}