1use alloy_primitives::{U256, hex};
2use phf::phf_map;
3use std::fmt;
4use strum_macros::{AsRefStr, EnumString};
5
6use crate::evm_version::SupportedEVMVersions;
7
8pub const OPCODES: [&str; 156] = [
13 "addmod",
14 "address",
15 "add",
16 "and",
17 "balance",
18 "basefee",
19 "blobbasefee",
20 "blobhash",
21 "blockhash",
22 "byte",
23 "callcode",
24 "calldatacopy",
25 "calldataload",
26 "calldatasize",
27 "caller",
28 "callvalue",
29 "call",
30 "chainid",
31 "clz",
32 "codecopy",
33 "codesize",
34 "coinbase",
35 "create2",
36 "create",
37 "delegatecall",
38 "difficulty",
39 "div",
40 "dup10",
41 "dup11",
42 "dup12",
43 "dup13",
44 "dup14",
45 "dup15",
46 "dup16",
47 "dup1",
48 "dup2",
49 "dup3",
50 "dup4",
51 "dup5",
52 "dup6",
53 "dup7",
54 "dup8",
55 "dup9",
56 "dupn",
57 "eq",
58 "exchange",
59 "exp",
60 "extcodecopy",
61 "extcodehash",
62 "extcodesize",
63 "gaslimit",
64 "gasprice",
65 "gas",
66 "gt",
67 "invalid",
68 "iszero",
69 "jumpdest",
70 "jumpi",
71 "jump",
72 "keccak256",
73 "log0",
74 "log1",
75 "log2",
76 "log3",
77 "log4",
78 "lt",
79 "mcopy",
80 "mload",
81 "mod",
82 "msize",
83 "mstore8",
84 "mstore",
85 "mulmod",
86 "mul",
87 "not",
88 "number",
89 "origin",
90 "or",
91 "pc",
92 "pop",
93 "prevrandao",
94 "push0",
95 "push10",
96 "push11",
97 "push12",
98 "push13",
99 "push14",
100 "push15",
101 "push16",
102 "push17",
103 "push18",
104 "push19",
105 "push1",
106 "push20",
107 "push21",
108 "push22",
109 "push23",
110 "push24",
111 "push25",
112 "push26",
113 "push27",
114 "push28",
115 "push29",
116 "push2",
117 "push30",
118 "push31",
119 "push32",
120 "push3",
121 "push4",
122 "push5",
123 "push6",
124 "push7",
125 "push8",
126 "push9",
127 "returndatacopy",
128 "returndatasize",
129 "return",
130 "revert",
131 "sar",
132 "sdiv",
133 "selfbalance",
134 "selfdestruct",
135 "sgt",
136 "sha3",
137 "shl",
138 "shr",
139 "signextend",
140 "sload",
141 "slotnum",
142 "slt",
143 "smod",
144 "sstore",
145 "staticcall",
146 "stop",
147 "sub",
148 "swap10",
149 "swap11",
150 "swap12",
151 "swap13",
152 "swap14",
153 "swap15",
154 "swap16",
155 "swap1",
156 "swap2",
157 "swap3",
158 "swap4",
159 "swap5",
160 "swap6",
161 "swap7",
162 "swap8",
163 "swap9",
164 "swapn",
165 "timestamp",
166 "tload",
167 "tstore",
168 "xor",
169];
170
171pub static OPCODES_MAP: phf::Map<&'static str, Opcode> = phf_map! {
173 "lt" => Opcode::Lt,
174 "gt" => Opcode::Gt,
175 "slt" => Opcode::Slt,
176 "sgt" => Opcode::Sgt,
177 "eq" => Opcode::Eq,
178 "iszero" => Opcode::Iszero,
179 "and" => Opcode::And,
180 "or" => Opcode::Or,
181 "xor" => Opcode::Xor,
182 "not" => Opcode::Not,
183 "sha3" => Opcode::Keccak256, "keccak256" => Opcode::Keccak256,
185 "address" => Opcode::Address,
186 "balance" => Opcode::Balance,
187 "origin" => Opcode::Origin,
188 "caller" => Opcode::Caller,
189 "callvalue" => Opcode::Callvalue,
190 "calldataload" => Opcode::Calldataload,
191 "calldatasize" => Opcode::Calldatasize,
192 "calldatacopy" => Opcode::Calldatacopy,
193 "codesize" => Opcode::Codesize,
194 "codecopy" => Opcode::Codecopy,
195 "basefee" => Opcode::Basefee,
196 "blobhash" => Opcode::Blobhash,
197 "blobbasefee" => Opcode::Blobbasefee,
198 "blockhash" => Opcode::Blockhash,
199 "coinbase" => Opcode::Coinbase,
200 "timestamp" => Opcode::Timestamp,
201 "number" => Opcode::Number,
202 "difficulty" => Opcode::Difficulty,
203 "prevrandao" => Opcode::Prevrandao,
204 "gaslimit" => Opcode::Gaslimit,
205 "chainid" => Opcode::Chainid,
206 "clz" => Opcode::Clz,
207 "selfbalance" => Opcode::Selfbalance,
208 "slotnum" => Opcode::Slotnum,
209 "pop" => Opcode::Pop,
210 "mload" => Opcode::Mload,
211 "mstore" => Opcode::Mstore,
212 "mstore8" => Opcode::Mstore8,
213 "sload" => Opcode::Sload,
214 "sstore" => Opcode::Sstore,
215 "jump" => Opcode::Jump,
216 "jumpi" => Opcode::Jumpi,
217 "pc" => Opcode::Pc,
218 "msize" => Opcode::Msize,
219 "mcopy" => Opcode::Mcopy,
220 "push0" => Opcode::Push0,
221 "push1" => Opcode::Push1,
222 "push2" => Opcode::Push2,
223 "push3" => Opcode::Push3,
224 "push4" => Opcode::Push4,
225 "push5" => Opcode::Push5,
226 "push6" => Opcode::Push6,
227 "push7" => Opcode::Push7,
228 "push8" => Opcode::Push8,
229 "push9" => Opcode::Push9,
230 "push10" => Opcode::Push10,
231 "push17" => Opcode::Push17,
232 "push18" => Opcode::Push18,
233 "push19" => Opcode::Push19,
234 "push20" => Opcode::Push20,
235 "push21" => Opcode::Push21,
236 "push22" => Opcode::Push22,
237 "push23" => Opcode::Push23,
238 "push24" => Opcode::Push24,
239 "push25" => Opcode::Push25,
240 "push26" => Opcode::Push26,
241 "dup1" => Opcode::Dup1,
242 "dup2" => Opcode::Dup2,
243 "dup3" => Opcode::Dup3,
244 "dup4" => Opcode::Dup4,
245 "dup5" => Opcode::Dup5,
246 "dup6" => Opcode::Dup6,
247 "dup7" => Opcode::Dup7,
248 "dup8" => Opcode::Dup8,
249 "dup9" => Opcode::Dup9,
250 "dup10" => Opcode::Dup10,
251 "swap1" => Opcode::Swap1,
252 "swap2" => Opcode::Swap2,
253 "swap3" => Opcode::Swap3,
254 "swap4" => Opcode::Swap4,
255 "swap5" => Opcode::Swap5,
256 "swap6" => Opcode::Swap6,
257 "swap7" => Opcode::Swap7,
258 "swap8" => Opcode::Swap8,
259 "swap9" => Opcode::Swap9,
260 "swap10" => Opcode::Swap10,
261 "stop" => Opcode::Stop,
262 "add" => Opcode::Add,
263 "mul" => Opcode::Mul,
264 "sub" => Opcode::Sub,
265 "div" => Opcode::Div,
266 "sdiv" => Opcode::Sdiv,
267 "mod" => Opcode::Mod,
268 "smod" => Opcode::Smod,
269 "addmod" => Opcode::Addmod,
270 "mulmod" => Opcode::Mulmod,
271 "exp" => Opcode::Exp,
272 "signextend" => Opcode::Signextend,
273 "byte" => Opcode::Byte,
274 "shl" => Opcode::Shl,
275 "shr" => Opcode::Shr,
276 "sar" => Opcode::Sar,
277 "gasprice" => Opcode::Gasprice,
278 "extcodesize" => Opcode::Extcodesize,
279 "extcodecopy" => Opcode::Extcodecopy,
280 "returndatasize" => Opcode::Returndatasize,
281 "returndatacopy" => Opcode::Returndatacopy,
282 "extcodehash" => Opcode::Extcodehash,
283 "gas" => Opcode::Gas,
284 "jumpdest" => Opcode::Jumpdest,
285 "push11" => Opcode::Push11,
286 "push12" => Opcode::Push12,
287 "push13" => Opcode::Push13,
288 "push14" => Opcode::Push14,
289 "push15" => Opcode::Push15,
290 "push16" => Opcode::Push16,
291 "push27" => Opcode::Push27,
292 "push28" => Opcode::Push28,
293 "push29" => Opcode::Push29,
294 "push30" => Opcode::Push30,
295 "push31" => Opcode::Push31,
296 "push32" => Opcode::Push32,
297 "dup11" => Opcode::Dup11,
298 "dup12" => Opcode::Dup12,
299 "dup13" => Opcode::Dup13,
300 "dup14" => Opcode::Dup14,
301 "dup15" => Opcode::Dup15,
302 "dup16" => Opcode::Dup16,
303 "dupn" => Opcode::Dupn,
304 "swap11" => Opcode::Swap11,
305 "swap12" => Opcode::Swap12,
306 "swap13" => Opcode::Swap13,
307 "swap14" => Opcode::Swap14,
308 "swap15" => Opcode::Swap15,
309 "swap16" => Opcode::Swap16,
310 "swapn" => Opcode::Swapn,
311 "exchange" => Opcode::Exchange,
312 "log0" => Opcode::Log0,
313 "log1" => Opcode::Log1,
314 "log2" => Opcode::Log2,
315 "log3" => Opcode::Log3,
316 "log4" => Opcode::Log4,
317 "tload" => Opcode::Tload,
318 "tstore" => Opcode::Tstore,
319 "create" => Opcode::Create,
320 "call" => Opcode::Call,
321 "callcode" => Opcode::Callcode,
322 "return" => Opcode::Return,
323 "delegatecall" => Opcode::Delegatecall,
324 "staticcall" => Opcode::Staticcall,
325 "create2" => Opcode::Create2,
326 "revert" => Opcode::Revert,
327 "invalid" => Opcode::Invalid,
328 "selfdestruct" => Opcode::Selfdestruct,
329};
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumString, AsRefStr)]
334#[strum(serialize_all = "lowercase")]
335pub enum Opcode {
336 Stop,
338 Add,
340 Mul,
342 Sub,
344 Div,
346 Sdiv,
348 Mod,
350 Smod,
352 Addmod,
354 Mulmod,
356 Exp,
358 Signextend,
360 Lt,
362 Gt,
364 Slt,
366 Sgt,
368 Eq,
370 Iszero,
372 And,
374 Or,
376 Xor,
378 Not,
380 Byte,
382 Shl,
384 Shr,
386 Sar,
388 #[strum(serialize = "keccak256", serialize = "sha3")]
390 Keccak256,
391 Address,
393 Balance,
395 Origin,
397 Caller,
399 Callvalue,
401 Calldataload,
403 Calldatasize,
405 Calldatacopy,
407 Codesize,
409 Codecopy,
411 Gasprice,
413 Extcodesize,
415 Extcodecopy,
417 Returndatasize,
419 Returndatacopy,
421 Blockhash,
423 Coinbase,
425 Timestamp,
427 Number,
429 Difficulty,
431 Prevrandao,
433 Gaslimit,
435 Chainid,
437 Clz,
439 Selfbalance,
441 Basefee,
443 Blobhash,
445 Blobbasefee,
447 Slotnum,
449 Pop,
451 Mload,
453 Mstore,
455 Mstore8,
457 Sload,
459 Sstore,
461 Jump,
463 Jumpi,
465 Pc,
467 Msize,
469 Gas,
471 Jumpdest,
473 Push0,
475 Push1,
477 Push2,
479 Push3,
481 Push4,
483 Push5,
485 Push6,
487 Push7,
489 Push8,
491 Push9,
493 Push10,
495 Push11,
497 Push12,
499 Push13,
501 Push14,
503 Push15,
505 Push16,
507 Push17,
509 Push18,
511 Push19,
513 Push20,
515 Push21,
517 Push22,
519 Push23,
521 Push24,
523 Push25,
525 Push26,
527 Push27,
529 Push28,
531 Push29,
533 Push30,
535 Push31,
537 Push32,
539 Dup1,
541 Dup2,
543 Dup3,
545 Dup4,
547 Dup5,
549 Dup6,
551 Dup7,
553 Dup8,
555 Dup9,
557 Dup10,
559 Dup11,
561 Dup12,
563 Dup13,
565 Dup14,
567 Dup15,
569 Dup16,
571 Dupn,
573 Swap1,
575 Swap2,
577 Swap3,
579 Swap4,
581 Swap5,
583 Swap6,
585 Swap7,
587 Swap8,
589 Swap9,
591 Swap10,
593 Swap11,
595 Swap12,
597 Swap13,
599 Swap14,
601 Swap15,
603 Swap16,
605 Swapn,
607 Exchange,
609 Log0,
611 Log1,
613 Log2,
615 Log3,
617 Log4,
619 Tload,
621 Tstore,
623 Mcopy,
625 Create,
627 Call,
629 Callcode,
631 Return,
633 Delegatecall,
636 Create2,
638 Staticcall,
640 Revert,
642 Invalid,
644 Selfdestruct,
646 Extcodehash,
648}
649
650impl Opcode {
651 pub fn string(&self) -> String {
653 let opcode_str = match self {
654 Opcode::Stop => "00",
655 Opcode::Add => "01",
656 Opcode::Mul => "02",
657 Opcode::Sub => "03",
658 Opcode::Div => "04",
659 Opcode::Sdiv => "05",
660 Opcode::Mod => "06",
661 Opcode::Smod => "07",
662 Opcode::Addmod => "08",
663 Opcode::Mulmod => "09",
664 Opcode::Exp => "0a",
665 Opcode::Signextend => "0b",
666 Opcode::Lt => "10",
667 Opcode::Gt => "11",
668 Opcode::Slt => "12",
669 Opcode::Sgt => "13",
670 Opcode::Eq => "14",
671 Opcode::Iszero => "15",
672 Opcode::And => "16",
673 Opcode::Or => "17",
674 Opcode::Xor => "18",
675 Opcode::Not => "19",
676 Opcode::Byte => "1a",
677 Opcode::Shl => "1b",
678 Opcode::Shr => "1c",
679 Opcode::Sar => "1d",
680 Opcode::Keccak256 => "20",
681 Opcode::Address => "30",
682 Opcode::Balance => "31",
683 Opcode::Origin => "32",
684 Opcode::Caller => "33",
685 Opcode::Callvalue => "34",
686 Opcode::Calldataload => "35",
687 Opcode::Calldatasize => "36",
688 Opcode::Calldatacopy => "37",
689 Opcode::Codesize => "38",
690 Opcode::Codecopy => "39",
691 Opcode::Gasprice => "3a",
692 Opcode::Extcodesize => "3b",
693 Opcode::Extcodecopy => "3c",
694 Opcode::Returndatasize => "3d",
695 Opcode::Returndatacopy => "3e",
696 Opcode::Extcodehash => "3f",
697 Opcode::Blockhash => "40",
698 Opcode::Coinbase => "41",
699 Opcode::Timestamp => "42",
700 Opcode::Number => "43",
701 Opcode::Difficulty => "44",
702 Opcode::Prevrandao => "44",
703 Opcode::Gaslimit => "45",
704 Opcode::Chainid => "46",
705 Opcode::Clz => "1e",
706 Opcode::Selfbalance => "47",
707 Opcode::Basefee => "48",
708 Opcode::Blobhash => "49",
709 Opcode::Blobbasefee => "4a",
710 Opcode::Slotnum => "4b",
711 Opcode::Pop => "50",
712 Opcode::Mload => "51",
713 Opcode::Mstore => "52",
714 Opcode::Mstore8 => "53",
715 Opcode::Sload => "54",
716 Opcode::Sstore => "55",
717 Opcode::Jump => "56",
718 Opcode::Jumpi => "57",
719 Opcode::Pc => "58",
720 Opcode::Msize => "59",
721 Opcode::Gas => "5a",
722 Opcode::Jumpdest => "5b",
723 Opcode::Tload => "5c",
724 Opcode::Tstore => "5d",
725 Opcode::Mcopy => "5e",
726 Opcode::Push0 => "5f",
727 Opcode::Push1 => "60",
728 Opcode::Push2 => "61",
729 Opcode::Push3 => "62",
730 Opcode::Push4 => "63",
731 Opcode::Push5 => "64",
732 Opcode::Push6 => "65",
733 Opcode::Push7 => "66",
734 Opcode::Push8 => "67",
735 Opcode::Push9 => "68",
736 Opcode::Push10 => "69",
737 Opcode::Push11 => "6a",
738 Opcode::Push12 => "6b",
739 Opcode::Push13 => "6c",
740 Opcode::Push14 => "6d",
741 Opcode::Push15 => "6e",
742 Opcode::Push16 => "6f",
743 Opcode::Push17 => "70",
744 Opcode::Push18 => "71",
745 Opcode::Push19 => "72",
746 Opcode::Push20 => "73",
747 Opcode::Push21 => "74",
748 Opcode::Push22 => "75",
749 Opcode::Push23 => "76",
750 Opcode::Push24 => "77",
751 Opcode::Push25 => "78",
752 Opcode::Push26 => "79",
753 Opcode::Push27 => "7a",
754 Opcode::Push28 => "7b",
755 Opcode::Push29 => "7c",
756 Opcode::Push30 => "7d",
757 Opcode::Push31 => "7e",
758 Opcode::Push32 => "7f",
759 Opcode::Dup1 => "80",
760 Opcode::Dup2 => "81",
761 Opcode::Dup3 => "82",
762 Opcode::Dup4 => "83",
763 Opcode::Dup5 => "84",
764 Opcode::Dup6 => "85",
765 Opcode::Dup7 => "86",
766 Opcode::Dup8 => "87",
767 Opcode::Dup9 => "88",
768 Opcode::Dup10 => "89",
769 Opcode::Dup11 => "8a",
770 Opcode::Dup12 => "8b",
771 Opcode::Dup13 => "8c",
772 Opcode::Dup14 => "8d",
773 Opcode::Dup15 => "8e",
774 Opcode::Dup16 => "8f",
775 Opcode::Swap1 => "90",
776 Opcode::Swap2 => "91",
777 Opcode::Swap3 => "92",
778 Opcode::Swap4 => "93",
779 Opcode::Swap5 => "94",
780 Opcode::Swap6 => "95",
781 Opcode::Swap7 => "96",
782 Opcode::Swap8 => "97",
783 Opcode::Swap9 => "98",
784 Opcode::Swap10 => "99",
785 Opcode::Swap11 => "9a",
786 Opcode::Swap12 => "9b",
787 Opcode::Swap13 => "9c",
788 Opcode::Swap14 => "9d",
789 Opcode::Swap15 => "9e",
790 Opcode::Swap16 => "9f",
791 Opcode::Log0 => "a0",
792 Opcode::Log1 => "a1",
793 Opcode::Log2 => "a2",
794 Opcode::Log3 => "a3",
795 Opcode::Log4 => "a4",
796 Opcode::Dupn => "e6",
797 Opcode::Swapn => "e7",
798 Opcode::Exchange => "e8",
799 Opcode::Create => "f0",
800 Opcode::Call => "f1",
801 Opcode::Callcode => "f2",
802 Opcode::Return => "f3",
803 Opcode::Delegatecall => "f4",
804 Opcode::Create2 => "f5",
805 Opcode::Staticcall => "fa",
806 Opcode::Revert => "fd",
807 Opcode::Invalid => "fe",
808 Opcode::Selfdestruct => "ff",
809 };
810 opcode_str.to_string()
811 }
812
813 pub fn is_value_push(&self) -> bool {
815 matches!(
816 self,
817 Opcode::Push1
818 | Opcode::Push2
819 | Opcode::Push3
820 | Opcode::Push4
821 | Opcode::Push5
822 | Opcode::Push6
823 | Opcode::Push7
824 | Opcode::Push8
825 | Opcode::Push9
826 | Opcode::Push10
827 | Opcode::Push11
828 | Opcode::Push12
829 | Opcode::Push13
830 | Opcode::Push14
831 | Opcode::Push15
832 | Opcode::Push16
833 | Opcode::Push17
834 | Opcode::Push18
835 | Opcode::Push19
836 | Opcode::Push20
837 | Opcode::Push21
838 | Opcode::Push22
839 | Opcode::Push23
840 | Opcode::Push24
841 | Opcode::Push25
842 | Opcode::Push26
843 | Opcode::Push27
844 | Opcode::Push28
845 | Opcode::Push29
846 | Opcode::Push30
847 | Opcode::Push31
848 | Opcode::Push32
849 )
850 }
851
852 pub fn prefix_push_literal(&self, literal: &str) -> String {
854 if self.is_value_push()
855 && let Ok(len) = u8::from_str_radix(&self.string(), 16)
856 && len >= 96
857 {
858 let size = (len - 96 + 1) * 2;
859 if literal.len() <= size as usize {
861 let zeros_needed = size - literal.len() as u8;
862 let zero_prefix = (0..zeros_needed).map(|_| "0").collect::<Vec<&str>>().join("");
863 return format!("{zero_prefix}{literal}");
864 }
865 }
866
867 literal.to_string()
868 }
869
870 pub fn push_size(&self) -> Option<usize> {
872 if !self.is_value_push() {
873 return None;
874 }
875 u8::from_str_radix(&self.string(), 16).ok().map(|byte| (byte - 0x5f) as usize)
876 }
877
878 pub fn stack_immediate_operands(&self) -> usize {
883 match self {
884 Opcode::Dupn | Opcode::Swapn => 1,
885 Opcode::Exchange => 2,
886 _ => 0,
887 }
888 }
889
890 pub fn has_stack_immediate(&self) -> bool {
892 self.stack_immediate_operands() > 0
893 }
894
895 pub fn encode_stack_immediate(&self, operands: &[usize]) -> Option<u8> {
903 match (self, operands) {
904 (Opcode::Dupn | Opcode::Swapn, &[n]) => {
905 if !(STACK_IMMEDIATE_MIN_N..=STACK_IMMEDIATE_MAX_N).contains(&n) {
906 return None;
907 }
908 let x = ((n + 256 - 145) % 256) as u8;
909 debug_assert_eq!(decode_single_immediate(x), Some(n));
910 Some(x)
911 }
912 (Opcode::Exchange, &[n, m]) => (0..=u8::MAX).find(|&x| decode_pair_immediate(x) == Some((n, m))),
913 _ => None,
914 }
915 }
916
917 pub fn encode_immediate(&self, values: &[U256]) -> Result<String, String> {
923 if let Some(size) = self.push_size() {
924 let value = values[0];
925 if value.byte_len() > size {
926 return Err(format!("value {value:#x} does not fit into \"{self:?}\" ({size} bytes)"));
927 }
928 return Ok(hex::encode(&value.to_be_bytes::<32>()[32 - size..]));
929 }
930
931 let positions: Option<Vec<usize>> = values.iter().map(|v| usize::try_from(*v).ok()).collect();
932 positions.and_then(|p| self.encode_stack_immediate(&p)).map(|x| format!("{x:02x}")).ok_or_else(|| {
933 let got: Vec<String> = values.iter().map(U256::to_string).collect();
934 format!("{}, got {}", self.stack_immediate_hint(), got.join(" "))
935 })
936 }
937
938 pub fn stack_immediate_hint(&self) -> String {
940 match self {
941 Opcode::Dupn | Opcode::Swapn => {
942 format!("\"{self:?}\" takes one stack depth between {STACK_IMMEDIATE_MIN_N} and {STACK_IMMEDIATE_MAX_N}")
943 }
944 Opcode::Exchange => "\"Exchange\" takes two stack positions n and m with 1 <= n < m and n + m <= 30".to_string(),
945 _ => String::new(),
946 }
947 }
948
949 pub fn push_overflows(&self, literal: &str) -> bool {
951 if self.is_value_push()
952 && let Ok(len) = u8::from_str_radix(&self.string(), 16)
953 && len >= 96
954 {
955 let size = (len - 96 + 1) * 2;
956 return literal.len() > size as usize;
957 }
958
959 false
960 }
961
962 pub fn requires_evm_version(&self) -> Option<SupportedEVMVersions> {
965 match self {
966 Opcode::Prevrandao => Some(SupportedEVMVersions::Paris),
968 Opcode::Push0 => Some(SupportedEVMVersions::Shanghai),
970 Opcode::Tload | Opcode::Tstore => Some(SupportedEVMVersions::Cancun),
972 Opcode::Mcopy => Some(SupportedEVMVersions::Cancun),
973 Opcode::Blobhash | Opcode::Blobbasefee => Some(SupportedEVMVersions::Cancun),
974 Opcode::Clz => Some(SupportedEVMVersions::Osaka),
976 Opcode::Slotnum | Opcode::Dupn | Opcode::Swapn | Opcode::Exchange => Some(SupportedEVMVersions::Amsterdam),
978 _ => None,
980 }
981 }
982}
983
984pub const STACK_IMMEDIATE_MIN_N: usize = 17;
986pub const STACK_IMMEDIATE_MAX_N: usize = 235;
988
989fn decode_single_immediate(x: u8) -> Option<usize> {
991 let x = x as usize;
992 if x <= 0x5a || x >= 0x80 { Some((x + 145) % 256) } else { None }
993}
994
995fn decode_pair_immediate(x: u8) -> Option<(usize, usize)> {
997 let x = x as usize;
998 if x > 0x51 && x < 0x80 {
999 return None;
1000 }
1001 let k = x ^ 0x8f;
1002 let (q, r) = (k / 16, k % 16);
1003 if q < r { Some((q + 1, r + 1)) } else { Some((r + 1, 29 - q)) }
1004}
1005
1006impl fmt::Display for Opcode {
1007 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1008 let opcode_str = self.string();
1009 write!(f, "{opcode_str}")
1010 }
1011}
1012
1013impl From<Opcode> for String {
1014 fn from(o: Opcode) -> Self {
1015 o.string()
1016 }
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::*;
1022
1023 #[test]
1025 fn test_opcode_array_equal() {
1026 for opcode in OPCODES {
1027 assert!(OPCODES_MAP.contains_key(opcode), "{opcode}");
1028 }
1029 for opcode in OPCODES_MAP.keys() {
1030 assert!(OPCODES.contains(opcode), "{opcode}");
1031 }
1032 }
1033
1034 fn is_forbidden_immediate(x: u8) -> bool {
1036 (0x5b..=0x7f).contains(&x)
1037 }
1038
1039 #[test]
1040 fn test_dupn_swapn_encoding_round_trips() {
1041 for opcode in [Opcode::Dupn, Opcode::Swapn] {
1042 for n in STACK_IMMEDIATE_MIN_N..=STACK_IMMEDIATE_MAX_N {
1043 let x = opcode.encode_stack_immediate(&[n]).unwrap_or_else(|| panic!("{opcode:?} {n} must be encodable"));
1044 assert!(!is_forbidden_immediate(x), "{opcode:?} {n} encoded to forbidden byte {x:#04x}");
1045 assert_eq!(decode_single_immediate(x), Some(n));
1046 }
1047 assert_eq!(opcode.encode_stack_immediate(&[0]), None);
1048 assert_eq!(opcode.encode_stack_immediate(&[16]), None);
1049 assert_eq!(opcode.encode_stack_immediate(&[236]), None);
1050 assert_eq!(opcode.encode_stack_immediate(&[17, 18]), None);
1051 }
1052 }
1053
1054 #[test]
1055 fn test_dupn_swapn_known_vectors() {
1056 assert_eq!(Opcode::Dupn.encode_stack_immediate(&[17]), Some(0x80));
1058 assert_eq!(Opcode::Dupn.encode_stack_immediate(&[144]), Some(0xff));
1059 assert_eq!(Opcode::Dupn.encode_stack_immediate(&[145]), Some(0x00));
1060 assert_eq!(Opcode::Swapn.encode_stack_immediate(&[235]), Some(0x5a));
1061 }
1062
1063 #[test]
1064 fn test_exchange_encoding_covers_all_valid_pairs() {
1065 let mut count = 0;
1066 for n in 1..30 {
1067 for m in (n + 1)..=(30 - n) {
1068 let x = Opcode::Exchange.encode_stack_immediate(&[n, m]).unwrap_or_else(|| panic!("exchange {n} {m} must be encodable"));
1069 assert!(!is_forbidden_immediate(x), "exchange {n} {m} encoded to forbidden byte {x:#04x}");
1070 assert_eq!(decode_pair_immediate(x), Some((n, m)));
1071 count += 1;
1072 }
1073 }
1074 let valid_bytes = (0..=u8::MAX).filter(|&x| decode_pair_immediate(x).is_some()).count();
1076 assert_eq!(count, valid_bytes);
1077
1078 assert_eq!(Opcode::Exchange.encode_stack_immediate(&[1, 2]), Some(0x8e));
1080
1081 assert_eq!(Opcode::Exchange.encode_stack_immediate(&[2, 1]), None);
1082 assert_eq!(Opcode::Exchange.encode_stack_immediate(&[1, 1]), None);
1083 assert_eq!(Opcode::Exchange.encode_stack_immediate(&[0, 5]), None);
1084 assert_eq!(Opcode::Exchange.encode_stack_immediate(&[14, 17]), None);
1085 assert_eq!(Opcode::Exchange.encode_stack_immediate(&[3]), None);
1086 }
1087
1088 #[test]
1089 fn test_stack_immediate_operands() {
1090 assert_eq!(Opcode::Dupn.stack_immediate_operands(), 1);
1091 assert_eq!(Opcode::Swapn.stack_immediate_operands(), 1);
1092 assert_eq!(Opcode::Exchange.stack_immediate_operands(), 2);
1093 assert!(!Opcode::Dup16.has_stack_immediate());
1094 assert!(!Opcode::Slotnum.has_stack_immediate());
1095 assert_eq!(Opcode::Dup16.encode_stack_immediate(&[17]), None);
1096 }
1097
1098 #[test]
1100 fn test_opcode_order() {
1101 let mut sorted_opcodes = OPCODES.to_vec();
1102 sorted_opcodes.sort_by(|a, b| {
1103 let common_len = a.chars().zip(b.chars()).take_while(|(c1, c2)| c1 == c2).count();
1105
1106 if common_len == a.len().min(b.len()) {
1108 b.len().cmp(&a.len()) } else {
1110 a.cmp(b) }
1112 });
1113 assert_eq!(OPCODES.to_vec(), sorted_opcodes, "Opcodes are not ordered correctly");
1114 }
1115}