1#![deny(missing_docs)]
5#![warn(clippy::all)]
6
7use std::collections::HashMap;
8
9pub mod forks;
10pub use forks::*;
11
12pub mod traits;
14pub use traits::*;
15
16pub mod validation;
18pub use validation::*;
19
20pub mod gas;
22pub use gas::{
23 DynamicGasCalculator, ExecutionContext, GasAnalysis, GasAnalysisResult, GasCostCategory,
24};
25
26#[cfg(feature = "unified-opcodes")]
28pub mod unified;
29#[cfg(feature = "unified-opcodes")]
30pub use unified::UnifiedOpcode;
31
32#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
34pub enum Fork {
35 Frontier,
37 IceAge,
39 Homestead,
41 DaoFork,
43 TangerineWhistle,
45 SpuriousDragon,
47 Byzantium,
49 Constantinople,
51 Petersburg,
53 Istanbul,
55 MuirGlacier,
57 Berlin,
59 London,
61 Altair,
63 ArrowGlacier,
65 GrayGlacier,
67 Bellatrix,
69 Paris,
71 Shanghai,
73 Capella,
75 Cancun,
77 Deneb,
79}
80
81#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
83pub enum Group {
84 StopArithmetic,
86 ComparisonBitwiseLogic,
88 Sha3,
90 EnvironmentalInformation,
92 BlockInformation,
94 StackMemoryStorageFlow,
96 Push,
98 Duplication,
100 Exchange,
102 Logging,
104 System,
106}
107
108#[derive(Clone, Debug, PartialEq, Eq)]
110pub struct OpcodeMetadata {
111 pub opcode: u8,
113 pub name: &'static str,
115 pub gas_cost: u16,
117 pub stack_inputs: u8,
119 pub stack_outputs: u8,
121 pub description: &'static str,
123 pub introduced_in: Fork,
125 pub group: Group,
127 pub eip: Option<u16>,
129 pub gas_history: &'static [(Fork, u16)],
131}
132
133pub trait OpCode: From<u8> + Into<u8> + Clone + Copy + std::fmt::Debug {
135 fn metadata(&self) -> OpcodeMetadata;
137
138 fn fork() -> Fork;
140
141 fn all_opcodes() -> Vec<Self>;
143
144 fn has_opcode(opcode: u8) -> bool {
146 Self::all_opcodes().iter().any(|op| (*op).into() == opcode)
147 }
148
149 fn gas_cost(&self) -> u16 {
151 let metadata = self.metadata();
152
153 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 fn stack_inputs(&self) -> u8 {
166 self.metadata().stack_inputs
167 }
168 fn stack_outputs(&self) -> u8 {
170 self.metadata().stack_outputs
171 }
172 fn group(&self) -> Group {
174 self.metadata().group
175 }
176 fn description(&self) -> &'static str {
178 self.metadata().description
179 }
180 fn introduced_in(&self) -> Fork {
182 self.metadata().introduced_in
183 }
184 fn eip(&self) -> Option<u16> {
186 self.metadata().eip
187 }
188}
189
190pub trait ForkOpcodes {
192 fn get_opcodes_for_fork(fork: Fork) -> HashMap<u8, OpcodeMetadata>;
194
195 fn is_opcode_available(fork: Fork, opcode: u8) -> bool {
197 Self::get_opcodes_for_fork(fork).contains_key(&opcode)
198 }
199
200 fn opcode_introduced_in(opcode: u8) -> Option<Fork>;
202}
203
204pub struct OpcodeRegistry {
206 opcodes: HashMap<Fork, HashMap<u8, OpcodeMetadata>>,
207}
208
209impl OpcodeRegistry {
210 pub fn new() -> Self {
212 let mut registry = Self {
213 opcodes: HashMap::new(),
214 };
215
216 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 pub fn get_opcodes(&self, fork: Fork) -> HashMap<u8, OpcodeMetadata> {
245 let mut result = HashMap::new();
246
247 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 pub fn is_opcode_available(&self, fork: Fork, opcode: u8) -> bool {
261 self.get_opcodes(fork).contains_key(&opcode)
262 }
263
264 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_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}