1use std::{
6 net::{IpAddr, Ipv4Addr, SocketAddr},
7 num::{NonZeroU32, NonZeroUsize},
8 path::{Path, PathBuf},
9 sync::Arc,
10 time::Duration,
11};
12
13use anyhow::Result;
14use iota_keys::keypair_file::{read_authority_keypair_from_file, read_keypair_from_file};
15use iota_metrics::MetricGroups;
16use iota_multiaddr::Multiaddr;
17use iota_names::config::IotaNamesConfig;
18use iota_sdk_crypto::simple::SimpleKeypair;
19use iota_sdk_types::Address;
20use iota_types::{
21 committee::EpochId,
22 crypto::{
23 AccountPrivateKey, AuthorityKeyPair, AuthorityPublicKeyBytes, KeypairTraits,
24 NetworkPrivateKey, get_key_pair_from_rng,
25 },
26 messages_checkpoint::CheckpointSequenceNumber,
27 supported_protocol_versions::{Chain, SupportedProtocolVersions},
28 traffic_control::PolicyConfig,
29};
30use once_cell::sync::OnceCell;
31use rand::{rand_core::UnwrapErr, rngs::SysRng};
32use serde::{Deserialize, Serialize};
33use starfish_config::Parameters as StarfishParameters;
34
35use crate::{
36 Config, certificate_deny_config::CertificateDenyConfig, genesis,
37 migration_tx_data::MigrationTxData, object_storage_config::ObjectStoreConfig, p2p::P2pConfig,
38 setting::Setting, transaction_deny_config::TransactionDenyConfig,
39 verifier_signing_config::VerifierSigningConfig,
40};
41
42pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = iota_types::transaction::DEFAULT_VALIDATOR_GAS_PRICE;
44
45pub const DEFAULT_COMMISSION_RATE: u64 = 200;
47
48pub const DEFAULT_FULL_CHECKPOINT_CONTENTS_CACHE_SIZE_MB: usize = 1024;
50
51#[derive(Clone, Debug, Deserialize, Serialize)]
52#[serde(rename_all = "kebab-case")]
53pub struct NodeConfig {
54 #[serde(default = "default_authority_key_pair")]
57 pub authority_key_pair: AuthorityKeyPairWithPath,
58 #[serde(default = "default_key_pair")]
61 pub protocol_key_pair: KeyPairWithPath,
62 #[serde(default = "default_key_pair")]
63 pub account_key_pair: KeyPairWithPath,
64 #[serde(default = "default_key_pair")]
67 pub network_key_pair: KeyPairWithPath,
68 pub db_path: PathBuf,
69
70 #[serde(default = "default_grpc_address")]
74 pub network_address: Multiaddr,
75 #[serde(default = "default_json_rpc_address")]
76 pub json_rpc_address: SocketAddr,
77
78 #[serde(default = "default_metrics_address")]
80 pub metrics_address: SocketAddr,
81
82 #[serde(default = "default_admin_interface_address")]
86 pub admin_interface_address: SocketAddr,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub consensus_config: Option<ConsensusConfig>,
91
92 #[serde(default = "default_enable_jsonrpc_api")]
95 pub enable_jsonrpc_api: bool,
96
97 #[serde(default, skip_serializing)]
101 pub enable_index_processing: Option<bool>,
102
103 #[serde(default)]
107 pub grpc_load_shed: Option<bool>,
108
109 #[serde(default = "default_grpc_concurrency_limit_per_core")]
126 pub grpc_concurrency_limit_per_core: NonZeroUsize,
127
128 #[serde(default)]
130 pub p2p_config: P2pConfig,
131
132 pub genesis: Genesis,
136
137 pub migration_tx_data_path: Option<PathBuf>,
139
140 #[serde(default = "default_authority_store_pruning_config")]
143 pub authority_store_pruning_config: AuthorityStorePruningConfig,
144
145 #[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
150 pub end_of_epoch_broadcast_channel_capacity: usize,
151
152 #[serde(default)]
156 pub checkpoint_executor_config: CheckpointExecutorConfig,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub metrics: Option<MetricsConfig>,
160
161 #[serde(skip)]
166 pub supported_protocol_versions: Option<SupportedProtocolVersions>,
167
168 #[serde(default)]
170 pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
171
172 #[serde(default)]
176 pub transaction_deny_config: TransactionDenyConfig,
177
178 #[serde(default)]
184 pub certificate_deny_config: CertificateDenyConfig,
185
186 #[serde(default)]
189 pub state_debug_dump_config: StateDebugDumpConfig,
190
191 #[serde(default)]
192 pub checkpoint_archive_config: Option<CheckpointArchiveConfig>,
193
194 #[serde(default)]
196 pub state_snapshot_write_config: StateSnapshotConfig,
197
198 #[serde(default = "default_transaction_kv_store_config")]
199 pub transaction_kv_store_read_config: TransactionKeyValueStoreReadConfig,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub transaction_kv_store_write_config: Option<TransactionKeyValueStoreWriteConfig>,
204
205 #[serde(default = "default_authority_overload_config")]
208 pub authority_overload_config: AuthorityOverloadConfig,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
214 pub run_with_range: Option<RunWithRange>,
215
216 #[serde(
222 skip_serializing_if = "is_default_traffic_controller_policy_config",
223 default = "default_traffic_controller_policy_config"
224 )]
225 pub policy_config: Option<PolicyConfig>,
226
227 #[serde(default)]
228 pub execution_cache_config: ExecutionCacheConfig,
229
230 #[serde(default = "default_full_checkpoint_contents_cache_size_mb")]
241 pub full_checkpoint_contents_cache_size_mb: usize,
242
243 #[serde(default = "bool_true")]
244 pub enable_validator_tx_finalizer: bool,
245
246 #[serde(default = "bool_true")]
252 pub enable_soft_locking: bool,
253
254 #[serde(default)]
255 pub verifier_signing_config: VerifierSigningConfig,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
261 pub enable_db_write_stall: Option<bool>,
262
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub iota_names_config: Option<IotaNamesConfig>,
265
266 #[serde(default)]
268 pub enable_grpc_api: bool,
269 #[serde(
275 default = "default_grpc_api_config",
276 skip_serializing_if = "is_default_grpc_api_config"
277 )]
278 pub grpc_api_config: Option<GrpcApiConfig>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
285 pub chain_override_for_testing: Option<Chain>,
286
287 #[serde(default, skip_serializing_if = "Option::is_none")]
290 pub validator_client_monitor_config:
291 Option<crate::validator_client_monitor_config::ValidatorClientMonitorConfig>,
292
293 #[serde(default, skip_serializing_if = "ListenerLimitsConfig::is_unset")]
295 pub validator_grpc_listener_limits: ListenerLimitsConfig,
296
297 #[serde(default, skip_serializing_if = "ListenerLimitsConfig::is_unset")]
299 pub json_rpc_listener_limits: ListenerLimitsConfig,
300
301 #[serde(default, skip_serializing_if = "ListenerLimitsConfig::is_unset")]
303 pub grpc_api_listener_limits: ListenerLimitsConfig,
304}
305
306#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
312#[serde(rename_all = "kebab-case")]
313pub struct ListenerLimitsConfig {
314 #[serde(default, skip_serializing_if = "Setting::is_unset")]
317 pub handshake_timeout: Setting<Duration>,
318
319 #[serde(default, skip_serializing_if = "Setting::is_unset")]
322 pub max_pending_connections: Setting<NonZeroUsize>,
323
324 #[serde(default, skip_serializing_if = "Setting::is_unset")]
326 pub max_connections: Setting<NonZeroUsize>,
327
328 #[serde(default, skip_serializing_if = "Setting::is_unset")]
330 pub max_connections_per_client_ip: Setting<NonZeroUsize>,
331
332 #[serde(default, skip_serializing_if = "Option::is_none")]
335 pub ipv6_prefix_len: Option<u8>,
336
337 #[serde(default, skip_serializing_if = "Setting::is_unset")]
340 pub max_connection_idle: Setting<Duration>,
341
342 #[serde(default, skip_serializing_if = "Setting::is_unset")]
344 pub max_concurrent_streams: Setting<NonZeroU32>,
345
346 #[serde(default, skip_serializing_if = "Setting::is_unset")]
349 pub http2_keepalive_interval: Setting<Duration>,
350
351 #[serde(default, skip_serializing_if = "Setting::is_unset")]
354 pub http1_header_read_timeout: Setting<Duration>,
355}
356
357impl ListenerLimitsConfig {
358 pub fn is_unset(&self) -> bool {
360 *self == Self::default()
361 }
362
363 fn validate(&self, key: &str) -> Result<()> {
366 for (name, duration) in [
367 ("handshake-timeout", self.handshake_timeout),
368 ("max-connection-idle", self.max_connection_idle),
369 ("http2-keepalive-interval", self.http2_keepalive_interval),
370 ("http1-header-read-timeout", self.http1_header_read_timeout),
371 ] {
372 if duration == Setting::Value(Duration::ZERO) {
373 anyhow::bail!(
374 "`{key}.{name}` is 0; set it greater than zero, or to `null` to turn it off"
375 );
376 }
377 }
378 if self.handshake_timeout == Setting::Disabled
381 && self.max_pending_connections != Setting::Disabled
382 {
383 anyhow::bail!(
384 "`{key}.handshake-timeout` is `null`, but `max-pending-connections` is on, and \
385 only the handshake timeout frees its slots; set a handshake timeout, or set \
386 `max-pending-connections` to `null` too"
387 );
388 }
389 if let Some(len) = self
390 .ipv6_prefix_len
391 .filter(|len| !(MIN_IPV6_PREFIX_LEN..=128).contains(len))
392 {
393 anyhow::bail!(
394 "`{key}.ipv6-prefix-len` is {len}; set it between {MIN_IPV6_PREFIX_LEN} and 128"
395 );
396 }
397 Ok(())
398 }
399}
400
401const MIN_IPV6_PREFIX_LEN: u8 = 32;
403
404#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
405#[serde(rename_all = "kebab-case")]
406pub struct TlsConfig {
407 cert: String,
409 key: String,
411}
412
413impl TlsConfig {
414 pub fn cert(&self) -> &str {
415 &self.cert
416 }
417
418 pub fn key(&self) -> &str {
419 &self.key
420 }
421}
422
423#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
425#[serde(rename_all = "kebab-case")]
426pub struct GrpcApiConfig {
427 #[serde(default = "default_grpc_api_address")]
429 pub address: SocketAddr,
430
431 #[serde(skip_serializing_if = "Option::is_none")]
435 pub tls: Option<TlsConfig>,
436
437 #[serde(default = "default_grpc_api_max_message_size_bytes")]
439 pub max_message_size_bytes: u32,
440
441 #[serde(default = "default_grpc_api_broadcast_buffer_size")]
443 pub broadcast_buffer_size: u32,
444
445 #[serde(default = "default_grpc_api_max_concurrent_stream_subscribers")]
451 pub max_concurrent_stream_subscribers: u32,
452
453 #[serde(default = "default_grpc_api_max_json_move_value_size")]
456 pub max_json_move_value_size: usize,
457
458 #[serde(default = "default_grpc_api_max_execute_transaction_batch_size")]
461 pub max_execute_transaction_batch_size: u32,
462
463 #[serde(default = "default_grpc_api_max_simulate_transaction_batch_size")]
466 pub max_simulate_transaction_batch_size: u32,
467
468 #[serde(default = "default_grpc_api_max_get_objects_batch_size")]
470 pub max_get_objects_batch_size: u32,
471
472 #[serde(default = "default_grpc_api_max_get_transactions_batch_size")]
475 pub max_get_transactions_batch_size: u32,
476
477 #[serde(default = "default_grpc_api_max_view_function_call_batch_size")]
480 pub max_view_function_call_batch_size: u32,
481
482 #[serde(default = "default_grpc_api_max_checkpoint_inclusion_timeout_ms")]
486 pub max_checkpoint_inclusion_timeout_ms: u64,
487}
488
489fn default_grpc_api_address() -> SocketAddr {
490 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 50051)
491}
492
493fn default_grpc_api_broadcast_buffer_size() -> u32 {
494 100
495}
496
497fn default_grpc_api_max_concurrent_stream_subscribers() -> u32 {
498 1024
499}
500
501fn default_grpc_api_max_message_size_bytes() -> u32 {
502 128 * 1024 * 1024 }
504
505fn default_grpc_api_max_json_move_value_size() -> usize {
506 1024 * 1024 }
508
509fn default_grpc_api_max_execute_transaction_batch_size() -> u32 {
510 20
511}
512
513fn default_grpc_api_max_simulate_transaction_batch_size() -> u32 {
514 20
515}
516
517fn default_grpc_api_max_get_objects_batch_size() -> u32 {
518 1000
519}
520
521fn default_grpc_api_max_get_transactions_batch_size() -> u32 {
522 1000
523}
524
525fn default_grpc_api_max_view_function_call_batch_size() -> u32 {
526 20
527}
528
529fn default_grpc_api_max_checkpoint_inclusion_timeout_ms() -> u64 {
530 60_000 }
532
533impl Default for GrpcApiConfig {
534 fn default() -> Self {
535 Self {
536 address: default_grpc_api_address(),
537 tls: None,
538 max_message_size_bytes: default_grpc_api_max_message_size_bytes(),
539 broadcast_buffer_size: default_grpc_api_broadcast_buffer_size(),
540 max_concurrent_stream_subscribers: default_grpc_api_max_concurrent_stream_subscribers(),
541 max_json_move_value_size: default_grpc_api_max_json_move_value_size(),
542 max_execute_transaction_batch_size: default_grpc_api_max_execute_transaction_batch_size(
543 ),
544 max_simulate_transaction_batch_size:
545 default_grpc_api_max_simulate_transaction_batch_size(),
546 max_get_objects_batch_size: default_grpc_api_max_get_objects_batch_size(),
547 max_get_transactions_batch_size: default_grpc_api_max_get_transactions_batch_size(),
548 max_view_function_call_batch_size: default_grpc_api_max_view_function_call_batch_size(),
549 max_checkpoint_inclusion_timeout_ms:
550 default_grpc_api_max_checkpoint_inclusion_timeout_ms(),
551 }
552 }
553}
554
555impl GrpcApiConfig {
556 const GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE: u32 = 4 * 1024 * 1024; const GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES: u32 =
560 Self::GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE;
561
562 pub fn tls_config(&self) -> Option<&TlsConfig> {
563 self.tls.as_ref()
564 }
565
566 pub fn max_message_size_bytes(&self) -> u32 {
567 self.max_message_size_bytes
569 .max(Self::GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES)
570 }
571
572 pub fn max_message_size_client_bytes(&self, client_max_message_size_bytes: Option<u32>) -> u32 {
576 client_max_message_size_bytes
577 .unwrap_or(Self::GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE)
580 .clamp(
582 Self::GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES,
583 self.max_message_size_bytes(),
584 )
585 }
586}
587
588#[derive(Clone, Debug, Default, Deserialize, Serialize)]
589#[serde(rename_all = "kebab-case")]
590pub struct ExecutionCacheConfig {
591 #[serde(default)]
592 pub writeback_cache: WritebackCacheConfig,
593}
594
595#[derive(Clone, Debug, Default, Deserialize, Serialize)]
596#[serde(rename_all = "kebab-case")]
597pub struct WritebackCacheConfig {
598 #[serde(default, skip_serializing_if = "Option::is_none")]
601 pub max_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
604 pub package_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
607 pub object_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
609 pub marker_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
611 pub object_by_id_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
614 pub transaction_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
616 pub executed_effect_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
618 pub effect_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub events_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
624 pub transaction_objects_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
629 pub backpressure_threshold: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
635 pub backpressure_threshold_for_rpc: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
645 pub backpressure_soft_limit_pct: Option<u32>,
646}
647
648impl WritebackCacheConfig {
649 pub fn max_cache_size(&self) -> u64 {
650 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MAX")
651 .ok()
652 .and_then(|s| s.parse().ok())
653 .or(self.max_cache_size)
654 .unwrap_or(100000)
655 }
656
657 pub fn package_cache_size(&self) -> u64 {
658 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_PACKAGE")
659 .ok()
660 .and_then(|s| s.parse().ok())
661 .or(self.package_cache_size)
662 .unwrap_or(1000)
663 }
664
665 pub fn object_cache_size(&self) -> u64 {
666 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT")
667 .ok()
668 .and_then(|s| s.parse().ok())
669 .or(self.object_cache_size)
670 .unwrap_or_else(|| self.max_cache_size())
671 }
672
673 pub fn marker_cache_size(&self) -> u64 {
674 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MARKER")
675 .ok()
676 .and_then(|s| s.parse().ok())
677 .or(self.marker_cache_size)
678 .unwrap_or_else(|| self.object_cache_size())
679 }
680
681 pub fn object_by_id_cache_size(&self) -> u64 {
682 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT_BY_ID")
683 .ok()
684 .and_then(|s| s.parse().ok())
685 .or(self.object_by_id_cache_size)
686 .unwrap_or_else(|| self.object_cache_size())
687 }
688
689 pub fn transaction_cache_size(&self) -> u64 {
690 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION")
691 .ok()
692 .and_then(|s| s.parse().ok())
693 .or(self.transaction_cache_size)
694 .unwrap_or_else(|| self.max_cache_size())
695 }
696
697 pub fn executed_effect_cache_size(&self) -> u64 {
698 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EXECUTED_EFFECT")
699 .ok()
700 .and_then(|s| s.parse().ok())
701 .or(self.executed_effect_cache_size)
702 .unwrap_or_else(|| self.transaction_cache_size())
703 }
704
705 pub fn effect_cache_size(&self) -> u64 {
706 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EFFECT")
707 .ok()
708 .and_then(|s| s.parse().ok())
709 .or(self.effect_cache_size)
710 .unwrap_or_else(|| self.executed_effect_cache_size())
711 }
712
713 pub fn events_cache_size(&self) -> u64 {
714 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EVENTS")
715 .ok()
716 .and_then(|s| s.parse().ok())
717 .or(self.events_cache_size)
718 .unwrap_or_else(|| self.transaction_cache_size())
719 }
720
721 pub fn transaction_objects_cache_size(&self) -> u64 {
722 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION_OBJECTS")
723 .ok()
724 .and_then(|s| s.parse().ok())
725 .or(self.transaction_objects_cache_size)
726 .unwrap_or(1000)
727 }
728
729 pub fn backpressure_threshold(&self) -> u64 {
730 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD")
731 .ok()
732 .and_then(|s| s.parse().ok())
733 .or(self.backpressure_threshold)
734 .unwrap_or(100_000)
735 }
736
737 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
738 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD_FOR_RPC")
739 .ok()
740 .and_then(|s| s.parse().ok())
741 .or(self.backpressure_threshold_for_rpc)
742 .unwrap_or(self.backpressure_threshold())
743 }
744
745 pub fn backpressure_soft_limit_pct(&self) -> u32 {
746 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_SOFT_LIMIT_PCT")
747 .ok()
748 .and_then(|s| s.parse().ok())
749 .or(self.backpressure_soft_limit_pct)
750 .unwrap_or(50)
751 .min(100)
752 }
753}
754
755#[derive(Clone, Debug, Deserialize, Serialize)]
756#[serde(rename_all = "kebab-case")]
757pub struct TransactionKeyValueStoreReadConfig {
758 #[serde(default = "default_base_url")]
759 pub base_url: String,
760
761 #[serde(default = "default_cache_size")]
762 pub cache_size: u64,
763}
764
765impl Default for TransactionKeyValueStoreReadConfig {
766 fn default() -> Self {
767 Self {
768 base_url: default_base_url(),
769 cache_size: default_cache_size(),
770 }
771 }
772}
773
774fn default_base_url() -> String {
775 "".to_string()
776}
777
778fn default_cache_size() -> u64 {
779 100_000
780}
781
782fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
783 TransactionKeyValueStoreReadConfig::default()
784}
785
786fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
787 AuthorityStorePruningConfig::default()
788}
789
790pub fn default_enable_jsonrpc_api() -> bool {
791 true
792}
793
794fn default_grpc_address() -> Multiaddr {
795 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
796}
797fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
798 AuthorityKeyPairWithPath::new(
799 get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut UnwrapErr(SysRng)).1,
800 )
801}
802
803fn default_key_pair() -> KeyPairWithPath {
804 KeyPairWithPath::new(AccountPrivateKey::random().into())
805}
806
807fn default_metrics_address() -> SocketAddr {
808 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
809}
810
811pub fn default_admin_interface_address() -> SocketAddr {
812 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1337)
813}
814
815pub fn default_json_rpc_address() -> SocketAddr {
816 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
817}
818
819pub fn default_grpc_api_config() -> Option<GrpcApiConfig> {
820 Some(GrpcApiConfig::default())
821}
822
823fn is_default_grpc_api_config(grpc_api_config: &Option<GrpcApiConfig>) -> bool {
824 serializes_like(grpc_api_config, &default_grpc_api_config())
825}
826
827fn serializes_like<T: Serialize>(value: &T, default: &T) -> bool {
833 match (serde_yaml::to_string(value), serde_yaml::to_string(default)) {
834 (Ok(value), Ok(default)) => value == default,
835 _ => false,
836 }
837}
838
839pub fn default_grpc_concurrency_limit_per_core() -> NonZeroUsize {
840 NonZeroUsize::new(1000).unwrap()
841}
842
843pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
844 128
845}
846
847pub fn default_full_checkpoint_contents_cache_size_mb() -> usize {
848 DEFAULT_FULL_CHECKPOINT_CONTENTS_CACHE_SIZE_MB
849}
850
851pub fn bool_true() -> bool {
852 true
853}
854
855impl Config for NodeConfig {}
856
857impl NodeConfig {
858 pub fn check_renamed_keys(&self) -> Result<()> {
868 let Some(enable_index_processing) = self.enable_index_processing else {
869 return Ok(());
870 };
871 if self.is_validator() || (enable_index_processing && self.enable_jsonrpc_api) {
872 tracing::warn!(
873 "`enable-index-processing` was renamed to `enable-jsonrpc-api` and has no \
874 effect; remove it from the config"
875 );
876 return Ok(());
877 }
878 anyhow::bail!(
879 "`enable-index-processing` was renamed to `enable-jsonrpc-api` (default true), which \
880 turns the JSON-RPC API and its index on or off together; remove the old key and \
881 set `enable-jsonrpc-api` to the value you want."
882 );
883 }
884
885 pub fn authority_key_pair(&self) -> &AuthorityKeyPair {
886 self.authority_key_pair.authority_keypair()
887 }
888
889 pub fn protocol_private_key(&self) -> NetworkPrivateKey {
890 self.protocol_key_pair.network_private_key()
891 }
892
893 pub fn network_private_key(&self) -> NetworkPrivateKey {
894 self.network_key_pair.network_private_key()
895 }
896
897 pub fn authority_public_key(&self) -> AuthorityPublicKeyBytes {
898 self.authority_key_pair().public().into()
899 }
900
901 pub fn db_path(&self) -> PathBuf {
902 self.db_path.join("live")
903 }
904
905 pub fn snapshot_path(&self) -> PathBuf {
906 self.db_path.join("snapshot")
907 }
908
909 pub fn network_address(&self) -> &Multiaddr {
910 &self.network_address
911 }
912
913 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
914 self.consensus_config.as_ref()
915 }
916
917 pub fn maintains_rpc_indexes(&self) -> bool {
920 self.consensus_config().is_none() && (self.enable_jsonrpc_api || self.enable_grpc_api)
921 }
922
923 pub fn genesis(&self) -> Result<&genesis::Genesis> {
924 self.genesis.genesis()
925 }
926
927 pub fn load_migration_tx_data(&self) -> Result<MigrationTxData> {
928 let Some(location) = &self.migration_tx_data_path else {
929 anyhow::bail!("no file location set");
930 };
931
932 let migration_tx_data = MigrationTxData::load(location)?;
934
935 migration_tx_data.validate_from_genesis(self.genesis.genesis()?)?;
937 Ok(migration_tx_data)
938 }
939
940 pub fn iota_address(&self) -> Address {
941 self.account_key_pair
942 .keypair()
943 .public_key()
944 .derive_address()
945 }
946
947 pub fn checkpoint_archive_config(&self) -> Option<&CheckpointArchiveConfig> {
948 self.checkpoint_archive_config.as_ref()
949 }
950
951 pub fn is_validator(&self) -> bool {
954 self.consensus_config.is_some()
955 }
956
957 pub fn validate(&self) -> Result<()> {
961 if self.is_validator() {
964 if self.enable_grpc_api {
965 anyhow::bail!(
966 "`enable-grpc-api` is set, but validators do not expose the gRPC API; turn \
967 it off, or move the API to a fullnode"
968 );
969 }
970 if self
971 .state_snapshot_write_config
972 .object_store_config
973 .is_some()
974 {
975 anyhow::bail!(
976 "`state-snapshot-write-config.object-store-config` is set, but snapshot \
977 upload is only supported on fullnodes; remove the setting or move the \
978 upload to a fullnode"
979 );
980 }
981 }
982 if self.enable_grpc_api && self.grpc_api_config.is_none() {
986 anyhow::bail!(
987 "`enable-grpc-api` is set but `grpc-api-config` is `null`; give it a value, \
988 remove the key to take the default config, or turn off `enable-grpc-api`"
989 );
990 }
991 if self
993 .state_snapshot_write_config
994 .object_store_config
995 .as_ref()
996 .is_some_and(|store| store.object_store.is_none())
997 {
998 anyhow::bail!(
999 "`state-snapshot-write-config.object-store-config` has no `object-store`; \
1000 snapshot upload needs a storage backend"
1001 );
1002 }
1003 if self
1007 .authority_store_pruning_config
1008 .num_epochs_to_retain_for_checkpoints()
1009 == Some(0)
1010 {
1011 anyhow::bail!(
1012 "`num-epochs-to-retain-for-checkpoints` is 0, but the previous epoch's \
1013 checkpoints are still needed after an epoch boundary; set it to 1 or more"
1014 );
1015 }
1016 for (key, limits) in [
1019 (
1020 "validator-grpc-listener-limits",
1021 &self.validator_grpc_listener_limits,
1022 ),
1023 ("json-rpc-listener-limits", &self.json_rpc_listener_limits),
1024 ("grpc-api-listener-limits", &self.grpc_api_listener_limits),
1025 ] {
1026 limits.validate(key)?;
1027 }
1028 Ok(())
1029 }
1030}
1031
1032#[derive(Debug, Clone, Deserialize, Serialize)]
1033#[serde(rename_all = "kebab-case")]
1034pub struct ConsensusConfig {
1035 pub db_path: PathBuf,
1037
1038 pub db_retention_epochs: Option<u64>,
1042
1043 pub db_pruner_period_secs: Option<u64>,
1047
1048 pub max_pending_transactions: Option<usize>,
1059
1060 pub max_submit_position: Option<usize>,
1066
1067 pub submit_delay_step_override_millis: Option<u64>,
1073
1074 #[serde(skip_serializing_if = "Option::is_none", alias = "starfish_parameters")]
1076 pub parameters: Option<StarfishParameters>,
1077
1078 #[serde(skip_serializing_if = "Option::is_none")]
1084 pub graduated_load_shedding_soft_limit_pct: Option<u32>,
1085}
1086
1087impl ConsensusConfig {
1088 pub fn db_path(&self) -> &Path {
1089 &self.db_path
1090 }
1091
1092 pub fn max_pending_transactions(&self) -> usize {
1096 self.max_pending_transactions.unwrap_or(20_000)
1097 }
1098
1099 pub fn graduated_load_shedding_soft_limit_pct(&self) -> u32 {
1104 self.graduated_load_shedding_soft_limit_pct
1105 .unwrap_or(50)
1106 .min(100)
1107 }
1108
1109 pub fn submit_delay_step_override(&self) -> Option<Duration> {
1110 self.submit_delay_step_override_millis
1111 .map(Duration::from_millis)
1112 }
1113
1114 pub fn db_retention_epochs(&self) -> u64 {
1115 self.db_retention_epochs.unwrap_or(0)
1116 }
1117
1118 pub fn db_pruner_period(&self) -> Duration {
1119 self.db_pruner_period_secs
1121 .map(Duration::from_secs)
1122 .unwrap_or(Duration::from_secs(3_600))
1123 }
1124}
1125
1126#[derive(Clone, Debug, Deserialize, Serialize)]
1127#[serde(rename_all = "kebab-case")]
1128pub struct CheckpointExecutorConfig {
1129 #[serde(default = "default_checkpoint_execution_max_concurrency")]
1134 pub checkpoint_execution_max_concurrency: usize,
1135
1136 #[serde(default = "default_local_execution_timeout_sec")]
1142 pub local_execution_timeout_sec: u64,
1143
1144 #[serde(default, skip_serializing_if = "Option::is_none")]
1149 pub data_ingestion_dir: Option<PathBuf>,
1150}
1151
1152#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1153#[serde(rename_all = "kebab-case")]
1154pub struct ExpensiveSafetyCheckConfig {
1155 #[serde(default)]
1160 enable_epoch_iota_conservation_check: bool,
1161
1162 #[serde(default)]
1166 enable_deep_per_tx_iota_conservation_check: bool,
1167
1168 #[serde(default)]
1171 force_disable_epoch_iota_conservation_check: bool,
1172
1173 #[serde(default)]
1176 enable_state_consistency_check: bool,
1177
1178 #[serde(default)]
1180 force_disable_state_consistency_check: bool,
1181}
1182
1183impl ExpensiveSafetyCheckConfig {
1184 pub fn new_enable_all() -> Self {
1185 Self {
1186 enable_epoch_iota_conservation_check: true,
1187 enable_deep_per_tx_iota_conservation_check: true,
1188 force_disable_epoch_iota_conservation_check: false,
1189 enable_state_consistency_check: true,
1190 force_disable_state_consistency_check: false,
1191 }
1192 }
1193
1194 pub fn new_disable_all() -> Self {
1195 Self {
1196 enable_epoch_iota_conservation_check: false,
1197 enable_deep_per_tx_iota_conservation_check: false,
1198 force_disable_epoch_iota_conservation_check: true,
1199 enable_state_consistency_check: false,
1200 force_disable_state_consistency_check: true,
1201 }
1202 }
1203
1204 pub fn force_disable_epoch_iota_conservation_check(&mut self) {
1205 self.force_disable_epoch_iota_conservation_check = true;
1206 }
1207
1208 pub fn enable_epoch_iota_conservation_check(&self) -> bool {
1209 (self.enable_epoch_iota_conservation_check || cfg!(debug_assertions))
1210 && !self.force_disable_epoch_iota_conservation_check
1211 }
1212
1213 pub fn force_disable_state_consistency_check(&mut self) {
1214 self.force_disable_state_consistency_check = true;
1215 }
1216
1217 pub fn enable_state_consistency_check(&self) -> bool {
1218 (self.enable_state_consistency_check || cfg!(debug_assertions))
1219 && !self.force_disable_state_consistency_check
1220 }
1221
1222 pub fn enable_deep_per_tx_iota_conservation_check(&self) -> bool {
1223 self.enable_deep_per_tx_iota_conservation_check || cfg!(debug_assertions)
1224 }
1225}
1226
1227fn default_checkpoint_execution_max_concurrency() -> usize {
1228 4
1229}
1230
1231fn default_local_execution_timeout_sec() -> u64 {
1232 30
1233}
1234
1235impl Default for CheckpointExecutorConfig {
1236 fn default() -> Self {
1237 Self {
1238 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
1239 local_execution_timeout_sec: default_local_execution_timeout_sec(),
1240 data_ingestion_dir: None,
1241 }
1242 }
1243}
1244
1245#[derive(Debug, Clone, Deserialize, Serialize)]
1246#[serde(rename_all = "kebab-case")]
1247pub struct AuthorityStorePruningConfig {
1248 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1250 pub num_latest_epoch_dbs_to_retain: usize,
1251 #[serde(default)]
1257 pub num_epochs_to_retain: u64,
1258 #[serde(
1267 default = "default_periodic_compaction_threshold_days",
1268 skip_serializing_if = "is_default_periodic_compaction_threshold_days"
1269 )]
1270 pub periodic_compaction_threshold_days: Option<usize>,
1271 #[serde(skip_serializing_if = "Option::is_none")]
1280 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1281 #[serde(skip_serializing_if = "Option::is_none")]
1294 pub num_epochs_to_retain_for_indexes: Option<u64>,
1295}
1296
1297pub const DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS: u64 = 30;
1301
1302fn default_num_latest_epoch_dbs_to_retain() -> usize {
1303 3
1304}
1305
1306fn default_periodic_compaction_threshold_days() -> Option<usize> {
1307 Some(1)
1308}
1309
1310fn is_default_periodic_compaction_threshold_days(days: &Option<usize>) -> bool {
1311 *days == default_periodic_compaction_threshold_days()
1312}
1313
1314impl Default for AuthorityStorePruningConfig {
1315 fn default() -> Self {
1316 Self {
1317 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1318 num_epochs_to_retain: 0,
1319 periodic_compaction_threshold_days: default_periodic_compaction_threshold_days(),
1320 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1321 num_epochs_to_retain_for_indexes: None,
1322 }
1323 }
1324}
1325
1326impl AuthorityStorePruningConfig {
1327 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1328 self.num_epochs_to_retain = num_epochs_to_retain;
1329 }
1330
1331 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1332 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1333 }
1334
1335 pub fn num_epochs_to_retain(&self) -> Option<u64> {
1338 (self.num_epochs_to_retain != u64::MAX).then_some(self.num_epochs_to_retain)
1339 }
1340
1341 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1346 match self.num_epochs_to_retain_for_checkpoints {
1347 Some(u64::MAX) => None,
1348 Some(n) => Some(n),
1349 None => Some(DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS),
1350 }
1351 }
1352
1353 pub fn num_epochs_to_retain_for_indexes(&self) -> Option<u64> {
1357 match self.num_epochs_to_retain_for_indexes {
1358 Some(n) if n == u64::MAX => None,
1359 Some(n) => Some(n),
1360 None => self.num_epochs_to_retain_for_checkpoints(),
1361 }
1362 }
1363
1364 pub fn check_index_retention_within_ledger(&self) -> Result<()> {
1368 let Some(ledger) = self.num_epochs_to_retain_for_checkpoints() else {
1369 return Ok(());
1371 };
1372 let index = self.num_epochs_to_retain_for_indexes();
1373 if index.is_none_or(|index| index > ledger) {
1374 let index = index.map_or("unlimited".to_owned(), |index| index.to_string());
1375 anyhow::bail!(
1376 "num-epochs-to-retain-for-indexes ({index}) must not exceed \
1377 num-epochs-to-retain-for-checkpoints ({ledger}): a longer index retention \
1378 lets iotax_queryTransactionBlocks return a transaction digest whose ledger \
1379 data has already been pruned. Lower num-epochs-to-retain-for-indexes or \
1380 raise num-epochs-to-retain-for-checkpoints."
1381 );
1382 }
1383 Ok(())
1384 }
1385}
1386
1387#[derive(Debug, Clone, Deserialize, Serialize)]
1388#[serde(rename_all = "kebab-case")]
1389pub struct MetricsConfig {
1390 #[serde(skip_serializing_if = "Option::is_none")]
1391 pub push_interval_seconds: Option<u64>,
1392 #[serde(skip_serializing_if = "Option::is_none")]
1393 pub push_url: Option<String>,
1394 #[serde(skip_serializing_if = "Option::is_none")]
1395 pub groups: Option<MetricGroups>,
1396}
1397
1398fn default_checkpoint_archive_download_concurrency() -> NonZeroUsize {
1399 NonZeroUsize::new(10).unwrap()
1400}
1401
1402fn default_checkpoint_archive_verify_concurrency() -> NonZeroUsize {
1403 std::thread::available_parallelism().unwrap_or(NonZeroUsize::new(4).unwrap())
1404}
1405
1406#[derive(Debug, Clone, Deserialize, Serialize)]
1409#[serde(rename_all = "kebab-case")]
1410pub struct CheckpointArchiveConfig {
1411 pub url: String,
1413 #[serde(default = "default_checkpoint_archive_download_concurrency")]
1415 pub download_concurrency: NonZeroUsize,
1416 #[serde(default = "default_checkpoint_archive_verify_concurrency")]
1419 pub verify_concurrency: NonZeroUsize,
1420}
1421
1422#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1430#[serde(rename_all = "kebab-case")]
1431pub struct StateSnapshotConfig {
1432 #[serde(skip_serializing_if = "Option::is_none")]
1433 pub object_store_config: Option<ObjectStoreConfig>,
1434 pub concurrency: usize,
1435}
1436
1437#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1438#[serde(rename_all = "kebab-case")]
1439pub struct TransactionKeyValueStoreWriteConfig {
1440 pub aws_access_key_id: String,
1441 pub aws_secret_access_key: String,
1442 pub aws_region: String,
1443 pub table_name: String,
1444 pub bucket_name: String,
1445 pub concurrency: usize,
1446}
1447
1448#[derive(Clone, Debug, Deserialize, Serialize)]
1453#[serde(rename_all = "kebab-case")]
1454pub struct AuthorityOverloadConfig {
1455 #[serde(default = "default_max_txn_age_in_queue")]
1459 pub max_txn_age_in_queue: Duration,
1460
1461 #[serde(default = "default_overload_monitor_interval")]
1463 pub overload_monitor_interval: Duration,
1464
1465 #[serde(default = "default_execution_queue_latency_soft_limit")]
1467 pub execution_queue_latency_soft_limit: Duration,
1468
1469 #[serde(default = "default_execution_queue_latency_hard_limit")]
1472 pub execution_queue_latency_hard_limit: Duration,
1473
1474 #[serde(default = "default_max_load_shedding_percentage")]
1476 pub max_load_shedding_percentage: u32,
1477
1478 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1481 pub min_load_shedding_percentage_above_hard_limit: u32,
1482
1483 #[serde(default = "default_safe_transaction_ready_rate")]
1486 pub safe_transaction_ready_rate: u32,
1487
1488 #[serde(default = "default_check_system_overload_at_signing")]
1491 pub check_system_overload_at_signing: bool,
1492
1493 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1496 pub check_system_overload_at_execution: bool,
1497
1498 #[serde(default = "default_max_transaction_manager_queue_length")]
1502 pub max_transaction_manager_queue_length: usize,
1503
1504 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1507 pub max_transaction_manager_per_object_queue_length: usize,
1508
1509 #[serde(default = "default_max_transaction_manager_queue_length_soft_limit_pct")]
1513 pub max_transaction_manager_queue_length_soft_limit_pct: u32,
1514}
1515
1516impl AuthorityOverloadConfig {
1517 pub fn max_transaction_manager_queue_length_soft_limit_pct(&self) -> u32 {
1520 self.max_transaction_manager_queue_length_soft_limit_pct
1521 .min(100)
1522 }
1523}
1524
1525fn default_max_txn_age_in_queue() -> Duration {
1526 Duration::from_millis(500)
1527}
1528
1529fn default_overload_monitor_interval() -> Duration {
1530 Duration::from_secs(10)
1531}
1532
1533fn default_execution_queue_latency_soft_limit() -> Duration {
1534 Duration::from_secs(1)
1535}
1536
1537fn default_execution_queue_latency_hard_limit() -> Duration {
1538 Duration::from_secs(10)
1539}
1540
1541fn default_max_load_shedding_percentage() -> u32 {
1542 95
1543}
1544
1545fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1546 50
1547}
1548
1549fn default_safe_transaction_ready_rate() -> u32 {
1550 100
1551}
1552
1553fn default_check_system_overload_at_signing() -> bool {
1554 true
1555}
1556
1557fn default_max_transaction_manager_queue_length() -> usize {
1558 100_000
1559}
1560
1561fn default_max_transaction_manager_queue_length_soft_limit_pct() -> u32 {
1562 50
1563}
1564
1565fn default_max_transaction_manager_per_object_queue_length() -> usize {
1566 20
1567}
1568
1569impl Default for AuthorityOverloadConfig {
1570 fn default() -> Self {
1571 Self {
1572 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1573 overload_monitor_interval: default_overload_monitor_interval(),
1574 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1575 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1576 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1577 min_load_shedding_percentage_above_hard_limit:
1578 default_min_load_shedding_percentage_above_hard_limit(),
1579 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1580 check_system_overload_at_signing: true,
1581 check_system_overload_at_execution: false,
1582 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1583 max_transaction_manager_queue_length_soft_limit_pct:
1584 default_max_transaction_manager_queue_length_soft_limit_pct(),
1585 max_transaction_manager_per_object_queue_length:
1586 default_max_transaction_manager_per_object_queue_length(),
1587 }
1588 }
1589}
1590
1591fn default_authority_overload_config() -> AuthorityOverloadConfig {
1592 AuthorityOverloadConfig::default()
1593}
1594
1595fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1596 Some(PolicyConfig::default_dos_protection_policy())
1597}
1598
1599fn is_default_traffic_controller_policy_config(policy_config: &Option<PolicyConfig>) -> bool {
1600 serializes_like(policy_config, &default_traffic_controller_policy_config())
1601}
1602
1603#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1604pub struct Genesis {
1605 #[serde(flatten)]
1606 location: Option<GenesisLocation>,
1607
1608 #[serde(skip)]
1609 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1610}
1611
1612impl Genesis {
1613 pub fn new(genesis: genesis::Genesis) -> Self {
1614 Self {
1615 location: Some(GenesisLocation::InPlace {
1616 genesis: Box::new(genesis),
1617 }),
1618 genesis: Default::default(),
1619 }
1620 }
1621
1622 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1623 Self {
1624 location: Some(GenesisLocation::File {
1625 genesis_file_location: path.into(),
1626 }),
1627 genesis: Default::default(),
1628 }
1629 }
1630
1631 pub fn new_empty() -> Self {
1632 Self {
1633 location: None,
1634 genesis: Default::default(),
1635 }
1636 }
1637
1638 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1639 match &self.location {
1640 Some(GenesisLocation::InPlace { genesis }) => Ok(genesis),
1641 Some(GenesisLocation::File {
1642 genesis_file_location,
1643 }) => self
1644 .genesis
1645 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1646 None => anyhow::bail!("no genesis location set"),
1647 }
1648 }
1649}
1650
1651#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1652#[serde(untagged)]
1653enum GenesisLocation {
1654 InPlace {
1655 genesis: Box<genesis::Genesis>,
1656 },
1657 File {
1658 #[serde(rename = "genesis-file-location")]
1659 genesis_file_location: PathBuf,
1660 },
1661}
1662
1663#[derive(Clone, Debug, Deserialize, Serialize)]
1666pub struct KeyPairWithPath {
1667 #[serde(flatten)]
1668 location: KeyPairLocation,
1669
1670 #[serde(skip)]
1671 keypair: OnceCell<Arc<SimpleKeypair>>,
1672}
1673
1674impl PartialEq for KeyPairWithPath {
1675 fn eq(&self, other: &Self) -> bool {
1676 self.location == other.location
1677 }
1678}
1679
1680impl Eq for KeyPairWithPath {}
1681
1682#[derive(Debug, Clone, Deserialize, Serialize)]
1683#[serde(untagged)]
1684enum KeyPairLocation {
1685 InPlace {
1686 #[serde(with = "bech32_formatted_keypair")]
1687 value: Arc<SimpleKeypair>,
1688 },
1689 File {
1690 path: PathBuf,
1691 },
1692}
1693
1694impl PartialEq for KeyPairLocation {
1695 fn eq(&self, other: &Self) -> bool {
1696 match (self, other) {
1697 (Self::InPlace { value: a }, Self::InPlace { value: b }) => {
1698 a.to_bytes() == b.to_bytes()
1699 }
1700 (Self::File { path: a }, Self::File { path: b }) => a == b,
1701 _ => false,
1702 }
1703 }
1704}
1705
1706impl Eq for KeyPairLocation {}
1707
1708impl KeyPairWithPath {
1709 pub fn new(kp: SimpleKeypair) -> Self {
1710 let cell: OnceCell<Arc<SimpleKeypair>> = OnceCell::new();
1711 let arc_kp = Arc::new(kp);
1712 cell.set(arc_kp.clone()).expect("failed to set keypair");
1715 Self {
1716 location: KeyPairLocation::InPlace { value: arc_kp },
1717 keypair: cell,
1718 }
1719 }
1720
1721 pub fn new_from_path(path: PathBuf) -> Self {
1722 let cell: OnceCell<Arc<SimpleKeypair>> = OnceCell::new();
1723 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1726 |e| panic!("invalid keypair file at path {path:?}: {e}"),
1727 )))
1728 .expect("failed to set keypair");
1729 Self {
1730 location: KeyPairLocation::File { path },
1731 keypair: cell,
1732 }
1733 }
1734
1735 pub fn keypair(&self) -> &SimpleKeypair {
1736 self.keypair
1737 .get_or_init(|| match &self.location {
1738 KeyPairLocation::InPlace { value } => value.clone(),
1739 KeyPairLocation::File { path } => {
1740 Arc::new(
1743 read_keypair_from_file(path).unwrap_or_else(|e| {
1744 panic!("invalid keypair file at path {path:?}: {e}")
1745 }),
1746 )
1747 }
1748 })
1749 .as_ref()
1750 }
1751
1752 pub fn network_private_key(&self) -> NetworkPrivateKey {
1756 self.keypair()
1757 .as_opt_ed25519()
1758 .cloned()
1759 .expect("only Ed25519 network keys are allowed")
1760 }
1761}
1762
1763#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1766pub struct AuthorityKeyPairWithPath {
1767 #[serde(flatten)]
1768 location: AuthorityKeyPairLocation,
1769
1770 #[serde(skip)]
1771 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1772}
1773
1774#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1775#[serde(untagged)]
1776enum AuthorityKeyPairLocation {
1777 InPlace { value: Arc<AuthorityKeyPair> },
1778 File { path: PathBuf },
1779}
1780
1781impl AuthorityKeyPairWithPath {
1782 pub fn new(kp: AuthorityKeyPair) -> Self {
1783 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1784 let arc_kp = Arc::new(kp);
1785 cell.set(arc_kp.clone())
1788 .expect("failed to set authority keypair");
1789 Self {
1790 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1791 keypair: cell,
1792 }
1793 }
1794
1795 pub fn new_from_path(path: PathBuf) -> Self {
1796 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1797 cell.set(Arc::new(
1800 read_authority_keypair_from_file(&path)
1801 .unwrap_or_else(|_| panic!("invalid authority keypair file at path {path:?}")),
1802 ))
1803 .expect("failed to set authority keypair");
1804 Self {
1805 location: AuthorityKeyPairLocation::File { path },
1806 keypair: cell,
1807 }
1808 }
1809
1810 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1811 self.keypair
1812 .get_or_init(|| match &self.location {
1813 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1814 AuthorityKeyPairLocation::File { path } => {
1815 Arc::new(
1818 read_authority_keypair_from_file(path)
1819 .unwrap_or_else(|_| panic!("invalid authority keypair file {path:?}")),
1820 )
1821 }
1822 })
1823 .as_ref()
1824 }
1825}
1826
1827#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1830#[serde(rename_all = "kebab-case")]
1831pub struct StateDebugDumpConfig {
1832 #[serde(skip_serializing_if = "Option::is_none")]
1833 pub dump_file_directory: Option<PathBuf>,
1834}
1835
1836#[cfg(test)]
1837mod tests {
1838 use std::{
1839 num::{NonZeroU32, NonZeroUsize},
1840 path::PathBuf,
1841 time::Duration,
1842 };
1843
1844 use fastcrypto::traits::KeyPair;
1845 use iota_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1846 use iota_sdk_crypto::simple::SimpleKeypair;
1847 use iota_types::{
1848 crypto::{AuthorityKeyPair, NetworkPrivateKey, get_key_pair_from_rng},
1849 traffic_control::PolicyConfig,
1850 };
1851 use rand::{SeedableRng, rngs::StdRng};
1852 use serde::Serialize;
1853 use serde_yaml::Value;
1854
1855 use super::{
1856 AuthorityStorePruningConfig, DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS, Genesis,
1857 GrpcApiConfig, ListenerLimitsConfig, ObjectStoreConfig, default_grpc_api_config,
1858 default_periodic_compaction_threshold_days, default_traffic_controller_policy_config,
1859 };
1860 use crate::{
1861 ConsensusConfig, NodeConfig, object_storage_config::ObjectStoreType, setting::Setting,
1862 };
1863
1864 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1865
1866 const POLICY_CONFIG: &[&str] = &["policy-config"];
1867 const GRPC_API_CONFIG: &[&str] = &["grpc-api-config"];
1868 const COMPACTION_THRESHOLD: &[&str] = &[
1869 "authority-store-pruning-config",
1870 "periodic-compaction-threshold-days",
1871 ];
1872
1873 fn template_config() -> NodeConfig {
1874 serde_yaml::from_str(TEMPLATE).unwrap()
1875 }
1876
1877 fn consensus_config() -> super::ConsensusConfig {
1878 serde_yaml::from_str("db-path: /opt/iota/consensus-db").unwrap()
1879 }
1880
1881 fn object_store_config() -> ObjectStoreConfig {
1882 ObjectStoreConfig {
1883 object_store: Some(ObjectStoreType::File),
1884 directory: Some(PathBuf::from("/opt/iota/snapshots")),
1885 ..Default::default()
1886 }
1887 }
1888
1889 fn round_trip(config: &NodeConfig) -> NodeConfig {
1890 serde_yaml::from_str(&serde_yaml::to_string(config).unwrap()).unwrap()
1891 }
1892
1893 fn as_yaml<T: Serialize>(value: &T) -> String {
1894 serde_yaml::to_string(value).unwrap()
1895 }
1896
1897 fn written_at(value: &Value, path: &[&str]) -> Option<Value> {
1900 let (last, parents) = path.split_last().unwrap();
1901 let mut current = value;
1902 for name in parents {
1903 current = current
1904 .as_mapping()
1905 .unwrap()
1906 .get(&Value::String((*name).to_owned()))
1907 .unwrap();
1908 }
1909 current
1910 .as_mapping()
1911 .unwrap()
1912 .get(&Value::String((*last).to_owned()))
1913 .cloned()
1914 }
1915
1916 #[test]
1917 fn a_retention_below_two_is_no_longer_rounded_up() {
1918 let config = AuthorityStorePruningConfig {
1919 num_epochs_to_retain_for_checkpoints: Some(1),
1920 ..Default::default()
1921 };
1922 assert_eq!(config.num_epochs_to_retain_for_checkpoints(), Some(1));
1923 }
1924
1925 #[test]
1926 fn index_retention_within_ledger_retention_is_accepted() {
1927 let config = AuthorityStorePruningConfig {
1928 num_epochs_to_retain_for_checkpoints: Some(2),
1929 num_epochs_to_retain_for_indexes: Some(2),
1930 ..Default::default()
1931 };
1932 assert!(config.check_index_retention_within_ledger().is_ok());
1933 }
1934
1935 #[test]
1936 fn index_retention_beyond_ledger_retention_is_refused() {
1937 let config = AuthorityStorePruningConfig {
1938 num_epochs_to_retain_for_checkpoints: Some(2),
1939 num_epochs_to_retain_for_indexes: Some(3),
1940 ..Default::default()
1941 };
1942 let err = config
1943 .check_index_retention_within_ledger()
1944 .unwrap_err()
1945 .to_string();
1946 assert!(err.contains("num-epochs-to-retain-for-indexes"), "{err}");
1947 assert!(
1948 err.contains("num-epochs-to-retain-for-checkpoints"),
1949 "{err}"
1950 );
1951 assert!(err.contains('3'), "{err}");
1952 assert!(err.contains('2'), "{err}");
1953 }
1954
1955 #[test]
1956 fn the_narrowest_ledger_retention_refuses_a_longer_index_retention() {
1957 let config = AuthorityStorePruningConfig {
1958 num_epochs_to_retain_for_checkpoints: Some(1),
1959 num_epochs_to_retain_for_indexes: Some(2),
1960 ..Default::default()
1961 };
1962 assert!(config.check_index_retention_within_ledger().is_err());
1963 }
1964
1965 #[test]
1966 fn equal_narrowest_retentions_are_accepted() {
1967 let config = AuthorityStorePruningConfig {
1968 num_epochs_to_retain_for_checkpoints: Some(1),
1969 num_epochs_to_retain_for_indexes: Some(1),
1970 ..Default::default()
1971 };
1972 assert!(config.check_index_retention_within_ledger().is_ok());
1973 }
1974
1975 #[test]
1976 fn unbounded_ledger_retention_accepts_any_index_retention() {
1977 let config = AuthorityStorePruningConfig {
1978 num_epochs_to_retain_for_checkpoints: Some(u64::MAX),
1979 num_epochs_to_retain_for_indexes: Some(1000),
1980 ..Default::default()
1981 };
1982 assert!(config.check_index_retention_within_ledger().is_ok());
1983 }
1984
1985 #[test]
1986 fn unset_ledger_retention_keeps_the_default_and_the_index_follows_it() {
1987 let config = AuthorityStorePruningConfig {
1988 num_epochs_to_retain_for_checkpoints: None,
1989 num_epochs_to_retain_for_indexes: None,
1990 ..Default::default()
1991 };
1992 assert_eq!(
1993 config.num_epochs_to_retain_for_checkpoints(),
1994 Some(DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS)
1995 );
1996 assert_eq!(
1997 config.num_epochs_to_retain_for_indexes(),
1998 Some(DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS)
1999 );
2000 assert!(config.check_index_retention_within_ledger().is_ok());
2001 }
2002
2003 #[test]
2004 fn unset_index_retention_follows_the_ledger() {
2005 let config = AuthorityStorePruningConfig {
2006 num_epochs_to_retain_for_checkpoints: Some(3),
2007 num_epochs_to_retain_for_indexes: None,
2008 ..Default::default()
2009 };
2010 assert_eq!(config.num_epochs_to_retain_for_indexes(), Some(3));
2011 assert!(config.check_index_retention_within_ledger().is_ok());
2012 }
2013
2014 #[test]
2015 fn index_pruning_turned_off_against_a_bounded_ledger_is_refused() {
2016 let config = AuthorityStorePruningConfig {
2018 num_epochs_to_retain_for_checkpoints: Some(2),
2019 num_epochs_to_retain_for_indexes: Some(u64::MAX),
2020 ..Default::default()
2021 };
2022 let err = config
2023 .check_index_retention_within_ledger()
2024 .unwrap_err()
2025 .to_string();
2026 assert!(err.contains("unlimited"), "{err}");
2027 }
2028
2029 #[test]
2030 fn max_retention_means_pruning_is_off_for_every_knob() {
2031 let config = AuthorityStorePruningConfig {
2032 num_epochs_to_retain: u64::MAX,
2033 num_epochs_to_retain_for_checkpoints: Some(u64::MAX),
2034 num_epochs_to_retain_for_indexes: Some(u64::MAX),
2035 ..Default::default()
2036 };
2037 assert_eq!(config.num_epochs_to_retain(), None);
2038 assert_eq!(config.num_epochs_to_retain_for_checkpoints(), None);
2039 assert_eq!(config.num_epochs_to_retain_for_indexes(), None);
2040 }
2041
2042 #[test]
2043 fn a_fullnode_serving_an_api_builds_the_indexes() {
2044 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2045
2046 let config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2047 assert!(config.maintains_rpc_indexes());
2048 }
2049
2050 #[test]
2051 fn a_fullnode_with_both_apis_off_builds_no_indexes() {
2052 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2053
2054 let mut config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2055 config.enable_jsonrpc_api = false;
2056 config.enable_grpc_api = false;
2057 assert!(!config.maintains_rpc_indexes());
2058 }
2059
2060 #[test]
2061 fn a_validator_builds_no_indexes_even_with_an_api_enabled() {
2062 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2063
2064 let mut config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2065 config.enable_jsonrpc_api = true;
2066 config.consensus_config = Some(ConsensusConfig {
2067 db_path: PathBuf::from("/tmp/consensus"),
2068 db_retention_epochs: None,
2069 db_pruner_period_secs: None,
2070 max_pending_transactions: None,
2071 max_submit_position: None,
2072 submit_delay_step_override_millis: None,
2073 parameters: None,
2074 graduated_load_shedding_soft_limit_pct: None,
2075 });
2076 assert!(!config.maintains_rpc_indexes());
2077 }
2078
2079 #[test]
2080 fn serialize_genesis_from_file() {
2081 let g = Genesis::new_from_file("path/to/file");
2082
2083 let s = serde_yaml::to_string(&g).unwrap();
2084 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
2085 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
2086 assert_eq!(g, loaded_genesis);
2087 }
2088
2089 #[test]
2090 fn fullnode_template() {
2091 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2092
2093 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2094 }
2095
2096 #[test]
2097 fn validate_requires_a_grpc_config_when_the_api_is_enabled() {
2098 let mut config = template_config();
2101 config.grpc_api_config = None;
2102 config.validate().unwrap();
2103
2104 config.enable_grpc_api = true;
2105 let err = config.validate().unwrap_err().to_string();
2106 assert!(err.contains("`grpc-api-config` is `null`"), "{err}");
2107
2108 config.grpc_api_config = Some(GrpcApiConfig::default());
2109 config.validate().unwrap();
2110
2111 config.grpc_api_config = None;
2114 config.consensus_config = Some(consensus_config());
2115 let err = config.validate().unwrap_err().to_string();
2116 assert!(err.contains("validators do not expose"), "{err}");
2117 }
2118
2119 #[test]
2120 fn validate_rejects_the_grpc_api_on_a_validator() {
2121 let mut config = template_config();
2122 config.consensus_config = Some(consensus_config());
2123 config.validate().unwrap();
2124
2125 config.enable_grpc_api = true;
2126 let err = config.validate().unwrap_err().to_string();
2127 assert!(err.contains("validators do not expose"), "{err}");
2128
2129 config.consensus_config = None;
2131 config.validate().unwrap();
2132 }
2133
2134 #[test]
2135 fn validate_rejects_snapshot_upload_on_a_validator() {
2136 let mut config = template_config();
2137 config.state_snapshot_write_config.object_store_config = Some(object_store_config());
2138 config.validate().unwrap();
2139
2140 config.consensus_config = Some(consensus_config());
2141 let err = config.validate().unwrap_err().to_string();
2142 assert!(err.contains("snapshot upload"), "{err}");
2143 }
2144
2145 #[test]
2146 fn validate_rejects_a_snapshot_store_without_a_backend() {
2147 let mut config = template_config();
2148 config.state_snapshot_write_config.object_store_config = Some(ObjectStoreConfig::default());
2149
2150 let err = config.validate().unwrap_err().to_string();
2151 assert!(err.contains("storage backend"), "{err}");
2152 }
2153
2154 #[test]
2155 fn validate_rejects_a_zero_checkpoint_retention() {
2156 let mut config = template_config();
2157 config
2158 .authority_store_pruning_config
2159 .set_num_epochs_to_retain_for_checkpoints(Some(0));
2160 let err = config.validate().unwrap_err().to_string();
2161 assert!(
2162 err.contains("num-epochs-to-retain-for-checkpoints"),
2163 "{err}"
2164 );
2165
2166 config
2167 .authority_store_pruning_config
2168 .set_num_epochs_to_retain_for_checkpoints(Some(1));
2169 config.validate().unwrap();
2170 }
2171
2172 const LISTENER_LIMITS_KEYS: [&str; 3] = [
2174 "validator-grpc-listener-limits",
2175 "json-rpc-listener-limits",
2176 "grpc-api-listener-limits",
2177 ];
2178
2179 fn config_with_listener_limits(key: &str, limits: &str) -> NodeConfig {
2181 serde_yaml::from_str(&format!("{TEMPLATE}\n{key}: {limits}\n")).unwrap()
2182 }
2183
2184 #[test]
2190 fn validate_rejects_a_handshake_timeout_turned_off_alone() {
2191 for key in LISTENER_LIMITS_KEYS {
2192 for limits in [
2193 "{handshake-timeout: null}",
2194 "{handshake-timeout: null, max-pending-connections: 1}",
2195 ] {
2196 let err = config_with_listener_limits(key, limits)
2197 .validate()
2198 .unwrap_err()
2199 .to_string();
2200 assert!(
2201 err.contains(&format!("`{key}.handshake-timeout` is `null`")),
2202 "{limits}: {err}"
2203 );
2204 }
2205 config_with_listener_limits(
2206 key,
2207 "{handshake-timeout: null, max-pending-connections: null}",
2208 )
2209 .validate()
2210 .unwrap();
2211 }
2212 }
2213
2214 #[test]
2217 fn validate_rejects_a_zero_listener_duration() {
2218 for key in LISTENER_LIMITS_KEYS {
2219 for name in [
2220 "handshake-timeout",
2221 "max-connection-idle",
2222 "http2-keepalive-interval",
2223 "http1-header-read-timeout",
2224 ] {
2225 let err =
2226 config_with_listener_limits(key, &format!("{{{name}: {{secs: 0, nanos: 0}}}}"))
2227 .validate()
2228 .unwrap_err()
2229 .to_string();
2230 assert!(err.contains(&format!("`{key}.{name}` is 0")), "{err}");
2231
2232 config_with_listener_limits(key, &format!("{{{name}: {{secs: 1, nanos: 0}}}}"))
2233 .validate()
2234 .unwrap();
2235 }
2236 }
2237 }
2238
2239 #[test]
2241 fn validate_rejects_an_ipv6_prefix_len_out_of_range() {
2242 for key in LISTENER_LIMITS_KEYS {
2243 for len in [0, 31, 129] {
2244 let err = config_with_listener_limits(key, &format!("{{ipv6-prefix-len: {len}}}"))
2245 .validate()
2246 .unwrap_err()
2247 .to_string();
2248 assert!(
2249 err.contains(&format!("`{key}.ipv6-prefix-len` is {len}")),
2250 "{err}"
2251 );
2252 }
2253 for len in [32, 128] {
2254 config_with_listener_limits(key, &format!("{{ipv6-prefix-len: {len}}}"))
2255 .validate()
2256 .unwrap();
2257 }
2258 }
2259 }
2260
2261 #[test]
2262 fn enable_soft_locking_defaults_to_enabled() {
2263 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2266
2267 let config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2268 assert!(config.enable_soft_locking);
2269 }
2270
2271 #[test]
2275 fn renamed_enable_index_processing_key() {
2276 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2277 let with_old_key = |value: bool| {
2278 let mut template: serde_yaml::Value = serde_yaml::from_str(TEMPLATE).unwrap();
2279 template
2280 .as_mapping_mut()
2281 .unwrap()
2282 .insert("enable-index-processing".into(), value.into());
2283 serde_yaml::from_value::<NodeConfig>(template).unwrap()
2284 };
2285
2286 let mut config = with_old_key(false);
2288 assert_eq!(config.enable_index_processing, Some(false));
2289 assert!(config.enable_jsonrpc_api);
2290 let err = config.check_renamed_keys().unwrap_err().to_string();
2291 assert!(err.contains("enable-index-processing"), "{err}");
2292 assert!(err.contains("enable-jsonrpc-api"), "{err}");
2293
2294 config.consensus_config = Some(consensus_config());
2296 assert!(config.check_renamed_keys().is_ok());
2297
2298 let mut config = with_old_key(true);
2300 assert!(config.check_renamed_keys().is_ok());
2301 config.enable_jsonrpc_api = false;
2302 assert!(config.check_renamed_keys().is_err());
2303
2304 let serialized = serde_yaml::to_string(&config).unwrap();
2306 assert!(
2307 !serialized.contains("enable-index-processing"),
2308 "{serialized}"
2309 );
2310
2311 let config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2312 assert!(config.check_renamed_keys().is_ok());
2313 }
2314
2315 #[test]
2316 fn load_key_pairs_to_node_config() {
2317 let authority_key_pair: AuthorityKeyPair =
2318 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2319 let protocol_private_key: NetworkPrivateKey =
2320 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2321 let network_private_key: NetworkPrivateKey =
2322 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2323
2324 write_authority_keypair_to_file(&authority_key_pair, PathBuf::from("authority.key"))
2325 .unwrap();
2326 write_keypair_to_file(
2327 &SimpleKeypair::from(protocol_private_key.clone()),
2328 PathBuf::from("protocol.key"),
2329 )
2330 .unwrap();
2331 write_keypair_to_file(
2332 &SimpleKeypair::from(network_private_key.clone()),
2333 PathBuf::from("network.key"),
2334 )
2335 .unwrap();
2336
2337 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
2338 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2339 assert_eq!(
2340 template.authority_key_pair().public(),
2341 authority_key_pair.public()
2342 );
2343 assert_eq!(
2344 template.network_private_key().public_key(),
2345 network_private_key.public_key()
2346 );
2347 assert_eq!(
2348 template.protocol_private_key().public_key(),
2349 protocol_private_key.public_key()
2350 );
2351 }
2352
2353 #[test]
2354 fn a_policy_config_survives_a_round_trip_in_all_three_states() {
2355 let mut config = template_config();
2356
2357 config.policy_config = None;
2358 assert!(round_trip(&config).policy_config.is_none());
2359
2360 config.policy_config = default_traffic_controller_policy_config();
2361 assert_eq!(
2362 as_yaml(&round_trip(&config).policy_config),
2363 as_yaml(&default_traffic_controller_policy_config())
2364 );
2365
2366 let configured = PolicyConfig {
2367 dry_run: !PolicyConfig::default_dos_protection_policy().dry_run,
2368 ..PolicyConfig::default_dos_protection_policy()
2369 };
2370 config.policy_config = Some(configured.clone());
2371 assert_eq!(
2372 as_yaml(&round_trip(&config).policy_config),
2373 as_yaml(&Some(configured))
2374 );
2375 }
2376
2377 #[test]
2378 fn a_grpc_api_config_survives_a_round_trip_in_all_three_states() {
2379 let mut config = template_config();
2380
2381 config.grpc_api_config = None;
2382 assert!(round_trip(&config).grpc_api_config.is_none());
2383
2384 config.grpc_api_config = default_grpc_api_config();
2385 assert_eq!(
2386 as_yaml(&round_trip(&config).grpc_api_config),
2387 as_yaml(&default_grpc_api_config())
2388 );
2389
2390 let configured = GrpcApiConfig {
2391 max_message_size_bytes: 1234,
2392 ..GrpcApiConfig::default()
2393 };
2394 config.grpc_api_config = Some(configured.clone());
2395 assert_eq!(
2396 as_yaml(&round_trip(&config).grpc_api_config),
2397 as_yaml(&Some(configured))
2398 );
2399 }
2400
2401 #[test]
2402 fn the_default_pruning_config_agrees_with_the_serde_default() {
2403 assert_eq!(
2404 super::AuthorityStorePruningConfig::default().periodic_compaction_threshold_days,
2405 default_periodic_compaction_threshold_days()
2406 );
2407 }
2408
2409 #[test]
2410 fn a_compaction_threshold_survives_a_round_trip_in_all_three_states() {
2411 let mut config = template_config();
2412
2413 for state in [None, default_periodic_compaction_threshold_days(), Some(7)] {
2414 config
2415 .authority_store_pruning_config
2416 .periodic_compaction_threshold_days = state;
2417 assert_eq!(
2418 round_trip(&config)
2419 .authority_store_pruning_config
2420 .periodic_compaction_threshold_days,
2421 state
2422 );
2423 }
2424 }
2425
2426 #[test]
2427 fn a_default_value_is_omitted_and_a_disabled_one_is_written_as_null() {
2428 let mut config = template_config();
2429 config.policy_config = default_traffic_controller_policy_config();
2430 config.grpc_api_config = default_grpc_api_config();
2431 config
2432 .authority_store_pruning_config
2433 .periodic_compaction_threshold_days = default_periodic_compaction_threshold_days();
2434
2435 let written = serde_yaml::to_value(&config).unwrap();
2436 assert_eq!(written_at(&written, POLICY_CONFIG), None);
2437 assert_eq!(written_at(&written, GRPC_API_CONFIG), None);
2438 assert_eq!(written_at(&written, COMPACTION_THRESHOLD), None);
2439
2440 config.policy_config = None;
2441 config.grpc_api_config = None;
2442 config
2443 .authority_store_pruning_config
2444 .periodic_compaction_threshold_days = None;
2445
2446 let written = serde_yaml::to_value(&config).unwrap();
2447 assert_eq!(written_at(&written, POLICY_CONFIG), Some(Value::Null));
2448 assert_eq!(written_at(&written, GRPC_API_CONFIG), Some(Value::Null));
2449 assert_eq!(
2450 written_at(&written, COMPACTION_THRESHOLD),
2451 Some(Value::Null)
2452 );
2453 }
2454
2455 fn limits(yaml: &str) -> ListenerLimitsConfig {
2456 serde_yaml::from_str(yaml).unwrap()
2457 }
2458
2459 #[test]
2460 fn a_node_config_without_listener_limits_leaves_every_limit_unset() {
2461 let config = template_config();
2462 assert_eq!(
2463 config.validator_grpc_listener_limits,
2464 ListenerLimitsConfig::default()
2465 );
2466 assert_eq!(
2467 config.json_rpc_listener_limits,
2468 ListenerLimitsConfig::default()
2469 );
2470 assert_eq!(
2471 config.grpc_api_listener_limits,
2472 ListenerLimitsConfig::default()
2473 );
2474 }
2475
2476 #[test]
2477 fn a_listener_limit_left_out_is_unset_and_null_disables_it() {
2478 assert_eq!(
2479 limits("max-connections: 30\nmax-connection-idle: null"),
2480 ListenerLimitsConfig {
2481 max_connections: Setting::Value(NonZeroUsize::new(30).unwrap()),
2482 max_connection_idle: Setting::Disabled,
2483 ..Default::default()
2484 }
2485 );
2486 }
2487
2488 #[test]
2490 fn every_listener_limit_is_read_from_its_own_key() {
2491 let read = limits(
2492 "
2493 handshake-timeout: {secs: 1, nanos: 0}
2494 max-pending-connections: 2
2495 max-connections: 3
2496 max-connections-per-client-ip: 4
2497 ipv6-prefix-len: 48
2498 max-connection-idle: {secs: 5, nanos: 0}
2499 max-concurrent-streams: 6
2500 http2-keepalive-interval: {secs: 7, nanos: 0}
2501 http1-header-read-timeout: {secs: 8, nanos: 0}
2502 ",
2503 );
2504 assert_eq!(
2505 read,
2506 ListenerLimitsConfig {
2507 handshake_timeout: Setting::Value(Duration::from_secs(1)),
2508 max_pending_connections: Setting::Value(NonZeroUsize::new(2).unwrap()),
2509 max_connections: Setting::Value(NonZeroUsize::new(3).unwrap()),
2510 max_connections_per_client_ip: Setting::Value(NonZeroUsize::new(4).unwrap()),
2511 ipv6_prefix_len: Some(48),
2512 max_connection_idle: Setting::Value(Duration::from_secs(5)),
2513 max_concurrent_streams: Setting::Value(NonZeroU32::new(6).unwrap()),
2514 http2_keepalive_interval: Setting::Value(Duration::from_secs(7)),
2515 http1_header_read_timeout: Setting::Value(Duration::from_secs(8)),
2516 }
2517 );
2518 }
2519
2520 #[test]
2521 fn every_listener_limit_can_be_disabled() {
2522 let read = limits(
2523 "
2524 handshake-timeout: null
2525 max-pending-connections: null
2526 max-connections: null
2527 max-connections-per-client-ip: null
2528 ipv6-prefix-len: null
2529 max-connection-idle: null
2530 max-concurrent-streams: null
2531 http2-keepalive-interval: null
2532 http1-header-read-timeout: null
2533 ",
2534 );
2535 assert_eq!(
2536 read,
2537 ListenerLimitsConfig {
2538 handshake_timeout: Setting::Disabled,
2539 max_pending_connections: Setting::Disabled,
2540 max_connections: Setting::Disabled,
2541 max_connections_per_client_ip: Setting::Disabled,
2542 ipv6_prefix_len: None,
2543 max_connection_idle: Setting::Disabled,
2544 max_concurrent_streams: Setting::Disabled,
2545 http2_keepalive_interval: Setting::Disabled,
2546 http1_header_read_timeout: Setting::Disabled,
2547 }
2548 );
2549 }
2550
2551 #[test]
2552 fn listener_limits_are_written_only_when_set() {
2553 let mut config = template_config();
2554 let written = serde_yaml::to_value(&config).unwrap();
2555 for key in [
2556 "validator-grpc-listener-limits",
2557 "json-rpc-listener-limits",
2558 "grpc-api-listener-limits",
2559 ] {
2560 assert_eq!(written_at(&written, &[key]), None, "{key} was written");
2561 }
2562
2563 config.json_rpc_listener_limits.max_connections = Setting::Value(NonZeroUsize::MIN);
2564 config.grpc_api_listener_limits.max_connection_idle = Setting::Disabled;
2565 let read_back = round_trip(&config);
2566 assert_eq!(
2567 read_back.json_rpc_listener_limits,
2568 config.json_rpc_listener_limits
2569 );
2570 assert_eq!(
2571 read_back.grpc_api_listener_limits,
2572 config.grpc_api_listener_limits
2573 );
2574 assert!(read_back.validator_grpc_listener_limits.is_unset());
2575 }
2576}
2577
2578#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
2582pub enum RunWithRange {
2583 Epoch(EpochId),
2584 Checkpoint(CheckpointSequenceNumber),
2585}
2586
2587impl RunWithRange {
2588 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
2590 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
2591 }
2592
2593 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
2594 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
2595 }
2596
2597 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
2598 match self {
2599 RunWithRange::Epoch(_) => None,
2600 RunWithRange::Checkpoint(seq) => Some(seq),
2601 }
2602 }
2603}
2604
2605mod bech32_formatted_keypair {
2609 use std::ops::Deref;
2610
2611 use fastcrypto::encoding::{Base64, Encoding};
2612 use iota_sdk_crypto::{ToFromBech32, simple::SimpleKeypair};
2613 use serde::{Deserialize, Deserializer, Serializer};
2614
2615 pub fn serialize<S, T>(kp: &T, serializer: S) -> Result<S::Ok, S::Error>
2616 where
2617 S: Serializer,
2618 T: Deref<Target = SimpleKeypair>,
2619 {
2620 use serde::ser::Error;
2621
2622 let s = kp.to_bech32().map_err(Error::custom)?;
2624
2625 serializer.serialize_str(&s)
2626 }
2627
2628 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
2629 where
2630 D: Deserializer<'de>,
2631 T: From<SimpleKeypair>,
2632 {
2633 use serde::de::Error;
2634
2635 let s = String::deserialize(deserializer)?;
2636
2637 SimpleKeypair::from_bech32(&s)
2639 .map_err(Error::custom)
2640 .or_else(|_: D::Error| {
2641 let bytes = Base64::decode(&s).map_err(Error::custom)?;
2643 SimpleKeypair::from_bytes(&bytes).map_err(Error::custom)
2644 })
2645 .map(Into::into)
2646 }
2647}