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