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