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: OnceLock<Bytecode> = OnceLock::new();
109 DEFAULT
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]
228 pub unsafe fn new_analyzed(
229 bytecode: Bytes,
230 original_len: usize,
231 jump_table: JumpTable,
232 ) -> Self {
233 assert!(
234 original_len <= bytecode.len(),
235 "original_len is greater than bytecode length"
236 );
237 assert!(
238 original_len <= jump_table.len(),
239 "jump table length is less than original length"
240 );
241 assert!(!bytecode.is_empty(), "bytecode cannot be empty");
242 Self(Arc::new(BytecodeInner {
243 kind: BytecodeKind::LegacyAnalyzed,
244 bytecode,
245 original_len,
246 jump_table,
247 hash: OnceLock::new(),
248 }))
249 }
250
251 #[inline]
253 pub fn kind(&self) -> BytecodeKind {
254 self.0.kind
255 }
256
257 #[inline]
259 pub fn is_legacy(&self) -> bool {
260 self.kind() == BytecodeKind::LegacyAnalyzed
261 }
262
263 #[inline]
265 pub fn is_eip7702(&self) -> bool {
266 self.kind() == BytecodeKind::Eip7702
267 }
268
269 #[inline]
271 pub fn eip7702_address(&self) -> Option<Address> {
272 if self.is_eip7702() {
273 Some(Address::from_slice(&self.0.bytecode[3..23]))
274 } else {
275 None
276 }
277 }
278
279 #[inline]
281 pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
282 if self.is_legacy() {
283 Some(&self.0.jump_table)
284 } else {
285 None
286 }
287 }
288
289 #[inline]
291 pub fn hash_slow(&self) -> B256 {
292 *self
293 .0
294 .hash
295 .get_or_init(|| keccak256(self.original_byte_slice()))
296 }
297
298 #[inline]
302 pub fn bytecode(&self) -> &Bytes {
303 &self.0.bytecode
304 }
305
306 #[inline]
308 pub fn bytecode_ptr(&self) -> *const u8 {
309 self.0.bytecode.as_ptr()
310 }
311
312 #[inline]
314 pub fn bytes(&self) -> Bytes {
315 self.0.bytecode.clone()
316 }
317
318 #[inline]
320 pub fn bytes_ref(&self) -> &Bytes {
321 &self.0.bytecode
322 }
323
324 #[inline]
326 pub fn bytes_slice(&self) -> &[u8] {
327 &self.0.bytecode
328 }
329
330 #[inline]
332 pub fn original_bytes(&self) -> Bytes {
333 self.0.bytecode.slice(..self.0.original_len)
334 }
335
336 #[inline]
338 pub fn original_byte_slice(&self) -> &[u8] {
339 &self.0.bytecode[..self.0.original_len]
340 }
341
342 #[inline]
344 pub fn len(&self) -> usize {
345 self.0.original_len
346 }
347
348 #[inline]
350 pub fn is_empty(&self) -> bool {
351 self.0.original_len == 0
352 }
353
354 #[inline]
356 pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
357 crate::BytecodeIterator::new(self)
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364 use crate::{eip7702::Eip7702DecodeError, opcode};
365 use bitvec::{bitvec, order::Lsb0};
366 use primitives::bytes;
367
368 #[test]
369 fn test_new_empty() {
370 for bytecode in [
371 Bytecode::default(),
372 Bytecode::new(),
373 Bytecode::new().clone(),
374 Bytecode::new_legacy(Bytes::new()),
375 ] {
376 assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
377 assert_eq!(bytecode.len(), 0);
378 assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
379 }
380 }
381
382 #[test]
383 fn test_new_analyzed() {
384 let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
385 let bytecode = Bytecode::new_legacy(raw);
386 let _ = unsafe {
388 Bytecode::new_analyzed(
389 bytecode.bytecode().clone(),
390 bytecode.len(),
391 bytecode.legacy_jump_table().unwrap().clone(),
392 )
393 };
394 }
395
396 #[test]
397 #[should_panic(expected = "original_len is greater than bytecode length")]
398 fn test_panic_on_large_original_len() {
399 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
400 let _ = unsafe {
402 Bytecode::new_analyzed(
403 bytecode.bytecode().clone(),
404 100,
405 bytecode.legacy_jump_table().unwrap().clone(),
406 )
407 };
408 }
409
410 #[test]
411 #[should_panic(expected = "jump table length is less than original length")]
412 fn test_panic_on_short_jump_table() {
413 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
414 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
415 let _ = unsafe {
417 Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table)
418 };
419 }
420
421 #[test]
422 #[should_panic(expected = "bytecode cannot be empty")]
423 fn test_panic_on_empty_bytecode() {
424 let bytecode = Bytes::from_static(&[]);
425 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
426 let _ = unsafe { Bytecode::new_analyzed(bytecode, 0, jump_table) };
428 }
429
430 #[test]
431 fn eip7702_sanity_decode() {
432 let raw = bytes!("ef01deadbeef");
433 assert_eq!(
434 Bytecode::new_eip7702_raw(raw),
435 Err(Eip7702DecodeError::InvalidLength)
436 );
437
438 let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
439 assert_eq!(
440 Bytecode::new_eip7702_raw(raw),
441 Err(Eip7702DecodeError::UnsupportedVersion)
442 );
443
444 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
445 let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
446 assert!(bytecode.is_eip7702());
447 assert_eq!(
448 bytecode.eip7702_address(),
449 Some(Address::from_slice(&raw[3..]))
450 );
451 assert_eq!(bytecode.original_bytes(), raw);
452 }
453
454 #[test]
455 fn eip7702_from_address() {
456 let address = Address::new([0x01; 20]);
457 let bytecode = Bytecode::new_eip7702(address);
458 assert_eq!(bytecode.eip7702_address(), Some(address));
459 assert_eq!(
460 bytecode.original_bytes(),
461 bytes!("ef01000101010101010101010101010101010101010101")
462 );
463 }
464}