sui_swarm_config/
node_config_builder.rs

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