11//! Contains the CLI entry point for the Base consensus binary.
22
3- use base_cli_utils:: GlobalArgs ;
3+ use std:: { fs:: File , path:: PathBuf , sync:: Arc , time:: Duration } ;
4+
5+ use base_cli_utils:: { CliStyles , GlobalArgs , LogConfig , RuntimeManager } ;
6+ use base_client_cli:: {
7+ BuilderClientArgs , JwtSecret , L1ClientArgs , L2ClientArgs , P2PArgs , RollupBoostFlags , RpcArgs ,
8+ SequencerArgs ,
9+ } ;
10+ use base_jwt:: JwtValidator ;
411use clap:: Parser ;
12+ use eyre:: bail;
13+ use kona_genesis:: { L1ChainConfig , RollupConfig } ;
14+ use kona_node_service:: { EngineConfig , L1ConfigBuilder , NodeMode , RollupNodeBuilder } ;
15+ use kona_registry:: { L1Config , scr_rollup_config_by_alloy_ident} ;
16+ use serde_json:: from_reader;
17+ use strum:: IntoEnumIterator ;
18+ use tracing:: { debug, error, info} ;
519
620use crate :: version;
721
@@ -11,24 +25,217 @@ use crate::version;
1125 author,
1226 version = version:: SHORT_VERSION ,
1327 long_version = version:: LONG_VERSION ,
28+ styles = CliStyles :: init( ) ,
1429 about,
1530 long_about = None
1631) ]
1732pub struct Cli {
1833 /// Global arguments for the Base Consensus CLI.
1934 #[ command( flatten) ]
2035 pub global : GlobalArgs ,
36+ /// The mode to run the node in.
37+ #[ arg(
38+ long = "mode" ,
39+ default_value_t = NodeMode :: Validator ,
40+ env = "KONA_NODE_MODE" ,
41+ help = format!(
42+ "The mode to run the node in. Supported modes are: {}" ,
43+ NodeMode :: iter( )
44+ . map( |mode| format!( "\" {}\" " , mode. to_string( ) ) )
45+ . collect:: <Vec <_>>( )
46+ . join( ", " )
47+ )
48+ ) ]
49+ pub node_mode : NodeMode ,
50+
51+ /// L1 RPC CLI arguments.
52+ #[ clap( flatten) ]
53+ pub l1_rpc_args : L1ClientArgs ,
54+
55+ /// L2 engine CLI arguments.
56+ #[ clap( flatten) ]
57+ pub l2_client_args : L2ClientArgs ,
58+
59+ /// Optional block builder client.
60+ #[ clap( flatten) ]
61+ pub builder_client_args : BuilderClientArgs ,
62+
63+ /// Path to a custom L2 rollup configuration file
64+ /// (overrides the default rollup configuration from the registry)
65+ #[ arg( long, visible_alias = "rollup-cfg" , env = "KONA_NODE_ROLLUP_CONFIG" ) ]
66+ pub l2_config_file : Option < PathBuf > ,
67+ /// Path to a custom L1 rollup configuration file
68+ /// (overrides the default rollup configuration from the registry)
69+ #[ arg( long, visible_alias = "rollup-l1-cfg" , env = "KONA_NODE_L1_CHAIN_CONFIG" ) ]
70+ pub l1_config_file : Option < PathBuf > ,
71+ /// P2P CLI arguments.
72+ #[ command( flatten) ]
73+ pub p2p_flags : P2PArgs ,
74+ /// RPC CLI arguments.
75+ #[ command( flatten) ]
76+ pub rpc_flags : RpcArgs ,
77+ /// SEQUENCER CLI arguments.
78+ #[ command( flatten) ]
79+ pub sequencer_flags : SequencerArgs ,
80+ /// Rollup Boost CLI arguments.
81+ #[ command( flatten) ]
82+ pub rollup_boost_flags : RollupBoostFlags ,
2183}
2284
2385impl Cli {
24- /// Parse the CLI arguments.
25- pub fn parse ( ) -> Self {
26- <Self as Parser >:: parse ( )
86+ /// Runs the CLI.
87+ pub fn run ( self ) -> eyre:: Result < ( ) > {
88+ // Initialize telemetry - allow subcommands to customize the filter.
89+ Self :: init_logs ( & self . global ) ?;
90+
91+ // Initialize unified metrics
92+ self . global . metrics . init_with ( || {
93+ kona_gossip:: Metrics :: init ( ) ;
94+ kona_disc:: Metrics :: init ( ) ;
95+ kona_engine:: Metrics :: init ( ) ;
96+ kona_node_service:: Metrics :: init ( ) ;
97+ kona_derive:: Metrics :: init ( ) ;
98+ kona_providers_alloy:: Metrics :: init ( ) ;
99+ version:: VersionInfo :: from_build ( ) . register_version_metrics ( ) ;
100+ } ) ?;
101+
102+ // Run the subcommand.
103+ RuntimeManager :: run_until_ctrl_c ( self . exec ( & self . global ) )
27104 }
28105
29- /// Run the CLI.
30- pub fn run ( self ) -> eyre:: Result < ( ) > {
31- // TODO: Implement the CLI logic
106+ /// Run the Node subcommand.
107+ pub async fn exec ( & self , args : & GlobalArgs ) -> eyre:: Result < ( ) > {
108+ let cfg = self . get_l2_config ( args) ?;
109+
110+ info ! (
111+ target: "rollup_node" ,
112+ chain_id = cfg. l2_chain_id. id( ) ,
113+ "Starting rollup node services"
114+ ) ;
115+ for hf in cfg. hardforks . to_string ( ) . lines ( ) {
116+ info ! ( target: "rollup_node" , "{hf}" ) ;
117+ }
118+
119+ let l1_config = L1ConfigBuilder {
120+ chain_config : self . get_l1_config ( cfg. l1_chain_id ) ?,
121+ trust_rpc : self . l1_rpc_args . l1_trust_rpc ,
122+ beacon : self . l1_rpc_args . l1_beacon . clone ( ) ,
123+ rpc_url : self . l1_rpc_args . l1_eth_rpc . clone ( ) ,
124+ slot_duration_override : self . l1_rpc_args . l1_slot_duration_override ,
125+ } ;
126+
127+ // TODO: If metrics are enabled, initialize the global cli metrics.
128+ // args.metrics.enabled.then(|| init_rollup_config_metrics(&cfg));
129+
130+ let jwt_secret = self . validate_jwt ( ) . await ?;
131+
132+ self . p2p_flags . check_ports ( ) ?;
133+ let genesis_signer = args. genesis_signer ( ) . ok ( ) ;
134+ let p2p_config = self
135+ . p2p_flags
136+ . clone ( )
137+ . config (
138+ & cfg,
139+ args. l2_chain_id . into ( ) ,
140+ Some ( self . l1_rpc_args . l1_eth_rpc . clone ( ) ) ,
141+ genesis_signer,
142+ )
143+ . await ?;
144+ let rpc_config = self . rpc_flags . clone ( ) . into ( ) ;
145+
146+ let engine_config = EngineConfig {
147+ config : Arc :: new ( cfg. clone ( ) ) ,
148+ builder_url : self . builder_client_args . l2_builder_rpc . clone ( ) ,
149+ builder_jwt_secret : self
150+ . builder_client_args
151+ . jwt_secret ( )
152+ . map_err ( |e| eyre:: eyre!( e) ) ?,
153+ builder_timeout : Duration :: from_millis ( self . builder_client_args . builder_timeout ) ,
154+ l2_url : self . l2_client_args . l2_engine_rpc . clone ( ) ,
155+ l2_jwt_secret : jwt_secret,
156+ l2_timeout : Duration :: from_millis ( self . l2_client_args . l2_engine_timeout ) ,
157+ l1_url : self . l1_rpc_args . l1_eth_rpc . clone ( ) ,
158+ mode : self . node_mode ,
159+ rollup_boost : self . rollup_boost_flags . clone ( ) . as_rollup_boost_args ( ) ,
160+ } ;
161+
162+ RollupNodeBuilder :: new (
163+ cfg,
164+ l1_config,
165+ self . l2_client_args . l2_trust_rpc ,
166+ engine_config,
167+ p2p_config,
168+ rpc_config,
169+ )
170+ . with_sequencer_config ( self . sequencer_flags . config ( ) )
171+ . build ( )
172+ . start ( )
173+ . await
174+ . map_err ( |e| {
175+ error ! ( target: "rollup_node" , "Failed to start rollup node service: {e}" ) ;
176+ eyre:: eyre!( "{e}" )
177+ } ) ?;
178+
32179 Ok ( ( ) )
33180 }
181+
182+ /// Validate the jwt secret if specified by exchanging capabilities with the engine.
183+ /// Since the engine client will fail if the jwt token is invalid, this allows to ensure
184+ /// that the jwt token passed as a cli arg is correct.
185+ pub async fn validate_jwt ( & self ) -> eyre:: Result < JwtSecret > {
186+ let jwt_secret = self . l2_client_args . jwt_secret ( ) . map_err ( |e| eyre:: eyre!( e) ) ?;
187+ let validator = JwtValidator :: new ( jwt_secret) ;
188+ validator
189+ . validate_with_engine ( self . l2_client_args . l2_engine_rpc . clone ( ) )
190+ . await
191+ . map_err ( |e| eyre:: eyre!( e) )
192+ }
193+
194+ /// Get the L1 config, either from a file or the known chains.
195+ pub fn get_l1_config ( & self , l1_chain_id : u64 ) -> eyre:: Result < L1ChainConfig > {
196+ match & self . l1_config_file {
197+ Some ( path) => {
198+ debug ! ( "Loading l1 config from file: {:?}" , path) ;
199+ let file = File :: open ( path)
200+ . map_err ( |e| eyre:: eyre!( "Failed to open l1 config file: {e}" ) ) ?;
201+ from_reader ( file) . map_err ( |e| eyre:: eyre!( "Failed to parse l1 config: {e}" ) )
202+ }
203+ None => {
204+ debug ! ( "Loading l1 config from known chains" ) ;
205+ let cfg = L1Config :: get_l1_genesis ( l1_chain_id) . map_err ( |e| {
206+ eyre:: eyre!( "Failed to find l1 config for chain ID {l1_chain_id}: {e}" )
207+ } ) ?;
208+ Ok ( cfg. into ( ) )
209+ }
210+ }
211+ }
212+
213+ /// Get the L2 rollup config, either from a file or the superchain registry.
214+ pub fn get_l2_config ( & self , args : & GlobalArgs ) -> eyre:: Result < RollupConfig > {
215+ match & self . l2_config_file {
216+ Some ( path) => {
217+ debug ! ( "Loading l2 config from file: {:?}" , path) ;
218+ let file = File :: open ( path)
219+ . map_err ( |e| eyre:: eyre!( "Failed to open l2 config file: {e}" ) ) ?;
220+ from_reader ( file) . map_err ( |e| eyre:: eyre!( "Failed to parse l2 config: {e}" ) )
221+ }
222+ None => {
223+ debug ! ( "Loading l2 config from superchain registry" ) ;
224+ let Some ( cfg) = scr_rollup_config_by_alloy_ident ( & args. l2_chain_id ) else {
225+ bail ! ( "Failed to find l2 config for chain ID {}" , args. l2_chain_id) ;
226+ } ;
227+ Ok ( cfg. clone ( ) )
228+ }
229+ }
230+ }
231+
232+ /// Initializes the logging system based on global arguments.
233+ pub fn init_logs ( args : & GlobalArgs ) -> eyre:: Result < ( ) > {
234+ // Filter out discovery warnings since they're very very noisy.
235+ let filter = tracing_subscriber:: EnvFilter :: from_default_env ( )
236+ . add_directive ( "discv5=error" . parse ( ) ?) ;
237+
238+ let config: LogConfig = args. logging . clone ( ) . into ( ) ;
239+ config. init_tracing_subscriber_with_filter ( filter)
240+ }
34241}
0 commit comments