1#![deny(missing_docs)]
30#![warn(clippy::all)]
31
32use std::fmt;
33use std::str::FromStr;
34
35pub mod gas;
36pub mod validation;
37
38#[cfg(feature = "unified-opcodes")]
39pub mod unified;
40#[cfg(feature = "unified-opcodes")]
41pub use unified::UnifiedOpcode;
42
43pub use gas::{DynamicGasCalculator, ExecutionContext, GasAnalysis, GasCostCategory};
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub enum Fork {
57 Frontier,
59 Homestead,
61 TangerineWhistle,
63 SpuriousDragon,
65 Byzantium,
68 Constantinople,
71 Petersburg,
73 Istanbul,
76 Berlin,
78 London,
80 Paris,
82 Shanghai,
84 Cancun,
87 Prague,
89 Fusaka,
91}
92
93impl Fork {
94 pub const fn ordered() -> &'static [Self] {
96 &[
97 Self::Frontier,
98 Self::Homestead,
99 Self::TangerineWhistle,
100 Self::SpuriousDragon,
101 Self::Byzantium,
102 Self::Constantinople,
103 Self::Petersburg,
104 Self::Istanbul,
105 Self::Berlin,
106 Self::London,
107 Self::Paris,
108 Self::Shanghai,
109 Self::Cancun,
110 Self::Prague,
111 Self::Fusaka,
112 ]
113 }
114
115 pub const fn latest() -> Self {
117 Self::Fusaka
118 }
119}
120
121impl fmt::Display for Fork {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 fmt::Debug::fmt(self, f)
124 }
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
133pub enum Group {
134 StopArithmetic,
136 ComparisonBitwiseLogic,
138 Sha3,
140 EnvironmentalInformation,
142 BlockInformation,
144 StackMemoryStorageFlow,
146 Push,
148 Duplication,
150 Exchange,
152 Logging,
154 System,
156 Eof,
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168pub struct OpCodeInfo {
169 pub name: &'static str,
171 pub inputs: u8,
173 pub outputs: u8,
175 pub base_gas: u16,
179 pub group: Group,
181 pub introduced_in: Fork,
183 pub eip: Option<u16>,
185 pub immediate_size: u8,
187 pub terminates: bool,
189}
190
191impl OpCodeInfo {
192 const fn new(
194 name: &'static str,
195 inputs: u8,
196 outputs: u8,
197 gas: u16,
198 group: Group,
199 fork: Fork,
200 ) -> Self {
201 Self {
202 name,
203 inputs,
204 outputs,
205 base_gas: gas,
206 group,
207 introduced_in: fork,
208 eip: None,
209 immediate_size: 0,
210 terminates: false,
211 }
212 }
213
214 const fn eip(mut self, eip: u16) -> Self {
216 self.eip = Some(eip);
217 self
218 }
219
220 const fn imm(mut self, size: u8) -> Self {
222 self.immediate_size = size;
223 self
224 }
225
226 const fn term(mut self) -> Self {
228 self.terminates = true;
229 self
230 }
231
232 pub const fn stack_diff(&self) -> i16 {
234 self.outputs as i16 - self.inputs as i16
235 }
236}
237
238#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
247#[repr(transparent)]
248pub struct OpCode(u8);
249
250macro_rules! opcodes {
255 ($(
256 $byte:literal => $name:ident
257 ($in:expr, $out:expr, $gas:expr, $group:ident, $fork:ident)
258 $([ $($chain:tt)* ])?
259 ;)*) => {
260 impl OpCode {
261 $(
262 #[doc = concat!("`", stringify!($name), "` (`0x", stringify!($byte), "`)")]
263 pub const $name: Self = Self($byte);
264 )*
265 }
266
267 static OPCODE_TABLE: [Option<OpCodeInfo>; 256] = {
268 const NONE: Option<OpCodeInfo> = None;
269 let mut t = [NONE; 256];
270 $(
271 t[$byte as usize] = Some(
272 OpCodeInfo::new(
273 stringify!($name),
274 $in, $out, $gas,
275 Group::$group,
276 Fork::$fork,
277 )
278 $( .$($chain)* )?
279 );
280 )*
281 t
282 };
283 };
284}
285
286opcodes! {
291 0x00 => STOP (0, 0, 0, StopArithmetic, Frontier) [term()];
293 0x01 => ADD (2, 1, 3, StopArithmetic, Frontier);
294 0x02 => MUL (2, 1, 5, StopArithmetic, Frontier);
295 0x03 => SUB (2, 1, 3, StopArithmetic, Frontier);
296 0x04 => DIV (2, 1, 5, StopArithmetic, Frontier);
297 0x05 => SDIV (2, 1, 5, StopArithmetic, Frontier);
298 0x06 => MOD (2, 1, 5, StopArithmetic, Frontier);
299 0x07 => SMOD (2, 1, 5, StopArithmetic, Frontier);
300 0x08 => ADDMOD (3, 1, 8, StopArithmetic, Frontier);
301 0x09 => MULMOD (3, 1, 8, StopArithmetic, Frontier);
302 0x0a => EXP (2, 1, 10, StopArithmetic, Frontier);
303 0x0b => SIGNEXTEND (2, 1, 5, StopArithmetic, Frontier);
304
305 0x10 => LT (2, 1, 3, ComparisonBitwiseLogic, Frontier);
307 0x11 => GT (2, 1, 3, ComparisonBitwiseLogic, Frontier);
308 0x12 => SLT (2, 1, 3, ComparisonBitwiseLogic, Frontier);
309 0x13 => SGT (2, 1, 3, ComparisonBitwiseLogic, Frontier);
310 0x14 => EQ (2, 1, 3, ComparisonBitwiseLogic, Frontier);
311 0x15 => ISZERO (1, 1, 3, ComparisonBitwiseLogic, Frontier);
312 0x16 => AND (2, 1, 3, ComparisonBitwiseLogic, Frontier);
313 0x17 => OR (2, 1, 3, ComparisonBitwiseLogic, Frontier);
314 0x18 => XOR (2, 1, 3, ComparisonBitwiseLogic, Frontier);
315 0x19 => NOT (1, 1, 3, ComparisonBitwiseLogic, Frontier);
316 0x1a => BYTE (2, 1, 3, ComparisonBitwiseLogic, Frontier);
317 0x1b => SHL (2, 1, 3, ComparisonBitwiseLogic, Constantinople) [eip(145)];
318 0x1c => SHR (2, 1, 3, ComparisonBitwiseLogic, Constantinople) [eip(145)];
319 0x1d => SAR (2, 1, 3, ComparisonBitwiseLogic, Constantinople) [eip(145)];
320 0x1e => CLZ (1, 1, 3, ComparisonBitwiseLogic, Fusaka) [eip(7939)];
321
322 0x20 => KECCAK256 (2, 1, 30, Sha3, Frontier);
324
325 0x30 => ADDRESS (0, 1, 2, EnvironmentalInformation, Frontier);
327 0x31 => BALANCE (1, 1, 20, EnvironmentalInformation, Frontier);
328 0x32 => ORIGIN (0, 1, 2, EnvironmentalInformation, Frontier);
329 0x33 => CALLER (0, 1, 2, EnvironmentalInformation, Frontier);
330 0x34 => CALLVALUE (0, 1, 2, EnvironmentalInformation, Frontier);
331 0x35 => CALLDATALOAD (1, 1, 3, EnvironmentalInformation, Frontier);
332 0x36 => CALLDATASIZE (0, 1, 2, EnvironmentalInformation, Frontier);
333 0x37 => CALLDATACOPY (3, 0, 3, EnvironmentalInformation, Frontier);
334 0x38 => CODESIZE (0, 1, 2, EnvironmentalInformation, Frontier);
335 0x39 => CODECOPY (3, 0, 3, EnvironmentalInformation, Frontier);
336 0x3a => GASPRICE (0, 1, 2, EnvironmentalInformation, Frontier);
337 0x3b => EXTCODESIZE (1, 1, 20, EnvironmentalInformation, Frontier);
338 0x3c => EXTCODECOPY (4, 0, 20, EnvironmentalInformation, Frontier);
339 0x3d => RETURNDATASIZE (0, 1, 2, EnvironmentalInformation, Byzantium) [eip(211)];
340 0x3e => RETURNDATACOPY (3, 0, 3, EnvironmentalInformation, Byzantium) [eip(211)];
341 0x3f => EXTCODEHASH (1, 1, 400, EnvironmentalInformation, Constantinople) [eip(1052)];
342
343 0x40 => BLOCKHASH (1, 1, 20, BlockInformation, Frontier);
345 0x41 => COINBASE (0, 1, 2, BlockInformation, Frontier);
346 0x42 => TIMESTAMP (0, 1, 2, BlockInformation, Frontier);
347 0x43 => NUMBER (0, 1, 2, BlockInformation, Frontier);
348 0x44 => DIFFICULTY (0, 1, 2, BlockInformation, Frontier);
349 0x45 => GASLIMIT (0, 1, 2, BlockInformation, Frontier);
350 0x46 => CHAINID (0, 1, 2, BlockInformation, Istanbul) [eip(1344)];
351 0x47 => SELFBALANCE (0, 1, 5, BlockInformation, Istanbul) [eip(1884)];
352 0x48 => BASEFEE (0, 1, 2, BlockInformation, London) [eip(3198)];
353 0x49 => BLOBHASH (1, 1, 3, BlockInformation, Cancun) [eip(4844)];
354 0x4a => BLOBBASEFEE (0, 1, 2, BlockInformation, Cancun) [eip(7516)];
355
356 0x50 => POP (1, 0, 2, StackMemoryStorageFlow, Frontier);
358 0x51 => MLOAD (1, 1, 3, StackMemoryStorageFlow, Frontier);
359 0x52 => MSTORE (2, 0, 3, StackMemoryStorageFlow, Frontier);
360 0x53 => MSTORE8 (2, 0, 3, StackMemoryStorageFlow, Frontier);
361 0x54 => SLOAD (1, 1, 50, StackMemoryStorageFlow, Frontier);
362 0x55 => SSTORE (2, 0, 5000, StackMemoryStorageFlow, Frontier);
363 0x56 => JUMP (1, 0, 8, StackMemoryStorageFlow, Frontier);
364 0x57 => JUMPI (2, 0, 10, StackMemoryStorageFlow, Frontier);
365 0x58 => PC (0, 1, 2, StackMemoryStorageFlow, Frontier);
366 0x59 => MSIZE (0, 1, 2, StackMemoryStorageFlow, Frontier);
367 0x5a => GAS (0, 1, 2, StackMemoryStorageFlow, Frontier);
368 0x5b => JUMPDEST (0, 0, 1, StackMemoryStorageFlow, Frontier);
369 0x5c => TLOAD (1, 1, 100, StackMemoryStorageFlow, Cancun) [eip(1153)];
370 0x5d => TSTORE (2, 0, 100, StackMemoryStorageFlow, Cancun) [eip(1153)];
371 0x5e => MCOPY (3, 0, 3, StackMemoryStorageFlow, Cancun) [eip(5656)];
372 0x5f => PUSH0 (0, 1, 2, Push, Shanghai) [eip(3855)];
373
374 0x60 => PUSH1 (0, 1, 3, Push, Frontier) [imm(1)];
376 0x61 => PUSH2 (0, 1, 3, Push, Frontier) [imm(2)];
377 0x62 => PUSH3 (0, 1, 3, Push, Frontier) [imm(3)];
378 0x63 => PUSH4 (0, 1, 3, Push, Frontier) [imm(4)];
379 0x64 => PUSH5 (0, 1, 3, Push, Frontier) [imm(5)];
380 0x65 => PUSH6 (0, 1, 3, Push, Frontier) [imm(6)];
381 0x66 => PUSH7 (0, 1, 3, Push, Frontier) [imm(7)];
382 0x67 => PUSH8 (0, 1, 3, Push, Frontier) [imm(8)];
383 0x68 => PUSH9 (0, 1, 3, Push, Frontier) [imm(9)];
384 0x69 => PUSH10 (0, 1, 3, Push, Frontier) [imm(10)];
385 0x6a => PUSH11 (0, 1, 3, Push, Frontier) [imm(11)];
386 0x6b => PUSH12 (0, 1, 3, Push, Frontier) [imm(12)];
387 0x6c => PUSH13 (0, 1, 3, Push, Frontier) [imm(13)];
388 0x6d => PUSH14 (0, 1, 3, Push, Frontier) [imm(14)];
389 0x6e => PUSH15 (0, 1, 3, Push, Frontier) [imm(15)];
390 0x6f => PUSH16 (0, 1, 3, Push, Frontier) [imm(16)];
391 0x70 => PUSH17 (0, 1, 3, Push, Frontier) [imm(17)];
392 0x71 => PUSH18 (0, 1, 3, Push, Frontier) [imm(18)];
393 0x72 => PUSH19 (0, 1, 3, Push, Frontier) [imm(19)];
394 0x73 => PUSH20 (0, 1, 3, Push, Frontier) [imm(20)];
395 0x74 => PUSH21 (0, 1, 3, Push, Frontier) [imm(21)];
396 0x75 => PUSH22 (0, 1, 3, Push, Frontier) [imm(22)];
397 0x76 => PUSH23 (0, 1, 3, Push, Frontier) [imm(23)];
398 0x77 => PUSH24 (0, 1, 3, Push, Frontier) [imm(24)];
399 0x78 => PUSH25 (0, 1, 3, Push, Frontier) [imm(25)];
400 0x79 => PUSH26 (0, 1, 3, Push, Frontier) [imm(26)];
401 0x7a => PUSH27 (0, 1, 3, Push, Frontier) [imm(27)];
402 0x7b => PUSH28 (0, 1, 3, Push, Frontier) [imm(28)];
403 0x7c => PUSH29 (0, 1, 3, Push, Frontier) [imm(29)];
404 0x7d => PUSH30 (0, 1, 3, Push, Frontier) [imm(30)];
405 0x7e => PUSH31 (0, 1, 3, Push, Frontier) [imm(31)];
406 0x7f => PUSH32 (0, 1, 3, Push, Frontier) [imm(32)];
407
408 0x80 => DUP1 ( 1, 2, 3, Duplication, Frontier);
410 0x81 => DUP2 ( 2, 3, 3, Duplication, Frontier);
411 0x82 => DUP3 ( 3, 4, 3, Duplication, Frontier);
412 0x83 => DUP4 ( 4, 5, 3, Duplication, Frontier);
413 0x84 => DUP5 ( 5, 6, 3, Duplication, Frontier);
414 0x85 => DUP6 ( 6, 7, 3, Duplication, Frontier);
415 0x86 => DUP7 ( 7, 8, 3, Duplication, Frontier);
416 0x87 => DUP8 ( 8, 9, 3, Duplication, Frontier);
417 0x88 => DUP9 ( 9, 10, 3, Duplication, Frontier);
418 0x89 => DUP10 (10, 11, 3, Duplication, Frontier);
419 0x8a => DUP11 (11, 12, 3, Duplication, Frontier);
420 0x8b => DUP12 (12, 13, 3, Duplication, Frontier);
421 0x8c => DUP13 (13, 14, 3, Duplication, Frontier);
422 0x8d => DUP14 (14, 15, 3, Duplication, Frontier);
423 0x8e => DUP15 (15, 16, 3, Duplication, Frontier);
424 0x8f => DUP16 (16, 17, 3, Duplication, Frontier);
425
426 0x90 => SWAP1 ( 2, 2, 3, Exchange, Frontier);
428 0x91 => SWAP2 ( 3, 3, 3, Exchange, Frontier);
429 0x92 => SWAP3 ( 4, 4, 3, Exchange, Frontier);
430 0x93 => SWAP4 ( 5, 5, 3, Exchange, Frontier);
431 0x94 => SWAP5 ( 6, 6, 3, Exchange, Frontier);
432 0x95 => SWAP6 ( 7, 7, 3, Exchange, Frontier);
433 0x96 => SWAP7 ( 8, 8, 3, Exchange, Frontier);
434 0x97 => SWAP8 ( 9, 9, 3, Exchange, Frontier);
435 0x98 => SWAP9 (10, 10, 3, Exchange, Frontier);
436 0x99 => SWAP10 (11, 11, 3, Exchange, Frontier);
437 0x9a => SWAP11 (12, 12, 3, Exchange, Frontier);
438 0x9b => SWAP12 (13, 13, 3, Exchange, Frontier);
439 0x9c => SWAP13 (14, 14, 3, Exchange, Frontier);
440 0x9d => SWAP14 (15, 15, 3, Exchange, Frontier);
441 0x9e => SWAP15 (16, 16, 3, Exchange, Frontier);
442 0x9f => SWAP16 (17, 17, 3, Exchange, Frontier);
443
444 0xa0 => LOG0 (2, 0, 375, Logging, Frontier);
446 0xa1 => LOG1 (3, 0, 750, Logging, Frontier);
447 0xa2 => LOG2 (4, 0, 1125, Logging, Frontier);
448 0xa3 => LOG3 (5, 0, 1500, Logging, Frontier);
449 0xa4 => LOG4 (6, 0, 1875, Logging, Frontier);
450
451 0xf0 => CREATE (3, 1, 32000, System, Frontier);
453 0xf1 => CALL (7, 1, 40, System, Frontier);
454 0xf2 => CALLCODE (7, 1, 40, System, Frontier);
455 0xf3 => RETURN (2, 0, 0, System, Frontier) [term()];
456 0xf4 => DELEGATECALL (6, 1, 40, System, Homestead) [eip(7)];
457 0xf5 => CREATE2 (4, 1, 32000, System, Constantinople) [eip(1014)];
458 0xfa => STATICCALL (6, 1, 700, System, Byzantium) [eip(214)];
459 0xfd => REVERT (2, 0, 0, System, Byzantium) [eip(140).term()];
460 0xfe => INVALID (0, 0, 0, System, Frontier) [term()];
461 0xff => SELFDESTRUCT (1, 0, 0, System, Frontier);
462
463 0xd0 => DATALOAD (1, 1, 4, Eof, Fusaka) [eip(7480)];
465 0xd1 => DATALOADN (0, 1, 3, Eof, Fusaka) [eip(7480).imm(2)];
466 0xd2 => DATASIZE (0, 1, 2, Eof, Fusaka) [eip(7480)];
467 0xd3 => DATACOPY (3, 0, 3, Eof, Fusaka) [eip(7480)];
468
469 0xe0 => RJUMP (0, 0, 2, Eof, Fusaka) [eip(4200).imm(2)];
471 0xe1 => RJUMPI (1, 0, 4, Eof, Fusaka) [eip(4200).imm(2)];
472 0xe2 => RJUMPV (1, 0, 4, Eof, Fusaka) [eip(4200).imm(1)];
473 0xe3 => CALLF (0, 0, 5, Eof, Fusaka) [eip(4750).imm(2)];
474 0xe4 => RETF (0, 0, 3, Eof, Fusaka) [eip(4750).term()];
475 0xe5 => JUMPF (0, 0, 5, Eof, Fusaka) [eip(6206).imm(2).term()];
476 0xe6 => DUPN (0, 1, 3, Eof, Fusaka) [eip(663).imm(1)];
477 0xe7 => SWAPN (0, 0, 3, Eof, Fusaka) [eip(663).imm(1)];
478 0xe8 => EXCHANGE (0, 0, 3, Eof, Fusaka) [eip(663).imm(1)];
479
480 0xec => EOFCREATE (4, 1, 32000, Eof, Fusaka) [eip(7620).imm(1)];
482 0xee => RETURNCONTRACT (2, 0, 0, Eof, Fusaka) [eip(7620).imm(1).term()];
483
484 0xf7 => RETURNDATALOAD (1, 1, 3, System, Fusaka) [eip(7069)];
486 0xf8 => EXTCALL (4, 1, 100, System, Fusaka) [eip(7069)];
487 0xf9 => EXTDELEGATECALL (3, 1, 100, System, Fusaka) [eip(7069)];
488 0xfb => EXTSTATICCALL (3, 1, 100, System, Fusaka) [eip(7069)];
489}
490
491impl OpCode {
494 pub const PREVRANDAO: Self = Self(0x44);
496}
497
498impl OpCode {
503 pub const fn new(byte: u8) -> Option<Self> {
505 if OPCODE_TABLE[byte as usize].is_some() {
506 Some(Self(byte))
507 } else {
508 None
509 }
510 }
511
512 pub const fn from_byte(byte: u8) -> Self {
516 Self(byte)
517 }
518
519 pub const fn byte(&self) -> u8 {
521 self.0
522 }
523
524 pub const fn info(&self) -> Option<&'static OpCodeInfo> {
526 match &OPCODE_TABLE[self.0 as usize] {
527 Some(info) => Some(info),
528 None => None,
529 }
530 }
531
532 pub fn name(&self) -> &'static str {
534 match self.info() {
535 Some(info) => info.name,
536 None => "UNKNOWN",
537 }
538 }
539
540 pub fn is_valid_in(&self, fork: Fork) -> bool {
542 match self.info() {
543 Some(info) => info.introduced_in <= fork,
544 None => false,
545 }
546 }
547
548 pub fn gas_cost(&self, fork: Fork) -> u16 {
554 match self.info() {
555 Some(info) => gas_cost_for_fork(self.0, info.base_gas, fork),
556 None => 0,
557 }
558 }
559
560 pub const fn is_push(&self) -> bool {
562 matches!(self.0, 0x5f..=0x7f)
563 }
564
565 pub const fn is_dup(&self) -> bool {
567 matches!(self.0, 0x80..=0x8f)
568 }
569
570 pub const fn is_swap(&self) -> bool {
572 matches!(self.0, 0x90..=0x9f)
573 }
574
575 pub const fn is_log(&self) -> bool {
577 matches!(self.0, 0xa0..=0xa4)
578 }
579
580 pub fn terminates(&self) -> bool {
582 self.info().is_some_and(|i| i.terminates)
583 }
584
585 pub fn is_control_flow(&self) -> bool {
587 matches!(
588 self.0,
589 0x00 | 0x56 | 0x57 | 0x5b | 0xf3 | 0xfd | 0xfe | 0xff |
590 0xe0..=0xe5
592 )
593 }
594
595 pub fn modifies_state(&self) -> bool {
597 matches!(
598 self.0,
599 0x55 | 0x5d | 0xf0 | 0xf1 | 0xf2 | 0xf4 | 0xf5 | 0xff |
600 0xec | 0xf8 | 0xf9
602 )
603 }
604
605 pub fn affects_memory(&self) -> bool {
607 matches!(
608 self.0,
609 0x20 | 0x37 | 0x39 | 0x3e | 0x51..=0x53 | 0x5e |
610 0xa0..=0xa4 | 0xf0..=0xf5 | 0xfa | 0xfd |
611 0xd3 | 0xec | 0xee
613 )
614 }
615
616 pub fn affects_storage(&self) -> bool {
618 matches!(self.0, 0x54 | 0x55 | 0x5c | 0x5d)
619 }
620
621 pub fn has_dynamic_gas(&self) -> bool {
623 matches!(
624 self.0,
625 0x0a | 0x20 |
626 0x31 | 0x37 | 0x39 | 0x3b | 0x3c | 0x3e | 0x3f |
627 0x54 | 0x55 | 0x5c | 0x5d | 0x5e |
628 0x51..=0x53 |
629 0xa0..=0xa4 |
630 0xf0..=0xf5 | 0xfa | 0xff |
631 0xd3 | 0xec | 0xf8 | 0xf9 | 0xfb
633 )
634 }
635
636 pub fn is_eof(&self) -> bool {
638 matches!(
639 self.0,
640 0xd0..=0xd3 | 0xe0..=0xe8 | 0xec | 0xee
641 )
642 }
643
644 pub fn iter_all() -> impl Iterator<Item = OpCode> {
646 (0u16..=255).filter_map(|b| Self::new(b as u8))
647 }
648
649 pub fn count_at(fork: Fork) -> usize {
651 Self::iter_all().filter(|op| op.is_valid_in(fork)).count()
652 }
653}
654
655fn gas_cost_for_fork(byte: u8, base: u16, fork: Fork) -> u16 {
664 use Fork::*;
665 match byte {
666 0x31 => {
668 if fork >= Berlin {
669 2600
670 } else if fork >= Istanbul {
671 700
672 } else if fork >= TangerineWhistle {
673 400
674 } else {
675 base
676 }
677 }
678 0x3b => {
680 if fork >= Berlin {
681 2600
682 } else if fork >= TangerineWhistle {
683 700
684 } else {
685 base
686 }
687 }
688 0x3c => {
690 if fork >= Berlin {
691 2600
692 } else if fork >= TangerineWhistle {
693 700
694 } else {
695 base
696 }
697 }
698 0x3f => {
700 if fork >= Berlin {
701 2600
702 } else if fork >= Istanbul {
703 700
704 } else {
705 base
706 }
707 }
708 0x54 => {
710 if fork >= Berlin {
711 2100
712 } else if fork >= Istanbul {
713 800
714 } else if fork >= TangerineWhistle {
715 200
716 } else {
717 base
718 }
719 }
720 0xf1 | 0xf2 => {
722 if fork >= Berlin {
723 100
724 } else if fork >= TangerineWhistle {
725 700
726 } else {
727 base
728 }
729 }
730 0xf4 => {
732 if fork >= Berlin {
733 100
734 } else if fork >= TangerineWhistle {
735 700
736 } else {
737 base
738 }
739 }
740 0xfa => {
742 if fork >= Berlin {
743 100
744 } else {
745 base
746 }
747 }
748 0xff => {
750 if fork >= TangerineWhistle {
751 5000
752 } else {
753 base
754 }
755 }
756 _ => base,
757 }
758}
759
760impl fmt::Debug for OpCode {
765 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
766 match self.info() {
767 Some(info) => write!(f, "{}", info.name),
768 None => write!(f, "UNKNOWN(0x{:02x})", self.0),
769 }
770 }
771}
772
773impl fmt::Display for OpCode {
774 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775 fmt::Debug::fmt(self, f)
776 }
777}
778
779impl From<u8> for OpCode {
780 fn from(byte: u8) -> Self {
781 Self(byte)
782 }
783}
784
785impl From<OpCode> for u8 {
786 fn from(op: OpCode) -> Self {
787 op.0
788 }
789}
790
791impl FromStr for OpCode {
792 type Err = String;
793
794 fn from_str(s: &str) -> Result<Self, Self::Err> {
795 for (i, entry) in OPCODE_TABLE.iter().enumerate() {
797 if let Some(info) = entry {
798 if info.name.eq_ignore_ascii_case(s) {
799 return Ok(Self(i as u8));
800 }
801 }
802 }
803 match s {
805 "SHA3" => Ok(Self::KECCAK256),
806 "PREVRANDAO" => Ok(Self::DIFFICULTY),
807 _ => Err(format!("unknown opcode: {s}")),
808 }
809 }
810}
811
812#[cfg(feature = "serde")]
817mod serde_impl {
818 use super::OpCode;
819
820 impl serde::Serialize for OpCode {
821 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
822 serializer.serialize_str(self.name())
823 }
824 }
825
826 impl<'de> serde::Deserialize<'de> for OpCode {
827 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
828 struct Visitor;
829 impl serde::de::Visitor<'_> for Visitor {
830 type Value = OpCode;
831 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
832 f.write_str("an opcode name or byte value")
833 }
834 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<OpCode, E> {
835 v.parse().map_err(E::custom)
836 }
837 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<OpCode, E> {
838 if v > 255 {
839 return Err(E::custom("opcode byte out of range"));
840 }
841 Ok(OpCode::from_byte(v as u8))
842 }
843 }
844 deserializer.deserialize_any(Visitor)
845 }
846 }
847}
848
849#[cfg(test)]
854mod tests {
855 use super::*;
856
857 #[test]
858 fn opcode_constants() {
859 assert_eq!(OpCode::STOP.byte(), 0x00);
860 assert_eq!(OpCode::ADD.byte(), 0x01);
861 assert_eq!(OpCode::PUSH1.byte(), 0x60);
862 assert_eq!(OpCode::DUP1.byte(), 0x80);
863 assert_eq!(OpCode::SWAP1.byte(), 0x90);
864 assert_eq!(OpCode::SELFDESTRUCT.byte(), 0xff);
865 assert_eq!(OpCode::PREVRANDAO, OpCode::DIFFICULTY);
866 }
867
868 #[test]
869 fn opcode_new_valid() {
870 assert!(OpCode::new(0x01).is_some()); assert!(OpCode::new(0x5f).is_some()); assert!(OpCode::new(0xff).is_some()); }
874
875 #[test]
876 fn opcode_new_invalid() {
877 assert!(OpCode::new(0x0c).is_none()); assert!(OpCode::new(0x21).is_none()); assert!(OpCode::new(0xef).is_none()); }
881
882 #[test]
883 fn opcode_info() {
884 let add = OpCode::ADD;
885 let info = add.info().unwrap();
886 assert_eq!(info.name, "ADD");
887 assert_eq!(info.inputs, 2);
888 assert_eq!(info.outputs, 1);
889 assert_eq!(info.base_gas, 3);
890 assert_eq!(info.group, Group::StopArithmetic);
891 assert_eq!(info.introduced_in, Fork::Frontier);
892 }
893
894 #[test]
895 fn fork_availability() {
896 assert!(OpCode::ADD.is_valid_in(Fork::Frontier));
898 assert!(OpCode::ADD.is_valid_in(Fork::Prague));
899
900 assert!(!OpCode::DELEGATECALL.is_valid_in(Fork::Frontier));
902 assert!(OpCode::DELEGATECALL.is_valid_in(Fork::Homestead));
903
904 assert!(!OpCode::TLOAD.is_valid_in(Fork::Shanghai));
906 assert!(OpCode::TLOAD.is_valid_in(Fork::Cancun));
907
908 assert!(!OpCode::PUSH0.is_valid_in(Fork::London));
910 assert!(OpCode::PUSH0.is_valid_in(Fork::Shanghai));
911 }
912
913 #[test]
914 fn gas_cost_frontier() {
915 assert_eq!(OpCode::ADD.gas_cost(Fork::Frontier), 3);
916 assert_eq!(OpCode::SLOAD.gas_cost(Fork::Frontier), 50);
917 assert_eq!(OpCode::BALANCE.gas_cost(Fork::Frontier), 20);
918 assert_eq!(OpCode::CALL.gas_cost(Fork::Frontier), 40);
919 }
920
921 #[test]
922 fn gas_cost_tangerine_whistle() {
923 assert_eq!(OpCode::SLOAD.gas_cost(Fork::TangerineWhistle), 200);
924 assert_eq!(OpCode::BALANCE.gas_cost(Fork::TangerineWhistle), 400);
925 assert_eq!(OpCode::CALL.gas_cost(Fork::TangerineWhistle), 700);
926 assert_eq!(OpCode::SELFDESTRUCT.gas_cost(Fork::TangerineWhistle), 5000);
927 }
928
929 #[test]
930 fn gas_cost_istanbul() {
931 assert_eq!(OpCode::SLOAD.gas_cost(Fork::Istanbul), 800);
932 assert_eq!(OpCode::BALANCE.gas_cost(Fork::Istanbul), 700);
933 }
934
935 #[test]
936 fn gas_cost_berlin() {
937 assert_eq!(OpCode::SLOAD.gas_cost(Fork::Berlin), 2100);
938 assert_eq!(OpCode::BALANCE.gas_cost(Fork::Berlin), 2600);
939 assert_eq!(OpCode::EXTCODESIZE.gas_cost(Fork::Berlin), 2600);
940 assert_eq!(OpCode::CALL.gas_cost(Fork::Berlin), 100);
941 assert_eq!(OpCode::STATICCALL.gas_cost(Fork::Berlin), 100);
942 }
943
944 #[test]
945 fn classification() {
946 assert!(OpCode::PUSH0.is_push());
947 assert!(OpCode::PUSH1.is_push());
948 assert!(OpCode::PUSH32.is_push());
949 assert!(!OpCode::ADD.is_push());
950
951 assert!(OpCode::DUP1.is_dup());
952 assert!(OpCode::DUP16.is_dup());
953 assert!(!OpCode::PUSH1.is_dup());
954
955 assert!(OpCode::SWAP1.is_swap());
956 assert!(OpCode::SWAP16.is_swap());
957 assert!(!OpCode::DUP1.is_swap());
958
959 assert!(OpCode::STOP.terminates());
960 assert!(OpCode::RETURN.terminates());
961 assert!(OpCode::REVERT.terminates());
962 assert!(!OpCode::ADD.terminates());
963 }
964
965 #[test]
966 fn display() {
967 assert_eq!(OpCode::ADD.to_string(), "ADD");
968 assert_eq!(OpCode::PUSH1.to_string(), "PUSH1");
969 assert_eq!(OpCode::from_byte(0x0c).to_string(), "UNKNOWN(0x0c)");
970 }
971
972 #[test]
973 fn from_str_roundtrip() {
974 for op in OpCode::iter_all() {
975 let name = op.name();
976 let parsed: OpCode = name.parse().unwrap();
977 assert_eq!(parsed, op, "roundtrip failed for {name}");
978 }
979 }
980
981 #[test]
982 fn from_str_aliases() {
983 assert_eq!("SHA3".parse::<OpCode>().unwrap(), OpCode::KECCAK256);
984 assert_eq!("PREVRANDAO".parse::<OpCode>().unwrap(), OpCode::DIFFICULTY);
985 }
986
987 #[test]
988 fn byte_roundtrip() {
989 for b in 0u8..=255 {
990 assert_eq!(OpCode::from_byte(b).byte(), b);
991 }
992 }
993
994 #[test]
995 fn immediate_sizes() {
996 assert_eq!(OpCode::PUSH0.info().unwrap().immediate_size, 0);
997 assert_eq!(OpCode::PUSH1.info().unwrap().immediate_size, 1);
998 assert_eq!(OpCode::PUSH32.info().unwrap().immediate_size, 32);
999 assert_eq!(OpCode::ADD.info().unwrap().immediate_size, 0);
1000 }
1001
1002 #[test]
1003 fn opcode_count_grows_with_forks() {
1004 let frontier = OpCode::count_at(Fork::Frontier);
1005 let homestead = OpCode::count_at(Fork::Homestead);
1006 let cancun = OpCode::count_at(Fork::Cancun);
1007
1008 assert!(homestead > frontier);
1009 assert!(cancun > homestead);
1010 }
1011
1012 #[test]
1013 fn eip_references() {
1014 assert_eq!(OpCode::PUSH0.info().unwrap().eip, Some(3855));
1015 assert_eq!(OpCode::TLOAD.info().unwrap().eip, Some(1153));
1016 assert_eq!(OpCode::CREATE2.info().unwrap().eip, Some(1014));
1017 assert_eq!(OpCode::ADD.info().unwrap().eip, None);
1018 }
1019}