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