diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 614344d5137..47149d1e684 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -19,9 +19,9 @@ use ethrex_common::types::fee_config::FeeConfig; use ethrex_common::types::requests::{EncodedRequests, Requests, compute_requests_hash}; use ethrex_common::types::{ AccountState, AccountUpdate, Block, BlockHash, BlockHeader, BlockNumber, ChainConfig, Code, - EIP4844Transaction, Receipt, Transaction, WrappedEIP4844Transaction, compute_receipts_root, - validate_block_header, validate_cancun_header_fields, validate_prague_header_fields, - validate_pre_cancun_header_fields, + EIP4844Transaction, Fork::*, Receipt, Transaction, WrappedEIP4844Transaction, + compute_receipts_root, validate_block_header, validate_cancun_header_fields, + validate_prague_header_fields, validate_pre_cancun_header_fields, }; use ethrex_common::types::{ELASTICITY_MULTIPLIER, P2PTransaction}; use ethrex_common::types::{Fork, MempoolTransaction}; @@ -1255,7 +1255,7 @@ impl Blockchain { // NOTE: We could add a tx size limit here, but it's not in the actual spec // Check init code size - if config.is_shanghai_activated(header.timestamp) + if config.is_fork_activated(Shanghai, header.timestamp) && tx.is_contract_creation() && tx.data().len() > MAX_INITCODE_SIZE as usize { @@ -1266,7 +1266,8 @@ impl Blockchain { return Err(MempoolError::TxMaxDataSizeError); } - if config.is_osaka_activated(header.timestamp) && tx.gas_limit() > POST_OSAKA_GAS_LIMIT_CAP + if config.is_fork_activated(Osaka, header.timestamp) + && tx.gas_limit() > POST_OSAKA_GAS_LIMIT_CAP { // https://eips.ethereum.org/EIPS/eip-7825 return Err(MempoolError::TxMaxGasLimitExceededError( @@ -1400,7 +1401,7 @@ impl Blockchain { .storage .get_block_header(latest_block_number)? .ok_or(StoreError::Custom("Latest block not in DB".to_string()))?; - Ok(chain_config.fork(latest_block.timestamp)) + Ok(chain_config.get_fork(latest_block.timestamp)) } } @@ -1423,7 +1424,7 @@ pub fn validate_requests_hash( chain_config: &ChainConfig, requests: &[Requests], ) -> Result<(), ChainError> { - if !chain_config.is_prague_activated(header.timestamp) { + if !chain_config.is_fork_activated(Prague, header.timestamp) { return Ok(()); } @@ -1510,7 +1511,7 @@ pub fn validate_block( validate_block_header(&block.header, parent_header, elasticity_multiplier) .map_err(InvalidBlockError::from)?; - if chain_config.is_osaka_activated(block.header.timestamp) { + if chain_config.is_fork_activated(Osaka, block.header.timestamp) { let block_rlp_size = block.encode_to_vec().len(); if block_rlp_size > MAX_RLP_BLOCK_SIZE as usize { return Err(error::ChainError::InvalidBlock( @@ -1521,14 +1522,14 @@ pub fn validate_block( )); } } - if chain_config.is_prague_activated(block.header.timestamp) { + if chain_config.is_fork_activated(Prague, block.header.timestamp) { validate_prague_header_fields(&block.header, parent_header, chain_config) .map_err(InvalidBlockError::from)?; verify_blob_gas_usage(block, chain_config)?; - if chain_config.is_osaka_activated(block.header.timestamp) { + if chain_config.is_fork_activated(Osaka, block.header.timestamp) { verify_transaction_max_gas_limit(block)?; } - } else if chain_config.is_cancun_activated(block.header.timestamp) { + } else if chain_config.is_fork_activated(Cancun, block.header.timestamp) { validate_cancun_header_fields(&block.header, parent_header, chain_config) .map_err(InvalidBlockError::from)?; verify_blob_gas_usage(block, chain_config)?; @@ -1570,7 +1571,7 @@ fn verify_blob_gas_usage(block: &Block, config: &ChainConfig) -> Result<(), Chai let mut blob_gas_used = 0_u32; let mut blobs_in_block = 0_u32; let max_blob_number_per_block = config - .get_fork_blob_schedule(block.header.timestamp) + .get_blob_schedule_for_time(block.header.timestamp) .map(|schedule| schedule.max) .ok_or(ChainError::Custom("Provided block fork is invalid".into()))?; let max_blob_gas_per_block = max_blob_number_per_block * GAS_PER_BLOB; diff --git a/crates/blockchain/constants.rs b/crates/blockchain/constants.rs index e0ba1f698d9..4cc8dcdcda2 100644 --- a/crates/blockchain/constants.rs +++ b/crates/blockchain/constants.rs @@ -18,9 +18,6 @@ pub const TX_ACCESS_LIST_ADDRESS_GAS: u64 = 2400; // Gas cost for each storage key specified on access lists pub const TX_ACCESS_LIST_STORAGE_KEY_GAS: u64 = 1900; -// Gas cost for each non zero byte on transaction data -pub const TX_DATA_NON_ZERO_GAS: u64 = 68; - // === EIP-170 constants === // Max bytecode size @@ -37,7 +34,7 @@ pub const MAX_TRANSACTION_DATA_SIZE: u32 = 4 * 32 * 1024; // 128 Kb // === EIP-2028 constants === // Gas cost for each non zero byte on transaction data -pub const TX_DATA_NON_ZERO_GAS_EIP2028: u64 = 16; +pub const TX_DATA_NON_ZERO_GAS: u64 = 16; // === EIP-4844 constants === diff --git a/crates/blockchain/mempool.rs b/crates/blockchain/mempool.rs index db14719c79a..b6b98a5c5de 100644 --- a/crates/blockchain/mempool.rs +++ b/crates/blockchain/mempool.rs @@ -6,14 +6,15 @@ use std::{ use crate::{ constants::{ TX_ACCESS_LIST_ADDRESS_GAS, TX_ACCESS_LIST_STORAGE_KEY_GAS, TX_CREATE_GAS_COST, - TX_DATA_NON_ZERO_GAS, TX_DATA_NON_ZERO_GAS_EIP2028, TX_DATA_ZERO_GAS_COST, TX_GAS_COST, - TX_INIT_CODE_WORD_GAS_COST, + TX_DATA_NON_ZERO_GAS, TX_DATA_ZERO_GAS_COST, TX_GAS_COST, TX_INIT_CODE_WORD_GAS_COST, }, error::MempoolError, }; use ethrex_common::{ Address, H160, H256, U256, - types::{BlobsBundle, BlockHeader, ChainConfig, MempoolTransaction, Transaction, TxType}, + types::{ + BlobsBundle, BlockHeader, ChainConfig, Fork::*, MempoolTransaction, Transaction, TxType, + }, }; use ethrex_storage::error::StoreError; use std::collections::HashSet; @@ -426,16 +427,10 @@ pub fn transaction_intrinsic_gas( let data_len = tx.data().len() as u64; if data_len > 0 { - let non_zero_gas_cost = if config.is_istanbul_activated(header.number) { - TX_DATA_NON_ZERO_GAS_EIP2028 - } else { - TX_DATA_NON_ZERO_GAS - }; - let non_zero_count = tx.data().iter().filter(|&&x| x != 0u8).count() as u64; gas = gas - .checked_add(non_zero_count * non_zero_gas_cost) + .checked_add(non_zero_count * TX_DATA_NON_ZERO_GAS) .ok_or(MempoolError::TxGasOverflowError)?; let zero_count = data_len - non_zero_count; @@ -444,7 +439,7 @@ pub fn transaction_intrinsic_gas( .checked_add(zero_count * TX_DATA_ZERO_GAS_COST) .ok_or(MempoolError::TxGasOverflowError)?; - if is_contract_creation && config.is_shanghai_activated(header.timestamp) { + if is_contract_creation && config.is_fork_activated(Shanghai, header.timestamp) { // Len in 32 bytes sized words let len_in_words = data_len.saturating_add(31) / 32; @@ -477,8 +472,7 @@ mod tests { use crate::error::MempoolError; use crate::mempool::{ Mempool, TX_ACCESS_LIST_ADDRESS_GAS, TX_ACCESS_LIST_STORAGE_KEY_GAS, TX_CREATE_GAS_COST, - TX_DATA_NON_ZERO_GAS, TX_DATA_NON_ZERO_GAS_EIP2028, TX_DATA_ZERO_GAS_COST, TX_GAS_COST, - TX_INIT_CODE_WORD_GAS_COST, + TX_DATA_NON_ZERO_GAS, TX_DATA_ZERO_GAS_COST, TX_GAS_COST, TX_INIT_CODE_WORD_GAS_COST, }; use std::collections::HashMap; @@ -572,29 +566,6 @@ mod tests { assert_eq!(intrinsic_gas, expected_gas_cost); } - #[test] - fn transaction_intrinsic_data_gas_pre_istanbul() { - let (config, header) = build_basic_config_and_header(false, false); - - let tx = EIP1559Transaction { - nonce: 3, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 0, - gas_limit: 100_000, - to: TxKind::Call(Address::from_low_u64_be(1)), // Normal tx - value: U256::zero(), // Value zero - data: Bytes::from(vec![0x0, 0x1, 0x1, 0x0, 0x1, 0x1]), // 6 bytes of data - access_list: Default::default(), // No access list - ..Default::default() - }; - - let tx = Transaction::EIP1559Transaction(tx); - let expected_gas_cost = TX_GAS_COST + 2 * TX_DATA_ZERO_GAS_COST + 4 * TX_DATA_NON_ZERO_GAS; - let intrinsic_gas = - transaction_intrinsic_gas(&tx, &header, &config).expect("Intrinsic gas"); - assert_eq!(intrinsic_gas, expected_gas_cost); - } - #[test] fn transaction_intrinsic_data_gas_post_istanbul() { let (config, header) = build_basic_config_and_header(true, false); @@ -612,8 +583,7 @@ mod tests { }; let tx = Transaction::EIP1559Transaction(tx); - let expected_gas_cost = - TX_GAS_COST + 2 * TX_DATA_ZERO_GAS_COST + 4 * TX_DATA_NON_ZERO_GAS_EIP2028; + let expected_gas_cost = TX_GAS_COST + 2 * TX_DATA_ZERO_GAS_COST + 4 * TX_DATA_NON_ZERO_GAS; let intrinsic_gas = transaction_intrinsic_gas(&tx, &header, &config).expect("Intrinsic gas"); assert_eq!(intrinsic_gas, expected_gas_cost); diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index bef51d950c1..47588dbb896 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -11,7 +11,9 @@ use ethrex_common::{ constants::{DEFAULT_OMMERS_HASH, DEFAULT_REQUESTS_HASH, GAS_PER_BLOB, MAX_RLP_BLOCK_SIZE}, types::{ AccountUpdate, BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, - ChainConfig, MempoolTransaction, Receipt, Transaction, TxType, Withdrawal, bloom_from_logs, + ChainConfig, + Fork::*, + MempoolTransaction, Receipt, Transaction, TxType, Withdrawal, bloom_from_logs, calc_excess_blob_gas, calculate_base_fee_per_blob_gas, calculate_base_fee_per_gas, compute_receipts_root, compute_transactions_root, compute_withdrawals_root, requests::{EncodedRequests, compute_requests_hash}, @@ -128,10 +130,10 @@ pub fn create_payload( .get_block_header_by_hash(args.parent)? .ok_or_else(|| ChainError::ParentNotFound)?; let chain_config = storage.get_chain_config(); - let fork = chain_config.fork(args.timestamp); + let fork = chain_config.get_fork(args.timestamp); let gas_limit = calc_gas_limit(parent_block.gas_limit, args.gas_ceil); let excess_blob_gas = chain_config - .get_fork_blob_schedule(args.timestamp) + .get_blob_schedule_for_time(args.timestamp) .map(|schedule| calc_excess_blob_gas(&parent_block, schedule, fork)); let header = BlockHeader { @@ -158,17 +160,17 @@ pub fn create_payload( args.elasticity_multiplier, ), withdrawals_root: chain_config - .is_shanghai_activated(args.timestamp) + .is_fork_activated(Shanghai, args.timestamp) .then_some(compute_withdrawals_root( args.withdrawals.as_ref().unwrap_or(&Vec::new()), )), blob_gas_used: chain_config - .is_cancun_activated(args.timestamp) + .is_fork_activated(Cancun, args.timestamp) .then_some(0), excess_blob_gas, parent_beacon_block_root: args.beacon_root, requests_hash: chain_config - .is_prague_activated(args.timestamp) + .is_fork_activated(Prague, args.timestamp) .then_some(*DEFAULT_REQUESTS_HASH), ..Default::default() }; @@ -229,7 +231,7 @@ impl PayloadBuildContext { let base_fee_per_blob_gas = calculate_base_fee_per_blob_gas( payload.header.excess_blob_gas.unwrap_or_default(), config - .get_fork_blob_schedule(payload.header.timestamp) + .get_blob_schedule_for_time(payload.header.timestamp) .map(|schedule| schedule.base_fee_update_fraction) .unwrap_or_default(), ); @@ -246,7 +248,7 @@ impl PayloadBuildContext { remaining_gas: payload.header.gas_limit, receipts: vec![], requests: config - .is_prague_activated(payload.header.timestamp) + .is_fork_activated(Prague, payload.header.timestamp) .then_some(Vec::new()), block_value: U256::zero(), base_fee_per_blob_gas, @@ -493,7 +495,7 @@ impl Blockchain { pub fn fill_transactions(&self, context: &mut PayloadBuildContext) -> Result<(), ChainError> { let chain_config = context.chain_config(); let max_blob_number_per_block = chain_config - .get_fork_blob_schedule(context.payload.header.timestamp) + .get_blob_schedule_for_time(context.payload.header.timestamp) .map(|schedule| schedule.max) .unwrap_or_default() as usize; @@ -542,7 +544,7 @@ impl Blockchain { context.payload_size + head_tx.encode_canonical_to_vec().len() as u64; if context .chain_config() - .is_osaka_activated(context.payload.header.timestamp) + .is_fork_activated(Osaka, context.payload.header.timestamp) && potential_rlp_block_size > MAX_RLP_BLOCK_SIZE { break; @@ -552,16 +554,6 @@ impl Blockchain { // TODO: maybe fetch hash too when filtering mempool so we don't have to compute it here (we can do this in the same refactor as adding timestamp) let tx_hash = head_tx.tx.hash(); - // Check whether the tx is replay-protected - if head_tx.tx.protected() && !chain_config.is_eip155_activated(context.block_number()) { - // Ignore replay protected tx & all txs from the sender - // Pull transaction from the mempool - debug!("Ignoring replay-protected transaction: {}", tx_hash); - txs.pop(); - self.remove_transaction_from_pool(&tx_hash)?; - continue; - } - // Execute tx let receipt = match self.apply_transaction(&head_tx, context) { Ok(receipt) => { @@ -609,7 +601,7 @@ impl Blockchain { let tx_hash = head.tx.hash(); let chain_config = context.chain_config(); let max_blob_number_per_block = chain_config - .get_fork_blob_schedule(context.payload.header.timestamp) + .get_blob_schedule_for_time(context.payload.header.timestamp) .map(|schedule| schedule.max) .unwrap_or_default() as usize; let Some(blobs_bundle) = self.mempool.get_blobs_bundle(tx_hash)? else { @@ -635,7 +627,7 @@ impl Blockchain { pub fn extract_requests(&self, context: &mut PayloadBuildContext) -> Result<(), EvmError> { if !context .chain_config() - .is_prague_activated(context.payload.header.timestamp) + .is_fork_activated(Prague, context.payload.header.timestamp) { return Ok(()); }; diff --git a/crates/common/types/block.rs b/crates/common/types/block.rs index e4f5eab06fa..6bc09de768b 100644 --- a/crates/common/types/block.rs +++ b/crates/common/types/block.rs @@ -732,9 +732,13 @@ fn validate_excess_blob_gas( chain_config: &ChainConfig, ) -> Result<(), InvalidBlockHeaderError> { let expected_excess_blob_gas = chain_config - .get_fork_blob_schedule(header.timestamp) + .get_blob_schedule_for_time(header.timestamp) .map(|schedule| { - calc_excess_blob_gas(parent_header, schedule, chain_config.fork(header.timestamp)) + calc_excess_blob_gas( + parent_header, + schedule, + chain_config.get_fork(header.timestamp), + ) }) .unwrap_or_default(); if header diff --git a/crates/common/types/genesis.rs b/crates/common/types/genesis.rs index 393c2302865..cb15ee6d691 100644 --- a/crates/common/types/genesis.rs +++ b/crates/common/types/genesis.rs @@ -1,3 +1,4 @@ +use self::Fork::*; use bytes::Bytes; use ethereum_types::{Address, Bloom, H256, U256}; use ethrex_crypto::keccak::keccak_hash; @@ -13,8 +14,8 @@ use std::{ use tracing::warn; use super::{ - AccountState, Block, BlockBody, BlockHeader, BlockNumber, INITIAL_BASE_FEE, - compute_receipts_root, compute_transactions_root, compute_withdrawals_root, + AccountState, Block, BlockBody, BlockHeader, INITIAL_BASE_FEE, compute_receipts_root, + compute_transactions_root, compute_withdrawals_root, }; use crate::{ constants::{DEFAULT_OMMERS_HASH, DEFAULT_REQUESTS_HASH}, @@ -283,11 +284,11 @@ lazy_static::lazy_static! { #[derive(Debug, PartialEq, Eq, PartialOrd, Default, Hash, Clone, Copy, Serialize, Deserialize)] pub enum Fork { Frontier = 0, - FrontierThawing = 1, - Homestead = 2, - DaoFork = 3, - Tangerine = 4, - SpuriousDragon = 5, + Homestead = 1, + DaoFork = 2, + EIP150 = 3, + EIP155 = 4, + EIP158 = 5, Byzantium = 6, Constantinople = 7, Petersburg = 8, @@ -310,15 +311,43 @@ pub enum Fork { BPO5 = 24, } +pub const FORKS: [Fork; 25] = [ + Frontier, + Homestead, + DaoFork, + EIP150, + EIP155, + EIP158, + Byzantium, + Constantinople, + Petersburg, + Istanbul, + MuirGlacier, + Berlin, + London, + ArrowGlacier, + GrayGlacier, + Paris, + Shanghai, + Cancun, + Prague, + Osaka, + BPO1, + BPO2, + BPO3, + BPO4, + BPO5, +]; + impl From 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::EIP150 => "EIP150", + Fork::EIP155 => "EIP155", + Fork::EIP158 => "EIP158", Fork::Byzantium => "Byzantium", Fork::Constantinople => "Constantinople", Fork::Petersburg => "Petersburg", @@ -343,53 +372,53 @@ impl From for &str { } impl ChainConfig { - pub fn is_bpo1_activated(&self, block_timestamp: u64) -> bool { - self.bpo1_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_bpo2_activated(&self, block_timestamp: u64) -> bool { - self.bpo2_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_bpo3_activated(&self, block_timestamp: u64) -> bool { - self.bpo3_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_bpo4_activated(&self, block_timestamp: u64) -> bool { - self.bpo4_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_bpo5_activated(&self, block_timestamp: u64) -> bool { - self.bpo5_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_osaka_activated(&self, block_timestamp: u64) -> bool { - self.osaka_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_prague_activated(&self, block_timestamp: u64) -> bool { - self.prague_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_shanghai_activated(&self, block_timestamp: u64) -> bool { - self.shanghai_time - .is_some_and(|time| time <= block_timestamp) - } - - pub fn is_cancun_activated(&self, block_timestamp: u64) -> bool { - self.cancun_time.is_some_and(|time| time <= block_timestamp) - } - - pub fn is_istanbul_activated(&self, block_number: BlockNumber) -> bool { - self.istanbul_block.is_some_and(|num| num <= block_number) + pub fn fork_activation_time_or_block(&self, fork: Fork) -> Option { + match fork { + Fork::Frontier => Some(0), + Fork::Homestead => self.homestead_block, + Fork::DaoFork => self.dao_fork_block, + Fork::EIP150 => self.eip150_block, + Fork::EIP155 => self.eip155_block, + Fork::EIP158 => self.eip158_block, + Fork::Byzantium => self.byzantium_block, + Fork::Constantinople => self.constantinople_block, + Fork::Petersburg => self.petersburg_block, + Fork::Istanbul => self.istanbul_block, + Fork::MuirGlacier => self.muir_glacier_block, + Fork::Berlin => self.berlin_block, + Fork::London => self.london_block, + Fork::ArrowGlacier => self.arrow_glacier_block, + Fork::GrayGlacier => self.gray_glacier_block, + Fork::Paris => self.merge_netsplit_block, + Fork::Shanghai => self.shanghai_time, + Fork::Cancun => self.cancun_time, + Fork::Prague => self.prague_time, + Fork::Osaka => self.osaka_time, + Fork::BPO1 => self.bpo1_time, + Fork::BPO2 => self.bpo2_time, + Fork::BPO3 => self.bpo3_time, + Fork::BPO4 => self.bpo4_time, + Fork::BPO5 => self.bpo5_time, + } } - pub fn is_london_activated(&self, block_number: BlockNumber) -> bool { - self.london_block.is_some_and(|num| num <= block_number) + pub fn get_blob_schedule_for_fork(&self, fork: Fork) -> Option { + match fork { + Fork::Cancun => Some(self.blob_schedule.cancun), + Fork::Prague => Some(self.blob_schedule.prague), + Fork::Osaka => Some(self.blob_schedule.osaka), + Fork::BPO1 => Some(self.blob_schedule.bpo1), + Fork::BPO2 => Some(self.blob_schedule.bpo2), + Fork::BPO3 => self.blob_schedule.bpo3, + Fork::BPO4 => self.blob_schedule.bpo4, + Fork::BPO5 => self.blob_schedule.bpo5, + _ => None, + } } - pub fn is_eip155_activated(&self, block_number: BlockNumber) -> bool { - self.eip155_block.is_some_and(|num| num <= block_number) + pub fn is_fork_activated(&self, fork: Fork, timestamp_or_block: u64) -> bool { + self.fork_activation_time_or_block(fork) + .is_some_and(|time_or_number| time_or_number <= timestamp_or_block) } pub fn display_config(&self) -> String { @@ -400,7 +429,6 @@ impl ChainConfig { ("Shanghai", self.shanghai_time), ("Cancun", self.cancun_time), ("Prague", self.prague_time), - ("Verkle", self.verkle_time), ("Osaka", self.osaka_time), ]; @@ -421,134 +449,39 @@ impl ChainConfig { } pub fn get_fork(&self, block_timestamp: u64) -> Fork { - if self.is_osaka_activated(block_timestamp) { - Fork::Osaka - } else if self.is_prague_activated(block_timestamp) { - Fork::Prague - } else if self.is_cancun_activated(block_timestamp) { - Fork::Cancun - } else if self.is_shanghai_activated(block_timestamp) { - Fork::Shanghai - } else { - Fork::Paris - } - } - - pub fn get_fork_blob_schedule(&self, block_timestamp: u64) -> Option { - if self.is_bpo5_activated(block_timestamp) { - Some(self.blob_schedule.bpo5.unwrap_or_default()) - } else if self.is_bpo4_activated(block_timestamp) { - Some(self.blob_schedule.bpo4.unwrap_or_default()) - } else if self.is_bpo3_activated(block_timestamp) { - Some(self.blob_schedule.bpo3.unwrap_or_default()) - } else if self.is_bpo2_activated(block_timestamp) { - Some(self.blob_schedule.bpo2) - } else if self.is_bpo1_activated(block_timestamp) { - Some(self.blob_schedule.bpo1) - } else if self.is_osaka_activated(block_timestamp) { - Some(self.blob_schedule.osaka) - } else if self.is_prague_activated(block_timestamp) { - Some(self.blob_schedule.prague) - } else if self.is_cancun_activated(block_timestamp) { - Some(self.blob_schedule.cancun) + FORKS + .into_iter() + .rfind(|fork| self.is_fork_activated(*fork, block_timestamp)) + .unwrap_or(Paris) + } + + pub fn get_blob_schedule_for_time(&self, block_timestamp: u64) -> Option { + if let Some(fork_with_current_blob_schedule) = FORKS.into_iter().rfind(|fork| { + self.get_blob_schedule_for_fork(*fork).is_some() + && self.is_fork_activated(*fork, block_timestamp) + }) { + self.get_blob_schedule_for_fork(fork_with_current_blob_schedule) } else { None } } - pub fn fork(&self, block_timestamp: u64) -> Fork { - self.get_fork(block_timestamp) - } - pub fn next_fork(&self, block_timestamp: u64) -> Option { - let next = if self.is_bpo5_activated(block_timestamp) { - None - } else if self.is_bpo4_activated(block_timestamp) && self.bpo5_time.is_some() { - Some(Fork::BPO5) - } else if self.is_bpo3_activated(block_timestamp) && self.bpo4_time.is_some() { - Some(Fork::BPO4) - } else if self.is_bpo2_activated(block_timestamp) && self.bpo3_time.is_some() { - Some(Fork::BPO3) - } else if self.is_bpo1_activated(block_timestamp) && self.bpo2_time.is_some() { - Some(Fork::BPO2) - } else if self.is_osaka_activated(block_timestamp) && self.bpo1_time.is_some() { - Some(Fork::BPO1) - } else if self.is_prague_activated(block_timestamp) && self.osaka_time.is_some() { - Some(Fork::Osaka) - } else if self.is_cancun_activated(block_timestamp) && self.prague_time.is_some() { - Some(Fork::Prague) - } else if self.is_shanghai_activated(block_timestamp) && self.cancun_time.is_some() { - Some(Fork::Cancun) + let current_fork = self.get_fork(block_timestamp); + if (current_fork as usize) < FORKS.len() { + FORKS.into_iter().find(|fork| { + *fork > current_fork && self.fork_activation_time_or_block(*fork).is_some() + }) } else { None - }; - match next { - Some(fork) if fork > self.fork(block_timestamp) => next, - _ => None, } } pub fn get_last_scheduled_fork(&self) -> Fork { - if self.bpo5_time.is_some() { - Fork::BPO5 - } else if self.bpo4_time.is_some() { - Fork::BPO4 - } else if self.bpo3_time.is_some() { - Fork::BPO3 - } else if self.bpo2_time.is_some() { - Fork::BPO2 - } else if self.bpo1_time.is_some() { - Fork::BPO1 - } else if self.osaka_time.is_some() { - Fork::Osaka - } else if self.prague_time.is_some() { - Fork::Prague - } else if self.cancun_time.is_some() { - Fork::Cancun - } else { - Fork::Paris - } - } - - pub fn get_activation_timestamp_for_fork(&self, fork: Fork) -> Option { - match fork { - Fork::Cancun => self.cancun_time, - Fork::Prague => self.prague_time, - Fork::Osaka => self.osaka_time, - Fork::BPO1 => self.bpo1_time, - Fork::BPO2 => self.bpo2_time, - Fork::BPO3 => self.bpo3_time, - Fork::BPO4 => self.bpo4_time, - Fork::BPO5 => self.bpo5_time, - Fork::Homestead => self.homestead_block, - Fork::DaoFork => self.dao_fork_block, - Fork::Byzantium => self.byzantium_block, - Fork::Constantinople => self.constantinople_block, - Fork::Petersburg => self.petersburg_block, - Fork::Istanbul => self.istanbul_block, - Fork::MuirGlacier => self.muir_glacier_block, - Fork::Berlin => self.berlin_block, - Fork::London => self.london_block, - Fork::ArrowGlacier => self.arrow_glacier_block, - Fork::GrayGlacier => self.gray_glacier_block, - Fork::Paris => self.merge_netsplit_block, - Fork::Shanghai => self.shanghai_time, - _ => None, - } - } - - pub fn get_blob_schedule_for_fork(&self, fork: Fork) -> Option { - match fork { - Fork::Cancun => Some(self.blob_schedule.cancun), - Fork::Prague => Some(self.blob_schedule.prague), - Fork::Osaka => Some(self.blob_schedule.osaka), - Fork::BPO1 => Some(self.blob_schedule.bpo1), - Fork::BPO2 => Some(self.blob_schedule.bpo2), - Fork::BPO3 => self.blob_schedule.bpo3, - Fork::BPO4 => self.blob_schedule.bpo4, - Fork::BPO5 => self.blob_schedule.bpo5, - _ => None, - } + FORKS + .into_iter() + .rfind(|fork| self.fork_activation_time_or_block(*fork).is_some()) + .unwrap_or(Paris) } pub fn gather_forks(&self, genesis_header: BlockHeader) -> (Vec, Vec) { @@ -639,23 +572,23 @@ impl Genesis { } let base_fee_per_gas = self.base_fee_per_gas.or_else(|| { self.config - .is_london_activated(0) + .is_fork_activated(London, 0) .then_some(INITIAL_BASE_FEE) }); let withdrawals_root = self .config - .is_shanghai_activated(self.timestamp) + .is_fork_activated(Shanghai, self.timestamp) .then_some(compute_withdrawals_root(&[])); let parent_beacon_block_root = self .config - .is_cancun_activated(self.timestamp) + .is_fork_activated(Cancun, self.timestamp) .then_some(H256::zero()); let requests_hash = self .config - .is_prague_activated(self.timestamp) + .is_fork_activated(Prague, self.timestamp) .then_some(self.requests_hash.unwrap_or(*DEFAULT_REQUESTS_HASH)); BlockHeader { diff --git a/crates/l2/sequencer/block_producer/payload_builder.rs b/crates/l2/sequencer/block_producer/payload_builder.rs index af3b92e6001..f9c5b10f64c 100644 --- a/crates/l2/sequencer/block_producer/payload_builder.rs +++ b/crates/l2/sequencer/block_producer/payload_builder.rs @@ -101,7 +101,6 @@ pub async fn fill_transactions( }; let mut acc_encoded_size = context.payload.encode_to_vec().len(); let fee_config_len = fee_config.to_vec().len(); - let chain_config = store.get_chain_config(); debug!("Fetching transactions from mempool"); // Fetch mempool transactions @@ -177,16 +176,6 @@ pub async fn fill_transactions( // TODO: maybe fetch hash too when filtering mempool so we don't have to compute it here (we can do this in the same refactor as adding timestamp) let tx_hash = head_tx.tx.hash(); - // Check whether the tx is replay-protected - if head_tx.tx.protected() && !chain_config.is_eip155_activated(context.block_number()) { - // Ignore replay protected tx & all txs from the sender - // Pull transaction from the mempool - debug!("Ignoring replay-protected transaction: {}", tx_hash); - txs.pop(); - blockchain.remove_transaction_from_pool(&tx_hash)?; - continue; - } - let maybe_sender_acc_info = store .get_account_info(latest_block_number, head_tx.tx.sender()) .await?; diff --git a/crates/networking/rpc/engine/blobs.rs b/crates/networking/rpc/engine/blobs.rs index 7a1c68408ac..532807f8c3d 100644 --- a/crates/networking/rpc/engine/blobs.rs +++ b/crates/networking/rpc/engine/blobs.rs @@ -1,7 +1,9 @@ use ethrex_common::{ H256, serde_utils::{self}, - types::{Blob, CELLS_PER_EXT_BLOB, Proof, blobs_bundle::kzg_commitment_to_versioned_hash}, + types::{ + Blob, CELLS_PER_EXT_BLOB, Fork::*, Proof, blobs_bundle::kzg_commitment_to_versioned_hash, + }, }; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -120,7 +122,7 @@ impl RpcHandler for BlobsV2Request { && !context .storage .get_chain_config() - .is_osaka_activated(current_block_header.timestamp) + .is_fork_activated(Osaka, current_block_header.timestamp) { // validation requested in https://github.com/ethereum/execution-apis/blob/a1d95fb555cd91efb3e0d6555e4ab556d9f5dd06/src/engine/osaka.md?plain=1#L130 return Err(RpcErr::UnsuportedFork( diff --git a/crates/networking/rpc/engine/fork_choice.rs b/crates/networking/rpc/engine/fork_choice.rs index e400d72f3d3..84ab7ea8545 100644 --- a/crates/networking/rpc/engine/fork_choice.rs +++ b/crates/networking/rpc/engine/fork_choice.rs @@ -3,7 +3,7 @@ use ethrex_blockchain::{ fork_choice::apply_fork_choice, payload::{BuildPayloadArgs, create_payload}, }; -use ethrex_common::types::{BlockHeader, ELASTICITY_MULTIPLIER}; +use ethrex_common::types::{BlockHeader, ELASTICITY_MULTIPLIER, Fork::*}; use serde_json::Value; use tracing::{info, warn}; @@ -37,7 +37,7 @@ impl RpcHandler for ForkChoiceUpdatedV1 { handle_forkchoice(&self.fork_choice_state, context.clone(), 1).await?; if let (Some(head_block), Some(attributes)) = (head_block_opt, &self.payload_attributes) { let chain_config = context.storage.get_chain_config(); - if chain_config.is_cancun_activated(attributes.timestamp) { + if chain_config.is_fork_activated(Cancun, attributes.timestamp) { return Err(RpcErr::UnsuportedFork( "forkChoiceV1 used to build Cancun payload".to_string(), )); @@ -70,11 +70,11 @@ impl RpcHandler for ForkChoiceUpdatedV2 { handle_forkchoice(&self.fork_choice_state, context.clone(), 2).await?; if let (Some(head_block), Some(attributes)) = (head_block_opt, &self.payload_attributes) { let chain_config = context.storage.get_chain_config(); - if chain_config.is_cancun_activated(attributes.timestamp) { + if chain_config.is_fork_activated(Cancun, attributes.timestamp) { return Err(RpcErr::UnsuportedFork( "forkChoiceV2 used to build Cancun payload".to_string(), )); - } else if chain_config.is_shanghai_activated(attributes.timestamp) { + } else if chain_config.is_fork_activated(Shanghai, attributes.timestamp) { validate_attributes_v2(attributes, &head_block)?; } else { // Behave as a v1 @@ -355,7 +355,7 @@ fn validate_attributes_v3( "Attribute parent_beacon_block_root is null".to_string(), )); } - if !chain_config.is_cancun_activated(attributes.timestamp) { + if !chain_config.is_fork_activated(Cancun, attributes.timestamp) { return Err(RpcErr::UnsuportedFork( "forkChoiceV3 used to build pre-Cancun payload".to_string(), )); diff --git a/crates/networking/rpc/engine/payload.rs b/crates/networking/rpc/engine/payload.rs index 596c0ff912d..652fb8adca3 100644 --- a/crates/networking/rpc/engine/payload.rs +++ b/crates/networking/rpc/engine/payload.rs @@ -2,7 +2,7 @@ use ethrex_blockchain::error::ChainError; use ethrex_blockchain::payload::PayloadBuildResult; use ethrex_common::types::payload::PayloadBundle; use ethrex_common::types::requests::{EncodedRequests, compute_requests_hash}; -use ethrex_common::types::{Block, BlockBody, BlockHash, BlockNumber, Fork}; +use ethrex_common::types::{Block, BlockBody, BlockHash, BlockNumber, Fork, Fork::*}; use ethrex_common::{H256, U256}; use ethrex_p2p::sync::SyncMode; use ethrex_rlp::error::RLPDecodeError; @@ -62,7 +62,7 @@ impl RpcHandler for NewPayloadV2Request { async fn handle(&self, context: RpcApiContext) -> Result { let chain_config = &context.storage.get_chain_config(); - if chain_config.is_shanghai_activated(self.payload.timestamp) { + if chain_config.is_fork_activated(Shanghai, self.payload.timestamp) { validate_execution_payload_v2(&self.payload)?; } else { // Behave as a v1 @@ -206,7 +206,7 @@ impl RpcHandler for NewPayloadV4Request { let chain_config = context.storage.get_chain_config(); - if !chain_config.is_prague_activated(block.header.timestamp) { + if !chain_config.is_fork_activated(Prague, block.header.timestamp) { return Err(RpcErr::UnsuportedFork(format!( "{:?}", chain_config.get_fork(block.header.timestamp) @@ -334,13 +334,13 @@ impl RpcHandler for GetPayloadV4Request { let payload_bundle = get_payload(self.payload_id, &context).await?; let chain_config = &context.storage.get_chain_config(); - if !chain_config.is_prague_activated(payload_bundle.block.header.timestamp) { + if !chain_config.is_fork_activated(Prague, payload_bundle.block.header.timestamp) { return Err(RpcErr::UnsuportedFork(format!( "{:?}", chain_config.get_fork(payload_bundle.block.header.timestamp) ))); } - if chain_config.is_osaka_activated(payload_bundle.block.header.timestamp) { + if chain_config.is_fork_activated(Osaka, payload_bundle.block.header.timestamp) { return Err(RpcErr::UnsuportedFork(format!("{:?}", Fork::Osaka))); } @@ -386,7 +386,7 @@ impl RpcHandler for GetPayloadV5Request { let payload_bundle = get_payload(self.payload_id, &context).await?; let chain_config = &context.storage.get_chain_config(); - if !chain_config.is_osaka_activated(payload_bundle.block.header.timestamp) { + if !chain_config.is_fork_activated(Osaka, payload_bundle.block.header.timestamp) { return Err(RpcErr::UnsuportedFork(format!( "{:?}", chain_config.get_fork(payload_bundle.block.header.timestamp) @@ -541,7 +541,7 @@ fn validate_execution_payload_v3(payload: &ExecutionPayload) -> Result<(), RpcEr fn validate_payload_v1_v2(block: &Block, context: &RpcApiContext) -> Result<(), RpcErr> { let chain_config = &context.storage.get_chain_config(); - if chain_config.is_cancun_activated(block.header.timestamp) { + if chain_config.is_fork_activated(Cancun, block.header.timestamp) { return Err(RpcErr::UnsuportedFork( "Cancun payload received".to_string(), )); diff --git a/crates/networking/rpc/eth/block.rs b/crates/networking/rpc/eth/block.rs index bf1c56d51d0..1f5636c2378 100644 --- a/crates/networking/rpc/eth/block.rs +++ b/crates/networking/rpc/eth/block.rs @@ -320,7 +320,7 @@ impl RpcHandler for GetBlobBaseFee { let blob_base_fee = calculate_base_fee_per_blob_gas( parent_header.excess_blob_gas.unwrap_or_default(), config - .get_fork_blob_schedule(header.timestamp) + .get_blob_schedule_for_time(header.timestamp) .map(|schedule| schedule.base_fee_update_fraction) .unwrap_or_default(), ); @@ -347,7 +347,7 @@ pub async fn get_all_block_rpc_receipts( let blob_base_fee = calculate_base_fee_per_blob_gas( header.excess_blob_gas.unwrap_or_default(), config - .get_fork_blob_schedule(header.timestamp) + .get_blob_schedule_for_time(header.timestamp) .map(|schedule| schedule.base_fee_update_fraction) .unwrap_or_default(), ); diff --git a/crates/networking/rpc/eth/client.rs b/crates/networking/rpc/eth/client.rs index 271cb4cd495..58b92c6c441 100644 --- a/crates/networking/rpc/eth/client.rs +++ b/crates/networking/rpc/eth/client.rs @@ -145,7 +145,7 @@ async fn get_config_for_fork( context: &RpcApiContext, ) -> Result { let chain_config = context.storage.get_chain_config(); - let activation_time = chain_config.get_activation_timestamp_for_fork(fork); + let activation_time = chain_config.fork_activation_time_or_block(fork); let genesis_header = context .storage .get_block_by_number(0) diff --git a/crates/networking/rpc/eth/fee_market.rs b/crates/networking/rpc/eth/fee_market.rs index 8e992ddee62..6f15c52332a 100644 --- a/crates/networking/rpc/eth/fee_market.rs +++ b/crates/networking/rpc/eth/fee_market.rs @@ -123,7 +123,7 @@ impl RpcHandler for FeeHistoryRequest { )))?; let max_blob_gas_per_block = config - .get_fork_blob_schedule(header.timestamp) + .get_blob_schedule_for_time(header.timestamp) .map(|schedule| schedule.max * GAS_PER_BLOB); let blob_gas_used_r = match (header.blob_gas_used, max_blob_gas_per_block) { (Some(blob_gas_used), Some(max_blob_gas)) => { @@ -133,7 +133,7 @@ impl RpcHandler for FeeHistoryRequest { }; let blob_schedule = config - .get_fork_blob_schedule(header.timestamp) + .get_blob_schedule_for_time(header.timestamp) .unwrap_or_default(); let fork = config.get_fork(header.timestamp); diff --git a/crates/networking/rpc/eth/transaction.rs b/crates/networking/rpc/eth/transaction.rs index 87ea64fb372..df8e52a3c0c 100644 --- a/crates/networking/rpc/eth/transaction.rs +++ b/crates/networking/rpc/eth/transaction.rs @@ -447,7 +447,7 @@ impl RpcHandler for EstimateGasRequest { _ => return Ok(Value::Null), }; - let current_fork = chain_config.fork(block_header.timestamp); + let current_fork = chain_config.get_fork(block_header.timestamp); let transaction = match self.transaction.nonce { Some(_nonce) => self.transaction.clone(), diff --git a/crates/vm/backends/levm/mod.rs b/crates/vm/backends/levm/mod.rs index 17adae8f0c1..d71b3101600 100644 --- a/crates/vm/backends/levm/mod.rs +++ b/crates/vm/backends/levm/mod.rs @@ -470,7 +470,7 @@ impl LEVM { ) -> Result<(), EvmError> { let chain_config = db.store.get_chain_config()?; let block_header = &block.header; - let fork = chain_config.fork(block_header.timestamp); + let fork = chain_config.get_fork(block_header.timestamp); // TODO: I don't like deciding the behavior based on the VMType here. if let VMType::L2(_) = vm_type { @@ -582,7 +582,7 @@ pub fn extract_all_requests_levm( } let chain_config = db.store.get_chain_config()?; - let fork = chain_config.fork(header.timestamp); + let fork = chain_config.get_fork(header.timestamp); if fork < Fork::Prague { return Ok(Default::default()); diff --git a/crates/vm/backends/mod.rs b/crates/vm/backends/mod.rs index 640be048178..47c3a5cebf8 100644 --- a/crates/vm/backends/mod.rs +++ b/crates/vm/backends/mod.rs @@ -120,7 +120,7 @@ impl Evm { /// This function is used to run/apply all the system contracts to the state. pub fn apply_system_calls(&mut self, block_header: &BlockHeader) -> Result<(), EvmError> { let chain_config = self.db.store.get_chain_config()?; - let fork = chain_config.fork(block_header.timestamp); + let fork = chain_config.get_fork(block_header.timestamp); if block_header.parent_beacon_block_root.is_some() && fork >= Fork::Cancun { LEVM::beacon_root_contract_call(block_header, &mut self.db, self.vm_type)?; diff --git a/crates/vm/levm/src/environment.rs b/crates/vm/levm/src/environment.rs index 4ce90d0f19a..8394fedc468 100644 --- a/crates/vm/levm/src/environment.rs +++ b/crates/vm/levm/src/environment.rs @@ -64,10 +64,10 @@ impl EVMConfig { } pub fn new_from_chain_config(chain_config: &ChainConfig, block_header: &BlockHeader) -> Self { - let fork = chain_config.fork(block_header.timestamp); + let fork = chain_config.get_fork(block_header.timestamp); let blob_schedule = chain_config - .get_fork_blob_schedule(block_header.timestamp) + .get_blob_schedule_for_time(block_header.timestamp) .unwrap_or_else(|| EVMConfig::canonical_values(fork)); EVMConfig::new(fork, blob_schedule) diff --git a/tooling/ef_tests/state/deserialize.rs b/tooling/ef_tests/state/deserialize.rs index f4772d7fc90..ef8e1f5748b 100644 --- a/tooling/ef_tests/state/deserialize.rs +++ b/tooling/ef_tests/state/deserialize.rs @@ -314,8 +314,10 @@ where "Prague" => Fork::Prague, "Osaka" => Fork::Osaka, "Byzantium" => Fork::Byzantium, - "EIP158" => Fork::SpuriousDragon, - "EIP150" => Fork::Tangerine, + "EIP158" => Fork::EIP158, + "EIP150" => Fork::EIP150, + "EIP155" => Fork::EIP155, + other => { return Err(serde::de::Error::custom(format!( "Unknown fork name: {other}", diff --git a/tooling/ef_tests/state/runner/revm_runner.rs b/tooling/ef_tests/state/runner/revm_runner.rs index c04d74c19aa..d1805d20ccf 100644 --- a/tooling/ef_tests/state/runner/revm_runner.rs +++ b/tooling/ef_tests/state/runner/revm_runner.rs @@ -676,11 +676,11 @@ pub async fn _ensure_post_state_revm( 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::EIP150 => SpecId::TANGERINE, + Fork::EIP155 => SpecId::SPURIOUS_DRAGON, + Fork::EIP158 => SpecId::SPURIOUS_DRAGON, Fork::Byzantium => SpecId::BYZANTIUM, Fork::Constantinople => SpecId::CONSTANTINOPLE, Fork::Petersburg => SpecId::PETERSBURG, diff --git a/tooling/ef_tests/state_v2/src/modules/deserialize.rs b/tooling/ef_tests/state_v2/src/modules/deserialize.rs index d8d61457577..018569722ec 100644 --- a/tooling/ef_tests/state_v2/src/modules/deserialize.rs +++ b/tooling/ef_tests/state_v2/src/modules/deserialize.rs @@ -151,8 +151,8 @@ where "Cancun" => Fork::Cancun, "Prague" => Fork::Prague, "Byzantium" => Fork::Byzantium, - "EIP158" => Fork::SpuriousDragon, - "EIP150" => Fork::Tangerine, + "EIP158" => Fork::EIP158, + "EIP150" => Fork::EIP150, other => { return Err(serde::de::Error::custom(format!( "Unknown fork name: {other}",