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    #[inline]
35    fn default() -> Self {
36        Self::new(Bytecode::default())
37    }
38}
39
40impl ExtBytecode {
41    /// Create new extended bytecode and set the instruction pointer to the start of the bytecode.
42    #[inline]
43    pub fn new(base: Bytecode) -> Self {
44        let instruction_pointer = base.bytecode_ptr();
45        Self {
46            base,
47            instruction_pointer,
48            bytecode_hash: None,
49            action: None,
50            previous_pointer: None,
51        }
52    }
53
54    /// Creates new `ExtBytecode` with the given hash.
55    pub fn new_with_hash(base: Bytecode, hash: B256) -> Self {
56        let instruction_pointer = base.bytecode_ptr();
57        Self {
58            base,
59            instruction_pointer,
60            bytecode_hash: Some(hash),
61            action: None,
62            previous_pointer: None,
63        }
64    }
65
66    /// Regenerates the bytecode hash.
67    pub fn regenerate_hash(&mut self) -> B256 {
68        let hash = self.base.hash_slow();
69        self.bytecode_hash = Some(hash);
70        hash
71    }
72
73    /// Returns the bytecode hash.
74    pub fn hash(&mut self) -> Option<B256> {
75        self.bytecode_hash
76    }
77}
78
79impl LoopControl for ExtBytecode {
80    #[inline]
81    fn is_end(&self) -> bool {
82        self.instruction_pointer.is_null()
83    }
84
85    #[inline]
86    fn revert_to_previous_pointer(&mut self) {
87        if let Some(previous_pointer) = self.previous_pointer {
88            self.instruction_pointer = previous_pointer;
89        }
90    }
91
92    #[inline]
93    fn set_action(&mut self, action: InterpreterAction) {
94        self.action = Some(action);
95        self.previous_pointer = Some(core::mem::replace(
96            &mut self.instruction_pointer,
97            ptr::null(),
98        ));
99    }
100
101    #[inline]
102    fn action(&mut self) -> &mut Option<InterpreterAction> {
103        &mut self.action
104    }
105}
106
107impl Jumps for ExtBytecode {
108    #[inline]
109    fn relative_jump(&mut self, offset: isize) {
110        self.instruction_pointer = unsafe { self.instruction_pointer.offset(offset) };
111    }
112
113    #[inline]
114    fn absolute_jump(&mut self, offset: usize) {
115        self.instruction_pointer = unsafe { self.base.bytes_ref().as_ptr().add(offset) };
116    }
117
118    #[inline]
119    fn is_valid_legacy_jump(&mut self, offset: usize) -> bool {
120        self.base
121            .legacy_jump_table()
122            .expect("Panic if not legacy")
123            .is_valid(offset)
124    }
125
126    #[inline]
127    fn opcode(&self) -> u8 {
128        // SAFETY: `instruction_pointer` always point to bytecode.
129        unsafe { *self.instruction_pointer }
130    }
131
132    #[inline]
133    fn pc(&self) -> usize {
134        // SAFETY: `instruction_pointer` should be at an offset from the start of the bytes.
135        // In practice this is always true unless a caller modifies the `instruction_pointer` field manually.
136        unsafe {
137            self.instruction_pointer
138                .offset_from(self.base.bytes_ref().as_ptr()) as usize
139        }
140    }
141}
142
143impl Immediates for ExtBytecode {
144    #[inline]
145    fn read_u16(&self) -> u16 {
146        unsafe { read_u16(self.instruction_pointer) }
147    }
148
149    #[inline]
150    fn read_u8(&self) -> u8 {
151        unsafe { *self.instruction_pointer }
152    }
153
154    #[inline]
155    fn read_slice(&self, len: usize) -> &[u8] {
156        unsafe { core::slice::from_raw_parts(self.instruction_pointer, len) }
157    }
158
159    #[inline]
160    fn read_offset_u16(&self, offset: isize) -> u16 {
161        unsafe {
162            read_u16(
163                self.instruction_pointer
164                    // Offset for max_index that is one byte
165                    .offset(offset),
166            )
167        }
168    }
169}
170
171impl LegacyBytecode for ExtBytecode {
172    fn bytecode_len(&self) -> usize {
173        self.base.len()
174    }
175
176    fn bytecode_slice(&self) -> &[u8] {
177        self.base.original_byte_slice()
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use primitives::Bytes;
185
186    #[test]
187    fn test_with_hash_constructor() {
188        let bytecode = Bytecode::new_raw(Bytes::from(&[0x60, 0x00][..]));
189        let hash = bytecode.hash_slow();
190        let ext_bytecode = ExtBytecode::new_with_hash(bytecode.clone(), hash);
191        assert_eq!(ext_bytecode.bytecode_hash, Some(hash));
192    }
193}