Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ethereum-forks): remove total difficulty for hardfork check #13362

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions crates/blockchain-tree/src/blockchain_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,12 @@ where
let provider = self.externals.provider_factory.provider()?;

// Validate that the block is post merge
let parent_td = provider
.header_td(&block.parent_hash)?
.ok_or_else(|| BlockchainTreeError::CanonicalChain { block_hash: block.parent_hash })?;

// Pass the parent total difficulty to short-circuit unnecessary calculations.
if !self
.externals
.provider_factory
.chain_spec()
.fork(EthereumHardfork::Paris)
.active_at_ttd(parent_td, U256::ZERO)
.active_at_ttd(block.number)
{
return Err(BlockExecutionError::Validation(BlockValidationError::BlockPreMerge {
hash: block.hash(),
Expand Down Expand Up @@ -1032,21 +1027,12 @@ where
durations_recorder.record_relative(MakeCanonicalAction::FindCanonicalHeader);
if let Some(header) = canonical_header {
info!(target: "blockchain_tree", %block_hash, "Block is already canonical, ignoring.");
// TODO: this could be fetched from the chainspec first
let td =
self.externals.provider_factory.provider()?.header_td(&block_hash)?.ok_or_else(
|| {
CanonicalError::from(BlockValidationError::MissingTotalDifficulty {
hash: block_hash,
})
},
)?;
if !self
.externals
.provider_factory
.chain_spec()
.fork(EthereumHardfork::Paris)
.active_at_ttd(td, U256::ZERO)
.active_at_ttd(header.number)
{
return Err(CanonicalError::from(BlockValidationError::BlockPreMerge {
hash: block_hash,
Expand Down
23 changes: 11 additions & 12 deletions crates/chainspec/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ impl From<Genesis> for ChainSpec {
hardforks.push((
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD {
activation_block_number: genesis.config.merge_netsplit_block.expect("Merge netsplit block is required"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattsse @Rjected, do I need to update merge_netsplit_block to not be optional in the genesis file?

total_difficulty: ttd,
fork_block: genesis.config.merge_netsplit_block,
},
Expand Down Expand Up @@ -763,10 +764,10 @@ impl ChainSpecBuilder {
/// Enable the Paris hardfork at the given TTD.
///
/// Does not set the merge netsplit block.
pub fn paris_at_ttd(self, ttd: U256) -> Self {
pub fn paris_at_ttd(self, ttd: U256, activation_block_number: BlockNumber) -> Self {
self.with_fork(
EthereumHardfork::Paris,
ForkCondition::TTD { total_difficulty: ttd, fork_block: None },
ForkCondition::TTD { activation_block_number, total_difficulty: ttd, fork_block: None },
)
}

Expand Down Expand Up @@ -844,7 +845,7 @@ impl ChainSpecBuilder {
self = self.london_activated();
self.hardforks.insert(
EthereumHardfork::Paris,
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 0, total_difficulty: U256::ZERO, fork_block: None },
);
self
}
Expand Down Expand Up @@ -886,7 +887,7 @@ impl ChainSpecBuilder {
pub fn build(self) -> ChainSpec {
let paris_block_and_final_difficulty = {
self.hardforks.get(EthereumHardfork::Paris).and_then(|cond| {
if let ForkCondition::TTD { fork_block, total_difficulty } = cond {
if let ForkCondition::TTD { total_difficulty, fork_block, .. } = cond {
fork_block.map(|fork_block| (fork_block, total_difficulty))
} else {
None
Expand Down Expand Up @@ -1131,6 +1132,7 @@ Post-merge hard forks (timestamp based):
.with_fork(
EthereumHardfork::Paris,
ForkCondition::TTD {
activation_block_number: 101,
fork_block: Some(101),
total_difficulty: U256::from(10_790_000),
},
Expand Down Expand Up @@ -1164,6 +1166,7 @@ Post-merge hard forks (timestamp based):
// Fork::ConditionTTD test case without a new chain spec to demonstrate ChainSpec::satisfy
// is independent of ChainSpec for this(these - including ForkCondition::Block) match arm(s)
let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD {
activation_block_number: 101,
fork_block: None,
total_difficulty: U256::from(10_790_000),
});
Expand Down Expand Up @@ -1668,18 +1671,14 @@ Post-merge hard forks (timestamp based):
let chainspec = ChainSpecBuilder::mainnet().build();

// Check that Paris is not active on terminal PoW block #15537393.
let terminal_block_ttd = U256::from(58750003716598352816469_u128);
let terminal_block_difficulty = U256::from(11055787484078698_u128);
assert!(!chainspec
.fork(EthereumHardfork::Paris)
.active_at_ttd(terminal_block_ttd, terminal_block_difficulty));
.active_at_ttd(15537393));

// Check that Paris is active on first PoS block #15537394.
let first_pos_block_ttd = U256::from(58750003716598352816469_u128);
let first_pos_difficulty = U256::ZERO;
assert!(chainspec
.fork(EthereumHardfork::Paris)
.active_at_ttd(first_pos_block_ttd, first_pos_difficulty));
.fork(EthereumHardfork::Paris)
.active_at_ttd(15537394));
}

#[test]
Expand Down Expand Up @@ -2162,7 +2161,7 @@ Post-merge hard forks (timestamp based):
fn holesky_paris_activated_at_genesis() {
assert!(HOLESKY
.fork(EthereumHardfork::Paris)
.active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty));
.active_at_ttd(0));
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions crates/consensus/beacon/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1991,7 +1991,7 @@ mod tests {
};
use alloy_rpc_types_engine::{ForkchoiceState, ForkchoiceUpdated, PayloadStatus};
use assert_matches::assert_matches;
use reth_chainspec::{ChainSpecBuilder, MAINNET};
use reth_chainspec::{ChainSpecBuilder, EthereumHardfork, MAINNET};
use reth_node_types::FullNodePrimitives;
use reth_primitives::BlockExt;
use reth_provider::{BlockWriter, ProviderFactory, StorageLocation};
Expand Down Expand Up @@ -2458,7 +2458,7 @@ mod tests {
.chain(MAINNET.chain)
.genesis(MAINNET.genesis.clone())
.london_activated()
.paris_at_ttd(U256::from(3))
.paris_at_ttd( U256::from(3), EthereumHardfork::London.activation_block(MAINNET.chain).unwrap())
.build(),
);

Expand Down
8 changes: 4 additions & 4 deletions crates/consensus/common/src/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ use reth_chainspec::{EthereumHardfork, Hardforks};
pub fn base_block_reward(
chain_spec: impl Hardforks,
block_number: BlockNumber,
block_difficulty: U256,
total_difficulty: U256,
_block_difficulty: U256,
_total_difficulty: U256,
) -> Option<u128> {
if chain_spec.fork(EthereumHardfork::Paris).active_at_ttd(total_difficulty, block_difficulty) {
if chain_spec.fork(EthereumHardfork::Paris).active_at_ttd(block_number) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I keep the active_at_ttd function as it is and add a new active_at_merge method instead?

None
} else {
Some(base_block_reward_pre_merge(chain_spec, block_number))
Expand Down Expand Up @@ -126,7 +126,7 @@ mod tests {
// Petersburg
((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
// Merge
((10000000, U256::from(58_750_000_000_000_000_000_000_u128)), None),
((20000000, U256::from(58_750_000_000_000_000_000_000_u128)), None),
];

for ((block_number, td), expected_reward) in cases {
Expand Down
6 changes: 3 additions & 3 deletions crates/ethereum-forks/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl core::fmt::Display for DisplayFork {
ForkCondition::Block(at) | ForkCondition::Timestamp(at) => {
write!(f, "{name_with_eip:32} @{at}")?;
}
ForkCondition::TTD { fork_block, total_difficulty } => {
ForkCondition::TTD { fork_block, total_difficulty, .. } => {
write!(
f,
"{:32} @{} ({})",
Expand Down Expand Up @@ -154,9 +154,9 @@ impl DisplayHardforks {
ForkCondition::Block(_) => {
pre_merge.push(display_fork);
}
ForkCondition::TTD { total_difficulty, .. } => {
ForkCondition::TTD { activation_block_number, total_difficulty, fork_block } => {
display_fork.activated_at =
ForkCondition::TTD { fork_block: known_paris_block, total_difficulty };
ForkCondition::TTD { activation_block_number, fork_block, total_difficulty };
with_merge.push(display_fork);
}
ForkCondition::Timestamp(_) => {
Expand Down
30 changes: 16 additions & 14 deletions crates/ethereum-forks/src/forkcondition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub enum ForkCondition {
Block(BlockNumber),
/// The fork is activated after a total difficulty has been reached.
TTD {
/// The activation block number for the fork.
activation_block_number: BlockNumber,
/// The block number at which TTD is reached, if it is known.
///
/// This should **NOT** be set unless you want this block advertised as [EIP-2124][eip2124]
Expand Down Expand Up @@ -58,9 +60,9 @@ impl ForkCondition {
/// `58_750_000_000_000_000_000_000`)
///
/// This will return false for any condition that is not TTD-based.
pub fn active_at_ttd(&self, ttd: U256, difficulty: U256) -> bool {
matches!(self, Self::TTD { total_difficulty, .. }
if ttd.saturating_sub(difficulty) >= *total_difficulty)
pub fn active_at_ttd(&self, block_number: BlockNumber) -> bool {
matches!(self, Self::TTD { activation_block_number, .. }
if block_number >= *activation_block_number)
}

/// Checks whether the fork condition is satisfied at the given timestamp.
Expand All @@ -87,7 +89,7 @@ impl ForkCondition {
pub fn active_at_head(&self, head: &Head) -> bool {
self.active_at_block(head.number) ||
self.active_at_timestamp(head.timestamp) ||
self.active_at_ttd(head.total_difficulty, head.difficulty)
self.active_at_ttd(head.number)
}

/// Get the total terminal difficulty for this fork condition.
Expand Down Expand Up @@ -128,15 +130,15 @@ mod tests {

// Test if TTD-based condition with known block activates
let fork_condition =
ForkCondition::TTD { fork_block: Some(10), total_difficulty: U256::from(1000) };
ForkCondition::TTD { activation_block_number: 10, fork_block: Some(10), total_difficulty: U256::from(1000) };
assert!(
fork_condition.active_at_block(10),
"The TTD condition should be active at block 10"
);

// Test if TTD-based condition with unknown block does not activate
let fork_condition =
ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(1000) };
ForkCondition::TTD { activation_block_number: 10, fork_block: None, total_difficulty: U256::from(1000) };
assert!(
!fork_condition.active_at_block(10),
"The TTD condition should not be active at block 10 with an unknown block number"
Expand Down Expand Up @@ -167,21 +169,21 @@ mod tests {
fn test_active_at_ttd() {
// Test if the condition activates at the correct total difficulty
let fork_condition =
ForkCondition::TTD { fork_block: Some(10), total_difficulty: U256::from(1000) };
ForkCondition::TTD { activation_block_number: 10, fork_block: Some(10), total_difficulty: U256::from(1000) };
assert!(
fork_condition.active_at_ttd(U256::from(1000000), U256::from(100)),
fork_condition.active_at_ttd(10),
"The TTD condition should be active when the total difficulty matches"
);

// Test if the condition does not activate when the total difficulty is lower
assert!(
!fork_condition.active_at_ttd(U256::from(900), U256::from(100)),
!fork_condition.active_at_ttd(9),
"The TTD condition should not be active when the total difficulty is lower"
);

// Test with a saturated subtraction
assert!(
!fork_condition.active_at_ttd(U256::from(900), U256::from(1000)),
!fork_condition.active_at_ttd(9),
"The TTD condition should not be active when the subtraction saturates"
);
}
Expand Down Expand Up @@ -259,25 +261,25 @@ mod tests {

// Test if the condition activates based on total difficulty and block number
let fork_condition =
ForkCondition::TTD { fork_block: Some(9), total_difficulty: U256::from(900) };
ForkCondition::TTD { activation_block_number: 10, fork_block: Some(9), total_difficulty: U256::from(900) };
assert!(
fork_condition.active_at_head(&head),
"The condition should be active at the given head total difficulty"
);
let fork_condition =
ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(900) };
ForkCondition::TTD { activation_block_number: 10, fork_block: None, total_difficulty: U256::from(900) };
assert!(
fork_condition.active_at_head(&head),
"The condition should be active at the given head total difficulty as the block number is unknown"
);
let fork_condition =
ForkCondition::TTD { fork_block: Some(11), total_difficulty: U256::from(900) };
ForkCondition::TTD { activation_block_number: 10, fork_block: Some(11), total_difficulty: U256::from(900) };
assert!(
fork_condition.active_at_head(&head),
"The condition should be active as the total difficulty is higher"
);
let fork_condition =
ForkCondition::TTD { fork_block: Some(10), total_difficulty: U256::from(9000) };
ForkCondition::TTD { activation_block_number: 10, fork_block: Some(10), total_difficulty: U256::from(9000) };
assert!(
fork_condition.active_at_head(&head),
"The condition should be active as the total difficulty is higher than head"
Expand Down
2 changes: 1 addition & 1 deletion crates/ethereum-forks/src/hardfork/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub static DEV_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| {
(EthereumHardfork::London.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: None, total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 0, fork_block: None, total_difficulty: U256::ZERO },
),
(EthereumHardfork::Shanghai.boxed(), ForkCondition::Timestamp(0)),
(EthereumHardfork::Cancun.boxed(), ForkCondition::Timestamp(0)),
Expand Down
4 changes: 3 additions & 1 deletion crates/ethereum-forks/src/hardfork/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ impl EthereumHardfork {
(
Self::Paris,
ForkCondition::TTD {
activation_block_number: 15537394,
fork_block: None,
total_difficulty: uint!(58_750_000_000_000_000_000_000_U256),
},
Expand Down Expand Up @@ -379,6 +380,7 @@ impl EthereumHardfork {
(
Self::Paris,
ForkCondition::TTD {
activation_block_number: 1735371,
fork_block: Some(1735371),
total_difficulty: uint!(17_000_000_000_000_000_U256),
},
Expand All @@ -403,7 +405,7 @@ impl EthereumHardfork {
(Self::MuirGlacier, ForkCondition::Block(0)),
(Self::Berlin, ForkCondition::Block(0)),
(Self::London, ForkCondition::Block(0)),
(Self::Paris, ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }),
(Self::Paris, ForkCondition::TTD { activation_block_number: 0, fork_block: Some(0), total_difficulty: U256::ZERO }),
(Self::Shanghai, ForkCondition::Timestamp(1696000704)),
(Self::Cancun, ForkCondition::Timestamp(1707305664)),
]
Expand Down
2 changes: 1 addition & 1 deletion crates/ethereum/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ where
let is_post_merge = self
.chain_spec
.fork(EthereumHardfork::Paris)
.active_at_ttd(total_difficulty, header.difficulty());
.active_at_ttd(header.number());

if is_post_merge {
// TODO: add `is_zero_difficulty` to `alloy_consensus::BlockHeader` trait
Expand Down
1 change: 1 addition & 0 deletions crates/optimism/chainspec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ impl From<Genesis> for OpChainSpec {
block_hardforks.push((
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD {
activation_block_number: genesis.config.merge_netsplit_block.expect("merge netsplit block is required"),
total_difficulty: ttd,
fork_block: genesis.config.merge_netsplit_block,
},
Expand Down
2 changes: 1 addition & 1 deletion crates/optimism/hardforks/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub static DEV_HARDFORKS: LazyLock<ChainHardforks> = LazyLock::new(|| {
(EthereumHardfork::London.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: None, total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 0, fork_block: None, total_difficulty: U256::ZERO },
),
(crate::OpHardfork::Bedrock.boxed(), ForkCondition::Block(0)),
(crate::OpHardfork::Regolith.boxed(), ForkCondition::Timestamp(0)),
Expand Down
8 changes: 4 additions & 4 deletions crates/optimism/hardforks/src/hardfork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl OpHardfork {
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(105235063)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(105235063), total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 105235063, fork_block: Some(105235063), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(105235063)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
Expand Down Expand Up @@ -251,7 +251,7 @@ impl OpHardfork {
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 0, fork_block: Some(0), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(0)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
Expand Down Expand Up @@ -283,7 +283,7 @@ impl OpHardfork {
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 0, fork_block: Some(0), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(0)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
Expand Down Expand Up @@ -315,7 +315,7 @@ impl OpHardfork {
(EthereumHardfork::GrayGlacier.boxed(), ForkCondition::Block(0)),
(
EthereumHardfork::Paris.boxed(),
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO },
ForkCondition::TTD { activation_block_number: 0, fork_block: Some(0), total_difficulty: U256::ZERO },
),
(Self::Bedrock.boxed(), ForkCondition::Block(0)),
(Self::Regolith.boxed(), ForkCondition::Timestamp(0)),
Expand Down
Loading