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