rtvm_interpreter/
opcode.rs

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