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