Skip to main content

reth_cli_commands/stage/dump/
mod.rs

1//! Database debugging tool
2use crate::common::{AccessRights, CliNodeComponents, CliNodeTypes, Environment, EnvironmentArgs};
3use clap::Parser;
4use reth_chainspec::{EthChainSpec, EthereumHardforks};
5use reth_cli::chainspec::ChainSpecParser;
6use reth_db::{init_db, mdbx::DatabaseArguments, DatabaseEnv};
7use reth_db_api::{
8    cursor::DbCursorRO, database::Database, models::ClientVersion, table::TableImporter, tables,
9    transaction::DbTx,
10};
11use reth_db_common::DbTool;
12use reth_node_builder::NodeTypesWithDB;
13use reth_node_core::{
14    args::DatadirArgs,
15    dirs::{DataDirPath, PlatformPath},
16};
17use std::{path::PathBuf, sync::Arc};
18use tracing::info;
19
20mod hashing_storage;
21use hashing_storage::dump_hashing_storage_stage;
22
23mod hashing_account;
24use hashing_account::dump_hashing_account_stage;
25
26mod execution;
27use execution::dump_execution_stage;
28
29mod merkle;
30use merkle::dump_merkle_stage;
31
32/// `reth dump-stage` command
33#[derive(Debug, Parser)]
34pub struct Command<C: ChainSpecParser> {
35    #[command(flatten)]
36    env: EnvironmentArgs<C>,
37
38    #[command(subcommand)]
39    command: Stages,
40}
41
42/// Supported stages to be dumped
43#[derive(Debug, Clone, Parser)]
44pub enum Stages {
45    /// Execution stage.
46    Execution(StageCommand),
47    /// `StorageHashing` stage.
48    StorageHashing(StageCommand),
49    /// `AccountHashing` stage.
50    AccountHashing(StageCommand),
51    /// Merkle stage.
52    Merkle(StageCommand),
53}
54
55/// Stage command that takes a range
56#[derive(Debug, Clone, Parser)]
57pub struct StageCommand {
58    /// The path to the new datadir folder.
59    #[arg(long, value_name = "OUTPUT_PATH", verbatim_doc_comment)]
60    output_datadir: PlatformPath<DataDirPath>,
61
62    /// From which block.
63    #[arg(long, short)]
64    from: u64,
65    /// To which block.
66    #[arg(long, short)]
67    to: u64,
68    /// If passed, it will dry-run a stage execution from the newly created database right after
69    /// dumping.
70    #[arg(long, short, default_value = "false")]
71    dry_run: bool,
72}
73
74macro_rules! handle_stage {
75    ($stage_fn:ident, $tool:expr, $command:expr, $runtime:expr) => {{
76        let StageCommand { output_datadir, from, to, dry_run, .. } = $command;
77        let output_datadir =
78            output_datadir.with_chain($tool.chain().chain(), DatadirArgs::default());
79        $stage_fn($tool, *from, *to, output_datadir, *dry_run, $runtime).await?
80    }};
81
82    ($stage_fn:ident, $tool:expr, $command:expr, $executor:expr, $consensus:expr, $runtime:expr) => {{
83        let StageCommand { output_datadir, from, to, dry_run, .. } = $command;
84        let output_datadir =
85            output_datadir.with_chain($tool.chain().chain(), DatadirArgs::default());
86        $stage_fn($tool, *from, *to, output_datadir, *dry_run, $executor, $consensus, $runtime)
87            .await?
88    }};
89}
90
91impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
92    /// Execute `dump-stage` command
93    pub async fn execute<N, Comp, F>(
94        self,
95        components: F,
96        runtime: reth_tasks::Runtime,
97    ) -> eyre::Result<()>
98    where
99        N: CliNodeTypes<ChainSpec = C::ChainSpec>,
100        Comp: CliNodeComponents<N>,
101        F: FnOnce(Arc<C::ChainSpec>) -> Comp,
102    {
103        let Environment { provider_factory, .. } =
104            self.env.init::<N>(AccessRights::RO, runtime.clone())?;
105        let tool = DbTool::new(provider_factory)?;
106        let components = components(tool.chain());
107        let evm_config = components.evm_config().clone();
108        let consensus = components.consensus().clone();
109
110        match &self.command {
111            Stages::Execution(cmd) => {
112                handle_stage!(
113                    dump_execution_stage,
114                    &tool,
115                    cmd,
116                    evm_config,
117                    consensus,
118                    runtime.clone()
119                )
120            }
121            Stages::StorageHashing(cmd) => {
122                handle_stage!(dump_hashing_storage_stage, &tool, cmd, runtime.clone())
123            }
124            Stages::AccountHashing(cmd) => {
125                handle_stage!(dump_hashing_account_stage, &tool, cmd, runtime.clone())
126            }
127            Stages::Merkle(cmd) => {
128                handle_stage!(dump_merkle_stage, &tool, cmd, evm_config, consensus, runtime.clone())
129            }
130        }
131
132        Ok(())
133    }
134}
135
136impl<C: ChainSpecParser> Command<C> {
137    /// Returns the underlying chain being used to run this command
138    pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
139        Some(&self.env.chain)
140    }
141}
142
143/// Sets up the database and initial state on [`tables::BlockBodyIndices`]. Also returns the tip
144/// block number.
145pub(crate) fn setup<N: NodeTypesWithDB>(
146    from: u64,
147    to: u64,
148    output_db: &PathBuf,
149    db_tool: &DbTool<N>,
150) -> eyre::Result<(DatabaseEnv, u64)> {
151    assert!(from < to, "FROM block should be lower than TO block.");
152
153    info!(target: "reth::cli", ?output_db, "Creating separate db");
154
155    let output_datadir = init_db(output_db, DatabaseArguments::new(ClientVersion::default()))?;
156
157    output_datadir.update(|tx| {
158        tx.import_table_with_range::<tables::BlockBodyIndices, _>(
159            &db_tool.provider_factory.db_ref().tx()?,
160            Some(from - 1),
161            to + 1,
162        )
163    })??;
164
165    let (tip_block_number, _) = db_tool
166        .provider_factory
167        .db_ref()
168        .view(|tx| tx.cursor_read::<tables::BlockBodyIndices>()?.last())??
169        .expect("some");
170
171    Ok((output_datadir, tip_block_number))
172}