From eba63b8f778d3f47893d9cc8405147014f63936a Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Sat, 11 Feb 2023 22:49:01 +0200 Subject: [PATCH] fix: chainspec ttd check (#1285) Co-authored-by: Georgios Konstantopoulos --- .../consensus/src/beacon/beacon_consensus.rs | 9 +++-- crates/executor/src/config.rs | 6 ++- crates/executor/src/executor.rs | 6 ++- crates/interfaces/src/consensus.rs | 2 +- crates/interfaces/src/p2p/bodies/response.rs | 10 ++++- crates/interfaces/src/test_utils/headers.rs | 2 +- crates/net/network/src/config.rs | 3 +- crates/primitives/src/chain/spec.rs | 37 ++++++++++++++++--- crates/primitives/src/header.rs | 4 +- crates/rpc/rpc-engine-api/src/engine_api.rs | 3 +- crates/stages/src/stages/bodies.rs | 5 ++- crates/stages/src/stages/total_difficulty.rs | 2 +- crates/storage/provider/src/utils.rs | 8 +++- 13 files changed, 73 insertions(+), 24 deletions(-) diff --git a/crates/consensus/src/beacon/beacon_consensus.rs b/crates/consensus/src/beacon/beacon_consensus.rs index 7f243383e0..6da2eb4721 100644 --- a/crates/consensus/src/beacon/beacon_consensus.rs +++ b/crates/consensus/src/beacon/beacon_consensus.rs @@ -50,7 +50,8 @@ impl Consensus for BeaconConsensus { } fn validate_header(&self, header: &SealedHeader, total_difficulty: U256) -> Result<(), Error> { - if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty) { + if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, header.difficulty) + { // EIP-3675: Upgrade consensus to Proof-of-Stake: // https://eips.ethereum.org/EIPS/eip-3675#replacing-difficulty-with-0 if header.difficulty != U256::ZERO { @@ -80,8 +81,8 @@ impl Consensus for BeaconConsensus { validation::validate_block_standalone(block) } - fn has_block_reward(&self, total_difficulty: U256) -> bool { - !self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty) + fn has_block_reward(&self, total_difficulty: U256, difficulty: U256) -> bool { + !self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, difficulty) } } @@ -96,6 +97,6 @@ mod test { fn test_has_block_reward_before_paris() { let chain_spec = ChainSpecBuilder::mainnet().build(); let (consensus, _) = BeaconConsensus::builder().build(chain_spec); - assert!(consensus.has_block_reward(U256::ZERO)); + assert!(consensus.has_block_reward(U256::ZERO, U256::ZERO)); } } diff --git a/crates/executor/src/config.rs b/crates/executor/src/config.rs index 7052e5b828..5525b62f09 100644 --- a/crates/executor/src/config.rs +++ b/crates/executor/src/config.rs @@ -98,7 +98,8 @@ mod tests { revm_spec( &MAINNET, Head { - total_difficulty: U256::from(58_750_000_000_000_000_000_000u128), + total_difficulty: U256::from(58_750_000_000_000_000_000_010_u128), + difficulty: U256::from(10_u128), ..Default::default() } ), @@ -110,7 +111,8 @@ mod tests { &MAINNET, Head { number: 15537394 - 10, - total_difficulty: U256::from(58_750_000_000_000_000_000_000u128), + total_difficulty: U256::from(58_750_000_000_000_000_000_010_u128), + difficulty: U256::from(10_u128), ..Default::default() } ), diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index aab7010e58..e3985e8208 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -69,8 +69,9 @@ where Head { number: header.number, timestamp: header.timestamp, + difficulty: header.difficulty, total_difficulty, - ..Default::default() + hash: Default::default(), }, ); @@ -220,7 +221,8 @@ where // 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 Ω: - if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty) { + if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, header.difficulty) + { None } else if self.chain_spec.fork(Hardfork::Petersburg).active_at_block(header.number) { Some(WEI_2ETH) diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index e728d5df56..b3dd3d7bb4 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -45,7 +45,7 @@ pub trait Consensus: Debug + Send + Sync { /// This flag is needed as reth's changeset is indexed on transaction level granularity. /// /// More info [here](https://github.com/paradigmxyz/reth/issues/237) - fn has_block_reward(&self, total_difficulty: U256) -> bool; + fn has_block_reward(&self, total_difficulty: U256, difficulty: U256) -> bool; } /// Consensus Errors diff --git a/crates/interfaces/src/p2p/bodies/response.rs b/crates/interfaces/src/p2p/bodies/response.rs index db64915951..dac7bf2763 100644 --- a/crates/interfaces/src/p2p/bodies/response.rs +++ b/crates/interfaces/src/p2p/bodies/response.rs @@ -1,4 +1,4 @@ -use reth_primitives::{BlockNumber, SealedBlock, SealedHeader}; +use reth_primitives::{BlockNumber, SealedBlock, SealedHeader, U256}; /// The block response #[derive(PartialEq, Eq, Debug)] @@ -22,4 +22,12 @@ impl BlockResponse { pub fn block_number(&self) -> BlockNumber { self.header().number } + + /// Return the reference to the response header + pub fn difficulty(&self) -> U256 { + match self { + BlockResponse::Full(block) => block.difficulty, + BlockResponse::Empty(header) => header.difficulty, + } + } } diff --git a/crates/interfaces/src/test_utils/headers.rs b/crates/interfaces/src/test_utils/headers.rs index d1412eab4f..e0d929d3f4 100644 --- a/crates/interfaces/src/test_utils/headers.rs +++ b/crates/interfaces/src/test_utils/headers.rs @@ -340,7 +340,7 @@ impl Consensus for TestConsensus { } } - fn has_block_reward(&self, _: U256) -> bool { + fn has_block_reward(&self, _: U256, _: U256) -> bool { true } } diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index 852b79e40d..776e172614 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -307,8 +307,9 @@ impl NetworkConfigBuilder { let head = head.unwrap_or(Head { hash: chain_spec.genesis_hash(), number: 0, - total_difficulty: U256::ZERO, timestamp: 0, + difficulty: chain_spec.genesis.difficulty, + total_difficulty: U256::ZERO, }); // set the status diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index cee8a7832c..df8a1f2ca8 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -32,7 +32,7 @@ pub static MAINNET: Lazy = Lazy::new(|| ChainSpec { Hardfork::Paris, ForkCondition::TTD { fork_block: None, - total_difficulty: U256::from(58_750_000_000_000_000_000_000u128), + total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), }, ), ]), @@ -472,12 +472,17 @@ impl ForkCondition { } } - /// Checks whether the fork condition is satisfied at the given total difficulty. + /// Checks whether the fork condition is satisfied at the given total difficulty and difficulty + /// of a current block. + /// + /// The fork is considered active if the _previous_ total difficulty is above the threshold. + /// To achieve that, we subtract the passed `difficulty` from the current block's total difficulty, + /// and check if it's above the Fork Condition's total difficulty (here: 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) -> bool { + pub fn active_at_ttd(&self, ttd: U256, difficulty: U256) -> bool { if let ForkCondition::TTD { total_difficulty, .. } = self { - ttd >= *total_difficulty + ttd.saturating_sub(difficulty) >= *total_difficulty } else { false } @@ -504,7 +509,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) + self.active_at_ttd(head.total_difficulty, head.difficulty) } /// Get the total terminal difficulty for this fork condition. @@ -532,6 +537,8 @@ impl ForkCondition { #[cfg(test)] mod tests { + use revm_primitives::U256; + use crate::{ Chain, ChainSpec, ChainSpecBuilder, ForkCondition, ForkHash, ForkId, Genesis, Hardfork, Head, GOERLI, MAINNET, SEPOLIA, @@ -834,4 +841,24 @@ mod tests { ], ); } + + /// Checks that the fork is not active at a terminal ttd block. + #[test] + fn check_terminal_ttd() { + 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(Hardfork::Paris) + .active_at_ttd(terminal_block_ttd, terminal_block_difficulty)); + + // 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(Hardfork::Paris) + .active_at_ttd(first_pos_block_ttd, first_pos_difficulty)); + } } diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index 92870f6b74..75cf618a53 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -24,7 +24,9 @@ pub struct Head { pub number: BlockNumber, /// The hash of the head block. pub hash: H256, - /// The total difficulty of the head block. + /// The difficulty of the head block. + pub difficulty: U256, + /// The total difficulty at the head block. pub total_difficulty: U256, /// The timestamp of the head block. pub timestamp: u64, diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 3ab80e35e5..1c1130b03b 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -161,7 +161,8 @@ impl EngineApi { })) }; - if !self.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td) { + // Short circuit the check by passing parent total difficulty. + if !self.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO) { return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { validation_error: EngineApiError::PayloadPreMerge.to_string(), })) diff --git a/crates/stages/src/stages/bodies.rs b/crates/stages/src/stages/bodies.rs index bbb0f77fa4..a0b5537418 100644 --- a/crates/stages/src/stages/bodies.rs +++ b/crates/stages/src/stages/bodies.rs @@ -113,6 +113,7 @@ impl Stage for BodyStage { for response in downloaded_bodies { // Write block let block_number = response.block_number(); + let difficulty = response.difficulty(); match response { BlockResponse::Full(block) => { @@ -161,7 +162,7 @@ impl Stage for BodyStage { .seek(block_number)? .ok_or(ProviderError::TotalDifficulty { number: block_number })? .1; - let has_reward = self.consensus.has_block_reward(td.into()); + let has_reward = self.consensus.has_block_reward(td.into(), difficulty); if has_reward { transition_id += 1; } @@ -664,7 +665,7 @@ mod tests { .seek(number)? .expect("Missing TD for header") .1; - if self.consensus.has_block_reward(td.into()) { + if self.consensus.has_block_reward(td.into(), header.difficulty) { expected_transition_id += 1; } diff --git a/crates/stages/src/stages/total_difficulty.rs b/crates/stages/src/stages/total_difficulty.rs index fb07d551cc..f05392886c 100644 --- a/crates/stages/src/stages/total_difficulty.rs +++ b/crates/stages/src/stages/total_difficulty.rs @@ -73,7 +73,7 @@ impl Stage for TotalDifficultyStage { let (key, header) = entry?; td += header.difficulty; - if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(td) { + if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(td, header.difficulty) { if header.difficulty != U256::ZERO { return Err(StageError::Validation { block: header.number, diff --git a/crates/storage/provider/src/utils.rs b/crates/storage/provider/src/utils.rs index 3c6e5d60b6..28d4f750dc 100644 --- a/crates/storage/provider/src/utils.rs +++ b/crates/storage/provider/src/utils.rs @@ -25,8 +25,12 @@ pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>( tx.put::(block.hash(), block.number)?; tx.put::( block.number, - if has_block_reward { U256::ZERO } else { U256::from(58_750_000_000_000_000_000_000u128) } - .into(), + if has_block_reward { + U256::ZERO + } else { + U256::from(58_750_000_000_000_000_000_000_u128) + block.difficulty + } + .into(), )?; // insert body ommers data