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)]
101 pub grpc_load_shed: Option<bool>,
102
103 #[serde(default = "default_grpc_concurrency_limit_per_core")]
120 pub grpc_concurrency_limit_per_core: NonZeroUsize,
121
122 #[serde(default)]
124 pub p2p_config: P2pConfig,
125
126 pub genesis: Genesis,
130
131 pub migration_tx_data_path: Option<PathBuf>,
133
134 #[serde(default = "default_authority_store_pruning_config")]
137 pub authority_store_pruning_config: AuthorityStorePruningConfig,
138
139 #[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
144 pub end_of_epoch_broadcast_channel_capacity: usize,
145
146 #[serde(default)]
150 pub checkpoint_executor_config: CheckpointExecutorConfig,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub metrics: Option<MetricsConfig>,
154
155 #[serde(skip)]
160 pub supported_protocol_versions: Option<SupportedProtocolVersions>,
161
162 #[serde(default)]
164 pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
165
166 #[serde(default)]
170 pub transaction_deny_config: TransactionDenyConfig,
171
172 #[serde(default)]
178 pub certificate_deny_config: CertificateDenyConfig,
179
180 #[serde(default)]
183 pub state_debug_dump_config: StateDebugDumpConfig,
184
185 #[serde(default)]
186 pub checkpoint_archive_config: Option<CheckpointArchiveConfig>,
187
188 #[serde(default)]
190 pub state_snapshot_write_config: StateSnapshotConfig,
191
192 #[serde(default = "default_transaction_kv_store_config")]
193 pub transaction_kv_store_read_config: TransactionKeyValueStoreReadConfig,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub transaction_kv_store_write_config: Option<TransactionKeyValueStoreWriteConfig>,
198
199 #[serde(default = "default_authority_overload_config")]
202 pub authority_overload_config: AuthorityOverloadConfig,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
208 pub run_with_range: Option<RunWithRange>,
209
210 #[serde(
216 skip_serializing_if = "is_default_traffic_controller_policy_config",
217 default = "default_traffic_controller_policy_config"
218 )]
219 pub policy_config: Option<PolicyConfig>,
220
221 #[serde(default)]
222 pub execution_cache_config: ExecutionCacheConfig,
223
224 #[serde(default = "default_full_checkpoint_contents_cache_size_mb")]
235 pub full_checkpoint_contents_cache_size_mb: usize,
236
237 #[serde(default = "bool_true")]
238 pub enable_validator_tx_finalizer: bool,
239
240 #[serde(default = "bool_true")]
246 pub enable_soft_locking: bool,
247
248 #[serde(default)]
249 pub verifier_signing_config: VerifierSigningConfig,
250
251 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub post_consensus_validation_readers: Option<NonZeroUsize>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
263 pub enable_db_write_stall: Option<bool>,
264
265 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub iota_names_config: Option<IotaNamesConfig>,
267
268 #[serde(default)]
270 pub enable_grpc_api: bool,
271 #[serde(
277 default = "default_grpc_api_config",
278 skip_serializing_if = "is_default_grpc_api_config"
279 )]
280 pub grpc_api_config: Option<GrpcApiConfig>,
281
282 #[serde(skip_serializing_if = "Option::is_none")]
287 pub chain_override_for_testing: Option<Chain>,
288
289 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub validator_client_monitor_config:
293 Option<crate::validator_client_monitor_config::ValidatorClientMonitorConfig>,
294
295 #[serde(default, skip_serializing_if = "ListenerLimitsConfig::is_unset")]
297 pub validator_grpc_listener_limits: ListenerLimitsConfig,
298
299 #[serde(default, skip_serializing_if = "ListenerLimitsConfig::is_unset")]
301 pub json_rpc_listener_limits: ListenerLimitsConfig,
302
303 #[serde(default, skip_serializing_if = "ListenerLimitsConfig::is_unset")]
305 pub grpc_api_listener_limits: ListenerLimitsConfig,
306}
307
308#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
314#[serde(rename_all = "kebab-case")]
315pub struct ListenerLimitsConfig {
316 #[serde(default, skip_serializing_if = "Setting::is_unset")]
319 pub handshake_timeout: Setting<Duration>,
320
321 #[serde(default, skip_serializing_if = "Setting::is_unset")]
324 pub max_pending_connections: Setting<NonZeroUsize>,
325
326 #[serde(default, skip_serializing_if = "Setting::is_unset")]
328 pub max_connections: Setting<NonZeroUsize>,
329
330 #[serde(default, skip_serializing_if = "Setting::is_unset")]
332 pub max_connections_per_client_ip: Setting<NonZeroUsize>,
333
334 #[serde(default, skip_serializing_if = "Option::is_none")]
337 pub ipv6_prefix_len: Option<u8>,
338
339 #[serde(default, skip_serializing_if = "Setting::is_unset")]
342 pub max_connection_idle: Setting<Duration>,
343
344 #[serde(default, skip_serializing_if = "Setting::is_unset")]
346 pub max_concurrent_streams: Setting<NonZeroU32>,
347
348 #[serde(default, skip_serializing_if = "Setting::is_unset")]
351 pub http2_keepalive_interval: Setting<Duration>,
352
353 #[serde(default, skip_serializing_if = "Setting::is_unset")]
356 pub http1_header_read_timeout: Setting<Duration>,
357}
358
359impl ListenerLimitsConfig {
360 pub fn is_unset(&self) -> bool {
362 *self == Self::default()
363 }
364
365 fn validate(&self, key: &str) -> Result<()> {
368 for (name, duration) in [
369 ("handshake-timeout", self.handshake_timeout),
370 ("max-connection-idle", self.max_connection_idle),
371 ("http2-keepalive-interval", self.http2_keepalive_interval),
372 ("http1-header-read-timeout", self.http1_header_read_timeout),
373 ] {
374 if duration == Setting::Value(Duration::ZERO) {
375 anyhow::bail!(
376 "`{key}.{name}` is 0; set it greater than zero, or to `null` to turn it off"
377 );
378 }
379 }
380 if self.handshake_timeout == Setting::Disabled
383 && self.max_pending_connections != Setting::Disabled
384 {
385 anyhow::bail!(
386 "`{key}.handshake-timeout` is `null`, but `max-pending-connections` is on, and \
387 only the handshake timeout frees its slots; set a handshake timeout, or set \
388 `max-pending-connections` to `null` too"
389 );
390 }
391 if let Some(len) = self
392 .ipv6_prefix_len
393 .filter(|len| !(MIN_IPV6_PREFIX_LEN..=128).contains(len))
394 {
395 anyhow::bail!(
396 "`{key}.ipv6-prefix-len` is {len}; set it between {MIN_IPV6_PREFIX_LEN} and 128"
397 );
398 }
399 Ok(())
400 }
401}
402
403const MIN_IPV6_PREFIX_LEN: u8 = 32;
405
406#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
407#[serde(rename_all = "kebab-case")]
408pub struct TlsConfig {
409 cert: String,
411 key: String,
413}
414
415impl TlsConfig {
416 pub fn cert(&self) -> &str {
417 &self.cert
418 }
419
420 pub fn key(&self) -> &str {
421 &self.key
422 }
423}
424
425#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
427#[serde(rename_all = "kebab-case")]
428pub struct GrpcApiConfig {
429 #[serde(default = "default_grpc_api_address")]
431 pub address: SocketAddr,
432
433 #[serde(skip_serializing_if = "Option::is_none")]
437 pub tls: Option<TlsConfig>,
438
439 #[serde(default = "default_grpc_api_max_message_size_bytes")]
441 pub max_message_size_bytes: u32,
442
443 #[serde(default = "default_grpc_api_broadcast_buffer_size")]
445 pub broadcast_buffer_size: u32,
446
447 #[serde(default = "default_grpc_api_max_concurrent_stream_subscribers")]
453 pub max_concurrent_stream_subscribers: u32,
454
455 #[serde(default = "default_grpc_api_max_json_move_value_size")]
458 pub max_json_move_value_size: usize,
459
460 #[serde(default = "default_grpc_api_max_execute_transaction_batch_size")]
463 pub max_execute_transaction_batch_size: u32,
464
465 #[serde(default = "default_grpc_api_max_simulate_transaction_batch_size")]
468 pub max_simulate_transaction_batch_size: u32,
469
470 #[serde(default = "default_grpc_api_max_get_objects_batch_size")]
472 pub max_get_objects_batch_size: u32,
473
474 #[serde(default = "default_grpc_api_max_get_transactions_batch_size")]
477 pub max_get_transactions_batch_size: u32,
478
479 #[serde(default = "default_grpc_api_max_view_function_call_batch_size")]
482 pub max_view_function_call_batch_size: u32,
483
484 #[serde(default = "default_grpc_api_max_checkpoint_inclusion_timeout_ms")]
488 pub max_checkpoint_inclusion_timeout_ms: u64,
489}
490
491fn default_grpc_api_address() -> SocketAddr {
492 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 50051)
493}
494
495fn default_grpc_api_broadcast_buffer_size() -> u32 {
496 100
497}
498
499fn default_grpc_api_max_concurrent_stream_subscribers() -> u32 {
500 1024
501}
502
503fn default_grpc_api_max_message_size_bytes() -> u32 {
504 128 * 1024 * 1024 }
506
507fn default_grpc_api_max_json_move_value_size() -> usize {
508 1024 * 1024 }
510
511fn default_grpc_api_max_execute_transaction_batch_size() -> u32 {
512 20
513}
514
515fn default_grpc_api_max_simulate_transaction_batch_size() -> u32 {
516 20
517}
518
519fn default_grpc_api_max_get_objects_batch_size() -> u32 {
520 1000
521}
522
523fn default_grpc_api_max_get_transactions_batch_size() -> u32 {
524 1000
525}
526
527fn default_grpc_api_max_view_function_call_batch_size() -> u32 {
528 20
529}
530
531fn default_grpc_api_max_checkpoint_inclusion_timeout_ms() -> u64 {
532 60_000 }
534
535impl Default for GrpcApiConfig {
536 fn default() -> Self {
537 Self {
538 address: default_grpc_api_address(),
539 tls: None,
540 max_message_size_bytes: default_grpc_api_max_message_size_bytes(),
541 broadcast_buffer_size: default_grpc_api_broadcast_buffer_size(),
542 max_concurrent_stream_subscribers: default_grpc_api_max_concurrent_stream_subscribers(),
543 max_json_move_value_size: default_grpc_api_max_json_move_value_size(),
544 max_execute_transaction_batch_size: default_grpc_api_max_execute_transaction_batch_size(
545 ),
546 max_simulate_transaction_batch_size:
547 default_grpc_api_max_simulate_transaction_batch_size(),
548 max_get_objects_batch_size: default_grpc_api_max_get_objects_batch_size(),
549 max_get_transactions_batch_size: default_grpc_api_max_get_transactions_batch_size(),
550 max_view_function_call_batch_size: default_grpc_api_max_view_function_call_batch_size(),
551 max_checkpoint_inclusion_timeout_ms:
552 default_grpc_api_max_checkpoint_inclusion_timeout_ms(),
553 }
554 }
555}
556
557impl GrpcApiConfig {
558 const GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE: u32 = 4 * 1024 * 1024; const GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES: u32 =
562 Self::GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE;
563
564 pub fn tls_config(&self) -> Option<&TlsConfig> {
565 self.tls.as_ref()
566 }
567
568 pub fn max_message_size_bytes(&self) -> u32 {
569 self.max_message_size_bytes
571 .max(Self::GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES)
572 }
573
574 pub fn max_message_size_client_bytes(&self, client_max_message_size_bytes: Option<u32>) -> u32 {
578 client_max_message_size_bytes
579 .unwrap_or(Self::GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE)
582 .clamp(
584 Self::GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES,
585 self.max_message_size_bytes(),
586 )
587 }
588}
589
590#[derive(Clone, Debug, Default, Deserialize, Serialize)]
591#[serde(rename_all = "kebab-case")]
592pub struct ExecutionCacheConfig {
593 #[serde(default)]
594 pub writeback_cache: WritebackCacheConfig,
595}
596
597#[derive(Clone, Debug, Default, Deserialize, Serialize)]
598#[serde(rename_all = "kebab-case")]
599pub struct WritebackCacheConfig {
600 #[serde(default, skip_serializing_if = "Option::is_none")]
603 pub max_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
606 pub package_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
609 pub object_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
611 pub marker_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
613 pub object_by_id_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
616 pub transaction_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
618 pub executed_effect_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
620 pub effect_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
623 pub events_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
626 pub transaction_objects_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
631 pub backpressure_threshold: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
637 pub backpressure_threshold_for_rpc: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
647 pub backpressure_soft_limit_pct: Option<u32>,
648}
649
650impl WritebackCacheConfig {
651 pub fn max_cache_size(&self) -> u64 {
652 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MAX")
653 .ok()
654 .and_then(|s| s.parse().ok())
655 .or(self.max_cache_size)
656 .unwrap_or(100000)
657 }
658
659 pub fn package_cache_size(&self) -> u64 {
660 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_PACKAGE")
661 .ok()
662 .and_then(|s| s.parse().ok())
663 .or(self.package_cache_size)
664 .unwrap_or(1000)
665 }
666
667 pub fn object_cache_size(&self) -> u64 {
668 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT")
669 .ok()
670 .and_then(|s| s.parse().ok())
671 .or(self.object_cache_size)
672 .unwrap_or_else(|| self.max_cache_size())
673 }
674
675 pub fn marker_cache_size(&self) -> u64 {
676 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MARKER")
677 .ok()
678 .and_then(|s| s.parse().ok())
679 .or(self.marker_cache_size)
680 .unwrap_or_else(|| self.object_cache_size())
681 }
682
683 pub fn object_by_id_cache_size(&self) -> u64 {
684 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT_BY_ID")
685 .ok()
686 .and_then(|s| s.parse().ok())
687 .or(self.object_by_id_cache_size)
688 .unwrap_or_else(|| self.object_cache_size())
689 }
690
691 pub fn transaction_cache_size(&self) -> u64 {
692 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION")
693 .ok()
694 .and_then(|s| s.parse().ok())
695 .or(self.transaction_cache_size)
696 .unwrap_or_else(|| self.max_cache_size())
697 }
698
699 pub fn executed_effect_cache_size(&self) -> u64 {
700 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EXECUTED_EFFECT")
701 .ok()
702 .and_then(|s| s.parse().ok())
703 .or(self.executed_effect_cache_size)
704 .unwrap_or_else(|| self.transaction_cache_size())
705 }
706
707 pub fn effect_cache_size(&self) -> u64 {
708 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EFFECT")
709 .ok()
710 .and_then(|s| s.parse().ok())
711 .or(self.effect_cache_size)
712 .unwrap_or_else(|| self.executed_effect_cache_size())
713 }
714
715 pub fn events_cache_size(&self) -> u64 {
716 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EVENTS")
717 .ok()
718 .and_then(|s| s.parse().ok())
719 .or(self.events_cache_size)
720 .unwrap_or_else(|| self.transaction_cache_size())
721 }
722
723 pub fn transaction_objects_cache_size(&self) -> u64 {
724 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION_OBJECTS")
725 .ok()
726 .and_then(|s| s.parse().ok())
727 .or(self.transaction_objects_cache_size)
728 .unwrap_or(1000)
729 }
730
731 pub fn backpressure_threshold(&self) -> u64 {
732 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD")
733 .ok()
734 .and_then(|s| s.parse().ok())
735 .or(self.backpressure_threshold)
736 .unwrap_or(100_000)
737 }
738
739 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
740 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD_FOR_RPC")
741 .ok()
742 .and_then(|s| s.parse().ok())
743 .or(self.backpressure_threshold_for_rpc)
744 .unwrap_or(self.backpressure_threshold())
745 }
746
747 pub fn backpressure_soft_limit_pct(&self) -> u32 {
748 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_SOFT_LIMIT_PCT")
749 .ok()
750 .and_then(|s| s.parse().ok())
751 .or(self.backpressure_soft_limit_pct)
752 .unwrap_or(50)
753 .min(100)
754 }
755}
756
757#[derive(Clone, Debug, Deserialize, Serialize)]
758#[serde(rename_all = "kebab-case")]
759pub struct TransactionKeyValueStoreReadConfig {
760 #[serde(default = "default_base_url")]
761 pub base_url: String,
762
763 #[serde(default = "default_cache_size")]
764 pub cache_size: u64,
765}
766
767impl Default for TransactionKeyValueStoreReadConfig {
768 fn default() -> Self {
769 Self {
770 base_url: default_base_url(),
771 cache_size: default_cache_size(),
772 }
773 }
774}
775
776fn default_base_url() -> String {
777 "".to_string()
778}
779
780fn default_cache_size() -> u64 {
781 100_000
782}
783
784fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
785 TransactionKeyValueStoreReadConfig::default()
786}
787
788fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
789 AuthorityStorePruningConfig::default()
790}
791
792pub fn default_enable_jsonrpc_api() -> bool {
793 true
794}
795
796fn default_grpc_address() -> Multiaddr {
797 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
798}
799fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
800 AuthorityKeyPairWithPath::new(
801 get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut UnwrapErr(SysRng)).1,
802 )
803}
804
805fn default_key_pair() -> KeyPairWithPath {
806 KeyPairWithPath::new(AccountPrivateKey::random().into())
807}
808
809fn default_metrics_address() -> SocketAddr {
810 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
811}
812
813pub fn default_admin_interface_address() -> SocketAddr {
814 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1337)
815}
816
817pub fn default_json_rpc_address() -> SocketAddr {
818 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
819}
820
821pub fn default_grpc_api_config() -> Option<GrpcApiConfig> {
822 Some(GrpcApiConfig::default())
823}
824
825fn is_default_grpc_api_config(grpc_api_config: &Option<GrpcApiConfig>) -> bool {
826 serializes_like(grpc_api_config, &default_grpc_api_config())
827}
828
829fn serializes_like<T: Serialize>(value: &T, default: &T) -> bool {
835 match (serde_yaml::to_string(value), serde_yaml::to_string(default)) {
836 (Ok(value), Ok(default)) => value == default,
837 _ => false,
838 }
839}
840
841pub fn default_grpc_concurrency_limit_per_core() -> NonZeroUsize {
842 NonZeroUsize::new(1000).unwrap()
843}
844
845pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
846 128
847}
848
849pub fn default_full_checkpoint_contents_cache_size_mb() -> usize {
850 DEFAULT_FULL_CHECKPOINT_CONTENTS_CACHE_SIZE_MB
851}
852
853pub fn bool_true() -> bool {
854 true
855}
856
857impl Config for NodeConfig {}
858
859impl NodeConfig {
860 pub fn authority_key_pair(&self) -> &AuthorityKeyPair {
861 self.authority_key_pair.authority_keypair()
862 }
863
864 pub fn protocol_private_key(&self) -> NetworkPrivateKey {
865 self.protocol_key_pair.network_private_key()
866 }
867
868 pub fn network_private_key(&self) -> NetworkPrivateKey {
869 self.network_key_pair.network_private_key()
870 }
871
872 pub fn authority_public_key(&self) -> AuthorityPublicKeyBytes {
873 self.authority_key_pair().public().into()
874 }
875
876 pub fn db_path(&self) -> PathBuf {
877 self.db_path.join("live")
878 }
879
880 pub fn snapshot_path(&self) -> PathBuf {
881 self.db_path.join("snapshot")
882 }
883
884 pub fn network_address(&self) -> &Multiaddr {
885 &self.network_address
886 }
887
888 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
889 self.consensus_config.as_ref()
890 }
891
892 pub fn maintains_rpc_indexes(&self) -> bool {
895 self.consensus_config().is_none() && (self.enable_jsonrpc_api || self.enable_grpc_api)
896 }
897
898 pub fn genesis(&self) -> Result<&genesis::Genesis> {
899 self.genesis.genesis()
900 }
901
902 pub fn load_migration_tx_data(&self) -> Result<MigrationTxData> {
903 let Some(location) = &self.migration_tx_data_path else {
904 anyhow::bail!("no file location set");
905 };
906
907 let migration_tx_data = MigrationTxData::load(location)?;
909
910 migration_tx_data.validate_from_genesis(self.genesis.genesis()?)?;
912 Ok(migration_tx_data)
913 }
914
915 pub fn iota_address(&self) -> Address {
916 self.account_key_pair
917 .keypair()
918 .public_key()
919 .derive_address()
920 }
921
922 pub fn checkpoint_archive_config(&self) -> Option<&CheckpointArchiveConfig> {
923 self.checkpoint_archive_config.as_ref()
924 }
925
926 pub fn is_validator(&self) -> bool {
929 self.consensus_config.is_some()
930 }
931
932 pub fn validate(&self) -> Result<()> {
936 if self.is_validator() {
939 if self.enable_grpc_api {
940 anyhow::bail!(
941 "`enable-grpc-api` is set, but validators do not expose the gRPC API; turn \
942 it off, or move the API to a fullnode"
943 );
944 }
945 if self
946 .state_snapshot_write_config
947 .object_store_config
948 .is_some()
949 {
950 anyhow::bail!(
951 "`state-snapshot-write-config.object-store-config` is set, but snapshot \
952 upload is only supported on fullnodes; remove the setting or move the \
953 upload to a fullnode"
954 );
955 }
956 }
957 if self.enable_grpc_api && self.grpc_api_config.is_none() {
961 anyhow::bail!(
962 "`enable-grpc-api` is set but `grpc-api-config` is `null`; give it a value, \
963 remove the key to take the default config, or turn off `enable-grpc-api`"
964 );
965 }
966 if self
968 .state_snapshot_write_config
969 .object_store_config
970 .as_ref()
971 .is_some_and(|store| store.object_store.is_none())
972 {
973 anyhow::bail!(
974 "`state-snapshot-write-config.object-store-config` has no `object-store`; \
975 snapshot upload needs a storage backend"
976 );
977 }
978 if self
982 .authority_store_pruning_config
983 .num_epochs_to_retain_for_checkpoints()
984 == Some(0)
985 {
986 anyhow::bail!(
987 "`num-epochs-to-retain-for-checkpoints` is 0, but the previous epoch's \
988 checkpoints are still needed after an epoch boundary; set it to 1 or more"
989 );
990 }
991 for (key, limits) in [
994 (
995 "validator-grpc-listener-limits",
996 &self.validator_grpc_listener_limits,
997 ),
998 ("json-rpc-listener-limits", &self.json_rpc_listener_limits),
999 ("grpc-api-listener-limits", &self.grpc_api_listener_limits),
1000 ] {
1001 limits.validate(key)?;
1002 }
1003 Ok(())
1004 }
1005}
1006
1007#[derive(Debug, Clone, Deserialize, Serialize)]
1008#[serde(rename_all = "kebab-case")]
1009pub struct ConsensusConfig {
1010 pub db_path: PathBuf,
1012
1013 pub db_retention_epochs: Option<u64>,
1017
1018 pub db_pruner_period_secs: Option<u64>,
1022
1023 pub max_pending_transactions: Option<usize>,
1038
1039 pub max_submit_position: Option<usize>,
1045
1046 pub submit_delay_step_override_millis: Option<u64>,
1052
1053 #[serde(skip_serializing_if = "Option::is_none", alias = "starfish_parameters")]
1055 pub parameters: Option<StarfishParameters>,
1056
1057 #[serde(skip_serializing_if = "Option::is_none")]
1063 pub graduated_load_shedding_soft_limit_pct: Option<u32>,
1064}
1065
1066impl ConsensusConfig {
1067 pub fn db_path(&self) -> &Path {
1068 &self.db_path
1069 }
1070
1071 pub fn max_pending_transactions(&self) -> usize {
1079 self.max_pending_transactions.unwrap_or(20_000)
1080 }
1081
1082 pub fn graduated_load_shedding_soft_limit_pct(&self) -> u32 {
1087 self.graduated_load_shedding_soft_limit_pct
1088 .unwrap_or(50)
1089 .min(100)
1090 }
1091
1092 pub fn submit_delay_step_override(&self) -> Option<Duration> {
1093 self.submit_delay_step_override_millis
1094 .map(Duration::from_millis)
1095 }
1096
1097 pub fn db_retention_epochs(&self) -> u64 {
1098 self.db_retention_epochs.unwrap_or(0)
1099 }
1100
1101 pub fn db_pruner_period(&self) -> Duration {
1102 self.db_pruner_period_secs
1104 .map(Duration::from_secs)
1105 .unwrap_or(Duration::from_secs(3_600))
1106 }
1107}
1108
1109#[derive(Clone, Debug, Deserialize, Serialize)]
1110#[serde(rename_all = "kebab-case")]
1111pub struct CheckpointExecutorConfig {
1112 #[serde(default = "default_checkpoint_execution_max_concurrency")]
1117 pub checkpoint_execution_max_concurrency: usize,
1118
1119 #[serde(default = "default_local_execution_timeout_sec")]
1125 pub local_execution_timeout_sec: u64,
1126
1127 #[serde(default, skip_serializing_if = "Option::is_none")]
1132 pub data_ingestion_dir: Option<PathBuf>,
1133}
1134
1135#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1136#[serde(rename_all = "kebab-case")]
1137pub struct ExpensiveSafetyCheckConfig {
1138 #[serde(default)]
1143 enable_epoch_iota_conservation_check: bool,
1144
1145 #[serde(default)]
1149 enable_deep_per_tx_iota_conservation_check: bool,
1150
1151 #[serde(default)]
1154 force_disable_epoch_iota_conservation_check: bool,
1155
1156 #[serde(default)]
1159 enable_state_consistency_check: bool,
1160
1161 #[serde(default)]
1163 force_disable_state_consistency_check: bool,
1164}
1165
1166impl ExpensiveSafetyCheckConfig {
1167 pub fn new_enable_all() -> Self {
1168 Self {
1169 enable_epoch_iota_conservation_check: true,
1170 enable_deep_per_tx_iota_conservation_check: true,
1171 force_disable_epoch_iota_conservation_check: false,
1172 enable_state_consistency_check: true,
1173 force_disable_state_consistency_check: false,
1174 }
1175 }
1176
1177 pub fn new_disable_all() -> Self {
1178 Self {
1179 enable_epoch_iota_conservation_check: false,
1180 enable_deep_per_tx_iota_conservation_check: false,
1181 force_disable_epoch_iota_conservation_check: true,
1182 enable_state_consistency_check: false,
1183 force_disable_state_consistency_check: true,
1184 }
1185 }
1186
1187 pub fn force_disable_epoch_iota_conservation_check(&mut self) {
1188 self.force_disable_epoch_iota_conservation_check = true;
1189 }
1190
1191 pub fn enable_epoch_iota_conservation_check(&self) -> bool {
1192 (self.enable_epoch_iota_conservation_check || cfg!(debug_assertions))
1193 && !self.force_disable_epoch_iota_conservation_check
1194 }
1195
1196 pub fn force_disable_state_consistency_check(&mut self) {
1197 self.force_disable_state_consistency_check = true;
1198 }
1199
1200 pub fn enable_state_consistency_check(&self) -> bool {
1201 (self.enable_state_consistency_check || cfg!(debug_assertions))
1202 && !self.force_disable_state_consistency_check
1203 }
1204
1205 pub fn enable_deep_per_tx_iota_conservation_check(&self) -> bool {
1206 self.enable_deep_per_tx_iota_conservation_check || cfg!(debug_assertions)
1207 }
1208}
1209
1210fn default_checkpoint_execution_max_concurrency() -> usize {
1211 4
1212}
1213
1214fn default_local_execution_timeout_sec() -> u64 {
1215 30
1216}
1217
1218impl Default for CheckpointExecutorConfig {
1219 fn default() -> Self {
1220 Self {
1221 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
1222 local_execution_timeout_sec: default_local_execution_timeout_sec(),
1223 data_ingestion_dir: None,
1224 }
1225 }
1226}
1227
1228#[derive(Debug, Clone, Deserialize, Serialize)]
1229#[serde(rename_all = "kebab-case")]
1230pub struct AuthorityStorePruningConfig {
1231 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1233 pub num_latest_epoch_dbs_to_retain: usize,
1234 #[serde(default)]
1240 pub num_epochs_to_retain: u64,
1241 #[serde(
1250 default = "default_periodic_compaction_threshold_days",
1251 skip_serializing_if = "is_default_periodic_compaction_threshold_days"
1252 )]
1253 pub periodic_compaction_threshold_days: Option<usize>,
1254 #[serde(skip_serializing_if = "Option::is_none")]
1263 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1264 #[serde(skip_serializing_if = "Option::is_none")]
1276 pub num_epochs_to_retain_for_indexes: Option<u64>,
1277}
1278
1279pub const DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS: u64 = 30;
1283
1284fn default_num_latest_epoch_dbs_to_retain() -> usize {
1285 3
1286}
1287
1288fn default_periodic_compaction_threshold_days() -> Option<usize> {
1289 Some(1)
1290}
1291
1292fn is_default_periodic_compaction_threshold_days(days: &Option<usize>) -> bool {
1293 *days == default_periodic_compaction_threshold_days()
1294}
1295
1296impl Default for AuthorityStorePruningConfig {
1297 fn default() -> Self {
1298 Self {
1299 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1300 num_epochs_to_retain: 0,
1301 periodic_compaction_threshold_days: default_periodic_compaction_threshold_days(),
1302 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1303 num_epochs_to_retain_for_indexes: None,
1304 }
1305 }
1306}
1307
1308impl AuthorityStorePruningConfig {
1309 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1310 self.num_epochs_to_retain = num_epochs_to_retain;
1311 }
1312
1313 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1314 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1315 }
1316
1317 pub fn num_epochs_to_retain(&self) -> Option<u64> {
1320 (self.num_epochs_to_retain != u64::MAX).then_some(self.num_epochs_to_retain)
1321 }
1322
1323 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1328 match self.num_epochs_to_retain_for_checkpoints {
1329 Some(u64::MAX) => None,
1330 Some(n) => Some(n),
1331 None => Some(DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS),
1332 }
1333 }
1334
1335 pub fn num_epochs_to_retain_for_indexes(&self) -> Option<u64> {
1339 match self.num_epochs_to_retain_for_indexes {
1340 Some(n) if n == u64::MAX => None,
1341 Some(n) => Some(n),
1342 None => self.num_epochs_to_retain_for_checkpoints(),
1343 }
1344 }
1345
1346 pub fn check_index_retention_within_ledger(&self) -> Result<()> {
1350 let Some(ledger) = self.num_epochs_to_retain_for_checkpoints() else {
1351 return Ok(());
1353 };
1354 let index = self.num_epochs_to_retain_for_indexes();
1355 if index.is_none_or(|index| index > ledger) {
1356 let index = index.map_or("unlimited".to_owned(), |index| index.to_string());
1357 anyhow::bail!(
1358 "num-epochs-to-retain-for-indexes ({index}) must not exceed \
1359 num-epochs-to-retain-for-checkpoints ({ledger}): a longer index retention \
1360 lets iotax_queryTransactionBlocks return a transaction digest whose ledger \
1361 data has already been pruned. Lower num-epochs-to-retain-for-indexes or \
1362 raise num-epochs-to-retain-for-checkpoints."
1363 );
1364 }
1365 Ok(())
1366 }
1367}
1368
1369#[derive(Debug, Clone, Deserialize, Serialize)]
1370#[serde(rename_all = "kebab-case")]
1371pub struct MetricsConfig {
1372 #[serde(skip_serializing_if = "Option::is_none")]
1373 pub push_interval_seconds: Option<u64>,
1374 #[serde(skip_serializing_if = "Option::is_none")]
1375 pub push_url: Option<String>,
1376 #[serde(skip_serializing_if = "Option::is_none")]
1377 pub groups: Option<MetricGroups>,
1378}
1379
1380fn default_checkpoint_archive_download_concurrency() -> NonZeroUsize {
1381 NonZeroUsize::new(10).unwrap()
1382}
1383
1384fn default_checkpoint_archive_verify_concurrency() -> NonZeroUsize {
1385 std::thread::available_parallelism().unwrap_or(NonZeroUsize::new(4).unwrap())
1386}
1387
1388#[derive(Debug, Clone, Deserialize, Serialize)]
1391#[serde(rename_all = "kebab-case")]
1392pub struct CheckpointArchiveConfig {
1393 pub url: String,
1395 #[serde(default = "default_checkpoint_archive_download_concurrency")]
1397 pub download_concurrency: NonZeroUsize,
1398 #[serde(default = "default_checkpoint_archive_verify_concurrency")]
1401 pub verify_concurrency: NonZeroUsize,
1402}
1403
1404#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1412#[serde(rename_all = "kebab-case")]
1413pub struct StateSnapshotConfig {
1414 #[serde(skip_serializing_if = "Option::is_none")]
1415 pub object_store_config: Option<ObjectStoreConfig>,
1416 pub concurrency: usize,
1417}
1418
1419#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1420#[serde(rename_all = "kebab-case")]
1421pub struct TransactionKeyValueStoreWriteConfig {
1422 pub aws_access_key_id: String,
1423 pub aws_secret_access_key: String,
1424 pub aws_region: String,
1425 pub table_name: String,
1426 pub bucket_name: String,
1427 pub concurrency: usize,
1428}
1429
1430#[derive(Clone, Debug, Deserialize, Serialize)]
1435#[serde(rename_all = "kebab-case")]
1436pub struct AuthorityOverloadConfig {
1437 #[serde(default = "default_max_txn_age_in_queue")]
1441 pub max_txn_age_in_queue: Duration,
1442
1443 #[serde(default = "default_overload_monitor_interval")]
1445 pub overload_monitor_interval: Duration,
1446
1447 #[serde(default = "default_execution_queue_latency_soft_limit")]
1449 pub execution_queue_latency_soft_limit: Duration,
1450
1451 #[serde(default = "default_execution_queue_latency_hard_limit")]
1454 pub execution_queue_latency_hard_limit: Duration,
1455
1456 #[serde(default = "default_max_load_shedding_percentage")]
1458 pub max_load_shedding_percentage: u32,
1459
1460 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1463 pub min_load_shedding_percentage_above_hard_limit: u32,
1464
1465 #[serde(default = "default_safe_transaction_ready_rate")]
1468 pub safe_transaction_ready_rate: u32,
1469
1470 #[serde(default = "default_check_system_overload_at_signing")]
1473 pub check_system_overload_at_signing: bool,
1474
1475 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1478 pub check_system_overload_at_execution: bool,
1479
1480 #[serde(default = "default_max_transaction_manager_queue_length")]
1484 pub max_transaction_manager_queue_length: usize,
1485
1486 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1489 pub max_transaction_manager_per_object_queue_length: usize,
1490
1491 #[serde(default = "default_max_transaction_manager_queue_length_soft_limit_pct")]
1495 pub max_transaction_manager_queue_length_soft_limit_pct: u32,
1496}
1497
1498impl AuthorityOverloadConfig {
1499 pub fn max_transaction_manager_queue_length_soft_limit_pct(&self) -> u32 {
1502 self.max_transaction_manager_queue_length_soft_limit_pct
1503 .min(100)
1504 }
1505}
1506
1507fn default_max_txn_age_in_queue() -> Duration {
1508 Duration::from_millis(500)
1509}
1510
1511fn default_overload_monitor_interval() -> Duration {
1512 Duration::from_secs(10)
1513}
1514
1515fn default_execution_queue_latency_soft_limit() -> Duration {
1516 Duration::from_secs(1)
1517}
1518
1519fn default_execution_queue_latency_hard_limit() -> Duration {
1520 Duration::from_secs(10)
1521}
1522
1523fn default_max_load_shedding_percentage() -> u32 {
1524 95
1525}
1526
1527fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1528 50
1529}
1530
1531fn default_safe_transaction_ready_rate() -> u32 {
1532 100
1533}
1534
1535fn default_check_system_overload_at_signing() -> bool {
1536 true
1537}
1538
1539fn default_max_transaction_manager_queue_length() -> usize {
1540 100_000
1541}
1542
1543fn default_max_transaction_manager_queue_length_soft_limit_pct() -> u32 {
1544 50
1545}
1546
1547fn default_max_transaction_manager_per_object_queue_length() -> usize {
1548 20
1549}
1550
1551impl Default for AuthorityOverloadConfig {
1552 fn default() -> Self {
1553 Self {
1554 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1555 overload_monitor_interval: default_overload_monitor_interval(),
1556 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1557 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1558 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1559 min_load_shedding_percentage_above_hard_limit:
1560 default_min_load_shedding_percentage_above_hard_limit(),
1561 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1562 check_system_overload_at_signing: true,
1563 check_system_overload_at_execution: false,
1564 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1565 max_transaction_manager_queue_length_soft_limit_pct:
1566 default_max_transaction_manager_queue_length_soft_limit_pct(),
1567 max_transaction_manager_per_object_queue_length:
1568 default_max_transaction_manager_per_object_queue_length(),
1569 }
1570 }
1571}
1572
1573fn default_authority_overload_config() -> AuthorityOverloadConfig {
1574 AuthorityOverloadConfig::default()
1575}
1576
1577fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1578 Some(PolicyConfig::default_dos_protection_policy())
1579}
1580
1581fn is_default_traffic_controller_policy_config(policy_config: &Option<PolicyConfig>) -> bool {
1582 serializes_like(policy_config, &default_traffic_controller_policy_config())
1583}
1584
1585#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1586pub struct Genesis {
1587 #[serde(flatten)]
1588 location: Option<GenesisLocation>,
1589
1590 #[serde(skip)]
1591 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1592}
1593
1594impl Genesis {
1595 pub fn new(genesis: genesis::Genesis) -> Self {
1596 Self {
1597 location: Some(GenesisLocation::InPlace {
1598 genesis: Box::new(genesis),
1599 }),
1600 genesis: Default::default(),
1601 }
1602 }
1603
1604 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1605 Self {
1606 location: Some(GenesisLocation::File {
1607 genesis_file_location: path.into(),
1608 }),
1609 genesis: Default::default(),
1610 }
1611 }
1612
1613 pub fn new_empty() -> Self {
1614 Self {
1615 location: None,
1616 genesis: Default::default(),
1617 }
1618 }
1619
1620 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1621 match &self.location {
1622 Some(GenesisLocation::InPlace { genesis }) => Ok(genesis),
1623 Some(GenesisLocation::File {
1624 genesis_file_location,
1625 }) => self
1626 .genesis
1627 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1628 None => anyhow::bail!("no genesis location set"),
1629 }
1630 }
1631}
1632
1633#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1634#[serde(untagged)]
1635enum GenesisLocation {
1636 InPlace {
1637 genesis: Box<genesis::Genesis>,
1638 },
1639 File {
1640 #[serde(rename = "genesis-file-location")]
1641 genesis_file_location: PathBuf,
1642 },
1643}
1644
1645#[derive(Clone, Debug, Deserialize, Serialize)]
1648pub struct KeyPairWithPath {
1649 #[serde(flatten)]
1650 location: KeyPairLocation,
1651
1652 #[serde(skip)]
1653 keypair: OnceCell<Arc<SimpleKeypair>>,
1654}
1655
1656impl PartialEq for KeyPairWithPath {
1657 fn eq(&self, other: &Self) -> bool {
1658 self.location == other.location
1659 }
1660}
1661
1662impl Eq for KeyPairWithPath {}
1663
1664#[derive(Debug, Clone, Deserialize, Serialize)]
1665#[serde(untagged)]
1666enum KeyPairLocation {
1667 InPlace {
1668 #[serde(with = "bech32_formatted_keypair")]
1669 value: Arc<SimpleKeypair>,
1670 },
1671 File {
1672 path: PathBuf,
1673 },
1674}
1675
1676impl PartialEq for KeyPairLocation {
1677 fn eq(&self, other: &Self) -> bool {
1678 match (self, other) {
1679 (Self::InPlace { value: a }, Self::InPlace { value: b }) => {
1680 a.to_bytes() == b.to_bytes()
1681 }
1682 (Self::File { path: a }, Self::File { path: b }) => a == b,
1683 _ => false,
1684 }
1685 }
1686}
1687
1688impl Eq for KeyPairLocation {}
1689
1690impl KeyPairWithPath {
1691 pub fn new(kp: SimpleKeypair) -> Self {
1692 let cell: OnceCell<Arc<SimpleKeypair>> = OnceCell::new();
1693 let arc_kp = Arc::new(kp);
1694 cell.set(arc_kp.clone()).expect("failed to set keypair");
1697 Self {
1698 location: KeyPairLocation::InPlace { value: arc_kp },
1699 keypair: cell,
1700 }
1701 }
1702
1703 pub fn new_from_path(path: PathBuf) -> Self {
1704 let cell: OnceCell<Arc<SimpleKeypair>> = OnceCell::new();
1705 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1708 |e| panic!("invalid keypair file at path {path:?}: {e}"),
1709 )))
1710 .expect("failed to set keypair");
1711 Self {
1712 location: KeyPairLocation::File { path },
1713 keypair: cell,
1714 }
1715 }
1716
1717 pub fn keypair(&self) -> &SimpleKeypair {
1718 self.keypair
1719 .get_or_init(|| match &self.location {
1720 KeyPairLocation::InPlace { value } => value.clone(),
1721 KeyPairLocation::File { path } => {
1722 Arc::new(
1725 read_keypair_from_file(path).unwrap_or_else(|e| {
1726 panic!("invalid keypair file at path {path:?}: {e}")
1727 }),
1728 )
1729 }
1730 })
1731 .as_ref()
1732 }
1733
1734 pub fn network_private_key(&self) -> NetworkPrivateKey {
1738 self.keypair()
1739 .as_opt_ed25519()
1740 .cloned()
1741 .expect("only Ed25519 network keys are allowed")
1742 }
1743}
1744
1745#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1748pub struct AuthorityKeyPairWithPath {
1749 #[serde(flatten)]
1750 location: AuthorityKeyPairLocation,
1751
1752 #[serde(skip)]
1753 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1754}
1755
1756#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1757#[serde(untagged)]
1758enum AuthorityKeyPairLocation {
1759 InPlace { value: Arc<AuthorityKeyPair> },
1760 File { path: PathBuf },
1761}
1762
1763impl AuthorityKeyPairWithPath {
1764 pub fn new(kp: AuthorityKeyPair) -> Self {
1765 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1766 let arc_kp = Arc::new(kp);
1767 cell.set(arc_kp.clone())
1770 .expect("failed to set authority keypair");
1771 Self {
1772 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1773 keypair: cell,
1774 }
1775 }
1776
1777 pub fn new_from_path(path: PathBuf) -> Self {
1778 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1779 cell.set(Arc::new(
1782 read_authority_keypair_from_file(&path)
1783 .unwrap_or_else(|_| panic!("invalid authority keypair file at path {path:?}")),
1784 ))
1785 .expect("failed to set authority keypair");
1786 Self {
1787 location: AuthorityKeyPairLocation::File { path },
1788 keypair: cell,
1789 }
1790 }
1791
1792 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1793 self.keypair
1794 .get_or_init(|| match &self.location {
1795 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1796 AuthorityKeyPairLocation::File { path } => {
1797 Arc::new(
1800 read_authority_keypair_from_file(path)
1801 .unwrap_or_else(|_| panic!("invalid authority keypair file {path:?}")),
1802 )
1803 }
1804 })
1805 .as_ref()
1806 }
1807}
1808
1809#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1812#[serde(rename_all = "kebab-case")]
1813pub struct StateDebugDumpConfig {
1814 #[serde(skip_serializing_if = "Option::is_none")]
1815 pub dump_file_directory: Option<PathBuf>,
1816}
1817
1818#[cfg(test)]
1819mod tests {
1820 use std::{
1821 num::{NonZeroU32, NonZeroUsize},
1822 path::PathBuf,
1823 time::Duration,
1824 };
1825
1826 use fastcrypto::traits::KeyPair;
1827 use iota_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1828 use iota_sdk_crypto::simple::SimpleKeypair;
1829 use iota_types::{
1830 crypto::{AuthorityKeyPair, NetworkPrivateKey, get_key_pair_from_rng},
1831 traffic_control::PolicyConfig,
1832 };
1833 use rand::{SeedableRng, rngs::StdRng};
1834 use serde::Serialize;
1835 use serde_yaml::Value;
1836
1837 use super::{
1838 AuthorityStorePruningConfig, DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS, Genesis,
1839 GrpcApiConfig, ListenerLimitsConfig, ObjectStoreConfig, default_grpc_api_config,
1840 default_periodic_compaction_threshold_days, default_traffic_controller_policy_config,
1841 };
1842 use crate::{
1843 ConsensusConfig, NodeConfig, object_storage_config::ObjectStoreType, setting::Setting,
1844 };
1845
1846 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1847
1848 const POLICY_CONFIG: &[&str] = &["policy-config"];
1849 const GRPC_API_CONFIG: &[&str] = &["grpc-api-config"];
1850 const COMPACTION_THRESHOLD: &[&str] = &[
1851 "authority-store-pruning-config",
1852 "periodic-compaction-threshold-days",
1853 ];
1854
1855 fn template_config() -> NodeConfig {
1856 serde_yaml::from_str(TEMPLATE).unwrap()
1857 }
1858
1859 fn consensus_config() -> super::ConsensusConfig {
1860 serde_yaml::from_str("db-path: /opt/iota/consensus-db").unwrap()
1861 }
1862
1863 fn object_store_config() -> ObjectStoreConfig {
1864 ObjectStoreConfig {
1865 object_store: Some(ObjectStoreType::File),
1866 directory: Some(PathBuf::from("/opt/iota/snapshots")),
1867 ..Default::default()
1868 }
1869 }
1870
1871 fn round_trip(config: &NodeConfig) -> NodeConfig {
1872 serde_yaml::from_str(&serde_yaml::to_string(config).unwrap()).unwrap()
1873 }
1874
1875 fn as_yaml<T: Serialize>(value: &T) -> String {
1876 serde_yaml::to_string(value).unwrap()
1877 }
1878
1879 fn written_at(value: &Value, path: &[&str]) -> Option<Value> {
1882 let (last, parents) = path.split_last().unwrap();
1883 let mut current = value;
1884 for name in parents {
1885 current = current
1886 .as_mapping()
1887 .unwrap()
1888 .get(&Value::String((*name).to_owned()))
1889 .unwrap();
1890 }
1891 current
1892 .as_mapping()
1893 .unwrap()
1894 .get(&Value::String((*last).to_owned()))
1895 .cloned()
1896 }
1897
1898 #[test]
1899 fn a_retention_below_two_is_no_longer_rounded_up() {
1900 let config = AuthorityStorePruningConfig {
1901 num_epochs_to_retain_for_checkpoints: Some(1),
1902 ..Default::default()
1903 };
1904 assert_eq!(config.num_epochs_to_retain_for_checkpoints(), Some(1));
1905 }
1906
1907 #[test]
1908 fn index_retention_within_ledger_retention_is_accepted() {
1909 let config = AuthorityStorePruningConfig {
1910 num_epochs_to_retain_for_checkpoints: Some(2),
1911 num_epochs_to_retain_for_indexes: Some(2),
1912 ..Default::default()
1913 };
1914 assert!(config.check_index_retention_within_ledger().is_ok());
1915 }
1916
1917 #[test]
1918 fn index_retention_beyond_ledger_retention_is_refused() {
1919 let config = AuthorityStorePruningConfig {
1920 num_epochs_to_retain_for_checkpoints: Some(2),
1921 num_epochs_to_retain_for_indexes: Some(3),
1922 ..Default::default()
1923 };
1924 let err = config
1925 .check_index_retention_within_ledger()
1926 .unwrap_err()
1927 .to_string();
1928 assert!(err.contains("num-epochs-to-retain-for-indexes"), "{err}");
1929 assert!(
1930 err.contains("num-epochs-to-retain-for-checkpoints"),
1931 "{err}"
1932 );
1933 assert!(err.contains('3'), "{err}");
1934 assert!(err.contains('2'), "{err}");
1935 }
1936
1937 #[test]
1938 fn the_narrowest_ledger_retention_refuses_a_longer_index_retention() {
1939 let config = AuthorityStorePruningConfig {
1940 num_epochs_to_retain_for_checkpoints: Some(1),
1941 num_epochs_to_retain_for_indexes: Some(2),
1942 ..Default::default()
1943 };
1944 assert!(config.check_index_retention_within_ledger().is_err());
1945 }
1946
1947 #[test]
1948 fn equal_narrowest_retentions_are_accepted() {
1949 let config = AuthorityStorePruningConfig {
1950 num_epochs_to_retain_for_checkpoints: Some(1),
1951 num_epochs_to_retain_for_indexes: Some(1),
1952 ..Default::default()
1953 };
1954 assert!(config.check_index_retention_within_ledger().is_ok());
1955 }
1956
1957 #[test]
1958 fn unbounded_ledger_retention_accepts_any_index_retention() {
1959 let config = AuthorityStorePruningConfig {
1960 num_epochs_to_retain_for_checkpoints: Some(u64::MAX),
1961 num_epochs_to_retain_for_indexes: Some(1000),
1962 ..Default::default()
1963 };
1964 assert!(config.check_index_retention_within_ledger().is_ok());
1965 }
1966
1967 #[test]
1968 fn unset_ledger_retention_keeps_the_default_and_the_index_follows_it() {
1969 let config = AuthorityStorePruningConfig {
1970 num_epochs_to_retain_for_checkpoints: None,
1971 num_epochs_to_retain_for_indexes: None,
1972 ..Default::default()
1973 };
1974 assert_eq!(
1975 config.num_epochs_to_retain_for_checkpoints(),
1976 Some(DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS)
1977 );
1978 assert_eq!(
1979 config.num_epochs_to_retain_for_indexes(),
1980 Some(DEFAULT_NUM_EPOCHS_TO_RETAIN_FOR_CHECKPOINTS)
1981 );
1982 assert!(config.check_index_retention_within_ledger().is_ok());
1983 }
1984
1985 #[test]
1986 fn unset_index_retention_follows_the_ledger() {
1987 let config = AuthorityStorePruningConfig {
1988 num_epochs_to_retain_for_checkpoints: Some(3),
1989 num_epochs_to_retain_for_indexes: None,
1990 ..Default::default()
1991 };
1992 assert_eq!(config.num_epochs_to_retain_for_indexes(), Some(3));
1993 assert!(config.check_index_retention_within_ledger().is_ok());
1994 }
1995
1996 #[test]
1997 fn index_pruning_turned_off_against_a_bounded_ledger_is_refused() {
1998 let config = AuthorityStorePruningConfig {
2000 num_epochs_to_retain_for_checkpoints: Some(2),
2001 num_epochs_to_retain_for_indexes: Some(u64::MAX),
2002 ..Default::default()
2003 };
2004 let err = config
2005 .check_index_retention_within_ledger()
2006 .unwrap_err()
2007 .to_string();
2008 assert!(err.contains("unlimited"), "{err}");
2009 }
2010
2011 #[test]
2012 fn max_retention_means_pruning_is_off_for_every_knob() {
2013 let config = AuthorityStorePruningConfig {
2014 num_epochs_to_retain: u64::MAX,
2015 num_epochs_to_retain_for_checkpoints: Some(u64::MAX),
2016 num_epochs_to_retain_for_indexes: Some(u64::MAX),
2017 ..Default::default()
2018 };
2019 assert_eq!(config.num_epochs_to_retain(), None);
2020 assert_eq!(config.num_epochs_to_retain_for_checkpoints(), None);
2021 assert_eq!(config.num_epochs_to_retain_for_indexes(), None);
2022 }
2023
2024 #[test]
2025 fn a_fullnode_serving_an_api_builds_the_indexes() {
2026 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2027
2028 let config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2029 assert!(config.maintains_rpc_indexes());
2030 }
2031
2032 #[test]
2033 fn a_fullnode_with_both_apis_off_builds_no_indexes() {
2034 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2035
2036 let mut config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2037 config.enable_jsonrpc_api = false;
2038 config.enable_grpc_api = false;
2039 assert!(!config.maintains_rpc_indexes());
2040 }
2041
2042 #[test]
2043 fn a_validator_builds_no_indexes_even_with_an_api_enabled() {
2044 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2045
2046 let mut config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2047 config.enable_jsonrpc_api = true;
2048 config.consensus_config = Some(ConsensusConfig {
2049 db_path: PathBuf::from("/tmp/consensus"),
2050 db_retention_epochs: None,
2051 db_pruner_period_secs: None,
2052 max_pending_transactions: None,
2053 max_submit_position: None,
2054 submit_delay_step_override_millis: None,
2055 parameters: None,
2056 graduated_load_shedding_soft_limit_pct: None,
2057 });
2058 assert!(!config.maintains_rpc_indexes());
2059 }
2060
2061 #[test]
2062 fn serialize_genesis_from_file() {
2063 let g = Genesis::new_from_file("path/to/file");
2064
2065 let s = serde_yaml::to_string(&g).unwrap();
2066 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
2067 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
2068 assert_eq!(g, loaded_genesis);
2069 }
2070
2071 #[test]
2072 fn fullnode_template() {
2073 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2074
2075 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2076 }
2077
2078 #[test]
2079 fn validate_requires_a_grpc_config_when_the_api_is_enabled() {
2080 let mut config = template_config();
2083 config.grpc_api_config = None;
2084 config.validate().unwrap();
2085
2086 config.enable_grpc_api = true;
2087 let err = config.validate().unwrap_err().to_string();
2088 assert!(err.contains("`grpc-api-config` is `null`"), "{err}");
2089
2090 config.grpc_api_config = Some(GrpcApiConfig::default());
2091 config.validate().unwrap();
2092
2093 config.grpc_api_config = None;
2096 config.consensus_config = Some(consensus_config());
2097 let err = config.validate().unwrap_err().to_string();
2098 assert!(err.contains("validators do not expose"), "{err}");
2099 }
2100
2101 #[test]
2102 fn validate_rejects_the_grpc_api_on_a_validator() {
2103 let mut config = template_config();
2104 config.consensus_config = Some(consensus_config());
2105 config.validate().unwrap();
2106
2107 config.enable_grpc_api = true;
2108 let err = config.validate().unwrap_err().to_string();
2109 assert!(err.contains("validators do not expose"), "{err}");
2110
2111 config.consensus_config = None;
2113 config.validate().unwrap();
2114 }
2115
2116 #[test]
2117 fn validate_rejects_snapshot_upload_on_a_validator() {
2118 let mut config = template_config();
2119 config.state_snapshot_write_config.object_store_config = Some(object_store_config());
2120 config.validate().unwrap();
2121
2122 config.consensus_config = Some(consensus_config());
2123 let err = config.validate().unwrap_err().to_string();
2124 assert!(err.contains("snapshot upload"), "{err}");
2125 }
2126
2127 #[test]
2128 fn validate_rejects_a_snapshot_store_without_a_backend() {
2129 let mut config = template_config();
2130 config.state_snapshot_write_config.object_store_config = Some(ObjectStoreConfig::default());
2131
2132 let err = config.validate().unwrap_err().to_string();
2133 assert!(err.contains("storage backend"), "{err}");
2134 }
2135
2136 #[test]
2137 fn validate_rejects_a_zero_checkpoint_retention() {
2138 let mut config = template_config();
2139 config
2140 .authority_store_pruning_config
2141 .set_num_epochs_to_retain_for_checkpoints(Some(0));
2142 let err = config.validate().unwrap_err().to_string();
2143 assert!(
2144 err.contains("num-epochs-to-retain-for-checkpoints"),
2145 "{err}"
2146 );
2147
2148 config
2149 .authority_store_pruning_config
2150 .set_num_epochs_to_retain_for_checkpoints(Some(1));
2151 config.validate().unwrap();
2152 }
2153
2154 const LISTENER_LIMITS_KEYS: [&str; 3] = [
2156 "validator-grpc-listener-limits",
2157 "json-rpc-listener-limits",
2158 "grpc-api-listener-limits",
2159 ];
2160
2161 fn config_with_listener_limits(key: &str, limits: &str) -> NodeConfig {
2163 serde_yaml::from_str(&format!("{TEMPLATE}\n{key}: {limits}\n")).unwrap()
2164 }
2165
2166 #[test]
2172 fn validate_rejects_a_handshake_timeout_turned_off_alone() {
2173 for key in LISTENER_LIMITS_KEYS {
2174 for limits in [
2175 "{handshake-timeout: null}",
2176 "{handshake-timeout: null, max-pending-connections: 1}",
2177 ] {
2178 let err = config_with_listener_limits(key, limits)
2179 .validate()
2180 .unwrap_err()
2181 .to_string();
2182 assert!(
2183 err.contains(&format!("`{key}.handshake-timeout` is `null`")),
2184 "{limits}: {err}"
2185 );
2186 }
2187 config_with_listener_limits(
2188 key,
2189 "{handshake-timeout: null, max-pending-connections: null}",
2190 )
2191 .validate()
2192 .unwrap();
2193 }
2194 }
2195
2196 #[test]
2199 fn validate_rejects_a_zero_listener_duration() {
2200 for key in LISTENER_LIMITS_KEYS {
2201 for name in [
2202 "handshake-timeout",
2203 "max-connection-idle",
2204 "http2-keepalive-interval",
2205 "http1-header-read-timeout",
2206 ] {
2207 let err =
2208 config_with_listener_limits(key, &format!("{{{name}: {{secs: 0, nanos: 0}}}}"))
2209 .validate()
2210 .unwrap_err()
2211 .to_string();
2212 assert!(err.contains(&format!("`{key}.{name}` is 0")), "{err}");
2213
2214 config_with_listener_limits(key, &format!("{{{name}: {{secs: 1, nanos: 0}}}}"))
2215 .validate()
2216 .unwrap();
2217 }
2218 }
2219 }
2220
2221 #[test]
2223 fn validate_rejects_an_ipv6_prefix_len_out_of_range() {
2224 for key in LISTENER_LIMITS_KEYS {
2225 for len in [0, 31, 129] {
2226 let err = config_with_listener_limits(key, &format!("{{ipv6-prefix-len: {len}}}"))
2227 .validate()
2228 .unwrap_err()
2229 .to_string();
2230 assert!(
2231 err.contains(&format!("`{key}.ipv6-prefix-len` is {len}")),
2232 "{err}"
2233 );
2234 }
2235 for len in [32, 128] {
2236 config_with_listener_limits(key, &format!("{{ipv6-prefix-len: {len}}}"))
2237 .validate()
2238 .unwrap();
2239 }
2240 }
2241 }
2242
2243 #[test]
2244 fn enable_soft_locking_defaults_to_enabled() {
2245 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
2248
2249 let config: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2250 assert!(config.enable_soft_locking);
2251 }
2252
2253 #[test]
2254 fn load_key_pairs_to_node_config() {
2255 let authority_key_pair: AuthorityKeyPair =
2256 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2257 let protocol_private_key: NetworkPrivateKey =
2258 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2259 let network_private_key: NetworkPrivateKey =
2260 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
2261
2262 write_authority_keypair_to_file(&authority_key_pair, PathBuf::from("authority.key"))
2263 .unwrap();
2264 write_keypair_to_file(
2265 &SimpleKeypair::from(protocol_private_key.clone()),
2266 PathBuf::from("protocol.key"),
2267 )
2268 .unwrap();
2269 write_keypair_to_file(
2270 &SimpleKeypair::from(network_private_key.clone()),
2271 PathBuf::from("network.key"),
2272 )
2273 .unwrap();
2274
2275 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
2276 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
2277 assert_eq!(
2278 template.authority_key_pair().public(),
2279 authority_key_pair.public()
2280 );
2281 assert_eq!(
2282 template.network_private_key().public_key(),
2283 network_private_key.public_key()
2284 );
2285 assert_eq!(
2286 template.protocol_private_key().public_key(),
2287 protocol_private_key.public_key()
2288 );
2289 }
2290
2291 #[test]
2292 fn a_policy_config_survives_a_round_trip_in_all_three_states() {
2293 let mut config = template_config();
2294
2295 config.policy_config = None;
2296 assert!(round_trip(&config).policy_config.is_none());
2297
2298 config.policy_config = default_traffic_controller_policy_config();
2299 assert_eq!(
2300 as_yaml(&round_trip(&config).policy_config),
2301 as_yaml(&default_traffic_controller_policy_config())
2302 );
2303
2304 let configured = PolicyConfig {
2305 dry_run: !PolicyConfig::default_dos_protection_policy().dry_run,
2306 ..PolicyConfig::default_dos_protection_policy()
2307 };
2308 config.policy_config = Some(configured.clone());
2309 assert_eq!(
2310 as_yaml(&round_trip(&config).policy_config),
2311 as_yaml(&Some(configured))
2312 );
2313 }
2314
2315 #[test]
2316 fn a_grpc_api_config_survives_a_round_trip_in_all_three_states() {
2317 let mut config = template_config();
2318
2319 config.grpc_api_config = None;
2320 assert!(round_trip(&config).grpc_api_config.is_none());
2321
2322 config.grpc_api_config = default_grpc_api_config();
2323 assert_eq!(
2324 as_yaml(&round_trip(&config).grpc_api_config),
2325 as_yaml(&default_grpc_api_config())
2326 );
2327
2328 let configured = GrpcApiConfig {
2329 max_message_size_bytes: 1234,
2330 ..GrpcApiConfig::default()
2331 };
2332 config.grpc_api_config = Some(configured.clone());
2333 assert_eq!(
2334 as_yaml(&round_trip(&config).grpc_api_config),
2335 as_yaml(&Some(configured))
2336 );
2337 }
2338
2339 #[test]
2340 fn the_default_pruning_config_agrees_with_the_serde_default() {
2341 assert_eq!(
2342 super::AuthorityStorePruningConfig::default().periodic_compaction_threshold_days,
2343 default_periodic_compaction_threshold_days()
2344 );
2345 }
2346
2347 #[test]
2348 fn a_compaction_threshold_survives_a_round_trip_in_all_three_states() {
2349 let mut config = template_config();
2350
2351 for state in [None, default_periodic_compaction_threshold_days(), Some(7)] {
2352 config
2353 .authority_store_pruning_config
2354 .periodic_compaction_threshold_days = state;
2355 assert_eq!(
2356 round_trip(&config)
2357 .authority_store_pruning_config
2358 .periodic_compaction_threshold_days,
2359 state
2360 );
2361 }
2362 }
2363
2364 #[test]
2365 fn a_default_value_is_omitted_and_a_disabled_one_is_written_as_null() {
2366 let mut config = template_config();
2367 config.policy_config = default_traffic_controller_policy_config();
2368 config.grpc_api_config = default_grpc_api_config();
2369 config
2370 .authority_store_pruning_config
2371 .periodic_compaction_threshold_days = default_periodic_compaction_threshold_days();
2372
2373 let written = serde_yaml::to_value(&config).unwrap();
2374 assert_eq!(written_at(&written, POLICY_CONFIG), None);
2375 assert_eq!(written_at(&written, GRPC_API_CONFIG), None);
2376 assert_eq!(written_at(&written, COMPACTION_THRESHOLD), None);
2377
2378 config.policy_config = None;
2379 config.grpc_api_config = None;
2380 config
2381 .authority_store_pruning_config
2382 .periodic_compaction_threshold_days = None;
2383
2384 let written = serde_yaml::to_value(&config).unwrap();
2385 assert_eq!(written_at(&written, POLICY_CONFIG), Some(Value::Null));
2386 assert_eq!(written_at(&written, GRPC_API_CONFIG), Some(Value::Null));
2387 assert_eq!(
2388 written_at(&written, COMPACTION_THRESHOLD),
2389 Some(Value::Null)
2390 );
2391 }
2392
2393 fn limits(yaml: &str) -> ListenerLimitsConfig {
2394 serde_yaml::from_str(yaml).unwrap()
2395 }
2396
2397 #[test]
2398 fn a_node_config_without_listener_limits_leaves_every_limit_unset() {
2399 let config = template_config();
2400 assert_eq!(
2401 config.validator_grpc_listener_limits,
2402 ListenerLimitsConfig::default()
2403 );
2404 assert_eq!(
2405 config.json_rpc_listener_limits,
2406 ListenerLimitsConfig::default()
2407 );
2408 assert_eq!(
2409 config.grpc_api_listener_limits,
2410 ListenerLimitsConfig::default()
2411 );
2412 }
2413
2414 #[test]
2415 fn a_listener_limit_left_out_is_unset_and_null_disables_it() {
2416 assert_eq!(
2417 limits("max-connections: 30\nmax-connection-idle: null"),
2418 ListenerLimitsConfig {
2419 max_connections: Setting::Value(NonZeroUsize::new(30).unwrap()),
2420 max_connection_idle: Setting::Disabled,
2421 ..Default::default()
2422 }
2423 );
2424 }
2425
2426 #[test]
2428 fn every_listener_limit_is_read_from_its_own_key() {
2429 let read = limits(
2430 "
2431 handshake-timeout: {secs: 1, nanos: 0}
2432 max-pending-connections: 2
2433 max-connections: 3
2434 max-connections-per-client-ip: 4
2435 ipv6-prefix-len: 48
2436 max-connection-idle: {secs: 5, nanos: 0}
2437 max-concurrent-streams: 6
2438 http2-keepalive-interval: {secs: 7, nanos: 0}
2439 http1-header-read-timeout: {secs: 8, nanos: 0}
2440 ",
2441 );
2442 assert_eq!(
2443 read,
2444 ListenerLimitsConfig {
2445 handshake_timeout: Setting::Value(Duration::from_secs(1)),
2446 max_pending_connections: Setting::Value(NonZeroUsize::new(2).unwrap()),
2447 max_connections: Setting::Value(NonZeroUsize::new(3).unwrap()),
2448 max_connections_per_client_ip: Setting::Value(NonZeroUsize::new(4).unwrap()),
2449 ipv6_prefix_len: Some(48),
2450 max_connection_idle: Setting::Value(Duration::from_secs(5)),
2451 max_concurrent_streams: Setting::Value(NonZeroU32::new(6).unwrap()),
2452 http2_keepalive_interval: Setting::Value(Duration::from_secs(7)),
2453 http1_header_read_timeout: Setting::Value(Duration::from_secs(8)),
2454 }
2455 );
2456 }
2457
2458 #[test]
2459 fn every_listener_limit_can_be_disabled() {
2460 let read = limits(
2461 "
2462 handshake-timeout: null
2463 max-pending-connections: null
2464 max-connections: null
2465 max-connections-per-client-ip: null
2466 ipv6-prefix-len: null
2467 max-connection-idle: null
2468 max-concurrent-streams: null
2469 http2-keepalive-interval: null
2470 http1-header-read-timeout: null
2471 ",
2472 );
2473 assert_eq!(
2474 read,
2475 ListenerLimitsConfig {
2476 handshake_timeout: Setting::Disabled,
2477 max_pending_connections: Setting::Disabled,
2478 max_connections: Setting::Disabled,
2479 max_connections_per_client_ip: Setting::Disabled,
2480 ipv6_prefix_len: None,
2481 max_connection_idle: Setting::Disabled,
2482 max_concurrent_streams: Setting::Disabled,
2483 http2_keepalive_interval: Setting::Disabled,
2484 http1_header_read_timeout: Setting::Disabled,
2485 }
2486 );
2487 }
2488
2489 #[test]
2490 fn listener_limits_are_written_only_when_set() {
2491 let mut config = template_config();
2492 let written = serde_yaml::to_value(&config).unwrap();
2493 for key in [
2494 "validator-grpc-listener-limits",
2495 "json-rpc-listener-limits",
2496 "grpc-api-listener-limits",
2497 ] {
2498 assert_eq!(written_at(&written, &[key]), None, "{key} was written");
2499 }
2500
2501 config.json_rpc_listener_limits.max_connections = Setting::Value(NonZeroUsize::MIN);
2502 config.grpc_api_listener_limits.max_connection_idle = Setting::Disabled;
2503 let read_back = round_trip(&config);
2504 assert_eq!(
2505 read_back.json_rpc_listener_limits,
2506 config.json_rpc_listener_limits
2507 );
2508 assert_eq!(
2509 read_back.grpc_api_listener_limits,
2510 config.grpc_api_listener_limits
2511 );
2512 assert!(read_back.validator_grpc_listener_limits.is_unset());
2513 }
2514}
2515
2516#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
2520pub enum RunWithRange {
2521 Epoch(EpochId),
2522 Checkpoint(CheckpointSequenceNumber),
2523}
2524
2525impl RunWithRange {
2526 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
2528 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
2529 }
2530
2531 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
2532 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
2533 }
2534
2535 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
2536 match self {
2537 RunWithRange::Epoch(_) => None,
2538 RunWithRange::Checkpoint(seq) => Some(seq),
2539 }
2540 }
2541}
2542
2543mod bech32_formatted_keypair {
2547 use std::ops::Deref;
2548
2549 use fastcrypto::encoding::{Base64, Encoding};
2550 use iota_sdk_crypto::{ToFromBech32, simple::SimpleKeypair};
2551 use serde::{Deserialize, Deserializer, Serializer};
2552
2553 pub fn serialize<S, T>(kp: &T, serializer: S) -> Result<S::Ok, S::Error>
2554 where
2555 S: Serializer,
2556 T: Deref<Target = SimpleKeypair>,
2557 {
2558 use serde::ser::Error;
2559
2560 let s = kp.to_bech32().map_err(Error::custom)?;
2562
2563 serializer.serialize_str(&s)
2564 }
2565
2566 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
2567 where
2568 D: Deserializer<'de>,
2569 T: From<SimpleKeypair>,
2570 {
2571 use serde::de::Error;
2572
2573 let s = String::deserialize(deserializer)?;
2574
2575 SimpleKeypair::from_bech32(&s)
2577 .map_err(Error::custom)
2578 .or_else(|_: D::Error| {
2579 let bytes = Base64::decode(&s).map_err(Error::custom)?;
2581 SimpleKeypair::from_bytes(&bytes).map_err(Error::custom)
2582 })
2583 .map(Into::into)
2584 }
2585}