eot/
lib.rs

1//! # EOT - EVM Opcode Table
2//!
3//! EVM opcodes library for all Ethereum forks, with complete fork inheritance, validation, and metadata
4#![deny(missing_docs)]
5#![warn(clippy::all)]
6
7use std::collections::HashMap;
8
9pub mod forks;
10pub use forks::*;
11
12// Core traits and types
13pub mod traits;
14pub use traits::*;
15
16// Validation and verification
17pub mod validation;
18pub use validation::*;
19
20// Gas analysis system
21pub mod gas;
22pub use gas::{
23    DynamicGasCalculator, ExecutionContext, GasAnalysis, GasAnalysisResult, GasCostCategory,
24};
25
26// Unified opcodes feature for bytecode manipulation tools
27#[cfg(feature = "unified-opcodes")]
28pub mod unified;
29#[cfg(feature = "unified-opcodes")]
30pub use unified::UnifiedOpcode;
31
32/// Ethereum hard fork identifiers in chronological order
33#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
34pub enum Fork {
35    /// Frontier (July 30, 2015) - Genesis block
36    Frontier,
37    /// Ice Age (September 7, 2015) - Difficulty bomb introduction
38    IceAge,
39    /// Homestead (March 14, 2016) - First major upgrade
40    Homestead,
41    /// DAO Fork (July 20, 2016) - Emergency response to DAO hack
42    DaoFork,
43    /// Tangerine Whistle (October 18, 2016) - Gas cost adjustments
44    TangerineWhistle,
45    /// Spurious Dragon (November 22, 2016) - More gas adjustments
46    SpuriousDragon,
47    /// Byzantium (October 16, 2017) - Metropolis part 1
48    Byzantium,
49    /// Constantinople (February 28, 2019) - Metropolis part 2
50    Constantinople,
51    /// Petersburg (February 28, 2019) - Constantinople fix
52    Petersburg,
53    /// Istanbul (December 8, 2019) - Gas optimizations
54    Istanbul,
55    /// Muir Glacier (January 2, 2020) - Difficulty bomb delay
56    MuirGlacier,
57    /// Berlin (April 15, 2021) - Gas cost changes
58    Berlin,
59    /// London (August 5, 2021) - EIP-1559 fee market
60    London,
61    /// Altair (October 27, 2021) - Beacon Chain upgrade
62    Altair,
63    /// Arrow Glacier (December 9, 2021) - Difficulty bomb delay
64    ArrowGlacier,
65    /// Gray Glacier (June 30, 2022) - Difficulty bomb delay
66    GrayGlacier,
67    /// Bellatrix (September 6, 2022) - Beacon Chain prep for merge
68    Bellatrix,
69    /// Paris (September 15, 2022) - The Merge to Proof of Stake
70    Paris,
71    /// Shanghai (April 12, 2023) - Withdrawals enabled
72    Shanghai,
73    /// Capella (April 12, 2023) - Beacon Chain withdrawals
74    Capella,
75    /// Cancun (March 13, 2024) - Proto-danksharding
76    Cancun,
77    /// Deneb (March 13, 2024) - Beacon Chain blobs
78    Deneb,
79}
80
81/// EVM opcode groups for better organization
82#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
83pub enum Group {
84    /// Stop and Arithmetic Operations (0x00-0x0f)
85    StopArithmetic,
86    /// Comparison & Bitwise Logic Operations (0x10-0x1f)
87    ComparisonBitwiseLogic,
88    /// SHA3 (0x20)
89    Sha3,
90    /// Environmental Information (0x30-0x3f)
91    EnvironmentalInformation,
92    /// Block Information (0x40-0x4f)
93    BlockInformation,
94    /// Stack, Memory, Storage and Flow Operations (0x50-0x5f)
95    StackMemoryStorageFlow,
96    /// Push Operations (0x60-0x7f)
97    Push,
98    /// Duplication Operations (0x80-0x8f)
99    Duplication,
100    /// Exchange Operations (0x90-0x9f)
101    Exchange,
102    /// Logging Operations (0xa0-0xa4)
103    Logging,
104    /// System operations (0xf0-0xff)
105    System,
106}
107
108/// Opcode metadata with complete information
109#[derive(Clone, Debug, PartialEq, Eq)]
110pub struct OpcodeMetadata {
111    /// The opcode byte value
112    pub opcode: u8,
113    /// Opcode name (e.g., "ADD", "PUSH1")
114    pub name: &'static str,
115    /// Base gas cost
116    pub gas_cost: u16,
117    /// Number of items popped from stack
118    pub stack_inputs: u8,
119    /// Number of items pushed to stack
120    pub stack_outputs: u8,
121    /// Human-readable description
122    pub description: &'static str,
123    /// Fork where this opcode was introduced
124    pub introduced_in: Fork,
125    /// Opcode group/category
126    pub group: Group,
127    /// EIP number that introduced this opcode (if applicable)
128    pub eip: Option<u16>,
129    /// Gas cost changes across forks
130    pub gas_history: &'static [(Fork, u16)],
131}
132
133/// Core trait that all opcode enums must implement
134pub trait OpCode: From<u8> + Into<u8> + Clone + Copy + std::fmt::Debug {
135    /// Get complete metadata for this opcode
136    fn metadata(&self) -> OpcodeMetadata;
137
138    /// Get the fork this opcode enum represents
139    fn fork() -> Fork;
140
141    /// Get all opcodes available in this fork
142    fn all_opcodes() -> Vec<Self>;
143
144    /// Check if an opcode exists in this fork
145    fn has_opcode(opcode: u8) -> bool {
146        Self::all_opcodes().iter().any(|op| (*op).into() == opcode)
147    }
148
149    /// Get gas cost for this opcode in this fork
150    fn gas_cost(&self) -> u16 {
151        let metadata = self.metadata();
152
153        // Find the most recent gas cost for this fork
154        let fork = Self::fork();
155        metadata
156            .gas_history
157            .iter()
158            .rev()
159            .find(|(f, _)| *f <= fork)
160            .map(|(_, cost)| *cost)
161            .unwrap_or(metadata.gas_cost)
162    }
163
164    /// Get stack inputs for this opcode
165    fn stack_inputs(&self) -> u8 {
166        self.metadata().stack_inputs
167    }
168    /// Get stack outputs for this opcode
169    fn stack_outputs(&self) -> u8 {
170        self.metadata().stack_outputs
171    }
172    /// Get opcode group
173    fn group(&self) -> Group {
174        self.metadata().group
175    }
176    /// Get opcode description
177    fn description(&self) -> &'static str {
178        self.metadata().description
179    }
180    /// Get fork where this opcode was introduced
181    fn introduced_in(&self) -> Fork {
182        self.metadata().introduced_in
183    }
184    /// Get EIP number for this opcode
185    fn eip(&self) -> Option<u16> {
186        self.metadata().eip
187    }
188}
189
190/// Fork inheritance utility to get all opcodes available in a specific fork
191pub trait ForkOpcodes {
192    /// Get all opcodes available in this fork (including inherited ones)
193    fn get_opcodes_for_fork(fork: Fork) -> HashMap<u8, OpcodeMetadata>;
194
195    /// Check if a specific opcode is available in a fork
196    fn is_opcode_available(fork: Fork, opcode: u8) -> bool {
197        Self::get_opcodes_for_fork(fork).contains_key(&opcode)
198    }
199
200    /// Get the fork where an opcode was introduced
201    fn opcode_introduced_in(opcode: u8) -> Option<Fork>;
202}
203
204/// Comprehensive opcode registry that manages all forks
205pub struct OpcodeRegistry {
206    opcodes: HashMap<Fork, HashMap<u8, OpcodeMetadata>>,
207}
208
209impl OpcodeRegistry {
210    /// Create a new opcode registry with all known opcodes
211    pub fn new() -> Self {
212        let mut registry = Self {
213            opcodes: HashMap::new(),
214        };
215
216        // Register all forks
217        registry.register_fork::<forks::Frontier>();
218        registry.register_fork::<forks::Homestead>();
219        registry.register_fork::<forks::Byzantium>();
220        registry.register_fork::<forks::Constantinople>();
221        registry.register_fork::<forks::Istanbul>();
222        registry.register_fork::<forks::Berlin>();
223        registry.register_fork::<forks::London>();
224        registry.register_fork::<forks::Shanghai>();
225        registry.register_fork::<forks::Cancun>();
226
227        registry
228    }
229
230    fn register_fork<T: OpCode>(&mut self) {
231        let fork = T::fork();
232        let mut opcodes = HashMap::new();
233
234        for opcode_enum in T::all_opcodes() {
235            let byte_val: u8 = opcode_enum.into();
236            let metadata = opcode_enum.metadata();
237            opcodes.insert(byte_val, metadata);
238        }
239
240        self.opcodes.insert(fork, opcodes);
241    }
242
243    /// Get all opcodes available in a specific fork
244    pub fn get_opcodes(&self, fork: Fork) -> HashMap<u8, OpcodeMetadata> {
245        let mut result = HashMap::new();
246
247        // Collect opcodes from all previous forks (inheritance)
248        for f in self.opcodes.keys() {
249            if *f <= fork {
250                if let Some(fork_opcodes) = self.opcodes.get(f) {
251                    result.extend(fork_opcodes.clone());
252                }
253            }
254        }
255
256        result
257    }
258
259    /// Check if a specific opcode is available in a fork
260    pub fn is_opcode_available(&self, fork: Fork, opcode: u8) -> bool {
261        self.get_opcodes(fork).contains_key(&opcode)
262    }
263
264    /// Validate opcode consistency across forks
265    pub fn validate(&self) -> Result<(), Vec<String>> {
266        validation::validate_registry(self)
267    }
268}
269
270impl Default for OpcodeRegistry {
271    fn default() -> Self {
272        Self::new()
273    }
274}
275
276/// Macro to generate opcode enums with metadata
277#[macro_export]
278macro_rules! opcodes {
279    (
280        $(#[$meta:meta])*
281        $enum_name:ident => $fork:ident {
282            $(
283                $opcode:literal => $name:ident {
284                    gas: $gas:literal,
285                    inputs: $inputs:literal,
286                    outputs: $outputs:literal,
287                    description: $description:literal,
288                    introduced_in: $introduced:ident,
289                    group: $group:ident,
290                    eip: $eip:expr,
291                    gas_history: [$($gas_fork:ident => $gas_cost:literal),*],
292                }
293            ),* $(,)?
294        }
295    ) => {
296        $(#[$meta])*
297        #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
298        pub enum $enum_name {
299            $(
300                #[doc = $description]
301                $name,
302            )*
303        }
304
305        impl From<u8> for $enum_name {
306            fn from(value: u8) -> Self {
307                match value {
308                    $(
309                        $opcode => Self::$name,
310                    )*
311                    _ => panic!("Invalid opcode 0x{:02x} for fork {}", value, stringify!($fork)),
312                }
313            }
314        }
315
316        impl From<$enum_name> for u8 {
317            fn from(opcode: $enum_name) -> Self {
318                match opcode {
319                    $(
320                        $enum_name::$name => $opcode,
321                    )*
322                }
323            }
324        }
325
326        impl $crate::OpCode for $enum_name {
327            fn metadata(&self) -> $crate::OpcodeMetadata {
328                match self {
329                    $(
330                        Self::$name => $crate::OpcodeMetadata {
331                            opcode: $opcode,
332                            name: stringify!($name),
333                            gas_cost: $gas,
334                            stack_inputs: $inputs,
335                            stack_outputs: $outputs,
336                            description: $description,
337                            introduced_in: $crate::Fork::$introduced,
338                            group: $crate::Group::$group,
339                            eip: $eip,
340                            gas_history: &[
341                                $(
342                                    ($crate::Fork::$gas_fork, $gas_cost),
343                                )*
344                            ],
345                        },
346                    )*
347                }
348            }
349
350            fn fork() -> $crate::Fork {
351                $crate::Fork::$fork
352            }
353
354            fn all_opcodes() -> Vec<Self> {
355                vec![
356                    $(
357                        Self::$name,
358                    )*
359                ]
360            }
361        }
362
363        impl std::fmt::Display for $enum_name {
364            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365                write!(f, "{}", self.metadata().name)
366            }
367        }
368    };
369}