Skip to content

Commit

Permalink
feat(cli): storage tries recovery (#4109)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkrasiuk authored Aug 8, 2023
1 parent 9175129 commit 32dd9af
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 1 deletion.
6 changes: 5 additions & 1 deletion bin/reth/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
cli::ext::RethCliExt,
db, debug_cmd,
dirs::{LogsDir, PlatformPath},
node, p2p,
node, p2p, recover,
runner::CliRunner,
stage, test_vectors,
version::{LONG_VERSION, SHORT_VERSION},
Expand Down Expand Up @@ -77,6 +77,7 @@ impl<Ext: RethCliExt> Cli<Ext> {
Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
Commands::Debug(command) => runner.run_command_until_exit(|ctx| command.execute(ctx)),
Commands::Recover(command) => runner.run_command_until_exit(|ctx| command.execute(ctx)),
}
}

Expand Down Expand Up @@ -132,6 +133,9 @@ pub enum Commands<Ext: RethCliExt = ()> {
/// Various debug routines
#[command(name = "debug")]
Debug(debug_cmd::Command),
/// Scripts for node recovery
#[command(name = "recover")]
Recover(recover::Command),
}

/// The log configuration.
Expand Down
1 change: 1 addition & 0 deletions bin/reth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod init;
pub mod node;
pub mod p2p;
pub mod prometheus_exporter;
pub mod recover;
pub mod runner;
pub mod stage;
pub mod test_vectors;
Expand Down
29 changes: 29 additions & 0 deletions bin/reth/src/recover/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! `reth recover` command.
use clap::{Parser, Subcommand};

use crate::runner::CliContext;

mod storage_tries;

/// `reth recover` command
#[derive(Debug, Parser)]
pub struct Command {
#[clap(subcommand)]
command: Subcommands,
}

/// `reth recover` subcommands
#[derive(Subcommand, Debug)]
pub enum Subcommands {
/// Recover the node by deleting dangling storage tries.
StorageTries(storage_tries::Command),
}

impl Command {
/// Execute `recover` command
pub async fn execute(self, ctx: CliContext) -> eyre::Result<()> {
match self.command {
Subcommands::StorageTries(command) => command.execute(ctx).await,
}
}
}
96 changes: 96 additions & 0 deletions bin/reth/src/recover/storage_tries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::{
args::utils::genesis_value_parser,
dirs::{DataDirPath, MaybePlatformPath},
init::init_genesis,
runner::CliContext,
};
use clap::Parser;
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRW},
init_db, tables,
transaction::DbTx,
};
use reth_primitives::{keccak256, ChainSpec};
use reth_provider::{AccountExtReader, BlockNumReader, ProviderFactory};
use std::{fs, sync::Arc};
use tracing::*;

/// `reth recover storage-tries` command
#[derive(Debug, Parser)]
pub struct Command {
/// The path to the data dir for all reth files and subdirectories.
///
/// Defaults to the OS-specific data directory:
///
/// - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/`
/// - Windows: `{FOLDERID_RoamingAppData}/reth/`
/// - macOS: `$HOME/Library/Application Support/reth/`
#[arg(long, value_name = "DATA_DIR", verbatim_doc_comment, default_value_t)]
datadir: MaybePlatformPath<DataDirPath>,

/// The chain this node is running.
///
/// Possible values are either a built-in chain or the path to a chain specification file.
///
/// Built-in chains:
/// - mainnet
/// - goerli
/// - sepolia
#[arg(
long,
value_name = "CHAIN_OR_PATH",
verbatim_doc_comment,
default_value = "mainnet",
value_parser = genesis_value_parser
)]
chain: Arc<ChainSpec>,

/// The number of blocks in the past to look through.
#[arg(long, default_value_t = 100)]
lookback: u64,
}

impl Command {
/// Execute `storage-tries` recovery command
pub async fn execute(self, _ctx: CliContext) -> eyre::Result<()> {
let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain);
let db_path = data_dir.db_path();
fs::create_dir_all(&db_path)?;
let db = Arc::new(init_db(db_path, None)?);

debug!(target: "reth::cli", chain=%self.chain.chain, genesis=?self.chain.genesis_hash(), "Initializing genesis");
init_genesis(db.clone(), self.chain.clone())?;

let factory = ProviderFactory::new(&db, self.chain.clone());
let mut provider = factory.provider_rw()?;

let best_block = provider.best_block_number()?;

let block_range = best_block.saturating_sub(self.lookback)..=best_block;
let changed_accounts = provider.changed_accounts_with_range(block_range)?;
let destroyed_accounts = provider
.basic_accounts(changed_accounts)?
.into_iter()
.filter_map(|(address, acc)| acc.is_none().then_some(address))
.collect::<Vec<_>>();

info!(target: "reth::cli", destroyed = destroyed_accounts.len(), "Starting recovery of storage tries");

let mut deleted_tries = 0;
let tx_mut = provider.tx_mut();
let mut storage_trie_cursor = tx_mut.cursor_dup_read::<tables::StoragesTrie>()?;
for address in destroyed_accounts {
let hashed_address = keccak256(address);
if storage_trie_cursor.seek_exact(hashed_address)?.is_some() {
deleted_tries += 1;
trace!(target: "reth::cli", ?address, ?hashed_address, "Deleting storage trie");
storage_trie_cursor.delete_current_duplicates()?;
}
}

provider.commit()?;
info!(target: "reth::cli", deleted = deleted_tries, "Finished recovery");

Ok(())
}
}

0 comments on commit 32dd9af

Please sign in to comment.