diff --git a/Cargo.lock b/Cargo.lock index 042209bfcf6..7df144c897b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9988,6 +9988,7 @@ dependencies = [ "reth-rpc-eth-api", "reth-rpc-eth-types", "reth-rpc-server-types", + "reth-scroll-evm", "reth-storage-api", "reth-tasks", "reth-testing-utils", diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 55b2bc4c21b..74d5e90fc42 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -2582,8 +2582,8 @@ pub enum BlockStatus { /// How a payload was inserted if it was valid. /// -/// If the payload was valid, but has already been seen, [`InsertPayloadOk::AlreadySeen(_)`] is -/// returned, otherwise [`InsertPayloadOk::Inserted(_)`] is returned. +/// If the payload was valid, but has already been seen, [`InsertPayloadOk::AlreadySeen`] is +/// returned, otherwise [`InsertPayloadOk::Inserted`] is returned. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum InsertPayloadOk { /// The payload was valid, but we have already seen it. diff --git a/crates/evm/evm/src/either.rs b/crates/evm/evm/src/either.rs index 904ce7ebbd6..d497e142d7a 100644 --- a/crates/evm/evm/src/either.rs +++ b/crates/evm/evm/src/either.rs @@ -58,7 +58,7 @@ where state: F, ) -> Result::Receipt>, Self::Error> where - F: FnMut(&revm::database::State), + F: FnMut(&mut revm::database::State), { match self { Self::Left(a) => a.execute_with_state_closure(block, state), diff --git a/crates/evm/evm/src/execute.rs b/crates/evm/evm/src/execute.rs index 40c680d3868..45e929d23dd 100644 --- a/crates/evm/evm/src/execute.rs +++ b/crates/evm/evm/src/execute.rs @@ -100,11 +100,11 @@ pub trait Executor: Sized { mut f: F, ) -> Result::Receipt>, Self::Error> where - F: FnMut(&State), + F: FnMut(&mut State), { let result = self.execute_one(block)?; let mut state = self.into_state(); - f(&state); + f(&mut state); Ok(BlockExecutionOutput { state: state.take_bundle(), result }) } diff --git a/crates/net/downloaders/src/bodies/bodies.rs b/crates/net/downloaders/src/bodies/bodies.rs index e4ef306b018..bd7fc602340 100644 --- a/crates/net/downloaders/src/bodies/bodies.rs +++ b/crates/net/downloaders/src/bodies/bodies.rs @@ -143,7 +143,7 @@ where /// Max requests to handle at the same time /// /// This depends on the number of active peers but will always be - /// [`min_concurrent_requests`..`max_concurrent_requests`] + /// `min_concurrent_requests..max_concurrent_requests` #[inline] fn concurrent_request_limit(&self) -> usize { let num_peers = self.client.num_connected_peers(); diff --git a/crates/net/downloaders/src/headers/reverse_headers.rs b/crates/net/downloaders/src/headers/reverse_headers.rs index 5f27467cf83..a0876ea216d 100644 --- a/crates/net/downloaders/src/headers/reverse_headers.rs +++ b/crates/net/downloaders/src/headers/reverse_headers.rs @@ -148,7 +148,7 @@ where /// Max requests to handle at the same time /// /// This depends on the number of active peers but will always be - /// [`min_concurrent_requests`..`max_concurrent_requests`] + /// `min_concurrent_requests..max_concurrent_requests` #[inline] fn concurrent_request_limit(&self) -> usize { let num_peers = self.client.num_connected_peers(); diff --git a/crates/net/network/src/test_utils/testnet.rs b/crates/net/network/src/test_utils/testnet.rs index d064ed7eda6..3e3c63d59b3 100644 --- a/crates/net/network/src/test_utils/testnet.rs +++ b/crates/net/network/src/test_utils/testnet.rs @@ -788,9 +788,9 @@ impl NetworkEventStream { peers } - /// Ensures that the first two events are a [`NetworkEvent::Peer(PeerEvent::PeerAdded`] and - /// [`NetworkEvent::ActivePeerSession`], returning the [`PeerId`] of the established - /// session. + /// Ensures that the first two events are a [`NetworkEvent::Peer`] and + /// [`PeerEvent::PeerAdded`][`NetworkEvent::ActivePeerSession`], returning the [`PeerId`] of the + /// established session. pub async fn peer_added_and_established(&mut self) -> Option { let peer_id = match self.inner.next().await { Some(NetworkEvent::Peer(PeerEvent::PeerAdded(peer_id))) => peer_id, diff --git a/crates/node/core/src/args/pruning.rs b/crates/node/core/src/args/pruning.rs index d6b8170440a..5dbbafc7c67 100644 --- a/crates/node/core/src/args/pruning.rs +++ b/crates/node/core/src/args/pruning.rs @@ -61,8 +61,8 @@ pub struct PruningArgs { pub receipts_before: Option, // Receipts Log Filter /// Configure receipts log filter. Format: - /// <`address`>:<`prune_mode`>[,<`address`>:<`prune_mode`>...] Where <`prune_mode`> can be - /// 'full', 'distance:<`blocks`>', or 'before:<`block_number`>' + /// <`address`>:<`prune_mode`>... where <`prune_mode`> can be 'full', 'distance:<`blocks`>', or + /// 'before:<`block_number`>' #[arg(long = "prune.receiptslogfilter", value_name = "FILTER_CONFIG", conflicts_with_all = &["receipts_full", "receipts_pre_merge", "receipts_distance", "receipts_before"], value_parser = parse_receipts_log_filter)] pub receipts_log_filter: Option, diff --git a/crates/prune/types/src/target.rs b/crates/prune/types/src/target.rs index d91faea0a11..a77b204e1ba 100644 --- a/crates/prune/types/src/target.rs +++ b/crates/prune/types/src/target.rs @@ -156,9 +156,9 @@ impl PruneModes { /// left in database after the pruning. /// /// 1. For [`PruneMode::Full`], it fails if `MIN_BLOCKS > 0`. -/// 2. For [`PruneMode::Distance(distance`)], it fails if `distance < MIN_BLOCKS + 1`. `+ 1` is -/// needed because `PruneMode::Distance(0)` means that we leave zero blocks from the latest, -/// meaning we have one block in the database. +/// 2. For [`PruneMode::Distance`], it fails if `distance < MIN_BLOCKS + 1`. `+ 1` is needed because +/// `PruneMode::Distance(0)` means that we leave zero blocks from the latest, meaning we have one +/// block in the database. #[cfg(any(test, feature = "serde"))] fn deserialize_opt_prune_mode_with_min_blocks< 'de, diff --git a/crates/ress/provider/src/lib.rs b/crates/ress/provider/src/lib.rs index 41318ebaaf1..cef54540b73 100644 --- a/crates/ress/provider/src/lib.rs +++ b/crates/ress/provider/src/lib.rs @@ -154,7 +154,7 @@ where // invalid blocks. if let Err(error) = self.evm_config.batch_executor(&mut db).execute_with_state_closure( &block, - |state: &State<_>| { + |state: &mut State<_>| { record.record_executed_state(state); }, ) { diff --git a/crates/rpc/rpc-e2e-tests/src/rpc_compat.rs b/crates/rpc/rpc-e2e-tests/src/rpc_compat.rs index 436ace0eeb0..d6b48e3f4fb 100644 --- a/crates/rpc/rpc-e2e-tests/src/rpc_compat.rs +++ b/crates/rpc/rpc-e2e-tests/src/rpc_compat.rs @@ -25,7 +25,7 @@ pub struct RpcTestCase { /// Action that runs RPC compatibility tests from execution-apis test data #[derive(Debug)] pub struct RunRpcCompatTests { - /// RPC methods to test (e.g., ["`eth_getLogs`"]) + /// RPC methods to test (e.g. `eth_getLogs`) pub methods: Vec, /// Path to the execution-apis tests directory pub test_data_path: String, diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index e0d1fcb601f..304849a9a17 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -64,6 +64,9 @@ alloy-serde.workspace = true revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee"] } revm-primitives = { workspace = true, features = ["serde"] } +# scroll +reth-scroll-evm = { workspace = true, optional = true } + # rpc jsonrpsee.workspace = true http.workspace = true @@ -104,3 +107,4 @@ jsonrpsee = { workspace = true, features = ["client"] } [features] js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"] +scroll = ["reth-scroll-evm"] diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index e5fa07e0e51..3e88bf8a82d 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -640,11 +640,18 @@ where let mut witness_record = ExecutionWitnessRecord::default(); + let mut withdraw_root_res: Result<_, reth_errors::ProviderError> = Ok(()); let _ = block_executor - .execute_with_state_closure(&(*block).clone(), |statedb: &State<_>| { + .execute_with_state_closure(&(*block).clone(), |statedb: &mut State<_>| { + #[cfg(feature = "scroll")] + { + use reth_scroll_evm::LoadWithdrawRoot; + withdraw_root_res = statedb.load_withdraw_root(); + } witness_record.record_executed_state(statedb); }) .map_err(|err| EthApiError::Internal(err.into()))?; + withdraw_root_res?; let ExecutionWitnessRecord { hashed_state, codes, keys, lowest_block_number } = witness_record; diff --git a/crates/scroll/evm/Cargo.toml b/crates/scroll/evm/Cargo.toml index 1874d574578..c1601ffb685 100644 --- a/crates/scroll/evm/Cargo.toml +++ b/crates/scroll/evm/Cargo.toml @@ -47,7 +47,7 @@ thiserror.workspace = true tracing.workspace = true [dev-dependencies] -alloy-primitives = { workspace = true, features = ["getrandom"] } +alloy-primitives = { workspace = true, features = ["getrandom", "rand"] } eyre.workspace = true [features] diff --git a/crates/scroll/evm/src/lib.rs b/crates/scroll/evm/src/lib.rs index 2268a1d7a03..d8135610e1f 100644 --- a/crates/scroll/evm/src/lib.rs +++ b/crates/scroll/evm/src/lib.rs @@ -23,6 +23,9 @@ pub use base_fee::{ mod receipt; pub use receipt::ScrollRethReceiptBuilder; +mod withdraw_root; +pub use withdraw_root::LoadWithdrawRoot; + use crate::build::ScrollBlockAssembler; use alloc::sync::Arc; diff --git a/crates/scroll/evm/src/withdraw_root.rs b/crates/scroll/evm/src/withdraw_root.rs new file mode 100644 index 00000000000..4775b889b55 --- /dev/null +++ b/crates/scroll/evm/src/withdraw_root.rs @@ -0,0 +1,99 @@ +use alloy_primitives::{address, Address, U256}; +use revm::{database::State, Database}; + +const L2_MESSAGE_QUEUE_ADDRESS: Address = address!("0x5300000000000000000000000000000000000000"); +const WITHDRAW_TRIE_ROOT_SLOT: U256 = U256::ZERO; + +/// Instance that implements the trait can load the `L2MessageQueue` withdraw root in state. +pub trait LoadWithdrawRoot { + /// Load the withdrawal root. + fn load_withdraw_root(&mut self) -> Result<(), DB::Error>; +} + +impl LoadWithdrawRoot for State { + fn load_withdraw_root(&mut self) -> Result<(), DB::Error> { + // we load the account in cache and query the storage slot. The storage slot will only be + // loaded from database if it is not already know. + self.load_cache_account(L2_MESSAGE_QUEUE_ADDRESS)?; + let _ = revm::Database::storage(self, L2_MESSAGE_QUEUE_ADDRESS, WITHDRAW_TRIE_ROOT_SLOT); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{collections::HashMap, convert::Infallible}; + + use alloy_primitives::B256; + use revm::{bytecode::Bytecode, state::AccountInfo}; + use revm_primitives::{StorageKey, StorageValue}; + + #[derive(Default)] + struct InMemoryDb { + pub accounts: HashMap, + pub storage: HashMap<(Address, U256), U256>, + } + + impl Database for InMemoryDb { + type Error = Infallible; + + fn basic(&mut self, address: Address) -> Result, Self::Error> { + Ok(self.accounts.get(&address).cloned()) + } + + fn code_by_hash(&mut self, _code_hash: B256) -> Result { + Ok(Default::default()) + } + + fn storage( + &mut self, + address: Address, + index: StorageKey, + ) -> Result { + Ok(self.storage.get(&(address, index)).copied().unwrap_or_default()) + } + + fn block_hash(&mut self, _number: u64) -> Result { + Ok(Default::default()) + } + } + + #[test] + fn test_should_load_withdraw_root() -> eyre::Result<()> { + // init db + let mut db = InMemoryDb::default(); + + // load L2 message queue contract + let withdraw_root = U256::random(); + db.accounts.insert(L2_MESSAGE_QUEUE_ADDRESS, Default::default()); + db.storage.insert((L2_MESSAGE_QUEUE_ADDRESS, U256::ZERO), withdraw_root); + + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + + assert!(state + .cache + .accounts + .get(&L2_MESSAGE_QUEUE_ADDRESS) + .map(|acc| acc.storage_slot(WITHDRAW_TRIE_ROOT_SLOT)) + .is_none()); + + // load root + state.load_withdraw_root()?; + + assert_eq!( + state + .cache + .accounts + .get(&L2_MESSAGE_QUEUE_ADDRESS) + .unwrap() + .storage_slot(WITHDRAW_TRIE_ROOT_SLOT) + .unwrap(), + withdraw_root + ); + + Ok(()) + } +} diff --git a/crates/scroll/rpc/Cargo.toml b/crates/scroll/rpc/Cargo.toml index 5bf30ccaa36..91850423b94 100644 --- a/crates/scroll/rpc/Cargo.toml +++ b/crates/scroll/rpc/Cargo.toml @@ -21,7 +21,7 @@ reth-rpc-eth-api = { workspace = true, features = ["scroll"] } reth-rpc-eth-types.workspace = true reth-tasks = { workspace = true, features = ["rayon"] } reth-transaction-pool.workspace = true -reth-rpc.workspace = true +reth-rpc = { workspace = true, features = ["scroll"] } reth-rpc-convert = { workspace = true, features = ["scroll"] } reth-node-api.workspace = true reth-node-builder.workspace = true diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index b646c407e97..2ed0a951f8e 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -1,6 +1,6 @@ //! MDBX implementation for reth's database abstraction layer. //! -//! This crate is an implementation of [`reth-db-api`] for MDBX, as well as a few other common +//! This crate is an implementation of `reth-db-api` for MDBX, as well as a few other common //! database types. //! //! # Overview diff --git a/crates/trie/db/src/lib.rs b/crates/trie/db/src/lib.rs index a0a19d0bb2f..c55ffbf8aa2 100644 --- a/crates/trie/db/src/lib.rs +++ b/crates/trie/db/src/lib.rs @@ -1,4 +1,4 @@ -//! An integration of [`reth-trie`] with [`reth-db`]. +//! An integration of `reth-trie` with `reth-db`. #![cfg_attr(not(test), warn(unused_crate_dependencies))] diff --git a/docs/vocs/docs/pages/cli/reth/node.mdx b/docs/vocs/docs/pages/cli/reth/node.mdx index dd29bbc103b..2f4c06ba055 100644 --- a/docs/vocs/docs/pages/cli/reth/node.mdx +++ b/docs/vocs/docs/pages/cli/reth/node.mdx @@ -728,7 +728,7 @@ Pruning: Prune receipts before the specified block number. The specified block number is not pruned --prune.receiptslogfilter - Configure receipts log filter. Format: <`address`>:<`prune_mode`>[,<`address`>:<`prune_mode`>...] Where <`prune_mode`> can be 'full', 'distance:<`blocks`>', or 'before:<`block_number`>' + Configure receipts log filter. Format: <`address`>:<`prune_mode`>... where <`prune_mode`> can be 'full', 'distance:<`blocks`>', or 'before:<`block_number`>' --prune.accounthistory.full Prunes all account history diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index 4c463c612a6..b4cbbf89fce 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -237,7 +237,7 @@ fn run_case(case: &BlockchainTest) -> Result<(), Error> { let executor = executor_provider.batch_executor(state_db); let output = executor - .execute_with_state_closure(&(*block).clone(), |statedb: &State<_>| { + .execute_with_state_closure(&(*block).clone(), |statedb: &mut State<_>| { witness_record.record_executed_state(statedb); }) .map_err(|err| Error::block_failed(block_number, err))?;