1use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
2use alloy_primitives::{keccak256, map::hash_map, Address, BlockNumber, B256};
3use reth_db_api::{models::BlockNumberAddress, transaction::DbTx};
4use reth_execution_errors::StorageRootError;
5use reth_storage_api::{BlockNumReader, StorageChangeSetReader};
6use reth_storage_errors::provider::ProviderResult;
7use reth_trie::{
8 hashed_cursor::HashedPostStateCursorFactory, HashedPostState, HashedStorage, StorageRoot,
9};
10
11#[cfg(feature = "metrics")]
12use reth_trie::metrics::TrieRootMetrics;
13
14pub trait DatabaseStorageRoot<'a, TX> {
16 fn from_tx(tx: &'a TX, address: Address) -> Self;
18
19 fn from_tx_hashed(tx: &'a TX, hashed_address: B256) -> Self;
21
22 fn overlay_root(
24 tx: &'a TX,
25 address: Address,
26 hashed_storage: HashedStorage,
27 ) -> Result<B256, StorageRootError>;
28}
29
30pub fn hashed_storage_from_reverts_with_provider<P>(
32 provider: &P,
33 address: Address,
34 from: BlockNumber,
35) -> ProviderResult<HashedStorage>
36where
37 P: StorageChangeSetReader + BlockNumReader,
38{
39 let mut storage = HashedStorage::new(false);
40 let tip = provider.last_block_number()?;
41
42 if from > tip {
43 return Ok(storage)
44 }
45
46 for (BlockNumberAddress((_, storage_address)), storage_change) in
47 provider.storage_changesets_range(from..=tip)?
48 {
49 if storage_address == address {
50 let hashed_slot = storage_change.key.to_hashed();
51 if let hash_map::Entry::Vacant(entry) = storage.storage.entry(hashed_slot) {
52 entry.insert(storage_change.value);
53 }
54 }
55 }
56
57 Ok(storage)
58}
59
60impl<'a, TX: DbTx> DatabaseStorageRoot<'a, TX>
61 for StorageRoot<DatabaseTrieCursorFactory<&'a TX>, DatabaseHashedCursorFactory<&'a TX>>
62{
63 fn from_tx(tx: &'a TX, address: Address) -> Self {
64 Self::new(
65 DatabaseTrieCursorFactory::new(tx),
66 DatabaseHashedCursorFactory::new(tx),
67 address,
68 Default::default(),
69 #[cfg(feature = "metrics")]
70 TrieRootMetrics::new(reth_trie::TrieType::Storage),
71 )
72 }
73
74 fn from_tx_hashed(tx: &'a TX, hashed_address: B256) -> Self {
75 Self::new_hashed(
76 DatabaseTrieCursorFactory::new(tx),
77 DatabaseHashedCursorFactory::new(tx),
78 hashed_address,
79 Default::default(),
80 #[cfg(feature = "metrics")]
81 TrieRootMetrics::new(reth_trie::TrieType::Storage),
82 )
83 }
84
85 fn overlay_root(
86 tx: &'a TX,
87 address: Address,
88 hashed_storage: HashedStorage,
89 ) -> Result<B256, StorageRootError> {
90 let prefix_set = hashed_storage.construct_prefix_set().freeze();
91 let state_sorted =
92 HashedPostState::from_hashed_storage(keccak256(address), hashed_storage).into_sorted();
93 StorageRoot::new(
94 DatabaseTrieCursorFactory::new(tx),
95 HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
96 address,
97 prefix_set,
98 #[cfg(feature = "metrics")]
99 TrieRootMetrics::new(reth_trie::TrieType::Storage),
100 )
101 .root()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use alloy_consensus::Header;
109 use alloy_primitives::U256;
110 use reth_db_api::{models::BlockNumberAddress, tables, transaction::DbTxMut};
111 use reth_primitives_traits::StorageEntry;
112 use reth_provider::{
113 test_utils::create_test_provider_factory, StaticFileProviderFactory, StaticFileSegment,
114 StaticFileWriter, StorageSettingsCache,
115 };
116
117 fn append_storage_changesets_to_static_files(
118 factory: &impl StaticFileProviderFactory<
119 Primitives: reth_primitives_traits::NodePrimitives<BlockHeader = Header>,
120 >,
121 changesets: Vec<(u64, Vec<reth_db_api::models::StorageBeforeTx>)>,
122 ) {
123 let sf = factory.static_file_provider();
124 let mut writer = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
125 for (block_number, changeset) in changesets {
126 writer.append_storage_changeset(changeset, block_number).unwrap();
127 }
128 writer.commit().unwrap();
129 }
130
131 fn append_headers_to_static_files(
132 factory: &impl StaticFileProviderFactory<
133 Primitives: reth_primitives_traits::NodePrimitives<BlockHeader = Header>,
134 >,
135 up_to_block: u64,
136 ) {
137 let sf = factory.static_file_provider();
138 let mut writer = sf.latest_writer(StaticFileSegment::Headers).unwrap();
139 let mut header = Header::default();
140 for num in 0..=up_to_block {
141 header.number = num;
142 writer.append_header(&header, &B256::ZERO).unwrap();
143 }
144 writer.commit().unwrap();
145 }
146
147 #[test]
148 fn test_hashed_storage_from_reverts_legacy() {
149 let factory = create_test_provider_factory();
150 let provider = factory.provider_rw().unwrap();
151
152 assert!(!provider.cached_storage_settings().use_hashed_state());
153
154 let address = Address::with_last_byte(42);
155 let slot1 = B256::from(U256::from(100));
156 let slot2 = B256::from(U256::from(200));
157
158 append_headers_to_static_files(&factory, 5);
159
160 provider
161 .tx_ref()
162 .put::<tables::StorageChangeSets>(
163 BlockNumberAddress((1, address)),
164 StorageEntry { key: slot1, value: U256::from(10) },
165 )
166 .unwrap();
167 provider
168 .tx_ref()
169 .put::<tables::StorageChangeSets>(
170 BlockNumberAddress((2, address)),
171 StorageEntry { key: slot2, value: U256::from(20) },
172 )
173 .unwrap();
174 provider
175 .tx_ref()
176 .put::<tables::StorageChangeSets>(
177 BlockNumberAddress((3, address)),
178 StorageEntry { key: slot1, value: U256::from(999) },
179 )
180 .unwrap();
181
182 let result = hashed_storage_from_reverts_with_provider(&*provider, address, 1).unwrap();
183
184 let hashed_slot1 = keccak256(slot1);
185 let hashed_slot2 = keccak256(slot2);
186
187 assert_eq!(result.storage.len(), 2);
188 assert_eq!(result.storage.get(&hashed_slot1), Some(&U256::from(10)));
189 assert_eq!(result.storage.get(&hashed_slot2), Some(&U256::from(20)));
190 }
191
192 #[test]
193 fn test_hashed_storage_from_reverts_hashed_state() {
194 use reth_db_api::models::{StorageBeforeTx, StorageSettings};
195
196 let factory = create_test_provider_factory();
197
198 factory.set_storage_settings_cache(StorageSettings::v2());
199
200 let provider = factory.provider_rw().unwrap();
201 assert!(provider.cached_storage_settings().use_hashed_state());
202 assert!(provider.cached_storage_settings().is_v2());
203
204 let address = Address::with_last_byte(42);
205 let plain_slot1 = B256::from(U256::from(100));
206 let plain_slot2 = B256::from(U256::from(200));
207 let hashed_slot1 = keccak256(plain_slot1);
208 let hashed_slot2 = keccak256(plain_slot2);
209
210 append_headers_to_static_files(&factory, 5);
211
212 append_storage_changesets_to_static_files(
213 &factory,
214 vec![
215 (0, vec![]),
216 (1, vec![StorageBeforeTx { address, key: hashed_slot1, value: U256::from(10) }]),
217 (2, vec![StorageBeforeTx { address, key: hashed_slot2, value: U256::from(20) }]),
218 (3, vec![StorageBeforeTx { address, key: hashed_slot1, value: U256::from(999) }]),
219 ],
220 );
221
222 let result = hashed_storage_from_reverts_with_provider(&*provider, address, 1).unwrap();
223
224 assert_eq!(result.storage.len(), 2);
225 assert_eq!(result.storage.get(&hashed_slot1), Some(&U256::from(10)));
226 assert_eq!(result.storage.get(&hashed_slot2), Some(&U256::from(20)));
227 }
228}