iota_swarm_config/
node_config_builder.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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        GrpcApiConfig, KeyPairWithPath, RunWithRange, StateArchiveConfig, StateSnapshotConfig,
19        default_enable_index_processing, default_end_of_epoch_broadcast_channel_capacity,
20        default_zklogin_oauth_providers,
21    },
22    p2p::{DiscoveryConfig, P2pConfig, SeedPeer, StateSyncConfig},
23    verifier_signing_config::VerifierSigningConfig,
24};
25use iota_names::config::IotaNamesConfig;
26use iota_protocol_config::Chain;
27use iota_types::{
28    crypto::{AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, NetworkKeyPair},
29    multiaddr::Multiaddr,
30    supported_protocol_versions::SupportedProtocolVersions,
31    traffic_control::{PolicyConfig, RemoteFirewallConfig},
32};
33
34use crate::{
35    genesis_config::{ValidatorGenesisConfig, ValidatorGenesisConfigBuilder},
36    network_config::NetworkConfig,
37};
38
39/// This builder contains information that's not included in
40/// ValidatorGenesisConfig for building a validator NodeConfig. It can be used
41/// to build either a genesis validator or a new validator.
42#[derive(Clone, Default)]
43pub struct ValidatorConfigBuilder {
44    config_directory: Option<PathBuf>,
45    supported_protocol_versions: Option<SupportedProtocolVersions>,
46    force_unpruned_checkpoints: bool,
47    jwk_fetch_interval: Option<Duration>,
48    authority_overload_config: Option<AuthorityOverloadConfig>,
49    execution_cache_type: Option<ExecutionCacheType>,
50    execution_cache_config: Option<ExecutionCacheConfig>,
51    data_ingestion_dir: Option<PathBuf>,
52    policy_config: Option<PolicyConfig>,
53    firewall_config: Option<RemoteFirewallConfig>,
54    max_submit_position: Option<usize>,
55    submit_delay_step_override_millis: Option<u64>,
56    discovery_config: Option<DiscoveryConfig>,
57    chain_override: Option<Chain>,
58}
59
60impl ValidatorConfigBuilder {
61    pub fn new() -> Self {
62        Self {
63            ..Default::default()
64        }
65    }
66
67    pub fn with_chain_override(mut self, chain: Chain) -> Self {
68        assert!(self.chain_override.is_none(), "Chain override already set");
69        self.chain_override = Some(chain);
70        self
71    }
72
73    pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
74        assert!(self.config_directory.is_none());
75        self.config_directory = Some(config_directory);
76        self
77    }
78
79    pub fn with_supported_protocol_versions(
80        mut self,
81        supported_protocol_versions: SupportedProtocolVersions,
82    ) -> Self {
83        assert!(self.supported_protocol_versions.is_none());
84        self.supported_protocol_versions = Some(supported_protocol_versions);
85        self
86    }
87
88    pub fn with_unpruned_checkpoints(mut self) -> Self {
89        self.force_unpruned_checkpoints = true;
90        self
91    }
92
93    pub fn with_jwk_fetch_interval(mut self, i: Duration) -> Self {
94        self.jwk_fetch_interval = Some(i);
95        self
96    }
97
98    pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
99        self.authority_overload_config = Some(config);
100        self
101    }
102
103    pub fn with_execution_cache_type(mut self, execution_cache_type: ExecutionCacheType) -> Self {
104        self.execution_cache_type = Some(execution_cache_type);
105        self
106    }
107
108    pub fn with_execution_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
109        self.execution_cache_config = Some(config);
110        self
111    }
112
113    pub fn with_data_ingestion_dir(mut self, path: PathBuf) -> Self {
114        self.data_ingestion_dir = Some(path);
115        self
116    }
117
118    pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
119        self.policy_config = config;
120        self
121    }
122
123    pub fn with_firewall_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
124        self.firewall_config = config;
125        self
126    }
127
128    pub fn with_max_submit_position(mut self, max_submit_position: usize) -> Self {
129        self.max_submit_position = Some(max_submit_position);
130        self
131    }
132
133    pub fn with_submit_delay_step_override_millis(
134        mut self,
135        submit_delay_step_override_millis: u64,
136    ) -> Self {
137        self.submit_delay_step_override_millis = Some(submit_delay_step_override_millis);
138        self
139    }
140
141    pub fn with_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
142        self.discovery_config = Some(discovery_config);
143        self
144    }
145
146    pub fn build_without_genesis(self, validator: ValidatorGenesisConfig) -> NodeConfig {
147        let key_path = get_key_path(&validator.authority_key_pair);
148        let config_directory = self
149            .config_directory
150            .unwrap_or_else(|| tempfile::tempdir().unwrap().keep());
151        let migration_tx_data_path =
152            Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
153        let db_path = config_directory
154            .join(AUTHORITIES_DB_NAME)
155            .join(key_path.clone());
156        let network_address = validator.network_address;
157        let consensus_db_path = config_directory.join(CONSENSUS_DB_NAME).join(key_path);
158        let localhost = local_ip_utils::localhost_for_testing();
159        let consensus_config = ConsensusConfig {
160            db_path: consensus_db_path,
161            db_retention_epochs: None,
162            db_pruner_period_secs: None,
163            max_pending_transactions: None,
164            max_submit_position: self.max_submit_position,
165            submit_delay_step_override_millis: self.submit_delay_step_override_millis,
166            parameters: Default::default(),
167            starfish_parameters: Default::default(),
168        };
169
170        let p2p_config = P2pConfig {
171            listen_address: validator.p2p_listen_address.unwrap_or_else(|| {
172                validator
173                    .p2p_address
174                    .udp_multiaddr_to_listen_address()
175                    .unwrap()
176            }),
177            external_address: Some(validator.p2p_address),
178            // Set a shorter timeout for checkpoint content download in tests, since
179            // checkpoint pruning also happens much faster, and network is local.
180            state_sync: Some(StateSyncConfig {
181                checkpoint_content_timeout_ms: Some(10_000),
182                ..Default::default()
183            }),
184            // Use discovery config if provided
185            discovery: self.discovery_config,
186            ..Default::default()
187        };
188
189        let mut pruning_config = AuthorityStorePruningConfig::default();
190        if self.force_unpruned_checkpoints {
191            pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
192        }
193        let pruning_config = pruning_config;
194        let checkpoint_executor_config = CheckpointExecutorConfig {
195            data_ingestion_dir: self.data_ingestion_dir,
196            ..Default::default()
197        };
198
199        NodeConfig {
200            authority_key_pair: AuthorityKeyPairWithPath::new(validator.authority_key_pair),
201            network_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
202                validator.network_key_pair,
203            )),
204            account_key_pair: KeyPairWithPath::new(validator.account_key_pair),
205            protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
206                validator.protocol_key_pair,
207            )),
208            db_path,
209            network_address,
210            metrics_address: validator.metrics_address,
211            admin_interface_address: validator.admin_interface_address,
212            json_rpc_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
213                .to_socket_addr()
214                .unwrap(),
215            consensus_config: Some(consensus_config),
216            remove_deprecated_tables: false,
217            enable_index_processing: default_enable_index_processing(),
218            genesis: Genesis::new_empty(),
219            migration_tx_data_path,
220            grpc_load_shed: None,
221            grpc_concurrency_limit: Some(DEFAULT_GRPC_CONCURRENCY_LIMIT),
222            p2p_config,
223            authority_store_pruning_config: pruning_config,
224            end_of_epoch_broadcast_channel_capacity:
225                default_end_of_epoch_broadcast_channel_capacity(),
226            checkpoint_executor_config,
227            metrics: None,
228            supported_protocol_versions: self.supported_protocol_versions,
229            db_checkpoint_config: Default::default(),
230            // By default, expensive checks will be enabled in debug build, but not in release
231            // build.
232            expensive_safety_check_config: ExpensiveSafetyCheckConfig::default(),
233            transaction_deny_config: Default::default(),
234            certificate_deny_config: Default::default(),
235            state_debug_dump_config: Default::default(),
236            state_archive_write_config: StateArchiveConfig::default(),
237            state_archive_read_config: vec![],
238            state_snapshot_write_config: StateSnapshotConfig::default(),
239            indexer_max_subscriptions: Default::default(),
240            transaction_kv_store_read_config: Default::default(),
241            transaction_kv_store_write_config: None,
242            enable_rest_api: true,
243            rest: Some(iota_rest_api::Config {
244                enable_unstable_apis: Some(true),
245                ..Default::default()
246            }),
247            jwk_fetch_interval_seconds: self
248                .jwk_fetch_interval
249                .map(|i| i.as_secs())
250                .unwrap_or(3600),
251            zklogin_oauth_providers: default_zklogin_oauth_providers(),
252            authority_overload_config: self.authority_overload_config.unwrap_or_default(),
253            execution_cache: self.execution_cache_type.unwrap_or_default(),
254            execution_cache_config: self.execution_cache_config.unwrap_or_default(),
255            run_with_range: None,
256            jsonrpc_server_type: None,
257            policy_config: self.policy_config,
258            firewall_config: self.firewall_config,
259            enable_validator_tx_finalizer: true,
260            verifier_signing_config: VerifierSigningConfig::default(),
261            enable_db_write_stall: None,
262            iota_names_config: None,
263            enable_grpc_api: false,
264            grpc_api_config: None,
265            chain_override_for_testing: self.chain_override,
266        }
267    }
268
269    pub fn build(
270        self,
271        validator: ValidatorGenesisConfig,
272        genesis: iota_config::genesis::Genesis,
273    ) -> NodeConfig {
274        let mut config = self.build_without_genesis(validator);
275        config.genesis = iota_config::node::Genesis::new(genesis);
276        config
277    }
278
279    pub fn build_new_validator<R: rand::RngCore + rand::CryptoRng>(
280        self,
281        rng: &mut R,
282        network_config: &NetworkConfig,
283    ) -> NodeConfig {
284        let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
285        self.build(validator_config, network_config.genesis.clone())
286    }
287}
288
289#[derive(Clone, Debug, Default)]
290pub struct FullnodeConfigBuilder {
291    config_directory: Option<PathBuf>,
292    // port for json rpc api
293    rpc_port: Option<u16>,
294    rpc_addr: Option<SocketAddr>,
295    supported_protocol_versions: Option<SupportedProtocolVersions>,
296    db_checkpoint_config: Option<DBCheckpointConfig>,
297    expensive_safety_check_config: Option<ExpensiveSafetyCheckConfig>,
298    db_path: Option<PathBuf>,
299    network_address: Option<Multiaddr>,
300    json_rpc_address: Option<SocketAddr>,
301    metrics_address: Option<SocketAddr>,
302    admin_interface_address: Option<SocketAddr>,
303    genesis: Option<Genesis>,
304    p2p_external_address: Option<Multiaddr>,
305    p2p_listen_address: Option<SocketAddr>,
306    network_key_pair: Option<KeyPairWithPath>,
307    run_with_range: Option<RunWithRange>,
308    policy_config: Option<PolicyConfig>,
309    fw_config: Option<RemoteFirewallConfig>,
310    data_ingestion_dir: Option<PathBuf>,
311    disable_pruning: bool,
312    iota_names_config: Option<IotaNamesConfig>,
313    enable_grpc_api: bool,
314    grpc_api_config: Option<GrpcApiConfig>,
315    discovery_config: Option<DiscoveryConfig>,
316    chain_override: Option<Chain>,
317}
318
319impl FullnodeConfigBuilder {
320    pub fn new() -> Self {
321        Self::default()
322    }
323
324    pub fn with_chain_override(mut self, chain: Chain) -> Self {
325        assert!(self.chain_override.is_none(), "Chain override already set");
326        self.chain_override = Some(chain);
327        self
328    }
329
330    pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
331        self.config_directory = Some(config_directory);
332        self
333    }
334
335    pub fn with_rpc_port(mut self, port: u16) -> Self {
336        assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
337        self.rpc_port = Some(port);
338        self
339    }
340
341    pub fn with_rpc_addr(mut self, addr: impl Into<SocketAddr>) -> Self {
342        assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
343        self.rpc_addr = Some(addr.into());
344        self
345    }
346
347    pub fn with_supported_protocol_versions(mut self, versions: SupportedProtocolVersions) -> Self {
348        self.supported_protocol_versions = Some(versions);
349        self
350    }
351
352    pub fn with_db_checkpoint_config(mut self, db_checkpoint_config: DBCheckpointConfig) -> Self {
353        self.db_checkpoint_config = Some(db_checkpoint_config);
354        self
355    }
356
357    pub fn with_disable_pruning(mut self, disable_pruning: bool) -> Self {
358        self.disable_pruning = disable_pruning;
359        self
360    }
361
362    pub fn with_expensive_safety_check_config(
363        mut self,
364        expensive_safety_check_config: ExpensiveSafetyCheckConfig,
365    ) -> Self {
366        self.expensive_safety_check_config = Some(expensive_safety_check_config);
367        self
368    }
369
370    pub fn with_db_path(mut self, db_path: PathBuf) -> Self {
371        self.db_path = Some(db_path);
372        self
373    }
374
375    pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
376        self.network_address = Some(network_address);
377        self
378    }
379
380    pub fn with_json_rpc_address(mut self, json_rpc_address: impl Into<SocketAddr>) -> Self {
381        self.json_rpc_address = Some(json_rpc_address.into());
382        self
383    }
384
385    pub fn with_metrics_address(mut self, metrics_address: impl Into<SocketAddr>) -> Self {
386        self.metrics_address = Some(metrics_address.into());
387        self
388    }
389
390    pub fn with_admin_interface_address(
391        mut self,
392        admin_interface_address: Option<impl Into<SocketAddr>>,
393    ) -> Self {
394        self.admin_interface_address = admin_interface_address.map(|addr| addr.into());
395        self
396    }
397
398    pub fn with_genesis(mut self, genesis: Genesis) -> Self {
399        self.genesis = Some(genesis);
400        self
401    }
402
403    pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
404        self.p2p_external_address = Some(p2p_external_address);
405        self
406    }
407
408    pub fn with_p2p_listen_address(mut self, p2p_listen_address: impl Into<SocketAddr>) -> Self {
409        self.p2p_listen_address = Some(p2p_listen_address.into());
410        self
411    }
412
413    pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
414        if let Some(network_key_pair) = network_key_pair {
415            self.network_key_pair =
416                Some(KeyPairWithPath::new(IotaKeyPair::Ed25519(network_key_pair)));
417        }
418        self
419    }
420
421    pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
422        if let Some(run_with_range) = run_with_range {
423            self.run_with_range = Some(run_with_range);
424        }
425        self
426    }
427
428    pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
429        self.policy_config = config;
430        self
431    }
432
433    pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
434        self.fw_config = config;
435        self
436    }
437
438    pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
439        self.data_ingestion_dir = path;
440        self
441    }
442
443    pub fn with_iota_names_config(mut self, config: Option<IotaNamesConfig>) -> Self {
444        self.iota_names_config = config;
445        self
446    }
447
448    pub fn with_enable_grpc_api(mut self, enable_grpc_api: bool) -> Self {
449        self.enable_grpc_api = enable_grpc_api;
450        self
451    }
452
453    pub fn with_grpc_api_config(mut self, config: GrpcApiConfig) -> Self {
454        self.grpc_api_config = Some(config);
455        self
456    }
457
458    pub fn with_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
459        self.discovery_config = Some(discovery_config);
460        self
461    }
462
463    pub fn build_from_parts<R: rand::RngCore + rand::CryptoRng>(
464        self,
465        rng: &mut R,
466        validator_configs: &[NodeConfig],
467        genesis: iota_config::node::Genesis,
468    ) -> NodeConfig {
469        // Take advantage of ValidatorGenesisConfigBuilder to build the keypairs and
470        // addresses, even though this is a fullnode.
471        let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
472        let ip = validator_config
473            .network_address
474            .to_socket_addr()
475            .unwrap()
476            .ip()
477            .to_string();
478
479        let key_path = get_key_path(&validator_config.authority_key_pair);
480        let config_directory = self
481            .config_directory
482            .unwrap_or_else(|| tempfile::tempdir().unwrap().keep());
483
484        let migration_tx_data_path =
485            Some(config_directory.join(IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME));
486
487        let p2p_config = {
488            let seed_peers = validator_configs
489                .iter()
490                .map(|config| SeedPeer {
491                    peer_id: Some(anemo::PeerId(
492                        config.network_key_pair().public().0.to_bytes(),
493                    )),
494                    address: config.p2p_config.external_address.clone().unwrap(),
495                })
496                .collect();
497
498            P2pConfig {
499                listen_address: self.p2p_listen_address.unwrap_or_else(|| {
500                    validator_config.p2p_listen_address.unwrap_or_else(|| {
501                        validator_config
502                            .p2p_address
503                            .udp_multiaddr_to_listen_address()
504                            .unwrap()
505                    })
506                }),
507                external_address: self
508                    .p2p_external_address
509                    .or(Some(validator_config.p2p_address.clone())),
510                seed_peers,
511                // Set a shorter timeout for checkpoint content download in tests, since
512                // checkpoint pruning also happens much faster, and network is local.
513                state_sync: Some(StateSyncConfig {
514                    checkpoint_content_timeout_ms: Some(10_000),
515                    ..Default::default()
516                }),
517                // Use discovery config if provided
518                discovery: self.discovery_config,
519                ..Default::default()
520            }
521        };
522
523        let json_rpc_address = self.rpc_addr.unwrap_or_else(|| {
524            let rpc_port = self
525                .rpc_port
526                .unwrap_or_else(|| local_ip_utils::get_available_port(&ip));
527            format!("{ip}:{rpc_port}").parse().unwrap()
528        });
529
530        let grpc_api_config = self.grpc_api_config.or_else(|| {
531            if self.enable_grpc_api {
532                Some(GrpcApiConfig {
533                    address: format!("{ip}:{}", local_ip_utils::get_available_port(&ip))
534                        .parse()
535                        .unwrap(),
536                    ..Default::default()
537                })
538            } else {
539                None
540            }
541        });
542
543        let checkpoint_executor_config = CheckpointExecutorConfig {
544            data_ingestion_dir: self.data_ingestion_dir,
545            ..Default::default()
546        };
547
548        let mut pruning_config = AuthorityStorePruningConfig::default();
549        if self.disable_pruning {
550            pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
551            pruning_config.set_num_epochs_to_retain(u64::MAX);
552        };
553
554        NodeConfig {
555            authority_key_pair: AuthorityKeyPairWithPath::new(validator_config.authority_key_pair),
556            account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
557            protocol_key_pair: KeyPairWithPath::new(IotaKeyPair::Ed25519(
558                validator_config.protocol_key_pair,
559            )),
560            network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
561                IotaKeyPair::Ed25519(validator_config.network_key_pair),
562            )),
563            db_path: self
564                .db_path
565                .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
566            network_address: self
567                .network_address
568                .unwrap_or(validator_config.network_address),
569            metrics_address: self
570                .metrics_address
571                .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
572            admin_interface_address: self
573                .admin_interface_address
574                .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
575            json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
576            consensus_config: None,
577            remove_deprecated_tables: false,
578            enable_index_processing: default_enable_index_processing(),
579            genesis,
580            migration_tx_data_path,
581            grpc_load_shed: None,
582            grpc_concurrency_limit: None,
583            p2p_config,
584            authority_store_pruning_config: pruning_config,
585            end_of_epoch_broadcast_channel_capacity:
586                default_end_of_epoch_broadcast_channel_capacity(),
587            checkpoint_executor_config,
588            metrics: None,
589            supported_protocol_versions: self.supported_protocol_versions,
590            db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
591            expensive_safety_check_config: self
592                .expensive_safety_check_config
593                .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
594            transaction_deny_config: Default::default(),
595            certificate_deny_config: Default::default(),
596            state_debug_dump_config: Default::default(),
597            state_archive_write_config: StateArchiveConfig::default(),
598            state_archive_read_config: vec![],
599            state_snapshot_write_config: StateSnapshotConfig::default(),
600            indexer_max_subscriptions: Default::default(),
601            transaction_kv_store_read_config: Default::default(),
602            transaction_kv_store_write_config: Default::default(),
603            enable_rest_api: true,
604            rest: Some(iota_rest_api::Config {
605                enable_unstable_apis: Some(true),
606                ..Default::default()
607            }),
608            // note: not used by fullnodes.
609            jwk_fetch_interval_seconds: 3600,
610            zklogin_oauth_providers: default_zklogin_oauth_providers(),
611            authority_overload_config: Default::default(),
612            run_with_range: self.run_with_range,
613            jsonrpc_server_type: None,
614            policy_config: self.policy_config,
615            firewall_config: self.fw_config,
616            execution_cache: ExecutionCacheType::default(),
617            execution_cache_config: ExecutionCacheConfig::default(),
618            // This is a validator specific feature.
619            enable_validator_tx_finalizer: false,
620            verifier_signing_config: VerifierSigningConfig::default(),
621            enable_db_write_stall: None,
622            iota_names_config: self.iota_names_config,
623            enable_grpc_api: self.enable_grpc_api,
624            grpc_api_config,
625            chain_override_for_testing: self.chain_override,
626        }
627    }
628
629    pub fn build<R: rand::RngCore + rand::CryptoRng>(
630        self,
631        rng: &mut R,
632        network_config: &NetworkConfig,
633    ) -> NodeConfig {
634        let genesis = self
635            .genesis
636            .as_ref()
637            .or_else(|| network_config.get_validator_genesis())
638            .cloned()
639            .unwrap_or_else(|| iota_config::node::Genesis::new(network_config.genesis.clone()));
640        self.build_from_parts(rng, network_config.validator_configs(), genesis)
641    }
642}
643
644/// Given a validator keypair, return a path that can be used to identify the
645/// validator.
646fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
647    let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
648    let mut key_path = Hex::encode(public_key);
649    // 12 is rather arbitrary here but it's a nice balance between being short and
650    // being unique.
651    key_path.truncate(12);
652    key_path
653}