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