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 verifier_signing_config: VerifierSigningConfig::default(),
265 enable_db_write_stall: None,
266 execution_time_observer_config: self.execution_time_observer_config,
267 chain_override_for_testing: self.chain_override,
268 validator_client_monitor_config: None,
269 fork_recovery: None,
270 transaction_driver_config: Some(TransactionDriverConfig::default()),
271 }
272 }
273
274 pub fn build_new_validator<R: rand::RngCore + rand::CryptoRng>(
275 self,
276 rng: &mut R,
277 network_config: &NetworkConfig,
278 ) -> NodeConfig {
279 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
280 self.build(validator_config, network_config.genesis.clone())
281 }
282}
283
284#[derive(Clone, Debug, Default)]
285pub struct FullnodeConfigBuilder {
286 config_directory: Option<PathBuf>,
287 rpc_port: Option<u16>,
289 rpc_addr: Option<SocketAddr>,
290 supported_protocol_versions: Option<SupportedProtocolVersions>,
291 db_checkpoint_config: Option<DBCheckpointConfig>,
292 expensive_safety_check_config: Option<ExpensiveSafetyCheckConfig>,
293 db_path: Option<PathBuf>,
294 network_address: Option<Multiaddr>,
295 json_rpc_address: Option<SocketAddr>,
296 metrics_address: Option<SocketAddr>,
297 admin_interface_port: Option<u16>,
298 genesis: Option<Genesis>,
299 p2p_external_address: Option<Multiaddr>,
300 p2p_listen_address: Option<SocketAddr>,
301 network_key_pair: Option<KeyPairWithPath>,
302 run_with_range: Option<RunWithRange>,
303 policy_config: Option<PolicyConfig>,
304 fw_config: Option<RemoteFirewallConfig>,
305 data_ingestion_dir: Option<PathBuf>,
306 disable_pruning: 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_db_path(mut self, db_path: PathBuf) -> Self {
370 self.db_path = Some(db_path);
371 self
372 }
373
374 pub fn with_network_address(mut self, network_address: Multiaddr) -> Self {
375 self.network_address = Some(network_address);
376 self
377 }
378
379 pub fn with_json_rpc_address(mut self, json_rpc_address: SocketAddr) -> Self {
380 self.json_rpc_address = Some(json_rpc_address);
381 self
382 }
383
384 pub fn with_metrics_address(mut self, metrics_address: SocketAddr) -> Self {
385 self.metrics_address = Some(metrics_address);
386 self
387 }
388
389 pub fn with_admin_interface_port(mut self, admin_interface_port: u16) -> Self {
390 self.admin_interface_port = Some(admin_interface_port);
391 self
392 }
393
394 pub fn with_genesis(mut self, genesis: Genesis) -> Self {
395 self.genesis = Some(genesis);
396 self
397 }
398
399 pub fn with_p2p_external_address(mut self, p2p_external_address: Multiaddr) -> Self {
400 self.p2p_external_address = Some(p2p_external_address);
401 self
402 }
403
404 pub fn with_p2p_listen_address(mut self, p2p_listen_address: SocketAddr) -> Self {
405 self.p2p_listen_address = Some(p2p_listen_address);
406 self
407 }
408
409 pub fn with_network_key_pair(mut self, network_key_pair: Option<NetworkKeyPair>) -> Self {
410 if let Some(network_key_pair) = network_key_pair {
411 self.network_key_pair =
412 Some(KeyPairWithPath::new(SuiKeyPair::Ed25519(network_key_pair)));
413 }
414 self
415 }
416
417 pub fn with_run_with_range(mut self, run_with_range: Option<RunWithRange>) -> Self {
418 if let Some(run_with_range) = run_with_range {
419 self.run_with_range = Some(run_with_range);
420 }
421 self
422 }
423
424 pub fn with_policy_config(mut self, config: Option<PolicyConfig>) -> Self {
425 self.policy_config = config;
426 self
427 }
428
429 pub fn with_fw_config(mut self, config: Option<RemoteFirewallConfig>) -> Self {
430 self.fw_config = config;
431 self
432 }
433
434 pub fn with_data_ingestion_dir(mut self, path: Option<PathBuf>) -> Self {
435 self.data_ingestion_dir = path;
436 self
437 }
438
439 pub fn with_transaction_driver_config(
440 mut self,
441 config: Option<TransactionDriverConfig>,
442 ) -> Self {
443 self.transaction_driver_config = config;
444 self
445 }
446
447 pub fn with_state_sync_config(mut self, config: StateSyncConfig) -> Self {
448 self.state_sync_config = Some(config);
449 self
450 }
451
452 pub fn build<R: rand::RngCore + rand::CryptoRng>(
453 self,
454 rng: &mut R,
455 network_config: &NetworkConfig,
456 ) -> NodeConfig {
457 let validator_config = ValidatorGenesisConfigBuilder::new().build(rng);
460 let ip = validator_config
461 .network_address
462 .to_socket_addr()
463 .unwrap()
464 .ip()
465 .to_string();
466
467 let key_path = get_key_path(&validator_config.key_pair);
468 let config_directory = self
469 .config_directory
470 .unwrap_or_else(|| mysten_common::tempdir().unwrap().keep());
471
472 let p2p_config = {
473 let seed_peers = network_config
474 .validator_configs
475 .iter()
476 .map(|config| SeedPeer {
477 peer_id: Some(anemo::PeerId(
478 config.network_key_pair().public().0.to_bytes(),
479 )),
480 address: config.p2p_config.external_address.clone().unwrap(),
481 })
482 .collect();
483
484 P2pConfig {
485 listen_address: self.p2p_listen_address.unwrap_or_else(|| {
486 validator_config.p2p_listen_address.unwrap_or_else(|| {
487 validator_config
488 .p2p_address
489 .udp_multiaddr_to_listen_address()
490 .unwrap()
491 })
492 }),
493 external_address: self
494 .p2p_external_address
495 .or(Some(validator_config.p2p_address.clone())),
496 seed_peers,
497 state_sync: Some(if let Some(mut config) = self.state_sync_config {
500 if config.checkpoint_content_timeout_ms.is_none() {
501 config.checkpoint_content_timeout_ms = Some(10_000);
502 }
503 config
504 } else {
505 StateSyncConfig {
506 checkpoint_content_timeout_ms: Some(10_000),
507 ..Default::default()
508 }
509 }),
510 ..Default::default()
511 }
512 };
513
514 let localhost = local_ip_utils::localhost_for_testing();
515 let json_rpc_address = self.rpc_addr.unwrap_or_else(|| {
516 let rpc_port = self
517 .rpc_port
518 .unwrap_or_else(|| local_ip_utils::get_available_port(&ip));
519 format!("{}:{}", ip, rpc_port).parse().unwrap()
520 });
521
522 let checkpoint_executor_config = CheckpointExecutorConfig {
523 data_ingestion_dir: self.data_ingestion_dir,
524 ..Default::default()
525 };
526
527 let mut pruning_config = AuthorityStorePruningConfig::default();
528 if self.disable_pruning {
529 pruning_config.set_num_epochs_to_retain_for_checkpoints(None);
530 pruning_config.set_num_epochs_to_retain(u64::MAX);
531 };
532
533 NodeConfig {
534 protocol_key_pair: AuthorityKeyPairWithPath::new(validator_config.key_pair),
535 account_key_pair: KeyPairWithPath::new(validator_config.account_key_pair),
536 worker_key_pair: KeyPairWithPath::new(SuiKeyPair::Ed25519(
537 validator_config.worker_key_pair,
538 )),
539 network_key_pair: self.network_key_pair.unwrap_or(KeyPairWithPath::new(
540 SuiKeyPair::Ed25519(validator_config.network_key_pair),
541 )),
542 db_path: self
543 .db_path
544 .unwrap_or(config_directory.join(FULL_NODE_DB_PATH).join(key_path)),
545 network_address: self
546 .network_address
547 .unwrap_or(validator_config.network_address),
548 metrics_address: self
549 .metrics_address
550 .unwrap_or(local_ip_utils::new_local_tcp_socket_for_testing()),
551 admin_interface_port: self
552 .admin_interface_port
553 .unwrap_or(local_ip_utils::get_available_port(&localhost)),
554 json_rpc_address: self.json_rpc_address.unwrap_or(json_rpc_address),
555 consensus_config: None,
556 remove_deprecated_tables: false,
557 enable_index_processing: default_enable_index_processing(),
558 genesis: self.genesis.unwrap_or(sui_config::node::Genesis::new(
559 network_config.genesis.clone(),
560 )),
561 grpc_load_shed: None,
562 grpc_concurrency_limit: None,
563 p2p_config,
564 authority_store_pruning_config: pruning_config,
565 end_of_epoch_broadcast_channel_capacity:
566 default_end_of_epoch_broadcast_channel_capacity(),
567 checkpoint_executor_config,
568 metrics: None,
569 supported_protocol_versions: self.supported_protocol_versions,
570 db_checkpoint_config: self.db_checkpoint_config.unwrap_or_default(),
571 expensive_safety_check_config: self
572 .expensive_safety_check_config
573 .unwrap_or_else(ExpensiveSafetyCheckConfig::new_enable_all),
574 name_service_package_address: None,
575 name_service_registry_id: None,
576 name_service_reverse_registry_id: None,
577 transaction_deny_config: Default::default(),
578 certificate_deny_config: Default::default(),
579 state_debug_dump_config: Default::default(),
580 state_archive_read_config: vec![],
581 state_snapshot_write_config: StateSnapshotConfig::default(),
582 indexer_max_subscriptions: Default::default(),
583 transaction_kv_store_read_config: Default::default(),
584 transaction_kv_store_write_config: Default::default(),
585 rpc: self.rpc_config.or_else(|| {
586 Some(sui_rpc_api::Config {
587 enable_indexing: Some(true),
588 ..Default::default()
589 })
590 }),
591 jwk_fetch_interval_seconds: 3600,
593 zklogin_oauth_providers: default_zklogin_oauth_providers(),
594 authority_overload_config: Default::default(),
595 run_with_range: self.run_with_range,
596 jsonrpc_server_type: None,
597 policy_config: self.policy_config,
598 firewall_config: self.fw_config,
599 execution_cache: ExecutionCacheConfig::default(),
600 state_accumulator_v2: true,
601 enable_soft_bundle: true,
602 verifier_signing_config: VerifierSigningConfig::default(),
603 enable_db_write_stall: None,
604 execution_time_observer_config: None,
605 chain_override_for_testing: self.chain_override,
606 validator_client_monitor_config: None,
607 fork_recovery: None,
608 transaction_driver_config: self
609 .transaction_driver_config
610 .or(Some(TransactionDriverConfig::default())),
611 }
612 }
613}
614
615fn get_key_path(key_pair: &AuthorityKeyPair) -> String {
617 let public_key: AuthorityPublicKeyBytes = key_pair.public().into();
618 let mut key_path = Hex::encode(public_key);
619 key_path.truncate(12);
621 key_path
622}