1#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
5 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
6 issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
7)]
8#![cfg_attr(not(test), warn(unused_crate_dependencies))]
9#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
10
11pub mod chainspec;
13pub mod commands;
15pub mod receipt_file_codec;
27
28pub mod ovm_file_codec;
32
33pub use commands::{import::ImportOpCommand, import_receipts::ImportReceiptsOpCommand};
34use reth_optimism_chainspec::OpChainSpec;
35
36use std::{ffi::OsString, fmt, sync::Arc};
37
38use chainspec::OpChainSpecParser;
39use clap::{command, Parser};
40use commands::Commands;
41use futures_util::Future;
42use reth_cli::chainspec::ChainSpecParser;
43use reth_cli_runner::CliRunner;
44use reth_db::DatabaseEnv;
45use reth_node_builder::{NodeBuilder, WithLaunchContext};
46use reth_node_core::{
47 args::LogArgs,
48 version::{LONG_VERSION, SHORT_VERSION},
49};
50use reth_optimism_consensus::OpBeaconConsensus;
51use reth_optimism_evm::OpExecutorProvider;
52use reth_optimism_node::{args::RollupArgs, OpNetworkPrimitives, OpNode};
53use reth_tracing::FileWorkerGuard;
54use tracing::info;
55
56use reth_node_metrics as _;
59use reth_node_metrics::recorder::install_prometheus_recorder;
60
61#[derive(Debug, Parser)]
65#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Reth", long_about = None)]
66pub struct Cli<Spec: ChainSpecParser = OpChainSpecParser, Ext: clap::Args + fmt::Debug = RollupArgs>
67{
68 #[command(subcommand)]
70 pub command: Commands<Spec, Ext>,
71
72 #[command(flatten)]
74 pub logs: LogArgs,
75}
76
77impl Cli {
78 pub fn parse_args() -> Self {
80 Self::parse()
81 }
82
83 pub fn try_parse_args_from<I, T>(itr: I) -> Result<Self, clap::error::Error>
85 where
86 I: IntoIterator<Item = T>,
87 T: Into<OsString> + Clone,
88 {
89 Self::try_parse_from(itr)
90 }
91}
92
93impl<C, Ext> Cli<C, Ext>
94where
95 C: ChainSpecParser<ChainSpec = OpChainSpec>,
96 Ext: clap::Args + fmt::Debug,
97{
98 pub fn run<L, Fut>(self, launcher: L) -> eyre::Result<()>
103 where
104 L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
105 Fut: Future<Output = eyre::Result<()>>,
106 {
107 self.with_runner(CliRunner::try_default_runtime()?, launcher)
108 }
109
110 pub fn with_runner<L, Fut>(mut self, runner: CliRunner, launcher: L) -> eyre::Result<()>
112 where
113 L: FnOnce(WithLaunchContext<NodeBuilder<Arc<DatabaseEnv>, C::ChainSpec>>, Ext) -> Fut,
114 Fut: Future<Output = eyre::Result<()>>,
115 {
116 if let Some(chain_spec) = self.command.chain_spec() {
119 self.logs.log_file_directory =
120 self.logs.log_file_directory.join(chain_spec.chain.to_string());
121 }
122 let _guard = self.init_tracing()?;
123 info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory);
124
125 let _ = install_prometheus_recorder();
127
128 match self.command {
129 Commands::Node(command) => {
130 runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
131 }
132 Commands::Init(command) => {
133 runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
134 }
135 Commands::InitState(command) => {
136 runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
137 }
138 Commands::ImportOp(command) => {
139 runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
140 }
141 Commands::ImportReceiptsOp(command) => {
142 runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
143 }
144 Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
145 Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute::<OpNode>()),
146 Commands::Stage(command) => runner.run_command_until_exit(|ctx| {
147 command.execute::<OpNode, _, _, OpNetworkPrimitives>(ctx, |spec| {
148 (OpExecutorProvider::optimism(spec.clone()), OpBeaconConsensus::new(spec))
149 })
150 }),
151 Commands::P2P(command) => {
152 runner.run_until_ctrl_c(command.execute::<OpNetworkPrimitives>())
153 }
154 Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
155 Commands::Recover(command) => {
156 runner.run_command_until_exit(|ctx| command.execute::<OpNode>(ctx))
157 }
158 Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
159 #[cfg(feature = "dev")]
160 Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
161 }
162 }
163
164 pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
169 let guard = self.logs.init_tracing()?;
170 Ok(guard)
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use crate::{chainspec::OpChainSpecParser, commands::Commands, Cli};
177 use clap::Parser;
178 use reth_cli_commands::{node::NoArgs, NodeCommand};
179 use reth_optimism_chainspec::{BASE_MAINNET, OP_DEV};
180 use reth_optimism_node::args::RollupArgs;
181
182 #[test]
183 fn parse_dev() {
184 let cmd = NodeCommand::<OpChainSpecParser, NoArgs>::parse_from(["op-reth", "--dev"]);
185 let chain = OP_DEV.clone();
186 assert_eq!(cmd.chain.chain, chain.chain);
187 assert_eq!(cmd.chain.genesis_hash(), chain.genesis_hash());
188 assert_eq!(
189 cmd.chain.paris_block_and_final_difficulty,
190 chain.paris_block_and_final_difficulty
191 );
192 assert_eq!(cmd.chain.hardforks, chain.hardforks);
193
194 assert!(cmd.rpc.http);
195 assert!(cmd.network.discovery.disable_discovery);
196
197 assert!(cmd.dev.dev);
198 }
199
200 #[test]
201 fn parse_node() {
202 let cmd = Cli::<OpChainSpecParser, RollupArgs>::parse_from([
203 "op-reth",
204 "node",
205 "--chain",
206 "base",
207 "--datadir",
208 "/mnt/datadirs/base",
209 "--instance",
210 "2",
211 "--http",
212 "--http.addr",
213 "0.0.0.0",
214 "--ws",
215 "--ws.addr",
216 "0.0.0.0",
217 "--http.api",
218 "admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots",
219 "--rollup.sequencer-http",
220 "https://mainnet-sequencer.base.org",
221 "--rpc-max-tracing-requests",
222 "1000000",
223 "--rpc.gascap",
224 "18446744073709551615",
225 "--rpc.max-connections",
226 "429496729",
227 "--rpc.max-logs-per-response",
228 "0",
229 "--rpc.max-subscriptions-per-connection",
230 "10000",
231 "--metrics",
232 "9003",
233 "--log.file.max-size",
234 "100",
235 ]);
236
237 match cmd.command {
238 Commands::Node(command) => {
239 assert_eq!(command.chain.as_ref(), BASE_MAINNET.as_ref());
240 }
241 _ => panic!("unexpected command"),
242 }
243 }
244}