revm_rwasm_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    ownable_account::{OwnableAccountBytecode, OWNABLE_ACCOUNT_MAGIC_BYTES},
10    rwasm::{RwasmBytecode, RWASM_MAGIC_BYTES},
11    BytecodeDecodeError, JumpTable, LegacyAnalyzedBytecode, LegacyRawBytecode,
12};
13use core::fmt::Debug;
14use primitives::{keccak256, Address, Bytes, B256, KECCAK_EMPTY};
15
16/// Main bytecode structure with all variants.
17#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub enum Bytecode {
20    /// EIP-7702 delegated bytecode
21    Eip7702(Eip7702Bytecode),
22    /// The bytecode has been analyzed for valid jump destinations.
23    LegacyAnalyzed(LegacyAnalyzedBytecode),
24    /// An Rwasm bytecode
25    Rwasm(RwasmBytecode),
26    /// delegated bytecode metadata
27    OwnableAccount(OwnableAccountBytecode),
28}
29
30impl Default for Bytecode {
31    #[inline]
32    fn default() -> Self {
33        Self::new()
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    pub fn hash_slow(&self) -> B256 {
55        if self.is_empty() {
56            KECCAK_EMPTY
57        } else {
58            keccak256(self.original_byte_slice())
59        }
60    }
61
62    /// Returns `true` if bytecode is EIP-7702.
63    pub const fn is_eip7702(&self) -> bool {
64        matches!(self, Self::Eip7702(_))
65    }
66
67    /// Returns `true` if bytecode is Metadata.
68    pub const fn is_ownable_account(&self) -> bool {
69        matches!(self, Self::OwnableAccount(_))
70    }
71
72    /// Creates a new legacy [`Bytecode`].
73    #[inline]
74    pub fn new_legacy(raw: Bytes) -> Self {
75        Self::LegacyAnalyzed(LegacyRawBytecode(raw).into_analyzed())
76    }
77
78    /// Creates a new raw [`Bytecode`].
79    ///
80    /// # Panics
81    ///
82    /// Panics if bytecode is in incorrect format. If you want to handle errors use [`Self::new_raw_checked`].
83    #[inline]
84    pub fn new_raw(bytecode: Bytes) -> Self {
85        Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
86    }
87
88    /// Creates a new EIP-7702 [`Bytecode`] from [`Address`].
89    #[inline]
90    pub fn new_eip7702(address: Address) -> Self {
91        Self::Eip7702(Eip7702Bytecode::new(address))
92    }
93
94    /// Creates a new metadata [`RwasmBytecode`] from [`Address`].
95    #[inline]
96    pub fn new_rwasm(raw_rwasm_module: Bytes) -> Self {
97        Self::Rwasm(RwasmBytecode::new(raw_rwasm_module).expect("Expect correct bytecode"))
98    }
99
100    /// Creates a new metadata [`OwnableAccountBytecode`] from [`Address`].
101    #[inline]
102    pub fn new_ownable_account(address: Address, metadata: Bytes) -> Self {
103        Self::OwnableAccount(OwnableAccountBytecode::new(address, metadata))
104    }
105
106    /// Creates a new raw [`Bytecode`].
107    ///
108    /// Returns an error on incorrect bytecode format.
109    #[inline]
110    pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
111        let prefix = bytes.get(..2);
112        match prefix {
113            Some(prefix) if prefix == &EIP7702_MAGIC_BYTES => {
114                let eip7702 = Eip7702Bytecode::new_raw(bytes)?;
115                Ok(Self::Eip7702(eip7702))
116            }
117            Some(prefix) if prefix == &OWNABLE_ACCOUNT_MAGIC_BYTES => {
118                let instance = OwnableAccountBytecode::new_raw(bytes)?;
119                Ok(Self::OwnableAccount(instance))
120            }
121            Some(prefix) if prefix == &RWASM_MAGIC_BYTES => {
122                let bytecode = RwasmBytecode::new(bytes)?;
123                Ok(Self::Rwasm(bytecode))
124            }
125            _ => Ok(Self::new_legacy(bytes)),
126        }
127    }
128
129    /// Create new checked bytecode.
130    ///
131    /// # Panics
132    ///
133    /// For possible panics see [`LegacyAnalyzedBytecode::new`].
134    pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
135        Self::LegacyAnalyzed(LegacyAnalyzedBytecode::new(
136            bytecode,
137            original_len,
138            jump_table,
139        ))
140    }
141
142    /// Returns a reference to the bytecode.
143    #[inline]
144    pub fn bytecode(&self) -> &Bytes {
145        match self {
146            Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
147            Self::Eip7702(code) => code.raw(),
148            Self::OwnableAccount(code) => code.raw(),
149            Self::Rwasm(code) => code.raw(),
150        }
151    }
152
153    /// Pointer to the executable bytecode.
154    pub fn bytecode_ptr(&self) -> *const u8 {
155        self.bytecode().as_ptr()
156    }
157
158    /// Returns bytes.
159    #[inline]
160    pub fn bytes(&self) -> Bytes {
161        self.bytes_ref().clone()
162    }
163
164    /// Returns raw bytes reference.
165    #[inline]
166    pub fn bytes_ref(&self) -> &Bytes {
167        match self {
168            Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
169            Self::Eip7702(code) => code.raw(),
170            Self::OwnableAccount(code) => code.raw(),
171            Self::Rwasm(code) => code.raw(),
172        }
173    }
174
175    /// Returns raw bytes slice.
176    #[inline]
177    pub fn bytes_slice(&self) -> &[u8] {
178        self.bytes_ref()
179    }
180
181    /// Returns the original bytecode.
182    #[inline]
183    pub fn original_bytes(&self) -> Bytes {
184        match self {
185            Self::LegacyAnalyzed(analyzed) => analyzed.original_bytes(),
186            Self::Eip7702(eip7702) => eip7702.raw().clone(),
187            Self::OwnableAccount(metadata) => metadata.raw().clone(),
188            Self::Rwasm(bytes) => bytes.raw().clone(),
189        }
190    }
191
192    /// Returns the original bytecode as a byte slice.
193    #[inline]
194    pub fn original_byte_slice(&self) -> &[u8] {
195        match self {
196            Self::LegacyAnalyzed(analyzed) => analyzed.original_byte_slice(),
197            Self::Eip7702(eip7702) => eip7702.raw(),
198            Self::OwnableAccount(data) => data.raw(),
199            Self::Rwasm(bytes) => bytes.raw(),
200        }
201    }
202
203    /// Returns the length of the original bytes.
204    #[inline]
205    pub fn len(&self) -> usize {
206        self.original_byte_slice().len()
207    }
208
209    /// Returns whether the bytecode is empty.
210    #[inline]
211    pub fn is_empty(&self) -> bool {
212        self.len() == 0
213    }
214
215    /// Returns an iterator over the opcodes in this bytecode, skipping immediates.
216    /// This is useful if you want to ignore immediates and just see what opcodes are inside.
217    #[inline]
218    pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
219        crate::BytecodeIterator::new(self)
220    }
221}