feat(cli): storage tries recovery (#4109)

This commit is contained in:
Roman Krasiuk
2023-08-08 11:59:43 +03:00
committed by GitHub
parent 91751290c7
commit 32dd9af531
4 changed files with 131 additions and 1 deletions

View File

@@ -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},
@@ -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)),
}
}
@@ -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.

View File

@@ -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;

View File

@@ -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,
}
}
}

View File

@@ -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(())
}
}