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}
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 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 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 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 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 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 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 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
592fn 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 key_path.truncate(12);
598 key_path
599}