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 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 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::Rng;
30
31    /// Constructs bytecode for inserting input into memory
32    pub fn build_memory_input_opcodes(start_offset: U256, input: &[u8]) -> Result<Vec<u8>> {
33        let mut opcodes = vec![];
34        let mut current_offset = start_offset;
35
36        // Iterate for each 32 bytes to prepend PUSH* and append MSTORE opcodes
37        let offset_step = U256::from(32);
38        for bytes in input.chunks(32) {
39            // Push the input value
40            build_push_bytes(bytes, &mut opcodes);
41
42            // Push the memory offset
43            build_push_u256(current_offset, &mut opcodes);
44
45            // Call MSTORE
46            opcodes.push(opcode::MSTORE);
47
48            // Increase the memory offset
49            current_offset += offset_step;
50        }
51
52        Ok(opcodes)
53    }
54
55    // Constructs a PUSH* instruction for an Uint256
56    fn build_push_u256(value: U256, opcodes: &mut Vec<u8>) {
57        let bytes = value.to_be_bytes_trimmed_vec();
58        build_push_bytes(&bytes, opcodes);
59    }
60
61    // Constructs a PUSH* instruction for the value of byte size is not greater than 32
62    fn build_push_bytes(bytes: &[u8], opcodes: &mut Vec<u8>) {
63        let len = bytes.len();
64        assert!(len <= 32);
65
66        let push_opcode = opcode::PUSH0 + len as u8;
67        opcodes.push(push_opcode);
68
69        opcodes.extend_from_slice(bytes);
70    }
71
72    #[test]
73    fn test_build_memory_input_opcodes() {
74        let mut rng = rand::rng();
75
76        // make the memory offset as 4 bytes for test
77        let start_offset = rng.random_range(0x0100_0000..=(u32::MAX - 100));
78        let mut current_offset = start_offset;
79
80        let mut all_inputs = vec![];
81        let mut expected_opcodes = vec![];
82
83        // Generate 32 bytes input array
84        let input_arr: [[u8; 32]; 3] = rng.random();
85        for input in input_arr {
86            all_inputs.extend(input);
87
88            expected_opcodes.push(opcode::PUSH32);
89            expected_opcodes.extend(input);
90            expected_opcodes.push(opcode::PUSH4);
91            expected_opcodes.extend(current_offset.to_be_bytes());
92            expected_opcodes.push(opcode::MSTORE);
93
94            current_offset += 32;
95        }
96
97        let last_input: [u8; 15] = rng.random();
98        {
99            all_inputs.extend(last_input);
100
101            expected_opcodes.push(opcode::PUSH15);
102            expected_opcodes.extend(last_input);
103            expected_opcodes.push(opcode::PUSH4);
104            expected_opcodes.extend(current_offset.to_be_bytes());
105
106            expected_opcodes.push(opcode::MSTORE);
107        }
108
109        let opcodes = build_memory_input_opcodes(U256::from(start_offset), &all_inputs).unwrap();
110        assert_eq!(opcodes, expected_opcodes);
111    }
112}