revm_interpreter/
instructions.rs

1//! EVM opcode implementations.
2
3#[macro_use]
4pub mod macros;
5/// Arithmetic operations (ADD, SUB, MUL, DIV, etc.).
6pub mod arithmetic;
7/// Bitwise operations (AND, OR, XOR, NOT, etc.).
8pub mod bitwise;
9/// Block information instructions (COINBASE, TIMESTAMP, etc.).
10pub mod block_info;
11/// Contract operations (CALL, CREATE, DELEGATECALL, etc.).
12pub mod contract;
13/// Control flow instructions (JUMP, JUMPI, REVERT, etc.).
14pub mod control;
15/// Host environment interactions (SLOAD, SSTORE, LOG, etc.).
16pub mod host;
17/// Signed 256-bit integer operations.
18pub mod i256;
19/// Memory operations (MLOAD, MSTORE, MSIZE, etc.).
20pub mod memory;
21/// Stack operations (PUSH, POP, DUP, SWAP, etc.).
22pub mod stack;
23/// System information instructions (ADDRESS, CALLER, etc.).
24pub mod system;
25/// Transaction information instructions (ORIGIN, GASPRICE, etc.).
26pub mod tx_info;
27/// Utility functions and helpers for instruction implementation.
28pub mod utility;
29
30pub use context_interface::cfg::gas::{self, *};
31
32use crate::{interpreter_types::InterpreterTypes, Host, InstructionContext};
33use primitives::hardfork::SpecId;
34
35/// EVM opcode function signature.
36#[derive(Debug)]
37pub struct Instruction<W: InterpreterTypes, H: ?Sized> {
38    fn_: fn(InstructionContext<'_, H, W>),
39    static_gas: u64,
40}
41
42impl<W: InterpreterTypes, H: Host + ?Sized> Instruction<W, H> {
43    /// Creates a new instruction with the given function and static gas cost.
44    #[inline]
45    pub const fn new(fn_: fn(InstructionContext<'_, H, W>), static_gas: u64) -> Self {
46        Self { fn_, static_gas }
47    }
48
49    /// Creates an unknown/invalid instruction.
50    #[inline]
51    pub const fn unknown() -> Self {
52        Self {
53            fn_: control::unknown,
54            static_gas: 0,
55        }
56    }
57
58    /// Executes the instruction with the given context.
59    #[inline(always)]
60    pub fn execute(self, ctx: InstructionContext<'_, H, W>) {
61        (self.fn_)(ctx)
62    }
63
64    /// Returns the static gas cost of this instruction.
65    #[inline(always)]
66    pub const fn static_gas(&self) -> u64 {
67        self.static_gas
68    }
69}
70
71impl<W: InterpreterTypes, H: Host + ?Sized> Copy for Instruction<W, H> {}
72impl<W: InterpreterTypes, H: Host + ?Sized> Clone for Instruction<W, H> {
73    fn clone(&self) -> Self {
74        *self
75    }
76}
77
78/// Instruction table is list of instruction function pointers mapped to 256 EVM opcodes.
79pub type InstructionTable<W, H> = [Instruction<W, H>; 256];
80
81/// Returns the default instruction table for the given interpreter types and host.
82#[inline]
83pub const fn instruction_table<WIRE: InterpreterTypes, H: Host>() -> [Instruction<WIRE, H>; 256] {
84    const { instruction_table_impl::<WIRE, H>() }
85}
86
87/// Create a instruction table with applied spec changes to static gas cost.
88#[inline]
89pub fn instruction_table_gas_changes_spec<WIRE: InterpreterTypes, H: Host>(
90    spec: SpecId,
91) -> [Instruction<WIRE, H>; 256] {
92    use bytecode::opcode::*;
93    use SpecId::*;
94    let mut table = instruction_table();
95
96    if spec.is_enabled_in(TANGERINE) {
97        // EIP-150: Gas cost changes for IO-heavy operations
98        table[SLOAD as usize].static_gas = 200;
99        table[BALANCE as usize].static_gas = 400;
100        table[EXTCODESIZE as usize].static_gas = 700;
101        table[EXTCODECOPY as usize].static_gas = 700;
102        table[CALL as usize].static_gas = 700;
103        table[CALLCODE as usize].static_gas = 700;
104        table[DELEGATECALL as usize].static_gas = 700;
105        table[STATICCALL as usize].static_gas = 700;
106        table[SELFDESTRUCT as usize].static_gas = 5000;
107    }
108
109    if spec.is_enabled_in(ISTANBUL) {
110        // EIP-1884: Repricing for trie-size-dependent opcodes
111        table[SLOAD as usize].static_gas = gas::ISTANBUL_SLOAD_GAS;
112        table[BALANCE as usize].static_gas = 700;
113        table[EXTCODEHASH as usize].static_gas = 700;
114    }
115
116    if spec.is_enabled_in(BERLIN) {
117        // warm account cost is base gas that is spend. Additional gas depends if account is cold loaded.
118        table[SLOAD as usize].static_gas = gas::WARM_STORAGE_READ_COST;
119        table[BALANCE as usize].static_gas = gas::WARM_STORAGE_READ_COST;
120        table[EXTCODESIZE as usize].static_gas = gas::WARM_STORAGE_READ_COST;
121        table[EXTCODEHASH as usize].static_gas = gas::WARM_STORAGE_READ_COST;
122        table[EXTCODECOPY as usize].static_gas = gas::WARM_STORAGE_READ_COST;
123        table[CALL as usize].static_gas = gas::WARM_STORAGE_READ_COST;
124        table[CALLCODE as usize].static_gas = gas::WARM_STORAGE_READ_COST;
125        table[DELEGATECALL as usize].static_gas = gas::WARM_STORAGE_READ_COST;
126        table[STATICCALL as usize].static_gas = gas::WARM_STORAGE_READ_COST;
127    }
128
129    table
130}
131
132const fn instruction_table_impl<WIRE: InterpreterTypes, H: Host>() -> [Instruction<WIRE, H>; 256] {
133    use bytecode::opcode::*;
134    let mut table = [Instruction::unknown(); 256];
135
136    table[STOP as usize] = Instruction::new(control::stop, 0);
137    table[ADD as usize] = Instruction::new(arithmetic::add, 3);
138    table[MUL as usize] = Instruction::new(arithmetic::mul, 5);
139    table[SUB as usize] = Instruction::new(arithmetic::sub, 3);
140    table[DIV as usize] = Instruction::new(arithmetic::div, 5);
141    table[SDIV as usize] = Instruction::new(arithmetic::sdiv, 5);
142    table[MOD as usize] = Instruction::new(arithmetic::rem, 5);
143    table[SMOD as usize] = Instruction::new(arithmetic::smod, 5);
144    table[ADDMOD as usize] = Instruction::new(arithmetic::addmod, 8);
145    table[MULMOD as usize] = Instruction::new(arithmetic::mulmod, 8);
146    table[EXP as usize] = Instruction::new(arithmetic::exp, gas::EXP); // base
147    table[SIGNEXTEND as usize] = Instruction::new(arithmetic::signextend, 5);
148
149    table[LT as usize] = Instruction::new(bitwise::lt, 3);
150    table[GT as usize] = Instruction::new(bitwise::gt, 3);
151    table[SLT as usize] = Instruction::new(bitwise::slt, 3);
152    table[SGT as usize] = Instruction::new(bitwise::sgt, 3);
153    table[EQ as usize] = Instruction::new(bitwise::eq, 3);
154    table[ISZERO as usize] = Instruction::new(bitwise::iszero, 3);
155    table[AND as usize] = Instruction::new(bitwise::bitand, 3);
156    table[OR as usize] = Instruction::new(bitwise::bitor, 3);
157    table[XOR as usize] = Instruction::new(bitwise::bitxor, 3);
158    table[NOT as usize] = Instruction::new(bitwise::not, 3);
159    table[BYTE as usize] = Instruction::new(bitwise::byte, 3);
160    table[SHL as usize] = Instruction::new(bitwise::shl, 3);
161    table[SHR as usize] = Instruction::new(bitwise::shr, 3);
162    table[SAR as usize] = Instruction::new(bitwise::sar, 3);
163    table[CLZ as usize] = Instruction::new(bitwise::clz, 5);
164
165    table[KECCAK256 as usize] = Instruction::new(system::keccak256, gas::KECCAK256);
166
167    table[ADDRESS as usize] = Instruction::new(system::address, 2);
168    table[BALANCE as usize] = Instruction::new(host::balance, 20);
169    table[ORIGIN as usize] = Instruction::new(tx_info::origin, 2);
170    table[CALLER as usize] = Instruction::new(system::caller, 2);
171    table[CALLVALUE as usize] = Instruction::new(system::callvalue, 2);
172    table[CALLDATALOAD as usize] = Instruction::new(system::calldataload, 3);
173    table[CALLDATASIZE as usize] = Instruction::new(system::calldatasize, 2);
174    table[CALLDATACOPY as usize] = Instruction::new(system::calldatacopy, 3);
175    table[CODESIZE as usize] = Instruction::new(system::codesize, 2);
176    table[CODECOPY as usize] = Instruction::new(system::codecopy, 3);
177
178    table[GASPRICE as usize] = Instruction::new(tx_info::gasprice, 2);
179    table[EXTCODESIZE as usize] = Instruction::new(host::extcodesize, 20);
180    table[EXTCODECOPY as usize] = Instruction::new(host::extcodecopy, 20);
181    table[RETURNDATASIZE as usize] = Instruction::new(system::returndatasize, 2);
182    table[RETURNDATACOPY as usize] = Instruction::new(system::returndatacopy, 3);
183    table[EXTCODEHASH as usize] = Instruction::new(host::extcodehash, 400);
184    table[BLOCKHASH as usize] = Instruction::new(host::blockhash, 20);
185    table[COINBASE as usize] = Instruction::new(block_info::coinbase, 2);
186    table[TIMESTAMP as usize] = Instruction::new(block_info::timestamp, 2);
187    table[NUMBER as usize] = Instruction::new(block_info::block_number, 2);
188    table[DIFFICULTY as usize] = Instruction::new(block_info::difficulty, 2);
189    table[GASLIMIT as usize] = Instruction::new(block_info::gaslimit, 2);
190    table[CHAINID as usize] = Instruction::new(block_info::chainid, 2);
191    table[SELFBALANCE as usize] = Instruction::new(host::selfbalance, 5);
192    table[BASEFEE as usize] = Instruction::new(block_info::basefee, 2);
193    table[BLOBHASH as usize] = Instruction::new(tx_info::blob_hash, 3);
194    table[BLOBBASEFEE as usize] = Instruction::new(block_info::blob_basefee, 2);
195    table[SLOTNUM as usize] = Instruction::new(block_info::slot_num, 2);
196
197    table[POP as usize] = Instruction::new(stack::pop, 2);
198    table[MLOAD as usize] = Instruction::new(memory::mload, 3);
199    table[MSTORE as usize] = Instruction::new(memory::mstore, 3);
200    table[MSTORE8 as usize] = Instruction::new(memory::mstore8, 3);
201    table[SLOAD as usize] = Instruction::new(host::sload, 50);
202    // SSTORE static gas can be found in GasParams as check for minimal stipend
203    // needs to be done before deduction of static gas.
204    table[SSTORE as usize] = Instruction::new(host::sstore, 0);
205    table[JUMP as usize] = Instruction::new(control::jump, 8);
206    table[JUMPI as usize] = Instruction::new(control::jumpi, 10);
207    table[PC as usize] = Instruction::new(control::pc, 2);
208    table[MSIZE as usize] = Instruction::new(memory::msize, 2);
209    table[GAS as usize] = Instruction::new(system::gas, 2);
210    table[JUMPDEST as usize] = Instruction::new(control::jumpdest, 1);
211    table[TLOAD as usize] = Instruction::new(host::tload, 100);
212    table[TSTORE as usize] = Instruction::new(host::tstore, 100);
213    table[MCOPY as usize] = Instruction::new(memory::mcopy, 3); // static 2, mostly dynamic
214
215    table[PUSH0 as usize] = Instruction::new(stack::push0, 2);
216    table[PUSH1 as usize] = Instruction::new(stack::push::<1, _, _>, 3);
217    table[PUSH2 as usize] = Instruction::new(stack::push::<2, _, _>, 3);
218    table[PUSH3 as usize] = Instruction::new(stack::push::<3, _, _>, 3);
219    table[PUSH4 as usize] = Instruction::new(stack::push::<4, _, _>, 3);
220    table[PUSH5 as usize] = Instruction::new(stack::push::<5, _, _>, 3);
221    table[PUSH6 as usize] = Instruction::new(stack::push::<6, _, _>, 3);
222    table[PUSH7 as usize] = Instruction::new(stack::push::<7, _, _>, 3);
223    table[PUSH8 as usize] = Instruction::new(stack::push::<8, _, _>, 3);
224    table[PUSH9 as usize] = Instruction::new(stack::push::<9, _, _>, 3);
225    table[PUSH10 as usize] = Instruction::new(stack::push::<10, _, _>, 3);
226    table[PUSH11 as usize] = Instruction::new(stack::push::<11, _, _>, 3);
227    table[PUSH12 as usize] = Instruction::new(stack::push::<12, _, _>, 3);
228    table[PUSH13 as usize] = Instruction::new(stack::push::<13, _, _>, 3);
229    table[PUSH14 as usize] = Instruction::new(stack::push::<14, _, _>, 3);
230    table[PUSH15 as usize] = Instruction::new(stack::push::<15, _, _>, 3);
231    table[PUSH16 as usize] = Instruction::new(stack::push::<16, _, _>, 3);
232    table[PUSH17 as usize] = Instruction::new(stack::push::<17, _, _>, 3);
233    table[PUSH18 as usize] = Instruction::new(stack::push::<18, _, _>, 3);
234    table[PUSH19 as usize] = Instruction::new(stack::push::<19, _, _>, 3);
235    table[PUSH20 as usize] = Instruction::new(stack::push::<20, _, _>, 3);
236    table[PUSH21 as usize] = Instruction::new(stack::push::<21, _, _>, 3);
237    table[PUSH22 as usize] = Instruction::new(stack::push::<22, _, _>, 3);
238    table[PUSH23 as usize] = Instruction::new(stack::push::<23, _, _>, 3);
239    table[PUSH24 as usize] = Instruction::new(stack::push::<24, _, _>, 3);
240    table[PUSH25 as usize] = Instruction::new(stack::push::<25, _, _>, 3);
241    table[PUSH26 as usize] = Instruction::new(stack::push::<26, _, _>, 3);
242    table[PUSH27 as usize] = Instruction::new(stack::push::<27, _, _>, 3);
243    table[PUSH28 as usize] = Instruction::new(stack::push::<28, _, _>, 3);
244    table[PUSH29 as usize] = Instruction::new(stack::push::<29, _, _>, 3);
245    table[PUSH30 as usize] = Instruction::new(stack::push::<30, _, _>, 3);
246    table[PUSH31 as usize] = Instruction::new(stack::push::<31, _, _>, 3);
247    table[PUSH32 as usize] = Instruction::new(stack::push::<32, _, _>, 3);
248
249    table[DUP1 as usize] = Instruction::new(stack::dup::<1, _, _>, 3);
250    table[DUP2 as usize] = Instruction::new(stack::dup::<2, _, _>, 3);
251    table[DUP3 as usize] = Instruction::new(stack::dup::<3, _, _>, 3);
252    table[DUP4 as usize] = Instruction::new(stack::dup::<4, _, _>, 3);
253    table[DUP5 as usize] = Instruction::new(stack::dup::<5, _, _>, 3);
254    table[DUP6 as usize] = Instruction::new(stack::dup::<6, _, _>, 3);
255    table[DUP7 as usize] = Instruction::new(stack::dup::<7, _, _>, 3);
256    table[DUP8 as usize] = Instruction::new(stack::dup::<8, _, _>, 3);
257    table[DUP9 as usize] = Instruction::new(stack::dup::<9, _, _>, 3);
258    table[DUP10 as usize] = Instruction::new(stack::dup::<10, _, _>, 3);
259    table[DUP11 as usize] = Instruction::new(stack::dup::<11, _, _>, 3);
260    table[DUP12 as usize] = Instruction::new(stack::dup::<12, _, _>, 3);
261    table[DUP13 as usize] = Instruction::new(stack::dup::<13, _, _>, 3);
262    table[DUP14 as usize] = Instruction::new(stack::dup::<14, _, _>, 3);
263    table[DUP15 as usize] = Instruction::new(stack::dup::<15, _, _>, 3);
264    table[DUP16 as usize] = Instruction::new(stack::dup::<16, _, _>, 3);
265
266    table[SWAP1 as usize] = Instruction::new(stack::swap::<1, _, _>, 3);
267    table[SWAP2 as usize] = Instruction::new(stack::swap::<2, _, _>, 3);
268    table[SWAP3 as usize] = Instruction::new(stack::swap::<3, _, _>, 3);
269    table[SWAP4 as usize] = Instruction::new(stack::swap::<4, _, _>, 3);
270    table[SWAP5 as usize] = Instruction::new(stack::swap::<5, _, _>, 3);
271    table[SWAP6 as usize] = Instruction::new(stack::swap::<6, _, _>, 3);
272    table[SWAP7 as usize] = Instruction::new(stack::swap::<7, _, _>, 3);
273    table[SWAP8 as usize] = Instruction::new(stack::swap::<8, _, _>, 3);
274    table[SWAP9 as usize] = Instruction::new(stack::swap::<9, _, _>, 3);
275    table[SWAP10 as usize] = Instruction::new(stack::swap::<10, _, _>, 3);
276    table[SWAP11 as usize] = Instruction::new(stack::swap::<11, _, _>, 3);
277    table[SWAP12 as usize] = Instruction::new(stack::swap::<12, _, _>, 3);
278    table[SWAP13 as usize] = Instruction::new(stack::swap::<13, _, _>, 3);
279    table[SWAP14 as usize] = Instruction::new(stack::swap::<14, _, _>, 3);
280    table[SWAP15 as usize] = Instruction::new(stack::swap::<15, _, _>, 3);
281    table[SWAP16 as usize] = Instruction::new(stack::swap::<16, _, _>, 3);
282
283    table[DUPN as usize] = Instruction::new(stack::dupn, 3);
284    table[SWAPN as usize] = Instruction::new(stack::swapn, 3);
285    table[EXCHANGE as usize] = Instruction::new(stack::exchange, 3);
286
287    table[LOG0 as usize] = Instruction::new(host::log::<0, _>, gas::LOG);
288    table[LOG1 as usize] = Instruction::new(host::log::<1, _>, gas::LOG);
289    table[LOG2 as usize] = Instruction::new(host::log::<2, _>, gas::LOG);
290    table[LOG3 as usize] = Instruction::new(host::log::<3, _>, gas::LOG);
291    table[LOG4 as usize] = Instruction::new(host::log::<4, _>, gas::LOG);
292
293    table[CREATE as usize] = Instruction::new(contract::create::<_, false, _>, 0);
294    table[CALL as usize] = Instruction::new(contract::call, 40);
295    table[CALLCODE as usize] = Instruction::new(contract::call_code, 40);
296    table[RETURN as usize] = Instruction::new(control::ret, 0);
297    table[DELEGATECALL as usize] = Instruction::new(contract::delegate_call, 40);
298    table[CREATE2 as usize] = Instruction::new(contract::create::<_, true, _>, 0);
299
300    table[STATICCALL as usize] = Instruction::new(contract::static_call, 40);
301    table[REVERT as usize] = Instruction::new(control::revert, 0);
302    table[INVALID as usize] = Instruction::new(control::invalid, 0);
303    table[SELFDESTRUCT as usize] = Instruction::new(host::selfdestruct, 0);
304    table
305}
306
307#[cfg(test)]
308mod tests {
309    use super::instruction_table;
310    use crate::{host::DummyHost, interpreter::EthInterpreter};
311    use bytecode::opcode::*;
312
313    #[test]
314    fn all_instructions_and_opcodes_used() {
315        // known unknown instruction we compare it with other instructions from table.
316        let unknown_instruction = 0x0C_usize;
317        let instr_table = instruction_table::<EthInterpreter, DummyHost>();
318
319        let unknown_istr = instr_table[unknown_instruction];
320        for (i, instr) in instr_table.iter().enumerate() {
321            let is_opcode_unknown = OpCode::new(i as u8).is_none();
322            //
323            let is_instr_unknown = std::ptr::fn_addr_eq(instr.fn_, unknown_istr.fn_);
324            assert_eq!(
325                is_instr_unknown, is_opcode_unknown,
326                "Opcode 0x{i:X?} is not handled",
327            );
328        }
329    }
330}