Skip to main content

revm_bytecode/
utils.rs

1//! Various utilities for the bytecode
2
3/// Reads a big-endian `i16` from a `u8` pointer.
4///
5/// # Safety
6///
7/// The pointer must point to at least 2 bytes.
8#[inline]
9pub const unsafe fn read_i16(ptr: *const u8) -> i16 {
10    read_u16(ptr) as i16
11}
12
13/// Reads a big-endian `u16` from a `u8` pointer.
14///
15/// # Safety
16///
17/// The pointer must point to at least 2 bytes.
18#[inline]
19pub const unsafe fn read_u16(ptr: *const u8) -> u16 {
20    u16::from_be_bytes(unsafe { ptr.cast::<[u8; 2]>().read() })
21}
22
23/// Bytecode test utilities
24#[cfg(test)]
25pub mod test {
26    use crate::opcode;
27    use anyhow::Result;
28    use primitives::U256;
29    use rand::RngExt;
30    use std::{vec, vec::Vec};
31
32    /// Constructs bytecode for inserting input into memory
33    pub fn build_memory_input_opcodes(start_offset: U256, input: &[u8]) -> Result<Vec<u8>> {
34        let mut opcodes = vec![];
35        let mut current_offset = start_offset;
36
37        // Iterate for each 32 bytes to prepend PUSH* and append MSTORE opcodes
38        let offset_step = U256::from(32);
39        for bytes in input.chunks(32) {
40            // Push the input value
41            build_push_bytes(bytes, &mut opcodes);
42
43            // Push the memory offset
44            build_push_u256(current_offset, &mut opcodes);
45
46            // Call MSTORE
47            opcodes.push(opcode::MSTORE);
48
49            // Increase the memory offset
50            current_offset += offset_step;
51        }
52
53        Ok(opcodes)
54    }
55
56    // Constructs a PUSH* instruction for an Uint256
57    fn build_push_u256(value: U256, opcodes: &mut Vec<u8>) {
58        let bytes = value.to_be_bytes_trimmed_vec();
59        build_push_bytes(&bytes, opcodes);
60    }
61
62    // Constructs a PUSH* instruction for the value of byte size is not greater than 32
63    fn build_push_bytes(bytes: &[u8], opcodes: &mut Vec<u8>) {
64        let len = bytes.len();
65        assert!(len <= 32);
66
67        let push_opcode = opcode::PUSH0 + len as u8;
68        opcodes.push(push_opcode);
69
70        opcodes.extend_from_slice(bytes);
71    }
72
73    #[test]
74    fn test_build_memory_input_opcodes() {
75        let mut rng = rand::rng();
76
77        // make the memory offset as 4 bytes for test
78        let start_offset = rng.random_range(0x0100_0000..=(u32::MAX - 100));
79        let mut current_offset = start_offset;
80
81        let mut all_inputs = vec![];
82        let mut expected_opcodes = vec![];
83
84        // Generate 32 bytes input array
85        let input_arr: [[u8; 32]; 3] = rng.random();
86        for input in input_arr {
87            all_inputs.extend(input);
88
89            expected_opcodes.push(opcode::PUSH32);
90            expected_opcodes.extend(input);
91            expected_opcodes.push(opcode::PUSH4);
92            expected_opcodes.extend(current_offset.to_be_bytes());
93            expected_opcodes.push(opcode::MSTORE);
94
95            current_offset += 32;
96        }
97
98        let last_input: [u8; 15] = rng.random();
99        {
100            all_inputs.extend(last_input);
101
102            expected_opcodes.push(opcode::PUSH15);
103            expected_opcodes.extend(last_input);
104            expected_opcodes.push(opcode::PUSH4);
105            expected_opcodes.extend(current_offset.to_be_bytes());
106
107            expected_opcodes.push(opcode::MSTORE);
108        }
109
110        let opcodes = build_memory_input_opcodes(U256::from(start_offset), &all_inputs).unwrap();
111        assert_eq!(opcodes, expected_opcodes);
112    }
113}