Skip to main content

reth_cli_commands/db/checksum/
rocksdb.rs

1//! RocksDB checksum implementation.
2
3use super::{checksum_hasher, PROGRESS_LOG_INTERVAL};
4use crate::common::CliNodeTypes;
5use clap::ValueEnum;
6use reth_chainspec::EthereumHardforks;
7use reth_db::{tables, DatabaseEnv};
8use reth_db_api::table::Table;
9use reth_db_common::DbTool;
10use reth_node_builder::NodeTypesWithDBAdapter;
11use reth_primitives_traits::FastInstant as Instant;
12use reth_provider::RocksDBProviderFactory;
13use std::hash::Hasher;
14use tracing::info;
15
16/// RocksDB tables that can be checksummed.
17#[derive(Debug, Clone, Copy, ValueEnum)]
18pub enum RocksDbTable {
19    /// Transaction hash to transaction number mapping
20    TransactionHashNumbers,
21    /// Account history indices
22    AccountsHistory,
23    /// Storage history indices
24    StoragesHistory,
25}
26
27impl RocksDbTable {
28    /// Returns the table name as a string
29    const fn name(&self) -> &'static str {
30        match self {
31            Self::TransactionHashNumbers => tables::TransactionHashNumbers::NAME,
32            Self::AccountsHistory => tables::AccountsHistory::NAME,
33            Self::StoragesHistory => tables::StoragesHistory::NAME,
34        }
35    }
36}
37
38/// Computes a checksum for a RocksDB table.
39pub fn checksum_rocksdb<N: CliNodeTypes<ChainSpec: EthereumHardforks>>(
40    tool: &DbTool<NodeTypesWithDBAdapter<N, DatabaseEnv>>,
41    table: RocksDbTable,
42    limit: Option<usize>,
43) -> eyre::Result<()> {
44    let rocksdb = tool.provider_factory.rocksdb_provider();
45
46    let start_time = Instant::now();
47    let limit = limit.unwrap_or(usize::MAX);
48
49    info!(
50        "Computing checksum for RocksDB table `{}`, limit={:?}",
51        table.name(),
52        if limit == usize::MAX { None } else { Some(limit) }
53    );
54
55    let (checksum, total) = match table {
56        RocksDbTable::TransactionHashNumbers => {
57            checksum_rocksdb_table::<tables::TransactionHashNumbers>(&rocksdb, limit)?
58        }
59        RocksDbTable::AccountsHistory => {
60            checksum_rocksdb_table::<tables::AccountsHistory>(&rocksdb, limit)?
61        }
62        RocksDbTable::StoragesHistory => {
63            checksum_rocksdb_table::<tables::StoragesHistory>(&rocksdb, limit)?
64        }
65    };
66
67    let elapsed = start_time.elapsed();
68
69    info!(
70        "Checksum for RocksDB table `{}`: {:#x} ({} entries, elapsed: {:?})",
71        table.name(),
72        checksum,
73        total,
74        elapsed
75    );
76
77    Ok(())
78}
79
80/// Computes checksum for a specific RocksDB table by iterating over rows.
81fn checksum_rocksdb_table<T: Table>(
82    rocksdb: &reth_provider::providers::RocksDBProvider,
83    limit: usize,
84) -> eyre::Result<(u64, usize)> {
85    let iter = rocksdb.raw_iter::<T>()?;
86    let mut hasher = checksum_hasher();
87    let mut total = 0usize;
88
89    for entry in iter {
90        let (key_bytes, value_bytes) = entry?;
91
92        hasher.write(&key_bytes);
93        hasher.write(&value_bytes);
94
95        total += 1;
96
97        if total.is_multiple_of(PROGRESS_LOG_INTERVAL) {
98            info!("Hashed {total} entries.");
99        }
100
101        if total >= limit {
102            break;
103        }
104    }
105
106    Ok((hasher.finish(), total))
107}