1use super::SaveBlocksInput;
2use crate::{
3 changesets_utils::StorageRevertsIter,
4 providers::{
5 database::{chain::ChainStorage, metrics, DatabaseProviderMetrics},
6 rocksdb::{
7 OwnedRocksReadSnapshot, PendingRocksDBBatches, RocksDBProvider, RocksDBWriteCtx,
8 RocksReadSnapshot,
9 },
10 static_file::{StaticFileWriteCtx, StaticFileWriter},
11 NodeTypesForProvider, StaticFileProvider,
12 },
13 to_range,
14 traits::{
15 AccountExtReader, BlockSource, ChangeSetReader, ReceiptProvider, StageCheckpointWriter,
16 },
17 AccountReader, BlockBodyWriter, BlockExecutionWriter, BlockHashReader, BlockNumReader,
18 BlockReader, BlockWriter, BundleStateInit, ChainStateBlockReader, ChainStateBlockWriter,
19 DBProvider, DbTxProvider, EitherReader, EitherWriter, EitherWriterDestination, HashingWriter,
20 HeaderProvider, HeaderSyncGapProvider, HistoryWriter, LatestStateProviderRef,
21 OriginalValuesKnown, PersistenceFrontiers, ProviderError, PruneCheckpointReader,
22 PruneCheckpointWriter, RawRocksDBBatch, RevertsInit, RocksBatchArg, RocksDBProviderFactory,
23 StageCheckpointReader, StateWriter, StaticFileProviderFactory, StatsReader, StorageReader,
24 StorageTrieWriter, TransactionVariant, TransactionsProvider, TransactionsProviderExt,
25 TrieWriter,
26};
27use alloy_consensus::{
28 transaction::{SignerRecoverable, TransactionMeta, TxHashRef},
29 BlockHeader, TxReceipt,
30};
31use alloy_eips::BlockHashOrNumber;
32use alloy_primitives::{
33 keccak256,
34 map::{hash_map, AddressSet, B256Map, HashMap},
35 Address, BlockHash, BlockNumber, StorageKey, StorageValue, TxHash, TxNumber, B256,
36};
37use itertools::Itertools;
38use parking_lot::RwLock;
39use rayon::slice::ParallelSliceMut;
40use reth_chain_state::ExecutedBlock;
41use reth_chainspec::{ChainInfo, ChainSpecProvider, EthChainSpec};
42use reth_db_api::{
43 cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW},
44 database::{Database, ReaderTxnTracker},
45 models::{
46 sharded_key, storage_sharded_key::StorageShardedKey, AccountBeforeTx, BlockNumberAddress,
47 BlockNumberAddressRange, ShardedKey, StorageBeforeTx, StorageSettings,
48 StoredBlockBodyIndices,
49 },
50 table::Table,
51 tables,
52 transaction::{DbTx, DbTxMut},
53 BlockNumberList,
54};
55use reth_execution_types::{
56 BlockExecutionOutput, BlockExecutionResult, Chain, ExecutionOutcome,
57 RecoveredBlockAndExecutionOutput,
58};
59use reth_node_types::{BlockTy, BodyTy, HeaderTy, NodeTypes, ReceiptTy, TxTy};
60use reth_primitives_traits::{
61 Account, Block as _, BlockBody as _, Bytecode, FastInstant as Instant, RecoveredBlock,
62 SealedHeader, StorageEntry,
63};
64use reth_prune_types::{
65 PruneCheckpoint, PruneMode, PruneModes, PruneSegment, MINIMUM_UNWIND_SAFE_DISTANCE,
66};
67use reth_stages_types::{FinishCheckpoint, StageCheckpoint, StageId};
68use reth_static_file_types::StaticFileSegment;
69use reth_storage_api::{
70 BlockBodyIndicesProvider, BlockBodyReader, HistoryInfo, HistoryReader, MetadataProvider,
71 MetadataWriter, NodePrimitivesProvider, StateProvider, StateWriteConfig,
72 StorageChangeSetReader, StoragePath, StorageSettingsCache, WriteStateInput,
73};
74use reth_storage_errors::provider::{ProviderResult, StaticFileWriterError};
75use reth_storage_overlay::OverlayManager;
76use reth_trie::{
77 bundle_state::{PlainStateReverts, PlainStorageChangeset, PlainStorageRevert, StateChangeset},
78 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
79 HashedPostStateSorted, SortedTrieData,
80};
81use reth_trie_db::{DatabaseStorageTrieCursor, TrieTableAdapter};
82use smallvec::SmallVec;
83use std::{
84 cmp::Ordering,
85 collections::{BTreeMap, BTreeSet},
86 fmt::Debug,
87 ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive},
88 path::PathBuf,
89 sync::{Arc, OnceLock},
90};
91use tracing::{debug, instrument, trace};
92
93#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
95pub enum CommitOrder {
96 #[default]
98 Normal,
99 Unwind,
102}
103
104impl CommitOrder {
105 pub const fn is_unwind(&self) -> bool {
107 matches!(self, Self::Unwind)
108 }
109}
110
111pub type DatabaseProviderRO<DB, N> = DatabaseProvider<<DB as Database>::TX, N>;
113
114#[derive(Debug)]
119pub struct DatabaseProviderRW<DB: Database, N: NodeTypes>(
120 pub DatabaseProvider<<DB as Database>::TXMut, N>,
121);
122
123impl<DB: Database, N: NodeTypes> Deref for DatabaseProviderRW<DB, N> {
124 type Target = DatabaseProvider<<DB as Database>::TXMut, N>;
125
126 fn deref(&self) -> &Self::Target {
127 &self.0
128 }
129}
130
131impl<DB: Database, N: NodeTypes> DerefMut for DatabaseProviderRW<DB, N> {
132 fn deref_mut(&mut self) -> &mut Self::Target {
133 &mut self.0
134 }
135}
136
137impl<DB: Database, N: NodeTypes> AsRef<DatabaseProvider<<DB as Database>::TXMut, N>>
138 for DatabaseProviderRW<DB, N>
139{
140 fn as_ref(&self) -> &DatabaseProvider<<DB as Database>::TXMut, N> {
141 &self.0
142 }
143}
144
145impl<DB: Database, N: NodeTypes> DatabaseProviderRW<DB, N> {
146 pub fn commit(self) -> ProviderResult<()> {
148 self.0.commit()
149 }
150
151 pub fn into_tx(self) -> <DB as Database>::TXMut {
153 self.0.into_tx()
154 }
155
156 #[cfg(any(test, feature = "test-utils"))]
158 pub const fn with_minimum_pruning_distance(mut self, distance: u64) -> Self {
159 self.0.minimum_pruning_distance = distance;
160 self
161 }
162}
163
164impl<DB: Database, N: NodeTypes> From<DatabaseProviderRW<DB, N>>
165 for DatabaseProvider<<DB as Database>::TXMut, N>
166{
167 fn from(provider: DatabaseProviderRW<DB, N>) -> Self {
168 provider.0
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174enum SaveBlocksMode {
175 Full,
178 BlocksOnly,
182}
183
184impl SaveBlocksMode {
185 const fn with_state(self) -> bool {
187 matches!(self, Self::Full)
188 }
189}
190
191pub struct DatabaseProvider<TX, N: NodeTypes> {
194 tx: TX,
196 chain_spec: Arc<N::ChainSpec>,
198 static_file_provider: StaticFileProvider<N::Primitives>,
200 prune_modes: PruneModes,
202 storage: Arc<N::Storage>,
204 storage_settings: Arc<RwLock<StorageSettings>>,
206 rocksdb_provider: RocksDBProvider,
208 rocksdb_history_snapshot: OnceLock<Option<OwnedRocksReadSnapshot>>,
214 overlay_manager: OverlayManager<N::Primitives>,
216 runtime: reth_tasks::Runtime,
218 db_path: PathBuf,
220 pending_rocksdb_batches: PendingRocksDBBatches,
222 commit_order: CommitOrder,
224 minimum_pruning_distance: u64,
226 metrics: Arc<DatabaseProviderMetrics>,
228 reader_txn_tracker: Option<Arc<dyn ReaderTxnTracker>>,
230}
231
232impl<TX: Debug, N: NodeTypes> Debug for DatabaseProvider<TX, N> {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 let mut s = f.debug_struct("DatabaseProvider");
235 s.field("tx", &self.tx)
236 .field("chain_spec", &self.chain_spec)
237 .field("static_file_provider", &self.static_file_provider)
238 .field("prune_modes", &self.prune_modes)
239 .field("storage", &self.storage)
240 .field("storage_settings", &self.storage_settings)
241 .field("rocksdb_provider", &self.rocksdb_provider)
242 .field("overlay_manager", &self.overlay_manager)
243 .field("runtime", &self.runtime)
244 .field("pending_rocksdb_batches", &"<pending batches>")
245 .field("commit_order", &self.commit_order)
246 .field("minimum_pruning_distance", &self.minimum_pruning_distance)
247 .field("reader_txn_tracker", &"<reader txn tracker>")
248 .finish()
249 }
250}
251
252impl<TX, N: NodeTypes> DatabaseProvider<TX, N> {
253 pub const fn prune_modes_ref(&self) -> &PruneModes {
255 &self.prune_modes
256 }
257
258 pub const fn with_minimum_pruning_distance(mut self, distance: u64) -> Self {
260 self.minimum_pruning_distance = distance;
261 self
262 }
263
264 pub(crate) fn with_reader_txn_tracker<T>(mut self, reader_txn_tracker: T) -> Self
266 where
267 T: ReaderTxnTracker + 'static,
268 {
269 self.reader_txn_tracker = Some(Arc::new(reader_txn_tracker));
270 self
271 }
272}
273
274impl<TX: DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
275 fn commit_unwind(self) -> ProviderResult<()> {
286 let storage_v2 = self.cached_storage_settings().storage_v2;
287 let reader_txn_tracker = self.reader_txn_tracker.clone();
288 self.tx.commit()?;
289
290 if let Some(reader_txn_tracker) = reader_txn_tracker.as_ref() {
291 reader_txn_tracker.wait_for_pre_commit_readers();
292 }
293
294 if storage_v2 {
295 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
296 for batch in batches {
297 self.rocksdb_provider.commit_batch(batch)?;
298 }
299 }
300
301 self.static_file_provider.commit()?;
302 Ok(())
303 }
304
305 pub fn latest<'a>(&'a self) -> Box<dyn StateProvider + 'a> {
307 trace!(target: "providers::db", "Returning latest state provider");
308 Box::new(LatestStateProviderRef::new(self))
309 }
310
311 #[cfg(feature = "test-utils")]
312 pub fn set_prune_modes(&mut self, prune_modes: PruneModes) {
314 self.prune_modes = prune_modes;
315 }
316}
317
318impl<TX, N: NodeTypes> NodePrimitivesProvider for DatabaseProvider<TX, N> {
319 type Primitives = N::Primitives;
320}
321
322impl<TX, N: NodeTypes> StaticFileProviderFactory for DatabaseProvider<TX, N> {
323 fn static_file_provider(&self) -> StaticFileProvider<Self::Primitives> {
325 self.static_file_provider.clone()
326 }
327
328 fn get_static_file_writer(
329 &self,
330 block: BlockNumber,
331 segment: StaticFileSegment,
332 ) -> ProviderResult<crate::providers::StaticFileProviderRWRefMut<'_, Self::Primitives>> {
333 self.static_file_provider.get_writer(block, segment)
334 }
335}
336
337impl<TX, N: NodeTypes> RocksDBProviderFactory for DatabaseProvider<TX, N> {
338 fn rocksdb_provider(&self) -> RocksDBProvider {
340 self.rocksdb_provider.clone()
341 }
342
343 fn set_pending_rocksdb_batch(&self, batch: rocksdb::WriteBatchWithTransaction<true>) {
344 self.pending_rocksdb_batches.lock().push(batch);
345 }
346
347 fn commit_pending_rocksdb_batches(&self) -> ProviderResult<()> {
348 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
349 for batch in batches {
350 self.rocksdb_provider.commit_batch(batch)?;
351 }
352 Ok(())
353 }
354}
355
356impl<TX: Debug + Send, N: NodeTypes<ChainSpec: EthChainSpec + 'static>> ChainSpecProvider
357 for DatabaseProvider<TX, N>
358{
359 type ChainSpec = N::ChainSpec;
360
361 fn chain_spec(&self) -> Arc<Self::ChainSpec> {
362 self.chain_spec.clone()
363 }
364}
365
366impl<TX: DbTxMut, N: NodeTypes> DatabaseProvider<TX, N> {
367 #[expect(clippy::too_many_arguments)]
369 fn new_rw_inner(
370 tx: TX,
371 chain_spec: Arc<N::ChainSpec>,
372 static_file_provider: StaticFileProvider<N::Primitives>,
373 prune_modes: PruneModes,
374 storage: Arc<N::Storage>,
375 storage_settings: Arc<RwLock<StorageSettings>>,
376 rocksdb_provider: RocksDBProvider,
377 overlay_manager: OverlayManager<N::Primitives>,
378 runtime: reth_tasks::Runtime,
379 db_path: PathBuf,
380 commit_order: CommitOrder,
381 metrics: Arc<DatabaseProviderMetrics>,
382 ) -> Self {
383 Self {
384 tx,
385 chain_spec,
386 static_file_provider,
387 prune_modes,
388 storage,
389 storage_settings,
390 rocksdb_provider,
391 overlay_manager,
392 runtime,
393 db_path,
394 rocksdb_history_snapshot: OnceLock::new(),
395 pending_rocksdb_batches: Default::default(),
396 commit_order,
397 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
398 metrics,
399 reader_txn_tracker: None,
400 }
401 }
402
403 #[expect(clippy::too_many_arguments)]
405 pub fn new_rw(
406 tx: TX,
407 chain_spec: Arc<N::ChainSpec>,
408 static_file_provider: StaticFileProvider<N::Primitives>,
409 prune_modes: PruneModes,
410 storage: Arc<N::Storage>,
411 storage_settings: Arc<RwLock<StorageSettings>>,
412 rocksdb_provider: RocksDBProvider,
413 overlay_manager: OverlayManager<N::Primitives>,
414 runtime: reth_tasks::Runtime,
415 db_path: PathBuf,
416 metrics: Arc<DatabaseProviderMetrics>,
417 ) -> Self {
418 Self::new_rw_inner(
419 tx,
420 chain_spec,
421 static_file_provider,
422 prune_modes,
423 storage,
424 storage_settings,
425 rocksdb_provider,
426 overlay_manager,
427 runtime,
428 db_path,
429 CommitOrder::Normal,
430 metrics,
431 )
432 }
433
434 #[expect(clippy::too_many_arguments)]
436 pub fn new_unwind_rw(
437 tx: TX,
438 chain_spec: Arc<N::ChainSpec>,
439 static_file_provider: StaticFileProvider<N::Primitives>,
440 prune_modes: PruneModes,
441 storage: Arc<N::Storage>,
442 storage_settings: Arc<RwLock<StorageSettings>>,
443 rocksdb_provider: RocksDBProvider,
444 overlay_manager: OverlayManager<N::Primitives>,
445 runtime: reth_tasks::Runtime,
446 db_path: PathBuf,
447 metrics: Arc<DatabaseProviderMetrics>,
448 ) -> Self {
449 Self::new_rw_inner(
450 tx,
451 chain_spec,
452 static_file_provider,
453 prune_modes,
454 storage,
455 storage_settings,
456 rocksdb_provider,
457 overlay_manager,
458 runtime,
459 db_path,
460 CommitOrder::Unwind,
461 metrics,
462 )
463 }
464}
465
466impl<TX, N: NodeTypes> AsRef<Self> for DatabaseProvider<TX, N> {
467 fn as_ref(&self) -> &Self {
468 self
469 }
470}
471
472impl<TX: DbTx + DbTxMut + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
473 pub fn with_rocksdb_batch<F, R>(&self, f: F) -> ProviderResult<R>
477 where
478 F: FnOnce(RocksBatchArg<'_>) -> ProviderResult<(R, Option<RawRocksDBBatch>)>,
479 {
480 let rocksdb = self.rocksdb_provider();
481 let rocksdb_batch = rocksdb.batch();
482
483 let (result, raw_batch) = f(rocksdb_batch)?;
484
485 if let Some(batch) = raw_batch {
486 self.set_pending_rocksdb_batch(batch);
487 }
488 let _ = raw_batch; Ok(result)
491 }
492
493 fn static_file_write_ctx(
495 &self,
496 save_mode: SaveBlocksMode,
497 first_block: BlockNumber,
498 last_block: BlockNumber,
499 ) -> ProviderResult<StaticFileWriteCtx> {
500 let tip = self.last_block_number()?.max(last_block);
501 Ok(StaticFileWriteCtx {
502 write_senders: EitherWriterDestination::senders(self).is_static_file() &&
503 self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()),
504 write_receipts: save_mode.with_state() &&
505 EitherWriter::receipts_destination(self).is_static_file(),
506 write_account_changesets: save_mode.with_state() &&
507 EitherWriterDestination::account_changesets(self).is_static_file(),
508 write_storage_changesets: save_mode.with_state() &&
509 EitherWriterDestination::storage_changesets(self).is_static_file(),
510 tip,
511 receipts_prune_mode: self.prune_modes.receipts,
512 receipts_prunable: self
514 .static_file_provider
515 .get_highest_static_file_tx(StaticFileSegment::Receipts)
516 .is_none() &&
517 PruneMode::Distance(self.minimum_pruning_distance)
518 .should_prune(first_block, tip),
519 })
520 }
521
522 fn rocksdb_write_ctx(&self, first_block: BlockNumber) -> RocksDBWriteCtx {
524 RocksDBWriteCtx {
525 first_block_number: first_block,
526 prune_tx_lookup: self.prune_modes.transaction_lookup,
527 storage_settings: self.cached_storage_settings(),
528 pending_batches: self.pending_rocksdb_batches.clone(),
529 }
530 }
531
532 #[instrument(level = "debug", target = "providers::db", skip_all, fields(block_count = input.persist_rest_blocks().len()))]
537 pub fn save_blocks(&self, input: &SaveBlocksInput<N::Primitives>) -> ProviderResult<()> {
538 let (db_tip, partial_state_trie) = self
539 .get_stage_checkpoint(StageId::Finish)?
540 .map(|checkpoint| {
541 let partial_state_trie = checkpoint
542 .finish_stage_checkpoint()
543 .and_then(|finish| finish.partial_state_trie())
544 .unwrap_or(checkpoint.block_number);
545 (checkpoint.block_number, partial_state_trie)
546 })
547 .unwrap_or_default();
548
549 if db_tip != input.prev_db_tip() || partial_state_trie != input.prev_partial_state_trie() {
550 return Err(ProviderError::other(std::io::Error::other(format!(
551 "persistence frontiers do not match Finish checkpoint: expected database/state-trie tips #{}/{}, got #{}/{}",
552 input.prev_db_tip(),
553 input.prev_partial_state_trie(),
554 db_tip,
555 partial_state_trie,
556 ))))
557 }
558
559 self.save_blocks_inner(
560 input.persist_rest_blocks(),
561 input.state_trie_blocks(),
562 input.state_trie_masking_blocks(),
563 (input.new_partial_state_trie() < input.new_db_tip())
564 .then_some(input.new_partial_state_trie()),
565 SaveBlocksMode::Full,
566 )
567 }
568
569 fn save_blocks_inner(
570 &self,
571 blocks: &[ExecutedBlock<N::Primitives>],
572 state_trie_blocks: &[ExecutedBlock<N::Primitives>],
573 state_trie_masking_blocks: &[ExecutedBlock<N::Primitives>],
574 partial_state_trie: Option<BlockNumber>,
575 save_mode: SaveBlocksMode,
576 ) -> ProviderResult<()> {
577 let total_start = Instant::now();
578 let block_count = blocks.len() as u64;
579 let last_block_number = blocks
582 .last()
583 .or_else(|| state_trie_masking_blocks.last())
584 .or_else(|| state_trie_blocks.last())
585 .expect("at least one persistence range must be non-empty")
586 .recovered_block()
587 .number();
588 let first_number = blocks.first().map(|block| block.recovered_block().number());
589
590 debug!(target: "providers::db", block_count, "Writing blocks and execution data to storage");
591
592 let mut tx_nums: SmallVec<[TxNumber; 4]> = SmallVec::with_capacity(blocks.len());
594 if !blocks.is_empty() {
595 let first_tx_num = self
596 .tx
597 .cursor_read::<tables::TransactionBlocks>()?
598 .last()?
599 .map(|(n, _)| n + 1)
600 .unwrap_or_default();
601 let mut current = first_tx_num;
602 for block in blocks {
603 tx_nums.push(current);
604 current += block.recovered_block().body().transaction_count() as u64;
605 }
606 }
607
608 let mut timings =
609 metrics::SaveBlocksTimings { batch_size: block_count, ..Default::default() };
610
611 let sf_provider = &self.static_file_provider;
613 let rocksdb_provider = &self.rocksdb_provider;
614 let sf_ctx = first_number
615 .map(|first_number| {
616 self.static_file_write_ctx(save_mode, first_number, last_block_number)
617 })
618 .transpose()?;
619 let rocksdb_ctx = first_number.map(|first_number| self.rocksdb_write_ctx(first_number));
620 let rocksdb_enabled =
621 rocksdb_ctx.as_ref().is_some_and(|ctx| ctx.storage_settings.storage_v2);
622
623 let mut sf_result = None;
624 let mut rocksdb_result = None;
625
626 let runtime = &self.runtime;
628 let span = tracing::Span::current();
631 runtime.storage_pool().in_place_scope(|s| {
632 if sf_ctx.is_some() {
634 s.spawn(|_| {
635 let _guard = span.enter();
636 let start = Instant::now();
637 let sf_ctx =
638 sf_ctx.expect("static file context exists when blocks are persisted");
639 sf_result = Some(
640 sf_provider
641 .write_blocks_data(blocks, &tx_nums, sf_ctx, runtime)
642 .map(|()| start.elapsed()),
643 );
644 });
645 }
646
647 if rocksdb_enabled {
649 s.spawn(|_| {
650 let _guard = span.enter();
651 let start = Instant::now();
652 let rocksdb_ctx =
653 rocksdb_ctx.clone().expect("RocksDB context exists when enabled");
654 rocksdb_result = Some(
655 rocksdb_provider
656 .write_blocks_data(blocks, &tx_nums, rocksdb_ctx, runtime)
657 .map(|()| start.elapsed()),
658 );
659 });
660 }
661
662 let mdbx_start = Instant::now();
664
665 if !blocks.is_empty() &&
667 !self.cached_storage_settings().storage_v2 &&
668 self.prune_modes.transaction_lookup.is_none_or(|m| !m.is_full())
669 {
670 let start = Instant::now();
671 let total_tx_count: usize =
672 blocks.iter().map(|b| b.recovered_block().body().transaction_count()).sum();
673 let mut all_tx_hashes = Vec::with_capacity(total_tx_count);
674 for (i, block) in blocks.iter().enumerate() {
675 let recovered_block = block.recovered_block();
676 for (tx_num, transaction) in
677 (tx_nums[i]..).zip(recovered_block.body().transactions_iter())
678 {
679 all_tx_hashes.push((*transaction.tx_hash(), tx_num));
680 }
681 }
682
683 all_tx_hashes.sort_unstable_by_key(|(hash, _)| *hash);
685
686 self.with_rocksdb_batch(|batch| {
688 let mut tx_hash_writer =
689 EitherWriter::new_transaction_hash_numbers(self, batch)?;
690 tx_hash_writer.put_transaction_hash_numbers_batch(all_tx_hashes, false)?;
691 let raw_batch = tx_hash_writer.into_raw_rocksdb_batch();
692 Ok(((), raw_batch))
693 })?;
694 self.metrics.record_duration(
695 metrics::Action::InsertTransactionHashNumbers,
696 start.elapsed(),
697 );
698 }
699
700 for (i, block) in blocks.iter().enumerate() {
701 let recovered_block = block.recovered_block();
702
703 let start = Instant::now();
704 self.insert_block_mdbx_only(recovered_block, tx_nums[i])?;
705 timings.insert_block += start.elapsed();
706
707 if save_mode.with_state() {
708 let execution_output = block.execution_outcome();
709 let sf_ctx =
710 sf_ctx.expect("static file context exists when blocks are persisted");
711
712 let start = Instant::now();
716 self.write_state(
717 WriteStateInput::Single {
718 outcome: execution_output,
719 block: recovered_block.number(),
720 },
721 OriginalValuesKnown::No,
722 StateWriteConfig {
723 write_receipts: !sf_ctx.write_receipts,
724 write_account_changesets: !sf_ctx.write_account_changesets,
725 write_storage_changesets: !sf_ctx.write_storage_changesets,
726 },
727 )?;
728 timings.write_state += start.elapsed();
729 }
730 }
731
732 if save_mode.with_state() && !state_trie_blocks.is_empty() {
735 let start = Instant::now();
736 let batch = ExecutedBlock::hashed_state_refs(state_trie_blocks);
737 let mask = ExecutedBlock::hashed_state_refs(state_trie_masking_blocks);
738 let merged_hashed_state =
739 HashedPostStateSorted::disjointed_merge_batch(&batch, &mask);
740 if !merged_hashed_state.is_empty() {
741 self.write_hashed_state(&merged_hashed_state)?;
742 }
743 timings.write_hashed_state += start.elapsed();
744
745 let start = Instant::now();
746 let batch = ExecutedBlock::trie_updates_refs(state_trie_blocks);
747 let mask = ExecutedBlock::trie_updates_refs(state_trie_masking_blocks);
748 let merged_trie =
749 Arc::new(TrieUpdatesSorted::disjointed_merge_batch(&batch, &mask));
750 if !merged_trie.is_empty() {
751 self.write_trie_updates_sorted(&merged_trie)?;
752 }
753 timings.write_trie_updates += start.elapsed();
754 }
755
756 if save_mode.with_state() &&
758 let Some(first_number) = first_number
759 {
760 let start = Instant::now();
761 self.update_history_indices(first_number..=last_block_number)?;
762 timings.update_history_indices = start.elapsed();
763 }
764
765 let start = Instant::now();
767 if !blocks.is_empty() {
768 self.update_pipeline_stages(last_block_number, false)?;
769 }
770 if save_mode.with_state() {
771 let checkpoint = match partial_state_trie {
772 Some(partial_state_trie) => StageCheckpoint::new(last_block_number)
773 .with_finish_stage_checkpoint(FinishCheckpoint {
774 partial_state_trie: Some(partial_state_trie),
775 }),
776 None => StageCheckpoint::new(last_block_number),
777 };
778 self.save_stage_checkpoint(StageId::Finish, checkpoint)?;
779 }
780 timings.update_pipeline_stages = start.elapsed();
781
782 timings.mdbx = mdbx_start.elapsed();
783
784 Ok::<_, ProviderError>(())
785 })?;
786
787 if !blocks.is_empty() {
789 timings.sf = sf_result.ok_or(StaticFileWriterError::ThreadPanic("static file"))??;
790 }
791
792 if rocksdb_enabled {
793 timings.rocksdb = rocksdb_result.ok_or_else(|| {
794 ProviderError::Database(reth_db_api::DatabaseError::Other(
795 "RocksDB thread panicked".into(),
796 ))
797 })??;
798 }
799
800 timings.total = total_start.elapsed();
801
802 self.metrics.record_save_blocks(&timings);
803 if let Some(first_number) = first_number {
804 debug!(target: "providers::db", range = ?first_number..=last_block_number, "Appended block data");
805 }
806
807 Ok(())
808 }
809
810 #[instrument(level = "debug", target = "providers::db", skip_all)]
814 fn insert_block_mdbx_only(
815 &self,
816 block: &RecoveredBlock<BlockTy<N>>,
817 first_tx_num: TxNumber,
818 ) -> ProviderResult<StoredBlockBodyIndices> {
819 if self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()) &&
820 EitherWriterDestination::senders(self).is_database()
821 {
822 let start = Instant::now();
823 let tx_nums_iter = std::iter::successors(Some(first_tx_num), |n| Some(n + 1));
824 let mut cursor = self.tx.cursor_write::<tables::TransactionSenders>()?;
825 for (tx_num, sender) in tx_nums_iter.zip(block.senders_iter().copied()) {
826 cursor.append(tx_num, &sender)?;
827 }
828 self.metrics
829 .record_duration(metrics::Action::InsertTransactionSenders, start.elapsed());
830 }
831
832 let block_number = block.number();
833 let tx_count = block.body().transaction_count() as u64;
834
835 let start = Instant::now();
836 self.tx.put::<tables::HeaderNumbers>(block.hash(), block_number)?;
837 self.metrics.record_duration(metrics::Action::InsertHeaderNumbers, start.elapsed());
838
839 self.write_block_body_indices(block_number, block.body(), first_tx_num, tx_count)?;
840
841 Ok(StoredBlockBodyIndices { first_tx_num, tx_count })
842 }
843
844 fn write_block_body_indices(
847 &self,
848 block_number: BlockNumber,
849 body: &BodyTy<N>,
850 first_tx_num: TxNumber,
851 tx_count: u64,
852 ) -> ProviderResult<()> {
853 let start = Instant::now();
855 self.tx
856 .cursor_write::<tables::BlockBodyIndices>()?
857 .append(block_number, &StoredBlockBodyIndices { first_tx_num, tx_count })?;
858 self.metrics.record_duration(metrics::Action::InsertBlockBodyIndices, start.elapsed());
859
860 if tx_count > 0 {
862 let start = Instant::now();
863 self.tx
864 .cursor_write::<tables::TransactionBlocks>()?
865 .append(first_tx_num + tx_count - 1, &block_number)?;
866 self.metrics.record_duration(metrics::Action::InsertTransactionBlocks, start.elapsed());
867 }
868
869 self.storage.writer().write_block_bodies(self, vec![(block_number, Some(body))])?;
871
872 Ok(())
873 }
874
875 pub fn unwind_trie_state_from(&self, from: BlockNumber) -> ProviderResult<()> {
880 let finish =
881 self.get_stage_checkpoint(reth_stages_types::StageId::Finish)?.ok_or_else(|| {
882 ProviderError::InsufficientChangesets { requested: from, available: 0..=0 }
883 })?;
884
885 let trie_revert = self
887 .overlay_manager
888 .get_or_compute_cached_changesets_range(self, from..=finish.block_number)?;
889
890 let changed_accounts = self.account_changesets_range(from..)?;
891
892 self.unwind_account_hashing(changed_accounts.iter())?;
894
895 self.unwind_account_history_indices(changed_accounts.iter())?;
897
898 let changed_storages = self.storage_changesets_range(from..)?;
899
900 self.unwind_storage_hashing(changed_storages.iter().copied())?;
902
903 self.unwind_storage_history_indices(changed_storages.iter().copied())?;
905
906 self.write_trie_updates_sorted(&trie_revert)?;
907
908 Ok(())
909 }
910
911 fn remove_receipts_from(
913 &self,
914 from_tx: TxNumber,
915 last_block: BlockNumber,
916 ) -> ProviderResult<()> {
917 self.remove::<tables::Receipts<ReceiptTy<N>>>(from_tx..)?;
919
920 if EitherWriter::receipts_destination(self).is_static_file() {
921 let static_file_receipt_num =
922 self.static_file_provider.get_highest_static_file_tx(StaticFileSegment::Receipts);
923
924 let to_delete = static_file_receipt_num
925 .map(|static_num| (static_num + 1).saturating_sub(from_tx))
926 .unwrap_or_default();
927
928 self.static_file_provider
929 .latest_writer(StaticFileSegment::Receipts)?
930 .prune_receipts(to_delete, last_block)?;
931 }
932
933 Ok(())
934 }
935
936 fn write_bytecodes(
938 &self,
939 bytecodes: impl IntoIterator<Item = (B256, Bytecode)>,
940 ) -> ProviderResult<()> {
941 let mut bytecodes_cursor = self.tx_ref().cursor_write::<tables::Bytecodes>()?;
942 for (hash, bytecode) in bytecodes {
943 bytecodes_cursor.upsert(hash, &bytecode)?;
944 }
945 Ok(())
946 }
947}
948
949fn unwind_history_shards<S, T, C>(
964 cursor: &mut C,
965 start_key: T::Key,
966 block_number: BlockNumber,
967 mut shard_belongs_to_key: impl FnMut(&T::Key) -> bool,
968) -> ProviderResult<Vec<u64>>
969where
970 T: Table<Value = BlockNumberList>,
971 T::Key: AsRef<ShardedKey<S>>,
972 C: DbCursorRO<T> + DbCursorRW<T>,
973{
974 let mut item = cursor.seek_exact(start_key)?;
976 while let Some((sharded_key, list)) = item {
977 if !shard_belongs_to_key(&sharded_key) {
979 break
980 }
981
982 cursor.delete_current()?;
985
986 let first = list.iter().next().expect("List can't be empty");
989
990 if first >= block_number {
993 item = cursor.prev()?;
994 continue
995 }
996 else if block_number <= sharded_key.as_ref().highest_block_number {
999 return Ok(list.iter().take_while(|i| *i < block_number).collect::<Vec<_>>())
1002 }
1003 return Ok(list.iter().collect::<Vec<_>>())
1006 }
1007
1008 Ok(Vec::new())
1010}
1011
1012impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
1013 #[expect(clippy::too_many_arguments)]
1015 pub fn new(
1016 tx: TX,
1017 chain_spec: Arc<N::ChainSpec>,
1018 static_file_provider: StaticFileProvider<N::Primitives>,
1019 prune_modes: PruneModes,
1020 storage: Arc<N::Storage>,
1021 storage_settings: Arc<RwLock<StorageSettings>>,
1022 rocksdb_provider: RocksDBProvider,
1023 overlay_manager: OverlayManager<N::Primitives>,
1024 runtime: reth_tasks::Runtime,
1025 db_path: PathBuf,
1026 metrics: Arc<DatabaseProviderMetrics>,
1027 ) -> Self {
1028 Self {
1029 tx,
1030 chain_spec,
1031 static_file_provider,
1032 prune_modes,
1033 storage,
1034 storage_settings,
1035 rocksdb_provider,
1036 overlay_manager,
1037 runtime,
1038 db_path,
1039 rocksdb_history_snapshot: OnceLock::new(),
1040 pending_rocksdb_batches: Default::default(),
1041 commit_order: CommitOrder::Normal,
1042 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
1043 metrics,
1044 reader_txn_tracker: None,
1045 }
1046 }
1047
1048 pub fn into_tx(self) -> TX {
1050 self.tx
1051 }
1052
1053 pub const fn tx_mut(&mut self) -> &mut TX {
1055 &mut self.tx
1056 }
1057
1058 pub const fn tx_ref(&self) -> &TX {
1060 &self.tx
1061 }
1062
1063 pub fn chain_spec(&self) -> &N::ChainSpec {
1065 &self.chain_spec
1066 }
1067}
1068
1069impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
1070 fn recovered_block<H, HF, B, BF>(
1071 &self,
1072 id: BlockHashOrNumber,
1073 _transaction_kind: TransactionVariant,
1074 header_by_number: HF,
1075 construct_block: BF,
1076 ) -> ProviderResult<Option<B>>
1077 where
1078 H: AsRef<HeaderTy<N>>,
1079 HF: FnOnce(BlockNumber) -> ProviderResult<Option<H>>,
1080 BF: FnOnce(H, BodyTy<N>, Vec<Address>) -> ProviderResult<Option<B>>,
1081 {
1082 let Some(block_number) = self.convert_hash_or_number(id)? else { return Ok(None) };
1083 let earliest_available = self.static_file_provider.earliest_history_height();
1084 if block_number < earliest_available {
1085 return Err(ProviderError::BlockExpired { requested: block_number, earliest_available })
1086 }
1087 let Some(header) = header_by_number(block_number)? else { return Ok(None) };
1088
1089 let Some(body) = self.block_body_indices(block_number)? else { return Ok(None) };
1096
1097 let tx_range = body.tx_num_range();
1098
1099 let transactions = if tx_range.is_empty() {
1100 vec![]
1101 } else {
1102 self.transactions_by_tx_range(tx_range.clone())?
1103 };
1104
1105 let body = self
1106 .storage
1107 .reader()
1108 .read_block_bodies(self, vec![(header.as_ref(), transactions)])?
1109 .pop()
1110 .ok_or(ProviderError::InvalidStorageOutput)?;
1111
1112 let senders = if tx_range.is_empty() {
1113 vec![]
1114 } else {
1115 let known_senders: HashMap<TxNumber, Address> =
1116 EitherReader::new_senders(self)?.senders_by_tx_range(tx_range.clone())?;
1117
1118 let mut senders = Vec::with_capacity(body.transactions().len());
1119 for (tx_num, tx) in tx_range.zip(body.transactions()) {
1120 match known_senders.get(&tx_num) {
1121 None => {
1122 let sender = tx.recover_signer_unchecked()?;
1123 senders.push(sender);
1124 }
1125 Some(sender) => senders.push(*sender),
1126 }
1127 }
1128 senders
1129 };
1130
1131 construct_block(header, body, senders)
1132 }
1133
1134 fn block_range<F, H, HF, R>(
1144 &self,
1145 range: RangeInclusive<BlockNumber>,
1146 headers_range: HF,
1147 mut assemble_block: F,
1148 ) -> ProviderResult<Vec<R>>
1149 where
1150 H: AsRef<HeaderTy<N>>,
1151 HF: FnOnce(RangeInclusive<BlockNumber>) -> ProviderResult<Vec<H>>,
1152 F: FnMut(H, BodyTy<N>, Range<TxNumber>) -> ProviderResult<R>,
1153 {
1154 if range.is_empty() {
1155 return Ok(Vec::new())
1156 }
1157
1158 let earliest_available = self.static_file_provider.earliest_history_height();
1161 if *range.start() < earliest_available {
1162 return Err(ProviderError::BlockExpired {
1163 requested: *range.start(),
1164 earliest_available,
1165 })
1166 }
1167
1168 let len = range.end().saturating_sub(*range.start()) as usize + 1;
1169 let mut blocks = Vec::with_capacity(len);
1170
1171 let headers = headers_range(range.clone())?;
1172
1173 let present_headers = self
1179 .block_body_indices_range(range)?
1180 .into_iter()
1181 .map(|b| b.tx_num_range())
1182 .zip(headers)
1183 .collect::<Vec<_>>();
1184
1185 let mut inputs = Vec::with_capacity(present_headers.len());
1186 for (tx_range, header) in &present_headers {
1187 let transactions = if tx_range.is_empty() {
1188 Vec::new()
1189 } else {
1190 self.transactions_by_tx_range(tx_range.clone())?
1191 };
1192
1193 inputs.push((header.as_ref(), transactions));
1194 }
1195
1196 let bodies = self.storage.reader().read_block_bodies(self, inputs)?;
1197
1198 for ((tx_range, header), body) in present_headers.into_iter().zip(bodies) {
1199 blocks.push(assemble_block(header, body, tx_range)?);
1200 }
1201
1202 Ok(blocks)
1203 }
1204
1205 fn block_with_senders_range<H, HF, B, BF>(
1216 &self,
1217 range: RangeInclusive<BlockNumber>,
1218 headers_range: HF,
1219 assemble_block: BF,
1220 ) -> ProviderResult<Vec<B>>
1221 where
1222 H: AsRef<HeaderTy<N>>,
1223 HF: Fn(RangeInclusive<BlockNumber>) -> ProviderResult<Vec<H>>,
1224 BF: Fn(H, BodyTy<N>, Vec<Address>) -> ProviderResult<B>,
1225 {
1226 self.block_range(range, headers_range, |header, body, tx_range| {
1227 let senders = if tx_range.is_empty() {
1228 Vec::new()
1229 } else {
1230 let known_senders: HashMap<TxNumber, Address> =
1231 EitherReader::new_senders(self)?.senders_by_tx_range(tx_range.clone())?;
1232
1233 let mut senders = Vec::with_capacity(body.transactions().len());
1234 for (tx_num, tx) in tx_range.zip(body.transactions()) {
1235 match known_senders.get(&tx_num) {
1236 None => {
1237 let sender = tx.recover_signer_unchecked()?;
1239 senders.push(sender);
1240 }
1241 Some(sender) => senders.push(*sender),
1242 }
1243 }
1244
1245 senders
1246 };
1247
1248 assemble_block(header, body, senders)
1249 })
1250 }
1251
1252 #[allow(clippy::clone_on_copy)]
1256 pub(crate) fn populate_bundle_state(
1257 &self,
1258 account_changeset: Vec<(u64, AccountBeforeTx)>,
1259 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1260 mut get_account: impl FnMut(Address) -> ProviderResult<Option<Account>>,
1261 mut get_storage: impl FnMut(Address, StorageKey) -> ProviderResult<Option<StorageValue>>,
1262 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1263 let mut state: BundleStateInit = HashMap::default();
1267
1268 let mut reverts: RevertsInit = HashMap::default();
1274
1275 for (block_number, account_before) in account_changeset.into_iter().rev() {
1277 let AccountBeforeTx { info: old_info, address } = account_before;
1278 match state.entry(address) {
1279 hash_map::Entry::Vacant(entry) => {
1280 let new_info = get_account(address)?;
1281 entry.insert((old_info.clone(), new_info, HashMap::default()));
1282 }
1283 hash_map::Entry::Occupied(mut entry) => {
1284 entry.get_mut().0 = old_info.clone();
1286 }
1287 }
1288 reverts.entry(block_number).or_default().entry(address).or_default().0 = Some(old_info);
1290 }
1291
1292 for (block_and_address, old_storage) in storage_changeset.into_iter().rev() {
1294 let BlockNumberAddress((block_number, address)) = block_and_address;
1295 let account_state = match state.entry(address) {
1297 hash_map::Entry::Vacant(entry) => {
1298 let present_info = get_account(address)?;
1299 entry.insert((present_info.clone(), present_info, HashMap::default()))
1300 }
1301 hash_map::Entry::Occupied(entry) => entry.into_mut(),
1302 };
1303
1304 match account_state.2.entry(old_storage.key) {
1306 hash_map::Entry::Vacant(entry) => {
1307 let new_storage = get_storage(address, old_storage.key)?.unwrap_or_default();
1308 entry.insert((old_storage.value, new_storage));
1309 }
1310 hash_map::Entry::Occupied(mut entry) => {
1311 entry.get_mut().0 = old_storage.value;
1312 }
1313 };
1314
1315 reverts
1316 .entry(block_number)
1317 .or_default()
1318 .entry(address)
1319 .or_default()
1320 .1
1321 .push(old_storage);
1322 }
1323
1324 Ok((state, reverts))
1325 }
1326
1327 fn populate_bundle_state_plain(
1330 &self,
1331 account_changeset: Vec<(u64, AccountBeforeTx)>,
1332 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1333 plain_accounts_cursor: &mut impl DbCursorRO<tables::PlainAccountState>,
1334 plain_storage_cursor: &mut impl DbDupCursorRO<tables::PlainStorageState>,
1335 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1336 self.populate_bundle_state(
1337 account_changeset,
1338 storage_changeset,
1339 |address| Ok(plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1)),
1340 |address, storage_key| {
1341 Ok(plain_storage_cursor
1342 .seek_by_key_subkey(address, storage_key)?
1343 .filter(|s| s.key == storage_key)
1344 .map(|s| s.value))
1345 },
1346 )
1347 }
1348
1349 fn populate_bundle_state_hashed(
1354 &self,
1355 account_changeset: Vec<(u64, AccountBeforeTx)>,
1356 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1357 hashed_accounts_cursor: &mut impl DbCursorRO<tables::HashedAccounts>,
1358 hashed_storage_cursor: &mut impl DbDupCursorRO<tables::HashedStorages>,
1359 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1360 self.populate_bundle_state(
1361 account_changeset,
1362 storage_changeset,
1363 |address| Ok(hashed_accounts_cursor.seek_exact(keccak256(address))?.map(|kv| kv.1)),
1364 |address, storage_key| {
1365 let hashed_storage_key = keccak256(storage_key);
1366 Ok(hashed_storage_cursor
1367 .seek_by_key_subkey(keccak256(address), hashed_storage_key)?
1368 .filter(|s| s.key == hashed_storage_key)
1369 .map(|s| s.value))
1370 },
1371 )
1372 }
1373}
1374
1375impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
1376 fn append_history_index<P, T>(
1384 &self,
1385 index_updates: impl IntoIterator<Item = (P, impl IntoIterator<Item = u64>)>,
1386 mut sharded_key_factory: impl FnMut(P, BlockNumber) -> T::Key,
1387 ) -> ProviderResult<()>
1388 where
1389 P: Copy,
1390 T: Table<Value = BlockNumberList>,
1391 {
1392 assert!(!T::DUPSORT, "append_history_index cannot be used with DUPSORT tables");
1395
1396 let mut cursor = self.tx.cursor_write::<T>()?;
1397
1398 for (partial_key, indices) in index_updates {
1399 let last_key = sharded_key_factory(partial_key, u64::MAX);
1400 let mut last_shard = cursor
1401 .seek_exact(last_key.clone())?
1402 .map(|(_, list)| list)
1403 .unwrap_or_else(BlockNumberList::empty);
1404
1405 last_shard.append(indices).map_err(ProviderError::other)?;
1406
1407 if last_shard.len() <= sharded_key::NUM_OF_INDICES_IN_SHARD as u64 {
1409 cursor.upsert(last_key, &last_shard)?;
1410 continue;
1411 }
1412
1413 let chunks = last_shard.iter().chunks(sharded_key::NUM_OF_INDICES_IN_SHARD);
1415 let mut chunks_peekable = chunks.into_iter().peekable();
1416
1417 while let Some(chunk) = chunks_peekable.next() {
1418 let shard = BlockNumberList::new_pre_sorted(chunk);
1419 let highest_block_number = if chunks_peekable.peek().is_some() {
1420 shard.iter().next_back().expect("`chunks` does not return empty list")
1421 } else {
1422 u64::MAX
1424 };
1425
1426 cursor.upsert(sharded_key_factory(partial_key, highest_block_number), &shard)?;
1427 }
1428 }
1429
1430 Ok(())
1431 }
1432
1433 pub fn anchor_pruned_static_files(&self, pivot: BlockNumber) -> ProviderResult<()> {
1445 if !self.cached_storage_settings().storage_v2 {
1446 return Err(ProviderError::other(StaticFileWriterError::new(
1447 "pruned anchor requires storage v2",
1448 )))
1449 }
1450 let static_files = self.static_file_provider();
1451 for segment in StaticFileSegment::iter().filter(|segment| !segment.is_headers()) {
1452 static_files.delete_segment(segment)?;
1453 static_files.get_writer(pivot, segment)?.initialize_pruned_anchor(pivot)?;
1454 }
1455 static_files.set_earliest_history_height(pivot);
1458 Ok(())
1459 }
1460}
1461
1462impl<TX: DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
1463 fn ensure_finish_may_advance(&self, proposed: &StageCheckpoint) -> ProviderResult<()> {
1468 let current = self.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default();
1469 let state_trie_frontier = |checkpoint: &StageCheckpoint| {
1470 checkpoint
1471 .finish_stage_checkpoint()
1472 .and_then(|finish| finish.partial_state_trie())
1473 .unwrap_or(checkpoint.block_number)
1474 };
1475 if proposed.block_number <= current.block_number &&
1476 state_trie_frontier(proposed) <= state_trie_frontier(¤t)
1477 {
1478 return Ok(())
1479 }
1480 match self.snap_attempt()? {
1481 Some(attempt) if !attempt.is_verified() => {
1482 Err(ProviderError::UnverifiedSnapState { attempt: attempt.id().into() })
1483 }
1484 _ => Ok(()),
1485 }
1486 }
1487
1488 pub fn ensure_snap_sync_layout(&self) -> ProviderResult<()> {
1490 if self.cached_storage_settings().use_hashed_state() {
1491 Ok(())
1492 } else {
1493 Err(ProviderError::SnapStorageLayoutUnsupported)
1494 }
1495 }
1496
1497 pub fn snap_bootstraps(&self) -> ProviderResult<bool> {
1503 if let Some(attempt) = self.snap_attempt()? {
1504 return Ok(!attempt.is_verified())
1505 }
1506 let genesis = self.chain_spec().genesis_header().number();
1507 Ok(self
1508 .get_stage_checkpoint(StageId::Execution)?
1509 .is_none_or(|checkpoint| checkpoint.block_number == genesis))
1510 }
1511
1512 pub fn ensure_sync_mode(&self, snap_enabled: bool) -> ProviderResult<()> {
1517 if snap_enabled {
1518 return if self.snap_bootstraps()? { self.ensure_snap_sync_layout() } else { Ok(()) }
1519 }
1520 match self.snap_attempt()? {
1521 Some(attempt) if !attempt.is_verified() => {
1522 Err(ProviderError::SnapStateRequiresSnapSync { attempt: attempt.id().into() })
1523 }
1524 _ => Ok(()),
1525 }
1526 }
1527}
1528
1529impl<TX: DbTx, N: NodeTypes> AccountReader for DatabaseProvider<TX, N> {
1530 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
1531 if self.cached_storage_settings().use_hashed_state() {
1532 let hashed_address = keccak256(address);
1533 Ok(self.tx.get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)?)
1534 } else {
1535 Ok(self.tx.get_by_encoded_key::<tables::PlainAccountState>(address)?)
1536 }
1537 }
1538}
1539
1540impl<TX: DbTx + 'static, N: NodeTypes> AccountExtReader for DatabaseProvider<TX, N> {
1541 fn changed_accounts_with_range(
1542 &self,
1543 range: RangeInclusive<BlockNumber>,
1544 ) -> ProviderResult<BTreeSet<Address>> {
1545 let mut reader = EitherReader::new_account_changesets(self)?;
1546
1547 reader.changed_accounts_with_range(range)
1548 }
1549
1550 fn basic_accounts(
1551 &self,
1552 iter: impl IntoIterator<Item = Address>,
1553 ) -> ProviderResult<Vec<(Address, Option<Account>)>> {
1554 if self.cached_storage_settings().use_hashed_state() {
1555 let mut hashed_accounts = self.tx.cursor_read::<tables::HashedAccounts>()?;
1556 Ok(iter
1557 .into_iter()
1558 .map(|address| {
1559 let hashed_address = keccak256(address);
1560 hashed_accounts.seek_exact(hashed_address).map(|a| (address, a.map(|(_, v)| v)))
1561 })
1562 .collect::<Result<Vec<_>, _>>()?)
1563 } else {
1564 let mut plain_accounts = self.tx.cursor_read::<tables::PlainAccountState>()?;
1565 Ok(iter
1566 .into_iter()
1567 .map(|address| {
1568 plain_accounts.seek_exact(address).map(|a| (address, a.map(|(_, v)| v)))
1569 })
1570 .collect::<Result<Vec<_>, _>>()?)
1571 }
1572 }
1573
1574 fn changed_accounts_and_blocks_with_range(
1575 &self,
1576 range: RangeInclusive<BlockNumber>,
1577 ) -> ProviderResult<BTreeMap<Address, Vec<u64>>> {
1578 let highest_static_block = self
1579 .static_file_provider
1580 .get_highest_static_file_block(StaticFileSegment::AccountChangeSets);
1581
1582 if let Some(highest) = highest_static_block &&
1583 self.cached_storage_settings().storage_v2
1584 {
1585 let start = *range.start();
1586 let static_end = (*range.end()).min(highest);
1587
1588 let mut changed_accounts_and_blocks: BTreeMap<_, Vec<u64>> = BTreeMap::default();
1589 if start <= static_end {
1590 for block in start..=static_end {
1591 let block_changesets = self.account_block_changeset(block)?;
1592 for changeset in block_changesets {
1593 changed_accounts_and_blocks
1594 .entry(changeset.address)
1595 .or_default()
1596 .push(block);
1597 }
1598 }
1599 }
1600
1601 Ok(changed_accounts_and_blocks)
1602 } else {
1603 let mut changeset_cursor = self.tx.cursor_read::<tables::AccountChangeSets>()?;
1604
1605 let account_transitions = changeset_cursor.walk_range(range)?.try_fold(
1606 BTreeMap::new(),
1607 |mut accounts: BTreeMap<Address, Vec<u64>>, entry| -> ProviderResult<_> {
1608 let (index, account) = entry?;
1609 accounts.entry(account.address).or_default().push(index);
1610 Ok(accounts)
1611 },
1612 )?;
1613
1614 Ok(account_transitions)
1615 }
1616 }
1617}
1618
1619impl<TX: DbTx, N: NodeTypes> StorageChangeSetReader for DatabaseProvider<TX, N> {
1620 fn storage_changeset(
1621 &self,
1622 block_number: BlockNumber,
1623 ) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
1624 if self.cached_storage_settings().storage_v2 {
1625 self.static_file_provider.storage_changeset(block_number)
1626 } else {
1627 let range = block_number..=block_number;
1628 let storage_range = BlockNumberAddress::range(range);
1629 self.tx
1630 .cursor_dup_read::<tables::StorageChangeSets>()?
1631 .walk_range(storage_range)?
1632 .map(|r| {
1633 let (bna, entry) = r?;
1634 Ok((bna, entry))
1635 })
1636 .collect()
1637 }
1638 }
1639
1640 fn get_storage_before_block(
1641 &self,
1642 block_number: BlockNumber,
1643 address: Address,
1644 storage_key: B256,
1645 ) -> ProviderResult<Option<StorageEntry>> {
1646 if self.cached_storage_settings().storage_v2 {
1647 self.static_file_provider.get_storage_before_block(block_number, address, storage_key)
1648 } else {
1649 Ok(self
1650 .tx
1651 .cursor_dup_read::<tables::StorageChangeSets>()?
1652 .seek_by_key_subkey(BlockNumberAddress((block_number, address)), storage_key)?
1653 .filter(|entry| entry.key == storage_key))
1654 }
1655 }
1656
1657 fn storage_changesets_range(
1658 &self,
1659 range: impl RangeBounds<BlockNumber>,
1660 ) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
1661 if self.cached_storage_settings().storage_v2 {
1662 self.static_file_provider.storage_changesets_range(range)
1663 } else {
1664 self.tx
1665 .cursor_dup_read::<tables::StorageChangeSets>()?
1666 .walk_range(BlockNumberAddressRange::from(range))?
1667 .map(|r| {
1668 let (bna, entry) = r?;
1669 Ok((bna, entry))
1670 })
1671 .collect()
1672 }
1673 }
1674}
1675
1676impl<TX: DbTx, N: NodeTypes> ChangeSetReader for DatabaseProvider<TX, N> {
1677 fn account_block_changeset(
1678 &self,
1679 block_number: BlockNumber,
1680 ) -> ProviderResult<Vec<AccountBeforeTx>> {
1681 if self.cached_storage_settings().storage_v2 {
1682 let static_changesets =
1683 self.static_file_provider.account_block_changeset(block_number)?;
1684 Ok(static_changesets)
1685 } else {
1686 let range = block_number..=block_number;
1687 self.tx
1688 .cursor_read::<tables::AccountChangeSets>()?
1689 .walk_range(range)?
1690 .map(|result| -> ProviderResult<_> {
1691 let (_, account_before) = result?;
1692 Ok(account_before)
1693 })
1694 .collect()
1695 }
1696 }
1697
1698 fn get_account_before_block(
1699 &self,
1700 block_number: BlockNumber,
1701 address: Address,
1702 ) -> ProviderResult<Option<AccountBeforeTx>> {
1703 if self.cached_storage_settings().storage_v2 {
1704 Ok(self.static_file_provider.get_account_before_block(block_number, address)?)
1705 } else {
1706 self.tx
1707 .cursor_dup_read::<tables::AccountChangeSets>()?
1708 .seek_by_key_subkey(block_number, address)?
1709 .filter(|acc| acc.address == address)
1710 .map(Ok)
1711 .transpose()
1712 }
1713 }
1714
1715 fn account_changesets_range(
1716 &self,
1717 range: impl core::ops::RangeBounds<BlockNumber>,
1718 ) -> ProviderResult<Vec<(BlockNumber, AccountBeforeTx)>> {
1719 if self.cached_storage_settings().storage_v2 {
1720 self.static_file_provider.account_changesets_range(range)
1721 } else {
1722 self.tx
1723 .cursor_read::<tables::AccountChangeSets>()?
1724 .walk_range(to_range(range))?
1725 .map(|r| r.map_err(Into::into))
1726 .collect()
1727 }
1728 }
1729}
1730
1731impl<TX: DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
1732 fn history_rocksdb_snapshot(&self) -> Option<&RocksReadSnapshot<'_>> {
1745 self.rocksdb_history_snapshot
1746 .get_or_init(|| {
1747 self.cached_storage_settings()
1748 .storage_v2
1749 .then(|| self.rocksdb_provider.owned_snapshot())
1750 })
1751 .as_ref()
1752 .map(OwnedRocksReadSnapshot::as_snapshot)
1753 }
1754}
1755
1756impl<TX: DbTx + 'static, N: NodeTypes> HistoryReader for DatabaseProvider<TX, N> {
1757 fn account_history_info(
1758 &self,
1759 address: Address,
1760 block_number: BlockNumber,
1761 lowest_available_block_number: Option<BlockNumber>,
1762 ) -> ProviderResult<HistoryInfo> {
1763 let visible_tip = self.best_block_number()?;
1764 let mut reader = EitherReader::new_accounts_history(self, self.history_rocksdb_snapshot())?;
1765 reader
1766 .account_history_info(address, block_number, lowest_available_block_number, visible_tip)
1767 .map(Into::into)
1768 }
1769
1770 fn storage_history_info(
1771 &self,
1772 address: Address,
1773 storage_key: B256,
1774 block_number: BlockNumber,
1775 lowest_available_block_number: Option<BlockNumber>,
1776 ) -> ProviderResult<HistoryInfo> {
1777 let visible_tip = self.best_block_number()?;
1778 let mut reader = EitherReader::new_storages_history(self, self.history_rocksdb_snapshot())?;
1779 reader
1780 .storage_history_info(
1781 address,
1782 storage_key,
1783 block_number,
1784 lowest_available_block_number,
1785 visible_tip,
1786 )
1787 .map(Into::into)
1788 }
1789}
1790
1791impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderSyncGapProvider
1792 for DatabaseProvider<TX, N>
1793{
1794 type Header = HeaderTy<N>;
1795
1796 fn local_tip_header(
1797 &self,
1798 highest_uninterrupted_block: BlockNumber,
1799 ) -> ProviderResult<SealedHeader<Self::Header>> {
1800 let static_file_provider = self.static_file_provider();
1801
1802 let next_static_file_block_num = static_file_provider
1805 .get_highest_static_file_block(StaticFileSegment::Headers)
1806 .map(|id| id + 1)
1807 .unwrap_or_default();
1808 let next_block = highest_uninterrupted_block + 1;
1809
1810 match next_static_file_block_num.cmp(&next_block) {
1811 Ordering::Greater => {
1814 let mut static_file_producer =
1815 static_file_provider.latest_writer(StaticFileSegment::Headers)?;
1816 static_file_producer.prune_headers(next_static_file_block_num - next_block)?;
1817 static_file_producer.commit()?
1820 }
1821 Ordering::Less => {
1822 return Err(ProviderError::HeaderNotFound(next_static_file_block_num.into()))
1824 }
1825 Ordering::Equal => {}
1826 }
1827
1828 let local_head = static_file_provider
1829 .sealed_header(highest_uninterrupted_block)?
1830 .ok_or_else(|| ProviderError::HeaderNotFound(highest_uninterrupted_block.into()))?;
1831
1832 Ok(local_head)
1833 }
1834}
1835
1836impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderProvider for DatabaseProvider<TX, N> {
1837 type Header = HeaderTy<N>;
1838
1839 fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
1840 if let Some(num) = self.block_number(block_hash)? {
1841 Ok(self.header_by_number(num)?)
1842 } else {
1843 Ok(None)
1844 }
1845 }
1846
1847 fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
1848 self.static_file_provider.header_by_number(num)
1849 }
1850
1851 fn headers_range(
1852 &self,
1853 range: impl RangeBounds<BlockNumber>,
1854 ) -> ProviderResult<Vec<Self::Header>> {
1855 self.static_file_provider.headers_range(range)
1856 }
1857
1858 fn sealed_header(
1859 &self,
1860 number: BlockNumber,
1861 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
1862 self.static_file_provider.sealed_header(number)
1863 }
1864
1865 fn sealed_headers_while(
1866 &self,
1867 range: impl RangeBounds<BlockNumber>,
1868 predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
1869 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
1870 self.static_file_provider.sealed_headers_while(range, predicate)
1871 }
1872}
1873
1874impl<TX: DbTx + 'static, N: NodeTypes> BlockHashReader for DatabaseProvider<TX, N> {
1875 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
1876 self.static_file_provider.block_hash(number)
1877 }
1878
1879 fn canonical_hashes_range(
1880 &self,
1881 start: BlockNumber,
1882 end: BlockNumber,
1883 ) -> ProviderResult<Vec<B256>> {
1884 self.static_file_provider.canonical_hashes_range(start, end)
1885 }
1886}
1887
1888impl<TX: DbTx + 'static, N: NodeTypes> BlockNumReader for DatabaseProvider<TX, N> {
1889 fn chain_info(&self) -> ProviderResult<ChainInfo> {
1890 let best_number = self.best_block_number()?;
1891 let best_hash = self.block_hash(best_number)?.unwrap_or_default();
1892 Ok(ChainInfo { best_hash, best_number })
1893 }
1894
1895 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
1896 Ok(self
1899 .get_stage_checkpoint(StageId::Finish)?
1900 .map(|checkpoint| checkpoint.block_number)
1901 .unwrap_or_default())
1902 }
1903
1904 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
1905 self.static_file_provider.last_block_number()
1906 }
1907
1908 fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
1909 Ok(self.tx.get::<tables::HeaderNumbers>(hash)?)
1910 }
1911}
1912
1913impl<TX: DbTx + 'static, N: NodeTypesForProvider> BlockReader for DatabaseProvider<TX, N> {
1914 type Block = BlockTy<N>;
1915
1916 fn find_block_by_hash(
1917 &self,
1918 hash: B256,
1919 source: BlockSource,
1920 ) -> ProviderResult<Option<Self::Block>> {
1921 if source.is_canonical() {
1922 self.block(hash.into())
1923 } else {
1924 Ok(None)
1925 }
1926 }
1927
1928 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
1936 if let Some(number) = self.convert_hash_or_number(id)? {
1937 let earliest_available = self.static_file_provider.earliest_history_height();
1938 if number < earliest_available {
1939 return Err(ProviderError::BlockExpired { requested: number, earliest_available })
1940 }
1941
1942 let Some(header) = self.header_by_number(number)? else { return Ok(None) };
1943
1944 let Some(transactions) = self.transactions_by_block(number.into())? else {
1949 return Ok(None)
1950 };
1951
1952 let body = self
1953 .storage
1954 .reader()
1955 .read_block_bodies(self, vec![(&header, transactions)])?
1956 .pop()
1957 .ok_or(ProviderError::InvalidStorageOutput)?;
1958
1959 return Ok(Some(Self::Block::new(header, body)))
1960 }
1961
1962 Ok(None)
1963 }
1964
1965 fn pending_block(&self) -> ProviderResult<Option<Arc<RecoveredBlock<Self::Block>>>> {
1966 Ok(None)
1967 }
1968
1969 fn pending_block_and_receipts(
1970 &self,
1971 ) -> ProviderResult<Option<RecoveredBlockAndExecutionOutput<Self::Block, Self::Receipt>>> {
1972 Ok(None)
1973 }
1974
1975 fn recovered_block(
1984 &self,
1985 id: BlockHashOrNumber,
1986 transaction_kind: TransactionVariant,
1987 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1988 self.recovered_block(
1989 id,
1990 transaction_kind,
1991 |block_number| self.header_by_number(block_number),
1992 |header, body, senders| {
1993 Self::Block::new(header, body)
1994 .try_into_recovered_unchecked(senders)
1998 .map(Some)
1999 .map_err(|_| ProviderError::SenderRecoveryError)
2000 },
2001 )
2002 }
2003
2004 fn sealed_block_with_senders(
2005 &self,
2006 id: BlockHashOrNumber,
2007 transaction_kind: TransactionVariant,
2008 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
2009 self.recovered_block(
2010 id,
2011 transaction_kind,
2012 |block_number| self.sealed_header(block_number),
2013 |header, body, senders| {
2014 Self::Block::new_sealed(header, body)
2015 .try_with_senders_unchecked(senders)
2019 .map(Some)
2020 .map_err(|_| ProviderError::SenderRecoveryError)
2021 },
2022 )
2023 }
2024
2025 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
2026 self.block_range(
2027 range,
2028 |range| self.headers_range(range),
2029 |header, body, _| Ok(Self::Block::new(header, body)),
2030 )
2031 }
2032
2033 fn block_with_senders_range(
2034 &self,
2035 range: RangeInclusive<BlockNumber>,
2036 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
2037 self.block_with_senders_range(
2038 range,
2039 |range| self.headers_range(range),
2040 |header, body, senders| {
2041 Self::Block::new(header, body)
2042 .try_into_recovered_unchecked(senders)
2043 .map_err(|_| ProviderError::SenderRecoveryError)
2044 },
2045 )
2046 }
2047
2048 fn recovered_block_range(
2049 &self,
2050 range: RangeInclusive<BlockNumber>,
2051 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
2052 self.block_with_senders_range(
2053 range,
2054 |range| self.sealed_headers_range(range),
2055 |header, body, senders| {
2056 Self::Block::new_sealed(header, body)
2057 .try_with_senders(senders)
2058 .map_err(|_| ProviderError::SenderRecoveryError)
2059 },
2060 )
2061 }
2062
2063 fn block_by_transaction_id(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
2064 Ok(self
2065 .tx
2066 .cursor_read::<tables::TransactionBlocks>()?
2067 .seek(id)
2068 .map(|b| b.map(|(_, bn)| bn))?)
2069 }
2070}
2071
2072impl<TX: DbTx + 'static, N: NodeTypesForProvider> TransactionsProviderExt
2073 for DatabaseProvider<TX, N>
2074{
2075 fn transaction_hashes_by_range(
2078 &self,
2079 tx_range: Range<TxNumber>,
2080 ) -> ProviderResult<Vec<(TxHash, TxNumber)>> {
2081 self.static_file_provider.transaction_hashes_by_range(tx_range)
2082 }
2083}
2084
2085impl<TX: DbTx + 'static, N: NodeTypesForProvider> TransactionsProvider for DatabaseProvider<TX, N> {
2087 type Transaction = TxTy<N>;
2088
2089 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
2090 self.with_rocksdb_snapshot(|rocksdb_ref| {
2091 let mut reader = EitherReader::new_transaction_hash_numbers(self, rocksdb_ref)?;
2092 reader.get_transaction_hash_number(tx_hash)
2093 })
2094 }
2095
2096 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
2097 self.static_file_provider.transaction_by_id(id)
2098 }
2099
2100 fn transaction_by_id_unhashed(
2101 &self,
2102 id: TxNumber,
2103 ) -> ProviderResult<Option<Self::Transaction>> {
2104 self.static_file_provider.transaction_by_id_unhashed(id)
2105 }
2106
2107 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
2108 if let Some(id) = self.transaction_id(hash)? {
2109 Ok(self.transaction_by_id_unhashed(id)?)
2110 } else {
2111 Ok(None)
2112 }
2113 }
2114
2115 fn transaction_by_hash_with_meta(
2116 &self,
2117 tx_hash: TxHash,
2118 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
2119 if let Some(transaction_id) = self.transaction_id(tx_hash)? &&
2120 let Some(transaction) = self.transaction_by_id_unhashed(transaction_id)? &&
2121 let Some(block_number) = self.block_by_transaction_id(transaction_id)? &&
2122 let Some(sealed_header) = self.sealed_header(block_number)?
2123 {
2124 let (header, block_hash) = sealed_header.split();
2125 if let Some(block_body) = self.block_body_indices(block_number)? {
2126 let index = transaction_id - block_body.first_tx_num();
2131
2132 let meta = TransactionMeta {
2133 tx_hash,
2134 index,
2135 block_hash,
2136 block_number,
2137 base_fee: header.base_fee_per_gas(),
2138 excess_blob_gas: header.excess_blob_gas(),
2139 timestamp: header.timestamp(),
2140 };
2141
2142 return Ok(Some((transaction, meta)))
2143 }
2144 }
2145
2146 Ok(None)
2147 }
2148
2149 fn transactions_by_block(
2150 &self,
2151 id: BlockHashOrNumber,
2152 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
2153 if let Some(block_number) = self.convert_hash_or_number(id)? &&
2154 let Some(body) = self.block_body_indices(block_number)?
2155 {
2156 let tx_range = body.tx_num_range();
2157 return if tx_range.is_empty() {
2158 Ok(Some(Vec::new()))
2159 } else {
2160 self.transactions_by_tx_range(tx_range).map(Some)
2161 }
2162 }
2163 Ok(None)
2164 }
2165
2166 fn transactions_by_block_range(
2167 &self,
2168 range: impl RangeBounds<BlockNumber>,
2169 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
2170 let range = to_range(range);
2171
2172 self.block_body_indices_range(range.start..=range.end.saturating_sub(1))?
2173 .into_iter()
2174 .map(|body| {
2175 let tx_num_range = body.tx_num_range();
2176 if tx_num_range.is_empty() {
2177 Ok(Vec::new())
2178 } else {
2179 self.transactions_by_tx_range(tx_num_range)
2180 }
2181 })
2182 .collect()
2183 }
2184
2185 fn transactions_by_tx_range(
2186 &self,
2187 range: impl RangeBounds<TxNumber>,
2188 ) -> ProviderResult<Vec<Self::Transaction>> {
2189 self.static_file_provider.transactions_by_tx_range(range)
2190 }
2191
2192 fn senders_by_tx_range(
2193 &self,
2194 range: impl RangeBounds<TxNumber>,
2195 ) -> ProviderResult<Vec<Address>> {
2196 if EitherWriterDestination::senders(self).is_static_file() {
2197 self.static_file_provider.senders_by_tx_range(range)
2198 } else {
2199 self.cursor_read_collect::<tables::TransactionSenders>(range)
2200 }
2201 }
2202
2203 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
2204 if EitherWriterDestination::senders(self).is_static_file() {
2205 self.static_file_provider.transaction_sender(id)
2206 } else {
2207 Ok(self.tx.get::<tables::TransactionSenders>(id)?)
2208 }
2209 }
2210}
2211
2212impl<TX: DbTx + 'static, N: NodeTypesForProvider> ReceiptProvider for DatabaseProvider<TX, N> {
2213 type Receipt = ReceiptTy<N>;
2214
2215 fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
2216 self.static_file_provider.get_with_static_file_or_database(
2217 StaticFileSegment::Receipts,
2218 id,
2219 |static_file| static_file.receipt(id),
2220 || Ok(self.tx.get::<tables::Receipts<Self::Receipt>>(id)?),
2221 )
2222 }
2223
2224 fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
2225 if let Some(id) = self.transaction_id(hash)? {
2226 self.receipt(id)
2227 } else {
2228 Ok(None)
2229 }
2230 }
2231
2232 fn receipts_by_block(
2233 &self,
2234 block: BlockHashOrNumber,
2235 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
2236 if let Some(number) = self.convert_hash_or_number(block)? &&
2237 let Some(body) = self.block_body_indices(number)?
2238 {
2239 let tx_range = body.tx_num_range();
2240 return if tx_range.is_empty() {
2241 Ok(Some(Vec::new()))
2242 } else {
2243 let receipts = self.receipts_by_tx_range(tx_range)?;
2244
2245 if receipts.len() != body.tx_count as usize {
2246 return Ok(None)
2247 }
2248
2249 Ok(Some(receipts))
2250 }
2251 }
2252 Ok(None)
2253 }
2254
2255 fn receipts_by_tx_range(
2256 &self,
2257 range: impl RangeBounds<TxNumber>,
2258 ) -> ProviderResult<Vec<Self::Receipt>> {
2259 self.static_file_provider.get_range_with_static_file_or_database(
2260 StaticFileSegment::Receipts,
2261 to_range(range),
2262 |static_file, range, _| static_file.receipts_by_tx_range(range),
2263 |range, _| self.cursor_read_collect::<tables::Receipts<Self::Receipt>>(range),
2264 |_| true,
2265 )
2266 }
2267
2268 fn receipts_by_block_range(
2269 &self,
2270 block_range: RangeInclusive<BlockNumber>,
2271 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
2272 if block_range.is_empty() {
2273 return Ok(Vec::new());
2274 }
2275
2276 let range_len = block_range.end().saturating_sub(*block_range.start()) as usize + 1;
2278 let mut block_body_indices = Vec::with_capacity(range_len);
2279 for block_num in block_range {
2280 if let Some(indices) = self.block_body_indices(block_num)? {
2281 block_body_indices.push(indices);
2282 } else {
2283 block_body_indices.push(StoredBlockBodyIndices::default());
2285 }
2286 }
2287
2288 if block_body_indices.is_empty() {
2289 return Ok(Vec::new());
2290 }
2291
2292 let non_empty_blocks: Vec<_> =
2294 block_body_indices.iter().filter(|indices| indices.tx_count > 0).collect();
2295
2296 if non_empty_blocks.is_empty() {
2297 return Ok(vec![Vec::new(); block_body_indices.len()]);
2299 }
2300
2301 let first_tx = non_empty_blocks[0].first_tx_num();
2303 let last_tx = non_empty_blocks[non_empty_blocks.len() - 1].last_tx_num();
2304
2305 let all_receipts = self.receipts_by_tx_range(first_tx..=last_tx)?;
2307 let mut receipts_iter = all_receipts.into_iter();
2308
2309 let mut result = Vec::with_capacity(block_body_indices.len());
2311 for indices in &block_body_indices {
2312 if indices.tx_count == 0 {
2313 result.push(Vec::new());
2314 } else {
2315 let block_receipts =
2316 receipts_iter.by_ref().take(indices.tx_count as usize).collect();
2317 result.push(block_receipts);
2318 }
2319 }
2320
2321 Ok(result)
2322 }
2323}
2324
2325impl<TX: DbTx + 'static, N: NodeTypesForProvider> BlockBodyIndicesProvider
2326 for DatabaseProvider<TX, N>
2327{
2328 fn block_body_indices(&self, num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
2329 Ok(self.tx.get::<tables::BlockBodyIndices>(num)?)
2330 }
2331
2332 fn block_body_indices_range(
2333 &self,
2334 range: RangeInclusive<BlockNumber>,
2335 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
2336 self.cursor_read_collect::<tables::BlockBodyIndices>(range)
2337 }
2338}
2339
2340impl<TX: DbTx, N: NodeTypes> StageCheckpointReader for DatabaseProvider<TX, N> {
2341 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
2342 Ok(if let Some(encoded) = id.get_pre_encoded() {
2343 self.tx.get_by_encoded_key::<tables::StageCheckpoints>(encoded)?
2344 } else {
2345 self.tx.get::<tables::StageCheckpoints>(id.to_string())?
2346 })
2347 }
2348
2349 fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult<Option<Vec<u8>>> {
2351 Ok(self.tx.get::<tables::StageCheckpointProgresses>(id.to_string())?)
2352 }
2353
2354 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
2355 self.tx
2356 .cursor_read::<tables::StageCheckpoints>()?
2357 .walk(None)?
2358 .collect::<Result<Vec<(String, StageCheckpoint)>, _>>()
2359 .map_err(ProviderError::Database)
2360 }
2361}
2362
2363impl<TX: DbTxMut + DbTx, N: NodeTypes> StageCheckpointWriter for DatabaseProvider<TX, N> {
2364 fn save_stage_checkpoint(
2366 &self,
2367 id: StageId,
2368 checkpoint: StageCheckpoint,
2369 ) -> ProviderResult<()> {
2370 if id.is_finish() {
2371 self.ensure_finish_may_advance(&checkpoint)?;
2372 }
2373 Ok(self.tx.put::<tables::StageCheckpoints>(id.to_string(), checkpoint)?)
2374 }
2375
2376 fn save_stage_checkpoint_progress(
2378 &self,
2379 id: StageId,
2380 checkpoint: Vec<u8>,
2381 ) -> ProviderResult<()> {
2382 Ok(self.tx.put::<tables::StageCheckpointProgresses>(id.to_string(), checkpoint)?)
2383 }
2384
2385 #[instrument(level = "debug", target = "providers::db", skip_all)]
2386 fn update_pipeline_stages(
2387 &self,
2388 block_number: BlockNumber,
2389 drop_stage_checkpoint: bool,
2390 ) -> ProviderResult<()> {
2391 let current = self.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default();
2392 let proposed = StageCheckpoint {
2393 block_number,
2394 ..if drop_stage_checkpoint { Default::default() } else { current }
2395 };
2396 self.ensure_finish_may_advance(&proposed)?;
2397
2398 let mut cursor = self.tx.cursor_write::<tables::StageCheckpoints>()?;
2400 for stage_id in StageId::ALL {
2401 let (_, checkpoint) = cursor.seek_exact(stage_id.to_string())?.unwrap_or_default();
2402 cursor.upsert(
2403 stage_id.to_string(),
2404 &StageCheckpoint {
2405 block_number,
2406 ..if drop_stage_checkpoint { Default::default() } else { checkpoint }
2407 },
2408 )?;
2409 }
2410
2411 Ok(())
2412 }
2413}
2414
2415impl<TX: DbTxMut + DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
2416 fn update_pipeline_stages_after_unwind(
2419 &self,
2420 block_number: BlockNumber,
2421 ) -> ProviderResult<PersistenceFrontiers> {
2422 let partial_state_trie = self
2423 .get_stage_checkpoint(StageId::Finish)?
2424 .map(|checkpoint| {
2425 checkpoint
2426 .finish_stage_checkpoint()
2427 .and_then(|finish| finish.partial_state_trie())
2428 .unwrap_or(checkpoint.block_number)
2429 })
2430 .unwrap_or(block_number)
2431 .min(block_number);
2432
2433 self.update_pipeline_stages(block_number, true)?;
2434 if partial_state_trie < block_number {
2435 self.save_stage_checkpoint(
2436 StageId::Finish,
2437 StageCheckpoint::new(block_number).with_finish_stage_checkpoint(FinishCheckpoint {
2438 partial_state_trie: Some(partial_state_trie),
2439 }),
2440 )?;
2441 }
2442
2443 Ok(PersistenceFrontiers { db_tip: block_number, partial_state_trie })
2444 }
2445}
2446
2447impl<TX: DbTx + 'static, N: NodeTypes> StorageReader for DatabaseProvider<TX, N> {
2448 fn plain_state_storages(
2449 &self,
2450 addresses_with_keys: impl IntoIterator<Item = (Address, impl IntoIterator<Item = B256>)>,
2451 ) -> ProviderResult<Vec<(Address, Vec<StorageEntry>)>> {
2452 if self.cached_storage_settings().use_hashed_state() {
2453 let mut hashed_storage = self.tx.cursor_dup_read::<tables::HashedStorages>()?;
2454
2455 addresses_with_keys
2456 .into_iter()
2457 .map(|(address, storage)| {
2458 let hashed_address = keccak256(address);
2459 storage
2460 .into_iter()
2461 .map(|key| -> ProviderResult<_> {
2462 let hashed_key = keccak256(key);
2463 let value = hashed_storage
2464 .seek_by_key_subkey(hashed_address, hashed_key)?
2465 .filter(|v| v.key == hashed_key)
2466 .map(|v| v.value)
2467 .unwrap_or_default();
2468 Ok(StorageEntry { key, value })
2469 })
2470 .collect::<ProviderResult<Vec<_>>>()
2471 .map(|storage| (address, storage))
2472 })
2473 .collect::<ProviderResult<Vec<(_, _)>>>()
2474 } else {
2475 let mut plain_storage = self.tx.cursor_dup_read::<tables::PlainStorageState>()?;
2476
2477 addresses_with_keys
2478 .into_iter()
2479 .map(|(address, storage)| {
2480 storage
2481 .into_iter()
2482 .map(|key| -> ProviderResult<_> {
2483 Ok(plain_storage
2484 .seek_by_key_subkey(address, key)?
2485 .filter(|v| v.key == key)
2486 .unwrap_or_else(|| StorageEntry { key, value: Default::default() }))
2487 })
2488 .collect::<ProviderResult<Vec<_>>>()
2489 .map(|storage| (address, storage))
2490 })
2491 .collect::<ProviderResult<Vec<(_, _)>>>()
2492 }
2493 }
2494
2495 fn changed_storages_with_range(
2496 &self,
2497 range: RangeInclusive<BlockNumber>,
2498 ) -> ProviderResult<BTreeMap<Address, BTreeSet<B256>>> {
2499 if self.cached_storage_settings().storage_v2 {
2500 self.storage_changesets_range(range)?.into_iter().try_fold(
2501 BTreeMap::new(),
2502 |mut accounts: BTreeMap<Address, BTreeSet<B256>>, entry| {
2503 let (BlockNumberAddress((_, address)), storage_entry) = entry;
2504 accounts.entry(address).or_default().insert(storage_entry.key);
2505 Ok(accounts)
2506 },
2507 )
2508 } else {
2509 self.tx
2510 .cursor_read::<tables::StorageChangeSets>()?
2511 .walk_range(BlockNumberAddress::range(range))?
2512 .try_fold(
2515 BTreeMap::new(),
2516 |mut accounts: BTreeMap<Address, BTreeSet<B256>>, entry| {
2517 let (BlockNumberAddress((_, address)), storage_entry) = entry?;
2518 accounts.entry(address).or_default().insert(storage_entry.key);
2519 Ok(accounts)
2520 },
2521 )
2522 }
2523 }
2524
2525 fn changed_storages_and_blocks_with_range(
2526 &self,
2527 range: RangeInclusive<BlockNumber>,
2528 ) -> ProviderResult<BTreeMap<(Address, B256), Vec<u64>>> {
2529 if self.cached_storage_settings().storage_v2 {
2530 self.storage_changesets_range(range)?.into_iter().try_fold(
2531 BTreeMap::new(),
2532 |mut storages: BTreeMap<(Address, B256), Vec<u64>>, (index, storage)| {
2533 storages
2534 .entry((index.address(), storage.key))
2535 .or_default()
2536 .push(index.block_number());
2537 Ok(storages)
2538 },
2539 )
2540 } else {
2541 let mut changeset_cursor = self.tx.cursor_read::<tables::StorageChangeSets>()?;
2542
2543 let storage_changeset_lists =
2544 changeset_cursor.walk_range(BlockNumberAddress::range(range))?.try_fold(
2545 BTreeMap::new(),
2546 |mut storages: BTreeMap<(Address, B256), Vec<u64>>,
2547 entry|
2548 -> ProviderResult<_> {
2549 let (index, storage) = entry?;
2550 storages
2551 .entry((index.address(), storage.key))
2552 .or_default()
2553 .push(index.block_number());
2554 Ok(storages)
2555 },
2556 )?;
2557
2558 Ok(storage_changeset_lists)
2559 }
2560 }
2561}
2562
2563impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateWriter
2564 for DatabaseProvider<TX, N>
2565{
2566 type Receipt = ReceiptTy<N>;
2567
2568 #[instrument(level = "debug", target = "providers::db", skip_all)]
2569 fn write_state<'a>(
2570 &self,
2571 execution_outcome: impl Into<WriteStateInput<'a, Self::Receipt>>,
2572 is_value_known: OriginalValuesKnown,
2573 config: StateWriteConfig,
2574 ) -> ProviderResult<()> {
2575 let execution_outcome = execution_outcome.into();
2576
2577 if self.cached_storage_settings().use_hashed_state() &&
2578 !config.write_receipts &&
2579 !config.write_account_changesets &&
2580 !config.write_storage_changesets
2581 {
2582 self.write_bytecodes(
2586 execution_outcome.state().contracts.iter().map(|(h, b)| (*h, Bytecode(b.clone()))),
2587 )?;
2588 return Ok(());
2589 }
2590
2591 let first_block = execution_outcome.first_block();
2592 let (plain_state, reverts) =
2593 execution_outcome.state().to_plain_state_and_reverts(is_value_known);
2594
2595 self.write_state_reverts(reverts, first_block, config)?;
2596 self.write_state_changes(plain_state)?;
2597
2598 if !config.write_receipts {
2599 return Ok(());
2600 }
2601
2602 let block_count = execution_outcome.len() as u64;
2603 let last_block = execution_outcome.last_block();
2604 let block_range = first_block..=last_block;
2605
2606 let tip = self.last_block_number()?.max(last_block);
2607
2608 let block_indices: Vec<_> = self
2610 .block_body_indices_range(block_range)?
2611 .into_iter()
2612 .map(|b| b.first_tx_num)
2613 .collect();
2614
2615 if block_indices.len() < block_count as usize {
2617 let missing_blocks = block_count - block_indices.len() as u64;
2618 return Err(ProviderError::BlockBodyIndicesNotFound(
2619 last_block.saturating_sub(missing_blocks - 1),
2620 ));
2621 }
2622
2623 let mut receipts_writer = EitherWriter::new_receipts(self, first_block)?;
2624
2625 let has_contract_log_filter = !self.prune_modes.receipts_log_filter.is_empty();
2626 let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?;
2627
2628 let prunable_receipts = (EitherWriter::receipts_destination(self).is_database() ||
2636 self.static_file_provider()
2637 .get_highest_static_file_tx(StaticFileSegment::Receipts)
2638 .is_none()) &&
2639 PruneMode::Distance(self.minimum_pruning_distance).should_prune(first_block, tip);
2640
2641 let mut allowed_addresses: AddressSet = AddressSet::default();
2643 for (_, addresses) in contract_log_pruner.range(..first_block) {
2644 allowed_addresses.extend(addresses.iter().copied());
2645 }
2646
2647 for (idx, (receipts, first_tx_index)) in
2648 execution_outcome.receipts().zip(block_indices).enumerate()
2649 {
2650 let block_number = first_block + idx as u64;
2651
2652 receipts_writer.increment_block(block_number)?;
2654
2655 if prunable_receipts &&
2657 self.prune_modes
2658 .receipts
2659 .is_some_and(|mode| mode.should_prune(block_number, tip))
2660 {
2661 continue
2662 }
2663
2664 if let Some(new_addresses) = contract_log_pruner.get(&block_number) {
2666 allowed_addresses.extend(new_addresses.iter().copied());
2667 }
2668
2669 for (idx, receipt) in receipts.iter().enumerate() {
2670 let receipt_idx = first_tx_index + idx as u64;
2671 if prunable_receipts &&
2674 has_contract_log_filter &&
2675 !receipt.logs().iter().any(|log| allowed_addresses.contains(&log.address))
2676 {
2677 continue
2678 }
2679
2680 receipts_writer.append_receipt(receipt_idx, receipt)?;
2681 }
2682 }
2683
2684 Ok(())
2685 }
2686
2687 fn write_state_reverts(
2688 &self,
2689 reverts: PlainStateReverts,
2690 first_block: BlockNumber,
2691 config: StateWriteConfig,
2692 ) -> ProviderResult<()> {
2693 if config.write_storage_changesets {
2695 tracing::trace!("Writing storage changes");
2696 let mut storages_cursor =
2697 self.tx_ref().cursor_dup_write::<tables::PlainStorageState>()?;
2698 for (block_index, mut storage_changes) in reverts.storage.into_iter().enumerate() {
2699 let block_number = first_block + block_index as BlockNumber;
2700
2701 tracing::trace!(block_number, "Writing block change");
2702 storage_changes.par_sort_unstable_by_key(|a| a.address);
2704 let total_changes =
2705 storage_changes.iter().map(|change| change.storage_revert.len()).sum();
2706 let mut changeset = Vec::with_capacity(total_changes);
2707 for PlainStorageRevert { address, wiped, storage_revert } in storage_changes {
2708 let mut storage = storage_revert
2709 .into_iter()
2710 .map(|(k, v)| (B256::from(k), v))
2711 .collect::<Vec<_>>();
2712 storage.par_sort_unstable_by_key(|a| a.0);
2714
2715 let mut wiped_storage = Vec::new();
2723 if wiped {
2724 tracing::trace!(?address, "Wiping storage");
2725 if let Some((_, entry)) = storages_cursor.seek_exact(address)? {
2726 wiped_storage.push((entry.key, entry.value));
2727 while let Some(entry) = storages_cursor.next_dup_val()? {
2728 wiped_storage.push((entry.key, entry.value))
2729 }
2730 }
2731 }
2732
2733 tracing::trace!(?address, ?storage, "Writing storage reverts");
2734 for (key, value) in StorageRevertsIter::new(storage, wiped_storage) {
2735 changeset.push(StorageBeforeTx { address, key, value });
2736 }
2737 }
2738
2739 let mut storage_changesets_writer =
2740 EitherWriter::new_storage_changesets(self, block_number)?;
2741 storage_changesets_writer.append_storage_changeset(block_number, changeset)?;
2742 }
2743 }
2744
2745 if !config.write_account_changesets {
2746 return Ok(());
2747 }
2748
2749 tracing::trace!(?first_block, "Writing account changes");
2751 for (block_index, account_block_reverts) in reverts.accounts.into_iter().enumerate() {
2752 let block_number = first_block + block_index as BlockNumber;
2753 let changeset = account_block_reverts
2754 .into_iter()
2755 .map(|(address, info)| AccountBeforeTx { address, info: info.map(Into::into) })
2756 .collect::<Vec<_>>();
2757 let mut account_changesets_writer =
2758 EitherWriter::new_account_changesets(self, block_number)?;
2759
2760 account_changesets_writer.append_account_changeset(block_number, changeset)?;
2761 }
2762
2763 Ok(())
2764 }
2765
2766 fn write_state_changes(&self, mut changes: StateChangeset) -> ProviderResult<()> {
2767 changes.accounts.par_sort_by_key(|a| a.0);
2770 changes.storage.par_sort_by_key(|a| a.address);
2771 changes.contracts.par_sort_by_key(|a| a.0);
2772
2773 if !self.cached_storage_settings().use_hashed_state() {
2774 tracing::trace!(len = changes.accounts.len(), "Writing new account state");
2776 let mut accounts_cursor = self.tx_ref().cursor_write::<tables::PlainAccountState>()?;
2777 for (address, account) in changes.accounts {
2779 if let Some(account) = account {
2780 tracing::trace!(?address, "Updating plain state account");
2781 accounts_cursor.upsert(address, &account.into())?;
2782 } else if accounts_cursor.seek_exact(address)?.is_some() {
2783 tracing::trace!(?address, "Deleting plain state account");
2784 accounts_cursor.delete_current()?;
2785 }
2786 }
2787
2788 tracing::trace!(len = changes.storage.len(), "Writing new storage state");
2790 let mut storages_cursor =
2791 self.tx_ref().cursor_dup_write::<tables::PlainStorageState>()?;
2792 for PlainStorageChangeset { address, wipe_storage, storage } in changes.storage {
2793 if wipe_storage && storages_cursor.seek_exact(address)?.is_some() {
2795 storages_cursor.delete_current_duplicates()?;
2796 }
2797 let mut storage = storage
2799 .into_iter()
2800 .map(|(k, value)| StorageEntry { key: k.into(), value })
2801 .collect::<Vec<_>>();
2802 storage.par_sort_unstable_by_key(|a| a.key);
2804
2805 for entry in storage {
2806 tracing::trace!(?address, ?entry.key, "Updating plain state storage");
2807 if let Some(db_entry) =
2808 storages_cursor.seek_by_key_subkey(address, entry.key)? &&
2809 db_entry.key == entry.key
2810 {
2811 storages_cursor.delete_current()?;
2812 }
2813
2814 if !entry.value.is_zero() {
2815 storages_cursor.upsert(address, &entry)?;
2816 }
2817 }
2818 }
2819 }
2820
2821 tracing::trace!(len = changes.contracts.len(), "Writing bytecodes");
2823 self.write_bytecodes(
2824 changes.contracts.into_iter().map(|(hash, bytecode)| (hash, Bytecode(bytecode))),
2825 )?;
2826
2827 Ok(())
2828 }
2829
2830 #[instrument(level = "debug", target = "providers::db", skip_all)]
2831 fn write_hashed_state(&self, hashed_state: &HashedPostStateSorted) -> ProviderResult<()> {
2832 let mut hashed_accounts_cursor = self.tx_ref().cursor_write::<tables::HashedAccounts>()?;
2834 for (hashed_address, account) in hashed_state.accounts() {
2835 if let Some(account) = account {
2836 hashed_accounts_cursor.upsert(*hashed_address, account)?;
2837 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
2838 hashed_accounts_cursor.delete_current()?;
2839 }
2840 }
2841
2842 let sorted_storages = hashed_state.account_storages().iter().sorted_by_key(|(key, _)| *key);
2844 let mut hashed_storage_cursor =
2845 self.tx_ref().cursor_dup_write::<tables::HashedStorages>()?;
2846 for (hashed_address, storage) in sorted_storages {
2847 for (hashed_slot, value) in storage.storage_slots_ref() {
2848 let entry = StorageEntry { key: *hashed_slot, value: *value };
2849
2850 if let Some(db_entry) =
2851 hashed_storage_cursor.seek_by_key_subkey(*hashed_address, entry.key)? &&
2852 db_entry.key == entry.key
2853 {
2854 hashed_storage_cursor.delete_current()?;
2855 }
2856
2857 if !entry.value.is_zero() {
2858 hashed_storage_cursor.upsert(*hashed_address, &entry)?;
2859 }
2860 }
2861 }
2862
2863 Ok(())
2864 }
2865
2866 fn remove_state_above(&self, block: BlockNumber) -> ProviderResult<()> {
2888 let range = block + 1..=self.last_block_number()?;
2889
2890 if range.is_empty() {
2891 return Ok(());
2892 }
2893
2894 let from_transaction_num = self.next_tx_num_after_block(block)?;
2896
2897 let storage_range = BlockNumberAddress::range(range.clone());
2898 let storage_changeset = if self.cached_storage_settings().storage_v2 {
2899 let changesets = self.storage_changesets_range(range.clone())?;
2900 let mut changeset_writer =
2901 self.static_file_provider.latest_writer(StaticFileSegment::StorageChangeSets)?;
2902 changeset_writer.prune_storage_changesets(block)?;
2903 changesets
2904 } else {
2905 self.take::<tables::StorageChangeSets>(storage_range)?.into_iter().collect()
2906 };
2907 let account_changeset = if self.cached_storage_settings().storage_v2 {
2908 let changesets = self.account_changesets_range(range)?;
2909 let mut changeset_writer =
2910 self.static_file_provider.latest_writer(StaticFileSegment::AccountChangeSets)?;
2911 changeset_writer.prune_account_changesets(block)?;
2912 changesets
2913 } else {
2914 self.take::<tables::AccountChangeSets>(range)?
2915 };
2916
2917 if self.cached_storage_settings().use_hashed_state() {
2918 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
2919 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
2920
2921 let (state, _) = self.populate_bundle_state_hashed(
2922 account_changeset,
2923 storage_changeset,
2924 &mut hashed_accounts_cursor,
2925 &mut hashed_storage_cursor,
2926 )?;
2927
2928 for (address, (old_account, new_account, storage)) in &state {
2929 if old_account != new_account {
2930 let hashed_address = keccak256(address);
2931 let existing_entry = hashed_accounts_cursor.seek_exact(hashed_address)?;
2932 if let Some(account) = old_account {
2933 hashed_accounts_cursor.upsert(hashed_address, account)?;
2934 } else if existing_entry.is_some() {
2935 hashed_accounts_cursor.delete_current()?;
2936 }
2937 }
2938
2939 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2940 let hashed_address = keccak256(address);
2941 let hashed_storage_key = keccak256(storage_key);
2942 let storage_entry =
2943 StorageEntry { key: hashed_storage_key, value: *old_storage_value };
2944 if hashed_storage_cursor
2945 .seek_by_key_subkey(hashed_address, hashed_storage_key)?
2946 .is_some_and(|s| s.key == hashed_storage_key)
2947 {
2948 hashed_storage_cursor.delete_current()?
2949 }
2950
2951 if !old_storage_value.is_zero() {
2952 hashed_storage_cursor.upsert(hashed_address, &storage_entry)?;
2953 }
2954 }
2955 }
2956 } else {
2957 let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
2962 let mut plain_storage_cursor =
2963 self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
2964
2965 let (state, _) = self.populate_bundle_state_plain(
2966 account_changeset,
2967 storage_changeset,
2968 &mut plain_accounts_cursor,
2969 &mut plain_storage_cursor,
2970 )?;
2971
2972 for (address, (old_account, new_account, storage)) in &state {
2973 if old_account != new_account {
2974 let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
2975 if let Some(account) = old_account {
2976 plain_accounts_cursor.upsert(*address, account)?;
2977 } else if existing_entry.is_some() {
2978 plain_accounts_cursor.delete_current()?;
2979 }
2980 }
2981
2982 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2983 let storage_entry =
2984 StorageEntry { key: *storage_key, value: *old_storage_value };
2985 if plain_storage_cursor
2986 .seek_by_key_subkey(*address, *storage_key)?
2987 .is_some_and(|s| s.key == *storage_key)
2988 {
2989 plain_storage_cursor.delete_current()?
2990 }
2991
2992 if !old_storage_value.is_zero() {
2993 plain_storage_cursor.upsert(*address, &storage_entry)?;
2994 }
2995 }
2996 }
2997 }
2998
2999 self.remove_receipts_from(from_transaction_num, block)?;
3000
3001 Ok(())
3002 }
3003
3004 fn take_state_above(
3026 &self,
3027 block: BlockNumber,
3028 ) -> ProviderResult<ExecutionOutcome<Self::Receipt>> {
3029 let range = block + 1..=self.last_block_number()?;
3030
3031 if range.is_empty() {
3032 return Ok(ExecutionOutcome::default())
3033 }
3034 let start_block_number = *range.start();
3035
3036 let block_bodies = self.block_body_indices_range(range.clone())?;
3038
3039 let from_transaction_num =
3041 block_bodies.first().expect("already checked if there are blocks").first_tx_num();
3042 let to_transaction_num =
3043 block_bodies.last().expect("already checked if there are blocks").last_tx_num();
3044
3045 let storage_range = BlockNumberAddress::range(range.clone());
3046 let storage_changeset = if let Some(highest_block) = self
3047 .static_file_provider
3048 .get_highest_static_file_block(StaticFileSegment::StorageChangeSets) &&
3049 self.cached_storage_settings().storage_v2
3050 {
3051 let changesets = self.storage_changesets_range(block + 1..=highest_block)?;
3052 let mut changeset_writer =
3053 self.static_file_provider.latest_writer(StaticFileSegment::StorageChangeSets)?;
3054 changeset_writer.prune_storage_changesets(block)?;
3055 changesets
3056 } else {
3057 self.take::<tables::StorageChangeSets>(storage_range)?.into_iter().collect()
3058 };
3059
3060 let highest_changeset_block = self
3062 .static_file_provider
3063 .get_highest_static_file_block(StaticFileSegment::AccountChangeSets);
3064 let account_changeset = if let Some(highest_block) = highest_changeset_block &&
3065 self.cached_storage_settings().storage_v2
3066 {
3067 let changesets = self.account_changesets_range(block + 1..highest_block + 1)?;
3069 let mut changeset_writer =
3070 self.static_file_provider.latest_writer(StaticFileSegment::AccountChangeSets)?;
3071 changeset_writer.prune_account_changesets(block)?;
3072
3073 changesets
3074 } else {
3075 self.take::<tables::AccountChangeSets>(range)?
3078 };
3079
3080 let (state, reverts) = if self.cached_storage_settings().use_hashed_state() {
3081 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3082 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3083
3084 let (state, reverts) = self.populate_bundle_state_hashed(
3085 account_changeset,
3086 storage_changeset,
3087 &mut hashed_accounts_cursor,
3088 &mut hashed_storage_cursor,
3089 )?;
3090
3091 for (address, (old_account, new_account, storage)) in &state {
3092 if old_account != new_account {
3093 let hashed_address = keccak256(address);
3094 let existing_entry = hashed_accounts_cursor.seek_exact(hashed_address)?;
3095 if let Some(account) = old_account {
3096 hashed_accounts_cursor.upsert(hashed_address, account)?;
3097 } else if existing_entry.is_some() {
3098 hashed_accounts_cursor.delete_current()?;
3099 }
3100 }
3101
3102 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
3103 let hashed_address = keccak256(address);
3104 let hashed_storage_key = keccak256(storage_key);
3105 let storage_entry =
3106 StorageEntry { key: hashed_storage_key, value: *old_storage_value };
3107 if hashed_storage_cursor
3108 .seek_by_key_subkey(hashed_address, hashed_storage_key)?
3109 .is_some_and(|s| s.key == hashed_storage_key)
3110 {
3111 hashed_storage_cursor.delete_current()?
3112 }
3113
3114 if !old_storage_value.is_zero() {
3115 hashed_storage_cursor.upsert(hashed_address, &storage_entry)?;
3116 }
3117 }
3118 }
3119
3120 (state, reverts)
3121 } else {
3122 let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
3127 let mut plain_storage_cursor =
3128 self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
3129
3130 let (state, reverts) = self.populate_bundle_state_plain(
3131 account_changeset,
3132 storage_changeset,
3133 &mut plain_accounts_cursor,
3134 &mut plain_storage_cursor,
3135 )?;
3136
3137 for (address, (old_account, new_account, storage)) in &state {
3138 if old_account != new_account {
3139 let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
3140 if let Some(account) = old_account {
3141 plain_accounts_cursor.upsert(*address, account)?;
3142 } else if existing_entry.is_some() {
3143 plain_accounts_cursor.delete_current()?;
3144 }
3145 }
3146
3147 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
3148 let storage_entry =
3149 StorageEntry { key: *storage_key, value: *old_storage_value };
3150 if plain_storage_cursor
3151 .seek_by_key_subkey(*address, *storage_key)?
3152 .is_some_and(|s| s.key == *storage_key)
3153 {
3154 plain_storage_cursor.delete_current()?
3155 }
3156
3157 if !old_storage_value.is_zero() {
3158 plain_storage_cursor.upsert(*address, &storage_entry)?;
3159 }
3160 }
3161 }
3162
3163 (state, reverts)
3164 };
3165
3166 let mut receipts_iter = self
3168 .static_file_provider
3169 .get_range_with_static_file_or_database(
3170 StaticFileSegment::Receipts,
3171 from_transaction_num..to_transaction_num + 1,
3172 |static_file, range, _| {
3173 static_file
3174 .receipts_by_tx_range(range.clone())
3175 .map(|r| range.into_iter().zip(r).collect())
3176 },
3177 |range, _| {
3178 self.tx
3179 .cursor_read::<tables::Receipts<Self::Receipt>>()?
3180 .walk_range(range)?
3181 .map(|r| r.map_err(Into::into))
3182 .collect()
3183 },
3184 |_| true,
3185 )?
3186 .into_iter()
3187 .peekable();
3188
3189 let mut receipts = Vec::with_capacity(block_bodies.len());
3190 for block_body in block_bodies {
3192 let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize);
3193 for num in block_body.tx_num_range() {
3194 if receipts_iter.peek().is_some_and(|(n, _)| *n == num) {
3195 block_receipts.push(receipts_iter.next().unwrap().1);
3196 }
3197 }
3198 receipts.push(block_receipts);
3199 }
3200
3201 self.remove_receipts_from(from_transaction_num, block)?;
3202
3203 Ok(ExecutionOutcome::new_init(
3204 state,
3205 reverts,
3206 Vec::new(),
3207 receipts,
3208 start_block_number,
3209 Vec::new(),
3210 ))
3211 }
3212}
3213
3214impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
3215 fn write_account_trie_updates<A: TrieTableAdapter>(
3216 tx: &TX,
3217 trie_updates: &TrieUpdatesSorted,
3218 num_entries: &mut usize,
3219 ) -> ProviderResult<()>
3220 where
3221 TX: DbTxMut,
3222 {
3223 let mut account_trie_cursor = tx.cursor_write::<A::AccountTrieTable>()?;
3224 for (key, updated_node) in trie_updates.account_nodes_ref() {
3226 let nibbles = A::AccountKey::from(*key);
3227 match updated_node {
3228 Some(node) => {
3229 if !key.is_empty() {
3230 *num_entries += 1;
3231 account_trie_cursor.upsert(nibbles, node)?;
3232 }
3233 }
3234 None => {
3235 *num_entries += 1;
3236 if account_trie_cursor.seek_exact(nibbles)?.is_some() {
3237 account_trie_cursor.delete_current()?;
3238 }
3239 }
3240 }
3241 }
3242 Ok(())
3243 }
3244
3245 fn write_storage_tries<A: TrieTableAdapter>(
3246 tx: &TX,
3247 storage_tries: Vec<(&B256, &StorageTrieUpdatesSorted)>,
3248 num_entries: &mut usize,
3249 ) -> ProviderResult<()>
3250 where
3251 TX: DbTxMut,
3252 {
3253 let mut cursor = tx.cursor_dup_write::<A::StorageTrieTable>()?;
3254 for (hashed_address, storage_trie_updates) in storage_tries {
3255 let mut db_storage_trie_cursor: DatabaseStorageTrieCursor<_, A> =
3256 DatabaseStorageTrieCursor::new(cursor, *hashed_address);
3257 *num_entries +=
3258 db_storage_trie_cursor.write_storage_trie_updates_sorted(storage_trie_updates)?;
3259 cursor = db_storage_trie_cursor.cursor;
3260 }
3261 Ok(())
3262 }
3263}
3264
3265impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> TrieWriter for DatabaseProvider<TX, N> {
3266 #[instrument(level = "debug", target = "providers::db", skip_all)]
3270 fn write_trie_updates_sorted(&self, trie_updates: &TrieUpdatesSorted) -> ProviderResult<usize> {
3271 if trie_updates.is_empty() {
3272 return Ok(0)
3273 }
3274
3275 let mut num_entries = 0;
3277
3278 reth_trie_db::with_adapter!(self, |A| {
3279 Self::write_account_trie_updates::<A>(self.tx_ref(), trie_updates, &mut num_entries)?;
3280 });
3281
3282 num_entries +=
3283 self.write_storage_trie_updates_sorted(trie_updates.storage_tries_ref().iter())?;
3284
3285 Ok(num_entries)
3286 }
3287}
3288
3289impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> StorageTrieWriter for DatabaseProvider<TX, N> {
3290 fn write_storage_trie_updates_sorted<'a>(
3296 &self,
3297 storage_tries: impl Iterator<Item = (&'a B256, &'a StorageTrieUpdatesSorted)>,
3298 ) -> ProviderResult<usize> {
3299 let mut num_entries = 0;
3300 let mut storage_tries = storage_tries.collect::<Vec<_>>();
3301 storage_tries.sort_unstable_by(|a, b| a.0.cmp(b.0));
3302 reth_trie_db::with_adapter!(self, |A| {
3303 Self::write_storage_tries::<A>(self.tx_ref(), storage_tries, &mut num_entries)?;
3304 });
3305 Ok(num_entries)
3306 }
3307}
3308
3309impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HashingWriter for DatabaseProvider<TX, N> {
3310 #[allow(clippy::clone_on_copy)]
3311 fn unwind_account_hashing<'a>(
3312 &self,
3313 changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
3314 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3315 let hashed_accounts = changesets
3319 .into_iter()
3320 .map(|(_, e)| (keccak256(e.address), e.info.clone()))
3321 .collect::<Vec<_>>()
3322 .into_iter()
3323 .rev()
3324 .collect::<BTreeMap<_, _>>();
3325
3326 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3328 for (hashed_address, account) in &hashed_accounts {
3329 if let Some(account) = account {
3330 hashed_accounts_cursor.upsert(*hashed_address, account)?;
3331 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
3332 hashed_accounts_cursor.delete_current()?;
3333 }
3334 }
3335
3336 Ok(hashed_accounts)
3337 }
3338
3339 fn unwind_account_hashing_range(
3340 &self,
3341 range: impl RangeBounds<BlockNumber>,
3342 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3343 let changesets = self.account_changesets_range(range)?;
3344 self.unwind_account_hashing(changesets.iter())
3345 }
3346
3347 fn insert_account_for_hashing(
3348 &self,
3349 changesets: impl IntoIterator<Item = (Address, Option<Account>)>,
3350 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3351 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3352 let hashed_accounts =
3353 changesets.into_iter().map(|(ad, ac)| (keccak256(ad), ac)).collect::<BTreeMap<_, _>>();
3354 for (hashed_address, account) in &hashed_accounts {
3355 if let Some(account) = account {
3356 hashed_accounts_cursor.upsert(*hashed_address, account)?;
3357 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
3358 hashed_accounts_cursor.delete_current()?;
3359 }
3360 }
3361 Ok(hashed_accounts)
3362 }
3363
3364 fn unwind_storage_hashing(
3365 &self,
3366 changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
3367 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3368 let mut hashed_storages = changesets
3370 .into_iter()
3371 .map(|(BlockNumberAddress((_, address)), storage_entry)| {
3372 let hashed_key = keccak256(storage_entry.key);
3373 (keccak256(address), hashed_key, storage_entry.value)
3374 })
3375 .collect::<Vec<_>>();
3376 hashed_storages.sort_by_key(|(ha, hk, _)| (*ha, *hk));
3377
3378 let mut hashed_storage_keys: B256Map<BTreeSet<B256>> =
3380 B256Map::with_capacity_and_hasher(hashed_storages.len(), Default::default());
3381 let mut hashed_storage = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3382 for (hashed_address, key, value) in hashed_storages.into_iter().rev() {
3383 hashed_storage_keys.entry(hashed_address).or_default().insert(key);
3384
3385 if hashed_storage
3386 .seek_by_key_subkey(hashed_address, key)?
3387 .is_some_and(|entry| entry.key == key)
3388 {
3389 hashed_storage.delete_current()?;
3390 }
3391
3392 if !value.is_zero() {
3393 hashed_storage.upsert(hashed_address, &StorageEntry { key, value })?;
3394 }
3395 }
3396 Ok(hashed_storage_keys)
3397 }
3398
3399 fn unwind_storage_hashing_range(
3400 &self,
3401 range: impl RangeBounds<BlockNumber>,
3402 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3403 let changesets = self.storage_changesets_range(range)?;
3404 self.unwind_storage_hashing(changesets.into_iter())
3405 }
3406
3407 fn insert_storage_for_hashing(
3408 &self,
3409 storages: impl IntoIterator<Item = (Address, impl IntoIterator<Item = StorageEntry>)>,
3410 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3411 let hashed_storages =
3413 storages.into_iter().fold(BTreeMap::new(), |mut map, (address, storage)| {
3414 let storage = storage.into_iter().fold(BTreeMap::new(), |mut map, entry| {
3415 map.insert(keccak256(entry.key), entry.value);
3416 map
3417 });
3418 map.insert(keccak256(address), storage);
3419 map
3420 });
3421
3422 let hashed_storage_keys = hashed_storages
3423 .iter()
3424 .map(|(hashed_address, entries)| (*hashed_address, entries.keys().copied().collect()))
3425 .collect();
3426
3427 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3428 hashed_storages.into_iter().try_for_each(|(hashed_address, storage)| {
3431 storage.into_iter().try_for_each(|(key, value)| -> ProviderResult<()> {
3432 if hashed_storage_cursor
3433 .seek_by_key_subkey(hashed_address, key)?
3434 .is_some_and(|entry| entry.key == key)
3435 {
3436 hashed_storage_cursor.delete_current()?;
3437 }
3438
3439 if !value.is_zero() {
3440 hashed_storage_cursor.upsert(hashed_address, &StorageEntry { key, value })?;
3441 }
3442 Ok(())
3443 })
3444 })?;
3445
3446 Ok(hashed_storage_keys)
3447 }
3448}
3449
3450impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HistoryWriter for DatabaseProvider<TX, N> {
3451 fn unwind_account_history_indices<'a>(
3452 &self,
3453 changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
3454 ) -> ProviderResult<usize> {
3455 let mut last_indices = changesets
3456 .into_iter()
3457 .map(|(index, account)| (account.address, *index))
3458 .collect::<Vec<_>>();
3459 last_indices.sort_unstable_by_key(|(a, _)| *a);
3460
3461 if self.cached_storage_settings().storage_v2 {
3462 let batch = self.rocksdb_provider.unwind_account_history_indices(&last_indices)?;
3463 self.pending_rocksdb_batches.lock().push(batch);
3464 } else {
3465 let mut cursor = self.tx.cursor_write::<tables::AccountsHistory>()?;
3467 for &(address, rem_index) in &last_indices {
3468 let partial_shard = unwind_history_shards::<_, tables::AccountsHistory, _>(
3469 &mut cursor,
3470 ShardedKey::last(address),
3471 rem_index,
3472 |sharded_key| sharded_key.key == address,
3473 )?;
3474
3475 if !partial_shard.is_empty() {
3478 cursor.insert(
3479 ShardedKey::last(address),
3480 &BlockNumberList::new_pre_sorted(partial_shard),
3481 )?;
3482 }
3483 }
3484 }
3485
3486 let changesets = last_indices.len();
3487 Ok(changesets)
3488 }
3489
3490 fn unwind_account_history_indices_range(
3491 &self,
3492 range: impl RangeBounds<BlockNumber>,
3493 ) -> ProviderResult<usize> {
3494 let changesets = self.account_changesets_range(range)?;
3495 self.unwind_account_history_indices(changesets.iter())
3496 }
3497
3498 fn insert_account_history_index(
3499 &self,
3500 account_transitions: impl IntoIterator<Item = (Address, impl IntoIterator<Item = u64>)>,
3501 ) -> ProviderResult<()> {
3502 self.append_history_index::<_, tables::AccountsHistory>(
3503 account_transitions,
3504 ShardedKey::new,
3505 )
3506 }
3507
3508 fn unwind_storage_history_indices(
3509 &self,
3510 changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
3511 ) -> ProviderResult<usize> {
3512 let mut storage_changesets = changesets
3513 .into_iter()
3514 .map(|(BlockNumberAddress((bn, address)), storage)| (address, storage.key, bn))
3515 .collect::<Vec<_>>();
3516 storage_changesets.sort_unstable_by_key(|(address, key, _)| (*address, *key));
3517
3518 if self.cached_storage_settings().storage_v2 {
3519 let batch =
3520 self.rocksdb_provider.unwind_storage_history_indices(&storage_changesets)?;
3521 self.pending_rocksdb_batches.lock().push(batch);
3522 } else {
3523 let mut cursor = self.tx.cursor_write::<tables::StoragesHistory>()?;
3525 for &(address, storage_key, rem_index) in &storage_changesets {
3526 let partial_shard = unwind_history_shards::<_, tables::StoragesHistory, _>(
3527 &mut cursor,
3528 StorageShardedKey::last(address, storage_key),
3529 rem_index,
3530 |storage_sharded_key| {
3531 storage_sharded_key.address == address &&
3532 storage_sharded_key.sharded_key.key == storage_key
3533 },
3534 )?;
3535
3536 if !partial_shard.is_empty() {
3539 cursor.insert(
3540 StorageShardedKey::last(address, storage_key),
3541 &BlockNumberList::new_pre_sorted(partial_shard),
3542 )?;
3543 }
3544 }
3545 }
3546
3547 let changesets = storage_changesets.len();
3548 Ok(changesets)
3549 }
3550
3551 fn unwind_storage_history_indices_range(
3552 &self,
3553 range: impl RangeBounds<BlockNumber>,
3554 ) -> ProviderResult<usize> {
3555 let changesets = self.storage_changesets_range(range)?;
3556 self.unwind_storage_history_indices(changesets.into_iter())
3557 }
3558
3559 fn insert_storage_history_index(
3560 &self,
3561 storage_transitions: impl IntoIterator<Item = ((Address, B256), impl IntoIterator<Item = u64>)>,
3562 ) -> ProviderResult<()> {
3563 self.append_history_index::<_, tables::StoragesHistory>(
3564 storage_transitions,
3565 |(address, storage_key), highest_block_number| {
3566 StorageShardedKey::new(address, storage_key, highest_block_number)
3567 },
3568 )
3569 }
3570
3571 #[instrument(level = "debug", target = "providers::db", skip_all)]
3572 fn update_history_indices(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<()> {
3573 let storage_settings = self.cached_storage_settings();
3574 if !storage_settings.storage_v2 {
3575 let indices = self.changed_accounts_and_blocks_with_range(range.clone())?;
3576 self.insert_account_history_index(indices)?;
3577 }
3578
3579 if !storage_settings.storage_v2 {
3580 let indices = self.changed_storages_and_blocks_with_range(range)?;
3581 self.insert_storage_history_index(indices)?;
3582 }
3583
3584 Ok(())
3585 }
3586}
3587
3588impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockExecutionWriter
3589 for DatabaseProvider<TX, N>
3590{
3591 fn take_block_and_execution_above(
3592 &self,
3593 block: BlockNumber,
3594 ) -> ProviderResult<Chain<Self::Primitives>> {
3595 let range = block + 1..=self.last_block_number()?;
3596
3597 self.unwind_trie_state_from(block + 1)?;
3598
3599 let execution_state = self.take_state_above(block)?;
3601
3602 let blocks = self.recovered_block_range(range)?;
3603
3604 self.remove_blocks_above(block)?;
3607
3608 self.update_pipeline_stages_after_unwind(block)?;
3610
3611 Ok(Chain::new(blocks, execution_state, BTreeMap::new()))
3612 }
3613
3614 fn remove_block_and_execution_above(
3615 &self,
3616 block: BlockNumber,
3617 ) -> ProviderResult<PersistenceFrontiers> {
3618 self.unwind_trie_state_from(block + 1)?;
3619
3620 self.remove_state_above(block)?;
3622
3623 self.remove_blocks_above(block)?;
3626
3627 self.update_pipeline_stages_after_unwind(block)
3629 }
3630}
3631
3632impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockWriter
3633 for DatabaseProvider<TX, N>
3634{
3635 type Block = BlockTy<N>;
3636 type Receipt = ReceiptTy<N>;
3637
3638 fn insert_block(
3643 &self,
3644 block: &RecoveredBlock<Self::Block>,
3645 ) -> ProviderResult<StoredBlockBodyIndices> {
3646 let block_number = block.number();
3647
3648 let executed_block = ExecutedBlock::new(
3650 Arc::new(block.clone()),
3651 Arc::new(BlockExecutionOutput {
3652 result: BlockExecutionResult {
3653 receipts: Default::default(),
3654 requests: Default::default(),
3655 gas_used: 0,
3656 blob_gas_used: 0,
3657 },
3658 state: Default::default(),
3659 }),
3660 SortedTrieData::default(),
3661 );
3662
3663 self.save_blocks_inner(
3664 std::slice::from_ref(&executed_block),
3665 &[],
3666 &[],
3667 None,
3668 SaveBlocksMode::BlocksOnly,
3669 )?;
3670
3671 self.block_body_indices(block_number)?
3673 .ok_or(ProviderError::BlockBodyIndicesNotFound(block_number))
3674 }
3675
3676 fn append_block_bodies(
3677 &self,
3678 bodies: Vec<(BlockNumber, Option<&BodyTy<N>>)>,
3679 ) -> ProviderResult<()> {
3680 let Some(from_block) = bodies.first().map(|(block, _)| *block) else { return Ok(()) };
3681
3682 let mut tx_writer =
3684 self.static_file_provider.get_writer(from_block, StaticFileSegment::Transactions)?;
3685
3686 let mut block_indices_cursor = self.tx.cursor_write::<tables::BlockBodyIndices>()?;
3687 let mut tx_block_cursor = self.tx.cursor_write::<tables::TransactionBlocks>()?;
3688
3689 let mut next_tx_num = tx_block_cursor.last()?.map(|(id, _)| id + 1).unwrap_or_default();
3691
3692 for (block_number, body) in &bodies {
3693 tx_writer.increment_block(*block_number)?;
3695
3696 let tx_count = body.as_ref().map(|b| b.transactions().len() as u64).unwrap_or_default();
3697 let block_indices = StoredBlockBodyIndices { first_tx_num: next_tx_num, tx_count };
3698
3699 let mut durations_recorder = metrics::DurationsRecorder::new(&self.metrics);
3700
3701 block_indices_cursor.append(*block_number, &block_indices)?;
3703
3704 durations_recorder.record_relative(metrics::Action::InsertBlockBodyIndices);
3705
3706 let Some(body) = body else { continue };
3707
3708 if !body.transactions().is_empty() {
3710 tx_block_cursor.append(block_indices.last_tx_num(), block_number)?;
3711 durations_recorder.record_relative(metrics::Action::InsertTransactionBlocks);
3712 }
3713
3714 for transaction in body.transactions() {
3716 tx_writer.append_transaction(next_tx_num, transaction)?;
3717
3718 next_tx_num += 1;
3720 }
3721 }
3722
3723 self.storage.writer().write_block_bodies(self, bodies)?;
3724
3725 Ok(())
3726 }
3727
3728 fn remove_blocks_above(&self, block: BlockNumber) -> ProviderResult<()> {
3729 let last_block_number = self.last_block_number()?;
3730 for hash in self.canonical_hashes_range(block + 1, last_block_number + 1)? {
3732 self.tx.delete::<tables::HeaderNumbers>(hash, None)?;
3733 }
3734
3735 let highest_static_file_block = self
3737 .static_file_provider()
3738 .get_highest_static_file_block(StaticFileSegment::Headers)
3739 .expect("todo: error handling, headers should exist");
3740
3741 debug!(target: "providers::db", ?block, "Removing static file blocks above block_number");
3747 self.static_file_provider()
3748 .get_writer(block, StaticFileSegment::Headers)?
3749 .prune_headers(highest_static_file_block.saturating_sub(block))?;
3750
3751 let unwind_tx_from = self.next_tx_num_after_block(block)?;
3753
3754 let unwind_tx_to = self
3756 .tx
3757 .cursor_read::<tables::BlockBodyIndices>()?
3758 .last()?
3759 .ok_or(ProviderError::BlockBodyIndicesNotFound(block))?
3761 .1
3762 .last_tx_num();
3763
3764 if unwind_tx_from <= unwind_tx_to {
3765 let hashes = self.transaction_hashes_by_range(unwind_tx_from..(unwind_tx_to + 1))?;
3766 self.with_rocksdb_batch(|batch| {
3767 let mut writer = EitherWriter::new_transaction_hash_numbers(self, batch)?;
3768 for (hash, _) in hashes {
3769 writer.delete_transaction_hash_number(hash)?;
3770 }
3771 Ok(((), writer.into_raw_rocksdb_batch()))
3772 })?;
3773 }
3774
3775 if self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()) {
3778 EitherWriter::new_senders(self, last_block_number)?
3779 .prune_senders(unwind_tx_from, block)?;
3780 }
3781
3782 self.remove_bodies_above(block)?;
3783
3784 Ok(())
3785 }
3786
3787 fn remove_bodies_above(&self, block: BlockNumber) -> ProviderResult<()> {
3788 self.storage.writer().remove_block_bodies_above(self, block)?;
3789
3790 let unwind_tx_from = self.next_tx_num_after_block(block)?;
3792
3793 self.remove::<tables::BlockBodyIndices>(block + 1..)?;
3794 self.remove::<tables::TransactionBlocks>(unwind_tx_from..)?;
3795
3796 let static_file_tx_num =
3797 self.static_file_provider.get_highest_static_file_tx(StaticFileSegment::Transactions);
3798
3799 let to_delete = static_file_tx_num
3800 .map(|static_tx| (static_tx + 1).saturating_sub(unwind_tx_from))
3801 .unwrap_or_default();
3802
3803 self.static_file_provider
3804 .latest_writer(StaticFileSegment::Transactions)?
3805 .prune_transactions(to_delete, block)?;
3806
3807 Ok(())
3808 }
3809
3810 fn append_blocks_with_state(
3819 &self,
3820 blocks: Vec<RecoveredBlock<Self::Block>>,
3821 execution_outcome: &ExecutionOutcome<Self::Receipt>,
3822 hashed_state: HashedPostStateSorted,
3823 ) -> ProviderResult<()> {
3824 if blocks.is_empty() {
3825 debug!(target: "providers::db", "Attempted to append empty block range");
3826 return Ok(())
3827 }
3828
3829 let first_number = blocks[0].number();
3832
3833 let last_block_number = blocks[blocks.len() - 1].number();
3836
3837 let mut durations_recorder = metrics::DurationsRecorder::new(&self.metrics);
3838
3839 let (account_transitions, storage_transitions) = {
3844 let mut account_transitions: BTreeMap<Address, Vec<u64>> = BTreeMap::new();
3845 let mut storage_transitions: BTreeMap<(Address, B256), Vec<u64>> = BTreeMap::new();
3846 for (block_idx, block_reverts) in execution_outcome.bundle.reverts.iter().enumerate() {
3847 let block_number = first_number + block_idx as u64;
3848 for (address, account_revert) in block_reverts {
3849 account_transitions.entry(*address).or_default().push(block_number);
3850 for storage_key in account_revert.storage.keys() {
3851 let key = B256::from(*storage_key);
3852 storage_transitions.entry((*address, key)).or_default().push(block_number);
3853 }
3854 }
3855 }
3856 (account_transitions, storage_transitions)
3857 };
3858
3859 for block in blocks {
3861 self.insert_block(&block)?;
3862 durations_recorder.record_relative(metrics::Action::InsertBlock);
3863 }
3864
3865 self.write_state(execution_outcome, OriginalValuesKnown::No, StateWriteConfig::default())?;
3866 durations_recorder.record_relative(metrics::Action::InsertState);
3867
3868 self.write_hashed_state(&hashed_state)?;
3870 durations_recorder.record_relative(metrics::Action::InsertHashes);
3871
3872 let storage_settings = self.cached_storage_settings();
3877 if storage_settings.storage_v2 {
3878 self.with_rocksdb_batch(|mut batch| {
3879 for (address, blocks) in account_transitions {
3880 batch.append_account_history_shard(address, blocks)?;
3881 }
3882 Ok(((), Some(batch.into_inner())))
3883 })?;
3884 } else {
3885 self.insert_account_history_index(account_transitions)?;
3886 }
3887 if storage_settings.storage_v2 {
3888 self.with_rocksdb_batch(|mut batch| {
3889 for ((address, key), blocks) in storage_transitions {
3890 batch.append_storage_history_shard(address, key, blocks)?;
3891 }
3892 Ok(((), Some(batch.into_inner())))
3893 })?;
3894 } else {
3895 self.insert_storage_history_index(storage_transitions)?;
3896 }
3897 durations_recorder.record_relative(metrics::Action::InsertHistoryIndices);
3898
3899 self.update_pipeline_stages(last_block_number, false)?;
3901 durations_recorder.record_relative(metrics::Action::UpdatePipelineStages);
3902
3903 debug!(target: "providers::db", range = ?first_number..=last_block_number, actions = ?durations_recorder.actions, "Appended blocks");
3904
3905 Ok(())
3906 }
3907
3908 fn clear_transaction_lookup(&self) -> ProviderResult<()> {
3909 if self.cached_storage_settings().storage_v2 {
3910 self.rocksdb_provider.clear::<tables::TransactionHashNumbers>()
3911 } else {
3912 self.tx.clear::<tables::TransactionHashNumbers>().map_err(Into::into)
3913 }
3914 }
3915}
3916
3917impl<TX: DbTx + 'static, N: NodeTypes> PruneCheckpointReader for DatabaseProvider<TX, N> {
3918 fn get_prune_checkpoint(
3919 &self,
3920 segment: PruneSegment,
3921 ) -> ProviderResult<Option<PruneCheckpoint>> {
3922 Ok(self.tx.get::<tables::PruneCheckpoints>(segment)?)
3923 }
3924
3925 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
3926 Ok(PruneSegment::variants()
3927 .filter_map(|segment| {
3928 self.tx
3929 .get::<tables::PruneCheckpoints>(segment)
3930 .transpose()
3931 .map(|chk| chk.map(|chk| (segment, chk)))
3932 })
3933 .collect::<Result<_, _>>()?)
3934 }
3935
3936 fn get_prune_checkpoints_for_availability(
3937 &self,
3938 ) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
3939 let mut checkpoints = self.get_prune_checkpoints()?;
3940 if !EitherWriter::receipts_destination(self).is_static_file() ||
3941 !checkpoints.iter().any(|(segment, checkpoint)| {
3942 matches!(segment, PruneSegment::Receipts | PruneSegment::ContractLogs) &&
3943 checkpoint.tx_number.is_some()
3944 })
3945 {
3946 return Ok(checkpoints)
3947 }
3948
3949 let Some(lowest_block) =
3950 self.static_file_provider.get_lowest_range_start(StaticFileSegment::Receipts)
3951 else {
3952 return Ok(checkpoints)
3953 };
3954 let Some(first_tx_range) = self
3955 .static_file_provider
3956 .get_segment_provider_for_block(StaticFileSegment::Receipts, lowest_block, None)?
3957 .user_header()
3958 .tx_range()
3959 else {
3960 return Ok(checkpoints)
3961 };
3962
3963 let pruned_tx = first_tx_range.start().checked_sub(1);
3966 let pruned_block = if let Some(tx) = pruned_tx {
3967 if let Some((_, block)) =
3968 self.tx.cursor_read::<tables::TransactionBlocks>()?.seek(tx)? &&
3969 let Some(indices) = self.tx.get::<tables::BlockBodyIndices>(block)? &&
3970 indices.tx_num_range().contains(&tx)
3971 {
3972 Some(block).max(lowest_block.checked_sub(1))
3973 } else {
3974 return Ok(checkpoints)
3975 }
3976 } else {
3977 lowest_block.checked_sub(1)
3978 };
3979
3980 let mut last_tx = first_tx_range.end();
3984 for (segment, checkpoint) in &mut checkpoints {
3985 if matches!(segment, PruneSegment::Receipts | PruneSegment::ContractLogs) &&
3986 let Some(tx) = checkpoint.tx_number &&
3987 tx >= first_tx_range.start()
3988 {
3989 while let Some(next_tx) = last_tx.checked_add(1) &&
3990 next_tx <= tx &&
3991 let Some(next_file) = self
3992 .static_file_provider
3993 .get_maybe_segment_provider(StaticFileSegment::Receipts, next_tx)? &&
3994 let Some(next_range) = next_file.user_header().tx_range() &&
3995 next_range.start() == next_tx
3996 {
3997 last_tx = next_range.end();
3998 }
3999 if tx <= last_tx {
4000 checkpoint.block_number = pruned_block;
4001 checkpoint.tx_number = pruned_tx;
4002 }
4003 }
4004 }
4005
4006 Ok(checkpoints)
4007 }
4008}
4009
4010impl<TX: DbTxMut, N: NodeTypes> PruneCheckpointWriter for DatabaseProvider<TX, N> {
4011 fn save_prune_checkpoint(
4012 &self,
4013 segment: PruneSegment,
4014 checkpoint: PruneCheckpoint,
4015 ) -> ProviderResult<()> {
4016 Ok(self.tx.put::<tables::PruneCheckpoints>(segment, checkpoint)?)
4017 }
4018}
4019
4020impl<TX: DbTx + 'static, N: NodeTypesForProvider> StatsReader for DatabaseProvider<TX, N> {
4021 fn count_entries<T: Table>(&self) -> ProviderResult<usize> {
4022 let db_entries = self.tx.entries::<T>()?;
4023 let static_file_entries = match self.static_file_provider.count_entries::<T>() {
4024 Ok(entries) => entries,
4025 Err(ProviderError::UnsupportedProvider) => 0,
4026 Err(err) => return Err(err),
4027 };
4028
4029 Ok(db_entries + static_file_entries)
4030 }
4031}
4032
4033impl<TX: DbTx + 'static, N: NodeTypes> ChainStateBlockReader for DatabaseProvider<TX, N> {
4034 fn last_finalized_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
4035 Ok(self.tx.get::<tables::ChainState>(tables::ChainStateKey::LastFinalizedBlock)?)
4036 }
4037
4038 fn last_safe_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
4039 Ok(self.tx.get::<tables::ChainState>(tables::ChainStateKey::LastSafeBlock)?)
4040 }
4041}
4042
4043impl<TX: DbTxMut, N: NodeTypes> ChainStateBlockWriter for DatabaseProvider<TX, N> {
4044 fn save_finalized_block_number(&self, block_number: BlockNumber) -> ProviderResult<()> {
4045 Ok(self
4046 .tx
4047 .put::<tables::ChainState>(tables::ChainStateKey::LastFinalizedBlock, block_number)?)
4048 }
4049
4050 fn save_safe_block_number(&self, block_number: BlockNumber) -> ProviderResult<()> {
4051 Ok(self.tx.put::<tables::ChainState>(tables::ChainStateKey::LastSafeBlock, block_number)?)
4052 }
4053}
4054
4055impl<TX: DbTx + 'static, N: NodeTypes> DbTxProvider for DatabaseProvider<TX, N> {
4056 type Tx = TX;
4057
4058 fn tx(&self) -> &Self::Tx {
4059 &self.tx
4060 }
4061}
4062
4063impl<TX: DbTx + 'static, N: NodeTypes> DBProvider for DatabaseProvider<TX, N> {
4064 fn tx_mut(&mut self) -> &mut Self::Tx {
4065 &mut self.tx
4066 }
4067
4068 fn into_tx(self) -> Self::Tx {
4069 self.tx
4070 }
4071
4072 fn prune_modes_ref(&self) -> &PruneModes {
4073 self.prune_modes_ref()
4074 }
4075
4076 #[instrument(
4078 name = "DatabaseProvider::commit",
4079 level = "debug",
4080 target = "providers::db",
4081 skip_all
4082 )]
4083 fn commit(self) -> ProviderResult<()> {
4084 if self.static_file_provider.has_unwind_queued() || self.commit_order.is_unwind() {
4085 self.commit_unwind()?;
4086 } else {
4087 let mut timings = metrics::CommitTimings::default();
4089
4090 let start = Instant::now();
4091 self.static_file_provider.finalize()?;
4092 timings.sf = start.elapsed();
4093
4094 let start = Instant::now();
4095 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
4096 for batch in batches {
4097 self.rocksdb_provider.commit_batch(batch)?;
4098 }
4099 timings.rocksdb = start.elapsed();
4100
4101 let start = Instant::now();
4102 self.tx.commit()?;
4103 timings.mdbx = start.elapsed();
4104
4105 self.metrics.record_commit(&timings);
4106 }
4107
4108 Ok(())
4109 }
4110}
4111
4112impl<TX: DbTx, N: NodeTypes> MetadataProvider for DatabaseProvider<TX, N> {
4113 fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>> {
4114 self.tx.get::<tables::Metadata>(key.to_string()).map_err(Into::into)
4115 }
4116}
4117
4118impl<TX: DbTxMut, N: NodeTypes> MetadataWriter for DatabaseProvider<TX, N> {
4119 fn write_metadata(&self, key: &str, value: Vec<u8>) -> ProviderResult<()> {
4120 self.tx.put::<tables::Metadata>(key.to_string(), value).map_err(Into::into)
4121 }
4122
4123 fn delete_metadata(&self, key: &str) -> ProviderResult<()> {
4124 self.tx.delete::<tables::Metadata>(key.to_string(), None)?;
4125 Ok(())
4126 }
4127}
4128
4129impl<TX: Send, N: NodeTypes> StorageSettingsCache for DatabaseProvider<TX, N> {
4130 fn cached_storage_settings(&self) -> StorageSettings {
4131 *self.storage_settings.read()
4132 }
4133
4134 fn set_storage_settings_cache(&self, settings: StorageSettings) {
4135 *self.storage_settings.write() = settings;
4136 }
4137}
4138
4139impl<TX: Send, N: NodeTypes> StoragePath for DatabaseProvider<TX, N> {
4140 fn storage_path(&self) -> PathBuf {
4141 self.db_path.clone()
4142 }
4143}
4144
4145#[cfg(test)]
4146mod tests {
4147 use super::*;
4148 use crate::{
4149 test_utils::{blocks::BlockchainTestData, create_test_provider_factory},
4150 BlockWriter,
4151 };
4152 use alloy_consensus::Header;
4153 use alloy_eips::BlockNumHash;
4154 use alloy_primitives::{
4155 map::{AddressMap, B256Map},
4156 U256,
4157 };
4158 use evm2::evm::AccountInfo;
4159 use reth_chain_state::{test_utils::TestBlockBuilder, ExecutedBlock};
4160 use reth_db_api::models::{SnapAttempt, StorageSettings};
4161 use reth_ethereum_primitives::Receipt;
4162 use reth_execution_types::{AccountRevertInit, BlockExecutionOutput, BlockExecutionResult};
4163 use reth_primitives_traits::SealedBlock;
4164 use reth_storage_api::{DatabaseProviderFactory, MetadataWriter};
4165 use reth_testing_utils::generators::{self, random_block, BlockParams};
4166 use reth_trie::{
4167 bundle_state::BundleState, HashedPostState, KeccakKeyHasher, Nibbles, SortedTrieData,
4168 StoredNibbles, StoredNibblesSubKey,
4169 };
4170 use std::{sync::mpsc, time::Duration};
4171
4172 fn save_genesis<TX, N>(
4175 provider: &DatabaseProvider<TX, N>,
4176 genesis: &ExecutedBlock<N::Primitives>,
4177 ) -> ProviderResult<()>
4178 where
4179 TX: DbTx + DbTxMut + 'static,
4180 N: NodeTypesForProvider,
4181 {
4182 assert_eq!(genesis.recovered_block().number(), 0);
4183 provider.save_blocks_inner(
4184 std::slice::from_ref(genesis),
4185 std::slice::from_ref(genesis),
4186 &[],
4187 None,
4188 SaveBlocksMode::Full,
4189 )
4190 }
4191
4192 #[test]
4193 fn snap_attempt_guards_finish_checkpoint_writers() {
4194 let factory = create_test_provider_factory();
4195 let provider = factory.database_provider_rw().unwrap();
4196 provider.update_pipeline_stages(5, false).unwrap();
4197 let mut attempt = SnapAttempt::start(
4198 None,
4199 BlockNumHash::new(10, B256::repeat_byte(1)),
4200 B256::repeat_byte(2),
4201 );
4202 provider.write_snap_attempt(&attempt).unwrap();
4203 provider.commit().unwrap();
4204
4205 let provider = factory.database_provider_rw().unwrap();
4206 let refused = provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(10));
4207 assert!(matches!(refused, Err(ProviderError::UnverifiedSnapState { attempt: 0 })));
4208 let refused = provider.update_pipeline_stages(10, false);
4209 assert!(matches!(refused, Err(ProviderError::UnverifiedSnapState { attempt: 0 })));
4210 for stage in [StageId::Finish, StageId::Headers] {
4211 assert_eq!(provider.get_stage_checkpoint(stage).unwrap().unwrap().block_number, 5);
4212 }
4213
4214 provider.update_pipeline_stages(4, true).unwrap();
4216 provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(3)).unwrap();
4217 assert_eq!(
4218 provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap().block_number,
4219 3
4220 );
4221
4222 attempt.abandon();
4224 provider.write_snap_attempt(&attempt).unwrap();
4225 let refused = provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(10));
4226 assert!(matches!(refused, Err(ProviderError::UnverifiedSnapState { attempt: 0 })));
4227
4228 provider.save_stage_checkpoint(StageId::Headers, StageCheckpoint::new(10)).unwrap();
4230 attempt.verify();
4231 provider.write_snap_attempt(&attempt).unwrap();
4232 provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(10)).unwrap();
4233 provider.update_pipeline_stages(11, false).unwrap();
4234 provider.commit().unwrap();
4235
4236 let provider = factory.database_provider_ro().unwrap();
4237 assert_eq!(
4238 provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap().block_number,
4239 11
4240 );
4241 }
4242
4243 #[test]
4244 fn snap_attempt_guards_partial_state_trie_advances() {
4245 for abandoned in [false, true] {
4246 let factory = create_test_provider_factory();
4247 let provider = factory.database_provider_rw().unwrap();
4248 provider.update_pipeline_stages(100, false).unwrap();
4249 let checkpoint = StageCheckpoint::new(100)
4250 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(50) });
4251 provider.save_stage_checkpoint(StageId::Finish, checkpoint).unwrap();
4252 let mut attempt = SnapAttempt::start(None, BlockNumHash::default(), B256::ZERO);
4253 if abandoned {
4254 attempt.abandon();
4255 }
4256 provider.write_snap_attempt(&attempt).unwrap();
4257
4258 for block in [100, 90] {
4259 let result =
4260 provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(block));
4261 assert!(matches!(result, Err(ProviderError::UnverifiedSnapState { .. })));
4262 let result = provider.update_pipeline_stages(block, true);
4263 assert!(matches!(result, Err(ProviderError::UnverifiedSnapState { .. })));
4264 for stage in [StageId::Finish, StageId::Headers] {
4265 assert_eq!(
4266 provider.get_stage_checkpoint(stage).unwrap().unwrap().block_number,
4267 100
4268 );
4269 }
4270 assert_eq!(
4271 provider.get_stage_checkpoint(StageId::Finish).unwrap(),
4272 Some(checkpoint)
4273 );
4274 }
4275
4276 let advanced = StageCheckpoint::new(100)
4277 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(60) });
4278 assert!(matches!(
4279 provider.save_stage_checkpoint(StageId::Finish, advanced),
4280 Err(ProviderError::UnverifiedSnapState { .. })
4281 ));
4282
4283 provider.update_pipeline_stages(90, false).unwrap();
4285 let rewind = StageCheckpoint::new(80)
4286 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(40) });
4287 provider.save_stage_checkpoint(StageId::Finish, rewind).unwrap();
4288 provider.update_pipeline_stages(30, true).unwrap();
4289
4290 attempt.verify();
4291 provider.write_snap_attempt(&attempt).unwrap();
4292 provider.save_stage_checkpoint(StageId::Finish, advanced).unwrap();
4293 provider.update_pipeline_stages(100, true).unwrap();
4294 }
4295 }
4296
4297 #[test]
4298 fn test_receipts_by_block_range_empty_range() {
4299 let factory = create_test_provider_factory();
4300 let provider = factory.provider().unwrap();
4301
4302 let start = 10u64;
4304 let end = 9u64;
4305 let result = provider.receipts_by_block_range(start..=end).unwrap();
4306 assert_eq!(result, Vec::<Vec<reth_ethereum_primitives::Receipt>>::new());
4307 }
4308
4309 #[test]
4310 fn unwind_commit_waits_for_pre_commit_readers() {
4311 let factory = create_test_provider_factory();
4312
4313 let reader = factory.provider().unwrap();
4314 let provider_rw = factory.unwind_provider_rw().unwrap();
4315 provider_rw.write_metadata("unwind-wait-test", vec![1]).unwrap();
4316 let (done_tx, done_rx) = mpsc::channel();
4317
4318 let handle = std::thread::spawn(move || {
4319 let result = provider_rw.commit();
4320 done_tx.send(result).unwrap();
4321 });
4322
4323 assert!(
4324 done_rx.recv_timeout(Duration::from_millis(50)).is_err(),
4325 "unwind commit should wait while an older read transaction is still open"
4326 );
4327
4328 drop(reader);
4329
4330 done_rx.recv_timeout(Duration::from_secs(1)).unwrap().unwrap();
4331 handle.join().unwrap();
4332 }
4333
4334 #[test]
4335 fn test_receipts_by_block_range_nonexistent_blocks() {
4336 let factory = create_test_provider_factory();
4337 let provider = factory.provider().unwrap();
4338
4339 let result = provider.receipts_by_block_range(10..=12).unwrap();
4341 assert_eq!(result, vec![vec![], vec![], vec![]]);
4342 }
4343
4344 #[test]
4345 fn test_receipts_by_block_range_single_block() {
4346 let factory = create_test_provider_factory();
4347 let data = BlockchainTestData::default();
4348
4349 let provider_rw = factory.provider_rw().unwrap();
4350 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4351 provider_rw
4352 .write_state(
4353 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4354 crate::OriginalValuesKnown::No,
4355 StateWriteConfig::default(),
4356 )
4357 .unwrap();
4358 provider_rw.insert_block(&data.blocks[0].0).unwrap();
4359 provider_rw
4360 .write_state(
4361 &data.blocks[0].1,
4362 crate::OriginalValuesKnown::No,
4363 StateWriteConfig::default(),
4364 )
4365 .unwrap();
4366 provider_rw.commit().unwrap();
4367
4368 let provider = factory.provider().unwrap();
4369 let result = provider.receipts_by_block_range(1..=1).unwrap();
4370
4371 assert_eq!(result.len(), 1);
4373 assert_eq!(result[0].len(), 1);
4374 assert_eq!(result[0][0], data.blocks[0].1.receipts()[0][0]);
4375 }
4376
4377 #[test]
4378 fn test_receipts_by_block_range_multiple_blocks() {
4379 let factory = create_test_provider_factory();
4380 let data = BlockchainTestData::default();
4381
4382 let provider_rw = factory.provider_rw().unwrap();
4383 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4384 provider_rw
4385 .write_state(
4386 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4387 crate::OriginalValuesKnown::No,
4388 StateWriteConfig::default(),
4389 )
4390 .unwrap();
4391 for (block, outcome) in data.blocks.iter().take(3) {
4392 provider_rw.insert_block(block).unwrap();
4393 provider_rw
4394 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4395 .unwrap();
4396 }
4397 provider_rw.commit().unwrap();
4398
4399 let provider = factory.provider().unwrap();
4400 let result = provider.receipts_by_block_range(1..=3).unwrap();
4401
4402 assert_eq!(result.len(), 3);
4404 for (i, block_receipts) in result.iter().enumerate() {
4405 assert_eq!(block_receipts.len(), 1);
4406 assert_eq!(block_receipts[0], data.blocks[i].1.receipts()[0][0]);
4407 }
4408 }
4409
4410 #[test]
4411 fn test_receipts_by_block_range_blocks_with_varying_tx_counts() {
4412 let factory = create_test_provider_factory();
4413 let data = BlockchainTestData::default();
4414
4415 let provider_rw = factory.provider_rw().unwrap();
4416 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4417 provider_rw
4418 .write_state(
4419 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4420 crate::OriginalValuesKnown::No,
4421 StateWriteConfig::default(),
4422 )
4423 .unwrap();
4424
4425 for (block, outcome) in data.blocks.iter().take(3) {
4427 provider_rw.insert_block(block).unwrap();
4428 provider_rw
4429 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4430 .unwrap();
4431 }
4432 provider_rw.commit().unwrap();
4433
4434 let provider = factory.provider().unwrap();
4435 let result = provider.receipts_by_block_range(1..=3).unwrap();
4436
4437 assert_eq!(result.len(), 3);
4439 for block_receipts in &result {
4440 assert_eq!(block_receipts.len(), 1);
4441 }
4442 }
4443
4444 #[test]
4445 fn test_receipts_by_block_range_partial_range() {
4446 let factory = create_test_provider_factory();
4447 let data = BlockchainTestData::default();
4448
4449 let provider_rw = factory.provider_rw().unwrap();
4450 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4451 provider_rw
4452 .write_state(
4453 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4454 crate::OriginalValuesKnown::No,
4455 StateWriteConfig::default(),
4456 )
4457 .unwrap();
4458 for (block, outcome) in data.blocks.iter().take(3) {
4459 provider_rw.insert_block(block).unwrap();
4460 provider_rw
4461 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4462 .unwrap();
4463 }
4464 provider_rw.commit().unwrap();
4465
4466 let provider = factory.provider().unwrap();
4467
4468 let result = provider.receipts_by_block_range(2..=5).unwrap();
4470 assert_eq!(result.len(), 4);
4471
4472 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]);
4479 assert_eq!(result[1][0], data.blocks[2].1.receipts()[0][0]);
4480 }
4481
4482 #[test]
4483 fn test_receipts_by_block_range_all_empty_blocks() {
4484 let factory = create_test_provider_factory();
4485 let mut rng = generators::rng();
4486
4487 let mut blocks = Vec::new();
4489 for i in 0..3 {
4490 let block =
4491 random_block(&mut rng, i, BlockParams { tx_count: Some(0), ..Default::default() });
4492 blocks.push(block);
4493 }
4494
4495 let provider_rw = factory.provider_rw().unwrap();
4496 for block in blocks {
4497 provider_rw.insert_block(&block.try_recover().unwrap()).unwrap();
4498 }
4499 provider_rw.commit().unwrap();
4500
4501 let provider = factory.provider().unwrap();
4502 let result = provider.receipts_by_block_range(1..=3).unwrap();
4503
4504 assert_eq!(result.len(), 3);
4505 for block_receipts in result {
4506 assert_eq!(block_receipts.len(), 0);
4507 }
4508 }
4509
4510 #[test]
4511 fn test_receipts_by_block_range_consistency_with_individual_calls() {
4512 let factory = create_test_provider_factory();
4513 let data = BlockchainTestData::default();
4514
4515 let provider_rw = factory.provider_rw().unwrap();
4516 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4517 provider_rw
4518 .write_state(
4519 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4520 crate::OriginalValuesKnown::No,
4521 StateWriteConfig::default(),
4522 )
4523 .unwrap();
4524 for (block, outcome) in data.blocks.iter().take(3) {
4525 provider_rw.insert_block(block).unwrap();
4526 provider_rw
4527 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4528 .unwrap();
4529 }
4530 provider_rw.commit().unwrap();
4531
4532 let provider = factory.provider().unwrap();
4533
4534 let range_result = provider.receipts_by_block_range(1..=3).unwrap();
4536
4537 let mut individual_results = Vec::new();
4539 for block_num in 1..=3 {
4540 let receipts =
4541 provider.receipts_by_block(block_num.into()).unwrap().unwrap_or_default();
4542 individual_results.push(receipts);
4543 }
4544
4545 assert_eq!(range_result, individual_results);
4546 }
4547
4548 #[test]
4549 fn test_receipts_by_block_returns_none_for_missing_unpruned_receipts() {
4550 let factory = create_test_provider_factory();
4551 let data = BlockchainTestData::default();
4552
4553 let provider_rw = factory.provider_rw().unwrap();
4554 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4555 provider_rw.insert_block(&data.blocks[0].0).unwrap();
4556 provider_rw.commit().unwrap();
4557
4558 let provider = factory.provider().unwrap();
4559 assert!(provider.receipts_by_block(1.into()).unwrap().is_none());
4560 }
4561
4562 #[test]
4563 fn test_write_trie_updates_sorted() {
4564 use reth_trie::{
4565 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
4566 BranchNodeCompact, StorageTrieEntry,
4567 };
4568
4569 let factory = create_test_provider_factory();
4570 let provider_rw = factory.provider_rw().unwrap();
4571
4572 {
4574 let tx = provider_rw.tx_ref();
4575 let mut cursor = tx.cursor_write::<tables::AccountsTrie>().unwrap();
4576
4577 let to_delete = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4]));
4579 cursor
4580 .upsert(
4581 to_delete,
4582 &BranchNodeCompact::new(
4583 0b1010_1010_1010_1010, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4587 None,
4588 ),
4589 )
4590 .unwrap();
4591
4592 let to_update = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2]));
4594 cursor
4595 .upsert(
4596 to_update,
4597 &BranchNodeCompact::new(
4598 0b0101_0101_0101_0101, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4602 None,
4603 ),
4604 )
4605 .unwrap();
4606 }
4607
4608 let storage_address1 = B256::repeat_byte(1u8);
4610 let storage_address2 = B256::repeat_byte(2u8);
4611 {
4612 let tx = provider_rw.tx_ref();
4613 let mut storage_cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
4614
4615 storage_cursor
4617 .upsert(
4618 storage_address1,
4619 &StorageTrieEntry {
4620 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0x2, 0x0])),
4621 node: BranchNodeCompact::new(
4622 0b0011_0011_0011_0011, 0b0000_0000_0000_0000,
4624 0b0000_0000_0000_0000,
4625 vec![],
4626 None,
4627 ),
4628 },
4629 )
4630 .unwrap();
4631
4632 storage_cursor
4634 .upsert(
4635 storage_address2,
4636 &StorageTrieEntry {
4637 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xa, 0xb])),
4638 node: BranchNodeCompact::new(
4639 0b1100_1100_1100_1100,
4640 0b0000_0000_0000_0000,
4641 0b0000_0000_0000_0000,
4642 vec![],
4643 None,
4644 ),
4645 },
4646 )
4647 .unwrap();
4648 storage_cursor
4649 .upsert(
4650 storage_address2,
4651 &StorageTrieEntry {
4652 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xc, 0xd])),
4653 node: BranchNodeCompact::new(
4654 0b0011_1100_0011_1100,
4655 0b0000_0000_0000_0000,
4656 0b0000_0000_0000_0000,
4657 vec![],
4658 None,
4659 ),
4660 },
4661 )
4662 .unwrap();
4663 }
4664
4665 let account_nodes = vec![
4667 (
4668 Nibbles::from_nibbles([0x1, 0x2]),
4669 Some(BranchNodeCompact::new(
4670 0b1111_1111_1111_1111, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4674 None,
4675 )),
4676 ),
4677 (Nibbles::from_nibbles([0x3, 0x4]), None), (
4679 Nibbles::from_nibbles([0x5, 0x6]),
4680 Some(BranchNodeCompact::new(
4681 0b1111_1111_1111_1111, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4685 None,
4686 )),
4687 ),
4688 ];
4689
4690 let storage_trie1 = StorageTrieUpdatesSorted {
4692 storage_nodes: vec![
4693 (
4694 Nibbles::from_nibbles([0x1, 0x0]),
4695 Some(BranchNodeCompact::new(
4696 0b1111_0000_0000_0000, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4700 None,
4701 )),
4702 ),
4703 (Nibbles::from_nibbles([0x2, 0x0]), None), ],
4705 };
4706
4707 let storage_trie2 = StorageTrieUpdatesSorted {
4708 storage_nodes: vec![
4709 (Nibbles::from_nibbles([0xa, 0xb]), None),
4710 (Nibbles::from_nibbles([0xc, 0xd]), None),
4711 ],
4712 };
4713
4714 let mut storage_tries = B256Map::default();
4715 storage_tries.insert(storage_address1, storage_trie1);
4716 storage_tries.insert(storage_address2, storage_trie2);
4717
4718 let trie_updates = TrieUpdatesSorted::new(account_nodes, storage_tries);
4719
4720 let num_entries = provider_rw.write_trie_updates_sorted(&trie_updates).unwrap();
4722
4723 assert_eq!(num_entries, 7);
4726
4727 let tx = provider_rw.tx_ref();
4729 let mut cursor = tx.cursor_read::<tables::AccountsTrie>().unwrap();
4730
4731 let nibbles1 = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2]));
4733 let entry1 = cursor.seek_exact(nibbles1).unwrap();
4734 assert!(entry1.is_some(), "Updated account node should exist");
4735 let expected_mask = reth_trie::TrieMask::new(0b1111_1111_1111_1111);
4736 assert_eq!(
4737 entry1.unwrap().1.state_mask,
4738 expected_mask,
4739 "Account node should have updated state_mask"
4740 );
4741
4742 let nibbles2 = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4]));
4744 let entry2 = cursor.seek_exact(nibbles2).unwrap();
4745 assert!(entry2.is_none(), "Deleted account node should not exist");
4746
4747 let nibbles3 = StoredNibbles(Nibbles::from_nibbles([0x5, 0x6]));
4749 let entry3 = cursor.seek_exact(nibbles3).unwrap();
4750 assert!(entry3.is_some(), "New account node should exist");
4751
4752 let mut storage_cursor = tx.cursor_dup_read::<tables::StoragesTrie>().unwrap();
4754
4755 let storage_entries1: Vec<_> = storage_cursor
4757 .walk_dup(Some(storage_address1), None)
4758 .unwrap()
4759 .collect::<Result<Vec<_>, _>>()
4760 .unwrap();
4761 assert_eq!(
4762 storage_entries1.len(),
4763 1,
4764 "Storage address1 should have 1 entry after deletion"
4765 );
4766 assert_eq!(
4767 storage_entries1[0].1.nibbles.0,
4768 Nibbles::from_nibbles([0x1, 0x0]),
4769 "Remaining entry should be [0x1, 0x0]"
4770 );
4771
4772 let storage_entries2: Vec<_> = storage_cursor
4774 .walk_dup(Some(storage_address2), None)
4775 .unwrap()
4776 .collect::<Result<Vec<_>, _>>()
4777 .unwrap();
4778 assert_eq!(storage_entries2.len(), 0, "Storage address2 should be empty after removal");
4779
4780 provider_rw.commit().unwrap();
4781 }
4782
4783 #[test]
4784 fn test_save_blocks_only_masks_trie_with_deferred_blocks() {
4785 use reth_trie::{
4786 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
4787 BranchNodeCompact, HashedPostStateSorted, HashedStorageSorted,
4788 };
4789
4790 fn branch(mask: u16) -> BranchNodeCompact {
4791 BranchNodeCompact::new(mask, 0, 0, vec![], None)
4792 }
4793
4794 let factory = create_test_provider_factory();
4795 factory.set_storage_settings_cache(StorageSettings::v1());
4796
4797 let mut test_block_builder = TestBlockBuilder::eth().with_state();
4798 let genesis = test_block_builder.get_executed_blocks(0..1).next().unwrap();
4799 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..3).collect();
4800
4801 let provider_rw = factory.provider_rw().unwrap();
4802 save_genesis(&provider_rw, &genesis).unwrap();
4803 provider_rw.commit().unwrap();
4804
4805 let kept_account = B256::with_last_byte(0x11);
4806 let masked_account = B256::with_last_byte(0x12);
4807 let kept_storage = B256::with_last_byte(0x21);
4808 let masked_storage = B256::with_last_byte(0x22);
4809 let kept_slot = B256::with_last_byte(0x31);
4810 let masked_slot = B256::with_last_byte(0x32);
4811 let kept_account_node = Nibbles::from_nibbles([0x1, 0x2]);
4812 let masked_account_node = Nibbles::from_nibbles([0x1, 0x3]);
4813 let kept_storage_node = Nibbles::from_nibbles([0x2, 0x1]);
4814 let masked_storage_node = Nibbles::from_nibbles([0x2, 0x2]);
4815 let full_persist_base = &blocks[0];
4816 let deferred_trie_base = &blocks[1];
4817
4818 let full_persist_hashed_state = HashedPostStateSorted::new(
4819 vec![
4820 (kept_account, Some(Account::default())),
4821 (masked_account, Some(Account { nonce: 1, ..Default::default() })),
4822 ],
4823 B256Map::from_iter([
4824 (kept_storage, HashedStorageSorted { storage_slots: vec![(kept_slot, U256::ONE)] }),
4825 (
4826 masked_storage,
4827 HashedStorageSorted { storage_slots: vec![(masked_slot, U256::from(2))] },
4828 ),
4829 ]),
4830 );
4831 let full_persist_trie_updates = TrieUpdatesSorted::new(
4832 vec![
4833 (kept_account_node, Some(branch(0b0000_1111_0000_1111))),
4834 (masked_account_node, Some(branch(0b1111_0000_1111_0000))),
4835 ],
4836 B256Map::from_iter([
4837 (
4838 kept_storage,
4839 StorageTrieUpdatesSorted {
4840 storage_nodes: vec![(kept_storage_node, Some(branch(0b1010)))],
4841 },
4842 ),
4843 (
4844 masked_storage,
4845 StorageTrieUpdatesSorted {
4846 storage_nodes: vec![(masked_storage_node, Some(branch(0b0101)))],
4847 },
4848 ),
4849 ]),
4850 );
4851
4852 let full_persist_block = ExecutedBlock::new(
4853 Arc::clone(&full_persist_base.recovered_block),
4854 Arc::clone(&full_persist_base.execution_output),
4855 SortedTrieData::new(
4856 Arc::new(full_persist_hashed_state),
4857 Arc::new(full_persist_trie_updates),
4858 ),
4859 );
4860
4861 let deferred_trie_hashed_state = HashedPostStateSorted::new(
4862 vec![(masked_account, Some(Account { nonce: 3, ..Default::default() }))],
4863 B256Map::from_iter([(
4864 masked_storage,
4865 HashedStorageSorted { storage_slots: vec![(masked_slot, U256::from(4))] },
4866 )]),
4867 );
4868 let deferred_trie_updates = TrieUpdatesSorted::new(
4869 vec![(masked_account_node, Some(branch(0b0011_0011)))],
4870 B256Map::from_iter([(
4871 masked_storage,
4872 StorageTrieUpdatesSorted {
4873 storage_nodes: vec![(masked_storage_node, Some(branch(0b1100)))],
4874 },
4875 )]),
4876 );
4877 let deferred_trie_block = ExecutedBlock::new(
4878 Arc::clone(&deferred_trie_base.recovered_block),
4879 Arc::clone(&deferred_trie_base.execution_output),
4880 SortedTrieData::new(
4881 Arc::new(deferred_trie_hashed_state),
4882 Arc::new(deferred_trie_updates),
4883 ),
4884 );
4885
4886 let provider_rw = factory.provider_rw().unwrap();
4887 let input = SaveBlocksInput::new(
4888 vec![full_persist_block.clone(), deferred_trie_block.clone()],
4889 0,
4890 0,
4891 2,
4892 0,
4893 );
4894 provider_rw.save_blocks(&input).unwrap();
4895 provider_rw.commit().unwrap();
4896
4897 let provider_rw = factory.provider_rw().unwrap();
4898 let input =
4899 SaveBlocksInput::new(vec![full_persist_block, deferred_trie_block.clone()], 2, 0, 2, 1);
4900 assert!(input.persist_rest_blocks().is_empty());
4901 provider_rw.save_blocks(&input).unwrap();
4902 provider_rw.commit().unwrap();
4903
4904 let provider = factory.provider().unwrap();
4905 let tx = provider.tx_ref();
4906 let finish_checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
4907 assert_eq!(finish_checkpoint.block_number, 2);
4908 assert_eq!(
4909 finish_checkpoint.finish_stage_checkpoint().unwrap().partial_state_trie,
4910 Some(1)
4911 );
4912 assert!(provider.block_hash(2).unwrap().is_some());
4913
4914 let mut hashed_accounts = tx.cursor_read::<tables::HashedAccounts>().unwrap();
4915 assert!(hashed_accounts.seek_exact(kept_account).unwrap().is_some());
4916 assert!(hashed_accounts.seek_exact(masked_account).unwrap().is_none());
4917
4918 let mut hashed_storages = tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
4919 assert!(hashed_storages.seek_by_key_subkey(kept_storage, kept_slot).unwrap().is_some());
4920 assert!(hashed_storages
4921 .walk_dup(Some(masked_storage), None)
4922 .unwrap()
4923 .next()
4924 .transpose()
4925 .unwrap()
4926 .is_none());
4927
4928 let mut account_trie = tx.cursor_read::<tables::AccountsTrie>().unwrap();
4929 assert!(account_trie.seek_exact(StoredNibbles(kept_account_node)).unwrap().is_some());
4930 assert!(account_trie.seek_exact(StoredNibbles(masked_account_node)).unwrap().is_none());
4931
4932 let mut storage_trie = tx.cursor_dup_read::<tables::StoragesTrie>().unwrap();
4933 let kept_entries: Vec<_> = storage_trie
4934 .walk_dup(Some(kept_storage), None)
4935 .unwrap()
4936 .collect::<Result<Vec<_>, _>>()
4937 .unwrap();
4938 assert_eq!(kept_entries.len(), 1);
4939 assert_eq!(kept_entries[0].1.nibbles.0, kept_storage_node);
4940
4941 let masked_entries: Vec<_> = storage_trie
4942 .walk_dup(Some(masked_storage), None)
4943 .unwrap()
4944 .collect::<Result<Vec<_>, _>>()
4945 .unwrap();
4946 assert!(masked_entries.is_empty());
4947
4948 drop(storage_trie);
4949 drop(account_trie);
4950 drop(hashed_storages);
4951 drop(hashed_accounts);
4952 drop(provider);
4953
4954 let provider_rw = factory.provider_rw().unwrap();
4955 let input = SaveBlocksInput::new(vec![deferred_trie_block], 2, 1, 2, 2);
4956 assert!(input.persist_rest_blocks().is_empty());
4957 provider_rw.save_blocks(&input).unwrap();
4958 provider_rw.commit().unwrap();
4959
4960 let provider = factory.provider().unwrap();
4961 let finish_checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
4962 assert_eq!(finish_checkpoint.block_number, 2);
4963 assert!(finish_checkpoint.finish_stage_checkpoint().is_none());
4964
4965 let mut hashed_accounts =
4966 provider.tx_ref().cursor_read::<tables::HashedAccounts>().unwrap();
4967 let (_, account) = hashed_accounts.seek_exact(masked_account).unwrap().unwrap();
4968 assert_eq!(account.nonce, 3);
4969
4970 let mut hashed_storages =
4971 provider.tx_ref().cursor_dup_read::<tables::HashedStorages>().unwrap();
4972 let storage =
4973 hashed_storages.seek_by_key_subkey(masked_storage, masked_slot).unwrap().unwrap();
4974 assert_eq!(storage.value, U256::from(4));
4975
4976 let mut account_trie = provider.tx_ref().cursor_read::<tables::AccountsTrie>().unwrap();
4977 assert!(account_trie.seek_exact(StoredNibbles(masked_account_node)).unwrap().is_some());
4978
4979 let mut storage_trie = provider.tx_ref().cursor_dup_read::<tables::StoragesTrie>().unwrap();
4980 let masked_entries: Vec<_> = storage_trie
4981 .walk_dup(Some(masked_storage), None)
4982 .unwrap()
4983 .collect::<Result<Vec<_>, _>>()
4984 .unwrap();
4985 assert_eq!(masked_entries.len(), 1);
4986 assert_eq!(masked_entries[0].1.nibbles.0, masked_storage_node);
4987 }
4988
4989 #[test]
4990 fn test_save_blocks_partial_cycles_do_not_duplicate_static_file_writes() {
4991 let factory = create_test_provider_factory();
4992 let mut test_block_builder = TestBlockBuilder::eth().with_state();
4993
4994 let genesis = test_block_builder.get_executed_blocks(0..1).next().unwrap();
4995 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..5).collect();
4996
4997 let provider_rw = factory.provider_rw().unwrap();
4998 save_genesis(&provider_rw, &genesis).unwrap();
4999 provider_rw.commit().unwrap();
5000
5001 let provider_rw = factory.provider_rw().unwrap();
5002 let input = SaveBlocksInput::new(blocks[..2].to_vec(), 0, 0, 2, 2);
5003 provider_rw.save_blocks(&input).unwrap();
5004 provider_rw.commit().unwrap();
5005
5006 let provider_rw = factory.provider_rw().unwrap();
5007 let input = SaveBlocksInput::new(blocks[2..].to_vec(), 2, 2, 4, 2);
5008 provider_rw.save_blocks(&input).unwrap();
5009 provider_rw.commit().unwrap();
5010
5011 let provider_rw = factory.provider_rw().unwrap();
5012 let stale_input = SaveBlocksInput::new(vec![blocks[0].clone()], 0, 0, 1, 1);
5013 let err = provider_rw.save_blocks(&stale_input).unwrap_err();
5014 assert!(err.to_string().contains("persistence frontiers do not match Finish checkpoint"));
5015 drop(provider_rw);
5016
5017 let provider = factory.provider().unwrap();
5018 let finish_checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
5019 assert_eq!(finish_checkpoint.block_number, 4);
5020 assert_eq!(
5021 finish_checkpoint.finish_stage_checkpoint().unwrap().partial_state_trie,
5022 Some(2)
5023 );
5024
5025 let static_files = factory.static_file_provider();
5026 assert_eq!(static_files.get_highest_static_file_block(StaticFileSegment::Headers), Some(4));
5027 assert_eq!(
5028 static_files.get_highest_static_file_block(StaticFileSegment::Transactions),
5029 Some(4)
5030 );
5031 assert_eq!(
5032 static_files.get_highest_static_file_block(StaticFileSegment::Receipts),
5033 Some(4)
5034 );
5035 }
5036
5037 #[test]
5038 fn unwind_restores_trie_nodes_masked_above_the_frontier() {
5039 let factory = create_test_provider_factory();
5040 let blocks = TestBlockBuilder::eth().get_executed_blocks(0..11).collect::<Vec<_>>();
5041 let provider = factory.provider_rw().unwrap();
5042 for block in &blocks {
5043 provider.insert_block(block.recovered_block()).unwrap();
5044 }
5045 for block in &blocks[6..] {
5046 factory.overlay_manager().insert_block(block.clone());
5047 }
5048 provider
5049 .save_stage_checkpoint(
5050 StageId::Finish,
5051 StageCheckpoint::new(10)
5052 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(5) }),
5053 )
5054 .unwrap();
5055 let address = Address::with_last_byte(1);
5056 provider
5059 .tx_ref()
5060 .put::<tables::AccountChangeSets>(
5061 9,
5062 AccountBeforeTx { address, info: Some(Account { nonce: 1, ..Default::default() }) },
5063 )
5064 .unwrap();
5065 for slot in 0..257 {
5066 provider
5067 .tx_ref()
5068 .put::<tables::StorageChangeSets>(
5069 BlockNumberAddress((9, address)),
5070 StorageEntry { key: B256::from(U256::from(slot)), value: U256::from(1) },
5071 )
5072 .unwrap();
5073 }
5074 provider.commit().unwrap();
5075 let provider = factory.provider_rw().unwrap();
5076 assert_eq!(provider.tx_ref().entries::<tables::StoragesTrie>().unwrap(), 0);
5077 provider.unwind_trie_state_from(9).unwrap();
5078 assert!(provider.tx_ref().entries::<tables::StoragesTrie>().unwrap() > 0);
5079 }
5080
5081 #[test]
5082 fn remove_block_and_execution_above_returns_persistence_frontiers() {
5083 let factory = create_test_provider_factory();
5084 let mut test_block_builder = TestBlockBuilder::eth().with_state();
5085
5086 let genesis = test_block_builder.get_executed_blocks(0..1).next().unwrap();
5087 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..5).collect();
5088
5089 let provider_rw = factory.provider_rw().unwrap();
5090 save_genesis(&provider_rw, &genesis).unwrap();
5091 provider_rw.commit().unwrap();
5092
5093 for block in &blocks[2..] {
5094 factory.overlay_manager().insert_block(block.clone());
5095 }
5096
5097 let provider_rw = factory.provider_rw().unwrap();
5098 let input = SaveBlocksInput::new(blocks, 0, 0, 4, 2);
5099 provider_rw.save_blocks(&input).unwrap();
5100 provider_rw.commit().unwrap();
5101
5102 let provider_rw = factory.provider_rw().unwrap();
5103 let frontiers = provider_rw.remove_block_and_execution_above(3).unwrap();
5104 assert_eq!(frontiers, PersistenceFrontiers { db_tip: 3, partial_state_trie: 2 });
5105 provider_rw.commit().unwrap();
5106
5107 let provider = factory.provider().unwrap();
5108 let checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
5109 assert_eq!(
5110 checkpoint.finish_stage_checkpoint().and_then(|finish| finish.partial_state_trie()),
5111 Some(2)
5112 );
5113 }
5114
5115 #[test]
5116 fn test_prunable_receipts_logic() {
5117 let insert_blocks =
5118 |provider_rw: &DatabaseProviderRW<_, _>, tip_block: u64, tx_count: u8| {
5119 let mut rng = generators::rng();
5120 for block_num in 0..=tip_block {
5121 let block = random_block(
5122 &mut rng,
5123 block_num,
5124 BlockParams { tx_count: Some(tx_count), ..Default::default() },
5125 );
5126 provider_rw.insert_block(&block.try_recover().unwrap()).unwrap();
5127 }
5128 };
5129
5130 let write_receipts = |provider_rw: DatabaseProviderRW<_, _>, block: u64| {
5131 let outcome = ExecutionOutcome {
5132 first_block: block,
5133 receipts: vec![vec![Receipt {
5134 tx_type: Default::default(),
5135 success: true,
5136 cumulative_gas_used: block, logs: vec![],
5138 }]],
5139 ..Default::default()
5140 };
5141 provider_rw
5142 .write_state(&outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
5143 .unwrap();
5144 provider_rw.commit().unwrap();
5145 };
5146
5147 {
5149 let factory = create_test_provider_factory();
5150 let storage_settings = StorageSettings::v1();
5151 factory.set_storage_settings_cache(storage_settings);
5152 let factory = factory.with_prune_modes(PruneModes {
5153 receipts: Some(PruneMode::Before(100)),
5154 ..Default::default()
5155 });
5156
5157 let tip_block = 200u64;
5158 let first_block = 1u64;
5159
5160 let provider_rw = factory.provider_rw().unwrap();
5162 insert_blocks(&provider_rw, tip_block, 1);
5163 provider_rw.commit().unwrap();
5164
5165 write_receipts(
5166 factory.provider_rw().unwrap().with_minimum_pruning_distance(100),
5167 first_block,
5168 );
5169 write_receipts(
5170 factory.provider_rw().unwrap().with_minimum_pruning_distance(100),
5171 tip_block - 1,
5172 );
5173
5174 let provider = factory.provider().unwrap();
5175
5176 assert!(provider.receipts_by_block(0.into()).unwrap().is_none());
5177 assert!(provider
5178 .receipts_by_block((tip_block - 1).into())
5179 .unwrap()
5180 .is_some_and(|r| r.len() == 1));
5181 }
5182
5183 {
5185 let factory = create_test_provider_factory();
5186 let storage_settings = StorageSettings::v2();
5187 factory.set_storage_settings_cache(storage_settings);
5188 let factory = factory.with_prune_modes(PruneModes {
5189 receipts: Some(PruneMode::Before(2)),
5190 ..Default::default()
5191 });
5192
5193 let tip_block = 200u64;
5194
5195 let provider_rw = factory.provider_rw().unwrap();
5197 insert_blocks(&provider_rw, tip_block, 1);
5198 provider_rw.commit().unwrap();
5199
5200 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 0);
5202 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 1);
5203
5204 assert!(factory
5205 .static_file_provider()
5206 .get_highest_static_file_tx(StaticFileSegment::Receipts)
5207 .is_none(),);
5208 assert!(factory
5209 .static_file_provider()
5210 .get_highest_static_file_block(StaticFileSegment::Receipts)
5211 .is_some_and(|b| b == 1),);
5212
5213 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 2);
5216 assert!(factory
5217 .static_file_provider()
5218 .get_highest_static_file_tx(StaticFileSegment::Receipts)
5219 .is_some_and(|num| num == 2),);
5220
5221 let factory = factory.with_prune_modes(PruneModes {
5225 receipts: Some(PruneMode::Before(100)),
5226 ..Default::default()
5227 });
5228 let provider_rw = factory.provider_rw().unwrap().with_minimum_pruning_distance(1);
5229 assert!(PruneMode::Distance(1).should_prune(3, tip_block));
5230 write_receipts(provider_rw, 3);
5231
5232 let provider = factory.provider().unwrap();
5237 assert!(EitherWriter::receipts_destination(&provider).is_static_file());
5238 for (num, has_receipt) in [(0, false), (1, false), (2, true), (3, true)] {
5239 let receipts = provider.receipts_by_block(num.into()).unwrap();
5240 if has_receipt {
5241 assert!(receipts.is_some_and(|r| r.len() == 1));
5242 } else {
5243 assert!(receipts.is_none());
5244 }
5245
5246 let receipt = provider.receipt(num).unwrap();
5247 if has_receipt {
5248 assert!(receipt.is_some_and(|r| r.cumulative_gas_used == num));
5249 } else {
5250 assert!(receipt.is_none());
5251 }
5252 }
5253 }
5254 }
5255
5256 #[test]
5257 fn test_unwind_storage_hashing_with_hashed_state() {
5258 let factory = create_test_provider_factory();
5259 let storage_settings = StorageSettings::v2();
5260 factory.set_storage_settings_cache(storage_settings);
5261
5262 let address = Address::random();
5263 let hashed_address = keccak256(address);
5264
5265 let plain_slot = B256::random();
5266 let hashed_slot = keccak256(plain_slot);
5267
5268 let current_value = U256::from(100);
5269 let old_value = U256::from(42);
5270
5271 let provider_rw = factory.provider_rw().unwrap();
5272 provider_rw
5273 .tx
5274 .cursor_dup_write::<tables::HashedStorages>()
5275 .unwrap()
5276 .upsert(hashed_address, &StorageEntry { key: hashed_slot, value: current_value })
5277 .unwrap();
5278
5279 let changesets = vec![(
5280 BlockNumberAddress((1, address)),
5281 StorageEntry { key: plain_slot, value: old_value },
5282 )];
5283
5284 let result = provider_rw.unwind_storage_hashing(changesets.into_iter()).unwrap();
5285
5286 assert_eq!(result.len(), 1);
5287 assert!(result.contains_key(&hashed_address));
5288 assert!(result[&hashed_address].contains(&hashed_slot));
5289
5290 let mut cursor = provider_rw.tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
5291 let entry = cursor
5292 .seek_by_key_subkey(hashed_address, hashed_slot)
5293 .unwrap()
5294 .expect("entry should exist");
5295 assert_eq!(entry.key, hashed_slot);
5296 assert_eq!(entry.value, old_value);
5297 }
5298
5299 #[test]
5300 fn test_write_and_remove_state_roundtrip_legacy() {
5301 let factory = create_test_provider_factory();
5302 let storage_settings = StorageSettings::v1();
5303 assert!(!storage_settings.use_hashed_state());
5304 factory.set_storage_settings_cache(storage_settings);
5305
5306 let address = Address::with_last_byte(1);
5307 let hashed_address = keccak256(address);
5308 let slot = U256::from(5);
5309 let slot_key = B256::from(slot);
5310 let hashed_slot = keccak256(slot_key);
5311
5312 let mut rng = generators::rng();
5313 let block0 =
5314 random_block(&mut rng, 0, BlockParams { tx_count: Some(0), ..Default::default() });
5315 let block1 =
5316 random_block(&mut rng, 1, BlockParams { tx_count: Some(0), ..Default::default() });
5317
5318 {
5319 let provider_rw = factory.provider_rw().unwrap();
5320 provider_rw.insert_block(&block0.try_recover().unwrap()).unwrap();
5321 provider_rw.insert_block(&block1.try_recover().unwrap()).unwrap();
5322 provider_rw
5323 .tx
5324 .cursor_write::<tables::PlainAccountState>()
5325 .unwrap()
5326 .upsert(address, &Account::default())
5327 .unwrap();
5328 provider_rw.commit().unwrap();
5329 }
5330
5331 let provider_rw = factory.provider_rw().unwrap();
5332
5333 let mut state_init: BundleStateInit = AddressMap::default();
5334 let mut storage_map: B256Map<(U256, U256)> = B256Map::default();
5335 storage_map.insert(slot_key, (U256::ZERO, U256::from(10)));
5336 state_init.insert(
5337 address,
5338 (
5339 Some(Account::default()),
5340 Some(Account { nonce: 1, ..Default::default() }),
5341 storage_map,
5342 ),
5343 );
5344
5345 let mut reverts_init: RevertsInit = HashMap::default();
5346 let mut block_reverts: AddressMap<AccountRevertInit> = AddressMap::default();
5347 block_reverts.insert(
5348 address,
5349 (
5350 Some(Some(Account::default())),
5351 vec![StorageEntry { key: slot_key, value: U256::ZERO }],
5352 ),
5353 );
5354 reverts_init.insert(1, block_reverts);
5355
5356 let execution_outcome =
5357 ExecutionOutcome::new_init(state_init, reverts_init, [], vec![vec![]], 1, vec![]);
5358
5359 provider_rw
5360 .write_state(
5361 &execution_outcome,
5362 OriginalValuesKnown::Yes,
5363 StateWriteConfig {
5364 write_receipts: false,
5365 write_account_changesets: true,
5366 write_storage_changesets: true,
5367 },
5368 )
5369 .unwrap();
5370
5371 let hashed_state =
5372 execution_outcome.hash_state_slow::<reth_trie::KeccakKeyHasher>().into_sorted();
5373 provider_rw.write_hashed_state(&hashed_state).unwrap();
5374
5375 let account = provider_rw
5376 .tx
5377 .cursor_read::<tables::PlainAccountState>()
5378 .unwrap()
5379 .seek_exact(address)
5380 .unwrap()
5381 .unwrap()
5382 .1;
5383 assert_eq!(account.nonce, 1);
5384
5385 let storage_entry = provider_rw
5386 .tx
5387 .cursor_dup_read::<tables::PlainStorageState>()
5388 .unwrap()
5389 .seek_by_key_subkey(address, slot_key)
5390 .unwrap()
5391 .unwrap();
5392 assert_eq!(storage_entry.key, slot_key);
5393 assert_eq!(storage_entry.value, U256::from(10));
5394
5395 let hashed_entry = provider_rw
5396 .tx
5397 .cursor_dup_read::<tables::HashedStorages>()
5398 .unwrap()
5399 .seek_by_key_subkey(hashed_address, hashed_slot)
5400 .unwrap()
5401 .unwrap();
5402 assert_eq!(hashed_entry.key, hashed_slot);
5403 assert_eq!(hashed_entry.value, U256::from(10));
5404
5405 let account_cs_entries = provider_rw
5406 .tx
5407 .cursor_dup_read::<tables::AccountChangeSets>()
5408 .unwrap()
5409 .walk(Some(1))
5410 .unwrap()
5411 .collect::<Result<Vec<_>, _>>()
5412 .unwrap();
5413 assert!(!account_cs_entries.is_empty());
5414
5415 let storage_cs_entries = provider_rw
5416 .tx
5417 .cursor_read::<tables::StorageChangeSets>()
5418 .unwrap()
5419 .walk(Some(BlockNumberAddress((1, address))))
5420 .unwrap()
5421 .collect::<Result<Vec<_>, _>>()
5422 .unwrap();
5423 assert!(!storage_cs_entries.is_empty());
5424 assert_eq!(storage_cs_entries[0].1.key, slot_key);
5425
5426 provider_rw.remove_state_above(0).unwrap();
5427
5428 let restored_account = provider_rw
5429 .tx
5430 .cursor_read::<tables::PlainAccountState>()
5431 .unwrap()
5432 .seek_exact(address)
5433 .unwrap()
5434 .unwrap()
5435 .1;
5436 assert_eq!(restored_account.nonce, 0);
5437
5438 let storage_gone = provider_rw
5439 .tx
5440 .cursor_dup_read::<tables::PlainStorageState>()
5441 .unwrap()
5442 .seek_by_key_subkey(address, slot_key)
5443 .unwrap();
5444 assert!(storage_gone.is_none() || storage_gone.unwrap().key != slot_key);
5445
5446 let account_cs_after = provider_rw
5447 .tx
5448 .cursor_dup_read::<tables::AccountChangeSets>()
5449 .unwrap()
5450 .walk(Some(1))
5451 .unwrap()
5452 .collect::<Result<Vec<_>, _>>()
5453 .unwrap();
5454 assert!(account_cs_after.is_empty());
5455
5456 let storage_cs_after = provider_rw
5457 .tx
5458 .cursor_read::<tables::StorageChangeSets>()
5459 .unwrap()
5460 .walk(Some(BlockNumberAddress((1, address))))
5461 .unwrap()
5462 .collect::<Result<Vec<_>, _>>()
5463 .unwrap();
5464 assert!(storage_cs_after.is_empty());
5465 }
5466
5467 #[test]
5468 fn test_unwind_storage_hashing_legacy() {
5469 let factory = create_test_provider_factory();
5470 let storage_settings = StorageSettings::v1();
5471 assert!(!storage_settings.use_hashed_state());
5472 factory.set_storage_settings_cache(storage_settings);
5473
5474 let address = Address::random();
5475 let hashed_address = keccak256(address);
5476
5477 let plain_slot = B256::random();
5478 let hashed_slot = keccak256(plain_slot);
5479
5480 let current_value = U256::from(100);
5481 let old_value = U256::from(42);
5482
5483 let provider_rw = factory.provider_rw().unwrap();
5484 provider_rw
5485 .tx
5486 .cursor_dup_write::<tables::HashedStorages>()
5487 .unwrap()
5488 .upsert(hashed_address, &StorageEntry { key: hashed_slot, value: current_value })
5489 .unwrap();
5490
5491 let changesets = vec![(
5492 BlockNumberAddress((1, address)),
5493 StorageEntry { key: plain_slot, value: old_value },
5494 )];
5495
5496 let result = provider_rw.unwind_storage_hashing(changesets.into_iter()).unwrap();
5497
5498 assert_eq!(result.len(), 1);
5499 assert!(result.contains_key(&hashed_address));
5500 assert!(result[&hashed_address].contains(&hashed_slot));
5501
5502 let mut cursor = provider_rw.tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
5503 let entry = cursor
5504 .seek_by_key_subkey(hashed_address, hashed_slot)
5505 .unwrap()
5506 .expect("entry should exist");
5507 assert_eq!(entry.key, hashed_slot);
5508 assert_eq!(entry.value, old_value);
5509 }
5510
5511 #[test]
5512 fn test_write_state_hashed() {
5513 use reth_trie::{HashedPostState, KeccakKeyHasher};
5514
5515 let factory = create_test_provider_factory();
5516 factory.set_storage_settings_cache(StorageSettings::v2());
5517
5518 let address = Address::with_last_byte(1);
5519 let slot = U256::from(5);
5520 let slot_key = B256::from(slot);
5521 let hashed_address = keccak256(address);
5522 let hashed_slot = keccak256(slot_key);
5523
5524 {
5525 let sf = factory.static_file_provider();
5526 let mut hw = sf.latest_writer(StaticFileSegment::Headers).unwrap();
5527 let h0 = alloy_consensus::Header { number: 0, ..Default::default() };
5528 hw.append_header(&h0, &B256::ZERO).unwrap();
5529 let h1 = alloy_consensus::Header { number: 1, ..Default::default() };
5530 hw.append_header(&h1, &B256::ZERO).unwrap();
5531 hw.commit().unwrap();
5532
5533 let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
5534 aw.append_account_changeset(vec![], 0).unwrap();
5535 aw.commit().unwrap();
5536
5537 let mut sw = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
5538 sw.append_storage_changeset(vec![], 0).unwrap();
5539 sw.commit().unwrap();
5540 }
5541
5542 let provider_rw = factory.provider_rw().unwrap();
5543
5544 let bundle = BundleState::builder(1..=1)
5545 .state_present_account_info(
5546 address,
5547 AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() },
5548 )
5549 .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
5550 .revert_account_info(1, address, Some(None))
5551 .revert_storage(1, address, vec![(slot, U256::ZERO)])
5552 .build();
5553
5554 let execution_outcome = ExecutionOutcome::new(bundle.clone(), vec![vec![]], 1, Vec::new());
5555
5556 provider_rw
5557 .tx
5558 .put::<tables::BlockBodyIndices>(
5559 1,
5560 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5561 )
5562 .unwrap();
5563
5564 provider_rw
5565 .write_state(
5566 &execution_outcome,
5567 OriginalValuesKnown::Yes,
5568 StateWriteConfig {
5569 write_receipts: false,
5570 write_account_changesets: true,
5571 write_storage_changesets: true,
5572 },
5573 )
5574 .unwrap();
5575
5576 let hashed_state =
5577 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5578 provider_rw.write_hashed_state(&hashed_state).unwrap();
5579
5580 let plain_storage_entries = provider_rw
5581 .tx
5582 .cursor_dup_read::<tables::PlainStorageState>()
5583 .unwrap()
5584 .walk(None)
5585 .unwrap()
5586 .collect::<Result<Vec<_>, _>>()
5587 .unwrap();
5588 assert!(plain_storage_entries.is_empty());
5589
5590 let hashed_entry = provider_rw
5591 .tx
5592 .cursor_dup_read::<tables::HashedStorages>()
5593 .unwrap()
5594 .seek_by_key_subkey(hashed_address, hashed_slot)
5595 .unwrap()
5596 .unwrap();
5597 assert_eq!(hashed_entry.key, hashed_slot);
5598 assert_eq!(hashed_entry.value, U256::from(10));
5599
5600 provider_rw.static_file_provider().commit().unwrap();
5601
5602 let sf = factory.static_file_provider();
5603 let storage_cs = sf.storage_changeset(1).unwrap();
5604 assert!(!storage_cs.is_empty());
5605 assert_eq!(storage_cs[0].1.key, slot_key);
5606
5607 let account_cs = sf.account_block_changeset(1).unwrap();
5608 assert!(!account_cs.is_empty());
5609 assert_eq!(account_cs[0].address, address);
5610 }
5611
5612 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5613 enum StorageMode {
5614 V1,
5615 V2,
5616 }
5617
5618 fn run_save_blocks_and_verify(mode: StorageMode) {
5619 use alloy_primitives::map::{FbBuildHasher, HashMap};
5620
5621 let factory = create_test_provider_factory();
5622
5623 match mode {
5624 StorageMode::V1 => factory.set_storage_settings_cache(StorageSettings::v1()),
5625 StorageMode::V2 => factory.set_storage_settings_cache(StorageSettings::v2()),
5626 }
5627
5628 let num_blocks = 3u64;
5629 let accounts_per_block = 5usize;
5630 let slots_per_account = 3usize;
5631
5632 let genesis = SealedBlock::<reth_ethereum_primitives::Block>::from_sealed_parts(
5633 SealedHeader::new(
5634 Header { number: 0, difficulty: U256::ONE, ..Default::default() },
5635 B256::ZERO,
5636 ),
5637 Default::default(),
5638 );
5639
5640 let genesis_executed: ExecutedBlock = ExecutedBlock::new(
5641 Arc::new(genesis.try_recover().unwrap()),
5642 Arc::new(BlockExecutionOutput {
5643 result: BlockExecutionResult {
5644 receipts: vec![],
5645 requests: Default::default(),
5646 gas_used: 0,
5647 blob_gas_used: 0,
5648 },
5649 state: Default::default(),
5650 }),
5651 SortedTrieData::default(),
5652 );
5653 let provider_rw = factory.provider_rw().unwrap();
5654 save_genesis(&provider_rw, &genesis_executed).unwrap();
5655 provider_rw.commit().unwrap();
5656
5657 let mut blocks: Vec<ExecutedBlock> = Vec::new();
5658 let mut parent_hash = B256::ZERO;
5659
5660 for block_num in 1..=num_blocks {
5661 let mut builder = BundleState::builder(block_num..=block_num);
5662
5663 for acct_idx in 0..accounts_per_block {
5664 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5665 let info = AccountInfo {
5666 nonce: block_num,
5667 balance: U256::from(block_num * 100 + acct_idx as u64),
5668 ..Default::default()
5669 };
5670
5671 let storage: HashMap<U256, (U256, U256), FbBuildHasher<32>> = (1..=
5672 slots_per_account as u64)
5673 .map(|s| {
5674 (
5675 U256::from(s + acct_idx as u64 * 100),
5676 (U256::ZERO, U256::from(block_num * 1000 + s)),
5677 )
5678 })
5679 .collect();
5680
5681 let revert_storage: Vec<(U256, U256)> = (1..=slots_per_account as u64)
5682 .map(|s| (U256::from(s + acct_idx as u64 * 100), U256::ZERO))
5683 .collect();
5684
5685 builder = builder
5686 .state_present_account_info(address, info)
5687 .revert_account_info(block_num, address, Some(None))
5688 .state_storage(address, storage)
5689 .revert_storage(block_num, address, revert_storage);
5690 }
5691
5692 let bundle = builder.build();
5693
5694 let hashed_state =
5695 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5696
5697 let header = Header {
5698 number: block_num,
5699 parent_hash,
5700 difficulty: U256::ONE,
5701 ..Default::default()
5702 };
5703 let block = SealedBlock::<reth_ethereum_primitives::Block>::seal_parts(
5704 header,
5705 Default::default(),
5706 );
5707 parent_hash = block.hash();
5708
5709 let executed = ExecutedBlock::new(
5710 Arc::new(block.try_recover().unwrap()),
5711 Arc::new(BlockExecutionOutput {
5712 result: BlockExecutionResult {
5713 receipts: vec![],
5714 requests: Default::default(),
5715 gas_used: 0,
5716 blob_gas_used: 0,
5717 },
5718 state: bundle,
5719 }),
5720 SortedTrieData::new(Arc::new(hashed_state), Default::default()),
5721 );
5722 blocks.push(executed);
5723 }
5724
5725 let provider_rw = factory.provider_rw().unwrap();
5726 let input = SaveBlocksInput::new(blocks, 0, 0, num_blocks, num_blocks);
5727 provider_rw.save_blocks(&input).unwrap();
5728 provider_rw.commit().unwrap();
5729
5730 let provider = factory.provider().unwrap();
5731
5732 for block_num in 1..=num_blocks {
5733 for acct_idx in 0..accounts_per_block {
5734 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5735 let hashed_address = keccak256(address);
5736
5737 let ha_entry = provider
5738 .tx_ref()
5739 .cursor_read::<tables::HashedAccounts>()
5740 .unwrap()
5741 .seek_exact(hashed_address)
5742 .unwrap();
5743 assert!(
5744 ha_entry.is_some(),
5745 "HashedAccounts missing for block {block_num} acct {acct_idx}"
5746 );
5747
5748 for s in 1..=slots_per_account as u64 {
5749 let slot = U256::from(s + acct_idx as u64 * 100);
5750 let slot_key = B256::from(slot);
5751 let hashed_slot = keccak256(slot_key);
5752
5753 let hs_entry = provider
5754 .tx_ref()
5755 .cursor_dup_read::<tables::HashedStorages>()
5756 .unwrap()
5757 .seek_by_key_subkey(hashed_address, hashed_slot)
5758 .unwrap();
5759 assert!(
5760 hs_entry.is_some(),
5761 "HashedStorages missing for block {block_num} acct {acct_idx} slot {s}"
5762 );
5763 let entry = hs_entry.unwrap();
5764 assert_eq!(entry.key, hashed_slot);
5765 assert_eq!(entry.value, U256::from(block_num * 1000 + s));
5766 }
5767 }
5768 }
5769
5770 for block_num in 1..=num_blocks {
5771 let header = provider.header_by_number(block_num).unwrap();
5772 assert!(header.is_some(), "Header missing for block {block_num}");
5773
5774 let indices = provider.block_body_indices(block_num).unwrap();
5775 assert!(indices.is_some(), "BlockBodyIndices missing for block {block_num}");
5776 }
5777
5778 let plain_accounts = provider.tx_ref().entries::<tables::PlainAccountState>().unwrap();
5779 let plain_storage = provider.tx_ref().entries::<tables::PlainStorageState>().unwrap();
5780
5781 if mode == StorageMode::V2 {
5782 assert_eq!(plain_accounts, 0, "v2: PlainAccountState should be empty");
5783 assert_eq!(plain_storage, 0, "v2: PlainStorageState should be empty");
5784
5785 let mdbx_account_cs = provider.tx_ref().entries::<tables::AccountChangeSets>().unwrap();
5786 assert_eq!(mdbx_account_cs, 0, "v2: AccountChangeSets in MDBX should be empty");
5787
5788 let mdbx_storage_cs = provider.tx_ref().entries::<tables::StorageChangeSets>().unwrap();
5789 assert_eq!(mdbx_storage_cs, 0, "v2: StorageChangeSets in MDBX should be empty");
5790
5791 provider.static_file_provider().commit().unwrap();
5792 let sf = factory.static_file_provider();
5793
5794 for block_num in 1..=num_blocks {
5795 let account_cs = sf.account_block_changeset(block_num).unwrap();
5796 assert!(
5797 !account_cs.is_empty(),
5798 "v2: static file AccountChangeSets should exist for block {block_num}"
5799 );
5800
5801 let storage_cs = sf.storage_changeset(block_num).unwrap();
5802 assert!(
5803 !storage_cs.is_empty(),
5804 "v2: static file StorageChangeSets should exist for block {block_num}"
5805 );
5806
5807 for (_, entry) in &storage_cs {
5808 assert!(
5809 entry.key != keccak256(entry.key),
5810 "v2: static file storage changeset should have plain slot keys"
5811 );
5812 }
5813 }
5814
5815 let rocksdb = factory.rocksdb_provider();
5816 for block_num in 1..=num_blocks {
5817 for acct_idx in 0..accounts_per_block {
5818 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5819 let shards = rocksdb.account_history_shards(address).unwrap();
5820 assert!(
5821 !shards.is_empty(),
5822 "v2: RocksDB AccountsHistory missing for block {block_num} acct {acct_idx}"
5823 );
5824
5825 for s in 1..=slots_per_account as u64 {
5826 let slot = U256::from(s + acct_idx as u64 * 100);
5827 let slot_key = B256::from(slot);
5828 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5829 assert!(
5830 !shards.is_empty(),
5831 "v2: RocksDB StoragesHistory missing for block {block_num} acct {acct_idx} slot {s}"
5832 );
5833 }
5834 }
5835 }
5836 } else {
5837 assert!(plain_accounts > 0, "v1: PlainAccountState should not be empty");
5838 assert!(plain_storage > 0, "v1: PlainStorageState should not be empty");
5839
5840 let mdbx_account_cs = provider.tx_ref().entries::<tables::AccountChangeSets>().unwrap();
5841 assert!(mdbx_account_cs > 0, "v1: AccountChangeSets in MDBX should not be empty");
5842
5843 let mdbx_storage_cs = provider.tx_ref().entries::<tables::StorageChangeSets>().unwrap();
5844 assert!(mdbx_storage_cs > 0, "v1: StorageChangeSets in MDBX should not be empty");
5845
5846 for block_num in 1..=num_blocks {
5847 let storage_entries: Vec<_> = provider
5848 .tx_ref()
5849 .cursor_dup_read::<tables::StorageChangeSets>()
5850 .unwrap()
5851 .walk_range(BlockNumberAddress::range(block_num..=block_num))
5852 .unwrap()
5853 .collect::<Result<Vec<_>, _>>()
5854 .unwrap();
5855 assert!(
5856 !storage_entries.is_empty(),
5857 "v1: MDBX StorageChangeSets should have entries for block {block_num}"
5858 );
5859
5860 for (_, entry) in &storage_entries {
5861 let slot_key = B256::from(entry.key);
5862 assert!(
5863 slot_key != keccak256(slot_key),
5864 "v1: storage changeset keys should be plain (not hashed)"
5865 );
5866 }
5867 }
5868
5869 let mdbx_account_history =
5870 provider.tx_ref().entries::<tables::AccountsHistory>().unwrap();
5871 assert!(mdbx_account_history > 0, "v1: AccountsHistory in MDBX should not be empty");
5872
5873 let mdbx_storage_history =
5874 provider.tx_ref().entries::<tables::StoragesHistory>().unwrap();
5875 assert!(mdbx_storage_history > 0, "v1: StoragesHistory in MDBX should not be empty");
5876 }
5877 }
5878
5879 #[test]
5880 fn test_save_blocks_v1_table_assertions() {
5881 run_save_blocks_and_verify(StorageMode::V1);
5882 }
5883
5884 #[test]
5885 fn test_save_blocks_v2_table_assertions() {
5886 run_save_blocks_and_verify(StorageMode::V2);
5887 }
5888
5889 #[test]
5890 fn test_write_and_remove_state_roundtrip_v2() {
5891 let factory = create_test_provider_factory();
5892 let storage_settings = StorageSettings::v2();
5893 assert!(storage_settings.use_hashed_state());
5894 factory.set_storage_settings_cache(storage_settings);
5895
5896 let address = Address::with_last_byte(1);
5897 let hashed_address = keccak256(address);
5898 let slot = U256::from(5);
5899 let slot_key = B256::from(slot);
5900 let hashed_slot = keccak256(slot_key);
5901
5902 {
5903 let sf = factory.static_file_provider();
5904 let mut hw = sf.latest_writer(StaticFileSegment::Headers).unwrap();
5905 let h0 = alloy_consensus::Header { number: 0, ..Default::default() };
5906 hw.append_header(&h0, &B256::ZERO).unwrap();
5907 let h1 = alloy_consensus::Header { number: 1, ..Default::default() };
5908 hw.append_header(&h1, &B256::ZERO).unwrap();
5909 hw.commit().unwrap();
5910
5911 let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
5912 aw.append_account_changeset(vec![], 0).unwrap();
5913 aw.commit().unwrap();
5914
5915 let mut sw = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
5916 sw.append_storage_changeset(vec![], 0).unwrap();
5917 sw.commit().unwrap();
5918 }
5919
5920 {
5921 let provider_rw = factory.provider_rw().unwrap();
5922 provider_rw
5923 .tx
5924 .put::<tables::BlockBodyIndices>(
5925 0,
5926 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5927 )
5928 .unwrap();
5929 provider_rw
5930 .tx
5931 .put::<tables::BlockBodyIndices>(
5932 1,
5933 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5934 )
5935 .unwrap();
5936 provider_rw
5937 .tx
5938 .cursor_write::<tables::HashedAccounts>()
5939 .unwrap()
5940 .upsert(hashed_address, &Account::default())
5941 .unwrap();
5942 provider_rw.commit().unwrap();
5943 }
5944
5945 let provider_rw = factory.provider_rw().unwrap();
5946
5947 let bundle = BundleState::builder(1..=1)
5948 .state_present_account_info(
5949 address,
5950 AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() },
5951 )
5952 .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
5953 .revert_account_info(1, address, Some(None))
5954 .revert_storage(1, address, vec![(slot, U256::ZERO)])
5955 .build();
5956
5957 let execution_outcome = ExecutionOutcome::new(bundle.clone(), vec![vec![]], 1, Vec::new());
5958
5959 provider_rw
5960 .write_state(
5961 &execution_outcome,
5962 OriginalValuesKnown::Yes,
5963 StateWriteConfig {
5964 write_receipts: false,
5965 write_account_changesets: true,
5966 write_storage_changesets: true,
5967 },
5968 )
5969 .unwrap();
5970
5971 let hashed_state =
5972 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5973 provider_rw.write_hashed_state(&hashed_state).unwrap();
5974
5975 let hashed_account = provider_rw
5976 .tx
5977 .cursor_read::<tables::HashedAccounts>()
5978 .unwrap()
5979 .seek_exact(hashed_address)
5980 .unwrap()
5981 .unwrap()
5982 .1;
5983 assert_eq!(hashed_account.nonce, 1);
5984
5985 let hashed_entry = provider_rw
5986 .tx
5987 .cursor_dup_read::<tables::HashedStorages>()
5988 .unwrap()
5989 .seek_by_key_subkey(hashed_address, hashed_slot)
5990 .unwrap()
5991 .unwrap();
5992 assert_eq!(hashed_entry.key, hashed_slot);
5993 assert_eq!(hashed_entry.value, U256::from(10));
5994
5995 let plain_accounts = provider_rw.tx.entries::<tables::PlainAccountState>().unwrap();
5996 assert_eq!(plain_accounts, 0, "v2: PlainAccountState should be empty");
5997
5998 let plain_storage = provider_rw.tx.entries::<tables::PlainStorageState>().unwrap();
5999 assert_eq!(plain_storage, 0, "v2: PlainStorageState should be empty");
6000
6001 provider_rw.static_file_provider().commit().unwrap();
6002
6003 let sf = factory.static_file_provider();
6004 let storage_cs = sf.storage_changeset(1).unwrap();
6005 assert!(!storage_cs.is_empty(), "v2: storage changesets should be in static files");
6006 assert_eq!(storage_cs[0].1.key, slot_key, "v2: changeset key should be plain");
6007
6008 provider_rw.remove_state_above(0).unwrap();
6009
6010 let restored_account = provider_rw
6011 .tx
6012 .cursor_read::<tables::HashedAccounts>()
6013 .unwrap()
6014 .seek_exact(hashed_address)
6015 .unwrap();
6016 assert!(
6017 restored_account.is_none(),
6018 "v2: account should be removed (didn't exist before block 1)"
6019 );
6020
6021 let storage_gone = provider_rw
6022 .tx
6023 .cursor_dup_read::<tables::HashedStorages>()
6024 .unwrap()
6025 .seek_by_key_subkey(hashed_address, hashed_slot)
6026 .unwrap();
6027 assert!(
6028 storage_gone.is_none() || storage_gone.unwrap().key != hashed_slot,
6029 "v2: storage should be reverted (removed or different key)"
6030 );
6031
6032 let mdbx_storage_cs = provider_rw.tx.entries::<tables::StorageChangeSets>().unwrap();
6033 assert_eq!(mdbx_storage_cs, 0, "v2: MDBX StorageChangeSets should remain empty");
6034
6035 let mdbx_account_cs = provider_rw.tx.entries::<tables::AccountChangeSets>().unwrap();
6036 assert_eq!(mdbx_account_cs, 0, "v2: MDBX AccountChangeSets should remain empty");
6037 }
6038
6039 #[test]
6040 fn test_unwind_storage_history_indices_v2() {
6041 let factory = create_test_provider_factory();
6042 factory.set_storage_settings_cache(StorageSettings::v2());
6043
6044 let address = Address::with_last_byte(1);
6045 let slot_key = B256::with_last_byte(42);
6046
6047 {
6048 let rocksdb = factory.rocksdb_provider();
6049 let mut batch = rocksdb.batch();
6050 batch.append_storage_history_shard(address, slot_key, vec![3u64, 7, 10]).unwrap();
6051 batch.commit().unwrap();
6052
6053 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
6054 assert!(!shards.is_empty(), "history should be written to rocksdb");
6055 }
6056
6057 let provider_rw = factory.provider_rw().unwrap();
6058
6059 let changesets = vec![
6060 (
6061 BlockNumberAddress((7, address)),
6062 StorageEntry { key: slot_key, value: U256::from(5) },
6063 ),
6064 (
6065 BlockNumberAddress((10, address)),
6066 StorageEntry { key: slot_key, value: U256::from(8) },
6067 ),
6068 ];
6069
6070 let count = provider_rw.unwind_storage_history_indices(changesets.into_iter()).unwrap();
6071 assert_eq!(count, 2);
6072
6073 provider_rw.commit().unwrap();
6074
6075 let rocksdb = factory.rocksdb_provider();
6076 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
6077
6078 assert!(
6079 !shards.is_empty(),
6080 "history shards should still exist with block 3 after partial unwind"
6081 );
6082
6083 let all_blocks: Vec<u64> = shards.iter().flat_map(|(_, list)| list.iter()).collect();
6084 assert!(all_blocks.contains(&3), "block 3 should remain");
6085 assert!(!all_blocks.contains(&7), "block 7 should be unwound");
6086 assert!(!all_blocks.contains(&10), "block 10 should be unwound");
6087 }
6088
6089 #[test]
6090 fn test_chain_state_block_numbers_are_read_by_exact_key() {
6091 for (finalized, safe) in
6092 [(None, None), (None, Some(100)), (Some(90), None), (Some(90), Some(100))]
6093 {
6094 let factory = create_test_provider_factory();
6095 let provider = factory.provider_rw().unwrap();
6096 if let Some(block_number) = finalized {
6097 provider.save_finalized_block_number(block_number).unwrap();
6098 }
6099 if let Some(block_number) = safe {
6100 provider.save_safe_block_number(block_number).unwrap();
6101 }
6102
6103 assert_eq!(provider.last_finalized_block_number().unwrap(), finalized);
6104 assert_eq!(provider.last_safe_block_number().unwrap(), safe);
6105 provider.commit().unwrap();
6106
6107 let provider = factory.provider().unwrap();
6108 assert_eq!(provider.last_finalized_block_number().unwrap(), finalized);
6109 assert_eq!(provider.last_safe_block_number().unwrap(), safe);
6110 }
6111 }
6112}