1use 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#[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 global_state_hash_v2: bool,
47 funds_withdraw_scheduler_type: FundsWithdrawSchedulerType,
48 execution_time_observer_config: Option<ExecutionTimeObserverConfig>,
49 chain_override: Option<Chain>,
50 state_sync_config: Option<StateSyncConfig>,
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_global_state_hash_v2_enabled(mut self, enabled: bool) -> Self {
118 self.global_state_hash_v2 = enabled;
119 self
120 }
121
122 pub fn with_funds_withdraw_scheduler_type(
123 mut self,
124 scheduler_type: FundsWithdrawSchedulerType,
125 ) -> Self {
126 self.funds_withdraw_scheduler_type = scheduler_type;
127 self
128 }
129
130 pub fn with_execution_time_observer_config(
131 mut self,
132 config: ExecutionTimeObserverConfig,
133 ) -> Self {
134 self.execution_time_observer_config = Some(config);
135 self
136 }
137
138 pub fn with_state_sync_config(mut self, config: StateSyncConfig) -> Self {
139 self.state_sync_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 parameters: Default::default(),
165 listen_address: None,
166 external_address: None,
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 state_sync: Some(if let Some(mut config) = self.state_sync_config {
180 if config.checkpoint_content_timeout_ms.is_none() {
181 config.checkpoint_content_timeout_ms = Some(10_000);
182 }
183 config
184 } else {
185 StateSyncConfig {
186 checkpoint_content_timeout_ms: Some(10_000),
187 ..Default::default()
188 }
189 }),
190 ..Default::default()
191 };
192
193 let mut pruning_config = AuthorityStorePruningConfig::default();
194 if self.force_unpruned_checkpoints {
195 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
196 }
197 let pruning_config = pruning_config;
198 let checkpoint_executor_config = CheckpointExecutorConfig {
199 data_ingestion_dir: self.data_ingestion_dir,
200 ..Default::default()
201 };
202
203 NodeConfig {
204 protocol_key_pair: AuthorityKeyPairWithPath::new(validator.key_pair),
205 network_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(validator.network_key_pair)),
206 account_key_pair: KeyPairWithPath::new(validator.account_key_pair),
207 worker_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(validator.worker_key_pair)),
208 db_path,
209 network_address,
210 metrics_address: validator.metrics_address,
211 admin_interface_port: local_ip_utils::get_available_port(&localhost),
212 json_rpc_address: local_ip_utils::new_tcp_address_for_testing(&localhost)
213 .to_socket_addr()
214 .unwrap(),
215 consensus_config: Some(consensus_config),
216 remove_deprecated_tables: false,
217 enable_index_processing: default_enable_index_processing(),
218 sync_post_process_one_tx: false,
219 genesis: sui_config::node::Genesis::new(genesis),
220 grpc_load_shed: None,
221 grpc_concurrency_limit: Some(DEFAULT_GRPC_CONCURRENCY_LIMIT),
222 p2p_config,
223 authority_store_pruning_config: pruning_config,
224 end_of_epoch_broadcast_channel_capacity:
225 default_end_of_epoch_broadcast_channel_capacity(),
226 checkpoint_executor_config,
227 metrics: None,
228 supported_protocol_versions: self.supported_protocol_versions,
229 db_checkpoint_config: Default::default(),
230 expensive_safety_check_config: ExpensiveSafetyCheckConfig::default(),
232 name_service_package_address: None,
233 name_service_registry_id: None,
234 name_service_reverse_registry_id: None,
235 transaction_deny_config: Default::default(),
236 dev_inspect_disabled: false,
237 certificate_deny_config: Default::default(),
238 state_debug_dump_config: Default::default(),
239 state_archive_read_config: vec![],
240 state_snapshot_write_config: StateSnapshotConfig::default(),
241 indexer_max_subscriptions: Default::default(),
242 transaction_kv_store_read_config: Default::default(),
243 transaction_kv_store_write_config: None,
244 rpc: Some(sui_rpc_api::Config {
245 ..Default::default()
246 }),
247 jwk_fetch_interval_seconds: self
248 .jwk_fetch_interval
249 .map(|i| i.as_secs())
250 .unwrap_or(3600),
251 zklogin_oauth_providers: default_zklogin_oauth_providers(),
252 authority_overload_config: self.authority_overload_config.unwrap_or_default(),
253 execution_cache: self.execution_cache_config.unwrap_or_default(),
254 run_with_range: None,
255 jsonrpc_server_type: None,
256 policy_config: self.policy_config,
257 firewall_config: self.firewall_config,
258 state_accumulator_v2: self.global_state_hash_v2,
259 funds_withdraw_scheduler_type: self.funds_withdraw_scheduler_type,
260 enable_soft_bundle: true,
261 verifier_signing_config: VerifierSigningConfig::default(),
262 enable_db_write_stall: None,
263 enable_db_sync_to_disk: None,
264 execution_time_observer_config: self.execution_time_observer_config,
265 chain_override_for_testing: self.chain_override,
266 validator_client_monitor_config: None,
267 fork_recovery: None,
268 transaction_driver_config: Some(TransactionDriverConfig::default()),
269 congestion_log: None,
270 }
271 }
272
273 pub fn build_new_validator<R: rand::RngCore + rand::CryptoRng>(
274 self,
275 rng: &mut R,
276 network_config: &NetworkConfig,
277 ) -> NodeConfig {
278 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
279 self.build(validator_config, network_config.genesis.clone())
280 }
281}
282
283#[derive(Clone, Debug, Default)]
284pub struct FullnodeConfigBuilder {
285 config_directory: Option<PathBuf>,
286 rpc_port: Option<u16>,
288 rpc_addr: Option<SocketAddr>,
289 supported_protocol_versions: Option<SupportedProtocolVersions>,
290 db_checkpoint_config: Option<DBCheckpointConfig>,
291 expensive_safety_check_config: Option<ExpensiveSafetyCheckConfig>,
292 db_path: Option<PathBuf>,
293 network_address: Option<Multiaddr>,
294 json_rpc_address: Option<SocketAddr>,
295 metrics_address: Option<SocketAddr>,
296 admin_interface_port: Option<u16>,
297 genesis: Option<Genesis>,
298 p2p_external_address: Option<Multiaddr>,
299 p2p_listen_address: Option<SocketAddr>,
300 network_key_pair: Option<KeyPairWithPath>,
301 run_with_range: Option<RunWithRange>,
302 policy_config: Option<PolicyConfig>,
303 fw_config: Option<RemoteFirewallConfig>,
304 data_ingestion_dir: Option<PathBuf>,
305 disable_pruning: bool,
306 sync_post_process_one_tx: bool,
307 chain_override: Option<Chain>,
308 transaction_driver_config: Option<TransactionDriverConfig>,
309 rpc_config: Option<sui_config::RpcConfig>,
310 state_sync_config: Option<StateSyncConfig>,
311}
312
313impl FullnodeConfigBuilder {
314 pub fn new() -> Self {
315 Self::default()
316 }
317
318 pub fn with_chain_override(mut self, chain: Chain) -> Self {
319 assert!(self.chain_override.is_none(), "Chain override already set");
320 self.chain_override = Some(chain);
321 self
322 }
323
324 pub fn with_config_directory(mut self, config_directory: PathBuf) -> Self {
325 self.config_directory = Some(config_directory);
326 self
327 }
328
329 pub fn with_rpc_port(mut self, port: u16) -> Self {
330 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
331 self.rpc_port = Some(port);
332 self
333 }
334
335 pub fn with_rpc_addr(mut self, addr: SocketAddr) -> Self {
336 assert!(self.rpc_addr.is_none() && self.rpc_port.is_none());
337 self.rpc_addr = Some(addr);
338 self
339 }
340
341 pub fn with_rpc_config(mut self, rpc_config: sui_config::RpcConfig) -> Self {
342 self.rpc_config = Some(rpc_config);
343 self
344 }
345
346 pub fn with_supported_protocol_versions(mut self, versions: SupportedProtocolVersions) -> Self {
347 self.supported_protocol_versions = Some(versions);
348 self
349 }
350
351 pub fn with_db_checkpoint_config(mut self, db_checkpoint_config: DBCheckpointConfig) -> Self {
352 self.db_checkpoint_config = Some(db_checkpoint_config);
353 self
354 }
355
356 pub fn with_disable_pruning(mut self, disable_pruning: bool) -> Self {
357 self.disable_pruning = disable_pruning;
358 self
359 }
360
361 pub fn with_expensive_safety_check_config(
362 mut self,
363 expensive_safety_check_config: ExpensiveSafetyCheckConfig,
364 ) -> Self {
365 self.expensive_safety_check_config = Some(expensive_safety_check_config);
366 self
367 }
368
369 pub fn with_sync_post_process_one_tx(mut self, sync: bool) -> Self {
370 self.sync_post_process_one_tx = sync;
371 self
372 }
373
374 pub fn with_db_path(mut self, db_path: PathBuf) -> Self {
375 self.db_path = Some(db_path);
376 self
377 }
378
379 pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
380 self.network_address = Some(network_address);
381 self
382 }
383
384 pub fn with_json_rpc_address(mut self, json_rpc_address: SocketAddr) -> Self {
385 self.json_rpc_address = Some(json_rpc_address);
386 self
387 }
388
389 pub fn with_metrics_address(mut self, metrics_address: SocketAddr) -> Self {
390 self.metrics_address = Some(metrics_address);
391 self
392 }
393
394 pub fn with_admin_interface_port(mut self, admin_interface_port: u16) -> Self {
395 self.admin_interface_port = Some(admin_interface_port);
396 self
397 }
398
399 pub fn with_genesis(mut self, genesis: Genesis) -> Self {
400 self.genesis = Some(genesis);
401 self
402 }
403
404 pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
405 self.p2p_external_address = Some(p2p_external_address);
406 self
407 }
408
409 pub fn with_p2p_listen_address(mut self, p2p_listen_address: SocketAddr) -> Self {
410 self.p2p_listen_address = Some(p2p_listen_address);
411 self
412 }
413
414 pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
415 if let Some(network_key_pair) = network_key_pair {
416 self.network_key_pair =
417 Some(KeyPairWithPath::new(SuiKeyPair::Ed25519(network_key_pair)));
418 }
419 self
420 }
421
422 pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
423 if let Some(run_with_range) = run_with_range {
424 self.run_with_range = Some(run_with_range);
425 }
426 self
427 }
428
429 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
430 self.policy_config = config;
431 self
432 }
433
434 pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
435 self.fw_config = config;
436 self
437 }
438
439 pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
440 self.data_ingestion_dir = path;
441 self
442 }
443
444 pub fn with_transaction_driver_config(
445 mut self,
446 config: Option<TransactionDriverConfig>,
447 ) -> Self {
448 self.transaction_driver_config = config;
449 self
450 }
451
452 pub fn with_state_sync_config(mut self, config: StateSyncConfig) -> Self {
453 self.state_sync_config = Some(config);
454 self
455 }
456
457 pub fn build<R: rand::RngCore + rand::CryptoRng>(
458 self,
459 rng: &mut R,
460 network_config: &NetworkConfig,
461 ) -> NodeConfig {
462 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
465 let ip = validator_config
466 .network_address
467 .to_socket_addr()
468 .unwrap()
469 .ip()
470 .to_string();
471
472 let key_path = get_key_path(&validator_config.key_pair);
473 let config_directory = self
474 .config_directory
475 .unwrap_or_else(|| mysten_common::tempdir().unwrap().keep());
476
477 let p2p_config = {
478 let seed_peers = network_config
479 .validator_configs
480 .iter()
481 .map(|config| SeedPeer {
482 peer_id: Some(anemo::PeerId(
483 config.network_key_pair().public().0.to_bytes(),
484 )),
485 address: config.p2p_config.external_address.clone().unwrap(),
486 })
487 .collect();
488
489 P2pConfig {
490 listen_address: self.p2p_listen_address.unwrap_or_else(|| {
491 validator_config.p2p_listen_address.unwrap_or_else(|| {
492 validator_config
493 .p2p_address
494 .udp_multiaddr_to_listen_address()
495 .unwrap()
496 })
497 }),
498 external_address: self
499 .p2p_external_address
500 .or(Some(validator_config.p2p_address.clone())),
501 seed_peers,
502 state_sync: Some(if let Some(mut config) = self.state_sync_config {
505 if config.checkpoint_content_timeout_ms.is_none() {
506 config.checkpoint_content_timeout_ms = Some(10_000);
507 }
508 config
509 } else {
510 StateSyncConfig {
511 checkpoint_content_timeout_ms: Some(10_000),
512 ..Default::default()
513 }
514 }),
515 ..Default::default()
516 }
517 };
518
519 let localhost = local_ip_utils::localhost_for_testing();
520 let json_rpc_address = self.rpc_addr.unwrap_or_else(|| {
521 let rpc_port = self
522 .rpc_port
523 .unwrap_or_else(|| local_ip_utils::get_available_port(&ip));
524 format!("{}:{}", ip, rpc_port).parse().unwrap()
525 });
526
527 let checkpoint_executor_config = CheckpointExecutorConfig {
528 data_ingestion_dir: self.data_ingestion_dir,
529 ..Default::default()
530 };
531
532 let mut pruning_config = AuthorityStorePruningConfig::default();
533 if self.disable_pruning {
534 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
535 pruning_config.set_num_epochs_to_retain(u64::MAX);
536 };
537
538 NodeConfig {
539 protocol_key_pair: AuthorityKeyPairWithPath::new(validator_config.key_pair),
540 account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
541 worker_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(
542 validator_config.worker_key_pair,
543 )),
544 network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
545 SuiKeyPair::Ed25519(validator_config.network_key_pair),
546 )),
547 db_path: self
548 .db_path
549 .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
550 network_address: self
551 .network_address
552 .unwrap_or(validator_config.network_address),
553 metrics_address: self
554 .metrics_address
555 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
556 admin_interface_port: self
557 .admin_interface_port
558 .unwrap_or(local_ip_utils::get_available_port(&localhost)),
559 json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
560 consensus_config: None,
561 remove_deprecated_tables: false,
562 enable_index_processing: default_enable_index_processing(),
563 sync_post_process_one_tx: self.sync_post_process_one_tx,
564 genesis: self.genesis.unwrap_or(sui_config::node::Genesis::new(
565 network_config.genesis.clone(),
566 )),
567 grpc_load_shed: None,
568 grpc_concurrency_limit: None,
569 p2p_config,
570 authority_store_pruning_config: pruning_config,
571 end_of_epoch_broadcast_channel_capacity:
572 default_end_of_epoch_broadcast_channel_capacity(),
573 checkpoint_executor_config,
574 metrics: None,
575 supported_protocol_versions: self.supported_protocol_versions,
576 db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
577 expensive_safety_check_config: self
578 .expensive_safety_check_config
579 .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
580 name_service_package_address: None,
581 name_service_registry_id: None,
582 name_service_reverse_registry_id: None,
583 transaction_deny_config: Default::default(),
584 dev_inspect_disabled: false,
585 certificate_deny_config: Default::default(),
586 state_debug_dump_config: Default::default(),
587 state_archive_read_config: vec![],
588 state_snapshot_write_config: StateSnapshotConfig::default(),
589 indexer_max_subscriptions: Default::default(),
590 transaction_kv_store_read_config: Default::default(),
591 transaction_kv_store_write_config: Default::default(),
592 rpc: self.rpc_config.or_else(|| {
593 Some(sui_rpc_api::Config {
594 enable_indexing: Some(true),
595 ..Default::default()
596 })
597 }),
598 jwk_fetch_interval_seconds: 3600,
600 zklogin_oauth_providers: default_zklogin_oauth_providers(),
601 authority_overload_config: Default::default(),
602 run_with_range: self.run_with_range,
603 jsonrpc_server_type: None,
604 policy_config: self.policy_config,
605 firewall_config: self.fw_config,
606 execution_cache: ExecutionCacheConfig::default(),
607 state_accumulator_v2: true,
608 funds_withdraw_scheduler_type: FundsWithdrawSchedulerType::default(),
609 enable_soft_bundle: true,
610 verifier_signing_config: VerifierSigningConfig::default(),
611 enable_db_write_stall: None,
612 enable_db_sync_to_disk: None,
613 execution_time_observer_config: None,
614 chain_override_for_testing: self.chain_override,
615 validator_client_monitor_config: None,
616 fork_recovery: None,
617 transaction_driver_config: self
618 .transaction_driver_config
619 .or(Some(TransactionDriverConfig::default())),
620 congestion_log: None,
621 }
622 }
623}
624
625fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
627 let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
628 let mut key_path = Hex::encode(public_key);
629 key_path.truncate(12);
631 key_path
632}