Skip to main content

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 consensus_config::{
9    NetworkPublicKey, ObserverParameters, Parameters as ConsensusParameters, PeerRecord,
10};
11use fastcrypto::encoding::{Encoding, Hex};
12use fastcrypto::traits::KeyPair;
13use sui_config::node::{
14    AuthorityKeyPairWithPath, AuthorityOverloadConfig, AuthorityStorePruningConfig,
15    CheckpointExecutorConfig, ConsensusTransactionPoolConfig, DBCheckpointConfig,
16    DEFAULT_GRPC_CONCURRENCY_LIMIT, ExecutionCacheConfig, ExecutionTimeObserverConfig,
17    ExpensiveSafetyCheckConfig, FundsWithdrawSchedulerType, Genesis, KeyPairWithPath,
18    StateSnapshotConfig, default_enable_index_processing,
19    default_end_of_epoch_broadcast_channel_capacity,
20};
21use sui_config::node::{RunWithRange, TransactionDriverConfig, default_zklogin_oauth_providers};
22use sui_config::p2p::{P2pConfig, SeedPeer, StateSyncConfig};
23use sui_config::transaction_deny_config::PeerDenySyncConfig;
24use sui_config::verifier_signing_config::VerifierSigningConfig;
25use sui_config::{
26    AUTHORITIES_DB_NAME, CONSENSUS_DB_NAME, ConsensusConfig, FULL_NODE_DB_PATH, NodeConfig,
27    local_ip_utils,
28};
29use sui_protocol_config::Chain;
30use sui_types::crypto::{AuthorityKeyPair, AuthorityPublicKeyBytes, NetworkKeyPair, SuiKeyPair};
31use sui_types::multiaddr::Multiaddr;
32use sui_types::node_role::FullNodeSyncMode;
33use sui_types::supported_protocol_versions::SupportedProtocolVersions;
34use sui_types::traffic_control::{PolicyConfig, RemoteFirewallConfig};
35
36use crate::genesis_config::{ValidatorGenesisConfig, ValidatorGenesisConfigBuilder};
37use crate::network_config::NetworkConfig;
38
39/// This builder contains information that's not included in ValidatorGenesisConfig for building
40/// a validator NodeConfig. It can be used to build either a genesis validator or a new validator.
41#[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_config: Option<ExecutionCacheConfig>,
49    data_ingestion_dir: Option<PathBuf>,
50    policy_config: Option<PolicyConfig>,
51    firewall_config: Option<RemoteFirewallConfig>,
52    global_state_hash_v2: bool,
53    funds_withdraw_scheduler_type: FundsWithdrawSchedulerType,
54    execution_time_observer_config: Option<ExecutionTimeObserverConfig>,
55    chain_override: Option<Chain>,
56    state_sync_config: Option<StateSyncConfig>,
57    observer_config: Option<ObserverParameters>,
58    peer_deny_sync_config: Option<PeerDenySyncConfig>,
59    consensus_transaction_pool_config: Option<ConsensusTransactionPoolConfig>,
60}
61
62impl ValidatorConfigBuilder {
63    pub fn new() -> Self {
64        Self {
65            global_state_hash_v2: true,
66            ..Default::default()
67        }
68    }
69
70    pub fn with_chain_override(mut self, chain: Chain) -> Self {
71        assert!(self.chain_override.is_none(), "Chain override already set");
72        self.chain_override = Some(chain);
73        self
74    }
75
76    pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
77        assert!(self.config_directory.is_none());
78        self.config_directory = Some(config_directory);
79        self
80    }
81
82    pub fn with_supported_protocol_versions(
83        mut self,
84        supported_protocol_versions: SupportedProtocolVersions,
85    ) -> Self {
86        assert!(self.supported_protocol_versions.is_none());
87        self.supported_protocol_versions = Some(supported_protocol_versions);
88        self
89    }
90
91    pub fn with_unpruned_checkpoints(mut self) -> Self {
92        self.force_unpruned_checkpoints = true;
93        self
94    }
95
96    pub fn with_jwk_fetch_interval(mut self, i: Duration) -> Self {
97        self.jwk_fetch_interval = Some(i);
98        self
99    }
100
101    pub fn with_authority_overload_config(mut self, config: AuthorityOverloadConfig) -> Self {
102        self.authority_overload_config = Some(config);
103        self
104    }
105
106    pub fn with_consensus_transaction_pool_config(
107        mut self,
108        config: ConsensusTransactionPoolConfig,
109    ) -> Self {
110        self.consensus_transaction_pool_config = Some(config);
111        self
112    }
113
114    pub fn with_execution_cache_config(mut self, config: ExecutionCacheConfig) -> Self {
115        self.execution_cache_config = Some(config);
116        self
117    }
118
119    pub fn with_data_ingestion_dir(mut self, path: PathBuf) -> Self {
120        self.data_ingestion_dir = Some(path);
121        self
122    }
123
124    pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
125        self.policy_config = config;
126        self
127    }
128
129    pub fn with_firewall_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
130        self.firewall_config = config;
131        self
132    }
133
134    pub fn with_global_state_hash_v2_enabled(mut self, enabled: bool) -> Self {
135        self.global_state_hash_v2 = enabled;
136        self
137    }
138
139    pub fn with_funds_withdraw_scheduler_type(
140        mut self,
141        scheduler_type: FundsWithdrawSchedulerType,
142    ) -> Self {
143        self.funds_withdraw_scheduler_type = scheduler_type;
144        self
145    }
146
147    pub fn with_execution_time_observer_config(
148        mut self,
149        config: ExecutionTimeObserverConfig,
150    ) -> Self {
151        self.execution_time_observer_config = Some(config);
152        self
153    }
154
155    pub fn with_state_sync_config(mut self, config: StateSyncConfig) -> Self {
156        self.state_sync_config = Some(config);
157        self
158    }
159
160    pub fn with_observer_config(mut self, config: ObserverParameters) -> Self {
161        self.observer_config = Some(config);
162        self
163    }
164
165    pub fn with_peer_deny_sync_config(mut self, config: PeerDenySyncConfig) -> Self {
166        self.peer_deny_sync_config = Some(config);
167        self
168    }
169
170    pub fn build(
171        self,
172        validator: ValidatorGenesisConfig,
173        genesis: sui_config::genesis::Genesis,
174    ) -> NodeConfig {
175        let key_path = get_key_path(&validator.key_pair);
176        let config_directory = self
177            .config_directory
178            .unwrap_or_else(|| mysten_common::tempdir().unwrap().keep());
179        let db_path = config_directory
180            .join(AUTHORITIES_DB_NAME)
181            .join(key_path.clone());
182
183        let network_address = validator.network_address;
184        let consensus_db_path = config_directory.join(CONSENSUS_DB_NAME).join(key_path);
185        let localhost = local_ip_utils::localhost_for_testing();
186        let parameters = self
187            .observer_config
188            .map(|observer_config| ConsensusParameters {
189                observer: ObserverParameters {
190                    server_port: observer_config
191                        .server_port
192                        .or_else(|| Some(local_ip_utils::get_available_port(&localhost))),
193                    allowlist: observer_config.allowlist,
194                    quorum_release: observer_config.quorum_release,
195                    peers: observer_config.peers,
196                },
197                ..Default::default()
198            });
199        let consensus_config = ConsensusConfig {
200            db_path: consensus_db_path,
201            db_retention_epochs: None,
202            db_pruner_period_secs: None,
203            max_pending_transactions: None,
204            parameters,
205            listen_address: None,
206            external_address: None,
207        };
208
209        let p2p_config = P2pConfig {
210            listen_address: validator.p2p_listen_address.unwrap_or_else(|| {
211                validator
212                    .p2p_address
213                    .udp_multiaddr_to_listen_address()
214                    .unwrap()
215            }),
216            external_address: Some(validator.p2p_address),
217            // Set a shorter timeout for checkpoint content download in tests, since
218            // checkpoint pruning also happens much faster, and network is local.
219            state_sync: Some(if let Some(mut config) = self.state_sync_config {
220                if config.checkpoint_content_timeout_ms.is_none() {
221                    config.checkpoint_content_timeout_ms = Some(10_000);
222                }
223                config
224            } else {
225                StateSyncConfig {
226                    checkpoint_content_timeout_ms: Some(10_000),
227                    ..Default::default()
228                }
229            }),
230            ..Default::default()
231        };
232
233        let mut pruning_config = AuthorityStorePruningConfig::default();
234        if self.force_unpruned_checkpoints {
235            pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
236        }
237        let pruning_config = pruning_config;
238        let checkpoint_executor_config = CheckpointExecutorConfig {
239            data_ingestion_dir: self.data_ingestion_dir,
240            ..Default::default()
241        };
242
243        NodeConfig {
244            recent_submission_dedup_window_ms: None,
245            address_prober: None,
246            protocol_key_pair: AuthorityKeyPairWithPath::new(validator.key_pair),
247            network_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(validator.network_key_pair)),
248            account_key_pair: KeyPairWithPath::new(validator.account_key_pair),
249            worker_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(validator.worker_key_pair)),
250            db_path,
251            network_address,
252            metrics_address: validator.metrics_address,
253            admin_interface_port: local_ip_utils::get_available_port(&localhost),
254            json_rpc_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
255                .to_socket_addr()
256                .unwrap(),
257            consensus_config: Some(consensus_config),
258            fullnode_sync_mode: None,
259            remove_deprecated_tables: false,
260            enable_index_processing: default_enable_index_processing(),
261            sync_post_process_one_tx: false,
262            genesis: sui_config::node::Genesis::new(genesis),
263            grpc_load_shed: None,
264            grpc_concurrency_limit: Some(DEFAULT_GRPC_CONCURRENCY_LIMIT),
265            p2p_config,
266            authority_store_pruning_config: pruning_config,
267            end_of_epoch_broadcast_channel_capacity:
268                default_end_of_epoch_broadcast_channel_capacity(),
269            checkpoint_executor_config,
270            metrics: None,
271            supported_protocol_versions: self.supported_protocol_versions,
272            db_checkpoint_config: Default::default(),
273            // By default, expensive checks will be enabled in debug build, but not in release build.
274            expensive_safety_check_config: ExpensiveSafetyCheckConfig::default(),
275            name_service_package_address: None,
276            name_service_registry_id: None,
277            name_service_reverse_registry_id: None,
278            transaction_deny_config: Default::default(),
279            peer_deny_sync_config: self.peer_deny_sync_config.unwrap_or_default(),
280            dev_inspect_disabled: false,
281            certificate_deny_config: Default::default(),
282            state_debug_dump_config: Default::default(),
283            state_archive_read_config: vec![],
284            state_snapshot_write_config: StateSnapshotConfig::default(),
285            indexer_max_subscriptions: Default::default(),
286            transaction_kv_store_read_config: Default::default(),
287            transaction_kv_store_write_config: None,
288            rpc: Some(sui_rpc_api::Config {
289                ..Default::default()
290            }),
291            jwk_fetch_interval_seconds: self
292                .jwk_fetch_interval
293                .map(|i| i.as_secs())
294                .unwrap_or(3600),
295            zklogin_oauth_providers: default_zklogin_oauth_providers(),
296            authority_overload_config: self.authority_overload_config.unwrap_or_default(),
297            execution_cache: self.execution_cache_config.unwrap_or_default(),
298            run_with_range: None,
299            jsonrpc_server_type: None,
300            disable_json_rpc: false,
301            policy_config: self.policy_config,
302            firewall_config: self.firewall_config,
303            state_accumulator_v2: self.global_state_hash_v2,
304            funds_withdraw_scheduler_type: self.funds_withdraw_scheduler_type,
305            enable_soft_bundle: true,
306            enable_simulate_allowed_proposers: true,
307            verifier_signing_config: VerifierSigningConfig::default(),
308            enable_db_write_stall: None,
309            enable_db_sync_to_disk: None,
310            execution_time_observer_config: self.execution_time_observer_config,
311            chain_override_for_testing: self.chain_override,
312            validator_client_monitor_config: None,
313            fork_recovery: None,
314            transaction_driver_config: Some(TransactionDriverConfig::default()),
315            consensus_transaction_pool: self.consensus_transaction_pool_config,
316            congestion_log: None,
317        }
318    }
319
320    pub fn build_new_validator<R: rand::RngCore + rand::CryptoRng>(
321        self,
322        rng: &mut R,
323        network_config: &NetworkConfig,
324    ) -> NodeConfig {
325        let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
326        self.build(validator_config, network_config.genesis.clone())
327    }
328}
329
330#[derive(Clone, Debug, Default)]
331pub struct FullnodeConfigBuilder {
332    config_directory: Option<PathBuf>,
333    // port for json rpc api
334    rpc_port: Option<u16>,
335    rpc_addr: Option<SocketAddr>,
336    supported_protocol_versions: Option<SupportedProtocolVersions>,
337    db_checkpoint_config: Option<DBCheckpointConfig>,
338    expensive_safety_check_config: Option<ExpensiveSafetyCheckConfig>,
339    db_path: Option<PathBuf>,
340    network_address: Option<Multiaddr>,
341    json_rpc_address: Option<SocketAddr>,
342    metrics_address: Option<SocketAddr>,
343    admin_interface_port: Option<u16>,
344    genesis: Option<Genesis>,
345    p2p_external_address: Option<Multiaddr>,
346    p2p_listen_address: Option<SocketAddr>,
347    network_key_pair: Option<KeyPairWithPath>,
348    run_with_range: Option<RunWithRange>,
349    policy_config: Option<PolicyConfig>,
350    fw_config: Option<RemoteFirewallConfig>,
351    data_ingestion_dir: Option<PathBuf>,
352    disable_pruning: bool,
353    disable_json_rpc: bool,
354    sync_post_process_one_tx: bool,
355    chain_override: Option<Chain>,
356    transaction_driver_config: Option<TransactionDriverConfig>,
357    rpc_config: Option<sui_config::RpcConfig>,
358    state_sync_config: Option<StateSyncConfig>,
359    observer_setup: Option<ObserverSetup>,
360}
361
362/// How a fullnode should be set up as a consensus observer.
363#[derive(Clone, Debug)]
364enum ObserverSetup {
365    /// Use the given observer parameters as-is. Peers must be non-empty.
366    Explicit(ObserverParameters),
367    /// Derive the observer peer from the validator at this index in the network
368    /// config, which must have its observer server enabled.
369    SubscribeToValidator { index: usize },
370}
371
372/// Builds the observer `PeerRecord` for connecting to the given validator's observer server.
373/// Panics if the validator does not have its observer server enabled.
374pub fn observer_peer_record(validator_config: &NodeConfig) -> PeerRecord {
375    let observer_port = validator_config
376        .consensus_config()
377        .and_then(|c| c.parameters.as_ref())
378        .and_then(|p| p.observer.server_port)
379        .expect("validator must have its observer server enabled");
380    let public_key = NetworkPublicKey::new(validator_config.network_key_pair().public().clone());
381    let host = validator_config
382        .network_address
383        .to_socket_addr()
384        .unwrap()
385        .ip()
386        .to_string();
387    let address = format!("/ip4/{host}/udp/{observer_port}/http")
388        .parse()
389        .unwrap();
390    PeerRecord {
391        public_key,
392        address,
393    }
394}
395
396impl FullnodeConfigBuilder {
397    pub fn new() -> Self {
398        Self::default()
399    }
400
401    pub fn with_chain_override(mut self, chain: Chain) -> Self {
402        assert!(self.chain_override.is_none(), "Chain override already set");
403        self.chain_override = Some(chain);
404        self
405    }
406
407    pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
408        self.config_directory = Some(config_directory);
409        self
410    }
411
412    pub fn with_rpc_port(mut self, port: u16) -> Self {
413        assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
414        self.rpc_port = Some(port);
415        self
416    }
417
418    pub fn with_rpc_addr(mut self, addr: SocketAddr) -> Self {
419        assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
420        self.rpc_addr = Some(addr);
421        self
422    }
423
424    pub fn with_rpc_config(mut self, rpc_config: sui_config::RpcConfig) -> Self {
425        self.rpc_config = Some(rpc_config);
426        self
427    }
428
429    pub fn with_supported_protocol_versions(mut self, versions: SupportedProtocolVersions) -> Self {
430        self.supported_protocol_versions = Some(versions);
431        self
432    }
433
434    pub fn with_db_checkpoint_config(mut self, db_checkpoint_config: DBCheckpointConfig) -> Self {
435        self.db_checkpoint_config = Some(db_checkpoint_config);
436        self
437    }
438
439    pub fn with_disable_pruning(mut self, disable_pruning: bool) -> Self {
440        self.disable_pruning = disable_pruning;
441        self
442    }
443
444    pub fn with_disable_json_rpc(mut self, disable_json_rpc: bool) -> Self {
445        self.disable_json_rpc = disable_json_rpc;
446        self
447    }
448
449    pub fn with_expensive_safety_check_config(
450        mut self,
451        expensive_safety_check_config: ExpensiveSafetyCheckConfig,
452    ) -> Self {
453        self.expensive_safety_check_config = Some(expensive_safety_check_config);
454        self
455    }
456
457    pub fn with_sync_post_process_one_tx(mut self, sync: bool) -> Self {
458        self.sync_post_process_one_tx = sync;
459        self
460    }
461
462    pub fn with_db_path(mut self, db_path: PathBuf) -> Self {
463        self.db_path = Some(db_path);
464        self
465    }
466
467    pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
468        self.network_address = Some(network_address);
469        self
470    }
471
472    pub fn with_json_rpc_address(mut self, json_rpc_address: SocketAddr) -> Self {
473        self.json_rpc_address = Some(json_rpc_address);
474        self
475    }
476
477    pub fn with_metrics_address(mut self, metrics_address: SocketAddr) -> Self {
478        self.metrics_address = Some(metrics_address);
479        self
480    }
481
482    pub fn with_admin_interface_port(mut self, admin_interface_port: u16) -> Self {
483        self.admin_interface_port = Some(admin_interface_port);
484        self
485    }
486
487    pub fn with_genesis(mut self, genesis: Genesis) -> Self {
488        self.genesis = Some(genesis);
489        self
490    }
491
492    pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
493        self.p2p_external_address = Some(p2p_external_address);
494        self
495    }
496
497    pub fn with_p2p_listen_address(mut self, p2p_listen_address: SocketAddr) -> Self {
498        self.p2p_listen_address = Some(p2p_listen_address);
499        self
500    }
501
502    pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
503        if let Some(network_key_pair) = network_key_pair {
504            self.network_key_pair =
505                Some(KeyPairWithPath::new(SuiKeyPair::Ed25519(network_key_pair)));
506        }
507        self
508    }
509
510    pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
511        if let Some(run_with_range) = run_with_range {
512            self.run_with_range = Some(run_with_range);
513        }
514        self
515    }
516
517    pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
518        self.policy_config = config;
519        self
520    }
521
522    pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
523        self.fw_config = config;
524        self
525    }
526
527    pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
528        self.data_ingestion_dir = path;
529        self
530    }
531
532    pub fn with_transaction_driver_config(
533        mut self,
534        config: Option<TransactionDriverConfig>,
535    ) -> Self {
536        self.transaction_driver_config = config;
537        self
538    }
539
540    pub fn with_state_sync_config(mut self, config: StateSyncConfig) -> Self {
541        self.state_sync_config = Some(config);
542        self
543    }
544
545    pub fn with_observer_config(mut self, config: ObserverParameters) -> Self {
546        self.observer_setup = Some(ObserverSetup::Explicit(config));
547        self
548    }
549
550    /// Sets up the fullnode as a consensus observer subscribed to the observer server of the
551    /// validator at `index` in the network config. The peer record is derived from the
552    /// network config at build time, so the validator must have its observer server enabled.
553    pub fn with_observer_subscribed_to_validator(mut self, index: usize) -> Self {
554        self.observer_setup = Some(ObserverSetup::SubscribeToValidator { index });
555        self
556    }
557
558    pub fn build<R: rand::RngCore + rand::CryptoRng>(
559        self,
560        rng: &mut R,
561        network_config: &NetworkConfig,
562    ) -> NodeConfig {
563        // Take advantage of ValidatorGenesisConfigBuilder to build the keypairs and addresses,
564        // even though this is a fullnode.
565        let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
566        let ip = validator_config
567            .network_address
568            .to_socket_addr()
569            .unwrap()
570            .ip()
571            .to_string();
572
573        let key_path = get_key_path(&validator_config.key_pair);
574        let config_directory = self
575            .config_directory
576            .unwrap_or_else(|| mysten_common::tempdir().unwrap().keep());
577
578        let consensus_db_path = config_directory.join(CONSENSUS_DB_NAME).join(&key_path);
579
580        // Resolve the observer parameters, deriving the peer record from the network config
581        // when subscribing to a validator.
582        let observer_config = self.observer_setup.map(|setup| match setup {
583            ObserverSetup::Explicit(config) => config,
584            ObserverSetup::SubscribeToValidator { index } => {
585                let validator_config = network_config
586                    .validator_configs
587                    .get(index)
588                    .unwrap_or_else(|| panic!("no validator at index {index} in network config"));
589                ObserverParameters {
590                    peers: vec![observer_peer_record(validator_config)],
591                    ..Default::default()
592                }
593            }
594        });
595
596        let fullnode_sync_mode = observer_config.as_ref().map(|c| {
597            // A consensus config without peers would make this node's intended role a validator.
598            assert!(
599                !c.peers.is_empty(),
600                "observer fullnode must be configured with at least one peer"
601            );
602            FullNodeSyncMode::ConsensusObserver
603        });
604
605        // Create consensus config, if observer config is provided.
606        let consensus_config = observer_config.map(|observer_config| ConsensusConfig {
607            db_path: consensus_db_path,
608            db_retention_epochs: None,
609            db_pruner_period_secs: None,
610            max_pending_transactions: None,
611            parameters: Some(ConsensusParameters {
612                observer: ObserverParameters {
613                    server_port: observer_config
614                        .server_port
615                        .or_else(|| Some(local_ip_utils::get_available_port(&ip))),
616                    allowlist: observer_config.allowlist,
617                    quorum_release: observer_config.quorum_release,
618                    peers: observer_config.peers,
619                },
620                ..Default::default()
621            }),
622            listen_address: None,
623            external_address: None,
624        });
625
626        let p2p_config = {
627            let seed_peers = network_config
628                .validator_configs
629                .iter()
630                .map(|config| SeedPeer {
631                    peer_id: Some(anemo::PeerId(
632                        config.network_key_pair().public().0.to_bytes(),
633                    )),
634                    address: config.p2p_config.external_address.clone().unwrap(),
635                })
636                .collect();
637
638            P2pConfig {
639                listen_address: self.p2p_listen_address.unwrap_or_else(|| {
640                    validator_config.p2p_listen_address.unwrap_or_else(|| {
641                        validator_config
642                            .p2p_address
643                            .udp_multiaddr_to_listen_address()
644                            .unwrap()
645                    })
646                }),
647                external_address: self
648                    .p2p_external_address
649                    .or(Some(validator_config.p2p_address.clone())),
650                seed_peers,
651                // Set a shorter timeout for checkpoint content download in tests, since
652                // checkpoint pruning also happens much faster, and network is local.
653                state_sync: Some(if let Some(mut config) = self.state_sync_config {
654                    if config.checkpoint_content_timeout_ms.is_none() {
655                        config.checkpoint_content_timeout_ms = Some(10_000);
656                    }
657                    config
658                } else {
659                    StateSyncConfig {
660                        checkpoint_content_timeout_ms: Some(10_000),
661                        ..Default::default()
662                    }
663                }),
664                ..Default::default()
665            }
666        };
667
668        let localhost = local_ip_utils::localhost_for_testing();
669        let json_rpc_address = self.rpc_addr.unwrap_or_else(|| {
670            let rpc_port = self
671                .rpc_port
672                .unwrap_or_else(|| local_ip_utils::get_available_port(&ip));
673            format!("{}:{}", ip, rpc_port).parse().unwrap()
674        });
675
676        let checkpoint_executor_config = CheckpointExecutorConfig {
677            data_ingestion_dir: self.data_ingestion_dir,
678            ..Default::default()
679        };
680
681        let mut pruning_config = AuthorityStorePruningConfig::default();
682        if self.disable_pruning {
683            pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
684            pruning_config.set_num_epochs_to_retain(u64::MAX);
685        };
686
687        NodeConfig {
688            recent_submission_dedup_window_ms: None,
689            address_prober: None,
690            protocol_key_pair: AuthorityKeyPairWithPath::new(validator_config.key_pair),
691            account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
692            worker_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(
693                validator_config.worker_key_pair,
694            )),
695            network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
696                SuiKeyPair::Ed25519(validator_config.network_key_pair),
697            )),
698            db_path: self
699                .db_path
700                .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
701            network_address: self
702                .network_address
703                .unwrap_or(validator_config.network_address),
704            metrics_address: self
705                .metrics_address
706                .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
707            admin_interface_port: self
708                .admin_interface_port
709                .unwrap_or(local_ip_utils::get_available_port(&localhost)),
710            json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
711            fullnode_sync_mode,
712            consensus_config,
713            remove_deprecated_tables: false,
714            enable_index_processing: default_enable_index_processing(),
715            sync_post_process_one_tx: self.sync_post_process_one_tx,
716            genesis: self.genesis.unwrap_or(sui_config::node::Genesis::new(
717                network_config.genesis.clone(),
718            )),
719            grpc_load_shed: None,
720            grpc_concurrency_limit: None,
721            p2p_config,
722            authority_store_pruning_config: pruning_config,
723            end_of_epoch_broadcast_channel_capacity:
724                default_end_of_epoch_broadcast_channel_capacity(),
725            checkpoint_executor_config,
726            metrics: None,
727            supported_protocol_versions: self.supported_protocol_versions,
728            db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
729            expensive_safety_check_config: self
730                .expensive_safety_check_config
731                .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
732            name_service_package_address: None,
733            name_service_registry_id: None,
734            name_service_reverse_registry_id: None,
735            transaction_deny_config: Default::default(),
736            peer_deny_sync_config: Default::default(),
737            dev_inspect_disabled: false,
738            certificate_deny_config: Default::default(),
739            state_debug_dump_config: Default::default(),
740            state_archive_read_config: vec![],
741            state_snapshot_write_config: StateSnapshotConfig::default(),
742            indexer_max_subscriptions: Default::default(),
743            transaction_kv_store_read_config: Default::default(),
744            transaction_kv_store_write_config: Default::default(),
745            rpc: self.rpc_config.or_else(|| {
746                Some(sui_rpc_api::Config {
747                    enable_indexing: Some(true),
748                    ..Default::default()
749                })
750            }),
751            // note: not used by fullnodes.
752            jwk_fetch_interval_seconds: 3600,
753            zklogin_oauth_providers: default_zklogin_oauth_providers(),
754            authority_overload_config: Default::default(),
755            run_with_range: self.run_with_range,
756            jsonrpc_server_type: None,
757            disable_json_rpc: self.disable_json_rpc,
758            policy_config: self.policy_config,
759            firewall_config: self.fw_config,
760            execution_cache: ExecutionCacheConfig::default(),
761            state_accumulator_v2: true,
762            funds_withdraw_scheduler_type: FundsWithdrawSchedulerType::default(),
763            enable_soft_bundle: true,
764            enable_simulate_allowed_proposers: true,
765            verifier_signing_config: VerifierSigningConfig::default(),
766            enable_db_write_stall: None,
767            enable_db_sync_to_disk: None,
768            execution_time_observer_config: None,
769            chain_override_for_testing: self.chain_override,
770            validator_client_monitor_config: None,
771            fork_recovery: None,
772            transaction_driver_config: self
773                .transaction_driver_config
774                .or(Some(TransactionDriverConfig::default())),
775            consensus_transaction_pool: None,
776            congestion_log: None,
777        }
778    }
779}
780
781/// Given a validator keypair, return a path that can be used to identify the validator.
782fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
783    let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
784    let mut key_path = Hex::encode(public_key);
785    // 12 is rather arbitrary here but it's a nice balance between being short and being unique.
786    key_path.truncate(12);
787    key_path
788}