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: OnceLock<Bytecode> = OnceLock::new();
109        DEFAULT
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    /// # Safety
207    ///
208    /// `bytecode` must satisfy the same padding invariants produced by
209    /// `analyze_legacy`. In particular, execution must never cause the
210    /// interpreter to read past the backing allocation when decoding opcode
211    /// immediates (`PUSH1`–`PUSH32` via `read_slice`, and `DUPN`/`SWAPN`/
212    /// `EXCHANGE` via `read_u8`).
213    ///
214    /// [`Bytecode::new_legacy`] handles this automatically.
215    /// This constructor is only for restoring trusted, previously analyzed
216    /// bytecode (e.g., from database storage) where the padding was already
217    /// applied.
218    ///
219    /// Violating this causes undefined behavior during execution due to
220    /// out-of-bounds reads from raw pointers.
221    ///
222    /// # Panics
223    ///
224    /// * If `original_len` is greater than `bytecode.len()`
225    /// * If jump table length is less than `original_len`
226    /// * If bytecode is empty
227    #[inline]
228    pub unsafe fn new_analyzed(
229        bytecode: Bytes,
230        original_len: usize,
231        jump_table: JumpTable,
232    ) -> Self {
233        assert!(
234            original_len <= bytecode.len(),
235            "original_len is greater than bytecode length"
236        );
237        assert!(
238            original_len <= jump_table.len(),
239            "jump table length is less than original length"
240        );
241        assert!(!bytecode.is_empty(), "bytecode cannot be empty");
242        Self(Arc::new(BytecodeInner {
243            kind: BytecodeKind::LegacyAnalyzed,
244            bytecode,
245            original_len,
246            jump_table,
247            hash: OnceLock::new(),
248        }))
249    }
250
251    /// Returns the kind of bytecode.
252    #[inline]
253    pub fn kind(&self) -> BytecodeKind {
254        self.0.kind
255    }
256
257    /// Returns `true` if bytecode is legacy.
258    #[inline]
259    pub fn is_legacy(&self) -> bool {
260        self.kind() == BytecodeKind::LegacyAnalyzed
261    }
262
263    /// Returns `true` if bytecode is EIP-7702.
264    #[inline]
265    pub fn is_eip7702(&self) -> bool {
266        self.kind() == BytecodeKind::Eip7702
267    }
268
269    /// Returns the EIP-7702 delegated address if this is EIP-7702 bytecode.
270    #[inline]
271    pub fn eip7702_address(&self) -> Option<Address> {
272        if self.is_eip7702() {
273            Some(Address::from_slice(&self.0.bytecode[3..23]))
274        } else {
275            None
276        }
277    }
278
279    /// Returns jump table if bytecode is legacy analyzed.
280    #[inline]
281    pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
282        if self.is_legacy() {
283            Some(&self.0.jump_table)
284        } else {
285            None
286        }
287    }
288
289    /// Calculates or returns cached hash of the bytecode.
290    #[inline]
291    pub fn hash_slow(&self) -> B256 {
292        *self
293            .0
294            .hash
295            .get_or_init(|| keccak256(self.original_byte_slice()))
296    }
297
298    /// Returns a reference to the bytecode bytes.
299    ///
300    /// For legacy bytecode, this includes padding. For EIP-7702, this is the raw bytes.
301    #[inline]
302    pub fn bytecode(&self) -> &Bytes {
303        &self.0.bytecode
304    }
305
306    /// Pointer to the bytecode bytes.
307    #[inline]
308    pub fn bytecode_ptr(&self) -> *const u8 {
309        self.0.bytecode.as_ptr()
310    }
311
312    /// Returns a clone of the bytecode bytes.
313    #[inline]
314    pub fn bytes(&self) -> Bytes {
315        self.0.bytecode.clone()
316    }
317
318    /// Returns a reference to the bytecode bytes.
319    #[inline]
320    pub fn bytes_ref(&self) -> &Bytes {
321        &self.0.bytecode
322    }
323
324    /// Returns the bytecode as a slice.
325    #[inline]
326    pub fn bytes_slice(&self) -> &[u8] {
327        &self.0.bytecode
328    }
329
330    /// Returns the original bytecode without padding.
331    #[inline]
332    pub fn original_bytes(&self) -> Bytes {
333        self.0.bytecode.slice(..self.0.original_len)
334    }
335
336    /// Returns the original bytecode as a byte slice without padding.
337    #[inline]
338    pub fn original_byte_slice(&self) -> &[u8] {
339        &self.0.bytecode[..self.0.original_len]
340    }
341
342    /// Returns the length of the original bytes (without padding).
343    #[inline]
344    pub fn len(&self) -> usize {
345        self.0.original_len
346    }
347
348    /// Returns whether the bytecode is empty.
349    #[inline]
350    pub fn is_empty(&self) -> bool {
351        self.0.original_len == 0
352    }
353
354    /// Returns an iterator over the opcodes in this bytecode, skipping immediates.
355    #[inline]
356    pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
357        crate::BytecodeIterator::new(self)
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use crate::{eip7702::Eip7702DecodeError, opcode};
365    use bitvec::{bitvec, order::Lsb0};
366    use primitives::bytes;
367
368    #[test]
369    fn test_new_empty() {
370        for bytecode in [
371            Bytecode::default(),
372            Bytecode::new(),
373            Bytecode::new().clone(),
374            Bytecode::new_legacy(Bytes::new()),
375        ] {
376            assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
377            assert_eq!(bytecode.len(), 0);
378            assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
379        }
380    }
381
382    #[test]
383    fn test_new_analyzed() {
384        let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
385        let bytecode = Bytecode::new_legacy(raw);
386        // SAFETY: bytecode was produced by `new_legacy` which pads correctly.
387        let _ = unsafe {
388            Bytecode::new_analyzed(
389                bytecode.bytecode().clone(),
390                bytecode.len(),
391                bytecode.legacy_jump_table().unwrap().clone(),
392            )
393        };
394    }
395
396    #[test]
397    #[should_panic(expected = "original_len is greater than bytecode length")]
398    fn test_panic_on_large_original_len() {
399        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
400        // SAFETY: testing the panic, not execution safety.
401        let _ = unsafe {
402            Bytecode::new_analyzed(
403                bytecode.bytecode().clone(),
404                100,
405                bytecode.legacy_jump_table().unwrap().clone(),
406            )
407        };
408    }
409
410    #[test]
411    #[should_panic(expected = "jump table length is less than original length")]
412    fn test_panic_on_short_jump_table() {
413        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
414        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
415        // SAFETY: testing the panic, not execution safety.
416        let _ = unsafe {
417            Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table)
418        };
419    }
420
421    #[test]
422    #[should_panic(expected = "bytecode cannot be empty")]
423    fn test_panic_on_empty_bytecode() {
424        let bytecode = Bytes::from_static(&[]);
425        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
426        // SAFETY: testing the panic, not execution safety.
427        let _ = unsafe { Bytecode::new_analyzed(bytecode, 0, jump_table) };
428    }
429
430    #[test]
431    fn eip7702_sanity_decode() {
432        let raw = bytes!("ef01deadbeef");
433        assert_eq!(
434            Bytecode::new_eip7702_raw(raw),
435            Err(Eip7702DecodeError::InvalidLength)
436        );
437
438        let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
439        assert_eq!(
440            Bytecode::new_eip7702_raw(raw),
441            Err(Eip7702DecodeError::UnsupportedVersion)
442        );
443
444        let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
445        let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
446        assert!(bytecode.is_eip7702());
447        assert_eq!(
448            bytecode.eip7702_address(),
449            Some(Address::from_slice(&raw[3..]))
450        );
451        assert_eq!(bytecode.original_bytes(), raw);
452    }
453
454    #[test]
455    fn eip7702_from_address() {
456        let address = Address::new([0x01; 20]);
457        let bytecode = Bytecode::new_eip7702(address);
458        assert_eq!(bytecode.eip7702_address(), Some(address));
459        assert_eq!(
460            bytecode.original_bytes(),
461            bytes!("ef01000101010101010101010101010101010101010101")
462        );
463    }
464}