diff --git a/bin/reth/src/cli.rs b/bin/reth/src/cli.rs index 4afa909392..ed49726b5a 100644 --- a/bin/reth/src/cli.rs +++ b/bin/reth/src/cli.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use crate::{ chain, db, dirs::{LogsDir, PlatformPath}, - dump_stage, node, p2p, + drop_stage, dump_stage, node, p2p, runner::CliRunner, stage, test_eth_chain, test_vectors, }; @@ -31,6 +31,7 @@ pub fn run() -> eyre::Result<()> { Commands::Db(command) => runner.run_until_ctrl_c(command.execute()), Commands::Stage(command) => runner.run_until_ctrl_c(command.execute()), Commands::DumpStage(command) => runner.run_until_ctrl_c(command.execute()), + Commands::DropStage(command) => runner.run_until_ctrl_c(command.execute()), Commands::P2P(command) => runner.run_until_ctrl_c(command.execute()), Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()), Commands::TestEthChain(command) => runner.run_until_ctrl_c(command.execute()), @@ -63,6 +64,9 @@ pub enum Commands { /// Dumps a stage from a range into a new database. #[command(name = "dump-stage")] DumpStage(dump_stage::Command), + /// Drops a stage's tables from the database. + #[command(name = "drop-stage")] + DropStage(drop_stage::Command), /// P2P Debugging utilities #[command(name = "p2p")] P2P(p2p::Command), diff --git a/bin/reth/src/db/mod.rs b/bin/reth/src/db/mod.rs index a0830ca13f..8e8b26bdc3 100644 --- a/bin/reth/src/db/mod.rs +++ b/bin/reth/src/db/mod.rs @@ -1,20 +1,14 @@ //! Database debugging tool -use crate::dirs::{DbPath, PlatformPath}; +use crate::{ + dirs::{DbPath, PlatformPath}, + utils::DbTool, +}; use clap::{Parser, Subcommand}; use comfy_table::{Cell, Row, Table as ComfyTable}; -use eyre::{Result, WrapErr}; +use eyre::WrapErr; use human_bytes::human_bytes; -use reth_db::{ - cursor::{DbCursorRO, Walker}, - database::Database, - table::Table, - tables, - transaction::DbTx, -}; -use reth_interfaces::test_utils::generators::random_block_range; -use reth_provider::insert_canonical_block; -use std::collections::BTreeMap; -use tracing::{error, info}; +use reth_db::{database::Database, tables}; +use tracing::error; /// DB List TUI mod tui; @@ -189,55 +183,3 @@ impl Command { Ok(()) } } - -/// Wrapper over DB that implements many useful DB queries. -pub(crate) struct DbTool<'a, DB: Database> { - pub(crate) db: &'a DB, -} - -impl<'a, DB: Database> DbTool<'a, DB> { - /// Takes a DB where the tables have already been created. - pub(crate) fn new(db: &'a DB) -> eyre::Result { - Ok(Self { db }) - } - - /// Seeds the database with some random data, only used for testing - fn seed(&mut self, len: u64) -> Result<()> { - info!(target: "reth::cli", "Generating random block range from 0 to {len}"); - let chain = random_block_range(0..len, Default::default(), 0..64); - - self.db.update(|tx| { - chain.into_iter().try_for_each(|block| { - insert_canonical_block(tx, block, None, true)?; - Ok::<_, eyre::Error>(()) - }) - })??; - - info!(target: "reth::cli", "Database seeded with {len} blocks"); - Ok(()) - } - - /// Grabs the contents of the table within a certain index range and places the - /// entries into a [`HashMap`][std::collections::HashMap]. - fn list(&mut self, start: usize, len: usize) -> Result> { - let data = self.db.view(|tx| { - let mut cursor = tx.cursor_read::().expect("Was not able to obtain a cursor."); - - // TODO: Upstream this in the DB trait. - let start_walker = cursor.current().transpose(); - let walker = Walker::new(&mut cursor, start_walker); - - walker.skip(start).take(len).collect::>() - })?; - - data.into_iter() - .collect::, _>>() - .map_err(|e| eyre::eyre!(e)) - } - - fn drop(&mut self, path: &PlatformPath) -> Result<()> { - info!(target: "reth::cli", "Dropping db at {}", path); - std::fs::remove_dir_all(path).wrap_err("Dropping the database failed")?; - Ok(()) - } -} diff --git a/bin/reth/src/drop_stage.rs b/bin/reth/src/drop_stage.rs new file mode 100644 index 0000000000..7e478b36ae --- /dev/null +++ b/bin/reth/src/drop_stage.rs @@ -0,0 +1,82 @@ +//! Database debugging tool +use crate::{ + dirs::{DbPath, PlatformPath}, + utils::DbTool, + StageEnum, +}; +use clap::Parser; +use reth_db::{ + database::Database, + mdbx::{Env, WriteMap}, + tables, + transaction::DbTxMut, +}; +use reth_primitives::ChainSpec; +use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::insert_genesis_state}; +use reth_stages::stages::EXECUTION; +use std::sync::Arc; +use tracing::info; + +/// `reth drop-stage` command +#[derive(Debug, Parser)] +pub struct Command { + /// The path to the database folder. + /// + /// Defaults to the OS-specific data directory: + /// + /// - Linux: `$XDG_DATA_HOME/reth/db` or `$HOME/.local/share/reth/db` + /// - Windows: `{FOLDERID_RoamingAppData}/reth/db` + /// - macOS: `$HOME/Library/Application Support/reth/db` + #[arg(global = true, long, value_name = "PATH", verbatim_doc_comment, default_value_t)] + db: PlatformPath, + + /// 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, + + stage: StageEnum, +} + +impl Command { + /// Execute `db` command + pub async fn execute(&self) -> eyre::Result<()> { + std::fs::create_dir_all(&self.db)?; + + let db = Env::::open(self.db.as_ref(), reth_db::mdbx::EnvKind::RW)?; + + let tool = DbTool::new(&db)?; + + match &self.stage { + StageEnum::Execution => { + tool.db.update(|tx| { + tx.clear::()?; + tx.clear::()?; + tx.clear::()?; + tx.clear::()?; + tx.clear::()?; + tx.put::(EXECUTION.0.to_string(), 0)?; + insert_genesis_state::>(tx, self.chain.genesis())?; + Ok::<_, eyre::Error>(()) + })??; + } + _ => { + info!("Nothing to do for stage {:?}", self.stage); + } + } + + Ok(()) + } +} diff --git a/bin/reth/src/dump_stage/execution.rs b/bin/reth/src/dump_stage/execution.rs index 7e164d9eb4..d06f0345a9 100644 --- a/bin/reth/src/dump_stage/execution.rs +++ b/bin/reth/src/dump_stage/execution.rs @@ -1,7 +1,7 @@ use crate::{ - db::DbTool, dirs::{DbPath, PlatformPath}, dump_stage::setup, + utils::DbTool, }; use eyre::Result; use reth_db::{ diff --git a/bin/reth/src/dump_stage/hashing_account.rs b/bin/reth/src/dump_stage/hashing_account.rs index 5f43ce61ab..6a281674b5 100644 --- a/bin/reth/src/dump_stage/hashing_account.rs +++ b/bin/reth/src/dump_stage/hashing_account.rs @@ -1,7 +1,7 @@ use crate::{ - db::DbTool, dirs::{DbPath, PlatformPath}, dump_stage::setup, + utils::DbTool, }; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables, transaction::DbTx}; diff --git a/bin/reth/src/dump_stage/hashing_storage.rs b/bin/reth/src/dump_stage/hashing_storage.rs index 03cbb74a3f..2d992a8fd6 100644 --- a/bin/reth/src/dump_stage/hashing_storage.rs +++ b/bin/reth/src/dump_stage/hashing_storage.rs @@ -1,7 +1,7 @@ use crate::{ - db::DbTool, dirs::{DbPath, PlatformPath}, dump_stage::setup, + utils::DbTool, }; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables}; diff --git a/bin/reth/src/dump_stage/merkle.rs b/bin/reth/src/dump_stage/merkle.rs index 6e6ce370ff..4b8e1bc98a 100644 --- a/bin/reth/src/dump_stage/merkle.rs +++ b/bin/reth/src/dump_stage/merkle.rs @@ -1,7 +1,7 @@ use crate::{ - db::DbTool, dirs::{DbPath, PlatformPath}, dump_stage::setup, + utils::DbTool, }; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables, transaction::DbTx}; diff --git a/bin/reth/src/dump_stage/mod.rs b/bin/reth/src/dump_stage/mod.rs index 4eddb3817d..5603c64a2c 100644 --- a/bin/reth/src/dump_stage/mod.rs +++ b/bin/reth/src/dump_stage/mod.rs @@ -12,8 +12,8 @@ mod merkle; use merkle::dump_merkle_stage; use crate::{ - db::DbTool, dirs::{DbPath, PlatformPath}, + utils::DbTool, }; use clap::Parser; use reth_db::{ diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index 36ebcf6324..2d17b8ae89 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -12,6 +12,7 @@ pub mod chain; pub mod cli; pub mod db; pub mod dirs; +pub mod drop_stage; pub mod dump_stage; pub mod node; pub mod p2p; @@ -21,3 +22,11 @@ pub mod stage; pub mod test_eth_chain; pub mod test_vectors; pub mod utils; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, clap::ValueEnum)] +enum StageEnum { + Headers, + Bodies, + Senders, + Execution, +} diff --git a/bin/reth/src/stage/mod.rs b/bin/reth/src/stage/mod.rs index b57bb7f454..eb4409dfdc 100644 --- a/bin/reth/src/stage/mod.rs +++ b/bin/reth/src/stage/mod.rs @@ -4,9 +4,9 @@ use crate::{ args::NetworkArgs, dirs::{ConfigPath, DbPath, PlatformPath}, - prometheus_exporter, + prometheus_exporter, StageEnum, }; -use clap::{Parser, ValueEnum}; +use clap::Parser; use reth_beacon_consensus::BeaconConsensus; use reth_downloaders::bodies::bodies::BodiesDownloaderBuilder; use reth_primitives::ChainSpec; @@ -86,14 +86,6 @@ pub struct Command { network: NetworkArgs, } -#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, ValueEnum)] -enum StageEnum { - Headers, - Bodies, - Senders, - Execution, -} - impl Command { /// Execute `stage` command pub async fn execute(&self) -> eyre::Result<()> { diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index e163afa71c..456ba277c9 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -1,12 +1,25 @@ //! Common CLI utility functions. - -use reth_interfaces::p2p::{ - download::DownloadClient, - headers::client::{HeadersClient, HeadersRequest}, - priority::Priority, +use crate::dirs::{DbPath, PlatformPath}; +use eyre::{Result, WrapErr}; +use reth_db::{ + cursor::{DbCursorRO, Walker}, + database::Database, + table::Table, + transaction::{DbTx, DbTxMut}, +}; +use reth_interfaces::{ + p2p::{ + download::DownloadClient, + headers::client::{HeadersClient, HeadersRequest}, + priority::Priority, + }, + test_utils::generators::random_block_range, }; use reth_network::FetchClient; use reth_primitives::{BlockHashOrNumber, HeadersDirection, SealedHeader}; +use reth_provider::insert_canonical_block; +use std::collections::BTreeMap; +use tracing::info; /// Get a single header from network pub async fn get_single_header( @@ -41,3 +54,66 @@ pub async fn get_single_header( Ok(header) } + +/// Wrapper over DB that implements many useful DB queries. +pub struct DbTool<'a, DB: Database> { + pub(crate) db: &'a DB, +} + +impl<'a, DB: Database> DbTool<'a, DB> { + /// Takes a DB where the tables have already been created. + pub(crate) fn new(db: &'a DB) -> eyre::Result { + Ok(Self { db }) + } + + /// Seeds the database with some random data, only used for testing + pub fn seed(&mut self, len: u64) -> Result<()> { + info!(target: "reth::cli", "Generating random block range from 0 to {len}"); + let chain = random_block_range(0..len, Default::default(), 0..64); + + self.db.update(|tx| { + chain.into_iter().try_for_each(|block| { + insert_canonical_block(tx, block, None, true)?; + Ok::<_, eyre::Error>(()) + }) + })??; + + info!(target: "reth::cli", "Database seeded with {len} blocks"); + Ok(()) + } + + /// Grabs the contents of the table within a certain index range and places the + /// entries into a [`HashMap`][std::collections::HashMap]. + pub fn list( + &mut self, + start: usize, + len: usize, + ) -> Result> { + let data = self.db.view(|tx| { + let mut cursor = tx.cursor_read::().expect("Was not able to obtain a cursor."); + + // TODO: Upstream this in the DB trait. + let start_walker = cursor.current().transpose(); + let walker = Walker::new(&mut cursor, start_walker); + + walker.skip(start).take(len).collect::>() + })?; + + data.into_iter() + .collect::, _>>() + .map_err(|e| eyre::eyre!(e)) + } + + /// Drops the database at the given path. + pub fn drop(&mut self, path: &PlatformPath) -> Result<()> { + info!(target: "reth::cli", "Dropping db at {}", path); + std::fs::remove_dir_all(path).wrap_err("Dropping the database failed")?; + Ok(()) + } + + /// Drops the provided table from the database. + pub fn drop_table(&mut self) -> Result<()> { + self.db.update(|tx| tx.clear::())??; + Ok(()) + } +} diff --git a/crates/staged-sync/src/utils/init.rs b/crates/staged-sync/src/utils/init.rs index 1038214231..2c08e85fed 100644 --- a/crates/staged-sync/src/utils/init.rs +++ b/crates/staged-sync/src/utils/init.rs @@ -1,6 +1,6 @@ use reth_db::{ cursor::{DbCursorRO, DbCursorRW}, - database::Database, + database::{Database, DatabaseGAT}, mdbx::{Env, WriteMap}, tables, transaction::{DbTx, DbTxMut}, @@ -59,7 +59,25 @@ pub fn init_genesis( drop(tx); debug!("Writing genesis block."); let tx = db.tx_mut()?; + insert_genesis_state::(&tx, genesis)?; + // Insert header + tx.put::(0, hash)?; + tx.put::(hash, 0)?; + tx.put::(0, Default::default())?; + tx.put::(0, 0)?; + tx.put::(0, header.difficulty.into())?; + tx.put::(0, header)?; + + tx.commit()?; + Ok(hash) +} + +/// Inserts the genesis state into the database. +pub fn insert_genesis_state( + tx: &>::TXMut, + genesis: &reth_primitives::Genesis, +) -> Result<(), InitDatabaseError> { let mut account_cursor = tx.cursor_write::()?; let mut storage_cursor = tx.cursor_write::()?; let mut bytecode_cursor = tx.cursor_write::()?; @@ -89,21 +107,8 @@ pub fn init_genesis( } } } - // Drop all cursor so we can commit the changes at the end of the fn. - drop(account_cursor); - drop(storage_cursor); - drop(bytecode_cursor); - // Insert header - tx.put::(0, hash)?; - tx.put::(hash, 0)?; - tx.put::(0, Default::default())?; - tx.put::(0, 0)?; - tx.put::(0, header.difficulty.into())?; - tx.put::(0, header)?; - - tx.commit()?; - Ok(hash) + Ok(()) } #[cfg(test)]