diff --git a/Cargo.lock b/Cargo.lock index cd22ac4c3..28913a716 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8561,6 +8561,7 @@ dependencies = [ "reth-optimism-forks", "reth-optimism-node", "reth-optimism-primitives", + "reth-optimism-storage", "reth-primitives-traits", "reth-provider", "reth-revm", @@ -8695,6 +8696,7 @@ dependencies = [ "reth-optimism-evm", "reth-optimism-forks", "reth-optimism-primitives", + "reth-optimism-storage", "reth-payload-builder", "reth-payload-builder-primitives", "reth-payload-primitives", @@ -8704,7 +8706,6 @@ dependencies = [ "reth-provider", "reth-revm", "reth-transaction-pool", - "reth-trie", "revm", "sha2 0.10.8", "thiserror 2.0.11", @@ -8797,11 +8798,15 @@ dependencies = [ name = "reth-optimism-storage" version = "1.2.0" dependencies = [ + "alloy-primitives", "reth-codecs", "reth-db-api", "reth-primitives", "reth-prune-types", "reth-stages-types", + "reth-storage-api", + "reth-trie-common", + "revm", ] [[package]] diff --git a/crates/optimism/consensus/Cargo.toml b/crates/optimism/consensus/Cargo.toml index fd6ec7f27..d61198f73 100644 --- a/crates/optimism/consensus/Cargo.toml +++ b/crates/optimism/consensus/Cargo.toml @@ -27,6 +27,7 @@ reth-optimism-forks.workspace = true reth-optimism-chainspec.workspace = true # remove this after feature cleanup reth-optimism-primitives = { workspace = true, features = ["serde", "reth-codec"] } +reth-optimism-storage.workspace = true # ethereum alloy-eips.workspace = true @@ -65,6 +66,7 @@ std = [ "reth-storage-api/std", "reth-storage-errors/std", "reth-trie-common/std", + "reth-optimism-storage/std", "alloy-chains/std", "alloy-eips/std", "alloy-primitives/std", diff --git a/crates/optimism/consensus/src/validation/isthmus.rs b/crates/optimism/consensus/src/validation/isthmus.rs index 6d7ce9197..648d19f0f 100644 --- a/crates/optimism/consensus/src/validation/isthmus.rs +++ b/crates/optimism/consensus/src/validation/isthmus.rs @@ -3,10 +3,9 @@ use crate::OpConsensusError; use alloy_consensus::BlockHeader; use core::fmt; -use reth_optimism_primitives::predeploys::ADDRESS_L2_TO_L1_MESSAGE_PASSER; +use reth_optimism_storage::predeploys::withdrawals_root; use reth_storage_api::StorageRootProvider; -use reth_trie_common::HashedStorage; -use revm::database::BundleAccount; +use revm::database::BundleState; /// Verifies that `withdrawals_root` (i.e. `l2tol1-msg-passer` storage root since Isthmus) field is /// set in block header. @@ -23,7 +22,7 @@ pub fn ensure_withdrawals_storage_root_is_some( /// /// See . pub fn verify_withdrawals_storage_root( - predeploy_account_updates: Option<&BundleAccount>, + state_updates: &BundleState, state: DB, header: H, ) -> Result<(), OpConsensusError> @@ -34,17 +33,7 @@ where let header_storage_root = header.withdrawals_root().ok_or(OpConsensusError::L2WithdrawalsRootMissing)?; - // if block contained l2 withdrawals transactions, use predeploy storage updates from - // execution - let hashed_storage_updates = predeploy_account_updates.map(|acc| { - HashedStorage::from_plain_storage( - acc.status, - acc.storage.iter().map(|(slot, value)| (slot, &value.present_value)), - ) - }); - - let storage_root = state - .storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, hashed_storage_updates.unwrap_or_default()) + let storage_root = withdrawals_root(state_updates, state) .map_err(OpConsensusError::L2WithdrawalsRootCalculationFail)?; if header_storage_root != storage_root { @@ -68,12 +57,13 @@ mod test { use reth_db_common::init::init_genesis; use reth_optimism_chainspec::OpChainSpecBuilder; use reth_optimism_node::OpNode; + use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER; use reth_provider::{ providers::BlockchainProvider, test_utils::create_test_provider_factory_with_node_types, StateWriter, }; use reth_storage_api::StateProviderFactory; - use reth_trie::test_utils::storage_root_prehashed; + use reth_trie::{test_utils::storage_root_prehashed, HashedStorage}; use reth_trie_common::HashedPostState; #[test] @@ -118,7 +108,7 @@ mod test { // validate block against existing state by passing empty state updates verify_withdrawals_storage_root( - None, + &BundleState::default(), state_provider_factory.latest().expect("load state"), &header, ) diff --git a/crates/optimism/payload/Cargo.toml b/crates/optimism/payload/Cargo.toml index 5fa4e9ac0..a6daa9769 100644 --- a/crates/optimism/payload/Cargo.toml +++ b/crates/optimism/payload/Cargo.toml @@ -27,13 +27,13 @@ reth-payload-util.workspace = true reth-payload-primitives = { workspace = true, features = ["op"] } reth-basic-payload-builder.workspace = true reth-chain-state.workspace = true -reth-trie.workspace = true # op-reth reth-optimism-consensus.workspace = true reth-optimism-evm.workspace = true reth-optimism-forks.workspace = true reth-optimism-primitives.workspace = true +reth-optimism-storage.workspace = true # ethereum revm.workspace = true diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index c7add5342..d4cdc6c86 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -28,9 +28,8 @@ use reth_execution_types::ExecutionOutcome; use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism; use reth_optimism_evm::{OpReceiptBuilder, ReceiptBuilderCtx}; use reth_optimism_forks::OpHardforks; -use reth_optimism_primitives::{ - transaction::signed::OpTransaction, ADDRESS_L2_TO_L1_MESSAGE_PASSER, -}; +use reth_optimism_primitives::transaction::signed::OpTransaction; +use reth_optimism_storage::predeploys; use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::PayloadBuilderAttributes; use reth_payload_util::{BestPayloadTransactions, NoopPayloadTransactions, PayloadTransactions}; @@ -49,7 +48,6 @@ use reth_revm::{ witness::ExecutionWitnessRecord, }; use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction, TransactionPool}; -use reth_trie::HashedStorage; use revm::{ context_interface::{ result::{ExecutionResult, ResultAndState}, @@ -400,22 +398,9 @@ impl OpBuilder<'_, Txs> { state.merge_transitions(BundleRetention::Reverts); let withdrawals_root = if ctx.is_isthmus_active() { - let hashed_storage_updates = - state.bundle_state.state().get(&ADDRESS_L2_TO_L1_MESSAGE_PASSER).map(|account| { - // block contained withdrawals transactions, use predeploy storage updates from - // execution - HashedStorage::from_plain_storage( - account.status, - account.storage.iter().map(|(slot, value)| (slot, &value.present_value)), - ) - }); - // withdrawals root field in block header is used for storage root of L2 predeploy // `l2tol1-message-passer` - Some(state.database.as_ref().storage_root( - ADDRESS_L2_TO_L1_MESSAGE_PASSER, - hashed_storage_updates.unwrap_or_default(), - )?) + Some(predeploys::withdrawals_root(&state.bundle_state, state.database.as_ref())?) } else if ctx.is_canyon_active() { Some(EMPTY_WITHDRAWALS) } else { diff --git a/crates/optimism/storage/Cargo.toml b/crates/optimism/storage/Cargo.toml index 4583f1367..3c9fde1d5 100644 --- a/crates/optimism/storage/Cargo.toml +++ b/crates/optimism/storage/Cargo.toml @@ -11,10 +11,29 @@ repository.workspace = true workspace = true [dependencies] +# reth reth-primitives.workspace = true +reth-trie-common.workspace = true +reth-storage-api.workspace = true + +# ethereum +alloy-primitives.workspace = true +revm.workspace = true [dev-dependencies] reth-codecs.workspace = true reth-db-api.workspace = true reth-prune-types.workspace = true reth-stages-types.workspace = true + +[features] +default = ["std"] +std = [ + "reth-primitives/std", + "reth-trie-common/std", + "reth-storage-api/std", + "alloy-primitives/std", + "revm/std", + "reth-prune-types/std", + "reth-stages-types/std", +] diff --git a/crates/optimism/storage/src/lib.rs b/crates/optimism/storage/src/lib.rs index 0d7fa5310..fa9a50e2b 100644 --- a/crates/optimism/storage/src/lib.rs +++ b/crates/optimism/storage/src/lib.rs @@ -6,6 +6,10 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod predeploys; +pub use predeploys::withdrawals_root; #[cfg(test)] mod tests { diff --git a/crates/optimism/storage/src/predeploys.rs b/crates/optimism/storage/src/predeploys.rs new file mode 100644 index 000000000..99c6883da --- /dev/null +++ b/crates/optimism/storage/src/predeploys.rs @@ -0,0 +1,29 @@ +//! Utilities for accessing Optimism predeploy state + +use alloy_primitives::{address, Address, B256}; +use reth_storage_api::{errors::ProviderResult, StorageRootProvider}; +use reth_trie_common::HashedStorage; +use revm::database::BundleState; + +/// The L2 contract `L2ToL1MessagePasser`, stores commitments to withdrawal transactions. +pub const ADDRESS_L2_TO_L1_MESSAGE_PASSER: Address = + address!("4200000000000000000000000000000000000016"); + +/// Computes the storage root of predeploy `L2ToL1MessagePasser.sol` with state updates from block +/// execution. +pub fn withdrawals_root( + state_updates: &BundleState, + state: DB, +) -> ProviderResult { + // if l2 withdrawals transactions were executed, use predeploy storage updates in storage root + // computation + let hashed_storage_updates = + state_updates.state().get(&ADDRESS_L2_TO_L1_MESSAGE_PASSER).map(|acc| { + HashedStorage::from_plain_storage( + acc.status, + acc.storage.iter().map(|(slot, value)| (slot, &value.present_value)), + ) + }); + + state.storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, hashed_storage_updates.unwrap_or_default()) +} diff --git a/crates/storage/errors/src/lib.rs b/crates/storage/errors/src/lib.rs index 179978c1c..aa8254ee1 100644 --- a/crates/storage/errors/src/lib.rs +++ b/crates/storage/errors/src/lib.rs @@ -19,6 +19,7 @@ pub mod lockfile; /// Provider error pub mod provider; +pub use provider::{ProviderError, ProviderResult}; /// Writer error pub mod writer;