mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-09 23:38:10 -05:00
refactor(reth): Extract Cli into ethereum-cli crate (#15857)
Co-authored-by: Harrish Bansal <145403921+Haxry@users.noreply.github.com>
This commit is contained in:
77
Cargo.lock
generated
77
Cargo.lock
generated
@@ -6913,51 +6913,31 @@ dependencies = [
|
||||
name = "reth"
|
||||
version = "1.3.12"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
"alloy-primitives 1.0.0",
|
||||
"alloy-rlp",
|
||||
"alloy-rpc-types",
|
||||
"aquamarine",
|
||||
"backon",
|
||||
"clap",
|
||||
"eyre",
|
||||
"futures",
|
||||
"reth-basic-payload-builder",
|
||||
"reth-chainspec",
|
||||
"reth-cli",
|
||||
"reth-cli-commands",
|
||||
"reth-cli-runner",
|
||||
"reth-cli-util",
|
||||
"reth-config",
|
||||
"reth-consensus",
|
||||
"reth-consensus-common",
|
||||
"reth-db",
|
||||
"reth-db-api",
|
||||
"reth-downloaders",
|
||||
"reth-errors",
|
||||
"reth-ethereum-cli",
|
||||
"reth-ethereum-payload-builder",
|
||||
"reth-ethereum-primitives",
|
||||
"reth-evm",
|
||||
"reth-execution-types",
|
||||
"reth-exex",
|
||||
"reth-fs-util",
|
||||
"reth-network",
|
||||
"reth-network-api",
|
||||
"reth-network-p2p",
|
||||
"reth-node-api",
|
||||
"reth-node-builder",
|
||||
"reth-node-core",
|
||||
"reth-node-ethereum",
|
||||
"reth-node-events",
|
||||
"reth-node-metrics",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-primitives-traits",
|
||||
"reth-provider",
|
||||
"reth-prune",
|
||||
"reth-ress-protocol",
|
||||
"reth-ress-provider",
|
||||
"reth-revm",
|
||||
@@ -6967,17 +6947,9 @@ dependencies = [
|
||||
"reth-rpc-eth-types",
|
||||
"reth-rpc-server-types",
|
||||
"reth-rpc-types-compat",
|
||||
"reth-stages",
|
||||
"reth-static-file",
|
||||
"reth-tasks",
|
||||
"reth-tokio-util",
|
||||
"reth-tracing",
|
||||
"reth-transaction-pool",
|
||||
"reth-trie",
|
||||
"reth-trie-db",
|
||||
"serde_json",
|
||||
"similar-asserts",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -7945,12 +7917,59 @@ dependencies = [
|
||||
name = "reth-ethereum-cli"
|
||||
version = "1.3.12"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
"alloy-primitives 1.0.0",
|
||||
"alloy-rlp",
|
||||
"alloy-rpc-types",
|
||||
"backon",
|
||||
"clap",
|
||||
"eyre",
|
||||
"futures",
|
||||
"reth-basic-payload-builder",
|
||||
"reth-chainspec",
|
||||
"reth-cli",
|
||||
"reth-cli-commands",
|
||||
"reth-ethereum-cli",
|
||||
"reth-cli-runner",
|
||||
"reth-cli-util",
|
||||
"reth-config",
|
||||
"reth-consensus",
|
||||
"reth-db",
|
||||
"reth-db-api",
|
||||
"reth-downloaders",
|
||||
"reth-errors",
|
||||
"reth-ethereum-payload-builder",
|
||||
"reth-ethereum-primitives",
|
||||
"reth-evm",
|
||||
"reth-execution-types",
|
||||
"reth-exex",
|
||||
"reth-fs-util",
|
||||
"reth-network",
|
||||
"reth-network-api",
|
||||
"reth-network-p2p",
|
||||
"reth-node-api",
|
||||
"reth-node-builder",
|
||||
"reth-node-core",
|
||||
"reth-node-ethereum",
|
||||
"reth-node-events",
|
||||
"reth-node-metrics",
|
||||
"reth-payload-builder",
|
||||
"reth-primitives-traits",
|
||||
"reth-provider",
|
||||
"reth-prune",
|
||||
"reth-revm",
|
||||
"reth-stages",
|
||||
"reth-static-file",
|
||||
"reth-tasks",
|
||||
"reth-tracing",
|
||||
"reth-transaction-pool",
|
||||
"reth-trie",
|
||||
"reth-trie-db",
|
||||
"serde_json",
|
||||
"similar-asserts",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -14,25 +14,15 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-cli.workspace = true
|
||||
reth-ethereum-cli.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
reth-config.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-fs-util.workspace = true
|
||||
reth-db = { workspace = true, features = ["mdbx"] }
|
||||
reth-db-api.workspace = true
|
||||
reth-exex.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-evm.workspace = true
|
||||
reth-revm.workspace = true
|
||||
reth-stages.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
reth-errors.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-cli-runner.workspace = true
|
||||
reth-cli-commands.workspace = true
|
||||
reth-cli-util.workspace = true
|
||||
reth-consensus-common.workspace = true
|
||||
reth-rpc-builder.workspace = true
|
||||
@@ -42,91 +32,95 @@ reth-rpc-api = { workspace = true, features = ["client"] }
|
||||
reth-rpc-eth-types.workspace = true
|
||||
reth-rpc-server-types.workspace = true
|
||||
reth-network = { workspace = true, features = ["serde"] }
|
||||
reth-network-p2p.workspace = true
|
||||
reth-network-api.workspace = true
|
||||
reth-downloaders.workspace = true
|
||||
reth-tracing.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-basic-payload-builder.workspace = true
|
||||
reth-static-file.workspace = true
|
||||
reth-trie = { workspace = true, features = ["metrics"] }
|
||||
reth-trie-db = { workspace = true, features = ["metrics"] }
|
||||
reth-node-api.workspace = true
|
||||
reth-node-core.workspace = true
|
||||
reth-ethereum-payload-builder.workspace = true
|
||||
reth-ethereum-primitives.workspace = true
|
||||
reth-node-ethereum = { workspace = true, features = ["js-tracer"] }
|
||||
reth-node-builder.workspace = true
|
||||
reth-node-events.workspace = true
|
||||
reth-node-metrics.workspace = true
|
||||
reth-consensus.workspace = true
|
||||
reth-prune.workspace = true
|
||||
reth-tokio-util.workspace = true
|
||||
reth-ress-protocol.workspace = true
|
||||
reth-ress-provider.workspace = true
|
||||
|
||||
# alloy
|
||||
alloy-eips = { workspace = true, features = ["kzg"] }
|
||||
alloy-rlp.workspace = true
|
||||
alloy-rpc-types = { workspace = true, features = ["engine"] }
|
||||
alloy-consensus.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
# tracing
|
||||
tracing.workspace = true
|
||||
|
||||
# io
|
||||
serde_json.workspace = true
|
||||
|
||||
# async
|
||||
tokio = { workspace = true, features = ["sync", "macros", "time", "rt-multi-thread"] }
|
||||
futures.workspace = true
|
||||
|
||||
# misc
|
||||
aquamarine.workspace = true
|
||||
eyre.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
backon.workspace = true
|
||||
similar-asserts.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["jemalloc", "reth-revm/portable"]
|
||||
|
||||
dev = ["reth-cli-commands/arbitrary"]
|
||||
dev = ["reth-ethereum-cli/dev"]
|
||||
|
||||
asm-keccak = [
|
||||
"reth-node-core/asm-keccak",
|
||||
"reth-primitives/asm-keccak",
|
||||
"alloy-primitives/asm-keccak",
|
||||
"reth-ethereum-cli/asm-keccak",
|
||||
]
|
||||
|
||||
jemalloc = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-node-core/jemalloc",
|
||||
"reth-node-metrics/jemalloc",
|
||||
"reth-ethereum-cli/jemalloc",
|
||||
]
|
||||
jemalloc-prof = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-cli-util/jemalloc-prof",
|
||||
"reth-ethereum-cli/jemalloc-prof",
|
||||
]
|
||||
tracy-allocator = [
|
||||
"reth-cli-util/tracy-allocator",
|
||||
"reth-ethereum-cli/tracy-allocator",
|
||||
]
|
||||
tracy-allocator = ["reth-cli-util/tracy-allocator"]
|
||||
|
||||
# Because jemalloc is default and preferred over snmalloc when both features are
|
||||
# enabled, `--no-default-features` should be used when enabling snmalloc or
|
||||
# snmalloc-native.
|
||||
snmalloc = ["reth-cli-util/snmalloc"]
|
||||
snmalloc-native = ["reth-cli-util/snmalloc-native"]
|
||||
snmalloc = [
|
||||
"reth-cli-util/snmalloc",
|
||||
"reth-ethereum-cli/snmalloc",
|
||||
]
|
||||
snmalloc-native = [
|
||||
"reth-cli-util/snmalloc-native",
|
||||
"reth-ethereum-cli/snmalloc-native",
|
||||
]
|
||||
|
||||
min-error-logs = ["tracing/release_max_level_error"]
|
||||
min-warn-logs = ["tracing/release_max_level_warn"]
|
||||
min-info-logs = ["tracing/release_max_level_info"]
|
||||
min-debug-logs = ["tracing/release_max_level_debug"]
|
||||
min-trace-logs = ["tracing/release_max_level_trace"]
|
||||
min-error-logs = [
|
||||
"tracing/release_max_level_error",
|
||||
"reth-ethereum-cli/min-error-logs",
|
||||
]
|
||||
min-warn-logs = [
|
||||
"tracing/release_max_level_warn",
|
||||
"reth-ethereum-cli/min-warn-logs",
|
||||
]
|
||||
min-info-logs = [
|
||||
"tracing/release_max_level_info",
|
||||
"reth-ethereum-cli/min-info-logs",
|
||||
]
|
||||
min-debug-logs = [
|
||||
"tracing/release_max_level_debug",
|
||||
"reth-ethereum-cli/min-debug-logs",
|
||||
]
|
||||
min-trace-logs = [
|
||||
"tracing/release_max_level_trace",
|
||||
"reth-ethereum-cli/min-trace-logs",
|
||||
]
|
||||
|
||||
[[bin]]
|
||||
name = "reth"
|
||||
|
||||
@@ -1,378 +1,15 @@
|
||||
//! CLI definition and entrypoint to executable
|
||||
|
||||
use crate::{
|
||||
args::LogArgs,
|
||||
commands::debug_cmd,
|
||||
version::{LONG_VERSION, SHORT_VERSION},
|
||||
};
|
||||
use clap::{Parser, Subcommand};
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::{
|
||||
config_cmd, db, dump_genesis, import, init_cmd, init_state,
|
||||
node::{self, NoArgs},
|
||||
p2p, prune, recover, stage,
|
||||
};
|
||||
use reth_cli_runner::CliRunner;
|
||||
use reth_db::DatabaseEnv;
|
||||
use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
|
||||
use reth_network::EthNetworkPrimitives;
|
||||
use reth_node_builder::{NodeBuilder, WithLaunchContext};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider, EthereumNode};
|
||||
use reth_node_metrics::recorder::install_prometheus_recorder;
|
||||
use reth_tracing::FileWorkerGuard;
|
||||
use std::{ffi::OsString, fmt, future::Future, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
/// Re-export of the `reth_node_core` types specifically in the `cli` module.
|
||||
/// Re-export of the [`reth_node_core`] types specifically in the `cli` module.
|
||||
///
|
||||
/// This is re-exported because the types in `reth_node_core::cli` originally existed in
|
||||
/// `reth::cli` but were moved to the `reth_node_core` crate. This re-export avoids a breaking
|
||||
/// change.
|
||||
/// `reth::cli` but were moved to the [`reth_node_core`] crate. This re-export avoids a
|
||||
/// breaking change.
|
||||
pub use crate::core::cli::*;
|
||||
|
||||
/// The main reth cli interface.
|
||||
/// Re-export of the [`reth_ethereum_cli`] types specifically in the `interface` module.
|
||||
///
|
||||
/// This is the entrypoint to the executable.
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Reth", long_about = None)]
|
||||
pub struct Cli<C: ChainSpecParser = EthereumChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs>
|
||||
{
|
||||
/// The command to run
|
||||
#[command(subcommand)]
|
||||
pub command: Commands<C, Ext>,
|
||||
|
||||
/// The logging configuration for the CLI.
|
||||
#[command(flatten)]
|
||||
pub logs: LogArgs,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
/// Parsers only the default CLI arguments
|
||||
pub fn parse_args() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
|
||||
/// Parsers only the default CLI arguments from the given iterator
|
||||
pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
{
|
||||
Self::try_parse_from(itr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cli<C, Ext> {
|
||||
/// Execute the configured cli command.
|
||||
///
|
||||
/// This accepts a closure that is used to launch the node via the
|
||||
/// [`NodeCommand`](node::NodeCommand).
|
||||
///
|
||||
/// This command will be run on the [default tokio runtime](reth_cli_runner::tokio_runtime).
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use reth::cli::Cli;
|
||||
/// use reth_node_ethereum::EthereumNode;
|
||||
///
|
||||
/// Cli::parse_args()
|
||||
/// .run(async move |builder, _| {
|
||||
/// let handle = builder.launch_node(EthereumNode::default()).await?;
|
||||
///
|
||||
/// handle.wait_for_node_exit().await
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Parse additional CLI arguments for the node command and use it to configure the node.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use clap::Parser;
|
||||
/// use reth::cli::Cli;
|
||||
/// use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
|
||||
///
|
||||
/// #[derive(Debug, Parser)]
|
||||
/// pub struct MyArgs {
|
||||
/// pub enable: bool,
|
||||
/// }
|
||||
///
|
||||
/// Cli::<EthereumChainSpecParser, MyArgs>::parse()
|
||||
/// .run(async move |builder, my_args: MyArgs|
|
||||
/// // launch the node
|
||||
/// Ok(()))
|
||||
/// .unwrap();
|
||||
/// ````
|
||||
pub fn run<L, Fut>(self, launcher: L) -> eyre::Result<()>
|
||||
where
|
||||
L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
{
|
||||
self.with_runner(CliRunner::try_default_runtime()?, launcher)
|
||||
}
|
||||
|
||||
/// Execute the configured cli command with the provided [`CliRunner`].
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use reth::cli::Cli;
|
||||
/// use reth_cli_runner::CliRunner;
|
||||
/// use reth_node_ethereum::EthereumNode;
|
||||
///
|
||||
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(4)
|
||||
/// .max_blocking_threads(256)
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// let runner = CliRunner::from_runtime(runtime);
|
||||
///
|
||||
/// Cli::parse_args()
|
||||
/// .with_runner(runner, |builder, _| async move {
|
||||
/// let handle = builder.launch_node(EthereumNode::default()).await?;
|
||||
/// handle.wait_for_node_exit().await
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn with_runner<L, Fut>(mut self, runner: CliRunner, launcher: L) -> eyre::Result<()>
|
||||
where
|
||||
L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
{
|
||||
// Add network name if available to the logs dir
|
||||
if let Some(chain_spec) = self.command.chain_spec() {
|
||||
self.logs.log_file_directory =
|
||||
self.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let _guard = self.init_tracing()?;
|
||||
info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory);
|
||||
|
||||
// Install the prometheus recorder to be sure to record all metrics
|
||||
let _ = install_prometheus_recorder();
|
||||
|
||||
let components = |spec: Arc<C::ChainSpec>| {
|
||||
(EthExecutorProvider::ethereum(spec.clone()), EthBeaconConsensus::new(spec))
|
||||
};
|
||||
match self.command {
|
||||
Commands::Node(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
|
||||
}
|
||||
Commands::Init(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::InitState(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::Import(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode, _, _>(components))
|
||||
}
|
||||
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
|
||||
Commands::Db(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::Stage(command) => runner.run_command_until_exit(|ctx| {
|
||||
command.execute::<EthereumNode, _, _, EthNetworkPrimitives>(ctx, components)
|
||||
}),
|
||||
Commands::P2P(command) => {
|
||||
runner.run_until_ctrl_c(command.execute::<EthNetworkPrimitives>())
|
||||
}
|
||||
#[cfg(feature = "dev")]
|
||||
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::<EthereumNode>(ctx))
|
||||
}
|
||||
Commands::Recover(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute::<EthereumNode>(ctx))
|
||||
}
|
||||
Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<EthereumNode>()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes tracing with the configured options.
|
||||
///
|
||||
/// If file logging is enabled, this function returns a guard that must be kept alive to ensure
|
||||
/// that all logs are flushed to disk.
|
||||
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
|
||||
let guard = self.logs.init_tracing()?;
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
|
||||
/// Commands to be executed
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[expect(clippy::large_enum_variant)]
|
||||
pub enum Commands<C: ChainSpecParser, Ext: clap::Args + fmt::Debug> {
|
||||
/// Start the node
|
||||
#[command(name = "node")]
|
||||
Node(Box<node::NodeCommand<C, Ext>>),
|
||||
/// Initialize the database from a genesis file.
|
||||
#[command(name = "init")]
|
||||
Init(init_cmd::InitCommand<C>),
|
||||
/// Initialize the database from a state dump file.
|
||||
#[command(name = "init-state")]
|
||||
InitState(init_state::InitStateCommand<C>),
|
||||
/// This syncs RLP encoded blocks from a file.
|
||||
#[command(name = "import")]
|
||||
Import(import::ImportCommand<C>),
|
||||
/// Dumps genesis block JSON configuration to stdout.
|
||||
DumpGenesis(dump_genesis::DumpGenesisCommand<C>),
|
||||
/// Database debugging utilities
|
||||
#[command(name = "db")]
|
||||
Db(db::Command<C>),
|
||||
/// Manipulate individual stages.
|
||||
#[command(name = "stage")]
|
||||
Stage(stage::Command<C>),
|
||||
/// P2P Debugging utilities
|
||||
#[command(name = "p2p")]
|
||||
P2P(p2p::Command<C>),
|
||||
/// Generate Test Vectors
|
||||
#[cfg(feature = "dev")]
|
||||
#[command(name = "test-vectors")]
|
||||
TestVectors(reth_cli_commands::test_vectors::Command),
|
||||
/// Write config to stdout
|
||||
#[command(name = "config")]
|
||||
Config(config_cmd::Command),
|
||||
/// Various debug routines
|
||||
#[command(name = "debug")]
|
||||
Debug(Box<debug_cmd::Command<C>>),
|
||||
/// Scripts for node recovery
|
||||
#[command(name = "recover")]
|
||||
Recover(recover::Command<C>),
|
||||
/// Prune according to the configuration without any limits
|
||||
#[command(name = "prune")]
|
||||
Prune(prune::PruneCommand<C>),
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Commands<C, Ext> {
|
||||
/// Returns the underlying chain being used for commands
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
match self {
|
||||
Self::Node(cmd) => cmd.chain_spec(),
|
||||
Self::Init(cmd) => cmd.chain_spec(),
|
||||
Self::InitState(cmd) => cmd.chain_spec(),
|
||||
Self::Import(cmd) => cmd.chain_spec(),
|
||||
Self::DumpGenesis(cmd) => cmd.chain_spec(),
|
||||
Self::Db(cmd) => cmd.chain_spec(),
|
||||
Self::Stage(cmd) => cmd.chain_spec(),
|
||||
Self::P2P(cmd) => cmd.chain_spec(),
|
||||
#[cfg(feature = "dev")]
|
||||
Self::TestVectors(cmd) => cmd.chain_spec(),
|
||||
Self::Config(_) => None,
|
||||
Self::Debug(cmd) => cmd.chain_spec(),
|
||||
Self::Recover(cmd) => cmd.chain_spec(),
|
||||
Self::Prune(cmd) => cmd.chain_spec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::args::ColorMode;
|
||||
use clap::CommandFactory;
|
||||
use reth_ethereum_cli::chainspec::SUPPORTED_CHAINS;
|
||||
|
||||
#[test]
|
||||
fn parse_color_mode() {
|
||||
let reth = Cli::try_parse_args_from(["reth", "node", "--color", "always"]).unwrap();
|
||||
assert_eq!(reth.logs.color, ColorMode::Always);
|
||||
}
|
||||
|
||||
/// Tests that the help message is parsed correctly. This ensures that clap args are configured
|
||||
/// correctly and no conflicts are introduced via attributes that would result in a panic at
|
||||
/// runtime
|
||||
#[test]
|
||||
fn test_parse_help_all_subcommands() {
|
||||
let reth = Cli::<EthereumChainSpecParser, NoArgs>::command();
|
||||
for sub_command in reth.get_subcommands() {
|
||||
let err = Cli::try_parse_args_from(["reth", sub_command.get_name(), "--help"])
|
||||
.err()
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Failed to parse help message {}", sub_command.get_name())
|
||||
});
|
||||
|
||||
// --help is treated as error, but
|
||||
// > Not a true "error" as it means --help or similar was used. The help message will be sent to stdout.
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the log directory is parsed correctly when using the node command. It's
|
||||
/// always tied to the specific chain's name.
|
||||
#[test]
|
||||
fn parse_logs_path_node() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "node"]).unwrap();
|
||||
if let Some(chain_spec) = reth.command.chain_spec() {
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", SUPPORTED_CHAINS[0]);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
|
||||
let mut iter = SUPPORTED_CHAINS.iter();
|
||||
iter.next();
|
||||
for chain in iter {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "node", "--chain", chain]).unwrap();
|
||||
let chain =
|
||||
reth.command.chain_spec().map(|c| c.chain.to_string()).unwrap_or(String::new());
|
||||
reth.logs.log_file_directory = reth.logs.log_file_directory.join(chain.clone());
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", chain);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the log directory is parsed correctly when using the init command. It
|
||||
/// uses the underlying environment in command to get the chain.
|
||||
#[test]
|
||||
fn parse_logs_path_init() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "init"]).unwrap();
|
||||
if let Some(chain_spec) = reth.command.chain_spec() {
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", SUPPORTED_CHAINS[0]);
|
||||
println!("{:?}", log_dir);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
|
||||
/// Tests that the config command does not return any chain spec leading to empty chain id.
|
||||
#[test]
|
||||
fn parse_empty_logs_path() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "config"]).unwrap();
|
||||
if let Some(chain_spec) = reth.command.chain_spec() {
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = "reth/logs".to_string();
|
||||
println!("{:?}", log_dir);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_env_filter_directives() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
unsafe { std::env::set_var("RUST_LOG", "info,evm=debug") };
|
||||
let reth = Cli::try_parse_args_from([
|
||||
"reth",
|
||||
"init",
|
||||
"--datadir",
|
||||
temp_dir.path().to_str().unwrap(),
|
||||
"--log.file.filter",
|
||||
"debug,net=trace",
|
||||
])
|
||||
.unwrap();
|
||||
assert!(reth.run(async move |_, _| Ok(())).is_ok());
|
||||
}
|
||||
}
|
||||
/// This is re-exported because the types in [`reth_ethereum_cli::interface`] originally
|
||||
/// existed in `reth::cli` but were moved to the [`reth_ethereum_cli`] crate. This re-export
|
||||
/// avoids a breaking change.
|
||||
pub use reth_ethereum_cli::interface::*;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
//! This contains all of the `reth` commands
|
||||
|
||||
pub mod debug_cmd;
|
||||
@@ -28,7 +28,6 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
pub mod cli;
|
||||
pub mod commands;
|
||||
|
||||
/// Re-exported utils.
|
||||
pub mod utils {
|
||||
@@ -191,3 +190,7 @@ pub use reth_cli_runner::{tokio_runtime, CliContext, CliRunner};
|
||||
|
||||
// for rendering diagrams
|
||||
use aquamarine as _;
|
||||
|
||||
// used in main
|
||||
use clap as _;
|
||||
use reth_cli_util as _;
|
||||
|
||||
@@ -13,12 +13,106 @@ workspace = true
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-cli.workspace = true
|
||||
reth-cli-commands.workspace = true
|
||||
reth-cli-runner.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
reth-db.workspace = true
|
||||
reth-ethereum-primitives.workspace = true
|
||||
reth-network.workspace = true
|
||||
reth-node-builder.workspace = true
|
||||
reth-node-core.workspace = true
|
||||
reth-node-ethereum.workspace = true
|
||||
reth-node-metrics.workspace = true
|
||||
reth-tracing.workspace = true
|
||||
reth-db-api.workspace = true
|
||||
reth-consensus.workspace = true
|
||||
reth-errors.workspace = true
|
||||
reth-ethereum-payload-builder.workspace = true
|
||||
reth-evm.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
reth-fs-util.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
reth-basic-payload-builder.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-revm.workspace = true
|
||||
reth-stages.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-trie.workspace = true
|
||||
reth-trie-db.workspace = true
|
||||
reth-cli-util.workspace = true
|
||||
reth-config.workspace = true
|
||||
reth-downloaders.workspace = true
|
||||
reth-exex.workspace = true
|
||||
reth-network-api.workspace = true
|
||||
reth-network-p2p.workspace = true
|
||||
reth-node-events.workspace = true
|
||||
reth-prune.workspace = true
|
||||
reth-static-file.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
|
||||
# serde
|
||||
serde_json.workspace = true
|
||||
|
||||
# backoff
|
||||
backon.workspace = true
|
||||
|
||||
# test
|
||||
similar-asserts.workspace = true
|
||||
|
||||
# async
|
||||
tokio.workspace = true
|
||||
futures.workspace = true
|
||||
|
||||
# alloy
|
||||
alloy-eips = { workspace = true, features = ["kzg"] }
|
||||
alloy-rlp.workspace = true
|
||||
alloy-rpc-types = { workspace = true, features = ["engine"] }
|
||||
alloy-consensus.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
# misc
|
||||
clap.workspace = true
|
||||
eyre.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
clap.workspace = true
|
||||
# reth
|
||||
reth-cli-commands.workspace = true
|
||||
reth-ethereum-cli.workspace = true
|
||||
|
||||
# fs
|
||||
tempfile.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["jemalloc", "reth-revm/portable"]
|
||||
|
||||
dev = ["reth-cli-commands/arbitrary"]
|
||||
|
||||
asm-keccak = [
|
||||
"reth-node-core/asm-keccak",
|
||||
"alloy-primitives/asm-keccak",
|
||||
]
|
||||
|
||||
jemalloc = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-node-core/jemalloc",
|
||||
"reth-node-metrics/jemalloc",
|
||||
]
|
||||
jemalloc-prof = [
|
||||
"reth-cli-util/jemalloc",
|
||||
"reth-cli-util/jemalloc-prof",
|
||||
]
|
||||
tracy-allocator = ["reth-cli-util/tracy-allocator"]
|
||||
|
||||
# Because jemalloc is default and preferred over snmalloc when both features are
|
||||
# enabled, `--no-default-features` should be used when enabling snmalloc or
|
||||
# snmalloc-native.
|
||||
snmalloc = ["reth-cli-util/snmalloc"]
|
||||
snmalloc-native = ["reth-cli-util/snmalloc-native"]
|
||||
|
||||
min-error-logs = ["tracing/release_max_level_error"]
|
||||
min-warn-logs = ["tracing/release_max_level_warn"]
|
||||
min-info-logs = ["tracing/release_max_level_info"]
|
||||
min-debug-logs = ["tracing/release_max_level_debug"]
|
||||
min-trace-logs = ["tracing/release_max_level_trace"]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Command for debugging execution.
|
||||
|
||||
use crate::{args::NetworkArgs, utils::get_single_header};
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use alloy_primitives::{BlockNumber, B256};
|
||||
use clap::Parser;
|
||||
@@ -24,6 +23,7 @@ use reth_network::{BlockDownloaderProvider, NetworkHandle};
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_network_p2p::{headers::client::HeadersClient, EthBlockClient};
|
||||
use reth_node_api::NodeTypesWithDBAdapter;
|
||||
use reth_node_core::{args::NetworkArgs, utils::get_single_header};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
||||
use reth_node_events::node::NodeEvent;
|
||||
use reth_provider::{
|
||||
@@ -1,10 +1,5 @@
|
||||
//! Command for debugging in-memory merkle trie calculation.
|
||||
|
||||
use crate::{
|
||||
api::BlockTy,
|
||||
args::NetworkArgs,
|
||||
utils::{get_single_body, get_single_header},
|
||||
};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use backon::{ConstantBuilder, Retryable};
|
||||
@@ -20,7 +15,11 @@ use reth_evm::execute::{BlockExecutorProvider, Executor};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_network::{BlockDownloaderProvider, NetworkHandle};
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_node_api::{BlockTy, NodePrimitives};
|
||||
use reth_node_core::{
|
||||
args::NetworkArgs,
|
||||
utils::{get_single_body, get_single_header},
|
||||
};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
||||
use reth_primitives_traits::SealedBlock;
|
||||
use reth_provider::{
|
||||
@@ -1,5 +1,4 @@
|
||||
//! Command for debugging merkle tree calculation.
|
||||
use crate::{args::NetworkArgs, providers::ExecutionOutcome, utils::get_single_header};
|
||||
use alloy_eips::BlockHashOrNumber;
|
||||
use backon::{ConstantBuilder, Retryable};
|
||||
use clap::Parser;
|
||||
@@ -13,10 +12,12 @@ use reth_consensus::{Consensus, ConsensusError};
|
||||
use reth_db_api::{cursor::DbCursorRO, tables, transaction::DbTx};
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_evm::execute::{BlockExecutorProvider, Executor};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_network::{BlockDownloaderProvider, NetworkHandle};
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_network_p2p::full_block::FullBlockClient;
|
||||
use reth_node_api::{BlockTy, NodePrimitives};
|
||||
use reth_node_core::{args::NetworkArgs, utils::get_single_header};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider};
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, BlockNumReader, BlockWriter, ChainSpecProvider,
|
||||
369
crates/ethereum/cli/src/interface.rs
Normal file
369
crates/ethereum/cli/src/interface.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
//! CLI definition and entrypoint to executable
|
||||
|
||||
use crate::{chainspec::EthereumChainSpecParser, debug_cmd};
|
||||
use clap::{Parser, Subcommand};
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::{
|
||||
config_cmd, db, dump_genesis, import, init_cmd, init_state,
|
||||
node::{self, NoArgs},
|
||||
p2p, prune, recover, stage,
|
||||
};
|
||||
use reth_cli_runner::CliRunner;
|
||||
use reth_db::DatabaseEnv;
|
||||
use reth_network::EthNetworkPrimitives;
|
||||
use reth_node_builder::{NodeBuilder, WithLaunchContext};
|
||||
use reth_node_core::{
|
||||
args::LogArgs,
|
||||
version::{LONG_VERSION, SHORT_VERSION},
|
||||
};
|
||||
use reth_node_ethereum::{consensus::EthBeaconConsensus, EthExecutorProvider, EthereumNode};
|
||||
use reth_node_metrics::recorder::install_prometheus_recorder;
|
||||
use reth_tracing::FileWorkerGuard;
|
||||
use std::{ffi::OsString, fmt, future::Future, sync::Arc};
|
||||
use tracing::info;
|
||||
|
||||
/// The main reth cli interface.
|
||||
///
|
||||
/// This is the entrypoint to the executable.
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Reth", long_about = None)]
|
||||
pub struct Cli<C: ChainSpecParser = EthereumChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs>
|
||||
{
|
||||
/// The command to run
|
||||
#[command(subcommand)]
|
||||
pub command: Commands<C, Ext>,
|
||||
|
||||
/// The logging configuration for the CLI.
|
||||
#[command(flatten)]
|
||||
pub logs: LogArgs,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
/// Parsers only the default CLI arguments
|
||||
pub fn parse_args() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
|
||||
/// Parsers only the default CLI arguments from the given iterator
|
||||
pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
{
|
||||
Self::try_parse_from(itr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cli<C, Ext> {
|
||||
/// Execute the configured cli command.
|
||||
///
|
||||
/// This accepts a closure that is used to launch the node via the
|
||||
/// [`NodeCommand`](node::NodeCommand).
|
||||
///
|
||||
/// This command will be run on the [default tokio runtime](reth_cli_runner::tokio_runtime).
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use reth_ethereum_cli::interface::Cli;
|
||||
/// use reth_node_ethereum::EthereumNode;
|
||||
///
|
||||
/// Cli::parse_args()
|
||||
/// .run(async move |builder, _| {
|
||||
/// let handle = builder.launch_node(EthereumNode::default()).await?;
|
||||
///
|
||||
/// handle.wait_for_node_exit().await
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Parse additional CLI arguments for the node command and use it to configure the node.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use clap::Parser;
|
||||
/// use reth_ethereum_cli::{chainspec::EthereumChainSpecParser, interface::Cli};
|
||||
///
|
||||
/// #[derive(Debug, Parser)]
|
||||
/// pub struct MyArgs {
|
||||
/// pub enable: bool,
|
||||
/// }
|
||||
///
|
||||
/// Cli::<EthereumChainSpecParser, MyArgs>::parse()
|
||||
/// .run(async move |builder, my_args: MyArgs|
|
||||
/// // launch the node
|
||||
/// Ok(()))
|
||||
/// .unwrap();
|
||||
/// ````
|
||||
pub fn run<L, Fut>(self, launcher: L) -> eyre::Result<()>
|
||||
where
|
||||
L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
{
|
||||
self.with_runner(CliRunner::try_default_runtime()?, launcher)
|
||||
}
|
||||
|
||||
/// Execute the configured cli command with the provided [`CliRunner`].
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use reth_cli_runner::CliRunner;
|
||||
/// use reth_ethereum_cli::interface::Cli;
|
||||
/// use reth_node_ethereum::EthereumNode;
|
||||
///
|
||||
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(4)
|
||||
/// .max_blocking_threads(256)
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// let runner = CliRunner::from_runtime(runtime);
|
||||
///
|
||||
/// Cli::parse_args()
|
||||
/// .with_runner(runner, |builder, _| async move {
|
||||
/// let handle = builder.launch_node(EthereumNode::default()).await?;
|
||||
/// handle.wait_for_node_exit().await
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn with_runner<L, Fut>(mut self, runner: CliRunner, launcher: L) -> eyre::Result<()>
|
||||
where
|
||||
L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
|
||||
Fut: Future<Output = eyre::Result<()>>,
|
||||
{
|
||||
// Add network name if available to the logs dir
|
||||
if let Some(chain_spec) = self.command.chain_spec() {
|
||||
self.logs.log_file_directory =
|
||||
self.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let _guard = self.init_tracing()?;
|
||||
info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory);
|
||||
|
||||
// Install the prometheus recorder to be sure to record all metrics
|
||||
let _ = install_prometheus_recorder();
|
||||
|
||||
let components = |spec: Arc<C::ChainSpec>| {
|
||||
(EthExecutorProvider::ethereum(spec.clone()), EthBeaconConsensus::new(spec))
|
||||
};
|
||||
match self.command {
|
||||
Commands::Node(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
|
||||
}
|
||||
Commands::Init(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::InitState(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::Import(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode, _, _>(components))
|
||||
}
|
||||
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
|
||||
Commands::Db(command) => {
|
||||
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
|
||||
}
|
||||
Commands::Stage(command) => runner.run_command_until_exit(|ctx| {
|
||||
command.execute::<EthereumNode, _, _, EthNetworkPrimitives>(ctx, components)
|
||||
}),
|
||||
Commands::P2P(command) => {
|
||||
runner.run_until_ctrl_c(command.execute::<EthNetworkPrimitives>())
|
||||
}
|
||||
#[cfg(feature = "dev")]
|
||||
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::<EthereumNode>(ctx))
|
||||
}
|
||||
Commands::Recover(command) => {
|
||||
runner.run_command_until_exit(|ctx| command.execute::<EthereumNode>(ctx))
|
||||
}
|
||||
Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<EthereumNode>()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes tracing with the configured options.
|
||||
///
|
||||
/// If file logging is enabled, this function returns a guard that must be kept alive to ensure
|
||||
/// that all logs are flushed to disk.
|
||||
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
|
||||
let guard = self.logs.init_tracing()?;
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
|
||||
/// Commands to be executed
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[expect(clippy::large_enum_variant)]
|
||||
pub enum Commands<C: ChainSpecParser, Ext: clap::Args + fmt::Debug> {
|
||||
/// Start the node
|
||||
#[command(name = "node")]
|
||||
Node(Box<node::NodeCommand<C, Ext>>),
|
||||
/// Initialize the database from a genesis file.
|
||||
#[command(name = "init")]
|
||||
Init(init_cmd::InitCommand<C>),
|
||||
/// Initialize the database from a state dump file.
|
||||
#[command(name = "init-state")]
|
||||
InitState(init_state::InitStateCommand<C>),
|
||||
/// This syncs RLP encoded blocks from a file.
|
||||
#[command(name = "import")]
|
||||
Import(import::ImportCommand<C>),
|
||||
/// Dumps genesis block JSON configuration to stdout.
|
||||
DumpGenesis(dump_genesis::DumpGenesisCommand<C>),
|
||||
/// Database debugging utilities
|
||||
#[command(name = "db")]
|
||||
Db(db::Command<C>),
|
||||
/// Manipulate individual stages.
|
||||
#[command(name = "stage")]
|
||||
Stage(stage::Command<C>),
|
||||
/// P2P Debugging utilities
|
||||
#[command(name = "p2p")]
|
||||
P2P(p2p::Command<C>),
|
||||
/// Generate Test Vectors
|
||||
#[cfg(feature = "dev")]
|
||||
#[command(name = "test-vectors")]
|
||||
TestVectors(reth_cli_commands::test_vectors::Command),
|
||||
/// Write config to stdout
|
||||
#[command(name = "config")]
|
||||
Config(config_cmd::Command),
|
||||
/// Various debug routines
|
||||
#[command(name = "debug")]
|
||||
Debug(Box<debug_cmd::Command<C>>),
|
||||
/// Scripts for node recovery
|
||||
#[command(name = "recover")]
|
||||
Recover(recover::Command<C>),
|
||||
/// Prune according to the configuration without any limits
|
||||
#[command(name = "prune")]
|
||||
Prune(prune::PruneCommand<C>),
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Commands<C, Ext> {
|
||||
/// Returns the underlying chain being used for commands
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
match self {
|
||||
Self::Node(cmd) => cmd.chain_spec(),
|
||||
Self::Init(cmd) => cmd.chain_spec(),
|
||||
Self::InitState(cmd) => cmd.chain_spec(),
|
||||
Self::Import(cmd) => cmd.chain_spec(),
|
||||
Self::DumpGenesis(cmd) => cmd.chain_spec(),
|
||||
Self::Db(cmd) => cmd.chain_spec(),
|
||||
Self::Stage(cmd) => cmd.chain_spec(),
|
||||
Self::P2P(cmd) => cmd.chain_spec(),
|
||||
#[cfg(feature = "dev")]
|
||||
Self::TestVectors(cmd) => cmd.chain_spec(),
|
||||
Self::Config(_) => None,
|
||||
Self::Debug(cmd) => cmd.chain_spec(),
|
||||
Self::Recover(cmd) => cmd.chain_spec(),
|
||||
Self::Prune(cmd) => cmd.chain_spec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chainspec::SUPPORTED_CHAINS;
|
||||
use clap::CommandFactory;
|
||||
use reth_node_core::args::ColorMode;
|
||||
|
||||
#[test]
|
||||
fn parse_color_mode() {
|
||||
let reth = Cli::try_parse_args_from(["reth", "node", "--color", "always"]).unwrap();
|
||||
assert_eq!(reth.logs.color, ColorMode::Always);
|
||||
}
|
||||
|
||||
/// Tests that the help message is parsed correctly. This ensures that clap args are configured
|
||||
/// correctly and no conflicts are introduced via attributes that would result in a panic at
|
||||
/// runtime
|
||||
#[test]
|
||||
fn test_parse_help_all_subcommands() {
|
||||
let reth = Cli::<EthereumChainSpecParser, NoArgs>::command();
|
||||
for sub_command in reth.get_subcommands() {
|
||||
let err = Cli::try_parse_args_from(["reth", sub_command.get_name(), "--help"])
|
||||
.err()
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Failed to parse help message {}", sub_command.get_name())
|
||||
});
|
||||
|
||||
// --help is treated as error, but
|
||||
// > Not a true "error" as it means --help or similar was used. The help message will be sent to stdout.
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the log directory is parsed correctly when using the node command. It's
|
||||
/// always tied to the specific chain's name.
|
||||
#[test]
|
||||
fn parse_logs_path_node() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "node"]).unwrap();
|
||||
if let Some(chain_spec) = reth.command.chain_spec() {
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", SUPPORTED_CHAINS[0]);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
|
||||
let mut iter = SUPPORTED_CHAINS.iter();
|
||||
iter.next();
|
||||
for chain in iter {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "node", "--chain", chain]).unwrap();
|
||||
let chain =
|
||||
reth.command.chain_spec().map(|c| c.chain.to_string()).unwrap_or(String::new());
|
||||
reth.logs.log_file_directory = reth.logs.log_file_directory.join(chain.clone());
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", chain);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the log directory is parsed correctly when using the init command. It
|
||||
/// uses the underlying environment in command to get the chain.
|
||||
#[test]
|
||||
fn parse_logs_path_init() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "init"]).unwrap();
|
||||
if let Some(chain_spec) = reth.command.chain_spec() {
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = format!("reth/logs/{}", SUPPORTED_CHAINS[0]);
|
||||
println!("{:?}", log_dir);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
|
||||
/// Tests that the config command does not return any chain spec leading to empty chain id.
|
||||
#[test]
|
||||
fn parse_empty_logs_path() {
|
||||
let mut reth = Cli::try_parse_args_from(["reth", "config"]).unwrap();
|
||||
if let Some(chain_spec) = reth.command.chain_spec() {
|
||||
reth.logs.log_file_directory =
|
||||
reth.logs.log_file_directory.join(chain_spec.chain.to_string());
|
||||
}
|
||||
let log_dir = reth.logs.log_file_directory;
|
||||
let end = "reth/logs".to_string();
|
||||
println!("{:?}", log_dir);
|
||||
assert!(log_dir.as_ref().ends_with(end), "{log_dir:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_env_filter_directives() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
|
||||
unsafe { std::env::set_var("RUST_LOG", "info,evm=debug") };
|
||||
let reth = Cli::try_parse_args_from([
|
||||
"reth",
|
||||
"init",
|
||||
"--datadir",
|
||||
temp_dir.path().to_str().unwrap(),
|
||||
"--log.file.filter",
|
||||
"debug,net=trace",
|
||||
])
|
||||
.unwrap();
|
||||
assert!(reth.run(async move |_, _| Ok(())).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,15 @@
|
||||
|
||||
/// Chain specification parser.
|
||||
pub mod chainspec;
|
||||
pub mod debug_cmd;
|
||||
pub mod interface;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::chainspec::EthereumChainSpecParser;
|
||||
use clap::Parser;
|
||||
use reth_chainspec::DEV;
|
||||
use reth_cli_commands::NodeCommand;
|
||||
use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
|
||||
|
||||
#[test]
|
||||
#[ignore = "reth cmd will print op-reth output if optimism feature enabled"]
|
||||
|
||||
Reference in New Issue
Block a user