1#[cfg(feature = "serde")]
8mod serde_impl;
9
10use crate::{
11 eip7702::{Eip7702DecodeError, EIP7702_MAGIC_BYTES, EIP7702_VERSION},
12 legacy::{analyze_legacy, pad_legacy},
13 opcode, BytecodeDecodeError, JumpTable,
14};
15use core::fmt;
16use primitives::{
17 alloy_primitives::Sealable, keccak256, Address, Bytes, OnceLock, B256, KECCAK_EMPTY,
18};
19use std::sync::Arc;
20
21#[derive(Clone)]
23pub struct Bytecode(Option<Arc<BytecodeInner>>);
24
25#[derive(Debug)]
30struct BytecodeInner {
31 kind: BytecodeKind,
33 bytecode: Bytes,
35 original_len: usize,
37 jump_table: OnceLock<JumpTable>,
39 hash: OnceLock<B256>,
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub enum BytecodeKind {
47 #[default]
49 LegacyAnalyzed,
50 Eip7702,
52}
53
54impl Default for Bytecode {
55 #[inline]
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl PartialEq for Bytecode {
62 #[inline]
63 fn eq(&self, other: &Self) -> bool {
64 self.original_byte_slice() == other.original_byte_slice()
65 }
66}
67
68impl Eq for Bytecode {}
69
70impl core::hash::Hash for Bytecode {
71 #[inline]
72 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
73 self.original_byte_slice().hash(state);
74 }
75}
76
77impl PartialOrd for Bytecode {
78 #[inline]
79 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
80 Some(self.cmp(other))
81 }
82}
83
84impl Ord for Bytecode {
85 #[inline]
86 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
87 self.original_byte_slice().cmp(other.original_byte_slice())
88 }
89}
90
91impl Sealable for Bytecode {
92 #[inline]
93 fn hash_slow(&self) -> B256 {
94 self.hash_slow()
95 }
96}
97
98impl Bytecode {
99 #[inline]
101 pub const fn new() -> Self {
102 Self(None)
103 }
104
105 #[inline]
106 fn empty_inner() -> &'static BytecodeInner {
107 static EMPTY: OnceLock<BytecodeInner> = OnceLock::new();
108 EMPTY.get_or_init(|| BytecodeInner {
109 kind: BytecodeKind::LegacyAnalyzed,
110 bytecode: Bytes::from_static(&[opcode::STOP]),
111 original_len: 0,
112 jump_table: OnceLock::new(),
113 hash: {
114 let hash = OnceLock::new();
115 let _ = hash.set(KECCAK_EMPTY);
116 hash
117 },
118 })
119 }
120
121 #[inline]
122 fn inner(&self) -> &BytecodeInner {
123 match &self.0 {
124 Some(inner) => inner,
125 None => Self::empty_inner(),
126 }
127 }
128
129 #[inline]
135 pub fn new_legacy(raw: Bytes) -> Self {
136 if raw.is_empty() {
137 return Self::new();
138 }
139
140 Self(Some(Arc::new(BytecodeInner {
141 kind: BytecodeKind::LegacyAnalyzed,
142 original_len: raw.len(),
143 bytecode: pad_legacy(raw),
144 jump_table: OnceLock::new(),
145 hash: OnceLock::new(),
146 })))
147 }
148
149 #[inline]
155 pub fn new_raw(bytecode: Bytes) -> Self {
156 Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
157 }
158
159 #[inline]
161 pub fn new_eip7702(address: Address) -> Self {
162 let raw: Bytes = [EIP7702_MAGIC_BYTES, &[EIP7702_VERSION], &address[..]]
163 .concat()
164 .into();
165 Self(Some(Arc::new(BytecodeInner {
166 kind: BytecodeKind::Eip7702,
167 original_len: raw.len(),
168 bytecode: raw,
169 jump_table: OnceLock::new(),
170 hash: OnceLock::new(),
171 })))
172 }
173
174 #[inline]
178 pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
179 if bytes.starts_with(EIP7702_MAGIC_BYTES) {
180 Self::new_eip7702_raw(bytes).map_err(Into::into)
181 } else {
182 Ok(Self::new_legacy(bytes))
183 }
184 }
185
186 #[inline]
190 pub fn new_eip7702_raw(bytes: Bytes) -> Result<Self, Eip7702DecodeError> {
191 if bytes.len() != 23 {
192 return Err(Eip7702DecodeError::InvalidLength);
193 }
194 if !bytes.starts_with(EIP7702_MAGIC_BYTES) {
195 return Err(Eip7702DecodeError::InvalidMagic);
196 }
197 if bytes[2] != EIP7702_VERSION {
198 return Err(Eip7702DecodeError::UnsupportedVersion);
199 }
200 Ok(Self(Some(Arc::new(BytecodeInner {
201 kind: BytecodeKind::Eip7702,
202 original_len: bytes.len(),
203 bytecode: bytes,
204 jump_table: OnceLock::new(),
205 hash: OnceLock::new(),
206 }))))
207 }
208
209 #[inline]
233 pub unsafe fn new_analyzed(
234 bytecode: Bytes,
235 original_len: usize,
236 jump_table: JumpTable,
237 ) -> Self {
238 assert!(
239 original_len <= bytecode.len(),
240 "original_len is greater than bytecode length"
241 );
242 assert!(
243 original_len <= jump_table.len(),
244 "jump table length is less than original length"
245 );
246 assert!(!bytecode.is_empty(), "bytecode cannot be empty");
247 let cached_jump_table = OnceLock::new();
248 let _ = cached_jump_table.set(jump_table);
249 Self(Some(Arc::new(BytecodeInner {
250 kind: BytecodeKind::LegacyAnalyzed,
251 bytecode,
252 original_len,
253 jump_table: cached_jump_table,
254 hash: OnceLock::new(),
255 })))
256 }
257
258 #[inline]
260 pub fn kind(&self) -> BytecodeKind {
261 self.inner().kind
262 }
263
264 #[inline]
266 pub fn is_legacy(&self) -> bool {
267 self.kind() == BytecodeKind::LegacyAnalyzed
268 }
269
270 #[inline]
272 pub fn is_eip7702(&self) -> bool {
273 self.kind() == BytecodeKind::Eip7702
274 }
275
276 #[inline]
278 pub fn eip7702_address(&self) -> Option<Address> {
279 if self.is_eip7702() {
280 Some(Address::from_slice(&self.inner().bytecode[3..23]))
281 } else {
282 None
283 }
284 }
285
286 #[inline]
288 pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
289 if self.is_legacy() {
290 Some(
291 self.inner()
292 .jump_table
293 .get_or_init(|| analyze_legacy(self.original_byte_slice())),
294 )
295 } else {
296 None
297 }
298 }
299
300 #[inline]
302 pub fn hash_slow(&self) -> B256 {
303 *self
304 .inner()
305 .hash
306 .get_or_init(|| keccak256(self.original_byte_slice()))
307 }
308
309 #[inline]
313 pub fn bytecode(&self) -> &Bytes {
314 &self.inner().bytecode
315 }
316
317 #[inline]
319 pub fn bytecode_ptr(&self) -> *const u8 {
320 self.bytecode().as_ptr()
321 }
322
323 #[inline]
325 pub fn bytes(&self) -> Bytes {
326 self.bytecode().clone()
327 }
328
329 #[inline]
331 pub fn bytes_ref(&self) -> &Bytes {
332 self.bytecode()
333 }
334
335 #[inline]
337 pub fn bytes_slice(&self) -> &[u8] {
338 self.bytecode()
339 }
340
341 #[inline]
343 pub fn original_bytes(&self) -> Bytes {
344 let inner = self.inner();
345 inner.bytecode.slice(..inner.original_len)
346 }
347
348 #[inline]
350 pub fn original_byte_slice(&self) -> &[u8] {
351 let inner = self.inner();
352 &inner.bytecode[..inner.original_len]
353 }
354
355 #[inline]
357 pub fn len(&self) -> usize {
358 self.inner().original_len
359 }
360
361 #[inline]
363 pub fn is_empty(&self) -> bool {
364 self.inner().original_len == 0
365 }
366
367 #[inline]
369 pub const fn is_default(&self) -> bool {
370 self.0.is_none()
371 }
372
373 #[inline]
375 pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
376 crate::BytecodeIterator::new(self)
377 }
378}
379
380impl fmt::Debug for Bytecode {
381 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
382 formatter
383 .debug_tuple("Bytecode")
384 .field(self.inner())
385 .finish()
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 extern crate std as test_std;
392
393 use super::*;
394 use crate::{eip7702::Eip7702DecodeError, opcode};
395 use bitvec::{bitvec, order::Lsb0};
396 use primitives::bytes;
397 use std::format;
398 use test_std::hash::{BuildHasher, RandomState};
399
400 #[test]
401 fn test_new_empty() {
402 let analyzed = unsafe {
403 Bytecode::new_analyzed(Bytes::from_static(&[opcode::STOP]), 0, JumpTable::default())
404 };
405 assert!(!analyzed.is_default());
406 assert!(analyzed.0.is_some());
407 let hasher = RandomState::new();
408 for bytecode in [
409 Bytecode::default(),
410 Bytecode::new(),
411 Bytecode::new().clone(),
412 Bytecode::new_legacy(Bytes::new()),
413 Bytecode::new_raw(Bytes::new()),
414 Bytecode::new_raw_checked(Bytes::new()).unwrap(),
415 ] {
416 assert!(bytecode.0.is_none());
417 assert!(bytecode.is_default());
418 assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
419 assert!(bytecode.is_default());
420 assert_eq!(bytecode.len(), 0);
421 assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
422 assert!(bytecode.is_empty());
423 assert!(bytecode.is_legacy());
424 assert!(!bytecode.is_eip7702());
425 assert_eq!(bytecode.eip7702_address(), None);
426 assert_eq!(bytecode.bytecode(), &Bytes::from_static(&[opcode::STOP]));
427 assert_eq!(bytecode.original_bytes(), Bytes::new());
428 assert!(bytecode.original_byte_slice().is_empty());
429 assert_eq!(bytecode.hash_slow(), KECCAK_EMPTY);
430 assert!(bytecode.legacy_jump_table().unwrap().is_empty());
431 assert!(bytecode.is_default());
432 assert!(bytecode.clone().is_default());
433 assert_eq!(bytecode, analyzed);
434 assert_eq!(bytecode.cmp(&analyzed), core::cmp::Ordering::Equal);
435 assert_eq!(hasher.hash_one(&bytecode), hasher.hash_one(&analyzed));
436 assert!(bytecode < Bytecode::new_legacy(Bytes::from_static(&[opcode::STOP])));
437 }
438 }
439
440 #[test]
441 fn test_new_analyzed() {
442 let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
443 let bytecode = Bytecode::new_legacy(raw);
444 let _ = unsafe {
446 Bytecode::new_analyzed(
447 bytecode.bytecode().clone(),
448 bytecode.len(),
449 bytecode.legacy_jump_table().unwrap().clone(),
450 )
451 };
452 }
453
454 #[test]
455 #[should_panic(expected = "original_len is greater than bytecode length")]
456 fn test_panic_on_large_original_len() {
457 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
458 let _ = unsafe {
460 Bytecode::new_analyzed(
461 bytecode.bytecode().clone(),
462 100,
463 bytecode.legacy_jump_table().unwrap().clone(),
464 )
465 };
466 }
467
468 #[test]
469 #[should_panic(expected = "jump table length is less than original length")]
470 fn test_panic_on_short_jump_table() {
471 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
472 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
473 let _ = unsafe {
475 Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table)
476 };
477 }
478
479 #[test]
480 #[should_panic(expected = "bytecode cannot be empty")]
481 fn test_panic_on_empty_bytecode() {
482 let bytecode = Bytes::from_static(&[]);
483 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
484 let _ = unsafe { Bytecode::new_analyzed(bytecode, 0, jump_table) };
486 }
487
488 #[test]
489 fn eip7702_sanity_decode() {
490 let raw = bytes!("ef01deadbeef");
491 assert_eq!(
492 Bytecode::new_eip7702_raw(raw),
493 Err(Eip7702DecodeError::InvalidLength)
494 );
495
496 let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
497 assert_eq!(
498 Bytecode::new_eip7702_raw(raw),
499 Err(Eip7702DecodeError::UnsupportedVersion)
500 );
501
502 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
503 let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
504 assert!(bytecode.is_eip7702());
505 assert_eq!(
506 bytecode.eip7702_address(),
507 Some(Address::from_slice(&raw[3..]))
508 );
509 assert_eq!(bytecode.original_bytes(), raw);
510 }
511
512 #[test]
513 fn eip7702_from_address() {
514 let address = Address::new([0x01; 20]);
515 let bytecode = Bytecode::new_eip7702(address);
516 assert_eq!(bytecode.eip7702_address(), Some(address));
517 assert_eq!(
518 bytecode.original_bytes(),
519 bytes!("ef01000101010101010101010101010101010101010101")
520 );
521 }
522
523 #[test]
524 fn eip7702_invalid_magic() {
525 let raw1 = bytes!("ee0101deadbeef00000000000000000000000000000000");
526 assert_eq!(
527 Bytecode::new_eip7702_raw(raw1),
528 Err(Eip7702DecodeError::InvalidMagic)
529 );
530
531 let raw2 = bytes!("ef0001deadbeef00000000000000000000000000000000");
532 assert_eq!(
533 Bytecode::new_eip7702_raw(raw2),
534 Err(Eip7702DecodeError::InvalidMagic)
535 );
536 }
537
538 #[test]
539 fn new_raw_checked_dispatch() {
540 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
541 let bytecode = Bytecode::new_raw_checked(raw).unwrap();
542 assert!(bytecode.is_eip7702());
543
544 assert_eq!(
545 Bytecode::new_raw_checked(bytes!("ef01")),
546 Err(BytecodeDecodeError::Eip7702(
547 Eip7702DecodeError::InvalidLength
548 ))
549 );
550
551 let bytecode = Bytecode::new_raw_checked(bytes!("ef00")).unwrap();
552 assert!(bytecode.is_legacy());
553 assert_eq!(bytecode.eip7702_address(), None);
554 }
555
556 #[test]
557 fn is_default() {
558 assert!(Bytecode::default().is_default());
559 }
560
561 #[test]
562 #[cfg(feature = "serde")]
563 fn is_default_after_serde() {
564 let bc = Bytecode::default();
565 let json = serde_json::to_string(&bc).unwrap();
566 let deser: Bytecode = serde_json::from_str(&json).unwrap();
567 assert!(deser.is_default());
568 }
569
570 #[test]
571 fn analysis_is_lazy_and_shared() {
572 let raw = bytes!("5b605b7f");
573 for bytecode in [
574 Bytecode::new_legacy(raw.clone()),
575 Bytecode::new_raw(raw.clone()),
576 Bytecode::new_raw_checked(raw.clone()).unwrap(),
577 ] {
578 let cloned = bytecode.clone();
579 assert_eq!(bytecode.original_byte_slice(), raw.as_ref());
580 assert_eq!(bytecode.original_bytes().as_ptr(), bytecode.bytecode_ptr());
581 assert_eq!(bytecode.len(), raw.len());
582 assert!(!bytecode.is_empty());
583 assert_eq!(bytecode, cloned);
584 assert_eq!(bytecode.cmp(&cloned), core::cmp::Ordering::Equal);
585 assert_eq!(bytecode.iter_opcodes().count(), 3);
586 assert_eq!(bytecode.hash_slow(), keccak256(&raw));
587 assert_eq!(bytecode.bytecode().len(), raw.len() + 33);
588 assert!(bytecode.inner().jump_table.get().is_none());
589
590 let table = bytecode.legacy_jump_table().unwrap();
591 assert_eq!(bytecode.original_bytes().as_ptr(), bytecode.bytecode_ptr());
592 assert_eq!(table.len(), raw.len());
593 assert!(table.is_valid(0));
594 assert!(!table.is_valid(2)); assert!(!table.is_valid(raw.len())); assert!(core::ptr::eq(
597 table,
598 bytecode.inner().jump_table.get().unwrap()
599 ));
600 assert!(core::ptr::eq(table, cloned.legacy_jump_table().unwrap()));
601 assert_eq!(bytecode.bytecode_ptr(), cloned.bytecode_ptr());
602 }
603 }
604
605 #[test]
606 fn supplied_analysis_is_preserved() {
607 static TABLE: &[u8] = &[1];
608 let padded = bytes!("5b600000");
609 let ptr = padded.as_ptr();
610 let bytecode =
612 unsafe { Bytecode::new_analyzed(padded, 2, JumpTable::from_static_slice(TABLE, 2)) };
613 assert!(bytecode.inner().jump_table.get().is_some());
614 assert_eq!(bytecode.original_bytes(), bytes!("5b60"));
615 assert_eq!(bytecode.bytecode_ptr(), ptr);
616 let table = bytecode.legacy_jump_table().unwrap();
617 assert!(core::ptr::eq(table.as_slice(), TABLE));
618 assert!(table.is_valid(0));
619 assert!(!table.is_valid(1));
620 }
621
622 #[test]
623 fn empty_and_delegated_jump_tables() {
624 assert!(Bytecode::new().legacy_jump_table().unwrap().is_empty());
625 let delegated = Bytecode::new_eip7702(Address::ZERO);
626 let from_raw = Bytecode::new_eip7702_raw(delegated.original_bytes()).unwrap();
627 for bytecode in [delegated, from_raw] {
628 assert!(bytecode.legacy_jump_table().is_none());
629 assert_eq!(bytecode.bytes_slice(), bytecode.original_byte_slice());
630 assert_eq!(bytecode.bytecode_ptr(), bytecode.original_bytes().as_ptr());
631 assert!(bytecode.inner().jump_table.get().is_none());
632 }
633 }
634
635 #[test]
636 #[cfg(feature = "serde")]
637 fn lazy_analysis_serde_compatibility() {
638 let bytecode = Bytecode::new_legacy(bytes!("5b60"));
639 assert!(bytecode.inner().jump_table.get().is_none());
640 let mut serialized = serde_json::to_value(&bytecode).unwrap();
641 assert_eq!(
642 serialized,
643 serde_json::json!({
644 "LegacyAnalyzed": {
645 "bytecode": std::format!("0x5b60{}", "00".repeat(33)),
646 "original_len": 2,
647 "jump_table": {
648 "order": "bitvec::order::Lsb0",
649 "head": { "width": 8, "index": 0 },
650 "bits": 2,
651 "data": [1]
652 }
653 }
654 })
655 );
656 assert!(bytecode.inner().jump_table.get().is_some());
657
658 serialized["LegacyAnalyzed"]["bytecode"] = serde_json::json!("0x5b60");
660 serialized["LegacyAnalyzed"]["jump_table"]["data"] = serde_json::json!([2]);
661 let restored: Bytecode = serde_json::from_value(serialized).unwrap();
662 assert!(restored.inner().jump_table.get().is_none());
663 assert_eq!(restored, bytecode);
664 assert_eq!(restored.bytecode(), bytecode.bytecode());
665 assert_eq!(restored.legacy_jump_table(), bytecode.legacy_jump_table());
666 }
667
668 #[test]
669 fn bytecode_access_does_not_initialize_jump_table() {
670 let bytecode = Bytecode::new_raw(bytes!("7f"));
671 let cloned = bytecode.clone();
672 assert!(bytecode.inner().jump_table.get().is_none());
673
674 let ptr = bytecode.bytecode_ptr();
676 assert_eq!(bytecode.bytecode().len(), 34);
677 assert_eq!(bytecode.bytecode()[0], opcode::PUSH32);
678 assert_eq!(&bytecode.bytecode()[1..], &[opcode::STOP; 33]);
679 assert_eq!(bytecode.bytecode().as_ptr(), ptr);
680 assert_eq!(bytecode.bytes().as_ptr(), ptr);
681 assert_eq!(bytecode.bytes_ref().as_ptr(), ptr);
682 assert_eq!(bytecode.bytes_slice().as_ptr(), ptr);
683 assert_eq!(cloned.bytecode_ptr(), ptr);
684 assert_eq!(bytecode.original_byte_slice(), &[opcode::PUSH32]);
685 assert!(bytecode.inner().jump_table.get().is_none());
686 let table = bytecode.legacy_jump_table().unwrap();
687 assert_eq!(table.len(), 1);
688 assert!(!table.is_valid(0));
689 }
690
691 #[test]
692 fn existing_zero_suffix_is_original_code() {
693 let mut raw = [0; 34];
694 raw[0] = opcode::PUSH32;
695 let raw = Bytes::copy_from_slice(&raw);
696 let bytecode = Bytecode::new_legacy(raw.clone());
697 assert_eq!(bytecode.bytecode_ptr(), raw.as_ptr());
698 assert_eq!(bytecode.len(), raw.len());
699 assert_eq!(bytecode.original_bytes(), raw);
700 assert_eq!(bytecode.hash_slow(), keccak256(&raw));
701 assert!(bytecode.inner().jump_table.get().is_none());
702 assert_eq!(bytecode.legacy_jump_table().unwrap().len(), raw.len());
703 }
704
705 #[test]
706 fn empty_debug_matches_shared_form() {
707 let empty = Bytecode::new();
708 let shared = Arc::new(empty.inner());
709 let _ = empty.hash_slow();
710 let _ = empty.legacy_jump_table();
711 assert_eq!(format!("{empty:?}"), format!("Bytecode({shared:?})"));
712 }
713
714 #[test]
715 #[cfg(feature = "serde")]
716 fn empty_serde_roundtrip() {
717 let empty = Bytecode::new();
718 let analyzed = unsafe {
719 Bytecode::new_analyzed(Bytes::from_static(&[opcode::STOP]), 0, JumpTable::default())
720 };
721 let serialized = serde_json::to_value(&empty).unwrap();
722 assert_eq!(serialized, serde_json::to_value(&analyzed).unwrap());
723 let restored: Bytecode = serde_json::from_value(serialized).unwrap();
724 assert_eq!(restored, empty);
725 assert!(restored.is_default());
726 assert!(restored.0.is_none());
727 assert_eq!(restored.bytes_slice(), [opcode::STOP]);
728 }
729}