reth_trie/hashed_cursor/
mod.rs1use 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
7mod post_state;
9pub use post_state::*;
10
11pub mod noop;
13
14#[cfg(any(test, feature = "test-utils"))]
16pub mod mock;
17
18#[cfg(feature = "metrics")]
20pub mod metrics;
21#[cfg(feature = "metrics")]
22pub use metrics::{HashedCursorMetrics, InstrumentedHashedCursor, InstrumentedHashedCursorFactory};
23
24#[cfg(not(feature = "metrics"))]
26pub type InstrumentedHashedCursorFactory<F> = F;
27
28#[auto_impl::auto_impl(&)]
30pub trait HashedCursorFactory {
31 type AccountCursor<'a>: HashedCursor<Value = Account>
33 where
34 Self: 'a;
35 type StorageCursor<'a>: HashedStorageCursor<Value = U256>
37 where
38 Self: 'a;
39
40 fn hashed_account_cursor(&self) -> Result<Self::AccountCursor<'_>, DatabaseError>;
42
43 fn hashed_storage_cursor(
45 &self,
46 hashed_address: B256,
47 ) -> Result<Self::StorageCursor<'_>, DatabaseError>;
48}
49
50#[auto_impl::auto_impl(&mut)]
52pub trait HashedCursor {
53 type Value: std::fmt::Debug;
55
56 fn seek(&mut self, key: B256) -> Result<Option<(B256, Self::Value)>, DatabaseError>;
59
60 fn next(&mut self) -> Result<Option<(B256, Self::Value)>, DatabaseError>;
62
63 fn reset(&mut self);
69}
70
71#[auto_impl::auto_impl(&mut)]
73pub trait HashedStorageCursor: HashedCursor {
74 fn is_storage_empty(&mut self) -> Result<bool, DatabaseError>;
79
80 fn set_hashed_address(&mut self, hashed_address: B256);
85}
86
87pub fn zero_destroyed_account_storage<'a>(
93 cursor_factory: &impl HashedCursorFactory,
94 accounts: impl IntoIterator<Item = (&'a Address, &'a BundleAccount)>,
95 hashed_state: &mut HashedPostState,
96) -> Result<(), DatabaseError> {
97 let mut destroyed_accounts = accounts
98 .into_iter()
99 .filter(|(_, account)| account.was_destroyed() && account.original_info.is_some())
100 .map(|(address, _)| keccak256(address));
101 let Some(mut hashed_address) = destroyed_accounts.next() else { return Ok(()) };
102 let mut cursor = cursor_factory.hashed_storage_cursor(hashed_address)?;
103
104 loop {
105 if let Some((hashed_slot, _)) = cursor.seek(B256::ZERO)? {
106 let storage = &mut hashed_state.storages.entry(hashed_address).or_default().storage;
107 storage.entry(hashed_slot).or_insert(U256::ZERO);
108 while let Some((hashed_slot, _)) = cursor.next()? {
109 storage.entry(hashed_slot).or_insert(U256::ZERO);
110 }
111 }
112
113 let Some(next_hashed_address) = destroyed_accounts.next() else { break };
114 hashed_address = next_hashed_address;
115 cursor.set_hashed_address(hashed_address);
116 }
117
118 Ok(())
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use revm::database::AccountStatus;
125
126 #[test]
127 fn zero_destroyed_storage_skips_new_accounts() {
128 let address = Address::with_last_byte(1);
129 let account = BundleAccount::new(None, None, Default::default(), AccountStatus::Destroyed);
130 let mut hashed_state = HashedPostState::default();
131
132 zero_destroyed_account_storage(
133 &mock::MockHashedCursorFactory::default(),
134 [(&address, &account)],
135 &mut hashed_state,
136 )
137 .unwrap();
138
139 assert!(hashed_state.storages.is_empty());
140 }
141}