1use crate::Config;
4use crate::certificate_deny_config::CertificateDenyConfig;
5use crate::genesis;
6use crate::object_storage_config::ObjectStoreConfig;
7use crate::p2p::P2pConfig;
8use crate::transaction_deny_config::{PeerDenySyncConfig, TransactionDenyConfig};
9use crate::validator_client_monitor_config::ValidatorClientMonitorConfig;
10use crate::verifier_signing_config::VerifierSigningConfig;
11use anyhow::Result;
12use consensus_config::Parameters as ConsensusParameters;
13use mysten_common::fatal;
14use nonzero_ext::nonzero;
15use once_cell::sync::OnceCell;
16use rand::rngs::OsRng;
17use serde::{Deserialize, Serialize};
18use serde_with::serde_as;
19use std::collections::{BTreeMap, BTreeSet};
20use std::net::SocketAddr;
21use std::num::{NonZeroU32, NonZeroUsize};
22use std::path::{Path, PathBuf};
23use std::sync::Arc;
24use std::time::Duration;
25use sui_keys::keypair_file::{read_authority_keypair_from_file, read_keypair_from_file};
26use sui_types::base_types::{ObjectID, SuiAddress};
27use sui_types::committee::EpochId;
28use sui_types::crypto::AuthorityPublicKeyBytes;
29use sui_types::crypto::KeypairTraits;
30use sui_types::crypto::NetworkKeyPair;
31use sui_types::crypto::SuiKeyPair;
32use sui_types::messages_checkpoint::CheckpointSequenceNumber;
33use sui_types::node_role::{FullNodeSyncMode, NodeRole};
34use sui_types::supported_protocol_versions::{Chain, SupportedProtocolVersions};
35use sui_types::traffic_control::{PolicyConfig, RemoteFirewallConfig};
36
37use sui_types::crypto::{AccountKeyPair, AuthorityKeyPair, get_key_pair_from_rng};
38use sui_types::multiaddr::Multiaddr;
39use tracing::info;
40
41pub const DEFAULT_GRPC_CONCURRENCY_LIMIT: usize = 20000000000;
43
44pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = sui_types::transaction::DEFAULT_VALIDATOR_GAS_PRICE;
46
47pub const DEFAULT_COMMISSION_RATE: u64 = 200;
49
50#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
52pub enum FundsWithdrawSchedulerType {
53 Naive,
54 #[default]
55 Eager,
56}
57
58#[serde_as]
59#[derive(Clone, Debug, Deserialize, Serialize)]
60#[serde(rename_all = "kebab-case")]
61pub struct NodeConfig {
62 #[serde(default = "default_authority_key_pair")]
63 pub protocol_key_pair: AuthorityKeyPairWithPath,
64 #[serde(default = "default_key_pair")]
65 pub worker_key_pair: KeyPairWithPath,
66 #[serde(default = "default_key_pair")]
67 pub account_key_pair: KeyPairWithPath,
68 #[serde(default = "default_key_pair")]
69 pub network_key_pair: KeyPairWithPath,
70
71 pub db_path: PathBuf,
72 #[serde(default = "default_grpc_address")]
73 pub network_address: Multiaddr,
74 #[serde(default = "default_json_rpc_address")]
78 pub json_rpc_address: SocketAddr,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub rpc: Option<crate::RpcConfig>,
82
83 #[serde(default = "default_metrics_address")]
84 pub metrics_address: SocketAddr,
85 #[serde(default = "default_admin_interface_port")]
86 pub admin_interface_port: u16,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub consensus_config: Option<ConsensusConfig>,
90
91 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub fullnode_sync_mode: Option<FullNodeSyncMode>,
96
97 #[serde(default)]
98 pub grpc_load_shed: Option<bool>,
99
100 #[serde(default = "default_concurrency_limit")]
101 pub grpc_concurrency_limit: Option<usize>,
102
103 #[serde(default)]
104 pub p2p_config: P2pConfig,
105
106 pub genesis: Genesis,
107
108 #[serde(default = "default_authority_store_pruning_config")]
109 pub authority_store_pruning_config: AuthorityStorePruningConfig,
110
111 #[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
115 pub end_of_epoch_broadcast_channel_capacity: usize,
116
117 #[serde(default)]
118 pub checkpoint_executor_config: CheckpointExecutorConfig,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub metrics: Option<MetricsConfig>,
122
123 #[serde(skip)]
127 pub supported_protocol_versions: Option<SupportedProtocolVersions>,
128
129 #[serde(default)]
130 pub db_checkpoint_config: DBCheckpointConfig,
131
132 #[serde(default)]
133 pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
134
135 #[serde(default)]
136 pub transaction_deny_config: TransactionDenyConfig,
137
138 #[serde(default)]
142 pub peer_deny_sync_config: PeerDenySyncConfig,
143
144 #[serde(default)]
146 pub dev_inspect_disabled: bool,
147
148 #[serde(default)]
149 pub certificate_deny_config: CertificateDenyConfig,
150
151 #[serde(default)]
152 pub state_debug_dump_config: StateDebugDumpConfig,
153
154 #[serde(default)]
155 pub state_archive_read_config: Vec<StateArchiveConfig>,
156
157 #[serde(default)]
158 pub state_snapshot_write_config: StateSnapshotConfig,
159
160 #[serde(default = "default_jwk_fetch_interval_seconds")]
161 pub jwk_fetch_interval_seconds: u64,
162
163 #[serde(default = "default_zklogin_oauth_providers")]
164 pub zklogin_oauth_providers: BTreeMap<Chain, BTreeSet<String>>,
165
166 #[serde(default = "default_authority_overload_config")]
167 pub authority_overload_config: AuthorityOverloadConfig,
168
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub run_with_range: Option<RunWithRange>,
171
172 #[serde(
174 skip_serializing_if = "Option::is_none",
175 default = "default_traffic_controller_policy_config"
176 )]
177 pub policy_config: Option<PolicyConfig>,
178
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub firewall_config: Option<RemoteFirewallConfig>,
181
182 #[serde(default)]
183 pub execution_cache: ExecutionCacheConfig,
184
185 #[serde(skip)]
187 #[serde(default = "bool_true")]
188 pub state_accumulator_v2: bool,
189
190 #[serde(skip)]
193 #[serde(default)]
194 pub funds_withdraw_scheduler_type: FundsWithdrawSchedulerType,
195
196 #[serde(default = "bool_true")]
197 pub enable_soft_bundle: bool,
198
199 #[serde(default)]
203 pub enable_simulate_allowed_proposers: bool,
204
205 #[serde(default)]
206 pub verifier_signing_config: VerifierSigningConfig,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
211 pub enable_db_write_stall: Option<bool>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
217 pub enable_db_sync_to_disk: Option<bool>,
218
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub execution_time_observer_config: Option<ExecutionTimeObserverConfig>,
221
222 #[serde(default, skip_serializing_if = "Option::is_none")]
225 pub recent_submission_dedup_window_ms: Option<u64>,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
231 pub chain_override_for_testing: Option<Chain>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
236 pub validator_client_monitor_config: Option<ValidatorClientMonitorConfig>,
237
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub fork_recovery: Option<ForkRecoveryConfig>,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub transaction_driver_config: Option<TransactionDriverConfig>,
245
246 #[serde(default)]
250 pub consensus_transaction_pool: ConsensusTransactionPoolConfig,
251
252 #[serde(skip_serializing_if = "Option::is_none")]
255 pub congestion_log: Option<CongestionLogConfig>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub address_prober: Option<AddressProberConfig>,
260}
261
262#[derive(Clone, Debug, Deserialize, Serialize)]
263#[serde(rename_all = "kebab-case")]
264pub struct TransactionDriverConfig {
265 #[serde(default, skip_serializing_if = "Vec::is_empty")]
268 pub allowed_submission_validators: Vec<String>,
269
270 #[serde(default, skip_serializing_if = "Vec::is_empty")]
273 pub blocked_submission_validators: Vec<String>,
274
275 #[serde(default = "bool_true")]
280 pub enable_early_validation: bool,
281}
282
283impl Default for TransactionDriverConfig {
284 fn default() -> Self {
285 Self {
286 allowed_submission_validators: vec![],
287 blocked_submission_validators: vec![],
288 enable_early_validation: true,
289 }
290 }
291}
292
293#[derive(Clone, Debug, Deserialize, Serialize)]
294#[serde(rename_all = "kebab-case")]
295pub struct ConsensusTransactionPoolConfig {
296 #[serde(default = "bool_true")]
298 pub enabled: bool,
299
300 pub max_pending_transactions: Option<usize>,
304}
305
306impl Default for ConsensusTransactionPoolConfig {
307 fn default() -> Self {
308 Self {
309 enabled: true,
310 max_pending_transactions: None,
311 }
312 }
313}
314
315impl ConsensusTransactionPoolConfig {
316 pub fn max_pending_transactions(&self, consensus_config: &ConsensusConfig) -> usize {
317 self.max_pending_transactions
318 .unwrap_or_else(|| consensus_config.max_pending_transactions())
319 }
320}
321
322#[derive(Clone, Debug, Deserialize, Serialize)]
323#[serde(rename_all = "kebab-case")]
324pub struct CongestionLogConfig {
325 pub path: PathBuf,
326 #[serde(default = "default_congestion_log_max_file_size")]
327 pub max_file_size: u64,
328 #[serde(default = "default_congestion_log_max_files")]
329 pub max_files: u32,
330}
331
332fn default_congestion_log_max_file_size() -> u64 {
333 100 * 1024 * 1024 }
335
336fn default_congestion_log_max_files() -> u32 {
337 10
338}
339
340#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
341#[serde(rename_all = "kebab-case")]
342pub enum ForkCrashBehavior {
343 #[serde(rename = "recover-once-per-version")]
352 #[default]
353 RecoverOncePerVersion,
354
355 #[serde(rename = "await-fork-recovery")]
358 AwaitForkRecovery,
359
360 #[serde(rename = "return-error")]
362 ReturnError,
363}
364
365#[derive(Clone, Debug, Default, Deserialize, Serialize)]
366#[serde(rename_all = "kebab-case")]
367pub struct ForkRecoveryConfig {
368 #[serde(default)]
371 pub transaction_overrides: BTreeMap<String, String>,
372
373 #[serde(default)]
377 pub checkpoint_overrides: BTreeMap<u64, String>,
378
379 #[serde(default)]
381 pub fork_crash_behavior: ForkCrashBehavior,
382}
383
384#[derive(Clone, Debug, Default, Deserialize, Serialize)]
388#[serde(rename_all = "kebab-case")]
389pub struct AddressProberConfig {
390 pub enabled: Option<bool>,
394
395 pub good_interval: Option<Duration>,
399
400 pub failed_interval: Option<Duration>,
405
406 pub failure_threshold: Option<u32>,
411
412 pub concurrency: Option<usize>,
416
417 pub consensus_probe_timeout: Option<Duration>,
421}
422
423impl AddressProberConfig {
424 pub fn enabled(&self) -> bool {
425 self.enabled.unwrap_or(true)
426 }
427
428 pub fn good_interval(&self) -> Duration {
429 self.good_interval.unwrap_or(Duration::from_secs(60 * 60))
430 }
431
432 pub fn failed_interval(&self) -> Duration {
433 self.failed_interval.unwrap_or(Duration::from_secs(60))
434 }
435
436 pub fn failure_threshold(&self) -> u32 {
437 self.failure_threshold.unwrap_or(3)
438 }
439
440 pub fn concurrency(&self) -> usize {
441 self.concurrency.unwrap_or(16)
442 }
443
444 pub fn consensus_probe_timeout(&self) -> Duration {
445 self.consensus_probe_timeout
446 .unwrap_or(Duration::from_secs(10))
447 }
448
449 pub fn validate(&self) -> anyhow::Result<()> {
450 anyhow::ensure!(
451 self.failed_interval() <= self.good_interval(),
452 "address prober failed_interval ({:?}) must be <= good_interval ({:?})",
453 self.failed_interval(),
454 self.good_interval(),
455 );
456 Ok(())
457 }
458}
459
460#[derive(Clone, Debug, Default, Deserialize, Serialize)]
461#[serde(rename_all = "kebab-case")]
462pub struct ExecutionTimeObserverConfig {
463 pub observation_channel_capacity: Option<NonZeroUsize>,
467
468 pub observation_cache_size: Option<NonZeroUsize>,
472
473 pub object_debt_channel_capacity: Option<NonZeroUsize>,
477
478 pub object_utilization_cache_size: Option<NonZeroUsize>,
482
483 pub report_object_utilization_metric_with_full_id: Option<bool>,
494
495 pub object_utilization_metric_tracked_ids: Option<BTreeMap<ObjectID, String>>,
506
507 pub observation_sharing_object_utilization_threshold: Option<Duration>,
512
513 pub observation_sharing_diff_threshold: Option<f64>,
518
519 pub observation_sharing_min_interval: Option<Duration>,
523
524 pub observation_sharing_rate_limit: Option<NonZeroU32>,
529
530 pub observation_sharing_burst_limit: Option<NonZeroU32>,
534
535 pub enable_gas_price_weighting: Option<bool>,
542
543 pub weighted_moving_average_window_size: Option<usize>,
550
551 #[cfg(msim)]
557 pub inject_synthetic_execution_time: Option<bool>,
558}
559
560impl ExecutionTimeObserverConfig {
561 pub fn observation_channel_capacity(&self) -> NonZeroUsize {
562 self.observation_channel_capacity
563 .unwrap_or(nonzero!(1_024usize))
564 }
565
566 pub fn observation_cache_size(&self) -> NonZeroUsize {
567 self.observation_cache_size.unwrap_or(nonzero!(10_000usize))
568 }
569
570 pub fn object_debt_channel_capacity(&self) -> NonZeroUsize {
571 self.object_debt_channel_capacity
572 .unwrap_or(nonzero!(128usize))
573 }
574
575 pub fn object_utilization_cache_size(&self) -> NonZeroUsize {
576 self.object_utilization_cache_size
577 .unwrap_or(nonzero!(50_000usize))
578 }
579
580 pub fn report_object_utilization_metric_with_full_id(&self) -> bool {
581 self.report_object_utilization_metric_with_full_id
582 .unwrap_or(false)
583 }
584
585 pub fn object_utilization_metric_tracked_ids(&self) -> impl Iterator<Item = (&ObjectID, &str)> {
586 self.object_utilization_metric_tracked_ids
587 .iter()
588 .flatten()
589 .map(|(id, name)| (id, name.as_str()))
590 }
591
592 pub fn observation_sharing_object_utilization_threshold(&self) -> Duration {
593 self.observation_sharing_object_utilization_threshold
594 .unwrap_or(Duration::from_millis(500))
595 }
596
597 pub fn observation_sharing_diff_threshold(&self) -> f64 {
598 self.observation_sharing_diff_threshold.unwrap_or(0.1)
599 }
600
601 pub fn observation_sharing_min_interval(&self) -> Duration {
602 self.observation_sharing_min_interval
603 .unwrap_or(Duration::from_secs(5))
604 }
605
606 pub fn observation_sharing_rate_limit(&self) -> NonZeroU32 {
607 self.observation_sharing_rate_limit
608 .unwrap_or(nonzero!(10u32))
609 }
610
611 pub fn observation_sharing_burst_limit(&self) -> NonZeroU32 {
612 self.observation_sharing_burst_limit
613 .unwrap_or(nonzero!(100u32))
614 }
615
616 pub fn enable_gas_price_weighting(&self) -> bool {
617 self.enable_gas_price_weighting.unwrap_or(false)
618 }
619
620 pub fn weighted_moving_average_window_size(&self) -> usize {
621 self.weighted_moving_average_window_size.unwrap_or(20)
622 }
623
624 #[cfg(msim)]
625 pub fn inject_synthetic_execution_time(&self) -> bool {
626 self.inject_synthetic_execution_time.unwrap_or(false)
627 }
628}
629
630#[allow(clippy::large_enum_variant)]
631#[derive(Clone, Debug, Deserialize, Serialize)]
632#[serde(rename_all = "kebab-case")]
633pub enum ExecutionCacheConfig {
634 PassthroughCache,
635 WritebackCache {
636 max_cache_size: Option<u64>,
639
640 package_cache_size: Option<u64>, object_cache_size: Option<u64>, marker_cache_size: Option<u64>, object_by_id_cache_size: Option<u64>, transaction_cache_size: Option<u64>, executed_effect_cache_size: Option<u64>, effect_cache_size: Option<u64>, events_cache_size: Option<u64>, transaction_objects_cache_size: Option<u64>, backpressure_threshold: Option<u64>,
656
657 backpressure_threshold_for_rpc: Option<u64>,
660 },
661}
662
663impl Default for ExecutionCacheConfig {
664 fn default() -> Self {
665 ExecutionCacheConfig::WritebackCache {
666 max_cache_size: None,
667 backpressure_threshold: None,
668 backpressure_threshold_for_rpc: None,
669 package_cache_size: None,
670 object_cache_size: None,
671 marker_cache_size: None,
672 object_by_id_cache_size: None,
673 transaction_cache_size: None,
674 executed_effect_cache_size: None,
675 effect_cache_size: None,
676 events_cache_size: None,
677 transaction_objects_cache_size: None,
678 }
679 }
680}
681
682impl ExecutionCacheConfig {
683 pub fn max_cache_size(&self) -> u64 {
684 std::env::var("SUI_MAX_CACHE_SIZE")
685 .ok()
686 .and_then(|s| s.parse().ok())
687 .unwrap_or_else(|| match self {
688 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
689 ExecutionCacheConfig::WritebackCache { max_cache_size, .. } => {
690 max_cache_size.unwrap_or(100000)
691 }
692 })
693 }
694
695 pub fn package_cache_size(&self) -> u64 {
696 std::env::var("SUI_PACKAGE_CACHE_SIZE")
697 .ok()
698 .and_then(|s| s.parse().ok())
699 .unwrap_or_else(|| match self {
700 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
701 ExecutionCacheConfig::WritebackCache {
702 package_cache_size, ..
703 } => package_cache_size.unwrap_or(1000),
704 })
705 }
706
707 pub fn object_cache_size(&self) -> u64 {
708 std::env::var("SUI_OBJECT_CACHE_SIZE")
709 .ok()
710 .and_then(|s| s.parse().ok())
711 .unwrap_or_else(|| match self {
712 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
713 ExecutionCacheConfig::WritebackCache {
714 object_cache_size, ..
715 } => object_cache_size.unwrap_or(self.max_cache_size()),
716 })
717 }
718
719 pub fn marker_cache_size(&self) -> u64 {
720 std::env::var("SUI_MARKER_CACHE_SIZE")
721 .ok()
722 .and_then(|s| s.parse().ok())
723 .unwrap_or_else(|| match self {
724 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
725 ExecutionCacheConfig::WritebackCache {
726 marker_cache_size, ..
727 } => marker_cache_size.unwrap_or(self.object_cache_size()),
728 })
729 }
730
731 pub fn object_by_id_cache_size(&self) -> u64 {
732 std::env::var("SUI_OBJECT_BY_ID_CACHE_SIZE")
733 .ok()
734 .and_then(|s| s.parse().ok())
735 .unwrap_or_else(|| match self {
736 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
737 ExecutionCacheConfig::WritebackCache {
738 object_by_id_cache_size,
739 ..
740 } => object_by_id_cache_size.unwrap_or(self.object_cache_size()),
741 })
742 }
743
744 pub fn transaction_cache_size(&self) -> u64 {
745 std::env::var("SUI_TRANSACTION_CACHE_SIZE")
746 .ok()
747 .and_then(|s| s.parse().ok())
748 .unwrap_or_else(|| match self {
749 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
750 ExecutionCacheConfig::WritebackCache {
751 transaction_cache_size,
752 ..
753 } => transaction_cache_size.unwrap_or(self.max_cache_size()),
754 })
755 }
756
757 pub fn executed_effect_cache_size(&self) -> u64 {
758 std::env::var("SUI_EXECUTED_EFFECT_CACHE_SIZE")
759 .ok()
760 .and_then(|s| s.parse().ok())
761 .unwrap_or_else(|| match self {
762 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
763 ExecutionCacheConfig::WritebackCache {
764 executed_effect_cache_size,
765 ..
766 } => executed_effect_cache_size.unwrap_or(self.transaction_cache_size()),
767 })
768 }
769
770 pub fn effect_cache_size(&self) -> u64 {
771 std::env::var("SUI_EFFECT_CACHE_SIZE")
772 .ok()
773 .and_then(|s| s.parse().ok())
774 .unwrap_or_else(|| match self {
775 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
776 ExecutionCacheConfig::WritebackCache {
777 effect_cache_size, ..
778 } => effect_cache_size.unwrap_or(self.executed_effect_cache_size()),
779 })
780 }
781
782 pub fn events_cache_size(&self) -> u64 {
783 std::env::var("SUI_EVENTS_CACHE_SIZE")
784 .ok()
785 .and_then(|s| s.parse().ok())
786 .unwrap_or_else(|| match self {
787 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
788 ExecutionCacheConfig::WritebackCache {
789 events_cache_size, ..
790 } => events_cache_size.unwrap_or(self.transaction_cache_size()),
791 })
792 }
793
794 pub fn transaction_objects_cache_size(&self) -> u64 {
795 std::env::var("SUI_TRANSACTION_OBJECTS_CACHE_SIZE")
796 .ok()
797 .and_then(|s| s.parse().ok())
798 .unwrap_or_else(|| match self {
799 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
800 ExecutionCacheConfig::WritebackCache {
801 transaction_objects_cache_size,
802 ..
803 } => transaction_objects_cache_size.unwrap_or(1000),
804 })
805 }
806
807 pub fn backpressure_threshold(&self) -> u64 {
808 std::env::var("SUI_BACKPRESSURE_THRESHOLD")
809 .ok()
810 .and_then(|s| s.parse().ok())
811 .unwrap_or_else(|| match self {
812 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
813 ExecutionCacheConfig::WritebackCache {
814 backpressure_threshold,
815 ..
816 } => backpressure_threshold.unwrap_or(100_000),
817 })
818 }
819
820 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
821 std::env::var("SUI_BACKPRESSURE_THRESHOLD_FOR_RPC")
822 .ok()
823 .and_then(|s| s.parse().ok())
824 .unwrap_or_else(|| match self {
825 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
826 ExecutionCacheConfig::WritebackCache {
827 backpressure_threshold_for_rpc,
828 ..
829 } => backpressure_threshold_for_rpc.unwrap_or(self.backpressure_threshold()),
830 })
831 }
832}
833
834fn default_jwk_fetch_interval_seconds() -> u64 {
835 3600
836}
837
838pub fn default_zklogin_oauth_providers() -> BTreeMap<Chain, BTreeSet<String>> {
839 let mut map = BTreeMap::new();
840
841 let experimental_providers = BTreeSet::from([
843 "Google".to_string(),
844 "Facebook".to_string(),
845 "Twitch".to_string(),
846 "Kakao".to_string(),
847 "Apple".to_string(),
848 "Slack".to_string(),
849 "TestIssuer".to_string(),
850 "TestIssuerKey8192".to_string(),
851 "Microsoft".to_string(),
852 "KarrierOne".to_string(),
853 "Credenza3".to_string(),
854 "Playtron".to_string(),
855 "Threedos".to_string(),
856 "Onefc".to_string(),
857 "FanTV".to_string(),
858 "Arden".to_string(), "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "EveFrontier".to_string(),
861 "TestEveFrontier".to_string(),
862 "AwsTenant-region:ap-southeast-1-tenant_id:ap-southeast-1_2QQPyQXDz".to_string(), "AwsTenant-region:eu-north-1-tenant_id:eu-north-1_Bpct2JyBg".to_string(), "AwsTenant-region:eu-north-1-tenant_id:eu-north-1_4HdQTpt3E".to_string(), ]);
866
867 let providers = BTreeSet::from([
869 "Google".to_string(),
870 "Facebook".to_string(),
871 "Twitch".to_string(),
872 "Apple".to_string(),
873 "KarrierOne".to_string(),
874 "Credenza3".to_string(),
875 "Playtron".to_string(),
876 "Onefc".to_string(),
877 "Threedos".to_string(),
878 "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "Arden".to_string(),
880 "FanTV".to_string(),
881 "EveFrontier".to_string(),
882 "TestEveFrontier".to_string(),
883 "AwsTenant-region:ap-southeast-1-tenant_id:ap-southeast-1_2QQPyQXDz".to_string(), "AwsTenant-region:eu-north-1-tenant_id:eu-north-1_Bpct2JyBg".to_string(), "AwsTenant-region:eu-north-1-tenant_id:eu-north-1_4HdQTpt3E".to_string(), ]);
887 map.insert(Chain::Mainnet, providers.clone());
888 map.insert(Chain::Testnet, providers);
889 map.insert(Chain::Unknown, experimental_providers);
890 map
891}
892
893fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
894 AuthorityStorePruningConfig::default()
895}
896
897fn default_grpc_address() -> Multiaddr {
898 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
899}
900fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
901 AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
902}
903
904fn default_key_pair() -> KeyPairWithPath {
905 KeyPairWithPath::new(
906 get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
907 .1
908 .into(),
909 )
910}
911
912fn default_metrics_address() -> SocketAddr {
913 use std::net::{IpAddr, Ipv4Addr};
914 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
915}
916
917pub fn default_admin_interface_port() -> u16 {
918 1337
919}
920
921pub fn default_json_rpc_address() -> SocketAddr {
922 use std::net::{IpAddr, Ipv4Addr};
923 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
924}
925
926pub fn default_concurrency_limit() -> Option<usize> {
927 Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
928}
929
930pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
931 128
932}
933
934pub fn bool_true() -> bool {
935 true
936}
937
938fn is_true(value: &bool) -> bool {
939 *value
940}
941
942impl Config for NodeConfig {}
943
944impl NodeConfig {
945 pub fn protocol_key_pair(&self) -> &AuthorityKeyPair {
946 self.protocol_key_pair.authority_keypair()
947 }
948
949 pub fn recent_submission_dedup_window(&self) -> Duration {
952 Duration::from_millis(self.recent_submission_dedup_window_ms.unwrap_or(1000))
953 }
954
955 pub fn worker_key_pair(&self) -> &NetworkKeyPair {
956 match self.worker_key_pair.keypair() {
957 SuiKeyPair::Ed25519(kp) => kp,
958 other => panic!(
959 "Invalid keypair type: {:?}, only Ed25519 is allowed for worker key",
960 other
961 ),
962 }
963 }
964
965 pub fn network_key_pair(&self) -> &NetworkKeyPair {
966 match self.network_key_pair.keypair() {
967 SuiKeyPair::Ed25519(kp) => kp,
968 other => panic!(
969 "Invalid keypair type: {:?}, only Ed25519 is allowed for network key",
970 other
971 ),
972 }
973 }
974
975 pub fn protocol_public_key(&self) -> AuthorityPublicKeyBytes {
976 self.protocol_key_pair().public().into()
977 }
978
979 pub fn db_path(&self) -> PathBuf {
980 self.db_path.join("live")
981 }
982
983 pub fn db_checkpoint_path(&self) -> PathBuf {
984 self.db_path.join("db_checkpoints")
985 }
986
987 pub fn db_store_path(&self) -> PathBuf {
988 self.db_path().join("store")
989 }
990
991 pub fn archive_path(&self) -> PathBuf {
992 self.db_path.join("archive")
993 }
994
995 pub fn snapshot_path(&self) -> PathBuf {
996 self.db_path.join("snapshot")
997 }
998
999 pub fn network_address(&self) -> &Multiaddr {
1000 &self.network_address
1001 }
1002
1003 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
1004 self.consensus_config.as_ref()
1005 }
1006
1007 pub fn intended_node_role(&self) -> NodeRole {
1012 let has_consensus_config = self.consensus_config.is_some();
1013
1014 match (self.fullnode_sync_mode, has_consensus_config) {
1015 (Some(FullNodeSyncMode::ConsensusObserver), _) => {
1016 assert!(
1017 self.has_observer_config_peers(),
1018 "Observer peers must be configured when sync mode is ConsensusObserver"
1019 );
1020 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
1021 }
1022 (Some(FullNodeSyncMode::StateSyncOnly), true) => {
1023 panic!("Consensus config should not be set for a StateSyncOnly full node");
1024 }
1025 (Some(FullNodeSyncMode::StateSyncOnly), false) => {
1026 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
1027 }
1028 (None, false) => NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly),
1029 (None, true) => NodeRole::Validator,
1030 }
1031 }
1032
1033 pub fn has_observer_config_peers(&self) -> bool {
1034 self.consensus_config
1035 .as_ref()
1036 .and_then(|c| c.parameters.as_ref())
1037 .map(|p| !p.observer.peers.is_empty())
1038 .unwrap_or(false)
1039 }
1040
1041 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1042 self.genesis.genesis()
1043 }
1044
1045 pub fn sui_address(&self) -> SuiAddress {
1046 (&self.account_key_pair.keypair().public()).into()
1047 }
1048
1049 pub fn archive_reader_config(&self) -> Option<ArchiveReaderConfig> {
1050 self.state_archive_read_config
1051 .first()
1052 .map(|config| ArchiveReaderConfig {
1053 ingestion_url: config.ingestion_url.clone(),
1054 remote_store_options: config.remote_store_options.clone(),
1055 remote_store_headers: config.remote_store_headers.clone(),
1056 download_concurrency: NonZeroUsize::new(config.concurrency)
1057 .unwrap_or(NonZeroUsize::new(5).unwrap()),
1058 remote_store_config: ObjectStoreConfig::default(),
1059 })
1060 }
1061
1062 pub fn rpc(&self) -> Option<&crate::RpcConfig> {
1063 self.rpc.as_ref()
1064 }
1065}
1066
1067#[derive(Debug, Clone, Deserialize, Serialize)]
1068pub enum ConsensusProtocol {
1069 #[serde(rename = "narwhal")]
1070 Narwhal,
1071 #[serde(rename = "mysticeti")]
1072 Mysticeti,
1073}
1074
1075#[derive(Debug, Clone, Deserialize, Serialize)]
1076#[serde(rename_all = "kebab-case")]
1077pub struct ConsensusConfig {
1078 pub db_path: PathBuf,
1080
1081 pub db_retention_epochs: Option<u64>,
1084
1085 pub db_pruner_period_secs: Option<u64>,
1088
1089 pub max_pending_transactions: Option<usize>,
1093
1094 pub parameters: Option<ConsensusParameters>,
1095
1096 #[serde(skip_serializing_if = "Option::is_none")]
1100 pub listen_address: Option<Multiaddr>,
1101
1102 #[serde(skip_serializing_if = "Option::is_none")]
1107 pub external_address: Option<Multiaddr>,
1108}
1109
1110impl ConsensusConfig {
1111 pub fn db_path(&self) -> &Path {
1112 &self.db_path
1113 }
1114
1115 pub fn max_pending_transactions(&self) -> usize {
1116 self.max_pending_transactions.unwrap_or(20_000)
1117 }
1118
1119 pub fn db_retention_epochs(&self) -> u64 {
1120 self.db_retention_epochs.unwrap_or(0)
1121 }
1122
1123 pub fn db_pruner_period(&self) -> Duration {
1124 self.db_pruner_period_secs
1126 .map(Duration::from_secs)
1127 .unwrap_or(Duration::from_secs(3_600))
1128 }
1129}
1130
1131#[derive(Clone, Debug, Deserialize, Serialize)]
1132#[serde(rename_all = "kebab-case")]
1133pub struct CheckpointExecutorConfig {
1134 #[serde(default = "default_checkpoint_execution_max_concurrency")]
1138 pub checkpoint_execution_max_concurrency: usize,
1139
1140 #[serde(default = "default_local_execution_timeout_sec")]
1146 pub local_execution_timeout_sec: u64,
1147
1148 #[serde(default, skip_serializing_if = "Option::is_none")]
1151 pub data_ingestion_dir: Option<PathBuf>,
1152}
1153
1154#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1155#[serde(rename_all = "kebab-case")]
1156pub struct ExpensiveSafetyCheckConfig {
1157 #[serde(default)]
1162 enable_epoch_sui_conservation_check: bool,
1163
1164 #[serde(default)]
1168 enable_deep_per_tx_sui_conservation_check: bool,
1169
1170 #[serde(default)]
1172 force_disable_epoch_sui_conservation_check: bool,
1173
1174 #[serde(default)]
1177 enable_state_consistency_check: bool,
1178
1179 #[serde(default)]
1181 force_disable_state_consistency_check: bool,
1182 }
1184
1185impl ExpensiveSafetyCheckConfig {
1186 pub fn new_enable_all() -> Self {
1187 Self {
1188 enable_epoch_sui_conservation_check: true,
1189 enable_deep_per_tx_sui_conservation_check: true,
1190 force_disable_epoch_sui_conservation_check: false,
1191 enable_state_consistency_check: true,
1192 force_disable_state_consistency_check: false,
1193 }
1194 }
1195
1196 pub fn new_disable_all() -> Self {
1197 Self {
1198 enable_epoch_sui_conservation_check: false,
1199 enable_deep_per_tx_sui_conservation_check: false,
1200 force_disable_epoch_sui_conservation_check: true,
1201 enable_state_consistency_check: false,
1202 force_disable_state_consistency_check: true,
1203 }
1204 }
1205
1206 pub fn force_disable_epoch_sui_conservation_check(&mut self) {
1207 self.force_disable_epoch_sui_conservation_check = true;
1208 }
1209
1210 pub fn enable_epoch_sui_conservation_check(&self) -> bool {
1211 (self.enable_epoch_sui_conservation_check || cfg!(debug_assertions))
1212 && !self.force_disable_epoch_sui_conservation_check
1213 }
1214
1215 pub fn force_disable_state_consistency_check(&mut self) {
1216 self.force_disable_state_consistency_check = true;
1217 }
1218
1219 pub fn enable_state_consistency_check(&self) -> bool {
1220 (self.enable_state_consistency_check || cfg!(debug_assertions))
1221 && !self.force_disable_state_consistency_check
1222 }
1223
1224 pub fn enable_deep_per_tx_sui_conservation_check(&self) -> bool {
1225 self.enable_deep_per_tx_sui_conservation_check || cfg!(debug_assertions)
1226 }
1227}
1228
1229fn default_checkpoint_execution_max_concurrency() -> usize {
1230 4
1231}
1232
1233fn default_local_execution_timeout_sec() -> u64 {
1234 30
1235}
1236
1237impl Default for CheckpointExecutorConfig {
1238 fn default() -> Self {
1239 Self {
1240 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
1241 local_execution_timeout_sec: default_local_execution_timeout_sec(),
1242 data_ingestion_dir: None,
1243 }
1244 }
1245}
1246
1247#[derive(Debug, Clone, Deserialize, Serialize)]
1248#[serde(rename_all = "kebab-case")]
1249pub struct AuthorityStorePruningConfig {
1250 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1252 pub num_latest_epoch_dbs_to_retain: usize,
1253 #[serde(default = "default_epoch_db_pruning_period_secs")]
1255 pub epoch_db_pruning_period_secs: u64,
1256 #[serde(default)]
1261 pub num_epochs_to_retain: u64,
1262 #[serde(skip_serializing_if = "Option::is_none")]
1264 pub pruning_run_delay_seconds: Option<u64>,
1265 #[serde(default = "default_max_checkpoints_in_batch")]
1267 pub max_checkpoints_in_batch: usize,
1268 #[serde(default = "default_max_transactions_in_batch")]
1270 pub max_transactions_in_batch: usize,
1271 #[serde(
1275 default = "default_periodic_compaction_threshold_days",
1276 skip_serializing_if = "Option::is_none"
1277 )]
1278 pub periodic_compaction_threshold_days: Option<usize>,
1279 #[serde(default, skip_serializing_if = "Option::is_none")]
1289 pub rpc_store_bitmap_periodic_compaction_days: Option<u64>,
1290 #[serde(skip_serializing_if = "Option::is_none")]
1292 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1293 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1295 pub killswitch_tombstone_pruning: bool,
1296 #[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
1297 pub smooth: bool,
1298}
1299
1300fn default_num_latest_epoch_dbs_to_retain() -> usize {
1301 3
1302}
1303
1304fn default_epoch_db_pruning_period_secs() -> u64 {
1305 3600
1306}
1307
1308fn default_max_transactions_in_batch() -> usize {
1309 1000
1310}
1311
1312fn default_max_checkpoints_in_batch() -> usize {
1313 10
1314}
1315
1316fn default_smoothing() -> bool {
1317 cfg!(not(test))
1318}
1319
1320fn default_periodic_compaction_threshold_days() -> Option<usize> {
1321 Some(1)
1322}
1323
1324impl Default for AuthorityStorePruningConfig {
1325 fn default() -> Self {
1326 Self {
1327 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1328 epoch_db_pruning_period_secs: default_epoch_db_pruning_period_secs(),
1329 num_epochs_to_retain: 0,
1330 pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
1331 max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
1332 max_transactions_in_batch: default_max_transactions_in_batch(),
1333 periodic_compaction_threshold_days: None,
1334 rpc_store_bitmap_periodic_compaction_days: None,
1335 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1336 killswitch_tombstone_pruning: false,
1337 smooth: true,
1338 }
1339 }
1340}
1341
1342impl AuthorityStorePruningConfig {
1343 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1344 self.num_epochs_to_retain = num_epochs_to_retain;
1345 }
1346
1347 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1348 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1349 }
1350
1351 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1352 self.num_epochs_to_retain_for_checkpoints
1353 .map(|n| {
1355 if n < 2 {
1356 info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
1357 2
1358 } else {
1359 n
1360 }
1361 })
1362 }
1363
1364 pub fn set_killswitch_tombstone_pruning(&mut self, killswitch_tombstone_pruning: bool) {
1365 self.killswitch_tombstone_pruning = killswitch_tombstone_pruning;
1366 }
1367}
1368
1369#[derive(Debug, Clone, Deserialize, Serialize)]
1370#[serde(rename_all = "kebab-case")]
1371pub struct MetricsConfig {
1372 #[serde(skip_serializing_if = "Option::is_none")]
1373 pub push_interval_seconds: Option<u64>,
1374 #[serde(skip_serializing_if = "Option::is_none")]
1375 pub push_url: Option<String>,
1376}
1377
1378#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1379#[serde(rename_all = "kebab-case")]
1380pub struct DBCheckpointConfig {
1381 #[serde(default)]
1382 pub perform_db_checkpoints_at_epoch_end: bool,
1383 #[serde(skip_serializing_if = "Option::is_none")]
1384 pub checkpoint_path: Option<PathBuf>,
1385 #[serde(skip_serializing_if = "Option::is_none")]
1386 pub object_store_config: Option<ObjectStoreConfig>,
1387 #[serde(skip_serializing_if = "Option::is_none")]
1388 pub prune_and_compact_before_upload: Option<bool>,
1389}
1390
1391#[derive(Debug, Clone)]
1392pub struct ArchiveReaderConfig {
1393 pub remote_store_config: ObjectStoreConfig,
1394 pub download_concurrency: NonZeroUsize,
1395 pub ingestion_url: Option<String>,
1396 pub remote_store_options: Vec<(String, String)>,
1397 pub remote_store_headers: Vec<(String, String)>,
1398}
1399
1400#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1401#[serde(rename_all = "kebab-case")]
1402pub struct StateArchiveConfig {
1403 #[serde(skip_serializing_if = "Option::is_none")]
1404 pub object_store_config: Option<ObjectStoreConfig>,
1405 pub concurrency: usize,
1406 #[serde(skip_serializing_if = "Option::is_none")]
1407 pub ingestion_url: Option<String>,
1408 #[serde(
1409 skip_serializing_if = "Vec::is_empty",
1410 default,
1411 deserialize_with = "deserialize_remote_store_options"
1412 )]
1413 pub remote_store_options: Vec<(String, String)>,
1414 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1418 pub remote_store_headers: Vec<(String, String)>,
1419}
1420
1421#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1422#[serde(rename_all = "kebab-case")]
1423pub struct StateSnapshotConfig {
1424 #[serde(skip_serializing_if = "Option::is_none")]
1425 pub object_store_config: Option<ObjectStoreConfig>,
1426 pub concurrency: usize,
1427 #[serde(default)]
1431 pub archive_interval_epochs: u64,
1432}
1433
1434#[derive(Clone, Debug, Deserialize, Serialize)]
1439#[serde(rename_all = "kebab-case")]
1440pub struct AuthorityOverloadConfig {
1441 #[serde(default = "default_max_txn_age_in_queue")]
1442 pub max_txn_age_in_queue: Duration,
1443
1444 #[serde(default = "default_overload_monitor_interval")]
1446 pub overload_monitor_interval: Duration,
1447
1448 #[serde(default = "default_execution_queue_latency_soft_limit")]
1450 pub execution_queue_latency_soft_limit: Duration,
1451
1452 #[serde(default = "default_execution_queue_latency_hard_limit")]
1454 pub execution_queue_latency_hard_limit: Duration,
1455
1456 #[serde(default = "default_max_load_shedding_percentage")]
1458 pub max_load_shedding_percentage: u32,
1459
1460 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1463 pub min_load_shedding_percentage_above_hard_limit: u32,
1464
1465 #[serde(default = "default_safe_transaction_ready_rate")]
1468 pub safe_transaction_ready_rate: u32,
1469
1470 #[serde(default = "default_check_system_overload_at_signing")]
1473 pub check_system_overload_at_signing: bool,
1474
1475 #[serde(default = "default_max_transaction_manager_queue_length")]
1478 pub max_transaction_manager_queue_length: usize,
1479
1480 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1483 pub max_transaction_manager_per_object_queue_length: usize,
1484
1485 #[serde(default = "default_admission_queue_capacity_fraction")]
1489 pub admission_queue_capacity_fraction: f64,
1490
1491 #[serde(default = "default_admission_queue_enabled")]
1496 pub admission_queue_enabled: bool,
1497
1498 #[serde(default = "default_admission_queue_failover_timeout")]
1503 pub admission_queue_failover_timeout: Duration,
1504}
1505
1506fn default_max_txn_age_in_queue() -> Duration {
1507 Duration::from_millis(1000)
1508}
1509
1510fn default_overload_monitor_interval() -> Duration {
1511 Duration::from_secs(10)
1512}
1513
1514fn default_execution_queue_latency_soft_limit() -> Duration {
1515 Duration::from_secs(1)
1516}
1517
1518fn default_execution_queue_latency_hard_limit() -> Duration {
1519 Duration::from_secs(10)
1520}
1521
1522fn default_max_load_shedding_percentage() -> u32 {
1523 95
1524}
1525
1526fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1527 50
1528}
1529
1530fn default_safe_transaction_ready_rate() -> u32 {
1531 100
1532}
1533
1534fn default_check_system_overload_at_signing() -> bool {
1535 true
1536}
1537
1538fn default_max_transaction_manager_queue_length() -> usize {
1539 100_000
1540}
1541
1542fn default_max_transaction_manager_per_object_queue_length() -> usize {
1543 2000
1544}
1545
1546fn default_admission_queue_capacity_fraction() -> f64 {
1547 0.5
1548}
1549
1550fn default_admission_queue_enabled() -> bool {
1551 true
1552}
1553
1554fn default_admission_queue_failover_timeout() -> Duration {
1555 Duration::from_secs(30)
1556}
1557
1558impl Default for AuthorityOverloadConfig {
1559 fn default() -> Self {
1560 Self {
1561 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1562 overload_monitor_interval: default_overload_monitor_interval(),
1563 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1564 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1565 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1566 min_load_shedding_percentage_above_hard_limit:
1567 default_min_load_shedding_percentage_above_hard_limit(),
1568 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1569 check_system_overload_at_signing: true,
1570 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1571 max_transaction_manager_per_object_queue_length:
1572 default_max_transaction_manager_per_object_queue_length(),
1573 admission_queue_capacity_fraction: default_admission_queue_capacity_fraction(),
1574 admission_queue_enabled: default_admission_queue_enabled(),
1575 admission_queue_failover_timeout: default_admission_queue_failover_timeout(),
1576 }
1577 }
1578}
1579
1580fn default_authority_overload_config() -> AuthorityOverloadConfig {
1581 AuthorityOverloadConfig::default()
1582}
1583
1584fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1585 Some(PolicyConfig::default_dos_protection_policy())
1586}
1587
1588#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1589pub struct Genesis {
1590 #[serde(flatten)]
1591 location: GenesisLocation,
1592
1593 #[serde(skip)]
1594 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1595}
1596
1597impl Genesis {
1598 pub fn new(genesis: genesis::Genesis) -> Self {
1599 Self {
1600 location: GenesisLocation::InPlace { genesis },
1601 genesis: Default::default(),
1602 }
1603 }
1604
1605 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1606 Self {
1607 location: GenesisLocation::File {
1608 genesis_file_location: path.into(),
1609 },
1610 genesis: Default::default(),
1611 }
1612 }
1613
1614 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1615 match &self.location {
1616 GenesisLocation::InPlace { genesis } => Ok(genesis),
1617 GenesisLocation::File {
1618 genesis_file_location,
1619 } => self
1620 .genesis
1621 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1622 }
1623 }
1624}
1625
1626#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1627#[serde(untagged)]
1628#[allow(clippy::large_enum_variant)]
1629enum GenesisLocation {
1630 InPlace {
1631 genesis: genesis::Genesis,
1632 },
1633 File {
1634 #[serde(rename = "genesis-file-location")]
1635 genesis_file_location: PathBuf,
1636 },
1637}
1638
1639#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1641pub struct KeyPairWithPath {
1642 #[serde(flatten)]
1643 location: KeyPairLocation,
1644
1645 #[serde(skip)]
1646 keypair: OnceCell<Arc<SuiKeyPair>>,
1647}
1648
1649#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1650#[serde_as]
1651#[serde(untagged)]
1652enum KeyPairLocation {
1653 InPlace {
1654 #[serde_as(as = "Arc<KeyPairBase64>")]
1655 value: Arc<SuiKeyPair>,
1656 },
1657 File {
1658 #[serde(rename = "path")]
1659 path: PathBuf,
1660 },
1661}
1662
1663impl KeyPairWithPath {
1664 pub fn new(kp: SuiKeyPair) -> Self {
1665 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1666 let arc_kp = Arc::new(kp);
1667 cell.set(arc_kp.clone()).expect("Failed to set keypair");
1669 Self {
1670 location: KeyPairLocation::InPlace { value: arc_kp },
1671 keypair: cell,
1672 }
1673 }
1674
1675 pub fn new_from_path(path: PathBuf) -> Self {
1676 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1677 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1679 |e| panic!("Invalid keypair file at path {:?}: {e}", path),
1680 )))
1681 .expect("Failed to set keypair");
1682 Self {
1683 location: KeyPairLocation::File { path },
1684 keypair: cell,
1685 }
1686 }
1687
1688 pub fn keypair(&self) -> &SuiKeyPair {
1689 self.keypair
1690 .get_or_init(|| match &self.location {
1691 KeyPairLocation::InPlace { value } => value.clone(),
1692 KeyPairLocation::File { path } => {
1693 Arc::new(
1695 read_keypair_from_file(path).unwrap_or_else(|e| {
1696 panic!("Invalid keypair file at path {:?}: {e}", path)
1697 }),
1698 )
1699 }
1700 })
1701 .as_ref()
1702 }
1703}
1704
1705#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1707pub struct AuthorityKeyPairWithPath {
1708 #[serde(flatten)]
1709 location: AuthorityKeyPairLocation,
1710
1711 #[serde(skip)]
1712 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1713}
1714
1715#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1716#[serde_as]
1717#[serde(untagged)]
1718enum AuthorityKeyPairLocation {
1719 InPlace { value: Arc<AuthorityKeyPair> },
1720 File { path: PathBuf },
1721}
1722
1723impl AuthorityKeyPairWithPath {
1724 pub fn new(kp: AuthorityKeyPair) -> Self {
1725 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1726 let arc_kp = Arc::new(kp);
1727 cell.set(arc_kp.clone())
1729 .expect("Failed to set authority keypair");
1730 Self {
1731 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1732 keypair: cell,
1733 }
1734 }
1735
1736 pub fn new_from_path(path: PathBuf) -> Self {
1737 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1738 cell.set(Arc::new(
1740 read_authority_keypair_from_file(&path)
1741 .unwrap_or_else(|_| panic!("Invalid authority keypair file at path {:?}", path)),
1742 ))
1743 .expect("Failed to set authority keypair");
1744 Self {
1745 location: AuthorityKeyPairLocation::File { path },
1746 keypair: cell,
1747 }
1748 }
1749
1750 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1751 self.keypair
1752 .get_or_init(|| match &self.location {
1753 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1754 AuthorityKeyPairLocation::File { path } => {
1755 Arc::new(
1757 read_authority_keypair_from_file(path).unwrap_or_else(|_| {
1758 panic!("Invalid authority keypair file {:?}", path)
1759 }),
1760 )
1761 }
1762 })
1763 .as_ref()
1764 }
1765}
1766
1767#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1770#[serde(rename_all = "kebab-case")]
1771pub struct StateDebugDumpConfig {
1772 #[serde(skip_serializing_if = "Option::is_none")]
1773 pub dump_file_directory: Option<PathBuf>,
1774}
1775
1776fn read_credential_from_path_or_literal(value: &str) -> Result<String, std::io::Error> {
1777 let path = Path::new(value);
1778 if path.exists() && path.is_file() {
1779 std::fs::read_to_string(path).map(|content| content.trim().to_string())
1780 } else {
1781 Ok(value.to_string())
1782 }
1783}
1784
1785fn deserialize_remote_store_options<'de, D>(
1787 deserializer: D,
1788) -> Result<Vec<(String, String)>, D::Error>
1789where
1790 D: serde::Deserializer<'de>,
1791{
1792 use serde::de::Error;
1793
1794 let raw_options: Vec<(String, String)> = Vec::deserialize(deserializer)?;
1795 let mut processed_options = Vec::new();
1796
1797 for (key, value) in raw_options {
1798 let is_service_account_path = matches!(
1801 key.as_str(),
1802 "google_service_account"
1803 | "service_account"
1804 | "google_service_account_path"
1805 | "service_account_path"
1806 );
1807
1808 let processed_value = if is_service_account_path {
1809 value
1810 } else {
1811 match read_credential_from_path_or_literal(&value) {
1812 Ok(processed) => processed,
1813 Err(e) => {
1814 return Err(D::Error::custom(format!(
1815 "Failed to read credential for key '{}': {}",
1816 key, e
1817 )));
1818 }
1819 }
1820 };
1821
1822 processed_options.push((key, processed_value));
1823 }
1824
1825 Ok(processed_options)
1826}
1827
1828#[cfg(test)]
1829mod tests {
1830 use std::path::PathBuf;
1831
1832 use fastcrypto::traits::KeyPair;
1833 use rand::{SeedableRng, rngs::StdRng};
1834 use sui_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1835 use sui_types::base_types::ObjectID;
1836 use sui_types::crypto::{AuthorityKeyPair, NetworkKeyPair, SuiKeyPair, get_key_pair_from_rng};
1837
1838 use super::{
1839 AuthorityStorePruningConfig, ExecutionTimeObserverConfig, Genesis, StateArchiveConfig,
1840 };
1841 use crate::NodeConfig;
1842
1843 #[test]
1844 fn serialize_genesis_from_file() {
1845 let g = Genesis::new_from_file("path/to/file");
1846
1847 let s = serde_yaml::to_string(&g).unwrap();
1848 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
1849 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
1850 assert_eq!(g, loaded_genesis);
1851 }
1852
1853 #[test]
1854 fn fullnode_template() {
1855 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1856
1857 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1858 }
1859
1860 #[test]
1862 fn legacy_validator_config() {
1863 const FILE: &str = include_str!("../data/sui-node-legacy.yaml");
1864
1865 let template: NodeConfig = serde_yaml::from_str(FILE).unwrap();
1866 assert_eq!(
1867 template
1868 .authority_store_pruning_config
1869 .rpc_store_bitmap_periodic_compaction_days,
1870 None
1871 );
1872 }
1873
1874 #[test]
1875 fn rpc_store_bitmap_periodic_compaction_days_override_deserializes() {
1876 assert_eq!(
1877 AuthorityStorePruningConfig::default().rpc_store_bitmap_periodic_compaction_days,
1878 None
1879 );
1880
1881 let omitted: AuthorityStorePruningConfig = serde_yaml::from_str("{}").unwrap();
1882 assert_eq!(omitted.rpc_store_bitmap_periodic_compaction_days, None);
1883
1884 let disabled: AuthorityStorePruningConfig =
1885 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 0").unwrap();
1886 assert_eq!(disabled.rpc_store_bitmap_periodic_compaction_days, Some(0));
1887
1888 let configured: AuthorityStorePruningConfig =
1889 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 17").unwrap();
1890 let serialized = serde_yaml::to_string(&configured).unwrap();
1891 let round_tripped: AuthorityStorePruningConfig = serde_yaml::from_str(&serialized).unwrap();
1892 assert_eq!(
1893 round_tripped.rpc_store_bitmap_periodic_compaction_days,
1894 Some(17)
1895 );
1896 }
1897
1898 #[test]
1899 fn execution_time_observer_config_tracked_ids() {
1900 let omitted: ExecutionTimeObserverConfig = serde_yaml::from_str("{}").unwrap();
1901 assert_eq!(omitted.object_utilization_metric_tracked_ids().count(), 0);
1902
1903 let yaml = r#"
1904 object-utilization-metric-tracked-ids:
1905 "0x0000000000000000000000000000000000000000000000000000000000000005": sui-system-state
1906 "0xe05dafb5133bcffb8d59f4e12465dc0e9faeaa05e3e342a08fe135800e3e4407": deepbook-sui-usdc
1907 "#;
1908 let configured: ExecutionTimeObserverConfig = serde_yaml::from_str(yaml).unwrap();
1909 let tracked: Vec<_> = configured.object_utilization_metric_tracked_ids().collect();
1910 assert_eq!(tracked.len(), 2);
1911 assert_eq!(
1912 tracked[0],
1913 (&ObjectID::from_single_byte(5), "sui-system-state")
1914 );
1915
1916 let serialized = serde_yaml::to_string(&configured).unwrap();
1917 let round_tripped: ExecutionTimeObserverConfig = serde_yaml::from_str(&serialized).unwrap();
1918 assert_eq!(
1919 round_tripped.object_utilization_metric_tracked_ids,
1920 configured.object_utilization_metric_tracked_ids
1921 );
1922 }
1923
1924 #[test]
1925 fn load_key_pairs_to_node_config() {
1926 let protocol_key_pair: AuthorityKeyPair =
1927 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1928 let worker_key_pair: NetworkKeyPair =
1929 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1930 let network_key_pair: NetworkKeyPair =
1931 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1932
1933 write_authority_keypair_to_file(&protocol_key_pair, PathBuf::from("protocol.key")).unwrap();
1934 write_keypair_to_file(
1935 &SuiKeyPair::Ed25519(worker_key_pair.copy()),
1936 PathBuf::from("worker.key"),
1937 )
1938 .unwrap();
1939 write_keypair_to_file(
1940 &SuiKeyPair::Ed25519(network_key_pair.copy()),
1941 PathBuf::from("network.key"),
1942 )
1943 .unwrap();
1944
1945 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
1946 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1947 assert_eq!(
1948 template.protocol_key_pair().public(),
1949 protocol_key_pair.public()
1950 );
1951 assert_eq!(
1952 template.network_key_pair().public(),
1953 network_key_pair.public()
1954 );
1955 assert_eq!(
1956 template.worker_key_pair().public(),
1957 worker_key_pair.public()
1958 );
1959 }
1960
1961 #[test]
1962 fn test_remote_store_options_file_path_support() {
1963 let temp_dir = std::env::temp_dir();
1965 let access_key_file = temp_dir.join("test_access_key");
1966 let secret_key_file = temp_dir.join("test_secret_key");
1967
1968 std::fs::write(&access_key_file, "test_access_key_value").unwrap();
1969 std::fs::write(&secret_key_file, "test_secret_key_value\n").unwrap();
1970
1971 let yaml_config = format!(
1972 r#"
1973object-store-config: null
1974concurrency: 5
1975ingestion-url: "https://example.com"
1976remote-store-options:
1977 - ["aws_access_key_id", "{}"]
1978 - ["aws_secret_access_key", "{}"]
1979 - ["literal_key", "literal_value"]
1980"#,
1981 access_key_file.to_string_lossy(),
1982 secret_key_file.to_string_lossy()
1983 );
1984
1985 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
1986
1987 assert_eq!(config.remote_store_options.len(), 3);
1989
1990 let access_key_option = config
1991 .remote_store_options
1992 .iter()
1993 .find(|(key, _)| key == "aws_access_key_id")
1994 .unwrap();
1995 assert_eq!(access_key_option.1, "test_access_key_value");
1996
1997 let secret_key_option = config
1998 .remote_store_options
1999 .iter()
2000 .find(|(key, _)| key == "aws_secret_access_key")
2001 .unwrap();
2002 assert_eq!(secret_key_option.1, "test_secret_key_value");
2003
2004 let literal_option = config
2005 .remote_store_options
2006 .iter()
2007 .find(|(key, _)| key == "literal_key")
2008 .unwrap();
2009 assert_eq!(literal_option.1, "literal_value");
2010
2011 std::fs::remove_file(&access_key_file).ok();
2013 std::fs::remove_file(&secret_key_file).ok();
2014 }
2015
2016 #[test]
2017 fn test_remote_store_options_literal_values_only() {
2018 let yaml_config = r#"
2019object-store-config: null
2020concurrency: 5
2021ingestion-url: "https://example.com"
2022remote-store-options:
2023 - ["aws_access_key_id", "literal_access_key"]
2024 - ["aws_secret_access_key", "literal_secret_key"]
2025"#;
2026
2027 let config: StateArchiveConfig = serde_yaml::from_str(yaml_config).unwrap();
2028
2029 assert_eq!(config.remote_store_options.len(), 2);
2030 assert_eq!(config.remote_store_options[0].1, "literal_access_key");
2031 assert_eq!(config.remote_store_options[1].1, "literal_secret_key");
2032 }
2033
2034 #[test]
2035 fn test_remote_store_options_gcs_service_account_path_preserved() {
2036 let temp_dir = std::env::temp_dir();
2037 let service_account_file = temp_dir.join("test_service_account.json");
2038 let aws_key_file = temp_dir.join("test_aws_key");
2039
2040 std::fs::write(&service_account_file, r#"{"type": "service_account"}"#).unwrap();
2041 std::fs::write(&aws_key_file, "aws_key_value").unwrap();
2042
2043 let yaml_config = format!(
2044 r#"
2045object-store-config: null
2046concurrency: 5
2047ingestion-url: "gs://my-bucket"
2048remote-store-options:
2049 - ["service_account", "{}"]
2050 - ["google_service_account_path", "{}"]
2051 - ["aws_access_key_id", "{}"]
2052"#,
2053 service_account_file.to_string_lossy(),
2054 service_account_file.to_string_lossy(),
2055 aws_key_file.to_string_lossy()
2056 );
2057
2058 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2059
2060 assert_eq!(config.remote_store_options.len(), 3);
2061
2062 let service_account_option = config
2064 .remote_store_options
2065 .iter()
2066 .find(|(key, _)| key == "service_account")
2067 .unwrap();
2068 assert_eq!(
2069 service_account_option.1,
2070 service_account_file.to_string_lossy()
2071 );
2072
2073 let gcs_path_option = config
2075 .remote_store_options
2076 .iter()
2077 .find(|(key, _)| key == "google_service_account_path")
2078 .unwrap();
2079 assert_eq!(gcs_path_option.1, service_account_file.to_string_lossy());
2080
2081 let aws_option = config
2083 .remote_store_options
2084 .iter()
2085 .find(|(key, _)| key == "aws_access_key_id")
2086 .unwrap();
2087 assert_eq!(aws_option.1, "aws_key_value");
2088
2089 std::fs::remove_file(&service_account_file).ok();
2091 std::fs::remove_file(&aws_key_file).ok();
2092 }
2093
2094 mod intended_node_role_tests {
2095 use super::*;
2096 use crate::ConsensusConfig;
2097 use consensus_config::Parameters as ConsensusParameters;
2098 use fastcrypto::ed25519::Ed25519KeyPair;
2099 use sui_types::node_role::{FullNodeSyncMode, NodeRole};
2100
2101 fn fullnode_template_config() -> NodeConfig {
2102 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2103 serde_yaml::from_str(TEMPLATE).unwrap()
2104 }
2105
2106 fn minimal_consensus_config() -> ConsensusConfig {
2107 ConsensusConfig {
2108 db_path: PathBuf::from("/tmp/consensus"),
2109 db_retention_epochs: None,
2110 db_pruner_period_secs: None,
2111 max_pending_transactions: None,
2112 parameters: Default::default(),
2113 listen_address: None,
2114 external_address: None,
2115 }
2116 }
2117
2118 fn consensus_config_with_observer_peers() -> ConsensusConfig {
2119 let mut config = minimal_consensus_config();
2120 let kp = Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32]));
2121 let peer = consensus_config::PeerRecord {
2122 public_key: consensus_config::NetworkPublicKey::new(kp.public().clone()),
2123 address: "/ip4/127.0.0.1/udp/8080".parse().unwrap(),
2124 };
2125 let mut params = ConsensusParameters::default();
2126 params.observer.peers = vec![peer];
2127 config.parameters = Some(params);
2128 config
2129 }
2130
2131 #[test]
2132 fn validator_with_consensus_config() {
2133 let mut config = fullnode_template_config();
2134 config.consensus_config = Some(minimal_consensus_config());
2135 config.fullnode_sync_mode = None;
2136
2137 assert_eq!(config.intended_node_role(), NodeRole::Validator);
2138 }
2139
2140 #[test]
2141 fn fullnode_explicit_state_sync() {
2142 let mut config = fullnode_template_config();
2143 config.consensus_config = None;
2144 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2145
2146 assert_eq!(
2147 config.intended_node_role(),
2148 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2149 );
2150 }
2151
2152 #[test]
2153 fn fullnode_implicit_state_sync() {
2154 let mut config = fullnode_template_config();
2155 config.consensus_config = None;
2156 config.fullnode_sync_mode = None;
2157
2158 assert_eq!(
2159 config.intended_node_role(),
2160 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2161 );
2162 }
2163
2164 #[test]
2165 fn fullnode_consensus_observer() {
2166 let mut config = fullnode_template_config();
2167 config.consensus_config = Some(consensus_config_with_observer_peers());
2168 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2169
2170 assert_eq!(
2171 config.intended_node_role(),
2172 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
2173 );
2174 }
2175
2176 #[test]
2177 #[should_panic(
2178 expected = "Consensus config should not be set for a StateSyncOnly full node"
2179 )]
2180 fn state_sync_with_consensus_config_panics() {
2181 let mut config = fullnode_template_config();
2182 config.consensus_config = Some(minimal_consensus_config());
2183 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2184
2185 config.intended_node_role();
2186 }
2187
2188 #[test]
2189 #[should_panic(expected = "Observer peers must be configured")]
2190 fn observer_without_peers_panics() {
2191 let mut config = fullnode_template_config();
2192 config.consensus_config = Some(minimal_consensus_config());
2193 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2194
2195 config.intended_node_role();
2196 }
2197 }
2198}
2199
2200#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
2203pub enum RunWithRange {
2204 Epoch(EpochId),
2205 Checkpoint(CheckpointSequenceNumber),
2206}
2207
2208impl RunWithRange {
2209 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
2211 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
2212 }
2213
2214 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
2215 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
2216 }
2217
2218 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
2219 match self {
2220 RunWithRange::Epoch(_) => None,
2221 RunWithRange::Checkpoint(seq) => Some(seq),
2222 }
2223 }
2224}