diff --git a/crates/consensus/beacon/src/beacon_consensus.rs b/crates/consensus/beacon/src/beacon_consensus.rs index 449d46845c76..897bc25f4a1f 100644 --- a/crates/consensus/beacon/src/beacon_consensus.rs +++ b/crates/consensus/beacon/src/beacon_consensus.rs @@ -2,10 +2,10 @@ use reth_consensus_common::validation; use reth_interfaces::consensus::{Consensus, ConsensusError}; use reth_primitives::{ - constants::MAXIMUM_EXTRA_DATA_SIZE, Chain, ChainSpec, Hardfork, Header, SealedBlock, - SealedHeader, EMPTY_OMMER_ROOT, U256, + constants::{ALLOWED_FUTURE_BLOCK_TIME_SECONDS, MAXIMUM_EXTRA_DATA_SIZE}, + Chain, ChainSpec, Hardfork, Header, SealedBlock, SealedHeader, EMPTY_OMMER_ROOT, U256, }; -use std::sync::Arc; +use std::{sync::Arc, time::SystemTime}; /// Ethereum beacon consensus /// @@ -59,6 +59,14 @@ impl Consensus for BeaconConsensus { return Err(ConsensusError::TheMergeOmmerRootIsNotEmpty) } + // Post-merge, the consensus layer is expected to perform checks such that the block + // timestamp is a function of the slot. This is different from pre-merge, where blocks + // are only allowed to be in the future (compared to the system's clock) by a certain + // threshold. + // + // Block validation with respect to the parent should ensure that the block timestamp + // is greater than its parent timestamp. + // validate header extradata for all networks post merge validate_header_extradata(header)?; @@ -69,6 +77,17 @@ impl Consensus for BeaconConsensus { // * difficulty, mix_hash & nonce aka PoW stuff // low priority as syncing is done in reverse order + // Check if timestamp is in future. Clock can drift but this can be consensus issue. + let present_timestamp = + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + + if header.timestamp > present_timestamp + ALLOWED_FUTURE_BLOCK_TIME_SECONDS { + return Err(ConsensusError::TimestampIsInFuture { + timestamp: header.timestamp, + present_timestamp, + }) + } + // Goerli exception: // * If the network is goerli pre-merge, ignore the extradata check, since we do not // support clique. diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index f61e160f0390..485d6146737b 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -5,10 +5,7 @@ use reth_primitives::{ SealedHeader, Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy, }; use reth_provider::{AccountReader, HeaderProvider, WithdrawalsProvider}; -use std::{ - collections::{hash_map::Entry, HashMap}, - time::SystemTime, -}; +use std::collections::{hash_map::Entry, HashMap}; /// Validate header standalone pub fn validate_header_standalone( @@ -23,16 +20,6 @@ pub fn validate_header_standalone( }) } - // Check if timestamp is in future. Clock can drift but this can be consensus issue. - let present_timestamp = - SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); - if header.timestamp > present_timestamp { - return Err(ConsensusError::TimestampIsInFuture { - timestamp: header.timestamp, - present_timestamp, - }) - } - // Check if base fee is set. if chain_spec.fork(Hardfork::London).active_at_block(header.number) && header.base_fee_per_gas.is_none() diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index 8530a2bc67c8..048a8801c4b3 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -108,6 +108,15 @@ pub const EMPTY_WITHDRAWALS: H256 = EMPTY_SET_HASH; /// the database. pub const BEACON_CONSENSUS_REORG_UNWIND_DEPTH: u64 = 3; +/// Max seconds from current time allowed for blocks, before they're considered future blocks. +/// +/// This is only used when checking whether or not the timestamp for pre-merge blocks is in the +/// future. +/// +/// See: +/// +pub const ALLOWED_FUTURE_BLOCK_TIME_SECONDS: u64 = 15; + #[cfg(test)] mod tests { use super::*;