aurora_evm/core/
memory.rs

1use super::prelude::*;
2use super::utils::USIZE_MAX;
3use crate::{ExitError, ExitFatal};
4use core::cmp::min;
5use core::ops::{BitAnd, Not};
6use primitive_types::{H256, U256};
7
8/// A sequential memory. It uses Rust's `Vec` for internal
9/// representation.
10#[derive(Clone, Debug)]
11pub struct Memory {
12    /// Memory data
13    data: Vec<u8>,
14    /// Memory effective length, that changed after resize operations.
15    effective_len: usize,
16    /// Memory limit
17    limit: usize,
18}
19
20impl Memory {
21    /// Create a new memory with the given limit.
22    #[must_use]
23    pub const fn new(limit: usize) -> Self {
24        Self {
25            data: Vec::new(),
26            effective_len: 0,
27            limit,
28        }
29    }
30
31    /// Memory limit.
32    #[must_use]
33    pub const fn limit(&self) -> usize {
34        self.limit
35    }
36
37    /// Get the length of the current memory range.
38    #[must_use]
39    pub fn len(&self) -> usize {
40        self.data.len()
41    }
42
43    /// Get the effective length.
44    #[must_use]
45    pub const fn effective_len(&self) -> usize {
46        self.effective_len
47    }
48
49    /// Return true if current effective memory range is zero.
50    #[must_use]
51    pub fn is_empty(&self) -> bool {
52        self.len() == 0
53    }
54
55    /// Return the full memory.
56    #[must_use]
57    pub const fn data(&self) -> &Vec<u8> {
58        &self.data
59    }
60
61    /// Resize the memory, making it cover the memory region of `offset..offset + len`,
62    /// with 32 bytes as the step. If the length is zero, this function does nothing.
63    ///
64    /// # Errors
65    /// Return `ExitError::InvalidRange` if `offset + len` is overflow.
66    pub fn resize_offset(&mut self, offset: usize, len: usize) -> Result<(), ExitError> {
67        if len == 0 {
68            return Ok(());
69        }
70
71        offset
72            .checked_add(len)
73            .map_or(Err(ExitError::InvalidRange), |end| self.resize_end(end))
74    }
75
76    /// Resize the memory, making it cover to `end`, with 32 bytes as the step.
77    ///
78    /// # Errors
79    /// Return `ExitError::InvalidRange` if `end` value is overflow in `next_multiple_of_32` call.
80    pub fn resize_end(&mut self, end: usize) -> Result<(), ExitError> {
81        if end > self.effective_len {
82            let new_end = next_multiple_of_32(end).ok_or(ExitError::InvalidRange)?;
83            self.effective_len = new_end;
84        }
85
86        Ok(())
87    }
88
89    /// Get memory region at given offset.
90    ///
91    /// ## Panics
92    ///
93    /// Value of `size` is considered trusted. If they're too large,
94    /// the program can run out of memory, or it can overflow.
95    #[must_use]
96    pub fn get(&self, mut offset: usize, size: usize) -> Vec<u8> {
97        if offset > self.data.len() {
98            offset = self.data.len();
99        }
100
101        let mut end = offset + size;
102        if end > self.data.len() {
103            end = self.data.len();
104        }
105
106        let mut ret = self.data[offset..end].to_vec();
107        ret.resize(size, 0);
108        ret
109    }
110
111    /// Get `H256` value from a specific offset in memory.
112    #[must_use]
113    pub fn get_h256(&self, offset: usize) -> H256 {
114        let mut ret = [0; 32];
115
116        let data_len = self.data.len();
117        if offset >= data_len {
118            return H256(ret);
119        }
120        let available_bytes = data_len - offset;
121        let count = 32.min(available_bytes);
122        ret[..count].copy_from_slice(&self.data[offset..offset + count]);
123
124        H256(ret)
125    }
126
127    /// Set memory region at given offset. The offset and value is considered
128    /// untrusted.
129    ///
130    /// # Errors
131    /// Return `ExitFatal::NotSupported` if `offset + target_size` is out of memory limit or overflow.
132    pub fn set(
133        &mut self,
134        offset: usize,
135        value: &[u8],
136        target_size: usize,
137    ) -> Result<(), ExitFatal> {
138        if target_size == 0 {
139            return Ok(());
140        }
141
142        let end_offset = match offset.checked_add(target_size) {
143            Some(pos) if pos <= self.limit => pos,
144            _ => return Err(ExitFatal::NotSupported),
145        };
146
147        if self.data.len() < end_offset {
148            self.data.resize(end_offset, 0);
149        }
150
151        let copy_len = min(value.len(), target_size);
152        let dest_slice = &mut self.data[offset..end_offset];
153        if copy_len > 0 {
154            dest_slice[..copy_len].copy_from_slice(&value[..copy_len]);
155        }
156
157        if target_size > copy_len {
158            dest_slice[copy_len..].fill(0);
159        }
160
161        Ok(())
162    }
163
164    /// Copy memory region form `src` to `dst` with length.
165    /// `copy_within` uses `memmove` to avoid `DoS` attacks.
166    ///
167    /// # Errors
168    /// Return `ExitFatal::Other`:
169    /// - `OverflowOnCopy` if `offset + length` is overflow
170    /// - `OutOfGasOnCopy` if `offst_length` out of memory limit
171    pub fn copy(
172        &mut self,
173        src_offset: usize,
174        dst_offset: usize,
175        length: usize,
176    ) -> Result<(), ExitFatal> {
177        // If length is zero - do nothing
178        if length == 0 {
179            return Ok(());
180        }
181
182        // Get maximum offset
183        let offset = core::cmp::max(src_offset, dst_offset);
184        let offset_length = offset
185            .checked_add(length)
186            .ok_or_else(|| ExitFatal::Other(Cow::from("OverflowOnCopy")))?;
187        if offset_length > self.limit {
188            return Err(ExitFatal::Other(Cow::from("OutOfGasOnCopy")));
189        }
190
191        // Resize data memory
192        if self.data.len() < offset_length {
193            self.data.resize(offset_length, 0);
194        }
195
196        self.data
197            .copy_within(src_offset..src_offset + length, dst_offset);
198        Ok(())
199    }
200
201    /// Copy `data` into the memory, for the given `length`.
202    ///
203    /// Copies `min(length, available_source_bytes)` from the source `data`
204    /// starting at `data_offset`, into `self.data` starting at `memory_offset`.
205    /// If `length` is greater than the number of bytes copied from source, the
206    /// remaining bytes in the destination range (up to `length`) are filled with zeros.
207    ///
208    /// # Errors
209    /// Returns `ExitFatal::NotSupported` if the destination range `memory_offset..memory_offset + length`
210    /// exceeds the memory limit or causes usize overflow.
211    pub fn copy_data(
212        &mut self,
213        memory_offset: usize,
214        data_offset: U256,
215        length: usize,
216        data: &[u8],
217    ) -> Result<(), ExitFatal> {
218        // 1. Handle zero length copy (no-op)
219        if length == 0 {
220            return Ok(());
221        }
222
223        // 2. Check destination bounds and calculate end offset
224        let dest_end_offset = match memory_offset.checked_add(length) {
225            Some(pos) if pos <= self.limit => pos,
226            _ => return Err(ExitFatal::NotSupported), // Error if overflow or exceeds limit
227        };
228
229        // 3. Ensure destination buffer (`self.data`) is large enough
230        //    Resize before taking mutable slices.
231        if self.data.len() < dest_end_offset {
232            self.data.resize(dest_end_offset, 0);
233        }
234
235        // 4. Preparing the copy and padding directly into self.data
236        // Get the mutable slice of the exact destination region length
237        // This is safe because we resized self.data to at least `dest_end_offset`
238        let dest_slice = &mut self.data[memory_offset..dest_end_offset];
239
240        // 5. Check source bounds and rethink zero the data slice
241        if data_offset > USIZE_MAX {
242            dest_slice.fill(0);
243            return Ok(());
244        }
245        let data_offset = data_offset.as_usize();
246        if data_offset > data.len() {
247            dest_slice.fill(0);
248            return Ok(());
249        }
250        // Calculate how many bytes are available in `data` from `data_offset`
251        let actual_len = data.len() - data_offset;
252        // Calculate copy length as the minimum of requested length and available length
253        let copy_len = min(actual_len, length);
254        // Copy data to `dest_slice`
255        if copy_len > 0 {
256            dest_slice[..copy_len].copy_from_slice(&data[data_offset..data_offset + copy_len]);
257        }
258        if length > copy_len {
259            dest_slice[copy_len..].fill(0);
260        }
261        Ok(())
262    }
263}
264
265/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned.
266#[inline]
267fn next_multiple_of_32(x: usize) -> Option<usize> {
268    let r = x.bitand(31).not().wrapping_add(1).bitand(31);
269    x.checked_add(r)
270}
271
272#[cfg(test)]
273mod tests {
274    use super::next_multiple_of_32;
275
276    #[test]
277    fn test_next_multiple_of_32() {
278        // next_multiple_of_32 returns x when it is a multiple of 32
279        for i in 0..32 {
280            let x = i * 32;
281            assert_eq!(Some(x), next_multiple_of_32(x));
282        }
283
284        // next_multiple_of_32 rounds up to the nearest multiple of 32 when `x % 32 != 0`
285        for x in 0..1024 {
286            if x % 32 == 0 {
287                continue;
288            }
289            let next_multiple = x + 32 - (x % 32);
290            assert_eq!(Some(next_multiple), next_multiple_of_32(x));
291        }
292
293        // next_multiple_of_32 returns None when the next multiple of 32 is too big
294        let last_multiple_of_32 = usize::MAX & !31;
295        for i in 0..63 {
296            let x = usize::MAX - i;
297            if x > last_multiple_of_32 {
298                assert_eq!(None, next_multiple_of_32(x));
299            } else {
300                assert_eq!(Some(last_multiple_of_32), next_multiple_of_32(x));
301            }
302        }
303    }
304}