1use crate::{database_state_frontiers, Overlay, OverlayBuilder};
2use alloy_primitives::{BlockHash, B256};
3use metrics::{Counter, Histogram};
4use reth_db_api::{tables, transaction::DbTx, DatabaseError};
5use reth_errors::ProviderResult;
6use reth_ethereum_primitives::EthPrimitives;
7use reth_metrics::Metrics;
8use reth_primitives_traits::{
9 dashmap::{self, DashMap},
10 NodePrimitives,
11};
12use reth_storage_api::{
13 BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
14 DatabaseProviderROFactory, DbTxProvider, PruneCheckpointReader, StageCheckpointReader,
15 StorageChangeSetReader, StorageSettingsCache,
16};
17use reth_trie::{
18 hashed_cursor::{HashedCursorFactory, HashedPostStateCursorFactory},
19 trie_cursor::{InMemoryTrieCursor, TrieCursor, TrieCursorFactory, TrieStorageCursor},
20 HashedPostStateSorted,
21};
22use reth_trie_db::{
23 DatabaseAccountTrieCursor, DatabaseHashedCursorFactory, DatabaseStorageTrieCursor,
24 LegacyKeyAdapter, PackedAccountsTrie, PackedKeyAdapter, PackedStoragesTrie,
25};
26use std::{sync::Arc, time::Instant};
27use tracing::instrument;
28
29#[derive(Clone, Metrics)]
31#[metrics(scope = "storage.providers.overlay")]
32pub(crate) struct OverlayStateProviderFactoryMetrics {
33 create_provider_duration: Histogram,
35 database_provider_ro_duration: Histogram,
37 overlay_cache_misses: Counter,
39}
40
41#[derive(Debug, Clone)]
46pub struct OverlayStateProviderFactory<F, N: NodePrimitives = EthPrimitives> {
47 factory: F,
49 overlay_builder: OverlayBuilder<N>,
51 overlay_cache: Arc<DashMap<(BlockHash, BlockHash), Overlay>>,
56 metrics: OverlayStateProviderFactoryMetrics,
58}
59
60impl<F, N: NodePrimitives> OverlayStateProviderFactory<F, N> {
61 pub fn new(factory: F, overlay_builder: OverlayBuilder<N>) -> Self {
63 Self {
64 factory,
65 overlay_builder,
66 overlay_cache: Default::default(),
67 metrics: Default::default(),
68 }
69 }
70
71 pub fn with_skip_overlay_for_reused_sparse_trie(mut self, anchor_hash: B256) -> Self {
74 self.overlay_builder =
75 self.overlay_builder.with_skip_overlay_for_reused_sparse_trie(anchor_hash);
76 self.overlay_cache = Default::default();
77 self
78 }
79
80 #[instrument(level = "debug", target = "providers::state::overlay", skip_all)]
83 fn get_overlay<Provider>(&self, provider: &Provider) -> ProviderResult<Overlay>
84 where
85 Provider: StageCheckpointReader
86 + PruneCheckpointReader
87 + ChangeSetReader
88 + StorageChangeSetReader
89 + DBProvider
90 + BlockNumReader
91 + StorageSettingsCache,
92 {
93 let (state_trie_tip_block, finish_tip_block) = database_state_frontiers(provider)?;
94
95 let overlay =
96 match self.overlay_cache.entry((state_trie_tip_block.hash, finish_tip_block.hash)) {
97 dashmap::Entry::Occupied(entry) => entry.get().clone(),
98 dashmap::Entry::Vacant(entry) => {
99 self.metrics.overlay_cache_misses.increment(1);
100 let overlay = self.overlay_builder.build_overlay_at_frontiers(
101 provider,
102 state_trie_tip_block,
103 finish_tip_block,
104 )?;
105 entry.insert(overlay.clone());
106 overlay
107 }
108 };
109
110 Ok(overlay)
111 }
112}
113
114impl<F, N> DatabaseProviderROFactory for OverlayStateProviderFactory<F, N>
115where
116 N: NodePrimitives,
117 F: DatabaseProviderFactory,
118 F::Provider: StageCheckpointReader
119 + PruneCheckpointReader
120 + BlockNumReader
121 + ChangeSetReader
122 + StorageChangeSetReader
123 + StorageSettingsCache,
124{
125 type Provider = OverlayStateProvider<F::Provider>;
126
127 #[instrument(level = "debug", target = "providers::state::overlay", skip_all)]
129 fn database_provider_ro(&self) -> ProviderResult<OverlayStateProvider<F::Provider>> {
130 let overall_start = Instant::now();
131
132 let provider = {
134 let start = Instant::now();
135 let res = self.factory.database_provider_ro()?;
136 self.metrics.create_provider_duration.record(start.elapsed());
137 res
138 };
139
140 let overlay = self.get_overlay(&provider)?;
141
142 let is_v2 = provider.cached_storage_settings().is_v2();
143 self.metrics.database_provider_ro_duration.record(overall_start.elapsed());
144 Ok(OverlayStateProvider::new(provider, overlay, is_v2))
145 }
146}
147
148#[derive(Debug)]
154pub struct OverlayStateProvider<Provider> {
155 provider: Provider,
156 overlay: Overlay,
157 is_v2: bool,
158}
159
160impl<Provider> OverlayStateProvider<Provider> {
161 pub const fn new(provider: Provider, overlay: Overlay, is_v2: bool) -> Self {
163 Self { provider, overlay, is_v2 }
164 }
165}
166
167impl<Provider> TrieCursorFactory for OverlayStateProvider<Provider>
168where
169 Provider: DbTxProvider,
170{
171 type AccountTrieCursor<'a>
172 = InMemoryTrieCursor<'a, Box<dyn TrieCursor + Send + 'a>>
173 where
174 Self: 'a;
175
176 type StorageTrieCursor<'a>
177 = InMemoryTrieCursor<'a, Box<dyn TrieStorageCursor + Send + 'a>>
178 where
179 Self: 'a;
180
181 fn account_trie_cursor(&self) -> Result<Self::AccountTrieCursor<'_>, DatabaseError> {
182 let cursor: Box<dyn TrieCursor + Send> = if self.is_v2 {
183 Box::new(DatabaseAccountTrieCursor::<_, PackedKeyAdapter>::new(
184 self.provider.tx().cursor_read::<PackedAccountsTrie>()?,
185 ))
186 } else {
187 Box::new(DatabaseAccountTrieCursor::<_, LegacyKeyAdapter>::new(
188 self.provider.tx().cursor_read::<tables::AccountsTrie>()?,
189 ))
190 };
191 Ok(InMemoryTrieCursor::new_account(cursor, &self.overlay.trie_updates))
192 }
193
194 fn storage_trie_cursor(
195 &self,
196 hashed_address: B256,
197 ) -> Result<Self::StorageTrieCursor<'_>, DatabaseError> {
198 let cursor: Box<dyn TrieStorageCursor + Send> = if self.is_v2 {
199 Box::new(DatabaseStorageTrieCursor::<_, PackedKeyAdapter>::new(
200 self.provider.tx().cursor_dup_read::<PackedStoragesTrie>()?,
201 hashed_address,
202 ))
203 } else {
204 Box::new(DatabaseStorageTrieCursor::<_, LegacyKeyAdapter>::new(
205 self.provider.tx().cursor_dup_read::<tables::StoragesTrie>()?,
206 hashed_address,
207 ))
208 };
209 Ok(InMemoryTrieCursor::new_storage(cursor, &self.overlay.trie_updates, hashed_address))
210 }
211}
212
213impl<Provider> HashedCursorFactory for OverlayStateProvider<Provider>
214where
215 Provider: DbTxProvider,
216{
217 type AccountCursor<'a>
218 = <HashedPostStateCursorFactory<
219 DatabaseHashedCursorFactory<&'a Provider::Tx>,
220 &'a Arc<HashedPostStateSorted>,
221 > as HashedCursorFactory>::AccountCursor<'a>
222 where
223 Self: 'a;
224
225 type StorageCursor<'a>
226 = <HashedPostStateCursorFactory<
227 DatabaseHashedCursorFactory<&'a Provider::Tx>,
228 &'a Arc<HashedPostStateSorted>,
229 > as HashedCursorFactory>::StorageCursor<'a>
230 where
231 Self: 'a;
232
233 fn hashed_account_cursor(&self) -> Result<Self::AccountCursor<'_>, DatabaseError> {
234 HashedPostStateCursorFactory::new(
235 DatabaseHashedCursorFactory::new(self.provider.tx()),
236 &self.overlay.hashed_post_state,
237 )
238 .hashed_account_cursor()
239 }
240
241 fn hashed_storage_cursor(
242 &self,
243 hashed_address: B256,
244 ) -> Result<Self::StorageCursor<'_>, DatabaseError> {
245 HashedPostStateCursorFactory::new(
246 DatabaseHashedCursorFactory::new(self.provider.tx()),
247 &self.overlay.hashed_post_state,
248 )
249 .hashed_storage_cursor(hashed_address)
250 }
251}
252
253#[cfg(all(test, feature = "partial-persistence"))]
254mod tests {
255 use super::*;
256 use crate::OverlayManager;
257 use alloy_primitives::U256;
258 use reth_chain_state::{test_utils::TestBlockBuilder, ExecutedBlock};
259 use reth_primitives_traits::Account;
260 use reth_provider::{
261 test_utils::{create_test_provider_factory, MockNodeTypesWithDB},
262 BlockWriter, ProviderFactory,
263 };
264 use reth_stages_types::{FinishCheckpoint, StageCheckpoint, StageId};
265 use reth_storage_api::StageCheckpointWriter;
266 use reth_trie::{
267 updates::TrieUpdatesSorted, BranchNodeCompact, ComputedTrieData, HashedPostState,
268 HashedStorage, Nibbles,
269 };
270
271 fn with_unique_trie_data(
272 block: &ExecutedBlock<EthPrimitives>,
273 id: u8,
274 ) -> ExecutedBlock<EthPrimitives> {
275 let hashed_address = B256::with_last_byte(id);
276 let hashed_slot = B256::with_last_byte(id.saturating_add(32));
277 let hashed_state = HashedPostState::default()
278 .with_accounts([(hashed_address, Some(Account::default()))])
279 .with_storages([(
280 hashed_address,
281 HashedStorage::from_iter([(hashed_slot, U256::from(id))]),
282 )])
283 .into_sorted();
284 let trie_updates = TrieUpdatesSorted::new(
285 vec![(
286 Nibbles::from_nibbles([id]),
287 Some(BranchNodeCompact::new(0, 0, 0, vec![], None)),
288 )],
289 Default::default(),
290 );
291
292 ExecutedBlock::new(
293 Arc::clone(&block.recovered_block),
294 Arc::clone(&block.execution_output),
295 ComputedTrieData::new(Arc::new(hashed_state), Arc::new(trie_updates)),
296 )
297 }
298
299 fn test_blocks() -> Vec<ExecutedBlock<EthPrimitives>> {
300 TestBlockBuilder::eth()
301 .get_executed_blocks(0..5)
302 .enumerate()
303 .map(|(index, block)| with_unique_trie_data(&block, index as u8 + 1))
304 .collect()
305 }
306
307 fn setup_frontiers(
308 state_trie_tip_index: usize,
309 finish_tip_index: usize,
310 ) -> (ProviderFactory<MockNodeTypesWithDB>, Vec<ExecutedBlock<EthPrimitives>>) {
311 let factory = create_test_provider_factory();
312 let blocks = test_blocks();
313 let provider_rw = factory.provider_rw().unwrap();
314 for block in &blocks[..=finish_tip_index] {
315 provider_rw.insert_block(block.recovered_block()).unwrap();
316 }
317 provider_rw
318 .save_stage_checkpoint(
319 StageId::Finish,
320 StageCheckpoint::new(blocks[finish_tip_index].block_number())
321 .with_finish_stage_checkpoint(FinishCheckpoint {
322 partial_state_trie: Some(blocks[state_trie_tip_index].block_number()),
323 }),
324 )
325 .unwrap();
326 provider_rw.commit().unwrap();
327
328 (factory, blocks)
329 }
330
331 fn account_keys(overlay: &Overlay) -> Vec<B256> {
332 overlay.hashed_post_state.accounts.iter().map(|(key, _)| *key).collect()
333 }
334
335 fn account_node_paths(overlay: &Overlay) -> Vec<Nibbles> {
336 overlay.trie_updates.account_nodes_ref().iter().map(|(path, _)| *path).collect()
337 }
338
339 #[test]
340 fn overlay_cache_is_keyed_by_both_durable_frontiers() {
341 let (factory, blocks) = setup_frontiers(1, 3);
342 let manager = OverlayManager::default();
343 for block in &blocks[2..=3] {
344 manager.insert_block(block.clone());
345 }
346 let overlay_factory = OverlayStateProviderFactory::new(
347 factory.clone(),
348 manager.overlay_builder(blocks[3].recovered_block().hash()),
349 );
350
351 let provider = factory.provider().unwrap();
352 let first = overlay_factory.get_overlay(&provider).unwrap();
353 assert_eq!(account_keys(&first), vec![B256::with_last_byte(3), B256::with_last_byte(4)]);
354 drop(provider);
355
356 let provider_rw = factory.provider_rw().unwrap();
357 provider_rw
358 .save_stage_checkpoint(
359 StageId::Finish,
360 StageCheckpoint::new(blocks[3].block_number()).with_finish_stage_checkpoint(
361 FinishCheckpoint { partial_state_trie: Some(blocks[2].block_number()) },
362 ),
363 )
364 .unwrap();
365 provider_rw.commit().unwrap();
366
367 let provider = factory.provider().unwrap();
368 let second = overlay_factory.get_overlay(&provider).unwrap();
369 assert_eq!(account_keys(&second), vec![B256::with_last_byte(4)]);
370 assert_eq!(account_node_paths(&second), vec![Nibbles::from_nibbles([4])]);
371 assert_eq!(overlay_factory.overlay_cache.len(), 2);
372 }
373}