Skip to main content

reth_trie/hashed_cursor/
mod.rs

1use alloy_primitives::{keccak256, Address, B256, U256};
2use reth_primitives_traits::Account;
3use reth_storage_errors::db::DatabaseError;
4use reth_trie_common::HashedPostState;
5use revm::database::BundleAccount;
6
7/// Implementation of hashed state cursor traits for the post state.
8mod post_state;
9pub use post_state::*;
10
11/// Implementation of noop hashed state cursor.
12pub mod noop;
13
14/// Mock trie cursor implementations.
15#[cfg(any(test, feature = "test-utils"))]
16pub mod mock;
17
18/// Metrics tracking hashed cursor implementations.
19pub mod metrics;
20#[cfg(feature = "metrics")]
21pub use metrics::HashedCursorMetrics;
22pub use metrics::{HashedCursorMetricsCache, InstrumentedHashedCursor};
23
24/// The factory trait for creating cursors over the hashed state.
25#[auto_impl::auto_impl(&)]
26pub trait HashedCursorFactory {
27    /// The hashed account cursor type.
28    type AccountCursor<'a>: HashedCursor<Value = Account>
29    where
30        Self: 'a;
31    /// The hashed storage cursor type.
32    type StorageCursor<'a>: HashedStorageCursor<Value = U256>
33    where
34        Self: 'a;
35
36    /// Returns a cursor for iterating over all hashed accounts in the state.
37    fn hashed_account_cursor(&self) -> Result<Self::AccountCursor<'_>, DatabaseError>;
38
39    /// Returns a cursor for iterating over all hashed storage entries in the state.
40    fn hashed_storage_cursor(
41        &self,
42        hashed_address: B256,
43    ) -> Result<Self::StorageCursor<'_>, DatabaseError>;
44}
45
46/// The cursor for iterating over hashed entries.
47#[auto_impl::auto_impl(&mut)]
48pub trait HashedCursor {
49    /// Value returned by the cursor.
50    type Value: std::fmt::Debug;
51
52    /// Seek an entry greater than or equal to the given key and position the cursor there.
53    /// Returns the first entry with the key greater than or equal to the sought key.
54    fn seek(&mut self, key: B256) -> Result<Option<(B256, Self::Value)>, DatabaseError>;
55
56    /// Move the cursor to the next entry and return it.
57    fn next(&mut self) -> Result<Option<(B256, Self::Value)>, DatabaseError>;
58
59    /// Reset the cursor to its initial state.
60    ///
61    /// # Important
62    ///
63    /// After calling this method, the subsequent operation MUST be a [`HashedCursor::seek`] call.
64    fn reset(&mut self);
65}
66
67/// The cursor for iterating over hashed storage entries.
68#[auto_impl::auto_impl(&mut)]
69pub trait HashedStorageCursor: HashedCursor {
70    /// Returns `true` if there are no entries for a given key.
71    fn is_storage_empty(&mut self) -> Result<bool, DatabaseError>;
72
73    /// Set the hashed address for the storage cursor.
74    ///
75    /// # Important
76    ///
77    /// After calling this method, the subsequent operation MUST be a [`HashedCursor::seek`] call.
78    fn set_hashed_address(&mut self, hashed_address: B256);
79}
80
81/// Materializes storage deletions for destroyed accounts as explicit zero-valued slot updates.
82///
83/// Final bundle values take precedence so that destroy-then-recreate transitions retain storage
84/// written by the recreated account.
85pub fn zero_destroyed_account_storage<'a>(
86    cursor_factory: &impl HashedCursorFactory,
87    accounts: impl IntoIterator<Item = (&'a Address, &'a BundleAccount)>,
88    hashed_state: &mut HashedPostState,
89) -> Result<(), DatabaseError> {
90    let mut destroyed_accounts = accounts
91        .into_iter()
92        .filter(|(_, account)| account.was_destroyed())
93        .map(|(address, _)| keccak256(address));
94    let Some(mut hashed_address) = destroyed_accounts.next() else { return Ok(()) };
95    let mut cursor = cursor_factory.hashed_storage_cursor(hashed_address)?;
96
97    loop {
98        if let Some((hashed_slot, _)) = cursor.seek(B256::ZERO)? {
99            let storage = &mut hashed_state.storages.entry(hashed_address).or_default().storage;
100            storage.entry(hashed_slot).or_insert(U256::ZERO);
101            while let Some((hashed_slot, _)) = cursor.next()? {
102                storage.entry(hashed_slot).or_insert(U256::ZERO);
103            }
104        }
105
106        let Some(next_hashed_address) = destroyed_accounts.next() else { break };
107        hashed_address = next_hashed_address;
108        cursor.set_hashed_address(hashed_address);
109    }
110
111    Ok(())
112}