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