revm_interpreter/
opcode.rs

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