diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 1627f74e2..00b72cea6 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -19,7 +19,9 @@ use reth_trie::{ proof::Proof, updates::TrieUpdates, witness::TrieWitness, AccountProof, HashedPostState, StateRoot, }; -use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseTrieWitness}; +use reth_trie_db::{ + DatabaseHashedPostState, DatabaseProof, DatabaseStateRoot, DatabaseTrieWitness, +}; use std::{collections::HashMap, fmt::Debug}; /// State provider for a given block number which takes a tx reference. diff --git a/crates/trie/db/Cargo.toml b/crates/trie/db/Cargo.toml index 3c479072b..8b7893a2f 100644 --- a/crates/trie/db/Cargo.toml +++ b/crates/trie/db/Cargo.toml @@ -18,6 +18,7 @@ reth-execution-errors.workspace = true reth-db.workspace = true reth-db-api.workspace = true reth-stages-types.workspace = true +reth-storage-errors.workspace = true reth-trie-common.workspace = true reth-trie.workspace = true diff --git a/crates/trie/db/src/lib.rs b/crates/trie/db/src/lib.rs index e8975bd18..c1537f4b2 100644 --- a/crates/trie/db/src/lib.rs +++ b/crates/trie/db/src/lib.rs @@ -8,6 +8,6 @@ mod witness; pub use prefix_set::PrefixSetLoader; pub use proof::DatabaseProof; -pub use state::DatabaseStateRoot; +pub use state::{DatabaseHashedPostState, DatabaseStateRoot}; pub use storage::DatabaseStorageRoot; pub use witness::DatabaseTrieWitness; diff --git a/crates/trie/db/src/state.rs b/crates/trie/db/src/state.rs index d589280e6..fe6f84c7c 100644 --- a/crates/trie/db/src/state.rs +++ b/crates/trie/db/src/state.rs @@ -1,14 +1,23 @@ use crate::PrefixSetLoader; -use reth_db_api::transaction::DbTx; +use reth_db::tables; +use reth_db_api::{ + cursor::DbCursorRO, + models::{AccountBeforeTx, BlockNumberAddress}, + transaction::DbTx, +}; use reth_execution_errors::StateRootError; -use reth_primitives::{BlockNumber, B256}; +use reth_primitives::{keccak256, Account, Address, BlockNumber, B256, U256}; +use reth_storage_errors::db::DatabaseError; use reth_trie::{ hashed_cursor::{DatabaseHashedCursorFactory, HashedPostStateCursorFactory}, trie_cursor::{DatabaseTrieCursorFactory, InMemoryTrieCursorFactory}, updates::TrieUpdates, - HashedPostState, StateRoot, StateRootProgress, + HashedPostState, HashedStorage, StateRoot, StateRootProgress, +}; +use std::{ + collections::{hash_map, HashMap}, + ops::RangeInclusive, }; -use std::ops::RangeInclusive; use tracing::debug; /// Extends [`StateRoot`] with operations specific for working with a database transaction. @@ -111,6 +120,13 @@ pub trait DatabaseStateRoot<'a, TX>: Sized { ) -> Result<(B256, TrieUpdates), StateRootError>; } +/// Extends [`HashedPostState`] with operations specific for working with a database transaction. +pub trait DatabaseHashedPostState: Sized { + /// Initializes [`HashedPostState`] from reverts. Iterates over state reverts from the specified + /// block up to the current tip and aggregates them into hashed state in reverse. + fn from_reverts(tx: &TX, from: BlockNumber) -> Result; +} + impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX> for StateRoot, DatabaseHashedCursorFactory<'a, TX>> { @@ -183,6 +199,52 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX> } } +impl DatabaseHashedPostState for HashedPostState { + fn from_reverts(tx: &TX, from: BlockNumber) -> Result { + // Iterate over account changesets and record value before first occurring account change. + let mut accounts = HashMap::>::default(); + let mut account_changesets_cursor = tx.cursor_read::()?; + for entry in account_changesets_cursor.walk_range(from..)? { + let (_, AccountBeforeTx { address, info }) = entry?; + if let hash_map::Entry::Vacant(entry) = accounts.entry(address) { + entry.insert(info); + } + } + + // Iterate over storage changesets and record value before first occurring storage change. + let mut storages = HashMap::>::default(); + let mut storage_changesets_cursor = tx.cursor_read::()?; + for entry in + storage_changesets_cursor.walk_range(BlockNumberAddress((from, Address::ZERO))..)? + { + let (BlockNumberAddress((_, address)), storage) = entry?; + let account_storage = storages.entry(address).or_default(); + if let hash_map::Entry::Vacant(entry) = account_storage.entry(storage.key) { + entry.insert(storage.value); + } + } + + let hashed_accounts = HashMap::from_iter( + accounts.into_iter().map(|(address, info)| (keccak256(address), info)), + ); + + let hashed_storages = HashMap::from_iter(storages.into_iter().map(|(address, storage)| { + ( + keccak256(address), + HashedStorage::from_iter( + // The `wiped` flag indicates only whether previous storage entries + // should be looked up in db or not. For reverts it's a noop since all + // wiped changes had been written as storage reverts. + false, + storage.into_iter().map(|(slot, value)| (keccak256(slot), value)), + ), + ) + })); + + Ok(Self { accounts: hashed_accounts, storages: hashed_storages }) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/trie/trie/src/state.rs b/crates/trie/trie/src/state.rs index d45e5564d..6c28dbb5f 100644 --- a/crates/trie/trie/src/state.rs +++ b/crates/trie/trie/src/state.rs @@ -4,14 +4,7 @@ use crate::{ }; use itertools::Itertools; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; -use reth_db::tables; -use reth_db_api::{ - cursor::DbCursorRO, - models::{AccountBeforeTx, BlockNumberAddress}, - transaction::DbTx, -}; -use reth_primitives::{keccak256, Account, Address, BlockNumber, B256, U256}; -use reth_storage_errors::db::DatabaseError; +use reth_primitives::{keccak256, Account, Address, B256, U256}; use revm::db::BundleAccount; use std::collections::{hash_map, HashMap, HashSet}; @@ -55,52 +48,6 @@ impl HashedPostState { Self { accounts, storages } } - /// Initializes [`HashedPostState`] from reverts. Iterates over state reverts from the specified - /// block up to the current tip and aggregates them into hashed state in reverse. - pub fn from_reverts(tx: &TX, from: BlockNumber) -> Result { - // Iterate over account changesets and record value before first occurring account change. - let mut accounts = HashMap::>::default(); - let mut account_changesets_cursor = tx.cursor_read::()?; - for entry in account_changesets_cursor.walk_range(from..)? { - let (_, AccountBeforeTx { address, info }) = entry?; - if let hash_map::Entry::Vacant(entry) = accounts.entry(address) { - entry.insert(info); - } - } - - // Iterate over storage changesets and record value before first occurring storage change. - let mut storages = HashMap::>::default(); - let mut storage_changesets_cursor = tx.cursor_read::()?; - for entry in - storage_changesets_cursor.walk_range(BlockNumberAddress((from, Address::ZERO))..)? - { - let (BlockNumberAddress((_, address)), storage) = entry?; - let account_storage = storages.entry(address).or_default(); - if let hash_map::Entry::Vacant(entry) = account_storage.entry(storage.key) { - entry.insert(storage.value); - } - } - - let hashed_accounts = HashMap::from_iter( - accounts.into_iter().map(|(address, info)| (keccak256(address), info)), - ); - - let hashed_storages = HashMap::from_iter(storages.into_iter().map(|(address, storage)| { - ( - keccak256(address), - HashedStorage::from_iter( - // The `wiped` flag indicates only whether previous storage entries - // should be looked up in db or not. For reverts it's a noop since all - // wiped changes had been written as storage reverts. - false, - storage.into_iter().map(|(slot, value)| (keccak256(slot), value)), - ), - ) - })); - - Ok(Self { accounts: hashed_accounts, storages: hashed_storages }) - } - /// Set account entries on hashed state. pub fn with_accounts( mut self,