diff --git a/Cargo.lock b/Cargo.lock index c328e6c56f..d475e5d6fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6858,6 +6858,7 @@ dependencies = [ "reth-interfaces", "reth-primitives", "reth-provider", + "revm", "serde_json", "thiserror", "tokio", diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index 9093f4ddcc..525bc0eb3b 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -306,8 +306,8 @@ impl Command { let hashed_state = state.hash_state_slow(); let (state_root, trie_updates) = state - .state_root_calculator(provider_factory.provider()?.tx_ref(), &hashed_state) - .root_with_updates()?; + .hash_state_slow() + .state_root_with_updates(provider_factory.provider()?.tx_ref())?; if state_root != block_with_senders.state_root { eyre::bail!( diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 10fefc611c..0aa5333432 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1174,12 +1174,9 @@ impl BlockchainTree { } None => { debug!(target: "blockchain_tree", blocks = ?block_hash_numbers, "Recomputing state root for insert"); - let (state_root, trie_updates) = state - .state_root_calculator( - self.externals.provider_factory.provider()?.tx_ref(), - &hashed_state, - ) - .root_with_updates() + let provider = self.externals.provider_factory.provider()?; + let (state_root, trie_updates) = hashed_state + .state_root_with_updates(provider.tx_ref()) .map_err(Into::::into)?; let tip = blocks.tip(); if state_root != tip.state_root { diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index 196e8cd5ce..72bba49491 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -6,16 +6,12 @@ use reth_db::{ }; use reth_interfaces::db::DatabaseError; use reth_primitives::{ - keccak256, logs_bloom, + logs_bloom, revm::compat::{into_reth_acc, into_revm_acc}, Account, Address, BlockNumber, Bloom, Bytecode, Log, Receipt, Receipts, StorageEntry, B256, U256, }; -use reth_trie::{ - hashed_cursor::{HashedPostState, HashedPostStateCursorFactory, HashedStorage}, - updates::TrieUpdates, - StateRoot, StateRootError, -}; +use reth_trie::HashedPostState; use revm::{ db::{states::BundleState, BundleAccount}, primitives::AccountInfo, @@ -135,106 +131,10 @@ impl BundleStateWithReceipts { self.bundle.bytecode(code_hash).map(Bytecode) } - /// Hash all changed accounts and storage entries that are currently stored in the post state. - /// - /// # Returns - /// - /// The hashed post state. + /// Returns [HashedPostState] for this bundle state. + /// See [HashedPostState::from_bundle_state] for more info. pub fn hash_state_slow(&self) -> HashedPostState { - let mut hashed_state = HashedPostState::default(); - - for (address, account) in self.bundle.state() { - let hashed_address = keccak256(address); - if let Some(account) = &account.info { - hashed_state.insert_account(hashed_address, into_reth_acc(account.clone())) - } else { - hashed_state.insert_destroyed_account(hashed_address); - } - - // insert storage. - let mut hashed_storage = HashedStorage::new(account.status.was_destroyed()); - - for (key, value) in account.storage.iter() { - let hashed_key = keccak256(B256::new(key.to_be_bytes())); - if value.present_value.is_zero() { - hashed_storage.insert_zero_valued_slot(hashed_key); - } else { - hashed_storage.insert_non_zero_valued_storage(hashed_key, value.present_value); - } - } - hashed_state.insert_hashed_storage(hashed_address, hashed_storage) - } - hashed_state.sorted() - } - - /// Returns [StateRoot] calculator based on database and in-memory state. - pub fn state_root_calculator<'a, 'b, TX: DbTx>( - &self, - tx: &'a TX, - hashed_post_state: &'b HashedPostState, - ) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'a, 'b, TX>> { - let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); - let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, hashed_post_state); - StateRoot::from_tx(tx) - .with_hashed_cursor_factory(hashed_cursor_factory) - .with_changed_account_prefixes(account_prefix_set) - .with_changed_storage_prefixes(storage_prefix_set) - .with_destroyed_accounts(hashed_post_state.destroyed_accounts()) - } - - /// Calculate the state root for this [BundleState]. - /// Internally, function calls [Self::hash_state_slow] to obtain the [HashedPostState]. - /// Afterwards, it retrieves the prefixsets from the [HashedPostState] and uses them to - /// calculate the incremental state root. - /// - /// # Example - /// - /// ``` - /// use reth_db::{database::Database, test_utils::create_test_rw_db}; - /// use reth_primitives::{Account, Receipts, U256}; - /// use reth_provider::BundleStateWithReceipts; - /// use std::collections::HashMap; - /// - /// // Initialize the database - /// let db = create_test_rw_db(); - /// - /// // Initialize the bundle state - /// let bundle = BundleStateWithReceipts::new_init( - /// HashMap::from([( - /// [0x11; 20].into(), - /// ( - /// None, - /// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), - /// HashMap::from([]), - /// ), - /// )]), - /// HashMap::from([]), - /// vec![], - /// Receipts::new(), - /// 0, - /// ); - /// - /// // Calculate the state root - /// let tx = db.tx().expect("failed to create transaction"); - /// let state_root = bundle.state_root_slow(&tx); - /// ``` - /// - /// # Returns - /// - /// The state root for this [BundleState]. - pub fn state_root_slow(&self, tx: &TX) -> Result { - let hashed_post_state = self.hash_state_slow(); - self.state_root_calculator(tx, &hashed_post_state).root() - } - - /// Calculates the state root for this [BundleState] and returns it alongside trie updates. - /// See [Self::state_root_slow] for more info. - pub fn state_root_slow_with_updates( - &self, - tx: &TX, - ) -> Result<(B256, TrieUpdates), StateRootError> { - let hashed_post_state = self.hash_state_slow(); - self.state_root_calculator(tx, &hashed_post_state).root_with_updates() + HashedPostState::from_bundle_state(&self.bundle.state) } /// Transform block number to the index of block. @@ -439,9 +339,10 @@ mod tests { transaction::DbTx, }; use reth_primitives::{ - revm::compat::into_reth_acc, Address, Receipt, Receipts, StorageEntry, B256, U256, + keccak256, revm::compat::into_reth_acc, Address, Receipt, Receipts, StorageEntry, B256, + U256, }; - use reth_trie::test_utils::state_root; + use reth_trie::{test_utils::state_root, StateRoot}; use revm::{ db::{ states::{ @@ -1265,7 +1166,8 @@ mod tests { let assert_state_root = |state: &State, expected: &PreState, msg| { assert_eq!( BundleStateWithReceipts::new(state.bundle_state.clone(), Receipts::default(), 0) - .state_root_slow(&tx) + .hash_state_slow() + .state_root(&tx) .unwrap(), state_root(expected.clone().into_iter().map(|(address, (account, storage))| ( address, diff --git a/crates/storage/provider/src/bundle_state/hashed_state_changes.rs b/crates/storage/provider/src/bundle_state/hashed_state_changes.rs index cfdacb9736..30b8cfd09e 100644 --- a/crates/storage/provider/src/bundle_state/hashed_state_changes.rs +++ b/crates/storage/provider/src/bundle_state/hashed_state_changes.rs @@ -5,7 +5,7 @@ use reth_db::{ DatabaseError, }; use reth_primitives::{Account, StorageEntry, B256, U256}; -use reth_trie::hashed_cursor::HashedPostState; +use reth_trie::HashedPostState; use std::collections::BTreeMap; /// Changes to the hashed state. @@ -73,7 +73,7 @@ mod tests { use super::*; use crate::test_utils::create_test_provider_factory; use reth_primitives::{keccak256, Address}; - use reth_trie::hashed_cursor::HashedStorage; + use reth_trie::HashedStorage; #[test] fn wiped_entries_are_removed() { diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 54d657be7c..1180a755cc 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -45,9 +45,7 @@ use reth_primitives::{ StorageEntry, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, B256, U256, }; -use reth_trie::{ - hashed_cursor::HashedPostState, prefix_set::PrefixSetMut, updates::TrieUpdates, StateRoot, -}; +use reth_trie::{prefix_set::PrefixSetMut, updates::TrieUpdates, HashedPostState, StateRoot}; use revm::primitives::{BlockEnv, CfgEnv, SpecId}; use std::{ collections::{hash_map, BTreeMap, BTreeSet, HashMap}, diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index a1f1ae1349..51616bee8c 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -60,7 +60,10 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> { impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> { fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult { - bundle_state.state_root_slow(self.db).map_err(|err| ProviderError::Database(err.into())) + bundle_state + .hash_state_slow() + .state_root(self.db) + .map_err(|err| ProviderError::Database(err.into())) } fn state_root_with_updates( @@ -68,7 +71,8 @@ impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> { bundle_state: &BundleStateWithReceipts, ) -> ProviderResult<(B256, TrieUpdates)> { bundle_state - .state_root_slow_with_updates(self.db) + .hash_state_slow() + .state_root_with_updates(self.db) .map_err(|err| ProviderError::Database(err.into())) } } diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index 0d90c20463..f7af38166e 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -9,7 +9,7 @@ use reth_primitives::{ Block, BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, BlockWithSenders, ChainSpec, Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, B256, }; -use reth_trie::{hashed_cursor::HashedPostState, updates::TrieUpdates}; +use reth_trie::{updates::TrieUpdates, HashedPostState}; use std::ops::RangeInclusive; /// Enum to control transaction hash inclusion. diff --git a/crates/trie/Cargo.toml b/crates/trie/Cargo.toml index 7f446a400f..a95c37525e 100644 --- a/crates/trie/Cargo.toml +++ b/crates/trie/Cargo.toml @@ -16,9 +16,12 @@ workspace = true reth-primitives.workspace = true reth-interfaces.workspace = true reth-db.workspace = true +revm.workspace = true +# alloy alloy-rlp.workspace = true alloy-chains.workspace = true + # tokio tokio = { workspace = true, default-features = false, features = ["sync"] } diff --git a/crates/trie/src/hashed_cursor/post_state.rs b/crates/trie/src/hashed_cursor/post_state.rs index 73cad571f4..e6177c78dd 100644 --- a/crates/trie/src/hashed_cursor/post_state.rs +++ b/crates/trie/src/hashed_cursor/post_state.rs @@ -1,183 +1,11 @@ use super::{HashedAccountCursor, HashedCursorFactory, HashedStorageCursor}; -use crate::prefix_set::{PrefixSet, PrefixSetMut}; -use ahash::{AHashMap, AHashSet}; +use crate::state::HashedPostState; use reth_db::{ cursor::{DbCursorRO, DbDupCursorRO}, tables, transaction::DbTx, }; -use reth_primitives::{trie::Nibbles, Account, StorageEntry, B256, U256}; - -/// The post state account storage with hashed slots. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct HashedStorage { - /// Hashed storage slots with non-zero. - non_zero_valued_storage: Vec<(B256, U256)>, - /// Slots that have been zero valued. - zero_valued_slots: AHashSet, - /// Whether the storage was wiped or not. - wiped: bool, - /// Whether the storage entries were sorted or not. - sorted: bool, -} - -impl HashedStorage { - /// Create new instance of [HashedStorage]. - pub fn new(wiped: bool) -> Self { - Self { - non_zero_valued_storage: Vec::new(), - zero_valued_slots: AHashSet::new(), - wiped, - sorted: true, // empty is sorted - } - } - - /// Returns `true` if the storage was wiped. - pub fn wiped(&self) -> bool { - self.wiped - } - - /// Returns all storage slots. - pub fn storage_slots(&self) -> impl Iterator + '_ { - self.zero_valued_slots - .iter() - .map(|slot| (*slot, U256::ZERO)) - .chain(self.non_zero_valued_storage.iter().cloned()) - } - - /// Sorts the non zero value storage entries. - pub fn sort_storage(&mut self) { - if !self.sorted { - self.non_zero_valued_storage.sort_unstable_by_key(|(slot, _)| *slot); - self.sorted = true; - } - } - - /// Insert non zero-valued storage entry. - pub fn insert_non_zero_valued_storage(&mut self, slot: B256, value: U256) { - debug_assert!(value != U256::ZERO, "value cannot be zero"); - self.non_zero_valued_storage.push((slot, value)); - self.sorted = false; - } - - /// Insert zero-valued storage slot. - pub fn insert_zero_valued_slot(&mut self, slot: B256) { - self.zero_valued_slots.insert(slot); - } -} - -/// The post state with hashed addresses as keys. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct HashedPostState { - /// Map of hashed addresses to account info. - accounts: Vec<(B256, Account)>, - /// Set of destroyed accounts. - destroyed_accounts: AHashSet, - /// Map of hashed addresses to hashed storage. - storages: AHashMap, - /// Whether the account and storage entries were sorted or not. - sorted: bool, -} - -impl Default for HashedPostState { - fn default() -> Self { - Self { - accounts: Vec::new(), - destroyed_accounts: AHashSet::new(), - storages: AHashMap::new(), - sorted: true, // empty is sorted - } - } -} - -impl HashedPostState { - /// Sort and return self. - pub fn sorted(mut self) -> Self { - self.sort(); - self - } - - /// Returns all accounts with their state. - pub fn accounts(&self) -> impl Iterator)> + '_ { - self.destroyed_accounts.iter().map(|hashed_address| (*hashed_address, None)).chain( - self.accounts.iter().map(|(hashed_address, account)| (*hashed_address, Some(*account))), - ) - } - - /// Returns all account storages. - pub fn storages(&self) -> impl Iterator { - self.storages.iter() - } - - /// Sort account and storage entries. - pub fn sort(&mut self) { - if !self.sorted { - for (_, storage) in self.storages.iter_mut() { - storage.sort_storage(); - } - - self.accounts.sort_unstable_by_key(|(address, _)| *address); - self.sorted = true; - } - } - - /// Insert non-empty account info. - pub fn insert_account(&mut self, hashed_address: B256, account: Account) { - self.accounts.push((hashed_address, account)); - self.sorted = false; - } - - /// Insert destroyed hashed account key. - pub fn insert_destroyed_account(&mut self, hashed_address: B256) { - self.destroyed_accounts.insert(hashed_address); - } - - /// Insert hashed storage entry. - pub fn insert_hashed_storage(&mut self, hashed_address: B256, hashed_storage: HashedStorage) { - self.sorted &= hashed_storage.sorted; - self.storages.insert(hashed_address, hashed_storage); - } - - /// Returns all destroyed accounts. - pub fn destroyed_accounts(&self) -> AHashSet { - self.destroyed_accounts.clone() - } - - /// Construct (PrefixSet)[PrefixSet] from hashed post state. - /// The prefix sets contain the hashed account and storage keys that have been changed in the - /// post state. - pub fn construct_prefix_sets(&self) -> (PrefixSet, AHashMap) { - // Initialize prefix sets. - let mut account_prefix_set = PrefixSetMut::default(); - let mut storage_prefix_set: AHashMap = AHashMap::default(); - - // Populate account prefix set. - for (hashed_address, _) in &self.accounts { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - } - for hashed_address in &self.destroyed_accounts { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - } - - // Populate storage prefix sets. - for (hashed_address, hashed_storage) in self.storages.iter() { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - - let storage_prefix_set_entry = storage_prefix_set.entry(*hashed_address).or_default(); - for (hashed_slot, _) in &hashed_storage.non_zero_valued_storage { - storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot)); - } - for hashed_slot in &hashed_storage.zero_valued_slots { - storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot)); - } - } - - ( - account_prefix_set.freeze(), - storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(), - ) - } -} +use reth_primitives::{Account, StorageEntry, B256, U256}; /// The hashed cursor factory for the post state. #[derive(Debug)] @@ -573,6 +401,8 @@ where #[cfg(test)] mod tests { + use crate::HashedStorage; + use super::*; use proptest::prelude::*; use reth_db::{database::Database, test_utils::create_test_rw_db, transaction::DbTxMut}; diff --git a/crates/trie/src/lib.rs b/crates/trie/src/lib.rs index 866ea33b44..189bd87788 100644 --- a/crates/trie/src/lib.rs +++ b/crates/trie/src/lib.rs @@ -32,6 +32,10 @@ pub use errors::*; // The iterators for traversing existing intermediate hashes and updated trie leaves. pub(crate) mod node_iter; +/// In-memory hashed state. +mod state; +pub use state::{HashedPostState, HashedStorage}; + /// Merkle proof generation. pub mod proof; diff --git a/crates/trie/src/state.rs b/crates/trie/src/state.rs new file mode 100644 index 0000000000..a1a4d61be8 --- /dev/null +++ b/crates/trie/src/state.rs @@ -0,0 +1,272 @@ +use crate::{ + hashed_cursor::HashedPostStateCursorFactory, + prefix_set::{PrefixSet, PrefixSetMut}, + updates::TrieUpdates, + StateRoot, StateRootError, +}; +use ahash::{AHashMap, AHashSet}; +use reth_db::transaction::DbTx; +use reth_primitives::{ + keccak256, revm::compat::into_reth_acc, trie::Nibbles, Account, Address, B256, U256, +}; +use revm::db::BundleAccount; + +/// The post state with hashed addresses as keys. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct HashedPostState { + /// Collection of hashed addresses and their account info. + pub(crate) accounts: Vec<(B256, Account)>, + /// Set of destroyed account keys. + pub(crate) destroyed_accounts: AHashSet, + /// Map of hashed addresses to hashed storage. + pub(crate) storages: AHashMap, + /// Flag indicating whether the account and storage entries were sorted. + pub(crate) sorted: bool, +} + +impl Default for HashedPostState { + fn default() -> Self { + Self { + accounts: Vec::new(), + destroyed_accounts: AHashSet::new(), + storages: AHashMap::new(), + sorted: true, // empty is sorted + } + } +} + +impl HashedPostState { + /// Initialize [HashedPostState] from bundle state. + /// Hashes all changed accounts and storage entries that are currently stored in the bundle + /// state. + pub fn from_bundle_state<'a>( + state: impl IntoIterator, + ) -> Self { + let mut hashed_state = Self::default(); + + for (address, account) in state { + let hashed_address = keccak256(address); + if let Some(account) = &account.info { + hashed_state.insert_account(hashed_address, into_reth_acc(account.clone())) + } else { + hashed_state.insert_destroyed_account(hashed_address); + } + + // insert storage. + let mut hashed_storage = HashedStorage::new(account.status.was_destroyed()); + + for (key, value) in account.storage.iter() { + let hashed_key = keccak256(B256::new(key.to_be_bytes())); + if value.present_value.is_zero() { + hashed_storage.insert_zero_valued_slot(hashed_key); + } else { + hashed_storage.insert_non_zero_valued_storage(hashed_key, value.present_value); + } + } + hashed_state.insert_hashed_storage(hashed_address, hashed_storage) + } + hashed_state.sorted() + } + + /// Sort and return self. + pub fn sorted(mut self) -> Self { + self.sort(); + self + } + + /// Returns all accounts with their state. + pub fn accounts(&self) -> impl Iterator)> + '_ { + self.destroyed_accounts.iter().map(|hashed_address| (*hashed_address, None)).chain( + self.accounts.iter().map(|(hashed_address, account)| (*hashed_address, Some(*account))), + ) + } + + /// Returns all account storages. + pub fn storages(&self) -> impl Iterator { + self.storages.iter() + } + + /// Sort account and storage entries. + pub fn sort(&mut self) { + if !self.sorted { + for (_, storage) in self.storages.iter_mut() { + storage.sort_storage(); + } + + self.accounts.sort_unstable_by_key(|(address, _)| *address); + self.sorted = true; + } + } + + /// Insert non-empty account info. + pub fn insert_account(&mut self, hashed_address: B256, account: Account) { + self.accounts.push((hashed_address, account)); + self.sorted = false; + } + + /// Insert destroyed hashed account key. + pub fn insert_destroyed_account(&mut self, hashed_address: B256) { + self.destroyed_accounts.insert(hashed_address); + } + + /// Insert hashed storage entry. + pub fn insert_hashed_storage(&mut self, hashed_address: B256, hashed_storage: HashedStorage) { + self.sorted &= hashed_storage.sorted; + self.storages.insert(hashed_address, hashed_storage); + } + + /// Returns all destroyed accounts. + pub fn destroyed_accounts(&self) -> AHashSet { + self.destroyed_accounts.clone() + } + + /// Construct [PrefixSet] from hashed post state. + /// The prefix sets contain the hashed account and storage keys that have been changed in the + /// post state. + pub fn construct_prefix_sets(&self) -> (PrefixSet, AHashMap) { + // Initialize prefix sets. + let mut account_prefix_set = PrefixSetMut::default(); + let mut storage_prefix_set: AHashMap = AHashMap::default(); + + // Populate account prefix set. + for (hashed_address, _) in &self.accounts { + account_prefix_set.insert(Nibbles::unpack(hashed_address)); + } + for hashed_address in &self.destroyed_accounts { + account_prefix_set.insert(Nibbles::unpack(hashed_address)); + } + + // Populate storage prefix sets. + for (hashed_address, hashed_storage) in self.storages.iter() { + account_prefix_set.insert(Nibbles::unpack(hashed_address)); + + let storage_prefix_set_entry = storage_prefix_set.entry(*hashed_address).or_default(); + for (hashed_slot, _) in &hashed_storage.non_zero_valued_storage { + storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot)); + } + for hashed_slot in &hashed_storage.zero_valued_slots { + storage_prefix_set_entry.insert(Nibbles::unpack(hashed_slot)); + } + } + + ( + account_prefix_set.freeze(), + storage_prefix_set.into_iter().map(|(k, v)| (k, v.freeze())).collect(), + ) + } + + /// Returns [StateRoot] calculator based on database and in-memory state. + fn state_root_calculator<'a, TX: DbTx>( + &self, + tx: &'a TX, + ) -> StateRoot<&'a TX, HashedPostStateCursorFactory<'a, '_, TX>> { + let (account_prefix_set, storage_prefix_set) = self.construct_prefix_sets(); + let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, self); + StateRoot::from_tx(tx) + .with_hashed_cursor_factory(hashed_cursor_factory) + .with_changed_account_prefixes(account_prefix_set) + .with_changed_storage_prefixes(storage_prefix_set) + .with_destroyed_accounts(self.destroyed_accounts()) + } + + /// Calculate the state root for this [HashedPostState]. + /// Internally, this method retrieves prefixsets and uses them + /// to calculate incremental state root. + /// + /// # Example + /// + /// ``` + /// use reth_db::{database::Database, test_utils::create_test_rw_db}; + /// use reth_primitives::{Account, U256}; + /// use reth_trie::HashedPostState; + /// + /// // Initialize the database + /// let db = create_test_rw_db(); + /// + /// // Initialize hashed post state + /// let mut hashed_state = HashedPostState::default(); + /// hashed_state.insert_account( + /// [0x11; 32].into(), + /// Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }, + /// ); + /// hashed_state.sort(); + /// + /// // Calculate the state root + /// let tx = db.tx().expect("failed to create transaction"); + /// let state_root = hashed_state.state_root(&tx); + /// ``` + /// + /// # Returns + /// + /// The state root for this [HashedPostState]. + pub fn state_root(&self, tx: &TX) -> Result { + self.state_root_calculator(tx).root() + } + + /// Calculates the state root for this [HashedPostState] and returns it alongside trie updates. + /// See [Self::state_root] for more info. + pub fn state_root_with_updates( + &self, + tx: &TX, + ) -> Result<(B256, TrieUpdates), StateRootError> { + self.state_root_calculator(tx).root_with_updates() + } +} + +/// The post state account storage with hashed slots. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct HashedStorage { + /// Hashed storage slots with non-zero. + pub(crate) non_zero_valued_storage: Vec<(B256, U256)>, + /// Slots that have been zero valued. + pub(crate) zero_valued_slots: AHashSet, + /// Whether the storage was wiped or not. + pub(crate) wiped: bool, + /// Whether the storage entries were sorted or not. + pub(crate) sorted: bool, +} + +impl HashedStorage { + /// Create new instance of [HashedStorage]. + pub fn new(wiped: bool) -> Self { + Self { + non_zero_valued_storage: Vec::new(), + zero_valued_slots: AHashSet::new(), + wiped, + sorted: true, // empty is sorted + } + } + + /// Returns `true` if the storage was wiped. + pub fn wiped(&self) -> bool { + self.wiped + } + + /// Returns all storage slots. + pub fn storage_slots(&self) -> impl Iterator + '_ { + self.zero_valued_slots + .iter() + .map(|slot| (*slot, U256::ZERO)) + .chain(self.non_zero_valued_storage.iter().cloned()) + } + + /// Sorts the non zero value storage entries. + pub fn sort_storage(&mut self) { + if !self.sorted { + self.non_zero_valued_storage.sort_unstable_by_key(|(slot, _)| *slot); + self.sorted = true; + } + } + + /// Insert non zero-valued storage entry. + pub fn insert_non_zero_valued_storage(&mut self, slot: B256, value: U256) { + debug_assert!(value != U256::ZERO, "value cannot be zero"); + self.non_zero_valued_storage.push((slot, value)); + self.sorted = false; + } + + /// Insert zero-valued storage slot. + pub fn insert_zero_valued_slot(&mut self, slot: B256) { + self.zero_valued_slots.insert(slot); + } +}