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 = "bool_true")]
210 pub enable_staggered_submission_signal: bool,
211
212 #[serde(default)]
213 pub verifier_signing_config: VerifierSigningConfig,
214
215 #[serde(skip_serializing_if = "Option::is_none")]
218 pub enable_db_write_stall: Option<bool>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
224 pub enable_db_sync_to_disk: Option<bool>,
225
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub execution_time_observer_config: Option<ExecutionTimeObserverConfig>,
228
229 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub recent_submission_dedup_window_ms: Option<u64>,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
238 pub chain_override_for_testing: Option<Chain>,
239
240 #[serde(skip_serializing_if = "Option::is_none")]
243 pub validator_client_monitor_config: Option<ValidatorClientMonitorConfig>,
244
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub fork_recovery: Option<ForkRecoveryConfig>,
248
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub transaction_driver_config: Option<TransactionDriverConfig>,
252
253 #[serde(default)]
257 pub consensus_transaction_pool: ConsensusTransactionPoolConfig,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
262 pub congestion_log: Option<CongestionLogConfig>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub address_prober: Option<AddressProberConfig>,
267}
268
269#[derive(Clone, Debug, Deserialize, Serialize)]
270#[serde(rename_all = "kebab-case")]
271pub struct TransactionDriverConfig {
272 #[serde(default, skip_serializing_if = "Vec::is_empty")]
275 pub allowed_submission_validators: Vec<String>,
276
277 #[serde(default, skip_serializing_if = "Vec::is_empty")]
280 pub blocked_submission_validators: Vec<String>,
281
282 #[serde(default = "bool_true")]
287 pub enable_early_validation: bool,
288}
289
290impl Default for TransactionDriverConfig {
291 fn default() -> Self {
292 Self {
293 allowed_submission_validators: vec![],
294 blocked_submission_validators: vec![],
295 enable_early_validation: true,
296 }
297 }
298}
299
300#[derive(Clone, Debug, Deserialize, Serialize)]
301#[serde(rename_all = "kebab-case")]
302pub struct ConsensusTransactionPoolConfig {
303 #[serde(default = "bool_true")]
305 pub enabled: bool,
306
307 pub max_pending_transactions: Option<usize>,
311
312 #[serde(default = "bool_true")]
314 pub rate_limit_user_bytes: bool,
315
316 pub max_user_bytes_per_second: Option<u64>,
322
323 pub max_queue_age_secs: Option<u64>,
329}
330
331impl Default for ConsensusTransactionPoolConfig {
332 fn default() -> Self {
333 Self {
334 enabled: true,
335 max_pending_transactions: None,
336 rate_limit_user_bytes: true,
337 max_user_bytes_per_second: None,
338 max_queue_age_secs: None,
339 }
340 }
341}
342
343impl ConsensusTransactionPoolConfig {
344 pub fn max_pending_transactions(&self, consensus_config: &ConsensusConfig) -> usize {
345 self.max_pending_transactions
346 .unwrap_or_else(|| consensus_config.max_pending_transactions())
347 }
348
349 pub fn max_user_bytes_per_second(&self, max_transactions_in_block_bytes: u64) -> Option<u64> {
350 self.rate_limit_user_bytes.then(|| {
351 self.max_user_bytes_per_second
352 .unwrap_or(max_transactions_in_block_bytes.saturating_mul(2))
353 })
354 }
355
356 pub const MIN_RECOMMENDED_MAX_QUEUE_AGE: Duration = Duration::from_secs(10);
357
358 pub fn max_queue_age(&self) -> Option<Duration> {
359 match self.max_queue_age_secs {
360 Some(0) => None,
361 Some(secs) => Some(Duration::from_secs(secs)),
362 None => Some(Duration::from_secs(60)),
363 }
364 }
365}
366
367#[derive(Clone, Debug, Deserialize, Serialize)]
368#[serde(rename_all = "kebab-case")]
369pub struct CongestionLogConfig {
370 pub path: PathBuf,
371 #[serde(default = "default_congestion_log_max_file_size")]
372 pub max_file_size: u64,
373 #[serde(default = "default_congestion_log_max_files")]
374 pub max_files: u32,
375}
376
377fn default_congestion_log_max_file_size() -> u64 {
378 100 * 1024 * 1024 }
380
381fn default_congestion_log_max_files() -> u32 {
382 10
383}
384
385#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
386#[serde(rename_all = "kebab-case")]
387pub enum ForkCrashBehavior {
388 #[serde(rename = "recover-once-per-version")]
397 #[default]
398 RecoverOncePerVersion,
399
400 #[serde(rename = "await-fork-recovery")]
403 AwaitForkRecovery,
404
405 #[serde(rename = "return-error")]
407 ReturnError,
408}
409
410#[derive(Clone, Debug, Default, Deserialize, Serialize)]
411#[serde(rename_all = "kebab-case")]
412pub struct ForkRecoveryConfig {
413 #[serde(default)]
416 pub transaction_overrides: BTreeMap<String, String>,
417
418 #[serde(default)]
422 pub checkpoint_overrides: BTreeMap<u64, String>,
423
424 #[serde(default)]
426 pub fork_crash_behavior: ForkCrashBehavior,
427}
428
429#[derive(Clone, Debug, Default, Deserialize, Serialize)]
433#[serde(rename_all = "kebab-case")]
434pub struct AddressProberConfig {
435 pub enabled: Option<bool>,
439
440 pub good_interval: Option<Duration>,
444
445 pub failed_interval: Option<Duration>,
450
451 pub failure_threshold: Option<u32>,
456
457 pub concurrency: Option<usize>,
461
462 pub consensus_probe_timeout: Option<Duration>,
466}
467
468impl AddressProberConfig {
469 pub fn enabled(&self) -> bool {
470 self.enabled.unwrap_or(true)
471 }
472
473 pub fn good_interval(&self) -> Duration {
474 self.good_interval.unwrap_or(Duration::from_secs(60 * 60))
475 }
476
477 pub fn failed_interval(&self) -> Duration {
478 self.failed_interval.unwrap_or(Duration::from_secs(60))
479 }
480
481 pub fn failure_threshold(&self) -> u32 {
482 self.failure_threshold.unwrap_or(3)
483 }
484
485 pub fn concurrency(&self) -> usize {
486 self.concurrency.unwrap_or(16)
487 }
488
489 pub fn consensus_probe_timeout(&self) -> Duration {
490 self.consensus_probe_timeout
491 .unwrap_or(Duration::from_secs(10))
492 }
493
494 pub fn validate(&self) -> anyhow::Result<()> {
495 anyhow::ensure!(
496 self.failed_interval() <= self.good_interval(),
497 "address prober failed_interval ({:?}) must be <= good_interval ({:?})",
498 self.failed_interval(),
499 self.good_interval(),
500 );
501 Ok(())
502 }
503}
504
505#[derive(Clone, Debug, Default, Deserialize, Serialize)]
506#[serde(rename_all = "kebab-case")]
507pub struct ExecutionTimeObserverConfig {
508 pub observation_channel_capacity: Option<NonZeroUsize>,
512
513 pub observation_cache_size: Option<NonZeroUsize>,
517
518 pub object_debt_channel_capacity: Option<NonZeroUsize>,
522
523 pub object_utilization_cache_size: Option<NonZeroUsize>,
527
528 pub report_object_utilization_metric_with_full_id: Option<bool>,
539
540 pub object_utilization_metric_tracked_ids: Option<BTreeMap<ObjectID, String>>,
551
552 pub observation_sharing_object_utilization_threshold: Option<Duration>,
557
558 pub observation_sharing_diff_threshold: Option<f64>,
563
564 pub observation_sharing_min_interval: Option<Duration>,
568
569 pub observation_sharing_rate_limit: Option<NonZeroU32>,
574
575 pub observation_sharing_burst_limit: Option<NonZeroU32>,
579
580 pub enable_gas_price_weighting: Option<bool>,
587
588 pub weighted_moving_average_window_size: Option<usize>,
595
596 #[cfg(msim)]
602 pub inject_synthetic_execution_time: Option<bool>,
603}
604
605impl ExecutionTimeObserverConfig {
606 pub fn observation_channel_capacity(&self) -> NonZeroUsize {
607 self.observation_channel_capacity
608 .unwrap_or(nonzero!(1_024usize))
609 }
610
611 pub fn observation_cache_size(&self) -> NonZeroUsize {
612 self.observation_cache_size.unwrap_or(nonzero!(10_000usize))
613 }
614
615 pub fn object_debt_channel_capacity(&self) -> NonZeroUsize {
616 self.object_debt_channel_capacity
617 .unwrap_or(nonzero!(128usize))
618 }
619
620 pub fn object_utilization_cache_size(&self) -> NonZeroUsize {
621 self.object_utilization_cache_size
622 .unwrap_or(nonzero!(50_000usize))
623 }
624
625 pub fn report_object_utilization_metric_with_full_id(&self) -> bool {
626 self.report_object_utilization_metric_with_full_id
627 .unwrap_or(false)
628 }
629
630 pub fn object_utilization_metric_tracked_ids(&self) -> impl Iterator<Item = (&ObjectID, &str)> {
631 self.object_utilization_metric_tracked_ids
632 .iter()
633 .flatten()
634 .map(|(id, name)| (id, name.as_str()))
635 }
636
637 pub fn observation_sharing_object_utilization_threshold(&self) -> Duration {
638 self.observation_sharing_object_utilization_threshold
639 .unwrap_or(Duration::from_millis(500))
640 }
641
642 pub fn observation_sharing_diff_threshold(&self) -> f64 {
643 self.observation_sharing_diff_threshold.unwrap_or(0.1)
644 }
645
646 pub fn observation_sharing_min_interval(&self) -> Duration {
647 self.observation_sharing_min_interval
648 .unwrap_or(Duration::from_secs(5))
649 }
650
651 pub fn observation_sharing_rate_limit(&self) -> NonZeroU32 {
652 self.observation_sharing_rate_limit
653 .unwrap_or(nonzero!(10u32))
654 }
655
656 pub fn observation_sharing_burst_limit(&self) -> NonZeroU32 {
657 self.observation_sharing_burst_limit
658 .unwrap_or(nonzero!(100u32))
659 }
660
661 pub fn enable_gas_price_weighting(&self) -> bool {
662 self.enable_gas_price_weighting.unwrap_or(false)
663 }
664
665 pub fn weighted_moving_average_window_size(&self) -> usize {
666 self.weighted_moving_average_window_size.unwrap_or(20)
667 }
668
669 #[cfg(msim)]
670 pub fn inject_synthetic_execution_time(&self) -> bool {
671 self.inject_synthetic_execution_time.unwrap_or(false)
672 }
673}
674
675#[allow(clippy::large_enum_variant)]
676#[derive(Clone, Debug, Deserialize, Serialize)]
677#[serde(rename_all = "kebab-case")]
678pub enum ExecutionCacheConfig {
679 PassthroughCache,
680 WritebackCache {
681 max_cache_size: Option<u64>,
684
685 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>,
701
702 backpressure_threshold_for_rpc: Option<u64>,
705 },
706}
707
708impl Default for ExecutionCacheConfig {
709 fn default() -> Self {
710 ExecutionCacheConfig::WritebackCache {
711 max_cache_size: None,
712 backpressure_threshold: None,
713 backpressure_threshold_for_rpc: None,
714 package_cache_size: None,
715 object_cache_size: None,
716 marker_cache_size: None,
717 object_by_id_cache_size: None,
718 transaction_cache_size: None,
719 executed_effect_cache_size: None,
720 effect_cache_size: None,
721 events_cache_size: None,
722 transaction_objects_cache_size: None,
723 }
724 }
725}
726
727impl ExecutionCacheConfig {
728 pub fn max_cache_size(&self) -> u64 {
729 std::env::var("SUI_MAX_CACHE_SIZE")
730 .ok()
731 .and_then(|s| s.parse().ok())
732 .unwrap_or_else(|| match self {
733 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
734 ExecutionCacheConfig::WritebackCache { max_cache_size, .. } => {
735 max_cache_size.unwrap_or(100000)
736 }
737 })
738 }
739
740 pub fn package_cache_size(&self) -> u64 {
741 std::env::var("SUI_PACKAGE_CACHE_SIZE")
742 .ok()
743 .and_then(|s| s.parse().ok())
744 .unwrap_or_else(|| match self {
745 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
746 ExecutionCacheConfig::WritebackCache {
747 package_cache_size, ..
748 } => package_cache_size.unwrap_or(1000),
749 })
750 }
751
752 pub fn object_cache_size(&self) -> u64 {
753 std::env::var("SUI_OBJECT_CACHE_SIZE")
754 .ok()
755 .and_then(|s| s.parse().ok())
756 .unwrap_or_else(|| match self {
757 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
758 ExecutionCacheConfig::WritebackCache {
759 object_cache_size, ..
760 } => object_cache_size.unwrap_or(self.max_cache_size()),
761 })
762 }
763
764 pub fn marker_cache_size(&self) -> u64 {
765 std::env::var("SUI_MARKER_CACHE_SIZE")
766 .ok()
767 .and_then(|s| s.parse().ok())
768 .unwrap_or_else(|| match self {
769 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
770 ExecutionCacheConfig::WritebackCache {
771 marker_cache_size, ..
772 } => marker_cache_size.unwrap_or(self.object_cache_size()),
773 })
774 }
775
776 pub fn object_by_id_cache_size(&self) -> u64 {
777 std::env::var("SUI_OBJECT_BY_ID_CACHE_SIZE")
778 .ok()
779 .and_then(|s| s.parse().ok())
780 .unwrap_or_else(|| match self {
781 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
782 ExecutionCacheConfig::WritebackCache {
783 object_by_id_cache_size,
784 ..
785 } => object_by_id_cache_size.unwrap_or(self.object_cache_size()),
786 })
787 }
788
789 pub fn transaction_cache_size(&self) -> u64 {
790 std::env::var("SUI_TRANSACTION_CACHE_SIZE")
791 .ok()
792 .and_then(|s| s.parse().ok())
793 .unwrap_or_else(|| match self {
794 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
795 ExecutionCacheConfig::WritebackCache {
796 transaction_cache_size,
797 ..
798 } => transaction_cache_size.unwrap_or(self.max_cache_size()),
799 })
800 }
801
802 pub fn executed_effect_cache_size(&self) -> u64 {
803 std::env::var("SUI_EXECUTED_EFFECT_CACHE_SIZE")
804 .ok()
805 .and_then(|s| s.parse().ok())
806 .unwrap_or_else(|| match self {
807 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
808 ExecutionCacheConfig::WritebackCache {
809 executed_effect_cache_size,
810 ..
811 } => executed_effect_cache_size.unwrap_or(self.transaction_cache_size()),
812 })
813 }
814
815 pub fn effect_cache_size(&self) -> u64 {
816 std::env::var("SUI_EFFECT_CACHE_SIZE")
817 .ok()
818 .and_then(|s| s.parse().ok())
819 .unwrap_or_else(|| match self {
820 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
821 ExecutionCacheConfig::WritebackCache {
822 effect_cache_size, ..
823 } => effect_cache_size.unwrap_or(self.executed_effect_cache_size()),
824 })
825 }
826
827 pub fn events_cache_size(&self) -> u64 {
828 std::env::var("SUI_EVENTS_CACHE_SIZE")
829 .ok()
830 .and_then(|s| s.parse().ok())
831 .unwrap_or_else(|| match self {
832 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
833 ExecutionCacheConfig::WritebackCache {
834 events_cache_size, ..
835 } => events_cache_size.unwrap_or(self.transaction_cache_size()),
836 })
837 }
838
839 pub fn transaction_objects_cache_size(&self) -> u64 {
840 std::env::var("SUI_TRANSACTION_OBJECTS_CACHE_SIZE")
841 .ok()
842 .and_then(|s| s.parse().ok())
843 .unwrap_or_else(|| match self {
844 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
845 ExecutionCacheConfig::WritebackCache {
846 transaction_objects_cache_size,
847 ..
848 } => transaction_objects_cache_size.unwrap_or(1000),
849 })
850 }
851
852 pub fn backpressure_threshold(&self) -> u64 {
853 std::env::var("SUI_BACKPRESSURE_THRESHOLD")
854 .ok()
855 .and_then(|s| s.parse().ok())
856 .unwrap_or_else(|| match self {
857 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
858 ExecutionCacheConfig::WritebackCache {
859 backpressure_threshold,
860 ..
861 } => backpressure_threshold.unwrap_or(100_000),
862 })
863 }
864
865 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
866 std::env::var("SUI_BACKPRESSURE_THRESHOLD_FOR_RPC")
867 .ok()
868 .and_then(|s| s.parse().ok())
869 .unwrap_or_else(|| match self {
870 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
871 ExecutionCacheConfig::WritebackCache {
872 backpressure_threshold_for_rpc,
873 ..
874 } => backpressure_threshold_for_rpc.unwrap_or(self.backpressure_threshold()),
875 })
876 }
877}
878
879fn default_jwk_fetch_interval_seconds() -> u64 {
880 3600
881}
882
883pub fn default_zklogin_oauth_providers() -> BTreeMap<Chain, BTreeSet<String>> {
884 let mut map = BTreeMap::new();
885
886 let experimental_providers = BTreeSet::from([
888 "Google".to_string(),
889 "Facebook".to_string(),
890 "Twitch".to_string(),
891 "Kakao".to_string(),
892 "Apple".to_string(),
893 "Slack".to_string(),
894 "TestIssuer".to_string(),
895 "TestIssuerKey8192".to_string(),
896 "Microsoft".to_string(),
897 "KarrierOne".to_string(),
898 "Credenza3".to_string(),
899 "Playtron".to_string(),
900 "Threedos".to_string(),
901 "Onefc".to_string(),
902 "FanTV".to_string(),
903 "Arden".to_string(), "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "EveFrontier".to_string(),
906 "TestEveFrontier".to_string(),
907 "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(), ]);
911
912 let providers = BTreeSet::from([
914 "Google".to_string(),
915 "Facebook".to_string(),
916 "Twitch".to_string(),
917 "Apple".to_string(),
918 "KarrierOne".to_string(),
919 "Credenza3".to_string(),
920 "Playtron".to_string(),
921 "Onefc".to_string(),
922 "Threedos".to_string(),
923 "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "Arden".to_string(),
925 "FanTV".to_string(),
926 "EveFrontier".to_string(),
927 "TestEveFrontier".to_string(),
928 "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(), ]);
932 map.insert(Chain::Mainnet, providers.clone());
933 map.insert(Chain::Testnet, providers);
934 map.insert(Chain::Unknown, experimental_providers);
935 map
936}
937
938fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
939 AuthorityStorePruningConfig::default()
940}
941
942fn default_grpc_address() -> Multiaddr {
943 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
944}
945fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
946 AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
947}
948
949fn default_key_pair() -> KeyPairWithPath {
950 KeyPairWithPath::new(
951 get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
952 .1
953 .into(),
954 )
955}
956
957fn default_metrics_address() -> SocketAddr {
958 use std::net::{IpAddr, Ipv4Addr};
959 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
960}
961
962pub fn default_admin_interface_port() -> u16 {
963 1337
964}
965
966pub fn default_json_rpc_address() -> SocketAddr {
967 use std::net::{IpAddr, Ipv4Addr};
968 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
969}
970
971pub fn default_concurrency_limit() -> Option<usize> {
972 Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
973}
974
975pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
976 128
977}
978
979pub fn bool_true() -> bool {
980 true
981}
982
983fn is_true(value: &bool) -> bool {
984 *value
985}
986
987fn is_zero(value: &usize) -> bool {
988 *value == 0
989}
990
991impl Config for NodeConfig {}
992
993impl NodeConfig {
994 pub fn load_from_path(path: &Path) -> Result<Self, anyhow::Error> {
997 <Self as Config>::load(path)
998 }
999
1000 pub fn protocol_key_pair(&self) -> &AuthorityKeyPair {
1001 self.protocol_key_pair.authority_keypair()
1002 }
1003
1004 pub fn recent_submission_dedup_window(&self) -> Duration {
1007 Duration::from_millis(self.recent_submission_dedup_window_ms.unwrap_or(1000))
1008 }
1009
1010 pub fn worker_key_pair(&self) -> &NetworkKeyPair {
1011 match self.worker_key_pair.keypair() {
1012 SuiKeyPair::Ed25519(kp) => kp,
1013 other => panic!(
1014 "Invalid keypair type: {:?}, only Ed25519 is allowed for worker key",
1015 other
1016 ),
1017 }
1018 }
1019
1020 pub fn network_key_pair(&self) -> &NetworkKeyPair {
1021 match self.network_key_pair.keypair() {
1022 SuiKeyPair::Ed25519(kp) => kp,
1023 other => panic!(
1024 "Invalid keypair type: {:?}, only Ed25519 is allowed for network key",
1025 other
1026 ),
1027 }
1028 }
1029
1030 pub fn protocol_public_key(&self) -> AuthorityPublicKeyBytes {
1031 self.protocol_key_pair().public().into()
1032 }
1033
1034 pub fn db_path(&self) -> PathBuf {
1035 self.db_path.join("live")
1036 }
1037
1038 pub fn db_checkpoint_path(&self) -> PathBuf {
1039 self.db_path.join("db_checkpoints")
1040 }
1041
1042 pub fn db_store_path(&self) -> PathBuf {
1043 self.db_path().join("store")
1044 }
1045
1046 pub fn archive_path(&self) -> PathBuf {
1047 self.db_path.join("archive")
1048 }
1049
1050 pub fn snapshot_path(&self) -> PathBuf {
1051 self.db_path.join("snapshot")
1052 }
1053
1054 pub fn network_address(&self) -> &Multiaddr {
1055 &self.network_address
1056 }
1057
1058 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
1059 self.consensus_config.as_ref()
1060 }
1061
1062 pub fn intended_node_role(&self) -> NodeRole {
1067 let has_consensus_config = self.consensus_config.is_some();
1068
1069 match (self.fullnode_sync_mode, has_consensus_config) {
1070 (Some(FullNodeSyncMode::ConsensusObserver), _) => {
1071 assert!(
1072 self.has_observer_config_peers(),
1073 "Observer peers must be configured when sync mode is ConsensusObserver"
1074 );
1075 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
1076 }
1077 (Some(FullNodeSyncMode::StateSyncOnly), true) => {
1078 panic!("Consensus config should not be set for a StateSyncOnly full node");
1079 }
1080 (Some(FullNodeSyncMode::StateSyncOnly), false) => {
1081 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
1082 }
1083 (None, false) => NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly),
1084 (None, true) => NodeRole::Validator,
1085 }
1086 }
1087
1088 pub fn has_observer_config_peers(&self) -> bool {
1089 self.consensus_config
1090 .as_ref()
1091 .and_then(|c| c.parameters.as_ref())
1092 .map(|p| !p.observer.peers.is_empty())
1093 .unwrap_or(false)
1094 }
1095
1096 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1097 self.genesis.genesis()
1098 }
1099
1100 pub fn sui_address(&self) -> SuiAddress {
1101 (&self.account_key_pair.keypair().public()).into()
1102 }
1103
1104 pub fn archive_reader_config(&self) -> Option<ArchiveReaderConfig> {
1105 self.state_archive_read_config
1106 .first()
1107 .map(|config| ArchiveReaderConfig {
1108 ingestion_url: config.ingestion_url.clone(),
1109 remote_store_options: config.remote_store_options.clone(),
1110 remote_store_headers: config.remote_store_headers.clone(),
1111 download_concurrency: NonZeroUsize::new(config.concurrency)
1112 .unwrap_or(NonZeroUsize::new(5).unwrap()),
1113 remote_store_config: ObjectStoreConfig::default(),
1114 })
1115 }
1116
1117 pub fn rpc(&self) -> Option<&crate::RpcConfig> {
1118 self.rpc.as_ref()
1119 }
1120}
1121
1122#[derive(Debug, Clone, Deserialize, Serialize)]
1123pub enum ConsensusProtocol {
1124 #[serde(rename = "narwhal")]
1125 Narwhal,
1126 #[serde(rename = "mysticeti")]
1127 Mysticeti,
1128}
1129
1130#[derive(Debug, Clone, Deserialize, Serialize)]
1131#[serde(rename_all = "kebab-case")]
1132pub struct ConsensusConfig {
1133 pub db_path: PathBuf,
1135
1136 pub db_retention_epochs: Option<u64>,
1139
1140 pub db_pruner_period_secs: Option<u64>,
1143
1144 pub max_pending_transactions: Option<usize>,
1148
1149 pub parameters: Option<ConsensusParameters>,
1150
1151 #[serde(skip_serializing_if = "Option::is_none")]
1155 pub listen_address: Option<Multiaddr>,
1156
1157 #[serde(skip_serializing_if = "Option::is_none")]
1162 pub external_address: Option<Multiaddr>,
1163}
1164
1165impl ConsensusConfig {
1166 pub fn db_path(&self) -> &Path {
1167 &self.db_path
1168 }
1169
1170 pub fn max_pending_transactions(&self) -> usize {
1171 self.max_pending_transactions.unwrap_or(20_000)
1172 }
1173
1174 pub fn db_retention_epochs(&self) -> u64 {
1175 self.db_retention_epochs.unwrap_or(0)
1176 }
1177
1178 pub fn db_pruner_period(&self) -> Duration {
1179 self.db_pruner_period_secs
1181 .map(Duration::from_secs)
1182 .unwrap_or(Duration::from_secs(3_600))
1183 }
1184}
1185
1186#[derive(Clone, Debug, Deserialize, Serialize)]
1187#[serde(rename_all = "kebab-case")]
1188pub struct CheckpointExecutorConfig {
1189 #[serde(default = "default_checkpoint_execution_max_concurrency")]
1193 pub checkpoint_execution_max_concurrency: usize,
1194
1195 #[serde(default = "default_local_execution_timeout_sec")]
1201 pub local_execution_timeout_sec: u64,
1202
1203 #[serde(default, skip_serializing_if = "Option::is_none")]
1206 pub data_ingestion_dir: Option<PathBuf>,
1207}
1208
1209#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1210#[serde(rename_all = "kebab-case")]
1211pub struct ExpensiveSafetyCheckConfig {
1212 #[serde(default)]
1217 enable_epoch_sui_conservation_check: bool,
1218
1219 #[serde(default)]
1223 enable_deep_per_tx_sui_conservation_check: bool,
1224
1225 #[serde(default)]
1227 force_disable_epoch_sui_conservation_check: bool,
1228
1229 #[serde(default)]
1232 enable_state_consistency_check: bool,
1233
1234 #[serde(default)]
1236 force_disable_state_consistency_check: bool,
1237 }
1239
1240impl ExpensiveSafetyCheckConfig {
1241 pub fn new_enable_all() -> Self {
1242 Self {
1243 enable_epoch_sui_conservation_check: true,
1244 enable_deep_per_tx_sui_conservation_check: true,
1245 force_disable_epoch_sui_conservation_check: false,
1246 enable_state_consistency_check: true,
1247 force_disable_state_consistency_check: false,
1248 }
1249 }
1250
1251 pub fn new_disable_all() -> Self {
1252 Self {
1253 enable_epoch_sui_conservation_check: false,
1254 enable_deep_per_tx_sui_conservation_check: false,
1255 force_disable_epoch_sui_conservation_check: true,
1256 enable_state_consistency_check: false,
1257 force_disable_state_consistency_check: true,
1258 }
1259 }
1260
1261 pub fn force_disable_epoch_sui_conservation_check(&mut self) {
1262 self.force_disable_epoch_sui_conservation_check = true;
1263 }
1264
1265 pub fn enable_epoch_sui_conservation_check(&self) -> bool {
1266 (self.enable_epoch_sui_conservation_check || cfg!(debug_assertions))
1267 && !self.force_disable_epoch_sui_conservation_check
1268 }
1269
1270 pub fn force_disable_state_consistency_check(&mut self) {
1271 self.force_disable_state_consistency_check = true;
1272 }
1273
1274 pub fn enable_state_consistency_check(&self) -> bool {
1275 (self.enable_state_consistency_check || cfg!(debug_assertions))
1276 && !self.force_disable_state_consistency_check
1277 }
1278
1279 pub fn enable_deep_per_tx_sui_conservation_check(&self) -> bool {
1280 self.enable_deep_per_tx_sui_conservation_check || cfg!(debug_assertions)
1281 }
1282}
1283
1284fn default_checkpoint_execution_max_concurrency() -> usize {
1285 4
1286}
1287
1288fn default_local_execution_timeout_sec() -> u64 {
1289 30
1290}
1291
1292impl Default for CheckpointExecutorConfig {
1293 fn default() -> Self {
1294 Self {
1295 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
1296 local_execution_timeout_sec: default_local_execution_timeout_sec(),
1297 data_ingestion_dir: None,
1298 }
1299 }
1300}
1301
1302#[derive(Debug, Clone, Deserialize, Serialize)]
1303#[serde(rename_all = "kebab-case")]
1304pub struct AuthorityStorePruningConfig {
1305 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1307 pub num_latest_epoch_dbs_to_retain: usize,
1308 #[serde(default = "default_epoch_db_pruning_period_secs")]
1310 pub epoch_db_pruning_period_secs: u64,
1311 #[serde(default)]
1316 pub num_epochs_to_retain: u64,
1317 #[serde(skip_serializing_if = "Option::is_none")]
1319 pub pruning_run_delay_seconds: Option<u64>,
1320 #[serde(default = "default_max_checkpoints_in_batch")]
1322 pub max_checkpoints_in_batch: usize,
1323 #[serde(default = "default_max_transactions_in_batch")]
1325 pub max_transactions_in_batch: usize,
1326 #[serde(
1330 default = "default_periodic_compaction_threshold_days",
1331 skip_serializing_if = "Option::is_none"
1332 )]
1333 pub periodic_compaction_threshold_days: Option<usize>,
1334 #[serde(default, skip_serializing_if = "Option::is_none")]
1344 pub rpc_store_bitmap_periodic_compaction_days: Option<u64>,
1345 #[serde(default, skip_serializing_if = "is_zero")]
1361 pub rpc_store_num_checkpoint_snapshots_to_retain: usize,
1362 #[serde(skip_serializing_if = "Option::is_none")]
1364 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1365 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1367 pub killswitch_tombstone_pruning: bool,
1368 #[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
1369 pub smooth: bool,
1370}
1371
1372fn default_num_latest_epoch_dbs_to_retain() -> usize {
1373 3
1374}
1375
1376fn default_epoch_db_pruning_period_secs() -> u64 {
1377 3600
1378}
1379
1380fn default_max_transactions_in_batch() -> usize {
1381 1000
1382}
1383
1384fn default_max_checkpoints_in_batch() -> usize {
1385 10
1386}
1387
1388fn default_smoothing() -> bool {
1389 cfg!(not(test))
1390}
1391
1392fn default_periodic_compaction_threshold_days() -> Option<usize> {
1393 Some(1)
1394}
1395
1396impl Default for AuthorityStorePruningConfig {
1397 fn default() -> Self {
1398 Self {
1399 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1400 epoch_db_pruning_period_secs: default_epoch_db_pruning_period_secs(),
1401 num_epochs_to_retain: 0,
1402 pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
1403 max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
1404 max_transactions_in_batch: default_max_transactions_in_batch(),
1405 periodic_compaction_threshold_days: None,
1406 rpc_store_bitmap_periodic_compaction_days: None,
1407 rpc_store_num_checkpoint_snapshots_to_retain: 0,
1408 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1409 killswitch_tombstone_pruning: false,
1410 smooth: true,
1411 }
1412 }
1413}
1414
1415impl AuthorityStorePruningConfig {
1416 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1417 self.num_epochs_to_retain = num_epochs_to_retain;
1418 }
1419
1420 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1421 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1422 }
1423
1424 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1425 self.num_epochs_to_retain_for_checkpoints
1426 .map(|n| {
1428 if n < 2 {
1429 info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
1430 2
1431 } else {
1432 n
1433 }
1434 })
1435 }
1436
1437 pub fn set_killswitch_tombstone_pruning(&mut self, killswitch_tombstone_pruning: bool) {
1438 self.killswitch_tombstone_pruning = killswitch_tombstone_pruning;
1439 }
1440}
1441
1442#[derive(Debug, Clone, Deserialize, Serialize)]
1443#[serde(rename_all = "kebab-case")]
1444pub struct MetricsConfig {
1445 #[serde(skip_serializing_if = "Option::is_none")]
1446 pub push_interval_seconds: Option<u64>,
1447 #[serde(skip_serializing_if = "Option::is_none")]
1448 pub push_url: Option<String>,
1449}
1450
1451#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1452#[serde(rename_all = "kebab-case")]
1453pub struct DBCheckpointConfig {
1454 #[serde(default)]
1455 pub perform_db_checkpoints_at_epoch_end: bool,
1456 #[serde(skip_serializing_if = "Option::is_none")]
1457 pub checkpoint_path: Option<PathBuf>,
1458 #[serde(skip_serializing_if = "Option::is_none")]
1459 pub object_store_config: Option<ObjectStoreConfig>,
1460 #[serde(skip_serializing_if = "Option::is_none")]
1461 pub prune_and_compact_before_upload: Option<bool>,
1462}
1463
1464#[derive(Debug, Clone)]
1465pub struct ArchiveReaderConfig {
1466 pub remote_store_config: ObjectStoreConfig,
1467 pub download_concurrency: NonZeroUsize,
1468 pub ingestion_url: Option<String>,
1469 pub remote_store_options: Vec<(String, String)>,
1470 pub remote_store_headers: Vec<(String, String)>,
1471}
1472
1473#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1474#[serde(rename_all = "kebab-case")]
1475pub struct StateArchiveConfig {
1476 #[serde(skip_serializing_if = "Option::is_none")]
1477 pub object_store_config: Option<ObjectStoreConfig>,
1478 pub concurrency: usize,
1479 #[serde(skip_serializing_if = "Option::is_none")]
1480 pub ingestion_url: Option<String>,
1481 #[serde(
1482 skip_serializing_if = "Vec::is_empty",
1483 default,
1484 deserialize_with = "deserialize_remote_store_options"
1485 )]
1486 pub remote_store_options: Vec<(String, String)>,
1487 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1491 pub remote_store_headers: Vec<(String, String)>,
1492}
1493
1494#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1495#[serde(rename_all = "kebab-case")]
1496pub struct StateSnapshotConfig {
1497 #[serde(skip_serializing_if = "Option::is_none")]
1498 pub object_store_config: Option<ObjectStoreConfig>,
1499 pub concurrency: usize,
1500 #[serde(default)]
1504 pub archive_interval_epochs: u64,
1505}
1506
1507#[derive(Clone, Debug, Deserialize, Serialize)]
1512#[serde(rename_all = "kebab-case")]
1513pub struct AuthorityOverloadConfig {
1514 #[serde(default = "default_max_txn_age_in_queue")]
1515 pub max_txn_age_in_queue: Duration,
1516
1517 #[serde(default = "default_overload_monitor_interval")]
1519 pub overload_monitor_interval: Duration,
1520
1521 #[serde(default = "default_execution_queue_latency_soft_limit")]
1523 pub execution_queue_latency_soft_limit: Duration,
1524
1525 #[serde(default = "default_execution_queue_latency_hard_limit")]
1527 pub execution_queue_latency_hard_limit: Duration,
1528
1529 #[serde(default = "default_max_load_shedding_percentage")]
1531 pub max_load_shedding_percentage: u32,
1532
1533 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1536 pub min_load_shedding_percentage_above_hard_limit: u32,
1537
1538 #[serde(default = "default_safe_transaction_ready_rate")]
1541 pub safe_transaction_ready_rate: u32,
1542
1543 #[serde(default = "default_check_system_overload_at_signing")]
1546 pub check_system_overload_at_signing: bool,
1547
1548 #[serde(default = "default_max_transaction_manager_queue_length")]
1551 pub max_transaction_manager_queue_length: usize,
1552
1553 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1556 pub max_transaction_manager_per_object_queue_length: usize,
1557
1558 #[serde(default = "default_admission_queue_capacity_fraction")]
1562 pub admission_queue_capacity_fraction: f64,
1563
1564 #[serde(default = "default_admission_queue_enabled")]
1569 pub admission_queue_enabled: bool,
1570
1571 #[serde(default = "default_admission_queue_failover_timeout")]
1576 pub admission_queue_failover_timeout: Duration,
1577}
1578
1579fn default_max_txn_age_in_queue() -> Duration {
1580 Duration::from_millis(1000)
1581}
1582
1583fn default_overload_monitor_interval() -> Duration {
1584 Duration::from_secs(10)
1585}
1586
1587fn default_execution_queue_latency_soft_limit() -> Duration {
1588 Duration::from_secs(1)
1589}
1590
1591fn default_execution_queue_latency_hard_limit() -> Duration {
1592 Duration::from_secs(10)
1593}
1594
1595fn default_max_load_shedding_percentage() -> u32 {
1596 95
1597}
1598
1599fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1600 50
1601}
1602
1603fn default_safe_transaction_ready_rate() -> u32 {
1604 100
1605}
1606
1607fn default_check_system_overload_at_signing() -> bool {
1608 true
1609}
1610
1611fn default_max_transaction_manager_queue_length() -> usize {
1612 100_000
1613}
1614
1615fn default_max_transaction_manager_per_object_queue_length() -> usize {
1616 2000
1617}
1618
1619fn default_admission_queue_capacity_fraction() -> f64 {
1620 0.5
1621}
1622
1623fn default_admission_queue_enabled() -> bool {
1624 true
1625}
1626
1627fn default_admission_queue_failover_timeout() -> Duration {
1628 Duration::from_secs(30)
1629}
1630
1631impl Default for AuthorityOverloadConfig {
1632 fn default() -> Self {
1633 Self {
1634 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1635 overload_monitor_interval: default_overload_monitor_interval(),
1636 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1637 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1638 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1639 min_load_shedding_percentage_above_hard_limit:
1640 default_min_load_shedding_percentage_above_hard_limit(),
1641 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1642 check_system_overload_at_signing: true,
1643 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1644 max_transaction_manager_per_object_queue_length:
1645 default_max_transaction_manager_per_object_queue_length(),
1646 admission_queue_capacity_fraction: default_admission_queue_capacity_fraction(),
1647 admission_queue_enabled: default_admission_queue_enabled(),
1648 admission_queue_failover_timeout: default_admission_queue_failover_timeout(),
1649 }
1650 }
1651}
1652
1653fn default_authority_overload_config() -> AuthorityOverloadConfig {
1654 AuthorityOverloadConfig::default()
1655}
1656
1657fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1658 Some(PolicyConfig::default_dos_protection_policy())
1659}
1660
1661#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1662pub struct Genesis {
1663 #[serde(flatten)]
1664 location: GenesisLocation,
1665
1666 #[serde(skip)]
1667 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1668}
1669
1670impl Genesis {
1671 pub fn new(genesis: genesis::Genesis) -> Self {
1672 Self {
1673 location: GenesisLocation::InPlace { genesis },
1674 genesis: Default::default(),
1675 }
1676 }
1677
1678 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1679 Self {
1680 location: GenesisLocation::File {
1681 genesis_file_location: path.into(),
1682 },
1683 genesis: Default::default(),
1684 }
1685 }
1686
1687 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1688 match &self.location {
1689 GenesisLocation::InPlace { genesis } => Ok(genesis),
1690 GenesisLocation::File {
1691 genesis_file_location,
1692 } => self
1693 .genesis
1694 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1695 }
1696 }
1697}
1698
1699#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1700#[serde(untagged)]
1701#[allow(clippy::large_enum_variant)]
1702enum GenesisLocation {
1703 InPlace {
1704 genesis: genesis::Genesis,
1705 },
1706 File {
1707 #[serde(rename = "genesis-file-location")]
1708 genesis_file_location: PathBuf,
1709 },
1710}
1711
1712#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1714pub struct KeyPairWithPath {
1715 #[serde(flatten)]
1716 location: KeyPairLocation,
1717
1718 #[serde(skip)]
1719 keypair: OnceCell<Arc<SuiKeyPair>>,
1720}
1721
1722#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1723#[serde_as]
1724#[serde(untagged)]
1725enum KeyPairLocation {
1726 InPlace {
1727 #[serde_as(as = "Arc<KeyPairBase64>")]
1728 value: Arc<SuiKeyPair>,
1729 },
1730 File {
1731 #[serde(rename = "path")]
1732 path: PathBuf,
1733 },
1734}
1735
1736impl KeyPairWithPath {
1737 pub fn new(kp: SuiKeyPair) -> Self {
1738 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1739 let arc_kp = Arc::new(kp);
1740 cell.set(arc_kp.clone()).expect("Failed to set keypair");
1742 Self {
1743 location: KeyPairLocation::InPlace { value: arc_kp },
1744 keypair: cell,
1745 }
1746 }
1747
1748 pub fn new_from_path(path: PathBuf) -> Self {
1749 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1750 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1752 |e| panic!("Invalid keypair file at path {:?}: {e}", path),
1753 )))
1754 .expect("Failed to set keypair");
1755 Self {
1756 location: KeyPairLocation::File { path },
1757 keypair: cell,
1758 }
1759 }
1760
1761 pub fn keypair(&self) -> &SuiKeyPair {
1762 self.keypair
1763 .get_or_init(|| match &self.location {
1764 KeyPairLocation::InPlace { value } => value.clone(),
1765 KeyPairLocation::File { path } => {
1766 Arc::new(
1768 read_keypair_from_file(path).unwrap_or_else(|e| {
1769 panic!("Invalid keypair file at path {:?}: {e}", path)
1770 }),
1771 )
1772 }
1773 })
1774 .as_ref()
1775 }
1776}
1777
1778#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1780pub struct AuthorityKeyPairWithPath {
1781 #[serde(flatten)]
1782 location: AuthorityKeyPairLocation,
1783
1784 #[serde(skip)]
1785 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1786}
1787
1788#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1789#[serde_as]
1790#[serde(untagged)]
1791enum AuthorityKeyPairLocation {
1792 InPlace { value: Arc<AuthorityKeyPair> },
1793 File { path: PathBuf },
1794}
1795
1796impl AuthorityKeyPairWithPath {
1797 pub fn new(kp: AuthorityKeyPair) -> Self {
1798 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1799 let arc_kp = Arc::new(kp);
1800 cell.set(arc_kp.clone())
1802 .expect("Failed to set authority keypair");
1803 Self {
1804 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1805 keypair: cell,
1806 }
1807 }
1808
1809 pub fn new_from_path(path: PathBuf) -> Self {
1810 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1811 cell.set(Arc::new(
1813 read_authority_keypair_from_file(&path)
1814 .unwrap_or_else(|_| panic!("Invalid authority keypair file at path {:?}", path)),
1815 ))
1816 .expect("Failed to set authority keypair");
1817 Self {
1818 location: AuthorityKeyPairLocation::File { path },
1819 keypair: cell,
1820 }
1821 }
1822
1823 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1824 self.keypair
1825 .get_or_init(|| match &self.location {
1826 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1827 AuthorityKeyPairLocation::File { path } => {
1828 Arc::new(
1830 read_authority_keypair_from_file(path).unwrap_or_else(|_| {
1831 panic!("Invalid authority keypair file {:?}", path)
1832 }),
1833 )
1834 }
1835 })
1836 .as_ref()
1837 }
1838}
1839
1840#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1843#[serde(rename_all = "kebab-case")]
1844pub struct StateDebugDumpConfig {
1845 #[serde(skip_serializing_if = "Option::is_none")]
1846 pub dump_file_directory: Option<PathBuf>,
1847}
1848
1849fn read_credential_from_path_or_literal(value: &str) -> Result<String, std::io::Error> {
1850 let path = Path::new(value);
1851 if path.exists() && path.is_file() {
1852 std::fs::read_to_string(path).map(|content| content.trim().to_string())
1853 } else {
1854 Ok(value.to_string())
1855 }
1856}
1857
1858fn deserialize_remote_store_options<'de, D>(
1860 deserializer: D,
1861) -> Result<Vec<(String, String)>, D::Error>
1862where
1863 D: serde::Deserializer<'de>,
1864{
1865 use serde::de::Error;
1866
1867 let raw_options: Vec<(String, String)> = Vec::deserialize(deserializer)?;
1868 let mut processed_options = Vec::new();
1869
1870 for (key, value) in raw_options {
1871 let is_service_account_path = matches!(
1874 key.as_str(),
1875 "google_service_account"
1876 | "service_account"
1877 | "google_service_account_path"
1878 | "service_account_path"
1879 );
1880
1881 let processed_value = if is_service_account_path {
1882 value
1883 } else {
1884 match read_credential_from_path_or_literal(&value) {
1885 Ok(processed) => processed,
1886 Err(e) => {
1887 return Err(D::Error::custom(format!(
1888 "Failed to read credential for key '{}': {}",
1889 key, e
1890 )));
1891 }
1892 }
1893 };
1894
1895 processed_options.push((key, processed_value));
1896 }
1897
1898 Ok(processed_options)
1899}
1900
1901#[cfg(test)]
1902mod tests {
1903 use std::path::PathBuf;
1904 use std::time::Duration;
1905
1906 use fastcrypto::traits::KeyPair;
1907 use rand::{SeedableRng, rngs::StdRng};
1908 use sui_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1909 use sui_types::base_types::ObjectID;
1910 use sui_types::crypto::{AuthorityKeyPair, NetworkKeyPair, SuiKeyPair, get_key_pair_from_rng};
1911
1912 use super::{
1913 AuthorityStorePruningConfig, ConsensusTransactionPoolConfig, ExecutionTimeObserverConfig,
1914 Genesis, StateArchiveConfig,
1915 };
1916 use crate::NodeConfig;
1917
1918 #[test]
1919 fn serialize_genesis_from_file() {
1920 let g = Genesis::new_from_file("path/to/file");
1921
1922 let s = serde_yaml::to_string(&g).unwrap();
1923 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
1924 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
1925 assert_eq!(g, loaded_genesis);
1926 }
1927
1928 #[test]
1929 fn fullnode_template() {
1930 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1931
1932 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1933 }
1934
1935 #[test]
1937 fn legacy_validator_config() {
1938 const FILE: &str = include_str!("../data/sui-node-legacy.yaml");
1939
1940 let template: NodeConfig = serde_yaml::from_str(FILE).unwrap();
1941 assert_eq!(
1942 template
1943 .authority_store_pruning_config
1944 .rpc_store_bitmap_periodic_compaction_days,
1945 None
1946 );
1947 }
1948
1949 #[test]
1950 fn rpc_store_bitmap_periodic_compaction_days_override_deserializes() {
1951 assert_eq!(
1952 AuthorityStorePruningConfig::default().rpc_store_bitmap_periodic_compaction_days,
1953 None
1954 );
1955
1956 let omitted: AuthorityStorePruningConfig = serde_yaml::from_str("{}").unwrap();
1957 assert_eq!(omitted.rpc_store_bitmap_periodic_compaction_days, None);
1958
1959 let disabled: AuthorityStorePruningConfig =
1960 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 0").unwrap();
1961 assert_eq!(disabled.rpc_store_bitmap_periodic_compaction_days, Some(0));
1962
1963 let configured: AuthorityStorePruningConfig =
1964 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 17").unwrap();
1965 let serialized = serde_yaml::to_string(&configured).unwrap();
1966 let round_tripped: AuthorityStorePruningConfig = serde_yaml::from_str(&serialized).unwrap();
1967 assert_eq!(
1968 round_tripped.rpc_store_bitmap_periodic_compaction_days,
1969 Some(17)
1970 );
1971 }
1972
1973 #[test]
1974 fn max_queue_age_defaults_and_disable() {
1975 let default = ConsensusTransactionPoolConfig::default();
1976 assert_eq!(default.max_queue_age(), Some(Duration::from_secs(60)));
1977
1978 let configured = ConsensusTransactionPoolConfig {
1979 max_queue_age_secs: Some(120),
1980 ..Default::default()
1981 };
1982 assert_eq!(configured.max_queue_age(), Some(Duration::from_secs(120)));
1983
1984 let disabled = ConsensusTransactionPoolConfig {
1985 max_queue_age_secs: Some(0),
1986 ..Default::default()
1987 };
1988 assert_eq!(disabled.max_queue_age(), None);
1989
1990 let parsed: ConsensusTransactionPoolConfig =
1991 serde_yaml::from_str("max-queue-age-secs: 0").unwrap();
1992 assert_eq!(parsed.max_queue_age(), None);
1993 let parsed: ConsensusTransactionPoolConfig =
1994 serde_yaml::from_str("max-queue-age-secs: 5").unwrap();
1995 assert_eq!(parsed.max_queue_age(), Some(Duration::from_secs(5)));
1996 let omitted: ConsensusTransactionPoolConfig = serde_yaml::from_str("{}").unwrap();
1997 assert_eq!(omitted.max_queue_age(), Some(Duration::from_secs(60)));
1998 }
1999
2000 #[test]
2001 fn execution_time_observer_config_tracked_ids() {
2002 let omitted: ExecutionTimeObserverConfig = serde_yaml::from_str("{}").unwrap();
2003 assert_eq!(omitted.object_utilization_metric_tracked_ids().count(), 0);
2004
2005 let yaml = r#"
2006 object-utilization-metric-tracked-ids:
2007 "0x0000000000000000000000000000000000000000000000000000000000000005": sui-system-state
2008 "0xe05dafb5133bcffb8d59f4e12465dc0e9faeaa05e3e342a08fe135800e3e4407": deepbook-sui-usdc
2009 "#;
2010 let configured: ExecutionTimeObserverConfig = serde_yaml::from_str(yaml).unwrap();
2011 let tracked: Vec<_> = configured.object_utilization_metric_tracked_ids().collect();
2012 assert_eq!(tracked.len(), 2);
2013 assert_eq!(
2014 tracked[0],
2015 (&ObjectID::from_single_byte(5), "sui-system-state")
2016 );
2017
2018 let serialized = serde_yaml::to_string(&configured).unwrap();
2019 let round_tripped: ExecutionTimeObserverConfig = serde_yaml::from_str(&serialized).unwrap();
2020 assert_eq!(
2021 round_tripped.object_utilization_metric_tracked_ids,
2022 configured.object_utilization_metric_tracked_ids
2023 );
2024 }
2025
2026 #[test]
2027 fn load_key_pairs_to_node_config() {
2028 let protocol_key_pair: AuthorityKeyPair =
2029 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2030 let worker_key_pair: NetworkKeyPair =
2031 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2032 let network_key_pair: NetworkKeyPair =
2033 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2034
2035 write_authority_keypair_to_file(&protocol_key_pair, PathBuf::from("protocol.key")).unwrap();
2036 write_keypair_to_file(
2037 &SuiKeyPair::Ed25519(worker_key_pair.copy()),
2038 PathBuf::from("worker.key"),
2039 )
2040 .unwrap();
2041 write_keypair_to_file(
2042 &SuiKeyPair::Ed25519(network_key_pair.copy()),
2043 PathBuf::from("network.key"),
2044 )
2045 .unwrap();
2046
2047 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
2048 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2049 assert_eq!(
2050 template.protocol_key_pair().public(),
2051 protocol_key_pair.public()
2052 );
2053 assert_eq!(
2054 template.network_key_pair().public(),
2055 network_key_pair.public()
2056 );
2057 assert_eq!(
2058 template.worker_key_pair().public(),
2059 worker_key_pair.public()
2060 );
2061 }
2062
2063 #[test]
2064 fn test_remote_store_options_file_path_support() {
2065 let temp_dir = std::env::temp_dir();
2067 let access_key_file = temp_dir.join("test_access_key");
2068 let secret_key_file = temp_dir.join("test_secret_key");
2069
2070 std::fs::write(&access_key_file, "test_access_key_value").unwrap();
2071 std::fs::write(&secret_key_file, "test_secret_key_value\n").unwrap();
2072
2073 let yaml_config = format!(
2074 r#"
2075object-store-config: null
2076concurrency: 5
2077ingestion-url: "https://example.com"
2078remote-store-options:
2079 - ["aws_access_key_id", "{}"]
2080 - ["aws_secret_access_key", "{}"]
2081 - ["literal_key", "literal_value"]
2082"#,
2083 access_key_file.to_string_lossy(),
2084 secret_key_file.to_string_lossy()
2085 );
2086
2087 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2088
2089 assert_eq!(config.remote_store_options.len(), 3);
2091
2092 let access_key_option = config
2093 .remote_store_options
2094 .iter()
2095 .find(|(key, _)| key == "aws_access_key_id")
2096 .unwrap();
2097 assert_eq!(access_key_option.1, "test_access_key_value");
2098
2099 let secret_key_option = config
2100 .remote_store_options
2101 .iter()
2102 .find(|(key, _)| key == "aws_secret_access_key")
2103 .unwrap();
2104 assert_eq!(secret_key_option.1, "test_secret_key_value");
2105
2106 let literal_option = config
2107 .remote_store_options
2108 .iter()
2109 .find(|(key, _)| key == "literal_key")
2110 .unwrap();
2111 assert_eq!(literal_option.1, "literal_value");
2112
2113 std::fs::remove_file(&access_key_file).ok();
2115 std::fs::remove_file(&secret_key_file).ok();
2116 }
2117
2118 #[test]
2119 fn test_remote_store_options_literal_values_only() {
2120 let yaml_config = r#"
2121object-store-config: null
2122concurrency: 5
2123ingestion-url: "https://example.com"
2124remote-store-options:
2125 - ["aws_access_key_id", "literal_access_key"]
2126 - ["aws_secret_access_key", "literal_secret_key"]
2127"#;
2128
2129 let config: StateArchiveConfig = serde_yaml::from_str(yaml_config).unwrap();
2130
2131 assert_eq!(config.remote_store_options.len(), 2);
2132 assert_eq!(config.remote_store_options[0].1, "literal_access_key");
2133 assert_eq!(config.remote_store_options[1].1, "literal_secret_key");
2134 }
2135
2136 #[test]
2137 fn test_remote_store_options_gcs_service_account_path_preserved() {
2138 let temp_dir = std::env::temp_dir();
2139 let service_account_file = temp_dir.join("test_service_account.json");
2140 let aws_key_file = temp_dir.join("test_aws_key");
2141
2142 std::fs::write(&service_account_file, r#"{"type": "service_account"}"#).unwrap();
2143 std::fs::write(&aws_key_file, "aws_key_value").unwrap();
2144
2145 let yaml_config = format!(
2146 r#"
2147object-store-config: null
2148concurrency: 5
2149ingestion-url: "gs://my-bucket"
2150remote-store-options:
2151 - ["service_account", "{}"]
2152 - ["google_service_account_path", "{}"]
2153 - ["aws_access_key_id", "{}"]
2154"#,
2155 service_account_file.to_string_lossy(),
2156 service_account_file.to_string_lossy(),
2157 aws_key_file.to_string_lossy()
2158 );
2159
2160 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2161
2162 assert_eq!(config.remote_store_options.len(), 3);
2163
2164 let service_account_option = config
2166 .remote_store_options
2167 .iter()
2168 .find(|(key, _)| key == "service_account")
2169 .unwrap();
2170 assert_eq!(
2171 service_account_option.1,
2172 service_account_file.to_string_lossy()
2173 );
2174
2175 let gcs_path_option = config
2177 .remote_store_options
2178 .iter()
2179 .find(|(key, _)| key == "google_service_account_path")
2180 .unwrap();
2181 assert_eq!(gcs_path_option.1, service_account_file.to_string_lossy());
2182
2183 let aws_option = config
2185 .remote_store_options
2186 .iter()
2187 .find(|(key, _)| key == "aws_access_key_id")
2188 .unwrap();
2189 assert_eq!(aws_option.1, "aws_key_value");
2190
2191 std::fs::remove_file(&service_account_file).ok();
2193 std::fs::remove_file(&aws_key_file).ok();
2194 }
2195
2196 mod intended_node_role_tests {
2197 use super::*;
2198 use crate::ConsensusConfig;
2199 use consensus_config::Parameters as ConsensusParameters;
2200 use fastcrypto::ed25519::Ed25519KeyPair;
2201 use sui_types::node_role::{FullNodeSyncMode, NodeRole};
2202
2203 fn fullnode_template_config() -> NodeConfig {
2204 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2205 serde_yaml::from_str(TEMPLATE).unwrap()
2206 }
2207
2208 fn minimal_consensus_config() -> ConsensusConfig {
2209 ConsensusConfig {
2210 db_path: PathBuf::from("/tmp/consensus"),
2211 db_retention_epochs: None,
2212 db_pruner_period_secs: None,
2213 max_pending_transactions: None,
2214 parameters: Default::default(),
2215 listen_address: None,
2216 external_address: None,
2217 }
2218 }
2219
2220 fn consensus_config_with_observer_peers() -> ConsensusConfig {
2221 let mut config = minimal_consensus_config();
2222 let kp = Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32]));
2223 let peer = consensus_config::PeerRecord {
2224 public_key: consensus_config::NetworkPublicKey::new(kp.public().clone()),
2225 address: "/ip4/127.0.0.1/udp/8080".parse().unwrap(),
2226 };
2227 let mut params = ConsensusParameters::default();
2228 params.observer.peers = vec![peer];
2229 config.parameters = Some(params);
2230 config
2231 }
2232
2233 #[test]
2234 fn validator_with_consensus_config() {
2235 let mut config = fullnode_template_config();
2236 config.consensus_config = Some(minimal_consensus_config());
2237 config.fullnode_sync_mode = None;
2238
2239 assert_eq!(config.intended_node_role(), NodeRole::Validator);
2240 }
2241
2242 #[test]
2243 fn fullnode_explicit_state_sync() {
2244 let mut config = fullnode_template_config();
2245 config.consensus_config = None;
2246 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2247
2248 assert_eq!(
2249 config.intended_node_role(),
2250 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2251 );
2252 }
2253
2254 #[test]
2255 fn fullnode_implicit_state_sync() {
2256 let mut config = fullnode_template_config();
2257 config.consensus_config = None;
2258 config.fullnode_sync_mode = None;
2259
2260 assert_eq!(
2261 config.intended_node_role(),
2262 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2263 );
2264 }
2265
2266 #[test]
2267 fn fullnode_consensus_observer() {
2268 let mut config = fullnode_template_config();
2269 config.consensus_config = Some(consensus_config_with_observer_peers());
2270 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2271
2272 assert_eq!(
2273 config.intended_node_role(),
2274 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
2275 );
2276 }
2277
2278 #[test]
2279 #[should_panic(
2280 expected = "Consensus config should not be set for a StateSyncOnly full node"
2281 )]
2282 fn state_sync_with_consensus_config_panics() {
2283 let mut config = fullnode_template_config();
2284 config.consensus_config = Some(minimal_consensus_config());
2285 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2286
2287 config.intended_node_role();
2288 }
2289
2290 #[test]
2291 #[should_panic(expected = "Observer peers must be configured")]
2292 fn observer_without_peers_panics() {
2293 let mut config = fullnode_template_config();
2294 config.consensus_config = Some(minimal_consensus_config());
2295 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2296
2297 config.intended_node_role();
2298 }
2299 }
2300}
2301
2302#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
2305pub enum RunWithRange {
2306 Epoch(EpochId),
2307 Checkpoint(CheckpointSequenceNumber),
2308}
2309
2310impl RunWithRange {
2311 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
2313 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
2314 }
2315
2316 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
2317 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
2318 }
2319
2320 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
2321 match self {
2322 RunWithRange::Epoch(_) => None,
2323 RunWithRange::Checkpoint(seq) => Some(seq),
2324 }
2325 }
2326}