Skip to main content

revm_bytecode/bytecode/
mod.rs

1//! Module that contains the bytecode struct with all variants supported by Ethereum mainnet.
2//!
3//! Those are:
4//! - Legacy bytecode with jump table analysis
5//! - EIP-7702 bytecode, introduced in Prague and contains address to delegated account
6
7#[cfg(feature = "serde")]
8mod serde_impl;
9
10use crate::{
11    eip7702::{Eip7702DecodeError, EIP7702_MAGIC_BYTES, EIP7702_VERSION},
12    legacy::analyze_legacy,
13    opcode, BytecodeDecodeError, JumpTable,
14};
15use primitives::{
16    alloy_primitives::Sealable, keccak256, Address, Bytes, OnceLock, B256, KECCAK_EMPTY,
17};
18use std::sync::Arc;
19
20/// Ethereum EVM bytecode.
21#[derive(Clone, Debug)]
22pub struct Bytecode(Arc<BytecodeInner>);
23
24/// Inner bytecode representation.
25///
26/// This struct is flattened to avoid nested allocations. The `kind` field determines
27/// how the bytecode should be interpreted.
28#[derive(Debug)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30struct BytecodeInner {
31    /// The kind of bytecode (Legacy or EIP-7702).
32    kind: BytecodeKind,
33    /// The bytecode bytes.
34    ///
35    /// For legacy bytecode, this may be padded with zeros at the end.
36    /// For EIP-7702 bytecode, this is exactly 23 bytes.
37    bytecode: Bytes,
38    /// The original length of the bytecode before padding.
39    ///
40    /// For EIP-7702 bytecode, this is always 23.
41    original_len: usize,
42    /// The jump table for legacy bytecode. Empty for EIP-7702.
43    jump_table: JumpTable,
44    /// Cached hash of the original bytecode.
45    #[cfg_attr(feature = "serde", serde(skip, default))]
46    hash: OnceLock<B256>,
47}
48
49/// The kind of bytecode.
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub enum BytecodeKind {
53    /// Legacy analyzed bytecode with jump table.
54    #[default]
55    LegacyAnalyzed,
56    /// EIP-7702 delegated bytecode.
57    Eip7702,
58}
59
60impl Default for Bytecode {
61    #[inline]
62    fn default() -> Self {
63        Self::new()
64    }
65}
66
67impl PartialEq for Bytecode {
68    #[inline]
69    fn eq(&self, other: &Self) -> bool {
70        self.original_byte_slice() == other.original_byte_slice()
71    }
72}
73
74impl Eq for Bytecode {}
75
76impl core::hash::Hash for Bytecode {
77    #[inline]
78    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
79        self.original_byte_slice().hash(state);
80    }
81}
82
83impl PartialOrd for Bytecode {
84    #[inline]
85    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
86        Some(self.cmp(other))
87    }
88}
89
90impl Ord for Bytecode {
91    #[inline]
92    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
93        self.original_byte_slice().cmp(other.original_byte_slice())
94    }
95}
96
97impl Sealable for Bytecode {
98    #[inline]
99    fn hash_slow(&self) -> B256 {
100        self.hash_slow()
101    }
102}
103
104impl Bytecode {
105    /// Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode.
106    #[inline]
107    pub fn new() -> Self {
108        static DEFAULT_BYTECODE: OnceLock<Bytecode> = OnceLock::new();
109        DEFAULT_BYTECODE
110            .get_or_init(|| {
111                Self(Arc::new(BytecodeInner {
112                    kind: BytecodeKind::LegacyAnalyzed,
113                    bytecode: Bytes::from_static(&[opcode::STOP]),
114                    original_len: 0,
115                    jump_table: JumpTable::default(),
116                    hash: {
117                        let hash = OnceLock::new();
118                        let _ = hash.set(KECCAK_EMPTY);
119                        hash
120                    },
121                }))
122            })
123            .clone()
124    }
125
126    /// Creates a new legacy [`Bytecode`] by analyzing raw bytes.
127    #[inline]
128    pub fn new_legacy(raw: Bytes) -> Self {
129        if raw.is_empty() {
130            return Self::new();
131        }
132
133        let original_len = raw.len();
134        let (jump_table, bytecode) = analyze_legacy(raw);
135        Self(Arc::new(BytecodeInner {
136            kind: BytecodeKind::LegacyAnalyzed,
137            original_len,
138            bytecode,
139            jump_table,
140            hash: OnceLock::new(),
141        }))
142    }
143
144    /// Creates a new raw [`Bytecode`].
145    ///
146    /// # Panics
147    ///
148    /// Panics if bytecode is in incorrect format. If you want to handle errors use [`Self::new_raw_checked`].
149    #[inline]
150    pub fn new_raw(bytecode: Bytes) -> Self {
151        Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
152    }
153
154    /// Creates a new EIP-7702 [`Bytecode`] from [`Address`].
155    #[inline]
156    pub fn new_eip7702(address: Address) -> Self {
157        let raw: Bytes = [EIP7702_MAGIC_BYTES, &[EIP7702_VERSION], &address[..]]
158            .concat()
159            .into();
160        Self(Arc::new(BytecodeInner {
161            kind: BytecodeKind::Eip7702,
162            original_len: raw.len(),
163            bytecode: raw,
164            jump_table: JumpTable::default(),
165            hash: OnceLock::new(),
166        }))
167    }
168
169    /// Creates a new raw [`Bytecode`].
170    ///
171    /// Returns an error on incorrect bytecode format.
172    #[inline]
173    pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
174        if bytes.starts_with(EIP7702_MAGIC_BYTES) {
175            Self::new_eip7702_raw(bytes).map_err(Into::into)
176        } else {
177            Ok(Self::new_legacy(bytes))
178        }
179    }
180
181    /// Creates a new EIP-7702 [`Bytecode`] from raw bytes.
182    ///
183    /// Returns an error if the bytes are not valid EIP-7702 bytecode.
184    #[inline]
185    pub fn new_eip7702_raw(bytes: Bytes) -> Result<Self, Eip7702DecodeError> {
186        if bytes.len() != 23 {
187            return Err(Eip7702DecodeError::InvalidLength);
188        }
189        if !bytes.starts_with(EIP7702_MAGIC_BYTES) {
190            return Err(Eip7702DecodeError::InvalidMagic);
191        }
192        if bytes[2] != EIP7702_VERSION {
193            return Err(Eip7702DecodeError::UnsupportedVersion);
194        }
195        Ok(Self(Arc::new(BytecodeInner {
196            kind: BytecodeKind::Eip7702,
197            original_len: bytes.len(),
198            bytecode: bytes,
199            jump_table: JumpTable::default(),
200            hash: OnceLock::new(),
201        })))
202    }
203
204    /// Create new checked bytecode from pre-analyzed components.
205    ///
206    /// # Panics
207    ///
208    /// * If `original_len` is greater than `bytecode.len()`
209    /// * If jump table length is less than `original_len`
210    /// * If bytecode is empty
211    #[inline]
212    pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
213        assert!(
214            original_len <= bytecode.len(),
215            "original_len is greater than bytecode length"
216        );
217        assert!(
218            original_len <= jump_table.len(),
219            "jump table length is less than original length"
220        );
221        assert!(!bytecode.is_empty(), "bytecode cannot be empty");
222        Self(Arc::new(BytecodeInner {
223            kind: BytecodeKind::LegacyAnalyzed,
224            bytecode,
225            original_len,
226            jump_table,
227            hash: OnceLock::new(),
228        }))
229    }
230
231    /// Returns the kind of bytecode.
232    #[inline]
233    pub fn kind(&self) -> BytecodeKind {
234        self.0.kind
235    }
236
237    /// Returns `true` if bytecode is legacy.
238    #[inline]
239    pub fn is_legacy(&self) -> bool {
240        self.kind() == BytecodeKind::LegacyAnalyzed
241    }
242
243    /// Returns `true` if bytecode is EIP-7702.
244    #[inline]
245    pub fn is_eip7702(&self) -> bool {
246        self.kind() == BytecodeKind::Eip7702
247    }
248
249    /// Returns the EIP-7702 delegated address if this is EIP-7702 bytecode.
250    #[inline]
251    pub fn eip7702_address(&self) -> Option<Address> {
252        if self.is_eip7702() {
253            Some(Address::from_slice(&self.0.bytecode[3..23]))
254        } else {
255            None
256        }
257    }
258
259    /// Returns jump table if bytecode is legacy analyzed.
260    #[inline]
261    pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
262        if self.is_legacy() {
263            Some(&self.0.jump_table)
264        } else {
265            None
266        }
267    }
268
269    /// Calculates or returns cached hash of the bytecode.
270    #[inline]
271    pub fn hash_slow(&self) -> B256 {
272        *self
273            .0
274            .hash
275            .get_or_init(|| keccak256(self.original_byte_slice()))
276    }
277
278    /// Returns a reference to the bytecode bytes.
279    ///
280    /// For legacy bytecode, this includes padding. For EIP-7702, this is the raw bytes.
281    #[inline]
282    pub fn bytecode(&self) -> &Bytes {
283        &self.0.bytecode
284    }
285
286    /// Pointer to the bytecode bytes.
287    #[inline]
288    pub fn bytecode_ptr(&self) -> *const u8 {
289        self.0.bytecode.as_ptr()
290    }
291
292    /// Returns a clone of the bytecode bytes.
293    #[inline]
294    pub fn bytes(&self) -> Bytes {
295        self.0.bytecode.clone()
296    }
297
298    /// Returns a reference to the bytecode bytes.
299    #[inline]
300    pub fn bytes_ref(&self) -> &Bytes {
301        &self.0.bytecode
302    }
303
304    /// Returns the bytecode as a slice.
305    #[inline]
306    pub fn bytes_slice(&self) -> &[u8] {
307        &self.0.bytecode
308    }
309
310    /// Returns the original bytecode without padding.
311    #[inline]
312    pub fn original_bytes(&self) -> Bytes {
313        self.0.bytecode.slice(..self.0.original_len)
314    }
315
316    /// Returns the original bytecode as a byte slice without padding.
317    #[inline]
318    pub fn original_byte_slice(&self) -> &[u8] {
319        &self.0.bytecode[..self.0.original_len]
320    }
321
322    /// Returns the length of the original bytes (without padding).
323    #[inline]
324    pub fn len(&self) -> usize {
325        self.0.original_len
326    }
327
328    /// Returns whether the bytecode is empty.
329    #[inline]
330    pub fn is_empty(&self) -> bool {
331        self.0.original_len == 0
332    }
333
334    /// Returns an iterator over the opcodes in this bytecode, skipping immediates.
335    #[inline]
336    pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
337        crate::BytecodeIterator::new(self)
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344    use crate::{eip7702::Eip7702DecodeError, opcode};
345    use bitvec::{bitvec, order::Lsb0};
346    use primitives::bytes;
347
348    #[test]
349    fn test_new_empty() {
350        for bytecode in [
351            Bytecode::default(),
352            Bytecode::new(),
353            Bytecode::new().clone(),
354            Bytecode::new_legacy(Bytes::new()),
355        ] {
356            assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
357            assert_eq!(bytecode.len(), 0);
358            assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
359        }
360    }
361
362    #[test]
363    fn test_new_analyzed() {
364        let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
365        let bytecode = Bytecode::new_legacy(raw);
366        let _ = Bytecode::new_analyzed(
367            bytecode.bytecode().clone(),
368            bytecode.len(),
369            bytecode.legacy_jump_table().unwrap().clone(),
370        );
371    }
372
373    #[test]
374    #[should_panic(expected = "original_len is greater than bytecode length")]
375    fn test_panic_on_large_original_len() {
376        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
377        let _ = Bytecode::new_analyzed(
378            bytecode.bytecode().clone(),
379            100,
380            bytecode.legacy_jump_table().unwrap().clone(),
381        );
382    }
383
384    #[test]
385    #[should_panic(expected = "jump table length is less than original length")]
386    fn test_panic_on_short_jump_table() {
387        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
388        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
389        let _ = Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table);
390    }
391
392    #[test]
393    #[should_panic(expected = "bytecode cannot be empty")]
394    fn test_panic_on_empty_bytecode() {
395        let bytecode = Bytes::from_static(&[]);
396        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
397        let _ = Bytecode::new_analyzed(bytecode, 0, jump_table);
398    }
399
400    #[test]
401    fn eip7702_sanity_decode() {
402        let raw = bytes!("ef01deadbeef");
403        assert_eq!(
404            Bytecode::new_eip7702_raw(raw),
405            Err(Eip7702DecodeError::InvalidLength)
406        );
407
408        let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
409        assert_eq!(
410            Bytecode::new_eip7702_raw(raw),
411            Err(Eip7702DecodeError::UnsupportedVersion)
412        );
413
414        let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
415        let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
416        assert!(bytecode.is_eip7702());
417        assert_eq!(
418            bytecode.eip7702_address(),
419            Some(Address::from_slice(&raw[3..]))
420        );
421        assert_eq!(bytecode.original_bytes(), raw);
422    }
423
424    #[test]
425    fn eip7702_from_address() {
426        let address = Address::new([0x01; 20]);
427        let bytecode = Bytecode::new_eip7702(address);
428        assert_eq!(bytecode.eip7702_address(), Some(address));
429        assert_eq!(
430            bytecode.original_bytes(),
431            bytes!("ef01000101010101010101010101010101010101010101")
432        );
433    }
434}