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