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.kind()
94 .cmp(&other.kind())
95 .then_with(|| self.original_byte_slice().cmp(other.original_byte_slice()))
96 }
97}
98
99impl Sealable for Bytecode {
100 #[inline]
101 fn hash_slow(&self) -> B256 {
102 self.hash_slow()
103 }
104}
105
106impl Bytecode {
107 #[inline]
109 pub fn new() -> Self {
110 static DEFAULT_BYTECODE: OnceLock<Bytecode> = OnceLock::new();
111 DEFAULT_BYTECODE
112 .get_or_init(|| {
113 Self(Arc::new(BytecodeInner {
114 kind: BytecodeKind::LegacyAnalyzed,
115 bytecode: Bytes::from_static(&[opcode::STOP]),
116 original_len: 0,
117 jump_table: JumpTable::default(),
118 hash: {
119 let hash = OnceLock::new();
120 let _ = hash.set(KECCAK_EMPTY);
121 hash
122 },
123 }))
124 })
125 .clone()
126 }
127
128 #[inline]
130 pub fn new_legacy(raw: Bytes) -> Self {
131 if raw.is_empty() {
132 return Self::new();
133 }
134
135 let original_len = raw.len();
136 let (jump_table, bytecode) = analyze_legacy(raw);
137 Self(Arc::new(BytecodeInner {
138 kind: BytecodeKind::LegacyAnalyzed,
139 original_len,
140 bytecode,
141 jump_table,
142 hash: OnceLock::new(),
143 }))
144 }
145
146 #[inline]
152 pub fn new_raw(bytecode: Bytes) -> Self {
153 Self::new_raw_checked(bytecode).expect("Expect correct bytecode")
154 }
155
156 #[inline]
158 pub fn new_eip7702(address: Address) -> Self {
159 let raw: Bytes = [EIP7702_MAGIC_BYTES, &[EIP7702_VERSION], &address[..]]
160 .concat()
161 .into();
162 Self(Arc::new(BytecodeInner {
163 kind: BytecodeKind::Eip7702,
164 original_len: raw.len(),
165 bytecode: raw,
166 jump_table: JumpTable::default(),
167 hash: OnceLock::new(),
168 }))
169 }
170
171 #[inline]
175 pub fn new_raw_checked(bytes: Bytes) -> Result<Self, BytecodeDecodeError> {
176 if bytes.starts_with(EIP7702_MAGIC_BYTES) {
177 Self::new_eip7702_raw(bytes).map_err(Into::into)
178 } else {
179 Ok(Self::new_legacy(bytes))
180 }
181 }
182
183 #[inline]
187 pub fn new_eip7702_raw(bytes: Bytes) -> Result<Self, Eip7702DecodeError> {
188 if bytes.len() != 23 {
189 return Err(Eip7702DecodeError::InvalidLength);
190 }
191 if !bytes.starts_with(EIP7702_MAGIC_BYTES) {
192 return Err(Eip7702DecodeError::InvalidMagic);
193 }
194 if bytes[2] != EIP7702_VERSION {
195 return Err(Eip7702DecodeError::UnsupportedVersion);
196 }
197 Ok(Self(Arc::new(BytecodeInner {
198 kind: BytecodeKind::Eip7702,
199 original_len: bytes.len(),
200 bytecode: bytes,
201 jump_table: JumpTable::default(),
202 hash: OnceLock::new(),
203 })))
204 }
205
206 #[inline]
214 pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self {
215 assert!(
216 original_len <= bytecode.len(),
217 "original_len is greater than bytecode length"
218 );
219 assert!(
220 original_len <= jump_table.len(),
221 "jump table length is less than original length"
222 );
223 assert!(!bytecode.is_empty(), "bytecode cannot be empty");
224 Self(Arc::new(BytecodeInner {
225 kind: BytecodeKind::LegacyAnalyzed,
226 bytecode,
227 original_len,
228 jump_table,
229 hash: OnceLock::new(),
230 }))
231 }
232
233 #[inline]
235 pub fn kind(&self) -> BytecodeKind {
236 self.0.kind
237 }
238
239 #[inline]
241 pub fn is_legacy(&self) -> bool {
242 self.kind() == BytecodeKind::LegacyAnalyzed
243 }
244
245 #[inline]
247 pub fn is_eip7702(&self) -> bool {
248 self.kind() == BytecodeKind::Eip7702
249 }
250
251 #[inline]
253 pub fn eip7702_address(&self) -> Option<Address> {
254 if self.is_eip7702() {
255 Some(Address::from_slice(&self.0.bytecode[3..23]))
256 } else {
257 None
258 }
259 }
260
261 #[inline]
263 pub fn legacy_jump_table(&self) -> Option<&JumpTable> {
264 if self.is_legacy() {
265 Some(&self.0.jump_table)
266 } else {
267 None
268 }
269 }
270
271 #[inline]
273 pub fn hash_slow(&self) -> B256 {
274 *self
275 .0
276 .hash
277 .get_or_init(|| keccak256(self.original_byte_slice()))
278 }
279
280 #[inline]
284 pub fn bytecode(&self) -> &Bytes {
285 &self.0.bytecode
286 }
287
288 #[inline]
290 pub fn bytecode_ptr(&self) -> *const u8 {
291 self.0.bytecode.as_ptr()
292 }
293
294 #[inline]
296 pub fn bytes(&self) -> Bytes {
297 self.0.bytecode.clone()
298 }
299
300 #[inline]
302 pub fn bytes_ref(&self) -> &Bytes {
303 &self.0.bytecode
304 }
305
306 #[inline]
308 pub fn bytes_slice(&self) -> &[u8] {
309 &self.0.bytecode
310 }
311
312 #[inline]
314 pub fn original_bytes(&self) -> Bytes {
315 self.0.bytecode.slice(..self.0.original_len)
316 }
317
318 #[inline]
320 pub fn original_byte_slice(&self) -> &[u8] {
321 &self.0.bytecode[..self.0.original_len]
322 }
323
324 #[inline]
326 pub fn len(&self) -> usize {
327 self.0.original_len
328 }
329
330 #[inline]
332 pub fn is_empty(&self) -> bool {
333 self.0.original_len == 0
334 }
335
336 #[inline]
338 pub fn iter_opcodes(&self) -> crate::BytecodeIterator<'_> {
339 crate::BytecodeIterator::new(self)
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346 use crate::{eip7702::Eip7702DecodeError, opcode};
347 use bitvec::{bitvec, order::Lsb0};
348 use primitives::bytes;
349
350 #[test]
351 fn test_new_empty() {
352 for bytecode in [
353 Bytecode::default(),
354 Bytecode::new(),
355 Bytecode::new().clone(),
356 Bytecode::new_legacy(Bytes::new()),
357 ] {
358 assert_eq!(bytecode.kind(), BytecodeKind::LegacyAnalyzed);
359 assert_eq!(bytecode.len(), 0);
360 assert_eq!(bytecode.bytes_slice(), [opcode::STOP]);
361 }
362 }
363
364 #[test]
365 fn test_new_analyzed() {
366 let raw = Bytes::from_static(&[opcode::PUSH1, 0x01]);
367 let bytecode = Bytecode::new_legacy(raw);
368 let _ = Bytecode::new_analyzed(
369 bytecode.bytecode().clone(),
370 bytecode.len(),
371 bytecode.legacy_jump_table().unwrap().clone(),
372 );
373 }
374
375 #[test]
376 #[should_panic(expected = "original_len is greater than bytecode length")]
377 fn test_panic_on_large_original_len() {
378 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
379 let _ = Bytecode::new_analyzed(
380 bytecode.bytecode().clone(),
381 100,
382 bytecode.legacy_jump_table().unwrap().clone(),
383 );
384 }
385
386 #[test]
387 #[should_panic(expected = "jump table length is less than original length")]
388 fn test_panic_on_short_jump_table() {
389 let bytecode = Bytecode::new_legacy(Bytes::from_static(&[opcode::PUSH1, 0x01]));
390 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 1]);
391 let _ = Bytecode::new_analyzed(bytecode.bytecode().clone(), bytecode.len(), jump_table);
392 }
393
394 #[test]
395 #[should_panic(expected = "bytecode cannot be empty")]
396 fn test_panic_on_empty_bytecode() {
397 let bytecode = Bytes::from_static(&[]);
398 let jump_table = JumpTable::new(bitvec![u8, Lsb0; 0; 0]);
399 let _ = Bytecode::new_analyzed(bytecode, 0, jump_table);
400 }
401
402 #[test]
403 fn eip7702_sanity_decode() {
404 let raw = bytes!("ef01deadbeef");
405 assert_eq!(
406 Bytecode::new_eip7702_raw(raw),
407 Err(Eip7702DecodeError::InvalidLength)
408 );
409
410 let raw = bytes!("ef0101deadbeef00000000000000000000000000000000");
411 assert_eq!(
412 Bytecode::new_eip7702_raw(raw),
413 Err(Eip7702DecodeError::UnsupportedVersion)
414 );
415
416 let raw = bytes!("ef0100deadbeef00000000000000000000000000000000");
417 let bytecode = Bytecode::new_eip7702_raw(raw.clone()).unwrap();
418 assert!(bytecode.is_eip7702());
419 assert_eq!(
420 bytecode.eip7702_address(),
421 Some(Address::from_slice(&raw[3..]))
422 );
423 assert_eq!(bytecode.original_bytes(), raw);
424 }
425
426 #[test]
427 fn eip7702_from_address() {
428 let address = Address::new([0x01; 20]);
429 let bytecode = Bytecode::new_eip7702(address);
430 assert_eq!(bytecode.eip7702_address(), Some(address));
431 assert_eq!(
432 bytecode.original_bytes(),
433 bytes!("ef01000101010101010101010101010101010101010101")
434 );
435 }
436}