fix: chainspec ttd check (#1285)

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
Roman Krasiuk
2023-02-11 22:49:01 +02:00
committed by GitHub
parent 4b0acce957
commit eba63b8f77
13 changed files with 73 additions and 24 deletions

View File

@@ -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));
}
}

View File

@@ -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()
}
),

View File

@@ -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 blocks 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)

View File

@@ -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

View File

@@ -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,
}
}
}

View File

@@ -340,7 +340,7 @@ impl Consensus for TestConsensus {
}
}
fn has_block_reward(&self, _: U256) -> bool {
fn has_block_reward(&self, _: U256, _: U256) -> bool {
true
}
}

View File

@@ -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

View File

@@ -32,7 +32,7 @@ pub static MAINNET: Lazy<ChainSpec> = 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));
}
}

View File

@@ -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,

View File

@@ -161,7 +161,8 @@ impl<Client: HeaderProvider + BlockProvider + StateProvider> EngineApi<Client> {
}))
};
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(),
}))

View File

@@ -113,6 +113,7 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
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<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
.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;
}

View File

@@ -73,7 +73,7 @@ impl<DB: Database> Stage<DB> 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,

View File

@@ -25,8 +25,12 @@ pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>(
tx.put::<tables::HeaderNumbers>(block.hash(), block.number)?;
tx.put::<tables::HeaderTD>(
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