reth_cli_commands/recover/
storage_tries.rs

1use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
2use alloy_consensus::BlockHeader;
3use clap::Parser;
4use reth_chainspec::{EthChainSpec, EthereumHardforks};
5use reth_cli::chainspec::ChainSpecParser;
6use reth_cli_runner::CliContext;
7use reth_db_api::{
8    cursor::{DbCursorRO, DbDupCursorRW},
9    tables,
10    transaction::DbTx,
11};
12use reth_provider::{BlockNumReader, HeaderProvider, ProviderError};
13use reth_trie::StateRoot;
14use reth_trie_db::DatabaseStateRoot;
15use std::sync::Arc;
16use tracing::*;
17
18/// `reth recover storage-tries` command
19#[derive(Debug, Parser)]
20pub struct Command<C: ChainSpecParser> {
21    #[command(flatten)]
22    env: EnvironmentArgs<C>,
23}
24
25impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
26    /// Execute `storage-tries` recovery command
27    pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(
28        self,
29        _ctx: CliContext,
30    ) -> eyre::Result<()> {
31        let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
32
33        let mut provider = provider_factory.provider_rw()?;
34        let best_block = provider.best_block_number()?;
35        let best_header = provider
36            .sealed_header(best_block)?
37            .ok_or_else(|| ProviderError::HeaderNotFound(best_block.into()))?;
38
39        let mut deleted_tries = 0;
40        let tx_mut = provider.tx_mut();
41        let mut hashed_account_cursor = tx_mut.cursor_read::<tables::HashedAccounts>()?;
42        let mut storage_trie_cursor = tx_mut.cursor_dup_read::<tables::StoragesTrie>()?;
43        let mut entry = storage_trie_cursor.first()?;
44
45        info!(target: "reth::cli", "Starting pruning of storage tries");
46        while let Some((hashed_address, _)) = entry {
47            if hashed_account_cursor.seek_exact(hashed_address)?.is_none() {
48                deleted_tries += 1;
49                storage_trie_cursor.delete_current_duplicates()?;
50            }
51
52            entry = storage_trie_cursor.next()?;
53        }
54
55        let state_root = StateRoot::from_tx(tx_mut).root()?;
56        if state_root != best_header.state_root() {
57            eyre::bail!(
58                "Recovery failed. Incorrect state root. Expected: {:?}. Received: {:?}",
59                best_header.state_root(),
60                state_root
61            );
62        }
63
64        provider.commit()?;
65        info!(target: "reth::cli", deleted = deleted_tries, "Finished recovery");
66
67        Ok(())
68    }
69}
70
71impl<C: ChainSpecParser> Command<C> {
72    /// Returns the underlying chain being used to run this command
73    pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
74        Some(&self.env.chain)
75    }
76}