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 KeyPairWithPath, RunWithRange, StateArchiveConfig, StateSnapshotConfig,
19 default_enable_index_processing, default_end_of_epoch_broadcast_channel_capacity,
20 default_zklogin_oauth_providers,
21 },
22 p2p::{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}
56
57impl ValidatorConfigBuilder {
58 pub fn new() -> Self {
59 Self {
60 ..Default::default()
61 }
62 }
63
64 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
65 assert!(self.config_directory.is_none());
66 self.config_directory = Some(config_directory);
67 self
68 }
69
70 pub fn with_supported_protocol_versions(
71 mut self,
72 supported_protocol_versions: SupportedProtocolVersions,
73 ) -> Self {
74 assert!(self.supported_protocol_versions.is_none());
75 self.supported_protocol_versions = Some(supported_protocol_versions);
76 self
77 }
78
79 pub fn with_unpruned_checkpoints(mut self) -> Self {
80 self.force_unpruned_checkpoints = true;
81 self
82 }
83
84 pub fn with_jwk_fetch_interval(mut self, i: Duration) -> Self {
85 self.jwk_fetch_interval = Some(i);
86 self
87 }
88
89 pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
90 self.authority_overload_config = Some(config);
91 self
92 }
93
94 pub fn with_execution_cache_type(mut self, execution_cache_type: ExecutionCacheType) -> Self {
95 self.execution_cache_type = Some(execution_cache_type);
96 self
97 }
98
99 pub fn with_execution_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
100 self.execution_cache_config = Some(config);
101 self
102 }
103
104 pub fn with_data_ingestion_dir(mut self, path: PathBuf) -> Self {
105 self.data_ingestion_dir = Some(path);
106 self
107 }
108
109 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
110 self.policy_config = config;
111 self
112 }
113
114 pub fn with_firewall_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
115 self.firewall_config = config;
116 self
117 }
118
119 pub fn with_max_submit_position(mut self, max_submit_position: usize) -> Self {
120 self.max_submit_position = Some(max_submit_position);
121 self
122 }
123
124 pub fn with_submit_delay_step_override_millis(
125 mut self,
126 submit_delay_step_override_millis: u64,
127 ) -> Self {
128 self.submit_delay_step_override_millis = Some(submit_delay_step_override_millis);
129 self
130 }
131
132 pub fn build_without_genesis(self, validator: ValidatorGenesisConfig) -> NodeConfig {
133 let key_path = get_key_path(&validator.authority_key_pair);
134 let config_directory = self
135 .config_directory
136 .unwrap_or_else(|| tempfile::tempdir().unwrap().keep());
137 let migration_tx_data_path =
138 Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
139 let db_path = config_directory
140 .join(AUTHORITIES_DB_NAME)
141 .join(key_path.clone());
142 let network_address = validator.network_address;
143 let consensus_db_path = config_directory.join(CONSENSUS_DB_NAME).join(key_path);
144 let localhost = local_ip_utils::localhost_for_testing();
145 let consensus_config = ConsensusConfig {
146 db_path: consensus_db_path,
147 db_retention_epochs: None,
148 db_pruner_period_secs: None,
149 max_pending_transactions: None,
150 max_submit_position: self.max_submit_position,
151 submit_delay_step_override_millis: self.submit_delay_step_override_millis,
152 parameters: Default::default(),
153 };
154
155 let p2p_config = P2pConfig {
156 listen_address: validator.p2p_listen_address.unwrap_or_else(|| {
157 validator
158 .p2p_address
159 .udp_multiaddr_to_listen_address()
160 .unwrap()
161 }),
162 external_address: Some(validator.p2p_address),
163 state_sync: Some(StateSyncConfig {
166 checkpoint_content_timeout_ms: Some(10_000),
167 ..Default::default()
168 }),
169 ..Default::default()
170 };
171
172 let mut pruning_config = AuthorityStorePruningConfig::default();
173 if self.force_unpruned_checkpoints {
174 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
175 }
176 let pruning_config = pruning_config;
177 let checkpoint_executor_config = CheckpointExecutorConfig {
178 data_ingestion_dir: self.data_ingestion_dir,
179 ..Default::default()
180 };
181
182 NodeConfig {
183 authority_key_pair: AuthorityKeyPairWithPath::new(validator.authority_key_pair),
184 network_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
185 validator.network_key_pair,
186 )),
187 account_key_pair: KeyPairWithPath::new(validator.account_key_pair),
188 protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
189 validator.protocol_key_pair,
190 )),
191 db_path,
192 network_address,
193 metrics_address: validator.metrics_address,
194 admin_interface_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
195 .to_socket_addr()
196 .unwrap(),
197 json_rpc_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
198 .to_socket_addr()
199 .unwrap(),
200 consensus_config: Some(consensus_config),
201 remove_deprecated_tables: false,
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 enable_rest_api: true,
228 rest: Some(iota_rest_api::Config {
229 enable_unstable_apis: Some(true),
230 ..Default::default()
231 }),
232 jwk_fetch_interval_seconds: self
233 .jwk_fetch_interval
234 .map(|i| i.as_secs())
235 .unwrap_or(3600),
236 zklogin_oauth_providers: default_zklogin_oauth_providers(),
237 authority_overload_config: self.authority_overload_config.unwrap_or_default(),
238 execution_cache: self.execution_cache_type.unwrap_or_default(),
239 execution_cache_config: self.execution_cache_config.unwrap_or_default(),
240 run_with_range: None,
241 jsonrpc_server_type: None,
242 policy_config: self.policy_config,
243 firewall_config: self.firewall_config,
244 enable_validator_tx_finalizer: true,
245 verifier_signing_config: VerifierSigningConfig::default(),
246 enable_db_write_stall: None,
247 iota_names_config: None,
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}
296
297impl FullnodeConfigBuilder {
298 pub fn new() -> Self {
299 Self::default()
300 }
301
302 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
303 self.config_directory = Some(config_directory);
304 self
305 }
306
307 pub fn with_rpc_port(mut self, port: u16) -> Self {
308 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
309 self.rpc_port = Some(port);
310 self
311 }
312
313 pub fn with_rpc_addr(mut self, addr: impl Into<SocketAddr>) -> Self {
314 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
315 self.rpc_addr = Some(addr.into());
316 self
317 }
318
319 pub fn with_supported_protocol_versions(mut self, versions: SupportedProtocolVersions) -> Self {
320 self.supported_protocol_versions = Some(versions);
321 self
322 }
323
324 pub fn with_db_checkpoint_config(mut self, db_checkpoint_config: DBCheckpointConfig) -> Self {
325 self.db_checkpoint_config = Some(db_checkpoint_config);
326 self
327 }
328
329 pub fn with_disable_pruning(mut self, disable_pruning: bool) -> Self {
330 self.disable_pruning = disable_pruning;
331 self
332 }
333
334 pub fn with_expensive_safety_check_config(
335 mut self,
336 expensive_safety_check_config: ExpensiveSafetyCheckConfig,
337 ) -> Self {
338 self.expensive_safety_check_config = Some(expensive_safety_check_config);
339 self
340 }
341
342 pub fn with_db_path(mut self, db_path: PathBuf) -> Self {
343 self.db_path = Some(db_path);
344 self
345 }
346
347 pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
348 self.network_address = Some(network_address);
349 self
350 }
351
352 pub fn with_json_rpc_address(mut self, json_rpc_address: impl Into<SocketAddr>) -> Self {
353 self.json_rpc_address = Some(json_rpc_address.into());
354 self
355 }
356
357 pub fn with_metrics_address(mut self, metrics_address: impl Into<SocketAddr>) -> Self {
358 self.metrics_address = Some(metrics_address.into());
359 self
360 }
361
362 pub fn with_admin_interface_address(
363 mut self,
364 admin_interface_address: impl Into<SocketAddr>,
365 ) -> Self {
366 self.admin_interface_address = Some(admin_interface_address.into());
367 self
368 }
369
370 pub fn with_genesis(mut self, genesis: Genesis) -> Self {
371 self.genesis = Some(genesis);
372 self
373 }
374
375 pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
376 self.p2p_external_address = Some(p2p_external_address);
377 self
378 }
379
380 pub fn with_p2p_listen_address(mut self, p2p_listen_address: impl Into<SocketAddr>) -> Self {
381 self.p2p_listen_address = Some(p2p_listen_address.into());
382 self
383 }
384
385 pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
386 if let Some(network_key_pair) = network_key_pair {
387 self.network_key_pair =
388 Some(KeyPairWithPath::new(IotaKeyPair::Ed25519(network_key_pair)));
389 }
390 self
391 }
392
393 pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
394 if let Some(run_with_range) = run_with_range {
395 self.run_with_range = Some(run_with_range);
396 }
397 self
398 }
399
400 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
401 self.policy_config = config;
402 self
403 }
404
405 pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
406 self.fw_config = config;
407 self
408 }
409
410 pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
411 self.data_ingestion_dir = path;
412 self
413 }
414
415 pub fn with_iota_names_config(mut self, config: Option<IotaNamesConfig>) -> Self {
416 self.iota_names_config = config;
417 self
418 }
419
420 pub fn build_from_parts<R: rand::RngCore + rand::CryptoRng>(
421 self,
422 rng: &mut R,
423 validator_configs: &[NodeConfig],
424 genesis: iota_config::node::Genesis,
425 ) -> NodeConfig {
426 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
429 let ip = validator_config
430 .network_address
431 .to_socket_addr()
432 .unwrap()
433 .ip()
434 .to_string();
435
436 let key_path = get_key_path(&validator_config.authority_key_pair);
437 let config_directory = self
438 .config_directory
439 .unwrap_or_else(|| tempfile::tempdir().unwrap().keep());
440
441 let migration_tx_data_path =
442 Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
443
444 let p2p_config = {
445 let seed_peers = validator_configs
446 .iter()
447 .map(|config| SeedPeer {
448 peer_id: Some(anemo::PeerId(
449 config.network_key_pair().public().0.to_bytes(),
450 )),
451 address: config.p2p_config.external_address.clone().unwrap(),
452 })
453 .collect();
454
455 P2pConfig {
456 listen_address: self.p2p_listen_address.unwrap_or_else(|| {
457 validator_config.p2p_listen_address.unwrap_or_else(|| {
458 validator_config
459 .p2p_address
460 .udp_multiaddr_to_listen_address()
461 .unwrap()
462 })
463 }),
464 external_address: self
465 .p2p_external_address
466 .or(Some(validator_config.p2p_address.clone())),
467 seed_peers,
468 state_sync: Some(StateSyncConfig {
471 checkpoint_content_timeout_ms: Some(10_000),
472 ..Default::default()
473 }),
474 ..Default::default()
475 }
476 };
477
478 let json_rpc_address = self.rpc_addr.unwrap_or_else(|| {
479 let rpc_port = self
480 .rpc_port
481 .unwrap_or_else(|| local_ip_utils::get_available_port(&ip));
482 format!("{ip}:{rpc_port}").parse().unwrap()
483 });
484
485 let checkpoint_executor_config = CheckpointExecutorConfig {
486 data_ingestion_dir: self.data_ingestion_dir,
487 ..Default::default()
488 };
489
490 let mut pruning_config = AuthorityStorePruningConfig::default();
491 if self.disable_pruning {
492 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
493 pruning_config.set_num_epochs_to_retain(u64::MAX);
494 };
495
496 NodeConfig {
497 authority_key_pair: AuthorityKeyPairWithPath::new(validator_config.authority_key_pair),
498 account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
499 protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
500 validator_config.protocol_key_pair,
501 )),
502 network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
503 IotaKeyPair::Ed25519(validator_config.network_key_pair),
504 )),
505 db_path: self
506 .db_path
507 .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
508 network_address: self
509 .network_address
510 .unwrap_or(validator_config.network_address),
511 metrics_address: self
512 .metrics_address
513 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
514 admin_interface_address: self
515 .admin_interface_address
516 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
517 json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
518 consensus_config: None,
519 remove_deprecated_tables: false,
520 enable_index_processing: default_enable_index_processing(),
521 genesis,
522 migration_tx_data_path,
523 grpc_load_shed: None,
524 grpc_concurrency_limit: None,
525 p2p_config,
526 authority_store_pruning_config: pruning_config,
527 end_of_epoch_broadcast_channel_capacity:
528 default_end_of_epoch_broadcast_channel_capacity(),
529 checkpoint_executor_config,
530 metrics: None,
531 supported_protocol_versions: self.supported_protocol_versions,
532 db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
533 expensive_safety_check_config: self
534 .expensive_safety_check_config
535 .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
536 transaction_deny_config: Default::default(),
537 certificate_deny_config: Default::default(),
538 state_debug_dump_config: Default::default(),
539 state_archive_write_config: StateArchiveConfig::default(),
540 state_archive_read_config: vec![],
541 state_snapshot_write_config: StateSnapshotConfig::default(),
542 indexer_max_subscriptions: Default::default(),
543 transaction_kv_store_read_config: Default::default(),
544 transaction_kv_store_write_config: Default::default(),
545 enable_rest_api: true,
546 rest: Some(iota_rest_api::Config {
547 enable_unstable_apis: Some(true),
548 ..Default::default()
549 }),
550 jwk_fetch_interval_seconds: 3600,
552 zklogin_oauth_providers: default_zklogin_oauth_providers(),
553 authority_overload_config: Default::default(),
554 run_with_range: self.run_with_range,
555 jsonrpc_server_type: None,
556 policy_config: self.policy_config,
557 firewall_config: self.fw_config,
558 execution_cache: ExecutionCacheType::default(),
559 execution_cache_config: ExecutionCacheConfig::default(),
560 enable_validator_tx_finalizer: false,
562 verifier_signing_config: VerifierSigningConfig::default(),
563 enable_db_write_stall: None,
564 iota_names_config: self.iota_names_config,
565 }
566 }
567
568 pub fn build<R: rand::RngCore + rand::CryptoRng>(
569 self,
570 rng: &mut R,
571 network_config: &NetworkConfig,
572 ) -> NodeConfig {
573 let genesis = self
574 .genesis
575 .as_ref()
576 .or_else(|| network_config.get_validator_genesis())
577 .cloned()
578 .unwrap_or_else(|| iota_config::node::Genesis::new(network_config.genesis.clone()));
579 self.build_from_parts(rng, network_config.validator_configs(), genesis)
580 }
581}
582
583fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
586 let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
587 let mut key_path = Hex::encode(public_key);
588 key_path.truncate(12);
591 key_path
592}