Skip to main content

revmc_builtins/
utils.rs

1use revm_interpreter::{Gas, InstructionResult, SharedMemory, as_usize_saturated};
2use revmc_context::{EvmContext, EvmWord};
3
4/// Splits the stack pointer into `N` elements by casting it to an array.
5///
6/// NOTE: this returns the arguments in **reverse order**. Use [`read_words!`] to get them in order.
7///
8/// The returned lifetime is valid for the entire duration of the builtin.
9///
10/// # Safety
11///
12/// Caller must ensure that `N` matches the number of elements popped in JIT code.
13#[inline(always)]
14pub(crate) unsafe fn read_words_rev<'a, const N: usize>(sp: *mut EvmWord) -> &'a mut [EvmWord; N] {
15    &mut *sp.cast::<[EvmWord; N]>()
16}
17
18#[inline]
19pub(crate) fn ensure_memory(
20    ecx: &mut EvmContext<'_>,
21    offset: usize,
22    len: usize,
23) -> InstructionResult {
24    ensure_memory_inner(ecx.memory, ecx.gas, offset, len)
25}
26
27#[inline]
28pub(crate) fn ensure_memory_inner(
29    memory: &mut SharedMemory,
30    gas: &mut Gas,
31    offset: usize,
32    len: usize,
33) -> InstructionResult {
34    let new_size = offset.saturating_add(len);
35    if new_size > memory.len() {
36        return resize_memory_inner(memory, gas, new_size);
37    }
38    InstructionResult::Stop
39}
40
41#[inline]
42pub(crate) fn resize_memory(ecx: &mut EvmContext<'_>, new_size: usize) -> InstructionResult {
43    resize_memory_inner(ecx.memory, ecx.gas, new_size)
44}
45
46fn resize_memory_inner(
47    memory: &mut SharedMemory,
48    gas: &mut Gas,
49    new_size: usize,
50) -> InstructionResult {
51    // Calculate words needed (memory is always word-aligned)
52    let new_num_words = revm_interpreter::interpreter::num_words(new_size);
53    let current_words = gas.memory().words_num;
54
55    if new_num_words > current_words {
56        // Calculate gas cost for memory expansion
57        // memory_gas(num_words, linear_cost, quadratic_cost)
58        // MEMORY = 3 (linear cost per word), MEMORY_QUAD_COEFFICIENT = 512 (1/512 for quadratic)
59        let new_cost = crate::gas::memory_gas(new_num_words, 3, 512);
60        let old_cost = crate::gas::memory_gas(current_words, 3, 512);
61        let cost = new_cost.saturating_sub(old_cost);
62
63        if !gas.record_cost(cost) {
64            return InstructionResult::MemoryOOG;
65        }
66
67        // Update memory words tracking
68        gas.memory_mut().words_num = new_num_words;
69
70        // Resize the actual memory (must be word-aligned, as per EVM spec)
71        memory.resize(new_num_words * 32);
72    }
73    InstructionResult::Stop
74}
75
76pub(crate) unsafe fn copy_operation(
77    ecx: &mut EvmContext<'_>,
78    rev![memory_offset, data_offset, len]: &mut [EvmWord; 3],
79    data: &[u8],
80) -> InstructionResult {
81    let len = try_into_usize!(len);
82    if len != 0 {
83        gas!(ecx, ecx.host.gas_params().copy_cost(len));
84        let memory_offset = try_into_usize!(memory_offset);
85        ensure_memory!(ecx, memory_offset, len);
86        let data_offset = data_offset.to_u256();
87        let data_offset = as_usize_saturated!(data_offset);
88        ecx.memory.set_data(memory_offset, data_offset, len, data);
89    }
90    InstructionResult::Stop
91}