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.kind()
94            .cmp(&other.kind())
95            .then_with(|| self.original_byte_slice().cmp(other.original_byte_slice()))
96    }
97}
98
99impl Sealable for Bytecode {
100    #[inline]
101    fn hash_slow(&self) -> B256 {
102        self.hash_slow()
103    }
104}
105
106impl Bytecode {
107    /// Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode.
108    #[inline]
109    pub fn new() -> Self {
110        static DEFAULT_BYTECODE: OnceLock<Bytecode> = OnceLock::new();
111        DEFAULT_BYTECODE
112            .get_or_init(|| {
113                Self(Arc::new(BytecodeInner {
114                    kind: BytecodeKind::LegacyAnalyzed,
115                    bytecode: Bytes::from_static(&[opcode::STOP]),
116                    original_len: 0,
117                    jump_table: JumpTable::default(),
118                    hash: {
119                        let hash = OnceLock::new();
120                        let _ = hash.set(KECCAK_EMPTY);
121                        hash
122                    },
123                }))
124            })
125            .clone()
126    }
127
128    /// Creates a new legacy [`Bytecode`] by analyzing raw bytes.
129    #[inline]
130    pub fn new_legacy(raw: Bytes) -> Self {
131        if raw.is_empty() {
132            return Self::new();
133        }
134
135        let original_len = raw.len();
136        let (jump_table, bytecode) = analyze_legacy(raw);
137        Self(Arc::new(BytecodeInner {
138            kind: BytecodeKind::LegacyAnalyzed,
139            original_len,
140            bytecode,
141            jump_table,
142            hash: OnceLock::new(),
143        }))
144    }
145
146    /// Creates a new raw [`Bytecode`].
147    ///
148    /// # Panics
149    ///
150    /// Panics if bytecode is in incorrect format. If you want to handle errors use [`Self::new_raw_checked`].
151    #[inline]
152    pub fn new_raw(bytecode: Bytes) -> Self {
153        Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
154    }
155
156    /// Creates a new EIP-7702 [`Bytecode`] from [`Address`].
157    #[inline]
158    pub fn new_eip7702(address: Address) -> Self {
159        let raw: Bytes = [EIP7702_MAGIC_BYTES, &[EIP7702_VERSION], &address[..]]
160            .concat()
161            .into();
162        Self(Arc::new(BytecodeInner {
163            kind: BytecodeKind::Eip7702,
164            original_len: raw.len(),
165            bytecode: raw,
166            jump_table: JumpTable::default(),
167            hash: OnceLock::new(),
168        }))
169    }
170
171    /// Creates a new raw [`Bytecode`].
172    ///
173    /// Returns an error on incorrect bytecode format.
174    #[inline]
175    pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
176        if bytes.starts_with(EIP7702_MAGIC_BYTES) {
177            Self::new_eip7702_raw(bytes).map_err(Into::into)
178        } else {
179            Ok(Self::new_legacy(bytes))
180        }
181    }
182
183    /// Creates a new EIP-7702 [`Bytecode`] from raw bytes.
184    ///
185    /// Returns an error if the bytes are not valid EIP-7702 bytecode.
186    #[inline]
187    pub fn new_eip7702_raw(bytes: Bytes) -> Result<Self, Eip7702DecodeError> {
188        if bytes.len() != 23 {
189            return Err(Eip7702DecodeError::InvalidLength);
190        }
191        if !bytes.starts_with(EIP7702_MAGIC_BYTES) {
192            return Err(Eip7702DecodeError::InvalidMagic);
193        }
194        if bytes[2] != EIP7702_VERSION {
195            return Err(Eip7702DecodeError::UnsupportedVersion);
196        }
197        Ok(Self(Arc::new(BytecodeInner {
198            kind: BytecodeKind::Eip7702,
199            original_len: bytes.len(),
200            bytecode: bytes,
201            jump_table: JumpTable::default(),
202            hash: OnceLock::new(),
203        })))
204    }
205
206    /// Create new checked bytecode from pre-analyzed components.
207    ///
208    /// # Panics
209    ///
210    /// * If `original_len` is greater than `bytecode.len()`
211    /// * If jump table length is less than `original_len`
212    /// * If bytecode is empty
213    #[inline]
214    pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
215        assert!(
216            original_len <= bytecode.len(),
217            "original_len is greater than bytecode length"
218        );
219        assert!(
220            original_len <= jump_table.len(),
221            "jump table length is less than original length"
222        );
223        assert!(!bytecode.is_empty(), "bytecode cannot be empty");
224        Self(Arc::new(BytecodeInner {
225            kind: BytecodeKind::LegacyAnalyzed,
226            bytecode,
227            original_len,
228            jump_table,
229            hash: OnceLock::new(),
230        }))
231    }
232
233    /// Returns the kind of bytecode.
234    #[inline]
235    pub fn kind(&self) -> BytecodeKind {
236        self.0.kind
237    }
238
239    /// Returns `true` if bytecode is legacy.
240    #[inline]
241    pub fn is_legacy(&self) -> bool {
242        self.kind() == BytecodeKind::LegacyAnalyzed
243    }
244
245    /// Returns `true` if bytecode is EIP-7702.
246    #[inline]
247    pub fn is_eip7702(&self) -> bool {
248        self.kind() == BytecodeKind::Eip7702
249    }
250
251    /// Returns the EIP-7702 delegated address if this is EIP-7702 bytecode.
252    #[inline]
253    pub fn eip7702_address(&self) -> Option<Address> {
254        if self.is_eip7702() {
255            Some(Address::from_slice(&self.0.bytecode[3..23]))
256        } else {
257            None
258        }
259    }
260
261    /// Returns jump table if bytecode is legacy analyzed.
262    #[inline]
263    pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
264        if self.is_legacy() {
265            Some(&self.0.jump_table)
266        } else {
267            None
268        }
269    }
270
271    /// Calculates or returns cached hash of the bytecode.
272    #[inline]
273    pub fn hash_slow(&self) -> B256 {
274        *self
275            .0
276            .hash
277            .get_or_init(|| keccak256(self.original_byte_slice()))
278    }
279
280    /// Returns a reference to the bytecode bytes.
281    ///
282    /// For legacy bytecode, this includes padding. For EIP-7702, this is the raw bytes.
283    #[inline]
284    pub fn bytecode(&self) -> &Bytes {
285        &self.0.bytecode
286    }
287
288    /// Pointer to the bytecode bytes.
289    #[inline]
290    pub fn bytecode_ptr(&self) -> *const u8 {
291        self.0.bytecode.as_ptr()
292    }
293
294    /// Returns a clone of the bytecode bytes.
295    #[inline]
296    pub fn bytes(&self) -> Bytes {
297        self.0.bytecode.clone()
298    }
299
300    /// Returns a reference to the bytecode bytes.
301    #[inline]
302    pub fn bytes_ref(&self) -> &Bytes {
303        &self.0.bytecode
304    }
305
306    /// Returns the bytecode as a slice.
307    #[inline]
308    pub fn bytes_slice(&self) -> &[u8] {
309        &self.0.bytecode
310    }
311
312    /// Returns the original bytecode without padding.
313    #[inline]
314    pub fn original_bytes(&self) -> Bytes {
315        self.0.bytecode.slice(..self.0.original_len)
316    }
317
318    /// Returns the original bytecode as a byte slice without padding.
319    #[inline]
320    pub fn original_byte_slice(&self) -> &[u8] {
321        &self.0.bytecode[..self.0.original_len]
322    }
323
324    /// Returns the length of the original bytes (without padding).
325    #[inline]
326    pub fn len(&self) -> usize {
327        self.0.original_len
328    }
329
330    /// Returns whether the bytecode is empty.
331    #[inline]
332    pub fn is_empty(&self) -> bool {
333        self.0.original_len == 0
334    }
335
336    /// Returns an iterator over the opcodes in this bytecode, skipping immediates.
337    #[inline]
338    pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
339        crate::BytecodeIterator::new(self)
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346    use crate::{eip7702::Eip7702DecodeError, opcode};
347    use bitvec::{bitvec, order::Lsb0};
348    use primitives::bytes;
349
350    #[test]
351    fn test_new_empty() {
352        for bytecode in [
353            Bytecode::default(),
354            Bytecode::new(),
355            Bytecode::new().clone(),
356            Bytecode::new_legacy(Bytes::new()),
357        ] {
358            assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
359            assert_eq!(bytecode.len(), 0);
360            assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
361        }
362    }
363
364    #[test]
365    fn test_new_analyzed() {
366        let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
367        let bytecode = Bytecode::new_legacy(raw);
368        let _ = Bytecode::new_analyzed(
369            bytecode.bytecode().clone(),
370            bytecode.len(),
371            bytecode.legacy_jump_table().unwrap().clone(),
372        );
373    }
374
375    #[test]
376    #[should_panic(expected = "original_len is greater than bytecode length")]
377    fn test_panic_on_large_original_len() {
378        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
379        let _ = Bytecode::new_analyzed(
380            bytecode.bytecode().clone(),
381            100,
382            bytecode.legacy_jump_table().unwrap().clone(),
383        );
384    }
385
386    #[test]
387    #[should_panic(expected = "jump table length is less than original length")]
388    fn test_panic_on_short_jump_table() {
389        let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
390        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
391        let _ = Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table);
392    }
393
394    #[test]
395    #[should_panic(expected = "bytecode cannot be empty")]
396    fn test_panic_on_empty_bytecode() {
397        let bytecode = Bytes::from_static(&[]);
398        let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
399        let _ = Bytecode::new_analyzed(bytecode, 0, jump_table);
400    }
401
402    #[test]
403    fn eip7702_sanity_decode() {
404        let raw = bytes!("ef01deadbeef");
405        assert_eq!(
406            Bytecode::new_eip7702_raw(raw),
407            Err(Eip7702DecodeError::InvalidLength)
408        );
409
410        let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
411        assert_eq!(
412            Bytecode::new_eip7702_raw(raw),
413            Err(Eip7702DecodeError::UnsupportedVersion)
414        );
415
416        let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
417        let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
418        assert!(bytecode.is_eip7702());
419        assert_eq!(
420            bytecode.eip7702_address(),
421            Some(Address::from_slice(&raw[3..]))
422        );
423        assert_eq!(bytecode.original_bytes(), raw);
424    }
425
426    #[test]
427    fn eip7702_from_address() {
428        let address = Address::new([0x01; 20]);
429        let bytecode = Bytecode::new_eip7702(address);
430        assert_eq!(bytecode.eip7702_address(), Some(address));
431        assert_eq!(
432            bytecode.original_bytes(),
433            bytes!("ef01000101010101010101010101010101010101010101")
434        );
435    }
436}