@@ -8,7 +8,6 @@ use miden_node_rpc::Rpc;
88use miden_node_store:: Store ;
99use miden_node_utils:: grpc:: UrlExt ;
1010use miden_node_validator:: Validator ;
11- use miden_protocol:: block:: BlockSigner ;
1211use miden_protocol:: crypto:: dsa:: ecdsa_k256_keccak:: SecretKey ;
1312use miden_protocol:: utils:: Deserializable ;
1413use tokio:: net:: TcpListener ;
@@ -22,9 +21,10 @@ use crate::commands::{
2221 ENV_BLOCK_PROVER_URL ,
2322 ENV_ENABLE_OTEL ,
2423 ENV_GENESIS_CONFIG_FILE ,
25- ENV_VALIDATOR_INSECURE_SECRET_KEY ,
24+ ENV_VALIDATOR_KEY ,
2625 INSECURE_VALIDATOR_KEY_HEX ,
2726 NtxBuilderConfig ,
27+ ValidatorConfig ,
2828 duration_to_human_readable_string,
2929} ;
3030
@@ -51,12 +51,12 @@ pub enum BundledCommand {
5151 ///
5252 /// If not provided, a predefined key is used.
5353 #[ arg(
54- long = "validator.insecure.secret- key" ,
55- env = ENV_VALIDATOR_INSECURE_SECRET_KEY ,
56- value_name = "VALIDATOR_INSECURE_SECRET_KEY " ,
54+ long = "validator.key" ,
55+ env = ENV_VALIDATOR_KEY ,
56+ value_name = "VALIDATOR_KEY " ,
5757 default_value = INSECURE_VALIDATOR_KEY_HEX
5858 ) ]
59- validator_insecure_secret_key : String ,
59+ validator_key : String ,
6060 } ,
6161
6262 /// Runs all three node components in the same process.
@@ -82,6 +82,9 @@ pub enum BundledCommand {
8282 #[ command( flatten) ]
8383 ntx_builder : NtxBuilderConfig ,
8484
85+ #[ command( flatten) ]
86+ validator : ValidatorConfig ,
87+
8588 /// Enables the exporting of traces for OpenTelemetry.
8689 ///
8790 /// This can be further configured using environment variables as defined in the official
@@ -99,15 +102,6 @@ pub enum BundledCommand {
99102 value_name = "DURATION"
100103 ) ]
101104 grpc_timeout : Duration ,
102-
103- /// Insecure, hex-encoded validator secret key for development and testing purposes.
104- #[ arg(
105- long = "validator.insecure.secret-key" ,
106- env = ENV_VALIDATOR_INSECURE_SECRET_KEY ,
107- value_name = "VALIDATOR_INSECURE_SECRET_KEY" ,
108- default_value = INSECURE_VALIDATOR_KEY_HEX
109- ) ]
110- validator_insecure_secret_key : String ,
111105 } ,
112106}
113107
@@ -118,14 +112,14 @@ impl BundledCommand {
118112 data_directory,
119113 accounts_directory,
120114 genesis_config_file,
121- validator_insecure_secret_key ,
115+ validator_key ,
122116 } => {
123117 // Currently the bundled bootstrap is identical to the store's bootstrap.
124118 crate :: commands:: store:: StoreCommand :: Bootstrap {
125119 data_directory,
126120 accounts_directory,
127121 genesis_config_file,
128- validator_insecure_secret_key ,
122+ validator_key ,
129123 }
130124 . handle ( )
131125 . await
@@ -137,20 +131,18 @@ impl BundledCommand {
137131 data_directory,
138132 block_producer,
139133 ntx_builder,
134+ validator,
140135 enable_otel : _,
141136 grpc_timeout,
142- validator_insecure_secret_key,
143137 } => {
144- let secret_key_bytes = hex:: decode ( validator_insecure_secret_key) ?;
145- let signer = SecretKey :: read_from_bytes ( & secret_key_bytes) ?;
146138 Self :: start (
147139 rpc_url,
148140 block_prover_url,
149141 data_directory,
150- ntx_builder,
151142 block_producer,
143+ ntx_builder,
144+ validator,
152145 grpc_timeout,
153- signer,
154146 )
155147 . await
156148 } ,
@@ -162,10 +154,10 @@ impl BundledCommand {
162154 rpc_url : Url ,
163155 block_prover_url : Option < Url > ,
164156 data_directory : PathBuf ,
165- ntx_builder : NtxBuilderConfig ,
166157 block_producer : BlockProducerConfig ,
158+ ntx_builder : NtxBuilderConfig ,
159+ validator : ValidatorConfig ,
167160 grpc_timeout : Duration ,
168- signer : impl BlockSigner + Send + Sync + ' static ,
169161 ) -> anyhow:: Result < ( ) > {
170162 // Start listening on all gRPC urls so that inter-component connections can be created
171163 // before each component is fully started up.
@@ -177,17 +169,19 @@ impl BundledCommand {
177169 . await
178170 . context ( "Failed to bind to RPC gRPC endpoint" ) ?;
179171
180- let block_producer_address = TcpListener :: bind ( "127.0.0.1:0" )
181- . await
182- . context ( "Failed to bind to block-producer gRPC endpoint" ) ?
183- . local_addr ( )
184- . context ( "Failed to retrieve the block-producer's gRPC address" ) ?;
172+ let ( block_producer_url, block_producer_address) = {
173+ let socket_addr = TcpListener :: bind ( "127.0.0.1:0" )
174+ . await
175+ . context ( "Failed to bind to block-producer gRPC endpoint" ) ?
176+ . local_addr ( )
177+ . context ( "Failed to retrieve the block-producer's gRPC address" ) ?;
178+ let url = Url :: parse ( & format ! ( "http://{socket_addr}" ) )
179+ . context ( "Failed to parse Block Producer URL" ) ?;
180+ ( url, socket_addr)
181+ } ;
185182
186- let validator_address = TcpListener :: bind ( "127.0.0.1:0" )
187- . await
188- . context ( "Failed to bind to validator gRPC endpoint" ) ?
189- . local_addr ( )
190- . context ( "Failed to retrieve the validator's gRPC address" ) ?;
183+ // Validator URL is either specified remote, or generated local.
184+ let ( validator_url, validator_socket_address) = validator. to_addresses ( ) . await ?;
191185
192186 // Store addresses for each exposed API
193187 let store_rpc_listener = TcpListener :: bind ( "127.0.0.1:0" )
@@ -231,85 +225,68 @@ impl BundledCommand {
231225 let should_start_ntx_builder = !ntx_builder. disabled ;
232226
233227 // Start block-producer. The block-producer's endpoint is available after loading completes.
234- let block_producer_id = join_set
235- . spawn ( {
236- let store_url = Url :: parse ( & format ! ( "http://{store_block_producer_address}" ) )
237- . context ( "Failed to parse URL" ) ?;
238- let validator_url = Url :: parse ( & format ! ( "http://{validator_address}" ) )
239- . context ( "Failed to parse URL" ) ?;
240- async move {
241- BlockProducer {
242- block_producer_address,
243- store_url,
244- validator_url,
245- batch_prover_url : block_producer. batch_prover_url ,
246- batch_interval : block_producer. batch_interval ,
247- block_interval : block_producer. block_interval ,
248- max_batches_per_block : block_producer. max_batches_per_block ,
249- max_txs_per_batch : block_producer. max_txs_per_batch ,
250- grpc_timeout,
251- mempool_tx_capacity : block_producer. mempool_tx_capacity ,
228+ let block_producer_id = {
229+ let validator_url = validator_url. clone ( ) ;
230+ join_set
231+ . spawn ( {
232+ let store_url = Url :: parse ( & format ! ( "http://{store_block_producer_address}" ) )
233+ . context ( "Failed to parse URL" ) ?;
234+ async move {
235+ BlockProducer {
236+ block_producer_address,
237+ store_url,
238+ validator_url,
239+ batch_prover_url : block_producer. batch_prover_url ,
240+ batch_interval : block_producer. batch_interval ,
241+ block_interval : block_producer. block_interval ,
242+ max_batches_per_block : block_producer. max_batches_per_block ,
243+ max_txs_per_batch : block_producer. max_txs_per_batch ,
244+ grpc_timeout,
245+ mempool_tx_capacity : block_producer. mempool_tx_capacity ,
246+ }
247+ . serve ( )
248+ . await
249+ . context ( "failed while serving block-producer component" )
252250 }
253- . serve ( )
254- . await
255- . context ( "failed while serving block-producer component" )
256- }
257- } )
258- . id ( ) ;
251+ } )
252+ . id ( )
253+ } ;
259254
260- let validator_id = join_set
261- . spawn ( {
262- async move {
263- Validator {
264- address : validator_address,
255+ // Start RPC component.
256+ let rpc_id = {
257+ let block_producer_url = block_producer_url. clone ( ) ;
258+ let validator_url = validator_url. clone ( ) ;
259+ join_set
260+ . spawn ( async move {
261+ let store_url = Url :: parse ( & format ! ( "http://{store_rpc_address}" ) )
262+ . context ( "Failed to parse URL" ) ?;
263+ Rpc {
264+ listener : grpc_rpc,
265+ store_url,
266+ block_producer_url : Some ( block_producer_url) ,
267+ validator_url,
265268 grpc_timeout,
266- signer,
267269 }
268270 . serve ( )
269271 . await
270- . context ( "failed while serving validator component" )
271- }
272- } )
273- . id ( ) ;
274-
275- // Start RPC component.
276- let rpc_id = join_set
277- . spawn ( async move {
278- let store_url = Url :: parse ( & format ! ( "http://{store_rpc_address}" ) )
279- . context ( "Failed to parse URL" ) ?;
280- let block_producer_url = Url :: parse ( & format ! ( "http://{block_producer_address}" ) )
281- . context ( "Failed to parse URL" ) ?;
282- let validator_url = Url :: parse ( & format ! ( "http://{validator_address}" ) )
283- . context ( "Failed to parse URL" ) ?;
284- Rpc {
285- listener : grpc_rpc,
286- store_url,
287- block_producer_url : Some ( block_producer_url) ,
288- validator_url,
289- grpc_timeout,
290- }
291- . serve ( )
292- . await
293- . context ( "failed while serving RPC component" )
294- } )
295- . id ( ) ;
272+ . context ( "failed while serving RPC component" )
273+ } )
274+ . id ( )
275+ } ;
296276
297277 // Lookup table so we can identify the failed component.
298278 let mut component_ids = HashMap :: from ( [
299279 ( store_id, "store" ) ,
300280 ( block_producer_id, "block-producer" ) ,
301- ( validator_id, "validator" ) ,
302281 ( rpc_id, "rpc" ) ,
303282 ] ) ;
304283
305284 // Start network transaction builder. The endpoint is available after loading completes.
306285 if should_start_ntx_builder {
307286 let store_ntx_builder_url = Url :: parse ( & format ! ( "http://{store_ntx_builder_address}" ) )
308287 . context ( "Failed to parse URL" ) ?;
309- let validator_url = Url :: parse ( & format ! ( "http://{validator_address}" ) )
310- . context ( "Failed to parse URL" ) ?;
311- let block_producer_url = Url :: parse ( & format ! ( "http://{block_producer_address}" ) )
312- . context ( "Failed to parse URL" ) ?;
288+ let block_producer_url = block_producer_url. clone ( ) ;
289+ let validator_url = validator_url. clone ( ) ;
313290
314291 let builder_config = ntx_builder. into_builder_config (
315292 store_ntx_builder_url,
@@ -331,6 +308,28 @@ impl BundledCommand {
331308 component_ids. insert ( id, "ntx-builder" ) ;
332309 }
333310
311+ // Start the Validator if we have bound a socket.
312+ if let Some ( address) = validator_socket_address {
313+ let secret_key_bytes = hex:: decode ( validator. validator_key ) ?;
314+ let signer = SecretKey :: read_from_bytes ( & secret_key_bytes) ?;
315+ let id = join_set
316+ . spawn ( {
317+ async move {
318+ Validator {
319+ address,
320+ grpc_timeout,
321+ signer,
322+ data_directory,
323+ }
324+ . serve ( )
325+ . await
326+ . context ( "failed while serving validator component" )
327+ }
328+ } )
329+ . id ( ) ;
330+ component_ids. insert ( id, "validator" ) ;
331+ }
332+
334333 // SAFETY: The joinset is definitely not empty.
335334 let component_result = join_set. join_next_with_id ( ) . await . unwrap ( ) ;
336335
0 commit comments