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_types::{
27 crypto::{AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, NetworkKeyPair},
28 multiaddr::Multiaddr,
29 supported_protocol_versions::SupportedProtocolVersions,
30 traffic_control::{PolicyConfig, RemoteFirewallConfig},
31};
32
33use crate::{
34 genesis_config::{ValidatorGenesisConfig, ValidatorGenesisConfigBuilder},
35 network_config::NetworkConfig,
36};
37
38#[derive(Clone, Default)]
42pub struct ValidatorConfigBuilder {
43 config_directory: Option<PathBuf>,
44 supported_protocol_versions: Option<SupportedProtocolVersions>,
45 force_unpruned_checkpoints: bool,
46 jwk_fetch_interval: Option<Duration>,
47 authority_overload_config: Option<AuthorityOverloadConfig>,
48 execution_cache_type: Option<ExecutionCacheType>,
49 execution_cache_config: Option<ExecutionCacheConfig>,
50 data_ingestion_dir: Option<PathBuf>,
51 policy_config: Option<PolicyConfig>,
52 firewall_config: Option<RemoteFirewallConfig>,
53 max_submit_position: Option<usize>,
54 submit_delay_step_override_millis: Option<u64>,
55 discovery_config: Option<DiscoveryConfig>,
56}
57
58impl ValidatorConfigBuilder {
59 pub fn new() -> Self {
60 Self {
61 ..Default::default()
62 }
63 }
64
65 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
66 assert!(self.config_directory.is_none());
67 self.config_directory = Some(config_directory);
68 self
69 }
70
71 pub fn with_supported_protocol_versions(
72 mut self,
73 supported_protocol_versions: SupportedProtocolVersions,
74 ) -> Self {
75 assert!(self.supported_protocol_versions.is_none());
76 self.supported_protocol_versions = Some(supported_protocol_versions);
77 self
78 }
79
80 pub fn with_unpruned_checkpoints(mut self) -> Self {
81 self.force_unpruned_checkpoints = true;
82 self
83 }
84
85 pub fn with_jwk_fetch_interval(mut self, i: Duration) -> Self {
86 self.jwk_fetch_interval = Some(i);
87 self
88 }
89
90 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
91 self.authority_overload_config = Some(config);
92 self
93 }
94
95 pub fn with_execution_cache_type(mut self, execution_cache_type: ExecutionCacheType) -> Self {
96 self.execution_cache_type = Some(execution_cache_type);
97 self
98 }
99
100 pub fn with_execution_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
101 self.execution_cache_config = Some(config);
102 self
103 }
104
105 pub fn with_data_ingestion_dir(mut self, path: PathBuf) -> Self {
106 self.data_ingestion_dir = Some(path);
107 self
108 }
109
110 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
111 self.policy_config = config;
112 self
113 }
114
115 pub fn with_firewall_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
116 self.firewall_config = config;
117 self
118 }
119
120 pub fn with_max_submit_position(mut self, max_submit_position: usize) -> Self {
121 self.max_submit_position = Some(max_submit_position);
122 self
123 }
124
125 pub fn with_submit_delay_step_override_millis(
126 mut self,
127 submit_delay_step_override_millis: u64,
128 ) -> Self {
129 self.submit_delay_step_override_millis = Some(submit_delay_step_override_millis);
130 self
131 }
132
133 pub fn with_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
134 self.discovery_config = Some(discovery_config);
135 self
136 }
137
138 pub fn build_without_genesis(self, validator: ValidatorGenesisConfig) -> NodeConfig {
139 let key_path = get_key_path(&validator.authority_key_pair);
140 let config_directory = self
141 .config_directory
142 .unwrap_or_else(|| tempfile::tempdir().unwrap().keep());
143 let migration_tx_data_path =
144 Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
145 let db_path = config_directory
146 .join(AUTHORITIES_DB_NAME)
147 .join(key_path.clone());
148 let network_address = validator.network_address;
149 let consensus_db_path = config_directory.join(CONSENSUS_DB_NAME).join(key_path);
150 let localhost = local_ip_utils::localhost_for_testing();
151 let consensus_config = ConsensusConfig {
152 db_path: consensus_db_path,
153 db_retention_epochs: None,
154 db_pruner_period_secs: None,
155 max_pending_transactions: None,
156 max_submit_position: self.max_submit_position,
157 submit_delay_step_override_millis: self.submit_delay_step_override_millis,
158 parameters: Default::default(),
159 starfish_parameters: Default::default(),
160 };
161
162 let p2p_config = P2pConfig {
163 listen_address: validator.p2p_listen_address.unwrap_or_else(|| {
164 validator
165 .p2p_address
166 .udp_multiaddr_to_listen_address()
167 .unwrap()
168 }),
169 external_address: Some(validator.p2p_address),
170 state_sync: Some(StateSyncConfig {
173 checkpoint_content_timeout_ms: Some(10_000),
174 ..Default::default()
175 }),
176 discovery: self.discovery_config,
178 ..Default::default()
179 };
180
181 let mut pruning_config = AuthorityStorePruningConfig::default();
182 if self.force_unpruned_checkpoints {
183 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
184 }
185 let pruning_config = pruning_config;
186 let checkpoint_executor_config = CheckpointExecutorConfig {
187 data_ingestion_dir: self.data_ingestion_dir,
188 ..Default::default()
189 };
190
191 NodeConfig {
192 authority_key_pair: AuthorityKeyPairWithPath::new(validator.authority_key_pair),
193 network_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
194 validator.network_key_pair,
195 )),
196 account_key_pair: KeyPairWithPath::new(validator.account_key_pair),
197 protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
198 validator.protocol_key_pair,
199 )),
200 db_path,
201 network_address,
202 metrics_address: validator.metrics_address,
203 admin_interface_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
204 .to_socket_addr()
205 .unwrap(),
206 json_rpc_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
207 .to_socket_addr()
208 .unwrap(),
209 consensus_config: Some(consensus_config),
210 remove_deprecated_tables: false,
211 enable_index_processing: default_enable_index_processing(),
212 genesis: Genesis::new_empty(),
213 migration_tx_data_path,
214 grpc_load_shed: None,
215 grpc_concurrency_limit: Some(DEFAULT_GRPC_CONCURRENCY_LIMIT),
216 p2p_config,
217 authority_store_pruning_config: pruning_config,
218 end_of_epoch_broadcast_channel_capacity:
219 default_end_of_epoch_broadcast_channel_capacity(),
220 checkpoint_executor_config,
221 metrics: None,
222 supported_protocol_versions: self.supported_protocol_versions,
223 db_checkpoint_config: Default::default(),
224 expensive_safety_check_config: ExpensiveSafetyCheckConfig::default(),
227 transaction_deny_config: Default::default(),
228 certificate_deny_config: Default::default(),
229 state_debug_dump_config: Default::default(),
230 state_archive_write_config: StateArchiveConfig::default(),
231 state_archive_read_config: vec![],
232 state_snapshot_write_config: StateSnapshotConfig::default(),
233 indexer_max_subscriptions: Default::default(),
234 transaction_kv_store_read_config: Default::default(),
235 transaction_kv_store_write_config: None,
236 enable_rest_api: true,
237 rest: Some(iota_rest_api::Config {
238 enable_unstable_apis: Some(true),
239 ..Default::default()
240 }),
241 jwk_fetch_interval_seconds: self
242 .jwk_fetch_interval
243 .map(|i| i.as_secs())
244 .unwrap_or(3600),
245 zklogin_oauth_providers: default_zklogin_oauth_providers(),
246 authority_overload_config: self.authority_overload_config.unwrap_or_default(),
247 execution_cache: self.execution_cache_type.unwrap_or_default(),
248 execution_cache_config: self.execution_cache_config.unwrap_or_default(),
249 run_with_range: None,
250 jsonrpc_server_type: None,
251 policy_config: self.policy_config,
252 firewall_config: self.firewall_config,
253 enable_validator_tx_finalizer: true,
254 verifier_signing_config: VerifierSigningConfig::default(),
255 enable_db_write_stall: None,
256 iota_names_config: None,
257 enable_grpc_api: false,
258 grpc_api_config: None,
259 }
260 }
261
262 pub fn build(
263 self,
264 validator: ValidatorGenesisConfig,
265 genesis: iota_config::genesis::Genesis,
266 ) -> NodeConfig {
267 let mut config = self.build_without_genesis(validator);
268 config.genesis = iota_config::node::Genesis::new(genesis);
269 config
270 }
271
272 pub fn build_new_validator<R: rand::RngCore + rand::CryptoRng>(
273 self,
274 rng: &mut R,
275 network_config: &NetworkConfig,
276 ) -> NodeConfig {
277 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
278 self.build(validator_config, network_config.genesis.clone())
279 }
280}
281
282#[derive(Clone, Debug, Default)]
283pub struct FullnodeConfigBuilder {
284 config_directory: Option<PathBuf>,
285 rpc_port: Option<u16>,
287 rpc_addr: Option<SocketAddr>,
288 supported_protocol_versions: Option<SupportedProtocolVersions>,
289 db_checkpoint_config: Option<DBCheckpointConfig>,
290 expensive_safety_check_config: Option<ExpensiveSafetyCheckConfig>,
291 db_path: Option<PathBuf>,
292 network_address: Option<Multiaddr>,
293 json_rpc_address: Option<SocketAddr>,
294 metrics_address: Option<SocketAddr>,
295 admin_interface_address: Option<SocketAddr>,
296 genesis: Option<Genesis>,
297 p2p_external_address: Option<Multiaddr>,
298 p2p_listen_address: Option<SocketAddr>,
299 network_key_pair: Option<KeyPairWithPath>,
300 run_with_range: Option<RunWithRange>,
301 policy_config: Option<PolicyConfig>,
302 fw_config: Option<RemoteFirewallConfig>,
303 data_ingestion_dir: Option<PathBuf>,
304 disable_pruning: bool,
305 iota_names_config: Option<IotaNamesConfig>,
306 grpc_api_config: Option<GrpcApiConfig>,
307 discovery_config: Option<DiscoveryConfig>,
308}
309
310impl FullnodeConfigBuilder {
311 pub fn new() -> Self {
312 Self::default()
313 }
314
315 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
316 self.config_directory = Some(config_directory);
317 self
318 }
319
320 pub fn with_rpc_port(mut self, port: u16) -> Self {
321 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
322 self.rpc_port = Some(port);
323 self
324 }
325
326 pub fn with_rpc_addr(mut self, addr: impl Into<SocketAddr>) -> Self {
327 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
328 self.rpc_addr = Some(addr.into());
329 self
330 }
331
332 pub fn with_supported_protocol_versions(mut self, versions: SupportedProtocolVersions) -> Self {
333 self.supported_protocol_versions = Some(versions);
334 self
335 }
336
337 pub fn with_db_checkpoint_config(mut self, db_checkpoint_config: DBCheckpointConfig) -> Self {
338 self.db_checkpoint_config = Some(db_checkpoint_config);
339 self
340 }
341
342 pub fn with_disable_pruning(mut self, disable_pruning: bool) -> Self {
343 self.disable_pruning = disable_pruning;
344 self
345 }
346
347 pub fn with_expensive_safety_check_config(
348 mut self,
349 expensive_safety_check_config: ExpensiveSafetyCheckConfig,
350 ) -> Self {
351 self.expensive_safety_check_config = Some(expensive_safety_check_config);
352 self
353 }
354
355 pub fn with_db_path(mut self, db_path: PathBuf) -> Self {
356 self.db_path = Some(db_path);
357 self
358 }
359
360 pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
361 self.network_address = Some(network_address);
362 self
363 }
364
365 pub fn with_json_rpc_address(mut self, json_rpc_address: impl Into<SocketAddr>) -> Self {
366 self.json_rpc_address = Some(json_rpc_address.into());
367 self
368 }
369
370 pub fn with_metrics_address(mut self, metrics_address: impl Into<SocketAddr>) -> Self {
371 self.metrics_address = Some(metrics_address.into());
372 self
373 }
374
375 pub fn with_admin_interface_address(
376 mut self,
377 admin_interface_address: impl Into<SocketAddr>,
378 ) -> Self {
379 self.admin_interface_address = Some(admin_interface_address.into());
380 self
381 }
382
383 pub fn with_genesis(mut self, genesis: Genesis) -> Self {
384 self.genesis = Some(genesis);
385 self
386 }
387
388 pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
389 self.p2p_external_address = Some(p2p_external_address);
390 self
391 }
392
393 pub fn with_p2p_listen_address(mut self, p2p_listen_address: impl Into<SocketAddr>) -> Self {
394 self.p2p_listen_address = Some(p2p_listen_address.into());
395 self
396 }
397
398 pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
399 if let Some(network_key_pair) = network_key_pair {
400 self.network_key_pair =
401 Some(KeyPairWithPath::new(IotaKeyPair::Ed25519(network_key_pair)));
402 }
403 self
404 }
405
406 pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
407 if let Some(run_with_range) = run_with_range {
408 self.run_with_range = Some(run_with_range);
409 }
410 self
411 }
412
413 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
414 self.policy_config = config;
415 self
416 }
417
418 pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
419 self.fw_config = config;
420 self
421 }
422
423 pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
424 self.data_ingestion_dir = path;
425 self
426 }
427
428 pub fn with_iota_names_config(mut self, config: Option<IotaNamesConfig>) -> Self {
429 self.iota_names_config = config;
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(|| tempfile::tempdir().unwrap().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 checkpoint_executor_config = CheckpointExecutorConfig {
511 data_ingestion_dir: self.data_ingestion_dir,
512 ..Default::default()
513 };
514
515 let mut pruning_config = AuthorityStorePruningConfig::default();
516 if self.disable_pruning {
517 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
518 pruning_config.set_num_epochs_to_retain(u64::MAX);
519 };
520
521 NodeConfig {
522 authority_key_pair: AuthorityKeyPairWithPath::new(validator_config.authority_key_pair),
523 account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
524 protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
525 validator_config.protocol_key_pair,
526 )),
527 network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
528 IotaKeyPair::Ed25519(validator_config.network_key_pair),
529 )),
530 db_path: self
531 .db_path
532 .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
533 network_address: self
534 .network_address
535 .unwrap_or(validator_config.network_address),
536 metrics_address: self
537 .metrics_address
538 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
539 admin_interface_address: self
540 .admin_interface_address
541 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
542 json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
543 consensus_config: None,
544 remove_deprecated_tables: false,
545 enable_index_processing: default_enable_index_processing(),
546 genesis,
547 migration_tx_data_path,
548 grpc_load_shed: None,
549 grpc_concurrency_limit: None,
550 p2p_config,
551 authority_store_pruning_config: pruning_config,
552 end_of_epoch_broadcast_channel_capacity:
553 default_end_of_epoch_broadcast_channel_capacity(),
554 checkpoint_executor_config,
555 metrics: None,
556 supported_protocol_versions: self.supported_protocol_versions,
557 db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
558 expensive_safety_check_config: self
559 .expensive_safety_check_config
560 .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
561 transaction_deny_config: Default::default(),
562 certificate_deny_config: Default::default(),
563 state_debug_dump_config: Default::default(),
564 state_archive_write_config: StateArchiveConfig::default(),
565 state_archive_read_config: vec![],
566 state_snapshot_write_config: StateSnapshotConfig::default(),
567 indexer_max_subscriptions: Default::default(),
568 transaction_kv_store_read_config: Default::default(),
569 transaction_kv_store_write_config: Default::default(),
570 enable_rest_api: true,
571 rest: Some(iota_rest_api::Config {
572 enable_unstable_apis: Some(true),
573 ..Default::default()
574 }),
575 jwk_fetch_interval_seconds: 3600,
577 zklogin_oauth_providers: default_zklogin_oauth_providers(),
578 authority_overload_config: Default::default(),
579 run_with_range: self.run_with_range,
580 jsonrpc_server_type: None,
581 policy_config: self.policy_config,
582 firewall_config: self.fw_config,
583 execution_cache: ExecutionCacheType::default(),
584 execution_cache_config: ExecutionCacheConfig::default(),
585 enable_validator_tx_finalizer: false,
587 verifier_signing_config: VerifierSigningConfig::default(),
588 enable_db_write_stall: None,
589 iota_names_config: self.iota_names_config,
590 enable_grpc_api: self.grpc_api_config.is_some(),
591 grpc_api_config: self.grpc_api_config,
592 }
593 }
594
595 pub fn build<R: rand::RngCore + rand::CryptoRng>(
596 self,
597 rng: &mut R,
598 network_config: &NetworkConfig,
599 ) -> NodeConfig {
600 let genesis = self
601 .genesis
602 .as_ref()
603 .or_else(|| network_config.get_validator_genesis())
604 .cloned()
605 .unwrap_or_else(|| iota_config::node::Genesis::new(network_config.genesis.clone()));
606 self.build_from_parts(rng, network_config.validator_configs(), genesis)
607 }
608}
609
610fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
613 let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
614 let mut key_path = Hex::encode(public_key);
615 key_path.truncate(12);
618 key_path
619}