diff --git a/bin/reth/src/test_eth_chain/runner.rs b/bin/reth/src/test_eth_chain/runner.rs index 48fda304d9f2..8bc4c2cc47f0 100644 --- a/bin/reth/src/test_eth_chain/runner.rs +++ b/bin/reth/src/test_eth_chain/runner.rs @@ -10,8 +10,8 @@ use reth_db::{ Error as DbError, }; use reth_primitives::{ - keccak256, Account as RethAccount, Address, ChainSpec, JsonU256, SealedBlock, SealedHeader, - StorageEntry, H256, U256, + keccak256, Account as RethAccount, Address, ChainSpec, Hardfork, JsonU256, SealedBlock, + SealedHeader, StorageEntry, H256, U256, }; use reth_rlp::Decodable; use reth_stages::{stages::execution::ExecutionStage, ExecInput, Stage, StageId, Transaction}; @@ -20,7 +20,7 @@ use std::{ ffi::OsStr, path::{Path, PathBuf}, }; -use tracing::{debug, trace}; +use tracing::{debug, trace, warn}; /// The outcome of a test. #[derive(Debug)] @@ -125,7 +125,7 @@ pub async fn run_test(path: PathBuf) -> eyre::Result { let chain_spec: ChainSpec = suite.network.into(); // if paris aka merge is not activated we dont have block rewards; - let has_block_reward = chain_spec.paris_status().block_number().is_some(); + let has_block_reward = chain_spec.fork_block(Hardfork::Paris).is_some(); // Create db and acquire transaction let db = create_test_rw_db(); @@ -198,8 +198,9 @@ pub async fn run_test(path: PathBuf) -> eyre::Result { { let mut transaction = Transaction::new(db.as_ref())?; - // ignore error - let _ = stage.execute(&mut transaction, input).await; + if let Err(err) = stage.execute(&mut transaction, input).await { + warn!("{:#}", err); + } transaction.commit()?; } diff --git a/crates/consensus/src/consensus.rs b/crates/consensus/src/consensus.rs index e57d027b2194..2c1627791109 100644 --- a/crates/consensus/src/consensus.rs +++ b/crates/consensus/src/consensus.rs @@ -1,7 +1,9 @@ //! Consensus for ethereum network use crate::verification; use reth_interfaces::consensus::{Consensus, Error, ForkchoiceState}; -use reth_primitives::{BlockNumber, ChainSpec, SealedBlock, SealedHeader, H256}; +use reth_primitives::{ + BlockNumber, ChainSpec, ForkDiscriminant, Hardfork, SealedBlock, SealedHeader, H256, +}; use tokio::sync::{watch, watch::error::SendError}; /// Ethereum beacon consensus @@ -46,7 +48,13 @@ impl Consensus for BeaconConsensus { verification::validate_header_standalone(header, &self.chain_spec)?; verification::validate_header_regarding_parent(parent, header, &self.chain_spec)?; - if Some(header.number) < self.chain_spec.paris_status().block_number() { + if !self.chain_spec.fork_active( + Hardfork::Paris, + ForkDiscriminant::ttd( + self.chain_spec.terminal_total_difficulty().unwrap_or_default(), + Some(header.number), + ), + ) { // TODO Consensus checks for old blocks: // * difficulty, mix_hash & nonce aka PoW stuff // low priority as syncing is done in reverse order @@ -59,6 +67,12 @@ impl Consensus for BeaconConsensus { } fn has_block_reward(&self, block_num: BlockNumber) -> bool { - Some(block_num) < self.chain_spec.paris_status().block_number() + !self.chain_spec.fork_active( + Hardfork::Paris, + ForkDiscriminant::ttd( + self.chain_spec.terminal_total_difficulty().unwrap_or_default(), + Some(block_num), + ), + ) } } diff --git a/crates/consensus/src/engine/mod.rs b/crates/consensus/src/engine/mod.rs index 376595443ba2..4392d9e3792d 100644 --- a/crates/consensus/src/engine/mod.rs +++ b/crates/consensus/src/engine/mod.rs @@ -188,7 +188,7 @@ impl ConsensusEngine }; if let Some(parent_td) = self.client.header_td(&block.parent_hash)? { - if Some(parent_td) <= self.chain_spec.paris_status().terminal_total_difficulty() { + if Some(parent_td) <= self.chain_spec.terminal_total_difficulty() { return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { validation_error: EngineApiError::PayloadPreMerge.to_string(), })) @@ -271,7 +271,6 @@ impl ConsensusEngine let merge_terminal_td = self .chain_spec - .paris_status() .terminal_total_difficulty() .ok_or(EngineApiError::UnknownMergeTerminalTotalDifficulty)?; @@ -516,8 +515,7 @@ mod tests { let (result_tx, result_rx) = oneshot::channel(); let parent = transform_block(random_block(100, None, None, Some(0)), |mut b| { - b.header.difficulty = - chain_spec.paris_status().terminal_total_difficulty().unwrap(); + b.header.difficulty = chain_spec.terminal_total_difficulty().unwrap(); b }); let block = random_block(101, Some(parent.hash()), None, Some(0)); @@ -555,7 +553,7 @@ mod tests { let parent = transform_block(random_block(100, None, None, Some(0)), |mut b| { b.header.timestamp = parent_timestamp; b.header.difficulty = - chain_spec.paris_status().terminal_total_difficulty().unwrap() + U256::from(1); + chain_spec.terminal_total_difficulty().unwrap() + U256::from(1); b }); let block = @@ -633,10 +631,7 @@ mod tests { tokio::spawn(engine); let transition_config = TransitionConfiguration { - terminal_total_difficulty: chain_spec - .paris_status() - .terminal_total_difficulty() - .unwrap() + + terminal_total_difficulty: chain_spec.terminal_total_difficulty().unwrap() + U256::from(1), ..Default::default() }; @@ -651,7 +646,7 @@ mod tests { assert_matches!( result_rx.await, Ok(Err(EngineApiError::TerminalTD { execution, consensus })) - if execution == chain_spec.paris_status().terminal_total_difficulty().unwrap() + if execution == chain_spec.terminal_total_difficulty().unwrap() && consensus == U256::from(transition_config.terminal_total_difficulty) ); } @@ -675,10 +670,7 @@ mod tests { let execution_terminal_block = random_block(terminal_block_number, None, None, None); let transition_config = TransitionConfiguration { - terminal_total_difficulty: chain_spec - .paris_status() - .terminal_total_difficulty() - .unwrap(), + terminal_total_difficulty: chain_spec.terminal_total_difficulty().unwrap(), terminal_block_hash: consensus_terminal_block.hash(), terminal_block_number: terminal_block_number.into(), }; @@ -737,10 +729,7 @@ mod tests { let terminal_block = random_block(terminal_block_number, None, None, None); let transition_config = TransitionConfiguration { - terminal_total_difficulty: chain_spec - .paris_status() - .terminal_total_difficulty() - .unwrap(), + terminal_total_difficulty: chain_spec.terminal_total_difficulty().unwrap(), terminal_block_hash: terminal_block.hash(), terminal_block_number: terminal_block_number.into(), }; diff --git a/crates/consensus/src/verification.rs b/crates/consensus/src/verification.rs index 753b7e6c3c15..85ddbf5b2e1d 100644 --- a/crates/consensus/src/verification.rs +++ b/crates/consensus/src/verification.rs @@ -1,8 +1,9 @@ //! ALl functions for verification of block use reth_interfaces::{consensus::Error, Result as RethResult}; use reth_primitives::{ - BlockNumber, ChainSpec, Hardfork, Header, SealedBlock, SealedHeader, Transaction, - TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy, EMPTY_OMMER_ROOT, U256, + BlockNumber, ChainSpec, ForkDiscriminant, Hardfork, Header, SealedBlock, SealedHeader, + Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy, EMPTY_OMMER_ROOT, + U256, }; use reth_provider::{AccountProvider, HeaderProvider}; use std::{ @@ -39,14 +40,21 @@ pub fn validate_header_standalone( } // Check if base fee is set. - if chain_spec.fork_active(Hardfork::London, header.number) && header.base_fee_per_gas.is_none() + if chain_spec.fork_active(Hardfork::London, header.number.into()) && + header.base_fee_per_gas.is_none() { return Err(Error::BaseFeeMissing) } // EIP-3675: Upgrade consensus to Proof-of-Stake: // https://eips.ethereum.org/EIPS/eip-3675#replacing-difficulty-with-0 - if Some(header.number) >= chain_spec.paris_status().block_number() { + if chain_spec.fork_active( + Hardfork::Paris, + ForkDiscriminant::ttd( + chain_spec.terminal_total_difficulty().unwrap_or_default(), + Some(header.number), + ), + ) { if header.difficulty != U256::ZERO { return Err(Error::TheMergeDifficultyIsNotZero) } @@ -78,7 +86,7 @@ pub fn validate_transaction_regarding_header( let chain_id = match transaction { Transaction::Legacy(TxLegacy { chain_id, .. }) => { // EIP-155: Simple replay attack protection: https://eips.ethereum.org/EIPS/eip-155 - if chain_spec.fork_active(Hardfork::SpuriousDragon, at_block_number) && + if chain_spec.fork_active(Hardfork::SpuriousDragon, at_block_number.into()) && chain_id.is_some() { return Err(Error::TransactionOldLegacyChainId) @@ -87,7 +95,7 @@ pub fn validate_transaction_regarding_header( } Transaction::Eip2930(TxEip2930 { chain_id, .. }) => { // EIP-2930: Optional access lists: https://eips.ethereum.org/EIPS/eip-2930 (New transaction type) - if !chain_spec.fork_active(Hardfork::Berlin, at_block_number) { + if !chain_spec.fork_active(Hardfork::Berlin, at_block_number.into()) { return Err(Error::TransactionEip2930Disabled) } Some(*chain_id) @@ -99,7 +107,7 @@ pub fn validate_transaction_regarding_header( .. }) => { // EIP-1559: Fee market change for ETH 1.0 chain https://eips.ethereum.org/EIPS/eip-1559 - if !chain_spec.fork_active(Hardfork::Berlin, at_block_number) { + if !chain_spec.fork_active(Hardfork::Berlin, at_block_number.into()) { return Err(Error::TransactionEip1559Disabled) } @@ -259,7 +267,13 @@ pub fn validate_header_regarding_parent( } // difficulty check is done by consensus. - if chain_spec.paris_status().block_number() > Some(child.number) { + if !chain_spec.fork_active( + Hardfork::Paris, + ForkDiscriminant::ttd( + chain_spec.terminal_total_difficulty().unwrap_or_default(), + Some(child.number), + ), + ) { // TODO how this needs to be checked? As ice age did increment it by some formula } @@ -287,7 +301,7 @@ pub fn validate_header_regarding_parent( } // EIP-1559 check base fee - if chain_spec.fork_active(Hardfork::London, child.number) { + if chain_spec.fork_active(Hardfork::London, child.number.into()) { let base_fee = child.base_fee_per_gas.ok_or(Error::BaseFeeMissing)?; let expected_base_fee = if chain_spec.fork_block(Hardfork::London) == Some(child.number) { diff --git a/crates/executor/src/config.rs b/crates/executor/src/config.rs index 3f161a30610d..e8b1434bd839 100644 --- a/crates/executor/src/config.rs +++ b/crates/executor/src/config.rs @@ -1,6 +1,6 @@ //! Reth block execution/validation configuration and constants -use reth_primitives::{BlockNumber, ChainSpec, Hardfork}; +use reth_primitives::{ChainSpec, ForkDiscriminant, Hardfork}; /// Two ethereum worth of wei pub const WEI_2ETH: u128 = 2000000000000000000u128; @@ -10,19 +10,19 @@ pub const WEI_3ETH: u128 = 3000000000000000000u128; pub const WEI_5ETH: u128 = 5000000000000000000u128; /// return revm_spec from spec configuration. -pub fn revm_spec(chain_spec: &ChainSpec, for_block: BlockNumber) -> revm::SpecId { - match for_block { - b if chain_spec.fork_active(Hardfork::Shanghai, b) => revm::MERGE_EOF, - b if Some(b) >= chain_spec.paris_status().block_number() => revm::MERGE, - b if chain_spec.fork_active(Hardfork::London, b) => revm::LONDON, - b if chain_spec.fork_active(Hardfork::Berlin, b) => revm::BERLIN, - b if chain_spec.fork_active(Hardfork::Istanbul, b) => revm::ISTANBUL, - b if chain_spec.fork_active(Hardfork::Petersburg, b) => revm::PETERSBURG, - b if chain_spec.fork_active(Hardfork::Byzantium, b) => revm::BYZANTIUM, - b if chain_spec.fork_active(Hardfork::SpuriousDragon, b) => revm::SPURIOUS_DRAGON, - b if chain_spec.fork_active(Hardfork::Tangerine, b) => revm::TANGERINE, - b if chain_spec.fork_active(Hardfork::Homestead, b) => revm::HOMESTEAD, - b if chain_spec.fork_active(Hardfork::Frontier, b) => revm::FRONTIER, +pub fn revm_spec(chain_spec: &ChainSpec, discriminant: ForkDiscriminant) -> revm::SpecId { + match discriminant { + d if chain_spec.fork_active(Hardfork::Shanghai, d) => revm::MERGE_EOF, + d if chain_spec.fork_active(Hardfork::Paris, d) => revm::MERGE, + d if chain_spec.fork_active(Hardfork::London, d) => revm::LONDON, + d if chain_spec.fork_active(Hardfork::Berlin, d) => revm::BERLIN, + d if chain_spec.fork_active(Hardfork::Istanbul, d) => revm::ISTANBUL, + d if chain_spec.fork_active(Hardfork::Petersburg, d) => revm::PETERSBURG, + d if chain_spec.fork_active(Hardfork::Byzantium, d) => revm::BYZANTIUM, + d if chain_spec.fork_active(Hardfork::SpuriousDragon, d) => revm::SPURIOUS_DRAGON, + d if chain_spec.fork_active(Hardfork::Tangerine, d) => revm::TANGERINE, + d if chain_spec.fork_active(Hardfork::Homestead, d) => revm::HOMESTEAD, + d if chain_spec.fork_active(Hardfork::Frontier, d) => revm::FRONTIER, _ => panic!("wrong configuration"), } } @@ -30,62 +30,106 @@ pub fn revm_spec(chain_spec: &ChainSpec, for_block: BlockNumber) -> revm::SpecId #[cfg(test)] mod tests { use crate::config::revm_spec; - use reth_primitives::{ChainSpecBuilder, MAINNET}; + use reth_primitives::{ChainSpecBuilder, ForkDiscriminant, MAINNET, U256}; #[test] fn test_to_revm_spec() { + let discriminant = ForkDiscriminant::new(1, U256::from(1), 1); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().paris_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().shangai_activated().build(), discriminant), + revm::MERGE_EOF + ); + assert_eq!( + revm_spec(&ChainSpecBuilder::mainnet().paris_activated().build(), discriminant), revm::MERGE ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().london_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().london_activated().build(), discriminant), revm::LONDON ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().berlin_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().berlin_activated().build(), discriminant), revm::BERLIN ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().istanbul_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().istanbul_activated().build(), discriminant), revm::ISTANBUL ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().petersburg_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().petersburg_activated().build(), discriminant), revm::PETERSBURG ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().byzantium_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().byzantium_activated().build(), discriminant), revm::BYZANTIUM ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().spurious_dragon_activated().build(), 1), + revm_spec( + &ChainSpecBuilder::mainnet().spurious_dragon_activated().build(), + discriminant + ), revm::SPURIOUS_DRAGON ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().tangerine_whistle_activated().build(), 1), + revm_spec( + &ChainSpecBuilder::mainnet().tangerine_whistle_activated().build(), + discriminant + ), revm::TANGERINE ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().homestead_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().homestead_activated().build(), discriminant), revm::HOMESTEAD ); assert_eq!( - revm_spec(&ChainSpecBuilder::mainnet().frontier_activated().build(), 1), + revm_spec(&ChainSpecBuilder::mainnet().frontier_activated().build(), discriminant), revm::FRONTIER ); } #[test] fn test_eth_spec() { - assert_eq!(revm_spec(&MAINNET, 15537394 + 10), revm::MERGE); - assert_eq!(revm_spec(&MAINNET, 15537394 - 10), revm::LONDON); - assert_eq!(revm_spec(&MAINNET, 12244000 + 10), revm::BERLIN); - assert_eq!(revm_spec(&MAINNET, 12244000 - 10), revm::ISTANBUL); - assert_eq!(revm_spec(&MAINNET, 7280000 + 10), revm::PETERSBURG); - assert_eq!(revm_spec(&MAINNET, 7280000 - 10), revm::BYZANTIUM); - assert_eq!(revm_spec(&MAINNET, 2675000 + 10), revm::SPURIOUS_DRAGON); - assert_eq!(revm_spec(&MAINNET, 2675000 - 10), revm::TANGERINE); - assert_eq!(revm_spec(&MAINNET, 1150000 + 10), revm::HOMESTEAD); - assert_eq!(revm_spec(&MAINNET, 1150000 - 10), revm::FRONTIER); + let post_merge_td = MAINNET.terminal_total_difficulty().unwrap(); + let pre_merge_td = post_merge_td.saturating_sub(U256::from(10)); + + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(15537394 + 10, post_merge_td, 1674477448)), + revm::MERGE + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(15537394 - 10, pre_merge_td, 1674477448)), + revm::LONDON + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(12244000 + 10, pre_merge_td, 1674477448)), + revm::BERLIN + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(12244000 - 10, pre_merge_td, 1674477448)), + revm::ISTANBUL + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(7280000 + 10, pre_merge_td, 1674477448)), + revm::PETERSBURG + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(7280000 - 10, pre_merge_td, 1674477448)), + revm::BYZANTIUM + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(2675000 + 10, pre_merge_td, 1674477448)), + revm::SPURIOUS_DRAGON + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(2675000 - 10, pre_merge_td, 1674477448)), + revm::TANGERINE + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(1150000 + 10, pre_merge_td, 1674477448)), + revm::HOMESTEAD + ); + assert_eq!( + revm_spec(&MAINNET, ForkDiscriminant::new(1150000 - 10, pre_merge_td, 1674477448)), + revm::FRONTIER + ); } } diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index 48fd8902ecbc..bbb836266e54 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -302,7 +302,7 @@ pub fn execute( let mut evm = EVM::new(); evm.database(db); - let spec_id = revm_spec(chain_spec, header.number); + let spec_id = revm_spec(chain_spec, header.into()); evm.env.cfg.chain_id = U256::from(chain_spec.chain().id()); evm.env.cfg.spec_id = spec_id; evm.env.cfg.perf_all_precompiles_have_balance = false; @@ -451,10 +451,10 @@ pub fn block_reward_changeset( // amount. We raise the block’s beneficiary account by Rblock; for each ommer, we raise the // block’s beneficiary by an additional 1/32 of the block reward and the beneficiary of the // ommer gets rewarded depending on the blocknumber. Formally we define the function Ω: - match header.number { - n if Some(n) >= chain_spec.paris_status().block_number() => None, - n if Some(n) >= chain_spec.fork_block(Hardfork::Petersburg) => Some(WEI_2ETH), - n if Some(n) >= chain_spec.fork_block(Hardfork::Byzantium) => Some(WEI_3ETH), + match header.into() { + d if chain_spec.fork_active(Hardfork::Paris, d) => None, + d if chain_spec.fork_active(Hardfork::Petersburg, d) => Some(WEI_2ETH), + d if chain_spec.fork_active(Hardfork::Byzantium, d) => Some(WEI_3ETH), _ => Some(WEI_5ETH), } .map(|reward| -> Result<_, _> { @@ -538,8 +538,8 @@ mod tests { transaction::DbTx, }; use reth_primitives::{ - hex_literal::hex, keccak256, Account, Address, Bytes, ChainSpecBuilder, SealedBlock, - StorageKey, H160, H256, MAINNET, U256, + hex_literal::hex, keccak256, Account, Address, Bytes, ChainSpecBuilder, ForkKind, + SealedBlock, StorageKey, H160, H256, MAINNET, U256, }; use reth_provider::{AccountProvider, BlockHashProvider, StateProvider}; use reth_rlp::Decodable; @@ -776,7 +776,7 @@ mod tests { let chain_spec = ChainSpecBuilder::from(&*MAINNET) .homestead_activated() - .with_fork(Hardfork::Dao, 1) + .with_fork(Hardfork::Dao, ForkKind::Block(1)) .build(); let mut db = SubState::new(State::new(db)); diff --git a/crates/net/eth-wire/src/builder.rs b/crates/net/eth-wire/src/builder.rs index 996ad27dbb30..e434fff49ee6 100644 --- a/crates/net/eth-wire/src/builder.rs +++ b/crates/net/eth-wire/src/builder.rs @@ -21,7 +21,7 @@ use reth_primitives::{Chain, ForkId, PeerId, H256, U256}; /// .total_difficulty(U256::from(100)) /// .blockhash(H256::from(MAINNET_GENESIS)) /// .genesis(H256::from(MAINNET_GENESIS)) -/// .forkid(Hardfork::Latest.fork_id(&MAINNET).unwrap()) +/// .forkid(Hardfork::London.fork_id(&MAINNET).unwrap()) /// .build(); /// /// assert_eq!( @@ -32,7 +32,7 @@ use reth_primitives::{Chain, ForkId, PeerId, H256, U256}; /// total_difficulty: U256::from(100), /// blockhash: H256::from(MAINNET_GENESIS), /// genesis: H256::from(MAINNET_GENESIS), -/// forkid: Hardfork::Latest.fork_id(&MAINNET).unwrap(), +/// forkid: Hardfork::London.fork_id(&MAINNET).unwrap(), /// } /// ); /// ``` diff --git a/crates/net/eth-wire/src/types/broadcast.rs b/crates/net/eth-wire/src/types/broadcast.rs index 6c1a07b66c48..8d095d433183 100644 --- a/crates/net/eth-wire/src/types/broadcast.rs +++ b/crates/net/eth-wire/src/types/broadcast.rs @@ -1,6 +1,6 @@ //! Types for broadcasting new data. use reth_codecs::derive_arbitrary; -use reth_primitives::{Header, TransactionSigned, H256, U128}; +use reth_primitives::{BlockNumber, Header, TransactionSigned, H256, U128}; use reth_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -47,7 +47,7 @@ pub struct BlockHashNumber { /// The block hash pub hash: H256, /// The block number - pub number: u64, + pub number: BlockNumber, } impl From> for NewBlockHashes { diff --git a/crates/net/eth-wire/src/types/status.rs b/crates/net/eth-wire/src/types/status.rs index 43b27f62def3..b07ee8cfdcca 100644 --- a/crates/net/eth-wire/src/types/status.rs +++ b/crates/net/eth-wire/src/types/status.rs @@ -50,10 +50,8 @@ impl From for Status { let mut chainspec = ChainSpec::from(genesis); let mut header = Header::from(chainspec.genesis().clone()); - let hardforks = chainspec.hardforks(); - // set initial base fee depending on eip-1559 - if Some(&0u64) == hardforks.get(&Hardfork::London) { + if Some(0u64) == chainspec.fork_block(Hardfork::London) { header.base_fee_per_gas = Some(EIP1559_INITIAL_BASE_FEE); } @@ -64,7 +62,7 @@ impl From for Status { chainspec.genesis_hash = sealed_header.hash(); // we need to calculate the fork id AFTER re-setting the genesis hash - let forkid = chainspec.fork_id(0); + let forkid = chainspec.fork_id(0.into()); Status { version: EthVersion::Eth67 as u8, @@ -97,7 +95,7 @@ impl Status { .chain(spec.chain) .genesis(spec.genesis_hash()) .blockhash(head.hash) - .forkid(spec.fork_id(head.number)) + .forkid(spec.fork_id(head.number.into())) } } diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index 8e4540ef541b..069867a804b1 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -297,7 +297,7 @@ impl NetworkConfigBuilder { let status = Status::spec_builder(&chain_spec, &head).build(); // set a fork filter based on the chain spec and head - let fork_filter = chain_spec.fork_filter(head.number); + let fork_filter = chain_spec.fork_filter(head.number.into()); // If default DNS config is used then we add the known dns network to bootstrap from if let Some(dns_networks) = diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index 155e278597c1..12e65e052bc1 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -7,7 +7,7 @@ use std::{fmt, str::FromStr}; // The chain spec module. mod spec; -pub use spec::{ChainSpec, ChainSpecBuilder, ParisStatus, GOERLI, MAINNET, SEPOLIA}; +pub use spec::{ChainSpec, ChainSpecBuilder, GOERLI, MAINNET, SEPOLIA}; // The chain info module. mod info; diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index ce1745466138..43086bcc26e7 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -1,6 +1,6 @@ use crate::{ - BlockNumber, Chain, ForkFilter, ForkHash, ForkId, Genesis, GenesisAccount, Hardfork, Header, - H160, H256, U256, + BlockNumber, Chain, ForkDiscriminant, ForkFilter, ForkHash, ForkId, ForkKind, Genesis, + GenesisAccount, Hardfork, Header, H160, H256, U256, }; use ethers_core::utils::Genesis as EthersGenesis; use hex_literal::hex; @@ -15,25 +15,24 @@ pub static MAINNET: Lazy = Lazy::new(|| ChainSpec { .expect("Can't deserialize Mainnet genesis json"), genesis_hash: H256(hex!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")), hardforks: BTreeMap::from([ - (Hardfork::Frontier, 0), - (Hardfork::Homestead, 1150000), - (Hardfork::Dao, 1920000), - (Hardfork::Tangerine, 2463000), - (Hardfork::SpuriousDragon, 2675000), - (Hardfork::Byzantium, 4370000), - (Hardfork::Constantinople, 7280000), - (Hardfork::Petersburg, 7280000), - (Hardfork::Istanbul, 9069000), - (Hardfork::Muirglacier, 9200000), - (Hardfork::Berlin, 12244000), - (Hardfork::London, 12965000), - (Hardfork::ArrowGlacier, 13773000), - (Hardfork::GrayGlacier, 15050000), - (Hardfork::Latest, 15050000), + (Hardfork::Frontier, ForkKind::Block(0)), + (Hardfork::Homestead, ForkKind::Block(1150000)), + (Hardfork::Dao, ForkKind::Block(1920000)), + (Hardfork::Tangerine, ForkKind::Block(2463000)), + (Hardfork::SpuriousDragon, ForkKind::Block(2675000)), + (Hardfork::Byzantium, ForkKind::Block(4370000)), + (Hardfork::Constantinople, ForkKind::Block(7280000)), + (Hardfork::Petersburg, ForkKind::Block(7280000)), + (Hardfork::Istanbul, ForkKind::Block(9069000)), + (Hardfork::Muirglacier, ForkKind::Block(9200000)), + (Hardfork::Berlin, ForkKind::Block(12244000)), + (Hardfork::London, ForkKind::Block(12965000)), + (Hardfork::ArrowGlacier, ForkKind::Block(13773000)), + (Hardfork::GrayGlacier, ForkKind::Block(15050000)), + (Hardfork::Paris, ForkKind::TTD(Some(15537394))), ]), dao_fork_support: true, - paris_block: Some(15537394), - paris_ttd: Some(U256::from(58750000000000000000000_u128)), + paris_ttd: Some(U256::from(58_750_000_000_000_000_000_000_u128)), }); /// The Goerli spec @@ -43,14 +42,14 @@ pub static GOERLI: Lazy = Lazy::new(|| ChainSpec { .expect("Can't deserialize Goerli genesis json"), genesis_hash: H256(hex!("bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")), hardforks: BTreeMap::from([ - (Hardfork::Frontier, 0), - (Hardfork::Istanbul, 1561651), - (Hardfork::Berlin, 4460644), - (Hardfork::London, 5062605), + (Hardfork::Frontier, ForkKind::Block(0)), + (Hardfork::Istanbul, ForkKind::Block(1561651)), + (Hardfork::Berlin, ForkKind::Block(4460644)), + (Hardfork::London, ForkKind::Block(5062605)), + (Hardfork::Paris, ForkKind::TTD(Some(7382818))), ]), dao_fork_support: true, - paris_block: Some(7382818), - paris_ttd: Some(U256::from(10790000)), + paris_ttd: Some(U256::from(10_790_000)), }); /// The Sepolia spec @@ -60,23 +59,22 @@ pub static SEPOLIA: Lazy = Lazy::new(|| ChainSpec { .expect("Can't deserialize Sepolia genesis json"), genesis_hash: H256(hex!("25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")), hardforks: BTreeMap::from([ - (Hardfork::Frontier, 0), - (Hardfork::Homestead, 0), - (Hardfork::Dao, 0), - (Hardfork::Tangerine, 0), - (Hardfork::SpuriousDragon, 0), - (Hardfork::Byzantium, 0), - (Hardfork::Constantinople, 0), - (Hardfork::Petersburg, 0), - (Hardfork::Istanbul, 0), - (Hardfork::Muirglacier, 0), - (Hardfork::Berlin, 0), - (Hardfork::London, 0), - (Hardfork::MergeNetsplit, 1735371), + (Hardfork::Frontier, ForkKind::Block(0)), + (Hardfork::Homestead, ForkKind::Block(0)), + (Hardfork::Dao, ForkKind::Block(0)), + (Hardfork::Tangerine, ForkKind::Block(0)), + (Hardfork::SpuriousDragon, ForkKind::Block(0)), + (Hardfork::Byzantium, ForkKind::Block(0)), + (Hardfork::Constantinople, ForkKind::Block(0)), + (Hardfork::Petersburg, ForkKind::Block(0)), + (Hardfork::Istanbul, ForkKind::Block(0)), + (Hardfork::Muirglacier, ForkKind::Block(0)), + (Hardfork::Berlin, ForkKind::Block(0)), + (Hardfork::London, ForkKind::Block(0)), + (Hardfork::Paris, ForkKind::Block(1735371)), ]), dao_fork_support: true, - paris_block: Some(1450408), - paris_ttd: Some(U256::from(17000000000000000_u64)), + paris_ttd: Some(U256::from(17_000_000_000_000_000_u64)), }); /// The Ethereum chain spec @@ -92,14 +90,11 @@ pub struct ChainSpec { pub genesis_hash: H256, /// The active hard forks and their block numbers - pub hardforks: BTreeMap, + pub hardforks: BTreeMap, /// Whether or not the DAO fork is supported pub dao_fork_support: bool, - /// The block number of the merge - pub paris_block: Option, - /// The merge terminal total difficulty pub paris_ttd: Option, } @@ -120,19 +115,42 @@ impl ChainSpec { self.genesis_hash } - /// Returns the supported hardforks and their fork block numbers - pub fn hardforks(&self) -> &BTreeMap { + /// Returns the supported hardforks and their [ForkKind] + pub fn hardforks(&self) -> &BTreeMap { &self.hardforks } - /// Get the first block number of the hardfork. + /// Get the first block number of the given hardfork. This method returns `None` for + /// timestamp-based forks and if the merge netsplit block is not known (when the given fork + /// is TTD-based) pub fn fork_block(&self, fork: Hardfork) -> Option { + self.hardforks.get(&fork).and_then(|kind| match kind { + ForkKind::Block(block_number) => Some(*block_number), + ForkKind::TTD(block_number) => *block_number, + _ => None, + }) + } + + /// Get the first block number/timestamp of the hardfork. + pub fn fork_kind(&self, fork: Hardfork) -> Option { self.hardforks.get(&fork).copied() } - /// Returns `true` if the given fork is active on the given block - pub fn fork_active(&self, fork: Hardfork, current_block: BlockNumber) -> bool { - self.fork_block(fork).map(|target| target <= current_block).unwrap_or_default() + /// Returns `true` if the given fork is active on the given should update docs here to reflect + /// that this returns true if the fork is active on the given block / ttd / timestamp contained + /// in the [ForkDiscriminant] + pub fn fork_active(&self, fork: Hardfork, discriminant: ForkDiscriminant) -> bool { + match self.hardforks.get(&fork) { + Some(kind) => match kind { + ForkKind::Block(block_number) => *block_number <= discriminant.block_number, + ForkKind::TTD(block_number) => { + self.paris_ttd <= Some(discriminant.total_difficulty) || + *block_number <= Some(discriminant.block_number) + } + ForkKind::Time(timestamp) => *timestamp <= discriminant.timestamp, + }, + None => false, + } } /// Returns `true` if the DAO fork is supported @@ -140,42 +158,56 @@ impl ChainSpec { self.dao_fork_support } - /// Get the Paris status - pub fn paris_status(&self) -> ParisStatus { - match self.paris_ttd { - Some(terminal_total_difficulty) => { - ParisStatus::Supported { terminal_total_difficulty, block: self.paris_block } - } - None => ParisStatus::NotSupported, - } + /// The merge terminal total difficulty + pub fn terminal_total_difficulty(&self) -> Option { + self.paris_ttd } - /// Get an iterator of all harforks with theirs respectives block number - pub fn forks_iter(&self) -> impl Iterator + '_ { + /// Get an iterator of all harforks with theirs respectives [ForkKind] + pub fn forks_iter(&self) -> impl Iterator + '_ { self.hardforks.iter().map(|(f, b)| (*f, *b)) } - /// Creates a [`ForkFilter`](crate::ForkFilter) for the given [BlockNumber]. - pub fn fork_filter(&self, block: BlockNumber) -> ForkFilter { - let future_forks = - self.forks_iter().map(|(_, b)| b).filter(|b| *b > block).collect::>(); + /// Creates a [`ForkFilter`](crate::ForkFilter) for the given [ForkDiscriminant]. + pub fn fork_filter(&self, discriminant: ForkDiscriminant) -> ForkFilter { + let future_forks = self + .forks_iter() + .filter(|(f, _)| !self.fork_active(*f, discriminant)) + .filter_map(|(_, k)| match k { + ForkKind::Block(block_number) => Some(block_number), + ForkKind::TTD(_) => None, + ForkKind::Time(timestamp) => Some(timestamp), + }) + .collect::>(); - ForkFilter::new(block, self.genesis_hash(), future_forks) + ForkFilter::new(discriminant.block_number, self.genesis_hash(), future_forks) } - /// Compute the forkid for the given [BlockNumber] - pub fn fork_id(&self, block: BlockNumber) -> ForkId { + /// Compute the forkid for the given [ForkDiscriminant] + pub fn fork_id(&self, discriminant: ForkDiscriminant) -> ForkId { let mut curr_forkhash = ForkHash::from(self.genesis_hash()); - let mut curr_block_number = 0; - - for (_, b) in self.forks_iter() { - if block >= b { - if b != curr_block_number { - curr_forkhash += b; - curr_block_number = b; + let mut forks = + self.forks_iter().filter(|(_, k)| !k.is_active_at_genesis()).collect::>(); + + forks.dedup_by(|a, b| a.1 == b.1); + + for (_, kind) in forks { + match kind { + ForkKind::Block(b) => { + if discriminant.block_number >= b { + curr_forkhash += b; + } else { + return ForkId { hash: curr_forkhash, next: b } + } + } + ForkKind::Time(t) => { + if discriminant.timestamp >= t { + curr_forkhash += t; + } else { + return ForkId { hash: curr_forkhash, next: t } + } } - } else { - return ForkId { hash: curr_forkhash, next: b } + _ => {} } } ForkId { hash: curr_forkhash, next: 0 } @@ -208,7 +240,7 @@ impl From for ChainSpec { let genesis_hash = Header::from(genesis_block.clone()).seal().hash(); let paris_ttd = genesis.config.terminal_total_difficulty.map(|ttd| ttd.into()); - let hardfork_opts = vec![ + let mut hardfork_opts = vec![ (Hardfork::Homestead, genesis.config.homestead_block), (Hardfork::Dao, genesis.config.dao_fork_block), (Hardfork::Tangerine, genesis.config.eip150_block), @@ -222,12 +254,17 @@ impl From for ChainSpec { (Hardfork::London, genesis.config.london_block), (Hardfork::ArrowGlacier, genesis.config.arrow_glacier_block), (Hardfork::GrayGlacier, genesis.config.gray_glacier_block), - (Hardfork::MergeNetsplit, genesis.config.merge_netsplit_block), ]; + // Paris block is not used to fork, and is not used in genesis.json + // except in Sepolia + if genesis.config.chain_id == Chain::sepolia().id() { + hardfork_opts.push((Hardfork::Paris, genesis.config.merge_netsplit_block)) + } + let configured_hardforks = hardfork_opts .iter() - .filter_map(|(hardfork, opt)| opt.map(|block| (*hardfork, block))) + .filter_map(|(hardfork, opt)| opt.map(|block| (*hardfork, ForkKind::Block(block)))) .collect::>(); Self { @@ -237,8 +274,6 @@ impl From for ChainSpec { hardforks: configured_hardforks, genesis_hash, paris_ttd, - // paris block is not used to fork, and is not used in genesis.json - paris_block: None, } } } @@ -249,9 +284,8 @@ pub struct ChainSpecBuilder { chain: Option, genesis: Option, genesis_hash: Option, - hardforks: BTreeMap, + hardforks: BTreeMap, dao_fork_support: bool, - paris_block: Option, paris_ttd: Option, } @@ -264,7 +298,6 @@ impl ChainSpecBuilder { genesis_hash: Some(MAINNET.genesis_hash), hardforks: MAINNET.hardforks.clone(), dao_fork_support: MAINNET.dao_fork_support, - paris_block: MAINNET.paris_block, paris_ttd: MAINNET.paris_ttd, } } @@ -287,84 +320,98 @@ impl ChainSpecBuilder { self } - /// Insert the given fork at the given block number - pub fn with_fork(mut self, fork: Hardfork, block: BlockNumber) -> Self { - self.hardforks.insert(fork, block); + /// Remove all [Hardfork]s from the spec. Useful when the builder has been created from a + /// existing chain spec. + pub fn clear_forks(mut self) -> Self { + self.hardforks.clear(); + self + } + + /// Insert the given fork at the given [ForkKind] + pub fn with_fork(mut self, fork: Hardfork, kind: ForkKind) -> Self { + self.hardforks.insert(fork, kind); self } /// Enables Frontier pub fn frontier_activated(mut self) -> Self { - self.hardforks.insert(Hardfork::Frontier, 0); + self.hardforks.insert(Hardfork::Frontier, ForkKind::Block(0)); self } /// Enables Homestead pub fn homestead_activated(mut self) -> Self { self = self.frontier_activated(); - self.hardforks.insert(Hardfork::Homestead, 0); + self.hardforks.insert(Hardfork::Homestead, ForkKind::Block(0)); self } /// Enables Tangerine pub fn tangerine_whistle_activated(mut self) -> Self { self = self.homestead_activated(); - self.hardforks.insert(Hardfork::Tangerine, 0); + self.hardforks.insert(Hardfork::Tangerine, ForkKind::Block(0)); self } /// Enables SpuriousDragon pub fn spurious_dragon_activated(mut self) -> Self { self = self.tangerine_whistle_activated(); - self.hardforks.insert(Hardfork::SpuriousDragon, 0); + self.hardforks.insert(Hardfork::SpuriousDragon, ForkKind::Block(0)); self } /// Enables Byzantium pub fn byzantium_activated(mut self) -> Self { self = self.spurious_dragon_activated(); - self.hardforks.insert(Hardfork::Byzantium, 0); + self.hardforks.insert(Hardfork::Byzantium, ForkKind::Block(0)); self } /// Enables Petersburg pub fn petersburg_activated(mut self) -> Self { self = self.byzantium_activated(); - self.hardforks.insert(Hardfork::Petersburg, 0); + self.hardforks.insert(Hardfork::Petersburg, ForkKind::Block(0)); self } /// Enables Istanbul pub fn istanbul_activated(mut self) -> Self { self = self.petersburg_activated(); - self.hardforks.insert(Hardfork::Istanbul, 0); + self.hardforks.insert(Hardfork::Istanbul, ForkKind::Block(0)); self } /// Enables Berlin pub fn berlin_activated(mut self) -> Self { self = self.istanbul_activated(); - self.hardforks.insert(Hardfork::Berlin, 0); + self.hardforks.insert(Hardfork::Berlin, ForkKind::Block(0)); self } /// Enables London pub fn london_activated(mut self) -> Self { self = self.berlin_activated(); - self.hardforks.insert(Hardfork::London, 0); - self - } - - /// Sets the DAO fork as supported - pub fn dao_fork_supported(mut self) -> Self { - self.dao_fork_support = true; + self.hardforks.insert(Hardfork::London, ForkKind::Block(0)); self } /// Enables Paris pub fn paris_activated(mut self) -> Self { self = self.berlin_activated(); - self.paris_block = Some(0); + self.hardforks.insert(Hardfork::Paris, ForkKind::TTD(Some(0))); + self + } + + /// Enables Shangai + pub fn shangai_activated(mut self) -> Self { + self = self.paris_activated(); + self.hardforks.insert(Hardfork::Shanghai, ForkKind::Time(0)); + self + } + + /// Sets the DAO fork as supported + pub fn dao_fork_supported(mut self) -> Self { + self.dao_fork_support = true; self } @@ -376,7 +423,6 @@ impl ChainSpecBuilder { genesis_hash: self.genesis_hash.expect("The genesis hash is required"), hardforks: self.hardforks, dao_fork_support: self.dao_fork_support, - paris_block: self.paris_block, paris_ttd: self.paris_ttd, } } @@ -390,51 +436,17 @@ impl From<&ChainSpec> for ChainSpecBuilder { genesis_hash: Some(value.genesis_hash), hardforks: value.hardforks.clone(), dao_fork_support: value.dao_fork_support, - paris_block: value.paris_block, paris_ttd: value.paris_ttd, } } } -/// Merge Status -#[derive(Debug)] -pub enum ParisStatus { - /// Paris is not supported - NotSupported, - /// Paris settings has been set in the chain spec - Supported { - /// The merge terminal total difficulty - terminal_total_difficulty: U256, - /// The Paris block number - block: Option, - }, -} - -impl ParisStatus { - /// Returns the Paris block number if it is known ahead of time. - /// - /// This is only the case for chains that have already activated the merge. - pub fn block_number(&self) -> Option { - match &self { - ParisStatus::NotSupported => None, - ParisStatus::Supported { block, .. } => *block, - } - } - - /// Returns the merge terminal total difficulty - pub fn terminal_total_difficulty(&self) -> Option { - match &self { - ParisStatus::NotSupported => None, - ParisStatus::Supported { terminal_total_difficulty, .. } => { - Some(*terminal_total_difficulty) - } - } - } -} - #[cfg(test)] mod tests { - use crate::{Chain, ChainSpec, ForkHash, Genesis, Hardfork, Header, GOERLI, MAINNET, SEPOLIA}; + use crate::{ + Chain, ChainSpec, ForkDiscriminant, ForkHash, ForkKind, Genesis, Hardfork, Header, GOERLI, + MAINNET, SEPOLIA, + }; #[test] fn test_empty_forkid() { @@ -446,22 +458,23 @@ mod tests { .chain(Chain::mainnet()) .genesis(empty_genesis) .genesis_hash(empty_sealed.hash()) - .with_fork(Hardfork::Frontier, 0) - .with_fork(Hardfork::Homestead, 0) - .with_fork(Hardfork::Tangerine, 0) - .with_fork(Hardfork::SpuriousDragon, 0) - .with_fork(Hardfork::Byzantium, 0) - .with_fork(Hardfork::Constantinople, 0) - .with_fork(Hardfork::Istanbul, 0) - .with_fork(Hardfork::Muirglacier, 0) - .with_fork(Hardfork::Berlin, 0) - .with_fork(Hardfork::London, 0) - .with_fork(Hardfork::ArrowGlacier, 0) - .with_fork(Hardfork::GrayGlacier, 0) + .clear_forks() + .with_fork(Hardfork::Frontier, ForkKind::Block(0)) + .with_fork(Hardfork::Homestead, ForkKind::Block(0)) + .with_fork(Hardfork::Tangerine, ForkKind::Block(0)) + .with_fork(Hardfork::SpuriousDragon, ForkKind::Block(0)) + .with_fork(Hardfork::Byzantium, ForkKind::Block(0)) + .with_fork(Hardfork::Constantinople, ForkKind::Block(0)) + .with_fork(Hardfork::Istanbul, ForkKind::Block(0)) + .with_fork(Hardfork::Muirglacier, ForkKind::Block(0)) + .with_fork(Hardfork::Berlin, ForkKind::Block(0)) + .with_fork(Hardfork::London, ForkKind::Block(0)) + .with_fork(Hardfork::ArrowGlacier, ForkKind::Block(0)) + .with_fork(Hardfork::GrayGlacier, ForkKind::Block(0)) .build(); // test at block one - all forks should be active - let res_forkid = spec.fork_id(1); + let res_forkid = spec.fork_id(1_u64.into()); let expected_forkhash = ForkHash::from(spec.genesis_hash()); // if blocks get activated at genesis then they should not be accumulated into the forkhash @@ -478,96 +491,96 @@ mod tests { .chain(Chain::mainnet()) .genesis(empty_genesis.clone()) .genesis_hash(empty_sealed.hash()) - .with_fork(Hardfork::Frontier, 0) - .with_fork(Hardfork::Homestead, 1) + .with_fork(Hardfork::Frontier, ForkKind::Block(0)) + .with_fork(Hardfork::Homestead, ForkKind::Block(1)) .build(); let duplicate_spec = ChainSpec::builder() .chain(Chain::mainnet()) .genesis(empty_genesis) .genesis_hash(empty_sealed.hash()) - .with_fork(Hardfork::Frontier, 0) - .with_fork(Hardfork::Homestead, 1) - .with_fork(Hardfork::Tangerine, 1) + .with_fork(Hardfork::Frontier, ForkKind::Block(0)) + .with_fork(Hardfork::Homestead, ForkKind::Block(1)) + .with_fork(Hardfork::Tangerine, ForkKind::Block(1)) .build(); - assert_eq!(unique_spec.fork_id(2), duplicate_spec.fork_id(2)); + assert_eq!(unique_spec.fork_id(2.into()), duplicate_spec.fork_id(2.into())); } // these tests check that the forkid computation is accurate #[test] fn test_mainnet_forkids() { - let frontier_forkid = MAINNET.fork_id(0); + let frontier_forkid = MAINNET.fork_id(0.into()); assert_eq!([0xfc, 0x64, 0xec, 0x04], frontier_forkid.hash.0); assert_eq!(1150000, frontier_forkid.next); - let homestead_forkid = MAINNET.fork_id(1150000); + let homestead_forkid = MAINNET.fork_id(1150000.into()); assert_eq!([0x97, 0xc2, 0xc3, 0x4c], homestead_forkid.hash.0); assert_eq!(1920000, homestead_forkid.next); - let dao_forkid = MAINNET.fork_id(1920000); + let dao_forkid = MAINNET.fork_id(1920000.into()); assert_eq!([0x91, 0xd1, 0xf9, 0x48], dao_forkid.hash.0); assert_eq!(2463000, dao_forkid.next); - let tangerine_forkid = MAINNET.fork_id(2463000); + let tangerine_forkid = MAINNET.fork_id(2463000.into()); assert_eq!([0x7a, 0x64, 0xda, 0x13], tangerine_forkid.hash.0); assert_eq!(2675000, tangerine_forkid.next); - let spurious_forkid = MAINNET.fork_id(2675000); + let spurious_forkid = MAINNET.fork_id(2675000.into()); assert_eq!([0x3e, 0xdd, 0x5b, 0x10], spurious_forkid.hash.0); assert_eq!(4370000, spurious_forkid.next); - let byzantium_forkid = MAINNET.fork_id(4370000); + let byzantium_forkid = MAINNET.fork_id(4370000.into()); assert_eq!([0xa0, 0x0b, 0xc3, 0x24], byzantium_forkid.hash.0); assert_eq!(7280000, byzantium_forkid.next); - let constantinople_forkid = MAINNET.fork_id(7280000); + let constantinople_forkid = MAINNET.fork_id(7280000.into()); assert_eq!([0x66, 0x8d, 0xb0, 0xaf], constantinople_forkid.hash.0); assert_eq!(9069000, constantinople_forkid.next); - let istanbul_forkid = MAINNET.fork_id(9069000); + let istanbul_forkid = MAINNET.fork_id(9069000.into()); assert_eq!([0x87, 0x9d, 0x6e, 0x30], istanbul_forkid.hash.0); assert_eq!(9200000, istanbul_forkid.next); - let muir_glacier_forkid = MAINNET.fork_id(9200000); + let muir_glacier_forkid = MAINNET.fork_id(9200000.into()); assert_eq!([0xe0, 0x29, 0xe9, 0x91], muir_glacier_forkid.hash.0); assert_eq!(12244000, muir_glacier_forkid.next); - let berlin_forkid = MAINNET.fork_id(12244000); + let berlin_forkid = MAINNET.fork_id(12244000.into()); assert_eq!([0x0e, 0xb4, 0x40, 0xf6], berlin_forkid.hash.0); assert_eq!(12965000, berlin_forkid.next); - let london_forkid = MAINNET.fork_id(12965000); + let london_forkid = MAINNET.fork_id(12965000.into()); assert_eq!([0xb7, 0x15, 0x07, 0x7d], london_forkid.hash.0); assert_eq!(13773000, london_forkid.next); - let arrow_glacier_forkid = MAINNET.fork_id(13773000); + let arrow_glacier_forkid = MAINNET.fork_id(13773000.into()); assert_eq!([0x20, 0xc3, 0x27, 0xfc], arrow_glacier_forkid.hash.0); assert_eq!(15050000, arrow_glacier_forkid.next); - let gray_glacier_forkid = MAINNET.fork_id(15050000); + let gray_glacier_forkid = MAINNET.fork_id(15050000.into()); assert_eq!([0xf0, 0xaf, 0xd0, 0xe3], gray_glacier_forkid.hash.0); assert_eq!(0, gray_glacier_forkid.next); // TODO: update post-gray glacier - let latest_forkid = MAINNET.fork_id(15050000); + let latest_forkid = MAINNET.fork_id(15050000.into()); assert_eq!(0, latest_forkid.next); } #[test] fn test_goerli_forkids() { - let frontier_forkid = GOERLI.fork_id(0); + let frontier_forkid = GOERLI.fork_id(ForkDiscriminant::block(0)); assert_eq!([0xa3, 0xf5, 0xab, 0x08], frontier_forkid.hash.0); assert_eq!(1561651, frontier_forkid.next); - let istanbul_forkid = GOERLI.fork_id(1561651); + let istanbul_forkid = GOERLI.fork_id(ForkDiscriminant::block(1561651)); assert_eq!([0xc2, 0x5e, 0xfa, 0x5c], istanbul_forkid.hash.0); assert_eq!(4460644, istanbul_forkid.next); - let berlin_forkid = GOERLI.fork_id(4460644); + let berlin_forkid = GOERLI.fork_id(ForkDiscriminant::block(4460644)); assert_eq!([0x75, 0x7a, 0x1c, 0x47], berlin_forkid.hash.0); assert_eq!(5062605, berlin_forkid.next); - let london_forkid = GOERLI.fork_id(12965000); + let london_forkid = GOERLI.fork_id(ForkDiscriminant::block(12965000)); assert_eq!([0xb8, 0xc6, 0x29, 0x9d], london_forkid.hash.0); assert_eq!(0, london_forkid.next); } @@ -575,7 +588,7 @@ mod tests { #[test] fn test_sepolia_forkids() { // Test vector is from - let mergenetsplit_forkid = SEPOLIA.fork_id(1735371); + let mergenetsplit_forkid = SEPOLIA.fork_id(ForkDiscriminant::block(1735371)); assert_eq!([0xb9, 0x6c, 0xbd, 0x13], mergenetsplit_forkid.hash.0); assert_eq!(0, mergenetsplit_forkid.next); } diff --git a/crates/primitives/src/forkid.rs b/crates/primitives/src/forkid.rs index 43d02658f471..ed0e1e2c4bfb 100644 --- a/crates/primitives/src/forkid.rs +++ b/crates/primitives/src/forkid.rs @@ -83,8 +83,8 @@ impl Add for ForkHash { pub struct ForkId { /// CRC32 checksum of the all fork blocks from genesis. pub hash: ForkHash, - /// Next upcoming fork block number, 0 if not yet known. - pub next: BlockNumber, + /// Next upcoming fork block number or timestamp, 0 if not yet known. + pub next: u64, } /// Reason for rejecting provided `ForkId`. @@ -127,7 +127,7 @@ impl ForkFilter { pub fn new(head: BlockNumber, genesis: H256, forks: F) -> Self where F: IntoIterator, - B: Into, + B: Into, { let genesis_fork_hash = ForkHash::from(genesis); let mut forks = forks.into_iter().map(Into::into).collect::>(); diff --git a/crates/primitives/src/forkkind.rs b/crates/primitives/src/forkkind.rs new file mode 100644 index 000000000000..d7b96ee4b601 --- /dev/null +++ b/crates/primitives/src/forkkind.rs @@ -0,0 +1,92 @@ +use serde::{Deserialize, Serialize}; + +use crate::{BlockNumber, ChainSpec, Header, U256}; + +/// Hardforks can be based on block numbers (pre-merge), TTD (Paris) +/// or timestamp (post-merge) +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub enum ForkKind { + /// A fork's block number + Block(BlockNumber), + + /// The terminal total difficulty (used by Paris fork) + TTD(Option), + + /// The unix timestamp of a fork + Time(u64), +} + +impl ForkKind { + /// Returns `true` is the fork is active at genesis + pub fn is_active_at_genesis(&self) -> bool { + match self { + ForkKind::Block(block_number) => *block_number == 0_u64, + ForkKind::TTD(_) => false, + ForkKind::Time(_) => false, + } + } +} + +/// This struct is used when it's needed to determine is a hardfork is active +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] +pub struct ForkDiscriminant { + /// The block number + pub block_number: BlockNumber, + /// The total difficulty + pub total_difficulty: U256, + /// The timestamp + pub timestamp: u64, +} + +impl ForkDiscriminant { + /// Returns a new [ForkDiscriminant] + pub fn new(block_number: BlockNumber, total_difficulty: U256, timestamp: u64) -> Self { + Self { block_number, total_difficulty, timestamp } + } + + /// Return a [ForkDiscriminant] with the given block + pub fn block(block_number: BlockNumber) -> Self { + Self { block_number, ..Default::default() } + } + + /// Return a [ForkDiscriminant] with the given ttd + pub fn ttd(total_difficulty: U256, block_number: Option) -> Self { + Self { + block_number: block_number.unwrap_or_default(), + total_difficulty, + ..Default::default() + } + } + + /// Return a [ForkDiscriminant] with the given timestamp + pub fn timestamp(timestamp: u64) -> Self { + Self { timestamp, ..Default::default() } + } + + /// Return a [ForkDiscriminant] from the given [ForkKind] + pub fn from_kind(kind: ForkKind, chain_spec: &ChainSpec) -> Self { + match kind { + ForkKind::Block(block_number) => ForkDiscriminant::block(block_number), + ForkKind::TTD(block_number) => { + ForkDiscriminant::ttd(chain_spec.paris_ttd.unwrap_or_default(), block_number) + } + ForkKind::Time(timestamp) => ForkDiscriminant::timestamp(timestamp), + } + } +} + +impl From for ForkDiscriminant { + fn from(value: BlockNumber) -> Self { + Self { block_number: value, ..Default::default() } + } +} + +impl From<&Header> for ForkDiscriminant { + fn from(value: &Header) -> Self { + Self { + block_number: value.number, + total_difficulty: value.difficulty, + timestamp: value.timestamp, + } + } +} diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index 42ea1f160eb2..16666fee7a54 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -2,12 +2,10 @@ use serde::{Deserialize, Serialize}; use std::{fmt::Display, str::FromStr}; -use crate::{BlockNumber, ChainSpec, ForkFilter, ForkHash, ForkId}; +use crate::{forkkind::ForkDiscriminant, ChainSpec, ForkFilter, ForkId}; #[allow(missing_docs)] -#[derive( - Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize, -)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub enum Hardfork { Frontier, Homestead, @@ -23,10 +21,8 @@ pub enum Hardfork { London, ArrowGlacier, GrayGlacier, - MergeNetsplit, + Paris, Shanghai, - #[default] - Latest, } impl Hardfork { @@ -37,24 +33,10 @@ impl Hardfork { /// /// If the hard fork is not present in the [`ChainSpec`] then `None` is returned. pub fn fork_id(&self, chain_spec: &ChainSpec) -> Option { - if let Some(fork_block) = chain_spec.fork_block(*self) { - let mut curr_forkhash = ForkHash::from(chain_spec.genesis_hash()); - let mut curr_block_number = 0; - - for (_, b) in chain_spec.forks_iter() { - if fork_block >= b { - if b != curr_block_number { - curr_forkhash += b; - curr_block_number = b; - } - } else { - return Some(ForkId { hash: curr_forkhash, next: b }) - } - } - Some(ForkId { hash: curr_forkhash, next: 0 }) - } else { - None - } + chain_spec + .fork_kind(*self) + .map(|k| ForkDiscriminant::from_kind(k, chain_spec)) + .map(|d| chain_spec.fork_id(d)) } /// Creates a [`ForkFilter`](crate::ForkFilter) for the given hardfork. @@ -64,15 +46,10 @@ impl Hardfork { /// /// This returns `None` if the hardfork is not present in the given [`ChainSpec`]. pub fn fork_filter(&self, chain_spec: &ChainSpec) -> Option { - if let Some(fork_block) = chain_spec.fork_block(*self) { - let future_forks: Vec = - chain_spec.forks_iter().filter(|(_, b)| b > &fork_block).map(|(_, b)| b).collect(); - - // pass in the chain spec's genesis hash to initialize the fork filter - Some(ForkFilter::new(fork_block, chain_spec.genesis_hash(), future_forks)) - } else { - None - } + chain_spec + .fork_kind(*self) + .map(|k| ForkDiscriminant::from_kind(k, chain_spec)) + .map(|d| chain_spec.fork_filter(d)) } } @@ -95,8 +72,7 @@ impl FromStr for Hardfork { "berlin" | "11" => Hardfork::Berlin, "london" | "12" => Hardfork::London, "arrowglacier" | "13" => Hardfork::ArrowGlacier, - "grayglacier" => Hardfork::GrayGlacier, - "latest" | "14" => Hardfork::Latest, + "grayglacier" | "14" => Hardfork::GrayGlacier, _ => return Err(format!("Unknown hardfork {s}")), }; Ok(hardfork) diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 5a5f2d83df64..f45f8e279670 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -17,6 +17,7 @@ mod chain; pub mod constants; mod error; mod forkid; +mod forkkind; mod genesis; mod hardfork; mod header; @@ -37,13 +38,12 @@ pub use account::Account; pub use bits::H512; pub use block::{Block, BlockHashOrNumber, SealedBlock}; pub use bloom::Bloom; -pub use chain::{ - Chain, ChainInfo, ChainSpec, ChainSpecBuilder, ParisStatus, GOERLI, MAINNET, SEPOLIA, -}; +pub use chain::{Chain, ChainInfo, ChainSpec, ChainSpecBuilder, GOERLI, MAINNET, SEPOLIA}; pub use constants::{ EMPTY_OMMER_ROOT, GOERLI_GENESIS, KECCAK_EMPTY, MAINNET_GENESIS, SEPOLIA_GENESIS, }; pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError}; +pub use forkkind::{ForkDiscriminant, ForkKind}; pub use genesis::{Genesis, GenesisAccount}; pub use hardfork::Hardfork; pub use header::{Header, HeadersDirection, SealedHeader}; diff --git a/crates/staged-sync/src/utils/init.rs b/crates/staged-sync/src/utils/init.rs index fc711fff4829..7177b5e7b245 100644 --- a/crates/staged-sync/src/utils/init.rs +++ b/crates/staged-sync/src/utils/init.rs @@ -53,7 +53,7 @@ pub fn init_genesis(db: Arc, chain: ChainSpec) -> Result