1use std::{net::SocketAddr, path::PathBuf};
6
7use fastcrypto::{
8 encoding::{Encoding, Hex},
9 traits::KeyPair,
10};
11use iota_config::{
12 AUTHORITIES_DB_NAME, CONSENSUS_DB_NAME, ConsensusConfig, FULL_NODE_DB_PATH,
13 IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME, NodeConfig, local_ip_utils,
14 node::{
15 AuthorityKeyPairWithPath, AuthorityOverloadConfig, AuthorityStorePruningConfig,
16 CheckpointExecutorConfig, DBCheckpointConfig, DEFAULT_GRPC_CONCURRENCY_LIMIT,
17 ExecutionCacheConfig, ExpensiveSafetyCheckConfig, Genesis, GrpcApiConfig, KeyPairWithPath,
18 RunWithRange, StateArchiveConfig, StateSnapshotConfig, default_enable_index_processing,
19 default_end_of_epoch_broadcast_channel_capacity,
20 default_full_checkpoint_contents_cache_size_mb,
21 },
22 p2p::{DiscoveryConfig, P2pConfig, SeedPeer, StateSyncConfig},
23 verifier_signing_config::VerifierSigningConfig,
24};
25use iota_names::config::IotaNamesConfig;
26use iota_protocol_config::Chain;
27use iota_types::{
28 crypto::{AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, NetworkKeyPair},
29 multiaddr::Multiaddr,
30 supported_protocol_versions::SupportedProtocolVersions,
31 traffic_control::{PolicyConfig, RemoteFirewallConfig},
32};
33
34use crate::{
35 genesis_config::{ValidatorGenesisConfig, ValidatorGenesisConfigBuilder},
36 network_config::NetworkConfig,
37};
38
39#[derive(Clone, Default)]
43pub struct ValidatorConfigBuilder {
44 config_directory: Option<PathBuf>,
45 supported_protocol_versions: Option<SupportedProtocolVersions>,
46 force_unpruned_checkpoints: bool,
47 authority_overload_config: Option<AuthorityOverloadConfig>,
48 execution_cache_config: Option<ExecutionCacheConfig>,
49 data_ingestion_dir: Option<PathBuf>,
50 policy_config: Option<PolicyConfig>,
51 firewall_config: Option<RemoteFirewallConfig>,
52 max_submit_position: Option<usize>,
53 submit_delay_step_override_millis: Option<u64>,
54 discovery_config: Option<DiscoveryConfig>,
55 chain_override: Option<Chain>,
56}
57
58impl ValidatorConfigBuilder {
59 pub fn new() -> Self {
60 Self {
61 ..Default::default()
62 }
63 }
64
65 pub fn with_chain_override(mut self, chain: Chain) -> Self {
66 assert!(self.chain_override.is_none(), "Chain override already set");
67 self.chain_override = Some(chain);
68 self
69 }
70
71 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
72 assert!(self.config_directory.is_none());
73 self.config_directory = Some(config_directory);
74 self
75 }
76
77 pub fn with_supported_protocol_versions(
78 mut self,
79 supported_protocol_versions: SupportedProtocolVersions,
80 ) -> Self {
81 assert!(self.supported_protocol_versions.is_none());
82 self.supported_protocol_versions = Some(supported_protocol_versions);
83 self
84 }
85
86 pub fn with_unpruned_checkpoints(mut self) -> Self {
87 self.force_unpruned_checkpoints = true;
88 self
89 }
90
91 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
92 self.authority_overload_config = Some(config);
93 self
94 }
95
96 pub fn with_execution_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
97 self.execution_cache_config = Some(config);
98 self
99 }
100
101 pub fn with_data_ingestion_dir(mut self, path: PathBuf) -> Self {
102 self.data_ingestion_dir = Some(path);
103 self
104 }
105
106 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
107 self.policy_config = config;
108 self
109 }
110
111 pub fn with_firewall_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
112 self.firewall_config = config;
113 self
114 }
115
116 pub fn with_max_submit_position(mut self, max_submit_position: usize) -> Self {
117 self.max_submit_position = Some(max_submit_position);
118 self
119 }
120
121 pub fn with_submit_delay_step_override_millis(
122 mut self,
123 submit_delay_step_override_millis: u64,
124 ) -> Self {
125 self.submit_delay_step_override_millis = Some(submit_delay_step_override_millis);
126 self
127 }
128
129 pub fn with_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
130 self.discovery_config = Some(discovery_config);
131 self
132 }
133
134 pub fn build_without_genesis(self, validator: ValidatorGenesisConfig) -> NodeConfig {
135 let key_path = get_key_path(&validator.authority_key_pair);
136 let config_directory = self
137 .config_directory
138 .unwrap_or_else(|| iota_common::tempdir().keep());
139 let migration_tx_data_path =
140 Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
141 let db_path = config_directory
142 .join(AUTHORITIES_DB_NAME)
143 .join(key_path.clone());
144 let network_address = validator.network_address;
145 let consensus_db_path = config_directory.join(CONSENSUS_DB_NAME).join(key_path);
146 let localhost = local_ip_utils::localhost_for_testing();
147 let consensus_config = ConsensusConfig {
148 db_path: consensus_db_path,
149 db_retention_epochs: None,
150 db_pruner_period_secs: None,
151 max_pending_transactions: None,
152 max_submit_position: self.max_submit_position,
153 submit_delay_step_override_millis: self.submit_delay_step_override_millis,
154 parameters: Default::default(),
155 graduated_load_shedding_soft_limit_pct: Default::default(),
156 };
157
158 let p2p_config = P2pConfig {
159 listen_address: validator.p2p_listen_address.unwrap_or_else(|| {
160 validator
161 .p2p_address
162 .udp_multiaddr_to_listen_address()
163 .unwrap()
164 }),
165 external_address: Some(validator.p2p_address),
166 state_sync: Some(StateSyncConfig {
169 checkpoint_content_timeout_ms: Some(10_000),
170 ..Default::default()
171 }),
172 discovery: self.discovery_config,
174 ..Default::default()
175 };
176
177 let mut pruning_config = AuthorityStorePruningConfig::default();
178 if self.force_unpruned_checkpoints {
179 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
180 }
181 let pruning_config = pruning_config;
182 let checkpoint_executor_config = CheckpointExecutorConfig {
183 data_ingestion_dir: self.data_ingestion_dir,
184 ..Default::default()
185 };
186
187 NodeConfig {
188 authority_key_pair: AuthorityKeyPairWithPath::new(validator.authority_key_pair),
189 network_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
190 validator.network_key_pair,
191 )),
192 account_key_pair: KeyPairWithPath::new(validator.account_key_pair),
193 protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
194 validator.protocol_key_pair,
195 )),
196 db_path,
197 network_address,
198 metrics_address: validator.metrics_address,
199 admin_interface_address: validator.admin_interface_address,
200 json_rpc_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
201 .to_socket_addr()
202 .unwrap(),
203 consensus_config: Some(consensus_config),
204 enable_index_processing: default_enable_index_processing(),
205 genesis: Genesis::new_empty(),
206 migration_tx_data_path,
207 grpc_load_shed: None,
208 grpc_concurrency_limit: Some(DEFAULT_GRPC_CONCURRENCY_LIMIT),
209 p2p_config,
210 authority_store_pruning_config: pruning_config,
211 end_of_epoch_broadcast_channel_capacity:
212 default_end_of_epoch_broadcast_channel_capacity(),
213 checkpoint_executor_config,
214 metrics: None,
215 supported_protocol_versions: self.supported_protocol_versions,
216 db_checkpoint_config: Default::default(),
217 expensive_safety_check_config: ExpensiveSafetyCheckConfig::default(),
220 transaction_deny_config: Default::default(),
221 certificate_deny_config: Default::default(),
222 state_debug_dump_config: Default::default(),
223 state_archive_write_config: StateArchiveConfig::default(),
224 state_archive_read_config: vec![],
225 state_snapshot_write_config: StateSnapshotConfig::default(),
226 indexer_max_subscriptions: Default::default(),
227 transaction_kv_store_read_config: Default::default(),
228 transaction_kv_store_write_config: None,
229 authority_overload_config: self.authority_overload_config.unwrap_or_default(),
230 execution_cache_config: self.execution_cache_config.unwrap_or_default(),
231 full_checkpoint_contents_cache_size_mb: default_full_checkpoint_contents_cache_size_mb(
232 ),
233 run_with_range: None,
234 jsonrpc_server_type: None,
235 policy_config: self.policy_config,
236 firewall_config: self.firewall_config,
237 enable_validator_tx_finalizer: true,
238 enable_soft_locking: true,
239 verifier_signing_config: VerifierSigningConfig::default(),
240 enable_db_write_stall: None,
241 iota_names_config: None,
242 enable_grpc_api: false,
243 grpc_api_config: None,
244 chain_override_for_testing: self.chain_override,
245 validator_client_monitor_config: None,
246 }
247 }
248
249 pub fn build(
250 self,
251 validator: ValidatorGenesisConfig,
252 genesis: iota_config::genesis::Genesis,
253 ) -> NodeConfig {
254 let mut config = self.build_without_genesis(validator);
255 config.genesis = iota_config::node::Genesis::new(genesis);
256 config
257 }
258
259 pub fn build_new_validator<R: rand::RngCore + rand::CryptoRng>(
260 self,
261 rng: &mut R,
262 network_config: &NetworkConfig,
263 ) -> NodeConfig {
264 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
265 self.build(validator_config, network_config.genesis.clone())
266 }
267}
268
269#[derive(Clone, Debug, Default)]
270pub struct FullnodeConfigBuilder {
271 config_directory: Option<PathBuf>,
272 rpc_port: Option<u16>,
274 rpc_addr: Option<SocketAddr>,
275 supported_protocol_versions: Option<SupportedProtocolVersions>,
276 db_checkpoint_config: Option<DBCheckpointConfig>,
277 expensive_safety_check_config: Option<ExpensiveSafetyCheckConfig>,
278 db_path: Option<PathBuf>,
279 network_address: Option<Multiaddr>,
280 json_rpc_address: Option<SocketAddr>,
281 metrics_address: Option<SocketAddr>,
282 admin_interface_address: Option<SocketAddr>,
283 genesis: Option<Genesis>,
284 p2p_external_address: Option<Multiaddr>,
285 p2p_listen_address: Option<SocketAddr>,
286 network_key_pair: Option<KeyPairWithPath>,
287 run_with_range: Option<RunWithRange>,
288 policy_config: Option<PolicyConfig>,
289 fw_config: Option<RemoteFirewallConfig>,
290 data_ingestion_dir: Option<PathBuf>,
291 disable_pruning: bool,
292 iota_names_config: Option<IotaNamesConfig>,
293 enable_grpc_api: bool,
294 grpc_api_config: Option<GrpcApiConfig>,
295 discovery_config: Option<DiscoveryConfig>,
296 chain_override: Option<Chain>,
297}
298
299impl FullnodeConfigBuilder {
300 pub fn new() -> Self {
301 Self::default()
302 }
303
304 pub fn with_chain_override(mut self, chain: Chain) -> Self {
305 assert!(self.chain_override.is_none(), "Chain override already set");
306 self.chain_override = Some(chain);
307 self
308 }
309
310 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
311 self.config_directory = Some(config_directory);
312 self
313 }
314
315 pub fn with_rpc_port(mut self, port: u16) -> Self {
316 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
317 self.rpc_port = Some(port);
318 self
319 }
320
321 pub fn with_rpc_addr(mut self, addr: impl Into<SocketAddr>) -> Self {
322 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
323 self.rpc_addr = Some(addr.into());
324 self
325 }
326
327 pub fn with_supported_protocol_versions(mut self, versions: SupportedProtocolVersions) -> Self {
328 self.supported_protocol_versions = Some(versions);
329 self
330 }
331
332 pub fn with_db_checkpoint_config(mut self, db_checkpoint_config: DBCheckpointConfig) -> Self {
333 self.db_checkpoint_config = Some(db_checkpoint_config);
334 self
335 }
336
337 pub fn with_disable_pruning(mut self, disable_pruning: bool) -> Self {
338 self.disable_pruning = disable_pruning;
339 self
340 }
341
342 pub fn with_expensive_safety_check_config(
343 mut self,
344 expensive_safety_check_config: ExpensiveSafetyCheckConfig,
345 ) -> Self {
346 self.expensive_safety_check_config = Some(expensive_safety_check_config);
347 self
348 }
349
350 pub fn with_db_path(mut self, db_path: PathBuf) -> Self {
351 self.db_path = Some(db_path);
352 self
353 }
354
355 pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
356 self.network_address = Some(network_address);
357 self
358 }
359
360 pub fn with_json_rpc_address(mut self, json_rpc_address: impl Into<SocketAddr>) -> Self {
361 self.json_rpc_address = Some(json_rpc_address.into());
362 self
363 }
364
365 pub fn with_metrics_address(mut self, metrics_address: impl Into<SocketAddr>) -> Self {
366 self.metrics_address = Some(metrics_address.into());
367 self
368 }
369
370 pub fn with_admin_interface_address(
371 mut self,
372 admin_interface_address: Option<impl Into<SocketAddr>>,
373 ) -> Self {
374 self.admin_interface_address = admin_interface_address.map(|addr| addr.into());
375 self
376 }
377
378 pub fn with_genesis(mut self, genesis: Genesis) -> Self {
379 self.genesis = Some(genesis);
380 self
381 }
382
383 pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
384 self.p2p_external_address = Some(p2p_external_address);
385 self
386 }
387
388 pub fn with_p2p_listen_address(mut self, p2p_listen_address: impl Into<SocketAddr>) -> Self {
389 self.p2p_listen_address = Some(p2p_listen_address.into());
390 self
391 }
392
393 pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
394 if let Some(network_key_pair) = network_key_pair {
395 self.network_key_pair =
396 Some(KeyPairWithPath::new(IotaKeyPair::Ed25519(network_key_pair)));
397 }
398 self
399 }
400
401 pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
402 if let Some(run_with_range) = run_with_range {
403 self.run_with_range = Some(run_with_range);
404 }
405 self
406 }
407
408 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
409 self.policy_config = config;
410 self
411 }
412
413 pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
414 self.fw_config = config;
415 self
416 }
417
418 pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
419 self.data_ingestion_dir = path;
420 self
421 }
422
423 pub fn with_iota_names_config(mut self, config: Option<IotaNamesConfig>) -> Self {
424 self.iota_names_config = config;
425 self
426 }
427
428 pub fn with_enable_grpc_api(mut self, enable_grpc_api: bool) -> Self {
429 self.enable_grpc_api = enable_grpc_api;
430 self
431 }
432
433 pub fn with_grpc_api_config(mut self, config: GrpcApiConfig) -> Self {
434 self.grpc_api_config = Some(config);
435 self
436 }
437
438 pub fn with_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
439 self.discovery_config = Some(discovery_config);
440 self
441 }
442
443 pub fn build_from_parts<R: rand::RngCore + rand::CryptoRng>(
444 self,
445 rng: &mut R,
446 validator_configs: &[NodeConfig],
447 genesis: iota_config::node::Genesis,
448 ) -> NodeConfig {
449 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
452 let ip = validator_config
453 .network_address
454 .to_socket_addr()
455 .unwrap()
456 .ip()
457 .to_string();
458
459 let key_path = get_key_path(&validator_config.authority_key_pair);
460 let config_directory = self
461 .config_directory
462 .unwrap_or_else(|| iota_common::tempdir().keep());
463
464 let migration_tx_data_path =
465 Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
466
467 let p2p_config = {
468 let seed_peers = validator_configs
469 .iter()
470 .map(|config| SeedPeer {
471 peer_id: Some(anemo::PeerId(
472 config.network_key_pair().public().0.to_bytes(),
473 )),
474 address: config.p2p_config.external_address.clone().unwrap(),
475 })
476 .collect();
477
478 P2pConfig {
479 listen_address: self.p2p_listen_address.unwrap_or_else(|| {
480 validator_config.p2p_listen_address.unwrap_or_else(|| {
481 validator_config
482 .p2p_address
483 .udp_multiaddr_to_listen_address()
484 .unwrap()
485 })
486 }),
487 external_address: self
488 .p2p_external_address
489 .or(Some(validator_config.p2p_address.clone())),
490 seed_peers,
491 state_sync: Some(StateSyncConfig {
494 checkpoint_content_timeout_ms: Some(10_000),
495 ..Default::default()
496 }),
497 discovery: self.discovery_config,
499 ..Default::default()
500 }
501 };
502
503 let json_rpc_address = self.rpc_addr.unwrap_or_else(|| {
504 let rpc_port = self
505 .rpc_port
506 .unwrap_or_else(|| local_ip_utils::get_available_port(&ip));
507 format!("{ip}:{rpc_port}").parse().unwrap()
508 });
509
510 let grpc_api_config = self.grpc_api_config.or_else(|| {
511 if self.enable_grpc_api {
512 Some(GrpcApiConfig {
513 address: format!("{ip}:{}", local_ip_utils::get_available_port(&ip))
514 .parse()
515 .unwrap(),
516 ..Default::default()
517 })
518 } else {
519 None
520 }
521 });
522
523 let checkpoint_executor_config = CheckpointExecutorConfig {
524 data_ingestion_dir: self.data_ingestion_dir,
525 ..Default::default()
526 };
527
528 let mut pruning_config = AuthorityStorePruningConfig::default();
529 if self.disable_pruning {
530 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
531 pruning_config.set_num_epochs_to_retain(u64::MAX);
532 };
533
534 NodeConfig {
535 authority_key_pair: AuthorityKeyPairWithPath::new(validator_config.authority_key_pair),
536 account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
537 protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
538 validator_config.protocol_key_pair,
539 )),
540 network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
541 IotaKeyPair::Ed25519(validator_config.network_key_pair),
542 )),
543 db_path: self
544 .db_path
545 .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
546 network_address: self
547 .network_address
548 .unwrap_or(validator_config.network_address),
549 metrics_address: self
550 .metrics_address
551 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
552 admin_interface_address: self
553 .admin_interface_address
554 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
555 json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
556 consensus_config: None,
557 enable_index_processing: default_enable_index_processing(),
558 genesis,
559 migration_tx_data_path,
560 grpc_load_shed: None,
561 grpc_concurrency_limit: None,
562 p2p_config,
563 authority_store_pruning_config: pruning_config,
564 end_of_epoch_broadcast_channel_capacity:
565 default_end_of_epoch_broadcast_channel_capacity(),
566 checkpoint_executor_config,
567 metrics: None,
568 supported_protocol_versions: self.supported_protocol_versions,
569 db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
570 expensive_safety_check_config: self
571 .expensive_safety_check_config
572 .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
573 transaction_deny_config: Default::default(),
574 certificate_deny_config: Default::default(),
575 state_debug_dump_config: Default::default(),
576 state_archive_write_config: StateArchiveConfig::default(),
577 state_archive_read_config: vec![],
578 state_snapshot_write_config: StateSnapshotConfig::default(),
579 indexer_max_subscriptions: Default::default(),
580 transaction_kv_store_read_config: Default::default(),
581 transaction_kv_store_write_config: Default::default(),
582 authority_overload_config: Default::default(),
583 run_with_range: self.run_with_range,
584 jsonrpc_server_type: None,
585 policy_config: self.policy_config,
586 firewall_config: self.fw_config,
587 execution_cache_config: ExecutionCacheConfig::default(),
588 full_checkpoint_contents_cache_size_mb: default_full_checkpoint_contents_cache_size_mb(
589 ),
590 enable_validator_tx_finalizer: false,
592 enable_soft_locking: true,
595 verifier_signing_config: VerifierSigningConfig::default(),
596 enable_db_write_stall: None,
597 iota_names_config: self.iota_names_config,
598 enable_grpc_api: self.enable_grpc_api,
599 grpc_api_config,
600 chain_override_for_testing: self.chain_override,
601 validator_client_monitor_config: None,
602 }
603 }
604
605 pub fn build<R: rand::RngCore + rand::CryptoRng>(
606 self,
607 rng: &mut R,
608 network_config: &NetworkConfig,
609 ) -> NodeConfig {
610 let genesis = self
611 .genesis
612 .as_ref()
613 .or_else(|| network_config.get_validator_genesis())
614 .cloned()
615 .unwrap_or_else(|| iota_config::node::Genesis::new(network_config.genesis.clone()));
616 self.build_from_parts(rng, network_config.validator_configs(), genesis)
617 }
618}
619
620fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
623 let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
624 let mut key_path = Hex::encode(public_key);
625 key_path.truncate(12);
628 key_path
629}