1use core::{hint::cold_path, num::NonZero};
2use revm_context_interface::journaled_state::AccountInfoLoad;
3use revm_interpreter::{InstructionResult, as_usize_saturated, host::LoadError};
4use revm_primitives::Address;
5use revmc_context::{EvmContext, EvmWord};
6
7pub type BuiltinResult = Result<(), BuiltinError>;
8
9#[derive(Debug)]
11#[repr(transparent)]
12pub struct BuiltinError(NonZero<u8>);
13
14impl From<BuiltinError> for InstructionResult {
15 #[inline]
16 fn from(value: BuiltinError) -> Self {
17 unsafe { core::mem::transmute::<_, _>(value.0.get()) }
19 }
20}
21
22impl From<InstructionResult> for BuiltinError {
23 #[inline]
24 fn from(value: InstructionResult) -> Self {
25 cold_path();
26 Self(unsafe { NonZero::new_unchecked(value as u8) })
27 }
28}
29
30impl From<LoadError> for BuiltinError {
31 #[inline]
32 fn from(value: LoadError) -> Self {
33 cold_path();
34 match value {
35 LoadError::ColdLoadSkipped => InstructionResult::OutOfGas.into(),
36 LoadError::DBError => InstructionResult::FatalExternalError.into(),
37 }
38 }
39}
40
41pub(crate) trait OkOrFatal<T> {
43 fn ok_or_fatal(self) -> Result<T, BuiltinError>;
44}
45
46impl<T> OkOrFatal<T> for Option<T> {
47 #[inline]
48 fn ok_or_fatal(self) -> Result<T, BuiltinError> {
49 self.ok_or_else(|| InstructionResult::FatalExternalError.into())
50 }
51}
52
53pub(crate) fn load_account<'a>(
57 ecx: &'a mut EvmContext<'_>,
58 address: Address,
59 load_code: bool,
60) -> Result<AccountInfoLoad<'a>, BuiltinError> {
61 let cold_load_gas = ecx.gas_params.cold_account_additional_cost();
62 let skip_cold_load = ecx.gas.remaining() < cold_load_gas;
63 let account = ecx.host.load_account_info_skip_cold_load(address, load_code, skip_cold_load)?;
64 if account.is_cold {
65 gas!(ecx, cold_load_gas);
66 }
67 Ok(account)
68}
69
70#[inline(always)]
80pub(crate) unsafe fn read_words_rev<'a, const N: usize>(sp: *mut EvmWord) -> &'a mut [EvmWord; N] {
81 unsafe { &mut *sp.cast::<[EvmWord; N]>() }
82}
83
84#[inline]
85pub(crate) fn ensure_memory(ecx: &mut EvmContext<'_>, offset: usize, len: usize) -> BuiltinResult {
86 revm_interpreter::interpreter::resize_memory(
87 &mut ecx.gas,
88 ecx.memory,
89 &ecx.gas_params,
90 offset,
91 len,
92 )?;
93 ecx.refresh_memory_cache();
94 Ok(())
95}
96
97pub(crate) unsafe fn copy_operation(
98 ecx: &mut EvmContext<'_>,
99 rev![memory_offset, data_offset, len]: &mut [EvmWord; 3],
100 data: &[u8],
101) -> BuiltinResult {
102 let len = try_into_usize!(len);
103 if len != 0 {
104 gas!(ecx, ecx.gas_params.copy_cost(len));
105 let memory_offset = try_into_usize!(memory_offset);
106 ensure_memory(ecx, memory_offset, len)?;
107 let data_offset = data_offset.to_u256();
108 let data_offset = as_usize_saturated!(data_offset);
109 ecx.memory.set_data(memory_offset, data_offset, len, data);
110 }
111 Ok(())
112}