Skip to main content

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