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 changed_accounts = self.account_changesets_range(from..)?;
883
884 self.unwind_account_hashing(changed_accounts.iter())?;
886
887 self.unwind_account_history_indices(changed_accounts.iter())?;
889
890 let changed_storages = self.storage_changesets_range(from..)?;
891
892 self.unwind_storage_hashing(changed_storages.iter().copied())?;
894
895 self.unwind_storage_history_indices(changed_storages.iter().copied())?;
897
898 let db_tip_block = self
901 .get_stage_checkpoint(reth_stages_types::StageId::Finish)?
902 .as_ref()
903 .map(|chk| chk.block_number)
904 .ok_or_else(|| ProviderError::InsufficientChangesets {
905 requested: from,
906 available: 0..=0,
907 })?;
908
909 let trie_revert = self
910 .overlay_manager
911 .get_or_compute_cached_changesets_range(self, from..=db_tip_block)?;
912 self.write_trie_updates_sorted(&trie_revert)?;
913
914 Ok(())
915 }
916
917 fn remove_receipts_from(
919 &self,
920 from_tx: TxNumber,
921 last_block: BlockNumber,
922 ) -> ProviderResult<()> {
923 self.remove::<tables::Receipts<ReceiptTy<N>>>(from_tx..)?;
925
926 if EitherWriter::receipts_destination(self).is_static_file() {
927 let static_file_receipt_num =
928 self.static_file_provider.get_highest_static_file_tx(StaticFileSegment::Receipts);
929
930 let to_delete = static_file_receipt_num
931 .map(|static_num| (static_num + 1).saturating_sub(from_tx))
932 .unwrap_or_default();
933
934 self.static_file_provider
935 .latest_writer(StaticFileSegment::Receipts)?
936 .prune_receipts(to_delete, last_block)?;
937 }
938
939 Ok(())
940 }
941
942 fn write_bytecodes(
944 &self,
945 bytecodes: impl IntoIterator<Item = (B256, Bytecode)>,
946 ) -> ProviderResult<()> {
947 let mut bytecodes_cursor = self.tx_ref().cursor_write::<tables::Bytecodes>()?;
948 for (hash, bytecode) in bytecodes {
949 bytecodes_cursor.upsert(hash, &bytecode)?;
950 }
951 Ok(())
952 }
953}
954
955fn unwind_history_shards<S, T, C>(
970 cursor: &mut C,
971 start_key: T::Key,
972 block_number: BlockNumber,
973 mut shard_belongs_to_key: impl FnMut(&T::Key) -> bool,
974) -> ProviderResult<Vec<u64>>
975where
976 T: Table<Value = BlockNumberList>,
977 T::Key: AsRef<ShardedKey<S>>,
978 C: DbCursorRO<T> + DbCursorRW<T>,
979{
980 let mut item = cursor.seek_exact(start_key)?;
982 while let Some((sharded_key, list)) = item {
983 if !shard_belongs_to_key(&sharded_key) {
985 break
986 }
987
988 cursor.delete_current()?;
991
992 let first = list.iter().next().expect("List can't be empty");
995
996 if first >= block_number {
999 item = cursor.prev()?;
1000 continue
1001 }
1002 else if block_number <= sharded_key.as_ref().highest_block_number {
1005 return Ok(list.iter().take_while(|i| *i < block_number).collect::<Vec<_>>())
1008 }
1009 return Ok(list.iter().collect::<Vec<_>>())
1012 }
1013
1014 Ok(Vec::new())
1016}
1017
1018impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
1019 #[expect(clippy::too_many_arguments)]
1021 pub fn new(
1022 tx: TX,
1023 chain_spec: Arc<N::ChainSpec>,
1024 static_file_provider: StaticFileProvider<N::Primitives>,
1025 prune_modes: PruneModes,
1026 storage: Arc<N::Storage>,
1027 storage_settings: Arc<RwLock<StorageSettings>>,
1028 rocksdb_provider: RocksDBProvider,
1029 overlay_manager: OverlayManager<N::Primitives>,
1030 runtime: reth_tasks::Runtime,
1031 db_path: PathBuf,
1032 metrics: Arc<DatabaseProviderMetrics>,
1033 ) -> Self {
1034 Self {
1035 tx,
1036 chain_spec,
1037 static_file_provider,
1038 prune_modes,
1039 storage,
1040 storage_settings,
1041 rocksdb_provider,
1042 overlay_manager,
1043 runtime,
1044 db_path,
1045 rocksdb_history_snapshot: OnceLock::new(),
1046 pending_rocksdb_batches: Default::default(),
1047 commit_order: CommitOrder::Normal,
1048 minimum_pruning_distance: MINIMUM_UNWIND_SAFE_DISTANCE,
1049 metrics,
1050 reader_txn_tracker: None,
1051 }
1052 }
1053
1054 pub fn into_tx(self) -> TX {
1056 self.tx
1057 }
1058
1059 pub const fn tx_mut(&mut self) -> &mut TX {
1061 &mut self.tx
1062 }
1063
1064 pub const fn tx_ref(&self) -> &TX {
1066 &self.tx
1067 }
1068
1069 pub fn chain_spec(&self) -> &N::ChainSpec {
1071 &self.chain_spec
1072 }
1073}
1074
1075impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
1076 fn recovered_block<H, HF, B, BF>(
1077 &self,
1078 id: BlockHashOrNumber,
1079 _transaction_kind: TransactionVariant,
1080 header_by_number: HF,
1081 construct_block: BF,
1082 ) -> ProviderResult<Option<B>>
1083 where
1084 H: AsRef<HeaderTy<N>>,
1085 HF: FnOnce(BlockNumber) -> ProviderResult<Option<H>>,
1086 BF: FnOnce(H, BodyTy<N>, Vec<Address>) -> ProviderResult<Option<B>>,
1087 {
1088 let Some(block_number) = self.convert_hash_or_number(id)? else { return Ok(None) };
1089 let earliest_available = self.static_file_provider.earliest_history_height();
1090 if block_number < earliest_available {
1091 return Err(ProviderError::BlockExpired { requested: block_number, earliest_available })
1092 }
1093 let Some(header) = header_by_number(block_number)? else { return Ok(None) };
1094
1095 let Some(body) = self.block_body_indices(block_number)? else { return Ok(None) };
1102
1103 let tx_range = body.tx_num_range();
1104
1105 let transactions = if tx_range.is_empty() {
1106 vec![]
1107 } else {
1108 self.transactions_by_tx_range(tx_range.clone())?
1109 };
1110
1111 let body = self
1112 .storage
1113 .reader()
1114 .read_block_bodies(self, vec![(header.as_ref(), transactions)])?
1115 .pop()
1116 .ok_or(ProviderError::InvalidStorageOutput)?;
1117
1118 let senders = if tx_range.is_empty() {
1119 vec![]
1120 } else {
1121 let known_senders: HashMap<TxNumber, Address> =
1122 EitherReader::new_senders(self)?.senders_by_tx_range(tx_range.clone())?;
1123
1124 let mut senders = Vec::with_capacity(body.transactions().len());
1125 for (tx_num, tx) in tx_range.zip(body.transactions()) {
1126 match known_senders.get(&tx_num) {
1127 None => {
1128 let sender = tx.recover_signer_unchecked()?;
1129 senders.push(sender);
1130 }
1131 Some(sender) => senders.push(*sender),
1132 }
1133 }
1134 senders
1135 };
1136
1137 construct_block(header, body, senders)
1138 }
1139
1140 fn block_range<F, H, HF, R>(
1150 &self,
1151 range: RangeInclusive<BlockNumber>,
1152 headers_range: HF,
1153 mut assemble_block: F,
1154 ) -> ProviderResult<Vec<R>>
1155 where
1156 H: AsRef<HeaderTy<N>>,
1157 HF: FnOnce(RangeInclusive<BlockNumber>) -> ProviderResult<Vec<H>>,
1158 F: FnMut(H, BodyTy<N>, Range<TxNumber>) -> ProviderResult<R>,
1159 {
1160 if range.is_empty() {
1161 return Ok(Vec::new())
1162 }
1163
1164 let earliest_available = self.static_file_provider.earliest_history_height();
1167 if *range.start() < earliest_available {
1168 return Err(ProviderError::BlockExpired {
1169 requested: *range.start(),
1170 earliest_available,
1171 })
1172 }
1173
1174 let len = range.end().saturating_sub(*range.start()) as usize + 1;
1175 let mut blocks = Vec::with_capacity(len);
1176
1177 let headers = headers_range(range.clone())?;
1178
1179 let present_headers = self
1185 .block_body_indices_range(range)?
1186 .into_iter()
1187 .map(|b| b.tx_num_range())
1188 .zip(headers)
1189 .collect::<Vec<_>>();
1190
1191 let mut inputs = Vec::with_capacity(present_headers.len());
1192 for (tx_range, header) in &present_headers {
1193 let transactions = if tx_range.is_empty() {
1194 Vec::new()
1195 } else {
1196 self.transactions_by_tx_range(tx_range.clone())?
1197 };
1198
1199 inputs.push((header.as_ref(), transactions));
1200 }
1201
1202 let bodies = self.storage.reader().read_block_bodies(self, inputs)?;
1203
1204 for ((tx_range, header), body) in present_headers.into_iter().zip(bodies) {
1205 blocks.push(assemble_block(header, body, tx_range)?);
1206 }
1207
1208 Ok(blocks)
1209 }
1210
1211 fn block_with_senders_range<H, HF, B, BF>(
1222 &self,
1223 range: RangeInclusive<BlockNumber>,
1224 headers_range: HF,
1225 assemble_block: BF,
1226 ) -> ProviderResult<Vec<B>>
1227 where
1228 H: AsRef<HeaderTy<N>>,
1229 HF: Fn(RangeInclusive<BlockNumber>) -> ProviderResult<Vec<H>>,
1230 BF: Fn(H, BodyTy<N>, Vec<Address>) -> ProviderResult<B>,
1231 {
1232 self.block_range(range, headers_range, |header, body, tx_range| {
1233 let senders = if tx_range.is_empty() {
1234 Vec::new()
1235 } else {
1236 let known_senders: HashMap<TxNumber, Address> =
1237 EitherReader::new_senders(self)?.senders_by_tx_range(tx_range.clone())?;
1238
1239 let mut senders = Vec::with_capacity(body.transactions().len());
1240 for (tx_num, tx) in tx_range.zip(body.transactions()) {
1241 match known_senders.get(&tx_num) {
1242 None => {
1243 let sender = tx.recover_signer_unchecked()?;
1245 senders.push(sender);
1246 }
1247 Some(sender) => senders.push(*sender),
1248 }
1249 }
1250
1251 senders
1252 };
1253
1254 assemble_block(header, body, senders)
1255 })
1256 }
1257
1258 #[allow(clippy::clone_on_copy)]
1262 pub(crate) fn populate_bundle_state(
1263 &self,
1264 account_changeset: Vec<(u64, AccountBeforeTx)>,
1265 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1266 mut get_account: impl FnMut(Address) -> ProviderResult<Option<Account>>,
1267 mut get_storage: impl FnMut(Address, StorageKey) -> ProviderResult<Option<StorageValue>>,
1268 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1269 let mut state: BundleStateInit = HashMap::default();
1273
1274 let mut reverts: RevertsInit = HashMap::default();
1280
1281 for (block_number, account_before) in account_changeset.into_iter().rev() {
1283 let AccountBeforeTx { info: old_info, address } = account_before;
1284 match state.entry(address) {
1285 hash_map::Entry::Vacant(entry) => {
1286 let new_info = get_account(address)?;
1287 entry.insert((old_info.clone(), new_info, HashMap::default()));
1288 }
1289 hash_map::Entry::Occupied(mut entry) => {
1290 entry.get_mut().0 = old_info.clone();
1292 }
1293 }
1294 reverts.entry(block_number).or_default().entry(address).or_default().0 = Some(old_info);
1296 }
1297
1298 for (block_and_address, old_storage) in storage_changeset.into_iter().rev() {
1300 let BlockNumberAddress((block_number, address)) = block_and_address;
1301 let account_state = match state.entry(address) {
1303 hash_map::Entry::Vacant(entry) => {
1304 let present_info = get_account(address)?;
1305 entry.insert((present_info.clone(), present_info, HashMap::default()))
1306 }
1307 hash_map::Entry::Occupied(entry) => entry.into_mut(),
1308 };
1309
1310 match account_state.2.entry(old_storage.key) {
1312 hash_map::Entry::Vacant(entry) => {
1313 let new_storage = get_storage(address, old_storage.key)?.unwrap_or_default();
1314 entry.insert((old_storage.value, new_storage));
1315 }
1316 hash_map::Entry::Occupied(mut entry) => {
1317 entry.get_mut().0 = old_storage.value;
1318 }
1319 };
1320
1321 reverts
1322 .entry(block_number)
1323 .or_default()
1324 .entry(address)
1325 .or_default()
1326 .1
1327 .push(old_storage);
1328 }
1329
1330 Ok((state, reverts))
1331 }
1332
1333 fn populate_bundle_state_plain(
1336 &self,
1337 account_changeset: Vec<(u64, AccountBeforeTx)>,
1338 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1339 plain_accounts_cursor: &mut impl DbCursorRO<tables::PlainAccountState>,
1340 plain_storage_cursor: &mut impl DbDupCursorRO<tables::PlainStorageState>,
1341 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1342 self.populate_bundle_state(
1343 account_changeset,
1344 storage_changeset,
1345 |address| Ok(plain_accounts_cursor.seek_exact(address)?.map(|kv| kv.1)),
1346 |address, storage_key| {
1347 Ok(plain_storage_cursor
1348 .seek_by_key_subkey(address, storage_key)?
1349 .filter(|s| s.key == storage_key)
1350 .map(|s| s.value))
1351 },
1352 )
1353 }
1354
1355 fn populate_bundle_state_hashed(
1360 &self,
1361 account_changeset: Vec<(u64, AccountBeforeTx)>,
1362 storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
1363 hashed_accounts_cursor: &mut impl DbCursorRO<tables::HashedAccounts>,
1364 hashed_storage_cursor: &mut impl DbDupCursorRO<tables::HashedStorages>,
1365 ) -> ProviderResult<(BundleStateInit, RevertsInit)> {
1366 self.populate_bundle_state(
1367 account_changeset,
1368 storage_changeset,
1369 |address| Ok(hashed_accounts_cursor.seek_exact(keccak256(address))?.map(|kv| kv.1)),
1370 |address, storage_key| {
1371 let hashed_storage_key = keccak256(storage_key);
1372 Ok(hashed_storage_cursor
1373 .seek_by_key_subkey(keccak256(address), hashed_storage_key)?
1374 .filter(|s| s.key == hashed_storage_key)
1375 .map(|s| s.value))
1376 },
1377 )
1378 }
1379}
1380
1381impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
1382 fn append_history_index<P, T>(
1390 &self,
1391 index_updates: impl IntoIterator<Item = (P, impl IntoIterator<Item = u64>)>,
1392 mut sharded_key_factory: impl FnMut(P, BlockNumber) -> T::Key,
1393 ) -> ProviderResult<()>
1394 where
1395 P: Copy,
1396 T: Table<Value = BlockNumberList>,
1397 {
1398 assert!(!T::DUPSORT, "append_history_index cannot be used with DUPSORT tables");
1401
1402 let mut cursor = self.tx.cursor_write::<T>()?;
1403
1404 for (partial_key, indices) in index_updates {
1405 let last_key = sharded_key_factory(partial_key, u64::MAX);
1406 let mut last_shard = cursor
1407 .seek_exact(last_key.clone())?
1408 .map(|(_, list)| list)
1409 .unwrap_or_else(BlockNumberList::empty);
1410
1411 last_shard.append(indices).map_err(ProviderError::other)?;
1412
1413 if last_shard.len() <= sharded_key::NUM_OF_INDICES_IN_SHARD as u64 {
1415 cursor.upsert(last_key, &last_shard)?;
1416 continue;
1417 }
1418
1419 let chunks = last_shard.iter().chunks(sharded_key::NUM_OF_INDICES_IN_SHARD);
1421 let mut chunks_peekable = chunks.into_iter().peekable();
1422
1423 while let Some(chunk) = chunks_peekable.next() {
1424 let shard = BlockNumberList::new_pre_sorted(chunk);
1425 let highest_block_number = if chunks_peekable.peek().is_some() {
1426 shard.iter().next_back().expect("`chunks` does not return empty list")
1427 } else {
1428 u64::MAX
1430 };
1431
1432 cursor.upsert(sharded_key_factory(partial_key, highest_block_number), &shard)?;
1433 }
1434 }
1435
1436 Ok(())
1437 }
1438
1439 pub fn anchor_pruned_static_files(&self, pivot: BlockNumber) -> ProviderResult<()> {
1451 if !self.cached_storage_settings().storage_v2 {
1452 return Err(ProviderError::other(StaticFileWriterError::new(
1453 "pruned anchor requires storage v2",
1454 )))
1455 }
1456 let static_files = self.static_file_provider();
1457 for segment in StaticFileSegment::iter().filter(|segment| !segment.is_headers()) {
1458 static_files.delete_segment(segment)?;
1459 static_files.get_writer(pivot, segment)?.initialize_pruned_anchor(pivot)?;
1460 }
1461 static_files.set_earliest_history_height(pivot);
1464 Ok(())
1465 }
1466}
1467
1468impl<TX: DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
1469 fn ensure_finish_may_advance(&self, proposed: &StageCheckpoint) -> ProviderResult<()> {
1474 let current = self.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default();
1475 let state_trie_frontier = |checkpoint: &StageCheckpoint| {
1476 checkpoint
1477 .finish_stage_checkpoint()
1478 .and_then(|finish| finish.partial_state_trie())
1479 .unwrap_or(checkpoint.block_number)
1480 };
1481 if proposed.block_number <= current.block_number &&
1482 state_trie_frontier(proposed) <= state_trie_frontier(¤t)
1483 {
1484 return Ok(())
1485 }
1486 match self.snap_attempt()? {
1487 Some(attempt) if !attempt.is_verified() => {
1488 Err(ProviderError::UnverifiedSnapState { attempt: attempt.id().into() })
1489 }
1490 _ => Ok(()),
1491 }
1492 }
1493
1494 pub fn ensure_snap_sync_layout(&self) -> ProviderResult<()> {
1496 if self.cached_storage_settings().use_hashed_state() {
1497 Ok(())
1498 } else {
1499 Err(ProviderError::SnapStorageLayoutUnsupported)
1500 }
1501 }
1502
1503 pub fn snap_bootstraps(&self) -> ProviderResult<bool> {
1509 if let Some(attempt) = self.snap_attempt()? {
1510 return Ok(!attempt.is_verified())
1511 }
1512 let genesis = self.chain_spec().genesis_header().number();
1513 Ok(self
1514 .get_stage_checkpoint(StageId::Execution)?
1515 .is_none_or(|checkpoint| checkpoint.block_number == genesis))
1516 }
1517
1518 pub fn ensure_sync_mode(&self, snap_enabled: bool) -> ProviderResult<()> {
1523 if snap_enabled {
1524 return if self.snap_bootstraps()? { self.ensure_snap_sync_layout() } else { Ok(()) }
1525 }
1526 match self.snap_attempt()? {
1527 Some(attempt) if !attempt.is_verified() => {
1528 Err(ProviderError::SnapStateRequiresSnapSync { attempt: attempt.id().into() })
1529 }
1530 _ => Ok(()),
1531 }
1532 }
1533}
1534
1535impl<TX: DbTx, N: NodeTypes> AccountReader for DatabaseProvider<TX, N> {
1536 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
1537 if self.cached_storage_settings().use_hashed_state() {
1538 let hashed_address = keccak256(address);
1539 Ok(self.tx.get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)?)
1540 } else {
1541 Ok(self.tx.get_by_encoded_key::<tables::PlainAccountState>(address)?)
1542 }
1543 }
1544}
1545
1546impl<TX: DbTx + 'static, N: NodeTypes> AccountExtReader for DatabaseProvider<TX, N> {
1547 fn changed_accounts_with_range(
1548 &self,
1549 range: RangeInclusive<BlockNumber>,
1550 ) -> ProviderResult<BTreeSet<Address>> {
1551 let mut reader = EitherReader::new_account_changesets(self)?;
1552
1553 reader.changed_accounts_with_range(range)
1554 }
1555
1556 fn basic_accounts(
1557 &self,
1558 iter: impl IntoIterator<Item = Address>,
1559 ) -> ProviderResult<Vec<(Address, Option<Account>)>> {
1560 if self.cached_storage_settings().use_hashed_state() {
1561 let mut hashed_accounts = self.tx.cursor_read::<tables::HashedAccounts>()?;
1562 Ok(iter
1563 .into_iter()
1564 .map(|address| {
1565 let hashed_address = keccak256(address);
1566 hashed_accounts.seek_exact(hashed_address).map(|a| (address, a.map(|(_, v)| v)))
1567 })
1568 .collect::<Result<Vec<_>, _>>()?)
1569 } else {
1570 let mut plain_accounts = self.tx.cursor_read::<tables::PlainAccountState>()?;
1571 Ok(iter
1572 .into_iter()
1573 .map(|address| {
1574 plain_accounts.seek_exact(address).map(|a| (address, a.map(|(_, v)| v)))
1575 })
1576 .collect::<Result<Vec<_>, _>>()?)
1577 }
1578 }
1579
1580 fn changed_accounts_and_blocks_with_range(
1581 &self,
1582 range: RangeInclusive<BlockNumber>,
1583 ) -> ProviderResult<BTreeMap<Address, Vec<u64>>> {
1584 let highest_static_block = self
1585 .static_file_provider
1586 .get_highest_static_file_block(StaticFileSegment::AccountChangeSets);
1587
1588 if let Some(highest) = highest_static_block &&
1589 self.cached_storage_settings().storage_v2
1590 {
1591 let start = *range.start();
1592 let static_end = (*range.end()).min(highest);
1593
1594 let mut changed_accounts_and_blocks: BTreeMap<_, Vec<u64>> = BTreeMap::default();
1595 if start <= static_end {
1596 for block in start..=static_end {
1597 let block_changesets = self.account_block_changeset(block)?;
1598 for changeset in block_changesets {
1599 changed_accounts_and_blocks
1600 .entry(changeset.address)
1601 .or_default()
1602 .push(block);
1603 }
1604 }
1605 }
1606
1607 Ok(changed_accounts_and_blocks)
1608 } else {
1609 let mut changeset_cursor = self.tx.cursor_read::<tables::AccountChangeSets>()?;
1610
1611 let account_transitions = changeset_cursor.walk_range(range)?.try_fold(
1612 BTreeMap::new(),
1613 |mut accounts: BTreeMap<Address, Vec<u64>>, entry| -> ProviderResult<_> {
1614 let (index, account) = entry?;
1615 accounts.entry(account.address).or_default().push(index);
1616 Ok(accounts)
1617 },
1618 )?;
1619
1620 Ok(account_transitions)
1621 }
1622 }
1623}
1624
1625impl<TX: DbTx, N: NodeTypes> StorageChangeSetReader for DatabaseProvider<TX, N> {
1626 fn storage_changeset(
1627 &self,
1628 block_number: BlockNumber,
1629 ) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
1630 if self.cached_storage_settings().storage_v2 {
1631 self.static_file_provider.storage_changeset(block_number)
1632 } else {
1633 let range = block_number..=block_number;
1634 let storage_range = BlockNumberAddress::range(range);
1635 self.tx
1636 .cursor_dup_read::<tables::StorageChangeSets>()?
1637 .walk_range(storage_range)?
1638 .map(|r| {
1639 let (bna, entry) = r?;
1640 Ok((bna, entry))
1641 })
1642 .collect()
1643 }
1644 }
1645
1646 fn get_storage_before_block(
1647 &self,
1648 block_number: BlockNumber,
1649 address: Address,
1650 storage_key: B256,
1651 ) -> ProviderResult<Option<StorageEntry>> {
1652 if self.cached_storage_settings().storage_v2 {
1653 self.static_file_provider.get_storage_before_block(block_number, address, storage_key)
1654 } else {
1655 Ok(self
1656 .tx
1657 .cursor_dup_read::<tables::StorageChangeSets>()?
1658 .seek_by_key_subkey(BlockNumberAddress((block_number, address)), storage_key)?
1659 .filter(|entry| entry.key == storage_key))
1660 }
1661 }
1662
1663 fn storage_changesets_range(
1664 &self,
1665 range: impl RangeBounds<BlockNumber>,
1666 ) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
1667 if self.cached_storage_settings().storage_v2 {
1668 self.static_file_provider.storage_changesets_range(range)
1669 } else {
1670 self.tx
1671 .cursor_dup_read::<tables::StorageChangeSets>()?
1672 .walk_range(BlockNumberAddressRange::from(range))?
1673 .map(|r| {
1674 let (bna, entry) = r?;
1675 Ok((bna, entry))
1676 })
1677 .collect()
1678 }
1679 }
1680}
1681
1682impl<TX: DbTx, N: NodeTypes> ChangeSetReader for DatabaseProvider<TX, N> {
1683 fn account_block_changeset(
1684 &self,
1685 block_number: BlockNumber,
1686 ) -> ProviderResult<Vec<AccountBeforeTx>> {
1687 if self.cached_storage_settings().storage_v2 {
1688 let static_changesets =
1689 self.static_file_provider.account_block_changeset(block_number)?;
1690 Ok(static_changesets)
1691 } else {
1692 let range = block_number..=block_number;
1693 self.tx
1694 .cursor_read::<tables::AccountChangeSets>()?
1695 .walk_range(range)?
1696 .map(|result| -> ProviderResult<_> {
1697 let (_, account_before) = result?;
1698 Ok(account_before)
1699 })
1700 .collect()
1701 }
1702 }
1703
1704 fn get_account_before_block(
1705 &self,
1706 block_number: BlockNumber,
1707 address: Address,
1708 ) -> ProviderResult<Option<AccountBeforeTx>> {
1709 if self.cached_storage_settings().storage_v2 {
1710 Ok(self.static_file_provider.get_account_before_block(block_number, address)?)
1711 } else {
1712 self.tx
1713 .cursor_dup_read::<tables::AccountChangeSets>()?
1714 .seek_by_key_subkey(block_number, address)?
1715 .filter(|acc| acc.address == address)
1716 .map(Ok)
1717 .transpose()
1718 }
1719 }
1720
1721 fn account_changesets_range(
1722 &self,
1723 range: impl core::ops::RangeBounds<BlockNumber>,
1724 ) -> ProviderResult<Vec<(BlockNumber, AccountBeforeTx)>> {
1725 if self.cached_storage_settings().storage_v2 {
1726 self.static_file_provider.account_changesets_range(range)
1727 } else {
1728 self.tx
1729 .cursor_read::<tables::AccountChangeSets>()?
1730 .walk_range(to_range(range))?
1731 .map(|r| r.map_err(Into::into))
1732 .collect()
1733 }
1734 }
1735}
1736
1737impl<TX: DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
1738 fn history_rocksdb_snapshot(&self) -> Option<&RocksReadSnapshot<'_>> {
1751 self.rocksdb_history_snapshot
1752 .get_or_init(|| {
1753 self.cached_storage_settings()
1754 .storage_v2
1755 .then(|| self.rocksdb_provider.owned_snapshot())
1756 })
1757 .as_ref()
1758 .map(OwnedRocksReadSnapshot::as_snapshot)
1759 }
1760}
1761
1762impl<TX: DbTx + 'static, N: NodeTypes> HistoryReader for DatabaseProvider<TX, N> {
1763 fn account_history_info(
1764 &self,
1765 address: Address,
1766 block_number: BlockNumber,
1767 lowest_available_block_number: Option<BlockNumber>,
1768 ) -> ProviderResult<HistoryInfo> {
1769 let visible_tip = self.best_block_number()?;
1770 let mut reader = EitherReader::new_accounts_history(self, self.history_rocksdb_snapshot())?;
1771 reader
1772 .account_history_info(address, block_number, lowest_available_block_number, visible_tip)
1773 .map(Into::into)
1774 }
1775
1776 fn storage_history_info(
1777 &self,
1778 address: Address,
1779 storage_key: B256,
1780 block_number: BlockNumber,
1781 lowest_available_block_number: Option<BlockNumber>,
1782 ) -> ProviderResult<HistoryInfo> {
1783 let visible_tip = self.best_block_number()?;
1784 let mut reader = EitherReader::new_storages_history(self, self.history_rocksdb_snapshot())?;
1785 reader
1786 .storage_history_info(
1787 address,
1788 storage_key,
1789 block_number,
1790 lowest_available_block_number,
1791 visible_tip,
1792 )
1793 .map(Into::into)
1794 }
1795}
1796
1797impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderSyncGapProvider
1798 for DatabaseProvider<TX, N>
1799{
1800 type Header = HeaderTy<N>;
1801
1802 fn local_tip_header(
1803 &self,
1804 highest_uninterrupted_block: BlockNumber,
1805 ) -> ProviderResult<SealedHeader<Self::Header>> {
1806 let static_file_provider = self.static_file_provider();
1807
1808 let next_static_file_block_num = static_file_provider
1811 .get_highest_static_file_block(StaticFileSegment::Headers)
1812 .map(|id| id + 1)
1813 .unwrap_or_default();
1814 let next_block = highest_uninterrupted_block + 1;
1815
1816 match next_static_file_block_num.cmp(&next_block) {
1817 Ordering::Greater => {
1820 let mut static_file_producer =
1821 static_file_provider.latest_writer(StaticFileSegment::Headers)?;
1822 static_file_producer.prune_headers(next_static_file_block_num - next_block)?;
1823 static_file_producer.commit()?
1826 }
1827 Ordering::Less => {
1828 return Err(ProviderError::HeaderNotFound(next_static_file_block_num.into()))
1830 }
1831 Ordering::Equal => {}
1832 }
1833
1834 let local_head = static_file_provider
1835 .sealed_header(highest_uninterrupted_block)?
1836 .ok_or_else(|| ProviderError::HeaderNotFound(highest_uninterrupted_block.into()))?;
1837
1838 Ok(local_head)
1839 }
1840}
1841
1842impl<TX: DbTx + 'static, N: NodeTypesForProvider> HeaderProvider for DatabaseProvider<TX, N> {
1843 type Header = HeaderTy<N>;
1844
1845 fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
1846 if let Some(num) = self.block_number(block_hash)? {
1847 Ok(self.header_by_number(num)?)
1848 } else {
1849 Ok(None)
1850 }
1851 }
1852
1853 fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
1854 self.static_file_provider.header_by_number(num)
1855 }
1856
1857 fn headers_range(
1858 &self,
1859 range: impl RangeBounds<BlockNumber>,
1860 ) -> ProviderResult<Vec<Self::Header>> {
1861 self.static_file_provider.headers_range(range)
1862 }
1863
1864 fn sealed_header(
1865 &self,
1866 number: BlockNumber,
1867 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
1868 self.static_file_provider.sealed_header(number)
1869 }
1870
1871 fn sealed_headers_while(
1872 &self,
1873 range: impl RangeBounds<BlockNumber>,
1874 predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
1875 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
1876 self.static_file_provider.sealed_headers_while(range, predicate)
1877 }
1878}
1879
1880impl<TX: DbTx + 'static, N: NodeTypes> BlockHashReader for DatabaseProvider<TX, N> {
1881 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
1882 self.static_file_provider.block_hash(number)
1883 }
1884
1885 fn canonical_hashes_range(
1886 &self,
1887 start: BlockNumber,
1888 end: BlockNumber,
1889 ) -> ProviderResult<Vec<B256>> {
1890 self.static_file_provider.canonical_hashes_range(start, end)
1891 }
1892}
1893
1894impl<TX: DbTx + 'static, N: NodeTypes> BlockNumReader for DatabaseProvider<TX, N> {
1895 fn chain_info(&self) -> ProviderResult<ChainInfo> {
1896 let best_number = self.best_block_number()?;
1897 let best_hash = self.block_hash(best_number)?.unwrap_or_default();
1898 Ok(ChainInfo { best_hash, best_number })
1899 }
1900
1901 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
1902 Ok(self
1905 .get_stage_checkpoint(StageId::Finish)?
1906 .map(|checkpoint| checkpoint.block_number)
1907 .unwrap_or_default())
1908 }
1909
1910 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
1911 self.static_file_provider.last_block_number()
1912 }
1913
1914 fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
1915 Ok(self.tx.get::<tables::HeaderNumbers>(hash)?)
1916 }
1917}
1918
1919impl<TX: DbTx + 'static, N: NodeTypesForProvider> BlockReader for DatabaseProvider<TX, N> {
1920 type Block = BlockTy<N>;
1921
1922 fn find_block_by_hash(
1923 &self,
1924 hash: B256,
1925 source: BlockSource,
1926 ) -> ProviderResult<Option<Self::Block>> {
1927 if source.is_canonical() {
1928 self.block(hash.into())
1929 } else {
1930 Ok(None)
1931 }
1932 }
1933
1934 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
1942 if let Some(number) = self.convert_hash_or_number(id)? {
1943 let earliest_available = self.static_file_provider.earliest_history_height();
1944 if number < earliest_available {
1945 return Err(ProviderError::BlockExpired { requested: number, earliest_available })
1946 }
1947
1948 let Some(header) = self.header_by_number(number)? else { return Ok(None) };
1949
1950 let Some(transactions) = self.transactions_by_block(number.into())? else {
1955 return Ok(None)
1956 };
1957
1958 let body = self
1959 .storage
1960 .reader()
1961 .read_block_bodies(self, vec![(&header, transactions)])?
1962 .pop()
1963 .ok_or(ProviderError::InvalidStorageOutput)?;
1964
1965 return Ok(Some(Self::Block::new(header, body)))
1966 }
1967
1968 Ok(None)
1969 }
1970
1971 fn pending_block(&self) -> ProviderResult<Option<Arc<RecoveredBlock<Self::Block>>>> {
1972 Ok(None)
1973 }
1974
1975 fn pending_block_and_receipts(
1976 &self,
1977 ) -> ProviderResult<Option<RecoveredBlockAndExecutionOutput<Self::Block, Self::Receipt>>> {
1978 Ok(None)
1979 }
1980
1981 fn recovered_block(
1990 &self,
1991 id: BlockHashOrNumber,
1992 transaction_kind: TransactionVariant,
1993 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
1994 self.recovered_block(
1995 id,
1996 transaction_kind,
1997 |block_number| self.header_by_number(block_number),
1998 |header, body, senders| {
1999 Self::Block::new(header, body)
2000 .try_into_recovered_unchecked(senders)
2004 .map(Some)
2005 .map_err(|_| ProviderError::SenderRecoveryError)
2006 },
2007 )
2008 }
2009
2010 fn sealed_block_with_senders(
2011 &self,
2012 id: BlockHashOrNumber,
2013 transaction_kind: TransactionVariant,
2014 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
2015 self.recovered_block(
2016 id,
2017 transaction_kind,
2018 |block_number| self.sealed_header(block_number),
2019 |header, body, senders| {
2020 Self::Block::new_sealed(header, body)
2021 .try_with_senders_unchecked(senders)
2025 .map(Some)
2026 .map_err(|_| ProviderError::SenderRecoveryError)
2027 },
2028 )
2029 }
2030
2031 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
2032 self.block_range(
2033 range,
2034 |range| self.headers_range(range),
2035 |header, body, _| Ok(Self::Block::new(header, body)),
2036 )
2037 }
2038
2039 fn block_with_senders_range(
2040 &self,
2041 range: RangeInclusive<BlockNumber>,
2042 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
2043 self.block_with_senders_range(
2044 range,
2045 |range| self.headers_range(range),
2046 |header, body, senders| {
2047 Self::Block::new(header, body)
2048 .try_into_recovered_unchecked(senders)
2049 .map_err(|_| ProviderError::SenderRecoveryError)
2050 },
2051 )
2052 }
2053
2054 fn recovered_block_range(
2055 &self,
2056 range: RangeInclusive<BlockNumber>,
2057 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
2058 self.block_with_senders_range(
2059 range,
2060 |range| self.sealed_headers_range(range),
2061 |header, body, senders| {
2062 Self::Block::new_sealed(header, body)
2063 .try_with_senders(senders)
2064 .map_err(|_| ProviderError::SenderRecoveryError)
2065 },
2066 )
2067 }
2068
2069 fn block_by_transaction_id(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
2070 Ok(self
2071 .tx
2072 .cursor_read::<tables::TransactionBlocks>()?
2073 .seek(id)
2074 .map(|b| b.map(|(_, bn)| bn))?)
2075 }
2076}
2077
2078impl<TX: DbTx + 'static, N: NodeTypesForProvider> TransactionsProviderExt
2079 for DatabaseProvider<TX, N>
2080{
2081 fn transaction_hashes_by_range(
2084 &self,
2085 tx_range: Range<TxNumber>,
2086 ) -> ProviderResult<Vec<(TxHash, TxNumber)>> {
2087 self.static_file_provider.transaction_hashes_by_range(tx_range)
2088 }
2089}
2090
2091impl<TX: DbTx + 'static, N: NodeTypesForProvider> TransactionsProvider for DatabaseProvider<TX, N> {
2093 type Transaction = TxTy<N>;
2094
2095 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
2096 self.with_rocksdb_snapshot(|rocksdb_ref| {
2097 let mut reader = EitherReader::new_transaction_hash_numbers(self, rocksdb_ref)?;
2098 reader.get_transaction_hash_number(tx_hash)
2099 })
2100 }
2101
2102 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
2103 self.static_file_provider.transaction_by_id(id)
2104 }
2105
2106 fn transaction_by_id_unhashed(
2107 &self,
2108 id: TxNumber,
2109 ) -> ProviderResult<Option<Self::Transaction>> {
2110 self.static_file_provider.transaction_by_id_unhashed(id)
2111 }
2112
2113 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
2114 if let Some(id) = self.transaction_id(hash)? {
2115 Ok(self.transaction_by_id_unhashed(id)?)
2116 } else {
2117 Ok(None)
2118 }
2119 }
2120
2121 fn transaction_by_hash_with_meta(
2122 &self,
2123 tx_hash: TxHash,
2124 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
2125 if let Some(transaction_id) = self.transaction_id(tx_hash)? &&
2126 let Some(transaction) = self.transaction_by_id_unhashed(transaction_id)? &&
2127 let Some(block_number) = self.block_by_transaction_id(transaction_id)? &&
2128 let Some(sealed_header) = self.sealed_header(block_number)?
2129 {
2130 let (header, block_hash) = sealed_header.split();
2131 if let Some(block_body) = self.block_body_indices(block_number)? {
2132 let index = transaction_id - block_body.first_tx_num();
2137
2138 let meta = TransactionMeta {
2139 tx_hash,
2140 index,
2141 block_hash,
2142 block_number,
2143 base_fee: header.base_fee_per_gas(),
2144 excess_blob_gas: header.excess_blob_gas(),
2145 timestamp: header.timestamp(),
2146 };
2147
2148 return Ok(Some((transaction, meta)))
2149 }
2150 }
2151
2152 Ok(None)
2153 }
2154
2155 fn transactions_by_block(
2156 &self,
2157 id: BlockHashOrNumber,
2158 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
2159 if let Some(block_number) = self.convert_hash_or_number(id)? &&
2160 let Some(body) = self.block_body_indices(block_number)?
2161 {
2162 let tx_range = body.tx_num_range();
2163 return if tx_range.is_empty() {
2164 Ok(Some(Vec::new()))
2165 } else {
2166 self.transactions_by_tx_range(tx_range).map(Some)
2167 }
2168 }
2169 Ok(None)
2170 }
2171
2172 fn transactions_by_block_range(
2173 &self,
2174 range: impl RangeBounds<BlockNumber>,
2175 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
2176 let range = to_range(range);
2177
2178 self.block_body_indices_range(range.start..=range.end.saturating_sub(1))?
2179 .into_iter()
2180 .map(|body| {
2181 let tx_num_range = body.tx_num_range();
2182 if tx_num_range.is_empty() {
2183 Ok(Vec::new())
2184 } else {
2185 self.transactions_by_tx_range(tx_num_range)
2186 }
2187 })
2188 .collect()
2189 }
2190
2191 fn transactions_by_tx_range(
2192 &self,
2193 range: impl RangeBounds<TxNumber>,
2194 ) -> ProviderResult<Vec<Self::Transaction>> {
2195 self.static_file_provider.transactions_by_tx_range(range)
2196 }
2197
2198 fn senders_by_tx_range(
2199 &self,
2200 range: impl RangeBounds<TxNumber>,
2201 ) -> ProviderResult<Vec<Address>> {
2202 if EitherWriterDestination::senders(self).is_static_file() {
2203 self.static_file_provider.senders_by_tx_range(range)
2204 } else {
2205 self.cursor_read_collect::<tables::TransactionSenders>(range)
2206 }
2207 }
2208
2209 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
2210 if EitherWriterDestination::senders(self).is_static_file() {
2211 self.static_file_provider.transaction_sender(id)
2212 } else {
2213 Ok(self.tx.get::<tables::TransactionSenders>(id)?)
2214 }
2215 }
2216}
2217
2218impl<TX: DbTx + 'static, N: NodeTypesForProvider> ReceiptProvider for DatabaseProvider<TX, N> {
2219 type Receipt = ReceiptTy<N>;
2220
2221 fn receipt(&self, id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
2222 self.static_file_provider.get_with_static_file_or_database(
2223 StaticFileSegment::Receipts,
2224 id,
2225 |static_file| static_file.receipt(id),
2226 || Ok(self.tx.get::<tables::Receipts<Self::Receipt>>(id)?),
2227 )
2228 }
2229
2230 fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
2231 if let Some(id) = self.transaction_id(hash)? {
2232 self.receipt(id)
2233 } else {
2234 Ok(None)
2235 }
2236 }
2237
2238 fn receipts_by_block(
2239 &self,
2240 block: BlockHashOrNumber,
2241 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
2242 if let Some(number) = self.convert_hash_or_number(block)? &&
2243 let Some(body) = self.block_body_indices(number)?
2244 {
2245 let tx_range = body.tx_num_range();
2246 return if tx_range.is_empty() {
2247 Ok(Some(Vec::new()))
2248 } else {
2249 let receipts = self.receipts_by_tx_range(tx_range)?;
2250
2251 if receipts.len() != body.tx_count as usize {
2252 return Ok(None)
2253 }
2254
2255 Ok(Some(receipts))
2256 }
2257 }
2258 Ok(None)
2259 }
2260
2261 fn receipts_by_tx_range(
2262 &self,
2263 range: impl RangeBounds<TxNumber>,
2264 ) -> ProviderResult<Vec<Self::Receipt>> {
2265 self.static_file_provider.get_range_with_static_file_or_database(
2266 StaticFileSegment::Receipts,
2267 to_range(range),
2268 |static_file, range, _| static_file.receipts_by_tx_range(range),
2269 |range, _| self.cursor_read_collect::<tables::Receipts<Self::Receipt>>(range),
2270 |_| true,
2271 )
2272 }
2273
2274 fn receipts_by_block_range(
2275 &self,
2276 block_range: RangeInclusive<BlockNumber>,
2277 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
2278 if block_range.is_empty() {
2279 return Ok(Vec::new());
2280 }
2281
2282 let range_len = block_range.end().saturating_sub(*block_range.start()) as usize + 1;
2284 let mut block_body_indices = Vec::with_capacity(range_len);
2285 for block_num in block_range {
2286 if let Some(indices) = self.block_body_indices(block_num)? {
2287 block_body_indices.push(indices);
2288 } else {
2289 block_body_indices.push(StoredBlockBodyIndices::default());
2291 }
2292 }
2293
2294 if block_body_indices.is_empty() {
2295 return Ok(Vec::new());
2296 }
2297
2298 let non_empty_blocks: Vec<_> =
2300 block_body_indices.iter().filter(|indices| indices.tx_count > 0).collect();
2301
2302 if non_empty_blocks.is_empty() {
2303 return Ok(vec![Vec::new(); block_body_indices.len()]);
2305 }
2306
2307 let first_tx = non_empty_blocks[0].first_tx_num();
2309 let last_tx = non_empty_blocks[non_empty_blocks.len() - 1].last_tx_num();
2310
2311 let all_receipts = self.receipts_by_tx_range(first_tx..=last_tx)?;
2313 let mut receipts_iter = all_receipts.into_iter();
2314
2315 let mut result = Vec::with_capacity(block_body_indices.len());
2317 for indices in &block_body_indices {
2318 if indices.tx_count == 0 {
2319 result.push(Vec::new());
2320 } else {
2321 let block_receipts =
2322 receipts_iter.by_ref().take(indices.tx_count as usize).collect();
2323 result.push(block_receipts);
2324 }
2325 }
2326
2327 Ok(result)
2328 }
2329}
2330
2331impl<TX: DbTx + 'static, N: NodeTypesForProvider> BlockBodyIndicesProvider
2332 for DatabaseProvider<TX, N>
2333{
2334 fn block_body_indices(&self, num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
2335 Ok(self.tx.get::<tables::BlockBodyIndices>(num)?)
2336 }
2337
2338 fn block_body_indices_range(
2339 &self,
2340 range: RangeInclusive<BlockNumber>,
2341 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
2342 self.cursor_read_collect::<tables::BlockBodyIndices>(range)
2343 }
2344}
2345
2346impl<TX: DbTx, N: NodeTypes> StageCheckpointReader for DatabaseProvider<TX, N> {
2347 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
2348 Ok(if let Some(encoded) = id.get_pre_encoded() {
2349 self.tx.get_by_encoded_key::<tables::StageCheckpoints>(encoded)?
2350 } else {
2351 self.tx.get::<tables::StageCheckpoints>(id.to_string())?
2352 })
2353 }
2354
2355 fn get_stage_checkpoint_progress(&self, id: StageId) -> ProviderResult<Option<Vec<u8>>> {
2357 Ok(self.tx.get::<tables::StageCheckpointProgresses>(id.to_string())?)
2358 }
2359
2360 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
2361 self.tx
2362 .cursor_read::<tables::StageCheckpoints>()?
2363 .walk(None)?
2364 .collect::<Result<Vec<(String, StageCheckpoint)>, _>>()
2365 .map_err(ProviderError::Database)
2366 }
2367}
2368
2369impl<TX: DbTxMut + DbTx, N: NodeTypes> StageCheckpointWriter for DatabaseProvider<TX, N> {
2370 fn save_stage_checkpoint(
2372 &self,
2373 id: StageId,
2374 checkpoint: StageCheckpoint,
2375 ) -> ProviderResult<()> {
2376 if id.is_finish() {
2377 self.ensure_finish_may_advance(&checkpoint)?;
2378 }
2379 Ok(self.tx.put::<tables::StageCheckpoints>(id.to_string(), checkpoint)?)
2380 }
2381
2382 fn save_stage_checkpoint_progress(
2384 &self,
2385 id: StageId,
2386 checkpoint: Vec<u8>,
2387 ) -> ProviderResult<()> {
2388 Ok(self.tx.put::<tables::StageCheckpointProgresses>(id.to_string(), checkpoint)?)
2389 }
2390
2391 #[instrument(level = "debug", target = "providers::db", skip_all)]
2392 fn update_pipeline_stages(
2393 &self,
2394 block_number: BlockNumber,
2395 drop_stage_checkpoint: bool,
2396 ) -> ProviderResult<()> {
2397 let current = self.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default();
2398 let proposed = StageCheckpoint {
2399 block_number,
2400 ..if drop_stage_checkpoint { Default::default() } else { current }
2401 };
2402 self.ensure_finish_may_advance(&proposed)?;
2403
2404 let mut cursor = self.tx.cursor_write::<tables::StageCheckpoints>()?;
2406 for stage_id in StageId::ALL {
2407 let (_, checkpoint) = cursor.seek_exact(stage_id.to_string())?.unwrap_or_default();
2408 cursor.upsert(
2409 stage_id.to_string(),
2410 &StageCheckpoint {
2411 block_number,
2412 ..if drop_stage_checkpoint { Default::default() } else { checkpoint }
2413 },
2414 )?;
2415 }
2416
2417 Ok(())
2418 }
2419}
2420
2421impl<TX: DbTxMut + DbTx, N: NodeTypes> DatabaseProvider<TX, N> {
2422 fn update_pipeline_stages_after_unwind(
2425 &self,
2426 block_number: BlockNumber,
2427 ) -> ProviderResult<PersistenceFrontiers> {
2428 let partial_state_trie = self
2429 .get_stage_checkpoint(StageId::Finish)?
2430 .map(|checkpoint| {
2431 checkpoint
2432 .finish_stage_checkpoint()
2433 .and_then(|finish| finish.partial_state_trie())
2434 .unwrap_or(checkpoint.block_number)
2435 })
2436 .unwrap_or(block_number)
2437 .min(block_number);
2438
2439 self.update_pipeline_stages(block_number, true)?;
2440 if partial_state_trie < block_number {
2441 self.save_stage_checkpoint(
2442 StageId::Finish,
2443 StageCheckpoint::new(block_number).with_finish_stage_checkpoint(FinishCheckpoint {
2444 partial_state_trie: Some(partial_state_trie),
2445 }),
2446 )?;
2447 }
2448
2449 Ok(PersistenceFrontiers { db_tip: block_number, partial_state_trie })
2450 }
2451}
2452
2453impl<TX: DbTx + 'static, N: NodeTypes> StorageReader for DatabaseProvider<TX, N> {
2454 fn plain_state_storages(
2455 &self,
2456 addresses_with_keys: impl IntoIterator<Item = (Address, impl IntoIterator<Item = B256>)>,
2457 ) -> ProviderResult<Vec<(Address, Vec<StorageEntry>)>> {
2458 if self.cached_storage_settings().use_hashed_state() {
2459 let mut hashed_storage = self.tx.cursor_dup_read::<tables::HashedStorages>()?;
2460
2461 addresses_with_keys
2462 .into_iter()
2463 .map(|(address, storage)| {
2464 let hashed_address = keccak256(address);
2465 storage
2466 .into_iter()
2467 .map(|key| -> ProviderResult<_> {
2468 let hashed_key = keccak256(key);
2469 let value = hashed_storage
2470 .seek_by_key_subkey(hashed_address, hashed_key)?
2471 .filter(|v| v.key == hashed_key)
2472 .map(|v| v.value)
2473 .unwrap_or_default();
2474 Ok(StorageEntry { key, value })
2475 })
2476 .collect::<ProviderResult<Vec<_>>>()
2477 .map(|storage| (address, storage))
2478 })
2479 .collect::<ProviderResult<Vec<(_, _)>>>()
2480 } else {
2481 let mut plain_storage = self.tx.cursor_dup_read::<tables::PlainStorageState>()?;
2482
2483 addresses_with_keys
2484 .into_iter()
2485 .map(|(address, storage)| {
2486 storage
2487 .into_iter()
2488 .map(|key| -> ProviderResult<_> {
2489 Ok(plain_storage
2490 .seek_by_key_subkey(address, key)?
2491 .filter(|v| v.key == key)
2492 .unwrap_or_else(|| StorageEntry { key, value: Default::default() }))
2493 })
2494 .collect::<ProviderResult<Vec<_>>>()
2495 .map(|storage| (address, storage))
2496 })
2497 .collect::<ProviderResult<Vec<(_, _)>>>()
2498 }
2499 }
2500
2501 fn changed_storages_with_range(
2502 &self,
2503 range: RangeInclusive<BlockNumber>,
2504 ) -> ProviderResult<BTreeMap<Address, BTreeSet<B256>>> {
2505 if self.cached_storage_settings().storage_v2 {
2506 self.storage_changesets_range(range)?.into_iter().try_fold(
2507 BTreeMap::new(),
2508 |mut accounts: BTreeMap<Address, BTreeSet<B256>>, entry| {
2509 let (BlockNumberAddress((_, address)), storage_entry) = entry;
2510 accounts.entry(address).or_default().insert(storage_entry.key);
2511 Ok(accounts)
2512 },
2513 )
2514 } else {
2515 self.tx
2516 .cursor_read::<tables::StorageChangeSets>()?
2517 .walk_range(BlockNumberAddress::range(range))?
2518 .try_fold(
2521 BTreeMap::new(),
2522 |mut accounts: BTreeMap<Address, BTreeSet<B256>>, entry| {
2523 let (BlockNumberAddress((_, address)), storage_entry) = entry?;
2524 accounts.entry(address).or_default().insert(storage_entry.key);
2525 Ok(accounts)
2526 },
2527 )
2528 }
2529 }
2530
2531 fn changed_storages_and_blocks_with_range(
2532 &self,
2533 range: RangeInclusive<BlockNumber>,
2534 ) -> ProviderResult<BTreeMap<(Address, B256), Vec<u64>>> {
2535 if self.cached_storage_settings().storage_v2 {
2536 self.storage_changesets_range(range)?.into_iter().try_fold(
2537 BTreeMap::new(),
2538 |mut storages: BTreeMap<(Address, B256), Vec<u64>>, (index, storage)| {
2539 storages
2540 .entry((index.address(), storage.key))
2541 .or_default()
2542 .push(index.block_number());
2543 Ok(storages)
2544 },
2545 )
2546 } else {
2547 let mut changeset_cursor = self.tx.cursor_read::<tables::StorageChangeSets>()?;
2548
2549 let storage_changeset_lists =
2550 changeset_cursor.walk_range(BlockNumberAddress::range(range))?.try_fold(
2551 BTreeMap::new(),
2552 |mut storages: BTreeMap<(Address, B256), Vec<u64>>,
2553 entry|
2554 -> ProviderResult<_> {
2555 let (index, storage) = entry?;
2556 storages
2557 .entry((index.address(), storage.key))
2558 .or_default()
2559 .push(index.block_number());
2560 Ok(storages)
2561 },
2562 )?;
2563
2564 Ok(storage_changeset_lists)
2565 }
2566 }
2567}
2568
2569impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> StateWriter
2570 for DatabaseProvider<TX, N>
2571{
2572 type Receipt = ReceiptTy<N>;
2573
2574 #[instrument(level = "debug", target = "providers::db", skip_all)]
2575 fn write_state<'a>(
2576 &self,
2577 execution_outcome: impl Into<WriteStateInput<'a, Self::Receipt>>,
2578 is_value_known: OriginalValuesKnown,
2579 config: StateWriteConfig,
2580 ) -> ProviderResult<()> {
2581 let execution_outcome = execution_outcome.into();
2582
2583 if self.cached_storage_settings().use_hashed_state() &&
2584 !config.write_receipts &&
2585 !config.write_account_changesets &&
2586 !config.write_storage_changesets
2587 {
2588 self.write_bytecodes(
2592 execution_outcome.state().contracts.iter().map(|(h, b)| (*h, Bytecode(b.clone()))),
2593 )?;
2594 return Ok(());
2595 }
2596
2597 let first_block = execution_outcome.first_block();
2598 let (plain_state, reverts) =
2599 execution_outcome.state().to_plain_state_and_reverts(is_value_known);
2600
2601 self.write_state_reverts(reverts, first_block, config)?;
2602 self.write_state_changes(plain_state)?;
2603
2604 if !config.write_receipts {
2605 return Ok(());
2606 }
2607
2608 let block_count = execution_outcome.len() as u64;
2609 let last_block = execution_outcome.last_block();
2610 let block_range = first_block..=last_block;
2611
2612 let tip = self.last_block_number()?.max(last_block);
2613
2614 let block_indices: Vec<_> = self
2616 .block_body_indices_range(block_range)?
2617 .into_iter()
2618 .map(|b| b.first_tx_num)
2619 .collect();
2620
2621 if block_indices.len() < block_count as usize {
2623 let missing_blocks = block_count - block_indices.len() as u64;
2624 return Err(ProviderError::BlockBodyIndicesNotFound(
2625 last_block.saturating_sub(missing_blocks - 1),
2626 ));
2627 }
2628
2629 let mut receipts_writer = EitherWriter::new_receipts(self, first_block)?;
2630
2631 let has_contract_log_filter = !self.prune_modes.receipts_log_filter.is_empty();
2632 let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?;
2633
2634 let prunable_receipts = (EitherWriter::receipts_destination(self).is_database() ||
2642 self.static_file_provider()
2643 .get_highest_static_file_tx(StaticFileSegment::Receipts)
2644 .is_none()) &&
2645 PruneMode::Distance(self.minimum_pruning_distance).should_prune(first_block, tip);
2646
2647 let mut allowed_addresses: AddressSet = AddressSet::default();
2649 for (_, addresses) in contract_log_pruner.range(..first_block) {
2650 allowed_addresses.extend(addresses.iter().copied());
2651 }
2652
2653 for (idx, (receipts, first_tx_index)) in
2654 execution_outcome.receipts().zip(block_indices).enumerate()
2655 {
2656 let block_number = first_block + idx as u64;
2657
2658 receipts_writer.increment_block(block_number)?;
2660
2661 if prunable_receipts &&
2663 self.prune_modes
2664 .receipts
2665 .is_some_and(|mode| mode.should_prune(block_number, tip))
2666 {
2667 continue
2668 }
2669
2670 if let Some(new_addresses) = contract_log_pruner.get(&block_number) {
2672 allowed_addresses.extend(new_addresses.iter().copied());
2673 }
2674
2675 for (idx, receipt) in receipts.iter().enumerate() {
2676 let receipt_idx = first_tx_index + idx as u64;
2677 if prunable_receipts &&
2680 has_contract_log_filter &&
2681 !receipt.logs().iter().any(|log| allowed_addresses.contains(&log.address))
2682 {
2683 continue
2684 }
2685
2686 receipts_writer.append_receipt(receipt_idx, receipt)?;
2687 }
2688 }
2689
2690 Ok(())
2691 }
2692
2693 fn write_state_reverts(
2694 &self,
2695 reverts: PlainStateReverts,
2696 first_block: BlockNumber,
2697 config: StateWriteConfig,
2698 ) -> ProviderResult<()> {
2699 if config.write_storage_changesets {
2701 tracing::trace!("Writing storage changes");
2702 let mut storages_cursor =
2703 self.tx_ref().cursor_dup_write::<tables::PlainStorageState>()?;
2704 for (block_index, mut storage_changes) in reverts.storage.into_iter().enumerate() {
2705 let block_number = first_block + block_index as BlockNumber;
2706
2707 tracing::trace!(block_number, "Writing block change");
2708 storage_changes.par_sort_unstable_by_key(|a| a.address);
2710 let total_changes =
2711 storage_changes.iter().map(|change| change.storage_revert.len()).sum();
2712 let mut changeset = Vec::with_capacity(total_changes);
2713 for PlainStorageRevert { address, wiped, storage_revert } in storage_changes {
2714 let mut storage = storage_revert
2715 .into_iter()
2716 .map(|(k, v)| (B256::from(k), v))
2717 .collect::<Vec<_>>();
2718 storage.par_sort_unstable_by_key(|a| a.0);
2720
2721 let mut wiped_storage = Vec::new();
2729 if wiped {
2730 tracing::trace!(?address, "Wiping storage");
2731 if let Some((_, entry)) = storages_cursor.seek_exact(address)? {
2732 wiped_storage.push((entry.key, entry.value));
2733 while let Some(entry) = storages_cursor.next_dup_val()? {
2734 wiped_storage.push((entry.key, entry.value))
2735 }
2736 }
2737 }
2738
2739 tracing::trace!(?address, ?storage, "Writing storage reverts");
2740 for (key, value) in StorageRevertsIter::new(storage, wiped_storage) {
2741 changeset.push(StorageBeforeTx { address, key, value });
2742 }
2743 }
2744
2745 let mut storage_changesets_writer =
2746 EitherWriter::new_storage_changesets(self, block_number)?;
2747 storage_changesets_writer.append_storage_changeset(block_number, changeset)?;
2748 }
2749 }
2750
2751 if !config.write_account_changesets {
2752 return Ok(());
2753 }
2754
2755 tracing::trace!(?first_block, "Writing account changes");
2757 for (block_index, account_block_reverts) in reverts.accounts.into_iter().enumerate() {
2758 let block_number = first_block + block_index as BlockNumber;
2759 let changeset = account_block_reverts
2760 .into_iter()
2761 .map(|(address, info)| AccountBeforeTx { address, info: info.map(Into::into) })
2762 .collect::<Vec<_>>();
2763 let mut account_changesets_writer =
2764 EitherWriter::new_account_changesets(self, block_number)?;
2765
2766 account_changesets_writer.append_account_changeset(block_number, changeset)?;
2767 }
2768
2769 Ok(())
2770 }
2771
2772 fn write_state_changes(&self, mut changes: StateChangeset) -> ProviderResult<()> {
2773 changes.accounts.par_sort_by_key(|a| a.0);
2776 changes.storage.par_sort_by_key(|a| a.address);
2777 changes.contracts.par_sort_by_key(|a| a.0);
2778
2779 if !self.cached_storage_settings().use_hashed_state() {
2780 tracing::trace!(len = changes.accounts.len(), "Writing new account state");
2782 let mut accounts_cursor = self.tx_ref().cursor_write::<tables::PlainAccountState>()?;
2783 for (address, account) in changes.accounts {
2785 if let Some(account) = account {
2786 tracing::trace!(?address, "Updating plain state account");
2787 accounts_cursor.upsert(address, &account.into())?;
2788 } else if accounts_cursor.seek_exact(address)?.is_some() {
2789 tracing::trace!(?address, "Deleting plain state account");
2790 accounts_cursor.delete_current()?;
2791 }
2792 }
2793
2794 tracing::trace!(len = changes.storage.len(), "Writing new storage state");
2796 let mut storages_cursor =
2797 self.tx_ref().cursor_dup_write::<tables::PlainStorageState>()?;
2798 for PlainStorageChangeset { address, wipe_storage, storage } in changes.storage {
2799 if wipe_storage && storages_cursor.seek_exact(address)?.is_some() {
2801 storages_cursor.delete_current_duplicates()?;
2802 }
2803 let mut storage = storage
2805 .into_iter()
2806 .map(|(k, value)| StorageEntry { key: k.into(), value })
2807 .collect::<Vec<_>>();
2808 storage.par_sort_unstable_by_key(|a| a.key);
2810
2811 for entry in storage {
2812 tracing::trace!(?address, ?entry.key, "Updating plain state storage");
2813 if let Some(db_entry) =
2814 storages_cursor.seek_by_key_subkey(address, entry.key)? &&
2815 db_entry.key == entry.key
2816 {
2817 storages_cursor.delete_current()?;
2818 }
2819
2820 if !entry.value.is_zero() {
2821 storages_cursor.upsert(address, &entry)?;
2822 }
2823 }
2824 }
2825 }
2826
2827 tracing::trace!(len = changes.contracts.len(), "Writing bytecodes");
2829 self.write_bytecodes(
2830 changes.contracts.into_iter().map(|(hash, bytecode)| (hash, Bytecode(bytecode))),
2831 )?;
2832
2833 Ok(())
2834 }
2835
2836 #[instrument(level = "debug", target = "providers::db", skip_all)]
2837 fn write_hashed_state(&self, hashed_state: &HashedPostStateSorted) -> ProviderResult<()> {
2838 let mut hashed_accounts_cursor = self.tx_ref().cursor_write::<tables::HashedAccounts>()?;
2840 for (hashed_address, account) in hashed_state.accounts() {
2841 if let Some(account) = account {
2842 hashed_accounts_cursor.upsert(*hashed_address, account)?;
2843 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
2844 hashed_accounts_cursor.delete_current()?;
2845 }
2846 }
2847
2848 let sorted_storages = hashed_state.account_storages().iter().sorted_by_key(|(key, _)| *key);
2850 let mut hashed_storage_cursor =
2851 self.tx_ref().cursor_dup_write::<tables::HashedStorages>()?;
2852 for (hashed_address, storage) in sorted_storages {
2853 for (hashed_slot, value) in storage.storage_slots_ref() {
2854 let entry = StorageEntry { key: *hashed_slot, value: *value };
2855
2856 if let Some(db_entry) =
2857 hashed_storage_cursor.seek_by_key_subkey(*hashed_address, entry.key)? &&
2858 db_entry.key == entry.key
2859 {
2860 hashed_storage_cursor.delete_current()?;
2861 }
2862
2863 if !entry.value.is_zero() {
2864 hashed_storage_cursor.upsert(*hashed_address, &entry)?;
2865 }
2866 }
2867 }
2868
2869 Ok(())
2870 }
2871
2872 fn remove_state_above(&self, block: BlockNumber) -> ProviderResult<()> {
2894 let range = block + 1..=self.last_block_number()?;
2895
2896 if range.is_empty() {
2897 return Ok(());
2898 }
2899
2900 let from_transaction_num = self.next_tx_num_after_block(block)?;
2902
2903 let storage_range = BlockNumberAddress::range(range.clone());
2904 let storage_changeset = if self.cached_storage_settings().storage_v2 {
2905 let changesets = self.storage_changesets_range(range.clone())?;
2906 let mut changeset_writer =
2907 self.static_file_provider.latest_writer(StaticFileSegment::StorageChangeSets)?;
2908 changeset_writer.prune_storage_changesets(block)?;
2909 changesets
2910 } else {
2911 self.take::<tables::StorageChangeSets>(storage_range)?.into_iter().collect()
2912 };
2913 let account_changeset = if self.cached_storage_settings().storage_v2 {
2914 let changesets = self.account_changesets_range(range)?;
2915 let mut changeset_writer =
2916 self.static_file_provider.latest_writer(StaticFileSegment::AccountChangeSets)?;
2917 changeset_writer.prune_account_changesets(block)?;
2918 changesets
2919 } else {
2920 self.take::<tables::AccountChangeSets>(range)?
2921 };
2922
2923 if self.cached_storage_settings().use_hashed_state() {
2924 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
2925 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
2926
2927 let (state, _) = self.populate_bundle_state_hashed(
2928 account_changeset,
2929 storage_changeset,
2930 &mut hashed_accounts_cursor,
2931 &mut hashed_storage_cursor,
2932 )?;
2933
2934 for (address, (old_account, new_account, storage)) in &state {
2935 if old_account != new_account {
2936 let hashed_address = keccak256(address);
2937 let existing_entry = hashed_accounts_cursor.seek_exact(hashed_address)?;
2938 if let Some(account) = old_account {
2939 hashed_accounts_cursor.upsert(hashed_address, account)?;
2940 } else if existing_entry.is_some() {
2941 hashed_accounts_cursor.delete_current()?;
2942 }
2943 }
2944
2945 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2946 let hashed_address = keccak256(address);
2947 let hashed_storage_key = keccak256(storage_key);
2948 let storage_entry =
2949 StorageEntry { key: hashed_storage_key, value: *old_storage_value };
2950 if hashed_storage_cursor
2951 .seek_by_key_subkey(hashed_address, hashed_storage_key)?
2952 .is_some_and(|s| s.key == hashed_storage_key)
2953 {
2954 hashed_storage_cursor.delete_current()?
2955 }
2956
2957 if !old_storage_value.is_zero() {
2958 hashed_storage_cursor.upsert(hashed_address, &storage_entry)?;
2959 }
2960 }
2961 }
2962 } else {
2963 let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
2968 let mut plain_storage_cursor =
2969 self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
2970
2971 let (state, _) = self.populate_bundle_state_plain(
2972 account_changeset,
2973 storage_changeset,
2974 &mut plain_accounts_cursor,
2975 &mut plain_storage_cursor,
2976 )?;
2977
2978 for (address, (old_account, new_account, storage)) in &state {
2979 if old_account != new_account {
2980 let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
2981 if let Some(account) = old_account {
2982 plain_accounts_cursor.upsert(*address, account)?;
2983 } else if existing_entry.is_some() {
2984 plain_accounts_cursor.delete_current()?;
2985 }
2986 }
2987
2988 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
2989 let storage_entry =
2990 StorageEntry { key: *storage_key, value: *old_storage_value };
2991 if plain_storage_cursor
2992 .seek_by_key_subkey(*address, *storage_key)?
2993 .is_some_and(|s| s.key == *storage_key)
2994 {
2995 plain_storage_cursor.delete_current()?
2996 }
2997
2998 if !old_storage_value.is_zero() {
2999 plain_storage_cursor.upsert(*address, &storage_entry)?;
3000 }
3001 }
3002 }
3003 }
3004
3005 self.remove_receipts_from(from_transaction_num, block)?;
3006
3007 Ok(())
3008 }
3009
3010 fn take_state_above(
3032 &self,
3033 block: BlockNumber,
3034 ) -> ProviderResult<ExecutionOutcome<Self::Receipt>> {
3035 let range = block + 1..=self.last_block_number()?;
3036
3037 if range.is_empty() {
3038 return Ok(ExecutionOutcome::default())
3039 }
3040 let start_block_number = *range.start();
3041
3042 let block_bodies = self.block_body_indices_range(range.clone())?;
3044
3045 let from_transaction_num =
3047 block_bodies.first().expect("already checked if there are blocks").first_tx_num();
3048 let to_transaction_num =
3049 block_bodies.last().expect("already checked if there are blocks").last_tx_num();
3050
3051 let storage_range = BlockNumberAddress::range(range.clone());
3052 let storage_changeset = if let Some(highest_block) = self
3053 .static_file_provider
3054 .get_highest_static_file_block(StaticFileSegment::StorageChangeSets) &&
3055 self.cached_storage_settings().storage_v2
3056 {
3057 let changesets = self.storage_changesets_range(block + 1..=highest_block)?;
3058 let mut changeset_writer =
3059 self.static_file_provider.latest_writer(StaticFileSegment::StorageChangeSets)?;
3060 changeset_writer.prune_storage_changesets(block)?;
3061 changesets
3062 } else {
3063 self.take::<tables::StorageChangeSets>(storage_range)?.into_iter().collect()
3064 };
3065
3066 let highest_changeset_block = self
3068 .static_file_provider
3069 .get_highest_static_file_block(StaticFileSegment::AccountChangeSets);
3070 let account_changeset = if let Some(highest_block) = highest_changeset_block &&
3071 self.cached_storage_settings().storage_v2
3072 {
3073 let changesets = self.account_changesets_range(block + 1..highest_block + 1)?;
3075 let mut changeset_writer =
3076 self.static_file_provider.latest_writer(StaticFileSegment::AccountChangeSets)?;
3077 changeset_writer.prune_account_changesets(block)?;
3078
3079 changesets
3080 } else {
3081 self.take::<tables::AccountChangeSets>(range)?
3084 };
3085
3086 let (state, reverts) = if self.cached_storage_settings().use_hashed_state() {
3087 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3088 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3089
3090 let (state, reverts) = self.populate_bundle_state_hashed(
3091 account_changeset,
3092 storage_changeset,
3093 &mut hashed_accounts_cursor,
3094 &mut hashed_storage_cursor,
3095 )?;
3096
3097 for (address, (old_account, new_account, storage)) in &state {
3098 if old_account != new_account {
3099 let hashed_address = keccak256(address);
3100 let existing_entry = hashed_accounts_cursor.seek_exact(hashed_address)?;
3101 if let Some(account) = old_account {
3102 hashed_accounts_cursor.upsert(hashed_address, account)?;
3103 } else if existing_entry.is_some() {
3104 hashed_accounts_cursor.delete_current()?;
3105 }
3106 }
3107
3108 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
3109 let hashed_address = keccak256(address);
3110 let hashed_storage_key = keccak256(storage_key);
3111 let storage_entry =
3112 StorageEntry { key: hashed_storage_key, value: *old_storage_value };
3113 if hashed_storage_cursor
3114 .seek_by_key_subkey(hashed_address, hashed_storage_key)?
3115 .is_some_and(|s| s.key == hashed_storage_key)
3116 {
3117 hashed_storage_cursor.delete_current()?
3118 }
3119
3120 if !old_storage_value.is_zero() {
3121 hashed_storage_cursor.upsert(hashed_address, &storage_entry)?;
3122 }
3123 }
3124 }
3125
3126 (state, reverts)
3127 } else {
3128 let mut plain_accounts_cursor = self.tx.cursor_write::<tables::PlainAccountState>()?;
3133 let mut plain_storage_cursor =
3134 self.tx.cursor_dup_write::<tables::PlainStorageState>()?;
3135
3136 let (state, reverts) = self.populate_bundle_state_plain(
3137 account_changeset,
3138 storage_changeset,
3139 &mut plain_accounts_cursor,
3140 &mut plain_storage_cursor,
3141 )?;
3142
3143 for (address, (old_account, new_account, storage)) in &state {
3144 if old_account != new_account {
3145 let existing_entry = plain_accounts_cursor.seek_exact(*address)?;
3146 if let Some(account) = old_account {
3147 plain_accounts_cursor.upsert(*address, account)?;
3148 } else if existing_entry.is_some() {
3149 plain_accounts_cursor.delete_current()?;
3150 }
3151 }
3152
3153 for (storage_key, (old_storage_value, _new_storage_value)) in storage {
3154 let storage_entry =
3155 StorageEntry { key: *storage_key, value: *old_storage_value };
3156 if plain_storage_cursor
3157 .seek_by_key_subkey(*address, *storage_key)?
3158 .is_some_and(|s| s.key == *storage_key)
3159 {
3160 plain_storage_cursor.delete_current()?
3161 }
3162
3163 if !old_storage_value.is_zero() {
3164 plain_storage_cursor.upsert(*address, &storage_entry)?;
3165 }
3166 }
3167 }
3168
3169 (state, reverts)
3170 };
3171
3172 let mut receipts_iter = self
3174 .static_file_provider
3175 .get_range_with_static_file_or_database(
3176 StaticFileSegment::Receipts,
3177 from_transaction_num..to_transaction_num + 1,
3178 |static_file, range, _| {
3179 static_file
3180 .receipts_by_tx_range(range.clone())
3181 .map(|r| range.into_iter().zip(r).collect())
3182 },
3183 |range, _| {
3184 self.tx
3185 .cursor_read::<tables::Receipts<Self::Receipt>>()?
3186 .walk_range(range)?
3187 .map(|r| r.map_err(Into::into))
3188 .collect()
3189 },
3190 |_| true,
3191 )?
3192 .into_iter()
3193 .peekable();
3194
3195 let mut receipts = Vec::with_capacity(block_bodies.len());
3196 for block_body in block_bodies {
3198 let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize);
3199 for num in block_body.tx_num_range() {
3200 if receipts_iter.peek().is_some_and(|(n, _)| *n == num) {
3201 block_receipts.push(receipts_iter.next().unwrap().1);
3202 }
3203 }
3204 receipts.push(block_receipts);
3205 }
3206
3207 self.remove_receipts_from(from_transaction_num, block)?;
3208
3209 Ok(ExecutionOutcome::new_init(
3210 state,
3211 reverts,
3212 Vec::new(),
3213 receipts,
3214 start_block_number,
3215 Vec::new(),
3216 ))
3217 }
3218}
3219
3220impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> DatabaseProvider<TX, N> {
3221 fn write_account_trie_updates<A: TrieTableAdapter>(
3222 tx: &TX,
3223 trie_updates: &TrieUpdatesSorted,
3224 num_entries: &mut usize,
3225 ) -> ProviderResult<()>
3226 where
3227 TX: DbTxMut,
3228 {
3229 let mut account_trie_cursor = tx.cursor_write::<A::AccountTrieTable>()?;
3230 for (key, updated_node) in trie_updates.account_nodes_ref() {
3232 let nibbles = A::AccountKey::from(*key);
3233 match updated_node {
3234 Some(node) => {
3235 if !key.is_empty() {
3236 *num_entries += 1;
3237 account_trie_cursor.upsert(nibbles, node)?;
3238 }
3239 }
3240 None => {
3241 *num_entries += 1;
3242 if account_trie_cursor.seek_exact(nibbles)?.is_some() {
3243 account_trie_cursor.delete_current()?;
3244 }
3245 }
3246 }
3247 }
3248 Ok(())
3249 }
3250
3251 fn write_storage_tries<A: TrieTableAdapter>(
3252 tx: &TX,
3253 storage_tries: Vec<(&B256, &StorageTrieUpdatesSorted)>,
3254 num_entries: &mut usize,
3255 ) -> ProviderResult<()>
3256 where
3257 TX: DbTxMut,
3258 {
3259 let mut cursor = tx.cursor_dup_write::<A::StorageTrieTable>()?;
3260 for (hashed_address, storage_trie_updates) in storage_tries {
3261 let mut db_storage_trie_cursor: DatabaseStorageTrieCursor<_, A> =
3262 DatabaseStorageTrieCursor::new(cursor, *hashed_address);
3263 *num_entries +=
3264 db_storage_trie_cursor.write_storage_trie_updates_sorted(storage_trie_updates)?;
3265 cursor = db_storage_trie_cursor.cursor;
3266 }
3267 Ok(())
3268 }
3269}
3270
3271impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> TrieWriter for DatabaseProvider<TX, N> {
3272 #[instrument(level = "debug", target = "providers::db", skip_all)]
3276 fn write_trie_updates_sorted(&self, trie_updates: &TrieUpdatesSorted) -> ProviderResult<usize> {
3277 if trie_updates.is_empty() {
3278 return Ok(0)
3279 }
3280
3281 let mut num_entries = 0;
3283
3284 reth_trie_db::with_adapter!(self, |A| {
3285 Self::write_account_trie_updates::<A>(self.tx_ref(), trie_updates, &mut num_entries)?;
3286 });
3287
3288 num_entries +=
3289 self.write_storage_trie_updates_sorted(trie_updates.storage_tries_ref().iter())?;
3290
3291 Ok(num_entries)
3292 }
3293}
3294
3295impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> StorageTrieWriter for DatabaseProvider<TX, N> {
3296 fn write_storage_trie_updates_sorted<'a>(
3302 &self,
3303 storage_tries: impl Iterator<Item = (&'a B256, &'a StorageTrieUpdatesSorted)>,
3304 ) -> ProviderResult<usize> {
3305 let mut num_entries = 0;
3306 let mut storage_tries = storage_tries.collect::<Vec<_>>();
3307 storage_tries.sort_unstable_by(|a, b| a.0.cmp(b.0));
3308 reth_trie_db::with_adapter!(self, |A| {
3309 Self::write_storage_tries::<A>(self.tx_ref(), storage_tries, &mut num_entries)?;
3310 });
3311 Ok(num_entries)
3312 }
3313}
3314
3315impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HashingWriter for DatabaseProvider<TX, N> {
3316 #[allow(clippy::clone_on_copy)]
3317 fn unwind_account_hashing<'a>(
3318 &self,
3319 changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
3320 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3321 let hashed_accounts = changesets
3325 .into_iter()
3326 .map(|(_, e)| (keccak256(e.address), e.info.clone()))
3327 .collect::<Vec<_>>()
3328 .into_iter()
3329 .rev()
3330 .collect::<BTreeMap<_, _>>();
3331
3332 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3334 for (hashed_address, account) in &hashed_accounts {
3335 if let Some(account) = account {
3336 hashed_accounts_cursor.upsert(*hashed_address, account)?;
3337 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
3338 hashed_accounts_cursor.delete_current()?;
3339 }
3340 }
3341
3342 Ok(hashed_accounts)
3343 }
3344
3345 fn unwind_account_hashing_range(
3346 &self,
3347 range: impl RangeBounds<BlockNumber>,
3348 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3349 let changesets = self.account_changesets_range(range)?;
3350 self.unwind_account_hashing(changesets.iter())
3351 }
3352
3353 fn insert_account_for_hashing(
3354 &self,
3355 changesets: impl IntoIterator<Item = (Address, Option<Account>)>,
3356 ) -> ProviderResult<BTreeMap<B256, Option<Account>>> {
3357 let mut hashed_accounts_cursor = self.tx.cursor_write::<tables::HashedAccounts>()?;
3358 let hashed_accounts =
3359 changesets.into_iter().map(|(ad, ac)| (keccak256(ad), ac)).collect::<BTreeMap<_, _>>();
3360 for (hashed_address, account) in &hashed_accounts {
3361 if let Some(account) = account {
3362 hashed_accounts_cursor.upsert(*hashed_address, account)?;
3363 } else if hashed_accounts_cursor.seek_exact(*hashed_address)?.is_some() {
3364 hashed_accounts_cursor.delete_current()?;
3365 }
3366 }
3367 Ok(hashed_accounts)
3368 }
3369
3370 fn unwind_storage_hashing(
3371 &self,
3372 changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
3373 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3374 let mut hashed_storages = changesets
3376 .into_iter()
3377 .map(|(BlockNumberAddress((_, address)), storage_entry)| {
3378 let hashed_key = keccak256(storage_entry.key);
3379 (keccak256(address), hashed_key, storage_entry.value)
3380 })
3381 .collect::<Vec<_>>();
3382 hashed_storages.sort_by_key(|(ha, hk, _)| (*ha, *hk));
3383
3384 let mut hashed_storage_keys: B256Map<BTreeSet<B256>> =
3386 B256Map::with_capacity_and_hasher(hashed_storages.len(), Default::default());
3387 let mut hashed_storage = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3388 for (hashed_address, key, value) in hashed_storages.into_iter().rev() {
3389 hashed_storage_keys.entry(hashed_address).or_default().insert(key);
3390
3391 if hashed_storage
3392 .seek_by_key_subkey(hashed_address, key)?
3393 .is_some_and(|entry| entry.key == key)
3394 {
3395 hashed_storage.delete_current()?;
3396 }
3397
3398 if !value.is_zero() {
3399 hashed_storage.upsert(hashed_address, &StorageEntry { key, value })?;
3400 }
3401 }
3402 Ok(hashed_storage_keys)
3403 }
3404
3405 fn unwind_storage_hashing_range(
3406 &self,
3407 range: impl RangeBounds<BlockNumber>,
3408 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3409 let changesets = self.storage_changesets_range(range)?;
3410 self.unwind_storage_hashing(changesets.into_iter())
3411 }
3412
3413 fn insert_storage_for_hashing(
3414 &self,
3415 storages: impl IntoIterator<Item = (Address, impl IntoIterator<Item = StorageEntry>)>,
3416 ) -> ProviderResult<B256Map<BTreeSet<B256>>> {
3417 let hashed_storages =
3419 storages.into_iter().fold(BTreeMap::new(), |mut map, (address, storage)| {
3420 let storage = storage.into_iter().fold(BTreeMap::new(), |mut map, entry| {
3421 map.insert(keccak256(entry.key), entry.value);
3422 map
3423 });
3424 map.insert(keccak256(address), storage);
3425 map
3426 });
3427
3428 let hashed_storage_keys = hashed_storages
3429 .iter()
3430 .map(|(hashed_address, entries)| (*hashed_address, entries.keys().copied().collect()))
3431 .collect();
3432
3433 let mut hashed_storage_cursor = self.tx.cursor_dup_write::<tables::HashedStorages>()?;
3434 hashed_storages.into_iter().try_for_each(|(hashed_address, storage)| {
3437 storage.into_iter().try_for_each(|(key, value)| -> ProviderResult<()> {
3438 if hashed_storage_cursor
3439 .seek_by_key_subkey(hashed_address, key)?
3440 .is_some_and(|entry| entry.key == key)
3441 {
3442 hashed_storage_cursor.delete_current()?;
3443 }
3444
3445 if !value.is_zero() {
3446 hashed_storage_cursor.upsert(hashed_address, &StorageEntry { key, value })?;
3447 }
3448 Ok(())
3449 })
3450 })?;
3451
3452 Ok(hashed_storage_keys)
3453 }
3454}
3455
3456impl<TX: DbTxMut + DbTx + 'static, N: NodeTypes> HistoryWriter for DatabaseProvider<TX, N> {
3457 fn unwind_account_history_indices<'a>(
3458 &self,
3459 changesets: impl Iterator<Item = &'a (BlockNumber, AccountBeforeTx)>,
3460 ) -> ProviderResult<usize> {
3461 let mut last_indices = changesets
3462 .into_iter()
3463 .map(|(index, account)| (account.address, *index))
3464 .collect::<Vec<_>>();
3465 last_indices.sort_unstable_by_key(|(a, _)| *a);
3466
3467 if self.cached_storage_settings().storage_v2 {
3468 let batch = self.rocksdb_provider.unwind_account_history_indices(&last_indices)?;
3469 self.pending_rocksdb_batches.lock().push(batch);
3470 } else {
3471 let mut cursor = self.tx.cursor_write::<tables::AccountsHistory>()?;
3473 for &(address, rem_index) in &last_indices {
3474 let partial_shard = unwind_history_shards::<_, tables::AccountsHistory, _>(
3475 &mut cursor,
3476 ShardedKey::last(address),
3477 rem_index,
3478 |sharded_key| sharded_key.key == address,
3479 )?;
3480
3481 if !partial_shard.is_empty() {
3484 cursor.insert(
3485 ShardedKey::last(address),
3486 &BlockNumberList::new_pre_sorted(partial_shard),
3487 )?;
3488 }
3489 }
3490 }
3491
3492 let changesets = last_indices.len();
3493 Ok(changesets)
3494 }
3495
3496 fn unwind_account_history_indices_range(
3497 &self,
3498 range: impl RangeBounds<BlockNumber>,
3499 ) -> ProviderResult<usize> {
3500 let changesets = self.account_changesets_range(range)?;
3501 self.unwind_account_history_indices(changesets.iter())
3502 }
3503
3504 fn insert_account_history_index(
3505 &self,
3506 account_transitions: impl IntoIterator<Item = (Address, impl IntoIterator<Item = u64>)>,
3507 ) -> ProviderResult<()> {
3508 self.append_history_index::<_, tables::AccountsHistory>(
3509 account_transitions,
3510 ShardedKey::new,
3511 )
3512 }
3513
3514 fn unwind_storage_history_indices(
3515 &self,
3516 changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
3517 ) -> ProviderResult<usize> {
3518 let mut storage_changesets = changesets
3519 .into_iter()
3520 .map(|(BlockNumberAddress((bn, address)), storage)| (address, storage.key, bn))
3521 .collect::<Vec<_>>();
3522 storage_changesets.sort_unstable_by_key(|(address, key, _)| (*address, *key));
3523
3524 if self.cached_storage_settings().storage_v2 {
3525 let batch =
3526 self.rocksdb_provider.unwind_storage_history_indices(&storage_changesets)?;
3527 self.pending_rocksdb_batches.lock().push(batch);
3528 } else {
3529 let mut cursor = self.tx.cursor_write::<tables::StoragesHistory>()?;
3531 for &(address, storage_key, rem_index) in &storage_changesets {
3532 let partial_shard = unwind_history_shards::<_, tables::StoragesHistory, _>(
3533 &mut cursor,
3534 StorageShardedKey::last(address, storage_key),
3535 rem_index,
3536 |storage_sharded_key| {
3537 storage_sharded_key.address == address &&
3538 storage_sharded_key.sharded_key.key == storage_key
3539 },
3540 )?;
3541
3542 if !partial_shard.is_empty() {
3545 cursor.insert(
3546 StorageShardedKey::last(address, storage_key),
3547 &BlockNumberList::new_pre_sorted(partial_shard),
3548 )?;
3549 }
3550 }
3551 }
3552
3553 let changesets = storage_changesets.len();
3554 Ok(changesets)
3555 }
3556
3557 fn unwind_storage_history_indices_range(
3558 &self,
3559 range: impl RangeBounds<BlockNumber>,
3560 ) -> ProviderResult<usize> {
3561 let changesets = self.storage_changesets_range(range)?;
3562 self.unwind_storage_history_indices(changesets.into_iter())
3563 }
3564
3565 fn insert_storage_history_index(
3566 &self,
3567 storage_transitions: impl IntoIterator<Item = ((Address, B256), impl IntoIterator<Item = u64>)>,
3568 ) -> ProviderResult<()> {
3569 self.append_history_index::<_, tables::StoragesHistory>(
3570 storage_transitions,
3571 |(address, storage_key), highest_block_number| {
3572 StorageShardedKey::new(address, storage_key, highest_block_number)
3573 },
3574 )
3575 }
3576
3577 #[instrument(level = "debug", target = "providers::db", skip_all)]
3578 fn update_history_indices(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<()> {
3579 let storage_settings = self.cached_storage_settings();
3580 if !storage_settings.storage_v2 {
3581 let indices = self.changed_accounts_and_blocks_with_range(range.clone())?;
3582 self.insert_account_history_index(indices)?;
3583 }
3584
3585 if !storage_settings.storage_v2 {
3586 let indices = self.changed_storages_and_blocks_with_range(range)?;
3587 self.insert_storage_history_index(indices)?;
3588 }
3589
3590 Ok(())
3591 }
3592}
3593
3594impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockExecutionWriter
3595 for DatabaseProvider<TX, N>
3596{
3597 fn take_block_and_execution_above(
3598 &self,
3599 block: BlockNumber,
3600 ) -> ProviderResult<Chain<Self::Primitives>> {
3601 let range = block + 1..=self.last_block_number()?;
3602
3603 self.unwind_trie_state_from(block + 1)?;
3604
3605 let execution_state = self.take_state_above(block)?;
3607
3608 let blocks = self.recovered_block_range(range)?;
3609
3610 self.remove_blocks_above(block)?;
3613
3614 self.update_pipeline_stages_after_unwind(block)?;
3616
3617 Ok(Chain::new(blocks, execution_state, BTreeMap::new()))
3618 }
3619
3620 fn remove_block_and_execution_above(
3621 &self,
3622 block: BlockNumber,
3623 ) -> ProviderResult<PersistenceFrontiers> {
3624 self.unwind_trie_state_from(block + 1)?;
3625
3626 self.remove_state_above(block)?;
3628
3629 self.remove_blocks_above(block)?;
3632
3633 self.update_pipeline_stages_after_unwind(block)
3635 }
3636}
3637
3638impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider> BlockWriter
3639 for DatabaseProvider<TX, N>
3640{
3641 type Block = BlockTy<N>;
3642 type Receipt = ReceiptTy<N>;
3643
3644 fn insert_block(
3649 &self,
3650 block: &RecoveredBlock<Self::Block>,
3651 ) -> ProviderResult<StoredBlockBodyIndices> {
3652 let block_number = block.number();
3653
3654 let executed_block = ExecutedBlock::new(
3656 Arc::new(block.clone()),
3657 Arc::new(BlockExecutionOutput {
3658 result: BlockExecutionResult {
3659 receipts: Default::default(),
3660 requests: Default::default(),
3661 gas_used: 0,
3662 blob_gas_used: 0,
3663 },
3664 state: Default::default(),
3665 }),
3666 SortedTrieData::default(),
3667 );
3668
3669 self.save_blocks_inner(
3670 std::slice::from_ref(&executed_block),
3671 &[],
3672 &[],
3673 None,
3674 SaveBlocksMode::BlocksOnly,
3675 )?;
3676
3677 self.block_body_indices(block_number)?
3679 .ok_or(ProviderError::BlockBodyIndicesNotFound(block_number))
3680 }
3681
3682 fn append_block_bodies(
3683 &self,
3684 bodies: Vec<(BlockNumber, Option<&BodyTy<N>>)>,
3685 ) -> ProviderResult<()> {
3686 let Some(from_block) = bodies.first().map(|(block, _)| *block) else { return Ok(()) };
3687
3688 let mut tx_writer =
3690 self.static_file_provider.get_writer(from_block, StaticFileSegment::Transactions)?;
3691
3692 let mut block_indices_cursor = self.tx.cursor_write::<tables::BlockBodyIndices>()?;
3693 let mut tx_block_cursor = self.tx.cursor_write::<tables::TransactionBlocks>()?;
3694
3695 let mut next_tx_num = tx_block_cursor.last()?.map(|(id, _)| id + 1).unwrap_or_default();
3697
3698 for (block_number, body) in &bodies {
3699 tx_writer.increment_block(*block_number)?;
3701
3702 let tx_count = body.as_ref().map(|b| b.transactions().len() as u64).unwrap_or_default();
3703 let block_indices = StoredBlockBodyIndices { first_tx_num: next_tx_num, tx_count };
3704
3705 let mut durations_recorder = metrics::DurationsRecorder::new(&self.metrics);
3706
3707 block_indices_cursor.append(*block_number, &block_indices)?;
3709
3710 durations_recorder.record_relative(metrics::Action::InsertBlockBodyIndices);
3711
3712 let Some(body) = body else { continue };
3713
3714 if !body.transactions().is_empty() {
3716 tx_block_cursor.append(block_indices.last_tx_num(), block_number)?;
3717 durations_recorder.record_relative(metrics::Action::InsertTransactionBlocks);
3718 }
3719
3720 for transaction in body.transactions() {
3722 tx_writer.append_transaction(next_tx_num, transaction)?;
3723
3724 next_tx_num += 1;
3726 }
3727 }
3728
3729 self.storage.writer().write_block_bodies(self, bodies)?;
3730
3731 Ok(())
3732 }
3733
3734 fn remove_blocks_above(&self, block: BlockNumber) -> ProviderResult<()> {
3735 let last_block_number = self.last_block_number()?;
3736 for hash in self.canonical_hashes_range(block + 1, last_block_number + 1)? {
3738 self.tx.delete::<tables::HeaderNumbers>(hash, None)?;
3739 }
3740
3741 let highest_static_file_block = self
3743 .static_file_provider()
3744 .get_highest_static_file_block(StaticFileSegment::Headers)
3745 .expect("todo: error handling, headers should exist");
3746
3747 debug!(target: "providers::db", ?block, "Removing static file blocks above block_number");
3753 self.static_file_provider()
3754 .get_writer(block, StaticFileSegment::Headers)?
3755 .prune_headers(highest_static_file_block.saturating_sub(block))?;
3756
3757 let unwind_tx_from = self.next_tx_num_after_block(block)?;
3759
3760 let unwind_tx_to = self
3762 .tx
3763 .cursor_read::<tables::BlockBodyIndices>()?
3764 .last()?
3765 .ok_or(ProviderError::BlockBodyIndicesNotFound(block))?
3767 .1
3768 .last_tx_num();
3769
3770 if unwind_tx_from <= unwind_tx_to {
3771 let hashes = self.transaction_hashes_by_range(unwind_tx_from..(unwind_tx_to + 1))?;
3772 self.with_rocksdb_batch(|batch| {
3773 let mut writer = EitherWriter::new_transaction_hash_numbers(self, batch)?;
3774 for (hash, _) in hashes {
3775 writer.delete_transaction_hash_number(hash)?;
3776 }
3777 Ok(((), writer.into_raw_rocksdb_batch()))
3778 })?;
3779 }
3780
3781 if self.prune_modes.sender_recovery.is_none_or(|m| !m.is_full()) {
3784 EitherWriter::new_senders(self, last_block_number)?
3785 .prune_senders(unwind_tx_from, block)?;
3786 }
3787
3788 self.remove_bodies_above(block)?;
3789
3790 Ok(())
3791 }
3792
3793 fn remove_bodies_above(&self, block: BlockNumber) -> ProviderResult<()> {
3794 self.storage.writer().remove_block_bodies_above(self, block)?;
3795
3796 let unwind_tx_from = self.next_tx_num_after_block(block)?;
3798
3799 self.remove::<tables::BlockBodyIndices>(block + 1..)?;
3800 self.remove::<tables::TransactionBlocks>(unwind_tx_from..)?;
3801
3802 let static_file_tx_num =
3803 self.static_file_provider.get_highest_static_file_tx(StaticFileSegment::Transactions);
3804
3805 let to_delete = static_file_tx_num
3806 .map(|static_tx| (static_tx + 1).saturating_sub(unwind_tx_from))
3807 .unwrap_or_default();
3808
3809 self.static_file_provider
3810 .latest_writer(StaticFileSegment::Transactions)?
3811 .prune_transactions(to_delete, block)?;
3812
3813 Ok(())
3814 }
3815
3816 fn append_blocks_with_state(
3825 &self,
3826 blocks: Vec<RecoveredBlock<Self::Block>>,
3827 execution_outcome: &ExecutionOutcome<Self::Receipt>,
3828 hashed_state: HashedPostStateSorted,
3829 ) -> ProviderResult<()> {
3830 if blocks.is_empty() {
3831 debug!(target: "providers::db", "Attempted to append empty block range");
3832 return Ok(())
3833 }
3834
3835 let first_number = blocks[0].number();
3838
3839 let last_block_number = blocks[blocks.len() - 1].number();
3842
3843 let mut durations_recorder = metrics::DurationsRecorder::new(&self.metrics);
3844
3845 let (account_transitions, storage_transitions) = {
3850 let mut account_transitions: BTreeMap<Address, Vec<u64>> = BTreeMap::new();
3851 let mut storage_transitions: BTreeMap<(Address, B256), Vec<u64>> = BTreeMap::new();
3852 for (block_idx, block_reverts) in execution_outcome.bundle.reverts.iter().enumerate() {
3853 let block_number = first_number + block_idx as u64;
3854 for (address, account_revert) in block_reverts {
3855 account_transitions.entry(*address).or_default().push(block_number);
3856 for storage_key in account_revert.storage.keys() {
3857 let key = B256::from(*storage_key);
3858 storage_transitions.entry((*address, key)).or_default().push(block_number);
3859 }
3860 }
3861 }
3862 (account_transitions, storage_transitions)
3863 };
3864
3865 for block in blocks {
3867 self.insert_block(&block)?;
3868 durations_recorder.record_relative(metrics::Action::InsertBlock);
3869 }
3870
3871 self.write_state(execution_outcome, OriginalValuesKnown::No, StateWriteConfig::default())?;
3872 durations_recorder.record_relative(metrics::Action::InsertState);
3873
3874 self.write_hashed_state(&hashed_state)?;
3876 durations_recorder.record_relative(metrics::Action::InsertHashes);
3877
3878 let storage_settings = self.cached_storage_settings();
3883 if storage_settings.storage_v2 {
3884 self.with_rocksdb_batch(|mut batch| {
3885 for (address, blocks) in account_transitions {
3886 batch.append_account_history_shard(address, blocks)?;
3887 }
3888 Ok(((), Some(batch.into_inner())))
3889 })?;
3890 } else {
3891 self.insert_account_history_index(account_transitions)?;
3892 }
3893 if storage_settings.storage_v2 {
3894 self.with_rocksdb_batch(|mut batch| {
3895 for ((address, key), blocks) in storage_transitions {
3896 batch.append_storage_history_shard(address, key, blocks)?;
3897 }
3898 Ok(((), Some(batch.into_inner())))
3899 })?;
3900 } else {
3901 self.insert_storage_history_index(storage_transitions)?;
3902 }
3903 durations_recorder.record_relative(metrics::Action::InsertHistoryIndices);
3904
3905 self.update_pipeline_stages(last_block_number, false)?;
3907 durations_recorder.record_relative(metrics::Action::UpdatePipelineStages);
3908
3909 debug!(target: "providers::db", range = ?first_number..=last_block_number, actions = ?durations_recorder.actions, "Appended blocks");
3910
3911 Ok(())
3912 }
3913
3914 fn clear_transaction_lookup(&self) -> ProviderResult<()> {
3915 if self.cached_storage_settings().storage_v2 {
3916 self.rocksdb_provider.clear::<tables::TransactionHashNumbers>()
3917 } else {
3918 self.tx.clear::<tables::TransactionHashNumbers>().map_err(Into::into)
3919 }
3920 }
3921}
3922
3923impl<TX: DbTx + 'static, N: NodeTypes> PruneCheckpointReader for DatabaseProvider<TX, N> {
3924 fn get_prune_checkpoint(
3925 &self,
3926 segment: PruneSegment,
3927 ) -> ProviderResult<Option<PruneCheckpoint>> {
3928 Ok(self.tx.get::<tables::PruneCheckpoints>(segment)?)
3929 }
3930
3931 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
3932 Ok(PruneSegment::variants()
3933 .filter_map(|segment| {
3934 self.tx
3935 .get::<tables::PruneCheckpoints>(segment)
3936 .transpose()
3937 .map(|chk| chk.map(|chk| (segment, chk)))
3938 })
3939 .collect::<Result<_, _>>()?)
3940 }
3941}
3942
3943impl<TX: DbTxMut, N: NodeTypes> PruneCheckpointWriter for DatabaseProvider<TX, N> {
3944 fn save_prune_checkpoint(
3945 &self,
3946 segment: PruneSegment,
3947 checkpoint: PruneCheckpoint,
3948 ) -> ProviderResult<()> {
3949 Ok(self.tx.put::<tables::PruneCheckpoints>(segment, checkpoint)?)
3950 }
3951}
3952
3953impl<TX: DbTx + 'static, N: NodeTypesForProvider> StatsReader for DatabaseProvider<TX, N> {
3954 fn count_entries<T: Table>(&self) -> ProviderResult<usize> {
3955 let db_entries = self.tx.entries::<T>()?;
3956 let static_file_entries = match self.static_file_provider.count_entries::<T>() {
3957 Ok(entries) => entries,
3958 Err(ProviderError::UnsupportedProvider) => 0,
3959 Err(err) => return Err(err),
3960 };
3961
3962 Ok(db_entries + static_file_entries)
3963 }
3964}
3965
3966impl<TX: DbTx + 'static, N: NodeTypes> ChainStateBlockReader for DatabaseProvider<TX, N> {
3967 fn last_finalized_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
3968 let mut finalized_blocks = self
3969 .tx
3970 .cursor_read::<tables::ChainState>()?
3971 .walk(Some(tables::ChainStateKey::LastFinalizedBlock))?
3972 .take(1)
3973 .collect::<Result<BTreeMap<tables::ChainStateKey, BlockNumber>, _>>()?;
3974
3975 let last_finalized_block_number = finalized_blocks.pop_first().map(|pair| pair.1);
3976 Ok(last_finalized_block_number)
3977 }
3978
3979 fn last_safe_block_number(&self) -> ProviderResult<Option<BlockNumber>> {
3980 let mut finalized_blocks = self
3981 .tx
3982 .cursor_read::<tables::ChainState>()?
3983 .walk(Some(tables::ChainStateKey::LastSafeBlock))?
3984 .take(1)
3985 .collect::<Result<BTreeMap<tables::ChainStateKey, BlockNumber>, _>>()?;
3986
3987 let last_finalized_block_number = finalized_blocks.pop_first().map(|pair| pair.1);
3988 Ok(last_finalized_block_number)
3989 }
3990}
3991
3992impl<TX: DbTxMut, N: NodeTypes> ChainStateBlockWriter for DatabaseProvider<TX, N> {
3993 fn save_finalized_block_number(&self, block_number: BlockNumber) -> ProviderResult<()> {
3994 Ok(self
3995 .tx
3996 .put::<tables::ChainState>(tables::ChainStateKey::LastFinalizedBlock, block_number)?)
3997 }
3998
3999 fn save_safe_block_number(&self, block_number: BlockNumber) -> ProviderResult<()> {
4000 Ok(self.tx.put::<tables::ChainState>(tables::ChainStateKey::LastSafeBlock, block_number)?)
4001 }
4002}
4003
4004impl<TX: DbTx + 'static, N: NodeTypes> DbTxProvider for DatabaseProvider<TX, N> {
4005 type Tx = TX;
4006
4007 fn tx(&self) -> &Self::Tx {
4008 &self.tx
4009 }
4010}
4011
4012impl<TX: DbTx + 'static, N: NodeTypes> DBProvider for DatabaseProvider<TX, N> {
4013 fn tx_mut(&mut self) -> &mut Self::Tx {
4014 &mut self.tx
4015 }
4016
4017 fn into_tx(self) -> Self::Tx {
4018 self.tx
4019 }
4020
4021 fn prune_modes_ref(&self) -> &PruneModes {
4022 self.prune_modes_ref()
4023 }
4024
4025 #[instrument(
4027 name = "DatabaseProvider::commit",
4028 level = "debug",
4029 target = "providers::db",
4030 skip_all
4031 )]
4032 fn commit(self) -> ProviderResult<()> {
4033 if self.static_file_provider.has_unwind_queued() || self.commit_order.is_unwind() {
4034 self.commit_unwind()?;
4035 } else {
4036 let mut timings = metrics::CommitTimings::default();
4038
4039 let start = Instant::now();
4040 self.static_file_provider.finalize()?;
4041 timings.sf = start.elapsed();
4042
4043 let start = Instant::now();
4044 let batches = std::mem::take(&mut *self.pending_rocksdb_batches.lock());
4045 for batch in batches {
4046 self.rocksdb_provider.commit_batch(batch)?;
4047 }
4048 timings.rocksdb = start.elapsed();
4049
4050 let start = Instant::now();
4051 self.tx.commit()?;
4052 timings.mdbx = start.elapsed();
4053
4054 self.metrics.record_commit(&timings);
4055 }
4056
4057 Ok(())
4058 }
4059}
4060
4061impl<TX: DbTx, N: NodeTypes> MetadataProvider for DatabaseProvider<TX, N> {
4062 fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>> {
4063 self.tx.get::<tables::Metadata>(key.to_string()).map_err(Into::into)
4064 }
4065}
4066
4067impl<TX: DbTxMut, N: NodeTypes> MetadataWriter for DatabaseProvider<TX, N> {
4068 fn write_metadata(&self, key: &str, value: Vec<u8>) -> ProviderResult<()> {
4069 self.tx.put::<tables::Metadata>(key.to_string(), value).map_err(Into::into)
4070 }
4071
4072 fn delete_metadata(&self, key: &str) -> ProviderResult<()> {
4073 self.tx.delete::<tables::Metadata>(key.to_string(), None)?;
4074 Ok(())
4075 }
4076}
4077
4078impl<TX: Send, N: NodeTypes> StorageSettingsCache for DatabaseProvider<TX, N> {
4079 fn cached_storage_settings(&self) -> StorageSettings {
4080 *self.storage_settings.read()
4081 }
4082
4083 fn set_storage_settings_cache(&self, settings: StorageSettings) {
4084 *self.storage_settings.write() = settings;
4085 }
4086}
4087
4088impl<TX: Send, N: NodeTypes> StoragePath for DatabaseProvider<TX, N> {
4089 fn storage_path(&self) -> PathBuf {
4090 self.db_path.clone()
4091 }
4092}
4093
4094#[cfg(test)]
4095mod tests {
4096 use super::*;
4097 use crate::{
4098 test_utils::{blocks::BlockchainTestData, create_test_provider_factory},
4099 BlockWriter,
4100 };
4101 use alloy_consensus::Header;
4102 use alloy_primitives::{
4103 map::{AddressMap, B256Map},
4104 U256,
4105 };
4106 use reth_chain_state::{test_utils::TestBlockBuilder, ExecutedBlock};
4107 use reth_db_api::models::StorageSettings;
4108 use reth_ethereum_primitives::Receipt;
4109 use reth_execution_types::{AccountRevertInit, BlockExecutionOutput, BlockExecutionResult};
4110 use reth_primitives_traits::SealedBlock;
4111 use reth_storage_api::{DatabaseProviderFactory, MetadataWriter};
4112 use reth_testing_utils::generators::{self, random_block, BlockParams};
4113 use reth_trie::{
4114 HashedPostState, KeccakKeyHasher, Nibbles, SortedTrieData, StoredNibbles,
4115 StoredNibblesSubKey,
4116 };
4117 use revm::{database::BundleState, state::AccountInfo};
4118 use std::{sync::mpsc, time::Duration};
4119
4120 fn save_genesis<TX, N>(
4123 provider: &DatabaseProvider<TX, N>,
4124 genesis: &ExecutedBlock<N::Primitives>,
4125 ) -> ProviderResult<()>
4126 where
4127 TX: DbTx + DbTxMut + 'static,
4128 N: NodeTypesForProvider,
4129 {
4130 assert_eq!(genesis.recovered_block().number(), 0);
4131 provider.save_blocks_inner(
4132 std::slice::from_ref(genesis),
4133 std::slice::from_ref(genesis),
4134 &[],
4135 None,
4136 SaveBlocksMode::Full,
4137 )
4138 }
4139
4140 #[test]
4141 fn snap_attempt_guards_finish_checkpoint_writers() {
4142 use alloy_eips::BlockNumHash;
4143 use reth_db_api::models::SnapAttempt;
4144
4145 let factory = create_test_provider_factory();
4146 let provider = factory.database_provider_rw().unwrap();
4147 provider.update_pipeline_stages(5, false).unwrap();
4148 let mut attempt = SnapAttempt::start(
4149 None,
4150 BlockNumHash::new(10, B256::repeat_byte(1)),
4151 B256::repeat_byte(2),
4152 );
4153 provider.write_snap_attempt(&attempt).unwrap();
4154 provider.commit().unwrap();
4155
4156 let provider = factory.database_provider_rw().unwrap();
4157 let refused = provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(10));
4158 assert!(matches!(refused, Err(ProviderError::UnverifiedSnapState { attempt: 0 })));
4159 let refused = provider.update_pipeline_stages(10, false);
4160 assert!(matches!(refused, Err(ProviderError::UnverifiedSnapState { attempt: 0 })));
4161 for stage in [StageId::Finish, StageId::Headers] {
4162 assert_eq!(provider.get_stage_checkpoint(stage).unwrap().unwrap().block_number, 5);
4163 }
4164
4165 provider.update_pipeline_stages(4, true).unwrap();
4167 provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(3)).unwrap();
4168 assert_eq!(
4169 provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap().block_number,
4170 3
4171 );
4172
4173 attempt.abandon();
4175 provider.write_snap_attempt(&attempt).unwrap();
4176 let refused = provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(10));
4177 assert!(matches!(refused, Err(ProviderError::UnverifiedSnapState { attempt: 0 })));
4178
4179 provider.save_stage_checkpoint(StageId::Headers, StageCheckpoint::new(10)).unwrap();
4181 attempt.verify();
4182 provider.write_snap_attempt(&attempt).unwrap();
4183 provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(10)).unwrap();
4184 provider.update_pipeline_stages(11, false).unwrap();
4185 provider.commit().unwrap();
4186
4187 let provider = factory.database_provider_ro().unwrap();
4188 assert_eq!(
4189 provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap().block_number,
4190 11
4191 );
4192 }
4193
4194 #[test]
4195 fn snap_attempt_guards_partial_state_trie_advances() {
4196 use alloy_eips::BlockNumHash;
4197 use reth_db_api::models::SnapAttempt;
4198
4199 for abandoned in [false, true] {
4200 let factory = create_test_provider_factory();
4201 let provider = factory.database_provider_rw().unwrap();
4202 provider.update_pipeline_stages(100, false).unwrap();
4203 let checkpoint = StageCheckpoint::new(100)
4204 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(50) });
4205 provider.save_stage_checkpoint(StageId::Finish, checkpoint).unwrap();
4206 let mut attempt = SnapAttempt::start(None, BlockNumHash::default(), B256::ZERO);
4207 if abandoned {
4208 attempt.abandon();
4209 }
4210 provider.write_snap_attempt(&attempt).unwrap();
4211
4212 for block in [100, 90] {
4213 let result =
4214 provider.save_stage_checkpoint(StageId::Finish, StageCheckpoint::new(block));
4215 assert!(matches!(result, Err(ProviderError::UnverifiedSnapState { .. })));
4216 let result = provider.update_pipeline_stages(block, true);
4217 assert!(matches!(result, Err(ProviderError::UnverifiedSnapState { .. })));
4218 for stage in [StageId::Finish, StageId::Headers] {
4219 assert_eq!(
4220 provider.get_stage_checkpoint(stage).unwrap().unwrap().block_number,
4221 100
4222 );
4223 }
4224 assert_eq!(
4225 provider.get_stage_checkpoint(StageId::Finish).unwrap(),
4226 Some(checkpoint)
4227 );
4228 }
4229
4230 let advanced = StageCheckpoint::new(100)
4231 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(60) });
4232 assert!(matches!(
4233 provider.save_stage_checkpoint(StageId::Finish, advanced),
4234 Err(ProviderError::UnverifiedSnapState { .. })
4235 ));
4236
4237 provider.update_pipeline_stages(90, false).unwrap();
4239 let rewind = StageCheckpoint::new(80)
4240 .with_finish_stage_checkpoint(FinishCheckpoint { partial_state_trie: Some(40) });
4241 provider.save_stage_checkpoint(StageId::Finish, rewind).unwrap();
4242 provider.update_pipeline_stages(30, true).unwrap();
4243
4244 attempt.verify();
4245 provider.write_snap_attempt(&attempt).unwrap();
4246 provider.save_stage_checkpoint(StageId::Finish, advanced).unwrap();
4247 provider.update_pipeline_stages(100, true).unwrap();
4248 }
4249 }
4250
4251 #[test]
4252 fn test_receipts_by_block_range_empty_range() {
4253 let factory = create_test_provider_factory();
4254 let provider = factory.provider().unwrap();
4255
4256 let start = 10u64;
4258 let end = 9u64;
4259 let result = provider.receipts_by_block_range(start..=end).unwrap();
4260 assert_eq!(result, Vec::<Vec<reth_ethereum_primitives::Receipt>>::new());
4261 }
4262
4263 #[test]
4264 fn unwind_commit_waits_for_pre_commit_readers() {
4265 let factory = create_test_provider_factory();
4266
4267 let reader = factory.provider().unwrap();
4268 let provider_rw = factory.unwind_provider_rw().unwrap();
4269 provider_rw.write_metadata("unwind-wait-test", vec![1]).unwrap();
4270 let (done_tx, done_rx) = mpsc::channel();
4271
4272 let handle = std::thread::spawn(move || {
4273 let result = provider_rw.commit();
4274 done_tx.send(result).unwrap();
4275 });
4276
4277 assert!(
4278 done_rx.recv_timeout(Duration::from_millis(50)).is_err(),
4279 "unwind commit should wait while an older read transaction is still open"
4280 );
4281
4282 drop(reader);
4283
4284 done_rx.recv_timeout(Duration::from_secs(1)).unwrap().unwrap();
4285 handle.join().unwrap();
4286 }
4287
4288 #[test]
4289 fn test_receipts_by_block_range_nonexistent_blocks() {
4290 let factory = create_test_provider_factory();
4291 let provider = factory.provider().unwrap();
4292
4293 let result = provider.receipts_by_block_range(10..=12).unwrap();
4295 assert_eq!(result, vec![vec![], vec![], vec![]]);
4296 }
4297
4298 #[test]
4299 fn test_receipts_by_block_range_single_block() {
4300 let factory = create_test_provider_factory();
4301 let data = BlockchainTestData::default();
4302
4303 let provider_rw = factory.provider_rw().unwrap();
4304 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4305 provider_rw
4306 .write_state(
4307 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4308 crate::OriginalValuesKnown::No,
4309 StateWriteConfig::default(),
4310 )
4311 .unwrap();
4312 provider_rw.insert_block(&data.blocks[0].0).unwrap();
4313 provider_rw
4314 .write_state(
4315 &data.blocks[0].1,
4316 crate::OriginalValuesKnown::No,
4317 StateWriteConfig::default(),
4318 )
4319 .unwrap();
4320 provider_rw.commit().unwrap();
4321
4322 let provider = factory.provider().unwrap();
4323 let result = provider.receipts_by_block_range(1..=1).unwrap();
4324
4325 assert_eq!(result.len(), 1);
4327 assert_eq!(result[0].len(), 1);
4328 assert_eq!(result[0][0], data.blocks[0].1.receipts()[0][0]);
4329 }
4330
4331 #[test]
4332 fn test_receipts_by_block_range_multiple_blocks() {
4333 let factory = create_test_provider_factory();
4334 let data = BlockchainTestData::default();
4335
4336 let provider_rw = factory.provider_rw().unwrap();
4337 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4338 provider_rw
4339 .write_state(
4340 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4341 crate::OriginalValuesKnown::No,
4342 StateWriteConfig::default(),
4343 )
4344 .unwrap();
4345 for (block, outcome) in data.blocks.iter().take(3) {
4346 provider_rw.insert_block(block).unwrap();
4347 provider_rw
4348 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4349 .unwrap();
4350 }
4351 provider_rw.commit().unwrap();
4352
4353 let provider = factory.provider().unwrap();
4354 let result = provider.receipts_by_block_range(1..=3).unwrap();
4355
4356 assert_eq!(result.len(), 3);
4358 for (i, block_receipts) in result.iter().enumerate() {
4359 assert_eq!(block_receipts.len(), 1);
4360 assert_eq!(block_receipts[0], data.blocks[i].1.receipts()[0][0]);
4361 }
4362 }
4363
4364 #[test]
4365 fn test_receipts_by_block_range_blocks_with_varying_tx_counts() {
4366 let factory = create_test_provider_factory();
4367 let data = BlockchainTestData::default();
4368
4369 let provider_rw = factory.provider_rw().unwrap();
4370 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4371 provider_rw
4372 .write_state(
4373 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4374 crate::OriginalValuesKnown::No,
4375 StateWriteConfig::default(),
4376 )
4377 .unwrap();
4378
4379 for (block, outcome) in data.blocks.iter().take(3) {
4381 provider_rw.insert_block(block).unwrap();
4382 provider_rw
4383 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4384 .unwrap();
4385 }
4386 provider_rw.commit().unwrap();
4387
4388 let provider = factory.provider().unwrap();
4389 let result = provider.receipts_by_block_range(1..=3).unwrap();
4390
4391 assert_eq!(result.len(), 3);
4393 for block_receipts in &result {
4394 assert_eq!(block_receipts.len(), 1);
4395 }
4396 }
4397
4398 #[test]
4399 fn test_receipts_by_block_range_partial_range() {
4400 let factory = create_test_provider_factory();
4401 let data = BlockchainTestData::default();
4402
4403 let provider_rw = factory.provider_rw().unwrap();
4404 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4405 provider_rw
4406 .write_state(
4407 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4408 crate::OriginalValuesKnown::No,
4409 StateWriteConfig::default(),
4410 )
4411 .unwrap();
4412 for (block, outcome) in data.blocks.iter().take(3) {
4413 provider_rw.insert_block(block).unwrap();
4414 provider_rw
4415 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4416 .unwrap();
4417 }
4418 provider_rw.commit().unwrap();
4419
4420 let provider = factory.provider().unwrap();
4421
4422 let result = provider.receipts_by_block_range(2..=5).unwrap();
4424 assert_eq!(result.len(), 4);
4425
4426 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]);
4433 assert_eq!(result[1][0], data.blocks[2].1.receipts()[0][0]);
4434 }
4435
4436 #[test]
4437 fn test_receipts_by_block_range_all_empty_blocks() {
4438 let factory = create_test_provider_factory();
4439 let mut rng = generators::rng();
4440
4441 let mut blocks = Vec::new();
4443 for i in 0..3 {
4444 let block =
4445 random_block(&mut rng, i, BlockParams { tx_count: Some(0), ..Default::default() });
4446 blocks.push(block);
4447 }
4448
4449 let provider_rw = factory.provider_rw().unwrap();
4450 for block in blocks {
4451 provider_rw.insert_block(&block.try_recover().unwrap()).unwrap();
4452 }
4453 provider_rw.commit().unwrap();
4454
4455 let provider = factory.provider().unwrap();
4456 let result = provider.receipts_by_block_range(1..=3).unwrap();
4457
4458 assert_eq!(result.len(), 3);
4459 for block_receipts in result {
4460 assert_eq!(block_receipts.len(), 0);
4461 }
4462 }
4463
4464 #[test]
4465 fn test_receipts_by_block_range_consistency_with_individual_calls() {
4466 let factory = create_test_provider_factory();
4467 let data = BlockchainTestData::default();
4468
4469 let provider_rw = factory.provider_rw().unwrap();
4470 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4471 provider_rw
4472 .write_state(
4473 &ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
4474 crate::OriginalValuesKnown::No,
4475 StateWriteConfig::default(),
4476 )
4477 .unwrap();
4478 for (block, outcome) in data.blocks.iter().take(3) {
4479 provider_rw.insert_block(block).unwrap();
4480 provider_rw
4481 .write_state(outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
4482 .unwrap();
4483 }
4484 provider_rw.commit().unwrap();
4485
4486 let provider = factory.provider().unwrap();
4487
4488 let range_result = provider.receipts_by_block_range(1..=3).unwrap();
4490
4491 let mut individual_results = Vec::new();
4493 for block_num in 1..=3 {
4494 let receipts =
4495 provider.receipts_by_block(block_num.into()).unwrap().unwrap_or_default();
4496 individual_results.push(receipts);
4497 }
4498
4499 assert_eq!(range_result, individual_results);
4500 }
4501
4502 #[test]
4503 fn test_receipts_by_block_returns_none_for_missing_unpruned_receipts() {
4504 let factory = create_test_provider_factory();
4505 let data = BlockchainTestData::default();
4506
4507 let provider_rw = factory.provider_rw().unwrap();
4508 provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
4509 provider_rw.insert_block(&data.blocks[0].0).unwrap();
4510 provider_rw.commit().unwrap();
4511
4512 let provider = factory.provider().unwrap();
4513 assert!(provider.receipts_by_block(1.into()).unwrap().is_none());
4514 }
4515
4516 #[test]
4517 fn test_write_trie_updates_sorted() {
4518 use reth_trie::{
4519 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
4520 BranchNodeCompact, StorageTrieEntry,
4521 };
4522
4523 let factory = create_test_provider_factory();
4524 let provider_rw = factory.provider_rw().unwrap();
4525
4526 {
4528 let tx = provider_rw.tx_ref();
4529 let mut cursor = tx.cursor_write::<tables::AccountsTrie>().unwrap();
4530
4531 let to_delete = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4]));
4533 cursor
4534 .upsert(
4535 to_delete,
4536 &BranchNodeCompact::new(
4537 0b1010_1010_1010_1010, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4541 None,
4542 ),
4543 )
4544 .unwrap();
4545
4546 let to_update = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2]));
4548 cursor
4549 .upsert(
4550 to_update,
4551 &BranchNodeCompact::new(
4552 0b0101_0101_0101_0101, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4556 None,
4557 ),
4558 )
4559 .unwrap();
4560 }
4561
4562 let storage_address1 = B256::repeat_byte(1u8);
4564 let storage_address2 = B256::repeat_byte(2u8);
4565 {
4566 let tx = provider_rw.tx_ref();
4567 let mut storage_cursor = tx.cursor_dup_write::<tables::StoragesTrie>().unwrap();
4568
4569 storage_cursor
4571 .upsert(
4572 storage_address1,
4573 &StorageTrieEntry {
4574 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0x2, 0x0])),
4575 node: BranchNodeCompact::new(
4576 0b0011_0011_0011_0011, 0b0000_0000_0000_0000,
4578 0b0000_0000_0000_0000,
4579 vec![],
4580 None,
4581 ),
4582 },
4583 )
4584 .unwrap();
4585
4586 storage_cursor
4588 .upsert(
4589 storage_address2,
4590 &StorageTrieEntry {
4591 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xa, 0xb])),
4592 node: BranchNodeCompact::new(
4593 0b1100_1100_1100_1100,
4594 0b0000_0000_0000_0000,
4595 0b0000_0000_0000_0000,
4596 vec![],
4597 None,
4598 ),
4599 },
4600 )
4601 .unwrap();
4602 storage_cursor
4603 .upsert(
4604 storage_address2,
4605 &StorageTrieEntry {
4606 nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xc, 0xd])),
4607 node: BranchNodeCompact::new(
4608 0b0011_1100_0011_1100,
4609 0b0000_0000_0000_0000,
4610 0b0000_0000_0000_0000,
4611 vec![],
4612 None,
4613 ),
4614 },
4615 )
4616 .unwrap();
4617 }
4618
4619 let account_nodes = vec![
4621 (
4622 Nibbles::from_nibbles([0x1, 0x2]),
4623 Some(BranchNodeCompact::new(
4624 0b1111_1111_1111_1111, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4628 None,
4629 )),
4630 ),
4631 (Nibbles::from_nibbles([0x3, 0x4]), None), (
4633 Nibbles::from_nibbles([0x5, 0x6]),
4634 Some(BranchNodeCompact::new(
4635 0b1111_1111_1111_1111, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4639 None,
4640 )),
4641 ),
4642 ];
4643
4644 let storage_trie1 = StorageTrieUpdatesSorted {
4646 storage_nodes: vec![
4647 (
4648 Nibbles::from_nibbles([0x1, 0x0]),
4649 Some(BranchNodeCompact::new(
4650 0b1111_0000_0000_0000, 0b0000_0000_0000_0000, 0b0000_0000_0000_0000, vec![],
4654 None,
4655 )),
4656 ),
4657 (Nibbles::from_nibbles([0x2, 0x0]), None), ],
4659 };
4660
4661 let storage_trie2 = StorageTrieUpdatesSorted {
4662 storage_nodes: vec![
4663 (Nibbles::from_nibbles([0xa, 0xb]), None),
4664 (Nibbles::from_nibbles([0xc, 0xd]), None),
4665 ],
4666 };
4667
4668 let mut storage_tries = B256Map::default();
4669 storage_tries.insert(storage_address1, storage_trie1);
4670 storage_tries.insert(storage_address2, storage_trie2);
4671
4672 let trie_updates = TrieUpdatesSorted::new(account_nodes, storage_tries);
4673
4674 let num_entries = provider_rw.write_trie_updates_sorted(&trie_updates).unwrap();
4676
4677 assert_eq!(num_entries, 7);
4680
4681 let tx = provider_rw.tx_ref();
4683 let mut cursor = tx.cursor_read::<tables::AccountsTrie>().unwrap();
4684
4685 let nibbles1 = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2]));
4687 let entry1 = cursor.seek_exact(nibbles1).unwrap();
4688 assert!(entry1.is_some(), "Updated account node should exist");
4689 let expected_mask = reth_trie::TrieMask::new(0b1111_1111_1111_1111);
4690 assert_eq!(
4691 entry1.unwrap().1.state_mask,
4692 expected_mask,
4693 "Account node should have updated state_mask"
4694 );
4695
4696 let nibbles2 = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4]));
4698 let entry2 = cursor.seek_exact(nibbles2).unwrap();
4699 assert!(entry2.is_none(), "Deleted account node should not exist");
4700
4701 let nibbles3 = StoredNibbles(Nibbles::from_nibbles([0x5, 0x6]));
4703 let entry3 = cursor.seek_exact(nibbles3).unwrap();
4704 assert!(entry3.is_some(), "New account node should exist");
4705
4706 let mut storage_cursor = tx.cursor_dup_read::<tables::StoragesTrie>().unwrap();
4708
4709 let storage_entries1: Vec<_> = storage_cursor
4711 .walk_dup(Some(storage_address1), None)
4712 .unwrap()
4713 .collect::<Result<Vec<_>, _>>()
4714 .unwrap();
4715 assert_eq!(
4716 storage_entries1.len(),
4717 1,
4718 "Storage address1 should have 1 entry after deletion"
4719 );
4720 assert_eq!(
4721 storage_entries1[0].1.nibbles.0,
4722 Nibbles::from_nibbles([0x1, 0x0]),
4723 "Remaining entry should be [0x1, 0x0]"
4724 );
4725
4726 let storage_entries2: Vec<_> = storage_cursor
4728 .walk_dup(Some(storage_address2), None)
4729 .unwrap()
4730 .collect::<Result<Vec<_>, _>>()
4731 .unwrap();
4732 assert_eq!(storage_entries2.len(), 0, "Storage address2 should be empty after removal");
4733
4734 provider_rw.commit().unwrap();
4735 }
4736
4737 #[test]
4738 fn test_save_blocks_only_masks_trie_with_deferred_blocks() {
4739 use reth_trie::{
4740 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
4741 BranchNodeCompact, HashedPostStateSorted, HashedStorageSorted,
4742 };
4743
4744 fn branch(mask: u16) -> BranchNodeCompact {
4745 BranchNodeCompact::new(mask, 0, 0, vec![], None)
4746 }
4747
4748 let factory = create_test_provider_factory();
4749 factory.set_storage_settings_cache(StorageSettings::v1());
4750
4751 let mut test_block_builder = TestBlockBuilder::eth().with_state();
4752 let genesis = test_block_builder.get_executed_blocks(0..1).next().unwrap();
4753 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..3).collect();
4754
4755 let provider_rw = factory.provider_rw().unwrap();
4756 save_genesis(&provider_rw, &genesis).unwrap();
4757 provider_rw.commit().unwrap();
4758
4759 let kept_account = B256::with_last_byte(0x11);
4760 let masked_account = B256::with_last_byte(0x12);
4761 let kept_storage = B256::with_last_byte(0x21);
4762 let masked_storage = B256::with_last_byte(0x22);
4763 let kept_slot = B256::with_last_byte(0x31);
4764 let masked_slot = B256::with_last_byte(0x32);
4765 let kept_account_node = Nibbles::from_nibbles([0x1, 0x2]);
4766 let masked_account_node = Nibbles::from_nibbles([0x1, 0x3]);
4767 let kept_storage_node = Nibbles::from_nibbles([0x2, 0x1]);
4768 let masked_storage_node = Nibbles::from_nibbles([0x2, 0x2]);
4769 let full_persist_base = &blocks[0];
4770 let deferred_trie_base = &blocks[1];
4771
4772 let full_persist_hashed_state = HashedPostStateSorted::new(
4773 vec![
4774 (kept_account, Some(Account::default())),
4775 (masked_account, Some(Account { nonce: 1, ..Default::default() })),
4776 ],
4777 B256Map::from_iter([
4778 (kept_storage, HashedStorageSorted { storage_slots: vec![(kept_slot, U256::ONE)] }),
4779 (
4780 masked_storage,
4781 HashedStorageSorted { storage_slots: vec![(masked_slot, U256::from(2))] },
4782 ),
4783 ]),
4784 );
4785 let full_persist_trie_updates = TrieUpdatesSorted::new(
4786 vec![
4787 (kept_account_node, Some(branch(0b0000_1111_0000_1111))),
4788 (masked_account_node, Some(branch(0b1111_0000_1111_0000))),
4789 ],
4790 B256Map::from_iter([
4791 (
4792 kept_storage,
4793 StorageTrieUpdatesSorted {
4794 storage_nodes: vec![(kept_storage_node, Some(branch(0b1010)))],
4795 },
4796 ),
4797 (
4798 masked_storage,
4799 StorageTrieUpdatesSorted {
4800 storage_nodes: vec![(masked_storage_node, Some(branch(0b0101)))],
4801 },
4802 ),
4803 ]),
4804 );
4805
4806 let full_persist_block = ExecutedBlock::new(
4807 Arc::clone(&full_persist_base.recovered_block),
4808 Arc::clone(&full_persist_base.execution_output),
4809 SortedTrieData::new(
4810 Arc::new(full_persist_hashed_state),
4811 Arc::new(full_persist_trie_updates),
4812 ),
4813 );
4814
4815 let deferred_trie_hashed_state = HashedPostStateSorted::new(
4816 vec![(masked_account, Some(Account { nonce: 3, ..Default::default() }))],
4817 B256Map::from_iter([(
4818 masked_storage,
4819 HashedStorageSorted { storage_slots: vec![(masked_slot, U256::from(4))] },
4820 )]),
4821 );
4822 let deferred_trie_updates = TrieUpdatesSorted::new(
4823 vec![(masked_account_node, Some(branch(0b0011_0011)))],
4824 B256Map::from_iter([(
4825 masked_storage,
4826 StorageTrieUpdatesSorted {
4827 storage_nodes: vec![(masked_storage_node, Some(branch(0b1100)))],
4828 },
4829 )]),
4830 );
4831 let deferred_trie_block = ExecutedBlock::new(
4832 Arc::clone(&deferred_trie_base.recovered_block),
4833 Arc::clone(&deferred_trie_base.execution_output),
4834 SortedTrieData::new(
4835 Arc::new(deferred_trie_hashed_state),
4836 Arc::new(deferred_trie_updates),
4837 ),
4838 );
4839
4840 let provider_rw = factory.provider_rw().unwrap();
4841 let input = SaveBlocksInput::new(
4842 vec![full_persist_block.clone(), deferred_trie_block.clone()],
4843 0,
4844 0,
4845 2,
4846 0,
4847 );
4848 provider_rw.save_blocks(&input).unwrap();
4849 provider_rw.commit().unwrap();
4850
4851 let provider_rw = factory.provider_rw().unwrap();
4852 let input =
4853 SaveBlocksInput::new(vec![full_persist_block, deferred_trie_block.clone()], 2, 0, 2, 1);
4854 assert!(input.persist_rest_blocks().is_empty());
4855 provider_rw.save_blocks(&input).unwrap();
4856 provider_rw.commit().unwrap();
4857
4858 let provider = factory.provider().unwrap();
4859 let tx = provider.tx_ref();
4860 let finish_checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
4861 assert_eq!(finish_checkpoint.block_number, 2);
4862 assert_eq!(
4863 finish_checkpoint.finish_stage_checkpoint().unwrap().partial_state_trie,
4864 Some(1)
4865 );
4866 assert!(provider.block_hash(2).unwrap().is_some());
4867
4868 let mut hashed_accounts = tx.cursor_read::<tables::HashedAccounts>().unwrap();
4869 assert!(hashed_accounts.seek_exact(kept_account).unwrap().is_some());
4870 assert!(hashed_accounts.seek_exact(masked_account).unwrap().is_none());
4871
4872 let mut hashed_storages = tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
4873 assert!(hashed_storages.seek_by_key_subkey(kept_storage, kept_slot).unwrap().is_some());
4874 assert!(hashed_storages
4875 .walk_dup(Some(masked_storage), None)
4876 .unwrap()
4877 .next()
4878 .transpose()
4879 .unwrap()
4880 .is_none());
4881
4882 let mut account_trie = tx.cursor_read::<tables::AccountsTrie>().unwrap();
4883 assert!(account_trie.seek_exact(StoredNibbles(kept_account_node)).unwrap().is_some());
4884 assert!(account_trie.seek_exact(StoredNibbles(masked_account_node)).unwrap().is_none());
4885
4886 let mut storage_trie = tx.cursor_dup_read::<tables::StoragesTrie>().unwrap();
4887 let kept_entries: Vec<_> = storage_trie
4888 .walk_dup(Some(kept_storage), None)
4889 .unwrap()
4890 .collect::<Result<Vec<_>, _>>()
4891 .unwrap();
4892 assert_eq!(kept_entries.len(), 1);
4893 assert_eq!(kept_entries[0].1.nibbles.0, kept_storage_node);
4894
4895 let masked_entries: Vec<_> = storage_trie
4896 .walk_dup(Some(masked_storage), None)
4897 .unwrap()
4898 .collect::<Result<Vec<_>, _>>()
4899 .unwrap();
4900 assert!(masked_entries.is_empty());
4901
4902 drop(storage_trie);
4903 drop(account_trie);
4904 drop(hashed_storages);
4905 drop(hashed_accounts);
4906 drop(provider);
4907
4908 let provider_rw = factory.provider_rw().unwrap();
4909 let input = SaveBlocksInput::new(vec![deferred_trie_block], 2, 1, 2, 2);
4910 assert!(input.persist_rest_blocks().is_empty());
4911 provider_rw.save_blocks(&input).unwrap();
4912 provider_rw.commit().unwrap();
4913
4914 let provider = factory.provider().unwrap();
4915 let finish_checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
4916 assert_eq!(finish_checkpoint.block_number, 2);
4917 assert!(finish_checkpoint.finish_stage_checkpoint().is_none());
4918
4919 let mut hashed_accounts =
4920 provider.tx_ref().cursor_read::<tables::HashedAccounts>().unwrap();
4921 let (_, account) = hashed_accounts.seek_exact(masked_account).unwrap().unwrap();
4922 assert_eq!(account.nonce, 3);
4923
4924 let mut hashed_storages =
4925 provider.tx_ref().cursor_dup_read::<tables::HashedStorages>().unwrap();
4926 let storage =
4927 hashed_storages.seek_by_key_subkey(masked_storage, masked_slot).unwrap().unwrap();
4928 assert_eq!(storage.value, U256::from(4));
4929
4930 let mut account_trie = provider.tx_ref().cursor_read::<tables::AccountsTrie>().unwrap();
4931 assert!(account_trie.seek_exact(StoredNibbles(masked_account_node)).unwrap().is_some());
4932
4933 let mut storage_trie = provider.tx_ref().cursor_dup_read::<tables::StoragesTrie>().unwrap();
4934 let masked_entries: Vec<_> = storage_trie
4935 .walk_dup(Some(masked_storage), None)
4936 .unwrap()
4937 .collect::<Result<Vec<_>, _>>()
4938 .unwrap();
4939 assert_eq!(masked_entries.len(), 1);
4940 assert_eq!(masked_entries[0].1.nibbles.0, masked_storage_node);
4941 }
4942
4943 #[test]
4944 fn test_save_blocks_partial_cycles_do_not_duplicate_static_file_writes() {
4945 let factory = create_test_provider_factory();
4946 let mut test_block_builder = TestBlockBuilder::eth().with_state();
4947
4948 let genesis = test_block_builder.get_executed_blocks(0..1).next().unwrap();
4949 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..5).collect();
4950
4951 let provider_rw = factory.provider_rw().unwrap();
4952 save_genesis(&provider_rw, &genesis).unwrap();
4953 provider_rw.commit().unwrap();
4954
4955 let provider_rw = factory.provider_rw().unwrap();
4956 let input = SaveBlocksInput::new(blocks[..2].to_vec(), 0, 0, 2, 2);
4957 provider_rw.save_blocks(&input).unwrap();
4958 provider_rw.commit().unwrap();
4959
4960 let provider_rw = factory.provider_rw().unwrap();
4961 let input = SaveBlocksInput::new(blocks[2..].to_vec(), 2, 2, 4, 2);
4962 provider_rw.save_blocks(&input).unwrap();
4963 provider_rw.commit().unwrap();
4964
4965 let provider_rw = factory.provider_rw().unwrap();
4966 let stale_input = SaveBlocksInput::new(vec![blocks[0].clone()], 0, 0, 1, 1);
4967 let err = provider_rw.save_blocks(&stale_input).unwrap_err();
4968 assert!(err.to_string().contains("persistence frontiers do not match Finish checkpoint"));
4969 drop(provider_rw);
4970
4971 let provider = factory.provider().unwrap();
4972 let finish_checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
4973 assert_eq!(finish_checkpoint.block_number, 4);
4974 assert_eq!(
4975 finish_checkpoint.finish_stage_checkpoint().unwrap().partial_state_trie,
4976 Some(2)
4977 );
4978
4979 let static_files = factory.static_file_provider();
4980 assert_eq!(static_files.get_highest_static_file_block(StaticFileSegment::Headers), Some(4));
4981 assert_eq!(
4982 static_files.get_highest_static_file_block(StaticFileSegment::Transactions),
4983 Some(4)
4984 );
4985 assert_eq!(
4986 static_files.get_highest_static_file_block(StaticFileSegment::Receipts),
4987 Some(4)
4988 );
4989 }
4990
4991 #[test]
4992 fn remove_block_and_execution_above_returns_persistence_frontiers() {
4993 let factory = create_test_provider_factory();
4994 let mut test_block_builder = TestBlockBuilder::eth().with_state();
4995
4996 let genesis = test_block_builder.get_executed_blocks(0..1).next().unwrap();
4997 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..5).collect();
4998
4999 let provider_rw = factory.provider_rw().unwrap();
5000 save_genesis(&provider_rw, &genesis).unwrap();
5001 provider_rw.commit().unwrap();
5002
5003 for block in &blocks[2..] {
5004 factory.overlay_manager().insert_block(block.clone());
5005 }
5006
5007 let provider_rw = factory.provider_rw().unwrap();
5008 let input = SaveBlocksInput::new(blocks, 0, 0, 4, 2);
5009 provider_rw.save_blocks(&input).unwrap();
5010 provider_rw.commit().unwrap();
5011
5012 let provider_rw = factory.provider_rw().unwrap();
5013 let frontiers = provider_rw.remove_block_and_execution_above(3).unwrap();
5014 assert_eq!(frontiers, PersistenceFrontiers { db_tip: 3, partial_state_trie: 2 });
5015 provider_rw.commit().unwrap();
5016
5017 let provider = factory.provider().unwrap();
5018 let checkpoint = provider.get_stage_checkpoint(StageId::Finish).unwrap().unwrap();
5019 assert_eq!(
5020 checkpoint.finish_stage_checkpoint().and_then(|finish| finish.partial_state_trie()),
5021 Some(2)
5022 );
5023 }
5024
5025 #[test]
5026 fn test_prunable_receipts_logic() {
5027 let insert_blocks =
5028 |provider_rw: &DatabaseProviderRW<_, _>, tip_block: u64, tx_count: u8| {
5029 let mut rng = generators::rng();
5030 for block_num in 0..=tip_block {
5031 let block = random_block(
5032 &mut rng,
5033 block_num,
5034 BlockParams { tx_count: Some(tx_count), ..Default::default() },
5035 );
5036 provider_rw.insert_block(&block.try_recover().unwrap()).unwrap();
5037 }
5038 };
5039
5040 let write_receipts = |provider_rw: DatabaseProviderRW<_, _>, block: u64| {
5041 let outcome = ExecutionOutcome {
5042 first_block: block,
5043 receipts: vec![vec![Receipt {
5044 tx_type: Default::default(),
5045 success: true,
5046 cumulative_gas_used: block, logs: vec![],
5048 }]],
5049 ..Default::default()
5050 };
5051 provider_rw
5052 .write_state(&outcome, crate::OriginalValuesKnown::No, StateWriteConfig::default())
5053 .unwrap();
5054 provider_rw.commit().unwrap();
5055 };
5056
5057 {
5059 let factory = create_test_provider_factory();
5060 let storage_settings = StorageSettings::v1();
5061 factory.set_storage_settings_cache(storage_settings);
5062 let factory = factory.with_prune_modes(PruneModes {
5063 receipts: Some(PruneMode::Before(100)),
5064 ..Default::default()
5065 });
5066
5067 let tip_block = 200u64;
5068 let first_block = 1u64;
5069
5070 let provider_rw = factory.provider_rw().unwrap();
5072 insert_blocks(&provider_rw, tip_block, 1);
5073 provider_rw.commit().unwrap();
5074
5075 write_receipts(
5076 factory.provider_rw().unwrap().with_minimum_pruning_distance(100),
5077 first_block,
5078 );
5079 write_receipts(
5080 factory.provider_rw().unwrap().with_minimum_pruning_distance(100),
5081 tip_block - 1,
5082 );
5083
5084 let provider = factory.provider().unwrap();
5085
5086 assert!(provider.receipts_by_block(0.into()).unwrap().is_none());
5087 assert!(provider
5088 .receipts_by_block((tip_block - 1).into())
5089 .unwrap()
5090 .is_some_and(|r| r.len() == 1));
5091 }
5092
5093 {
5095 let factory = create_test_provider_factory();
5096 let storage_settings = StorageSettings::v2();
5097 factory.set_storage_settings_cache(storage_settings);
5098 let factory = factory.with_prune_modes(PruneModes {
5099 receipts: Some(PruneMode::Before(2)),
5100 ..Default::default()
5101 });
5102
5103 let tip_block = 200u64;
5104
5105 let provider_rw = factory.provider_rw().unwrap();
5107 insert_blocks(&provider_rw, tip_block, 1);
5108 provider_rw.commit().unwrap();
5109
5110 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 0);
5112 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 1);
5113
5114 assert!(factory
5115 .static_file_provider()
5116 .get_highest_static_file_tx(StaticFileSegment::Receipts)
5117 .is_none(),);
5118 assert!(factory
5119 .static_file_provider()
5120 .get_highest_static_file_block(StaticFileSegment::Receipts)
5121 .is_some_and(|b| b == 1),);
5122
5123 write_receipts(factory.provider_rw().unwrap().with_minimum_pruning_distance(100), 2);
5126 assert!(factory
5127 .static_file_provider()
5128 .get_highest_static_file_tx(StaticFileSegment::Receipts)
5129 .is_some_and(|num| num == 2),);
5130
5131 let factory = factory.with_prune_modes(PruneModes {
5135 receipts: Some(PruneMode::Before(100)),
5136 ..Default::default()
5137 });
5138 let provider_rw = factory.provider_rw().unwrap().with_minimum_pruning_distance(1);
5139 assert!(PruneMode::Distance(1).should_prune(3, tip_block));
5140 write_receipts(provider_rw, 3);
5141
5142 let provider = factory.provider().unwrap();
5147 assert!(EitherWriter::receipts_destination(&provider).is_static_file());
5148 for (num, has_receipt) in [(0, false), (1, false), (2, true), (3, true)] {
5149 let receipts = provider.receipts_by_block(num.into()).unwrap();
5150 if has_receipt {
5151 assert!(receipts.is_some_and(|r| r.len() == 1));
5152 } else {
5153 assert!(receipts.is_none());
5154 }
5155
5156 let receipt = provider.receipt(num).unwrap();
5157 if has_receipt {
5158 assert!(receipt.is_some_and(|r| r.cumulative_gas_used == num));
5159 } else {
5160 assert!(receipt.is_none());
5161 }
5162 }
5163 }
5164 }
5165
5166 #[test]
5167 fn test_unwind_storage_hashing_with_hashed_state() {
5168 let factory = create_test_provider_factory();
5169 let storage_settings = StorageSettings::v2();
5170 factory.set_storage_settings_cache(storage_settings);
5171
5172 let address = Address::random();
5173 let hashed_address = keccak256(address);
5174
5175 let plain_slot = B256::random();
5176 let hashed_slot = keccak256(plain_slot);
5177
5178 let current_value = U256::from(100);
5179 let old_value = U256::from(42);
5180
5181 let provider_rw = factory.provider_rw().unwrap();
5182 provider_rw
5183 .tx
5184 .cursor_dup_write::<tables::HashedStorages>()
5185 .unwrap()
5186 .upsert(hashed_address, &StorageEntry { key: hashed_slot, value: current_value })
5187 .unwrap();
5188
5189 let changesets = vec![(
5190 BlockNumberAddress((1, address)),
5191 StorageEntry { key: plain_slot, value: old_value },
5192 )];
5193
5194 let result = provider_rw.unwind_storage_hashing(changesets.into_iter()).unwrap();
5195
5196 assert_eq!(result.len(), 1);
5197 assert!(result.contains_key(&hashed_address));
5198 assert!(result[&hashed_address].contains(&hashed_slot));
5199
5200 let mut cursor = provider_rw.tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
5201 let entry = cursor
5202 .seek_by_key_subkey(hashed_address, hashed_slot)
5203 .unwrap()
5204 .expect("entry should exist");
5205 assert_eq!(entry.key, hashed_slot);
5206 assert_eq!(entry.value, old_value);
5207 }
5208
5209 #[test]
5210 fn test_write_and_remove_state_roundtrip_legacy() {
5211 let factory = create_test_provider_factory();
5212 let storage_settings = StorageSettings::v1();
5213 assert!(!storage_settings.use_hashed_state());
5214 factory.set_storage_settings_cache(storage_settings);
5215
5216 let address = Address::with_last_byte(1);
5217 let hashed_address = keccak256(address);
5218 let slot = U256::from(5);
5219 let slot_key = B256::from(slot);
5220 let hashed_slot = keccak256(slot_key);
5221
5222 let mut rng = generators::rng();
5223 let block0 =
5224 random_block(&mut rng, 0, BlockParams { tx_count: Some(0), ..Default::default() });
5225 let block1 =
5226 random_block(&mut rng, 1, BlockParams { tx_count: Some(0), ..Default::default() });
5227
5228 {
5229 let provider_rw = factory.provider_rw().unwrap();
5230 provider_rw.insert_block(&block0.try_recover().unwrap()).unwrap();
5231 provider_rw.insert_block(&block1.try_recover().unwrap()).unwrap();
5232 provider_rw
5233 .tx
5234 .cursor_write::<tables::PlainAccountState>()
5235 .unwrap()
5236 .upsert(address, &Account::default())
5237 .unwrap();
5238 provider_rw.commit().unwrap();
5239 }
5240
5241 let provider_rw = factory.provider_rw().unwrap();
5242
5243 let mut state_init: BundleStateInit = AddressMap::default();
5244 let mut storage_map: B256Map<(U256, U256)> = B256Map::default();
5245 storage_map.insert(slot_key, (U256::ZERO, U256::from(10)));
5246 state_init.insert(
5247 address,
5248 (
5249 Some(Account::default()),
5250 Some(Account { nonce: 1, ..Default::default() }),
5251 storage_map,
5252 ),
5253 );
5254
5255 let mut reverts_init: RevertsInit = HashMap::default();
5256 let mut block_reverts: AddressMap<AccountRevertInit> = AddressMap::default();
5257 block_reverts.insert(
5258 address,
5259 (
5260 Some(Some(Account::default())),
5261 vec![StorageEntry { key: slot_key, value: U256::ZERO }],
5262 ),
5263 );
5264 reverts_init.insert(1, block_reverts);
5265
5266 let execution_outcome =
5267 ExecutionOutcome::new_init(state_init, reverts_init, [], vec![vec![]], 1, vec![]);
5268
5269 provider_rw
5270 .write_state(
5271 &execution_outcome,
5272 OriginalValuesKnown::Yes,
5273 StateWriteConfig {
5274 write_receipts: false,
5275 write_account_changesets: true,
5276 write_storage_changesets: true,
5277 },
5278 )
5279 .unwrap();
5280
5281 let hashed_state =
5282 execution_outcome.hash_state_slow::<reth_trie::KeccakKeyHasher>().into_sorted();
5283 provider_rw.write_hashed_state(&hashed_state).unwrap();
5284
5285 let account = provider_rw
5286 .tx
5287 .cursor_read::<tables::PlainAccountState>()
5288 .unwrap()
5289 .seek_exact(address)
5290 .unwrap()
5291 .unwrap()
5292 .1;
5293 assert_eq!(account.nonce, 1);
5294
5295 let storage_entry = provider_rw
5296 .tx
5297 .cursor_dup_read::<tables::PlainStorageState>()
5298 .unwrap()
5299 .seek_by_key_subkey(address, slot_key)
5300 .unwrap()
5301 .unwrap();
5302 assert_eq!(storage_entry.key, slot_key);
5303 assert_eq!(storage_entry.value, U256::from(10));
5304
5305 let hashed_entry = provider_rw
5306 .tx
5307 .cursor_dup_read::<tables::HashedStorages>()
5308 .unwrap()
5309 .seek_by_key_subkey(hashed_address, hashed_slot)
5310 .unwrap()
5311 .unwrap();
5312 assert_eq!(hashed_entry.key, hashed_slot);
5313 assert_eq!(hashed_entry.value, U256::from(10));
5314
5315 let account_cs_entries = provider_rw
5316 .tx
5317 .cursor_dup_read::<tables::AccountChangeSets>()
5318 .unwrap()
5319 .walk(Some(1))
5320 .unwrap()
5321 .collect::<Result<Vec<_>, _>>()
5322 .unwrap();
5323 assert!(!account_cs_entries.is_empty());
5324
5325 let storage_cs_entries = provider_rw
5326 .tx
5327 .cursor_read::<tables::StorageChangeSets>()
5328 .unwrap()
5329 .walk(Some(BlockNumberAddress((1, address))))
5330 .unwrap()
5331 .collect::<Result<Vec<_>, _>>()
5332 .unwrap();
5333 assert!(!storage_cs_entries.is_empty());
5334 assert_eq!(storage_cs_entries[0].1.key, slot_key);
5335
5336 provider_rw.remove_state_above(0).unwrap();
5337
5338 let restored_account = provider_rw
5339 .tx
5340 .cursor_read::<tables::PlainAccountState>()
5341 .unwrap()
5342 .seek_exact(address)
5343 .unwrap()
5344 .unwrap()
5345 .1;
5346 assert_eq!(restored_account.nonce, 0);
5347
5348 let storage_gone = provider_rw
5349 .tx
5350 .cursor_dup_read::<tables::PlainStorageState>()
5351 .unwrap()
5352 .seek_by_key_subkey(address, slot_key)
5353 .unwrap();
5354 assert!(storage_gone.is_none() || storage_gone.unwrap().key != slot_key);
5355
5356 let account_cs_after = provider_rw
5357 .tx
5358 .cursor_dup_read::<tables::AccountChangeSets>()
5359 .unwrap()
5360 .walk(Some(1))
5361 .unwrap()
5362 .collect::<Result<Vec<_>, _>>()
5363 .unwrap();
5364 assert!(account_cs_after.is_empty());
5365
5366 let storage_cs_after = provider_rw
5367 .tx
5368 .cursor_read::<tables::StorageChangeSets>()
5369 .unwrap()
5370 .walk(Some(BlockNumberAddress((1, address))))
5371 .unwrap()
5372 .collect::<Result<Vec<_>, _>>()
5373 .unwrap();
5374 assert!(storage_cs_after.is_empty());
5375 }
5376
5377 #[test]
5378 fn test_unwind_storage_hashing_legacy() {
5379 let factory = create_test_provider_factory();
5380 let storage_settings = StorageSettings::v1();
5381 assert!(!storage_settings.use_hashed_state());
5382 factory.set_storage_settings_cache(storage_settings);
5383
5384 let address = Address::random();
5385 let hashed_address = keccak256(address);
5386
5387 let plain_slot = B256::random();
5388 let hashed_slot = keccak256(plain_slot);
5389
5390 let current_value = U256::from(100);
5391 let old_value = U256::from(42);
5392
5393 let provider_rw = factory.provider_rw().unwrap();
5394 provider_rw
5395 .tx
5396 .cursor_dup_write::<tables::HashedStorages>()
5397 .unwrap()
5398 .upsert(hashed_address, &StorageEntry { key: hashed_slot, value: current_value })
5399 .unwrap();
5400
5401 let changesets = vec![(
5402 BlockNumberAddress((1, address)),
5403 StorageEntry { key: plain_slot, value: old_value },
5404 )];
5405
5406 let result = provider_rw.unwind_storage_hashing(changesets.into_iter()).unwrap();
5407
5408 assert_eq!(result.len(), 1);
5409 assert!(result.contains_key(&hashed_address));
5410 assert!(result[&hashed_address].contains(&hashed_slot));
5411
5412 let mut cursor = provider_rw.tx.cursor_dup_read::<tables::HashedStorages>().unwrap();
5413 let entry = cursor
5414 .seek_by_key_subkey(hashed_address, hashed_slot)
5415 .unwrap()
5416 .expect("entry should exist");
5417 assert_eq!(entry.key, hashed_slot);
5418 assert_eq!(entry.value, old_value);
5419 }
5420
5421 #[test]
5422 fn test_write_state_hashed() {
5423 use reth_trie::{HashedPostState, KeccakKeyHasher};
5424 use revm::{database::BundleState, state::AccountInfo};
5425
5426 let factory = create_test_provider_factory();
5427 factory.set_storage_settings_cache(StorageSettings::v2());
5428
5429 let address = Address::with_last_byte(1);
5430 let slot = U256::from(5);
5431 let slot_key = B256::from(slot);
5432 let hashed_address = keccak256(address);
5433 let hashed_slot = keccak256(slot_key);
5434
5435 {
5436 let sf = factory.static_file_provider();
5437 let mut hw = sf.latest_writer(StaticFileSegment::Headers).unwrap();
5438 let h0 = alloy_consensus::Header { number: 0, ..Default::default() };
5439 hw.append_header(&h0, &B256::ZERO).unwrap();
5440 let h1 = alloy_consensus::Header { number: 1, ..Default::default() };
5441 hw.append_header(&h1, &B256::ZERO).unwrap();
5442 hw.commit().unwrap();
5443
5444 let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
5445 aw.append_account_changeset(vec![], 0).unwrap();
5446 aw.commit().unwrap();
5447
5448 let mut sw = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
5449 sw.append_storage_changeset(vec![], 0).unwrap();
5450 sw.commit().unwrap();
5451 }
5452
5453 let provider_rw = factory.provider_rw().unwrap();
5454
5455 let bundle = BundleState::builder(1..=1)
5456 .state_present_account_info(
5457 address,
5458 AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() },
5459 )
5460 .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
5461 .revert_account_info(1, address, Some(None))
5462 .revert_storage(1, address, vec![(slot, U256::ZERO)])
5463 .build();
5464
5465 let execution_outcome = ExecutionOutcome::new(bundle.clone(), vec![vec![]], 1, Vec::new());
5466
5467 provider_rw
5468 .tx
5469 .put::<tables::BlockBodyIndices>(
5470 1,
5471 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5472 )
5473 .unwrap();
5474
5475 provider_rw
5476 .write_state(
5477 &execution_outcome,
5478 OriginalValuesKnown::Yes,
5479 StateWriteConfig {
5480 write_receipts: false,
5481 write_account_changesets: true,
5482 write_storage_changesets: true,
5483 },
5484 )
5485 .unwrap();
5486
5487 let hashed_state =
5488 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5489 provider_rw.write_hashed_state(&hashed_state).unwrap();
5490
5491 let plain_storage_entries = provider_rw
5492 .tx
5493 .cursor_dup_read::<tables::PlainStorageState>()
5494 .unwrap()
5495 .walk(None)
5496 .unwrap()
5497 .collect::<Result<Vec<_>, _>>()
5498 .unwrap();
5499 assert!(plain_storage_entries.is_empty());
5500
5501 let hashed_entry = provider_rw
5502 .tx
5503 .cursor_dup_read::<tables::HashedStorages>()
5504 .unwrap()
5505 .seek_by_key_subkey(hashed_address, hashed_slot)
5506 .unwrap()
5507 .unwrap();
5508 assert_eq!(hashed_entry.key, hashed_slot);
5509 assert_eq!(hashed_entry.value, U256::from(10));
5510
5511 provider_rw.static_file_provider().commit().unwrap();
5512
5513 let sf = factory.static_file_provider();
5514 let storage_cs = sf.storage_changeset(1).unwrap();
5515 assert!(!storage_cs.is_empty());
5516 assert_eq!(storage_cs[0].1.key, slot_key);
5517
5518 let account_cs = sf.account_block_changeset(1).unwrap();
5519 assert!(!account_cs.is_empty());
5520 assert_eq!(account_cs[0].address, address);
5521 }
5522
5523 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5524 enum StorageMode {
5525 V1,
5526 V2,
5527 }
5528
5529 fn run_save_blocks_and_verify(mode: StorageMode) {
5530 use alloy_primitives::map::{FbBuildHasher, HashMap};
5531
5532 let factory = create_test_provider_factory();
5533
5534 match mode {
5535 StorageMode::V1 => factory.set_storage_settings_cache(StorageSettings::v1()),
5536 StorageMode::V2 => factory.set_storage_settings_cache(StorageSettings::v2()),
5537 }
5538
5539 let num_blocks = 3u64;
5540 let accounts_per_block = 5usize;
5541 let slots_per_account = 3usize;
5542
5543 let genesis = SealedBlock::<reth_ethereum_primitives::Block>::from_sealed_parts(
5544 SealedHeader::new(
5545 Header { number: 0, difficulty: U256::ONE, ..Default::default() },
5546 B256::ZERO,
5547 ),
5548 Default::default(),
5549 );
5550
5551 let genesis_executed: ExecutedBlock = ExecutedBlock::new(
5552 Arc::new(genesis.try_recover().unwrap()),
5553 Arc::new(BlockExecutionOutput {
5554 result: BlockExecutionResult {
5555 receipts: vec![],
5556 requests: Default::default(),
5557 gas_used: 0,
5558 blob_gas_used: 0,
5559 },
5560 state: Default::default(),
5561 }),
5562 SortedTrieData::default(),
5563 );
5564 let provider_rw = factory.provider_rw().unwrap();
5565 save_genesis(&provider_rw, &genesis_executed).unwrap();
5566 provider_rw.commit().unwrap();
5567
5568 let mut blocks: Vec<ExecutedBlock> = Vec::new();
5569 let mut parent_hash = B256::ZERO;
5570
5571 for block_num in 1..=num_blocks {
5572 let mut builder = BundleState::builder(block_num..=block_num);
5573
5574 for acct_idx in 0..accounts_per_block {
5575 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5576 let info = AccountInfo {
5577 nonce: block_num,
5578 balance: U256::from(block_num * 100 + acct_idx as u64),
5579 ..Default::default()
5580 };
5581
5582 let storage: HashMap<U256, (U256, U256), FbBuildHasher<32>> = (1..=
5583 slots_per_account as u64)
5584 .map(|s| {
5585 (
5586 U256::from(s + acct_idx as u64 * 100),
5587 (U256::ZERO, U256::from(block_num * 1000 + s)),
5588 )
5589 })
5590 .collect();
5591
5592 let revert_storage: Vec<(U256, U256)> = (1..=slots_per_account as u64)
5593 .map(|s| (U256::from(s + acct_idx as u64 * 100), U256::ZERO))
5594 .collect();
5595
5596 builder = builder
5597 .state_present_account_info(address, info)
5598 .revert_account_info(block_num, address, Some(None))
5599 .state_storage(address, storage)
5600 .revert_storage(block_num, address, revert_storage);
5601 }
5602
5603 let bundle = builder.build();
5604
5605 let hashed_state =
5606 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5607
5608 let header = Header {
5609 number: block_num,
5610 parent_hash,
5611 difficulty: U256::ONE,
5612 ..Default::default()
5613 };
5614 let block = SealedBlock::<reth_ethereum_primitives::Block>::seal_parts(
5615 header,
5616 Default::default(),
5617 );
5618 parent_hash = block.hash();
5619
5620 let executed = ExecutedBlock::new(
5621 Arc::new(block.try_recover().unwrap()),
5622 Arc::new(BlockExecutionOutput {
5623 result: BlockExecutionResult {
5624 receipts: vec![],
5625 requests: Default::default(),
5626 gas_used: 0,
5627 blob_gas_used: 0,
5628 },
5629 state: bundle,
5630 }),
5631 SortedTrieData::new(Arc::new(hashed_state), Default::default()),
5632 );
5633 blocks.push(executed);
5634 }
5635
5636 let provider_rw = factory.provider_rw().unwrap();
5637 let input = SaveBlocksInput::new(blocks, 0, 0, num_blocks, num_blocks);
5638 provider_rw.save_blocks(&input).unwrap();
5639 provider_rw.commit().unwrap();
5640
5641 let provider = factory.provider().unwrap();
5642
5643 for block_num in 1..=num_blocks {
5644 for acct_idx in 0..accounts_per_block {
5645 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5646 let hashed_address = keccak256(address);
5647
5648 let ha_entry = provider
5649 .tx_ref()
5650 .cursor_read::<tables::HashedAccounts>()
5651 .unwrap()
5652 .seek_exact(hashed_address)
5653 .unwrap();
5654 assert!(
5655 ha_entry.is_some(),
5656 "HashedAccounts missing for block {block_num} acct {acct_idx}"
5657 );
5658
5659 for s in 1..=slots_per_account as u64 {
5660 let slot = U256::from(s + acct_idx as u64 * 100);
5661 let slot_key = B256::from(slot);
5662 let hashed_slot = keccak256(slot_key);
5663
5664 let hs_entry = provider
5665 .tx_ref()
5666 .cursor_dup_read::<tables::HashedStorages>()
5667 .unwrap()
5668 .seek_by_key_subkey(hashed_address, hashed_slot)
5669 .unwrap();
5670 assert!(
5671 hs_entry.is_some(),
5672 "HashedStorages missing for block {block_num} acct {acct_idx} slot {s}"
5673 );
5674 let entry = hs_entry.unwrap();
5675 assert_eq!(entry.key, hashed_slot);
5676 assert_eq!(entry.value, U256::from(block_num * 1000 + s));
5677 }
5678 }
5679 }
5680
5681 for block_num in 1..=num_blocks {
5682 let header = provider.header_by_number(block_num).unwrap();
5683 assert!(header.is_some(), "Header missing for block {block_num}");
5684
5685 let indices = provider.block_body_indices(block_num).unwrap();
5686 assert!(indices.is_some(), "BlockBodyIndices missing for block {block_num}");
5687 }
5688
5689 let plain_accounts = provider.tx_ref().entries::<tables::PlainAccountState>().unwrap();
5690 let plain_storage = provider.tx_ref().entries::<tables::PlainStorageState>().unwrap();
5691
5692 if mode == StorageMode::V2 {
5693 assert_eq!(plain_accounts, 0, "v2: PlainAccountState should be empty");
5694 assert_eq!(plain_storage, 0, "v2: PlainStorageState should be empty");
5695
5696 let mdbx_account_cs = provider.tx_ref().entries::<tables::AccountChangeSets>().unwrap();
5697 assert_eq!(mdbx_account_cs, 0, "v2: AccountChangeSets in MDBX should be empty");
5698
5699 let mdbx_storage_cs = provider.tx_ref().entries::<tables::StorageChangeSets>().unwrap();
5700 assert_eq!(mdbx_storage_cs, 0, "v2: StorageChangeSets in MDBX should be empty");
5701
5702 provider.static_file_provider().commit().unwrap();
5703 let sf = factory.static_file_provider();
5704
5705 for block_num in 1..=num_blocks {
5706 let account_cs = sf.account_block_changeset(block_num).unwrap();
5707 assert!(
5708 !account_cs.is_empty(),
5709 "v2: static file AccountChangeSets should exist for block {block_num}"
5710 );
5711
5712 let storage_cs = sf.storage_changeset(block_num).unwrap();
5713 assert!(
5714 !storage_cs.is_empty(),
5715 "v2: static file StorageChangeSets should exist for block {block_num}"
5716 );
5717
5718 for (_, entry) in &storage_cs {
5719 assert!(
5720 entry.key != keccak256(entry.key),
5721 "v2: static file storage changeset should have plain slot keys"
5722 );
5723 }
5724 }
5725
5726 let rocksdb = factory.rocksdb_provider();
5727 for block_num in 1..=num_blocks {
5728 for acct_idx in 0..accounts_per_block {
5729 let address = Address::with_last_byte((block_num * 10 + acct_idx as u64) as u8);
5730 let shards = rocksdb.account_history_shards(address).unwrap();
5731 assert!(
5732 !shards.is_empty(),
5733 "v2: RocksDB AccountsHistory missing for block {block_num} acct {acct_idx}"
5734 );
5735
5736 for s in 1..=slots_per_account as u64 {
5737 let slot = U256::from(s + acct_idx as u64 * 100);
5738 let slot_key = B256::from(slot);
5739 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5740 assert!(
5741 !shards.is_empty(),
5742 "v2: RocksDB StoragesHistory missing for block {block_num} acct {acct_idx} slot {s}"
5743 );
5744 }
5745 }
5746 }
5747 } else {
5748 assert!(plain_accounts > 0, "v1: PlainAccountState should not be empty");
5749 assert!(plain_storage > 0, "v1: PlainStorageState should not be empty");
5750
5751 let mdbx_account_cs = provider.tx_ref().entries::<tables::AccountChangeSets>().unwrap();
5752 assert!(mdbx_account_cs > 0, "v1: AccountChangeSets in MDBX should not be empty");
5753
5754 let mdbx_storage_cs = provider.tx_ref().entries::<tables::StorageChangeSets>().unwrap();
5755 assert!(mdbx_storage_cs > 0, "v1: StorageChangeSets in MDBX should not be empty");
5756
5757 for block_num in 1..=num_blocks {
5758 let storage_entries: Vec<_> = provider
5759 .tx_ref()
5760 .cursor_dup_read::<tables::StorageChangeSets>()
5761 .unwrap()
5762 .walk_range(BlockNumberAddress::range(block_num..=block_num))
5763 .unwrap()
5764 .collect::<Result<Vec<_>, _>>()
5765 .unwrap();
5766 assert!(
5767 !storage_entries.is_empty(),
5768 "v1: MDBX StorageChangeSets should have entries for block {block_num}"
5769 );
5770
5771 for (_, entry) in &storage_entries {
5772 let slot_key = B256::from(entry.key);
5773 assert!(
5774 slot_key != keccak256(slot_key),
5775 "v1: storage changeset keys should be plain (not hashed)"
5776 );
5777 }
5778 }
5779
5780 let mdbx_account_history =
5781 provider.tx_ref().entries::<tables::AccountsHistory>().unwrap();
5782 assert!(mdbx_account_history > 0, "v1: AccountsHistory in MDBX should not be empty");
5783
5784 let mdbx_storage_history =
5785 provider.tx_ref().entries::<tables::StoragesHistory>().unwrap();
5786 assert!(mdbx_storage_history > 0, "v1: StoragesHistory in MDBX should not be empty");
5787 }
5788 }
5789
5790 #[test]
5791 fn test_save_blocks_v1_table_assertions() {
5792 run_save_blocks_and_verify(StorageMode::V1);
5793 }
5794
5795 #[test]
5796 fn test_save_blocks_v2_table_assertions() {
5797 run_save_blocks_and_verify(StorageMode::V2);
5798 }
5799
5800 #[test]
5801 fn test_write_and_remove_state_roundtrip_v2() {
5802 let factory = create_test_provider_factory();
5803 let storage_settings = StorageSettings::v2();
5804 assert!(storage_settings.use_hashed_state());
5805 factory.set_storage_settings_cache(storage_settings);
5806
5807 let address = Address::with_last_byte(1);
5808 let hashed_address = keccak256(address);
5809 let slot = U256::from(5);
5810 let slot_key = B256::from(slot);
5811 let hashed_slot = keccak256(slot_key);
5812
5813 {
5814 let sf = factory.static_file_provider();
5815 let mut hw = sf.latest_writer(StaticFileSegment::Headers).unwrap();
5816 let h0 = alloy_consensus::Header { number: 0, ..Default::default() };
5817 hw.append_header(&h0, &B256::ZERO).unwrap();
5818 let h1 = alloy_consensus::Header { number: 1, ..Default::default() };
5819 hw.append_header(&h1, &B256::ZERO).unwrap();
5820 hw.commit().unwrap();
5821
5822 let mut aw = sf.latest_writer(StaticFileSegment::AccountChangeSets).unwrap();
5823 aw.append_account_changeset(vec![], 0).unwrap();
5824 aw.commit().unwrap();
5825
5826 let mut sw = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
5827 sw.append_storage_changeset(vec![], 0).unwrap();
5828 sw.commit().unwrap();
5829 }
5830
5831 {
5832 let provider_rw = factory.provider_rw().unwrap();
5833 provider_rw
5834 .tx
5835 .put::<tables::BlockBodyIndices>(
5836 0,
5837 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5838 )
5839 .unwrap();
5840 provider_rw
5841 .tx
5842 .put::<tables::BlockBodyIndices>(
5843 1,
5844 StoredBlockBodyIndices { first_tx_num: 0, tx_count: 0 },
5845 )
5846 .unwrap();
5847 provider_rw
5848 .tx
5849 .cursor_write::<tables::HashedAccounts>()
5850 .unwrap()
5851 .upsert(hashed_address, &Account::default())
5852 .unwrap();
5853 provider_rw.commit().unwrap();
5854 }
5855
5856 let provider_rw = factory.provider_rw().unwrap();
5857
5858 let bundle = BundleState::builder(1..=1)
5859 .state_present_account_info(
5860 address,
5861 AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() },
5862 )
5863 .state_storage(address, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
5864 .revert_account_info(1, address, Some(None))
5865 .revert_storage(1, address, vec![(slot, U256::ZERO)])
5866 .build();
5867
5868 let execution_outcome = ExecutionOutcome::new(bundle.clone(), vec![vec![]], 1, Vec::new());
5869
5870 provider_rw
5871 .write_state(
5872 &execution_outcome,
5873 OriginalValuesKnown::Yes,
5874 StateWriteConfig {
5875 write_receipts: false,
5876 write_account_changesets: true,
5877 write_storage_changesets: true,
5878 },
5879 )
5880 .unwrap();
5881
5882 let hashed_state =
5883 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle.state()).into_sorted();
5884 provider_rw.write_hashed_state(&hashed_state).unwrap();
5885
5886 let hashed_account = provider_rw
5887 .tx
5888 .cursor_read::<tables::HashedAccounts>()
5889 .unwrap()
5890 .seek_exact(hashed_address)
5891 .unwrap()
5892 .unwrap()
5893 .1;
5894 assert_eq!(hashed_account.nonce, 1);
5895
5896 let hashed_entry = provider_rw
5897 .tx
5898 .cursor_dup_read::<tables::HashedStorages>()
5899 .unwrap()
5900 .seek_by_key_subkey(hashed_address, hashed_slot)
5901 .unwrap()
5902 .unwrap();
5903 assert_eq!(hashed_entry.key, hashed_slot);
5904 assert_eq!(hashed_entry.value, U256::from(10));
5905
5906 let plain_accounts = provider_rw.tx.entries::<tables::PlainAccountState>().unwrap();
5907 assert_eq!(plain_accounts, 0, "v2: PlainAccountState should be empty");
5908
5909 let plain_storage = provider_rw.tx.entries::<tables::PlainStorageState>().unwrap();
5910 assert_eq!(plain_storage, 0, "v2: PlainStorageState should be empty");
5911
5912 provider_rw.static_file_provider().commit().unwrap();
5913
5914 let sf = factory.static_file_provider();
5915 let storage_cs = sf.storage_changeset(1).unwrap();
5916 assert!(!storage_cs.is_empty(), "v2: storage changesets should be in static files");
5917 assert_eq!(storage_cs[0].1.key, slot_key, "v2: changeset key should be plain");
5918
5919 provider_rw.remove_state_above(0).unwrap();
5920
5921 let restored_account = provider_rw
5922 .tx
5923 .cursor_read::<tables::HashedAccounts>()
5924 .unwrap()
5925 .seek_exact(hashed_address)
5926 .unwrap();
5927 assert!(
5928 restored_account.is_none(),
5929 "v2: account should be removed (didn't exist before block 1)"
5930 );
5931
5932 let storage_gone = provider_rw
5933 .tx
5934 .cursor_dup_read::<tables::HashedStorages>()
5935 .unwrap()
5936 .seek_by_key_subkey(hashed_address, hashed_slot)
5937 .unwrap();
5938 assert!(
5939 storage_gone.is_none() || storage_gone.unwrap().key != hashed_slot,
5940 "v2: storage should be reverted (removed or different key)"
5941 );
5942
5943 let mdbx_storage_cs = provider_rw.tx.entries::<tables::StorageChangeSets>().unwrap();
5944 assert_eq!(mdbx_storage_cs, 0, "v2: MDBX StorageChangeSets should remain empty");
5945
5946 let mdbx_account_cs = provider_rw.tx.entries::<tables::AccountChangeSets>().unwrap();
5947 assert_eq!(mdbx_account_cs, 0, "v2: MDBX AccountChangeSets should remain empty");
5948 }
5949
5950 #[test]
5951 fn test_unwind_storage_history_indices_v2() {
5952 let factory = create_test_provider_factory();
5953 factory.set_storage_settings_cache(StorageSettings::v2());
5954
5955 let address = Address::with_last_byte(1);
5956 let slot_key = B256::with_last_byte(42);
5957
5958 {
5959 let rocksdb = factory.rocksdb_provider();
5960 let mut batch = rocksdb.batch();
5961 batch.append_storage_history_shard(address, slot_key, vec![3u64, 7, 10]).unwrap();
5962 batch.commit().unwrap();
5963
5964 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5965 assert!(!shards.is_empty(), "history should be written to rocksdb");
5966 }
5967
5968 let provider_rw = factory.provider_rw().unwrap();
5969
5970 let changesets = vec![
5971 (
5972 BlockNumberAddress((7, address)),
5973 StorageEntry { key: slot_key, value: U256::from(5) },
5974 ),
5975 (
5976 BlockNumberAddress((10, address)),
5977 StorageEntry { key: slot_key, value: U256::from(8) },
5978 ),
5979 ];
5980
5981 let count = provider_rw.unwind_storage_history_indices(changesets.into_iter()).unwrap();
5982 assert_eq!(count, 2);
5983
5984 provider_rw.commit().unwrap();
5985
5986 let rocksdb = factory.rocksdb_provider();
5987 let shards = rocksdb.storage_history_shards(address, slot_key).unwrap();
5988
5989 assert!(
5990 !shards.is_empty(),
5991 "history shards should still exist with block 3 after partial unwind"
5992 );
5993
5994 let all_blocks: Vec<u64> = shards.iter().flat_map(|(_, list)| list.iter()).collect();
5995 assert!(all_blocks.contains(&3), "block 3 should remain");
5996 assert!(!all_blocks.contains(&7), "block 7 should be unwound");
5997 assert!(!all_blocks.contains(&10), "block 10 should be unwound");
5998 }
5999}