11//! This module contains all CLI-specific code for the host binary.
22
3- use crate :: {
4- blobs:: OnlineBlobProvider ,
5- kv:: {
6- DiskKeyValueStore , LocalKeyValueStore , MemoryKeyValueStore , SharedKeyValueStore ,
7- SplitKeyValueStore ,
8- } ,
9- } ;
10- use alloy_primitives:: B256 ;
11- use alloy_provider:: ReqwestProvider ;
12- use alloy_rpc_client:: RpcClient ;
13- use alloy_transport_http:: Http ;
14- use anyhow:: { anyhow, Result } ;
3+ use crate :: single:: SingleChainHostCli ;
154use clap:: {
165 builder:: styling:: { AnsiColor , Color , Style } ,
17- ArgAction , Parser ,
6+ ArgAction , Parser , Subcommand ,
187} ;
19- use op_alloy_genesis:: RollupConfig ;
20- use reqwest:: Client ;
218use serde:: Serialize ;
22- use std:: { path:: PathBuf , sync:: Arc } ;
23- use tokio:: sync:: RwLock ;
249
2510mod parser;
2611pub ( crate ) use parser:: parse_b256;
@@ -37,164 +22,22 @@ primary thread.
3722" ;
3823
3924/// The host binary CLI application arguments.
40- #[ derive( Default , Parser , Serialize , Clone , Debug ) ]
25+ #[ derive( Parser , Serialize , Clone , Debug ) ]
4126#[ command( about = ABOUT , version, styles = cli_styles( ) ) ]
4227pub struct HostCli {
4328 /// Verbosity level (0-2)
4429 #[ arg( long, short, action = ArgAction :: Count ) ]
4530 pub v : u8 ,
46- /// Hash of the L1 head block. Derivation stops after this block is processed.
47- #[ clap( long, value_parser = parse_b256, env) ]
48- pub l1_head : B256 ,
49- /// Hash of the agreed upon safe L2 block committed to by `--agreed-l2-output-root`.
50- #[ clap( long, visible_alias = "l2-head" , value_parser = parse_b256, env) ]
51- pub agreed_l2_head_hash : B256 ,
52- /// Agreed safe L2 Output Root to start derivation from.
53- #[ clap( long, visible_alias = "l2-output-root" , value_parser = parse_b256, env) ]
54- pub agreed_l2_output_root : B256 ,
55- /// Claimed L2 output root at block # `--claimed-l2-block-number` to validate.
56- #[ clap( long, visible_alias = "l2-claim" , value_parser = parse_b256, env) ]
57- pub claimed_l2_output_root : B256 ,
58- /// Number of the L2 block that the claimed output root commits to.
59- #[ clap( long, visible_alias = "l2-block-number" , env) ]
60- pub claimed_l2_block_number : u64 ,
61- /// Address of L2 JSON-RPC endpoint to use (eth and debug namespace required).
62- #[ clap(
63- long,
64- visible_alias = "l2" ,
65- requires = "l1_node_address" ,
66- requires = "l1_beacon_address" ,
67- env
68- ) ]
69- pub l2_node_address : Option < String > ,
70- /// Address of L1 JSON-RPC endpoint to use (eth and debug namespace required)
71- #[ clap(
72- long,
73- visible_alias = "l1" ,
74- requires = "l2_node_address" ,
75- requires = "l1_beacon_address" ,
76- env
77- ) ]
78- pub l1_node_address : Option < String > ,
79- /// Address of the L1 Beacon API endpoint to use.
80- #[ clap(
81- long,
82- visible_alias = "beacon" ,
83- requires = "l1_node_address" ,
84- requires = "l2_node_address" ,
85- env
86- ) ]
87- pub l1_beacon_address : Option < String > ,
88- /// The Data Directory for preimage data storage. Optional if running in online mode,
89- /// required if running in offline mode.
90- #[ clap(
91- long,
92- visible_alias = "db" ,
93- required_unless_present_all = [ "l2_node_address" , "l1_node_address" , "l1_beacon_address" ] ,
94- env
95- ) ]
96- pub data_dir : Option < PathBuf > ,
97- /// Run the client program natively.
98- #[ clap( long, conflicts_with = "server" , required_unless_present = "server" ) ]
99- pub native : bool ,
100- /// Run in pre-image server mode without executing any client program. If not provided, the
101- /// host will run the client program in the host process.
102- #[ clap( long, conflicts_with = "native" , required_unless_present = "native" ) ]
103- pub server : bool ,
104- /// The L2 chain ID of a supported chain. If provided, the host will look for the corresponding
105- /// rollup config in the superchain registry.
106- #[ clap(
107- long,
108- conflicts_with = "rollup_config_path" ,
109- required_unless_present = "rollup_config_path" ,
110- env
111- ) ]
112- pub l2_chain_id : Option < u64 > ,
113- /// Path to rollup config. If provided, the host will use this config instead of attempting to
114- /// look up the config in the superchain registry.
115- #[ clap(
116- long,
117- alias = "rollup-cfg" ,
118- conflicts_with = "l2_chain_id" ,
119- required_unless_present = "l2_chain_id" ,
120- env
121- ) ]
122- pub rollup_config_path : Option < PathBuf > ,
31+ /// Host mode
32+ #[ clap( subcommand) ]
33+ pub mode : HostMode ,
12334}
12435
125- impl HostCli {
126- /// Returns `true` if the host is running in offline mode.
127- pub const fn is_offline ( & self ) -> bool {
128- self . l1_node_address . is_none ( ) &&
129- self . l2_node_address . is_none ( ) &&
130- self . l1_beacon_address . is_none ( )
131- }
132-
133- /// Returns an HTTP provider for the given URL.
134- fn http_provider ( url : & str ) -> ReqwestProvider {
135- let url = url. parse ( ) . unwrap ( ) ;
136- let http = Http :: < Client > :: new ( url) ;
137- ReqwestProvider :: new ( RpcClient :: new ( http, true ) )
138- }
139-
140- /// Creates the providers associated with the [HostCli] configuration.
141- ///
142- /// ## Returns
143- /// - A [ReqwestProvider] for the L1 node.
144- /// - An [OnlineBlobProvider] for the L1 beacon node.
145- /// - A [ReqwestProvider] for the L2 node.
146- pub async fn create_providers (
147- & self ,
148- ) -> Result < ( ReqwestProvider , OnlineBlobProvider , ReqwestProvider ) > {
149- let blob_provider = OnlineBlobProvider :: new_http (
150- self . l1_beacon_address . clone ( ) . ok_or ( anyhow ! ( "Beacon API URL must be set" ) ) ?,
151- )
152- . await
153- . map_err ( |e| anyhow ! ( "Failed to load blob provider configuration: {e}" ) ) ?;
154- let l1_provider = Self :: http_provider (
155- self . l1_node_address . as_ref ( ) . ok_or ( anyhow ! ( "Provider must be set" ) ) ?,
156- ) ;
157- let l2_provider = Self :: http_provider (
158- self . l2_node_address . as_ref ( ) . ok_or ( anyhow ! ( "L2 node address must be set" ) ) ?,
159- ) ;
160-
161- Ok ( ( l1_provider, blob_provider, l2_provider) )
162- }
163-
164- /// Parses the CLI arguments and returns a new instance of a [SharedKeyValueStore], as it is
165- /// configured to be created.
166- pub fn construct_kv_store ( & self ) -> SharedKeyValueStore {
167- let local_kv_store = LocalKeyValueStore :: new ( self . clone ( ) ) ;
168-
169- let kv_store: SharedKeyValueStore = if let Some ( ref data_dir) = self . data_dir {
170- let disk_kv_store = DiskKeyValueStore :: new ( data_dir. clone ( ) ) ;
171- let split_kv_store = SplitKeyValueStore :: new ( local_kv_store, disk_kv_store) ;
172- Arc :: new ( RwLock :: new ( split_kv_store) )
173- } else {
174- let mem_kv_store = MemoryKeyValueStore :: new ( ) ;
175- let split_kv_store = SplitKeyValueStore :: new ( local_kv_store, mem_kv_store) ;
176- Arc :: new ( RwLock :: new ( split_kv_store) )
177- } ;
178-
179- kv_store
180- }
181-
182- /// Reads the [RollupConfig] from the file system and returns it as a string.
183- pub fn read_rollup_config ( & self ) -> Result < RollupConfig > {
184- let path = self . rollup_config_path . as_ref ( ) . ok_or_else ( || {
185- anyhow:: anyhow!(
186- "No rollup config path provided. Please provide a path to the rollup config."
187- )
188- } ) ?;
189-
190- // Read the serialized config from the file system.
191- let ser_config = std:: fs:: read_to_string ( path)
192- . map_err ( |e| anyhow ! ( "Error reading RollupConfig file: {e}" ) ) ?;
193-
194- // Deserialize the config and return it.
195- serde_json:: from_str ( & ser_config)
196- . map_err ( |e| anyhow ! ( "Error deserializing RollupConfig: {e}" ) )
197- }
36+ /// Operation modes for the host binary.
37+ #[ derive( Subcommand , Serialize , Clone , Debug ) ]
38+ pub enum HostMode {
39+ /// Run the host in single-chain mode.
40+ Single ( SingleChainHostCli ) ,
19841}
19942
20043/// Styles for the CLI application.
@@ -208,69 +51,3 @@ const fn cli_styles() -> clap::builder::Styles {
20851 . valid ( Style :: new ( ) . bold ( ) . underline ( ) . fg_color ( Some ( Color :: Ansi ( AnsiColor :: Green ) ) ) )
20952 . placeholder ( Style :: new ( ) . fg_color ( Some ( Color :: Ansi ( AnsiColor :: White ) ) ) )
21053}
211-
212- #[ cfg( test) ]
213- mod test {
214- use crate :: HostCli ;
215- use alloy_primitives:: B256 ;
216- use clap:: Parser ;
217-
218- #[ test]
219- fn test_flags ( ) {
220- let zero_hash_str = & B256 :: ZERO . to_string ( ) ;
221- let default_flags = [
222- "host" ,
223- "--l1-head" ,
224- zero_hash_str,
225- "--l2-head" ,
226- zero_hash_str,
227- "--l2-output-root" ,
228- zero_hash_str,
229- "--l2-claim" ,
230- zero_hash_str,
231- "--l2-block-number" ,
232- "0" ,
233- ] ;
234-
235- let cases = [
236- // valid
237- ( [ "--server" , "--l2-chain-id" , "0" , "--data-dir" , "dummy" ] . as_slice ( ) , true ) ,
238- ( [ "--server" , "--rollup-config-path" , "dummy" , "--data-dir" , "dummy" ] . as_slice ( ) , true ) ,
239- ( [ "--native" , "--l2-chain-id" , "0" , "--data-dir" , "dummy" ] . as_slice ( ) , true ) ,
240- ( [ "--native" , "--rollup-config-path" , "dummy" , "--data-dir" , "dummy" ] . as_slice ( ) , true ) ,
241- (
242- [
243- "--l1-node-address" ,
244- "dummy" ,
245- "--l2-node-address" ,
246- "dummy" ,
247- "--l1-beacon-address" ,
248- "dummy" ,
249- "--server" ,
250- "--l2-chain-id" ,
251- "0" ,
252- ]
253- . as_slice ( ) ,
254- true ,
255- ) ,
256- // invalid
257- ( [ "--server" , "--native" , "--l2-chain-id" , "0" ] . as_slice ( ) , false ) ,
258- ( [ "--l2-chain-id" , "0" , "--rollup-config-path" , "dummy" , "--server" ] . as_slice ( ) , false ) ,
259- ( [ "--server" ] . as_slice ( ) , false ) ,
260- ( [ "--native" ] . as_slice ( ) , false ) ,
261- ( [ "--rollup-config-path" , "dummy" ] . as_slice ( ) , false ) ,
262- ( [ "--l2-chain-id" , "0" ] . as_slice ( ) , false ) ,
263- ( [ "--l1-node-address" , "dummy" , "--server" , "--l2-chain-id" , "0" ] . as_slice ( ) , false ) ,
264- ( [ "--l2-node-address" , "dummy" , "--server" , "--l2-chain-id" , "0" ] . as_slice ( ) , false ) ,
265- ( [ "--l1-beacon-address" , "dummy" , "--server" , "--l2-chain-id" , "0" ] . as_slice ( ) , false ) ,
266- ( [ ] . as_slice ( ) , false ) ,
267- ] ;
268-
269- for ( args_ext, valid) in cases. into_iter ( ) {
270- let args = default_flags. iter ( ) . chain ( args_ext. iter ( ) ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
271-
272- let parsed = HostCli :: try_parse_from ( args) ;
273- assert_eq ! ( parsed. is_ok( ) , valid) ;
274- }
275- }
276- }
0 commit comments