refactor: reduce Hardforks trait usage (#13728)

This commit is contained in:
Arsenii Kulikov
2025-01-08 17:02:49 +03:00
committed by GitHub
parent 9d51260fbc
commit 8f2ecc44e8
17 changed files with 126 additions and 76 deletions

1
Cargo.lock generated
View File

@ -8259,6 +8259,7 @@ version = "1.1.5"
dependencies = [
"alloy-chains",
"alloy-primitives",
"auto_impl",
"once_cell",
"reth-ethereum-forks",
"serde",

View File

@ -765,6 +765,10 @@ impl Hardforks for ChainSpec {
}
impl EthereumHardforks for ChainSpec {
fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
self.fork(fork)
}
fn get_final_paris_total_difficulty(&self) -> Option<U256> {
self.get_final_paris_total_difficulty()
}

View File

@ -5,7 +5,7 @@ use std::{path::PathBuf, sync::Arc};
use alloy_eips::BlockHashOrNumber;
use backon::{ConstantBuilder, Retryable};
use clap::{Parser, Subcommand};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_util::{get_secret_key, hash_or_num_value_parser};
use reth_config::Config;
@ -73,7 +73,7 @@ pub enum Subcommands {
Rlpx(rlpx::Command),
}
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>> Command<C> {
/// Execute `p2p` command
pub async fn execute<N: NetworkPrimitives>(self) -> eyre::Result<()> {
let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain());

View File

@ -4,7 +4,7 @@ use std::sync::Arc;
use crate::common::CliNodeTypes;
use clap::{Parser, Subcommand};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_runner::CliContext;
use reth_eth_wire::NetPrimitivesFor;
@ -40,7 +40,7 @@ pub enum Subcommands<C: ChainSpecParser> {
Unwind(unwind::Command<C>),
}
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>> Command<C> {
/// Execute `stage` command
pub async fn execute<N, E, F, P>(self, ctx: CliContext, executor: F) -> eyre::Result<()>
where

View File

@ -6,7 +6,7 @@ use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use alloy_eips::BlockHashOrNumber;
use alloy_primitives::Sealable;
use clap::Parser;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_runner::CliContext;
use reth_cli_util::get_secret_key;
@ -104,7 +104,7 @@ pub struct Command<C: ChainSpecParser> {
network: NetworkArgs,
}
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>> Command<C> {
/// Execute `stage` command
pub async fn execute<N, E, F, P>(self, ctx: CliContext, executor: F) -> eyre::Result<()>
where

View File

@ -1,6 +1,6 @@
use alloy_consensus::constants::ETH_TO_WEI;
use alloy_primitives::BlockNumber;
use reth_chainspec::{EthereumHardfork, EthereumHardforks, Hardforks};
use reth_chainspec::EthereumHardforks;
/// Calculates the base block reward.
///
@ -35,10 +35,13 @@ pub fn base_block_reward<ChainSpec: EthereumHardforks>(
/// Calculates the base block reward __before__ the merge (Paris hardfork).
///
/// Caution: The caller must ensure that the block number is before the merge.
pub fn base_block_reward_pre_merge(chain_spec: impl Hardforks, block_number: BlockNumber) -> u128 {
if chain_spec.fork(EthereumHardfork::Constantinople).active_at_block(block_number) {
pub fn base_block_reward_pre_merge(
chain_spec: impl EthereumHardforks,
block_number: BlockNumber,
) -> u128 {
if chain_spec.is_constantinople_active_at_block(block_number) {
ETH_TO_WEI * 2
} else if chain_spec.fork(EthereumHardfork::Byzantium).active_at_block(block_number) {
} else if chain_spec.is_byzantium_active_at_block(block_number) {
ETH_TO_WEI * 3
} else {
ETH_TO_WEI * 5

View File

@ -25,8 +25,7 @@ pub fn validate_header_base_fee<H: BlockHeader, ChainSpec: EthereumHardforks>(
header: &H,
chain_spec: &ChainSpec,
) -> Result<(), ConsensusError> {
if chain_spec.is_fork_active_at_block(EthereumHardfork::London, header.number()) &&
header.base_fee_per_gas().is_none()
if chain_spec.is_london_active_at_block(header.number()) && header.base_fee_per_gas().is_none()
{
return Err(ConsensusError::BaseFeeMissing)
}
@ -253,23 +252,25 @@ pub fn validate_against_parent_eip1559_base_fee<
parent: &H,
chain_spec: &ChainSpec,
) -> Result<(), ConsensusError> {
if chain_spec.fork(EthereumHardfork::London).active_at_block(header.number()) {
if chain_spec.is_london_active_at_block(header.number()) {
let base_fee = header.base_fee_per_gas().ok_or(ConsensusError::BaseFeeMissing)?;
let expected_base_fee =
if chain_spec.fork(EthereumHardfork::London).transitions_at_block(header.number()) {
alloy_eips::eip1559::INITIAL_BASE_FEE
} else {
// This BaseFeeMissing will not happen as previous blocks are checked to have
// them.
let base_fee = parent.base_fee_per_gas().ok_or(ConsensusError::BaseFeeMissing)?;
calc_next_block_base_fee(
parent.gas_used(),
parent.gas_limit(),
base_fee,
chain_spec.base_fee_params_at_timestamp(header.timestamp()),
)
};
let expected_base_fee = if chain_spec
.ethereum_fork_activation(EthereumHardfork::London)
.transitions_at_block(header.number())
{
alloy_eips::eip1559::INITIAL_BASE_FEE
} else {
// This BaseFeeMissing will not happen as previous blocks are checked to have
// them.
let base_fee = parent.base_fee_per_gas().ok_or(ConsensusError::BaseFeeMissing)?;
calc_next_block_base_fee(
parent.gas_used(),
parent.gas_limit(),
base_fee,
chain_spec.base_fee_params_at_timestamp(header.timestamp()),
)
};
if expected_base_fee != base_fee {
return Err(ConsensusError::BaseFeeDiff(GotExpected {
expected: expected_base_fee,

View File

@ -1,54 +1,80 @@
use alloy_primitives::U256;
use crate::{hardforks::Hardforks, EthereumHardfork, ForkCondition};
use crate::{EthereumHardfork, ForkCondition};
/// Helper methods for Ethereum forks.
#[auto_impl::auto_impl(&, Arc)]
pub trait EthereumHardforks: Hardforks {
pub trait EthereumHardforks: Clone {
/// Retrieves [`ForkCondition`] by an [`EthereumHardfork`]. If `fork` is not present, returns
/// [`ForkCondition::Never`].
fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition;
/// Convenience method to check if an [`EthereumHardfork`] is active at a given timestamp.
fn is_ethereum_fork_active_at_timestamp(&self, fork: EthereumHardfork, timestamp: u64) -> bool {
self.ethereum_fork_activation(fork).active_at_timestamp(timestamp)
}
/// Convenience method to check if an [`EthereumHardfork`] is active at a given block number.
fn is_ethereum_fork_active_at_block(&self, fork: EthereumHardfork, block_number: u64) -> bool {
self.ethereum_fork_activation(fork).active_at_block(block_number)
}
/// Convenience method to check if [`EthereumHardfork::Shanghai`] is active at a given
/// timestamp.
fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp)
self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Shanghai, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Cancun`] is active at a given timestamp.
fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp)
self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Cancun, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Prague`] is active at a given timestamp.
fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp)
self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Prague, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Osaka`] is active at a given timestamp.
fn is_osaka_active_at_timestamp(&self, timestamp: u64) -> bool {
self.is_fork_active_at_timestamp(EthereumHardfork::Osaka, timestamp)
self.is_ethereum_fork_active_at_timestamp(EthereumHardfork::Osaka, timestamp)
}
/// Convenience method to check if [`EthereumHardfork::Byzantium`] is active at a given block
/// number.
fn is_byzantium_active_at_block(&self, block_number: u64) -> bool {
self.fork(EthereumHardfork::Byzantium).active_at_block(block_number)
self.is_ethereum_fork_active_at_block(EthereumHardfork::Byzantium, block_number)
}
/// Convenience method to check if [`EthereumHardfork::SpuriousDragon`] is active at a given
/// block number.
fn is_spurious_dragon_active_at_block(&self, block_number: u64) -> bool {
self.fork(EthereumHardfork::SpuriousDragon).active_at_block(block_number)
self.is_ethereum_fork_active_at_block(EthereumHardfork::SpuriousDragon, block_number)
}
/// Convenience method to check if [`EthereumHardfork::Homestead`] is active at a given block
/// number.
fn is_homestead_active_at_block(&self, block_number: u64) -> bool {
self.fork(EthereumHardfork::Homestead).active_at_block(block_number)
self.is_ethereum_fork_active_at_block(EthereumHardfork::Homestead, block_number)
}
/// Convenience method to check if [`EthereumHardfork::London`] is active at a given block
/// number.
fn is_london_active_at_block(&self, block_number: u64) -> bool {
self.is_ethereum_fork_active_at_block(EthereumHardfork::London, block_number)
}
/// Convenience method to check if [`EthereumHardfork::Constantinople`] is active at a given
/// block number.
fn is_constantinople_active_at_block(&self, block_number: u64) -> bool {
self.is_ethereum_fork_active_at_block(EthereumHardfork::Constantinople, block_number)
}
/// The Paris hardfork (merge) is activated via block number. If we have knowledge of the block,
/// this function will return true if the block number is greater than or equal to the Paris
/// (merge) block.
fn is_paris_active_at_block(&self, block_number: u64) -> Option<bool> {
match self.fork(EthereumHardfork::Paris) {
match self.ethereum_fork_activation(EthereumHardfork::Paris) {
ForkCondition::TTD { activation_block_number, .. } => {
Some(block_number >= activation_block_number)
}

View File

@ -11,7 +11,7 @@
use alloy_consensus::{BlockHeader, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::{eip7840::BlobParams, merge::ALLOWED_FUTURE_BLOCK_TIME_SECONDS};
use alloy_primitives::U256;
use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_consensus::{
Consensus, ConsensusError, FullConsensus, HeaderValidator, PostExecutionInput,
};
@ -56,16 +56,16 @@ impl<ChainSpec: EthChainSpec + EthereumHardforks> EthBeaconConsensus<ChainSpec>
parent: &SealedHeader<H>,
) -> Result<(), ConsensusError> {
// Determine the parent gas limit, considering elasticity multiplier on the London fork.
let parent_gas_limit =
if self.chain_spec.fork(EthereumHardfork::London).transitions_at_block(header.number())
{
parent.gas_limit() *
self.chain_spec
.base_fee_params_at_timestamp(header.timestamp())
.elasticity_multiplier as u64
} else {
parent.gas_limit()
};
let parent_gas_limit = if !self.chain_spec.is_london_active_at_block(parent.number()) &&
self.chain_spec.is_london_active_at_block(header.number())
{
parent.gas_limit() *
self.chain_spec
.base_fee_params_at_timestamp(header.timestamp())
.elasticity_multiplier as u64
} else {
parent.gas_limit()
};
// Check for an increase in gas limit beyond the allowed threshold.
if header.gas_limit() > parent_gas_limit {
@ -209,12 +209,10 @@ where
fn validate_header_with_total_difficulty(
&self,
header: &H,
total_difficulty: U256,
_total_difficulty: U256,
) -> Result<(), ConsensusError> {
let is_post_merge = self
.chain_spec
.fork(EthereumHardfork::Paris)
.active_at_ttd(total_difficulty, header.difficulty());
let is_post_merge =
self.chain_spec.is_paris_active_at_block(header.number()).is_some_and(|active| active);
if is_post_merge {
// TODO: add `is_zero_difficulty` to `alloy_consensus::BlockHeader` trait

View File

@ -313,6 +313,10 @@ impl Hardforks for OpChainSpec {
}
impl EthereumHardforks for OpChainSpec {
fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition {
self.fork(fork)
}
fn get_final_paris_total_difficulty(&self) -> Option<U256> {
self.inner.get_final_paris_total_difficulty()
}
@ -322,7 +326,11 @@ impl EthereumHardforks for OpChainSpec {
}
}
impl OpHardforks for OpChainSpec {}
impl OpHardforks for OpChainSpec {
fn op_fork_activation(&self, fork: OpHardfork) -> ForkCondition {
self.fork(fork)
}
}
impl From<Genesis> for OpChainSpec {
fn from(genesis: Genesis) -> Self {

View File

@ -6,7 +6,7 @@ use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::B256;
use alloy_trie::root::ordered_trie_root_with_encoder;
use reth_chainspec::ChainSpec;
use reth_optimism_forks::OpHardfork;
use reth_optimism_forks::{OpHardfork, OpHardforks};
use reth_optimism_primitives::OpReceipt;
use reth_primitives::ReceiptWithBloom;
@ -46,7 +46,7 @@ pub(crate) fn calculate_receipt_root_optimism(
/// NOTE: Prefer calculate receipt root optimism if you have log blooms memoized.
pub fn calculate_receipt_root_no_memo_optimism(
receipts: &[&OpReceipt],
chain_spec: impl reth_chainspec::Hardforks,
chain_spec: impl OpHardforks,
timestamp: u64,
) -> B256 {
// There is a minor bug in op-geth and op-erigon where in the Regolith hardfork,
@ -54,8 +54,8 @@ pub fn calculate_receipt_root_no_memo_optimism(
// encoding. In the Regolith Hardfork, we must strip the deposit nonce from the
// receipts before calculating the receipt root. This was corrected in the Canyon
// hardfork.
if chain_spec.is_fork_active_at_timestamp(OpHardfork::Regolith, timestamp) &&
!chain_spec.is_fork_active_at_timestamp(OpHardfork::Canyon, timestamp)
if chain_spec.is_regolith_active_at_timestamp(timestamp) &&
!chain_spec.is_canyon_active_at_timestamp(timestamp)
{
let receipts = receipts
.iter()

View File

@ -23,6 +23,7 @@ alloy-primitives.workspace = true
serde = { workspace = true, optional = true }
# misc
auto_impl.workspace = true
once_cell.workspace = true
[features]

View File

@ -17,51 +17,56 @@ mod dev;
pub use dev::DEV_HARDFORKS;
pub use hardfork::OpHardfork;
use reth_ethereum_forks::EthereumHardforks;
use reth_ethereum_forks::{EthereumHardforks, ForkCondition};
/// Extends [`EthereumHardforks`] with optimism helper methods.
#[auto_impl::auto_impl(&, Arc)]
pub trait OpHardforks: EthereumHardforks {
/// Retrieves [`ForkCondition`] by an [`OpHardfork`]. If `fork` is not present, returns
/// [`ForkCondition::Never`].
fn op_fork_activation(&self, fork: OpHardfork) -> ForkCondition;
/// Convenience method to check if [`OpHardfork::Bedrock`] is active at a given block
/// number.
fn is_bedrock_active_at_block(&self, block_number: u64) -> bool {
self.fork(OpHardfork::Bedrock).active_at_block(block_number)
self.op_fork_activation(OpHardfork::Bedrock).active_at_block(block_number)
}
/// Returns `true` if [`Regolith`](OpHardfork::Regolith) is active at given block
/// timestamp.
fn is_regolith_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Regolith).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Regolith).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Canyon`](OpHardfork::Canyon) is active at given block timestamp.
fn is_canyon_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Canyon).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Canyon).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Ecotone`](OpHardfork::Ecotone) is active at given block timestamp.
fn is_ecotone_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Ecotone).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Ecotone).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Fjord`](OpHardfork::Fjord) is active at given block timestamp.
fn is_fjord_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Fjord).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Fjord).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Granite`](OpHardfork::Granite) is active at given block timestamp.
fn is_granite_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Granite).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Granite).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Holocene`](OpHardfork::Holocene) is active at given block
/// timestamp.
fn is_holocene_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Holocene).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Holocene).active_at_timestamp(timestamp)
}
/// Returns `true` if [`Isthmus`](OpHardfork::Isthmus) is active at given block
/// timestamp.
fn is_isthmus_active_at_timestamp(&self, timestamp: u64) -> bool {
self.fork(OpHardfork::Isthmus).active_at_timestamp(timestamp)
self.op_fork_activation(OpHardfork::Isthmus).active_at_timestamp(timestamp)
}
}

View File

@ -12,6 +12,7 @@ use op_alloy_network::Network;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_evm::ConfigureEvm;
use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism;
use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::{OpBlock, OpReceipt, OpTransactionSigned};
use reth_primitives::{logs_bloom, BlockBody, SealedBlockWithSenders};
use reth_provider::{
@ -40,7 +41,7 @@ where
Block = OpBlock,
Receipt = OpReceipt,
Header = reth_primitives::Header,
> + ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>
> + ChainSpecProvider<ChainSpec: EthChainSpec + OpHardforks>
+ StateProviderFactory,
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<N::Provider>>>,
Evm: ConfigureEvm<

View File

@ -14,7 +14,6 @@ workspace = true
[dependencies]
# reth
reth-chainspec.workspace = true
reth-primitives.workspace = true
reth-rpc-api.workspace = true
reth-storage-api.workspace = true
reth-payload-builder.workspace = true
@ -49,6 +48,7 @@ parking_lot.workspace = true
[dev-dependencies]
reth-ethereum-engine-primitives.workspace = true
reth-provider = { workspace = true, features = ["test-utils"] }
reth-primitives.workspace = true
reth-payload-builder = { workspace = true, features = ["test-utils"] }
reth-tokio-util.workspace = true
reth-testing-utils.workspace = true

View File

@ -16,14 +16,13 @@ use alloy_rpc_types_engine::{
use async_trait::async_trait;
use jsonrpsee_core::RpcResult;
use parking_lot::Mutex;
use reth_chainspec::{EthereumHardforks, Hardforks};
use reth_chainspec::{EthereumHardfork, EthereumHardforks};
use reth_engine_primitives::{BeaconConsensusEngineHandle, EngineTypes, EngineValidator};
use reth_payload_builder::PayloadStore;
use reth_payload_primitives::{
validate_payload_timestamp, EngineApiMessageVersion, PayloadBuilderAttributes,
PayloadOrAttributes,
};
use reth_primitives::EthereumHardfork;
use reth_rpc_api::EngineApiServer;
use reth_rpc_types_compat::engine::payload::convert_to_payload_body_v1;
use reth_storage_api::{BlockReader, HeaderProvider, StateProviderFactory};
@ -615,7 +614,7 @@ where
let merge_terminal_td = self
.inner
.chain_spec
.fork(EthereumHardfork::Paris)
.ethereum_fork_activation(EthereumHardfork::Paris)
.ttd()
.expect("the engine API should not be running for chains w/o paris");
@ -1024,7 +1023,7 @@ mod tests {
use super::*;
use alloy_rpc_types_engine::{ClientCode, ClientVersionV1};
use assert_matches::assert_matches;
use reth_chainspec::{ChainSpec, MAINNET};
use reth_chainspec::{ChainSpec, EthereumHardfork, MAINNET};
use reth_engine_primitives::BeaconEngineMessage;
use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator};
use reth_payload_builder::test_utils::spawn_test_payload_service;

View File

@ -114,7 +114,10 @@ where
.chain_spec
.get_final_paris_total_difficulty()
.is_some(),
terminal_total_difficulty: self.chain_spec.fork(EthereumHardfork::Paris).ttd(),
terminal_total_difficulty: self
.chain_spec
.ethereum_fork_activation(EthereumHardfork::Paris)
.ttd(),
deposit_contract_address: self.chain_spec.deposit_contract().map(|dc| dc.address),
..self.chain_spec.genesis().config.clone()
};
@ -125,7 +128,7 @@ where
$(
// don't overwrite if already set
if $config.$field.is_none() {
$config.$field = match self.chain_spec.fork(EthereumHardfork::$fork) {
$config.$field = match self.chain_spec.ethereum_fork_activation(EthereumHardfork::$fork) {
ForkCondition::Block(block) => Some(block),
ForkCondition::TTD { fork_block, .. } => fork_block,
ForkCondition::Timestamp(ts) => Some(ts),