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