Skip to main content

soldb_core/
opcode.rs

1//! EVM opcodes by byte, with the mnemonics a trace spells them with.
2//!
3//! The names are REVM's, which is what the replay backend and Anvil's `structLogs`
4//! report. A byte with no instruction is spelled `UNKNOWN(0x..)`, as the replay backend
5//! spells it. A step whose backend spelled its mnemonic differently keeps that spelling;
6//! see [`crate::StepTable`].
7
8use std::collections::HashMap;
9use std::sync::OnceLock;
10
11pub const STOP: u8 = 0x00;
12pub const KECCAK256: u8 = 0x20;
13pub const CALLDATACOPY: u8 = 0x37;
14pub const CODECOPY: u8 = 0x39;
15pub const EXTCODECOPY: u8 = 0x3c;
16pub const RETURNDATACOPY: u8 = 0x3e;
17pub const MLOAD: u8 = 0x51;
18pub const MSTORE: u8 = 0x52;
19pub const MSTORE8: u8 = 0x53;
20pub const SLOAD: u8 = 0x54;
21pub const SSTORE: u8 = 0x55;
22pub const JUMP: u8 = 0x56;
23pub const JUMPI: u8 = 0x57;
24pub const JUMPDEST: u8 = 0x5b;
25pub const TLOAD: u8 = 0x5c;
26pub const TSTORE: u8 = 0x5d;
27pub const MCOPY: u8 = 0x5e;
28pub const CREATE: u8 = 0xf0;
29pub const CALL: u8 = 0xf1;
30pub const CALLCODE: u8 = 0xf2;
31pub const RETURN: u8 = 0xf3;
32pub const DELEGATECALL: u8 = 0xf4;
33pub const CREATE2: u8 = 0xf5;
34pub const STATICCALL: u8 = 0xfa;
35pub const REVERT: u8 = 0xfd;
36pub const INVALID: u8 = 0xfe;
37pub const SELFDESTRUCT: u8 = 0xff;
38
39/// The mnemonic of every byte.
40const NAMES: [&str; 256] = [
41    "STOP",
42    "ADD",
43    "MUL",
44    "SUB",
45    "DIV",
46    "SDIV",
47    "MOD",
48    "SMOD",
49    "ADDMOD",
50    "MULMOD",
51    "EXP",
52    "SIGNEXTEND",
53    "UNKNOWN(0x0c)",
54    "UNKNOWN(0x0d)",
55    "UNKNOWN(0x0e)",
56    "UNKNOWN(0x0f)",
57    "LT",
58    "GT",
59    "SLT",
60    "SGT",
61    "EQ",
62    "ISZERO",
63    "AND",
64    "OR",
65    "XOR",
66    "NOT",
67    "BYTE",
68    "SHL",
69    "SHR",
70    "SAR",
71    "CLZ",
72    "UNKNOWN(0x1f)",
73    "KECCAK256",
74    "UNKNOWN(0x21)",
75    "UNKNOWN(0x22)",
76    "UNKNOWN(0x23)",
77    "UNKNOWN(0x24)",
78    "UNKNOWN(0x25)",
79    "UNKNOWN(0x26)",
80    "UNKNOWN(0x27)",
81    "UNKNOWN(0x28)",
82    "UNKNOWN(0x29)",
83    "UNKNOWN(0x2a)",
84    "UNKNOWN(0x2b)",
85    "UNKNOWN(0x2c)",
86    "UNKNOWN(0x2d)",
87    "UNKNOWN(0x2e)",
88    "UNKNOWN(0x2f)",
89    "ADDRESS",
90    "BALANCE",
91    "ORIGIN",
92    "CALLER",
93    "CALLVALUE",
94    "CALLDATALOAD",
95    "CALLDATASIZE",
96    "CALLDATACOPY",
97    "CODESIZE",
98    "CODECOPY",
99    "GASPRICE",
100    "EXTCODESIZE",
101    "EXTCODECOPY",
102    "RETURNDATASIZE",
103    "RETURNDATACOPY",
104    "EXTCODEHASH",
105    "BLOCKHASH",
106    "COINBASE",
107    "TIMESTAMP",
108    "NUMBER",
109    "DIFFICULTY",
110    "GASLIMIT",
111    "CHAINID",
112    "SELFBALANCE",
113    "BASEFEE",
114    "BLOBHASH",
115    "BLOBBASEFEE",
116    "SLOTNUM",
117    "UNKNOWN(0x4c)",
118    "UNKNOWN(0x4d)",
119    "UNKNOWN(0x4e)",
120    "UNKNOWN(0x4f)",
121    "POP",
122    "MLOAD",
123    "MSTORE",
124    "MSTORE8",
125    "SLOAD",
126    "SSTORE",
127    "JUMP",
128    "JUMPI",
129    "PC",
130    "MSIZE",
131    "GAS",
132    "JUMPDEST",
133    "TLOAD",
134    "TSTORE",
135    "MCOPY",
136    "PUSH0",
137    "PUSH1",
138    "PUSH2",
139    "PUSH3",
140    "PUSH4",
141    "PUSH5",
142    "PUSH6",
143    "PUSH7",
144    "PUSH8",
145    "PUSH9",
146    "PUSH10",
147    "PUSH11",
148    "PUSH12",
149    "PUSH13",
150    "PUSH14",
151    "PUSH15",
152    "PUSH16",
153    "PUSH17",
154    "PUSH18",
155    "PUSH19",
156    "PUSH20",
157    "PUSH21",
158    "PUSH22",
159    "PUSH23",
160    "PUSH24",
161    "PUSH25",
162    "PUSH26",
163    "PUSH27",
164    "PUSH28",
165    "PUSH29",
166    "PUSH30",
167    "PUSH31",
168    "PUSH32",
169    "DUP1",
170    "DUP2",
171    "DUP3",
172    "DUP4",
173    "DUP5",
174    "DUP6",
175    "DUP7",
176    "DUP8",
177    "DUP9",
178    "DUP10",
179    "DUP11",
180    "DUP12",
181    "DUP13",
182    "DUP14",
183    "DUP15",
184    "DUP16",
185    "SWAP1",
186    "SWAP2",
187    "SWAP3",
188    "SWAP4",
189    "SWAP5",
190    "SWAP6",
191    "SWAP7",
192    "SWAP8",
193    "SWAP9",
194    "SWAP10",
195    "SWAP11",
196    "SWAP12",
197    "SWAP13",
198    "SWAP14",
199    "SWAP15",
200    "SWAP16",
201    "LOG0",
202    "LOG1",
203    "LOG2",
204    "LOG3",
205    "LOG4",
206    "UNKNOWN(0xa5)",
207    "UNKNOWN(0xa6)",
208    "UNKNOWN(0xa7)",
209    "UNKNOWN(0xa8)",
210    "UNKNOWN(0xa9)",
211    "UNKNOWN(0xaa)",
212    "UNKNOWN(0xab)",
213    "UNKNOWN(0xac)",
214    "UNKNOWN(0xad)",
215    "UNKNOWN(0xae)",
216    "UNKNOWN(0xaf)",
217    "UNKNOWN(0xb0)",
218    "UNKNOWN(0xb1)",
219    "UNKNOWN(0xb2)",
220    "UNKNOWN(0xb3)",
221    "UNKNOWN(0xb4)",
222    "UNKNOWN(0xb5)",
223    "UNKNOWN(0xb6)",
224    "UNKNOWN(0xb7)",
225    "UNKNOWN(0xb8)",
226    "UNKNOWN(0xb9)",
227    "UNKNOWN(0xba)",
228    "UNKNOWN(0xbb)",
229    "UNKNOWN(0xbc)",
230    "UNKNOWN(0xbd)",
231    "UNKNOWN(0xbe)",
232    "UNKNOWN(0xbf)",
233    "UNKNOWN(0xc0)",
234    "UNKNOWN(0xc1)",
235    "UNKNOWN(0xc2)",
236    "UNKNOWN(0xc3)",
237    "UNKNOWN(0xc4)",
238    "UNKNOWN(0xc5)",
239    "UNKNOWN(0xc6)",
240    "UNKNOWN(0xc7)",
241    "UNKNOWN(0xc8)",
242    "UNKNOWN(0xc9)",
243    "UNKNOWN(0xca)",
244    "UNKNOWN(0xcb)",
245    "UNKNOWN(0xcc)",
246    "UNKNOWN(0xcd)",
247    "UNKNOWN(0xce)",
248    "UNKNOWN(0xcf)",
249    "UNKNOWN(0xd0)",
250    "UNKNOWN(0xd1)",
251    "UNKNOWN(0xd2)",
252    "UNKNOWN(0xd3)",
253    "UNKNOWN(0xd4)",
254    "UNKNOWN(0xd5)",
255    "UNKNOWN(0xd6)",
256    "UNKNOWN(0xd7)",
257    "UNKNOWN(0xd8)",
258    "UNKNOWN(0xd9)",
259    "UNKNOWN(0xda)",
260    "UNKNOWN(0xdb)",
261    "UNKNOWN(0xdc)",
262    "UNKNOWN(0xdd)",
263    "UNKNOWN(0xde)",
264    "UNKNOWN(0xdf)",
265    "UNKNOWN(0xe0)",
266    "UNKNOWN(0xe1)",
267    "UNKNOWN(0xe2)",
268    "UNKNOWN(0xe3)",
269    "UNKNOWN(0xe4)",
270    "UNKNOWN(0xe5)",
271    "DUPN",
272    "SWAPN",
273    "EXCHANGE",
274    "UNKNOWN(0xe9)",
275    "UNKNOWN(0xea)",
276    "UNKNOWN(0xeb)",
277    "UNKNOWN(0xec)",
278    "UNKNOWN(0xed)",
279    "UNKNOWN(0xee)",
280    "UNKNOWN(0xef)",
281    "CREATE",
282    "CALL",
283    "CALLCODE",
284    "RETURN",
285    "DELEGATECALL",
286    "CREATE2",
287    "UNKNOWN(0xf6)",
288    "UNKNOWN(0xf7)",
289    "UNKNOWN(0xf8)",
290    "UNKNOWN(0xf9)",
291    "STATICCALL",
292    "UNKNOWN(0xfb)",
293    "UNKNOWN(0xfc)",
294    "REVERT",
295    "INVALID",
296    "SELFDESTRUCT",
297];
298
299/// The mnemonic of `op`.
300#[must_use]
301pub fn mnemonic(op: u8) -> &'static str {
302    NAMES[usize::from(op)]
303}
304
305/// The byte whose mnemonic is `name`, exactly as [`mnemonic`] spells it.
306#[must_use]
307pub fn from_mnemonic(name: &str) -> Option<u8> {
308    static BYTES: OnceLock<HashMap<&'static str, u8>> = OnceLock::new();
309    BYTES
310        .get_or_init(|| (0..=u8::MAX).map(|op| (mnemonic(op), op)).collect())
311        .get(name)
312        .copied()
313}
314
315/// The byte a backend means by `name`: the one [`from_mnemonic`] finds, the one another
316/// client names differently (`SHA3`, `PREVRANDAO`, `SUICIDE`), or the one it spells out
317/// for a byte without an instruction, such as geth's `opcode 0xef not defined`.
318#[must_use]
319pub fn from_spelling(name: &str) -> Option<u8> {
320    if let Some(op) = from_mnemonic(name) {
321        return Some(op);
322    }
323    match name {
324        "SHA3" => Some(KECCAK256),
325        "PREVRANDAO" | "RANDOM" => Some(0x44),
326        "SUICIDE" => Some(SELFDESTRUCT),
327        _ => {
328            let start = name.find("0x").or_else(|| name.find("0X"))? + 2;
329            let digits: String = name
330                .get(start..)?
331                .chars()
332                .take_while(char::is_ascii_hexdigit)
333                .collect();
334            if digits.is_empty() || digits.len() > 2 {
335                return None;
336            }
337            u8::from_str_radix(&digits, 16).ok()
338        }
339    }
340}
341
342/// Whether `op` is one of the four call instructions.
343#[must_use]
344pub fn is_call(op: u8) -> bool {
345    matches!(op, CALL | CALLCODE | DELEGATECALL | STATICCALL)
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351
352    #[test]
353    fn every_byte_round_trips_through_its_mnemonic() {
354        for op in 0..=u8::MAX {
355            assert_eq!(from_mnemonic(mnemonic(op)), Some(op), "{}", mnemonic(op));
356        }
357        assert_eq!(mnemonic(SSTORE), "SSTORE");
358        assert_eq!(mnemonic(0x0c), "UNKNOWN(0x0c)");
359        assert_eq!(from_mnemonic("SHA3"), None);
360    }
361
362    #[test]
363    fn other_clients_spellings_name_the_same_byte() {
364        assert_eq!(from_spelling("SHA3"), Some(KECCAK256));
365        assert_eq!(from_spelling("PREVRANDAO"), Some(0x44));
366        assert_eq!(from_spelling("opcode 0xef not defined"), Some(0xef));
367        assert_eq!(from_spelling("UNKNOWN(0x0c)"), Some(0x0c));
368        assert_eq!(from_spelling("WHATEVER"), None);
369        assert_eq!(from_spelling("opcode 0x123"), None);
370    }
371}