revm_bytecode/
opcode.rs

1//! EVM opcode definitions and utilities. It contains opcode information and utilities to work with opcodes.
2
3#[cfg(feature = "parse")]
4pub mod parse;
5
6use core::{fmt, ptr::NonNull};
7
8/// An EVM opcode
9///
10/// This is always a valid opcode, as declared in the [`opcode`][self] module or the
11/// [`OPCODE_INFO`] constant.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[repr(transparent)]
14pub struct OpCode(u8);
15
16impl fmt::Display for OpCode {
17    /// Formats the opcode as a string
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        let n = self.get();
20        if let Some(val) = OPCODE_INFO[n as usize] {
21            f.write_str(val.name())
22        } else {
23            write!(f, "UNKNOWN(0x{n:02X})")
24        }
25    }
26}
27
28impl OpCode {
29    /// Instantiates a new opcode from a u8.
30    ///
31    /// Returns None if the opcode is not valid.
32    #[inline]
33    pub const fn new(opcode: u8) -> Option<Self> {
34        match OPCODE_INFO[opcode as usize] {
35            Some(_) => Some(Self(opcode)),
36            None => None,
37        }
38    }
39
40    /// Returns true if the opcode is a jump destination.
41    #[inline]
42    pub const fn is_jumpdest(&self) -> bool {
43        self.0 == JUMPDEST
44    }
45
46    /// Takes a u8 and returns true if it is a jump destination.
47    #[inline]
48    pub const fn is_jumpdest_by_op(opcode: u8) -> bool {
49        if let Some(opcode) = Self::new(opcode) {
50            opcode.is_jumpdest()
51        } else {
52            false
53        }
54    }
55
56    /// Returns true if the opcode is a legacy jump instruction.
57    #[inline]
58    pub const fn is_jump(self) -> bool {
59        self.0 == JUMP
60    }
61
62    /// Takes a u8 and returns true if it is a jump instruction.
63    #[inline]
64    pub const fn is_jump_by_op(opcode: u8) -> bool {
65        if let Some(opcode) = Self::new(opcode) {
66            opcode.is_jump()
67        } else {
68            false
69        }
70    }
71
72    /// Returns true if the opcode is a `PUSH` instruction.
73    #[inline]
74    pub const fn is_push(self) -> bool {
75        self.0 >= PUSH1 && self.0 <= PUSH32
76    }
77
78    /// Takes a u8 and returns true if it is a push instruction.
79    #[inline]
80    pub fn is_push_by_op(opcode: u8) -> bool {
81        if let Some(opcode) = Self::new(opcode) {
82            opcode.is_push()
83        } else {
84            false
85        }
86    }
87
88    /// Instantiates a new opcode from a u8 without checking if it is valid.
89    ///
90    /// # Safety
91    ///
92    /// All code using `Opcode` values assume that they are valid opcodes, so providing an invalid
93    /// opcode may cause undefined behavior.
94    #[inline]
95    pub unsafe fn new_unchecked(opcode: u8) -> Self {
96        Self(opcode)
97    }
98
99    /// Returns the opcode as a string. This is the inverse of [`parse`](Self::parse).
100    #[doc(alias = "name")]
101    #[inline]
102    pub const fn as_str(self) -> &'static str {
103        self.info().name()
104    }
105
106    /// Returns the opcode name.
107    #[inline]
108    pub const fn name_by_op(opcode: u8) -> &'static str {
109        if let Some(opcode) = Self::new(opcode) {
110            opcode.as_str()
111        } else {
112            "Unknown"
113        }
114    }
115
116    /// Returns the number of input stack elements.
117    #[inline]
118    pub const fn inputs(&self) -> u8 {
119        self.info().inputs()
120    }
121
122    /// Returns the number of output stack elements.
123    #[inline]
124    pub const fn outputs(&self) -> u8 {
125        self.info().outputs()
126    }
127
128    /// Calculates the difference between the number of input and output stack elements.
129    #[inline]
130    pub const fn io_diff(&self) -> i16 {
131        self.info().io_diff()
132    }
133
134    /// Returns the opcode information for the given opcode.
135    /// Check [OpCodeInfo] for more information.
136    #[inline]
137    pub const fn info_by_op(opcode: u8) -> Option<OpCodeInfo> {
138        if let Some(opcode) = Self::new(opcode) {
139            Some(opcode.info())
140        } else {
141            None
142        }
143    }
144
145    /// Returns the opcode as a usize.
146    #[inline]
147    pub const fn as_usize(&self) -> usize {
148        self.0 as usize
149    }
150
151    /// Returns the opcode information.
152    #[inline]
153    pub const fn info(&self) -> OpCodeInfo {
154        if let Some(t) = OPCODE_INFO[self.0 as usize] {
155            t
156        } else {
157            panic!("opcode not found")
158        }
159    }
160
161    /// Returns the number of both input and output stack elements.
162    ///
163    /// Can be slightly faster that calling `inputs` and `outputs` separately.
164    pub const fn input_output(&self) -> (u8, u8) {
165        let info = self.info();
166        (info.inputs, info.outputs)
167    }
168
169    /// Returns the opcode as a u8.
170    #[inline]
171    pub const fn get(self) -> u8 {
172        self.0
173    }
174
175    /// Returns true if the opcode modifies memory.
176    ///
177    /// <https://bluealloy.github.io/revm/crates/interpreter/memory.html#opcodes>
178    ///
179    /// <https://github.com/crytic/evm-opcodes>
180    #[inline]
181    pub const fn modifies_memory(&self) -> bool {
182        matches!(
183            *self,
184            OpCode::EXTCODECOPY
185                | OpCode::MLOAD
186                | OpCode::MSTORE
187                | OpCode::MSTORE8
188                | OpCode::MCOPY
189                | OpCode::CODECOPY
190                | OpCode::CALLDATACOPY
191                | OpCode::RETURNDATACOPY
192                | OpCode::CALL
193                | OpCode::CALLCODE
194                | OpCode::DELEGATECALL
195                | OpCode::STATICCALL
196                | OpCode::DATACOPY
197                | OpCode::EOFCREATE
198                | OpCode::RETURNCONTRACT
199                | OpCode::EXTCALL
200                | OpCode::EXTDELEGATECALL
201                | OpCode::EXTSTATICCALL
202        )
203    }
204}
205
206/// Information about opcode, such as name, and stack inputs and outputs
207#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
208pub struct OpCodeInfo {
209    /// Invariant: `(name_ptr, name_len)` is a [`&'static str`][str].
210    ///
211    /// It is a shorted variant of [`str`] as
212    /// the name length is always less than 256 characters.
213    name_ptr: NonNull<u8>,
214    name_len: u8,
215    /// Stack inputs
216    inputs: u8,
217    /// Stack outputs
218    outputs: u8,
219    /// Number of intermediate bytes
220    ///
221    /// RJUMPV is a special case where the bytes len depends on bytecode value,
222    /// for RJUMV size will be set to one byte as it is the minimum immediate size.
223    immediate_size: u8,
224    /// Used by EOF verification
225    ///
226    /// All not EOF opcodes are marked false.
227    not_eof: bool,
228    /// If the opcode stops execution. aka STOP, RETURN, ..
229    terminating: bool,
230}
231
232// SAFETY: The `NonNull` is just a `&'static str`.
233unsafe impl Send for OpCodeInfo {}
234unsafe impl Sync for OpCodeInfo {}
235
236impl fmt::Debug for OpCodeInfo {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        f.debug_struct("OpCodeInfo")
239            .field("name", &self.name())
240            .field("inputs", &self.inputs())
241            .field("outputs", &self.outputs())
242            .field("not_eof", &self.is_disabled_in_eof())
243            .field("terminating", &self.is_terminating())
244            .field("immediate_size", &self.immediate_size())
245            .finish()
246    }
247}
248
249impl OpCodeInfo {
250    /// Creates a new opcode info with the given name and default values.
251    pub const fn new(name: &'static str) -> Self {
252        assert!(name.len() < 256, "opcode name is too long");
253        Self {
254            name_ptr: unsafe { NonNull::new_unchecked(name.as_ptr().cast_mut()) },
255            name_len: name.len() as u8,
256            inputs: 0,
257            outputs: 0,
258            not_eof: false,
259            terminating: false,
260            immediate_size: 0,
261        }
262    }
263
264    /// Returns the opcode name.
265    #[inline]
266    pub const fn name(&self) -> &'static str {
267        // SAFETY: `self.name_*` can only be initialized with a valid `&'static str`.
268        unsafe {
269            let slice = std::slice::from_raw_parts(self.name_ptr.as_ptr(), self.name_len as usize);
270            core::str::from_utf8_unchecked(slice)
271        }
272    }
273
274    /// Calculates the difference between the number of input and output stack elements.
275    #[inline]
276    pub const fn io_diff(&self) -> i16 {
277        self.outputs as i16 - self.inputs as i16
278    }
279
280    /// Returns the number of input stack elements.
281    #[inline]
282    pub const fn inputs(&self) -> u8 {
283        self.inputs
284    }
285
286    /// Returns the number of output stack elements.
287    #[inline]
288    pub const fn outputs(&self) -> u8 {
289        self.outputs
290    }
291
292    /// Returns whether this opcode is disabled in EOF bytecode.
293    #[inline]
294    pub const fn is_disabled_in_eof(&self) -> bool {
295        self.not_eof
296    }
297
298    /// Returns whether this opcode terminates execution, e.g. `STOP`, `RETURN`, etc.
299    #[inline]
300    pub const fn is_terminating(&self) -> bool {
301        self.terminating
302    }
303
304    /// Returns the size of the immediate value in bytes.
305    #[inline]
306    pub const fn immediate_size(&self) -> u8 {
307        self.immediate_size
308    }
309}
310
311/// Sets the EOF flag to false.
312#[inline]
313pub const fn not_eof(mut op: OpCodeInfo) -> OpCodeInfo {
314    op.not_eof = true;
315    op
316}
317
318/// Used for [`OPCODE_INFO`] to set the immediate bytes number in the [`OpCodeInfo`].
319///
320/// RJUMPV is special case where the bytes len is depending on bytecode value,
321/// for RJUMPV size will be set to one byte while minimum is two.
322#[inline]
323pub const fn immediate_size(mut op: OpCodeInfo, n: u8) -> OpCodeInfo {
324    op.immediate_size = n;
325    op
326}
327
328/// Use for [`OPCODE_INFO`] to set the terminating flag to true in the [`OpCodeInfo`].
329#[inline]
330pub const fn terminating(mut op: OpCodeInfo) -> OpCodeInfo {
331    op.terminating = true;
332    op
333}
334
335/// Use for [`OPCODE_INFO`] to sets the number of stack inputs and outputs in the [`OpCodeInfo`].
336#[inline]
337pub const fn stack_io(mut op: OpCodeInfo, inputs: u8, outputs: u8) -> OpCodeInfo {
338    op.inputs = inputs;
339    op.outputs = outputs;
340    op
341}
342
343/// Alias for the [`JUMPDEST`] opcode
344pub const NOP: u8 = JUMPDEST;
345
346/// Created all opcodes constants and two maps:
347///  * `OPCODE_INFO` maps opcode number to the opcode info
348///  * `NAME_TO_OPCODE` that maps opcode name to the opcode number.
349macro_rules! opcodes {
350    ($($val:literal => $name:ident => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => {
351        // Constants for each opcode. This also takes care of duplicate names.
352        $(
353            #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
354            pub const $name: u8 = $val;
355        )*
356        impl OpCode {$(
357            #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
358            pub const $name: Self = Self($val);
359        )*}
360
361        /// Maps each opcode to its info.
362        pub static OPCODE_INFO: [Option<OpCodeInfo>; 256] = {
363            let mut map = [None; 256];
364            let mut prev: u8 = 0;
365            $(
366                let val: u8 = $val;
367                assert!(val == 0 || val > prev, "opcodes must be sorted in ascending order");
368                prev = val;
369                let info = OpCodeInfo::new(stringify!($name));
370                $(
371                let info = $modifier(info, $($($modifier_arg),*)?);
372                )*
373                map[$val] = Some(info);
374            )*
375            let _ = prev;
376            map
377        };
378
379
380        /// Maps each name to its opcode.
381        #[cfg(feature = "parse")]
382        pub(crate) static NAME_TO_OPCODE: phf::Map<&'static str, OpCode> = stringify_with_cb! { phf_map_cb; $($name)* };
383    };
384}
385
386/// Callback for creating a [`phf`] map with `stringify_with_cb`.
387#[cfg(feature = "parse")]
388macro_rules! phf_map_cb {
389    ($(#[doc = $s:literal] $id:ident)*) => {
390        phf::phf_map! {
391            $($s => OpCode::$id),*
392        }
393    };
394}
395
396/// Stringifies identifiers with `paste` so that they are available as literals.
397///
398/// This doesn't work with [`stringify!`] because it cannot be expanded inside of another macro.
399#[cfg(feature = "parse")]
400macro_rules! stringify_with_cb {
401    ($callback:ident; $($id:ident)*) => { paste::paste! {
402        $callback! { $(#[doc = "" $id ""] $id)* }
403    }};
404}
405
406// When adding new opcodes:
407// 1. add the opcode to the list below; make sure it's sorted by opcode value
408// 2. implement the opcode in the corresponding module;
409//    the function signature must be the exact same as the others
410opcodes! {
411    0x00 => STOP     => stack_io(0, 0), terminating;
412    0x01 => ADD      => stack_io(2, 1);
413    0x02 => MUL      => stack_io(2, 1);
414    0x03 => SUB      => stack_io(2, 1);
415    0x04 => DIV      => stack_io(2, 1);
416    0x05 => SDIV     => stack_io(2, 1);
417    0x06 => MOD      => stack_io(2, 1);
418    0x07 => SMOD     => stack_io(2, 1);
419    0x08 => ADDMOD   => stack_io(3, 1);
420    0x09 => MULMOD   => stack_io(3, 1);
421    0x0A => EXP      => stack_io(2, 1);
422    0x0B => SIGNEXTEND => stack_io(2, 1);
423    // 0x0C
424    // 0x0D
425    // 0x0E
426    // 0x0F
427    0x10 => LT   => stack_io(2, 1);
428    0x11 => GT   => stack_io(2, 1);
429    0x12 => SLT  => stack_io(2, 1);
430    0x13 => SGT  => stack_io(2, 1);
431    0x14 => EQ   => stack_io(2, 1);
432    0x15 => ISZERO => stack_io(1, 1);
433    0x16 => AND  => stack_io(2, 1);
434    0x17 => OR   => stack_io(2, 1);
435    0x18 => XOR  => stack_io(2, 1);
436    0x19 => NOT  => stack_io(1, 1);
437    0x1A => BYTE => stack_io(2, 1);
438    0x1B => SHL  => stack_io(2, 1);
439    0x1C => SHR  => stack_io(2, 1);
440    0x1D => SAR  => stack_io(2, 1);
441    // 0x1E
442    // 0x1F
443    0x20 => KECCAK256 => stack_io(2, 1);
444    // 0x21
445    // 0x22
446    // 0x23
447    // 0x24
448    // 0x25
449    // 0x26
450    // 0x27
451    // 0x28
452    // 0x29
453    // 0x2A
454    // 0x2B
455    // 0x2C
456    // 0x2D
457    // 0x2E
458    // 0x2F
459    0x30 => ADDRESS    => stack_io(0, 1);
460    0x31 => BALANCE    => stack_io(1, 1);
461    0x32 => ORIGIN     => stack_io(0, 1);
462    0x33 => CALLER     => stack_io(0, 1);
463    0x34 => CALLVALUE  => stack_io(0, 1);
464    0x35 => CALLDATALOAD => stack_io(1, 1);
465    0x36 => CALLDATASIZE => stack_io(0, 1);
466    0x37 => CALLDATACOPY => stack_io(3, 0);
467    0x38 => CODESIZE   => stack_io(0, 1), not_eof;
468    0x39 => CODECOPY   => stack_io(3, 0), not_eof;
469
470    0x3A => GASPRICE     => stack_io(0, 1);
471    0x3B => EXTCODESIZE  => stack_io(1, 1), not_eof;
472    0x3C => EXTCODECOPY  => stack_io(4, 0), not_eof;
473    0x3D => RETURNDATASIZE => stack_io(0, 1);
474    0x3E => RETURNDATACOPY => stack_io(3, 0);
475    0x3F => EXTCODEHASH  => stack_io(1, 1), not_eof;
476    0x40 => BLOCKHASH    => stack_io(1, 1);
477    0x41 => COINBASE     => stack_io(0, 1);
478    0x42 => TIMESTAMP    => stack_io(0, 1);
479    0x43 => NUMBER       => stack_io(0, 1);
480    0x44 => DIFFICULTY   => stack_io(0, 1);
481    0x45 => GASLIMIT     => stack_io(0, 1);
482    0x46 => CHAINID      => stack_io(0, 1);
483    0x47 => SELFBALANCE  => stack_io(0, 1);
484    0x48 => BASEFEE      => stack_io(0, 1);
485    0x49 => BLOBHASH     => stack_io(1, 1);
486    0x4A => BLOBBASEFEE  => stack_io(0, 1);
487    // 0x4B
488    // 0x4C
489    // 0x4D
490    // 0x4E
491    // 0x4F
492    0x50 => POP      => stack_io(1, 0);
493    0x51 => MLOAD    => stack_io(1, 1);
494    0x52 => MSTORE   => stack_io(2, 0);
495    0x53 => MSTORE8  => stack_io(2, 0);
496    0x54 => SLOAD    => stack_io(1, 1);
497    0x55 => SSTORE   => stack_io(2, 0);
498    0x56 => JUMP     => stack_io(1, 0), not_eof;
499    0x57 => JUMPI    => stack_io(2, 0), not_eof;
500    0x58 => PC       => stack_io(0, 1), not_eof;
501    0x59 => MSIZE    => stack_io(0, 1);
502    0x5A => GAS      => stack_io(0, 1), not_eof;
503    0x5B => JUMPDEST => stack_io(0, 0);
504    0x5C => TLOAD    => stack_io(1, 1);
505    0x5D => TSTORE   => stack_io(2, 0);
506    0x5E => MCOPY    => stack_io(3, 0);
507
508    0x5F => PUSH0  => stack_io(0, 1);
509    0x60 => PUSH1  => stack_io(0, 1), immediate_size(1);
510    0x61 => PUSH2  => stack_io(0, 1), immediate_size(2);
511    0x62 => PUSH3  => stack_io(0, 1), immediate_size(3);
512    0x63 => PUSH4  => stack_io(0, 1), immediate_size(4);
513    0x64 => PUSH5  => stack_io(0, 1), immediate_size(5);
514    0x65 => PUSH6  => stack_io(0, 1), immediate_size(6);
515    0x66 => PUSH7  => stack_io(0, 1), immediate_size(7);
516    0x67 => PUSH8  => stack_io(0, 1), immediate_size(8);
517    0x68 => PUSH9  => stack_io(0, 1), immediate_size(9);
518    0x69 => PUSH10 => stack_io(0, 1), immediate_size(10);
519    0x6A => PUSH11 => stack_io(0, 1), immediate_size(11);
520    0x6B => PUSH12 => stack_io(0, 1), immediate_size(12);
521    0x6C => PUSH13 => stack_io(0, 1), immediate_size(13);
522    0x6D => PUSH14 => stack_io(0, 1), immediate_size(14);
523    0x6E => PUSH15 => stack_io(0, 1), immediate_size(15);
524    0x6F => PUSH16 => stack_io(0, 1), immediate_size(16);
525    0x70 => PUSH17 => stack_io(0, 1), immediate_size(17);
526    0x71 => PUSH18 => stack_io(0, 1), immediate_size(18);
527    0x72 => PUSH19 => stack_io(0, 1), immediate_size(19);
528    0x73 => PUSH20 => stack_io(0, 1), immediate_size(20);
529    0x74 => PUSH21 => stack_io(0, 1), immediate_size(21);
530    0x75 => PUSH22 => stack_io(0, 1), immediate_size(22);
531    0x76 => PUSH23 => stack_io(0, 1), immediate_size(23);
532    0x77 => PUSH24 => stack_io(0, 1), immediate_size(24);
533    0x78 => PUSH25 => stack_io(0, 1), immediate_size(25);
534    0x79 => PUSH26 => stack_io(0, 1), immediate_size(26);
535    0x7A => PUSH27 => stack_io(0, 1), immediate_size(27);
536    0x7B => PUSH28 => stack_io(0, 1), immediate_size(28);
537    0x7C => PUSH29 => stack_io(0, 1), immediate_size(29);
538    0x7D => PUSH30 => stack_io(0, 1), immediate_size(30);
539    0x7E => PUSH31 => stack_io(0, 1), immediate_size(31);
540    0x7F => PUSH32 => stack_io(0, 1), immediate_size(32);
541
542    0x80 => DUP1  => stack_io(1, 2);
543    0x81 => DUP2  => stack_io(2, 3);
544    0x82 => DUP3  => stack_io(3, 4);
545    0x83 => DUP4  => stack_io(4, 5);
546    0x84 => DUP5  => stack_io(5, 6);
547    0x85 => DUP6  => stack_io(6, 7);
548    0x86 => DUP7  => stack_io(7, 8);
549    0x87 => DUP8  => stack_io(8, 9);
550    0x88 => DUP9  => stack_io(9, 10);
551    0x89 => DUP10 => stack_io(10, 11);
552    0x8A => DUP11 => stack_io(11, 12);
553    0x8B => DUP12 => stack_io(12, 13);
554    0x8C => DUP13 => stack_io(13, 14);
555    0x8D => DUP14 => stack_io(14, 15);
556    0x8E => DUP15 => stack_io(15, 16);
557    0x8F => DUP16 => stack_io(16, 17);
558
559    0x90 => SWAP1  => stack_io(2, 2);
560    0x91 => SWAP2  => stack_io(3, 3);
561    0x92 => SWAP3  => stack_io(4, 4);
562    0x93 => SWAP4  => stack_io(5, 5);
563    0x94 => SWAP5  => stack_io(6, 6);
564    0x95 => SWAP6  => stack_io(7, 7);
565    0x96 => SWAP7  => stack_io(8, 8);
566    0x97 => SWAP8  => stack_io(9, 9);
567    0x98 => SWAP9  => stack_io(10, 10);
568    0x99 => SWAP10 => stack_io(11, 11);
569    0x9A => SWAP11 => stack_io(12, 12);
570    0x9B => SWAP12 => stack_io(13, 13);
571    0x9C => SWAP13 => stack_io(14, 14);
572    0x9D => SWAP14 => stack_io(15, 15);
573    0x9E => SWAP15 => stack_io(16, 16);
574    0x9F => SWAP16 => stack_io(17, 17);
575
576    0xA0 => LOG0 => stack_io(2, 0);
577    0xA1 => LOG1 => stack_io(3, 0);
578    0xA2 => LOG2 => stack_io(4, 0);
579    0xA3 => LOG3 => stack_io(5, 0);
580    0xA4 => LOG4 => stack_io(6, 0);
581    // 0xA5
582    // 0xA6
583    // 0xA7
584    // 0xA8
585    // 0xA9
586    // 0xAA
587    // 0xAB
588    // 0xAC
589    // 0xAD
590    // 0xAE
591    // 0xAF
592    // 0xB0
593    // 0xB1
594    // 0xB2
595    // 0xB3
596    // 0xB4
597    // 0xB5
598    // 0xB6
599    // 0xB7
600    // 0xB8
601    // 0xB9
602    // 0xBA
603    // 0xBB
604    // 0xBC
605    // 0xBD
606    // 0xBE
607    // 0xBF
608    // 0xC0
609    // 0xC1
610    // 0xC2
611    // 0xC3
612    // 0xC4
613    // 0xC5
614    // 0xC6
615    // 0xC7
616    // 0xC8
617    // 0xC9
618    // 0xCA
619    // 0xCB
620    // 0xCC
621    // 0xCD
622    // 0xCE
623    // 0xCF
624    0xD0 => DATALOAD=> stack_io(1, 1);
625    0xD1 => DATALOADN => stack_io(0, 1), immediate_size(2);
626    0xD2 => DATASIZE=> stack_io(0, 1);
627    0xD3 => DATACOPY=> stack_io(3, 0);
628    // 0xD4
629    // 0xD5
630    // 0xD6
631    // 0xD7
632    // 0xD8
633    // 0xD9
634    // 0xDA
635    // 0xDB
636    // 0xDC
637    // 0xDD
638    // 0xDE
639    // 0xDF
640    0xE0 => RJUMP    => stack_io(0, 0), immediate_size(2), terminating;
641    0xE1 => RJUMPI   => stack_io(1, 0), immediate_size(2);
642    0xE2 => RJUMPV   => stack_io(1, 0), immediate_size(1);
643    0xE3 => CALLF    => stack_io(0, 0), immediate_size(2);
644    0xE4 => RETF     => stack_io(0, 0), terminating;
645    0xE5 => JUMPF    => stack_io(0, 0), immediate_size(2), terminating;
646    0xE6 => DUPN     => stack_io(0, 1), immediate_size(1);
647    0xE7 => SWAPN    => stack_io(0, 0), immediate_size(1);
648    0xE8 => EXCHANGE => stack_io(0, 0), immediate_size(1);
649    // 0xE9
650    // 0xEA
651    // 0xEB
652    0xEC => EOFCREATE      => stack_io(4, 1), immediate_size(1);
653    0xED => TXCREATE       => stack_io(5, 1);
654    0xEE => RETURNCONTRACT => stack_io(2, 0), immediate_size(1), terminating;
655    // 0xEF
656    0xF0 => CREATE       => stack_io(3, 1), not_eof;
657    0xF1 => CALL         => stack_io(7, 1), not_eof;
658    0xF2 => CALLCODE     => stack_io(7, 1), not_eof;
659    0xF3 => RETURN       => stack_io(2, 0), terminating;
660    0xF4 => DELEGATECALL => stack_io(6, 1), not_eof;
661    0xF5 => CREATE2      => stack_io(4, 1), not_eof;
662    // 0xF6
663    0xF7 => RETURNDATALOAD  => stack_io(1, 1);
664    0xF8 => EXTCALL         => stack_io(4, 1);
665    0xF9 => EXTDELEGATECALL => stack_io(3, 1);
666    0xFA => STATICCALL      => stack_io(6, 1), not_eof;
667    0xFB => EXTSTATICCALL   => stack_io(3, 1);
668    // 0xFC
669    0xFD => REVERT       => stack_io(2, 0), terminating;
670    0xFE => INVALID      => stack_io(0, 0), terminating;
671    0xFF => SELFDESTRUCT => stack_io(1, 0), not_eof, terminating;
672}
673
674#[cfg(test)]
675mod tests {
676    use super::*;
677
678    #[test]
679    fn test_opcode() {
680        let opcode = OpCode::new(0x00).unwrap();
681        assert!(!opcode.is_jumpdest());
682        assert!(!opcode.is_jump());
683        assert!(!opcode.is_push());
684        assert_eq!(opcode.as_str(), "STOP");
685        assert_eq!(opcode.get(), 0x00);
686    }
687
688    #[test]
689    fn test_eof_disable() {
690        const REJECTED_IN_EOF: &[u8] = &[
691            0x38, 0x39, 0x3b, 0x3c, 0x3f, 0x5a, 0xf1, 0xf2, 0xf4, 0xfa, 0xff,
692        ];
693
694        for opcode in REJECTED_IN_EOF {
695            let opcode = OpCode::new(*opcode).unwrap();
696            assert!(
697                opcode.info().is_disabled_in_eof(),
698                "not disabled in EOF: {opcode:#?}",
699            );
700        }
701    }
702
703    #[test]
704    fn test_immediate_size() {
705        let mut expected = [0u8; 256];
706        // PUSH opcodes
707        for push in PUSH1..=PUSH32 {
708            expected[push as usize] = push - PUSH1 + 1;
709        }
710        expected[DATALOADN as usize] = 2;
711        expected[RJUMP as usize] = 2;
712        expected[RJUMPI as usize] = 2;
713        expected[RJUMPV as usize] = 1;
714        expected[CALLF as usize] = 2;
715        expected[JUMPF as usize] = 2;
716        expected[DUPN as usize] = 1;
717        expected[SWAPN as usize] = 1;
718        expected[EXCHANGE as usize] = 1;
719        expected[EOFCREATE as usize] = 1;
720        expected[RETURNCONTRACT as usize] = 1;
721
722        for (i, opcode) in OPCODE_INFO.iter().enumerate() {
723            if let Some(opcode) = opcode {
724                assert_eq!(
725                    opcode.immediate_size(),
726                    expected[i],
727                    "immediate_size check failed for {opcode:#?}",
728                );
729            }
730        }
731    }
732
733    #[test]
734    fn test_enabled_opcodes() {
735        // List obtained from https://eips.ethereum.org/EIPS/eip-3670
736        let opcodes = [
737            0x10..=0x1d,
738            0x20..=0x20,
739            0x30..=0x3f,
740            0x40..=0x48,
741            0x50..=0x5b,
742            0x54..=0x5f,
743            0x60..=0x6f,
744            0x70..=0x7f,
745            0x80..=0x8f,
746            0x90..=0x9f,
747            0xa0..=0xa4,
748            0xf0..=0xf5,
749            0xfa..=0xfa,
750            0xfd..=0xfd,
751            //0xfe,
752            0xff..=0xff,
753        ];
754        for i in opcodes {
755            for opcode in i {
756                OpCode::new(opcode).expect("Opcode should be valid and enabled");
757            }
758        }
759    }
760
761    #[test]
762    fn count_opcodes() {
763        let mut opcode_num = 0;
764        let mut eof_opcode_num = 0;
765        for opcode in OPCODE_INFO.into_iter().flatten() {
766            opcode_num += 1;
767            if !opcode.is_disabled_in_eof() {
768                eof_opcode_num += 1;
769            }
770        }
771        assert_eq!(opcode_num, 169);
772        assert_eq!(eof_opcode_num, 153);
773    }
774
775    #[test]
776    fn test_terminating_opcodes() {
777        let terminating = [
778            RETF,
779            REVERT,
780            RETURN,
781            INVALID,
782            SELFDESTRUCT,
783            RETURNCONTRACT,
784            STOP,
785            RJUMP,
786            JUMPF,
787        ];
788        let mut opcodes = [false; 256];
789        for terminating in terminating.iter() {
790            opcodes[*terminating as usize] = true;
791        }
792
793        for (i, opcode) in OPCODE_INFO.into_iter().enumerate() {
794            assert_eq!(
795                opcode.map(|opcode| opcode.terminating).unwrap_or_default(),
796                opcodes[i],
797                "Opcode {:?} terminating check failed.",
798                opcode
799            );
800        }
801    }
802
803    #[test]
804    #[cfg(feature = "parse")]
805    fn test_parsing() {
806        for i in 0..=u8::MAX {
807            if let Some(op) = OpCode::new(i) {
808                assert_eq!(OpCode::parse(op.as_str()), Some(op));
809            }
810        }
811    }
812}