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