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