1#[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#[derive(Clone, Debug)]
22pub struct Bytecode(Arc<BytecodeInner>);
23
24#[derive(Debug)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30struct BytecodeInner {
31 kind: BytecodeKind,
33 bytecode: Bytes,
38 original_len: usize,
42 jump_table: JumpTable,
44 #[cfg_attr(feature = "serde", serde(skip, default))]
46 hash: OnceLock<B256>,
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub enum BytecodeKind {
53 #[default]
55 LegacyAnalyzed,
56 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 #[inline]
107 pub fn new() -> Self {
108 static DEFAULT_BYTECODE: OnceLock<Bytecode> = OnceLock::new();
109 DEFAULT_BYTECODE
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 #[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 #[inline]
150 pub fn new_raw(bytecode: Bytes) -> Self {
151 Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
152 }
153
154 #[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 #[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 #[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 #[inline]
212 pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
213 assert!(
214 original_len <= bytecode.len(),
215 "original_len is greater than bytecode length"
216 );
217 assert!(
218 original_len <= jump_table.len(),
219 "jump table length is less than original length"
220 );
221 assert!(!bytecode.is_empty(), "bytecode cannot be empty");
222 Self(Arc::new(BytecodeInner {
223 kind: BytecodeKind::LegacyAnalyzed,
224 bytecode,
225 original_len,
226 jump_table,
227 hash: OnceLock::new(),
228 }))
229 }
230
231 #[inline]
233 pub fn kind(&self) -> BytecodeKind {
234 self.0.kind
235 }
236
237 #[inline]
239 pub fn is_legacy(&self) -> bool {
240 self.kind() == BytecodeKind::LegacyAnalyzed
241 }
242
243 #[inline]
245 pub fn is_eip7702(&self) -> bool {
246 self.kind() == BytecodeKind::Eip7702
247 }
248
249 #[inline]
251 pub fn eip7702_address(&self) -> Option<Address> {
252 if self.is_eip7702() {
253 Some(Address::from_slice(&self.0.bytecode[3..23]))
254 } else {
255 None
256 }
257 }
258
259 #[inline]
261 pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
262 if self.is_legacy() {
263 Some(&self.0.jump_table)
264 } else {
265 None
266 }
267 }
268
269 #[inline]
271 pub fn hash_slow(&self) -> B256 {
272 *self
273 .0
274 .hash
275 .get_or_init(|| keccak256(self.original_byte_slice()))
276 }
277
278 #[inline]
282 pub fn bytecode(&self) -> &Bytes {
283 &self.0.bytecode
284 }
285
286 #[inline]
288 pub fn bytecode_ptr(&self) -> *const u8 {
289 self.0.bytecode.as_ptr()
290 }
291
292 #[inline]
294 pub fn bytes(&self) -> Bytes {
295 self.0.bytecode.clone()
296 }
297
298 #[inline]
300 pub fn bytes_ref(&self) -> &Bytes {
301 &self.0.bytecode
302 }
303
304 #[inline]
306 pub fn bytes_slice(&self) -> &[u8] {
307 &self.0.bytecode
308 }
309
310 #[inline]
312 pub fn original_bytes(&self) -> Bytes {
313 self.0.bytecode.slice(..self.0.original_len)
314 }
315
316 #[inline]
318 pub fn original_byte_slice(&self) -> &[u8] {
319 &self.0.bytecode[..self.0.original_len]
320 }
321
322 #[inline]
324 pub fn len(&self) -> usize {
325 self.0.original_len
326 }
327
328 #[inline]
330 pub fn is_empty(&self) -> bool {
331 self.0.original_len == 0
332 }
333
334 #[inline]
336 pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
337 crate::BytecodeIterator::new(self)
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::{eip7702::Eip7702DecodeError, opcode};
345 use bitvec::{bitvec, order::Lsb0};
346 use primitives::bytes;
347
348 #[test]
349 fn test_new_empty() {
350 for bytecode in [
351 Bytecode::default(),
352 Bytecode::new(),
353 Bytecode::new().clone(),
354 Bytecode::new_legacy(Bytes::new()),
355 ] {
356 assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
357 assert_eq!(bytecode.len(), 0);
358 assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
359 }
360 }
361
362 #[test]
363 fn test_new_analyzed() {
364 let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
365 let bytecode = Bytecode::new_legacy(raw);
366 let _ = Bytecode::new_analyzed(
367 bytecode.bytecode().clone(),
368 bytecode.len(),
369 bytecode.legacy_jump_table().unwrap().clone(),
370 );
371 }
372
373 #[test]
374 #[should_panic(expected = "original_len is greater than bytecode length")]
375 fn test_panic_on_large_original_len() {
376 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
377 let _ = Bytecode::new_analyzed(
378 bytecode.bytecode().clone(),
379 100,
380 bytecode.legacy_jump_table().unwrap().clone(),
381 );
382 }
383
384 #[test]
385 #[should_panic(expected = "jump table length is less than original length")]
386 fn test_panic_on_short_jump_table() {
387 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
388 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
389 let _ = Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table);
390 }
391
392 #[test]
393 #[should_panic(expected = "bytecode cannot be empty")]
394 fn test_panic_on_empty_bytecode() {
395 let bytecode = Bytes::from_static(&[]);
396 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
397 let _ = Bytecode::new_analyzed(bytecode, 0, jump_table);
398 }
399
400 #[test]
401 fn eip7702_sanity_decode() {
402 let raw = bytes!("ef01deadbeef");
403 assert_eq!(
404 Bytecode::new_eip7702_raw(raw),
405 Err(Eip7702DecodeError::InvalidLength)
406 );
407
408 let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
409 assert_eq!(
410 Bytecode::new_eip7702_raw(raw),
411 Err(Eip7702DecodeError::UnsupportedVersion)
412 );
413
414 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
415 let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
416 assert!(bytecode.is_eip7702());
417 assert_eq!(
418 bytecode.eip7702_address(),
419 Some(Address::from_slice(&raw[3..]))
420 );
421 assert_eq!(bytecode.original_bytes(), raw);
422 }
423
424 #[test]
425 fn eip7702_from_address() {
426 let address = Address::new([0x01; 20]);
427 let bytecode = Bytecode::new_eip7702(address);
428 assert_eq!(bytecode.eip7702_address(), Some(address));
429 assert_eq!(
430 bytecode.original_bytes(),
431 bytes!("ef01000101010101010101010101010101010101010101")
432 );
433 }
434}