From b8e36499130d93b9c35c244307bcb7e40a9df0e5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 3 Dec 2024 13:13:54 -0600 Subject: [PATCH 1/8] Add placeholder OpHardfork::Isthmus --- crates/optimism/chainspec/src/lib.rs | 11 ++++++++++- crates/optimism/evm/src/config.rs | 8 ++++++-- crates/optimism/hardforks/src/hardfork.rs | 7 ++++++- crates/optimism/hardforks/src/lib.rs | 12 +++++++++--- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/crates/optimism/chainspec/src/lib.rs b/crates/optimism/chainspec/src/lib.rs index a3dab80705e5..e4e2047369b7 100644 --- a/crates/optimism/chainspec/src/lib.rs +++ b/crates/optimism/chainspec/src/lib.rs @@ -36,7 +36,7 @@ use reth_chainspec::{ }; use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork}; use reth_network_peers::NodeRecord; -use reth_optimism_forks::OpHardforks; +use reth_optimism_forks::{OpHardfork, OpHardforks}; #[cfg(feature = "std")] pub(crate) use std::sync::LazyLock; @@ -166,6 +166,13 @@ impl OpChainSpecBuilder { self } + /// Enable Isthmus at genesis + pub fn isthmus_activated(mut self) -> Self { + self = self.holocene_activated(); + self.inner = self.inner.with_fork(OpHardfork::Isthmus, ForkCondition::Timestamp(0)); + self + } + /// Build the resulting [`OpChainSpec`]. /// /// # Panics @@ -401,6 +408,7 @@ impl From for OpChainSpec { (OpHardfork::Fjord.boxed(), genesis_info.fjord_time), (OpHardfork::Granite.boxed(), genesis_info.granite_time), (OpHardfork::Holocene.boxed(), genesis_info.holocene_time), + (OpHardfork::Isthmus.boxed(), genesis_info.isthmus_time), ]; let mut time_hardforks = time_hardfork_opts @@ -1017,6 +1025,7 @@ mod tests { OpHardfork::Fjord.boxed(), OpHardfork::Granite.boxed(), OpHardfork::Holocene.boxed(), + OpHardfork::Isthmus.boxed(), ]; assert!(expected_hardforks diff --git a/crates/optimism/evm/src/config.rs b/crates/optimism/evm/src/config.rs index f2d35ba56c41..bdb590759c9d 100644 --- a/crates/optimism/evm/src/config.rs +++ b/crates/optimism/evm/src/config.rs @@ -12,7 +12,9 @@ pub fn revm_spec_by_timestamp_after_bedrock( chain_spec: &OpChainSpec, timestamp: u64, ) -> revm_primitives::SpecId { - if chain_spec.fork(OpHardfork::Holocene).active_at_timestamp(timestamp) { + if chain_spec.fork(OpHardfork::Isthmus).active_at_timestamp(timestamp) { + todo!() + } else if chain_spec.fork(OpHardfork::Holocene).active_at_timestamp(timestamp) { revm_primitives::HOLOCENE } else if chain_spec.fork(OpHardfork::Granite).active_at_timestamp(timestamp) { revm_primitives::GRANITE @@ -31,7 +33,9 @@ pub fn revm_spec_by_timestamp_after_bedrock( /// Map the latest active hardfork at the given block to a revm [`SpecId`](revm_primitives::SpecId). pub fn revm_spec(chain_spec: &OpChainSpec, block: &Head) -> revm_primitives::SpecId { - if chain_spec.fork(OpHardfork::Holocene).active_at_head(block) { + if chain_spec.fork(OpHardfork::Isthmus).active_at_head(block) { + todo!() + } else if chain_spec.fork(OpHardfork::Holocene).active_at_head(block) { revm_primitives::HOLOCENE } else if chain_spec.fork(OpHardfork::Granite).active_at_head(block) { revm_primitives::GRANITE diff --git a/crates/optimism/hardforks/src/hardfork.rs b/crates/optimism/hardforks/src/hardfork.rs index 661816ae5fe0..962d7bca4bcd 100644 --- a/crates/optimism/hardforks/src/hardfork.rs +++ b/crates/optimism/hardforks/src/hardfork.rs @@ -33,6 +33,8 @@ hardfork!( Granite, /// Holocene: Holocene, + /// Isthmus: + Isthmus, } ); @@ -159,6 +161,7 @@ impl OpHardfork { Self::Fjord => Some(1716998400), Self::Granite => Some(1723478400), Self::Holocene => Some(1732633200), + Self::Isthmus => todo!(), }, ) } @@ -194,6 +197,7 @@ impl OpHardfork { Self::Fjord => Some(1720627201), Self::Granite => Some(1726070401), Self::Holocene => None, + Self::Isthmus => todo!(), }, ) } @@ -357,7 +361,7 @@ mod tests { #[test] fn check_op_hardfork_from_str() { let hardfork_str = - ["beDrOck", "rEgOlITH", "cAnYoN", "eCoToNe", "FJorD", "GRaNiTe", "hOlOcEnE"]; + ["beDrOck", "rEgOlITH", "cAnYoN", "eCoToNe", "FJorD", "GRaNiTe", "hOlOcEnE", "isthMUS"]; let expected_hardforks = [ OpHardfork::Bedrock, OpHardfork::Regolith, @@ -366,6 +370,7 @@ mod tests { OpHardfork::Fjord, OpHardfork::Granite, OpHardfork::Holocene, + OpHardfork::Isthmus, ]; let hardforks: Vec = diff --git a/crates/optimism/hardforks/src/lib.rs b/crates/optimism/hardforks/src/lib.rs index bf6ca98ce4e9..36f42155e942 100644 --- a/crates/optimism/hardforks/src/lib.rs +++ b/crates/optimism/hardforks/src/lib.rs @@ -27,6 +27,12 @@ pub trait OpHardforks: EthereumHardforks { self.fork(OpHardfork::Bedrock).active_at_block(block_number) } + /// Returns `true` if [`Regolith`](OpHardfork::Regolith) is active at given block + /// timestamp. + fn is_regolith_active_at_timestamp(&self, timestamp: u64) -> bool { + self.fork(OpHardfork::Regolith).active_at_timestamp(timestamp) + } + /// Returns `true` if [`Canyon`](OpHardfork::Canyon) is active at given block timestamp. fn is_canyon_active_at_timestamp(&self, timestamp: u64) -> bool { self.fork(OpHardfork::Canyon).active_at_timestamp(timestamp) @@ -53,9 +59,9 @@ pub trait OpHardforks: EthereumHardforks { self.fork(OpHardfork::Holocene).active_at_timestamp(timestamp) } - /// Returns `true` if [`Regolith`](OpHardfork::Regolith) is active at given block + /// Returns `true` if [`Isthmus`](OpHardfork::Isthmus) is active at given block /// timestamp. - fn is_regolith_active_at_timestamp(&self, timestamp: u64) -> bool { - self.fork(OpHardfork::Regolith).active_at_timestamp(timestamp) + fn is_isthmus_active_at_timestamp(&self, timestamp: u64) -> bool { + self.fork(OpHardfork::Isthmus).active_at_timestamp(timestamp) } } From d4cdf7b705751bae62fa343c74991f69fcfe63a2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 4 Dec 2024 15:44:04 -0600 Subject: [PATCH 2/8] Write storage root of l2-to-l1-message-passer predeploy to header --- Cargo.lock | 27 +++++++++++++----------- crates/consensus/consensus/src/lib.rs | 5 +++++ crates/optimism/consensus/Cargo.toml | 3 +++ crates/optimism/consensus/src/error.rs | 11 ++++++++++ crates/optimism/consensus/src/lib.rs | 17 ++++++++++++--- crates/optimism/payload/src/builder.rs | 29 +++++++++++++++++++------- 6 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 crates/optimism/consensus/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 3448ddbd4519..a9757db7c17e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5329,9 +5329,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77284451ec70602f148f4f3bc6d1106fdfefd57c11ff459c4b2985e400ed1a18" +checksum = "78f0daa0d0936d436a21b57571b1e27c5663aa2ab62f6edae5ba5be999f9f93e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5347,9 +5347,9 @@ dependencies = [ [[package]] name = "op-alloy-genesis" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912ec93ec839076e8bbaaf7bd3d80aeedbe38cd5e8e3e76dfc67d217637e651" +checksum = "3eb0964932faa7050b74689f017aca66ffa3e52501080278a81bb0a43836c8dd" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5362,9 +5362,9 @@ dependencies = [ [[package]] name = "op-alloy-network" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef4620ba6309ecc18e1aaa339836ca839b001a420ca245add040a3bde1ae9b1" +checksum = "cd9a690fcc404e44c3589dd39cf22895df42f7ef8671a07828b8c376c39be46a" dependencies = [ "alloy-consensus", "alloy-network", @@ -5377,9 +5377,9 @@ dependencies = [ [[package]] name = "op-alloy-protocol" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab24c1b9c21cedd691938b5667c951b04ae8b89429d7cb7a88f30afb79cbbf1" +checksum = "6d8c057c1a5bdf72d1f86c470a4d90f2d2ad1b273caa547c04cd6affe45b466d" dependencies = [ "alloc-no-stdlib", "alloy-consensus", @@ -5401,9 +5401,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc32eba4d43bbd23f1f16dece7afd991d41ab4ffc2494a72b048e9f38db622" +checksum = "73741855ffaa2041b33cb616d7db7180c1149b648c68c23bee9e15501073fb32" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5420,9 +5420,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07175fcfd9d03a587ece7ce79fc288331e6d9ae523464eb677c751d5737713b" +checksum = "ebedc32e24013c8b3faea62d091bccbb90f871286fe2238c6f7e2ff29974df8e" dependencies = [ "alloy-eips", "alloy-primitives", @@ -5431,6 +5431,7 @@ dependencies = [ "derive_more 1.0.0", "ethereum_ssz", "op-alloy-consensus", + "op-alloy-genesis", "op-alloy-protocol", "serde", "snap", @@ -8313,6 +8314,7 @@ dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-trie", + "derive_more 1.0.0", "reth-chainspec", "reth-consensus", "reth-consensus-common", @@ -8320,6 +8322,7 @@ dependencies = [ "reth-optimism-forks", "reth-optimism-primitives", "reth-primitives", + "reth-storage-api", "reth-trie-common", "tracing", ] diff --git a/crates/consensus/consensus/src/lib.rs b/crates/consensus/consensus/src/lib.rs index ba1b1321e776..61c9d8dba937 100644 --- a/crates/consensus/consensus/src/lib.rs +++ b/crates/consensus/consensus/src/lib.rs @@ -451,6 +451,11 @@ pub enum ConsensusError { /// The block's timestamp. timestamp: u64, }, + /// Custom error + // todo: remove in favour of AT Consensus::Error, so OpConsensusError can wrap ConsensusError + // in a variant instead + #[display("custom l2 error (search for it in debug logs)")] + Other, } impl ConsensusError { diff --git a/crates/optimism/consensus/Cargo.toml b/crates/optimism/consensus/Cargo.toml index 30f16e4eb228..526fab602f96 100644 --- a/crates/optimism/consensus/Cargo.toml +++ b/crates/optimism/consensus/Cargo.toml @@ -18,6 +18,7 @@ reth-consensus-common.workspace = true reth-consensus.workspace = true reth-primitives.workspace = true reth-trie-common.workspace = true +reth-storage-api.workspace = true # op-reth reth-optimism-forks.workspace = true @@ -29,6 +30,8 @@ alloy-primitives.workspace = true alloy-consensus.workspace = true alloy-trie.workspace = true +# misc +derive_more = { workspace = true, features = ["display", "error"] } tracing.workspace = true [dev-dependencies] diff --git a/crates/optimism/consensus/src/error.rs b/crates/optimism/consensus/src/error.rs new file mode 100644 index 000000000000..18b36519fae7 --- /dev/null +++ b/crates/optimism/consensus/src/error.rs @@ -0,0 +1,11 @@ +//! Optimism consensus errors + +use derive_more::{Display, Error}; + +/// Optimism consensus error. +#[derive(Debug, PartialEq, Eq, Clone, Display, Error)] +pub enum OpConsensusError { + /// Block body has non-empty withdrawals list. + #[display("non-empty withdrawals list")] + WithdrawalsNonEmpty, +} diff --git a/crates/optimism/consensus/src/lib.rs b/crates/optimism/consensus/src/lib.rs index b50efd5f6f26..5dc731eeddff 100644 --- a/crates/optimism/consensus/src/lib.rs +++ b/crates/optimism/consensus/src/lib.rs @@ -19,13 +19,17 @@ use reth_consensus_common::validation::{ validate_against_parent_4844, validate_against_parent_eip1559_base_fee, validate_against_parent_hash_number, validate_against_parent_timestamp, validate_body_against_header, validate_cancun_gas, validate_header_base_fee, - validate_header_extradata, validate_header_gas, validate_shanghai_withdrawals, + validate_header_extradata, validate_header_gas, }; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_forks::OpHardforks; use reth_optimism_primitives::OpPrimitives; use reth_primitives::{BlockBody, BlockWithSenders, GotExpected, SealedBlock, SealedHeader}; use std::{sync::Arc, time::SystemTime}; +use tracing::debug; + +pub mod error; +pub use error::OpConsensusError; mod proof; pub use proof::calculate_receipt_root_no_memo_optimism; @@ -83,8 +87,15 @@ impl Consensus for OpBeaconConsensus { } // EIP-4895: Beacon chain push withdrawals as operations - if self.chain_spec.is_shanghai_active_at_timestamp(block.timestamp) { - validate_shanghai_withdrawals(block)?; + if self.chain_spec.is_shanghai_active_at_timestamp(block.timestamp) && + block.body.withdrawals.as_ref().is_some_and(|withdrawals| !withdrawals.is_empty()) + { + debug!(target: "op::consensus", + block_number=block.number, + err=%OpConsensusError::WithdrawalsNonEmpty, + "block failed validation", + ); + return Err(ConsensusError::Other) } if self.chain_spec.is_cancun_active_at_timestamp(block.timestamp) { diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 91e70d0c3c8b..9f2e20ab525f 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -4,7 +4,7 @@ use std::{fmt::Display, sync::Arc}; use alloy_consensus::{Header, Transaction, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE}; -use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_primitives::{address, Address, Bytes, B256, U256}; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_engine::PayloadId; use reth_basic_payload_builder::*; @@ -24,7 +24,7 @@ use reth_primitives::{ }; use reth_provider::{ HashedPostStateProvider, ProviderError, StateProofProvider, StateProviderFactory, - StateRootProvider, + StateRootProvider, StorageRootProvider, }; use reth_revm::database::StateProviderDatabase; use reth_transaction_pool::{ @@ -49,6 +49,10 @@ use op_alloy_rpc_types_engine::OpPayloadAttributes; use reth_revm::witness::ExecutionWitnessRecord; use reth_transaction_pool::pool::BestPayloadTransactions; +/// The L2 contract `L2ToL1MessagePasser`, stores commitments to withdrawal transactions. +pub const ADDRESS_PREDEPLOY_L2_TO_L1_MESSAGE_PASSER: Address = + address!("4200000000000000000000000000000000000016"); + /// Optimism's payload builder #[derive(Debug, Clone, PartialEq, Eq)] pub struct OpPayloadBuilder { @@ -288,14 +292,15 @@ where Txs: OpPayloadTransactions, { /// Executes the payload and returns the outcome. - pub fn execute( + pub fn execute( self, state: &mut State, ctx: &OpPayloadBuilderCtx, ) -> Result, PayloadBuilderError> where EvmConfig: ConfigureEvm
, - DB: Database, + DB: Database + AsRef

, + P: StorageRootProvider, { let Self { pool, best } = self; debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload"); @@ -323,13 +328,21 @@ where } } - let withdrawals_root = ctx.commit_withdrawals(state)?; + debug_assert!(ctx.attributes().payload_attributes.withdrawals.is_empty()); // merge all transitions into bundle state, this would apply the withdrawal balance changes // and 4788 contract call state.merge_transitions(BundleRetention::Reverts); - Ok(BuildOutcomeKind::Better { payload: ExecutedPayload { info, withdrawals_root } }) + let storage_root_msg_passer = state + .database + .as_ref() + .storage_root(ADDRESS_PREDEPLOY_L2_TO_L1_MESSAGE_PASSER, Default::default())?; + + // withdrawals root field in block header is used for storage root of L2 predeploy + let payload = ExecutedPayload { info, withdrawals_root: Some(storage_root_msg_passer) }; + + Ok(BuildOutcomeKind::Better { payload }) } /// Builds the payload on top of the state. @@ -341,7 +354,7 @@ where where EvmConfig: ConfigureEvm

, DB: Database + AsRef

, - P: StateRootProvider + HashedPostStateProvider, + P: StateRootProvider + HashedPostStateProvider + StorageRootProvider, { let ExecutedPayload { info, withdrawals_root } = match self.execute(&mut state, &ctx)? { BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload, @@ -467,7 +480,7 @@ where where EvmConfig: ConfigureEvm

, DB: Database + AsRef

, - P: StateProofProvider, + P: StateProofProvider + StorageRootProvider, { let _ = self.execute(state, ctx)?; let ExecutionWitnessRecord { hashed_state, codes, keys } = From 7619585658877af84d28301aa830fffa34c4795a Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 7 Dec 2024 11:32:54 -0500 Subject: [PATCH 3/8] Validate isthmus storage root --- Cargo.lock | 2 + crates/optimism/consensus/Cargo.toml | 1 + crates/optimism/consensus/src/error.rs | 26 +++++++- crates/optimism/consensus/src/lib.rs | 67 +++++++++++++++++--- crates/optimism/node/src/node.rs | 13 ++-- crates/optimism/payload/Cargo.toml | 4 +- crates/optimism/payload/src/builder.rs | 9 +-- crates/optimism/primitives/src/lib.rs | 2 + crates/optimism/primitives/src/predeploys.rs | 8 +++ crates/storage/errors/src/lib.rs | 2 + 10 files changed, 113 insertions(+), 21 deletions(-) create mode 100644 crates/optimism/primitives/src/predeploys.rs diff --git a/Cargo.lock b/Cargo.lock index 4b13f506eefc..27f1ef31e188 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8363,6 +8363,7 @@ dependencies = [ "reth-optimism-primitives", "reth-primitives", "reth-storage-api", + "reth-storage-errors", "reth-trie-common", "tracing", ] @@ -8483,6 +8484,7 @@ dependencies = [ "reth-optimism-consensus", "reth-optimism-evm", "reth-optimism-forks", + "reth-optimism-primitives", "reth-payload-builder", "reth-payload-builder-primitives", "reth-payload-primitives", diff --git a/crates/optimism/consensus/Cargo.toml b/crates/optimism/consensus/Cargo.toml index 2f6d2e386c33..57dfc7bcd680 100644 --- a/crates/optimism/consensus/Cargo.toml +++ b/crates/optimism/consensus/Cargo.toml @@ -19,6 +19,7 @@ reth-consensus.workspace = true reth-primitives.workspace = true reth-trie-common.workspace = true reth-storage-api.workspace = true +reth-storage-errors.workspace = true # op-reth reth-optimism-forks.workspace = true diff --git a/crates/optimism/consensus/src/error.rs b/crates/optimism/consensus/src/error.rs index 18b36519fae7..180b9fdc2962 100644 --- a/crates/optimism/consensus/src/error.rs +++ b/crates/optimism/consensus/src/error.rs @@ -1,11 +1,33 @@ //! Optimism consensus errors -use derive_more::{Display, Error}; +use alloy_primitives::B256; +use derive_more::{Display, Error, From}; +use reth_storage_errors::ProviderError; /// Optimism consensus error. -#[derive(Debug, PartialEq, Eq, Clone, Display, Error)] +#[derive(Debug, PartialEq, Eq, Clone, Display, Error, From)] pub enum OpConsensusError { /// Block body has non-empty withdrawals list. #[display("non-empty withdrawals list")] WithdrawalsNonEmpty, + /// Failed to load storage root of + /// [`L2toL1MessagePasser`](reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER). + #[display("failed to load storage root of L2toL1MessagePasser pre-deploy: {_0}")] + #[from] + LoadStorageRootFailed(ProviderError), + /// Storage root of + /// [`L2toL1MessagePasser`](reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER) missing + /// in block (withdrawals root field). + #[display("storage root of L2toL1MessagePasser missing (withdrawals root field empty)")] + StorageRootMissing, + /// Storage root of + /// [`L2toL1MessagePasser`](reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER) + /// in block (withdrawals field), doesn't match local storage root. + #[display("L2toL1MessagePasser storage root mismatch, got: {}, expected {expected}", got.map(|hash| hash.to_string()).unwrap_or_else(|| "null".to_string()))] + StorageRootMismatch { + /// Storage root of pre-deploy in block. + got: Option, + /// Storage root of pre-deploy loaded from local state. + expected: B256, + }, } diff --git a/crates/optimism/consensus/src/lib.rs b/crates/optimism/consensus/src/lib.rs index eb4293b309fe..fda4ec406efd 100644 --- a/crates/optimism/consensus/src/lib.rs +++ b/crates/optimism/consensus/src/lib.rs @@ -9,6 +9,8 @@ // The `optimism` feature must be enabled to use this crate. #![cfg(feature = "optimism")] +use core::fmt; + use alloy_consensus::{BlockHeader, Header, EMPTY_OMMER_ROOT_HASH}; use alloy_primitives::{B64, U256}; use reth_chainspec::EthereumHardforks; @@ -23,8 +25,9 @@ use reth_consensus_common::validation::{ }; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_forks::OpHardforks; -use reth_optimism_primitives::OpPrimitives; +use reth_optimism_primitives::{predeploys::ADDRESS_L2_TO_L1_MESSAGE_PASSER, OpPrimitives}; use reth_primitives::{BlockBody, BlockWithSenders, GotExpected, SealedBlock, SealedHeader}; +use reth_storage_api::{StateProviderFactory, StorageRootProvider}; use std::{sync::Arc, time::SystemTime}; use tracing::debug; @@ -41,19 +44,23 @@ pub use validation::validate_block_post_execution; /// /// Provides basic checks as outlined in the execution specs. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct OpBeaconConsensus { +pub struct OpBeaconConsensus

{ /// Configuration chain_spec: Arc, + provider: P, } -impl OpBeaconConsensus { +impl

OpBeaconConsensus

{ /// Create a new instance of [`OpBeaconConsensus`] - pub const fn new(chain_spec: Arc) -> Self { - Self { chain_spec } + pub const fn new(chain_spec: Arc, provider: P) -> Self { + Self { chain_spec, provider } } } -impl FullConsensus for OpBeaconConsensus { +impl

FullConsensus for OpBeaconConsensus

+where + P: StateProviderFactory + fmt::Debug, +{ fn validate_block_post_execution( &self, block: &BlockWithSenders, @@ -63,7 +70,10 @@ impl FullConsensus for OpBeaconConsensus { } } -impl Consensus for OpBeaconConsensus { +impl

Consensus for OpBeaconConsensus

+where + P: StateProviderFactory + fmt::Debug, +{ fn validate_body_against_header( &self, body: &BlockBody, @@ -102,11 +112,52 @@ impl Consensus for OpBeaconConsensus { validate_cancun_gas(block)?; } + if self.chain_spec.is_isthmus_active_at_timestamp(block.timestamp) { + let storage_root_msg_passer = self + .provider + .latest() + .map_err(|err| { + debug!(target: "op::consensus", + block_number=block.number, + err=%OpConsensusError::LoadStorageRootFailed(err), + "failed to load latest state", + ); + + ConsensusError::Other + })? + .storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, Default::default()) + .map_err(|err| { + debug!(target: "op::consensus", + block_number=block.number, + err=%OpConsensusError::LoadStorageRootFailed(err), + "failed to load storage root for L2toL1MessagePasser pre-deploy", + ); + + ConsensusError::Other + })?; + + if block.withdrawals_root.is_none_or(|root| root != storage_root_msg_passer) { + debug!(target: "op::consensus", + block_number=block.number, + err=%OpConsensusError::StorageRootMismatch { + got: block.withdrawals_root, + expected: storage_root_msg_passer + }, + "block failed validation", + ); + + return Err(ConsensusError::Other) + } + } + Ok(()) } } -impl HeaderValidator for OpBeaconConsensus { +impl

HeaderValidator for OpBeaconConsensus

+where + P: Send + Sync + fmt::Debug, +{ fn validate_header(&self, header: &SealedHeader) -> Result<(), ConsensusError> { validate_header_gas(header.header())?; validate_header_base_fee(header.header(), &self.chain_spec) diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index 35e33ccd75a4..515b8a905a10 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -7,6 +7,7 @@ use crate::{ OpEngineTypes, }; use alloy_consensus::Header; +use core::fmt; use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig}; use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks}; use reth_db::transaction::{DbTx, DbTxMut}; @@ -36,7 +37,7 @@ use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; use reth_primitives::{BlockBody, PooledTransactionsElement, TransactionSigned}; use reth_provider::{ providers::ChainStorage, BlockBodyReader, BlockBodyWriter, CanonStateSubscriptions, - ChainSpecProvider, DBProvider, EthStorage, ProviderResult, ReadBodyInput, + ChainSpecProvider, DBProvider, EthStorage, ProviderResult, ReadBodyInput, StorageRootProvider, }; use reth_rpc_server_types::RethRpcModule; use reth_tracing::tracing::{debug, info}; @@ -175,6 +176,7 @@ where Primitives = OpPrimitives, Storage = OpStorage, >, + Provider: StorageRootProvider + fmt::Debug, >, { type ComponentsBuilder = ComponentsBuilder< @@ -658,12 +660,15 @@ pub struct OpConsensusBuilder; impl ConsensusBuilder for OpConsensusBuilder where - Node: FullNodeTypes>, + Node: FullNodeTypes< + Types: NodeTypes, + Provider: StorageRootProvider + fmt::Debug, + >, { - type Consensus = Arc; + type Consensus = Arc>; async fn build_consensus(self, ctx: &BuilderContext) -> eyre::Result { - Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec()))) + Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec(), ctx.provider().clone()))) } } diff --git a/crates/optimism/payload/Cargo.toml b/crates/optimism/payload/Cargo.toml index 1c4f855b6aa2..1551e715221f 100644 --- a/crates/optimism/payload/Cargo.toml +++ b/crates/optimism/payload/Cargo.toml @@ -33,6 +33,7 @@ reth-optimism-chainspec.workspace = true reth-optimism-consensus.workspace = true reth-optimism-evm.workspace = true reth-optimism-forks.workspace = true +reth-optimism-primitives.workspace = true # ethereum revm.workspace = true @@ -57,5 +58,6 @@ optimism = [ "reth-optimism-evm/optimism", "revm/optimism", "reth-execution-types/optimism", - "reth-optimism-consensus/optimism" + "reth-optimism-consensus/optimism", + "reth-optimism-primitives/optimism", ] \ No newline at end of file diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index bfe426a1cf46..e456128f5197 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloy_consensus::{Header, Transaction, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE}; -use alloy_primitives::{address, Address, Bytes, B256, U256}; +use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_engine::PayloadId; use op_alloy_consensus::DepositTransaction; @@ -20,6 +20,7 @@ use reth_execution_types::ExecutionOutcome; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism; use reth_optimism_forks::OpHardforks; +use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER; use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::PayloadBuilderAttributes; use reth_payload_util::PayloadTransactions; @@ -47,10 +48,6 @@ use revm::{ use std::{fmt::Display, sync::Arc}; use tracing::{debug, trace, warn}; -/// The L2 contract `L2ToL1MessagePasser`, stores commitments to withdrawal transactions. -pub const ADDRESS_PREDEPLOY_L2_TO_L1_MESSAGE_PASSER: Address = - address!("4200000000000000000000000000000000000016"); - /// Optimism's payload builder #[derive(Debug, Clone)] pub struct OpPayloadBuilder { @@ -344,7 +341,7 @@ where let storage_root_msg_passer = state .database .as_ref() - .storage_root(ADDRESS_PREDEPLOY_L2_TO_L1_MESSAGE_PASSER, Default::default())?; + .storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, Default::default())?; // withdrawals root field in block header is used for storage root of L2 predeploy let payload = ExecutedPayload { info, withdrawals_root: Some(storage_root_msg_passer) }; diff --git a/crates/optimism/primitives/src/lib.rs b/crates/optimism/primitives/src/lib.rs index b1f029d20bc2..14aacdb2a857 100644 --- a/crates/optimism/primitives/src/lib.rs +++ b/crates/optimism/primitives/src/lib.rs @@ -14,8 +14,10 @@ extern crate alloc; pub mod bedrock; +pub mod predeploys; pub mod transaction; +pub use predeploys::ADDRESS_L2_TO_L1_MESSAGE_PASSER; pub use transaction::{signed::OpTransactionSigned, tx_type::OpTxType, OpTransaction}; /// Optimism primitive types. diff --git a/crates/optimism/primitives/src/predeploys.rs b/crates/optimism/primitives/src/predeploys.rs new file mode 100644 index 000000000000..1a306e86967c --- /dev/null +++ b/crates/optimism/primitives/src/predeploys.rs @@ -0,0 +1,8 @@ +//! Addresses of OP pre-deploys. +// todo: move to alloy + +use alloy_primitives::{address, Address}; + +/// The L2 contract `L2ToL1MessagePasser`, stores commitments to withdrawal transactions. +pub const ADDRESS_L2_TO_L1_MESSAGE_PASSER: Address = + address!("4200000000000000000000000000000000000016"); diff --git a/crates/storage/errors/src/lib.rs b/crates/storage/errors/src/lib.rs index 6abb0cd9b425..4beca464b12b 100644 --- a/crates/storage/errors/src/lib.rs +++ b/crates/storage/errors/src/lib.rs @@ -22,3 +22,5 @@ pub mod provider; /// Writer error pub mod writer; + +pub use provider::ProviderError; From c125ea28db6d81ab92a42e7dd9f15ba4f358ed31 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 9 Dec 2024 13:09:14 -0500 Subject: [PATCH 4/8] Fix conflicts --- crates/optimism/node/src/node.rs | 7 ++++--- crates/optimism/node/tests/it/priority.rs | 3 +++ crates/storage/provider/src/providers/mod.rs | 9 ++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index 515b8a905a10..5a88407a47aa 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -37,7 +37,7 @@ use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; use reth_primitives::{BlockBody, PooledTransactionsElement, TransactionSigned}; use reth_provider::{ providers::ChainStorage, BlockBodyReader, BlockBodyWriter, CanonStateSubscriptions, - ChainSpecProvider, DBProvider, EthStorage, ProviderResult, ReadBodyInput, StorageRootProvider, + ChainSpecProvider, DBProvider, EthStorage, ProviderResult, ReadBodyInput, }; use reth_rpc_server_types::RethRpcModule; use reth_tracing::tracing::{debug, info}; @@ -151,6 +151,7 @@ impl OpNode { ChainSpec = OpChainSpec, Primitives = OpPrimitives, >, + Provider: fmt::Debug, >, { let RollupArgs { disable_txpool_gossip, compute_pending_block, discovery_v4, .. } = args; @@ -176,7 +177,7 @@ where Primitives = OpPrimitives, Storage = OpStorage, >, - Provider: StorageRootProvider + fmt::Debug, + Provider: fmt::Debug, >, { type ComponentsBuilder = ComponentsBuilder< @@ -662,7 +663,7 @@ impl ConsensusBuilder for OpConsensusBuilder where Node: FullNodeTypes< Types: NodeTypes, - Provider: StorageRootProvider + fmt::Debug, + Provider: fmt::Debug, >, { type Consensus = Arc>; diff --git a/crates/optimism/node/tests/it/priority.rs b/crates/optimism/node/tests/it/priority.rs index 1b49ed684bfc..44ed46d7816a 100644 --- a/crates/optimism/node/tests/it/priority.rs +++ b/crates/optimism/node/tests/it/priority.rs @@ -1,5 +1,7 @@ //! Node builder test that customizes priority of transactions in the block. +use core::fmt; + use alloy_consensus::TxEip1559; use alloy_genesis::Genesis; use alloy_network::TxSignerSync; @@ -100,6 +102,7 @@ where ChainSpec = OpChainSpec, Primitives = OpPrimitives, >, + Provider: fmt::Debug, >, { let RollupArgs { disable_txpool_gossip, compute_pending_block, discovery_v4, .. } = diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index b4a99541a89a..b421e934e5fb 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -1,3 +1,5 @@ +use core::fmt; + use crate::{ AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, BlockchainTreePendingStateProvider, CanonStateNotifications, @@ -117,7 +119,6 @@ impl TreeNodeTypes for T where T: ProviderNodeTypes + NodeTypesForTree {} /// This type serves as the main entry point for interacting with the blockchain and provides data /// from database storage and from the blockchain tree (pending state etc.) It is a simple wrapper /// type that holds an instance of the database and the blockchain tree. -#[allow(missing_debug_implementations)] pub struct BlockchainProvider { /// Provider type used to access the database. database: ProviderFactory, @@ -983,3 +984,9 @@ impl AccountReader for BlockchainProvider { self.database.provider()?.basic_account(address) } } + +impl fmt::Debug for BlockchainProvider { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BlockchainProvider").finish_non_exhaustive() + } +} From c8ced49e551d150d7cedceaf122f2e66243c193b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 9 Dec 2024 13:24:21 -0500 Subject: [PATCH 5/8] Conditionally replaces withdrawals root with l2tol1-message-passer storage root --- crates/optimism/payload/src/builder.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index e456128f5197..7604dab8d56e 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -338,13 +338,16 @@ where // and 4788 contract call state.merge_transitions(BundleRetention::Reverts); - let storage_root_msg_passer = state - .database - .as_ref() - .storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, Default::default())?; - // withdrawals root field in block header is used for storage root of L2 predeploy - let payload = ExecutedPayload { info, withdrawals_root: Some(storage_root_msg_passer) }; + // `l2tol1-message-passer` + let withdrawals_root = ctx.is_isthmus_active().then_some({ + state + .database + .as_ref() + .storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, Default::default())? + }); + + let payload = ExecutedPayload { info, withdrawals_root }; Ok(BuildOutcomeKind::Better { payload }) } @@ -676,6 +679,11 @@ impl OpPayloadBuilderCtx { self.chain_spec.is_holocene_active_at_timestamp(self.attributes().timestamp()) } + /// Returns true if isthmus is active for the payload. + pub fn is_isthmus_active(&self) -> bool { + self.chain_spec.is_isthmus_active_at_timestamp(self.attributes().timestamp()) + } + /// Returns true if the fees are higher than the previous payload. pub fn is_better_payload(&self, total_fees: U256) -> bool { is_better_payload(self.best_payload.as_ref(), total_fees) From 6bb4e805a61859234fe913dcf95899672b35aa37 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 9 Dec 2024 13:37:00 -0500 Subject: [PATCH 6/8] Fix merge conflicts --- crates/optimism/chainspec/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/optimism/chainspec/src/lib.rs b/crates/optimism/chainspec/src/lib.rs index 5decc15edcd8..7a1dcd19b3f4 100644 --- a/crates/optimism/chainspec/src/lib.rs +++ b/crates/optimism/chainspec/src/lib.rs @@ -1038,7 +1038,7 @@ mod tests { OpHardfork::Fjord.boxed(), OpHardfork::Granite.boxed(), OpHardfork::Holocene.boxed(), - OpHardfork::Isthmus.boxed(), + //OpHardfork::Isthmus.boxed(), ]; assert!(expected_hardforks From e64b7126f3f6c5bb409bcb09412b178ab3e0bd02 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 9 Dec 2024 14:07:52 -0500 Subject: [PATCH 7/8] Fix bug, return empty withdrawals root --- crates/optimism/payload/src/builder.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 7604dab8d56e..150988b6a051 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -5,7 +5,7 @@ use crate::{ error::OpPayloadBuilderError, payload::{OpBuiltPayload, OpPayloadBuilderAttributes}, }; -use alloy_consensus::{Header, Transaction, EMPTY_OMMER_ROOT_HASH}; +use alloy_consensus::{constants::EMPTY_WITHDRAWALS, Header, Transaction, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE}; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rpc_types_debug::ExecutionWitness; @@ -340,11 +340,13 @@ where // withdrawals root field in block header is used for storage root of L2 predeploy // `l2tol1-message-passer` - let withdrawals_root = ctx.is_isthmus_active().then_some({ + let withdrawals_root = Some(if ctx.is_isthmus_active() { state .database .as_ref() .storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, Default::default())? + } else { + EMPTY_WITHDRAWALS }); let payload = ExecutedPayload { info, withdrawals_root }; From c7a2c14be89405c0fd37a48cfb3a059206a8ad92 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 11 Dec 2024 13:01:25 -0500 Subject: [PATCH 8/8] Add link to issue in todo --- crates/consensus/consensus/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/consensus/consensus/src/lib.rs b/crates/consensus/consensus/src/lib.rs index 61c9d8dba937..8c7b9ff3aa00 100644 --- a/crates/consensus/consensus/src/lib.rs +++ b/crates/consensus/consensus/src/lib.rs @@ -453,7 +453,7 @@ pub enum ConsensusError { }, /// Custom error // todo: remove in favour of AT Consensus::Error, so OpConsensusError can wrap ConsensusError - // in a variant instead + // in a variant instead #[display("custom l2 error (search for it in debug logs)")] Other, }