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)]
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 object_utilization_metric_tracked_ids: Option<BTreeMap<ObjectID, String>>,
538
539 pub observation_sharing_object_utilization_threshold: Option<Duration>,
544
545 pub observation_sharing_diff_threshold: Option<f64>,
550
551 pub observation_sharing_min_interval: Option<Duration>,
555
556 pub observation_sharing_rate_limit: Option<NonZeroU32>,
561
562 pub observation_sharing_burst_limit: Option<NonZeroU32>,
566
567 pub enable_gas_price_weighting: Option<bool>,
574
575 pub weighted_moving_average_window_size: Option<usize>,
582
583 #[cfg(msim)]
589 pub inject_synthetic_execution_time: Option<bool>,
590}
591
592impl ExecutionTimeObserverConfig {
593 pub fn observation_channel_capacity(&self) -> NonZeroUsize {
594 self.observation_channel_capacity
595 .unwrap_or(nonzero!(1_024usize))
596 }
597
598 pub fn observation_cache_size(&self) -> NonZeroUsize {
599 self.observation_cache_size.unwrap_or(nonzero!(10_000usize))
600 }
601
602 pub fn object_debt_channel_capacity(&self) -> NonZeroUsize {
603 self.object_debt_channel_capacity
604 .unwrap_or(nonzero!(128usize))
605 }
606
607 pub fn object_utilization_cache_size(&self) -> NonZeroUsize {
608 self.object_utilization_cache_size
609 .unwrap_or(nonzero!(50_000usize))
610 }
611
612 pub fn report_object_utilization_metric_with_full_id(&self) -> bool {
613 self.report_object_utilization_metric_with_full_id
614 .unwrap_or(false)
615 }
616
617 pub fn object_utilization_metric_tracked_ids(&self) -> impl Iterator<Item = (&ObjectID, &str)> {
618 self.object_utilization_metric_tracked_ids
619 .iter()
620 .flatten()
621 .map(|(id, name)| (id, name.as_str()))
622 }
623
624 pub fn observation_sharing_object_utilization_threshold(&self) -> Duration {
625 self.observation_sharing_object_utilization_threshold
626 .unwrap_or(Duration::from_millis(500))
627 }
628
629 pub fn observation_sharing_diff_threshold(&self) -> f64 {
630 self.observation_sharing_diff_threshold.unwrap_or(0.1)
631 }
632
633 pub fn observation_sharing_min_interval(&self) -> Duration {
634 self.observation_sharing_min_interval
635 .unwrap_or(Duration::from_secs(5))
636 }
637
638 pub fn observation_sharing_rate_limit(&self) -> NonZeroU32 {
639 self.observation_sharing_rate_limit
640 .unwrap_or(nonzero!(10u32))
641 }
642
643 pub fn observation_sharing_burst_limit(&self) -> NonZeroU32 {
644 self.observation_sharing_burst_limit
645 .unwrap_or(nonzero!(100u32))
646 }
647
648 pub fn enable_gas_price_weighting(&self) -> bool {
649 self.enable_gas_price_weighting.unwrap_or(false)
650 }
651
652 pub fn weighted_moving_average_window_size(&self) -> usize {
653 self.weighted_moving_average_window_size.unwrap_or(20)
654 }
655
656 #[cfg(msim)]
657 pub fn inject_synthetic_execution_time(&self) -> bool {
658 self.inject_synthetic_execution_time.unwrap_or(false)
659 }
660}
661
662#[allow(clippy::large_enum_variant)]
663#[derive(Clone, Debug, Deserialize, Serialize)]
664#[serde(rename_all = "kebab-case")]
665pub enum ExecutionCacheConfig {
666 PassthroughCache,
667 WritebackCache {
668 max_cache_size: Option<u64>,
671
672 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>,
688
689 backpressure_threshold_for_rpc: Option<u64>,
692 },
693}
694
695impl Default for ExecutionCacheConfig {
696 fn default() -> Self {
697 ExecutionCacheConfig::WritebackCache {
698 max_cache_size: None,
699 backpressure_threshold: None,
700 backpressure_threshold_for_rpc: None,
701 package_cache_size: None,
702 object_cache_size: None,
703 marker_cache_size: None,
704 object_by_id_cache_size: None,
705 transaction_cache_size: None,
706 executed_effect_cache_size: None,
707 effect_cache_size: None,
708 events_cache_size: None,
709 transaction_objects_cache_size: None,
710 }
711 }
712}
713
714impl ExecutionCacheConfig {
715 pub fn max_cache_size(&self) -> u64 {
716 std::env::var("SUI_MAX_CACHE_SIZE")
717 .ok()
718 .and_then(|s| s.parse().ok())
719 .unwrap_or_else(|| match self {
720 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
721 ExecutionCacheConfig::WritebackCache { max_cache_size, .. } => {
722 max_cache_size.unwrap_or(100000)
723 }
724 })
725 }
726
727 pub fn package_cache_size(&self) -> u64 {
728 std::env::var("SUI_PACKAGE_CACHE_SIZE")
729 .ok()
730 .and_then(|s| s.parse().ok())
731 .unwrap_or_else(|| match self {
732 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
733 ExecutionCacheConfig::WritebackCache {
734 package_cache_size, ..
735 } => package_cache_size.unwrap_or(1000),
736 })
737 }
738
739 pub fn object_cache_size(&self) -> u64 {
740 std::env::var("SUI_OBJECT_CACHE_SIZE")
741 .ok()
742 .and_then(|s| s.parse().ok())
743 .unwrap_or_else(|| match self {
744 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
745 ExecutionCacheConfig::WritebackCache {
746 object_cache_size, ..
747 } => object_cache_size.unwrap_or(self.max_cache_size()),
748 })
749 }
750
751 pub fn marker_cache_size(&self) -> u64 {
752 std::env::var("SUI_MARKER_CACHE_SIZE")
753 .ok()
754 .and_then(|s| s.parse().ok())
755 .unwrap_or_else(|| match self {
756 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
757 ExecutionCacheConfig::WritebackCache {
758 marker_cache_size, ..
759 } => marker_cache_size.unwrap_or(self.object_cache_size()),
760 })
761 }
762
763 pub fn object_by_id_cache_size(&self) -> u64 {
764 std::env::var("SUI_OBJECT_BY_ID_CACHE_SIZE")
765 .ok()
766 .and_then(|s| s.parse().ok())
767 .unwrap_or_else(|| match self {
768 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
769 ExecutionCacheConfig::WritebackCache {
770 object_by_id_cache_size,
771 ..
772 } => object_by_id_cache_size.unwrap_or(self.object_cache_size()),
773 })
774 }
775
776 pub fn transaction_cache_size(&self) -> u64 {
777 std::env::var("SUI_TRANSACTION_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 transaction_cache_size,
784 ..
785 } => transaction_cache_size.unwrap_or(self.max_cache_size()),
786 })
787 }
788
789 pub fn executed_effect_cache_size(&self) -> u64 {
790 std::env::var("SUI_EXECUTED_EFFECT_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 executed_effect_cache_size,
797 ..
798 } => executed_effect_cache_size.unwrap_or(self.transaction_cache_size()),
799 })
800 }
801
802 pub fn effect_cache_size(&self) -> u64 {
803 std::env::var("SUI_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 effect_cache_size, ..
810 } => effect_cache_size.unwrap_or(self.executed_effect_cache_size()),
811 })
812 }
813
814 pub fn events_cache_size(&self) -> u64 {
815 std::env::var("SUI_EVENTS_CACHE_SIZE")
816 .ok()
817 .and_then(|s| s.parse().ok())
818 .unwrap_or_else(|| match self {
819 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
820 ExecutionCacheConfig::WritebackCache {
821 events_cache_size, ..
822 } => events_cache_size.unwrap_or(self.transaction_cache_size()),
823 })
824 }
825
826 pub fn transaction_objects_cache_size(&self) -> u64 {
827 std::env::var("SUI_TRANSACTION_OBJECTS_CACHE_SIZE")
828 .ok()
829 .and_then(|s| s.parse().ok())
830 .unwrap_or_else(|| match self {
831 ExecutionCacheConfig::PassthroughCache => fatal!("invalid cache config"),
832 ExecutionCacheConfig::WritebackCache {
833 transaction_objects_cache_size,
834 ..
835 } => transaction_objects_cache_size.unwrap_or(1000),
836 })
837 }
838
839 pub fn backpressure_threshold(&self) -> u64 {
840 std::env::var("SUI_BACKPRESSURE_THRESHOLD")
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 backpressure_threshold,
847 ..
848 } => backpressure_threshold.unwrap_or(100_000),
849 })
850 }
851
852 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
853 std::env::var("SUI_BACKPRESSURE_THRESHOLD_FOR_RPC")
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_for_rpc,
860 ..
861 } => backpressure_threshold_for_rpc.unwrap_or(self.backpressure_threshold()),
862 })
863 }
864}
865
866#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
867#[serde(rename_all = "lowercase")]
868pub enum ServerType {
869 WebSocket,
870 Http,
871 Both,
872}
873
874#[derive(Clone, Debug, Deserialize, Serialize)]
875#[serde(rename_all = "kebab-case")]
876pub struct TransactionKeyValueStoreReadConfig {
877 #[serde(default = "default_base_url")]
878 pub base_url: String,
879
880 #[serde(default = "default_cache_size")]
881 pub cache_size: u64,
882}
883
884impl Default for TransactionKeyValueStoreReadConfig {
885 fn default() -> Self {
886 Self {
887 base_url: default_base_url(),
888 cache_size: default_cache_size(),
889 }
890 }
891}
892
893fn default_base_url() -> String {
894 "https://transactions.sui.io/".to_string()
895}
896
897fn default_cache_size() -> u64 {
898 100_000
899}
900
901fn default_jwk_fetch_interval_seconds() -> u64 {
902 3600
903}
904
905pub fn default_zklogin_oauth_providers() -> BTreeMap<Chain, BTreeSet<String>> {
906 let mut map = BTreeMap::new();
907
908 let experimental_providers = BTreeSet::from([
910 "Google".to_string(),
911 "Facebook".to_string(),
912 "Twitch".to_string(),
913 "Kakao".to_string(),
914 "Apple".to_string(),
915 "Slack".to_string(),
916 "TestIssuer".to_string(),
917 "TestIssuerKey8192".to_string(),
918 "Microsoft".to_string(),
919 "KarrierOne".to_string(),
920 "Credenza3".to_string(),
921 "Playtron".to_string(),
922 "Threedos".to_string(),
923 "Onefc".to_string(),
924 "FanTV".to_string(),
925 "Arden".to_string(), "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "EveFrontier".to_string(),
928 "TestEveFrontier".to_string(),
929 "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(), ]);
933
934 let providers = BTreeSet::from([
936 "Google".to_string(),
937 "Facebook".to_string(),
938 "Twitch".to_string(),
939 "Apple".to_string(),
940 "KarrierOne".to_string(),
941 "Credenza3".to_string(),
942 "Playtron".to_string(),
943 "Onefc".to_string(),
944 "Threedos".to_string(),
945 "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "Arden".to_string(),
947 "FanTV".to_string(),
948 "EveFrontier".to_string(),
949 "TestEveFrontier".to_string(),
950 "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(), ]);
954 map.insert(Chain::Mainnet, providers.clone());
955 map.insert(Chain::Testnet, providers);
956 map.insert(Chain::Unknown, experimental_providers);
957 map
958}
959
960fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
961 TransactionKeyValueStoreReadConfig::default()
962}
963
964fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
965 AuthorityStorePruningConfig::default()
966}
967
968pub fn default_enable_index_processing() -> bool {
969 true
970}
971
972fn default_grpc_address() -> Multiaddr {
973 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
974}
975fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
976 AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
977}
978
979fn default_key_pair() -> KeyPairWithPath {
980 KeyPairWithPath::new(
981 get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
982 .1
983 .into(),
984 )
985}
986
987fn default_metrics_address() -> SocketAddr {
988 use std::net::{IpAddr, Ipv4Addr};
989 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
990}
991
992pub fn default_admin_interface_port() -> u16 {
993 1337
994}
995
996pub fn default_json_rpc_address() -> SocketAddr {
997 use std::net::{IpAddr, Ipv4Addr};
998 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
999}
1000
1001pub fn default_concurrency_limit() -> Option<usize> {
1002 Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
1003}
1004
1005pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
1006 128
1007}
1008
1009pub fn bool_true() -> bool {
1010 true
1011}
1012
1013fn is_true(value: &bool) -> bool {
1014 *value
1015}
1016
1017impl Config for NodeConfig {}
1018
1019impl NodeConfig {
1020 pub fn protocol_key_pair(&self) -> &AuthorityKeyPair {
1021 self.protocol_key_pair.authority_keypair()
1022 }
1023
1024 pub fn recent_submission_dedup_window(&self) -> Duration {
1027 Duration::from_millis(self.recent_submission_dedup_window_ms.unwrap_or(1000))
1028 }
1029
1030 pub fn worker_key_pair(&self) -> &NetworkKeyPair {
1031 match self.worker_key_pair.keypair() {
1032 SuiKeyPair::Ed25519(kp) => kp,
1033 other => panic!(
1034 "Invalid keypair type: {:?}, only Ed25519 is allowed for worker key",
1035 other
1036 ),
1037 }
1038 }
1039
1040 pub fn network_key_pair(&self) -> &NetworkKeyPair {
1041 match self.network_key_pair.keypair() {
1042 SuiKeyPair::Ed25519(kp) => kp,
1043 other => panic!(
1044 "Invalid keypair type: {:?}, only Ed25519 is allowed for network key",
1045 other
1046 ),
1047 }
1048 }
1049
1050 pub fn protocol_public_key(&self) -> AuthorityPublicKeyBytes {
1051 self.protocol_key_pair().public().into()
1052 }
1053
1054 pub fn db_path(&self) -> PathBuf {
1055 self.db_path.join("live")
1056 }
1057
1058 pub fn db_checkpoint_path(&self) -> PathBuf {
1059 self.db_path.join("db_checkpoints")
1060 }
1061
1062 pub fn db_store_path(&self) -> PathBuf {
1063 self.db_path().join("store")
1064 }
1065
1066 pub fn archive_path(&self) -> PathBuf {
1067 self.db_path.join("archive")
1068 }
1069
1070 pub fn snapshot_path(&self) -> PathBuf {
1071 self.db_path.join("snapshot")
1072 }
1073
1074 pub fn network_address(&self) -> &Multiaddr {
1075 &self.network_address
1076 }
1077
1078 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
1079 self.consensus_config.as_ref()
1080 }
1081
1082 pub fn intended_node_role(&self) -> NodeRole {
1087 let has_consensus_config = self.consensus_config.is_some();
1088
1089 match (self.fullnode_sync_mode, has_consensus_config) {
1090 (Some(FullNodeSyncMode::ConsensusObserver), _) => {
1091 assert!(
1092 self.has_observer_config_peers(),
1093 "Observer peers must be configured when sync mode is ConsensusObserver"
1094 );
1095 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
1096 }
1097 (Some(FullNodeSyncMode::StateSyncOnly), true) => {
1098 panic!("Consensus config should not be set for a StateSyncOnly full node");
1099 }
1100 (Some(FullNodeSyncMode::StateSyncOnly), false) => {
1101 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
1102 }
1103 (None, false) => NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly),
1104 (None, true) => NodeRole::Validator,
1105 }
1106 }
1107
1108 pub fn has_observer_config_peers(&self) -> bool {
1109 self.consensus_config
1110 .as_ref()
1111 .and_then(|c| c.parameters.as_ref())
1112 .map(|p| !p.observer.peers.is_empty())
1113 .unwrap_or(false)
1114 }
1115
1116 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1117 self.genesis.genesis()
1118 }
1119
1120 pub fn sui_address(&self) -> SuiAddress {
1121 (&self.account_key_pair.keypair().public()).into()
1122 }
1123
1124 pub fn archive_reader_config(&self) -> Option<ArchiveReaderConfig> {
1125 self.state_archive_read_config
1126 .first()
1127 .map(|config| ArchiveReaderConfig {
1128 ingestion_url: config.ingestion_url.clone(),
1129 remote_store_options: config.remote_store_options.clone(),
1130 remote_store_headers: config.remote_store_headers.clone(),
1131 download_concurrency: NonZeroUsize::new(config.concurrency)
1132 .unwrap_or(NonZeroUsize::new(5).unwrap()),
1133 remote_store_config: ObjectStoreConfig::default(),
1134 })
1135 }
1136
1137 pub fn jsonrpc_server_type(&self) -> ServerType {
1138 self.jsonrpc_server_type.unwrap_or(ServerType::Http)
1139 }
1140
1141 pub fn json_rpc_enabled(&self) -> bool {
1145 !self.disable_json_rpc
1146 }
1147
1148 pub fn rpc(&self) -> Option<&crate::RpcConfig> {
1149 self.rpc.as_ref()
1150 }
1151}
1152
1153#[derive(Debug, Clone, Deserialize, Serialize)]
1154pub enum ConsensusProtocol {
1155 #[serde(rename = "narwhal")]
1156 Narwhal,
1157 #[serde(rename = "mysticeti")]
1158 Mysticeti,
1159}
1160
1161#[derive(Debug, Clone, Deserialize, Serialize)]
1162#[serde(rename_all = "kebab-case")]
1163pub struct ConsensusConfig {
1164 pub db_path: PathBuf,
1166
1167 pub db_retention_epochs: Option<u64>,
1170
1171 pub db_pruner_period_secs: Option<u64>,
1174
1175 pub max_pending_transactions: Option<usize>,
1179
1180 pub parameters: Option<ConsensusParameters>,
1181
1182 #[serde(skip_serializing_if = "Option::is_none")]
1186 pub listen_address: Option<Multiaddr>,
1187
1188 #[serde(skip_serializing_if = "Option::is_none")]
1193 pub external_address: Option<Multiaddr>,
1194}
1195
1196impl ConsensusConfig {
1197 pub fn db_path(&self) -> &Path {
1198 &self.db_path
1199 }
1200
1201 pub fn max_pending_transactions(&self) -> usize {
1202 self.max_pending_transactions.unwrap_or(20_000)
1203 }
1204
1205 pub fn db_retention_epochs(&self) -> u64 {
1206 self.db_retention_epochs.unwrap_or(0)
1207 }
1208
1209 pub fn db_pruner_period(&self) -> Duration {
1210 self.db_pruner_period_secs
1212 .map(Duration::from_secs)
1213 .unwrap_or(Duration::from_secs(3_600))
1214 }
1215}
1216
1217#[derive(Clone, Debug, Deserialize, Serialize)]
1218#[serde(rename_all = "kebab-case")]
1219pub struct CheckpointExecutorConfig {
1220 #[serde(default = "default_checkpoint_execution_max_concurrency")]
1224 pub checkpoint_execution_max_concurrency: usize,
1225
1226 #[serde(default = "default_local_execution_timeout_sec")]
1232 pub local_execution_timeout_sec: u64,
1233
1234 #[serde(default, skip_serializing_if = "Option::is_none")]
1237 pub data_ingestion_dir: Option<PathBuf>,
1238}
1239
1240#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1241#[serde(rename_all = "kebab-case")]
1242pub struct ExpensiveSafetyCheckConfig {
1243 #[serde(default)]
1248 enable_epoch_sui_conservation_check: bool,
1249
1250 #[serde(default)]
1254 enable_deep_per_tx_sui_conservation_check: bool,
1255
1256 #[serde(default)]
1258 force_disable_epoch_sui_conservation_check: bool,
1259
1260 #[serde(default)]
1263 enable_state_consistency_check: bool,
1264
1265 #[serde(default)]
1267 force_disable_state_consistency_check: bool,
1268
1269 #[serde(default)]
1270 enable_secondary_index_checks: bool,
1271 }
1273
1274impl ExpensiveSafetyCheckConfig {
1275 pub fn new_enable_all() -> Self {
1276 Self {
1277 enable_epoch_sui_conservation_check: true,
1278 enable_deep_per_tx_sui_conservation_check: true,
1279 force_disable_epoch_sui_conservation_check: false,
1280 enable_state_consistency_check: true,
1281 force_disable_state_consistency_check: false,
1282 enable_secondary_index_checks: false, }
1284 }
1285
1286 pub fn new_enable_all_with_secondary_index_checks() -> Self {
1287 Self {
1288 enable_secondary_index_checks: true,
1289 ..Self::new_enable_all()
1290 }
1291 }
1292
1293 pub fn new_disable_all() -> Self {
1294 Self {
1295 enable_epoch_sui_conservation_check: false,
1296 enable_deep_per_tx_sui_conservation_check: false,
1297 force_disable_epoch_sui_conservation_check: true,
1298 enable_state_consistency_check: false,
1299 force_disable_state_consistency_check: true,
1300 enable_secondary_index_checks: false,
1301 }
1302 }
1303
1304 pub fn force_disable_epoch_sui_conservation_check(&mut self) {
1305 self.force_disable_epoch_sui_conservation_check = true;
1306 }
1307
1308 pub fn enable_epoch_sui_conservation_check(&self) -> bool {
1309 (self.enable_epoch_sui_conservation_check || cfg!(debug_assertions))
1310 && !self.force_disable_epoch_sui_conservation_check
1311 }
1312
1313 pub fn force_disable_state_consistency_check(&mut self) {
1314 self.force_disable_state_consistency_check = true;
1315 }
1316
1317 pub fn enable_state_consistency_check(&self) -> bool {
1318 (self.enable_state_consistency_check || cfg!(debug_assertions))
1319 && !self.force_disable_state_consistency_check
1320 }
1321
1322 pub fn enable_deep_per_tx_sui_conservation_check(&self) -> bool {
1323 self.enable_deep_per_tx_sui_conservation_check || cfg!(debug_assertions)
1324 }
1325
1326 pub fn enable_secondary_index_checks(&self) -> bool {
1327 self.enable_secondary_index_checks
1328 }
1329}
1330
1331fn default_checkpoint_execution_max_concurrency() -> usize {
1332 4
1333}
1334
1335fn default_local_execution_timeout_sec() -> u64 {
1336 30
1337}
1338
1339impl Default for CheckpointExecutorConfig {
1340 fn default() -> Self {
1341 Self {
1342 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
1343 local_execution_timeout_sec: default_local_execution_timeout_sec(),
1344 data_ingestion_dir: None,
1345 }
1346 }
1347}
1348
1349#[derive(Debug, Clone, Deserialize, Serialize)]
1350#[serde(rename_all = "kebab-case")]
1351pub struct AuthorityStorePruningConfig {
1352 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1354 pub num_latest_epoch_dbs_to_retain: usize,
1355 #[serde(default = "default_epoch_db_pruning_period_secs")]
1357 pub epoch_db_pruning_period_secs: u64,
1358 #[serde(default)]
1363 pub num_epochs_to_retain: u64,
1364 #[serde(skip_serializing_if = "Option::is_none")]
1366 pub pruning_run_delay_seconds: Option<u64>,
1367 #[serde(default = "default_max_checkpoints_in_batch")]
1369 pub max_checkpoints_in_batch: usize,
1370 #[serde(default = "default_max_transactions_in_batch")]
1372 pub max_transactions_in_batch: usize,
1373 #[serde(
1377 default = "default_periodic_compaction_threshold_days",
1378 skip_serializing_if = "Option::is_none"
1379 )]
1380 pub periodic_compaction_threshold_days: Option<usize>,
1381 #[serde(default, skip_serializing_if = "Option::is_none")]
1391 pub rpc_store_bitmap_periodic_compaction_days: Option<u64>,
1392 #[serde(skip_serializing_if = "Option::is_none")]
1394 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1395 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1397 pub killswitch_tombstone_pruning: bool,
1398 #[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
1399 pub smooth: bool,
1400 #[serde(skip_serializing_if = "Option::is_none")]
1401 pub num_epochs_to_retain_for_indexes: Option<u64>,
1402}
1403
1404fn default_num_latest_epoch_dbs_to_retain() -> usize {
1405 3
1406}
1407
1408fn default_epoch_db_pruning_period_secs() -> u64 {
1409 3600
1410}
1411
1412fn default_max_transactions_in_batch() -> usize {
1413 1000
1414}
1415
1416fn default_max_checkpoints_in_batch() -> usize {
1417 10
1418}
1419
1420fn default_smoothing() -> bool {
1421 cfg!(not(test))
1422}
1423
1424fn default_periodic_compaction_threshold_days() -> Option<usize> {
1425 Some(1)
1426}
1427
1428impl Default for AuthorityStorePruningConfig {
1429 fn default() -> Self {
1430 Self {
1431 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1432 epoch_db_pruning_period_secs: default_epoch_db_pruning_period_secs(),
1433 num_epochs_to_retain: 0,
1434 pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
1435 max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
1436 max_transactions_in_batch: default_max_transactions_in_batch(),
1437 periodic_compaction_threshold_days: None,
1438 rpc_store_bitmap_periodic_compaction_days: None,
1439 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1440 killswitch_tombstone_pruning: false,
1441 smooth: true,
1442 num_epochs_to_retain_for_indexes: None,
1443 }
1444 }
1445}
1446
1447impl AuthorityStorePruningConfig {
1448 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1449 self.num_epochs_to_retain = num_epochs_to_retain;
1450 }
1451
1452 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1453 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1454 }
1455
1456 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1457 self.num_epochs_to_retain_for_checkpoints
1458 .map(|n| {
1460 if n < 2 {
1461 info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
1462 2
1463 } else {
1464 n
1465 }
1466 })
1467 }
1468
1469 pub fn set_killswitch_tombstone_pruning(&mut self, killswitch_tombstone_pruning: bool) {
1470 self.killswitch_tombstone_pruning = killswitch_tombstone_pruning;
1471 }
1472}
1473
1474#[derive(Debug, Clone, Deserialize, Serialize)]
1475#[serde(rename_all = "kebab-case")]
1476pub struct MetricsConfig {
1477 #[serde(skip_serializing_if = "Option::is_none")]
1478 pub push_interval_seconds: Option<u64>,
1479 #[serde(skip_serializing_if = "Option::is_none")]
1480 pub push_url: Option<String>,
1481}
1482
1483#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1484#[serde(rename_all = "kebab-case")]
1485pub struct DBCheckpointConfig {
1486 #[serde(default)]
1487 pub perform_db_checkpoints_at_epoch_end: bool,
1488 #[serde(skip_serializing_if = "Option::is_none")]
1489 pub checkpoint_path: Option<PathBuf>,
1490 #[serde(skip_serializing_if = "Option::is_none")]
1491 pub object_store_config: Option<ObjectStoreConfig>,
1492 #[serde(skip_serializing_if = "Option::is_none")]
1493 pub perform_index_db_checkpoints_at_epoch_end: Option<bool>,
1494 #[serde(skip_serializing_if = "Option::is_none")]
1495 pub prune_and_compact_before_upload: Option<bool>,
1496}
1497
1498#[derive(Debug, Clone)]
1499pub struct ArchiveReaderConfig {
1500 pub remote_store_config: ObjectStoreConfig,
1501 pub download_concurrency: NonZeroUsize,
1502 pub ingestion_url: Option<String>,
1503 pub remote_store_options: Vec<(String, String)>,
1504 pub remote_store_headers: Vec<(String, String)>,
1505}
1506
1507#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1508#[serde(rename_all = "kebab-case")]
1509pub struct StateArchiveConfig {
1510 #[serde(skip_serializing_if = "Option::is_none")]
1511 pub object_store_config: Option<ObjectStoreConfig>,
1512 pub concurrency: usize,
1513 #[serde(skip_serializing_if = "Option::is_none")]
1514 pub ingestion_url: Option<String>,
1515 #[serde(
1516 skip_serializing_if = "Vec::is_empty",
1517 default,
1518 deserialize_with = "deserialize_remote_store_options"
1519 )]
1520 pub remote_store_options: Vec<(String, String)>,
1521 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1525 pub remote_store_headers: Vec<(String, String)>,
1526}
1527
1528#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1529#[serde(rename_all = "kebab-case")]
1530pub struct StateSnapshotConfig {
1531 #[serde(skip_serializing_if = "Option::is_none")]
1532 pub object_store_config: Option<ObjectStoreConfig>,
1533 pub concurrency: usize,
1534 #[serde(default)]
1538 pub archive_interval_epochs: u64,
1539}
1540
1541#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1542#[serde(rename_all = "kebab-case")]
1543pub struct TransactionKeyValueStoreWriteConfig {
1544 pub aws_access_key_id: String,
1545 pub aws_secret_access_key: String,
1546 pub aws_region: String,
1547 pub table_name: String,
1548 pub bucket_name: String,
1549 pub concurrency: usize,
1550}
1551
1552#[derive(Clone, Debug, Deserialize, Serialize)]
1557#[serde(rename_all = "kebab-case")]
1558pub struct AuthorityOverloadConfig {
1559 #[serde(default = "default_max_txn_age_in_queue")]
1560 pub max_txn_age_in_queue: Duration,
1561
1562 #[serde(default = "default_overload_monitor_interval")]
1564 pub overload_monitor_interval: Duration,
1565
1566 #[serde(default = "default_execution_queue_latency_soft_limit")]
1568 pub execution_queue_latency_soft_limit: Duration,
1569
1570 #[serde(default = "default_execution_queue_latency_hard_limit")]
1572 pub execution_queue_latency_hard_limit: Duration,
1573
1574 #[serde(default = "default_max_load_shedding_percentage")]
1576 pub max_load_shedding_percentage: u32,
1577
1578 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1581 pub min_load_shedding_percentage_above_hard_limit: u32,
1582
1583 #[serde(default = "default_safe_transaction_ready_rate")]
1586 pub safe_transaction_ready_rate: u32,
1587
1588 #[serde(default = "default_check_system_overload_at_signing")]
1591 pub check_system_overload_at_signing: bool,
1592
1593 #[serde(default = "default_max_transaction_manager_queue_length")]
1596 pub max_transaction_manager_queue_length: usize,
1597
1598 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1601 pub max_transaction_manager_per_object_queue_length: usize,
1602
1603 #[serde(default = "default_admission_queue_capacity_fraction")]
1607 pub admission_queue_capacity_fraction: f64,
1608
1609 #[serde(default = "default_admission_queue_enabled")]
1614 pub admission_queue_enabled: bool,
1615
1616 #[serde(default = "default_admission_queue_failover_timeout")]
1621 pub admission_queue_failover_timeout: Duration,
1622}
1623
1624fn default_max_txn_age_in_queue() -> Duration {
1625 Duration::from_millis(1000)
1626}
1627
1628fn default_overload_monitor_interval() -> Duration {
1629 Duration::from_secs(10)
1630}
1631
1632fn default_execution_queue_latency_soft_limit() -> Duration {
1633 Duration::from_secs(1)
1634}
1635
1636fn default_execution_queue_latency_hard_limit() -> Duration {
1637 Duration::from_secs(10)
1638}
1639
1640fn default_max_load_shedding_percentage() -> u32 {
1641 95
1642}
1643
1644fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1645 50
1646}
1647
1648fn default_safe_transaction_ready_rate() -> u32 {
1649 100
1650}
1651
1652fn default_check_system_overload_at_signing() -> bool {
1653 true
1654}
1655
1656fn default_max_transaction_manager_queue_length() -> usize {
1657 100_000
1658}
1659
1660fn default_max_transaction_manager_per_object_queue_length() -> usize {
1661 2000
1662}
1663
1664fn default_admission_queue_capacity_fraction() -> f64 {
1665 0.5
1666}
1667
1668fn default_admission_queue_enabled() -> bool {
1669 true
1670}
1671
1672fn default_admission_queue_failover_timeout() -> Duration {
1673 Duration::from_secs(30)
1674}
1675
1676impl Default for AuthorityOverloadConfig {
1677 fn default() -> Self {
1678 Self {
1679 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1680 overload_monitor_interval: default_overload_monitor_interval(),
1681 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1682 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1683 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1684 min_load_shedding_percentage_above_hard_limit:
1685 default_min_load_shedding_percentage_above_hard_limit(),
1686 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1687 check_system_overload_at_signing: true,
1688 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1689 max_transaction_manager_per_object_queue_length:
1690 default_max_transaction_manager_per_object_queue_length(),
1691 admission_queue_capacity_fraction: default_admission_queue_capacity_fraction(),
1692 admission_queue_enabled: default_admission_queue_enabled(),
1693 admission_queue_failover_timeout: default_admission_queue_failover_timeout(),
1694 }
1695 }
1696}
1697
1698fn default_authority_overload_config() -> AuthorityOverloadConfig {
1699 AuthorityOverloadConfig::default()
1700}
1701
1702fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1703 Some(PolicyConfig::default_dos_protection_policy())
1704}
1705
1706#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1707pub struct Genesis {
1708 #[serde(flatten)]
1709 location: GenesisLocation,
1710
1711 #[serde(skip)]
1712 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1713}
1714
1715impl Genesis {
1716 pub fn new(genesis: genesis::Genesis) -> Self {
1717 Self {
1718 location: GenesisLocation::InPlace { genesis },
1719 genesis: Default::default(),
1720 }
1721 }
1722
1723 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1724 Self {
1725 location: GenesisLocation::File {
1726 genesis_file_location: path.into(),
1727 },
1728 genesis: Default::default(),
1729 }
1730 }
1731
1732 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1733 match &self.location {
1734 GenesisLocation::InPlace { genesis } => Ok(genesis),
1735 GenesisLocation::File {
1736 genesis_file_location,
1737 } => self
1738 .genesis
1739 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1740 }
1741 }
1742}
1743
1744#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1745#[serde(untagged)]
1746#[allow(clippy::large_enum_variant)]
1747enum GenesisLocation {
1748 InPlace {
1749 genesis: genesis::Genesis,
1750 },
1751 File {
1752 #[serde(rename = "genesis-file-location")]
1753 genesis_file_location: PathBuf,
1754 },
1755}
1756
1757#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1759pub struct KeyPairWithPath {
1760 #[serde(flatten)]
1761 location: KeyPairLocation,
1762
1763 #[serde(skip)]
1764 keypair: OnceCell<Arc<SuiKeyPair>>,
1765}
1766
1767#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1768#[serde_as]
1769#[serde(untagged)]
1770enum KeyPairLocation {
1771 InPlace {
1772 #[serde_as(as = "Arc<KeyPairBase64>")]
1773 value: Arc<SuiKeyPair>,
1774 },
1775 File {
1776 #[serde(rename = "path")]
1777 path: PathBuf,
1778 },
1779}
1780
1781impl KeyPairWithPath {
1782 pub fn new(kp: SuiKeyPair) -> Self {
1783 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1784 let arc_kp = Arc::new(kp);
1785 cell.set(arc_kp.clone()).expect("Failed to set keypair");
1787 Self {
1788 location: KeyPairLocation::InPlace { value: arc_kp },
1789 keypair: cell,
1790 }
1791 }
1792
1793 pub fn new_from_path(path: PathBuf) -> Self {
1794 let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
1795 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1797 |e| panic!("Invalid keypair file at path {:?}: {e}", &path),
1798 )))
1799 .expect("Failed to set keypair");
1800 Self {
1801 location: KeyPairLocation::File { path },
1802 keypair: cell,
1803 }
1804 }
1805
1806 pub fn keypair(&self) -> &SuiKeyPair {
1807 self.keypair
1808 .get_or_init(|| match &self.location {
1809 KeyPairLocation::InPlace { value } => value.clone(),
1810 KeyPairLocation::File { path } => {
1811 Arc::new(
1813 read_keypair_from_file(path).unwrap_or_else(|e| {
1814 panic!("Invalid keypair file at path {:?}: {e}", path)
1815 }),
1816 )
1817 }
1818 })
1819 .as_ref()
1820 }
1821}
1822
1823#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1825pub struct AuthorityKeyPairWithPath {
1826 #[serde(flatten)]
1827 location: AuthorityKeyPairLocation,
1828
1829 #[serde(skip)]
1830 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1831}
1832
1833#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1834#[serde_as]
1835#[serde(untagged)]
1836enum AuthorityKeyPairLocation {
1837 InPlace { value: Arc<AuthorityKeyPair> },
1838 File { path: PathBuf },
1839}
1840
1841impl AuthorityKeyPairWithPath {
1842 pub fn new(kp: AuthorityKeyPair) -> Self {
1843 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1844 let arc_kp = Arc::new(kp);
1845 cell.set(arc_kp.clone())
1847 .expect("Failed to set authority keypair");
1848 Self {
1849 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1850 keypair: cell,
1851 }
1852 }
1853
1854 pub fn new_from_path(path: PathBuf) -> Self {
1855 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1856 cell.set(Arc::new(
1858 read_authority_keypair_from_file(&path)
1859 .unwrap_or_else(|_| panic!("Invalid authority keypair file at path {:?}", &path)),
1860 ))
1861 .expect("Failed to set authority keypair");
1862 Self {
1863 location: AuthorityKeyPairLocation::File { path },
1864 keypair: cell,
1865 }
1866 }
1867
1868 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1869 self.keypair
1870 .get_or_init(|| match &self.location {
1871 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1872 AuthorityKeyPairLocation::File { path } => {
1873 Arc::new(
1875 read_authority_keypair_from_file(path).unwrap_or_else(|_| {
1876 panic!("Invalid authority keypair file {:?}", &path)
1877 }),
1878 )
1879 }
1880 })
1881 .as_ref()
1882 }
1883}
1884
1885#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1888#[serde(rename_all = "kebab-case")]
1889pub struct StateDebugDumpConfig {
1890 #[serde(skip_serializing_if = "Option::is_none")]
1891 pub dump_file_directory: Option<PathBuf>,
1892}
1893
1894fn read_credential_from_path_or_literal(value: &str) -> Result<String, std::io::Error> {
1895 let path = Path::new(value);
1896 if path.exists() && path.is_file() {
1897 std::fs::read_to_string(path).map(|content| content.trim().to_string())
1898 } else {
1899 Ok(value.to_string())
1900 }
1901}
1902
1903fn deserialize_remote_store_options<'de, D>(
1905 deserializer: D,
1906) -> Result<Vec<(String, String)>, D::Error>
1907where
1908 D: serde::Deserializer<'de>,
1909{
1910 use serde::de::Error;
1911
1912 let raw_options: Vec<(String, String)> = Vec::deserialize(deserializer)?;
1913 let mut processed_options = Vec::new();
1914
1915 for (key, value) in raw_options {
1916 let is_service_account_path = matches!(
1919 key.as_str(),
1920 "google_service_account"
1921 | "service_account"
1922 | "google_service_account_path"
1923 | "service_account_path"
1924 );
1925
1926 let processed_value = if is_service_account_path {
1927 value
1928 } else {
1929 match read_credential_from_path_or_literal(&value) {
1930 Ok(processed) => processed,
1931 Err(e) => {
1932 return Err(D::Error::custom(format!(
1933 "Failed to read credential for key '{}': {}",
1934 key, e
1935 )));
1936 }
1937 }
1938 };
1939
1940 processed_options.push((key, processed_value));
1941 }
1942
1943 Ok(processed_options)
1944}
1945
1946#[cfg(test)]
1947mod tests {
1948 use std::path::PathBuf;
1949
1950 use fastcrypto::traits::KeyPair;
1951 use rand::{SeedableRng, rngs::StdRng};
1952 use sui_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1953 use sui_types::base_types::ObjectID;
1954 use sui_types::crypto::{AuthorityKeyPair, NetworkKeyPair, SuiKeyPair, get_key_pair_from_rng};
1955
1956 use super::{
1957 AuthorityStorePruningConfig, ExecutionTimeObserverConfig, Genesis, StateArchiveConfig,
1958 };
1959 use crate::NodeConfig;
1960
1961 #[test]
1962 fn serialize_genesis_from_file() {
1963 let g = Genesis::new_from_file("path/to/file");
1964
1965 let s = serde_yaml::to_string(&g).unwrap();
1966 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
1967 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
1968 assert_eq!(g, loaded_genesis);
1969 }
1970
1971 #[test]
1972 fn fullnode_template() {
1973 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1974
1975 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1976 }
1977
1978 #[test]
1980 fn legacy_validator_config() {
1981 const FILE: &str = include_str!("../data/sui-node-legacy.yaml");
1982
1983 let template: NodeConfig = serde_yaml::from_str(FILE).unwrap();
1984 assert_eq!(
1985 template
1986 .authority_store_pruning_config
1987 .rpc_store_bitmap_periodic_compaction_days,
1988 None
1989 );
1990 }
1991
1992 #[test]
1993 fn rpc_store_bitmap_periodic_compaction_days_override_deserializes() {
1994 assert_eq!(
1995 AuthorityStorePruningConfig::default().rpc_store_bitmap_periodic_compaction_days,
1996 None
1997 );
1998
1999 let omitted: AuthorityStorePruningConfig = serde_yaml::from_str("{}").unwrap();
2000 assert_eq!(omitted.rpc_store_bitmap_periodic_compaction_days, None);
2001
2002 let disabled: AuthorityStorePruningConfig =
2003 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 0").unwrap();
2004 assert_eq!(disabled.rpc_store_bitmap_periodic_compaction_days, Some(0));
2005
2006 let configured: AuthorityStorePruningConfig =
2007 serde_yaml::from_str("rpc-store-bitmap-periodic-compaction-days: 17").unwrap();
2008 let serialized = serde_yaml::to_string(&configured).unwrap();
2009 let round_tripped: AuthorityStorePruningConfig = serde_yaml::from_str(&serialized).unwrap();
2010 assert_eq!(
2011 round_tripped.rpc_store_bitmap_periodic_compaction_days,
2012 Some(17)
2013 );
2014 }
2015
2016 #[test]
2017 fn execution_time_observer_config_tracked_ids() {
2018 let omitted: ExecutionTimeObserverConfig = serde_yaml::from_str("{}").unwrap();
2019 assert_eq!(omitted.object_utilization_metric_tracked_ids().count(), 0);
2020
2021 let yaml = r#"
2022 object-utilization-metric-tracked-ids:
2023 "0x0000000000000000000000000000000000000000000000000000000000000005": sui-system-state
2024 "0xe05dafb5133bcffb8d59f4e12465dc0e9faeaa05e3e342a08fe135800e3e4407": deepbook-sui-usdc
2025 "#;
2026 let configured: ExecutionTimeObserverConfig = serde_yaml::from_str(yaml).unwrap();
2027 let tracked: Vec<_> = configured.object_utilization_metric_tracked_ids().collect();
2028 assert_eq!(tracked.len(), 2);
2029 assert_eq!(
2030 tracked[0],
2031 (&ObjectID::from_single_byte(5), "sui-system-state")
2032 );
2033
2034 let serialized = serde_yaml::to_string(&configured).unwrap();
2035 let round_tripped: ExecutionTimeObserverConfig = serde_yaml::from_str(&serialized).unwrap();
2036 assert_eq!(
2037 round_tripped.object_utilization_metric_tracked_ids,
2038 configured.object_utilization_metric_tracked_ids
2039 );
2040 }
2041
2042 #[test]
2043 fn load_key_pairs_to_node_config() {
2044 let protocol_key_pair: AuthorityKeyPair =
2045 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2046 let worker_key_pair: NetworkKeyPair =
2047 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2048 let network_key_pair: NetworkKeyPair =
2049 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2050
2051 write_authority_keypair_to_file(&protocol_key_pair, PathBuf::from("protocol.key")).unwrap();
2052 write_keypair_to_file(
2053 &SuiKeyPair::Ed25519(worker_key_pair.copy()),
2054 PathBuf::from("worker.key"),
2055 )
2056 .unwrap();
2057 write_keypair_to_file(
2058 &SuiKeyPair::Ed25519(network_key_pair.copy()),
2059 PathBuf::from("network.key"),
2060 )
2061 .unwrap();
2062
2063 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
2064 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2065 assert_eq!(
2066 template.protocol_key_pair().public(),
2067 protocol_key_pair.public()
2068 );
2069 assert_eq!(
2070 template.network_key_pair().public(),
2071 network_key_pair.public()
2072 );
2073 assert_eq!(
2074 template.worker_key_pair().public(),
2075 worker_key_pair.public()
2076 );
2077 }
2078
2079 #[test]
2080 fn test_remote_store_options_file_path_support() {
2081 let temp_dir = std::env::temp_dir();
2083 let access_key_file = temp_dir.join("test_access_key");
2084 let secret_key_file = temp_dir.join("test_secret_key");
2085
2086 std::fs::write(&access_key_file, "test_access_key_value").unwrap();
2087 std::fs::write(&secret_key_file, "test_secret_key_value\n").unwrap();
2088
2089 let yaml_config = format!(
2090 r#"
2091object-store-config: null
2092concurrency: 5
2093ingestion-url: "https://example.com"
2094remote-store-options:
2095 - ["aws_access_key_id", "{}"]
2096 - ["aws_secret_access_key", "{}"]
2097 - ["literal_key", "literal_value"]
2098"#,
2099 access_key_file.to_string_lossy(),
2100 secret_key_file.to_string_lossy()
2101 );
2102
2103 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2104
2105 assert_eq!(config.remote_store_options.len(), 3);
2107
2108 let access_key_option = config
2109 .remote_store_options
2110 .iter()
2111 .find(|(key, _)| key == "aws_access_key_id")
2112 .unwrap();
2113 assert_eq!(access_key_option.1, "test_access_key_value");
2114
2115 let secret_key_option = config
2116 .remote_store_options
2117 .iter()
2118 .find(|(key, _)| key == "aws_secret_access_key")
2119 .unwrap();
2120 assert_eq!(secret_key_option.1, "test_secret_key_value");
2121
2122 let literal_option = config
2123 .remote_store_options
2124 .iter()
2125 .find(|(key, _)| key == "literal_key")
2126 .unwrap();
2127 assert_eq!(literal_option.1, "literal_value");
2128
2129 std::fs::remove_file(&access_key_file).ok();
2131 std::fs::remove_file(&secret_key_file).ok();
2132 }
2133
2134 #[test]
2135 fn test_remote_store_options_literal_values_only() {
2136 let yaml_config = r#"
2137object-store-config: null
2138concurrency: 5
2139ingestion-url: "https://example.com"
2140remote-store-options:
2141 - ["aws_access_key_id", "literal_access_key"]
2142 - ["aws_secret_access_key", "literal_secret_key"]
2143"#;
2144
2145 let config: StateArchiveConfig = serde_yaml::from_str(yaml_config).unwrap();
2146
2147 assert_eq!(config.remote_store_options.len(), 2);
2148 assert_eq!(config.remote_store_options[0].1, "literal_access_key");
2149 assert_eq!(config.remote_store_options[1].1, "literal_secret_key");
2150 }
2151
2152 #[test]
2153 fn test_remote_store_options_gcs_service_account_path_preserved() {
2154 let temp_dir = std::env::temp_dir();
2155 let service_account_file = temp_dir.join("test_service_account.json");
2156 let aws_key_file = temp_dir.join("test_aws_key");
2157
2158 std::fs::write(&service_account_file, r#"{"type": "service_account"}"#).unwrap();
2159 std::fs::write(&aws_key_file, "aws_key_value").unwrap();
2160
2161 let yaml_config = format!(
2162 r#"
2163object-store-config: null
2164concurrency: 5
2165ingestion-url: "gs://my-bucket"
2166remote-store-options:
2167 - ["service_account", "{}"]
2168 - ["google_service_account_path", "{}"]
2169 - ["aws_access_key_id", "{}"]
2170"#,
2171 service_account_file.to_string_lossy(),
2172 service_account_file.to_string_lossy(),
2173 aws_key_file.to_string_lossy()
2174 );
2175
2176 let config: StateArchiveConfig = serde_yaml::from_str(&yaml_config).unwrap();
2177
2178 assert_eq!(config.remote_store_options.len(), 3);
2179
2180 let service_account_option = config
2182 .remote_store_options
2183 .iter()
2184 .find(|(key, _)| key == "service_account")
2185 .unwrap();
2186 assert_eq!(
2187 service_account_option.1,
2188 service_account_file.to_string_lossy()
2189 );
2190
2191 let gcs_path_option = config
2193 .remote_store_options
2194 .iter()
2195 .find(|(key, _)| key == "google_service_account_path")
2196 .unwrap();
2197 assert_eq!(gcs_path_option.1, service_account_file.to_string_lossy());
2198
2199 let aws_option = config
2201 .remote_store_options
2202 .iter()
2203 .find(|(key, _)| key == "aws_access_key_id")
2204 .unwrap();
2205 assert_eq!(aws_option.1, "aws_key_value");
2206
2207 std::fs::remove_file(&service_account_file).ok();
2209 std::fs::remove_file(&aws_key_file).ok();
2210 }
2211
2212 mod intended_node_role_tests {
2213 use super::*;
2214 use crate::ConsensusConfig;
2215 use consensus_config::Parameters as ConsensusParameters;
2216 use fastcrypto::ed25519::Ed25519KeyPair;
2217 use sui_types::node_role::{FullNodeSyncMode, NodeRole};
2218
2219 fn fullnode_template_config() -> NodeConfig {
2220 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2221 serde_yaml::from_str(TEMPLATE).unwrap()
2222 }
2223
2224 fn minimal_consensus_config() -> ConsensusConfig {
2225 ConsensusConfig {
2226 db_path: PathBuf::from("/tmp/consensus"),
2227 db_retention_epochs: None,
2228 db_pruner_period_secs: None,
2229 max_pending_transactions: None,
2230 parameters: Default::default(),
2231 listen_address: None,
2232 external_address: None,
2233 }
2234 }
2235
2236 fn consensus_config_with_observer_peers() -> ConsensusConfig {
2237 let mut config = minimal_consensus_config();
2238 let kp = Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32]));
2239 let peer = consensus_config::PeerRecord {
2240 public_key: consensus_config::NetworkPublicKey::new(kp.public().clone()),
2241 address: "/ip4/127.0.0.1/udp/8080".parse().unwrap(),
2242 };
2243 let mut params = ConsensusParameters::default();
2244 params.observer.peers = vec![peer];
2245 config.parameters = Some(params);
2246 config
2247 }
2248
2249 #[test]
2250 fn validator_with_consensus_config() {
2251 let mut config = fullnode_template_config();
2252 config.consensus_config = Some(minimal_consensus_config());
2253 config.fullnode_sync_mode = None;
2254
2255 assert_eq!(config.intended_node_role(), NodeRole::Validator);
2256 }
2257
2258 #[test]
2259 fn fullnode_explicit_state_sync() {
2260 let mut config = fullnode_template_config();
2261 config.consensus_config = None;
2262 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2263
2264 assert_eq!(
2265 config.intended_node_role(),
2266 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2267 );
2268 }
2269
2270 #[test]
2271 fn fullnode_implicit_state_sync() {
2272 let mut config = fullnode_template_config();
2273 config.consensus_config = None;
2274 config.fullnode_sync_mode = None;
2275
2276 assert_eq!(
2277 config.intended_node_role(),
2278 NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
2279 );
2280 }
2281
2282 #[test]
2283 fn fullnode_consensus_observer() {
2284 let mut config = fullnode_template_config();
2285 config.consensus_config = Some(consensus_config_with_observer_peers());
2286 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2287
2288 assert_eq!(
2289 config.intended_node_role(),
2290 NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver)
2291 );
2292 }
2293
2294 #[test]
2295 #[should_panic(
2296 expected = "Consensus config should not be set for a StateSyncOnly full node"
2297 )]
2298 fn state_sync_with_consensus_config_panics() {
2299 let mut config = fullnode_template_config();
2300 config.consensus_config = Some(minimal_consensus_config());
2301 config.fullnode_sync_mode = Some(FullNodeSyncMode::StateSyncOnly);
2302
2303 config.intended_node_role();
2304 }
2305
2306 #[test]
2307 #[should_panic(expected = "Observer peers must be configured")]
2308 fn observer_without_peers_panics() {
2309 let mut config = fullnode_template_config();
2310 config.consensus_config = Some(minimal_consensus_config());
2311 config.fullnode_sync_mode = Some(FullNodeSyncMode::ConsensusObserver);
2312
2313 config.intended_node_role();
2314 }
2315 }
2316}
2317
2318#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
2321pub enum RunWithRange {
2322 Epoch(EpochId),
2323 Checkpoint(CheckpointSequenceNumber),
2324}
2325
2326impl RunWithRange {
2327 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
2329 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
2330 }
2331
2332 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
2333 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
2334 }
2335
2336 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
2337 match self {
2338 RunWithRange::Epoch(_) => None,
2339 RunWithRange::Checkpoint(seq) => Some(seq),
2340 }
2341 }
2342}