use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory, PrefixSetLoader}; use reth_db::tables; use reth_db_api::{ cursor::DbCursorRO, models::{AccountBeforeTx, BlockNumberAddress}, transaction::DbTx, }; use reth_execution_errors::StateRootError; use reth_primitives::{keccak256, Account, Address, BlockNumber, B256, U256}; use reth_storage_errors::db::DatabaseError; use reth_trie::{ hashed_cursor::HashedPostStateCursorFactory, prefix_set::TriePrefixSetsMut, trie_cursor::InMemoryTrieCursorFactory, updates::TrieUpdates, HashedPostState, HashedStorage, StateRoot, StateRootProgress, }; use std::{ collections::{hash_map, HashMap}, ops::RangeInclusive, }; use tracing::debug; /// Extends [`StateRoot`] with operations specific for working with a database transaction. pub trait DatabaseStateRoot<'a, TX>: Sized { /// Create a new [`StateRoot`] instance. fn from_tx(tx: &'a TX) -> Self; /// Given a block number range, identifies all the accounts and storage keys that /// have changed. /// /// # Returns /// /// An instance of state root calculator with account and storage prefixes loaded. fn incremental_root_calculator( tx: &'a TX, range: RangeInclusive, ) -> Result; /// Computes the state root of the trie with the changed account and storage prefixes and /// existing trie nodes. /// /// # Returns /// /// The updated state root. fn incremental_root( tx: &'a TX, range: RangeInclusive, ) -> Result; /// Computes the state root of the trie with the changed account and storage prefixes and /// existing trie nodes collecting updates in the process. /// /// Ignores the threshold. /// /// # Returns /// /// The updated state root and the trie updates. fn incremental_root_with_updates( tx: &'a TX, range: RangeInclusive, ) -> Result<(B256, TrieUpdates), StateRootError>; /// Computes the state root of the trie with the changed account and storage prefixes and /// existing trie nodes collecting updates in the process. /// /// # Returns /// /// The intermediate progress of state root computation. fn incremental_root_with_progress( tx: &'a TX, range: RangeInclusive, ) -> Result; /// Calculate the state root for this [`HashedPostState`]. /// Internally, this method retrieves prefixsets and uses them /// to calculate incremental state root. /// /// # Example /// /// ``` /// use reth_db::test_utils::create_test_rw_db; /// use reth_db_api::database::Database; /// use reth_primitives::{Account, U256}; /// use reth_trie::{updates::TrieUpdates, HashedPostState, StateRoot}; /// use reth_trie_db::DatabaseStateRoot; /// /// // Initialize the database /// let db = create_test_rw_db(); /// /// // Initialize hashed post state /// let mut hashed_state = HashedPostState::default(); /// hashed_state.accounts.insert( /// [0x11; 32].into(), /// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), /// ); /// /// // Calculate the state root /// let tx = db.tx().expect("failed to create transaction"); /// let state_root = StateRoot::overlay_root(&tx, hashed_state); /// ``` /// /// # Returns /// /// The state root for this [`HashedPostState`]. fn overlay_root(tx: &'a TX, post_state: HashedPostState) -> Result; /// Calculates the state root for this [`HashedPostState`] and returns it alongside trie /// updates. See [`Self::overlay_root`] for more info. fn overlay_root_with_updates( tx: &'a TX, post_state: HashedPostState, ) -> Result<(B256, TrieUpdates), StateRootError>; /// Calculates the state root for provided [`HashedPostState`] using cached intermediate nodes. fn overlay_root_from_nodes( tx: &'a TX, intermediate_nodes: TrieUpdates, post_state: HashedPostState, prefix_sets: TriePrefixSetsMut, ) -> Result; /// Calculates the state root and trie updates for provided [`HashedPostState`] using /// cached intermediate nodes. fn overlay_root_from_nodes_with_updates( tx: &'a TX, intermediate_nodes: TrieUpdates, post_state: HashedPostState, prefix_sets: TriePrefixSetsMut, ) -> 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>> { fn from_tx(tx: &'a TX) -> Self { Self::new(DatabaseTrieCursorFactory::new(tx), DatabaseHashedCursorFactory::new(tx)) } fn incremental_root_calculator( tx: &'a TX, range: RangeInclusive, ) -> Result { let loaded_prefix_sets = PrefixSetLoader::new(tx).load(range)?; Ok(Self::from_tx(tx).with_prefix_sets(loaded_prefix_sets)) } fn incremental_root( tx: &'a TX, range: RangeInclusive, ) -> Result { debug!(target: "trie::loader", ?range, "incremental state root"); Self::incremental_root_calculator(tx, range)?.root() } fn incremental_root_with_updates( tx: &'a TX, range: RangeInclusive, ) -> Result<(B256, TrieUpdates), StateRootError> { debug!(target: "trie::loader", ?range, "incremental state root"); Self::incremental_root_calculator(tx, range)?.root_with_updates() } fn incremental_root_with_progress( tx: &'a TX, range: RangeInclusive, ) -> Result { debug!(target: "trie::loader", ?range, "incremental state root with progress"); Self::incremental_root_calculator(tx, range)?.root_with_progress() } fn overlay_root(tx: &'a TX, post_state: HashedPostState) -> Result { let prefix_sets = post_state.construct_prefix_sets().freeze(); let state_sorted = post_state.into_sorted(); StateRoot::new( DatabaseTrieCursorFactory::new(tx), HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), ) .with_prefix_sets(prefix_sets) .root() } fn overlay_root_with_updates( tx: &'a TX, post_state: HashedPostState, ) -> Result<(B256, TrieUpdates), StateRootError> { let prefix_sets = post_state.construct_prefix_sets().freeze(); let state_sorted = post_state.into_sorted(); StateRoot::new( DatabaseTrieCursorFactory::new(tx), HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), ) .with_prefix_sets(prefix_sets) .root_with_updates() } fn overlay_root_from_nodes( tx: &'a TX, intermediate_nodes: TrieUpdates, post_state: HashedPostState, prefix_sets: TriePrefixSetsMut, ) -> Result { let state_sorted = post_state.into_sorted(); let nodes_sorted = intermediate_nodes.into_sorted(); StateRoot::new( InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted), HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), ) .with_prefix_sets(prefix_sets.freeze()) .root() } fn overlay_root_from_nodes_with_updates( tx: &'a TX, intermediate_nodes: TrieUpdates, post_state: HashedPostState, prefix_sets: TriePrefixSetsMut, ) -> Result<(B256, TrieUpdates), StateRootError> { let state_sorted = post_state.into_sorted(); let nodes_sorted = intermediate_nodes.into_sorted(); StateRoot::new( InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted), HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), ) .with_prefix_sets(prefix_sets.freeze()) .root_with_updates() } } 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::*; use reth_db::test_utils::create_test_rw_db; use reth_db_api::database::Database; use reth_primitives::{hex, revm_primitives::AccountInfo, Address, U256}; use revm::db::BundleState; use std::collections::HashMap; #[test] fn from_bundle_state_with_rayon() { let address1 = Address::with_last_byte(1); let address2 = Address::with_last_byte(2); let slot1 = U256::from(1015); let slot2 = U256::from(2015); let account1 = AccountInfo { nonce: 1, ..Default::default() }; let account2 = AccountInfo { nonce: 2, ..Default::default() }; let bundle_state = BundleState::builder(2..=2) .state_present_account_info(address1, account1) .state_present_account_info(address2, account2) .state_storage(address1, HashMap::from([(slot1, (U256::ZERO, U256::from(10)))])) .state_storage(address2, HashMap::from([(slot2, (U256::ZERO, U256::from(20)))])) .build(); assert_eq!(bundle_state.reverts.len(), 1); let post_state = HashedPostState::from_bundle_state(&bundle_state.state); assert_eq!(post_state.accounts.len(), 2); assert_eq!(post_state.storages.len(), 2); let db = create_test_rw_db(); let tx = db.tx().expect("failed to create transaction"); assert_eq!( StateRoot::overlay_root(&tx, post_state).unwrap(), hex!("b464525710cafcf5d4044ac85b72c08b1e76231b8d91f288fe438cc41d8eaafd") ); } }