mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 09:08:05 -05:00
feat(cli): storage tries recovery (#4109)
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
29
bin/reth/src/recover/mod.rs
Normal file
29
bin/reth/src/recover/mod.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
96
bin/reth/src/recover/storage_tries.rs
Normal file
96
bin/reth/src/recover/storage_tries.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user