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")]
75 pub json_rpc_address: SocketAddr,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub rpc: Option<crate::RpcConfig>,
79
80 #[serde(default = "default_metrics_address")]
81 pub metrics_address: SocketAddr,
82 #[serde(default = "default_admin_interface_port")]
83 pub admin_interface_port: u16,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub consensus_config: Option<ConsensusConfig>,
87
88 #[serde(default, skip_serializing_if = "Option::is_none")]
92 pub fullnode_sync_mode: Option<FullNodeSyncMode>,
93
94 #[serde(default = "default_enable_index_processing")]
95 pub enable_index_processing: bool,
96
97 #[serde(default)]
102 pub sync_post_process_one_tx: bool,
103
104 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
105 pub remove_deprecated_tables: bool,
106
107 #[serde(default)]
108 pub jsonrpc_server_type: Option<ServerType>,
113
114 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
122 pub disable_json_rpc: bool,
123
124 #[serde(default)]
125 pub grpc_load_shed: Option<bool>,
126
127 #[serde(default = "default_concurrency_limit")]
128 pub grpc_concurrency_limit: Option<usize>,
129
130 #[serde(default)]
131 pub p2p_config: P2pConfig,
132
133 pub genesis: Genesis,
134
135 #[serde(default = "default_authority_store_pruning_config")]
136 pub authority_store_pruning_config: AuthorityStorePruningConfig,
137
138 #[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
142 pub end_of_epoch_broadcast_channel_capacity: usize,
143
144 #[serde(default)]
145 pub checkpoint_executor_config: CheckpointExecutorConfig,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub metrics: Option<MetricsConfig>,
149
150 #[serde(skip)]
154 pub supported_protocol_versions: Option<SupportedProtocolVersions>,
155
156 #[serde(default)]
157 pub db_checkpoint_config: DBCheckpointConfig,
158
159 #[serde(default)]
160 pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub name_service_package_address: Option<SuiAddress>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub name_service_registry_id: Option<ObjectID>,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub name_service_reverse_registry_id: Option<ObjectID>,
170
171 #[serde(default)]
172 pub transaction_deny_config: TransactionDenyConfig,
173
174 #[serde(default)]
178 pub peer_deny_sync_config: PeerDenySyncConfig,
179
180 #[serde(default)]
182 pub dev_inspect_disabled: bool,
183
184 #[serde(default)]
185 pub certificate_deny_config: CertificateDenyConfig,
186
187 #[serde(default)]
188 pub state_debug_dump_config: StateDebugDumpConfig,
189
190 #[serde(default)]
191 pub state_archive_read_config: Vec<StateArchiveConfig>,
192
193 #[serde(default)]
194 pub state_snapshot_write_config: StateSnapshotConfig,
195
196 #[serde(default)]
197 pub indexer_max_subscriptions: Option<usize>,
198
199 #[serde(default = "default_transaction_kv_store_config")]
200 pub transaction_kv_store_read_config: TransactionKeyValueStoreReadConfig,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub transaction_kv_store_write_config: Option<TransactionKeyValueStoreWriteConfig>,
204
205 #[serde(default = "default_jwk_fetch_interval_seconds")]
206 pub jwk_fetch_interval_seconds: u64,
207
208 #[serde(default = "default_zklogin_oauth_providers")]
209 pub zklogin_oauth_providers: BTreeMap<Chain, BTreeSet<String>>,
210
211 #[serde(default = "default_authority_overload_config")]
212 pub authority_overload_config: AuthorityOverloadConfig,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub run_with_range: Option<RunWithRange>,
216
217 #[serde(
219 skip_serializing_if = "Option::is_none",
220 default = "default_traffic_controller_policy_config"
221 )]
222 pub policy_config: Option<PolicyConfig>,
223
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub firewall_config: Option<RemoteFirewallConfig>,
226
227 #[serde(default)]
228 pub execution_cache: ExecutionCacheConfig,
229
230 #[serde(skip)]
232 #[serde(default = "bool_true")]
233 pub state_accumulator_v2: bool,
234
235 #[serde(skip)]
238 #[serde(default)]
239 pub funds_withdraw_scheduler_type: FundsWithdrawSchedulerType,
240
241 #[serde(default = "bool_true")]
242 pub enable_soft_bundle: bool,
243
244 #[serde(default = "bool_true")]
248 pub enable_simulate_allowed_proposers: bool,
249
250 #[serde(default)]
251 pub verifier_signing_config: VerifierSigningConfig,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
256 pub enable_db_write_stall: Option<bool>,
257
258 #[serde(skip_serializing_if = "Option::is_none")]
262 pub enable_db_sync_to_disk: Option<bool>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub execution_time_observer_config: Option<ExecutionTimeObserverConfig>,
266
267 #[serde(default, skip_serializing_if = "Option::is_none")]
270 pub recent_submission_dedup_window_ms: Option<u64>,
271
272 #[serde(skip_serializing_if = "Option::is_none")]
276 pub chain_override_for_testing: Option<Chain>,
277
278 #[serde(skip_serializing_if = "Option::is_none")]
281 pub validator_client_monitor_config: Option<ValidatorClientMonitorConfig>,
282
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub fork_recovery: Option<ForkRecoveryConfig>,
286
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub transaction_driver_config: Option<TransactionDriverConfig>,
290
291 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub consensus_transaction_pool: Option<ConsensusTransactionPoolConfig>,
296
297 #[serde(skip_serializing_if = "Option::is_none")]
300 pub congestion_log: Option<CongestionLogConfig>,
301
302 #[serde(skip_serializing_if = "Option::is_none")]
304 pub address_prober: Option<AddressProberConfig>,
305}
306
307#[derive(Clone, Debug, Deserialize, Serialize)]
308#[serde(rename_all = "kebab-case")]
309pub struct TransactionDriverConfig {
310 #[serde(default, skip_serializing_if = "Vec::is_empty")]
313 pub allowed_submission_validators: Vec<String>,
314
315 #[serde(default, skip_serializing_if = "Vec::is_empty")]
318 pub blocked_submission_validators: Vec<String>,
319
320 #[serde(default = "bool_true")]
325 pub enable_early_validation: bool,
326}
327
328impl Default for TransactionDriverConfig {
329 fn default() -> Self {
330 Self {
331 allowed_submission_validators: vec![],
332 blocked_submission_validators: vec![],
333 enable_early_validation: true,
334 }
335 }
336}
337
338#[derive(Clone, Debug, Default, Deserialize, Serialize)]
339#[serde(rename_all = "kebab-case")]
340pub struct ConsensusTransactionPoolConfig {
341 pub max_pending_transactions: Option<usize>,
345}
346
347impl ConsensusTransactionPoolConfig {
348 pub fn max_pending_transactions(&self, consensus_config: &ConsensusConfig) -> usize {
349 self.max_pending_transactions
350 .unwrap_or_else(|| consensus_config.max_pending_transactions())
351 }
352}
353
354#[derive(Clone, Debug, Deserialize, Serialize)]
355#[serde(rename_all = "kebab-case")]
356pub struct CongestionLogConfig {
357 pub path: PathBuf,
358 #[serde(default = "default_congestion_log_max_file_size")]
359 pub max_file_size: u64,
360 #[serde(default = "default_congestion_log_max_files")]
361 pub max_files: u32,
362}
363
364fn default_congestion_log_max_file_size() -> u64 {
365 100 * 1024 * 1024 }
367
368fn default_congestion_log_max_files() -> u32 {
369 10
370}
371
372#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
373#[serde(rename_all = "kebab-case")]
374pub enum ForkCrashBehavior {
375 #[serde(rename = "recover-once-per-version")]
384 #[default]
385 RecoverOncePerVersion,
386
387 #[serde(rename = "await-fork-recovery")]
390 AwaitForkRecovery,
391
392 #[serde(rename = "return-error")]
394 ReturnError,
395}
396
397#[derive(Clone, Debug, Default, Deserialize, Serialize)]
398#[serde(rename_all = "kebab-case")]
399pub struct ForkRecoveryConfig {
400 #[serde(default)]
403 pub transaction_overrides: BTreeMap<String, String>,
404
405 #[serde(default)]
409 pub checkpoint_overrides: BTreeMap<u64, String>,
410
411 #[serde(default)]
413 pub fork_crash_behavior: ForkCrashBehavior,
414}
415
416#[derive(Clone, Debug, Default, Deserialize, Serialize)]
420#[serde(rename_all = "kebab-case")]
421pub struct AddressProberConfig {
422 pub enabled: Option<bool>,
426
427 pub good_interval: Option<Duration>,
431
432 pub failed_interval: Option<Duration>,
437
438 pub failure_threshold: Option<u32>,
443
444 pub concurrency: Option<usize>,
448
449 pub consensus_probe_timeout: Option<Duration>,
453}
454
455impl AddressProberConfig {
456 pub fn enabled(&self) -> bool {
457 self.enabled.unwrap_or(true)
458 }
459
460 pub fn good_interval(&self) -> Duration {
461 self.good_interval.unwrap_or(Duration::from_secs(60 * 60))
462 }
463
464 pub fn failed_interval(&self) -> Duration {
465 self.failed_interval.unwrap_or(Duration::from_secs(60))
466 }
467
468 pub fn failure_threshold(&self) -> u32 {
469 self.failure_threshold.unwrap_or(3)
470 }
471
472 pub fn concurrency(&self) -> usize {
473 self.concurrency.unwrap_or(16)
474 }
475
476 pub fn consensus_probe_timeout(&self) -> Duration {
477 self.consensus_probe_timeout
478 .unwrap_or(Duration::from_secs(10))
479 }
480
481 pub fn validate(&self) -> anyhow::Result<()> {
482 anyhow::ensure!(
483 self.failed_interval() <= self.good_interval(),
484 "address prober failed_interval ({:?}) must be <= good_interval ({:?})",
485 self.failed_interval(),
486 self.good_interval(),
487 );
488 Ok(())
489 }
490}
491
492#[derive(Clone, Debug, Default, Deserialize, Serialize)]
493#[serde(rename_all = "kebab-case")]
494pub struct ExecutionTimeObserverConfig {
495 pub observation_channel_capacity: Option<NonZeroUsize>,
499
500 pub observation_cache_size: Option<NonZeroUsize>,
504
505 pub object_debt_channel_capacity: Option<NonZeroUsize>,
509
510 pub object_utilization_cache_size: Option<NonZeroUsize>,
514
515 pub report_object_utilization_metric_with_full_id: Option<bool>,
526
527 pub observation_sharing_object_utilization_threshold: Option<Duration>,
532
533 pub observation_sharing_diff_threshold: Option<f64>,
538
539 pub observation_sharing_min_interval: Option<Duration>,
543
544 pub observation_sharing_rate_limit: Option<NonZeroU32>,
549
550 pub observation_sharing_burst_limit: Option<NonZeroU32>,
554
555 pub enable_gas_price_weighting: Option<bool>,
562
563 pub weighted_moving_average_window_size: Option<usize>,
570
571 #[cfg(msim)]
577 pub inject_synthetic_execution_time: Option<bool>,
578}
579
580impl ExecutionTimeObserverConfig {
581 pub fn observation_channel_capacity(&self) -> NonZeroUsize {
582 self.observation_channel_capacity
583 .unwrap_or(nonzero!(1_024usize))
584 }
585
586 pub fn observation_cache_size(&self) -> NonZeroUsize {
587 self.observation_cache_size.unwrap_or(nonzero!(10_000usize))
588 }
589
590 pub fn object_debt_channel_capacity(&self) -> NonZeroUsize {
591 self.object_debt_channel_capacity
592 .unwrap_or(nonzero!(128usize))
593 }
594
595 pub fn object_utilization_cache_size(&self) -> NonZeroUsize {
596 self.object_utilization_cache_size
597 .unwrap_or(nonzero!(50_000usize))
598 }
599
600 pub fn report_object_utilization_metric_with_full_id(&self) -> bool {
601 self.report_object_utilization_metric_with_full_id
602 .unwrap_or(false)
603 }
604
605 pub fn observation_sharing_object_utilization_threshold(&self) -> Duration {
606 self.observation_sharing_object_utilization_threshold
607 .unwrap_or(Duration::from_millis(500))
608 }
609
610 pub fn observation_sharing_diff_threshold(&self) -> f64 {
611 self.observation_sharing_diff_threshold.unwrap_or(0.1)
612 }
613
614 pub fn observation_sharing_min_interval(&self) -> Duration {
615 self.observation_sharing_min_interval
616 .unwrap_or(Duration::from_secs(5))
617 }
618
619 pub fn observation_sharing_rate_limit(&self) -> NonZeroU32 {
620 self.observation_sharing_rate_limit
621 .unwrap_or(nonzero!(10u32))
622 }
623
624 pub fn observation_sharing_burst_limit(&self) -> NonZeroU32 {
625 self.observation_sharing_burst_limit
626 .unwrap_or(nonzero!(100u32))
627 }
628
629 pub fn enable_gas_price_weighting(&self) -> bool {
630 self.enable_gas_price_weighting.unwrap_or(false)
631 }
632
633 pub fn weighted_moving_average_window_size(&self) -> usize {
634 self.weighted_moving_average_window_size.unwrap_or(20)
635 }
636
637 #[cfg(msim)]
638 pub fn inject_synthetic_execution_time(&self) -> bool {
639 self.inject_synthetic_execution_time.unwrap_or(false)
640 }
641}
642
643#[allow(clippy::large_enum_variant)]
644#[derive(Clone, Debug, Deserialize, Serialize)]
645#[serde(rename_all = "kebab-case")]
646pub enum ExecutionCacheConfig {
647 PassthroughCache,
648 WritebackCache {
649 max_cache_size: Option<u64>,
652
653 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>,
669
670 backpressure_threshold_for_rpc: Option<u64>,
673 },
674}
675
676impl Default for ExecutionCacheConfig {
677 fn default() -> Self {
678 ExecutionCacheConfig::WritebackCache {
679 max_cache_size: None,
680 backpressure_threshold: None,
681 backpressure_threshold_for_rpc: None,
682 package_cache_size: None,
683 object_cache_size: None,
684 marker_cache_size: None,
685 object_by_id_cache_size: None,
686 transaction_cache_size: None,
687 executed_effect_cache_size: None,
688 effect_cache_size: None,
689 events_cache_size: None,
690 transaction_objects_cache_size: None,
691 }
692 }
693}
694
695impl ExecutionCacheConfig {
696 pub fn max_cache_size(&self) -> u64 {
697 std::env::var("SUI_MAX_CACHE_SIZE")
698 .ok()
699 .and_then(|s| s.parse().ok())
700 .unwrap_or_else(|| match self {
701 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
702 ExecutionCacheConfig::WritebackCache { max_cache_size, .. } => {
703 max_cache_size.unwrap_or(100000)
704 }
705 })
706 }
707
708 pub fn package_cache_size(&self) -> u64 {
709 std::env::var("SUI_PACKAGE_CACHE_SIZE")
710 .ok()
711 .and_then(|s| s.parse().ok())
712 .unwrap_or_else(|| match self {
713 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
714 ExecutionCacheConfig::WritebackCache {
715 package_cache_size, ..
716 } => package_cache_size.unwrap_or(1000),
717 })
718 }
719
720 pub fn object_cache_size(&self) -> u64 {
721 std::env::var("SUI_OBJECT_CACHE_SIZE")
722 .ok()
723 .and_then(|s| s.parse().ok())
724 .unwrap_or_else(|| match self {
725 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
726 ExecutionCacheConfig::WritebackCache {
727 object_cache_size, ..
728 } => object_cache_size.unwrap_or(self.max_cache_size()),
729 })
730 }
731
732 pub fn marker_cache_size(&self) -> u64 {
733 std::env::var("SUI_MARKER_CACHE_SIZE")
734 .ok()
735 .and_then(|s| s.parse().ok())
736 .unwrap_or_else(|| match self {
737 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
738 ExecutionCacheConfig::WritebackCache {
739 marker_cache_size, ..
740 } => marker_cache_size.unwrap_or(self.object_cache_size()),
741 })
742 }
743
744 pub fn object_by_id_cache_size(&self) -> u64 {
745 std::env::var("SUI_OBJECT_BY_ID_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 object_by_id_cache_size,
752 ..
753 } => object_by_id_cache_size.unwrap_or(self.object_cache_size()),
754 })
755 }
756
757 pub fn transaction_cache_size(&self) -> u64 {
758 std::env::var("SUI_TRANSACTION_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 transaction_cache_size,
765 ..
766 } => transaction_cache_size.unwrap_or(self.max_cache_size()),
767 })
768 }
769
770 pub fn executed_effect_cache_size(&self) -> u64 {
771 std::env::var("SUI_EXECUTED_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 executed_effect_cache_size,
778 ..
779 } => executed_effect_cache_size.unwrap_or(self.transaction_cache_size()),
780 })
781 }
782
783 pub fn effect_cache_size(&self) -> u64 {
784 std::env::var("SUI_EFFECT_CACHE_SIZE")
785 .ok()
786 .and_then(|s| s.parse().ok())
787 .unwrap_or_else(|| match self {
788 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
789 ExecutionCacheConfig::WritebackCache {
790 effect_cache_size, ..
791 } => effect_cache_size.unwrap_or(self.executed_effect_cache_size()),
792 })
793 }
794
795 pub fn events_cache_size(&self) -> u64 {
796 std::env::var("SUI_EVENTS_CACHE_SIZE")
797 .ok()
798 .and_then(|s| s.parse().ok())
799 .unwrap_or_else(|| match self {
800 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
801 ExecutionCacheConfig::WritebackCache {
802 events_cache_size, ..
803 } => events_cache_size.unwrap_or(self.transaction_cache_size()),
804 })
805 }
806
807 pub fn transaction_objects_cache_size(&self) -> u64 {
808 std::env::var("SUI_TRANSACTION_OBJECTS_CACHE_SIZE")
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 transaction_objects_cache_size,
815 ..
816 } => transaction_objects_cache_size.unwrap_or(1000),
817 })
818 }
819
820 pub fn backpressure_threshold(&self) -> u64 {
821 std::env::var("SUI_BACKPRESSURE_THRESHOLD")
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,
828 ..
829 } => backpressure_threshold.unwrap_or(100_000),
830 })
831 }
832
833 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
834 std::env::var("SUI_BACKPRESSURE_THRESHOLD_FOR_RPC")
835 .ok()
836 .and_then(|s| s.parse().ok())
837 .unwrap_or_else(|| match self {
838 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
839 ExecutionCacheConfig::WritebackCache {
840 backpressure_threshold_for_rpc,
841 ..
842 } => backpressure_threshold_for_rpc.unwrap_or(self.backpressure_threshold()),
843 })
844 }
845}
846
847#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
848#[serde(rename_all = "lowercase")]
849pub enum ServerType {
850 WebSocket,
851 Http,
852 Both,
853}
854
855#[derive(Clone, Debug, Deserialize, Serialize)]
856#[serde(rename_all = "kebab-case")]
857pub struct TransactionKeyValueStoreReadConfig {
858 #[serde(default = "default_base_url")]
859 pub base_url: String,
860
861 #[serde(default = "default_cache_size")]
862 pub cache_size: u64,
863}
864
865impl Default for TransactionKeyValueStoreReadConfig {
866 fn default() -> Self {
867 Self {
868 base_url: default_base_url(),
869 cache_size: default_cache_size(),
870 }
871 }
872}
873
874fn default_base_url() -> String {
875 "https://transactions.sui.io/".to_string()
876}
877
878fn default_cache_size() -> u64 {
879 100_000
880}
881
882fn default_jwk_fetch_interval_seconds() -> u64 {
883 3600
884}
885
886pub fn default_zklogin_oauth_providers() -> BTreeMap<Chain, BTreeSet<String>> {
887 let mut map = BTreeMap::new();
888
889 let experimental_providers = BTreeSet::from([
891 "Google".to_string(),
892 "Facebook".to_string(),
893 "Twitch".to_string(),
894 "Kakao".to_string(),
895 "Apple".to_string(),
896 "Slack".to_string(),
897 "TestIssuer".to_string(),
898 "TestIssuerKey8192".to_string(),
899 "Microsoft".to_string(),
900 "KarrierOne".to_string(),
901 "Credenza3".to_string(),
902 "Playtron".to_string(),
903 "Threedos".to_string(),
904 "Onefc".to_string(),
905 "FanTV".to_string(),
906 "Arden".to_string(), "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "EveFrontier".to_string(),
909 "TestEveFrontier".to_string(),
910 "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(), ]);
914
915 let providers = BTreeSet::from([
917 "Google".to_string(),
918 "Facebook".to_string(),
919 "Twitch".to_string(),
920 "Apple".to_string(),
921 "KarrierOne".to_string(),
922 "Credenza3".to_string(),
923 "Playtron".to_string(),
924 "Onefc".to_string(),
925 "Threedos".to_string(),
926 "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "Arden".to_string(),
928 "FanTV".to_string(),
929 "EveFrontier".to_string(),
930 "TestEveFrontier".to_string(),
931 "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(), ]);
935 map.insert(Chain::Mainnet, providers.clone());
936 map.insert(Chain::Testnet, providers);
937 map.insert(Chain::Unknown, experimental_providers);
938 map
939}
940
941fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
942 TransactionKeyValueStoreReadConfig::default()
943}
944
945fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
946 AuthorityStorePruningConfig::default()
947}
948
949pub fn default_enable_index_processing() -> bool {
950 true
951}
952
953fn default_grpc_address() -> Multiaddr {
954 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
955}
956fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
957 AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
958}
959
960fn default_key_pair() -> KeyPairWithPath {
961 KeyPairWithPath::new(
962 get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
963 .1
964 .into(),
965 )
966}
967
968fn default_metrics_address() -> SocketAddr {
969 use std::net::{IpAddr, Ipv4Addr};
970 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
971}
972
973pub fn default_admin_interface_port() -> u16 {
974 1337
975}
976
977pub fn default_json_rpc_address() -> SocketAddr {
978 use std::net::{IpAddr, Ipv4Addr};
979 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
980}
981
982pub fn default_concurrency_limit() -> Option<usize> {
983 Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
984}
985
986pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
987 128
988}
989
990pub fn bool_true() -> bool {
991 true
992}
993
994fn is_true(value: &bool) -> bool {
995 *value
996}
997
998impl Config for NodeConfig {}
999
1000impl NodeConfig {
1001 pub fn protocol_key_pair(&self) -> &AuthorityKeyPair {
1002 self.protocol_key_pair.authority_keypair()
1003 }
1004
1005 pub fn recent_submission_dedup_window(&self) -> Duration {
1008 Duration::from_millis(self.recent_submission_dedup_window_ms.unwrap_or(1000))
1009 }
1010
1011 pub fn worker_key_pair(&self) -> &NetworkKeyPair {
1012 match self.worker_key_pair.keypair() {
1013 SuiKeyPair::Ed25519(kp) => kp,
1014 other => panic!(
1015 "Invalid keypair type: {:?}, only Ed25519 is allowed for worker key",
1016 other
1017 ),
1018 }
1019 }
1020
1021 pub fn network_key_pair(&self) -> &NetworkKeyPair {
1022 match self.network_key_pair.keypair() {
1023 SuiKeyPair::Ed25519(kp) => kp,
1024 other => panic!(
1025 "Invalid keypair type: {:?}, only Ed25519 is allowed for network key",
1026 other
1027 ),
1028 }
1029 }
1030
1031 pub fn protocol_public_key(&self) -> AuthorityPublicKeyBytes {
1032 self.protocol_key_pair().public().into()
1033 }
1034
1035 pub fn db_path(&self) -> PathBuf {
1036 self.db_path.join("live")
1037 }
1038
1039 pub fn db_checkpoint_path(&self) -> PathBuf {
1040 self.db_path.join("db_checkpoints")
1041 }
1042
1043 pub fn db_store_path(&self) -> PathBuf {
1044 self.db_path().join("store")
1045 }
1046
1047 pub fn archive_path(&self) -> PathBuf {
1048 self.db_path.join("archive")
1049 }
1050
1051 pub fn snapshot_path(&self) -> PathBuf {
1052 self.db_path.join("snapshot")
1053 }
1054
1055 pub fn network_address(&self) -> &Multiaddr {
1056 &self.network_address
1057 }
1058
1059 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
1060 self.consensus_config.as_ref()
1061 }
1062
1063 pub fn intended_node_role(&self) -> NodeRole {
1068 let has_consensus_config = self.consensus_config.is_some();
1069
1070 match (self.fullnode_sync_mode, has_consensus_config) {
1071 (Some(FullNodeSyncMode::ConsensusObserver), _) => {
1072 assert!(
1073 self.has_observer_config_peers(),
1074 "Observer peers must be configured when sync mode is ConsensusObserver"
1075 );
1076 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
1077 }
1078 (Some(FullNodeSyncMode::StateSyncOnly), true) => {
1079 panic!("Consensus config should not be set for a StateSyncOnly full node");
1080 }
1081 (Some(FullNodeSyncMode::StateSyncOnly), false) => {
1082 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
1083 }
1084 (None, false) => NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly),
1085 (None, true) => NodeRole::Validator,
1086 }
1087 }
1088
1089 pub fn has_observer_config_peers(&self) -> bool {
1090 self.consensus_config
1091 .as_ref()
1092 .and_then(|c| c.parameters.as_ref())
1093 .map(|p| !p.observer.peers.is_empty())
1094 .unwrap_or(false)
1095 }
1096
1097 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1098 self.genesis.genesis()
1099 }
1100
1101 pub fn sui_address(&self) -> SuiAddress {
1102 (&self.account_key_pair.keypair().public()).into()
1103 }
1104
1105 pub fn archive_reader_config(&self) -> Option<ArchiveReaderConfig> {
1106 self.state_archive_read_config
1107 .first()
1108 .map(|config| ArchiveReaderConfig {
1109 ingestion_url: config.ingestion_url.clone(),
1110 remote_store_options: config.remote_store_options.clone(),
1111 remote_store_headers: config.remote_store_headers.clone(),
1112 download_concurrency: NonZeroUsize::new(config.concurrency)
1113 .unwrap_or(NonZeroUsize::new(5).unwrap()),
1114 remote_store_config: ObjectStoreConfig::default(),
1115 })
1116 }
1117
1118 pub fn jsonrpc_server_type(&self) -> ServerType {
1119 self.jsonrpc_server_type.unwrap_or(ServerType::Http)
1120 }
1121
1122 pub fn json_rpc_enabled(&self) -> bool {
1126 !self.disable_json_rpc
1127 }
1128
1129 pub fn rpc(&self) -> Option<&crate::RpcConfig> {
1130 self.rpc.as_ref()
1131 }
1132}
1133
1134#[derive(Debug, Clone, Deserialize, Serialize)]
1135pub enum ConsensusProtocol {
1136 #[serde(rename = "narwhal")]
1137 Narwhal,
1138 #[serde(rename = "mysticeti")]
1139 Mysticeti,
1140}
1141
1142#[derive(Debug, Clone, Deserialize, Serialize)]
1143#[serde(rename_all = "kebab-case")]
1144pub struct ConsensusConfig {
1145 pub db_path: PathBuf,
1147
1148 pub db_retention_epochs: Option<u64>,
1151
1152 pub db_pruner_period_secs: Option<u64>,
1155
1156 pub max_pending_transactions: Option<usize>,
1160
1161 pub parameters: Option<ConsensusParameters>,
1162
1163 #[serde(skip_serializing_if = "Option::is_none")]
1167 pub listen_address: Option<Multiaddr>,
1168
1169 #[serde(skip_serializing_if = "Option::is_none")]
1174 pub external_address: Option<Multiaddr>,
1175}
1176
1177impl ConsensusConfig {
1178 pub fn db_path(&self) -> &Path {
1179 &self.db_path
1180 }
1181
1182 pub fn max_pending_transactions(&self) -> usize {
1183 self.max_pending_transactions.unwrap_or(20_000)
1184 }
1185
1186 pub fn db_retention_epochs(&self) -> u64 {
1187 self.db_retention_epochs.unwrap_or(0)
1188 }
1189
1190 pub fn db_pruner_period(&self) -> Duration {
1191 self.db_pruner_period_secs
1193 .map(Duration::from_secs)
1194 .unwrap_or(Duration::from_secs(3_600))
1195 }
1196}
1197
1198#[derive(Clone, Debug, Deserialize, Serialize)]
1199#[serde(rename_all = "kebab-case")]
1200pub struct CheckpointExecutorConfig {
1201 #[serde(default = "default_checkpoint_execution_max_concurrency")]
1205 pub checkpoint_execution_max_concurrency: usize,
1206
1207 #[serde(default = "default_local_execution_timeout_sec")]
1213 pub local_execution_timeout_sec: u64,
1214
1215 #[serde(default, skip_serializing_if = "Option::is_none")]
1218 pub data_ingestion_dir: Option<PathBuf>,
1219}
1220
1221#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1222#[serde(rename_all = "kebab-case")]
1223pub struct ExpensiveSafetyCheckConfig {
1224 #[serde(default)]
1229 enable_epoch_sui_conservation_check: bool,
1230
1231 #[serde(default)]
1235 enable_deep_per_tx_sui_conservation_check: bool,
1236
1237 #[serde(default)]
1239 force_disable_epoch_sui_conservation_check: bool,
1240
1241 #[serde(default)]
1244 enable_state_consistency_check: bool,
1245
1246 #[serde(default)]
1248 force_disable_state_consistency_check: bool,
1249
1250 #[serde(default)]
1251 enable_secondary_index_checks: bool,
1252 }
1254
1255impl ExpensiveSafetyCheckConfig {
1256 pub fn new_enable_all() -> Self {
1257 Self {
1258 enable_epoch_sui_conservation_check: true,
1259 enable_deep_per_tx_sui_conservation_check: true,
1260 force_disable_epoch_sui_conservation_check: false,
1261 enable_state_consistency_check: true,
1262 force_disable_state_consistency_check: false,
1263 enable_secondary_index_checks: false, }
1265 }
1266
1267 pub fn new_enable_all_with_secondary_index_checks() -> Self {
1268 Self {
1269 enable_secondary_index_checks: true,
1270 ..Self::new_enable_all()
1271 }
1272 }
1273
1274 pub fn new_disable_all() -> Self {
1275 Self {
1276 enable_epoch_sui_conservation_check: false,
1277 enable_deep_per_tx_sui_conservation_check: false,
1278 force_disable_epoch_sui_conservation_check: true,
1279 enable_state_consistency_check: false,
1280 force_disable_state_consistency_check: true,
1281 enable_secondary_index_checks: false,
1282 }
1283 }
1284
1285 pub fn force_disable_epoch_sui_conservation_check(&mut self) {
1286 self.force_disable_epoch_sui_conservation_check = true;
1287 }
1288
1289 pub fn enable_epoch_sui_conservation_check(&self) -> bool {
1290 (self.enable_epoch_sui_conservation_check || cfg!(debug_assertions))
1291 && !self.force_disable_epoch_sui_conservation_check
1292 }
1293
1294 pub fn force_disable_state_consistency_check(&mut self) {
1295 self.force_disable_state_consistency_check = true;
1296 }
1297
1298 pub fn enable_state_consistency_check(&self) -> bool {
1299 (self.enable_state_consistency_check || cfg!(debug_assertions))
1300 && !self.force_disable_state_consistency_check
1301 }
1302
1303 pub fn enable_deep_per_tx_sui_conservation_check(&self) -> bool {
1304 self.enable_deep_per_tx_sui_conservation_check || cfg!(debug_assertions)
1305 }
1306
1307 pub fn enable_secondary_index_checks(&self) -> bool {
1308 self.enable_secondary_index_checks
1309 }
1310}
1311
1312fn default_checkpoint_execution_max_concurrency() -> usize {
1313 4
1314}
1315
1316fn default_local_execution_timeout_sec() -> u64 {
1317 30
1318}
1319
1320impl Default for CheckpointExecutorConfig {
1321 fn default() -> Self {
1322 Self {
1323 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
1324 local_execution_timeout_sec: default_local_execution_timeout_sec(),
1325 data_ingestion_dir: None,
1326 }
1327 }
1328}
1329
1330#[derive(Debug, Clone, Deserialize, Serialize)]
1331#[serde(rename_all = "kebab-case")]
1332pub struct AuthorityStorePruningConfig {
1333 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1335 pub num_latest_epoch_dbs_to_retain: usize,
1336 #[serde(default = "default_epoch_db_pruning_period_secs")]
1338 pub epoch_db_pruning_period_secs: u64,
1339 #[serde(default)]
1344 pub num_epochs_to_retain: u64,
1345 #[serde(skip_serializing_if = "Option::is_none")]
1347 pub pruning_run_delay_seconds: Option<u64>,
1348 #[serde(default = "default_max_checkpoints_in_batch")]
1350 pub max_checkpoints_in_batch: usize,
1351 #[serde(default = "default_max_transactions_in_batch")]
1353 pub max_transactions_in_batch: usize,
1354 #[serde(
1358 default = "default_periodic_compaction_threshold_days",
1359 skip_serializing_if = "Option::is_none"
1360 )]
1361 pub periodic_compaction_threshold_days: Option<usize>,
1362 #[serde(default, skip_serializing_if = "Option::is_none")]
1372 pub rpc_store_bitmap_periodic_compaction_days: Option<u64>,
1373 #[serde(skip_serializing_if = "Option::is_none")]
1375 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1376 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1378 pub killswitch_tombstone_pruning: bool,
1379 #[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
1380 pub smooth: bool,
1381 #[serde(skip_serializing_if = "Option::is_none")]
1382 pub num_epochs_to_retain_for_indexes: Option<u64>,
1383}
1384
1385fn default_num_latest_epoch_dbs_to_retain() -> usize {
1386 3
1387}
1388
1389fn default_epoch_db_pruning_period_secs() -> u64 {
1390 3600
1391}
1392
1393fn default_max_transactions_in_batch() -> usize {
1394 1000
1395}
1396
1397fn default_max_checkpoints_in_batch() -> usize {
1398 10
1399}
1400
1401fn default_smoothing() -> bool {
1402 cfg!(not(test))
1403}
1404
1405fn default_periodic_compaction_threshold_days() -> Option<usize> {
1406 Some(1)
1407}
1408
1409impl Default for AuthorityStorePruningConfig {
1410 fn default() -> Self {
1411 Self {
1412 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1413 epoch_db_pruning_period_secs: default_epoch_db_pruning_period_secs(),
1414 num_epochs_to_retain: 0,
1415 pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
1416 max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
1417 max_transactions_in_batch: default_max_transactions_in_batch(),
1418 periodic_compaction_threshold_days: None,
1419 rpc_store_bitmap_periodic_compaction_days: None,
1420 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1421 killswitch_tombstone_pruning: false,
1422 smooth: true,
1423 num_epochs_to_retain_for_indexes: None,
1424 }
1425 }
1426}
1427
1428impl AuthorityStorePruningConfig {
1429 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1430 self.num_epochs_to_retain = num_epochs_to_retain;
1431 }
1432
1433 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1434 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1435 }
1436
1437 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1438 self.num_epochs_to_retain_for_checkpoints
1439 .map(|n| {
1441 if n < 2 {
1442 info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
1443 2
1444 } else {
1445 n
1446 }
1447 })
1448 }
1449
1450 pub fn set_killswitch_tombstone_pruning(&mut self, killswitch_tombstone_pruning: bool) {
1451 self.killswitch_tombstone_pruning = killswitch_tombstone_pruning;
1452 }
1453}
1454
1455#[derive(Debug, Clone, Deserialize, Serialize)]
1456#[serde(rename_all = "kebab-case")]
1457pub struct MetricsConfig {
1458 #[serde(skip_serializing_if = "Option::is_none")]
1459 pub push_interval_seconds: Option<u64>,
1460 #[serde(skip_serializing_if = "Option::is_none")]
1461 pub push_url: Option<String>,
1462}
1463
1464#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1465#[serde(rename_all = "kebab-case")]
1466pub struct DBCheckpointConfig {
1467 #[serde(default)]
1468 pub perform_db_checkpoints_at_epoch_end: bool,
1469 #[serde(skip_serializing_if = "Option::is_none")]
1470 pub checkpoint_path: Option<PathBuf>,
1471 #[serde(skip_serializing_if = "Option::is_none")]
1472 pub object_store_config: Option<ObjectStoreConfig>,
1473 #[serde(skip_serializing_if = "Option::is_none")]
1474 pub perform_index_db_checkpoints_at_epoch_end: Option<bool>,
1475 #[serde(skip_serializing_if = "Option::is_none")]
1476 pub prune_and_compact_before_upload: Option<bool>,
1477}
1478
1479#[derive(Debug, Clone)]
1480pub struct ArchiveReaderConfig {
1481 pub remote_store_config: ObjectStoreConfig,
1482 pub download_concurrency: NonZeroUsize,
1483 pub ingestion_url: Option<String>,
1484 pub remote_store_options: Vec<(String, String)>,
1485 pub remote_store_headers: Vec<(String, String)>,
1486}
1487
1488#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1489#[serde(rename_all = "kebab-case")]
1490pub struct StateArchiveConfig {
1491 #[serde(skip_serializing_if = "Option::is_none")]
1492 pub object_store_config: Option<ObjectStoreConfig>,
1493 pub concurrency: usize,
1494 #[serde(skip_serializing_if = "Option::is_none")]
1495 pub ingestion_url: Option<String>,
1496 #[serde(
1497 skip_serializing_if = "Vec::is_empty",
1498 default,
1499 deserialize_with = "deserialize_remote_store_options"
1500 )]
1501 pub remote_store_options: Vec<(String, String)>,
1502 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1506 pub remote_store_headers: Vec<(String, String)>,
1507}
1508
1509#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1510#[serde(rename_all = "kebab-case")]
1511pub struct StateSnapshotConfig {
1512 #[serde(skip_serializing_if = "Option::is_none")]
1513 pub object_store_config: Option<ObjectStoreConfig>,
1514 pub concurrency: usize,
1515 #[serde(default)]
1519 pub archive_interval_epochs: u64,
1520}
1521
1522#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1523#[serde(rename_all = "kebab-case")]
1524pub struct TransactionKeyValueStoreWriteConfig {
1525 pub aws_access_key_id: String,
1526 pub aws_secret_access_key: String,
1527 pub aws_region: String,
1528 pub table_name: String,
1529 pub bucket_name: String,
1530 pub concurrency: usize,
1531}
1532
1533#[derive(Clone, Debug, Deserialize, Serialize)]
1538#[serde(rename_all = "kebab-case")]
1539pub struct AuthorityOverloadConfig {
1540 #[serde(default = "default_max_txn_age_in_queue")]
1541 pub max_txn_age_in_queue: Duration,
1542
1543 #[serde(default = "default_overload_monitor_interval")]
1545 pub overload_monitor_interval: Duration,
1546
1547 #[serde(default = "default_execution_queue_latency_soft_limit")]
1549 pub execution_queue_latency_soft_limit: Duration,
1550
1551 #[serde(default = "default_execution_queue_latency_hard_limit")]
1553 pub execution_queue_latency_hard_limit: Duration,
1554
1555 #[serde(default = "default_max_load_shedding_percentage")]
1557 pub max_load_shedding_percentage: u32,
1558
1559 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1562 pub min_load_shedding_percentage_above_hard_limit: u32,
1563
1564 #[serde(default = "default_safe_transaction_ready_rate")]
1567 pub safe_transaction_ready_rate: u32,
1568
1569 #[serde(default = "default_check_system_overload_at_signing")]
1572 pub check_system_overload_at_signing: bool,
1573
1574 #[serde(default = "default_max_transaction_manager_queue_length")]
1577 pub max_transaction_manager_queue_length: usize,
1578
1579 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1582 pub max_transaction_manager_per_object_queue_length: usize,
1583
1584 #[serde(default = "default_admission_queue_capacity_fraction")]
1588 pub admission_queue_capacity_fraction: f64,
1589
1590 #[serde(default = "default_admission_queue_enabled")]
1595 pub admission_queue_enabled: bool,
1596
1597 #[serde(default = "default_admission_queue_failover_timeout")]
1602 pub admission_queue_failover_timeout: Duration,
1603}
1604
1605fn default_max_txn_age_in_queue() -> Duration {
1606 Duration::from_millis(1000)
1607}
1608
1609fn default_overload_monitor_interval() -> Duration {
1610 Duration::from_secs(10)
1611}
1612
1613fn default_execution_queue_latency_soft_limit() -> Duration {
1614 Duration::from_secs(1)
1615}
1616
1617fn default_execution_queue_latency_hard_limit() -> Duration {
1618 Duration::from_secs(10)
1619}
1620
1621fn default_max_load_shedding_percentage() -> u32 {
1622 95
1623}
1624
1625fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1626 50
1627}
1628
1629fn default_safe_transaction_ready_rate() -> u32 {
1630 100
1631}
1632
1633fn default_check_system_overload_at_signing() -> bool {
1634 true
1635}
1636
1637fn default_max_transaction_manager_queue_length() -> usize {
1638 100_000
1639}
1640
1641fn default_max_transaction_manager_per_object_queue_length() -> usize {
1642 2000
1643}
1644
1645fn default_admission_queue_capacity_fraction() -> f64 {
1646 0.5
1647}
1648
1649fn default_admission_queue_enabled() -> bool {
1650 true
1651}
1652
1653fn default_admission_queue_failover_timeout() -> Duration {
1654 Duration::from_secs(30)
1655}
1656
1657impl Default for AuthorityOverloadConfig {
1658 fn default() -> Self {
1659 Self {
1660 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1661 overload_monitor_interval: default_overload_monitor_interval(),
1662 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1663 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1664 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1665 min_load_shedding_percentage_above_hard_limit:
1666 default_min_load_shedding_percentage_above_hard_limit(),
1667 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1668 check_system_overload_at_signing: true,
1669 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1670 max_transaction_manager_per_object_queue_length:
1671 default_max_transaction_manager_per_object_queue_length(),
1672 admission_queue_capacity_fraction: default_admission_queue_capacity_fraction(),
1673 admission_queue_enabled: default_admission_queue_enabled(),
1674 admission_queue_failover_timeout: default_admission_queue_failover_timeout(),
1675 }
1676 }
1677}
1678
1679fn default_authority_overload_config() -> AuthorityOverloadConfig {
1680 AuthorityOverloadConfig::default()
1681}
1682
1683fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1684 Some(PolicyConfig::default_dos_protection_policy())
1685}
1686
1687#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1688pub struct Genesis {
1689 #[serde(flatten)]
1690 location: GenesisLocation,
1691
1692 #[serde(skip)]
1693 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1694}
1695
1696impl Genesis {
1697 pub fn new(genesis: genesis::Genesis) -> Self {
1698 Self {
1699 location: GenesisLocation::InPlace { genesis },
1700 genesis: Default::default(),
1701 }
1702 }
1703
1704 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1705 Self {
1706 location: GenesisLocation::File {
1707 genesis_file_location: path.into(),
1708 },
1709 genesis: Default::default(),
1710 }
1711 }
1712
1713 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1714 match &self.location {
1715 GenesisLocation::InPlace { genesis } => Ok(genesis),
1716 GenesisLocation::File {
1717 genesis_file_location,
1718 } => self
1719 .genesis
1720 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1721 }
1722 }
1723}
1724
1725#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1726#[serde(untagged)]
1727#[allow(clippy::large_enum_variant)]
1728enum GenesisLocation {
1729 InPlace {
1730 genesis: genesis::Genesis,
1731 },
1732 File {
1733 #[serde(rename = "genesis-file-location")]
1734 genesis_file_location: PathBuf,
1735 },
1736}
1737
1738#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1740pub struct KeyPairWithPath {
1741 #[serde(flatten)]
1742 location: KeyPairLocation,
1743
1744 #[serde(skip)]
1745 keypair: OnceCell<Arc<SuiKeyPair>>,
1746}
1747
1748#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1749#[serde_as]
1750#[serde(untagged)]
1751enum KeyPairLocation {
1752 InPlace {
1753 #[serde_as(as = "Arc<KeyPairBase64>")]
1754 value: Arc<SuiKeyPair>,
1755 },
1756 File {
1757 #[serde(rename = "path")]
1758 path: PathBuf,
1759 },
1760}
1761
1762impl KeyPairWithPath {
1763 pub fn new(kp: SuiKeyPair) -> Self {
1764 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1765 let arc_kp = Arc::new(kp);
1766 cell.set(arc_kp.clone()).expect("Failed to set keypair");
1768 Self {
1769 location: KeyPairLocation::InPlace { value: arc_kp },
1770 keypair: cell,
1771 }
1772 }
1773
1774 pub fn new_from_path(path: PathBuf) -> Self {
1775 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1776 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1778 |e| panic!("Invalid keypair file at path {:?}: {e}", &path),
1779 )))
1780 .expect("Failed to set keypair");
1781 Self {
1782 location: KeyPairLocation::File { path },
1783 keypair: cell,
1784 }
1785 }
1786
1787 pub fn keypair(&self) -> &SuiKeyPair {
1788 self.keypair
1789 .get_or_init(|| match &self.location {
1790 KeyPairLocation::InPlace { value } => value.clone(),
1791 KeyPairLocation::File { path } => {
1792 Arc::new(
1794 read_keypair_from_file(path).unwrap_or_else(|e| {
1795 panic!("Invalid keypair file at path {:?}: {e}", path)
1796 }),
1797 )
1798 }
1799 })
1800 .as_ref()
1801 }
1802}
1803
1804#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1806pub struct AuthorityKeyPairWithPath {
1807 #[serde(flatten)]
1808 location: AuthorityKeyPairLocation,
1809
1810 #[serde(skip)]
1811 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1812}
1813
1814#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1815#[serde_as]
1816#[serde(untagged)]
1817enum AuthorityKeyPairLocation {
1818 InPlace { value: Arc<AuthorityKeyPair> },
1819 File { path: PathBuf },
1820}
1821
1822impl AuthorityKeyPairWithPath {
1823 pub fn new(kp: AuthorityKeyPair) -> Self {
1824 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1825 let arc_kp = Arc::new(kp);
1826 cell.set(arc_kp.clone())
1828 .expect("Failed to set authority keypair");
1829 Self {
1830 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1831 keypair: cell,
1832 }
1833 }
1834
1835 pub fn new_from_path(path: PathBuf) -> Self {
1836 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1837 cell.set(Arc::new(
1839 read_authority_keypair_from_file(&path)
1840 .unwrap_or_else(|_| panic!("Invalid authority keypair file at path {:?}", &path)),
1841 ))
1842 .expect("Failed to set authority keypair");
1843 Self {
1844 location: AuthorityKeyPairLocation::File { path },
1845 keypair: cell,
1846 }
1847 }
1848
1849 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1850 self.keypair
1851 .get_or_init(|| match &self.location {
1852 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1853 AuthorityKeyPairLocation::File { path } => {
1854 Arc::new(
1856 read_authority_keypair_from_file(path).unwrap_or_else(|_| {
1857 panic!("Invalid authority keypair file {:?}", &path)
1858 }),
1859 )
1860 }
1861 })
1862 .as_ref()
1863 }
1864}
1865
1866#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1869#[serde(rename_all = "kebab-case")]
1870pub struct StateDebugDumpConfig {
1871 #[serde(skip_serializing_if = "Option::is_none")]
1872 pub dump_file_directory: Option<PathBuf>,
1873}
1874
1875fn read_credential_from_path_or_literal(value: &str) -> Result<String, std::io::Error> {
1876 let path = Path::new(value);
1877 if path.exists() && path.is_file() {
1878 std::fs::read_to_string(path).map(|content| content.trim().to_string())
1879 } else {
1880 Ok(value.to_string())
1881 }
1882}
1883
1884fn deserialize_remote_store_options<'de, D>(
1886 deserializer: D,
1887) -> Result<Vec<(String, String)>, D::Error>
1888where
1889 D: serde::Deserializer<'de>,
1890{
1891 use serde::de::Error;
1892
1893 let raw_options: Vec<(String, String)> = Vec::deserialize(deserializer)?;
1894 let mut processed_options = Vec::new();
1895
1896 for (key, value) in raw_options {
1897 let is_service_account_path = matches!(
1900 key.as_str(),
1901 "google_service_account"
1902 | "service_account"
1903 | "google_service_account_path"
1904 | "service_account_path"
1905 );
1906
1907 let processed_value = if is_service_account_path {
1908 value
1909 } else {
1910 match read_credential_from_path_or_literal(&value) {
1911 Ok(processed) => processed,
1912 Err(e) => {
1913 return Err(D::Error::custom(format!(
1914 "Failed to read credential for key '{}': {}",
1915 key, e
1916 )));
1917 }
1918 }
1919 };
1920
1921 processed_options.push((key, processed_value));
1922 }
1923
1924 Ok(processed_options)
1925}
1926
1927#[cfg(test)]
1928mod tests {
1929 use std::path::PathBuf;
1930
1931 use fastcrypto::traits::KeyPair;
1932 use rand::{SeedableRng, rngs::StdRng};
1933 use sui_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1934 use sui_types::crypto::{AuthorityKeyPair, NetworkKeyPair, SuiKeyPair, get_key_pair_from_rng};
1935
1936 use super::{AuthorityStorePruningConfig, Genesis, StateArchiveConfig};
1937 use crate::NodeConfig;
1938
1939 #[test]
1940 fn serialize_genesis_from_file() {
1941 let g = Genesis::new_from_file("path/to/file");
1942
1943 let s = serde_yaml::to_string(&g).unwrap();
1944 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
1945 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
1946 assert_eq!(g, loaded_genesis);
1947 }
1948
1949 #[test]
1950 fn fullnode_template() {
1951 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1952
1953 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1954 }
1955
1956 #[test]
1958 fn legacy_validator_config() {
1959 const FILE: &str = include_str!("../data/sui-node-legacy.yaml");
1960
1961 let template: NodeConfig = serde_yaml::from_str(FILE).unwrap();
1962 assert_eq!(
1963 template
1964 .authority_store_pruning_config
1965 .rpc_store_bitmap_periodic_compaction_days,
1966 None
1967 );
1968 }
1969
1970 #[test]
1971 fn rpc_store_bitmap_periodic_compaction_days_override_deserializes() {
1972 assert_eq!(
1973 AuthorityStorePruningConfig::default().rpc_store_bitmap_periodic_compaction_days,
1974 None
1975 );
1976
1977 let omitted: AuthorityStorePruningConfig = serde_yaml::from_str("{}").unwrap();
1978 assert_eq!(omitted.rpc_store_bitmap_periodic_compaction_days, None);
1979
1980 let disabled: AuthorityStorePruningConfig =
1981 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 0").unwrap();
1982 assert_eq!(disabled.rpc_store_bitmap_periodic_compaction_days, Some(0));
1983
1984 let configured: AuthorityStorePruningConfig =
1985 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 17").unwrap();
1986 let serialized = serde_yaml::to_string(&configured).unwrap();
1987 let round_tripped: AuthorityStorePruningConfig = serde_yaml::from_str(&serialized).unwrap();
1988 assert_eq!(
1989 round_tripped.rpc_store_bitmap_periodic_compaction_days,
1990 Some(17)
1991 );
1992 }
1993
1994 #[test]
1995 fn load_key_pairs_to_node_config() {
1996 let protocol_key_pair: AuthorityKeyPair =
1997 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1998 let worker_key_pair: NetworkKeyPair =
1999 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2000 let network_key_pair: NetworkKeyPair =
2001 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2002
2003 write_authority_keypair_to_file(&protocol_key_pair, PathBuf::from("protocol.key")).unwrap();
2004 write_keypair_to_file(
2005 &SuiKeyPair::Ed25519(worker_key_pair.copy()),
2006 PathBuf::from("worker.key"),
2007 )
2008 .unwrap();
2009 write_keypair_to_file(
2010 &SuiKeyPair::Ed25519(network_key_pair.copy()),
2011 PathBuf::from("network.key"),
2012 )
2013 .unwrap();
2014
2015 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
2016 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2017 assert_eq!(
2018 template.protocol_key_pair().public(),
2019 protocol_key_pair.public()
2020 );
2021 assert_eq!(
2022 template.network_key_pair().public(),
2023 network_key_pair.public()
2024 );
2025 assert_eq!(
2026 template.worker_key_pair().public(),
2027 worker_key_pair.public()
2028 );
2029 }
2030
2031 #[test]
2032 fn test_remote_store_options_file_path_support() {
2033 let temp_dir = std::env::temp_dir();
2035 let access_key_file = temp_dir.join("test_access_key");
2036 let secret_key_file = temp_dir.join("test_secret_key");
2037
2038 std::fs::write(&access_key_file, "test_access_key_value").unwrap();
2039 std::fs::write(&secret_key_file, "test_secret_key_value\n").unwrap();
2040
2041 let yaml_config = format!(
2042 r#"
2043object-store-config: null
2044concurrency: 5
2045ingestion-url: "https://example.com"
2046remote-store-options:
2047 - ["aws_access_key_id", "{}"]
2048 - ["aws_secret_access_key", "{}"]
2049 - ["literal_key", "literal_value"]
2050"#,
2051 access_key_file.to_string_lossy(),
2052 secret_key_file.to_string_lossy()
2053 );
2054
2055 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2056
2057 assert_eq!(config.remote_store_options.len(), 3);
2059
2060 let access_key_option = config
2061 .remote_store_options
2062 .iter()
2063 .find(|(key, _)| key == "aws_access_key_id")
2064 .unwrap();
2065 assert_eq!(access_key_option.1, "test_access_key_value");
2066
2067 let secret_key_option = config
2068 .remote_store_options
2069 .iter()
2070 .find(|(key, _)| key == "aws_secret_access_key")
2071 .unwrap();
2072 assert_eq!(secret_key_option.1, "test_secret_key_value");
2073
2074 let literal_option = config
2075 .remote_store_options
2076 .iter()
2077 .find(|(key, _)| key == "literal_key")
2078 .unwrap();
2079 assert_eq!(literal_option.1, "literal_value");
2080
2081 std::fs::remove_file(&access_key_file).ok();
2083 std::fs::remove_file(&secret_key_file).ok();
2084 }
2085
2086 #[test]
2087 fn test_remote_store_options_literal_values_only() {
2088 let yaml_config = r#"
2089object-store-config: null
2090concurrency: 5
2091ingestion-url: "https://example.com"
2092remote-store-options:
2093 - ["aws_access_key_id", "literal_access_key"]
2094 - ["aws_secret_access_key", "literal_secret_key"]
2095"#;
2096
2097 let config: StateArchiveConfig = serde_yaml::from_str(yaml_config).unwrap();
2098
2099 assert_eq!(config.remote_store_options.len(), 2);
2100 assert_eq!(config.remote_store_options[0].1, "literal_access_key");
2101 assert_eq!(config.remote_store_options[1].1, "literal_secret_key");
2102 }
2103
2104 #[test]
2105 fn test_remote_store_options_gcs_service_account_path_preserved() {
2106 let temp_dir = std::env::temp_dir();
2107 let service_account_file = temp_dir.join("test_service_account.json");
2108 let aws_key_file = temp_dir.join("test_aws_key");
2109
2110 std::fs::write(&service_account_file, r#"{"type": "service_account"}"#).unwrap();
2111 std::fs::write(&aws_key_file, "aws_key_value").unwrap();
2112
2113 let yaml_config = format!(
2114 r#"
2115object-store-config: null
2116concurrency: 5
2117ingestion-url: "gs://my-bucket"
2118remote-store-options:
2119 - ["service_account", "{}"]
2120 - ["google_service_account_path", "{}"]
2121 - ["aws_access_key_id", "{}"]
2122"#,
2123 service_account_file.to_string_lossy(),
2124 service_account_file.to_string_lossy(),
2125 aws_key_file.to_string_lossy()
2126 );
2127
2128 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2129
2130 assert_eq!(config.remote_store_options.len(), 3);
2131
2132 let service_account_option = config
2134 .remote_store_options
2135 .iter()
2136 .find(|(key, _)| key == "service_account")
2137 .unwrap();
2138 assert_eq!(
2139 service_account_option.1,
2140 service_account_file.to_string_lossy()
2141 );
2142
2143 let gcs_path_option = config
2145 .remote_store_options
2146 .iter()
2147 .find(|(key, _)| key == "google_service_account_path")
2148 .unwrap();
2149 assert_eq!(gcs_path_option.1, service_account_file.to_string_lossy());
2150
2151 let aws_option = config
2153 .remote_store_options
2154 .iter()
2155 .find(|(key, _)| key == "aws_access_key_id")
2156 .unwrap();
2157 assert_eq!(aws_option.1, "aws_key_value");
2158
2159 std::fs::remove_file(&service_account_file).ok();
2161 std::fs::remove_file(&aws_key_file).ok();
2162 }
2163
2164 mod intended_node_role_tests {
2165 use super::*;
2166 use crate::ConsensusConfig;
2167 use consensus_config::Parameters as ConsensusParameters;
2168 use fastcrypto::ed25519::Ed25519KeyPair;
2169 use sui_types::node_role::{FullNodeSyncMode, NodeRole};
2170
2171 fn fullnode_template_config() -> NodeConfig {
2172 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2173 serde_yaml::from_str(TEMPLATE).unwrap()
2174 }
2175
2176 fn minimal_consensus_config() -> ConsensusConfig {
2177 ConsensusConfig {
2178 db_path: PathBuf::from("/tmp/consensus"),
2179 db_retention_epochs: None,
2180 db_pruner_period_secs: None,
2181 max_pending_transactions: None,
2182 parameters: Default::default(),
2183 listen_address: None,
2184 external_address: None,
2185 }
2186 }
2187
2188 fn consensus_config_with_observer_peers() -> ConsensusConfig {
2189 let mut config = minimal_consensus_config();
2190 let kp = Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32]));
2191 let peer = consensus_config::PeerRecord {
2192 public_key: consensus_config::NetworkPublicKey::new(kp.public().clone()),
2193 address: "/ip4/127.0.0.1/udp/8080".parse().unwrap(),
2194 };
2195 let mut params = ConsensusParameters::default();
2196 params.observer.peers = vec![peer];
2197 config.parameters = Some(params);
2198 config
2199 }
2200
2201 #[test]
2202 fn validator_with_consensus_config() {
2203 let mut config = fullnode_template_config();
2204 config.consensus_config = Some(minimal_consensus_config());
2205 config.fullnode_sync_mode = None;
2206
2207 assert_eq!(config.intended_node_role(), NodeRole::Validator);
2208 }
2209
2210 #[test]
2211 fn fullnode_explicit_state_sync() {
2212 let mut config = fullnode_template_config();
2213 config.consensus_config = None;
2214 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2215
2216 assert_eq!(
2217 config.intended_node_role(),
2218 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2219 );
2220 }
2221
2222 #[test]
2223 fn fullnode_implicit_state_sync() {
2224 let mut config = fullnode_template_config();
2225 config.consensus_config = None;
2226 config.fullnode_sync_mode = None;
2227
2228 assert_eq!(
2229 config.intended_node_role(),
2230 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2231 );
2232 }
2233
2234 #[test]
2235 fn fullnode_consensus_observer() {
2236 let mut config = fullnode_template_config();
2237 config.consensus_config = Some(consensus_config_with_observer_peers());
2238 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2239
2240 assert_eq!(
2241 config.intended_node_role(),
2242 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
2243 );
2244 }
2245
2246 #[test]
2247 #[should_panic(
2248 expected = "Consensus config should not be set for a StateSyncOnly full node"
2249 )]
2250 fn state_sync_with_consensus_config_panics() {
2251 let mut config = fullnode_template_config();
2252 config.consensus_config = Some(minimal_consensus_config());
2253 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2254
2255 config.intended_node_role();
2256 }
2257
2258 #[test]
2259 #[should_panic(expected = "Observer peers must be configured")]
2260 fn observer_without_peers_panics() {
2261 let mut config = fullnode_template_config();
2262 config.consensus_config = Some(minimal_consensus_config());
2263 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2264
2265 config.intended_node_role();
2266 }
2267 }
2268}
2269
2270#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
2273pub enum RunWithRange {
2274 Epoch(EpochId),
2275 Checkpoint(CheckpointSequenceNumber),
2276}
2277
2278impl RunWithRange {
2279 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
2281 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
2282 }
2283
2284 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
2285 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
2286 }
2287
2288 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
2289 match self {
2290 RunWithRange::Epoch(_) => None,
2291 RunWithRange::Checkpoint(seq) => Some(seq),
2292 }
2293 }
2294}