diff --git a/cmd/ef_tests/levm/report.rs b/cmd/ef_tests/levm/report.rs index cef2d1b3e3..970e144aff 100644 --- a/cmd/ef_tests/levm/report.rs +++ b/cmd/ef_tests/levm/report.rs @@ -1,12 +1,11 @@ use crate::runner::{EFTestRunnerError, InternalError}; use colored::Colorize; -use ethrex_core::{Address, H256}; +use ethrex_core::{types::Fork, Address, H256}; use ethrex_levm::{ errors::{TransactionReport, TxResult, VMError}, Account, StorageSlot, }; use ethrex_storage::{error::StoreError, AccountUpdate}; -use ethrex_vm::SpecId; use itertools::Itertools; use revm::primitives::{EVMError, ExecutionResult as RevmExecutionResult}; use serde::{Deserialize, Serialize}; @@ -133,21 +132,21 @@ pub fn summary_for_slack(reports: &[EFTestReport]) -> String { }} ] }}"#, - fork_summary_for_slack(reports, SpecId::PRAGUE), - fork_summary_for_slack(reports, SpecId::CANCUN), - fork_summary_for_slack(reports, SpecId::SHANGHAI), - fork_summary_for_slack(reports, SpecId::HOMESTEAD), - fork_summary_for_slack(reports, SpecId::ISTANBUL), - fork_summary_for_slack(reports, SpecId::LONDON), - fork_summary_for_slack(reports, SpecId::BYZANTIUM), - fork_summary_for_slack(reports, SpecId::BERLIN), - fork_summary_for_slack(reports, SpecId::CONSTANTINOPLE), - fork_summary_for_slack(reports, SpecId::MERGE), - fork_summary_for_slack(reports, SpecId::FRONTIER), + fork_summary_for_slack(reports, Fork::Prague), + fork_summary_for_slack(reports, Fork::Cancun), + fork_summary_for_slack(reports, Fork::Shanghai), + fork_summary_for_slack(reports, Fork::Byzantium), + fork_summary_for_slack(reports, Fork::Berlin), + fork_summary_for_slack(reports, Fork::Constantinople), + fork_summary_for_slack(reports, Fork::Paris), + fork_summary_for_slack(reports, Fork::Homestead), + fork_summary_for_slack(reports, Fork::Istanbul), + fork_summary_for_slack(reports, Fork::London), + fork_summary_for_slack(reports, Fork::Frontier), ) } -fn fork_summary_for_slack(reports: &[EFTestReport], fork: SpecId) -> String { +fn fork_summary_for_slack(reports: &[EFTestReport], fork: Fork) -> String { let fork_str: &str = fork.into(); let (fork_tests, fork_passed_tests, fork_success_percentage) = fork_statistics(reports, fork); format!(r#"*{fork_str}:* {fork_passed_tests}/{fork_tests} ({fork_success_percentage:.2}%)"#) @@ -173,21 +172,21 @@ pub fn summary_for_github(reports: &[EFTestReport]) -> String { let success_percentage = (total_passed as f64 / total_run as f64) * 100.0; format!( r#"Summary: {total_passed}/{total_run} ({success_percentage:.2}%)\n\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n"#, - fork_summary_for_github(reports, SpecId::PRAGUE), - fork_summary_for_github(reports, SpecId::CANCUN), - fork_summary_for_github(reports, SpecId::SHANGHAI), - fork_summary_for_github(reports, SpecId::HOMESTEAD), - fork_summary_for_github(reports, SpecId::ISTANBUL), - fork_summary_for_github(reports, SpecId::LONDON), - fork_summary_for_github(reports, SpecId::BYZANTIUM), - fork_summary_for_github(reports, SpecId::BERLIN), - fork_summary_for_github(reports, SpecId::CONSTANTINOPLE), - fork_summary_for_github(reports, SpecId::MERGE), - fork_summary_for_github(reports, SpecId::FRONTIER), + fork_summary_for_github(reports, Fork::Prague), + fork_summary_for_github(reports, Fork::Cancun), + fork_summary_for_github(reports, Fork::Shanghai), + fork_summary_for_github(reports, Fork::Byzantium), + fork_summary_for_github(reports, Fork::Berlin), + fork_summary_for_github(reports, Fork::Constantinople), + fork_summary_for_github(reports, Fork::Paris), + fork_summary_for_github(reports, Fork::Homestead), + fork_summary_for_github(reports, Fork::Istanbul), + fork_summary_for_github(reports, Fork::London), + fork_summary_for_github(reports, Fork::Frontier), ) } -fn fork_summary_for_github(reports: &[EFTestReport], fork: SpecId) -> String { +fn fork_summary_for_github(reports: &[EFTestReport], fork: Fork) -> String { let fork_str: &str = fork.into(); let (fork_tests, fork_passed_tests, fork_success_percentage) = fork_statistics(reports, fork); format!("{fork_str}: {fork_passed_tests}/{fork_tests} ({fork_success_percentage:.2}%)") @@ -221,22 +220,22 @@ pub fn summary_for_shell(reports: &[EFTestReport]) -> String { } else { format!("{}", total_passed).red() }, - fork_summary_shell(reports, SpecId::PRAGUE), - fork_summary_shell(reports, SpecId::CANCUN), - fork_summary_shell(reports, SpecId::SHANGHAI), - fork_summary_shell(reports, SpecId::MERGE), - fork_summary_shell(reports, SpecId::LONDON), - fork_summary_shell(reports, SpecId::BERLIN), - fork_summary_shell(reports, SpecId::ISTANBUL), - fork_summary_shell(reports, SpecId::CONSTANTINOPLE), - fork_summary_shell(reports, SpecId::BYZANTIUM), - fork_summary_shell(reports, SpecId::HOMESTEAD), - fork_summary_shell(reports, SpecId::FRONTIER), + fork_summary_shell(reports, Fork::Prague), + fork_summary_shell(reports, Fork::Cancun), + fork_summary_shell(reports, Fork::Shanghai), + fork_summary_shell(reports, Fork::Paris), + fork_summary_shell(reports, Fork::London), + fork_summary_shell(reports, Fork::Berlin), + fork_summary_shell(reports, Fork::Istanbul), + fork_summary_shell(reports, Fork::Constantinople), + fork_summary_shell(reports, Fork::Byzantium), + fork_summary_shell(reports, Fork::Homestead), + fork_summary_shell(reports, Fork::Frontier), test_dir_summary_for_shell(reports), ) } -fn fork_summary_shell(reports: &[EFTestReport], fork: SpecId) -> String { +fn fork_summary_shell(reports: &[EFTestReport], fork: Fork) -> String { let fork_str: &str = fork.into(); let (fork_tests, fork_passed_tests, fork_success_percentage) = fork_statistics(reports, fork); format!( @@ -252,7 +251,7 @@ fn fork_summary_shell(reports: &[EFTestReport], fork: SpecId) -> String { ) } -fn fork_statistics(reports: &[EFTestReport], fork: SpecId) -> (usize, usize, f64) { +fn fork_statistics(reports: &[EFTestReport], fork: Fork) -> (usize, usize, f64) { let fork_tests = reports.iter().filter(|report| report.fork == fork).count(); let fork_passed_tests = reports .iter() @@ -312,17 +311,17 @@ impl Display for EFTestsReport { let total_run = self.0.len(); writeln!(f, "Summary: {total_passed}/{total_run}",)?; writeln!(f)?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::PRAGUE))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::CANCUN))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::SHANGHAI))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::HOMESTEAD))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::ISTANBUL))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::LONDON))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::BYZANTIUM))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::BERLIN))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::CONSTANTINOPLE))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::MERGE))?; - writeln!(f, "{}", fork_summary_shell(&self.0, SpecId::FRONTIER))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Prague))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Cancun))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Shanghai))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Byzantium))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Berlin))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Constantinople))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Paris))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Homestead))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Istanbul))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::London))?; + writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Frontier))?; writeln!(f)?; writeln!(f, "Failed tests:")?; writeln!(f)?; @@ -397,14 +396,14 @@ pub struct EFTestReport { pub name: String, pub dir: String, pub test_hash: H256, - pub fork: SpecId, + pub fork: Fork, pub skipped: bool, pub failed_vectors: HashMap<TestVector, EFTestRunnerError>, pub re_run_report: Option<TestReRunReport>, } impl EFTestReport { - pub fn new(name: String, dir: String, test_hash: H256, fork: SpecId) -> Self { + pub fn new(name: String, dir: String, test_hash: H256, fork: Fork) -> Self { EFTestReport { name, dir, diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index 10fc4ebc07..9bd1468929 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -108,7 +108,7 @@ pub fn prepare_vm_for_tx(vector: &TestVector, test: &EFTest) -> Result<VM, EFTes origin: tx.sender, refunded_gas: 0, gas_limit: tx.gas_limit, - spec_id: test.fork(), + fork: test.fork(), block_number: test.env.current_number, coinbase: test.env.current_coinbase, timestamp: test.env.current_timestamp, diff --git a/cmd/ef_tests/levm/runner/revm_runner.rs b/cmd/ef_tests/levm/runner/revm_runner.rs index cfcce780c6..baa42a13f6 100644 --- a/cmd/ef_tests/levm/runner/revm_runner.rs +++ b/cmd/ef_tests/levm/runner/revm_runner.rs @@ -14,7 +14,7 @@ use ethrex_levm::{ Account, StorageSlot, }; use ethrex_storage::{error::StoreError, AccountUpdate}; -use ethrex_vm::{db::StoreWrapper, EvmState, RevmAddress, RevmU256}; +use ethrex_vm::{db::StoreWrapper, fork_to_spec_id, EvmState, RevmAddress, RevmU256}; use revm::{ db::State, inspectors::TracerEip3155 as RevmTracerEip3155, @@ -195,7 +195,7 @@ pub fn prepare_revm_for_tx<'state>( .with_block_env(block_env) .with_tx_env(tx_env) .modify_cfg_env(|cfg| cfg.chain_id = chain_spec.chain_id) - .with_spec_id(test.fork()) + .with_spec_id(fork_to_spec_id(test.fork())) .with_external_context( RevmTracerEip3155::new(Box::new(std::io::stderr())).without_summary(), ); diff --git a/cmd/ef_tests/levm/types.rs b/cmd/ef_tests/levm/types.rs index 5295f7f4da..80219ed677 100644 --- a/cmd/ef_tests/levm/types.rs +++ b/cmd/ef_tests/levm/types.rs @@ -11,10 +11,9 @@ use crate::{ }; use bytes::Bytes; use ethrex_core::{ - types::{Genesis, GenesisAccount, TxKind}, + types::{Fork, Genesis, GenesisAccount, TxKind}, Address, H256, U256, }; -use ethrex_vm::SpecId; use serde::Deserialize; use std::collections::HashMap; @@ -33,21 +32,21 @@ pub struct EFTest { } impl EFTest { - pub fn fork(&self) -> SpecId { + pub fn fork(&self) -> Fork { match &self.post { - EFTestPost::Prague(_) => SpecId::PRAGUE, - EFTestPost::Cancun(_) => SpecId::CANCUN, - EFTestPost::Shanghai(_) => SpecId::SHANGHAI, - EFTestPost::Homestead(_) => SpecId::HOMESTEAD, - EFTestPost::Istanbul(_) => SpecId::ISTANBUL, - EFTestPost::London(_) => SpecId::LONDON, - EFTestPost::Byzantium(_) => SpecId::BYZANTIUM, - EFTestPost::Berlin(_) => SpecId::BERLIN, + EFTestPost::Prague(_) => Fork::Prague, + EFTestPost::Cancun(_) => Fork::Cancun, + EFTestPost::Shanghai(_) => Fork::Shanghai, + EFTestPost::Homestead(_) => Fork::Homestead, + EFTestPost::Istanbul(_) => Fork::Istanbul, + EFTestPost::London(_) => Fork::London, + EFTestPost::Byzantium(_) => Fork::Byzantium, + EFTestPost::Berlin(_) => Fork::Berlin, EFTestPost::Constantinople(_) | EFTestPost::ConstantinopleFix(_) => { - SpecId::CONSTANTINOPLE + Fork::Constantinople } - EFTestPost::Paris(_) => SpecId::MERGE, - EFTestPost::Frontier(_) => SpecId::FRONTIER, + EFTestPost::Paris(_) => Fork::Paris, + EFTestPost::Frontier(_) => Fork::Frontier, } } } diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index 6b171fd7a9..8d1cd5a3cc 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -446,10 +446,9 @@ fn apply_plain_transaction( &context.payload.header, store_wrapper.clone(), block_cache, - spec_id( - &context.chain_config().map_err(ChainError::from)?, - context.payload.header.timestamp, - ), + context + .chain_config()? + .fork(context.payload.header.timestamp), ) .map_err(EvmError::from)?; diff --git a/crates/common/types/genesis.rs b/crates/common/types/genesis.rs index cf4e0f9674..d84ede355d 100644 --- a/crates/common/types/genesis.rs +++ b/crates/common/types/genesis.rs @@ -87,11 +87,57 @@ pub struct ChainConfig { pub terminal_total_difficulty_passed: bool, } -#[derive(Debug, PartialEq, PartialOrd)] +#[repr(u8)] +#[derive(Debug, PartialEq, PartialOrd, Default, Clone, Copy, Serialize, Deserialize)] pub enum Fork { - Paris = 0, - Shanghai = 1, - Cancun = 2, + Frontier = 0, + FrontierThawing = 1, + Homestead = 2, + DaoFork = 3, + Tangerine = 4, + SpuriousDragon = 5, + Byzantium = 6, + Constantinople = 7, + Petersburg = 8, + Istanbul = 9, + MuirGlacier = 10, + Berlin = 11, + London = 12, + ArrowGlacier = 13, + GrayGlacier = 14, + Paris = 15, + Shanghai = 16, + #[default] + Cancun = 17, + Prague = 18, + PragueEof = 19, +} + +impl From<Fork> for &str { + fn from(fork: Fork) -> Self { + match fork { + Fork::Frontier => "Frontier", + Fork::FrontierThawing => "FrontierThawing", + Fork::Homestead => "Homestead", + Fork::DaoFork => "DaoFork", + Fork::Tangerine => "Tangerine", + Fork::SpuriousDragon => "SpuriousDragon", + Fork::Byzantium => "Byzantium", + Fork::Constantinople => "Constantinople", + Fork::Petersburg => "Petersburg", + Fork::Istanbul => "Istanbul", + Fork::MuirGlacier => "MuirGlacier", + Fork::Berlin => "Berlin", + Fork::London => "London", + Fork::ArrowGlacier => "ArrowGlacier", + Fork::GrayGlacier => "GrayGlacier", + Fork::Paris => "Paris", + Fork::Shanghai => "Shanghai", + Fork::Cancun => "Cancun", + Fork::Prague => "Prague", + Fork::PragueEof => "Prague EOF", + } + } } impl ChainConfig { @@ -122,6 +168,10 @@ impl ChainConfig { } } + pub fn fork(&self, block_timestamp: u64) -> Fork { + self.get_fork(block_timestamp) + } + pub fn gather_forks(&self) -> (Vec<u64>, Vec<u64>) { let block_number_based_forks: Vec<u64> = vec![ self.homestead_block, diff --git a/crates/vm/levm/src/environment.rs b/crates/vm/levm/src/environment.rs index b1e1ae8956..3600b841c8 100644 --- a/crates/vm/levm/src/environment.rs +++ b/crates/vm/levm/src/environment.rs @@ -1,4 +1,4 @@ -use ethrex_core::{Address, H256, U256}; +use ethrex_core::{types::Fork, Address, H256, U256}; pub use revm_primitives::SpecId; use std::collections::HashMap; @@ -12,7 +12,7 @@ pub struct Environment { pub origin: Address, pub refunded_gas: u64, pub gas_limit: u64, - pub spec_id: SpecId, + pub fork: Fork, pub block_number: U256, pub coinbase: Address, pub timestamp: U256, diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index a659876415..9ddaac4b5d 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -6,9 +6,8 @@ use crate::{ }; use bytes::Bytes; /// Contains the gas costs of the EVM instructions -use ethrex_core::U256; +use ethrex_core::{types::Fork, U256}; use num_bigint::BigUint; -use revm_primitives::SpecId; // Opcodes cost pub const STOP: u64 = 0; @@ -464,14 +463,14 @@ pub fn create( new_memory_size: usize, current_memory_size: usize, code_size_in_memory: usize, - spec_id: SpecId, + fork: Fork, ) -> Result<u64, VMError> { compute_gas_create( new_memory_size, current_memory_size, code_size_in_memory, false, - spec_id, + fork, ) } @@ -479,14 +478,14 @@ pub fn create_2( new_memory_size: usize, current_memory_size: usize, code_size_in_memory: usize, - spec_id: SpecId, + fork: Fork, ) -> Result<u64, VMError> { compute_gas_create( new_memory_size, current_memory_size, code_size_in_memory, true, - spec_id, + fork, ) } @@ -495,7 +494,7 @@ fn compute_gas_create( current_memory_size: usize, code_size_in_memory: usize, is_create_2: bool, - spec_id: SpecId, + fork: Fork, ) -> Result<u64, VMError> { let minimum_word_size = (code_size_in_memory .checked_add(31) @@ -508,7 +507,7 @@ fn compute_gas_create( .map_err(|_| VMError::VeryLargeNumber)?; // [EIP-3860] - Apply extra gas cost of 2 for every 32-byte chunk of initcode - let init_code_cost = if spec_id >= SpecId::SHANGHAI { + let init_code_cost = if fork >= Fork::Shanghai { minimum_word_size .checked_mul(INIT_CODE_WORD_COST) .ok_or(OutOfGasError::GasCostOverflow)? // will not panic since it's 2 @@ -560,13 +559,13 @@ pub fn selfdestruct( Ok(gas_cost) } -pub fn tx_calldata(calldata: &Bytes, spec_id: SpecId) -> Result<u64, OutOfGasError> { +pub fn tx_calldata(calldata: &Bytes, fork: Fork) -> Result<u64, OutOfGasError> { // This cost applies both for call and create // 4 gas for each zero byte in the transaction data 16 gas for each non-zero byte in the transaction. let mut calldata_cost: u64 = 0; for byte in calldata { calldata_cost = if *byte != 0 { - if spec_id >= SpecId::ISTANBUL { + if fork >= Fork::Istanbul { calldata_cost .checked_add(CALLDATA_COST_NON_ZERO_BYTE) .ok_or(OutOfGasError::GasUsedOverflow)? @@ -681,7 +680,7 @@ pub fn call( value_to_transfer: U256, gas_from_stack: U256, gas_left: u64, - spec_id: SpecId, + fork: Fork, ) -> Result<(u64, u64), VMError> { let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; @@ -689,7 +688,7 @@ pub fn call( address_was_cold, CALL_STATIC, CALL_COLD_DYNAMIC, - if spec_id >= SpecId::BERLIN { + if fork >= Fork::Berlin { CALL_WARM_DYNAMIC } else { //https://eips.ethereum.org/EIPS/eip-2929 @@ -966,7 +965,7 @@ pub fn modexp( base_size: usize, exponent_size: usize, modulus_size: usize, - spec_id: SpecId, + fork: Fork, ) -> Result<u64, VMError> { let base_size: u64 = base_size .try_into() @@ -980,7 +979,7 @@ pub fn modexp( let max_length = base_size.max(modulus_size); - if spec_id >= SpecId::BERLIN { + if fork >= Fork::Berlin { modexp_eip2565(max_length, exponent_first_32_bytes, exponent_size) } else { modexp_eip198(max_length, exponent_first_32_bytes, exponent_size) diff --git a/crates/vm/levm/src/opcode_handlers/block.rs b/crates/vm/levm/src/opcode_handlers/block.rs index c0f52a02a1..17987d7dcd 100644 --- a/crates/vm/levm/src/opcode_handlers/block.rs +++ b/crates/vm/levm/src/opcode_handlers/block.rs @@ -7,10 +7,9 @@ use crate::{ vm::VM, }; use ethrex_core::{ - types::{BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BASE_FEE_PER_BLOB_GAS}, + types::{Fork, BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BASE_FEE_PER_BLOB_GAS}, U256, }; -use revm_primitives::SpecId; // Block Information (11) // Opcodes: BLOCKHASH, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, CHAINID, SELFBALANCE, BASEFEE, BLOBHASH, BLOBBASEFEE @@ -165,7 +164,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result<OpcodeSuccess, VMError> { // [EIP-4844] - BLOBHASH is only available from CANCUN - if self.env.spec_id < SpecId::CANCUN { + if self.env.fork < Fork::Cancun { return Err(VMError::InvalidOpcode); } @@ -212,7 +211,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result<OpcodeSuccess, VMError> { // [EIP-7516] - BLOBBASEFEE is only available from CANCUN - if self.env.spec_id < SpecId::CANCUN { + if self.env.fork < Fork::Cancun { return Err(VMError::InvalidOpcode); } self.increase_consumed_gas(current_call_frame, gas_cost::BLOBBASEFEE)?; diff --git a/crates/vm/levm/src/opcode_handlers/push.rs b/crates/vm/levm/src/opcode_handlers/push.rs index ac4c7ab759..fa5cbde2a3 100644 --- a/crates/vm/levm/src/opcode_handlers/push.rs +++ b/crates/vm/levm/src/opcode_handlers/push.rs @@ -5,8 +5,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethrex_core::U256; -use revm_primitives::SpecId; +use ethrex_core::{types::Fork, U256}; // Push Operations // Opcodes: PUSH0, PUSH1 ... PUSH32 @@ -38,7 +37,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result<OpcodeSuccess, VMError> { // [EIP-3855] - PUSH0 is only available from SHANGHAI - if self.env.spec_id < SpecId::SHANGHAI { + if self.env.fork < Fork::Shanghai { return Err(VMError::InvalidOpcode); } diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index d906e3ec65..eb5b0a95f8 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -6,8 +6,7 @@ use crate::{ memory::{self, calculate_memory_size}, vm::VM, }; -use ethrex_core::{H256, U256}; -use revm_primitives::SpecId; +use ethrex_core::{types::Fork, H256, U256}; // Stack, Memory, Storage and Flow Operations (15) // Opcodes: POP, MLOAD, MSTORE, MSTORE8, SLOAD, SSTORE, JUMP, JUMPI, PC, MSIZE, GAS, JUMPDEST, TLOAD, TSTORE, MCOPY @@ -26,7 +25,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result<OpcodeSuccess, VMError> { // [EIP-1153] - TLOAD is only available from CANCUN - if self.env.spec_id < SpecId::CANCUN { + if self.env.fork < Fork::Cancun { return Err(VMError::InvalidOpcode); } @@ -50,7 +49,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result<OpcodeSuccess, VMError> { // [EIP-1153] - TLOAD is only available from CANCUN - if self.env.spec_id < SpecId::CANCUN { + if self.env.fork < Fork::Cancun { return Err(VMError::InvalidOpcode); } @@ -264,7 +263,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result<OpcodeSuccess, VMError> { // [EIP-5656] - MCOPY is only available from CANCUN - if self.env.spec_id < SpecId::CANCUN { + if self.env.fork < Fork::Cancun { return Err(VMError::InvalidOpcode); } diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 3af13ae59d..37b530a774 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -11,8 +11,7 @@ use crate::{ Account, }; use bytes::Bytes; -use ethrex_core::{Address, U256}; -use revm_primitives::SpecId; +use ethrex_core::{types::Fork, Address, U256}; // System Operations (10) // Opcodes: CREATE, CALL, CALLCODE, RETURN, DELEGATECALL, CREATE2, STATICCALL, REVERT, INVALID, SELFDESTRUCT @@ -75,7 +74,7 @@ impl VM { value_to_transfer, gas, gas_left, - self.env.spec_id, + self.env.fork, )?; self.increase_consumed_gas(current_call_frame, cost)?; @@ -395,7 +394,7 @@ impl VM { new_size, current_call_frame.memory.len(), code_size_in_memory, - self.env.spec_id, + self.env.fork, )?, )?; @@ -430,7 +429,7 @@ impl VM { new_size, current_call_frame.memory.len(), code_size_in_memory, - self.env.spec_id, + self.env.fork, )?, )?; @@ -520,7 +519,7 @@ impl VM { )?; // [EIP-6780] - SELFDESTRUCT only in same transaction from CANCUN - if self.env.spec_id >= SpecId::CANCUN { + if self.env.fork >= Fork::Cancun { increase_account_balance( &mut self.cache, &mut self.db, @@ -583,7 +582,7 @@ impl VM { return Err(VMError::OpcodeNotAllowedInStaticContext); } // 2. [EIP-3860] - Cant exceed init code max size - if code_size_in_memory > INIT_CODE_MAX_SIZE && self.env.spec_id >= SpecId::SHANGHAI { + if code_size_in_memory > INIT_CODE_MAX_SIZE && self.env.fork >= Fork::Shanghai { return Err(VMError::OutOfGas(OutOfGasError::ConsumedGasOverflow)); } diff --git a/crates/vm/levm/src/precompiles.rs b/crates/vm/levm/src/precompiles.rs index 2b0e14132a..eadca8619f 100644 --- a/crates/vm/levm/src/precompiles.rs +++ b/crates/vm/levm/src/precompiles.rs @@ -4,7 +4,7 @@ use bls12_381::{ }; use bytes::Bytes; -use ethrex_core::{serde_utils::bool, Address, H160, H256, U256}; +use ethrex_core::{serde_utils::bool, types::Fork, Address, H160, H256, U256}; use keccak_hash::keccak256; use kzg_rs::{Bytes32, Bytes48, KzgSettings}; use lambdaworks_math::{ @@ -32,7 +32,6 @@ use lambdaworks_math::{ }; use libsecp256k1::{self, Message, RecoveryId, Signature}; use num_bigint::BigUint; -use revm_primitives::SpecId; use sha3::Digest; use std::ops::Mul; @@ -176,14 +175,14 @@ const FP2_ZERO_MAPPED_TO_G2: [u8; 256] = [ pub const G1_POINT_AT_INFINITY: [u8; 128] = [0_u8; 128]; pub const G2_POINT_AT_INFINITY: [u8; 256] = [0_u8; 256]; -pub fn is_precompile(callee_address: &Address, spec_id: SpecId) -> bool { +pub fn is_precompile(callee_address: &Address, fork: Fork) -> bool { // Cancun specs is the only one that allows point evaluation precompile - if *callee_address == POINT_EVALUATION_ADDRESS && spec_id < SpecId::CANCUN { + if *callee_address == POINT_EVALUATION_ADDRESS && fork < Fork::Cancun { return false; } // Prague or newers forks should only use this precompiles // https://eips.ethereum.org/EIPS/eip-2537 - if PRECOMPILES_POST_CANCUN.contains(callee_address) && spec_id < SpecId::PRAGUE { + if PRECOMPILES_POST_CANCUN.contains(callee_address) && fork < Fork::Prague { return false; } @@ -192,7 +191,7 @@ pub fn is_precompile(callee_address: &Address, spec_id: SpecId) -> bool { pub fn execute_precompile( current_call_frame: &mut CallFrame, - spec_id: SpecId, + fork: Fork, ) -> Result<Bytes, VMError> { let callee_address = current_call_frame.code_address; let gas_for_call = current_call_frame @@ -218,7 +217,7 @@ pub fn execute_precompile( ¤t_call_frame.calldata, gas_for_call, consumed_gas, - spec_id, + fork, )?, address if address == ECADD_ADDRESS => { ecadd(¤t_call_frame.calldata, gas_for_call, consumed_gas)? @@ -400,7 +399,7 @@ pub fn modexp( calldata: &Bytes, gas_for_call: u64, consumed_gas: &mut u64, - spec_id: SpecId, + fork: Fork, ) -> Result<Bytes, VMError> { // If calldata does not reach the required length, we should fill the rest with zeros let calldata = fill_with_zeros(calldata, 96)?; @@ -426,7 +425,7 @@ pub fn modexp( if base_size == U256::zero() && modulus_size == U256::zero() { // On Berlin or newer there is a floor cost for the modexp precompile // On older versions in this return there is no cost added, see more https://eips.ethereum.org/EIPS/eip-2565 - if spec_id >= SpecId::BERLIN { + if fork >= Fork::Berlin { increase_precompile_consumed_gas(gas_for_call, MODEXP_STATIC_COST, consumed_gas)?; } return Ok(Bytes::new()); @@ -465,13 +464,7 @@ pub fn modexp( // Use of unwrap_or_default because if e == 0 get_slice_or_default returns an empty vec let exp_first_32 = BigUint::from_bytes_be(e.get(0..bytes_to_take).unwrap_or_default()); - let gas_cost = gas_cost::modexp( - &exp_first_32, - base_size, - exponent_size, - modulus_size, - spec_id, - )?; + let gas_cost = gas_cost::modexp(&exp_first_32, base_size, exponent_size, modulus_size, fork)?; increase_precompile_consumed_gas(gas_for_call, gas_cost, consumed_gas)?; diff --git a/crates/vm/levm/src/utils.rs b/crates/vm/levm/src/utils.rs index beae2e8e98..d07653a27a 100644 --- a/crates/vm/levm/src/utils.rs +++ b/crates/vm/levm/src/utils.rs @@ -16,12 +16,11 @@ use crate::{ AccountInfo, }; use bytes::Bytes; -use ethrex_core::{Address, H256, U256}; +use ethrex_core::{types::Fork, Address, H256, U256}; use ethrex_rlp; use ethrex_rlp::encode::RLPEncode; use keccak_hash::keccak; use libsecp256k1::{Message, RecoveryId, Signature}; -use revm_primitives::SpecId; use sha3::{Digest, Keccak256}; use std::{ collections::{HashMap, HashSet}, @@ -219,7 +218,7 @@ pub fn update_account_bytecode( // ==================== Gas related functions ======================= pub fn get_intrinsic_gas( is_create: bool, - spec_id: SpecId, + fork: Fork, access_list: &AccessList, authorization_list: &Option<AuthorizationList>, initial_call_frame: &CallFrame, @@ -230,7 +229,7 @@ pub fn get_intrinsic_gas( // Calldata Cost // 4 gas for each zero byte in the transaction data 16 gas for each non-zero byte in the transaction. let calldata_cost = - gas_cost::tx_calldata(&initial_call_frame.calldata, spec_id).map_err(VMError::OutOfGas)?; + gas_cost::tx_calldata(&initial_call_frame.calldata, fork).map_err(VMError::OutOfGas)?; intrinsic_gas = intrinsic_gas .checked_add(calldata_cost) @@ -300,10 +299,10 @@ pub fn get_intrinsic_gas( /// After EIP-7691 the maximum number of blob hashes changes. For more /// information see /// [EIP-7691](https://eips.ethereum.org/EIPS/eip-7691#specification). -pub const fn max_blobs_per_block(specid: SpecId) -> usize { - match specid { - SpecId::PRAGUE => MAX_BLOB_COUNT_ELECTRA, - SpecId::PRAGUE_EOF => MAX_BLOB_COUNT_ELECTRA, +pub const fn max_blobs_per_block(fork: Fork) -> usize { + match fork { + Fork::Prague => MAX_BLOB_COUNT_ELECTRA, + Fork::PragueEof => MAX_BLOB_COUNT_ELECTRA, _ => MAX_BLOB_COUNT, } } @@ -315,22 +314,23 @@ pub const fn max_blobs_per_block(specid: SpecId) -> usize { /// calc_excess_blob_gas functions defined in EIP-4844 use the new /// values for the first block of the fork (and for all subsequent /// blocks)." -pub const fn get_blob_base_fee_update_fraction_value(specid: SpecId) -> U256 { - match specid { - SpecId::PRAGUE => BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, - SpecId::PRAGUE_EOF => BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, + +pub const fn get_blob_base_fee_update_fraction_value(fork: Fork) -> U256 { + match fork { + Fork::Prague => BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, + Fork::PragueEof => BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, _ => BLOB_BASE_FEE_UPDATE_FRACTION, } } pub fn get_base_fee_per_blob_gas( block_excess_blob_gas: Option<U256>, - spec_id: SpecId, + fork: Fork, ) -> Result<U256, VMError> { fake_exponential( MIN_BASE_FEE_PER_BLOB_GAS, block_excess_blob_gas.unwrap_or_default(), - get_blob_base_fee_update_fraction_value(spec_id), + get_blob_base_fee_update_fraction_value(fork), ) } @@ -360,7 +360,7 @@ pub fn get_max_blob_gas_price( pub fn get_blob_gas_price( tx_blob_hashes: Vec<H256>, block_excess_blob_gas: Option<U256>, - spec_id: SpecId, + fork: Fork, ) -> Result<U256, VMError> { let blobhash_amount: u64 = tx_blob_hashes .len() @@ -371,7 +371,7 @@ pub fn get_blob_gas_price( .checked_mul(BLOB_GAS_PER_BLOB) .unwrap_or_default(); - let base_fee_per_blob_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, spec_id)?; + let base_fee_per_blob_gas = get_base_fee_per_blob_gas(block_excess_blob_gas, fork)?; let blob_gas_price: U256 = blob_gas_price.into(); let blob_fee: U256 = blob_gas_price diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index eab730d63f..ce00ccb866 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -21,8 +21,10 @@ use crate::{ AccountInfo, TransientStorage, }; use bytes::Bytes; -use ethrex_core::{types::TxKind, Address, H256, U256}; -use revm_primitives::SpecId; +use ethrex_core::{ + types::{Fork, TxKind}, + Address, H256, U256, +}; use std::{ cmp::max, collections::{HashMap, HashSet}, @@ -87,7 +89,7 @@ impl VM { let mut default_touched_accounts = HashSet::from_iter([env.origin].iter().cloned()); // [EIP-3651] - Add coinbase to cache if the spec is SHANGHAI or higher - if env.spec_id >= SpecId::SHANGHAI { + if env.fork >= Fork::Shanghai { default_touched_accounts.insert(env.coinbase); } @@ -105,10 +107,10 @@ impl VM { // Add precompiled contracts addresses to cache. // TODO: Use the addresses from precompiles.rs in a future - let max_precompile_address = match env.spec_id { - spec if spec >= SpecId::PRAGUE => SIZE_PRECOMPILES_PRAGUE, - spec if spec >= SpecId::CANCUN => SIZE_PRECOMPILES_CANCUN, - spec if spec < SpecId::CANCUN => SIZE_PRECOMPILES_PRE_CANCUN, + let max_precompile_address = match env.fork { + spec if spec >= Fork::Prague => SIZE_PRECOMPILES_PRAGUE, + spec if spec >= Fork::Cancun => SIZE_PRECOMPILES_CANCUN, + spec if spec < Fork::Cancun => SIZE_PRECOMPILES_PRE_CANCUN, _ => return Err(VMError::Internal(InternalError::InvalidSpecId)), }; for i in 1..=max_precompile_address { @@ -212,8 +214,8 @@ impl VM { self.env.transient_storage.clone(), ); - if is_precompile(¤t_call_frame.code_address, self.env.spec_id) { - let precompile_result = execute_precompile(current_call_frame, self.env.spec_id); + if is_precompile(¤t_call_frame.code_address, self.env.fork) { + let precompile_result = execute_precompile(current_call_frame, self.env.fork); match precompile_result { Ok(output) => { @@ -508,7 +510,7 @@ impl VM { let intrinsic_gas = get_intrinsic_gas( self.is_create(), - self.env.spec_id, + self.env.fork, &self.access_list, &self.authorization_list, initial_call_frame, @@ -525,7 +527,7 @@ impl VM { initial_call_frame: &CallFrame, report: &TransactionReport, ) -> Result<u64, VMError> { - if self.env.spec_id >= SpecId::PRAGUE { + if self.env.fork >= Fork::Prague { // If the transaction is a CREATE transaction, the calldata is emptied and the bytecode is assigned. let calldata = if self.is_create() { &initial_call_frame.bytecode @@ -537,7 +539,7 @@ impl VM { // tx_calldata = nonzero_bytes_in_calldata * 16 + zero_bytes_in_calldata * 4 // this is actually tokens_in_calldata * STANDARD_TOKEN_COST // see it in https://eips.ethereum.org/EIPS/eip-7623 - let tokens_in_calldata: u64 = gas_cost::tx_calldata(calldata, self.env.spec_id) + let tokens_in_calldata: u64 = gas_cost::tx_calldata(calldata, self.env.fork) .map_err(VMError::OutOfGas)? .checked_div(STANDARD_TOKEN_COST) .ok_or(VMError::Internal(InternalError::DivisionError))?; @@ -570,11 +572,11 @@ impl VM { let sender_address = self.env.origin; let sender_account = get_account(&mut self.cache, &self.db, sender_address); - if self.env.spec_id >= SpecId::PRAGUE { + if self.env.fork >= Fork::Prague { // check for gas limit is grater or equal than the minimum required let intrinsic_gas: u64 = get_intrinsic_gas( self.is_create(), - self.env.spec_id, + self.env.fork, &self.access_list, &self.authorization_list, initial_call_frame, @@ -582,7 +584,7 @@ impl VM { // calldata_cost = tokens_in_calldata * 4 let calldata_cost: u64 = - gas_cost::tx_calldata(&initial_call_frame.calldata, self.env.spec_id) + gas_cost::tx_calldata(&initial_call_frame.calldata, self.env.fork) .map_err(VMError::OutOfGas)?; // same as calculated in gas_used() @@ -652,13 +654,13 @@ impl VM { let blob_gas_cost = get_blob_gas_price( self.env.tx_blob_hashes.clone(), self.env.block_excess_blob_gas, - self.env.spec_id, + self.env.fork, )?; // (2) INSUFFICIENT_MAX_FEE_PER_BLOB_GAS if let Some(tx_max_fee_per_blob_gas) = self.env.tx_max_fee_per_blob_gas { if tx_max_fee_per_blob_gas - < get_base_fee_per_blob_gas(self.env.block_excess_blob_gas, self.env.spec_id)? + < get_base_fee_per_blob_gas(self.env.block_excess_blob_gas, self.env.fork)? { return Err(VMError::TxValidation( TxValidationError::InsufficientMaxFeePerBlobGas, @@ -696,7 +698,7 @@ impl VM { if self.is_create() { // [EIP-3860] - INITCODE_SIZE_EXCEEDED if initial_call_frame.calldata.len() > INIT_CODE_MAX_SIZE - && self.env.spec_id >= SpecId::SHANGHAI + && self.env.fork >= Fork::Shanghai { return Err(VMError::TxValidation( TxValidationError::InitcodeSizeExceeded, @@ -738,7 +740,7 @@ impl VM { // Transaction is type 3 if tx_max_fee_per_blob_gas is Some if self.env.tx_max_fee_per_blob_gas.is_some() { // (11) TYPE_3_TX_PRE_FORK - if self.env.spec_id < SpecId::CANCUN { + if self.env.fork < Fork::Cancun { return Err(VMError::TxValidation(TxValidationError::Type3TxPreFork)); } @@ -762,7 +764,7 @@ impl VM { } // (14) TYPE_3_TX_BLOB_COUNT_EXCEEDED - if blob_hashes.len() > max_blobs_per_block(self.env.spec_id) { + if blob_hashes.len() > max_blobs_per_block(self.env.fork) { return Err(VMError::TxValidation( TxValidationError::Type3TxBlobCountExceeded, )); @@ -780,7 +782,7 @@ impl VM { // Transaction is type 4 if authorization_list is Some if let Some(auth_list) = &self.authorization_list { // (16) TYPE_4_TX_PRE_FORK - if self.env.spec_id < SpecId::PRAGUE { + if self.env.fork < Fork::Prague { return Err(VMError::TxValidation(TxValidationError::Type4TxPreFork)); } @@ -1024,7 +1026,7 @@ impl VM { ) -> Result<(StorageSlot, bool), VMError> { // [EIP-2929] - Introduced conditional tracking of accessed storage slots for Berlin and later specs. let mut storage_slot_was_cold = false; - if self.env.spec_id >= SpecId::BERLIN { + if self.env.fork >= Fork::Berlin { storage_slot_was_cold = self .accrued_substate .touched_storage_slots diff --git a/crates/vm/levm/tests/tests.rs b/crates/vm/levm/tests/tests.rs index d366710156..904f4a28c6 100644 --- a/crates/vm/levm/tests/tests.rs +++ b/crates/vm/levm/tests/tests.rs @@ -2,7 +2,10 @@ #![allow(clippy::unwrap_used)] use bytes::Bytes; -use ethrex_core::{types::TxKind, Address, H256, U256}; +use ethrex_core::{ + types::{Fork, TxKind}, + Address, H256, U256, +}; use ethrex_levm::{ account::Account, constants::*, @@ -4559,13 +4562,7 @@ fn modexp_test() { let calldata = Bytes::from(calldata); let mut consumed_gas = 0; - let result = modexp( - &calldata, - 10000, - &mut consumed_gas, - ethrex_levm::SpecId::CANCUN, - ) - .unwrap(); + let result = modexp(&calldata, 10000, &mut consumed_gas, Fork::Cancun).unwrap(); let expected_result = Bytes::from(hex::decode("08").unwrap()); @@ -4580,13 +4577,7 @@ fn modexp_test_2() { let calldata = Bytes::from(calldata); let mut consumed_gas = 0; - let result = modexp( - &calldata, - 10000, - &mut consumed_gas, - ethrex_levm::SpecId::CANCUN, - ) - .unwrap(); + let result = modexp(&calldata, 10000, &mut consumed_gas, Fork::Cancun).unwrap(); let expected_result = Bytes::from( hex::decode("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab").unwrap(), diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index a52b3b86e6..a5490da971 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -94,7 +94,7 @@ cfg_if::cfg_if! { pub fn beacon_root_contract_call_levm( store_wrapper: Arc<StoreWrapper>, block_header: &BlockHeader, - spec_id: SpecId, + fork: Fork, ) -> Result<TransactionReport, EvmError> { lazy_static! { static ref SYSTEM_ADDRESS: Address = @@ -125,7 +125,7 @@ cfg_if::cfg_if! { block_blob_gas_used: block_header.blob_gas_used.map(U256::from), block_gas_limit: 30_000_000, transient_storage: HashMap::new(), - spec_id, + fork, ..Default::default() }; @@ -220,12 +220,12 @@ cfg_if::cfg_if! { }); let mut block_cache: CacheDB = HashMap::new(); let block_header = &block.header; - let spec_id = spec_id(&state.chain_config()?, block_header.timestamp); + let fork = state.chain_config()?.fork(block_header.timestamp); //eip 4788: execute beacon_root_contract_call before block transactions cfg_if::cfg_if! { if #[cfg(not(feature = "l2"))] { - if block_header.parent_beacon_block_root.is_some() && spec_id == SpecId::CANCUN { - let report = beacon_root_contract_call_levm(store_wrapper.clone(), block_header, spec_id)?; + if block_header.parent_beacon_block_root.is_some() && fork == Fork::Cancun { + let report = beacon_root_contract_call_levm(store_wrapper.clone(), block_header, fork)?; block_cache.extend(report.new_state); } } @@ -239,7 +239,7 @@ cfg_if::cfg_if! { let mut cumulative_gas_used = 0; for tx in block.body.transactions.iter() { - let report = execute_tx_levm(tx, block_header, store_wrapper.clone(), block_cache.clone(), spec_id).map_err(EvmError::from)?; + let report = execute_tx_levm(tx, block_header, store_wrapper.clone(), block_cache.clone(), fork).map_err(EvmError::from)?; let mut new_state = report.new_state.clone(); @@ -292,7 +292,7 @@ cfg_if::cfg_if! { block_header: &BlockHeader, db: Arc<dyn LevmDatabase>, block_cache: CacheDB, - spec_id: SpecId + fork: Fork ) -> Result<TransactionReport, VMError> { let gas_price : U256 = tx.effective_gas_price(block_header.base_fee_per_gas).ok_or(VMError::InvalidTransaction)?.into(); @@ -300,7 +300,7 @@ cfg_if::cfg_if! { origin: tx.sender(), refunded_gas: 0, gas_limit: tx.gas_limit(), - spec_id, + fork, block_number: block_header.number.into(), coinbase: block_header.coinbase, timestamp: block_header.timestamp.into(), @@ -979,10 +979,31 @@ fn access_list_inspector( /// Returns the spec id according to the block timestamp and the stored chain config /// WARNING: Assumes at least Merge fork is active pub fn spec_id(chain_config: &ChainConfig, block_timestamp: u64) -> SpecId { - match chain_config.get_fork(block_timestamp) { - Fork::Cancun => SpecId::CANCUN, - Fork::Shanghai => SpecId::SHANGHAI, + fork_to_spec_id(chain_config.get_fork(block_timestamp)) +} + +pub fn fork_to_spec_id(fork: Fork) -> SpecId { + match fork { + Fork::Frontier => SpecId::FRONTIER, + Fork::FrontierThawing => SpecId::FRONTIER_THAWING, + Fork::Homestead => SpecId::HOMESTEAD, + Fork::DaoFork => SpecId::DAO_FORK, + Fork::Tangerine => SpecId::TANGERINE, + Fork::SpuriousDragon => SpecId::SPURIOUS_DRAGON, + Fork::Byzantium => SpecId::BYZANTIUM, + Fork::Constantinople => SpecId::CONSTANTINOPLE, + Fork::Petersburg => SpecId::PETERSBURG, + Fork::Istanbul => SpecId::ISTANBUL, + Fork::MuirGlacier => SpecId::MUIR_GLACIER, + Fork::Berlin => SpecId::BERLIN, + Fork::London => SpecId::LONDON, + Fork::ArrowGlacier => SpecId::ARROW_GLACIER, + Fork::GrayGlacier => SpecId::GRAY_GLACIER, Fork::Paris => SpecId::MERGE, + Fork::Shanghai => SpecId::SHANGHAI, + Fork::Cancun => SpecId::CANCUN, + Fork::Prague => SpecId::PRAGUE, + Fork::PragueEof => SpecId::PRAGUE_EOF, } }