1use crate::{
2 changesets_utils::StorageRevertsIter,
3 providers::{
4 database::{chain::ChainStorage, metrics, DatabaseProviderMetrics},
5 rocksdb::{PendingRocksDBBatches, RocksDBProvider, RocksDBWriteCtx},
6 static_file::{StaticFileWriteCtx, StaticFileWriter},
7 NodeTypesForProvider, StaticFileProvider,
8 },
9 to_range,
10 traits::{
11 AccountExtReader, BlockSource, ChangeSetReader, ReceiptProvider, StageCheckpointWriter,
12 },
13 AccountReader, BlockBodyWriter, BlockExecutionWriter, BlockHashReader, BlockNumReader,
14 BlockReader, BlockWriter, BundleStateInit, ChainStateBlockReader, ChainStateBlockWriter,
15 DBProvider, EitherReader, EitherWriter, EitherWriterDestination, HashingWriter, HeaderProvider,
16 HeaderSyncGapProvider, HistoricalStateProvider, HistoricalStateProviderRef, HistoryWriter,
17 LatestStateProvider, LatestStateProviderRef, OriginalValuesKnown, ProviderError,
18 PruneCheckpointReader, PruneCheckpointWriter, RawRocksDBBatch, RevertsInit, RocksBatchArg,
19 RocksDBProviderFactory, StageCheckpointReader, StateProviderBox, StateWriter,
20 StaticFileProviderFactory, StatsReader, StorageReader, StorageTrieWriter, TransactionVariant,
21 TransactionsProvider, TransactionsProviderExt, TrieWriter,
22};
23use alloy_consensus::{
24 transaction::{SignerRecoverable, TransactionMeta, TxHashRef},
25 BlockHeader, TxReceipt,
26};
27use alloy_eips::BlockHashOrNumber;
28use alloy_primitives::{
29 keccak256,
30 map::{hash_map, AddressSet, B256Map, HashMap},
31 Address, BlockHash, BlockNumber, StorageKey, StorageValue, TxHash, TxNumber, B256,
32};
33use itertools::Itertools;
34use parking_lot::RwLock;
35use rayon::slice::ParallelSliceMut;
36use reth_chain_state::ExecutedBlock;
37use reth_chainspec::{ChainInfo, ChainSpecProvider, EthChainSpec};
38use reth_db_api::{
39 cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
40 database::{Database, ReaderTxnTracker},
41 models::{
42 sharded_key, storage_sharded_key::StorageShardedKey, AccountBeforeTx, BlockNumberAddress,
43 BlockNumberAddressRange, ShardedKey, StorageBeforeTx, StorageSettings,
44 StoredBlockBodyIndices,
45 },
46 table::Table,
47 tables,
48 transaction::{DbTx, DbTxMut},
49 BlockNumberList,
50};
51use reth_execution_types::{BlockExecutionOutput, BlockExecutionResult, Chain, ExecutionOutcome};
52use reth_node_types::{BlockTy, BodyTy, HeaderTy, NodeTypes, ReceiptTy, TxTy};
53use reth_primitives_traits::{
54 Account, Block as _, BlockBody as _, Bytecode, FastInstant as Instant, RecoveredBlock,
55 SealedHeader, StorageEntry,
56};
57use reth_prune_types::{
58 PruneCheckpoint, PruneMode, PruneModes, PruneSegment, MINIMUM_UNWIND_SAFE_DISTANCE,
59};
60use reth_stages_types::{StageCheckpoint, StageId};
61use reth_static_file_types::StaticFileSegment;
62use reth_storage_api::{
63 BlockBodyIndicesProvider, BlockBodyReader, MetadataProvider, MetadataWriter,
64 NodePrimitivesProvider, StateProvider, StateReader, StateWriteConfig, StorageChangeSetReader,
65 StoragePath, StorageSettingsCache, TryIntoHistoricalStateProvider, WriteStateInput,
66};
67use reth_storage_errors::provider::{ProviderResult, StaticFileWriterError};
68use reth_trie::{
69 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
70 ComputedTrieData, HashedPostStateSorted,
71};
72use reth_trie_db::{ChangesetCache, DatabaseStorageTrieCursor, TrieTableAdapter};
73use revm::database::states::{
74 PlainStateReverts, PlainStorageChangeset, PlainStorageRevert, StateChangeset,
75};
76use smallvec::SmallVec;
77use std::{
78 cmp::Ordering,
79 collections::{BTreeMap, BTreeSet},
80 fmt::Debug,
81 ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive},
82 path::PathBuf,
83 sync::Arc,
84};
85use tracing::{debug, instrument, trace};
86
87#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
89pub enum CommitOrder {
90 #[default]
92 Normal,
93 Unwind,
96}
97
98impl CommitOrder {
99 pub const fn is_unwind(&self) -> bool {
101 matches!(self, Self::Unwind)
102 }
103}
104
105pub type DatabaseProviderRO<DB, N> = DatabaseProvider<<DB as Database>::TX, N>;
107
108#[derive(Debug)]
113pub struct DatabaseProviderRW<DB: Database, N: NodeTypes>(
114 pub DatabaseProvider<<DB as Database>::TXMut, N>,
115);
116
117impl<DB: Database, N: NodeTypes> Deref for DatabaseProviderRW<DB, N> {
118 type Target = DatabaseProvider<<DB as Database>::TXMut, N>;
119
120 fn deref(&self) -> &Self::Target {
121 &self.0
122 }
123}
124
125impl<DB: Database, N: NodeTypes> DerefMut for DatabaseProviderRW<DB, N> {
126 fn deref_mut(&mut self) -> &mut Self::Target {
127 &mut self.0
128 }
129}
130
131impl<DB: Database, N: NodeTypes> AsRef<DatabaseProvider<<DB as Database>::TXMut, N>>
132 for DatabaseProviderRW<DB, N>
133{
134 fn as_ref(&self) -> &DatabaseProvider<<DB as Database>::TXMut, N> {
135 &self.0
136 }
137}
138
139impl<DB: Database, N: NodeTypes + 'static> DatabaseProviderRW<DB, N> {
140 pub fn commit(self) -> ProviderResult<()> {
142 self.0.commit()
143 }
144
145 pub fn into_tx(self) -> <DB as Database>::TXMut {
147 self.0.into_tx()
148 }
149
150 #[cfg(any(test, feature = "test-utils"))]
152 pub const fn with_minimum_pruning_distance(mut self, distance: u64) -> Self {
153 self.0.minimum_pruning_distance = distance;
154 self
155 }
156}
157
158impl<DB: Database, N: NodeTypes> From<DatabaseProviderRW<DB, N>>
159 for DatabaseProvider<<DB as Database>::TXMut, N>
160{
161 fn from(provider: DatabaseProviderRW<DB, N>) -> Self {
162 provider.0
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum SaveBlocksMode {
169 Full,
172 BlocksOnly,
176}
177
178impl SaveBlocksMode {
179 pub const fn with_state(self) -> bool {
181 matches!(self, Self::Full)
182 }
183}
184
185pub struct DatabaseProvider<TX, N: NodeTypes> {
188 tx: TX,
190 chain_spec: Arc<N::ChainSpec>,
192 static_file_provider: StaticFileProvider<N::Primitives>,
194 prune_modes: PruneModes,
196 storage: Arc<N::Storage>,
198 storage_settings: Arc<RwLock<StorageSettings>>,
200 rocksdb_provider: RocksDBProvider,
202 changeset_cache: ChangesetCache,
204 runtime: reth_tasks::Runtime,
206 db_path: PathBuf,
208 pending_rocksdb_batches: PendingRocksDBBatches,
210 commit_order: CommitOrder,
212 minimum_pruning_distance: u64,
214 metrics: Arc<DatabaseProviderMetrics>,
216 reader_txn_tracker: Option<Arc<dyn ReaderTxnTracker>>,
218}
219
220impl<TX: Debug, N: NodeTypes> Debug for DatabaseProvider<TX, N> {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 let mut s = f.debug_struct("DatabaseProvider");
223 s.field("tx", &self.tx)
224 .field("chain_spec", &self.chain_spec)
225 .field("static_file_provider", &self.static_file_provider)
226 .field("prune_modes", &self.prune_modes)
227 .field("storage", &self.storage)
228 .field("storage_settings", &self.storage_settings)
229 .field("rocksdb_provider", &self.rocksdb_provider)
230 .field("changeset_cache", &self.changeset_cache)
231 .field("runtime", &self.runtime)
232 .field("pending_rocksdb_batches", &"<pending batches>")
233 .field("commit_order", &self.commit_order)
234 .field("minimum_pruning_distance", &self.minimum_pruning_distance)
235 .field("reader_txn_tracker", &"<reader txn tracker>")
236 .finish()
237 }
238}
239
240impl<TX, N: NodeTypes> DatabaseProvider<TX, N> {
241 pub const fn prune_modes_ref(&self) -> &PruneModes {
243 &self.prune_modes
244 }
245
246 pub const fn with_minimum_pruning_distance(mut self, distance: u64) -> Self {
248 self.minimum_pruning_distance = distance;
249 self
250 }
251
252 pub(crate) fn with_reader_txn_tracker<T>(mut self, reader_txn_tracker: T) -> Self
254 where
255 T: ReaderTxnTracker + 'static,
256 {
257 self.reader_txn_tracker = Some(Arc::new(reader_txn_tracker));
258 self
259 }
260}
261
262impl<TX: DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
263 fn commit_unwind(self) -> ProviderResult<()> {
274 let storage_v2 = self.cached_storage_settings().storage_v2;
275 let reader_txn_tracker = self.reader_txn_tracker.clone();
276 self.tx.commit()?;
277
278 if let Some(reader_txn_tracker) = reader_txn_tracker.as_ref() {
279 reader_txn_tracker.wait_for_pre_commit_readers();
280 }
281
282 if storage_v2 {
283 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
284 for batch in batches {
285 self.rocksdb_provider.commit_batch(batch)?;
286 }
287 }
288
289 self.static_file_provider.commit()?;
290 Ok(())
291 }
292
293 pub fn latest<'a>(&'a self) -> Box<dyn StateProvider + 'a> {
295 trace!(target: "providers::db", "Returning latest state provider");
296 Box::new(LatestStateProviderRef::new(self))
297 }
298
299 pub fn history_by_block_hash<'a>(
301 &'a self,
302 block_hash: BlockHash,
303 ) -> ProviderResult<Box<dyn StateProvider + 'a>> {
304 let block_number =
305 self.block_number(block_hash)?.ok_or(ProviderError::BlockHashNotFound(block_hash))?;
306 self.history_by_block_number(block_number)
307 }
308
309 pub fn history_by_block_number<'a>(
311 &'a self,
312 mut block_number: BlockNumber,
313 ) -> ProviderResult<Box<dyn StateProvider + 'a>> {
314 if block_number == self.best_block_number().unwrap_or_default() &&
315 block_number == self.last_block_number().unwrap_or_default()
316 {
317 return Ok(Box::new(LatestStateProviderRef::new(self)))
318 }
319
320 block_number += 1;
322
323 let account_history_prune_checkpoint =
324 self.get_prune_checkpoint(PruneSegment::AccountHistory)?;
325 let storage_history_prune_checkpoint =
326 self.get_prune_checkpoint(PruneSegment::StorageHistory)?;
327
328 let mut state_provider =
329 HistoricalStateProviderRef::new(self, block_number, self.changeset_cache.clone());
330 if let Some(prune_checkpoint_block_number) =
333 account_history_prune_checkpoint.and_then(|checkpoint| checkpoint.block_number)
334 {
335 state_provider = state_provider.with_lowest_available_account_history_block_number(
336 prune_checkpoint_block_number + 1,
337 );
338 }
339 if let Some(prune_checkpoint_block_number) =
340 storage_history_prune_checkpoint.and_then(|checkpoint| checkpoint.block_number)
341 {
342 state_provider = state_provider.with_lowest_available_storage_history_block_number(
343 prune_checkpoint_block_number + 1,
344 );
345 }
346
347 Ok(Box::new(state_provider))
348 }
349
350 #[cfg(feature = "test-utils")]
351 pub fn set_prune_modes(&mut self, prune_modes: PruneModes) {
353 self.prune_modes = prune_modes;
354 }
355}
356
357impl<TX, N: NodeTypes> NodePrimitivesProvider for DatabaseProvider<TX, N> {
358 type Primitives = N::Primitives;
359}
360
361impl<TX, N: NodeTypes> StaticFileProviderFactory for DatabaseProvider<TX, N> {
362 fn static_file_provider(&self) -> StaticFileProvider<Self::Primitives> {
364 self.static_file_provider.clone()
365 }
366
367 fn get_static_file_writer(
368 &self,
369 block: BlockNumber,
370 segment: StaticFileSegment,
371 ) -> ProviderResult<crate::providers::StaticFileProviderRWRefMut<'_, Self::Primitives>> {
372 self.static_file_provider.get_writer(block, segment)
373 }
374}
375
376impl<TX, N: NodeTypes> RocksDBProviderFactory for DatabaseProvider<TX, N> {
377 fn rocksdb_provider(&self) -> RocksDBProvider {
379 self.rocksdb_provider.clone()
380 }
381
382 fn set_pending_rocksdb_batch(&self, batch: rocksdb::WriteBatchWithTransaction<true>) {
383 self.pending_rocksdb_batches.lock().push(batch);
384 }
385
386 fn commit_pending_rocksdb_batches(&self) -> ProviderResult<()> {
387 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
388 for batch in batches {
389 self.rocksdb_provider.commit_batch(batch)?;
390 }
391 Ok(())
392 }
393}
394
395impl<TX: Debug + Send, N: NodeTypes<ChainSpec: EthChainSpec + 'static>> ChainSpecProvider
396 for DatabaseProvider<TX, N>
397{
398 type ChainSpec = N::ChainSpec;
399
400 fn chain_spec(&self) -> Arc<Self::ChainSpec> {
401 self.chain_spec.clone()
402 }
403}
404
405impl<TX: DbTxMut, N: NodeTypes> DatabaseProvider<TX, N> {
406 #[expect(clippy::too_many_arguments)]
408 fn new_rw_inner(
409 tx: TX,
410 chain_spec: Arc<N::ChainSpec>,
411 static_file_provider: StaticFileProvider<N::Primitives>,
412 prune_modes: PruneModes,
413 storage: Arc<N::Storage>,
414 storage_settings: Arc<RwLock<StorageSettings>>,
415 rocksdb_provider: RocksDBProvider,
416 changeset_cache: ChangesetCache,
417 runtime: reth_tasks::Runtime,
418 db_path: PathBuf,
419 commit_order: CommitOrder,
420 metrics: Arc<DatabaseProviderMetrics>,
421 ) -> Self {
422 Self {
423 tx,
424 chain_spec,
425 static_file_provider,
426 prune_modes,
427 storage,
428 storage_settings,
429 rocksdb_provider,
430 changeset_cache,
431 runtime,
432 db_path,
433 pending_rocksdb_batches: Default::default(),
434 commit_order,
435 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
436 metrics,
437 reader_txn_tracker: None,
438 }
439 }
440
441 #[expect(clippy::too_many_arguments)]
443 pub fn new_rw(
444 tx: TX,
445 chain_spec: Arc<N::ChainSpec>,
446 static_file_provider: StaticFileProvider<N::Primitives>,
447 prune_modes: PruneModes,
448 storage: Arc<N::Storage>,
449 storage_settings: Arc<RwLock<StorageSettings>>,
450 rocksdb_provider: RocksDBProvider,
451 changeset_cache: ChangesetCache,
452 runtime: reth_tasks::Runtime,
453 db_path: PathBuf,
454 metrics: Arc<DatabaseProviderMetrics>,
455 ) -> Self {
456 Self::new_rw_inner(
457 tx,
458 chain_spec,
459 static_file_provider,
460 prune_modes,
461 storage,
462 storage_settings,
463 rocksdb_provider,
464 changeset_cache,
465 runtime,
466 db_path,
467 CommitOrder::Normal,
468 metrics,
469 )
470 }
471
472 #[expect(clippy::too_many_arguments)]
474 pub fn new_unwind_rw(
475 tx: TX,
476 chain_spec: Arc<N::ChainSpec>,
477 static_file_provider: StaticFileProvider<N::Primitives>,
478 prune_modes: PruneModes,
479 storage: Arc<N::Storage>,
480 storage_settings: Arc<RwLock<StorageSettings>>,
481 rocksdb_provider: RocksDBProvider,
482 changeset_cache: ChangesetCache,
483 runtime: reth_tasks::Runtime,
484 db_path: PathBuf,
485 metrics: Arc<DatabaseProviderMetrics>,
486 ) -> Self {
487 Self::new_rw_inner(
488 tx,
489 chain_spec,
490 static_file_provider,
491 prune_modes,
492 storage,
493 storage_settings,
494 rocksdb_provider,
495 changeset_cache,
496 runtime,
497 db_path,
498 CommitOrder::Unwind,
499 metrics,
500 )
501 }
502}
503
504impl<TX, N: NodeTypes> AsRef<Self> for DatabaseProvider<TX, N> {
505 fn as_ref(&self) -> &Self {
506 self
507 }
508}
509
510impl<TX: DbTx + DbTxMut + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
511 pub fn with_rocksdb_batch<F, R>(&self, f: F) -> ProviderResult<R>
515 where
516 F: FnOnce(RocksBatchArg<'_>) -> ProviderResult<(R, Option<RawRocksDBBatch>)>,
517 {
518 let rocksdb = self.rocksdb_provider();
519 let rocksdb_batch = rocksdb.batch();
520
521 let (result, raw_batch) = f(rocksdb_batch)?;
522
523 if let Some(batch) = raw_batch {
524 self.set_pending_rocksdb_batch(batch);
525 }
526 let _ = raw_batch; Ok(result)
529 }
530
531 fn static_file_write_ctx(
533 &self,
534 save_mode: SaveBlocksMode,
535 first_block: BlockNumber,
536 last_block: BlockNumber,
537 ) -> ProviderResult<StaticFileWriteCtx> {
538 let tip = self.last_block_number()?.max(last_block);
539 Ok(StaticFileWriteCtx {
540 write_senders: EitherWriterDestination::senders(self).is_static_file() &&
541 self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()),
542 write_receipts: save_mode.with_state() &&
543 EitherWriter::receipts_destination(self).is_static_file(),
544 write_account_changesets: save_mode.with_state() &&
545 EitherWriterDestination::account_changesets(self).is_static_file(),
546 write_storage_changesets: save_mode.with_state() &&
547 EitherWriterDestination::storage_changesets(self).is_static_file(),
548 tip,
549 receipts_prune_mode: self.prune_modes.receipts,
550 receipts_prunable: self
552 .static_file_provider
553 .get_highest_static_file_tx(StaticFileSegment::Receipts)
554 .is_none() &&
555 PruneMode::Distance(self.minimum_pruning_distance)
556 .should_prune(first_block, tip),
557 })
558 }
559
560 fn rocksdb_write_ctx(&self, first_block: BlockNumber) -> RocksDBWriteCtx {
562 RocksDBWriteCtx {
563 first_block_number: first_block,
564 prune_tx_lookup: self.prune_modes.transaction_lookup,
565 storage_settings: self.cached_storage_settings(),
566 pending_batches: self.pending_rocksdb_batches.clone(),
567 }
568 }
569
570 #[instrument(level = "debug", target = "providers::db", skip_all, fields(block_count = blocks.len()))]
579 pub fn save_blocks(
580 &self,
581 blocks: Vec<ExecutedBlock<N::Primitives>>,
582 save_mode: SaveBlocksMode,
583 ) -> ProviderResult<()> {
584 if blocks.is_empty() {
585 debug!(target: "providers::db", "Attempted to write empty block range");
586 return Ok(())
587 }
588
589 let total_start = Instant::now();
590 let block_count = blocks.len() as u64;
591 let first_number = blocks.first().unwrap().recovered_block().number();
592 let last_block_number = blocks.last().unwrap().recovered_block().number();
593
594 debug!(target: "providers::db", block_count, "Writing blocks and execution data to storage");
595
596 let first_tx_num = self
598 .tx
599 .cursor_read::<tables::TransactionBlocks>()?
600 .last()?
601 .map(|(n, _)| n + 1)
602 .unwrap_or_default();
603
604 let tx_nums: SmallVec<[TxNumber; 4]> = {
605 let mut nums = SmallVec::with_capacity(blocks.len());
606 let mut current = first_tx_num;
607 for block in &blocks {
608 nums.push(current);
609 current += block.recovered_block().body().transaction_count() as u64;
610 }
611 nums
612 };
613
614 let mut timings =
615 metrics::SaveBlocksTimings { batch_size: block_count, ..Default::default() };
616
617 let sf_provider = &self.static_file_provider;
619 let sf_ctx = self.static_file_write_ctx(save_mode, first_number, last_block_number)?;
620 let rocksdb_provider = self.rocksdb_provider.clone();
621 let rocksdb_ctx = self.rocksdb_write_ctx(first_number);
622 let rocksdb_enabled = rocksdb_ctx.storage_settings.storage_v2;
623
624 let mut sf_result = None;
625 let mut rocksdb_result = None;
626
627 let runtime = &self.runtime;
629 let span = tracing::Span::current();
632 runtime.storage_pool().in_place_scope(|s| {
633 s.spawn(|_| {
635 let _guard = span.enter();
636 let start = Instant::now();
637 sf_result = Some(
638 sf_provider
639 .write_blocks_data(&blocks, &tx_nums, sf_ctx, runtime)
640 .map(|()| start.elapsed()),
641 );
642 });
643
644 if rocksdb_enabled {
646 s.spawn(|_| {
647 let _guard = span.enter();
648 let start = Instant::now();
649 rocksdb_result = Some(
650 rocksdb_provider
651 .write_blocks_data(&blocks, &tx_nums, rocksdb_ctx, runtime)
652 .map(|()| start.elapsed()),
653 );
654 });
655 }
656
657 let mdbx_start = Instant::now();
659
660 if !self.cached_storage_settings().storage_v2 &&
662 self.prune_modes.transaction_lookup.is_none_or(|m| !m.is_full())
663 {
664 let start = Instant::now();
665 let total_tx_count: usize =
666 blocks.iter().map(|b| b.recovered_block().body().transaction_count()).sum();
667 let mut all_tx_hashes = Vec::with_capacity(total_tx_count);
668 for (i, block) in blocks.iter().enumerate() {
669 let recovered_block = block.recovered_block();
670 for (tx_num, transaction) in
671 (tx_nums[i]..).zip(recovered_block.body().transactions_iter())
672 {
673 all_tx_hashes.push((*transaction.tx_hash(), tx_num));
674 }
675 }
676
677 all_tx_hashes.sort_unstable_by_key(|(hash, _)| *hash);
679
680 self.with_rocksdb_batch(|batch| {
682 let mut tx_hash_writer =
683 EitherWriter::new_transaction_hash_numbers(self, batch)?;
684 tx_hash_writer.put_transaction_hash_numbers_batch(all_tx_hashes, false)?;
685 let raw_batch = tx_hash_writer.into_raw_rocksdb_batch();
686 Ok(((), raw_batch))
687 })?;
688 self.metrics.record_duration(
689 metrics::Action::InsertTransactionHashNumbers,
690 start.elapsed(),
691 );
692 }
693
694 for (i, block) in blocks.iter().enumerate() {
695 let recovered_block = block.recovered_block();
696
697 let start = Instant::now();
698 self.insert_block_mdbx_only(recovered_block, tx_nums[i])?;
699 timings.insert_block += start.elapsed();
700
701 if save_mode.with_state() {
702 let execution_output = block.execution_outcome();
703
704 let start = Instant::now();
708 self.write_state(
709 WriteStateInput::Single {
710 outcome: execution_output,
711 block: recovered_block.number(),
712 },
713 OriginalValuesKnown::No,
714 StateWriteConfig {
715 write_receipts: !sf_ctx.write_receipts,
716 write_account_changesets: !sf_ctx.write_account_changesets,
717 write_storage_changesets: !sf_ctx.write_storage_changesets,
718 },
719 )?;
720 timings.write_state += start.elapsed();
721 }
722 }
723
724 if save_mode.with_state() {
727 let start = Instant::now();
729 let merged_hashed_state = HashedPostStateSorted::merge_batch(
730 blocks.iter().rev().map(|b| b.trie_data().sorted.hashed_state),
731 );
732 if !merged_hashed_state.is_empty() {
733 self.write_hashed_state(&merged_hashed_state)?;
734 }
735 timings.write_hashed_state += start.elapsed();
736
737 let start = Instant::now();
738 let merged_trie =
739 TrieUpdatesSorted::merge_batch(blocks.iter().rev().map(|b| b.trie_updates()));
740 if !merged_trie.is_empty() {
741 self.write_trie_updates_sorted(&merged_trie)?;
742 }
743 timings.write_trie_updates += start.elapsed();
744 }
745
746 if save_mode.with_state() {
748 let start = Instant::now();
749 self.update_history_indices(first_number..=last_block_number)?;
750 timings.update_history_indices = start.elapsed();
751 }
752
753 let start = Instant::now();
755 self.update_pipeline_stages(last_block_number, false)?;
756 timings.update_pipeline_stages = start.elapsed();
757
758 timings.mdbx = mdbx_start.elapsed();
759
760 Ok::<_, ProviderError>(())
761 })?;
762
763 timings.sf = sf_result.ok_or(StaticFileWriterError::ThreadPanic("static file"))??;
765
766 if rocksdb_enabled {
767 timings.rocksdb = rocksdb_result.ok_or_else(|| {
768 ProviderError::Database(reth_db_api::DatabaseError::Other(
769 "RocksDB thread panicked".into(),
770 ))
771 })??;
772 }
773
774 timings.total = total_start.elapsed();
775
776 self.metrics.record_save_blocks(&timings);
777 debug!(target: "providers::db", range = ?first_number..=last_block_number, "Appended block data");
778
779 Ok(())
780 }
781
782 #[instrument(level = "debug", target = "providers::db", skip_all)]
786 fn insert_block_mdbx_only(
787 &self,
788 block: &RecoveredBlock<BlockTy<N>>,
789 first_tx_num: TxNumber,
790 ) -> ProviderResult<StoredBlockBodyIndices> {
791 if self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()) &&
792 EitherWriterDestination::senders(self).is_database()
793 {
794 let start = Instant::now();
795 let tx_nums_iter = std::iter::successors(Some(first_tx_num), |n| Some(n + 1));
796 let mut cursor = self.tx.cursor_write::<tables::TransactionSenders>()?;
797 for (tx_num, sender) in tx_nums_iter.zip(block.senders_iter().copied()) {
798 cursor.append(tx_num, &sender)?;
799 }
800 self.metrics
801 .record_duration(metrics::Action::InsertTransactionSenders, start.elapsed());
802 }
803
804 let block_number = block.number();
805 let tx_count = block.body().transaction_count() as u64;
806
807 let start = Instant::now();
808 self.tx.put::<tables::HeaderNumbers>(block.hash(), block_number)?;
809 self.metrics.record_duration(metrics::Action::InsertHeaderNumbers, start.elapsed());
810
811 self.write_block_body_indices(block_number, block.body(), first_tx_num, tx_count)?;
812
813 Ok(StoredBlockBodyIndices { first_tx_num, tx_count })
814 }
815
816 fn write_block_body_indices(
819 &self,
820 block_number: BlockNumber,
821 body: &BodyTy<N>,
822 first_tx_num: TxNumber,
823 tx_count: u64,
824 ) -> ProviderResult<()> {
825 let start = Instant::now();
827 self.tx
828 .cursor_write::<tables::BlockBodyIndices>()?
829 .append(block_number, &StoredBlockBodyIndices { first_tx_num, tx_count })?;
830 self.metrics.record_duration(metrics::Action::InsertBlockBodyIndices, start.elapsed());
831
832 if tx_count > 0 {
834 let start = Instant::now();
835 self.tx
836 .cursor_write::<tables::TransactionBlocks>()?
837 .append(first_tx_num + tx_count - 1, &block_number)?;
838 self.metrics.record_duration(metrics::Action::InsertTransactionBlocks, start.elapsed());
839 }
840
841 self.storage.writer().write_block_bodies(self, vec![(block_number, Some(body))])?;
843
844 Ok(())
845 }
846
847 pub fn unwind_trie_state_from(&self, from: BlockNumber) -> ProviderResult<()> {
852 let changed_accounts = self.account_changesets_range(from..)?;
853
854 self.unwind_account_hashing(changed_accounts.iter())?;
856
857 self.unwind_account_history_indices(changed_accounts.iter())?;
859
860 let changed_storages = self.storage_changesets_range(from..)?;
861
862 self.unwind_storage_hashing(changed_storages.iter().copied())?;
864
865 self.unwind_storage_history_indices(changed_storages.iter().copied())?;
867
868 let db_tip_block = self
871 .get_stage_checkpoint(reth_stages_types::StageId::Finish)?
872 .as_ref()
873 .map(|chk| chk.block_number)
874 .ok_or_else(|| ProviderError::InsufficientChangesets {
875 requested: from,
876 available: 0..=0,
877 })?;
878
879 let trie_revert = self.changeset_cache.get_or_compute_range(self, from..=db_tip_block)?;
880 self.write_trie_updates_sorted(&trie_revert)?;
881
882 Ok(())
883 }
884
885 fn remove_receipts_from(
887 &self,
888 from_tx: TxNumber,
889 last_block: BlockNumber,
890 ) -> ProviderResult<()> {
891 self.remove::<tables::Receipts<ReceiptTy<N>>>(from_tx..)?;
893
894 if EitherWriter::receipts_destination(self).is_static_file() {
895 let static_file_receipt_num =
896 self.static_file_provider.get_highest_static_file_tx(StaticFileSegment::Receipts);
897
898 let to_delete = static_file_receipt_num
899 .map(|static_num| (static_num + 1).saturating_sub(from_tx))
900 .unwrap_or_default();
901
902 self.static_file_provider
903 .latest_writer(StaticFileSegment::Receipts)?
904 .prune_receipts(to_delete, last_block)?;
905 }
906
907 Ok(())
908 }
909
910 fn write_bytecodes(
912 &self,
913 bytecodes: impl IntoIterator<Item = (B256, Bytecode)>,
914 ) -> ProviderResult<()> {
915 let mut bytecodes_cursor = self.tx_ref().cursor_write::<tables::Bytecodes>()?;
916 for (hash, bytecode) in bytecodes {
917 bytecodes_cursor.upsert(hash, &bytecode)?;
918 }
919 Ok(())
920 }
921}
922
923impl<TX: DbTx + 'static, N: NodeTypes> TryIntoHistoricalStateProvider for DatabaseProvider<TX, N> {
924 fn try_into_history_at_block(
925 self,
926 mut block_number: BlockNumber,
927 ) -> ProviderResult<StateProviderBox> {
928 let best_block = self.best_block_number().unwrap_or_default();
929
930 if block_number > best_block {
932 return Err(ProviderError::BlockNotExecuted {
933 requested: block_number,
934 executed: best_block,
935 });
936 }
937
938 if block_number == best_block {
940 return Ok(Box::new(LatestStateProvider::new(self)));
941 }
942
943 block_number += 1;
945
946 let account_history_prune_checkpoint =
947 self.get_prune_checkpoint(PruneSegment::AccountHistory)?;
948 let storage_history_prune_checkpoint =
949 self.get_prune_checkpoint(PruneSegment::StorageHistory)?;
950 let changeset_cache = self.changeset_cache.clone();
951
952 let mut state_provider = HistoricalStateProvider::new(self, block_number, changeset_cache);
953
954 if let Some(prune_checkpoint_block_number) =
957 account_history_prune_checkpoint.and_then(|checkpoint| checkpoint.block_number)
958 {
959 state_provider = state_provider.with_lowest_available_account_history_block_number(
960 prune_checkpoint_block_number + 1,
961 );
962 }
963 if let Some(prune_checkpoint_block_number) =
964 storage_history_prune_checkpoint.and_then(|checkpoint| checkpoint.block_number)
965 {
966 state_provider = state_provider.with_lowest_available_storage_history_block_number(
967 prune_checkpoint_block_number + 1,
968 );
969 }
970
971 Ok(Box::new(state_provider))
972 }
973}
974
975fn unwind_history_shards<S, T, C>(
990 cursor: &mut C,
991 start_key: T::Key,
992 block_number: BlockNumber,
993 mut shard_belongs_to_key: impl FnMut(&T::Key) -> bool,
994) -> ProviderResult<Vec<u64>>
995where
996 T: Table<Value = BlockNumberList>,
997 T::Key: AsRef<ShardedKey<S>>,
998 C: DbCursorRO<T> + DbCursorRW<T>,
999{
1000 let mut item = cursor.seek_exact(start_key)?;
1002 while let Some((sharded_key, list)) = item {
1003 if !shard_belongs_to_key(&sharded_key) {
1005 break
1006 }
1007
1008 cursor.delete_current()?;
1011
1012 let first = list.iter().next().expect("List can't be empty");
1015
1016 if first >= block_number {
1019 item = cursor.prev()?;
1020 continue
1021 }
1022 else if block_number <= sharded_key.as_ref().highest_block_number {
1025 return Ok(list.iter().take_while(|i| *i < block_number).collect::<Vec<_>>())
1028 }
1029 return Ok(list.iter().collect::<Vec<_>>())
1032 }
1033
1034 Ok(Vec::new())
1036}
1037
1038impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
1039 #[expect(clippy::too_many_arguments)]
1041 pub fn new(
1042 tx: TX,
1043 chain_spec: Arc<N::ChainSpec>,
1044 static_file_provider: StaticFileProvider<N::Primitives>,
1045 prune_modes: PruneModes,
1046 storage: Arc<N::Storage>,
1047 storage_settings: Arc<RwLock<StorageSettings>>,
1048 rocksdb_provider: RocksDBProvider,
1049 changeset_cache: ChangesetCache,
1050 runtime: reth_tasks::Runtime,
1051 db_path: PathBuf,
1052 metrics: Arc<DatabaseProviderMetrics>,
1053 ) -> Self {
1054 Self {
1055 tx,
1056 chain_spec,
1057 static_file_provider,
1058 prune_modes,
1059 storage,
1060 storage_settings,
1061 rocksdb_provider,
1062 changeset_cache,
1063 runtime,
1064 db_path,
1065 pending_rocksdb_batches: Default::default(),
1066 commit_order: CommitOrder::Normal,
1067 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
1068 metrics,
1069 reader_txn_tracker: None,
1070 }
1071 }
1072
1073 pub fn into_tx(self) -> TX {
1075 self.tx
1076 }
1077
1078 pub const fn tx_mut(&mut self) -> &mut TX {
1080 &mut self.tx
1081 }
1082
1083 pub const fn tx_ref(&self) -> &TX {
1085 &self.tx
1086 }
1087
1088 pub fn chain_spec(&self) -> &N::ChainSpec {
1090 &self.chain_spec
1091 }
1092}
1093
1094impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
1095 fn recovered_block<H, HF, B, BF>(
1096 &self,
1097 id: BlockHashOrNumber,
1098 _transaction_kind: TransactionVariant,
1099 header_by_number: HF,
1100 construct_block: BF,
1101 ) -> ProviderResult<Option<B>>
1102 where
1103 H: AsRef<HeaderTy<N>>,
1104 HF: FnOnce(BlockNumber) -> ProviderResult<Option<H>>,
1105 BF: FnOnce(H, BodyTy<N>, Vec<Address>) -> ProviderResult<Option<B>>,
1106 {
1107 let Some(block_number) = self.convert_hash_or_number(id)? else { return Ok(None) };
1108 let earliest_available = self.static_file_provider.earliest_history_height();
1109 if block_number < earliest_available {
1110 return Err(ProviderError::BlockExpired { requested: block_number, earliest_available })
1111 }
1112 let Some(header) = header_by_number(block_number)? else { return Ok(None) };
1113
1114 let Some(body) = self.block_body_indices(block_number)? else { return Ok(None) };
1121
1122 let tx_range = body.tx_num_range();
1123
1124 let transactions = if tx_range.is_empty() {
1125 vec![]
1126 } else {
1127 self.transactions_by_tx_range(tx_range.clone())?
1128 };
1129
1130 let body = self
1131 .storage
1132 .reader()
1133 .read_block_bodies(self, vec![(header.as_ref(), transactions)])?
1134 .pop()
1135 .ok_or(ProviderError::InvalidStorageOutput)?;
1136
1137 let senders = if tx_range.is_empty() {
1138 vec![]
1139 } else {
1140 let known_senders: HashMap<TxNumber, Address> =
1141 EitherReader::new_senders(self)?.senders_by_tx_range(tx_range.clone())?;
1142
1143 let mut senders = Vec::with_capacity(body.transactions().len());
1144 for (tx_num, tx) in tx_range.zip(body.transactions()) {
1145 match known_senders.get(&tx_num) {
1146 None => {
1147 let sender = tx.recover_signer_unchecked()?;
1148 senders.push(sender);
1149 }
1150 Some(sender) => senders.push(*sender),
1151 }
1152 }
1153 senders
1154 };
1155
1156 construct_block(header, body, senders)
1157 }
1158
1159 fn block_range<F, H, HF, R>(
1169 &self,
1170 range: RangeInclusive<BlockNumber>,
1171 headers_range: HF,
1172 mut assemble_block: F,
1173 ) -> ProviderResult<Vec<R>>
1174 where
1175 H: AsRef<HeaderTy<N>>,
1176 HF: FnOnce(RangeInclusive<BlockNumber>) -> ProviderResult<Vec<H>>,
1177 F: FnMut(H, BodyTy<N>, Range<TxNumber>) -> ProviderResult<R>,
1178 {
1179 if range.is_empty() {
1180 return Ok(Vec::new())
1181 }
1182
1183 let len = range.end().saturating_sub(*range.start()) as usize + 1;
1184 let mut blocks = Vec::with_capacity(len);
1185
1186 let headers = headers_range(range.clone())?;
1187
1188 let present_headers = self
1194 .block_body_indices_range(range)?
1195 .into_iter()
1196 .map(|b| b.tx_num_range())
1197 .zip(headers)
1198 .collect::<Vec<_>>();
1199
1200 let mut inputs = Vec::with_capacity(present_headers.len());
1201 for (tx_range, header) in &present_headers {
1202 let transactions = if tx_range.is_empty() {
1203 Vec::new()
1204 } else {
1205 self.transactions_by_tx_range(tx_range.clone())?
1206 };
1207
1208 inputs.push((header.as_ref(), transactions));
1209 }
1210
1211 let bodies = self.storage.reader().read_block_bodies(self, inputs)?;
1212
1213 for ((tx_range, header), body) in present_headers.into_iter().zip(bodies) {
1214 blocks.push(assemble_block(header, body, tx_range)?);
1215 }
1216
1217 Ok(blocks)
1218 }
1219
1220 fn block_with_senders_range<H, HF, B, BF>(
1231 &self,
1232 range: RangeInclusive<BlockNumber>,
1233 headers_range: HF,
1234 assemble_block: BF,
1235 ) -> ProviderResult<Vec<B>>
1236 where
1237 H: AsRef<HeaderTy<N>>,
1238 HF: Fn(RangeInclusive<BlockNumber>) -> ProviderResult<Vec<H>>,
1239 BF: Fn(H, BodyTy<N>, Vec<Address>) -> ProviderResult<B>,
1240 {
1241 self.block_range(range, headers_range, |header, body, tx_range| {
1242 let senders = if tx_range.is_empty() {
1243 Vec::new()
1244 } else {
1245 let known_senders: HashMap<TxNumber, Address> =
1246 EitherReader::new_senders(self)?.senders_by_tx_range(tx_range.clone())?;
1247
1248 let mut senders = Vec::with_capacity(body.transactions().len());
1249 for (tx_num, tx) in tx_range.zip(body.transactions()) {
1250 match known_senders.get(&tx_num) {
1251 None => {
1252 let sender = tx.recover_signer_unchecked()?;
1254 senders.push(sender);
1255 }
1256 Some(sender) => senders.push(*sender),
1257 }
1258 }
1259
1260 senders
1261 };
1262
1263 assemble_block(header, body, senders)
1264 })
1265 }
1266
1267 fn populate_bundle_state(
1271 &self,
1272 account_changeset: Vec<(u64, AccountBeforeTx)>,
1273 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1274 mut get_account: impl FnMut(Address) -> ProviderResult<Option<Account>>,
1275 mut get_storage: impl FnMut(Address, StorageKey) -> ProviderResult<Option<StorageValue>>,
1276 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1277 let mut state: BundleStateInit = HashMap::default();
1281
1282 let mut reverts: RevertsInit = HashMap::default();
1288
1289 for (block_number, account_before) in account_changeset.into_iter().rev() {
1291 let AccountBeforeTx { info: old_info, address } = account_before;
1292 match state.entry(address) {
1293 hash_map::Entry::Vacant(entry) => {
1294 let new_info = get_account(address)?;
1295 entry.insert((old_info, new_info, HashMap::default()));
1296 }
1297 hash_map::Entry::Occupied(mut entry) => {
1298 entry.get_mut().0 = old_info;
1300 }
1301 }
1302 reverts.entry(block_number).or_default().entry(address).or_default().0 = Some(old_info);
1304 }
1305
1306 for (block_and_address, old_storage) in storage_changeset.into_iter().rev() {
1308 let BlockNumberAddress((block_number, address)) = block_and_address;
1309 let account_state = match state.entry(address) {
1311 hash_map::Entry::Vacant(entry) => {
1312 let present_info = get_account(address)?;
1313 entry.insert((present_info, present_info, HashMap::default()))
1314 }
1315 hash_map::Entry::Occupied(entry) => entry.into_mut(),
1316 };
1317
1318 match account_state.2.entry(old_storage.key) {
1320 hash_map::Entry::Vacant(entry) => {
1321 let new_storage = get_storage(address, old_storage.key)?.unwrap_or_default();
1322 entry.insert((old_storage.value, new_storage));
1323 }
1324 hash_map::Entry::Occupied(mut entry) => {
1325 entry.get_mut().0 = old_storage.value;
1326 }
1327 };
1328
1329 reverts
1330 .entry(block_number)
1331 .or_default()
1332 .entry(address)
1333 .or_default()
1334 .1
1335 .push(old_storage);
1336 }
1337
1338 Ok((state, reverts))
1339 }
1340
1341 fn populate_bundle_state_plain(
1344 &self,
1345 account_changeset: Vec<(u64, AccountBeforeTx)>,
1346 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1347 plain_accounts_cursor: &mut impl DbCursorRO<tables::PlainAccountState>,
1348 plain_storage_cursor: &mut impl DbDupCursorRO<tables::PlainStorageState>,
1349 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1350 self.populate_bundle_state(
1351 account_changeset,
1352 storage_changeset,
1353 |address| Ok(plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1)),
1354 |address, storage_key| {
1355 Ok(plain_storage_cursor
1356 .seek_by_key_subkey(address, storage_key)?
1357 .filter(|s| s.key == storage_key)
1358 .map(|s| s.value))
1359 },
1360 )
1361 }
1362
1363 fn populate_bundle_state_hashed(
1368 &self,
1369 account_changeset: Vec<(u64, AccountBeforeTx)>,
1370 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1371 hashed_accounts_cursor: &mut impl DbCursorRO<tables::HashedAccounts>,
1372 hashed_storage_cursor: &mut impl DbDupCursorRO<tables::HashedStorages>,
1373 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1374 self.populate_bundle_state(
1375 account_changeset,
1376 storage_changeset,
1377 |address| Ok(hashed_accounts_cursor.seek_exact(keccak256(address))?.map(|kv| kv.1)),
1378 |address, storage_key| {
1379 let hashed_storage_key = keccak256(storage_key);
1380 Ok(hashed_storage_cursor
1381 .seek_by_key_subkey(keccak256(address), hashed_storage_key)?
1382 .filter(|s| s.key == hashed_storage_key)
1383 .map(|s| s.value))
1384 },
1385 )
1386 }
1387
1388 fn populate_bundle_state_with_provider(
1389 &self,
1390 account_changeset: Vec<(u64, AccountBeforeTx)>,
1391 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1392 state_provider: impl StateProvider,
1393 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1394 self.populate_bundle_state(
1395 account_changeset,
1396 storage_changeset,
1397 |address| state_provider.basic_account(&address),
1398 |address, storage_key| state_provider.storage(address, storage_key),
1399 )
1400 }
1401}
1402
1403impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
1404 fn append_history_index<P, T>(
1412 &self,
1413 index_updates: impl IntoIterator<Item = (P, impl IntoIterator<Item = u64>)>,
1414 mut sharded_key_factory: impl FnMut(P, BlockNumber) -> T::Key,
1415 ) -> ProviderResult<()>
1416 where
1417 P: Copy,
1418 T: Table<Value = BlockNumberList>,
1419 {
1420 assert!(!T::DUPSORT, "append_history_index cannot be used with DUPSORT tables");
1423
1424 let mut cursor = self.tx.cursor_write::<T>()?;
1425
1426 for (partial_key, indices) in index_updates {
1427 let last_key = sharded_key_factory(partial_key, u64::MAX);
1428 let mut last_shard = cursor
1429 .seek_exact(last_key.clone())?
1430 .map(|(_, list)| list)
1431 .unwrap_or_else(BlockNumberList::empty);
1432
1433 last_shard.append(indices).map_err(ProviderError::other)?;
1434
1435 if last_shard.len() <= sharded_key::NUM_OF_INDICES_IN_SHARD as u64 {
1437 cursor.upsert(last_key, &last_shard)?;
1438 continue;
1439 }
1440
1441 let chunks = last_shard.iter().chunks(sharded_key::NUM_OF_INDICES_IN_SHARD);
1443 let mut chunks_peekable = chunks.into_iter().peekable();
1444
1445 while let Some(chunk) = chunks_peekable.next() {
1446 let shard = BlockNumberList::new_pre_sorted(chunk);
1447 let highest_block_number = if chunks_peekable.peek().is_some() {
1448 shard.iter().next_back().expect("`chunks` does not return empty list")
1449 } else {
1450 u64::MAX
1452 };
1453
1454 cursor.upsert(sharded_key_factory(partial_key, highest_block_number), &shard)?;
1455 }
1456 }
1457
1458 Ok(())
1459 }
1460}
1461
1462impl<TX: DbTx, N: NodeTypes> AccountReader for DatabaseProvider<TX, N> {
1463 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
1464 if self.cached_storage_settings().use_hashed_state() {
1465 let hashed_address = keccak256(address);
1466 Ok(self.tx.get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)?)
1467 } else {
1468 Ok(self.tx.get_by_encoded_key::<tables::PlainAccountState>(address)?)
1469 }
1470 }
1471}
1472
1473impl<TX: DbTx + 'static, N: NodeTypes> AccountExtReader for DatabaseProvider<TX, N> {
1474 fn changed_accounts_with_range(
1475 &self,
1476 range: RangeInclusive<BlockNumber>,
1477 ) -> ProviderResult<BTreeSet<Address>> {
1478 let mut reader = EitherReader::new_account_changesets(self)?;
1479
1480 reader.changed_accounts_with_range(range)
1481 }
1482
1483 fn basic_accounts(
1484 &self,
1485 iter: impl IntoIterator<Item = Address>,
1486 ) -> ProviderResult<Vec<(Address, Option<Account>)>> {
1487 if self.cached_storage_settings().use_hashed_state() {
1488 let mut hashed_accounts = self.tx.cursor_read::<tables::HashedAccounts>()?;
1489 Ok(iter
1490 .into_iter()
1491 .map(|address| {
1492 let hashed_address = keccak256(address);
1493 hashed_accounts.seek_exact(hashed_address).map(|a| (address, a.map(|(_, v)| v)))
1494 })
1495 .collect::<Result<Vec<_>, _>>()?)
1496 } else {
1497 let mut plain_accounts = self.tx.cursor_read::<tables::PlainAccountState>()?;
1498 Ok(iter
1499 .into_iter()
1500 .map(|address| {
1501 plain_accounts.seek_exact(address).map(|a| (address, a.map(|(_, v)| v)))
1502 })
1503 .collect::<Result<Vec<_>, _>>()?)
1504 }
1505 }
1506
1507 fn changed_accounts_and_blocks_with_range(
1508 &self,
1509 range: RangeInclusive<BlockNumber>,
1510 ) -> ProviderResult<BTreeMap<Address, Vec<u64>>> {
1511 let highest_static_block = self
1512 .static_file_provider
1513 .get_highest_static_file_block(StaticFileSegment::AccountChangeSets);
1514
1515 if let Some(highest) = highest_static_block &&
1516 self.cached_storage_settings().storage_v2
1517 {
1518 let start = *range.start();
1519 let static_end = (*range.end()).min(highest);
1520
1521 let mut changed_accounts_and_blocks: BTreeMap<_, Vec<u64>> = BTreeMap::default();
1522 if start <= static_end {
1523 for block in start..=static_end {
1524 let block_changesets = self.account_block_changeset(block)?;
1525 for changeset in block_changesets {
1526 changed_accounts_and_blocks
1527 .entry(changeset.address)
1528 .or_default()
1529 .push(block);
1530 }
1531 }
1532 }
1533
1534 Ok(changed_accounts_and_blocks)
1535 } else {
1536 let mut changeset_cursor = self.tx.cursor_read::<tables::AccountChangeSets>()?;
1537
1538 let account_transitions = changeset_cursor.walk_range(range)?.try_fold(
1539 BTreeMap::new(),
1540 |mut accounts: BTreeMap<Address, Vec<u64>>, entry| -> ProviderResult<_> {
1541 let (index, account) = entry?;
1542 accounts.entry(account.address).or_default().push(index);
1543 Ok(accounts)
1544 },
1545 )?;
1546
1547 Ok(account_transitions)
1548 }
1549 }
1550}
1551
1552impl<TX: DbTx, N: NodeTypes> StorageChangeSetReader for DatabaseProvider<TX, N> {
1553 fn storage_changeset(
1554 &self,
1555 block_number: BlockNumber,
1556 ) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
1557 if self.cached_storage_settings().storage_v2 {
1558 self.static_file_provider.storage_changeset(block_number)
1559 } else {
1560 let range = block_number..=block_number;
1561 let storage_range = BlockNumberAddress::range(range);
1562 self.tx
1563 .cursor_dup_read::<tables::StorageChangeSets>()?
1564 .walk_range(storage_range)?
1565 .map(|r| {
1566 let (bna, entry) = r?;
1567 Ok((bna, entry))
1568 })
1569 .collect()
1570 }
1571 }
1572
1573 fn get_storage_before_block(
1574 &self,
1575 block_number: BlockNumber,
1576 address: Address,
1577 storage_key: B256,
1578 ) -> ProviderResult<Option<StorageEntry>> {
1579 if self.cached_storage_settings().storage_v2 {
1580 self.static_file_provider.get_storage_before_block(block_number, address, storage_key)
1581 } else {
1582 Ok(self
1583 .tx
1584 .cursor_dup_read::<tables::StorageChangeSets>()?
1585 .seek_by_key_subkey(BlockNumberAddress((block_number, address)), storage_key)?
1586 .filter(|entry| entry.key == storage_key))
1587 }
1588 }
1589
1590 fn storage_changesets_range(
1591 &self,
1592 range: impl RangeBounds<BlockNumber>,
1593 ) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
1594 if self.cached_storage_settings().storage_v2 {
1595 self.static_file_provider.storage_changesets_range(range)
1596 } else {
1597 self.tx
1598 .cursor_dup_read::<tables::StorageChangeSets>()?
1599 .walk_range(BlockNumberAddressRange::from(range))?
1600 .map(|r| {
1601 let (bna, entry) = r?;
1602 Ok((bna, entry))
1603 })
1604 .collect()
1605 }
1606 }
1607}
1608
1609impl<TX: DbTx, N: NodeTypes> ChangeSetReader for DatabaseProvider<TX, N> {
1610 fn account_block_changeset(
1611 &self,
1612 block_number: BlockNumber,
1613 ) -> ProviderResult<Vec<AccountBeforeTx>> {
1614 if self.cached_storage_settings().storage_v2 {
1615 let static_changesets =
1616 self.static_file_provider.account_block_changeset(block_number)?;
1617 Ok(static_changesets)
1618 } else {
1619 let range = block_number..=block_number;
1620 self.tx
1621 .cursor_read::<tables::AccountChangeSets>()?
1622 .walk_range(range)?
1623 .map(|result| -> ProviderResult<_> {
1624 let (_, account_before) = result?;
1625 Ok(account_before)
1626 })
1627 .collect()
1628 }
1629 }
1630
1631 fn get_account_before_block(
1632 &self,
1633 block_number: BlockNumber,
1634 address: Address,
1635 ) -> ProviderResult<Option<AccountBeforeTx>> {
1636 if self.cached_storage_settings().storage_v2 {
1637 Ok(self.static_file_provider.get_account_before_block(block_number, address)?)
1638 } else {
1639 self.tx
1640 .cursor_dup_read::<tables::AccountChangeSets>()?
1641 .seek_by_key_subkey(block_number, address)?
1642 .filter(|acc| acc.address == address)
1643 .map(Ok)
1644 .transpose()
1645 }
1646 }
1647
1648 fn account_changesets_range(
1649 &self,
1650 range: impl core::ops::RangeBounds<BlockNumber>,
1651 ) -> ProviderResult<Vec<(BlockNumber, AccountBeforeTx)>> {
1652 if self.cached_storage_settings().storage_v2 {
1653 self.static_file_provider.account_changesets_range(range)
1654 } else {
1655 self.tx
1656 .cursor_read::<tables::AccountChangeSets>()?
1657 .walk_range(to_range(range))?
1658 .map(|r| r.map_err(Into::into))
1659 .collect()
1660 }
1661 }
1662}
1663
1664impl<Tx: DbTx + 'static, N: NodeTypesForProvider> StateReader for DatabaseProvider<Tx, N> {
1665 type Receipt = ReceiptTy<N>;
1666
1667 fn get_state(
1668 &self,
1669 block: BlockNumber,
1670 ) -> ProviderResult<Option<ExecutionOutcome<Self::Receipt>>> {
1671 let Some(block_body) = self.block_body_indices(block)? else { return Ok(None) };
1672
1673 let from_transaction_num = block_body.first_tx_num();
1674 let to_transaction_num = block_body.last_tx_num();
1675
1676 let account_changeset = self.account_changesets_range(block..=block)?;
1677 let storage_changeset = self.storage_changeset(block)?;
1678
1679 let Some(block_hash) = self.block_hash(block)? else { return Ok(None) };
1680 let state_provider = self.history_by_block_hash(block_hash)?;
1681 let (state, reverts) = self.populate_bundle_state_with_provider(
1682 account_changeset,
1683 storage_changeset,
1684 state_provider,
1685 )?;
1686
1687 let receipts = self.receipts_by_tx_range(from_transaction_num..=to_transaction_num)?;
1688
1689 Ok(Some(ExecutionOutcome::new_init(
1690 state,
1691 reverts,
1692 Vec::new(),
1694 vec![receipts],
1695 block,
1696 Vec::new(),
1697 )))
1698 }
1699}
1700
1701impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderSyncGapProvider
1702 for DatabaseProvider<TX, N>
1703{
1704 type Header = HeaderTy<N>;
1705
1706 fn local_tip_header(
1707 &self,
1708 highest_uninterrupted_block: BlockNumber,
1709 ) -> ProviderResult<SealedHeader<Self::Header>> {
1710 let static_file_provider = self.static_file_provider();
1711
1712 let next_static_file_block_num = static_file_provider
1715 .get_highest_static_file_block(StaticFileSegment::Headers)
1716 .map(|id| id + 1)
1717 .unwrap_or_default();
1718 let next_block = highest_uninterrupted_block + 1;
1719
1720 match next_static_file_block_num.cmp(&next_block) {
1721 Ordering::Greater => {
1724 let mut static_file_producer =
1725 static_file_provider.latest_writer(StaticFileSegment::Headers)?;
1726 static_file_producer.prune_headers(next_static_file_block_num - next_block)?;
1727 static_file_producer.commit()?
1730 }
1731 Ordering::Less => {
1732 return Err(ProviderError::HeaderNotFound(next_static_file_block_num.into()))
1734 }
1735 Ordering::Equal => {}
1736 }
1737
1738 let local_head = static_file_provider
1739 .sealed_header(highest_uninterrupted_block)?
1740 .ok_or_else(|| ProviderError::HeaderNotFound(highest_uninterrupted_block.into()))?;
1741
1742 Ok(local_head)
1743 }
1744}
1745
1746impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderProvider for DatabaseProvider<TX, N> {
1747 type Header = HeaderTy<N>;
1748
1749 fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
1750 if let Some(num) = self.block_number(block_hash)? {
1751 Ok(self.header_by_number(num)?)
1752 } else {
1753 Ok(None)
1754 }
1755 }
1756
1757 fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
1758 self.static_file_provider.header_by_number(num)
1759 }
1760
1761 fn headers_range(
1762 &self,
1763 range: impl RangeBounds<BlockNumber>,
1764 ) -> ProviderResult<Vec<Self::Header>> {
1765 self.static_file_provider.headers_range(range)
1766 }
1767
1768 fn sealed_header(
1769 &self,
1770 number: BlockNumber,
1771 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
1772 self.static_file_provider.sealed_header(number)
1773 }
1774
1775 fn sealed_headers_while(
1776 &self,
1777 range: impl RangeBounds<BlockNumber>,
1778 predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
1779 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
1780 self.static_file_provider.sealed_headers_while(range, predicate)
1781 }
1782}
1783
1784impl<TX: DbTx + 'static, N: NodeTypes> BlockHashReader for DatabaseProvider<TX, N> {
1785 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
1786 self.static_file_provider.block_hash(number)
1787 }
1788
1789 fn canonical_hashes_range(
1790 &self,
1791 start: BlockNumber,
1792 end: BlockNumber,
1793 ) -> ProviderResult<Vec<B256>> {
1794 self.static_file_provider.canonical_hashes_range(start, end)
1795 }
1796}
1797
1798impl<TX: DbTx + 'static, N: NodeTypes> BlockNumReader for DatabaseProvider<TX, N> {
1799 fn chain_info(&self) -> ProviderResult<ChainInfo> {
1800 let best_number = self.best_block_number()?;
1801 let best_hash = self.block_hash(best_number)?.unwrap_or_default();
1802 Ok(ChainInfo { best_hash, best_number })
1803 }
1804
1805 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
1806 Ok(self
1809 .get_stage_checkpoint(StageId::Finish)?
1810 .map(|checkpoint| checkpoint.block_number)
1811 .unwrap_or_default())
1812 }
1813
1814 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
1815 self.static_file_provider.last_block_number()
1816 }
1817
1818 fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
1819 Ok(self.tx.get::<tables::HeaderNumbers>(hash)?)
1820 }
1821}
1822
1823impl<TX: DbTx + 'static, N: NodeTypesForProvider> BlockReader for DatabaseProvider<TX, N> {
1824 type Block = BlockTy<N>;
1825
1826 fn find_block_by_hash(
1827 &self,
1828 hash: B256,
1829 source: BlockSource,
1830 ) -> ProviderResult<Option<Self::Block>> {
1831 if source.is_canonical() {
1832 self.block(hash.into())
1833 } else {
1834 Ok(None)
1835 }
1836 }
1837
1838 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
1846 if let Some(number) = self.convert_hash_or_number(id)? {
1847 let earliest_available = self.static_file_provider.earliest_history_height();
1848 if number < earliest_available {
1849 return Err(ProviderError::BlockExpired { requested: number, earliest_available })
1850 }
1851
1852 let Some(header) = self.header_by_number(number)? else { return Ok(None) };
1853
1854 let Some(transactions) = self.transactions_by_block(number.into())? else {
1859 return Ok(None)
1860 };
1861
1862 let body = self
1863 .storage
1864 .reader()
1865 .read_block_bodies(self, vec![(&header, transactions)])?
1866 .pop()
1867 .ok_or(ProviderError::InvalidStorageOutput)?;
1868
1869 return Ok(Some(Self::Block::new(header, body)))
1870 }
1871
1872 Ok(None)
1873 }
1874
1875 fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1876 Ok(None)
1877 }
1878
1879 fn pending_block_and_receipts(
1880 &self,
1881 ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<Self::Receipt>)>> {
1882 Ok(None)
1883 }
1884
1885 fn recovered_block(
1894 &self,
1895 id: BlockHashOrNumber,
1896 transaction_kind: TransactionVariant,
1897 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1898 self.recovered_block(
1899 id,
1900 transaction_kind,
1901 |block_number| self.header_by_number(block_number),
1902 |header, body, senders| {
1903 Self::Block::new(header, body)
1904 .try_into_recovered_unchecked(senders)
1908 .map(Some)
1909 .map_err(|_| ProviderError::SenderRecoveryError)
1910 },
1911 )
1912 }
1913
1914 fn sealed_block_with_senders(
1915 &self,
1916 id: BlockHashOrNumber,
1917 transaction_kind: TransactionVariant,
1918 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1919 self.recovered_block(
1920 id,
1921 transaction_kind,
1922 |block_number| self.sealed_header(block_number),
1923 |header, body, senders| {
1924 Self::Block::new_sealed(header, body)
1925 .try_with_senders_unchecked(senders)
1929 .map(Some)
1930 .map_err(|_| ProviderError::SenderRecoveryError)
1931 },
1932 )
1933 }
1934
1935 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
1936 self.block_range(
1937 range,
1938 |range| self.headers_range(range),
1939 |header, body, _| Ok(Self::Block::new(header, body)),
1940 )
1941 }
1942
1943 fn block_with_senders_range(
1944 &self,
1945 range: RangeInclusive<BlockNumber>,
1946 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1947 self.block_with_senders_range(
1948 range,
1949 |range| self.headers_range(range),
1950 |header, body, senders| {
1951 Self::Block::new(header, body)
1952 .try_into_recovered_unchecked(senders)
1953 .map_err(|_| ProviderError::SenderRecoveryError)
1954 },
1955 )
1956 }
1957
1958 fn recovered_block_range(
1959 &self,
1960 range: RangeInclusive<BlockNumber>,
1961 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1962 self.block_with_senders_range(
1963 range,
1964 |range| self.sealed_headers_range(range),
1965 |header, body, senders| {
1966 Self::Block::new_sealed(header, body)
1967 .try_with_senders(senders)
1968 .map_err(|_| ProviderError::SenderRecoveryError)
1969 },
1970 )
1971 }
1972
1973 fn block_by_transaction_id(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
1974 Ok(self
1975 .tx
1976 .cursor_read::<tables::TransactionBlocks>()?
1977 .seek(id)
1978 .map(|b| b.map(|(_, bn)| bn))?)
1979 }
1980}
1981
1982impl<TX: DbTx + 'static, N: NodeTypesForProvider> TransactionsProviderExt
1983 for DatabaseProvider<TX, N>
1984{
1985 fn transaction_hashes_by_range(
1988 &self,
1989 tx_range: Range<TxNumber>,
1990 ) -> ProviderResult<Vec<(TxHash, TxNumber)>> {
1991 self.static_file_provider.transaction_hashes_by_range(tx_range)
1992 }
1993}
1994
1995impl<TX: DbTx + 'static, N: NodeTypesForProvider> TransactionsProvider for DatabaseProvider<TX, N> {
1997 type Transaction = TxTy<N>;
1998
1999 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
2000 self.with_rocksdb_snapshot(|rocksdb_ref| {
2001 let mut reader = EitherReader::new_transaction_hash_numbers(self, rocksdb_ref)?;
2002 reader.get_transaction_hash_number(tx_hash)
2003 })
2004 }
2005
2006 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
2007 self.static_file_provider.transaction_by_id(id)
2008 }
2009
2010 fn transaction_by_id_unhashed(
2011 &self,
2012 id: TxNumber,
2013 ) -> ProviderResult<Option<Self::Transaction>> {
2014 self.static_file_provider.transaction_by_id_unhashed(id)
2015 }
2016
2017 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
2018 if let Some(id) = self.transaction_id(hash)? {
2019 Ok(self.transaction_by_id_unhashed(id)?)
2020 } else {
2021 Ok(None)
2022 }
2023 }
2024
2025 fn transaction_by_hash_with_meta(
2026 &self,
2027 tx_hash: TxHash,
2028 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
2029 if let Some(transaction_id) = self.transaction_id(tx_hash)? &&
2030 let Some(transaction) = self.transaction_by_id_unhashed(transaction_id)? &&
2031 let Some(block_number) = self.block_by_transaction_id(transaction_id)? &&
2032 let Some(sealed_header) = self.sealed_header(block_number)?
2033 {
2034 let (header, block_hash) = sealed_header.split();
2035 if let Some(block_body) = self.block_body_indices(block_number)? {
2036 let index = transaction_id - block_body.first_tx_num();
2041
2042 let meta = TransactionMeta {
2043 tx_hash,
2044 index,
2045 block_hash,
2046 block_number,
2047 base_fee: header.base_fee_per_gas(),
2048 excess_blob_gas: header.excess_blob_gas(),
2049 timestamp: header.timestamp(),
2050 };
2051
2052 return Ok(Some((transaction, meta)))
2053 }
2054 }
2055
2056 Ok(None)
2057 }
2058
2059 fn transactions_by_block(
2060 &self,
2061 id: BlockHashOrNumber,
2062 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
2063 if let Some(block_number) = self.convert_hash_or_number(id)? &&
2064 let Some(body) = self.block_body_indices(block_number)?
2065 {
2066 let tx_range = body.tx_num_range();
2067 return if tx_range.is_empty() {
2068 Ok(Some(Vec::new()))
2069 } else {
2070 self.transactions_by_tx_range(tx_range).map(Some)
2071 }
2072 }
2073 Ok(None)
2074 }
2075
2076 fn transactions_by_block_range(
2077 &self,
2078 range: impl RangeBounds<BlockNumber>,
2079 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
2080 let range = to_range(range);
2081
2082 self.block_body_indices_range(range.start..=range.end.saturating_sub(1))?
2083 .into_iter()
2084 .map(|body| {
2085 let tx_num_range = body.tx_num_range();
2086 if tx_num_range.is_empty() {
2087 Ok(Vec::new())
2088 } else {
2089 self.transactions_by_tx_range(tx_num_range)
2090 }
2091 })
2092 .collect()
2093 }
2094
2095 fn transactions_by_tx_range(
2096 &self,
2097 range: impl RangeBounds<TxNumber>,
2098 ) -> ProviderResult<Vec<Self::Transaction>> {
2099 self.static_file_provider.transactions_by_tx_range(range)
2100 }
2101
2102 fn senders_by_tx_range(
2103 &self,
2104 range: impl RangeBounds<TxNumber>,
2105 ) -> ProviderResult<Vec<Address>> {
2106 if EitherWriterDestination::senders(self).is_static_file() {
2107 self.static_file_provider.senders_by_tx_range(range)
2108 } else {
2109 self.cursor_read_collect::<tables::TransactionSenders>(range)
2110 }
2111 }
2112
2113 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
2114 if EitherWriterDestination::senders(self).is_static_file() {
2115 self.static_file_provider.transaction_sender(id)
2116 } else {
2117 Ok(self.tx.get::<tables::TransactionSenders>(id)?)
2118 }
2119 }
2120}
2121
2122impl<TX: DbTx + 'static, N: NodeTypesForProvider> ReceiptProvider for DatabaseProvider<TX, N> {
2123 type Receipt = ReceiptTy<N>;
2124
2125 fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
2126 self.static_file_provider.get_with_static_file_or_database(
2127 StaticFileSegment::Receipts,
2128 id,
2129 |static_file| static_file.receipt(id),
2130 || Ok(self.tx.get::<tables::Receipts<Self::Receipt>>(id)?),
2131 )
2132 }
2133
2134 fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
2135 if let Some(id) = self.transaction_id(hash)? {
2136 self.receipt(id)
2137 } else {
2138 Ok(None)
2139 }
2140 }
2141
2142 fn receipts_by_block(
2143 &self,
2144 block: BlockHashOrNumber,
2145 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
2146 if let Some(number) = self.convert_hash_or_number(block)? &&
2147 let Some(body) = self.block_body_indices(number)?
2148 {
2149 let tx_range = body.tx_num_range();
2150 return if tx_range.is_empty() {
2151 Ok(Some(Vec::new()))
2152 } else {
2153 let receipts = self.receipts_by_tx_range(tx_range)?;
2154
2155 if receipts.len() != body.tx_count as usize {
2156 return Ok(None)
2157 }
2158
2159 Ok(Some(receipts))
2160 }
2161 }
2162 Ok(None)
2163 }
2164
2165 fn receipts_by_tx_range(
2166 &self,
2167 range: impl RangeBounds<TxNumber>,
2168 ) -> ProviderResult<Vec<Self::Receipt>> {
2169 self.static_file_provider.get_range_with_static_file_or_database(
2170 StaticFileSegment::Receipts,
2171 to_range(range),
2172 |static_file, range, _| static_file.receipts_by_tx_range(range),
2173 |range, _| self.cursor_read_collect::<tables::Receipts<Self::Receipt>>(range),
2174 |_| true,
2175 )
2176 }
2177
2178 fn receipts_by_block_range(
2179 &self,
2180 block_range: RangeInclusive<BlockNumber>,
2181 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
2182 if block_range.is_empty() {
2183 return Ok(Vec::new());
2184 }
2185
2186 let range_len = block_range.end().saturating_sub(*block_range.start()) as usize + 1;
2188 let mut block_body_indices = Vec::with_capacity(range_len);
2189 for block_num in block_range {
2190 if let Some(indices) = self.block_body_indices(block_num)? {
2191 block_body_indices.push(indices);
2192 } else {
2193 block_body_indices.push(StoredBlockBodyIndices::default());
2195 }
2196 }
2197
2198 if block_body_indices.is_empty() {
2199 return Ok(Vec::new());
2200 }
2201
2202 let non_empty_blocks: Vec<_> =
2204 block_body_indices.iter().filter(|indices| indices.tx_count > 0).collect();
2205
2206 if non_empty_blocks.is_empty() {
2207 return Ok(vec![Vec::new(); block_body_indices.len()]);
2209 }
2210
2211 let first_tx = non_empty_blocks[0].first_tx_num();
2213 let last_tx = non_empty_blocks[non_empty_blocks.len() - 1].last_tx_num();
2214
2215 let all_receipts = self.receipts_by_tx_range(first_tx..=last_tx)?;
2217 let mut receipts_iter = all_receipts.into_iter();
2218
2219 let mut result = Vec::with_capacity(block_body_indices.len());
2221 for indices in &block_body_indices {
2222 if indices.tx_count == 0 {
2223 result.push(Vec::new());
2224 } else {
2225 let block_receipts =
2226 receipts_iter.by_ref().take(indices.tx_count as usize).collect();
2227 result.push(block_receipts);
2228 }
2229 }
2230
2231 Ok(result)
2232 }
2233}
2234
2235impl<TX: DbTx + 'static, N: NodeTypesForProvider> BlockBodyIndicesProvider
2236 for DatabaseProvider<TX, N>
2237{
2238 fn block_body_indices(&self, num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
2239 Ok(self.tx.get::<tables::BlockBodyIndices>(num)?)
2240 }
2241
2242 fn block_body_indices_range(
2243 &self,
2244 range: RangeInclusive<BlockNumber>,
2245 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
2246 self.cursor_read_collect::<tables::BlockBodyIndices>(range)
2247 }
2248}
2249
2250impl<TX: DbTx, N: NodeTypes> StageCheckpointReader for DatabaseProvider<TX, N> {
2251 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
2252 Ok(if let Some(encoded) = id.get_pre_encoded() {
2253 self.tx.get_by_encoded_key::<tables::StageCheckpoints>(encoded)?
2254 } else {
2255 self.tx.get::<tables::StageCheckpoints>(id.to_string())?
2256 })
2257 }
2258
2259 fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult<Option<Vec<u8>>> {
2261 Ok(self.tx.get::<tables::StageCheckpointProgresses>(id.to_string())?)
2262 }
2263
2264 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
2265 self.tx
2266 .cursor_read::<tables::StageCheckpoints>()?
2267 .walk(None)?
2268 .collect::<Result<Vec<(String, StageCheckpoint)>, _>>()
2269 .map_err(ProviderError::Database)
2270 }
2271}
2272
2273impl<TX: DbTxMut, N: NodeTypes> StageCheckpointWriter for DatabaseProvider<TX, N> {
2274 fn save_stage_checkpoint(
2276 &self,
2277 id: StageId,
2278 checkpoint: StageCheckpoint,
2279 ) -> ProviderResult<()> {
2280 Ok(self.tx.put::<tables::StageCheckpoints>(id.to_string(), checkpoint)?)
2281 }
2282
2283 fn save_stage_checkpoint_progress(
2285 &self,
2286 id: StageId,
2287 checkpoint: Vec<u8>,
2288 ) -> ProviderResult<()> {
2289 Ok(self.tx.put::<tables::StageCheckpointProgresses>(id.to_string(), checkpoint)?)
2290 }
2291
2292 #[instrument(level = "debug", target = "providers::db", skip_all)]
2293 fn update_pipeline_stages(
2294 &self,
2295 block_number: BlockNumber,
2296 drop_stage_checkpoint: bool,
2297 ) -> ProviderResult<()> {
2298 let mut cursor = self.tx.cursor_write::<tables::StageCheckpoints>()?;
2300 for stage_id in StageId::ALL {
2301 let (_, checkpoint) = cursor.seek_exact(stage_id.to_string())?.unwrap_or_default();
2302 cursor.upsert(
2303 stage_id.to_string(),
2304 &StageCheckpoint {
2305 block_number,
2306 ..if drop_stage_checkpoint { Default::default() } else { checkpoint }
2307 },
2308 )?;
2309 }
2310
2311 Ok(())
2312 }
2313}
2314
2315impl<TX: DbTx + 'static, N: NodeTypes> StorageReader for DatabaseProvider<TX, N> {
2316 fn plain_state_storages(
2317 &self,
2318 addresses_with_keys: impl IntoIterator<Item = (Address, impl IntoIterator<Item = B256>)>,
2319 ) -> ProviderResult<Vec<(Address, Vec<StorageEntry>)>> {
2320 if self.cached_storage_settings().use_hashed_state() {
2321 let mut hashed_storage = self.tx.cursor_dup_read::<tables::HashedStorages>()?;
2322
2323 addresses_with_keys
2324 .into_iter()
2325 .map(|(address, storage)| {
2326 let hashed_address = keccak256(address);
2327 storage
2328 .into_iter()
2329 .map(|key| -> ProviderResult<_> {
2330 let hashed_key = keccak256(key);
2331 let value = hashed_storage
2332 .seek_by_key_subkey(hashed_address, hashed_key)?
2333 .filter(|v| v.key == hashed_key)
2334 .map(|v| v.value)
2335 .unwrap_or_default();
2336 Ok(StorageEntry { key, value })
2337 })
2338 .collect::<ProviderResult<Vec<_>>>()
2339 .map(|storage| (address, storage))
2340 })
2341 .collect::<ProviderResult<Vec<(_, _)>>>()
2342 } else {
2343 let mut plain_storage = self.tx.cursor_dup_read::<tables::PlainStorageState>()?;
2344
2345 addresses_with_keys
2346 .into_iter()
2347 .map(|(address, storage)| {
2348 storage
2349 .into_iter()
2350 .map(|key| -> ProviderResult<_> {
2351 Ok(plain_storage
2352 .seek_by_key_subkey(address, key)?
2353 .filter(|v| v.key == key)
2354 .unwrap_or_else(|| StorageEntry { key, value: Default::default() }))
2355 })
2356 .collect::<ProviderResult<Vec<_>>>()
2357 .map(|storage| (address, storage))
2358 })
2359 .collect::<ProviderResult<Vec<(_, _)>>>()
2360 }
2361 }
2362
2363 fn changed_storages_with_range(
2364 &self,
2365 range: RangeInclusive<BlockNumber>,
2366 ) -> ProviderResult<BTreeMap<Address, BTreeSet<B256>>> {
2367 if self.cached_storage_settings().storage_v2 {
2368 self.storage_changesets_range(range)?.into_iter().try_fold(
2369 BTreeMap::new(),
2370 |mut accounts: BTreeMap<Address, BTreeSet<B256>>, entry| {
2371 let (BlockNumberAddress((_, address)), storage_entry) = entry;
2372 accounts.entry(address).or_default().insert(storage_entry.key);
2373 Ok(accounts)
2374 },
2375 )
2376 } else {
2377 self.tx
2378 .cursor_read::<tables::StorageChangeSets>()?
2379 .walk_range(BlockNumberAddress::range(range))?
2380 .try_fold(
2383 BTreeMap::new(),
2384 |mut accounts: BTreeMap<Address, BTreeSet<B256>>, entry| {
2385 let (BlockNumberAddress((_, address)), storage_entry) = entry?;
2386 accounts.entry(address).or_default().insert(storage_entry.key);
2387 Ok(accounts)
2388 },
2389 )
2390 }
2391 }
2392
2393 fn changed_storages_and_blocks_with_range(
2394 &self,
2395 range: RangeInclusive<BlockNumber>,
2396 ) -> ProviderResult<BTreeMap<(Address, B256), Vec<u64>>> {
2397 if self.cached_storage_settings().storage_v2 {
2398 self.storage_changesets_range(range)?.into_iter().try_fold(
2399 BTreeMap::new(),
2400 |mut storages: BTreeMap<(Address, B256), Vec<u64>>, (index, storage)| {
2401 storages
2402 .entry((index.address(), storage.key))
2403 .or_default()
2404 .push(index.block_number());
2405 Ok(storages)
2406 },
2407 )
2408 } else {
2409 let mut changeset_cursor = self.tx.cursor_read::<tables::StorageChangeSets>()?;
2410
2411 let storage_changeset_lists =
2412 changeset_cursor.walk_range(BlockNumberAddress::range(range))?.try_fold(
2413 BTreeMap::new(),
2414 |mut storages: BTreeMap<(Address, B256), Vec<u64>>,
2415 entry|
2416 -> ProviderResult<_> {
2417 let (index, storage) = entry?;
2418 storages
2419 .entry((index.address(), storage.key))
2420 .or_default()
2421 .push(index.block_number());
2422 Ok(storages)
2423 },
2424 )?;
2425
2426 Ok(storage_changeset_lists)
2427 }
2428 }
2429}
2430
2431impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateWriter
2432 for DatabaseProvider<TX, N>
2433{
2434 type Receipt = ReceiptTy<N>;
2435
2436 #[instrument(level = "debug", target = "providers::db", skip_all)]
2437 fn write_state<'a>(
2438 &self,
2439 execution_outcome: impl Into<WriteStateInput<'a, Self::Receipt>>,
2440 is_value_known: OriginalValuesKnown,
2441 config: StateWriteConfig,
2442 ) -> ProviderResult<()> {
2443 let execution_outcome = execution_outcome.into();
2444
2445 if self.cached_storage_settings().use_hashed_state() &&
2446 !config.write_receipts &&
2447 !config.write_account_changesets &&
2448 !config.write_storage_changesets
2449 {
2450 self.write_bytecodes(
2454 execution_outcome.state().contracts.iter().map(|(h, b)| (*h, Bytecode(b.clone()))),
2455 )?;
2456 return Ok(());
2457 }
2458
2459 let first_block = execution_outcome.first_block();
2460 let (plain_state, reverts) =
2461 execution_outcome.state().to_plain_state_and_reverts(is_value_known);
2462
2463 self.write_state_reverts(reverts, first_block, config)?;
2464 self.write_state_changes(plain_state)?;
2465
2466 if !config.write_receipts {
2467 return Ok(());
2468 }
2469
2470 let block_count = execution_outcome.len() as u64;
2471 let last_block = execution_outcome.last_block();
2472 let block_range = first_block..=last_block;
2473
2474 let tip = self.last_block_number()?.max(last_block);
2475
2476 let block_indices: Vec<_> = self
2478 .block_body_indices_range(block_range)?
2479 .into_iter()
2480 .map(|b| b.first_tx_num)
2481 .collect();
2482
2483 if block_indices.len() < block_count as usize {
2485 let missing_blocks = block_count - block_indices.len() as u64;
2486 return Err(ProviderError::BlockBodyIndicesNotFound(
2487 last_block.saturating_sub(missing_blocks - 1),
2488 ));
2489 }
2490
2491 let mut receipts_writer = EitherWriter::new_receipts(self, first_block)?;
2492
2493 let has_contract_log_filter = !self.prune_modes.receipts_log_filter.is_empty();
2494 let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?;
2495
2496 let prunable_receipts = (EitherWriter::receipts_destination(self).is_database() ||
2504 self.static_file_provider()
2505 .get_highest_static_file_tx(StaticFileSegment::Receipts)
2506 .is_none()) &&
2507 PruneMode::Distance(self.minimum_pruning_distance).should_prune(first_block, tip);
2508
2509 let mut allowed_addresses: AddressSet = AddressSet::default();
2511 for (_, addresses) in contract_log_pruner.range(..first_block) {
2512 allowed_addresses.extend(addresses.iter().copied());
2513 }
2514
2515 for (idx, (receipts, first_tx_index)) in
2516 execution_outcome.receipts().zip(block_indices).enumerate()
2517 {
2518 let block_number = first_block + idx as u64;
2519
2520 receipts_writer.increment_block(block_number)?;
2522
2523 if prunable_receipts &&
2525 self.prune_modes
2526 .receipts
2527 .is_some_and(|mode| mode.should_prune(block_number, tip))
2528 {
2529 continue
2530 }
2531
2532 if let Some(new_addresses) = contract_log_pruner.get(&block_number) {
2534 allowed_addresses.extend(new_addresses.iter().copied());
2535 }
2536
2537 for (idx, receipt) in receipts.iter().enumerate() {
2538 let receipt_idx = first_tx_index + idx as u64;
2539 if prunable_receipts &&
2542 has_contract_log_filter &&
2543 !receipt.logs().iter().any(|log| allowed_addresses.contains(&log.address))
2544 {
2545 continue
2546 }
2547
2548 receipts_writer.append_receipt(receipt_idx, receipt)?;
2549 }
2550 }
2551
2552 Ok(())
2553 }
2554
2555 fn write_state_reverts(
2556 &self,
2557 reverts: PlainStateReverts,
2558 first_block: BlockNumber,
2559 config: StateWriteConfig,
2560 ) -> ProviderResult<()> {
2561 if config.write_storage_changesets {
2563 tracing::trace!("Writing storage changes");
2564 let mut storages_cursor =
2565 self.tx_ref().cursor_dup_write::<tables::PlainStorageState>()?;
2566 for (block_index, mut storage_changes) in reverts.storage.into_iter().enumerate() {
2567 let block_number = first_block + block_index as BlockNumber;
2568
2569 tracing::trace!(block_number, "Writing block change");
2570 storage_changes.par_sort_unstable_by_key(|a| a.address);
2572 let total_changes =
2573 storage_changes.iter().map(|change| change.storage_revert.len()).sum();
2574 let mut changeset = Vec::with_capacity(total_changes);
2575 for PlainStorageRevert { address, wiped, storage_revert } in storage_changes {
2576 let mut storage = storage_revert
2577 .into_iter()
2578 .map(|(k, v)| (B256::from(k.to_be_bytes()), v))
2579 .collect::<Vec<_>>();
2580 storage.par_sort_unstable_by_key(|a| a.0);
2582
2583 let mut wiped_storage = Vec::new();
2591 if wiped {
2592 tracing::trace!(?address, "Wiping storage");
2593 if let Some((_, entry)) = storages_cursor.seek_exact(address)? {
2594 wiped_storage.push((entry.key, entry.value));
2595 while let Some(entry) = storages_cursor.next_dup_val()? {
2596 wiped_storage.push((entry.key, entry.value))
2597 }
2598 }
2599 }
2600
2601 tracing::trace!(?address, ?storage, "Writing storage reverts");
2602 for (key, value) in StorageRevertsIter::new(storage, wiped_storage) {
2603 changeset.push(StorageBeforeTx { address, key, value });
2604 }
2605 }
2606
2607 let mut storage_changesets_writer =
2608 EitherWriter::new_storage_changesets(self, block_number)?;
2609 storage_changesets_writer.append_storage_changeset(block_number, changeset)?;
2610 }
2611 }
2612
2613 if !config.write_account_changesets {
2614 return Ok(());
2615 }
2616
2617 tracing::trace!(?first_block, "Writing account changes");
2619 for (block_index, account_block_reverts) in reverts.accounts.into_iter().enumerate() {
2620 let block_number = first_block + block_index as BlockNumber;
2621 let changeset = account_block_reverts
2622 .into_iter()
2623 .map(|(address, info)| AccountBeforeTx { address, info: info.map(Into::into) })
2624 .collect::<Vec<_>>();
2625 let mut account_changesets_writer =
2626 EitherWriter::new_account_changesets(self, block_number)?;
2627
2628 account_changesets_writer.append_account_changeset(block_number, changeset)?;
2629 }
2630
2631 Ok(())
2632 }
2633
2634 fn write_state_changes(&self, mut changes: StateChangeset) -> ProviderResult<()> {
2635 changes.accounts.par_sort_by_key(|a| a.0);
2638 changes.storage.par_sort_by_key(|a| a.address);
2639 changes.contracts.par_sort_by_key(|a| a.0);
2640
2641 if !self.cached_storage_settings().use_hashed_state() {
2642 tracing::trace!(len = changes.accounts.len(), "Writing new account state");
2644 let mut accounts_cursor = self.tx_ref().cursor_write::<tables::PlainAccountState>()?;
2645 for (address, account) in changes.accounts {
2647 if let Some(account) = account {
2648 tracing::trace!(?address, "Updating plain state account");
2649 accounts_cursor.upsert(address, &account.into())?;
2650 } else if accounts_cursor.seek_exact(address)?.is_some() {
2651 tracing::trace!(?address, "Deleting plain state account");
2652 accounts_cursor.delete_current()?;
2653 }
2654 }
2655
2656 tracing::trace!(len = changes.storage.len(), "Writing new storage state");
2658 let mut storages_cursor =
2659 self.tx_ref().cursor_dup_write::<tables::PlainStorageState>()?;
2660 for PlainStorageChangeset { address, wipe_storage, storage } in changes.storage {
2661 if wipe_storage && storages_cursor.seek_exact(address)?.is_some() {
2663 storages_cursor.delete_current_duplicates()?;
2664 }
2665 let mut storage = storage
2667 .into_iter()
2668 .map(|(k, value)| StorageEntry { key: k.into(), value })
2669 .collect::<Vec<_>>();
2670 storage.par_sort_unstable_by_key(|a| a.key);
2672
2673 for entry in storage {
2674 tracing::trace!(?address, ?entry.key, "Updating plain state storage");
2675 if let Some(db_entry) =
2676 storages_cursor.seek_by_key_subkey(address, entry.key)? &&
2677 db_entry.key == entry.key
2678 {
2679 storages_cursor.delete_current()?;
2680 }
2681
2682 if !entry.value.is_zero() {
2683 storages_cursor.upsert(address, &entry)?;
2684 }
2685 }
2686 }
2687 }
2688
2689 tracing::trace!(len = changes.contracts.len(), "Writing bytecodes");
2691 self.write_bytecodes(
2692 changes.contracts.into_iter().map(|(hash, bytecode)| (hash, Bytecode(bytecode))),
2693 )?;
2694
2695 Ok(())
2696 }
2697
2698 #[instrument(level = "debug", target = "providers::db", skip_all)]
2699 fn write_hashed_state(&self, hashed_state: &HashedPostStateSorted) -> ProviderResult<()> {
2700 let mut hashed_accounts_cursor = self.tx_ref().cursor_write::<tables::HashedAccounts>()?;
2702 for (hashed_address, account) in hashed_state.accounts() {
2703 if let Some(account) = account {
2704 hashed_accounts_cursor.upsert(*hashed_address, account)?;
2705 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
2706 hashed_accounts_cursor.delete_current()?;
2707 }
2708 }
2709
2710 let sorted_storages = hashed_state.account_storages().iter().sorted_by_key(|(key, _)| *key);
2712 let mut hashed_storage_cursor =
2713 self.tx_ref().cursor_dup_write::<tables::HashedStorages>()?;
2714 for (hashed_address, storage) in sorted_storages {
2715 if storage.is_wiped() && hashed_storage_cursor.seek_exact(*hashed_address)?.is_some() {
2716 hashed_storage_cursor.delete_current_duplicates()?;
2717 }
2718
2719 for (hashed_slot, value) in storage.storage_slots_ref() {
2720 let entry = StorageEntry { key: *hashed_slot, value: *value };
2721
2722 if let Some(db_entry) =
2723 hashed_storage_cursor.seek_by_key_subkey(*hashed_address, entry.key)? &&
2724 db_entry.key == entry.key
2725 {
2726 hashed_storage_cursor.delete_current()?;
2727 }
2728
2729 if !entry.value.is_zero() {
2730 hashed_storage_cursor.upsert(*hashed_address, &entry)?;
2731 }
2732 }
2733 }
2734
2735 Ok(())
2736 }
2737
2738 fn remove_state_above(&self, block: BlockNumber) -> ProviderResult<()> {
2760 let range = block + 1..=self.last_block_number()?;
2761
2762 if range.is_empty() {
2763 return Ok(());
2764 }
2765
2766 let block_bodies = self.block_body_indices_range(range.clone())?;
2768
2769 let from_transaction_num =
2771 block_bodies.first().expect("already checked if there are blocks").first_tx_num();
2772
2773 let storage_range = BlockNumberAddress::range(range.clone());
2774 let storage_changeset = if self.cached_storage_settings().storage_v2 {
2775 let changesets = self.storage_changesets_range(range.clone())?;
2776 let mut changeset_writer =
2777 self.static_file_provider.latest_writer(StaticFileSegment::StorageChangeSets)?;
2778 changeset_writer.prune_storage_changesets(block)?;
2779 changesets
2780 } else {
2781 self.take::<tables::StorageChangeSets>(storage_range)?.into_iter().collect()
2782 };
2783 let account_changeset = if self.cached_storage_settings().storage_v2 {
2784 let changesets = self.account_changesets_range(range)?;
2785 let mut changeset_writer =
2786 self.static_file_provider.latest_writer(StaticFileSegment::AccountChangeSets)?;
2787 changeset_writer.prune_account_changesets(block)?;
2788 changesets
2789 } else {
2790 self.take::<tables::AccountChangeSets>(range)?
2791 };
2792
2793 if self.cached_storage_settings().use_hashed_state() {
2794 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
2795 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
2796
2797 let (state, _) = self.populate_bundle_state_hashed(
2798 account_changeset,
2799 storage_changeset,
2800 &mut hashed_accounts_cursor,
2801 &mut hashed_storage_cursor,
2802 )?;
2803
2804 for (address, (old_account, new_account, storage)) in &state {
2805 if old_account != new_account {
2806 let hashed_address = keccak256(address);
2807 let existing_entry = hashed_accounts_cursor.seek_exact(hashed_address)?;
2808 if let Some(account) = old_account {
2809 hashed_accounts_cursor.upsert(hashed_address, account)?;
2810 } else if existing_entry.is_some() {
2811 hashed_accounts_cursor.delete_current()?;
2812 }
2813 }
2814
2815 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2816 let hashed_address = keccak256(address);
2817 let hashed_storage_key = keccak256(storage_key);
2818 let storage_entry =
2819 StorageEntry { key: hashed_storage_key, value: *old_storage_value };
2820 if hashed_storage_cursor
2821 .seek_by_key_subkey(hashed_address, hashed_storage_key)?
2822 .is_some_and(|s| s.key == hashed_storage_key)
2823 {
2824 hashed_storage_cursor.delete_current()?
2825 }
2826
2827 if !old_storage_value.is_zero() {
2828 hashed_storage_cursor.upsert(hashed_address, &storage_entry)?;
2829 }
2830 }
2831 }
2832 } else {
2833 let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
2838 let mut plain_storage_cursor =
2839 self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
2840
2841 let (state, _) = self.populate_bundle_state_plain(
2842 account_changeset,
2843 storage_changeset,
2844 &mut plain_accounts_cursor,
2845 &mut plain_storage_cursor,
2846 )?;
2847
2848 for (address, (old_account, new_account, storage)) in &state {
2849 if old_account != new_account {
2850 let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
2851 if let Some(account) = old_account {
2852 plain_accounts_cursor.upsert(*address, account)?;
2853 } else if existing_entry.is_some() {
2854 plain_accounts_cursor.delete_current()?;
2855 }
2856 }
2857
2858 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2859 let storage_entry =
2860 StorageEntry { key: *storage_key, value: *old_storage_value };
2861 if plain_storage_cursor
2862 .seek_by_key_subkey(*address, *storage_key)?
2863 .is_some_and(|s| s.key == *storage_key)
2864 {
2865 plain_storage_cursor.delete_current()?
2866 }
2867
2868 if !old_storage_value.is_zero() {
2869 plain_storage_cursor.upsert(*address, &storage_entry)?;
2870 }
2871 }
2872 }
2873 }
2874
2875 self.remove_receipts_from(from_transaction_num, block)?;
2876
2877 Ok(())
2878 }
2879
2880 fn take_state_above(
2902 &self,
2903 block: BlockNumber,
2904 ) -> ProviderResult<ExecutionOutcome<Self::Receipt>> {
2905 let range = block + 1..=self.last_block_number()?;
2906
2907 if range.is_empty() {
2908 return Ok(ExecutionOutcome::default())
2909 }
2910 let start_block_number = *range.start();
2911
2912 let block_bodies = self.block_body_indices_range(range.clone())?;
2914
2915 let from_transaction_num =
2917 block_bodies.first().expect("already checked if there are blocks").first_tx_num();
2918 let to_transaction_num =
2919 block_bodies.last().expect("already checked if there are blocks").last_tx_num();
2920
2921 let storage_range = BlockNumberAddress::range(range.clone());
2922 let storage_changeset = if let Some(highest_block) = self
2923 .static_file_provider
2924 .get_highest_static_file_block(StaticFileSegment::StorageChangeSets) &&
2925 self.cached_storage_settings().storage_v2
2926 {
2927 let changesets = self.storage_changesets_range(block + 1..=highest_block)?;
2928 let mut changeset_writer =
2929 self.static_file_provider.latest_writer(StaticFileSegment::StorageChangeSets)?;
2930 changeset_writer.prune_storage_changesets(block)?;
2931 changesets
2932 } else {
2933 self.take::<tables::StorageChangeSets>(storage_range)?.into_iter().collect()
2934 };
2935
2936 let highest_changeset_block = self
2938 .static_file_provider
2939 .get_highest_static_file_block(StaticFileSegment::AccountChangeSets);
2940 let account_changeset = if let Some(highest_block) = highest_changeset_block &&
2941 self.cached_storage_settings().storage_v2
2942 {
2943 let changesets = self.account_changesets_range(block + 1..highest_block + 1)?;
2945 let mut changeset_writer =
2946 self.static_file_provider.latest_writer(StaticFileSegment::AccountChangeSets)?;
2947 changeset_writer.prune_account_changesets(block)?;
2948
2949 changesets
2950 } else {
2951 self.take::<tables::AccountChangeSets>(range)?
2954 };
2955
2956 let (state, reverts) = if self.cached_storage_settings().use_hashed_state() {
2957 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
2958 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
2959
2960 let (state, reverts) = self.populate_bundle_state_hashed(
2961 account_changeset,
2962 storage_changeset,
2963 &mut hashed_accounts_cursor,
2964 &mut hashed_storage_cursor,
2965 )?;
2966
2967 for (address, (old_account, new_account, storage)) in &state {
2968 if old_account != new_account {
2969 let hashed_address = keccak256(address);
2970 let existing_entry = hashed_accounts_cursor.seek_exact(hashed_address)?;
2971 if let Some(account) = old_account {
2972 hashed_accounts_cursor.upsert(hashed_address, account)?;
2973 } else if existing_entry.is_some() {
2974 hashed_accounts_cursor.delete_current()?;
2975 }
2976 }
2977
2978 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2979 let hashed_address = keccak256(address);
2980 let hashed_storage_key = keccak256(storage_key);
2981 let storage_entry =
2982 StorageEntry { key: hashed_storage_key, value: *old_storage_value };
2983 if hashed_storage_cursor
2984 .seek_by_key_subkey(hashed_address, hashed_storage_key)?
2985 .is_some_and(|s| s.key == hashed_storage_key)
2986 {
2987 hashed_storage_cursor.delete_current()?
2988 }
2989
2990 if !old_storage_value.is_zero() {
2991 hashed_storage_cursor.upsert(hashed_address, &storage_entry)?;
2992 }
2993 }
2994 }
2995
2996 (state, reverts)
2997 } else {
2998 let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
3003 let mut plain_storage_cursor =
3004 self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
3005
3006 let (state, reverts) = self.populate_bundle_state_plain(
3007 account_changeset,
3008 storage_changeset,
3009 &mut plain_accounts_cursor,
3010 &mut plain_storage_cursor,
3011 )?;
3012
3013 for (address, (old_account, new_account, storage)) in &state {
3014 if old_account != new_account {
3015 let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
3016 if let Some(account) = old_account {
3017 plain_accounts_cursor.upsert(*address, account)?;
3018 } else if existing_entry.is_some() {
3019 plain_accounts_cursor.delete_current()?;
3020 }
3021 }
3022
3023 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
3024 let storage_entry =
3025 StorageEntry { key: *storage_key, value: *old_storage_value };
3026 if plain_storage_cursor
3027 .seek_by_key_subkey(*address, *storage_key)?
3028 .is_some_and(|s| s.key == *storage_key)
3029 {
3030 plain_storage_cursor.delete_current()?
3031 }
3032
3033 if !old_storage_value.is_zero() {
3034 plain_storage_cursor.upsert(*address, &storage_entry)?;
3035 }
3036 }
3037 }
3038
3039 (state, reverts)
3040 };
3041
3042 let mut receipts_iter = self
3044 .static_file_provider
3045 .get_range_with_static_file_or_database(
3046 StaticFileSegment::Receipts,
3047 from_transaction_num..to_transaction_num + 1,
3048 |static_file, range, _| {
3049 static_file
3050 .receipts_by_tx_range(range.clone())
3051 .map(|r| range.into_iter().zip(r).collect())
3052 },
3053 |range, _| {
3054 self.tx
3055 .cursor_read::<tables::Receipts<Self::Receipt>>()?
3056 .walk_range(range)?
3057 .map(|r| r.map_err(Into::into))
3058 .collect()
3059 },
3060 |_| true,
3061 )?
3062 .into_iter()
3063 .peekable();
3064
3065 let mut receipts = Vec::with_capacity(block_bodies.len());
3066 for block_body in block_bodies {
3068 let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize);
3069 for num in block_body.tx_num_range() {
3070 if receipts_iter.peek().is_some_and(|(n, _)| *n == num) {
3071 block_receipts.push(receipts_iter.next().unwrap().1);
3072 }
3073 }
3074 receipts.push(block_receipts);
3075 }
3076
3077 self.remove_receipts_from(from_transaction_num, block)?;
3078
3079 Ok(ExecutionOutcome::new_init(
3080 state,
3081 reverts,
3082 Vec::new(),
3083 receipts,
3084 start_block_number,
3085 Vec::new(),
3086 ))
3087 }
3088}
3089
3090impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
3091 fn write_account_trie_updates<A: TrieTableAdapter>(
3092 tx: &TX,
3093 trie_updates: &TrieUpdatesSorted,
3094 num_entries: &mut usize,
3095 ) -> ProviderResult<()>
3096 where
3097 TX: DbTxMut,
3098 {
3099 let mut account_trie_cursor = tx.cursor_write::<A::AccountTrieTable>()?;
3100 for (key, updated_node) in trie_updates.account_nodes_ref() {
3102 let nibbles = A::AccountKey::from(*key);
3103 match updated_node {
3104 Some(node) => {
3105 if !key.is_empty() {
3106 *num_entries += 1;
3107 account_trie_cursor.upsert(nibbles, node)?;
3108 }
3109 }
3110 None => {
3111 *num_entries += 1;
3112 if account_trie_cursor.seek_exact(nibbles)?.is_some() {
3113 account_trie_cursor.delete_current()?;
3114 }
3115 }
3116 }
3117 }
3118 Ok(())
3119 }
3120
3121 fn write_storage_tries<A: TrieTableAdapter>(
3122 tx: &TX,
3123 storage_tries: Vec<(&B256, &StorageTrieUpdatesSorted)>,
3124 num_entries: &mut usize,
3125 ) -> ProviderResult<()>
3126 where
3127 TX: DbTxMut,
3128 {
3129 let mut cursor = tx.cursor_dup_write::<A::StorageTrieTable>()?;
3130 for (hashed_address, storage_trie_updates) in storage_tries {
3131 let mut db_storage_trie_cursor: DatabaseStorageTrieCursor<_, A> =
3132 DatabaseStorageTrieCursor::new(cursor, *hashed_address);
3133 *num_entries +=
3134 db_storage_trie_cursor.write_storage_trie_updates_sorted(storage_trie_updates)?;
3135 cursor = db_storage_trie_cursor.cursor;
3136 }
3137 Ok(())
3138 }
3139}
3140
3141impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> TrieWriter for DatabaseProvider<TX, N> {
3142 #[instrument(level = "debug", target = "providers::db", skip_all)]
3146 fn write_trie_updates_sorted(&self, trie_updates: &TrieUpdatesSorted) -> ProviderResult<usize> {
3147 if trie_updates.is_empty() {
3148 return Ok(0)
3149 }
3150
3151 let mut num_entries = 0;
3153
3154 reth_trie_db::with_adapter!(self, |A| {
3155 Self::write_account_trie_updates::<A>(self.tx_ref(), trie_updates, &mut num_entries)?;
3156 });
3157
3158 num_entries +=
3159 self.write_storage_trie_updates_sorted(trie_updates.storage_tries_ref().iter())?;
3160
3161 Ok(num_entries)
3162 }
3163}
3164
3165impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> StorageTrieWriter for DatabaseProvider<TX, N> {
3166 fn write_storage_trie_updates_sorted<'a>(
3172 &self,
3173 storage_tries: impl Iterator<Item = (&'a B256, &'a StorageTrieUpdatesSorted)>,
3174 ) -> ProviderResult<usize> {
3175 let mut num_entries = 0;
3176 let mut storage_tries = storage_tries.collect::<Vec<_>>();
3177 storage_tries.sort_unstable_by(|a, b| a.0.cmp(b.0));
3178 reth_trie_db::with_adapter!(self, |A| {
3179 Self::write_storage_tries::<A>(self.tx_ref(), storage_tries, &mut num_entries)?;
3180 });
3181 Ok(num_entries)
3182 }
3183}
3184
3185impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HashingWriter for DatabaseProvider<TX, N> {
3186 fn unwind_account_hashing<'a>(
3187 &self,
3188 changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
3189 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3190 let hashed_accounts = changesets
3194 .into_iter()
3195 .map(|(_, e)| (keccak256(e.address), e.info))
3196 .collect::<Vec<_>>()
3197 .into_iter()
3198 .rev()
3199 .collect::<BTreeMap<_, _>>();
3200
3201 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3203 for (hashed_address, account) in &hashed_accounts {
3204 if let Some(account) = account {
3205 hashed_accounts_cursor.upsert(*hashed_address, account)?;
3206 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
3207 hashed_accounts_cursor.delete_current()?;
3208 }
3209 }
3210
3211 Ok(hashed_accounts)
3212 }
3213
3214 fn unwind_account_hashing_range(
3215 &self,
3216 range: impl RangeBounds<BlockNumber>,
3217 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3218 let changesets = self.account_changesets_range(range)?;
3219 self.unwind_account_hashing(changesets.iter())
3220 }
3221
3222 fn insert_account_for_hashing(
3223 &self,
3224 changesets: impl IntoIterator<Item = (Address, Option<Account>)>,
3225 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3226 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3227 let hashed_accounts =
3228 changesets.into_iter().map(|(ad, ac)| (keccak256(ad), ac)).collect::<BTreeMap<_, _>>();
3229 for (hashed_address, account) in &hashed_accounts {
3230 if let Some(account) = account {
3231 hashed_accounts_cursor.upsert(*hashed_address, account)?;
3232 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
3233 hashed_accounts_cursor.delete_current()?;
3234 }
3235 }
3236 Ok(hashed_accounts)
3237 }
3238
3239 fn unwind_storage_hashing(
3240 &self,
3241 changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
3242 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3243 let mut hashed_storages = changesets
3245 .into_iter()
3246 .map(|(BlockNumberAddress((_, address)), storage_entry)| {
3247 let hashed_key = keccak256(storage_entry.key);
3248 (keccak256(address), hashed_key, storage_entry.value)
3249 })
3250 .collect::<Vec<_>>();
3251 hashed_storages.sort_by_key(|(ha, hk, _)| (*ha, *hk));
3252
3253 let mut hashed_storage_keys: B256Map<BTreeSet<B256>> =
3255 B256Map::with_capacity_and_hasher(hashed_storages.len(), Default::default());
3256 let mut hashed_storage = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3257 for (hashed_address, key, value) in hashed_storages.into_iter().rev() {
3258 hashed_storage_keys.entry(hashed_address).or_default().insert(key);
3259
3260 if hashed_storage
3261 .seek_by_key_subkey(hashed_address, key)?
3262 .is_some_and(|entry| entry.key == key)
3263 {
3264 hashed_storage.delete_current()?;
3265 }
3266
3267 if !value.is_zero() {
3268 hashed_storage.upsert(hashed_address, &StorageEntry { key, value })?;
3269 }
3270 }
3271 Ok(hashed_storage_keys)
3272 }
3273
3274 fn unwind_storage_hashing_range(
3275 &self,
3276 range: impl RangeBounds<BlockNumber>,
3277 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3278 let changesets = self.storage_changesets_range(range)?;
3279 self.unwind_storage_hashing(changesets.into_iter())
3280 }
3281
3282 fn insert_storage_for_hashing(
3283 &self,
3284 storages: impl IntoIterator<Item = (Address, impl IntoIterator<Item = StorageEntry>)>,
3285 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3286 let hashed_storages =
3288 storages.into_iter().fold(BTreeMap::new(), |mut map, (address, storage)| {
3289 let storage = storage.into_iter().fold(BTreeMap::new(), |mut map, entry| {
3290 map.insert(keccak256(entry.key), entry.value);
3291 map
3292 });
3293 map.insert(keccak256(address), storage);
3294 map
3295 });
3296
3297 let hashed_storage_keys = hashed_storages
3298 .iter()
3299 .map(|(hashed_address, entries)| (*hashed_address, entries.keys().copied().collect()))
3300 .collect();
3301
3302 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3303 hashed_storages.into_iter().try_for_each(|(hashed_address, storage)| {
3306 storage.into_iter().try_for_each(|(key, value)| -> ProviderResult<()> {
3307 if hashed_storage_cursor
3308 .seek_by_key_subkey(hashed_address, key)?
3309 .is_some_and(|entry| entry.key == key)
3310 {
3311 hashed_storage_cursor.delete_current()?;
3312 }
3313
3314 if !value.is_zero() {
3315 hashed_storage_cursor.upsert(hashed_address, &StorageEntry { key, value })?;
3316 }
3317 Ok(())
3318 })
3319 })?;
3320
3321 Ok(hashed_storage_keys)
3322 }
3323}
3324
3325impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HistoryWriter for DatabaseProvider<TX, N> {
3326 fn unwind_account_history_indices<'a>(
3327 &self,
3328 changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
3329 ) -> ProviderResult<usize> {
3330 let mut last_indices = changesets
3331 .into_iter()
3332 .map(|(index, account)| (account.address, *index))
3333 .collect::<Vec<_>>();
3334 last_indices.sort_unstable_by_key(|(a, _)| *a);
3335
3336 if self.cached_storage_settings().storage_v2 {
3337 let batch = self.rocksdb_provider.unwind_account_history_indices(&last_indices)?;
3338 self.pending_rocksdb_batches.lock().push(batch);
3339 } else {
3340 let mut cursor = self.tx.cursor_write::<tables::AccountsHistory>()?;
3342 for &(address, rem_index) in &last_indices {
3343 let partial_shard = unwind_history_shards::<_, tables::AccountsHistory, _>(
3344 &mut cursor,
3345 ShardedKey::last(address),
3346 rem_index,
3347 |sharded_key| sharded_key.key == address,
3348 )?;
3349
3350 if !partial_shard.is_empty() {
3353 cursor.insert(
3354 ShardedKey::last(address),
3355 &BlockNumberList::new_pre_sorted(partial_shard),
3356 )?;
3357 }
3358 }
3359 }
3360
3361 let changesets = last_indices.len();
3362 Ok(changesets)
3363 }
3364
3365 fn unwind_account_history_indices_range(
3366 &self,
3367 range: impl RangeBounds<BlockNumber>,
3368 ) -> ProviderResult<usize> {
3369 let changesets = self.account_changesets_range(range)?;
3370 self.unwind_account_history_indices(changesets.iter())
3371 }
3372
3373 fn insert_account_history_index(
3374 &self,
3375 account_transitions: impl IntoIterator<Item = (Address, impl IntoIterator<Item = u64>)>,
3376 ) -> ProviderResult<()> {
3377 self.append_history_index::<_, tables::AccountsHistory>(
3378 account_transitions,
3379 ShardedKey::new,
3380 )
3381 }
3382
3383 fn unwind_storage_history_indices(
3384 &self,
3385 changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
3386 ) -> ProviderResult<usize> {
3387 let mut storage_changesets = changesets
3388 .into_iter()
3389 .map(|(BlockNumberAddress((bn, address)), storage)| (address, storage.key, bn))
3390 .collect::<Vec<_>>();
3391 storage_changesets.sort_unstable_by_key(|(address, key, _)| (*address, *key));
3392
3393 if self.cached_storage_settings().storage_v2 {
3394 let batch =
3395 self.rocksdb_provider.unwind_storage_history_indices(&storage_changesets)?;
3396 self.pending_rocksdb_batches.lock().push(batch);
3397 } else {
3398 let mut cursor = self.tx.cursor_write::<tables::StoragesHistory>()?;
3400 for &(address, storage_key, rem_index) in &storage_changesets {
3401 let partial_shard = unwind_history_shards::<_, tables::StoragesHistory, _>(
3402 &mut cursor,
3403 StorageShardedKey::last(address, storage_key),
3404 rem_index,
3405 |storage_sharded_key| {
3406 storage_sharded_key.address == address &&
3407 storage_sharded_key.sharded_key.key == storage_key
3408 },
3409 )?;
3410
3411 if !partial_shard.is_empty() {
3414 cursor.insert(
3415 StorageShardedKey::last(address, storage_key),
3416 &BlockNumberList::new_pre_sorted(partial_shard),
3417 )?;
3418 }
3419 }
3420 }
3421
3422 let changesets = storage_changesets.len();
3423 Ok(changesets)
3424 }
3425
3426 fn unwind_storage_history_indices_range(
3427 &self,
3428 range: impl RangeBounds<BlockNumber>,
3429 ) -> ProviderResult<usize> {
3430 let changesets = self.storage_changesets_range(range)?;
3431 self.unwind_storage_history_indices(changesets.into_iter())
3432 }
3433
3434 fn insert_storage_history_index(
3435 &self,
3436 storage_transitions: impl IntoIterator<Item = ((Address, B256), impl IntoIterator<Item = u64>)>,
3437 ) -> ProviderResult<()> {
3438 self.append_history_index::<_, tables::StoragesHistory>(
3439 storage_transitions,
3440 |(address, storage_key), highest_block_number| {
3441 StorageShardedKey::new(address, storage_key, highest_block_number)
3442 },
3443 )
3444 }
3445
3446 #[instrument(level = "debug", target = "providers::db", skip_all)]
3447 fn update_history_indices(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<()> {
3448 let storage_settings = self.cached_storage_settings();
3449 if !storage_settings.storage_v2 {
3450 let indices = self.changed_accounts_and_blocks_with_range(range.clone())?;
3451 self.insert_account_history_index(indices)?;
3452 }
3453
3454 if !storage_settings.storage_v2 {
3455 let indices = self.changed_storages_and_blocks_with_range(range)?;
3456 self.insert_storage_history_index(indices)?;
3457 }
3458
3459 Ok(())
3460 }
3461}
3462
3463impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockExecutionWriter
3464 for DatabaseProvider<TX, N>
3465{
3466 fn take_block_and_execution_above(
3467 &self,
3468 block: BlockNumber,
3469 ) -> ProviderResult<Chain<Self::Primitives>> {
3470 let range = block + 1..=self.last_block_number()?;
3471
3472 self.unwind_trie_state_from(block + 1)?;
3473
3474 let execution_state = self.take_state_above(block)?;
3476
3477 let blocks = self.recovered_block_range(range)?;
3478
3479 self.remove_blocks_above(block)?;
3482
3483 self.update_pipeline_stages(block, true)?;
3485
3486 Ok(Chain::new(blocks, execution_state, BTreeMap::new()))
3487 }
3488
3489 fn remove_block_and_execution_above(&self, block: BlockNumber) -> ProviderResult<()> {
3490 self.unwind_trie_state_from(block + 1)?;
3491
3492 self.remove_state_above(block)?;
3494
3495 self.remove_blocks_above(block)?;
3498
3499 self.update_pipeline_stages(block, true)?;
3501
3502 Ok(())
3503 }
3504}
3505
3506impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockWriter
3507 for DatabaseProvider<TX, N>
3508{
3509 type Block = BlockTy<N>;
3510 type Receipt = ReceiptTy<N>;
3511
3512 fn insert_block(
3517 &self,
3518 block: &RecoveredBlock<Self::Block>,
3519 ) -> ProviderResult<StoredBlockBodyIndices> {
3520 let block_number = block.number();
3521
3522 let executed_block = ExecutedBlock::new(
3524 Arc::new(block.clone()),
3525 Arc::new(BlockExecutionOutput {
3526 result: BlockExecutionResult {
3527 receipts: Default::default(),
3528 requests: Default::default(),
3529 gas_used: 0,
3530 blob_gas_used: 0,
3531 },
3532 state: Default::default(),
3533 }),
3534 ComputedTrieData::default(),
3535 );
3536
3537 self.save_blocks(vec![executed_block], SaveBlocksMode::BlocksOnly)?;
3539
3540 self.block_body_indices(block_number)?
3542 .ok_or(ProviderError::BlockBodyIndicesNotFound(block_number))
3543 }
3544
3545 fn append_block_bodies(
3546 &self,
3547 bodies: Vec<(BlockNumber, Option<&BodyTy<N>>)>,
3548 ) -> ProviderResult<()> {
3549 let Some(from_block) = bodies.first().map(|(block, _)| *block) else { return Ok(()) };
3550
3551 let mut tx_writer =
3553 self.static_file_provider.get_writer(from_block, StaticFileSegment::Transactions)?;
3554
3555 let mut block_indices_cursor = self.tx.cursor_write::<tables::BlockBodyIndices>()?;
3556 let mut tx_block_cursor = self.tx.cursor_write::<tables::TransactionBlocks>()?;
3557
3558 let mut next_tx_num = tx_block_cursor.last()?.map(|(id, _)| id + 1).unwrap_or_default();
3560
3561 for (block_number, body) in &bodies {
3562 tx_writer.increment_block(*block_number)?;
3564
3565 let tx_count = body.as_ref().map(|b| b.transactions().len() as u64).unwrap_or_default();
3566 let block_indices = StoredBlockBodyIndices { first_tx_num: next_tx_num, tx_count };
3567
3568 let mut durations_recorder = metrics::DurationsRecorder::new(&self.metrics);
3569
3570 block_indices_cursor.append(*block_number, &block_indices)?;
3572
3573 durations_recorder.record_relative(metrics::Action::InsertBlockBodyIndices);
3574
3575 let Some(body) = body else { continue };
3576
3577 if !body.transactions().is_empty() {
3579 tx_block_cursor.append(block_indices.last_tx_num(), block_number)?;
3580 durations_recorder.record_relative(metrics::Action::InsertTransactionBlocks);
3581 }
3582
3583 for transaction in body.transactions() {
3585 tx_writer.append_transaction(next_tx_num, transaction)?;
3586
3587 next_tx_num += 1;
3589 }
3590 }
3591
3592 self.storage.writer().write_block_bodies(self, bodies)?;
3593
3594 Ok(())
3595 }
3596
3597 fn remove_blocks_above(&self, block: BlockNumber) -> ProviderResult<()> {
3598 let last_block_number = self.last_block_number()?;
3599 for hash in self.canonical_hashes_range(block + 1, last_block_number + 1)? {
3601 self.tx.delete::<tables::HeaderNumbers>(hash, None)?;
3602 }
3603
3604 let highest_static_file_block = self
3606 .static_file_provider()
3607 .get_highest_static_file_block(StaticFileSegment::Headers)
3608 .expect("todo: error handling, headers should exist");
3609
3610 debug!(target: "providers::db", ?block, "Removing static file blocks above block_number");
3616 self.static_file_provider()
3617 .get_writer(block, StaticFileSegment::Headers)?
3618 .prune_headers(highest_static_file_block.saturating_sub(block))?;
3619
3620 let unwind_tx_from = self
3622 .block_body_indices(block)?
3623 .map(|b| b.next_tx_num())
3624 .ok_or(ProviderError::BlockBodyIndicesNotFound(block))?;
3625
3626 let unwind_tx_to = self
3628 .tx
3629 .cursor_read::<tables::BlockBodyIndices>()?
3630 .last()?
3631 .ok_or(ProviderError::BlockBodyIndicesNotFound(block))?
3633 .1
3634 .last_tx_num();
3635
3636 if unwind_tx_from <= unwind_tx_to {
3637 let hashes = self.transaction_hashes_by_range(unwind_tx_from..(unwind_tx_to + 1))?;
3638 self.with_rocksdb_batch(|batch| {
3639 let mut writer = EitherWriter::new_transaction_hash_numbers(self, batch)?;
3640 for (hash, _) in hashes {
3641 writer.delete_transaction_hash_number(hash)?;
3642 }
3643 Ok(((), writer.into_raw_rocksdb_batch()))
3644 })?;
3645 }
3646
3647 if self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()) {
3650 EitherWriter::new_senders(self, last_block_number)?
3651 .prune_senders(unwind_tx_from, block)?;
3652 }
3653
3654 self.remove_bodies_above(block)?;
3655
3656 Ok(())
3657 }
3658
3659 fn remove_bodies_above(&self, block: BlockNumber) -> ProviderResult<()> {
3660 self.storage.writer().remove_block_bodies_above(self, block)?;
3661
3662 let unwind_tx_from = self
3664 .block_body_indices(block)?
3665 .map(|b| b.next_tx_num())
3666 .ok_or(ProviderError::BlockBodyIndicesNotFound(block))?;
3667
3668 self.remove::<tables::BlockBodyIndices>(block + 1..)?;
3669 self.remove::<tables::TransactionBlocks>(unwind_tx_from..)?;
3670
3671 let static_file_tx_num =
3672 self.static_file_provider.get_highest_static_file_tx(StaticFileSegment::Transactions);
3673
3674 let to_delete = static_file_tx_num
3675 .map(|static_tx| (static_tx + 1).saturating_sub(unwind_tx_from))
3676 .unwrap_or_default();
3677
3678 self.static_file_provider
3679 .latest_writer(StaticFileSegment::Transactions)?
3680 .prune_transactions(to_delete, block)?;
3681
3682 Ok(())
3683 }
3684
3685 fn append_blocks_with_state(
3694 &self,
3695 blocks: Vec<RecoveredBlock<Self::Block>>,
3696 execution_outcome: &ExecutionOutcome<Self::Receipt>,
3697 hashed_state: HashedPostStateSorted,
3698 ) -> ProviderResult<()> {
3699 if blocks.is_empty() {
3700 debug!(target: "providers::db", "Attempted to append empty block range");
3701 return Ok(())
3702 }
3703
3704 let first_number = blocks[0].number();
3707
3708 let last_block_number = blocks[blocks.len() - 1].number();
3711
3712 let mut durations_recorder = metrics::DurationsRecorder::new(&self.metrics);
3713
3714 let (account_transitions, storage_transitions) = {
3719 let mut account_transitions: BTreeMap<Address, Vec<u64>> = BTreeMap::new();
3720 let mut storage_transitions: BTreeMap<(Address, B256), Vec<u64>> = BTreeMap::new();
3721 for (block_idx, block_reverts) in execution_outcome.bundle.reverts.iter().enumerate() {
3722 let block_number = first_number + block_idx as u64;
3723 for (address, account_revert) in block_reverts {
3724 account_transitions.entry(*address).or_default().push(block_number);
3725 for storage_key in account_revert.storage.keys() {
3726 let key = B256::from(storage_key.to_be_bytes());
3727 storage_transitions.entry((*address, key)).or_default().push(block_number);
3728 }
3729 }
3730 }
3731 (account_transitions, storage_transitions)
3732 };
3733
3734 for block in blocks {
3736 self.insert_block(&block)?;
3737 durations_recorder.record_relative(metrics::Action::InsertBlock);
3738 }
3739
3740 self.write_state(execution_outcome, OriginalValuesKnown::No, StateWriteConfig::default())?;
3741 durations_recorder.record_relative(metrics::Action::InsertState);
3742
3743 self.write_hashed_state(&hashed_state)?;
3745 durations_recorder.record_relative(metrics::Action::InsertHashes);
3746
3747 let storage_settings = self.cached_storage_settings();
3752 if storage_settings.storage_v2 {
3753 self.with_rocksdb_batch(|mut batch| {
3754 for (address, blocks) in account_transitions {
3755 batch.append_account_history_shard(address, blocks)?;
3756 }
3757 Ok(((), Some(batch.into_inner())))
3758 })?;
3759 } else {
3760 self.insert_account_history_index(account_transitions)?;
3761 }
3762 if storage_settings.storage_v2 {
3763 self.with_rocksdb_batch(|mut batch| {
3764 for ((address, key), blocks) in storage_transitions {
3765 batch.append_storage_history_shard(address, key, blocks)?;
3766 }
3767 Ok(((), Some(batch.into_inner())))
3768 })?;
3769 } else {
3770 self.insert_storage_history_index(storage_transitions)?;
3771 }
3772 durations_recorder.record_relative(metrics::Action::InsertHistoryIndices);
3773
3774 self.update_pipeline_stages(last_block_number, false)?;
3776 durations_recorder.record_relative(metrics::Action::UpdatePipelineStages);
3777
3778 debug!(target: "providers::db", range = ?first_number..=last_block_number, actions = ?durations_recorder.actions, "Appended blocks");
3779
3780 Ok(())
3781 }
3782}
3783
3784impl<TX: DbTx + 'static, N: NodeTypes> PruneCheckpointReader for DatabaseProvider<TX, N> {
3785 fn get_prune_checkpoint(
3786 &self,
3787 segment: PruneSegment,
3788 ) -> ProviderResult<Option<PruneCheckpoint>> {
3789 Ok(self.tx.get::<tables::PruneCheckpoints>(segment)?)
3790 }
3791
3792 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
3793 Ok(PruneSegment::variants()
3794 .filter_map(|segment| {
3795 self.tx
3796 .get::<tables::PruneCheckpoints>(segment)
3797 .transpose()
3798 .map(|chk| chk.map(|chk| (segment, chk)))
3799 })
3800 .collect::<Result<_, _>>()?)
3801 }
3802}
3803
3804impl<TX: DbTxMut, N: NodeTypes> PruneCheckpointWriter for DatabaseProvider<TX, N> {
3805 fn save_prune_checkpoint(
3806 &self,
3807 segment: PruneSegment,
3808 checkpoint: PruneCheckpoint,
3809 ) -> ProviderResult<()> {
3810 Ok(self.tx.put::<tables::PruneCheckpoints>(segment, checkpoint)?)
3811 }
3812}
3813
3814impl<TX: DbTx + 'static, N: NodeTypesForProvider> StatsReader for DatabaseProvider<TX, N> {
3815 fn count_entries<T: Table>(&self) -> ProviderResult<usize> {
3816 let db_entries = self.tx.entries::<T>()?;
3817 let static_file_entries = match self.static_file_provider.count_entries::<T>() {
3818 Ok(entries) => entries,
3819 Err(ProviderError::UnsupportedProvider) => 0,
3820 Err(err) => return Err(err),
3821 };
3822
3823 Ok(db_entries + static_file_entries)
3824 }
3825}
3826
3827impl<TX: DbTx + 'static, N: NodeTypes> ChainStateBlockReader for DatabaseProvider<TX, N> {
3828 fn last_finalized_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
3829 let mut finalized_blocks = self
3830 .tx
3831 .cursor_read::<tables::ChainState>()?
3832 .walk(Some(tables::ChainStateKey::LastFinalizedBlock))?
3833 .take(1)
3834 .collect::<Result<BTreeMap<tables::ChainStateKey, BlockNumber>, _>>()?;
3835
3836 let last_finalized_block_number = finalized_blocks.pop_first().map(|pair| pair.1);
3837 Ok(last_finalized_block_number)
3838 }
3839
3840 fn last_safe_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
3841 let mut finalized_blocks = self
3842 .tx
3843 .cursor_read::<tables::ChainState>()?
3844 .walk(Some(tables::ChainStateKey::LastSafeBlock))?
3845 .take(1)
3846 .collect::<Result<BTreeMap<tables::ChainStateKey, BlockNumber>, _>>()?;
3847
3848 let last_finalized_block_number = finalized_blocks.pop_first().map(|pair| pair.1);
3849 Ok(last_finalized_block_number)
3850 }
3851}
3852
3853impl<TX: DbTxMut, N: NodeTypes> ChainStateBlockWriter for DatabaseProvider<TX, N> {
3854 fn save_finalized_block_number(&self, block_number: BlockNumber) -> ProviderResult<()> {
3855 Ok(self
3856 .tx
3857 .put::<tables::ChainState>(tables::ChainStateKey::LastFinalizedBlock, block_number)?)
3858 }
3859
3860 fn save_safe_block_number(&self, block_number: BlockNumber) -> ProviderResult<()> {
3861 Ok(self.tx.put::<tables::ChainState>(tables::ChainStateKey::LastSafeBlock, block_number)?)
3862 }
3863}
3864
3865impl<TX: DbTx + 'static, N: NodeTypes + 'static> DBProvider for DatabaseProvider<TX, N> {
3866 type Tx = TX;
3867
3868 fn tx_ref(&self) -> &Self::Tx {
3869 &self.tx
3870 }
3871
3872 fn tx_mut(&mut self) -> &mut Self::Tx {
3873 &mut self.tx
3874 }
3875
3876 fn into_tx(self) -> Self::Tx {
3877 self.tx
3878 }
3879
3880 fn prune_modes_ref(&self) -> &PruneModes {
3881 self.prune_modes_ref()
3882 }
3883
3884 #[instrument(
3886 name = "DatabaseProvider::commit",
3887 level = "debug",
3888 target = "providers::db",
3889 skip_all
3890 )]
3891 fn commit(self) -> ProviderResult<()> {
3892 if self.static_file_provider.has_unwind_queued() || self.commit_order.is_unwind() {
3893 self.commit_unwind()?;
3894 } else {
3895 let mut timings = metrics::CommitTimings::default();
3897
3898 let start = Instant::now();
3899 self.static_file_provider.finalize()?;
3900 timings.sf = start.elapsed();
3901
3902 let start = Instant::now();
3903 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
3904 for batch in batches {
3905 self.rocksdb_provider.commit_batch(batch)?;
3906 }
3907 timings.rocksdb = start.elapsed();
3908
3909 let start = Instant::now();
3910 self.tx.commit()?;
3911 timings.mdbx = start.elapsed();
3912
3913 self.metrics.record_commit(&timings);
3914 }
3915
3916 Ok(())
3917 }
3918}
3919
3920impl<TX: DbTx, N: NodeTypes> MetadataProvider for DatabaseProvider<TX, N> {
3921 fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>> {
3922 self.tx.get::<tables::Metadata>(key.to_string()).map_err(Into::into)
3923 }
3924}
3925
3926impl<TX: DbTxMut, N: NodeTypes> MetadataWriter for DatabaseProvider<TX, N> {
3927 fn write_metadata(&self, key: &str, value: Vec<u8>) -> ProviderResult<()> {
3928 self.tx.put::<tables::Metadata>(key.to_string(), value).map_err(Into::into)
3929 }
3930}
3931
3932impl<TX: Send, N: NodeTypes> StorageSettingsCache for DatabaseProvider<TX, N> {
3933 fn cached_storage_settings(&self) -> StorageSettings {
3934 *self.storage_settings.read()
3935 }
3936
3937 fn set_storage_settings_cache(&self, settings: StorageSettings) {
3938 *self.storage_settings.write() = settings;
3939 }
3940}
3941
3942impl<TX: Send, N: NodeTypes> StoragePath for DatabaseProvider<TX, N> {
3943 fn storage_path(&self) -> PathBuf {
3944 self.db_path.clone()
3945 }
3946}
3947
3948#[cfg(test)]
3949mod tests {
3950 use super::*;
3951 use crate::{
3952 test_utils::{blocks::BlockchainTestData, create_test_provider_factory},
3953 BlockWriter,
3954 };
3955 use alloy_consensus::Header;
3956 use alloy_primitives::{
3957 map::{AddressMap, B256Map},
3958 U256,
3959 };
3960 use reth_chain_state::ExecutedBlock;
3961 use reth_db_api::models::StorageSettings;
3962 use reth_ethereum_primitives::Receipt;
3963 use reth_execution_types::{AccountRevertInit, BlockExecutionOutput, BlockExecutionResult};
3964 use reth_primitives_traits::SealedBlock;
3965 use reth_storage_api::MetadataWriter;
3966 use reth_testing_utils::generators::{self, random_block, BlockParams};
3967 use reth_trie::{
3968 HashedPostState, KeccakKeyHasher, Nibbles, SortedTrieData, StoredNibbles,
3969 StoredNibblesSubKey,
3970 };
3971 use revm::{database::BundleState, state::AccountInfo};
3972 use std::{sync::mpsc, time::Duration};
3973
3974 #[test]
3975 fn test_receipts_by_block_range_empty_range() {
3976 let factory = create_test_provider_factory();
3977 let provider = factory.provider().unwrap();
3978
3979 let start = 10u64;
3981 let end = 9u64;
3982 let result = provider.receipts_by_block_range(start..=end).unwrap();
3983 assert_eq!(result, Vec::<Vec<reth_ethereum_primitives::Receipt>>::new());
3984 }
3985
3986 #[test]
3987 fn unwind_commit_waits_for_pre_commit_readers() {
3988 let factory = create_test_provider_factory();
3989
3990 let reader = factory.provider().unwrap();
3991 let provider_rw = factory.unwind_provider_rw().unwrap();
3992 provider_rw.write_metadata("unwind-wait-test", vec![1]).unwrap();
3993 let (done_tx, done_rx) = mpsc::channel();
3994
3995 let handle = std::thread::spawn(move || {
3996 let result = provider_rw.commit();
3997 done_tx.send(result).unwrap();
3998 });
3999
4000 assert!(
4001 done_rx.recv_timeout(Duration::from_millis(50)).is_err(),
4002 "unwind commit should wait while an older read transaction is still open"
4003 );
4004
4005 drop(reader);
4006
4007 done_rx.recv_timeout(Duration::from_secs(1)).unwrap().unwrap();
4008 handle.join().unwrap();
4009 }
4010
4011 #[test]
4012 fn test_receipts_by_block_range_nonexistent_blocks() {
4013 let factory = create_test_provider_factory();
4014 let provider = factory.provider().unwrap();
4015
4016 let result = provider.receipts_by_block_range(10..=12).unwrap();
4018 assert_eq!(result, vec![vec![], vec![], vec![]]);
4019 }
4020
4021 #[test]
4022 fn test_receipts_by_block_range_single_block() {
4023 let factory = create_test_provider_factory();
4024 let data = BlockchainTestData::default();
4025
4026 let provider_rw = factory.provider_rw().unwrap();
4027 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4028 provider_rw
4029 .write_state(
4030 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4031 crate::OriginalValuesKnown::No,
4032 StateWriteConfig::default(),
4033 )
4034 .unwrap();
4035 provider_rw.insert_block(&data.blocks[0].0).unwrap();
4036 provider_rw
4037 .write_state(
4038 &data.blocks[0].1,
4039 crate::OriginalValuesKnown::No,
4040 StateWriteConfig::default(),
4041 )
4042 .unwrap();
4043 provider_rw.commit().unwrap();
4044
4045 let provider = factory.provider().unwrap();
4046 let result = provider.receipts_by_block_range(1..=1).unwrap();
4047
4048 assert_eq!(result.len(), 1);
4050 assert_eq!(result[0].len(), 1);
4051 assert_eq!(result[0][0], data.blocks[0].1.receipts()[0][0]);
4052 }
4053
4054 #[test]
4055 fn test_receipts_by_block_range_multiple_blocks() {
4056 let factory = create_test_provider_factory();
4057 let data = BlockchainTestData::default();
4058
4059 let provider_rw = factory.provider_rw().unwrap();
4060 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4061 provider_rw
4062 .write_state(
4063 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4064 crate::OriginalValuesKnown::No,
4065 StateWriteConfig::default(),
4066 )
4067 .unwrap();
4068 for i in 0..3 {
4069 provider_rw.insert_block(&data.blocks[i].0).unwrap();
4070 provider_rw
4071 .write_state(
4072 &data.blocks[i].1,
4073 crate::OriginalValuesKnown::No,
4074 StateWriteConfig::default(),
4075 )
4076 .unwrap();
4077 }
4078 provider_rw.commit().unwrap();
4079
4080 let provider = factory.provider().unwrap();
4081 let result = provider.receipts_by_block_range(1..=3).unwrap();
4082
4083 assert_eq!(result.len(), 3);
4085 for (i, block_receipts) in result.iter().enumerate() {
4086 assert_eq!(block_receipts.len(), 1);
4087 assert_eq!(block_receipts[0], data.blocks[i].1.receipts()[0][0]);
4088 }
4089 }
4090
4091 #[test]
4092 fn test_receipts_by_block_range_blocks_with_varying_tx_counts() {
4093 let factory = create_test_provider_factory();
4094 let data = BlockchainTestData::default();
4095
4096 let provider_rw = factory.provider_rw().unwrap();
4097 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4098 provider_rw
4099 .write_state(
4100 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4101 crate::OriginalValuesKnown::No,
4102 StateWriteConfig::default(),
4103 )
4104 .unwrap();
4105
4106 for i in 0..3 {
4108 provider_rw.insert_block(&data.blocks[i].0).unwrap();
4109 provider_rw
4110 .write_state(
4111 &data.blocks[i].1,
4112 crate::OriginalValuesKnown::No,
4113 StateWriteConfig::default(),
4114 )
4115 .unwrap();
4116 }
4117 provider_rw.commit().unwrap();
4118
4119 let provider = factory.provider().unwrap();
4120 let result = provider.receipts_by_block_range(1..=3).unwrap();
4121
4122 assert_eq!(result.len(), 3);
4124 for block_receipts in &result {
4125 assert_eq!(block_receipts.len(), 1);
4126 }
4127 }
4128
4129 #[test]
4130 fn test_receipts_by_block_range_partial_range() {
4131 let factory = create_test_provider_factory();
4132 let data = BlockchainTestData::default();
4133
4134 let provider_rw = factory.provider_rw().unwrap();
4135 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4136 provider_rw
4137 .write_state(
4138 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4139 crate::OriginalValuesKnown::No,
4140 StateWriteConfig::default(),
4141 )
4142 .unwrap();
4143 for i in 0..3 {
4144 provider_rw.insert_block(&data.blocks[i].0).unwrap();
4145 provider_rw
4146 .write_state(
4147 &data.blocks[i].1,
4148 crate::OriginalValuesKnown::No,
4149 StateWriteConfig::default(),
4150 )
4151 .unwrap();
4152 }
4153 provider_rw.commit().unwrap();
4154
4155 let provider = factory.provider().unwrap();
4156
4157 let result = provider.receipts_by_block_range(2..=5).unwrap();
4159 assert_eq!(result.len(), 4);
4160
4161 assert_eq!(result[0].len(), 1); assert_eq!(result[1].len(), 1); assert_eq!(result[2].len(), 0); assert_eq!(result[3].len(), 0); assert_eq!(result[0][0], data.blocks[1].1.receipts()[0][0]);
4168 assert_eq!(result[1][0], data.blocks[2].1.receipts()[0][0]);
4169 }
4170
4171 #[test]
4172 fn test_receipts_by_block_range_all_empty_blocks() {
4173 let factory = create_test_provider_factory();
4174 let mut rng = generators::rng();
4175
4176 let mut blocks = Vec::new();
4178 for i in 0..3 {
4179 let block =
4180 random_block(&mut rng, i, BlockParams { tx_count: Some(0), ..Default::default() });
4181 blocks.push(block);
4182 }
4183
4184 let provider_rw = factory.provider_rw().unwrap();
4185 for block in blocks {
4186 provider_rw.insert_block(&block.try_recover().unwrap()).unwrap();
4187 }
4188 provider_rw.commit().unwrap();
4189
4190 let provider = factory.provider().unwrap();
4191 let result = provider.receipts_by_block_range(1..=3).unwrap();
4192
4193 assert_eq!(result.len(), 3);
4194 for block_receipts in result {
4195 assert_eq!(block_receipts.len(), 0);
4196 }
4197 }
4198
4199 #[test]
4200 fn test_receipts_by_block_range_consistency_with_individual_calls() {
4201 let factory = create_test_provider_factory();
4202 let data = BlockchainTestData::default();
4203
4204 let provider_rw = factory.provider_rw().unwrap();
4205 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4206 provider_rw
4207 .write_state(
4208 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4209 crate::OriginalValuesKnown::No,
4210 StateWriteConfig::default(),
4211 )
4212 .unwrap();
4213 for i in 0..3 {
4214 provider_rw.insert_block(&data.blocks[i].0).unwrap();
4215 provider_rw
4216 .write_state(
4217 &data.blocks[i].1,
4218 crate::OriginalValuesKnown::No,
4219 StateWriteConfig::default(),
4220 )
4221 .unwrap();
4222 }
4223 provider_rw.commit().unwrap();
4224
4225 let provider = factory.provider().unwrap();
4226
4227 let range_result = provider.receipts_by_block_range(1..=3).unwrap();
4229
4230 let mut individual_results = Vec::new();
4232 for block_num in 1..=3 {
4233 let receipts =
4234 provider.receipts_by_block(block_num.into()).unwrap().unwrap_or_default();
4235 individual_results.push(receipts);
4236 }
4237
4238 assert_eq!(range_result, individual_results);
4239 }
4240
4241 #[test]
4242 fn test_receipts_by_block_returns_none_for_missing_unpruned_receipts() {
4243 let factory = create_test_provider_factory();
4244 let data = BlockchainTestData::default();
4245
4246 let provider_rw = factory.provider_rw().unwrap();
4247 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4248 provider_rw.insert_block(&data.blocks[0].0).unwrap();
4249 provider_rw.commit().unwrap();
4250
4251 let provider = factory.provider().unwrap();
4252 assert!(provider.receipts_by_block(1.into()).unwrap().is_none());
4253 }
4254
4255 #[test]
4256 fn test_write_trie_updates_sorted() {
4257 use reth_trie::{
4258 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
4259 BranchNodeCompact, StorageTrieEntry,
4260 };
4261
4262 let factory = create_test_provider_factory();
4263 let provider_rw = factory.provider_rw().unwrap();
4264
4265 {
4267 let tx = provider_rw.tx_ref();
4268 let mut cursor = tx.cursor_write::<tables::AccountsTrie>().unwrap();
4269
4270 let to_delete = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4]));
4272 cursor
4273 .upsert(
4274 to_delete,
4275 &BranchNodeCompact::new(
4276 0b1010_1010_1010_1010, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4280 None,
4281 ),
4282 )
4283 .unwrap();
4284
4285 let to_update = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2]));
4287 cursor
4288 .upsert(
4289 to_update,
4290 &BranchNodeCompact::new(
4291 0b0101_0101_0101_0101, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4295 None,
4296 ),
4297 )
4298 .unwrap();
4299 }
4300
4301 let storage_address1 = B256::from([1u8; 32]);
4303 let storage_address2 = B256::from([2u8; 32]);
4304 {
4305 let tx = provider_rw.tx_ref();
4306 let mut storage_cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
4307
4308 storage_cursor
4310 .upsert(
4311 storage_address1,
4312 &StorageTrieEntry {
4313 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0x2, 0x0])),
4314 node: BranchNodeCompact::new(
4315 0b0011_0011_0011_0011, 0b0000_0000_0000_0000,
4317 0b0000_0000_0000_0000,
4318 vec![],
4319 None,
4320 ),
4321 },
4322 )
4323 .unwrap();
4324
4325 storage_cursor
4327 .upsert(
4328 storage_address2,
4329 &StorageTrieEntry {
4330 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xa, 0xb])),
4331 node: BranchNodeCompact::new(
4332 0b1100_1100_1100_1100, 0b0000_0000_0000_0000,
4334 0b0000_0000_0000_0000,
4335 vec![],
4336 None,
4337 ),
4338 },
4339 )
4340 .unwrap();
4341 storage_cursor
4342 .upsert(
4343 storage_address2,
4344 &StorageTrieEntry {
4345 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xc, 0xd])),
4346 node: BranchNodeCompact::new(
4347 0b0011_1100_0011_1100, 0b0000_0000_0000_0000,
4349 0b0000_0000_0000_0000,
4350 vec![],
4351 None,
4352 ),
4353 },
4354 )
4355 .unwrap();
4356 }
4357
4358 let account_nodes = vec![
4360 (
4361 Nibbles::from_nibbles([0x1, 0x2]),
4362 Some(BranchNodeCompact::new(
4363 0b1111_1111_1111_1111, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4367 None,
4368 )),
4369 ),
4370 (Nibbles::from_nibbles([0x3, 0x4]), None), (
4372 Nibbles::from_nibbles([0x5, 0x6]),
4373 Some(BranchNodeCompact::new(
4374 0b1111_1111_1111_1111, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4378 None,
4379 )),
4380 ),
4381 ];
4382
4383 let storage_trie1 = StorageTrieUpdatesSorted {
4385 is_deleted: false,
4386 storage_nodes: vec![
4387 (
4388 Nibbles::from_nibbles([0x1, 0x0]),
4389 Some(BranchNodeCompact::new(
4390 0b1111_0000_0000_0000, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4394 None,
4395 )),
4396 ),
4397 (Nibbles::from_nibbles([0x2, 0x0]), None), ],
4399 };
4400
4401 let storage_trie2 = StorageTrieUpdatesSorted {
4402 is_deleted: true, storage_nodes: vec![],
4404 };
4405
4406 let mut storage_tries = B256Map::default();
4407 storage_tries.insert(storage_address1, storage_trie1);
4408 storage_tries.insert(storage_address2, storage_trie2);
4409
4410 let trie_updates = TrieUpdatesSorted::new(account_nodes, storage_tries);
4411
4412 let num_entries = provider_rw.write_trie_updates_sorted(&trie_updates).unwrap();
4414
4415 assert_eq!(num_entries, 5);
4418
4419 let tx = provider_rw.tx_ref();
4421 let mut cursor = tx.cursor_read::<tables::AccountsTrie>().unwrap();
4422
4423 let nibbles1 = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2]));
4425 let entry1 = cursor.seek_exact(nibbles1).unwrap();
4426 assert!(entry1.is_some(), "Updated account node should exist");
4427 let expected_mask = reth_trie::TrieMask::new(0b1111_1111_1111_1111);
4428 assert_eq!(
4429 entry1.unwrap().1.state_mask,
4430 expected_mask,
4431 "Account node should have updated state_mask"
4432 );
4433
4434 let nibbles2 = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4]));
4436 let entry2 = cursor.seek_exact(nibbles2).unwrap();
4437 assert!(entry2.is_none(), "Deleted account node should not exist");
4438
4439 let nibbles3 = StoredNibbles(Nibbles::from_nibbles([0x5, 0x6]));
4441 let entry3 = cursor.seek_exact(nibbles3).unwrap();
4442 assert!(entry3.is_some(), "New account node should exist");
4443
4444 let mut storage_cursor = tx.cursor_dup_read::<tables::StoragesTrie>().unwrap();
4446
4447 let storage_entries1: Vec<_> = storage_cursor
4449 .walk_dup(Some(storage_address1), None)
4450 .unwrap()
4451 .collect::<Result<Vec<_>, _>>()
4452 .unwrap();
4453 assert_eq!(
4454 storage_entries1.len(),
4455 1,
4456 "Storage address1 should have 1 entry after deletion"
4457 );
4458 assert_eq!(
4459 storage_entries1[0].1.nibbles.0,
4460 Nibbles::from_nibbles([0x1, 0x0]),
4461 "Remaining entry should be [0x1, 0x0]"
4462 );
4463
4464 let storage_entries2: Vec<_> = storage_cursor
4466 .walk_dup(Some(storage_address2), None)
4467 .unwrap()
4468 .collect::<Result<Vec<_>, _>>()
4469 .unwrap();
4470 assert_eq!(storage_entries2.len(), 0, "Storage address2 should be empty after wipe");
4471
4472 provider_rw.commit().unwrap();
4473 }
4474
4475 #[test]
4476 fn test_prunable_receipts_logic() {
4477 let insert_blocks =
4478 |provider_rw: &DatabaseProviderRW<_, _>, tip_block: u64, tx_count: u8| {
4479 let mut rng = generators::rng();
4480 for block_num in 0..=tip_block {
4481 let block = random_block(
4482 &mut rng,
4483 block_num,
4484 BlockParams { tx_count: Some(tx_count), ..Default::default() },
4485 );
4486 provider_rw.insert_block(&block.try_recover().unwrap()).unwrap();
4487 }
4488 };
4489
4490 let write_receipts = |provider_rw: DatabaseProviderRW<_, _>, block: u64| {
4491 let outcome = ExecutionOutcome {
4492 first_block: block,
4493 receipts: vec![vec![Receipt {
4494 tx_type: Default::default(),
4495 success: true,
4496 cumulative_gas_used: block, logs: vec![],
4498 }]],
4499 ..Default::default()
4500 };
4501 provider_rw
4502 .write_state(&outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4503 .unwrap();
4504 provider_rw.commit().unwrap();
4505 };
4506
4507 {
4509 let factory = create_test_provider_factory();
4510 let storage_settings = StorageSettings::v1();
4511 factory.set_storage_settings_cache(storage_settings);
4512 let factory = factory.with_prune_modes(PruneModes {
4513 receipts: Some(PruneMode::Before(100)),
4514 ..Default::default()
4515 });
4516
4517 let tip_block = 200u64;
4518 let first_block = 1u64;
4519
4520 let provider_rw = factory.provider_rw().unwrap();
4522 insert_blocks(&provider_rw, tip_block, 1);
4523 provider_rw.commit().unwrap();
4524
4525 write_receipts(
4526 factory.provider_rw().unwrap().with_minimum_pruning_distance(100),
4527 first_block,
4528 );
4529 write_receipts(
4530 factory.provider_rw().unwrap().with_minimum_pruning_distance(100),
4531 tip_block - 1,
4532 );
4533
4534 let provider = factory.provider().unwrap();
4535
4536 assert!(provider.receipts_by_block(0.into()).unwrap().is_none());
4537 assert!(provider
4538 .receipts_by_block((tip_block - 1).into())
4539 .unwrap()
4540 .is_some_and(|r| r.len() == 1));
4541 }
4542
4543 {
4545 let factory = create_test_provider_factory();
4546 let storage_settings = StorageSettings::v2();
4547 factory.set_storage_settings_cache(storage_settings);
4548 let factory = factory.with_prune_modes(PruneModes {
4549 receipts: Some(PruneMode::Before(2)),
4550 ..Default::default()
4551 });
4552
4553 let tip_block = 200u64;
4554
4555 let provider_rw = factory.provider_rw().unwrap();
4557 insert_blocks(&provider_rw, tip_block, 1);
4558 provider_rw.commit().unwrap();
4559
4560 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 0);
4562 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 1);
4563
4564 assert!(factory
4565 .static_file_provider()
4566 .get_highest_static_file_tx(StaticFileSegment::Receipts)
4567 .is_none(),);
4568 assert!(factory
4569 .static_file_provider()
4570 .get_highest_static_file_block(StaticFileSegment::Receipts)
4571 .is_some_and(|b| b == 1),);
4572
4573 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 2);
4576 assert!(factory
4577 .static_file_provider()
4578 .get_highest_static_file_tx(StaticFileSegment::Receipts)
4579 .is_some_and(|num| num == 2),);
4580
4581 let factory = factory.with_prune_modes(PruneModes {
4585 receipts: Some(PruneMode::Before(100)),
4586 ..Default::default()
4587 });
4588 let provider_rw = factory.provider_rw().unwrap().with_minimum_pruning_distance(1);
4589 assert!(PruneMode::Distance(1).should_prune(3, tip_block));
4590 write_receipts(provider_rw, 3);
4591
4592 let provider = factory.provider().unwrap();
4597 assert!(EitherWriter::receipts_destination(&provider).is_static_file());
4598 for (num, has_receipt) in [(0, false), (1, false), (2, true), (3, true)] {
4599 let receipts = provider.receipts_by_block(num.into()).unwrap();
4600 if has_receipt {
4601 assert!(receipts.is_some_and(|r| r.len() == 1));
4602 } else {
4603 assert!(receipts.is_none());
4604 }
4605
4606 let receipt = provider.receipt(num).unwrap();
4607 if has_receipt {
4608 assert!(receipt.is_some_and(|r| r.cumulative_gas_used == num));
4609 } else {
4610 assert!(receipt.is_none());
4611 }
4612 }
4613 }
4614 }
4615
4616 #[test]
4617 fn test_try_into_history_rejects_unexecuted_blocks() {
4618 use reth_storage_api::TryIntoHistoricalStateProvider;
4619
4620 let factory = create_test_provider_factory();
4621
4622 let data = BlockchainTestData::default();
4624 let provider_rw = factory.provider_rw().unwrap();
4625 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4626 provider_rw
4627 .write_state(
4628 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4629 crate::OriginalValuesKnown::No,
4630 StateWriteConfig::default(),
4631 )
4632 .unwrap();
4633 provider_rw.commit().unwrap();
4634
4635 let provider = factory.provider().unwrap();
4637
4638 let result = provider.try_into_history_at_block(0);
4640 assert!(result.is_ok(), "Block 0 should be available");
4641
4642 let provider = factory.provider().unwrap();
4644 let result = provider.try_into_history_at_block(100);
4645
4646 match result {
4648 Err(ProviderError::BlockNotExecuted { requested: 100, .. }) => {}
4649 Err(e) => panic!("Expected BlockNotExecuted error, got: {e:?}"),
4650 Ok(_) => panic!("Expected error, got Ok"),
4651 }
4652 }
4653
4654 #[test]
4655 fn test_unwind_storage_hashing_with_hashed_state() {
4656 let factory = create_test_provider_factory();
4657 let storage_settings = StorageSettings::v2();
4658 factory.set_storage_settings_cache(storage_settings);
4659
4660 let address = Address::random();
4661 let hashed_address = keccak256(address);
4662
4663 let plain_slot = B256::random();
4664 let hashed_slot = keccak256(plain_slot);
4665
4666 let current_value = U256::from(100);
4667 let old_value = U256::from(42);
4668
4669 let provider_rw = factory.provider_rw().unwrap();
4670 provider_rw
4671 .tx
4672 .cursor_dup_write::<tables::HashedStorages>()
4673 .unwrap()
4674 .upsert(hashed_address, &StorageEntry { key: hashed_slot, value: current_value })
4675 .unwrap();
4676
4677 let changesets = vec![(
4678 BlockNumberAddress((1, address)),
4679 StorageEntry { key: plain_slot, value: old_value },
4680 )];
4681
4682 let result = provider_rw.unwind_storage_hashing(changesets.into_iter()).unwrap();
4683
4684 assert_eq!(result.len(), 1);
4685 assert!(result.contains_key(&hashed_address));
4686 assert!(result[&hashed_address].contains(&hashed_slot));
4687
4688 let mut cursor = provider_rw.tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
4689 let entry = cursor
4690 .seek_by_key_subkey(hashed_address, hashed_slot)
4691 .unwrap()
4692 .expect("entry should exist");
4693 assert_eq!(entry.key, hashed_slot);
4694 assert_eq!(entry.value, old_value);
4695 }
4696
4697 #[test]
4698 fn test_write_and_remove_state_roundtrip_legacy() {
4699 let factory = create_test_provider_factory();
4700 let storage_settings = StorageSettings::v1();
4701 assert!(!storage_settings.use_hashed_state());
4702 factory.set_storage_settings_cache(storage_settings);
4703
4704 let address = Address::with_last_byte(1);
4705 let hashed_address = keccak256(address);
4706 let slot = U256::from(5);
4707 let slot_key = B256::from(slot);
4708 let hashed_slot = keccak256(slot_key);
4709
4710 let mut rng = generators::rng();
4711 let block0 =
4712 random_block(&mut rng, 0, BlockParams { tx_count: Some(0), ..Default::default() });
4713 let block1 =
4714 random_block(&mut rng, 1, BlockParams { tx_count: Some(0), ..Default::default() });
4715
4716 {
4717 let provider_rw = factory.provider_rw().unwrap();
4718 provider_rw.insert_block(&block0.try_recover().unwrap()).unwrap();
4719 provider_rw.insert_block(&block1.try_recover().unwrap()).unwrap();
4720 provider_rw
4721 .tx
4722 .cursor_write::<tables::PlainAccountState>()
4723 .unwrap()
4724 .upsert(address, &Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None })
4725 .unwrap();
4726 provider_rw.commit().unwrap();
4727 }
4728
4729 let provider_rw = factory.provider_rw().unwrap();
4730
4731 let mut state_init: BundleStateInit = AddressMap::default();
4732 let mut storage_map: B256Map<(U256, U256)> = B256Map::default();
4733 storage_map.insert(slot_key, (U256::ZERO, U256::from(10)));
4734 state_init.insert(
4735 address,
4736 (
4737 Some(Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None }),
4738 Some(Account { nonce: 1, balance: U256::ZERO, bytecode_hash: None }),
4739 storage_map,
4740 ),
4741 );
4742
4743 let mut reverts_init: RevertsInit = HashMap::default();
4744 let mut block_reverts: AddressMap<AccountRevertInit> = AddressMap::default();
4745 block_reverts.insert(
4746 address,
4747 (
4748 Some(Some(Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None })),
4749 vec![StorageEntry { key: slot_key, value: U256::ZERO }],
4750 ),
4751 );
4752 reverts_init.insert(1, block_reverts);
4753
4754 let execution_outcome =
4755 ExecutionOutcome::new_init(state_init, reverts_init, [], vec![vec![]], 1, vec![]);
4756
4757 provider_rw
4758 .write_state(
4759 &execution_outcome,
4760 OriginalValuesKnown::Yes,
4761 StateWriteConfig {
4762 write_receipts: false,
4763 write_account_changesets: true,
4764 write_storage_changesets: true,
4765 },
4766 )
4767 .unwrap();
4768
4769 let hashed_state =
4770 execution_outcome.hash_state_slow::<reth_trie::KeccakKeyHasher>().into_sorted();
4771 provider_rw.write_hashed_state(&hashed_state).unwrap();
4772
4773 let account = provider_rw
4774 .tx
4775 .cursor_read::<tables::PlainAccountState>()
4776 .unwrap()
4777 .seek_exact(address)
4778 .unwrap()
4779 .unwrap()
4780 .1;
4781 assert_eq!(account.nonce, 1);
4782
4783 let storage_entry = provider_rw
4784 .tx
4785 .cursor_dup_read::<tables::PlainStorageState>()
4786 .unwrap()
4787 .seek_by_key_subkey(address, slot_key)
4788 .unwrap()
4789 .unwrap();
4790 assert_eq!(storage_entry.key, slot_key);
4791 assert_eq!(storage_entry.value, U256::from(10));
4792
4793 let hashed_entry = provider_rw
4794 .tx
4795 .cursor_dup_read::<tables::HashedStorages>()
4796 .unwrap()
4797 .seek_by_key_subkey(hashed_address, hashed_slot)
4798 .unwrap()
4799 .unwrap();
4800 assert_eq!(hashed_entry.key, hashed_slot);
4801 assert_eq!(hashed_entry.value, U256::from(10));
4802
4803 let account_cs_entries = provider_rw
4804 .tx
4805 .cursor_dup_read::<tables::AccountChangeSets>()
4806 .unwrap()
4807 .walk(Some(1))
4808 .unwrap()
4809 .collect::<Result<Vec<_>, _>>()
4810 .unwrap();
4811 assert!(!account_cs_entries.is_empty());
4812
4813 let storage_cs_entries = provider_rw
4814 .tx
4815 .cursor_read::<tables::StorageChangeSets>()
4816 .unwrap()
4817 .walk(Some(BlockNumberAddress((1, address))))
4818 .unwrap()
4819 .collect::<Result<Vec<_>, _>>()
4820 .unwrap();
4821 assert!(!storage_cs_entries.is_empty());
4822 assert_eq!(storage_cs_entries[0].1.key, slot_key);
4823
4824 provider_rw.remove_state_above(0).unwrap();
4825
4826 let restored_account = provider_rw
4827 .tx
4828 .cursor_read::<tables::PlainAccountState>()
4829 .unwrap()
4830 .seek_exact(address)
4831 .unwrap()
4832 .unwrap()
4833 .1;
4834 assert_eq!(restored_account.nonce, 0);
4835
4836 let storage_gone = provider_rw
4837 .tx
4838 .cursor_dup_read::<tables::PlainStorageState>()
4839 .unwrap()
4840 .seek_by_key_subkey(address, slot_key)
4841 .unwrap();
4842 assert!(storage_gone.is_none() || storage_gone.unwrap().key != slot_key);
4843
4844 let account_cs_after = provider_rw
4845 .tx
4846 .cursor_dup_read::<tables::AccountChangeSets>()
4847 .unwrap()
4848 .walk(Some(1))
4849 .unwrap()
4850 .collect::<Result<Vec<_>, _>>()
4851 .unwrap();
4852 assert!(account_cs_after.is_empty());
4853
4854 let storage_cs_after = provider_rw
4855 .tx
4856 .cursor_read::<tables::StorageChangeSets>()
4857 .unwrap()
4858 .walk(Some(BlockNumberAddress((1, address))))
4859 .unwrap()
4860 .collect::<Result<Vec<_>, _>>()
4861 .unwrap();
4862 assert!(storage_cs_after.is_empty());
4863 }
4864
4865 #[test]
4866 fn test_unwind_storage_hashing_legacy() {
4867 let factory = create_test_provider_factory();
4868 let storage_settings = StorageSettings::v1();
4869 assert!(!storage_settings.use_hashed_state());
4870 factory.set_storage_settings_cache(storage_settings);
4871
4872 let address = Address::random();
4873 let hashed_address = keccak256(address);
4874
4875 let plain_slot = B256::random();
4876 let hashed_slot = keccak256(plain_slot);
4877
4878 let current_value = U256::from(100);
4879 let old_value = U256::from(42);
4880
4881 let provider_rw = factory.provider_rw().unwrap();
4882 provider_rw
4883 .tx
4884 .cursor_dup_write::<tables::HashedStorages>()
4885 .unwrap()
4886 .upsert(hashed_address, &StorageEntry { key: hashed_slot, value: current_value })
4887 .unwrap();
4888
4889 let changesets = vec![(
4890 BlockNumberAddress((1, address)),
4891 StorageEntry { key: plain_slot, value: old_value },
4892 )];
4893
4894 let result = provider_rw.unwind_storage_hashing(changesets.into_iter()).unwrap();
4895
4896 assert_eq!(result.len(), 1);
4897 assert!(result.contains_key(&hashed_address));
4898 assert!(result[&hashed_address].contains(&hashed_slot));
4899
4900 let mut cursor = provider_rw.tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
4901 let entry = cursor
4902 .seek_by_key_subkey(hashed_address, hashed_slot)
4903 .unwrap()
4904 .expect("entry should exist");
4905 assert_eq!(entry.key, hashed_slot);
4906 assert_eq!(entry.value, old_value);
4907 }
4908
4909 #[test]
4910 fn test_write_state_and_historical_read_hashed() {
4911 use reth_storage_api::StateProvider;
4912 use reth_trie::{HashedPostState, KeccakKeyHasher};
4913 use revm::{database::BundleState, state::AccountInfo};
4914
4915 let factory = create_test_provider_factory();
4916 factory.set_storage_settings_cache(StorageSettings::v2());
4917
4918 let address = Address::with_last_byte(1);
4919 let slot = U256::from(5);
4920 let slot_key = B256::from(slot);
4921 let hashed_address = keccak256(address);
4922 let hashed_slot = keccak256(slot_key);
4923
4924 {
4925 let sf = factory.static_file_provider();
4926 let mut hw = sf.latest_writer(StaticFileSegment::Headers).unwrap();
4927 let h0 = alloy_consensus::Header { number: 0, ..Default::default() };
4928 hw.append_header(&h0, &B256::ZERO).unwrap();
4929 let h1 = alloy_consensus::Header { number: 1, ..Default::default() };
4930 hw.append_header(&h1, &B256::ZERO).unwrap();
4931 hw.commit().unwrap();
4932
4933 let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
4934 aw.append_account_changeset(vec![], 0).unwrap();
4935 aw.commit().unwrap();
4936
4937 let mut sw = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
4938 sw.append_storage_changeset(vec![], 0).unwrap();
4939 sw.commit().unwrap();
4940 }
4941
4942 let provider_rw = factory.provider_rw().unwrap();
4943
4944 let bundle = BundleState::builder(1..=1)
4945 .state_present_account_info(
4946 address,
4947 AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() },
4948 )
4949 .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
4950 .revert_account_info(1, address, Some(None))
4951 .revert_storage(1, address, vec![(slot, U256::ZERO)])
4952 .build();
4953
4954 let execution_outcome = ExecutionOutcome::new(bundle.clone(), vec![vec![]], 1, Vec::new());
4955
4956 provider_rw
4957 .tx
4958 .put::<tables::BlockBodyIndices>(
4959 1,
4960 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
4961 )
4962 .unwrap();
4963
4964 provider_rw
4965 .write_state(
4966 &execution_outcome,
4967 OriginalValuesKnown::Yes,
4968 StateWriteConfig {
4969 write_receipts: false,
4970 write_account_changesets: true,
4971 write_storage_changesets: true,
4972 },
4973 )
4974 .unwrap();
4975
4976 let hashed_state =
4977 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
4978 provider_rw.write_hashed_state(&hashed_state).unwrap();
4979
4980 let plain_storage_entries = provider_rw
4981 .tx
4982 .cursor_dup_read::<tables::PlainStorageState>()
4983 .unwrap()
4984 .walk(None)
4985 .unwrap()
4986 .collect::<Result<Vec<_>, _>>()
4987 .unwrap();
4988 assert!(plain_storage_entries.is_empty());
4989
4990 let hashed_entry = provider_rw
4991 .tx
4992 .cursor_dup_read::<tables::HashedStorages>()
4993 .unwrap()
4994 .seek_by_key_subkey(hashed_address, hashed_slot)
4995 .unwrap()
4996 .unwrap();
4997 assert_eq!(hashed_entry.key, hashed_slot);
4998 assert_eq!(hashed_entry.value, U256::from(10));
4999
5000 provider_rw.static_file_provider().commit().unwrap();
5001
5002 let sf = factory.static_file_provider();
5003 let storage_cs = sf.storage_changeset(1).unwrap();
5004 assert!(!storage_cs.is_empty());
5005 assert_eq!(storage_cs[0].1.key, slot_key);
5006
5007 let account_cs = sf.account_block_changeset(1).unwrap();
5008 assert!(!account_cs.is_empty());
5009 assert_eq!(account_cs[0].address, address);
5010
5011 let historical_value =
5012 HistoricalStateProviderRef::new(&*provider_rw, 0, ChangesetCache::new())
5013 .storage(address, slot_key)
5014 .unwrap();
5015 assert_eq!(historical_value, None);
5016 }
5017
5018 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5019 enum StorageMode {
5020 V1,
5021 V2,
5022 }
5023
5024 fn run_save_blocks_and_verify(mode: StorageMode) {
5025 use alloy_primitives::map::{FbBuildHasher, HashMap};
5026
5027 let factory = create_test_provider_factory();
5028
5029 match mode {
5030 StorageMode::V1 => factory.set_storage_settings_cache(StorageSettings::v1()),
5031 StorageMode::V2 => factory.set_storage_settings_cache(StorageSettings::v2()),
5032 }
5033
5034 let num_blocks = 3u64;
5035 let accounts_per_block = 5usize;
5036 let slots_per_account = 3usize;
5037
5038 let genesis = SealedBlock::<reth_ethereum_primitives::Block>::from_sealed_parts(
5039 SealedHeader::new(
5040 Header { number: 0, difficulty: U256::from(1), ..Default::default() },
5041 B256::ZERO,
5042 ),
5043 Default::default(),
5044 );
5045
5046 let genesis_executed = ExecutedBlock::new(
5047 Arc::new(genesis.try_recover().unwrap()),
5048 Arc::new(BlockExecutionOutput {
5049 result: BlockExecutionResult {
5050 receipts: vec![],
5051 requests: Default::default(),
5052 gas_used: 0,
5053 blob_gas_used: 0,
5054 },
5055 state: Default::default(),
5056 }),
5057 ComputedTrieData::default(),
5058 );
5059 let provider_rw = factory.provider_rw().unwrap();
5060 provider_rw.save_blocks(vec![genesis_executed], SaveBlocksMode::Full).unwrap();
5061 provider_rw.commit().unwrap();
5062
5063 let mut blocks: Vec<ExecutedBlock> = Vec::new();
5064 let mut parent_hash = B256::ZERO;
5065
5066 for block_num in 1..=num_blocks {
5067 let mut builder = BundleState::builder(block_num..=block_num);
5068
5069 for acct_idx in 0..accounts_per_block {
5070 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5071 let info = AccountInfo {
5072 nonce: block_num,
5073 balance: U256::from(block_num * 100 + acct_idx as u64),
5074 ..Default::default()
5075 };
5076
5077 let storage: HashMap<U256, (U256, U256), FbBuildHasher<32>> = (1..=
5078 slots_per_account as u64)
5079 .map(|s| {
5080 (
5081 U256::from(s + acct_idx as u64 * 100),
5082 (U256::ZERO, U256::from(block_num * 1000 + s)),
5083 )
5084 })
5085 .collect();
5086
5087 let revert_storage: Vec<(U256, U256)> = (1..=slots_per_account as u64)
5088 .map(|s| (U256::from(s + acct_idx as u64 * 100), U256::ZERO))
5089 .collect();
5090
5091 builder = builder
5092 .state_present_account_info(address, info)
5093 .revert_account_info(block_num, address, Some(None))
5094 .state_storage(address, storage)
5095 .revert_storage(block_num, address, revert_storage);
5096 }
5097
5098 let bundle = builder.build();
5099
5100 let hashed_state =
5101 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5102
5103 let header = Header {
5104 number: block_num,
5105 parent_hash,
5106 difficulty: U256::from(1),
5107 ..Default::default()
5108 };
5109 let block = SealedBlock::<reth_ethereum_primitives::Block>::seal_parts(
5110 header,
5111 Default::default(),
5112 );
5113 parent_hash = block.hash();
5114
5115 let executed = ExecutedBlock::new(
5116 Arc::new(block.try_recover().unwrap()),
5117 Arc::new(BlockExecutionOutput {
5118 result: BlockExecutionResult {
5119 receipts: vec![],
5120 requests: Default::default(),
5121 gas_used: 0,
5122 blob_gas_used: 0,
5123 },
5124 state: bundle,
5125 }),
5126 ComputedTrieData {
5127 sorted: SortedTrieData::new(Arc::new(hashed_state), Default::default()),
5128 ..Default::default()
5129 },
5130 );
5131 blocks.push(executed);
5132 }
5133
5134 let provider_rw = factory.provider_rw().unwrap();
5135 provider_rw.save_blocks(blocks, SaveBlocksMode::Full).unwrap();
5136 provider_rw.commit().unwrap();
5137
5138 let provider = factory.provider().unwrap();
5139
5140 for block_num in 1..=num_blocks {
5141 for acct_idx in 0..accounts_per_block {
5142 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5143 let hashed_address = keccak256(address);
5144
5145 let ha_entry = provider
5146 .tx_ref()
5147 .cursor_read::<tables::HashedAccounts>()
5148 .unwrap()
5149 .seek_exact(hashed_address)
5150 .unwrap();
5151 assert!(
5152 ha_entry.is_some(),
5153 "HashedAccounts missing for block {block_num} acct {acct_idx}"
5154 );
5155
5156 for s in 1..=slots_per_account as u64 {
5157 let slot = U256::from(s + acct_idx as u64 * 100);
5158 let slot_key = B256::from(slot);
5159 let hashed_slot = keccak256(slot_key);
5160
5161 let hs_entry = provider
5162 .tx_ref()
5163 .cursor_dup_read::<tables::HashedStorages>()
5164 .unwrap()
5165 .seek_by_key_subkey(hashed_address, hashed_slot)
5166 .unwrap();
5167 assert!(
5168 hs_entry.is_some(),
5169 "HashedStorages missing for block {block_num} acct {acct_idx} slot {s}"
5170 );
5171 let entry = hs_entry.unwrap();
5172 assert_eq!(entry.key, hashed_slot);
5173 assert_eq!(entry.value, U256::from(block_num * 1000 + s));
5174 }
5175 }
5176 }
5177
5178 for block_num in 1..=num_blocks {
5179 let header = provider.header_by_number(block_num).unwrap();
5180 assert!(header.is_some(), "Header missing for block {block_num}");
5181
5182 let indices = provider.block_body_indices(block_num).unwrap();
5183 assert!(indices.is_some(), "BlockBodyIndices missing for block {block_num}");
5184 }
5185
5186 let plain_accounts = provider.tx_ref().entries::<tables::PlainAccountState>().unwrap();
5187 let plain_storage = provider.tx_ref().entries::<tables::PlainStorageState>().unwrap();
5188
5189 if mode == StorageMode::V2 {
5190 assert_eq!(plain_accounts, 0, "v2: PlainAccountState should be empty");
5191 assert_eq!(plain_storage, 0, "v2: PlainStorageState should be empty");
5192
5193 let mdbx_account_cs = provider.tx_ref().entries::<tables::AccountChangeSets>().unwrap();
5194 assert_eq!(mdbx_account_cs, 0, "v2: AccountChangeSets in MDBX should be empty");
5195
5196 let mdbx_storage_cs = provider.tx_ref().entries::<tables::StorageChangeSets>().unwrap();
5197 assert_eq!(mdbx_storage_cs, 0, "v2: StorageChangeSets in MDBX should be empty");
5198
5199 provider.static_file_provider().commit().unwrap();
5200 let sf = factory.static_file_provider();
5201
5202 for block_num in 1..=num_blocks {
5203 let account_cs = sf.account_block_changeset(block_num).unwrap();
5204 assert!(
5205 !account_cs.is_empty(),
5206 "v2: static file AccountChangeSets should exist for block {block_num}"
5207 );
5208
5209 let storage_cs = sf.storage_changeset(block_num).unwrap();
5210 assert!(
5211 !storage_cs.is_empty(),
5212 "v2: static file StorageChangeSets should exist for block {block_num}"
5213 );
5214
5215 for (_, entry) in &storage_cs {
5216 assert!(
5217 entry.key != keccak256(entry.key),
5218 "v2: static file storage changeset should have plain slot keys"
5219 );
5220 }
5221 }
5222
5223 let rocksdb = factory.rocksdb_provider();
5224 for block_num in 1..=num_blocks {
5225 for acct_idx in 0..accounts_per_block {
5226 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5227 let shards = rocksdb.account_history_shards(address).unwrap();
5228 assert!(
5229 !shards.is_empty(),
5230 "v2: RocksDB AccountsHistory missing for block {block_num} acct {acct_idx}"
5231 );
5232
5233 for s in 1..=slots_per_account as u64 {
5234 let slot = U256::from(s + acct_idx as u64 * 100);
5235 let slot_key = B256::from(slot);
5236 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5237 assert!(
5238 !shards.is_empty(),
5239 "v2: RocksDB StoragesHistory missing for block {block_num} acct {acct_idx} slot {s}"
5240 );
5241 }
5242 }
5243 }
5244 } else {
5245 assert!(plain_accounts > 0, "v1: PlainAccountState should not be empty");
5246 assert!(plain_storage > 0, "v1: PlainStorageState should not be empty");
5247
5248 let mdbx_account_cs = provider.tx_ref().entries::<tables::AccountChangeSets>().unwrap();
5249 assert!(mdbx_account_cs > 0, "v1: AccountChangeSets in MDBX should not be empty");
5250
5251 let mdbx_storage_cs = provider.tx_ref().entries::<tables::StorageChangeSets>().unwrap();
5252 assert!(mdbx_storage_cs > 0, "v1: StorageChangeSets in MDBX should not be empty");
5253
5254 for block_num in 1..=num_blocks {
5255 let storage_entries: Vec<_> = provider
5256 .tx_ref()
5257 .cursor_dup_read::<tables::StorageChangeSets>()
5258 .unwrap()
5259 .walk_range(BlockNumberAddress::range(block_num..=block_num))
5260 .unwrap()
5261 .collect::<Result<Vec<_>, _>>()
5262 .unwrap();
5263 assert!(
5264 !storage_entries.is_empty(),
5265 "v1: MDBX StorageChangeSets should have entries for block {block_num}"
5266 );
5267
5268 for (_, entry) in &storage_entries {
5269 let slot_key = B256::from(entry.key);
5270 assert!(
5271 slot_key != keccak256(slot_key),
5272 "v1: storage changeset keys should be plain (not hashed)"
5273 );
5274 }
5275 }
5276
5277 let mdbx_account_history =
5278 provider.tx_ref().entries::<tables::AccountsHistory>().unwrap();
5279 assert!(mdbx_account_history > 0, "v1: AccountsHistory in MDBX should not be empty");
5280
5281 let mdbx_storage_history =
5282 provider.tx_ref().entries::<tables::StoragesHistory>().unwrap();
5283 assert!(mdbx_storage_history > 0, "v1: StoragesHistory in MDBX should not be empty");
5284 }
5285 }
5286
5287 #[test]
5288 fn test_save_blocks_v1_table_assertions() {
5289 run_save_blocks_and_verify(StorageMode::V1);
5290 }
5291
5292 #[test]
5293 fn test_save_blocks_v2_table_assertions() {
5294 run_save_blocks_and_verify(StorageMode::V2);
5295 }
5296
5297 #[test]
5298 fn test_write_and_remove_state_roundtrip_v2() {
5299 let factory = create_test_provider_factory();
5300 let storage_settings = StorageSettings::v2();
5301 assert!(storage_settings.use_hashed_state());
5302 factory.set_storage_settings_cache(storage_settings);
5303
5304 let address = Address::with_last_byte(1);
5305 let hashed_address = keccak256(address);
5306 let slot = U256::from(5);
5307 let slot_key = B256::from(slot);
5308 let hashed_slot = keccak256(slot_key);
5309
5310 {
5311 let sf = factory.static_file_provider();
5312 let mut hw = sf.latest_writer(StaticFileSegment::Headers).unwrap();
5313 let h0 = alloy_consensus::Header { number: 0, ..Default::default() };
5314 hw.append_header(&h0, &B256::ZERO).unwrap();
5315 let h1 = alloy_consensus::Header { number: 1, ..Default::default() };
5316 hw.append_header(&h1, &B256::ZERO).unwrap();
5317 hw.commit().unwrap();
5318
5319 let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
5320 aw.append_account_changeset(vec![], 0).unwrap();
5321 aw.commit().unwrap();
5322
5323 let mut sw = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
5324 sw.append_storage_changeset(vec![], 0).unwrap();
5325 sw.commit().unwrap();
5326 }
5327
5328 {
5329 let provider_rw = factory.provider_rw().unwrap();
5330 provider_rw
5331 .tx
5332 .put::<tables::BlockBodyIndices>(
5333 0,
5334 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5335 )
5336 .unwrap();
5337 provider_rw
5338 .tx
5339 .put::<tables::BlockBodyIndices>(
5340 1,
5341 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5342 )
5343 .unwrap();
5344 provider_rw
5345 .tx
5346 .cursor_write::<tables::HashedAccounts>()
5347 .unwrap()
5348 .upsert(
5349 hashed_address,
5350 &Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None },
5351 )
5352 .unwrap();
5353 provider_rw.commit().unwrap();
5354 }
5355
5356 let provider_rw = factory.provider_rw().unwrap();
5357
5358 let bundle = BundleState::builder(1..=1)
5359 .state_present_account_info(
5360 address,
5361 AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() },
5362 )
5363 .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
5364 .revert_account_info(1, address, Some(None))
5365 .revert_storage(1, address, vec![(slot, U256::ZERO)])
5366 .build();
5367
5368 let execution_outcome = ExecutionOutcome::new(bundle.clone(), vec![vec![]], 1, Vec::new());
5369
5370 provider_rw
5371 .write_state(
5372 &execution_outcome,
5373 OriginalValuesKnown::Yes,
5374 StateWriteConfig {
5375 write_receipts: false,
5376 write_account_changesets: true,
5377 write_storage_changesets: true,
5378 },
5379 )
5380 .unwrap();
5381
5382 let hashed_state =
5383 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5384 provider_rw.write_hashed_state(&hashed_state).unwrap();
5385
5386 let hashed_account = provider_rw
5387 .tx
5388 .cursor_read::<tables::HashedAccounts>()
5389 .unwrap()
5390 .seek_exact(hashed_address)
5391 .unwrap()
5392 .unwrap()
5393 .1;
5394 assert_eq!(hashed_account.nonce, 1);
5395
5396 let hashed_entry = provider_rw
5397 .tx
5398 .cursor_dup_read::<tables::HashedStorages>()
5399 .unwrap()
5400 .seek_by_key_subkey(hashed_address, hashed_slot)
5401 .unwrap()
5402 .unwrap();
5403 assert_eq!(hashed_entry.key, hashed_slot);
5404 assert_eq!(hashed_entry.value, U256::from(10));
5405
5406 let plain_accounts = provider_rw.tx.entries::<tables::PlainAccountState>().unwrap();
5407 assert_eq!(plain_accounts, 0, "v2: PlainAccountState should be empty");
5408
5409 let plain_storage = provider_rw.tx.entries::<tables::PlainStorageState>().unwrap();
5410 assert_eq!(plain_storage, 0, "v2: PlainStorageState should be empty");
5411
5412 provider_rw.static_file_provider().commit().unwrap();
5413
5414 let sf = factory.static_file_provider();
5415 let storage_cs = sf.storage_changeset(1).unwrap();
5416 assert!(!storage_cs.is_empty(), "v2: storage changesets should be in static files");
5417 assert_eq!(storage_cs[0].1.key, slot_key, "v2: changeset key should be plain");
5418
5419 provider_rw.remove_state_above(0).unwrap();
5420
5421 let restored_account = provider_rw
5422 .tx
5423 .cursor_read::<tables::HashedAccounts>()
5424 .unwrap()
5425 .seek_exact(hashed_address)
5426 .unwrap();
5427 assert!(
5428 restored_account.is_none(),
5429 "v2: account should be removed (didn't exist before block 1)"
5430 );
5431
5432 let storage_gone = provider_rw
5433 .tx
5434 .cursor_dup_read::<tables::HashedStorages>()
5435 .unwrap()
5436 .seek_by_key_subkey(hashed_address, hashed_slot)
5437 .unwrap();
5438 assert!(
5439 storage_gone.is_none() || storage_gone.unwrap().key != hashed_slot,
5440 "v2: storage should be reverted (removed or different key)"
5441 );
5442
5443 let mdbx_storage_cs = provider_rw.tx.entries::<tables::StorageChangeSets>().unwrap();
5444 assert_eq!(mdbx_storage_cs, 0, "v2: MDBX StorageChangeSets should remain empty");
5445
5446 let mdbx_account_cs = provider_rw.tx.entries::<tables::AccountChangeSets>().unwrap();
5447 assert_eq!(mdbx_account_cs, 0, "v2: MDBX AccountChangeSets should remain empty");
5448 }
5449
5450 #[test]
5451 fn test_unwind_storage_history_indices_v2() {
5452 let factory = create_test_provider_factory();
5453 factory.set_storage_settings_cache(StorageSettings::v2());
5454
5455 let address = Address::with_last_byte(1);
5456 let slot_key = B256::from(U256::from(42));
5457
5458 {
5459 let rocksdb = factory.rocksdb_provider();
5460 let mut batch = rocksdb.batch();
5461 batch.append_storage_history_shard(address, slot_key, vec![3u64, 7, 10]).unwrap();
5462 batch.commit().unwrap();
5463
5464 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5465 assert!(!shards.is_empty(), "history should be written to rocksdb");
5466 }
5467
5468 let provider_rw = factory.provider_rw().unwrap();
5469
5470 let changesets = vec![
5471 (
5472 BlockNumberAddress((7, address)),
5473 StorageEntry { key: slot_key, value: U256::from(5) },
5474 ),
5475 (
5476 BlockNumberAddress((10, address)),
5477 StorageEntry { key: slot_key, value: U256::from(8) },
5478 ),
5479 ];
5480
5481 let count = provider_rw.unwind_storage_history_indices(changesets.into_iter()).unwrap();
5482 assert_eq!(count, 2);
5483
5484 provider_rw.commit().unwrap();
5485
5486 let rocksdb = factory.rocksdb_provider();
5487 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5488
5489 assert!(
5490 !shards.is_empty(),
5491 "history shards should still exist with block 3 after partial unwind"
5492 );
5493
5494 let all_blocks: Vec<u64> = shards.iter().flat_map(|(_, list)| list.iter()).collect();
5495 assert!(all_blocks.contains(&3), "block 3 should remain");
5496 assert!(!all_blocks.contains(&7), "block 7 should be unwound");
5497 assert!(!all_blocks.contains(&10), "block 10 should be unwound");
5498 }
5499}