reth_cli_commands/recover/
storage_tries.rs

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