Skip to main content

ethrex_levm/
opcodes.rs

1use crate::{
2    errors::{OpcodeResult, VMError},
3    opcode_handlers::{
4        OpInvalidHandler, OpStopHandler, OpcodeHandler, arithmetic::*, bitwise_comparison::*,
5        block::*, dup::*, environment::*, exchange::*, frame_tx::*, keccak::*, logging::*, push::*,
6        stack_memory_storage_flow::*, system::*,
7    },
8    vm::VM,
9};
10use ethrex_common::types::Fork;
11use std::cell::OnceCell;
12use strum::EnumString;
13
14#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, EnumString, Hash)]
15pub enum Opcode {
16    // Stop and Arithmetic Operations
17    STOP = 0x00,
18    ADD = 0x01,
19    MUL = 0x02,
20    SUB = 0x03,
21    DIV = 0x04,
22    SDIV = 0x05,
23    MOD = 0x06,
24    SMOD = 0x07,
25    ADDMOD = 0x08,
26    MULMOD = 0x09,
27    EXP = 0x0A,
28    SIGNEXTEND = 0x0B,
29
30    // Comparison & Bitwise Logic Operations
31    LT = 0x10,
32    GT = 0x11,
33    SLT = 0x12,
34    SGT = 0x13,
35    EQ = 0x14,
36    ISZERO = 0x15,
37    AND = 0x16,
38    OR = 0x17,
39    XOR = 0x18,
40    NOT = 0x19,
41    BYTE = 0x1A,
42    SHL = 0x1B,
43    SHR = 0x1C,
44    SAR = 0x1D,
45    CLZ = 0x1E,
46
47    // KECCAK256
48    KECCAK256 = 0x20,
49
50    // Environmental Information
51    ADDRESS = 0x30,
52    BALANCE = 0x31,
53    ORIGIN = 0x32,
54    CALLER = 0x33,
55    CALLVALUE = 0x34,
56    CALLDATALOAD = 0x35,
57    CALLDATASIZE = 0x36,
58    CALLDATACOPY = 0x37,
59    CODESIZE = 0x38,
60    CODECOPY = 0x39,
61    GASPRICE = 0x3A,
62    EXTCODESIZE = 0x3B,
63    EXTCODECOPY = 0x3C,
64    RETURNDATASIZE = 0x3D,
65    RETURNDATACOPY = 0x3E,
66    EXTCODEHASH = 0x3F,
67
68    // Block Information
69    BLOCKHASH = 0x40,
70    COINBASE = 0x41,
71    TIMESTAMP = 0x42,
72    NUMBER = 0x43,
73    PREVRANDAO = 0x44,
74    GASLIMIT = 0x45,
75    CHAINID = 0x46,
76    SELFBALANCE = 0x47,
77    BASEFEE = 0x48,
78    BLOBHASH = 0x49,
79    BLOBBASEFEE = 0x4A,
80    SLOTNUM = 0x4B,
81
82    // Stack, Memory, Storage, and Flow Operations
83    POP = 0x50,
84    MLOAD = 0x51,
85    MSTORE = 0x52,
86    MSTORE8 = 0x53,
87    SLOAD = 0x54,
88    SSTORE = 0x55,
89    JUMP = 0x56,
90    JUMPI = 0x57,
91    PC = 0x58,
92    MSIZE = 0x59,
93    GAS = 0x5A,
94    JUMPDEST = 0x5B,
95    TLOAD = 0x5C,
96    TSTORE = 0x5D,
97    MCOPY = 0x5E,
98
99    // Push Operations
100    PUSH0 = 0x5F,
101    PUSH1 = 0x60,
102    PUSH2 = 0x61,
103    PUSH3 = 0x62,
104    PUSH4 = 0x63,
105    PUSH5 = 0x64,
106    PUSH6 = 0x65,
107    PUSH7 = 0x66,
108    PUSH8 = 0x67,
109    PUSH9 = 0x68,
110    PUSH10 = 0x69,
111    PUSH11 = 0x6A,
112    PUSH12 = 0x6B,
113    PUSH13 = 0x6C,
114    PUSH14 = 0x6D,
115    PUSH15 = 0x6E,
116    PUSH16 = 0x6F,
117    PUSH17 = 0x70,
118    PUSH18 = 0x71,
119    PUSH19 = 0x72,
120    PUSH20 = 0x73,
121    PUSH21 = 0x74,
122    PUSH22 = 0x75,
123    PUSH23 = 0x76,
124    PUSH24 = 0x77,
125    PUSH25 = 0x78,
126    PUSH26 = 0x79,
127    PUSH27 = 0x7A,
128    PUSH28 = 0x7B,
129    PUSH29 = 0x7C,
130    PUSH30 = 0x7D,
131    PUSH31 = 0x7E,
132    PUSH32 = 0x7F,
133
134    // Duplication Operations
135    DUP1 = 0x80,
136    DUP2 = 0x81,
137    DUP3 = 0x82,
138    DUP4 = 0x83,
139    DUP5 = 0x84,
140    DUP6 = 0x85,
141    DUP7 = 0x86,
142    DUP8 = 0x87,
143    DUP9 = 0x88,
144    DUP10 = 0x89,
145    DUP11 = 0x8A,
146    DUP12 = 0x8B,
147    DUP13 = 0x8C,
148    DUP14 = 0x8D,
149    DUP15 = 0x8E,
150    DUP16 = 0x8F,
151
152    // Swap Operations
153    SWAP1 = 0x90,
154    SWAP2 = 0x91,
155    SWAP3 = 0x92,
156    SWAP4 = 0x93,
157    SWAP5 = 0x94,
158    SWAP6 = 0x95,
159    SWAP7 = 0x96,
160    SWAP8 = 0x97,
161    SWAP9 = 0x98,
162    SWAP10 = 0x99,
163    SWAP11 = 0x9A,
164    SWAP12 = 0x9B,
165    SWAP13 = 0x9C,
166    SWAP14 = 0x9D,
167    SWAP15 = 0x9E,
168    SWAP16 = 0x9F,
169    // Logging Operations
170    LOG0 = 0xA0,
171    LOG1 = 0xA1,
172    LOG2 = 0xA2,
173    LOG3 = 0xA3,
174    LOG4 = 0xA4,
175    // EIP-8141 Frame Transaction opcodes
176    APPROVE = 0xAA,
177    TXPARAM = 0xB0,
178    FRAMEDATALOAD = 0xB1,
179    FRAMEDATACOPY = 0xB2,
180    FRAMEPARAM = 0xB3,
181    SIGPARAM = 0xB4,
182    // EIP-8024
183    DUPN = 0xE6,
184    SWAPN = 0xE7,
185    EXCHANGE = 0xE8,
186    // System Operations
187    CREATE = 0xF0,
188    CALL = 0xF1,
189    CALLCODE = 0xF2,
190    RETURN = 0xF3,
191    DELEGATECALL = 0xF4,
192    CREATE2 = 0xF5,
193    STATICCALL = 0xFA,
194    REVERT = 0xFD,
195    INVALID = 0xFE,
196    SELFDESTRUCT = 0xFF,
197}
198
199impl From<u8> for Opcode {
200    #[expect(clippy::as_conversions)]
201    fn from(byte: u8) -> Self {
202        // We use a manual lookup table instead of a match because it gives improved perfomance
203        // See https://godbolt.org/z/eG8M1jz3M
204        const OPCODE_TABLE: [Opcode; 256] = const {
205            let mut table = [Opcode::INVALID; 256];
206            table[0x00] = Opcode::STOP;
207            table[0x01] = Opcode::ADD;
208            table[0x16] = Opcode::AND;
209            table[0x17] = Opcode::OR;
210            table[0x18] = Opcode::XOR;
211            table[0x19] = Opcode::NOT;
212            table[0x1A] = Opcode::BYTE;
213            table[0x1B] = Opcode::SHL;
214            table[0x1C] = Opcode::SHR;
215            table[0x1D] = Opcode::SAR;
216            table[0x1E] = Opcode::CLZ;
217            table[0x02] = Opcode::MUL;
218            table[0x03] = Opcode::SUB;
219            table[0x04] = Opcode::DIV;
220            table[0x05] = Opcode::SDIV;
221            table[0x06] = Opcode::MOD;
222            table[0x07] = Opcode::SMOD;
223            table[0x08] = Opcode::ADDMOD;
224            table[0x09] = Opcode::MULMOD;
225            table[0x0A] = Opcode::EXP;
226            table[0x0B] = Opcode::SIGNEXTEND;
227            table[0x10] = Opcode::LT;
228            table[0x11] = Opcode::GT;
229            table[0x12] = Opcode::SLT;
230            table[0x13] = Opcode::SGT;
231            table[0x14] = Opcode::EQ;
232            table[0x15] = Opcode::ISZERO;
233            table[0x20] = Opcode::KECCAK256;
234            table[0x30] = Opcode::ADDRESS;
235            table[0x31] = Opcode::BALANCE;
236            table[0x32] = Opcode::ORIGIN;
237            table[0x33] = Opcode::CALLER;
238            table[0x34] = Opcode::CALLVALUE;
239            table[0x35] = Opcode::CALLDATALOAD;
240            table[0x36] = Opcode::CALLDATASIZE;
241            table[0x37] = Opcode::CALLDATACOPY;
242            table[0x38] = Opcode::CODESIZE;
243            table[0x39] = Opcode::CODECOPY;
244            table[0x3A] = Opcode::GASPRICE;
245            table[0x3B] = Opcode::EXTCODESIZE;
246            table[0x3C] = Opcode::EXTCODECOPY;
247            table[0x3D] = Opcode::RETURNDATASIZE;
248            table[0x3E] = Opcode::RETURNDATACOPY;
249            table[0x3F] = Opcode::EXTCODEHASH;
250            table[0x40] = Opcode::BLOCKHASH;
251            table[0x41] = Opcode::COINBASE;
252            table[0x42] = Opcode::TIMESTAMP;
253            table[0x43] = Opcode::NUMBER;
254            table[0x44] = Opcode::PREVRANDAO;
255            table[0x45] = Opcode::GASLIMIT;
256            table[0x46] = Opcode::CHAINID;
257            table[0x47] = Opcode::SELFBALANCE;
258            table[0x48] = Opcode::BASEFEE;
259            table[0x49] = Opcode::BLOBHASH;
260            table[0x4A] = Opcode::BLOBBASEFEE;
261            table[0x4B] = Opcode::SLOTNUM;
262            table[0x50] = Opcode::POP;
263            table[0x56] = Opcode::JUMP;
264            table[0x57] = Opcode::JUMPI;
265            table[0x58] = Opcode::PC;
266            table[0x5B] = Opcode::JUMPDEST;
267            table[0x5F] = Opcode::PUSH0;
268            table[0x60] = Opcode::PUSH1;
269            table[0x61] = Opcode::PUSH2;
270            table[0x62] = Opcode::PUSH3;
271            table[0x63] = Opcode::PUSH4;
272            table[0x64] = Opcode::PUSH5;
273            table[0x65] = Opcode::PUSH6;
274            table[0x66] = Opcode::PUSH7;
275            table[0x67] = Opcode::PUSH8;
276            table[0x68] = Opcode::PUSH9;
277            table[0x69] = Opcode::PUSH10;
278            table[0x6A] = Opcode::PUSH11;
279            table[0x6B] = Opcode::PUSH12;
280            table[0x6C] = Opcode::PUSH13;
281            table[0x6D] = Opcode::PUSH14;
282            table[0x6E] = Opcode::PUSH15;
283            table[0x6F] = Opcode::PUSH16;
284            table[0x70] = Opcode::PUSH17;
285            table[0x71] = Opcode::PUSH18;
286            table[0x72] = Opcode::PUSH19;
287            table[0x73] = Opcode::PUSH20;
288            table[0x74] = Opcode::PUSH21;
289            table[0x75] = Opcode::PUSH22;
290            table[0x76] = Opcode::PUSH23;
291            table[0x77] = Opcode::PUSH24;
292            table[0x78] = Opcode::PUSH25;
293            table[0x79] = Opcode::PUSH26;
294            table[0x7A] = Opcode::PUSH27;
295            table[0x7B] = Opcode::PUSH28;
296            table[0x7C] = Opcode::PUSH29;
297            table[0x7D] = Opcode::PUSH30;
298            table[0x7E] = Opcode::PUSH31;
299            table[0x7F] = Opcode::PUSH32;
300            table[0x80] = Opcode::DUP1;
301            table[0x81] = Opcode::DUP2;
302            table[0x82] = Opcode::DUP3;
303            table[0x83] = Opcode::DUP4;
304            table[0x84] = Opcode::DUP5;
305            table[0x85] = Opcode::DUP6;
306            table[0x86] = Opcode::DUP7;
307            table[0x87] = Opcode::DUP8;
308            table[0x88] = Opcode::DUP9;
309            table[0x89] = Opcode::DUP10;
310            table[0x8A] = Opcode::DUP11;
311            table[0x8B] = Opcode::DUP12;
312            table[0x8C] = Opcode::DUP13;
313            table[0x8D] = Opcode::DUP14;
314            table[0x8E] = Opcode::DUP15;
315            table[0x8F] = Opcode::DUP16;
316            table[0x90] = Opcode::SWAP1;
317            table[0x91] = Opcode::SWAP2;
318            table[0x92] = Opcode::SWAP3;
319            table[0x93] = Opcode::SWAP4;
320            table[0x94] = Opcode::SWAP5;
321            table[0x95] = Opcode::SWAP6;
322            table[0x96] = Opcode::SWAP7;
323            table[0x97] = Opcode::SWAP8;
324            table[0x98] = Opcode::SWAP9;
325            table[0x99] = Opcode::SWAP10;
326            table[0x9A] = Opcode::SWAP11;
327            table[0x9B] = Opcode::SWAP12;
328            table[0x9C] = Opcode::SWAP13;
329            table[0x9D] = Opcode::SWAP14;
330            table[0x9E] = Opcode::SWAP15;
331            table[0x9F] = Opcode::SWAP16;
332            table[0xA0] = Opcode::LOG0;
333            table[0xA1] = Opcode::LOG1;
334            table[0xA2] = Opcode::LOG2;
335            table[0xA3] = Opcode::LOG3;
336            table[0xA4] = Opcode::LOG4;
337            table[0xAA] = Opcode::APPROVE;
338            table[0xB0] = Opcode::TXPARAM;
339            table[0xB1] = Opcode::FRAMEDATALOAD;
340            table[0xB2] = Opcode::FRAMEDATACOPY;
341            table[0xB3] = Opcode::FRAMEPARAM;
342            table[0xB4] = Opcode::SIGPARAM;
343            table[0x51] = Opcode::MLOAD;
344            table[0x52] = Opcode::MSTORE;
345            table[0x53] = Opcode::MSTORE8;
346            table[0x54] = Opcode::SLOAD;
347            table[0x55] = Opcode::SSTORE;
348            table[0x59] = Opcode::MSIZE;
349            table[0x5A] = Opcode::GAS;
350            table[0x5E] = Opcode::MCOPY;
351            table[0x5C] = Opcode::TLOAD;
352            table[0x5D] = Opcode::TSTORE;
353            table[0xE6] = Opcode::DUPN;
354            table[0xE7] = Opcode::SWAPN;
355            table[0xE8] = Opcode::EXCHANGE;
356            table[0xF0] = Opcode::CREATE;
357            table[0xF1] = Opcode::CALL;
358            table[0xF2] = Opcode::CALLCODE;
359            table[0xF3] = Opcode::RETURN;
360            table[0xF5] = Opcode::CREATE2;
361            table[0xF4] = Opcode::DELEGATECALL;
362            table[0xFA] = Opcode::STATICCALL;
363            table[0xFD] = Opcode::REVERT;
364            table[0xFF] = Opcode::SELFDESTRUCT;
365
366            table
367        };
368        #[expect(clippy::indexing_slicing)] // can never happen
369        OPCODE_TABLE[byte as usize]
370    }
371}
372
373impl From<Opcode> for u8 {
374    #[allow(clippy::as_conversions)]
375    fn from(opcode: Opcode) -> Self {
376        opcode as u8
377    }
378}
379
380impl From<Opcode> for usize {
381    #[allow(clippy::as_conversions)]
382    fn from(opcode: Opcode) -> Self {
383        opcode as usize
384    }
385}
386
387/// Represents an opcode function handler.
388#[derive(Debug, Clone, Copy)]
389pub(crate) struct OpCodeFn(fn(&mut VM<'_>, &mut OnceCell<VMError>) -> OpcodeResult);
390
391impl OpCodeFn {
392    pub const fn new<T>() -> Self
393    where
394        T: OpcodeHandler,
395    {
396        Self(Self::wrap::<T>)
397    }
398
399    /// Call the opcode handler.
400    #[inline(always)]
401    pub fn call(self, vm: &mut VM<'_>, error: &mut OnceCell<VMError>) -> OpcodeResult {
402        (self.0)(vm, error)
403    }
404
405    fn wrap<T>(vm: &mut VM<'_>, error: &mut OnceCell<VMError>) -> OpcodeResult
406    where
407        T: OpcodeHandler,
408    {
409        T::eval(vm).unwrap_or_else(|err| {
410            _ = error.set(err);
411            OpcodeResult::Halt
412        })
413    }
414}
415
416impl<'a> VM<'a> {
417    /// Returns the opcode lookup function-pointer table for the given fork.
418    ///
419    /// The per-fork tables are `const`-evaluated once into process statics (the builders below
420    /// are all `const fn`), so this is a branch returning a shared `&'static` reference — no
421    /// per-tx rebuild or 2 KB copy into the VM. `fork` is constant within a block, so every tx
422    /// in a block resolves to the same table. This is faster than a conventional match.
423    #[allow(clippy::as_conversions, clippy::indexing_slicing)]
424    pub(crate) fn build_opcode_table(fork: Fork) -> &'static [OpCodeFn; 256] {
425        // Built once at compile time; immutable, so sharing across all VMs is trivially safe.
426        // Instantiated with `'static` so the initializers don't reference the impl's `'a`.
427        static HEGOTA: [OpCodeFn; 256] = VM::<'static>::build_opcode_table_hegota();
428        static AMSTERDAM: [OpCodeFn; 256] = VM::<'static>::build_opcode_table_amsterdam();
429        static OSAKA: [OpCodeFn; 256] = VM::<'static>::build_opcode_table_osaka();
430        static PRE_OSAKA: [OpCodeFn; 256] = VM::<'static>::build_opcode_table_pre_osaka();
431        static PRE_CANCUN: [OpCodeFn; 256] = VM::<'static>::build_opcode_table_pre_cancun();
432        static PRE_SHANGHAI: [OpCodeFn; 256] = VM::<'static>::build_opcode_table_pre_shanghai();
433
434        if fork >= Fork::Hegota {
435            &HEGOTA
436        } else if fork >= Fork::Amsterdam {
437            &AMSTERDAM
438        } else if fork >= Fork::Osaka {
439            &OSAKA
440        } else if fork >= Fork::Cancun {
441            &PRE_OSAKA
442        } else if fork >= Fork::Shanghai {
443            &PRE_CANCUN
444        } else {
445            &PRE_SHANGHAI
446        }
447    }
448
449    #[allow(clippy::as_conversions, clippy::indexing_slicing)]
450    const fn build_opcode_table_pre_shanghai() -> [OpCodeFn; 256] {
451        let mut opcode_table: [OpCodeFn; 256] = [OpCodeFn::new::<OpInvalidHandler>(); 256];
452
453        opcode_table[Opcode::STOP as usize] = OpCodeFn::new::<OpStopHandler>();
454        opcode_table[Opcode::MLOAD as usize] = OpCodeFn::new::<OpMLoadHandler>();
455        opcode_table[Opcode::MSTORE as usize] = OpCodeFn::new::<OpMStoreHandler>();
456        opcode_table[Opcode::MSTORE8 as usize] = OpCodeFn::new::<OpMStore8Handler>();
457        opcode_table[Opcode::JUMP as usize] = OpCodeFn::new::<OpJumpHandler>();
458        opcode_table[Opcode::SLOAD as usize] = OpCodeFn::new::<OpSLoadHandler>();
459        opcode_table[Opcode::SSTORE as usize] = OpCodeFn::new::<OpSStoreHandler>();
460        opcode_table[Opcode::MSIZE as usize] = OpCodeFn::new::<OpMSizeHandler>();
461        opcode_table[Opcode::GAS as usize] = OpCodeFn::new::<OpGasHandler>();
462        opcode_table[Opcode::PUSH1 as usize] = OpCodeFn::new::<OpPushHandler<1>>();
463        opcode_table[Opcode::PUSH2 as usize] = OpCodeFn::new::<OpPushHandler<2>>();
464        opcode_table[Opcode::PUSH3 as usize] = OpCodeFn::new::<OpPushHandler<3>>();
465        opcode_table[Opcode::PUSH4 as usize] = OpCodeFn::new::<OpPushHandler<4>>();
466        opcode_table[Opcode::PUSH5 as usize] = OpCodeFn::new::<OpPushHandler<5>>();
467        opcode_table[Opcode::PUSH6 as usize] = OpCodeFn::new::<OpPushHandler<6>>();
468        opcode_table[Opcode::PUSH7 as usize] = OpCodeFn::new::<OpPushHandler<7>>();
469        opcode_table[Opcode::PUSH8 as usize] = OpCodeFn::new::<OpPushHandler<8>>();
470        opcode_table[Opcode::PUSH9 as usize] = OpCodeFn::new::<OpPushHandler<9>>();
471        opcode_table[Opcode::PUSH10 as usize] = OpCodeFn::new::<OpPushHandler<10>>();
472        opcode_table[Opcode::PUSH11 as usize] = OpCodeFn::new::<OpPushHandler<11>>();
473        opcode_table[Opcode::PUSH12 as usize] = OpCodeFn::new::<OpPushHandler<12>>();
474        opcode_table[Opcode::PUSH13 as usize] = OpCodeFn::new::<OpPushHandler<13>>();
475        opcode_table[Opcode::PUSH14 as usize] = OpCodeFn::new::<OpPushHandler<14>>();
476        opcode_table[Opcode::PUSH15 as usize] = OpCodeFn::new::<OpPushHandler<15>>();
477        opcode_table[Opcode::PUSH16 as usize] = OpCodeFn::new::<OpPushHandler<16>>();
478        opcode_table[Opcode::PUSH17 as usize] = OpCodeFn::new::<OpPushHandler<17>>();
479        opcode_table[Opcode::PUSH18 as usize] = OpCodeFn::new::<OpPushHandler<18>>();
480        opcode_table[Opcode::PUSH19 as usize] = OpCodeFn::new::<OpPushHandler<19>>();
481        opcode_table[Opcode::PUSH20 as usize] = OpCodeFn::new::<OpPushHandler<20>>();
482        opcode_table[Opcode::PUSH21 as usize] = OpCodeFn::new::<OpPushHandler<21>>();
483        opcode_table[Opcode::PUSH22 as usize] = OpCodeFn::new::<OpPushHandler<22>>();
484        opcode_table[Opcode::PUSH23 as usize] = OpCodeFn::new::<OpPushHandler<23>>();
485        opcode_table[Opcode::PUSH24 as usize] = OpCodeFn::new::<OpPushHandler<24>>();
486        opcode_table[Opcode::PUSH25 as usize] = OpCodeFn::new::<OpPushHandler<25>>();
487        opcode_table[Opcode::PUSH26 as usize] = OpCodeFn::new::<OpPushHandler<26>>();
488        opcode_table[Opcode::PUSH27 as usize] = OpCodeFn::new::<OpPushHandler<27>>();
489        opcode_table[Opcode::PUSH28 as usize] = OpCodeFn::new::<OpPushHandler<28>>();
490        opcode_table[Opcode::PUSH29 as usize] = OpCodeFn::new::<OpPushHandler<29>>();
491        opcode_table[Opcode::PUSH30 as usize] = OpCodeFn::new::<OpPushHandler<30>>();
492        opcode_table[Opcode::PUSH31 as usize] = OpCodeFn::new::<OpPushHandler<31>>();
493        opcode_table[Opcode::PUSH32 as usize] = OpCodeFn::new::<OpPushHandler<32>>();
494
495        opcode_table[Opcode::DUP1 as usize] = OpCodeFn::new::<OpDupHandler<0>>();
496        opcode_table[Opcode::DUP2 as usize] = OpCodeFn::new::<OpDupHandler<1>>();
497        opcode_table[Opcode::DUP3 as usize] = OpCodeFn::new::<OpDupHandler<2>>();
498        opcode_table[Opcode::DUP4 as usize] = OpCodeFn::new::<OpDupHandler<3>>();
499        opcode_table[Opcode::DUP5 as usize] = OpCodeFn::new::<OpDupHandler<4>>();
500        opcode_table[Opcode::DUP6 as usize] = OpCodeFn::new::<OpDupHandler<5>>();
501        opcode_table[Opcode::DUP7 as usize] = OpCodeFn::new::<OpDupHandler<6>>();
502        opcode_table[Opcode::DUP8 as usize] = OpCodeFn::new::<OpDupHandler<7>>();
503        opcode_table[Opcode::DUP9 as usize] = OpCodeFn::new::<OpDupHandler<8>>();
504        opcode_table[Opcode::DUP10 as usize] = OpCodeFn::new::<OpDupHandler<9>>();
505        opcode_table[Opcode::DUP11 as usize] = OpCodeFn::new::<OpDupHandler<10>>();
506        opcode_table[Opcode::DUP12 as usize] = OpCodeFn::new::<OpDupHandler<11>>();
507        opcode_table[Opcode::DUP13 as usize] = OpCodeFn::new::<OpDupHandler<12>>();
508        opcode_table[Opcode::DUP14 as usize] = OpCodeFn::new::<OpDupHandler<13>>();
509        opcode_table[Opcode::DUP15 as usize] = OpCodeFn::new::<OpDupHandler<14>>();
510        opcode_table[Opcode::DUP16 as usize] = OpCodeFn::new::<OpDupHandler<15>>();
511
512        opcode_table[Opcode::SWAP1 as usize] = OpCodeFn::new::<OpSwapHandler<1>>();
513        opcode_table[Opcode::SWAP2 as usize] = OpCodeFn::new::<OpSwapHandler<2>>();
514        opcode_table[Opcode::SWAP3 as usize] = OpCodeFn::new::<OpSwapHandler<3>>();
515        opcode_table[Opcode::SWAP4 as usize] = OpCodeFn::new::<OpSwapHandler<4>>();
516        opcode_table[Opcode::SWAP5 as usize] = OpCodeFn::new::<OpSwapHandler<5>>();
517        opcode_table[Opcode::SWAP6 as usize] = OpCodeFn::new::<OpSwapHandler<6>>();
518        opcode_table[Opcode::SWAP7 as usize] = OpCodeFn::new::<OpSwapHandler<7>>();
519        opcode_table[Opcode::SWAP8 as usize] = OpCodeFn::new::<OpSwapHandler<8>>();
520        opcode_table[Opcode::SWAP9 as usize] = OpCodeFn::new::<OpSwapHandler<9>>();
521        opcode_table[Opcode::SWAP10 as usize] = OpCodeFn::new::<OpSwapHandler<10>>();
522        opcode_table[Opcode::SWAP11 as usize] = OpCodeFn::new::<OpSwapHandler<11>>();
523        opcode_table[Opcode::SWAP12 as usize] = OpCodeFn::new::<OpSwapHandler<12>>();
524        opcode_table[Opcode::SWAP13 as usize] = OpCodeFn::new::<OpSwapHandler<13>>();
525        opcode_table[Opcode::SWAP14 as usize] = OpCodeFn::new::<OpSwapHandler<14>>();
526        opcode_table[Opcode::SWAP15 as usize] = OpCodeFn::new::<OpSwapHandler<15>>();
527        opcode_table[Opcode::SWAP16 as usize] = OpCodeFn::new::<OpSwapHandler<16>>();
528        opcode_table[Opcode::POP as usize] = OpCodeFn::new::<OpPopHandler>();
529        opcode_table[Opcode::ADD as usize] = OpCodeFn::new::<OpAddHandler>();
530        opcode_table[Opcode::MUL as usize] = OpCodeFn::new::<OpMulHandler>();
531        opcode_table[Opcode::SUB as usize] = OpCodeFn::new::<OpSubHandler>();
532        opcode_table[Opcode::DIV as usize] = OpCodeFn::new::<OpDivHandler>();
533        opcode_table[Opcode::SDIV as usize] = OpCodeFn::new::<OpSDivHandler>();
534        opcode_table[Opcode::MOD as usize] = OpCodeFn::new::<OpModHandler>();
535        opcode_table[Opcode::SMOD as usize] = OpCodeFn::new::<OpSModHandler>();
536        opcode_table[Opcode::ADDMOD as usize] = OpCodeFn::new::<OpAddModHandler>();
537        opcode_table[Opcode::MULMOD as usize] = OpCodeFn::new::<OpMulModHandler>();
538        opcode_table[Opcode::EXP as usize] = OpCodeFn::new::<OpExpHandler>();
539        opcode_table[Opcode::CALL as usize] = OpCodeFn::new::<OpCallHandler>();
540        opcode_table[Opcode::CALLCODE as usize] = OpCodeFn::new::<OpCallCodeHandler>();
541        opcode_table[Opcode::RETURN as usize] = OpCodeFn::new::<OpReturnHandler>();
542        opcode_table[Opcode::DELEGATECALL as usize] = OpCodeFn::new::<OpDelegateCallHandler>();
543        opcode_table[Opcode::STATICCALL as usize] = OpCodeFn::new::<OpStaticCallHandler>();
544        opcode_table[Opcode::CREATE as usize] = OpCodeFn::new::<OpCreateHandler>();
545        opcode_table[Opcode::CREATE2 as usize] = OpCodeFn::new::<OpCreate2Handler>();
546        opcode_table[Opcode::JUMPI as usize] = OpCodeFn::new::<OpJumpIHandler>();
547        opcode_table[Opcode::JUMPDEST as usize] = OpCodeFn::new::<OpJumpDestHandler>();
548        opcode_table[Opcode::ADDRESS as usize] = OpCodeFn::new::<OpAddressHandler>();
549        opcode_table[Opcode::ORIGIN as usize] = OpCodeFn::new::<OpOriginHandler>();
550        opcode_table[Opcode::BALANCE as usize] = OpCodeFn::new::<OpBalanceHandler>();
551        opcode_table[Opcode::CALLER as usize] = OpCodeFn::new::<OpCallerHandler>();
552        opcode_table[Opcode::CALLVALUE as usize] = OpCodeFn::new::<OpCallValueHandler>();
553        opcode_table[Opcode::CODECOPY as usize] = OpCodeFn::new::<OpCodeCopyHandler>();
554        opcode_table[Opcode::SIGNEXTEND as usize] = OpCodeFn::new::<OpSignExtendHandler>();
555        opcode_table[Opcode::LT as usize] = OpCodeFn::new::<OpLtHandler>();
556        opcode_table[Opcode::GT as usize] = OpCodeFn::new::<OpGtHandler>();
557        opcode_table[Opcode::SLT as usize] = OpCodeFn::new::<OpSLtHandler>();
558        opcode_table[Opcode::SGT as usize] = OpCodeFn::new::<OpSGtHandler>();
559        opcode_table[Opcode::EQ as usize] = OpCodeFn::new::<OpEqHandler>();
560        opcode_table[Opcode::ISZERO as usize] = OpCodeFn::new::<OpIsZeroHandler>();
561        opcode_table[Opcode::KECCAK256 as usize] = OpCodeFn::new::<OpKeccak256Handler>();
562        opcode_table[Opcode::CALLDATALOAD as usize] = OpCodeFn::new::<OpCallDataLoadHandler>();
563        opcode_table[Opcode::CALLDATASIZE as usize] = OpCodeFn::new::<OpCallDataSizeHandler>();
564        opcode_table[Opcode::CALLDATACOPY as usize] = OpCodeFn::new::<OpCallDataCopyHandler>();
565        opcode_table[Opcode::RETURNDATASIZE as usize] = OpCodeFn::new::<OpReturnDataSizeHandler>();
566        opcode_table[Opcode::RETURNDATACOPY as usize] = OpCodeFn::new::<OpReturnDataCopyHandler>();
567        opcode_table[Opcode::PC as usize] = OpCodeFn::new::<OpPcHandler>();
568        opcode_table[Opcode::BLOCKHASH as usize] = OpCodeFn::new::<OpBlockHashHandler>();
569        opcode_table[Opcode::COINBASE as usize] = OpCodeFn::new::<OpCoinbaseHandler>();
570        opcode_table[Opcode::TIMESTAMP as usize] = OpCodeFn::new::<OpTimestampHandler>();
571        opcode_table[Opcode::NUMBER as usize] = OpCodeFn::new::<OpNumberHandler>();
572        opcode_table[Opcode::PREVRANDAO as usize] = OpCodeFn::new::<OpPrevRandaoHandler>();
573        opcode_table[Opcode::GASLIMIT as usize] = OpCodeFn::new::<OpGasLimitHandler>();
574        opcode_table[Opcode::CHAINID as usize] = OpCodeFn::new::<OpChainIdHandler>();
575        opcode_table[Opcode::BASEFEE as usize] = OpCodeFn::new::<OpBaseFeeHandler>();
576        opcode_table[Opcode::AND as usize] = OpCodeFn::new::<OpAndHandler>();
577        opcode_table[Opcode::OR as usize] = OpCodeFn::new::<OpOrHandler>();
578        opcode_table[Opcode::XOR as usize] = OpCodeFn::new::<OpXorHandler>();
579        opcode_table[Opcode::NOT as usize] = OpCodeFn::new::<OpNotHandler>();
580        opcode_table[Opcode::BYTE as usize] = OpCodeFn::new::<OpByteHandler>();
581        opcode_table[Opcode::SHL as usize] = OpCodeFn::new::<OpShlHandler>();
582        opcode_table[Opcode::SHR as usize] = OpCodeFn::new::<OpShrHandler>();
583        opcode_table[Opcode::SAR as usize] = OpCodeFn::new::<OpSarHandler>();
584        opcode_table[Opcode::SELFBALANCE as usize] = OpCodeFn::new::<OpSelfBalanceHandler>();
585        opcode_table[Opcode::CODESIZE as usize] = OpCodeFn::new::<OpCodeSizeHandler>();
586        opcode_table[Opcode::GASPRICE as usize] = OpCodeFn::new::<OpGasPriceHandler>();
587        opcode_table[Opcode::EXTCODESIZE as usize] = OpCodeFn::new::<OpExtCodeSizeHandler>();
588        opcode_table[Opcode::EXTCODECOPY as usize] = OpCodeFn::new::<OpExtCodeCopyHandler>();
589        opcode_table[Opcode::EXTCODEHASH as usize] = OpCodeFn::new::<OpExtCodeHashHandler>();
590        opcode_table[Opcode::REVERT as usize] = OpCodeFn::new::<OpRevertHandler>();
591        opcode_table[Opcode::INVALID as usize] = OpCodeFn::new::<OpInvalidHandler>();
592        opcode_table[Opcode::SELFDESTRUCT as usize] = OpCodeFn::new::<OpSelfDestructHandler>();
593
594        opcode_table[Opcode::LOG0 as usize] = OpCodeFn::new::<OpLogHandler<0>>();
595        opcode_table[Opcode::LOG1 as usize] = OpCodeFn::new::<OpLogHandler<1>>();
596        opcode_table[Opcode::LOG2 as usize] = OpCodeFn::new::<OpLogHandler<2>>();
597        opcode_table[Opcode::LOG3 as usize] = OpCodeFn::new::<OpLogHandler<3>>();
598        opcode_table[Opcode::LOG4 as usize] = OpCodeFn::new::<OpLogHandler<4>>();
599
600        opcode_table
601    }
602
603    #[allow(clippy::as_conversions, clippy::indexing_slicing)]
604    const fn build_opcode_table_pre_cancun() -> [OpCodeFn; 256] {
605        let mut opcode_table: [OpCodeFn; 256] = Self::build_opcode_table_pre_shanghai();
606
607        // [EIP-3855] - PUSH0 is only available from SHANGHAI
608        opcode_table[Opcode::PUSH0 as usize] = OpCodeFn::new::<OpPush0Handler>();
609
610        opcode_table
611    }
612
613    #[allow(clippy::as_conversions, clippy::indexing_slicing)]
614    const fn build_opcode_table_pre_osaka() -> [OpCodeFn; 256] {
615        const {
616            let mut opcode_table: [OpCodeFn; 256] = Self::build_opcode_table_pre_cancun();
617
618            // [EIP-5656] - MCOPY is only available from CANCUN
619            opcode_table[Opcode::MCOPY as usize] = OpCodeFn::new::<OpMCopyHandler>();
620
621            // [EIP-1153] - TLOAD is only available from CANCUN
622            opcode_table[Opcode::TLOAD as usize] = OpCodeFn::new::<OpTLoadHandler>();
623            opcode_table[Opcode::TSTORE as usize] = OpCodeFn::new::<OpTStoreHandler>();
624
625            // [EIP-7516] - BLOBBASEFEE is only available from CANCUN
626            opcode_table[Opcode::BLOBBASEFEE as usize] = OpCodeFn::new::<OpBlobBaseFeeHandler>();
627            // [EIP-4844] - BLOBHASH is only available from CANCUN
628            opcode_table[Opcode::BLOBHASH as usize] = OpCodeFn::new::<OpBlobHashHandler>();
629
630            opcode_table
631        }
632    }
633
634    #[allow(clippy::as_conversions, clippy::indexing_slicing)]
635    const fn build_opcode_table_osaka() -> [OpCodeFn; 256] {
636        let mut opcode_table: [OpCodeFn; 256] = Self::build_opcode_table_pre_osaka();
637
638        opcode_table[Opcode::CLZ as usize] = OpCodeFn::new::<OpClzHandler>();
639
640        opcode_table
641    }
642
643    #[expect(clippy::as_conversions, clippy::indexing_slicing)]
644    const fn build_opcode_table_amsterdam() -> [OpCodeFn; 256] {
645        let mut opcode_table: [OpCodeFn; 256] = Self::build_opcode_table_osaka();
646
647        // EIP-8024 opcodes
648        opcode_table[Opcode::DUPN as usize] = OpCodeFn::new::<OpDupNHandler>();
649        opcode_table[Opcode::SWAPN as usize] = OpCodeFn::new::<OpSwapNHandler>();
650        opcode_table[Opcode::EXCHANGE as usize] = OpCodeFn::new::<OpExchangeHandler>();
651        // EIP-7843 opcode
652        opcode_table[Opcode::SLOTNUM as usize] = OpCodeFn::new::<OpSlotNumHandler>();
653        opcode_table
654    }
655
656    #[expect(clippy::as_conversions, clippy::indexing_slicing)]
657    const fn build_opcode_table_hegota() -> [OpCodeFn; 256] {
658        let mut opcode_table: [OpCodeFn; 256] = Self::build_opcode_table_amsterdam();
659
660        // EIP-8141 Frame Transaction opcodes (Hegota)
661        opcode_table[Opcode::APPROVE as usize] = OpCodeFn::new::<OpApproveHandler>();
662        opcode_table[Opcode::TXPARAM as usize] = OpCodeFn::new::<OpTxParamHandler>();
663        opcode_table[Opcode::FRAMEDATALOAD as usize] = OpCodeFn::new::<OpFrameDataLoadHandler>();
664        opcode_table[Opcode::FRAMEDATACOPY as usize] = OpCodeFn::new::<OpFrameDataCopyHandler>();
665        opcode_table[Opcode::FRAMEPARAM as usize] = OpCodeFn::new::<OpFrameParamHandler>();
666        opcode_table[Opcode::SIGPARAM as usize] = OpCodeFn::new::<OpSigParamHandler>();
667
668        opcode_table
669    }
670}
671
672#[cfg(test)]
673mod tests {
674    use super::*;
675
676    #[test]
677    #[allow(
678        clippy::indexing_slicing,
679        reason = "fixed 256-entry table indexed by u8"
680    )]
681    fn frame_opcodes_not_installed_before_hegota() {
682        fn same_handler(a: OpCodeFn, b: OpCodeFn) -> bool {
683            // Compare handler identity by fn-pointer address without an `as`
684            // cast (the workspace denies clippy::as_conversions).
685            std::ptr::fn_addr_eq(a.0, b.0)
686        }
687        // 0xEF is never assigned in any table -> it holds the invalid handler.
688        for fork in [Fork::Osaka, Fork::Amsterdam] {
689            let table = VM::build_opcode_table(fork);
690            for byte in [0xAAusize, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4] {
691                assert!(
692                    same_handler(table[byte], table[0xEF]),
693                    "frame opcode {byte:#x} must be invalid at {fork:?}"
694                );
695            }
696        }
697        let hegota = VM::build_opcode_table(Fork::Hegota);
698        assert!(!same_handler(hegota[0xAA], hegota[0xEF]));
699    }
700}