reth_optimism_cli/
app.rs

1use crate::{Cli, Commands};
2use eyre::{eyre, Result};
3use reth_cli::chainspec::ChainSpecParser;
4use reth_cli_commands::launcher::Launcher;
5use reth_cli_runner::CliRunner;
6use reth_node_metrics::recorder::install_prometheus_recorder;
7use reth_optimism_chainspec::OpChainSpec;
8use reth_optimism_consensus::OpBeaconConsensus;
9use reth_optimism_node::{OpExecutorProvider, OpNode};
10use reth_rpc_server_types::RpcModuleValidator;
11use reth_tracing::{FileWorkerGuard, Layers};
12use std::{fmt, sync::Arc};
13use tracing::info;
14
15/// A wrapper around a parsed CLI that handles command execution.
16#[derive(Debug)]
17pub struct CliApp<Spec: ChainSpecParser, Ext: clap::Args + fmt::Debug, Rpc: RpcModuleValidator> {
18    cli: Cli<Spec, Ext, Rpc>,
19    runner: Option<CliRunner>,
20    layers: Option<Layers>,
21    guard: Option<FileWorkerGuard>,
22}
23
24impl<C, Ext, Rpc> CliApp<C, Ext, Rpc>
25where
26    C: ChainSpecParser<ChainSpec = OpChainSpec>,
27    Ext: clap::Args + fmt::Debug,
28    Rpc: RpcModuleValidator,
29{
30    pub(crate) fn new(cli: Cli<C, Ext, Rpc>) -> Self {
31        Self { cli, runner: None, layers: Some(Layers::new()), guard: None }
32    }
33
34    /// Sets the runner for the CLI commander.
35    ///
36    /// This replaces any existing runner with the provided one.
37    pub fn set_runner(&mut self, runner: CliRunner) {
38        self.runner = Some(runner);
39    }
40
41    /// Access to tracing layers.
42    ///
43    /// Returns a mutable reference to the tracing layers, or error
44    /// if tracing initialized and layers have detached already.
45    pub fn access_tracing_layers(&mut self) -> Result<&mut Layers> {
46        self.layers.as_mut().ok_or_else(|| eyre!("Tracing already initialized"))
47    }
48
49    /// Execute the configured cli command.
50    ///
51    /// This accepts a closure that is used to launch the node via the
52    /// [`NodeCommand`](reth_cli_commands::node::NodeCommand).
53    pub fn run(mut self, launcher: impl Launcher<C, Ext>) -> Result<()> {
54        let runner = match self.runner.take() {
55            Some(runner) => runner,
56            None => CliRunner::try_default_runtime()?,
57        };
58
59        // add network name to logs dir
60        // Add network name if available to the logs dir
61        if let Some(chain_spec) = self.cli.command.chain_spec() {
62            self.cli.logs.log_file_directory =
63                self.cli.logs.log_file_directory.join(chain_spec.chain.to_string());
64        }
65
66        self.init_tracing()?;
67        // Install the prometheus recorder to be sure to record all metrics
68        let _ = install_prometheus_recorder();
69
70        let components = |spec: Arc<OpChainSpec>| {
71            (OpExecutorProvider::optimism(spec.clone()), Arc::new(OpBeaconConsensus::new(spec)))
72        };
73
74        match self.cli.command {
75            Commands::Node(command) => {
76                // Validate RPC modules using the configured validator
77                if let Some(http_api) = &command.rpc.http_api {
78                    Rpc::validate_selection(http_api, "http.api").map_err(|e| eyre!("{e}"))?;
79                }
80                if let Some(ws_api) = &command.rpc.ws_api {
81                    Rpc::validate_selection(ws_api, "ws.api").map_err(|e| eyre!("{e}"))?;
82                }
83
84                runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
85            }
86            Commands::Init(command) => {
87                runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
88            }
89            Commands::InitState(command) => {
90                runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
91            }
92            Commands::ImportOp(command) => {
93                runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
94            }
95            Commands::ImportReceiptsOp(command) => {
96                runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
97            }
98            Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
99            Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute::<OpNode>()),
100            Commands::Stage(command) => {
101                runner.run_command_until_exit(|ctx| command.execute::<OpNode, _>(ctx, components))
102            }
103            Commands::P2P(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
104            Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
105            Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
106            #[cfg(feature = "dev")]
107            Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
108            Commands::ReExecute(command) => {
109                runner.run_until_ctrl_c(command.execute::<OpNode>(components))
110            }
111        }
112    }
113
114    /// Initializes tracing with the configured options.
115    ///
116    /// If file logging is enabled, this function stores guard to the struct.
117    pub fn init_tracing(&mut self) -> Result<()> {
118        if self.guard.is_none() {
119            let layers = self.layers.take().unwrap_or_default();
120            self.guard = self.cli.logs.init_tracing_with_layers(layers)?;
121            info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.cli.logs.log_file_directory);
122        }
123        Ok(())
124    }
125}