revm_interpreter/interpreter/
ext_bytecode.rs

1use super::{Immediates, Jumps, LegacyBytecode};
2use crate::{interpreter_types::LoopControl, InterpreterAction};
3use bytecode::{utils::read_u16, Bytecode};
4use core::{ops::Deref, ptr};
5use primitives::B256;
6
7#[cfg(feature = "serde")]
8mod serde;
9
10/// Extended bytecode structure that wraps base bytecode with additional execution metadata.
11#[derive(Debug)]
12pub struct ExtBytecode {
13    bytecode_hash: Option<B256>,
14    /// Actions that the EVM should do. It contains return value of the Interpreter or inputs for `CALL` or `CREATE` instructions.
15    /// For `RETURN` or `REVERT` instructions it contains the result of the instruction.
16    pub action: Option<InterpreterAction>,
17    /// The base bytecode.
18    base: Bytecode,
19    /// The previous instruction pointer.
20    previous_pointer: Option<*const u8>,
21    /// The current instruction pointer.
22    instruction_pointer: *const u8,
23}
24
25impl Deref for ExtBytecode {
26    type Target = Bytecode;
27
28    fn deref(&self) -> &Self::Target {
29        &self.base
30    }
31}
32
33impl Default for ExtBytecode {
34    fn default() -> Self {
35        Self::new(Bytecode::default())
36    }
37}
38
39impl ExtBytecode {
40    /// Create new extended bytecode and set the instruction pointer to the start of the bytecode.
41    pub fn new(base: Bytecode) -> Self {
42        let instruction_pointer = base.bytecode_ptr();
43        Self {
44            base,
45            instruction_pointer,
46            bytecode_hash: None,
47            action: None,
48            previous_pointer: None,
49        }
50    }
51
52    /// Creates new `ExtBytecode` with the given hash.
53    pub fn new_with_hash(base: Bytecode, hash: B256) -> Self {
54        let instruction_pointer = base.bytecode_ptr();
55        Self {
56            base,
57            instruction_pointer,
58            bytecode_hash: Some(hash),
59            action: None,
60            previous_pointer: None,
61        }
62    }
63
64    /// Regenerates the bytecode hash.
65    pub fn regenerate_hash(&mut self) -> B256 {
66        let hash = self.base.hash_slow();
67        self.bytecode_hash = Some(hash);
68        hash
69    }
70
71    /// Returns the bytecode hash.
72    pub fn hash(&mut self) -> Option<B256> {
73        self.bytecode_hash
74    }
75}
76
77impl LoopControl for ExtBytecode {
78    #[inline]
79    fn is_end(&self) -> bool {
80        self.instruction_pointer.is_null()
81    }
82
83    #[inline]
84    fn revert_to_previous_pointer(&mut self) {
85        if let Some(previous_pointer) = self.previous_pointer {
86            self.instruction_pointer = previous_pointer;
87        }
88    }
89
90    #[inline]
91    fn set_action(&mut self, action: InterpreterAction) {
92        self.action = Some(action);
93        self.previous_pointer = Some(core::mem::replace(
94            &mut self.instruction_pointer,
95            ptr::null(),
96        ));
97    }
98
99    #[inline]
100    fn action(&mut self) -> &mut Option<InterpreterAction> {
101        &mut self.action
102    }
103}
104
105impl Jumps for ExtBytecode {
106    #[inline]
107    fn relative_jump(&mut self, offset: isize) {
108        self.instruction_pointer = unsafe { self.instruction_pointer.offset(offset) };
109    }
110
111    #[inline]
112    fn absolute_jump(&mut self, offset: usize) {
113        self.instruction_pointer = unsafe { self.base.bytes_ref().as_ptr().add(offset) };
114    }
115
116    #[inline]
117    fn is_valid_legacy_jump(&mut self, offset: usize) -> bool {
118        self.base
119            .legacy_jump_table()
120            .expect("Panic if not legacy")
121            .is_valid(offset)
122    }
123
124    #[inline]
125    fn opcode(&self) -> u8 {
126        // SAFETY: `instruction_pointer` always point to bytecode.
127        unsafe { *self.instruction_pointer }
128    }
129
130    #[inline]
131    fn pc(&self) -> usize {
132        // SAFETY: `instruction_pointer` should be at an offset from the start of the bytes.
133        // In practice this is always true unless a caller modifies the `instruction_pointer` field manually.
134        unsafe {
135            self.instruction_pointer
136                .offset_from(self.base.bytes_ref().as_ptr()) as usize
137        }
138    }
139}
140
141impl Immediates for ExtBytecode {
142    #[inline]
143    fn read_u16(&self) -> u16 {
144        unsafe { read_u16(self.instruction_pointer) }
145    }
146
147    #[inline]
148    fn read_u8(&self) -> u8 {
149        unsafe { *self.instruction_pointer }
150    }
151
152    #[inline]
153    fn read_slice(&self, len: usize) -> &[u8] {
154        unsafe { core::slice::from_raw_parts(self.instruction_pointer, len) }
155    }
156
157    #[inline]
158    fn read_offset_u16(&self, offset: isize) -> u16 {
159        unsafe {
160            read_u16(
161                self.instruction_pointer
162                    // Offset for max_index that is one byte
163                    .offset(offset),
164            )
165        }
166    }
167}
168
169impl LegacyBytecode for ExtBytecode {
170    fn bytecode_len(&self) -> usize {
171        self.base.len()
172    }
173
174    fn bytecode_slice(&self) -> &[u8] {
175        self.base.original_byte_slice()
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use primitives::Bytes;
183
184    #[test]
185    fn test_with_hash_constructor() {
186        let bytecode = Bytecode::new_raw(Bytes::from(&[0x60, 0x00][..]));
187        let hash = bytecode.hash_slow();
188        let ext_bytecode = ExtBytecode::new_with_hash(bytecode.clone(), hash);
189        assert_eq!(ext_bytecode.bytecode_hash, Some(hash));
190    }
191}