revm_bytecode/
bytecode.rs

1//! Module that contains the bytecode enum with all variants supported by Ethereum mainnet.
2//!
3//! Those are:
4//! - Legacy bytecode with jump table analysis. Found in [`LegacyAnalyzedBytecode`]
5//! - EIP-7702 bytecode, introduces in Prague and contains address to delegated account.
6
7use crate::{
8    eip7702::{Eip7702Bytecode, EIP7702_MAGIC_BYTES},
9    BytecodeDecodeError, JumpTable, LegacyAnalyzedBytecode, LegacyRawBytecode,
10};
11use primitives::{alloy_primitives::Sealable, keccak256, Address, Bytes, B256, KECCAK_EMPTY};
12
13/// Main bytecode structure with all variants.
14#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub enum Bytecode {
17    /// EIP-7702 delegated bytecode
18    Eip7702(Eip7702Bytecode),
19    /// The bytecode has been analyzed for valid jump destinations.
20    LegacyAnalyzed(LegacyAnalyzedBytecode),
21}
22
23impl Default for Bytecode {
24    #[inline]
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl Sealable for Bytecode {
31    #[inline]
32    fn hash_slow(&self) -> B256 {
33        self.hash_slow()
34    }
35}
36
37impl Bytecode {
38    /// Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode.
39    #[inline]
40    pub fn new() -> Self {
41        Self::LegacyAnalyzed(LegacyAnalyzedBytecode::default())
42    }
43
44    /// Returns jump table if bytecode is analyzed.
45    #[inline]
46    pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
47        match &self {
48            Self::LegacyAnalyzed(analyzed) => Some(analyzed.jump_table()),
49            _ => None,
50        }
51    }
52
53    /// Calculates hash of the bytecode.
54    #[inline]
55    pub fn hash_slow(&self) -> B256 {
56        if self.is_empty() {
57            KECCAK_EMPTY
58        } else {
59            keccak256(self.original_byte_slice())
60        }
61    }
62
63    /// Returns `true` if bytecode is EIP-7702.
64    #[inline]
65    pub const fn is_eip7702(&self) -> bool {
66        matches!(self, Self::Eip7702(_))
67    }
68
69    /// Creates a new legacy [`Bytecode`].
70    #[inline]
71    pub fn new_legacy(raw: Bytes) -> Self {
72        Self::LegacyAnalyzed(LegacyRawBytecode(raw).into_analyzed())
73    }
74
75    /// Creates a new raw [`Bytecode`].
76    ///
77    /// # Panics
78    ///
79    /// Panics if bytecode is in incorrect format. If you want to handle errors use [`Self::new_raw_checked`].
80    #[inline]
81    pub fn new_raw(bytecode: Bytes) -> Self {
82        Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
83    }
84
85    /// Creates a new EIP-7702 [`Bytecode`] from [`Address`].
86    #[inline]
87    pub fn new_eip7702(address: Address) -> Self {
88        Self::Eip7702(Eip7702Bytecode::new(address))
89    }
90
91    /// Creates a new raw [`Bytecode`].
92    ///
93    /// Returns an error on incorrect bytecode format.
94    #[inline]
95    pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
96        let prefix = bytes.get(..2);
97        match prefix {
98            Some(prefix) if prefix == &EIP7702_MAGIC_BYTES => {
99                let eip7702 = Eip7702Bytecode::new_raw(bytes)?;
100                Ok(Self::Eip7702(eip7702))
101            }
102            _ => Ok(Self::new_legacy(bytes)),
103        }
104    }
105
106    /// Create new checked bytecode.
107    ///
108    /// # Panics
109    ///
110    /// For possible panics see [`LegacyAnalyzedBytecode::new`].
111    #[inline]
112    pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
113        Self::LegacyAnalyzed(LegacyAnalyzedBytecode::new(
114            bytecode,
115            original_len,
116            jump_table,
117        ))
118    }
119
120    /// Returns a reference to the bytecode.
121    #[inline]
122    pub fn bytecode(&self) -> &Bytes {
123        match self {
124            Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
125            Self::Eip7702(code) => code.raw(),
126        }
127    }
128
129    /// Pointer to the executable bytecode.
130    #[inline]
131    pub fn bytecode_ptr(&self) -> *const u8 {
132        self.bytecode().as_ptr()
133    }
134
135    /// Returns bytes.
136    #[inline]
137    pub fn bytes(&self) -> Bytes {
138        self.bytes_ref().clone()
139    }
140
141    /// Returns raw bytes reference.
142    #[inline]
143    pub fn bytes_ref(&self) -> &Bytes {
144        match self {
145            Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
146            Self::Eip7702(code) => code.raw(),
147        }
148    }
149
150    /// Returns raw bytes slice.
151    #[inline]
152    pub fn bytes_slice(&self) -> &[u8] {
153        self.bytes_ref()
154    }
155
156    /// Returns the original bytecode.
157    #[inline]
158    pub fn original_bytes(&self) -> Bytes {
159        match self {
160            Self::LegacyAnalyzed(analyzed) => analyzed.original_bytes(),
161            Self::Eip7702(eip7702) => eip7702.raw().clone(),
162        }
163    }
164
165    /// Returns the original bytecode as a byte slice.
166    #[inline]
167    pub fn original_byte_slice(&self) -> &[u8] {
168        match self {
169            Self::LegacyAnalyzed(analyzed) => analyzed.original_byte_slice(),
170            Self::Eip7702(eip7702) => eip7702.raw(),
171        }
172    }
173
174    /// Returns the length of the original bytes.
175    #[inline]
176    pub fn len(&self) -> usize {
177        self.original_byte_slice().len()
178    }
179
180    /// Returns whether the bytecode is empty.
181    #[inline]
182    pub fn is_empty(&self) -> bool {
183        self.len() == 0
184    }
185
186    /// Returns an iterator over the opcodes in this bytecode, skipping immediates.
187    /// This is useful if you want to ignore immediates and just see what opcodes are inside.
188    #[inline]
189    pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
190        crate::BytecodeIterator::new(self)
191    }
192}