1use std::{
6 net::{IpAddr, Ipv4Addr, SocketAddr},
7 num::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_names::config::IotaNamesConfig;
16use iota_types::{
17 base_types::IotaAddress,
18 committee::EpochId,
19 crypto::{
20 AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, KeypairTraits,
21 NetworkKeyPair, get_key_pair_from_rng,
22 },
23 messages_checkpoint::CheckpointSequenceNumber,
24 multiaddr::Multiaddr,
25 supported_protocol_versions::{Chain, SupportedProtocolVersions},
26 traffic_control::{PolicyConfig, RemoteFirewallConfig},
27};
28use once_cell::sync::OnceCell;
29use rand::rngs::OsRng;
30use serde::{Deserialize, Serialize};
31use starfish_config::Parameters as StarfishParameters;
32use tracing::info;
33
34use crate::{
35 Config, certificate_deny_config::CertificateDenyConfig, genesis,
36 migration_tx_data::MigrationTxData, object_storage_config::ObjectStoreConfig, p2p::P2pConfig,
37 transaction_deny_config::TransactionDenyConfig, verifier_signing_config::VerifierSigningConfig,
38};
39
40pub const DEFAULT_GRPC_CONCURRENCY_LIMIT: usize = 20000000000;
42
43pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = iota_types::transaction::DEFAULT_VALIDATOR_GAS_PRICE;
45
46pub const DEFAULT_COMMISSION_RATE: u64 = 200;
48
49#[derive(Clone, Debug, Deserialize, Serialize)]
50#[serde(rename_all = "kebab-case")]
51pub struct NodeConfig {
52 #[serde(default = "default_authority_key_pair")]
55 pub authority_key_pair: AuthorityKeyPairWithPath,
56 #[serde(default = "default_key_pair")]
59 pub protocol_key_pair: KeyPairWithPath,
60 #[serde(default = "default_key_pair")]
61 pub account_key_pair: KeyPairWithPath,
62 #[serde(default = "default_key_pair")]
65 pub network_key_pair: KeyPairWithPath,
66 pub db_path: PathBuf,
67
68 #[serde(default = "default_grpc_address")]
72 pub network_address: Multiaddr,
73 #[serde(default = "default_json_rpc_address")]
74 pub json_rpc_address: SocketAddr,
75
76 #[serde(default = "default_metrics_address")]
78 pub metrics_address: SocketAddr,
79
80 #[serde(default = "default_admin_interface_address")]
84 pub admin_interface_address: SocketAddr,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub consensus_config: Option<ConsensusConfig>,
89
90 #[serde(default = "default_enable_index_processing")]
95 pub enable_index_processing: bool,
96
97 #[serde(default)]
99 pub jsonrpc_server_type: Option<ServerType>,
104
105 #[serde(default)]
109 pub grpc_load_shed: Option<bool>,
110
111 #[serde(default = "default_concurrency_limit")]
112 pub grpc_concurrency_limit: Option<usize>,
113
114 #[serde(default)]
116 pub p2p_config: P2pConfig,
117
118 pub genesis: Genesis,
122
123 pub migration_tx_data_path: Option<PathBuf>,
125
126 #[serde(default = "default_authority_store_pruning_config")]
129 pub authority_store_pruning_config: AuthorityStorePruningConfig,
130
131 #[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
136 pub end_of_epoch_broadcast_channel_capacity: usize,
137
138 #[serde(default)]
142 pub checkpoint_executor_config: CheckpointExecutorConfig,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub metrics: Option<MetricsConfig>,
146
147 #[serde(skip)]
152 pub supported_protocol_versions: Option<SupportedProtocolVersions>,
153
154 #[serde(default)]
158 pub db_checkpoint_config: DBCheckpointConfig,
159
160 #[serde(default)]
162 pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
163
164 #[serde(default)]
168 pub transaction_deny_config: TransactionDenyConfig,
169
170 #[serde(default)]
176 pub certificate_deny_config: CertificateDenyConfig,
177
178 #[serde(default)]
181 pub state_debug_dump_config: StateDebugDumpConfig,
182
183 #[serde(default)]
187 pub state_archive_write_config: StateArchiveConfig,
188
189 #[serde(default)]
190 pub state_archive_read_config: Vec<StateArchiveConfig>,
191
192 #[serde(default)]
194 pub state_snapshot_write_config: StateSnapshotConfig,
195
196 #[serde(default)]
197 pub indexer_max_subscriptions: Option<usize>,
198
199 #[serde(default = "default_transaction_kv_store_config")]
200 pub transaction_kv_store_read_config: TransactionKeyValueStoreReadConfig,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub transaction_kv_store_write_config: Option<TransactionKeyValueStoreWriteConfig>,
205
206 #[serde(default = "default_authority_overload_config")]
209 pub authority_overload_config: AuthorityOverloadConfig,
210
211 #[serde(skip_serializing_if = "Option::is_none")]
215 pub run_with_range: Option<RunWithRange>,
216
217 #[serde(
219 skip_serializing_if = "Option::is_none",
220 default = "default_traffic_controller_policy_config"
221 )]
222 pub policy_config: Option<PolicyConfig>,
223
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub firewall_config: Option<RemoteFirewallConfig>,
226
227 #[serde(default)]
228 pub execution_cache: ExecutionCacheType,
229
230 #[serde(default)]
231 pub execution_cache_config: ExecutionCacheConfig,
232
233 #[serde(default = "bool_true")]
234 pub enable_validator_tx_finalizer: bool,
235
236 #[serde(default)]
237 pub verifier_signing_config: VerifierSigningConfig,
238
239 #[serde(skip_serializing_if = "Option::is_none")]
243 pub enable_db_write_stall: Option<bool>,
244
245 #[serde(default, skip_serializing_if = "Option::is_none")]
246 pub iota_names_config: Option<IotaNamesConfig>,
247
248 #[serde(default)]
250 pub enable_grpc_api: bool,
251 #[serde(
252 default = "default_grpc_api_config",
253 skip_serializing_if = "Option::is_none"
254 )]
255 pub grpc_api_config: Option<GrpcApiConfig>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
262 pub chain_override_for_testing: Option<Chain>,
263}
264
265#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
266#[serde(rename_all = "kebab-case")]
267pub struct TlsConfig {
268 cert: String,
270 key: String,
272}
273
274impl TlsConfig {
275 pub fn cert(&self) -> &str {
276 &self.cert
277 }
278
279 pub fn key(&self) -> &str {
280 &self.key
281 }
282}
283
284#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
286#[serde(rename_all = "kebab-case")]
287pub struct GrpcApiConfig {
288 #[serde(default = "default_grpc_api_address")]
290 pub address: SocketAddr,
291
292 #[serde(skip_serializing_if = "Option::is_none")]
296 pub tls: Option<TlsConfig>,
297
298 #[serde(default = "default_grpc_api_max_message_size_bytes")]
300 pub max_message_size_bytes: u32,
301
302 #[serde(default = "default_grpc_api_broadcast_buffer_size")]
304 pub broadcast_buffer_size: u32,
305
306 #[serde(default = "default_grpc_api_max_concurrent_stream_subscribers")]
312 pub max_concurrent_stream_subscribers: u32,
313
314 #[serde(default = "default_grpc_api_max_json_move_value_size")]
317 pub max_json_move_value_size: usize,
318
319 #[serde(default = "default_grpc_api_max_execute_transaction_batch_size")]
322 pub max_execute_transaction_batch_size: u32,
323
324 #[serde(default = "default_grpc_api_max_simulate_transaction_batch_size")]
327 pub max_simulate_transaction_batch_size: u32,
328
329 #[serde(default = "default_grpc_api_max_checkpoint_inclusion_timeout_ms")]
333 pub max_checkpoint_inclusion_timeout_ms: u64,
334}
335
336fn default_grpc_api_address() -> SocketAddr {
337 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 50051)
338}
339
340fn default_grpc_api_broadcast_buffer_size() -> u32 {
341 100
342}
343
344fn default_grpc_api_max_concurrent_stream_subscribers() -> u32 {
345 1024
346}
347
348fn default_grpc_api_max_message_size_bytes() -> u32 {
349 128 * 1024 * 1024 }
351
352fn default_grpc_api_max_json_move_value_size() -> usize {
353 1024 * 1024 }
355
356fn default_grpc_api_max_execute_transaction_batch_size() -> u32 {
357 20
358}
359
360fn default_grpc_api_max_simulate_transaction_batch_size() -> u32 {
361 20
362}
363
364fn default_grpc_api_max_checkpoint_inclusion_timeout_ms() -> u64 {
365 60_000 }
367
368impl Default for GrpcApiConfig {
369 fn default() -> Self {
370 Self {
371 address: default_grpc_api_address(),
372 tls: None,
373 max_message_size_bytes: default_grpc_api_max_message_size_bytes(),
374 broadcast_buffer_size: default_grpc_api_broadcast_buffer_size(),
375 max_concurrent_stream_subscribers: default_grpc_api_max_concurrent_stream_subscribers(),
376 max_json_move_value_size: default_grpc_api_max_json_move_value_size(),
377 max_execute_transaction_batch_size: default_grpc_api_max_execute_transaction_batch_size(
378 ),
379 max_simulate_transaction_batch_size:
380 default_grpc_api_max_simulate_transaction_batch_size(),
381 max_checkpoint_inclusion_timeout_ms:
382 default_grpc_api_max_checkpoint_inclusion_timeout_ms(),
383 }
384 }
385}
386
387impl GrpcApiConfig {
388 const GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE: u32 = 4 * 1024 * 1024; const GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES: u32 =
392 Self::GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE;
393
394 pub fn tls_config(&self) -> Option<&TlsConfig> {
395 self.tls.as_ref()
396 }
397
398 pub fn max_message_size_bytes(&self) -> u32 {
399 self.max_message_size_bytes
401 .max(Self::GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES)
402 }
403
404 pub fn max_message_size_client_bytes(&self, client_max_message_size_bytes: Option<u32>) -> u32 {
408 client_max_message_size_bytes
409 .unwrap_or(Self::GRPC_TONIC_DEFAULT_MAX_RECV_MESSAGE_SIZE)
412 .clamp(
414 Self::GRPC_MIN_CLIENT_MAX_MESSAGE_SIZE_BYTES,
415 self.max_message_size_bytes(),
416 )
417 }
418}
419
420#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
421#[serde(rename_all = "kebab-case")]
422pub enum ExecutionCacheType {
423 #[default]
424 WritebackCache,
425 PassthroughCache,
426}
427
428impl From<ExecutionCacheType> for u8 {
429 fn from(cache_type: ExecutionCacheType) -> Self {
430 match cache_type {
431 ExecutionCacheType::WritebackCache => 0,
432 ExecutionCacheType::PassthroughCache => 1,
433 }
434 }
435}
436
437impl From<&u8> for ExecutionCacheType {
438 fn from(cache_type: &u8) -> Self {
439 match cache_type {
440 0 => ExecutionCacheType::WritebackCache,
441 1 => ExecutionCacheType::PassthroughCache,
442 _ => unreachable!("Invalid value for ExecutionCacheType"),
443 }
444 }
445}
446
447pub type ExecutionCacheTypeAtomicU8 = std::sync::atomic::AtomicU8;
450
451impl From<ExecutionCacheType> for ExecutionCacheTypeAtomicU8 {
452 fn from(cache_type: ExecutionCacheType) -> Self {
453 ExecutionCacheTypeAtomicU8::new(u8::from(cache_type))
454 }
455}
456
457impl ExecutionCacheType {
458 pub fn cache_type(self) -> Self {
459 if std::env::var("DISABLE_WRITEBACK_CACHE").is_ok() {
460 Self::PassthroughCache
461 } else {
462 self
463 }
464 }
465}
466
467#[derive(Clone, Debug, Default, Deserialize, Serialize)]
468#[serde(rename_all = "kebab-case")]
469pub struct ExecutionCacheConfig {
470 #[serde(default)]
471 pub writeback_cache: WritebackCacheConfig,
472}
473
474#[derive(Clone, Debug, Default, Deserialize, Serialize)]
475#[serde(rename_all = "kebab-case")]
476pub struct WritebackCacheConfig {
477 #[serde(default, skip_serializing_if = "Option::is_none")]
480 pub max_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
483 pub package_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
486 pub object_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
488 pub marker_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
490 pub object_by_id_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
493 pub transaction_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
495 pub executed_effect_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub effect_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
500 pub events_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
503 pub transaction_objects_cache_size: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub backpressure_threshold: Option<u64>, #[serde(default, skip_serializing_if = "Option::is_none")]
514 pub backpressure_threshold_for_rpc: Option<u64>, }
516
517impl WritebackCacheConfig {
518 pub fn max_cache_size(&self) -> u64 {
519 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MAX")
520 .ok()
521 .and_then(|s| s.parse().ok())
522 .or(self.max_cache_size)
523 .unwrap_or(100000)
524 }
525
526 pub fn package_cache_size(&self) -> u64 {
527 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_PACKAGE")
528 .ok()
529 .and_then(|s| s.parse().ok())
530 .or(self.package_cache_size)
531 .unwrap_or(1000)
532 }
533
534 pub fn object_cache_size(&self) -> u64 {
535 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT")
536 .ok()
537 .and_then(|s| s.parse().ok())
538 .or(self.object_cache_size)
539 .unwrap_or_else(|| self.max_cache_size())
540 }
541
542 pub fn marker_cache_size(&self) -> u64 {
543 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MARKER")
544 .ok()
545 .and_then(|s| s.parse().ok())
546 .or(self.marker_cache_size)
547 .unwrap_or_else(|| self.object_cache_size())
548 }
549
550 pub fn object_by_id_cache_size(&self) -> u64 {
551 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT_BY_ID")
552 .ok()
553 .and_then(|s| s.parse().ok())
554 .or(self.object_by_id_cache_size)
555 .unwrap_or_else(|| self.object_cache_size())
556 }
557
558 pub fn transaction_cache_size(&self) -> u64 {
559 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION")
560 .ok()
561 .and_then(|s| s.parse().ok())
562 .or(self.transaction_cache_size)
563 .unwrap_or_else(|| self.max_cache_size())
564 }
565
566 pub fn executed_effect_cache_size(&self) -> u64 {
567 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EXECUTED_EFFECT")
568 .ok()
569 .and_then(|s| s.parse().ok())
570 .or(self.executed_effect_cache_size)
571 .unwrap_or_else(|| self.transaction_cache_size())
572 }
573
574 pub fn effect_cache_size(&self) -> u64 {
575 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EFFECT")
576 .ok()
577 .and_then(|s| s.parse().ok())
578 .or(self.effect_cache_size)
579 .unwrap_or_else(|| self.executed_effect_cache_size())
580 }
581
582 pub fn events_cache_size(&self) -> u64 {
583 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EVENTS")
584 .ok()
585 .and_then(|s| s.parse().ok())
586 .or(self.events_cache_size)
587 .unwrap_or_else(|| self.transaction_cache_size())
588 }
589
590 pub fn transaction_objects_cache_size(&self) -> u64 {
591 std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION_OBJECTS")
592 .ok()
593 .and_then(|s| s.parse().ok())
594 .or(self.transaction_objects_cache_size)
595 .unwrap_or(1000)
596 }
597
598 pub fn backpressure_threshold(&self) -> u64 {
599 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD")
600 .ok()
601 .and_then(|s| s.parse().ok())
602 .or(self.backpressure_threshold)
603 .unwrap_or(100_000)
604 }
605
606 pub fn backpressure_threshold_for_rpc(&self) -> u64 {
607 std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD_FOR_RPC")
608 .ok()
609 .and_then(|s| s.parse().ok())
610 .or(self.backpressure_threshold_for_rpc)
611 .unwrap_or(self.backpressure_threshold())
612 }
613}
614
615#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
616#[serde(rename_all = "lowercase")]
617pub enum ServerType {
618 WebSocket,
619 Http,
620 Both,
621}
622
623#[derive(Clone, Debug, Deserialize, Serialize)]
624#[serde(rename_all = "kebab-case")]
625pub struct TransactionKeyValueStoreReadConfig {
626 #[serde(default = "default_base_url")]
627 pub base_url: String,
628
629 #[serde(default = "default_cache_size")]
630 pub cache_size: u64,
631}
632
633impl Default for TransactionKeyValueStoreReadConfig {
634 fn default() -> Self {
635 Self {
636 base_url: default_base_url(),
637 cache_size: default_cache_size(),
638 }
639 }
640}
641
642fn default_base_url() -> String {
643 "".to_string()
644}
645
646fn default_cache_size() -> u64 {
647 100_000
648}
649
650fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
651 TransactionKeyValueStoreReadConfig::default()
652}
653
654fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
655 AuthorityStorePruningConfig::default()
656}
657
658pub fn default_enable_index_processing() -> bool {
659 true
660}
661
662fn default_grpc_address() -> Multiaddr {
663 "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
664}
665fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
666 AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
667}
668
669fn default_key_pair() -> KeyPairWithPath {
670 KeyPairWithPath::new(
671 get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
672 .1
673 .into(),
674 )
675}
676
677fn default_metrics_address() -> SocketAddr {
678 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
679}
680
681pub fn default_admin_interface_address() -> SocketAddr {
682 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1337)
683}
684
685pub fn default_json_rpc_address() -> SocketAddr {
686 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
687}
688
689pub fn default_grpc_api_config() -> Option<GrpcApiConfig> {
690 Some(GrpcApiConfig::default())
691}
692
693pub fn default_concurrency_limit() -> Option<usize> {
694 Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
695}
696
697pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
698 128
699}
700
701pub fn bool_true() -> bool {
702 true
703}
704
705fn is_true(value: &bool) -> bool {
706 *value
707}
708
709impl Config for NodeConfig {}
710
711impl NodeConfig {
712 pub fn authority_key_pair(&self) -> &AuthorityKeyPair {
713 self.authority_key_pair.authority_keypair()
714 }
715
716 pub fn protocol_key_pair(&self) -> &NetworkKeyPair {
717 match self.protocol_key_pair.keypair() {
718 IotaKeyPair::Ed25519(kp) => kp,
719 other => {
720 panic!("invalid keypair type: {other:?}, only Ed25519 is allowed for protocol key")
721 }
722 }
723 }
724
725 pub fn network_key_pair(&self) -> &NetworkKeyPair {
726 match self.network_key_pair.keypair() {
727 IotaKeyPair::Ed25519(kp) => kp,
728 other => {
729 panic!("invalid keypair type: {other:?}, only Ed25519 is allowed for network key")
730 }
731 }
732 }
733
734 pub fn authority_public_key(&self) -> AuthorityPublicKeyBytes {
735 self.authority_key_pair().public().into()
736 }
737
738 pub fn db_path(&self) -> PathBuf {
739 self.db_path.join("live")
740 }
741
742 pub fn db_checkpoint_path(&self) -> PathBuf {
743 self.db_path.join("db_checkpoints")
744 }
745
746 pub fn archive_path(&self) -> PathBuf {
747 self.db_path.join("archive")
748 }
749
750 pub fn snapshot_path(&self) -> PathBuf {
751 self.db_path.join("snapshot")
752 }
753
754 pub fn network_address(&self) -> &Multiaddr {
755 &self.network_address
756 }
757
758 pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
759 self.consensus_config.as_ref()
760 }
761
762 pub fn genesis(&self) -> Result<&genesis::Genesis> {
763 self.genesis.genesis()
764 }
765
766 pub fn load_migration_tx_data(&self) -> Result<MigrationTxData> {
767 let Some(location) = &self.migration_tx_data_path else {
768 anyhow::bail!("no file location set");
769 };
770
771 let migration_tx_data = MigrationTxData::load(location)?;
773
774 migration_tx_data.validate_from_genesis(self.genesis.genesis()?)?;
776 Ok(migration_tx_data)
777 }
778
779 pub fn iota_address(&self) -> IotaAddress {
780 (&self.account_key_pair.keypair().public()).into()
781 }
782
783 pub fn archive_reader_config(&self) -> Vec<ArchiveReaderConfig> {
784 self.state_archive_read_config
785 .iter()
786 .flat_map(|config| {
787 config
788 .object_store_config
789 .as_ref()
790 .map(|remote_store_config| ArchiveReaderConfig {
791 remote_store_config: remote_store_config.clone(),
792 download_concurrency: NonZeroUsize::new(config.concurrency)
793 .unwrap_or(NonZeroUsize::new(5).unwrap()),
794 use_for_pruning_watermark: config.use_for_pruning_watermark,
795 })
796 })
797 .collect()
798 }
799
800 pub fn jsonrpc_server_type(&self) -> ServerType {
801 self.jsonrpc_server_type.unwrap_or(ServerType::Http)
802 }
803}
804
805#[derive(Debug, Clone, Deserialize, Serialize)]
806#[serde(rename_all = "kebab-case")]
807pub struct ConsensusConfig {
808 pub db_path: PathBuf,
810
811 pub db_retention_epochs: Option<u64>,
814
815 pub db_pruner_period_secs: Option<u64>,
818
819 pub max_pending_transactions: Option<usize>,
825
826 pub max_submit_position: Option<usize>,
832
833 pub submit_delay_step_override_millis: Option<u64>,
839
840 #[serde(skip_serializing_if = "Option::is_none", alias = "starfish_parameters")]
842 pub parameters: Option<StarfishParameters>,
843}
844
845impl ConsensusConfig {
846 pub fn db_path(&self) -> &Path {
847 &self.db_path
848 }
849
850 pub fn max_pending_transactions(&self) -> usize {
851 self.max_pending_transactions.unwrap_or(20_000)
852 }
853
854 pub fn submit_delay_step_override(&self) -> Option<Duration> {
855 self.submit_delay_step_override_millis
856 .map(Duration::from_millis)
857 }
858
859 pub fn db_retention_epochs(&self) -> u64 {
860 self.db_retention_epochs.unwrap_or(0)
861 }
862
863 pub fn db_pruner_period(&self) -> Duration {
864 self.db_pruner_period_secs
866 .map(Duration::from_secs)
867 .unwrap_or(Duration::from_secs(3_600))
868 }
869}
870
871#[derive(Clone, Debug, Deserialize, Serialize)]
872#[serde(rename_all = "kebab-case")]
873pub struct CheckpointExecutorConfig {
874 #[serde(default = "default_checkpoint_execution_max_concurrency")]
879 pub checkpoint_execution_max_concurrency: usize,
880
881 #[serde(default = "default_local_execution_timeout_sec")]
887 pub local_execution_timeout_sec: u64,
888
889 #[serde(default, skip_serializing_if = "Option::is_none")]
894 pub data_ingestion_dir: Option<PathBuf>,
895}
896
897#[derive(Clone, Debug, Default, Deserialize, Serialize)]
898#[serde(rename_all = "kebab-case")]
899pub struct ExpensiveSafetyCheckConfig {
900 #[serde(default)]
905 enable_epoch_iota_conservation_check: bool,
906
907 #[serde(default)]
911 enable_deep_per_tx_iota_conservation_check: bool,
912
913 #[serde(default)]
916 force_disable_epoch_iota_conservation_check: bool,
917
918 #[serde(default)]
921 enable_state_consistency_check: bool,
922
923 #[serde(default)]
925 force_disable_state_consistency_check: bool,
926
927 #[serde(default)]
928 enable_secondary_index_checks: bool,
929 }
931
932impl ExpensiveSafetyCheckConfig {
933 pub fn new_enable_all() -> Self {
934 Self {
935 enable_epoch_iota_conservation_check: true,
936 enable_deep_per_tx_iota_conservation_check: true,
937 force_disable_epoch_iota_conservation_check: false,
938 enable_state_consistency_check: true,
939 force_disable_state_consistency_check: false,
940 enable_secondary_index_checks: false, }
942 }
943
944 pub fn new_disable_all() -> Self {
945 Self {
946 enable_epoch_iota_conservation_check: false,
947 enable_deep_per_tx_iota_conservation_check: false,
948 force_disable_epoch_iota_conservation_check: true,
949 enable_state_consistency_check: false,
950 force_disable_state_consistency_check: true,
951 enable_secondary_index_checks: false,
952 }
953 }
954
955 pub fn force_disable_epoch_iota_conservation_check(&mut self) {
956 self.force_disable_epoch_iota_conservation_check = true;
957 }
958
959 pub fn enable_epoch_iota_conservation_check(&self) -> bool {
960 (self.enable_epoch_iota_conservation_check || cfg!(debug_assertions))
961 && !self.force_disable_epoch_iota_conservation_check
962 }
963
964 pub fn force_disable_state_consistency_check(&mut self) {
965 self.force_disable_state_consistency_check = true;
966 }
967
968 pub fn enable_state_consistency_check(&self) -> bool {
969 (self.enable_state_consistency_check || cfg!(debug_assertions))
970 && !self.force_disable_state_consistency_check
971 }
972
973 pub fn enable_deep_per_tx_iota_conservation_check(&self) -> bool {
974 self.enable_deep_per_tx_iota_conservation_check || cfg!(debug_assertions)
975 }
976
977 pub fn enable_secondary_index_checks(&self) -> bool {
978 self.enable_secondary_index_checks
979 }
980}
981
982fn default_checkpoint_execution_max_concurrency() -> usize {
983 4
984}
985
986fn default_local_execution_timeout_sec() -> u64 {
987 30
988}
989
990impl Default for CheckpointExecutorConfig {
991 fn default() -> Self {
992 Self {
993 checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
994 local_execution_timeout_sec: default_local_execution_timeout_sec(),
995 data_ingestion_dir: None,
996 }
997 }
998}
999
1000#[derive(Debug, Clone, Deserialize, Serialize)]
1001#[serde(rename_all = "kebab-case")]
1002pub struct AuthorityStorePruningConfig {
1003 #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
1005 pub num_latest_epoch_dbs_to_retain: usize,
1006 #[serde(default)]
1011 pub num_epochs_to_retain: u64,
1012 #[serde(skip_serializing_if = "Option::is_none")]
1014 pub pruning_run_delay_seconds: Option<u64>,
1015 #[serde(default = "default_max_checkpoints_in_batch")]
1018 pub max_checkpoints_in_batch: usize,
1019 #[serde(default = "default_max_transactions_in_batch")]
1021 pub max_transactions_in_batch: usize,
1022 #[serde(
1027 default = "default_periodic_compaction_threshold_days",
1028 skip_serializing_if = "Option::is_none"
1029 )]
1030 pub periodic_compaction_threshold_days: Option<usize>,
1031 #[serde(skip_serializing_if = "Option::is_none")]
1034 pub num_epochs_to_retain_for_checkpoints: Option<u64>,
1035 #[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
1036 pub smooth: bool,
1037 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1043 pub enable_compaction_filter: bool,
1044 #[serde(skip_serializing_if = "Option::is_none")]
1045 pub num_epochs_to_retain_for_indexes: Option<u64>,
1046}
1047
1048fn default_num_latest_epoch_dbs_to_retain() -> usize {
1049 3
1050}
1051
1052fn default_max_transactions_in_batch() -> usize {
1053 1000
1054}
1055
1056fn default_max_checkpoints_in_batch() -> usize {
1057 10
1058}
1059
1060fn default_smoothing() -> bool {
1061 cfg!(not(test))
1062}
1063
1064fn default_periodic_compaction_threshold_days() -> Option<usize> {
1065 Some(1)
1066}
1067
1068impl Default for AuthorityStorePruningConfig {
1069 fn default() -> Self {
1070 Self {
1071 num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
1072 num_epochs_to_retain: 0,
1073 pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
1074 max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
1075 max_transactions_in_batch: default_max_transactions_in_batch(),
1076 periodic_compaction_threshold_days: None,
1077 num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
1078 smooth: true,
1079 enable_compaction_filter: cfg!(test) || cfg!(msim),
1080 num_epochs_to_retain_for_indexes: None,
1081 }
1082 }
1083}
1084
1085impl AuthorityStorePruningConfig {
1086 pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
1087 self.num_epochs_to_retain = num_epochs_to_retain;
1088 }
1089
1090 pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
1091 self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
1092 }
1093
1094 pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
1095 self.num_epochs_to_retain_for_checkpoints
1096 .map(|n| {
1098 if n < 2 {
1099 info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
1100 2
1101 } else {
1102 n
1103 }
1104 })
1105 }
1106}
1107
1108#[derive(Debug, Clone, Deserialize, Serialize)]
1109#[serde(rename_all = "kebab-case")]
1110pub struct MetricsConfig {
1111 #[serde(skip_serializing_if = "Option::is_none")]
1112 pub push_interval_seconds: Option<u64>,
1113 #[serde(skip_serializing_if = "Option::is_none")]
1114 pub push_url: Option<String>,
1115}
1116
1117#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1118#[serde(rename_all = "kebab-case")]
1119pub struct DBCheckpointConfig {
1120 #[serde(default)]
1121 pub perform_db_checkpoints_at_epoch_end: bool,
1122 #[serde(skip_serializing_if = "Option::is_none")]
1123 pub checkpoint_path: Option<PathBuf>,
1124 #[serde(skip_serializing_if = "Option::is_none")]
1125 pub object_store_config: Option<ObjectStoreConfig>,
1126 #[serde(skip_serializing_if = "Option::is_none")]
1127 pub perform_index_db_checkpoints_at_epoch_end: Option<bool>,
1128 #[serde(skip_serializing_if = "Option::is_none")]
1129 pub prune_and_compact_before_upload: Option<bool>,
1130}
1131
1132#[derive(Debug, Clone)]
1133pub struct ArchiveReaderConfig {
1134 pub remote_store_config: ObjectStoreConfig,
1135 pub download_concurrency: NonZeroUsize,
1136 pub use_for_pruning_watermark: bool,
1137}
1138
1139#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1140#[serde(rename_all = "kebab-case")]
1141pub struct StateArchiveConfig {
1142 #[serde(skip_serializing_if = "Option::is_none")]
1143 pub object_store_config: Option<ObjectStoreConfig>,
1144 pub concurrency: usize,
1145 pub use_for_pruning_watermark: bool,
1146}
1147
1148#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1149#[serde(rename_all = "kebab-case")]
1150pub struct StateSnapshotConfig {
1151 #[serde(skip_serializing_if = "Option::is_none")]
1152 pub object_store_config: Option<ObjectStoreConfig>,
1153 pub concurrency: usize,
1154}
1155
1156#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1157#[serde(rename_all = "kebab-case")]
1158pub struct TransactionKeyValueStoreWriteConfig {
1159 pub aws_access_key_id: String,
1160 pub aws_secret_access_key: String,
1161 pub aws_region: String,
1162 pub table_name: String,
1163 pub bucket_name: String,
1164 pub concurrency: usize,
1165}
1166
1167#[derive(Clone, Debug, Deserialize, Serialize)]
1172#[serde(rename_all = "kebab-case")]
1173pub struct AuthorityOverloadConfig {
1174 #[serde(default = "default_max_txn_age_in_queue")]
1175 pub max_txn_age_in_queue: Duration,
1176
1177 #[serde(default = "default_overload_monitor_interval")]
1179 pub overload_monitor_interval: Duration,
1180
1181 #[serde(default = "default_execution_queue_latency_soft_limit")]
1183 pub execution_queue_latency_soft_limit: Duration,
1184
1185 #[serde(default = "default_execution_queue_latency_hard_limit")]
1187 pub execution_queue_latency_hard_limit: Duration,
1188
1189 #[serde(default = "default_max_load_shedding_percentage")]
1191 pub max_load_shedding_percentage: u32,
1192
1193 #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1196 pub min_load_shedding_percentage_above_hard_limit: u32,
1197
1198 #[serde(default = "default_safe_transaction_ready_rate")]
1201 pub safe_transaction_ready_rate: u32,
1202
1203 #[serde(default = "default_check_system_overload_at_signing")]
1206 pub check_system_overload_at_signing: bool,
1207
1208 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1211 pub check_system_overload_at_execution: bool,
1212
1213 #[serde(default = "default_max_transaction_manager_queue_length")]
1216 pub max_transaction_manager_queue_length: usize,
1217
1218 #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1221 pub max_transaction_manager_per_object_queue_length: usize,
1222}
1223
1224fn default_max_txn_age_in_queue() -> Duration {
1225 Duration::from_millis(500)
1226}
1227
1228fn default_overload_monitor_interval() -> Duration {
1229 Duration::from_secs(10)
1230}
1231
1232fn default_execution_queue_latency_soft_limit() -> Duration {
1233 Duration::from_secs(1)
1234}
1235
1236fn default_execution_queue_latency_hard_limit() -> Duration {
1237 Duration::from_secs(10)
1238}
1239
1240fn default_max_load_shedding_percentage() -> u32 {
1241 95
1242}
1243
1244fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1245 50
1246}
1247
1248fn default_safe_transaction_ready_rate() -> u32 {
1249 100
1250}
1251
1252fn default_check_system_overload_at_signing() -> bool {
1253 true
1254}
1255
1256fn default_max_transaction_manager_queue_length() -> usize {
1257 100_000
1258}
1259
1260fn default_max_transaction_manager_per_object_queue_length() -> usize {
1261 20
1262}
1263
1264impl Default for AuthorityOverloadConfig {
1265 fn default() -> Self {
1266 Self {
1267 max_txn_age_in_queue: default_max_txn_age_in_queue(),
1268 overload_monitor_interval: default_overload_monitor_interval(),
1269 execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1270 execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1271 max_load_shedding_percentage: default_max_load_shedding_percentage(),
1272 min_load_shedding_percentage_above_hard_limit:
1273 default_min_load_shedding_percentage_above_hard_limit(),
1274 safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1275 check_system_overload_at_signing: true,
1276 check_system_overload_at_execution: false,
1277 max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1278 max_transaction_manager_per_object_queue_length:
1279 default_max_transaction_manager_per_object_queue_length(),
1280 }
1281 }
1282}
1283
1284fn default_authority_overload_config() -> AuthorityOverloadConfig {
1285 AuthorityOverloadConfig::default()
1286}
1287
1288fn default_traffic_controller_policy_config() -> Option<PolicyConfig> {
1289 Some(PolicyConfig::default_dos_protection_policy())
1290}
1291
1292#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1293pub struct Genesis {
1294 #[serde(flatten)]
1295 location: Option<GenesisLocation>,
1296
1297 #[serde(skip)]
1298 genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1299}
1300
1301impl Genesis {
1302 pub fn new(genesis: genesis::Genesis) -> Self {
1303 Self {
1304 location: Some(GenesisLocation::InPlace {
1305 genesis: Box::new(genesis),
1306 }),
1307 genesis: Default::default(),
1308 }
1309 }
1310
1311 pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1312 Self {
1313 location: Some(GenesisLocation::File {
1314 genesis_file_location: path.into(),
1315 }),
1316 genesis: Default::default(),
1317 }
1318 }
1319
1320 pub fn new_empty() -> Self {
1321 Self {
1322 location: None,
1323 genesis: Default::default(),
1324 }
1325 }
1326
1327 pub fn genesis(&self) -> Result<&genesis::Genesis> {
1328 match &self.location {
1329 Some(GenesisLocation::InPlace { genesis }) => Ok(genesis),
1330 Some(GenesisLocation::File {
1331 genesis_file_location,
1332 }) => self
1333 .genesis
1334 .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1335 None => anyhow::bail!("no genesis location set"),
1336 }
1337 }
1338}
1339
1340#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1341#[serde(untagged)]
1342enum GenesisLocation {
1343 InPlace {
1344 genesis: Box<genesis::Genesis>,
1345 },
1346 File {
1347 #[serde(rename = "genesis-file-location")]
1348 genesis_file_location: PathBuf,
1349 },
1350}
1351
1352#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1355pub struct KeyPairWithPath {
1356 #[serde(flatten)]
1357 location: KeyPairLocation,
1358
1359 #[serde(skip)]
1360 keypair: OnceCell<Arc<IotaKeyPair>>,
1361}
1362
1363#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1364#[serde(untagged)]
1365enum KeyPairLocation {
1366 InPlace {
1367 #[serde(with = "bech32_formatted_keypair")]
1368 value: Arc<IotaKeyPair>,
1369 },
1370 File {
1371 path: PathBuf,
1372 },
1373}
1374
1375impl KeyPairWithPath {
1376 pub fn new(kp: IotaKeyPair) -> Self {
1377 let cell: OnceCell<Arc<IotaKeyPair>> = OnceCell::new();
1378 let arc_kp = Arc::new(kp);
1379 cell.set(arc_kp.clone()).expect("failed to set keypair");
1382 Self {
1383 location: KeyPairLocation::InPlace { value: arc_kp },
1384 keypair: cell,
1385 }
1386 }
1387
1388 pub fn new_from_path(path: PathBuf) -> Self {
1389 let cell: OnceCell<Arc<IotaKeyPair>> = OnceCell::new();
1390 cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1393 |e| panic!("invalid keypair file at path {:?}: {e}", &path),
1394 )))
1395 .expect("failed to set keypair");
1396 Self {
1397 location: KeyPairLocation::File { path },
1398 keypair: cell,
1399 }
1400 }
1401
1402 pub fn keypair(&self) -> &IotaKeyPair {
1403 self.keypair
1404 .get_or_init(|| match &self.location {
1405 KeyPairLocation::InPlace { value } => value.clone(),
1406 KeyPairLocation::File { path } => {
1407 Arc::new(
1410 read_keypair_from_file(path).unwrap_or_else(|e| {
1411 panic!("invalid keypair file at path {path:?}: {e}")
1412 }),
1413 )
1414 }
1415 })
1416 .as_ref()
1417 }
1418}
1419
1420#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1423pub struct AuthorityKeyPairWithPath {
1424 #[serde(flatten)]
1425 location: AuthorityKeyPairLocation,
1426
1427 #[serde(skip)]
1428 keypair: OnceCell<Arc<AuthorityKeyPair>>,
1429}
1430
1431#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1432#[serde(untagged)]
1433enum AuthorityKeyPairLocation {
1434 InPlace { value: Arc<AuthorityKeyPair> },
1435 File { path: PathBuf },
1436}
1437
1438impl AuthorityKeyPairWithPath {
1439 pub fn new(kp: AuthorityKeyPair) -> Self {
1440 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1441 let arc_kp = Arc::new(kp);
1442 cell.set(arc_kp.clone())
1445 .expect("failed to set authority keypair");
1446 Self {
1447 location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1448 keypair: cell,
1449 }
1450 }
1451
1452 pub fn new_from_path(path: PathBuf) -> Self {
1453 let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1454 cell.set(Arc::new(
1457 read_authority_keypair_from_file(&path)
1458 .unwrap_or_else(|_| panic!("invalid authority keypair file at path {:?}", &path)),
1459 ))
1460 .expect("failed to set authority keypair");
1461 Self {
1462 location: AuthorityKeyPairLocation::File { path },
1463 keypair: cell,
1464 }
1465 }
1466
1467 pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1468 self.keypair
1469 .get_or_init(|| match &self.location {
1470 AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1471 AuthorityKeyPairLocation::File { path } => {
1472 Arc::new(
1475 read_authority_keypair_from_file(path).unwrap_or_else(|_| {
1476 panic!("invalid authority keypair file {:?}", &path)
1477 }),
1478 )
1479 }
1480 })
1481 .as_ref()
1482 }
1483}
1484
1485#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1488#[serde(rename_all = "kebab-case")]
1489pub struct StateDebugDumpConfig {
1490 #[serde(skip_serializing_if = "Option::is_none")]
1491 pub dump_file_directory: Option<PathBuf>,
1492}
1493
1494#[cfg(test)]
1495mod tests {
1496 use std::path::PathBuf;
1497
1498 use fastcrypto::traits::KeyPair;
1499 use iota_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1500 use iota_types::crypto::{
1501 AuthorityKeyPair, IotaKeyPair, NetworkKeyPair, get_key_pair_from_rng,
1502 };
1503 use rand::{SeedableRng, rngs::StdRng};
1504
1505 use super::Genesis;
1506 use crate::NodeConfig;
1507
1508 #[test]
1509 fn serialize_genesis_from_file() {
1510 let g = Genesis::new_from_file("path/to/file");
1511
1512 let s = serde_yaml::to_string(&g).unwrap();
1513 assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
1514 let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
1515 assert_eq!(g, loaded_genesis);
1516 }
1517
1518 #[test]
1519 fn fullnode_template() {
1520 const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1521
1522 let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1523 }
1524
1525 #[test]
1526 fn load_key_pairs_to_node_config() {
1527 let authority_key_pair: AuthorityKeyPair =
1528 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1529 let protocol_key_pair: NetworkKeyPair =
1530 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1531 let network_key_pair: NetworkKeyPair =
1532 get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1533
1534 write_authority_keypair_to_file(&authority_key_pair, PathBuf::from("authority.key"))
1535 .unwrap();
1536 write_keypair_to_file(
1537 &IotaKeyPair::Ed25519(protocol_key_pair.copy()),
1538 PathBuf::from("protocol.key"),
1539 )
1540 .unwrap();
1541 write_keypair_to_file(
1542 &IotaKeyPair::Ed25519(network_key_pair.copy()),
1543 PathBuf::from("network.key"),
1544 )
1545 .unwrap();
1546
1547 const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
1548 let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1549 assert_eq!(
1550 template.authority_key_pair().public(),
1551 authority_key_pair.public()
1552 );
1553 assert_eq!(
1554 template.network_key_pair().public(),
1555 network_key_pair.public()
1556 );
1557 assert_eq!(
1558 template.protocol_key_pair().public(),
1559 protocol_key_pair.public()
1560 );
1561 }
1562}
1563
1564#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
1568pub enum RunWithRange {
1569 Epoch(EpochId),
1570 Checkpoint(CheckpointSequenceNumber),
1571}
1572
1573impl RunWithRange {
1574 pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
1576 matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
1577 }
1578
1579 pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
1580 matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
1581 }
1582
1583 pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
1584 match self {
1585 RunWithRange::Epoch(_) => None,
1586 RunWithRange::Checkpoint(seq) => Some(seq),
1587 }
1588 }
1589}
1590
1591mod bech32_formatted_keypair {
1595 use std::ops::Deref;
1596
1597 use iota_types::crypto::{EncodeDecodeBase64, IotaKeyPair};
1598 use serde::{Deserialize, Deserializer, Serializer};
1599
1600 pub fn serialize<S, T>(kp: &T, serializer: S) -> Result<S::Ok, S::Error>
1601 where
1602 S: Serializer,
1603 T: Deref<Target = IotaKeyPair>,
1604 {
1605 use serde::ser::Error;
1606
1607 let s = kp.encode().map_err(Error::custom)?;
1609
1610 serializer.serialize_str(&s)
1611 }
1612
1613 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
1614 where
1615 D: Deserializer<'de>,
1616 T: From<IotaKeyPair>,
1617 {
1618 use serde::de::Error;
1619
1620 let s = String::deserialize(deserializer)?;
1621
1622 IotaKeyPair::decode(&s)
1624 .or_else(|_| {
1625 IotaKeyPair::decode_base64(&s)
1627 })
1628 .map(Into::into)
1629 .map_err(Error::custom)
1630 }
1631}