diff --git a/crates/consensus/beacon-core/Cargo.toml b/crates/consensus/beacon-core/Cargo.toml index 01623eaf9e6..232631f73b6 100644 --- a/crates/consensus/beacon-core/Cargo.toml +++ b/crates/consensus/beacon-core/Cargo.toml @@ -14,4 +14,7 @@ workspace = true # reth reth-consensus-common.workspace = true reth-primitives.workspace = true -reth-interfaces.workspace = true \ No newline at end of file +reth-interfaces.workspace = true + +[features] +optimism = ["reth-primitives/optimism"] \ No newline at end of file diff --git a/crates/consensus/beacon-core/src/lib.rs b/crates/consensus/beacon-core/src/lib.rs index e9b4114a3b7..599e0100922 100644 --- a/crates/consensus/beacon-core/src/lib.rs +++ b/crates/consensus/beacon-core/src/lib.rs @@ -46,14 +46,27 @@ impl Consensus for BeaconConsensus { Ok(()) } + #[allow(unused_assignments)] + #[allow(unused_mut)] fn validate_header_with_total_difficulty( &self, header: &Header, total_difficulty: U256, ) -> Result<(), ConsensusError> { - if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, header.difficulty) + let mut is_post_merge = self + .chain_spec + .fork(Hardfork::Paris) + .active_at_ttd(total_difficulty, header.difficulty); + + #[cfg(feature = "optimism")] { - if !header.is_zero_difficulty() { + // If OP-Stack then bedrock activation number determines when TTD (eth Merge) has been + // reached. + is_post_merge = self.chain_spec.is_bedrock_active_at_block(header.number); + } + + if is_post_merge { + if !self.chain_spec.is_optimism() && !header.is_zero_difficulty() { return Err(ConsensusError::TheMergeDifficultyIsNotZero) } @@ -94,10 +107,10 @@ impl Consensus for BeaconConsensus { }) } - // Goerli exception: + // Goerli and early OP exception: // * If the network is goerli pre-merge, ignore the extradata check, since we do not - // support clique. - if self.chain_spec.chain != Chain::goerli() { + // support clique. Same goes for OP blocks below Bedrock. + if self.chain_spec.chain != Chain::goerli() && !self.chain_spec.is_optimism() { validate_header_extradata(header)?; } } diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index 52389143c40..2c86a4d414e 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -65,4 +65,5 @@ optimism = [ "reth-interfaces/optimism", "reth-provider/optimism", "reth-blockchain-tree/optimism", + "reth-beacon-consensus-core/optimism" ] diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 160eddf9df5..f8b599f8dc7 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -574,6 +574,12 @@ impl ChainSpec { self.chain.is_optimism() } + /// Returns `true` if this chain is Optimism mainnet. + #[inline] + pub fn is_optimism_mainnet(&self) -> bool { + self.chain == Chain::optimism_mainnet() + } + /// Get the genesis block specification. /// /// To get the header for the genesis block, use [`Self::genesis_header`] instead. @@ -779,6 +785,13 @@ impl ChainSpec { self.fork(Hardfork::Homestead).active_at_block(block_number) } + /// Convenience method to check if [Hardfork::Bedrock] is active at a given block number. + #[cfg(feature = "optimism")] + #[inline] + pub fn is_bedrock_active_at_block(&self, block_number: u64) -> bool { + self.fork(Hardfork::Bedrock).active_at_block(block_number) + } + /// Creates a [`ForkFilter`] for the block described by [Head]. pub fn fork_filter(&self, head: Head) -> ForkFilter { let forks = self.forks_iter().filter_map(|(_, condition)| { @@ -3176,4 +3189,10 @@ Post-merge hard forks (timestamp based): BASE_MAINNET.latest_fork_id() ) } + + #[cfg(feature = "optimism")] + #[test] + fn is_bedrock_active() { + assert!(!OP_MAINNET.is_bedrock_active_at_block(1)) + } }