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