iota_config/
node.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{BTreeMap, BTreeSet},
7    net::{IpAddr, Ipv4Addr, SocketAddr},
8    num::NonZeroUsize,
9    path::{Path, PathBuf},
10    sync::Arc,
11    time::Duration,
12};
13
14use anyhow::Result;
15use consensus_config::Parameters as ConsensusParameters;
16use iota_keys::keypair_file::{read_authority_keypair_from_file, read_keypair_from_file};
17use iota_names::config::IotaNamesConfig;
18use iota_types::{
19    base_types::IotaAddress,
20    committee::EpochId,
21    crypto::{
22        AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, KeypairTraits,
23        NetworkKeyPair, get_key_pair_from_rng,
24    },
25    messages_checkpoint::CheckpointSequenceNumber,
26    multiaddr::Multiaddr,
27    supported_protocol_versions::{Chain, SupportedProtocolVersions},
28    traffic_control::{PolicyConfig, RemoteFirewallConfig},
29};
30use once_cell::sync::OnceCell;
31use rand::rngs::OsRng;
32use serde::{Deserialize, Serialize};
33use tracing::info;
34
35use crate::{
36    Config, certificate_deny_config::CertificateDenyConfig, genesis,
37    migration_tx_data::MigrationTxData, object_storage_config::ObjectStoreConfig, p2p::P2pConfig,
38    transaction_deny_config::TransactionDenyConfig, verifier_signing_config::VerifierSigningConfig,
39};
40
41// Default max number of concurrent requests served
42pub const DEFAULT_GRPC_CONCURRENCY_LIMIT: usize = 20000000000;
43
44/// Default gas price of 1000 Nanos
45pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = iota_types::transaction::DEFAULT_VALIDATOR_GAS_PRICE;
46
47/// Default commission rate of 2%
48pub const DEFAULT_COMMISSION_RATE: u64 = 200;
49
50#[derive(Clone, Debug, Deserialize, Serialize)]
51#[serde(rename_all = "kebab-case")]
52pub struct NodeConfig {
53    /// The public key bytes corresponding to the private key that the validator
54    /// holds to sign transactions.
55    #[serde(default = "default_authority_key_pair")]
56    pub authority_key_pair: AuthorityKeyPairWithPath,
57    /// The public key bytes corresponding to the private key that the validator
58    /// holds to sign consensus blocks.
59    #[serde(default = "default_key_pair")]
60    pub protocol_key_pair: KeyPairWithPath,
61    #[serde(default = "default_key_pair")]
62    pub account_key_pair: KeyPairWithPath,
63    /// The public key bytes corresponding to the private key that the validator
64    /// uses to establish TLS connections.
65    #[serde(default = "default_key_pair")]
66    pub network_key_pair: KeyPairWithPath,
67    pub db_path: PathBuf,
68
69    /// The network address for gRPC communication.
70    ///
71    /// Can be overwritten with args `listen-address` parameters.
72    #[serde(default = "default_grpc_address")]
73    pub network_address: Multiaddr,
74    #[serde(default = "default_json_rpc_address")]
75    pub json_rpc_address: SocketAddr,
76
77    /// Flag to enable the REST API under `/api/v1`
78    /// endpoint on the same interface as `json` `rpc` server.
79    #[serde(default)]
80    pub enable_rest_api: bool,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub rest: Option<iota_rest_api::Config>,
83
84    /// The address for Prometheus metrics.
85    #[serde(default = "default_metrics_address")]
86    pub metrics_address: SocketAddr,
87
88    /// The address for the admin interface that is
89    /// run in the metrics separate runtime and provides access to
90    /// admin node commands such as logging and tracing options.
91    #[serde(default = "default_admin_interface_address")]
92    pub admin_interface_address: SocketAddr,
93
94    /// Configuration struct for the consensus.
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub consensus_config: Option<ConsensusConfig>,
97
98    /// Flag to enable index processing for a full node.
99    ///
100    /// If set to true, node creates `IndexStore` for transaction
101    /// data including ownership and balance information.
102    #[serde(default = "default_enable_index_processing")]
103    pub enable_index_processing: bool,
104
105    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
106    pub remove_deprecated_tables: bool,
107
108    // only allow websocket connections for jsonrpc traffic
109    #[serde(default)]
110    /// Determines the jsonrpc server type as either:
111    /// - 'websocket' for a websocket based service (deprecated)
112    /// - 'http' for an http based service
113    /// - 'both' for both a websocket and http based service (deprecated)
114    pub jsonrpc_server_type: Option<ServerType>,
115
116    /// Flag to enable gRPC load shedding to manage and
117    /// mitigate overload conditions by shedding excess
118    /// load with `LoadShedLayer` middleware.
119    #[serde(default)]
120    pub grpc_load_shed: Option<bool>,
121
122    #[serde(default = "default_concurrency_limit")]
123    pub grpc_concurrency_limit: Option<usize>,
124
125    /// Configuration struct for P2P.
126    #[serde(default)]
127    pub p2p_config: P2pConfig,
128
129    /// Contains genesis location that might be `InPlace`
130    /// for reading all genesis data to memory or `InFile`,
131    /// and `OnceCell` pointer to a genesis struct.
132    pub genesis: Genesis,
133
134    /// Contains the path where to find the migration blob.
135    pub migration_tx_data_path: Option<PathBuf>,
136
137    /// Configuration for pruning of the authority store, to define when
138    /// an old data is removed from the storage space.
139    #[serde(default = "default_authority_store_pruning_config")]
140    pub authority_store_pruning_config: AuthorityStorePruningConfig,
141
142    /// Size of the broadcast channel used for notifying other systems of end of
143    /// epoch.
144    ///
145    /// If unspecified, this will default to `128`.
146    #[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
147    pub end_of_epoch_broadcast_channel_capacity: usize,
148
149    /// Configuration for the checkpoint executor for limiting
150    /// the number of checkpoints to execute concurrently,
151    /// and to allow for checkpoint post-processing.
152    #[serde(default)]
153    pub checkpoint_executor_config: CheckpointExecutorConfig,
154
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub metrics: Option<MetricsConfig>,
157
158    /// In a `iota-node` binary, this is set to
159    /// SupportedProtocolVersions::SYSTEM_DEFAULT in iota-node/src/main.rs.
160    /// It is present in the config so that it can be changed by tests in
161    /// order to test protocol upgrades.
162    #[serde(skip)]
163    pub supported_protocol_versions: Option<SupportedProtocolVersions>,
164
165    /// Configuration to manage database checkpoints,
166    /// including whether to perform checkpoints at the end of an epoch,
167    /// the path for storing checkpoints, and other related settings.
168    #[serde(default)]
169    pub db_checkpoint_config: DBCheckpointConfig,
170
171    /// Configuration for enabling/disabling expensive safety checks.
172    #[serde(default)]
173    pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
174
175    /// Configuration to specify rules for denying transactions
176    /// based on `objectsIDs`, `addresses`, or enable/disable many
177    /// features such as publishing new packages or using shared objects.
178    #[serde(default)]
179    pub transaction_deny_config: TransactionDenyConfig,
180
181    /// Config used to deny execution for certificate digests
182    /// know for crashing or hanging validator nodes.
183    ///
184    /// Should be used for a fast temporary fixes and
185    /// removed once the issue is fixed.
186    #[serde(default)]
187    pub certificate_deny_config: CertificateDenyConfig,
188
189    /// Used to determine how state debug information is dumped
190    /// when a node forks.
191    #[serde(default)]
192    pub state_debug_dump_config: StateDebugDumpConfig,
193
194    /// Configuration for writing state archive. If `ObjectStorage`
195    /// config is provided, `ArchiveWriter` will be created
196    /// for checkpoints archival.
197    #[serde(default)]
198    pub state_archive_write_config: StateArchiveConfig,
199
200    #[serde(default)]
201    pub state_archive_read_config: Vec<StateArchiveConfig>,
202
203    /// Determines if snapshot should be uploaded to the remote storage.
204    #[serde(default)]
205    pub state_snapshot_write_config: StateSnapshotConfig,
206
207    #[serde(default)]
208    pub indexer_max_subscriptions: Option<usize>,
209
210    #[serde(default = "default_transaction_kv_store_config")]
211    pub transaction_kv_store_read_config: TransactionKeyValueStoreReadConfig,
212
213    // TODO: write config seem to be unused.
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub transaction_kv_store_write_config: Option<TransactionKeyValueStoreWriteConfig>,
216
217    #[serde(default = "default_jwk_fetch_interval_seconds")]
218    pub jwk_fetch_interval_seconds: u64,
219
220    #[serde(default = "default_zklogin_oauth_providers")]
221    pub zklogin_oauth_providers: BTreeMap<Chain, BTreeSet<String>>,
222
223    /// Configuration for defining thresholds and settings
224    /// for managing system overload conditions in a node.
225    #[serde(default = "default_authority_overload_config")]
226    pub authority_overload_config: AuthorityOverloadConfig,
227
228    /// Specifies the ending epoch for a node for debugging purposes.
229    ///
230    ///  Ignored if set by config, can be configured only by cli arguments.
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub run_with_range: Option<RunWithRange>,
233
234    // For killswitch use None
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub policy_config: Option<PolicyConfig>,
237
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub firewall_config: Option<RemoteFirewallConfig>,
240
241    #[serde(default)]
242    pub execution_cache: ExecutionCacheType,
243
244    #[serde(default)]
245    pub execution_cache_config: ExecutionCacheConfig,
246
247    #[serde(default = "bool_true")]
248    pub enable_validator_tx_finalizer: bool,
249
250    #[serde(default)]
251    pub verifier_signing_config: VerifierSigningConfig,
252
253    /// If a value is set, it determines if writes to DB can stall, which can
254    /// halt the whole process. By default, write stall is enabled on
255    /// validators but not on fullnodes.
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub enable_db_write_stall: Option<bool>,
258
259    #[serde(default, skip_serializing_if = "Option::is_none")]
260    pub iota_names_config: Option<IotaNamesConfig>,
261
262    /// Flag to enable the gRPC API.
263    #[serde(default)]
264    pub enable_grpc_api: bool,
265    #[serde(
266        default = "default_grpc_api_config",
267        skip_serializing_if = "Option::is_none"
268    )]
269    pub grpc_api_config: Option<iota_grpc_api::Config>,
270}
271
272#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
273#[serde(rename_all = "kebab-case")]
274pub enum ExecutionCacheType {
275    #[default]
276    WritebackCache,
277    PassthroughCache,
278}
279
280impl From<ExecutionCacheType> for u8 {
281    fn from(cache_type: ExecutionCacheType) -> Self {
282        match cache_type {
283            ExecutionCacheType::WritebackCache => 0,
284            ExecutionCacheType::PassthroughCache => 1,
285        }
286    }
287}
288
289impl From<&u8> for ExecutionCacheType {
290    fn from(cache_type: &u8) -> Self {
291        match cache_type {
292            0 => ExecutionCacheType::WritebackCache,
293            1 => ExecutionCacheType::PassthroughCache,
294            _ => unreachable!("Invalid value for ExecutionCacheType"),
295        }
296    }
297}
298
299/// Type alias for atomic representation of ExecutionCacheType for lock-free
300/// operations
301pub type ExecutionCacheTypeAtomicU8 = std::sync::atomic::AtomicU8;
302
303impl From<ExecutionCacheType> for ExecutionCacheTypeAtomicU8 {
304    fn from(cache_type: ExecutionCacheType) -> Self {
305        ExecutionCacheTypeAtomicU8::new(u8::from(cache_type))
306    }
307}
308
309impl ExecutionCacheType {
310    pub fn cache_type(self) -> Self {
311        if std::env::var("DISABLE_WRITEBACK_CACHE").is_ok() {
312            Self::PassthroughCache
313        } else {
314            self
315        }
316    }
317}
318
319#[derive(Clone, Debug, Default, Deserialize, Serialize)]
320#[serde(rename_all = "kebab-case")]
321pub struct ExecutionCacheConfig {
322    #[serde(default)]
323    pub writeback_cache: WritebackCacheConfig,
324}
325
326#[derive(Clone, Debug, Default, Deserialize, Serialize)]
327#[serde(rename_all = "kebab-case")]
328pub struct WritebackCacheConfig {
329    /// Maximum number of entries in each cache. (There are several
330    /// different caches).
331    #[serde(default, skip_serializing_if = "Option::is_none")]
332    pub max_cache_size: Option<u64>, // defaults to 100000
333
334    #[serde(default, skip_serializing_if = "Option::is_none")]
335    pub package_cache_size: Option<u64>, // defaults to 1000
336
337    #[serde(default, skip_serializing_if = "Option::is_none")]
338    pub object_cache_size: Option<u64>, // defaults to max_cache_size
339    #[serde(default, skip_serializing_if = "Option::is_none")]
340    pub marker_cache_size: Option<u64>, // defaults to object_cache_size
341    #[serde(default, skip_serializing_if = "Option::is_none")]
342    pub object_by_id_cache_size: Option<u64>, // defaults to object_cache_size
343
344    #[serde(default, skip_serializing_if = "Option::is_none")]
345    pub transaction_cache_size: Option<u64>, // defaults to max_cache_size
346    #[serde(default, skip_serializing_if = "Option::is_none")]
347    pub executed_effect_cache_size: Option<u64>, // defaults to transaction_cache_size
348    #[serde(default, skip_serializing_if = "Option::is_none")]
349    pub effect_cache_size: Option<u64>, // defaults to executed_effect_cache_size
350
351    #[serde(default, skip_serializing_if = "Option::is_none")]
352    pub events_cache_size: Option<u64>, // defaults to transaction_cache_size
353
354    #[serde(default, skip_serializing_if = "Option::is_none")]
355    pub transaction_objects_cache_size: Option<u64>, // defaults to 1000
356
357    /// Number of uncommitted transactions at which to pause consensus
358    /// handler.
359    #[serde(default, skip_serializing_if = "Option::is_none")]
360    pub backpressure_threshold: Option<u64>, // defaults to 100_000
361
362    /// Number of uncommitted transactions at which to refuse new
363    /// transaction submissions. Defaults to backpressure_threshold
364    /// if unset.
365    #[serde(default, skip_serializing_if = "Option::is_none")]
366    pub backpressure_threshold_for_rpc: Option<u64>, // defaults to backpressure_threshold
367}
368
369impl WritebackCacheConfig {
370    pub fn max_cache_size(&self) -> u64 {
371        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MAX")
372            .ok()
373            .and_then(|s| s.parse().ok())
374            .or(self.max_cache_size)
375            .unwrap_or(100000)
376    }
377
378    pub fn package_cache_size(&self) -> u64 {
379        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_PACKAGE")
380            .ok()
381            .and_then(|s| s.parse().ok())
382            .or(self.package_cache_size)
383            .unwrap_or(1000)
384    }
385
386    pub fn object_cache_size(&self) -> u64 {
387        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT")
388            .ok()
389            .and_then(|s| s.parse().ok())
390            .or(self.object_cache_size)
391            .unwrap_or_else(|| self.max_cache_size())
392    }
393
394    pub fn marker_cache_size(&self) -> u64 {
395        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_MARKER")
396            .ok()
397            .and_then(|s| s.parse().ok())
398            .or(self.marker_cache_size)
399            .unwrap_or_else(|| self.object_cache_size())
400    }
401
402    pub fn object_by_id_cache_size(&self) -> u64 {
403        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_OBJECT_BY_ID")
404            .ok()
405            .and_then(|s| s.parse().ok())
406            .or(self.object_by_id_cache_size)
407            .unwrap_or_else(|| self.object_cache_size())
408    }
409
410    pub fn transaction_cache_size(&self) -> u64 {
411        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION")
412            .ok()
413            .and_then(|s| s.parse().ok())
414            .or(self.transaction_cache_size)
415            .unwrap_or_else(|| self.max_cache_size())
416    }
417
418    pub fn executed_effect_cache_size(&self) -> u64 {
419        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EXECUTED_EFFECT")
420            .ok()
421            .and_then(|s| s.parse().ok())
422            .or(self.executed_effect_cache_size)
423            .unwrap_or_else(|| self.transaction_cache_size())
424    }
425
426    pub fn effect_cache_size(&self) -> u64 {
427        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EFFECT")
428            .ok()
429            .and_then(|s| s.parse().ok())
430            .or(self.effect_cache_size)
431            .unwrap_or_else(|| self.executed_effect_cache_size())
432    }
433
434    pub fn events_cache_size(&self) -> u64 {
435        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_EVENTS")
436            .ok()
437            .and_then(|s| s.parse().ok())
438            .or(self.events_cache_size)
439            .unwrap_or_else(|| self.transaction_cache_size())
440    }
441
442    pub fn transaction_objects_cache_size(&self) -> u64 {
443        std::env::var("IOTA_CACHE_WRITEBACK_SIZE_TRANSACTION_OBJECTS")
444            .ok()
445            .and_then(|s| s.parse().ok())
446            .or(self.transaction_objects_cache_size)
447            .unwrap_or(1000)
448    }
449
450    pub fn backpressure_threshold(&self) -> u64 {
451        std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD")
452            .ok()
453            .and_then(|s| s.parse().ok())
454            .or(self.backpressure_threshold)
455            .unwrap_or(100_000)
456    }
457
458    pub fn backpressure_threshold_for_rpc(&self) -> u64 {
459        std::env::var("IOTA_CACHE_WRITEBACK_BACKPRESSURE_THRESHOLD_FOR_RPC")
460            .ok()
461            .and_then(|s| s.parse().ok())
462            .or(self.backpressure_threshold_for_rpc)
463            .unwrap_or(self.backpressure_threshold())
464    }
465}
466
467#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
468#[serde(rename_all = "lowercase")]
469pub enum ServerType {
470    WebSocket,
471    Http,
472    Both,
473}
474
475#[derive(Clone, Debug, Deserialize, Serialize)]
476#[serde(rename_all = "kebab-case")]
477pub struct TransactionKeyValueStoreReadConfig {
478    #[serde(default = "default_base_url")]
479    pub base_url: String,
480
481    #[serde(default = "default_cache_size")]
482    pub cache_size: u64,
483}
484
485impl Default for TransactionKeyValueStoreReadConfig {
486    fn default() -> Self {
487        Self {
488            base_url: default_base_url(),
489            cache_size: default_cache_size(),
490        }
491    }
492}
493
494fn default_base_url() -> String {
495    "".to_string()
496}
497
498fn default_cache_size() -> u64 {
499    100_000
500}
501
502fn default_jwk_fetch_interval_seconds() -> u64 {
503    3600
504}
505
506pub fn default_zklogin_oauth_providers() -> BTreeMap<Chain, BTreeSet<String>> {
507    let mut map = BTreeMap::new();
508
509    // providers that are available on devnet only.
510    let experimental_providers = BTreeSet::from([
511        "Google".to_string(),
512        "Facebook".to_string(),
513        "Twitch".to_string(),
514        "Kakao".to_string(),
515        "Apple".to_string(),
516        "Slack".to_string(),
517        "TestIssuer".to_string(),
518        "Microsoft".to_string(),
519        "KarrierOne".to_string(),
520        "Credenza3".to_string(),
521    ]);
522
523    // providers that are available for mainnet and testnet.
524    let providers = BTreeSet::from([
525        "Google".to_string(),
526        "Facebook".to_string(),
527        "Twitch".to_string(),
528        "Apple".to_string(),
529        "KarrierOne".to_string(),
530        "Credenza3".to_string(),
531    ]);
532    map.insert(Chain::Mainnet, providers.clone());
533    map.insert(Chain::Testnet, providers);
534    map.insert(Chain::Unknown, experimental_providers);
535    map
536}
537
538fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
539    TransactionKeyValueStoreReadConfig::default()
540}
541
542fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
543    AuthorityStorePruningConfig::default()
544}
545
546pub fn default_enable_index_processing() -> bool {
547    true
548}
549
550fn default_grpc_address() -> Multiaddr {
551    "/ip4/0.0.0.0/tcp/8080".parse().unwrap()
552}
553fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
554    AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
555}
556
557fn default_key_pair() -> KeyPairWithPath {
558    KeyPairWithPath::new(
559        get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
560            .1
561            .into(),
562    )
563}
564
565fn default_metrics_address() -> SocketAddr {
566    use std::net::{IpAddr, Ipv4Addr};
567    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
568}
569
570pub fn default_admin_interface_address() -> SocketAddr {
571    SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1337)
572}
573
574pub fn default_json_rpc_address() -> SocketAddr {
575    use std::net::{IpAddr, Ipv4Addr};
576    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
577}
578
579pub fn default_grpc_api_config() -> Option<iota_grpc_api::Config> {
580    None
581}
582
583pub fn default_concurrency_limit() -> Option<usize> {
584    Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
585}
586
587pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
588    128
589}
590
591pub fn bool_true() -> bool {
592    true
593}
594
595fn is_true(value: &bool) -> bool {
596    *value
597}
598
599impl Config for NodeConfig {}
600
601impl NodeConfig {
602    pub fn authority_key_pair(&self) -> &AuthorityKeyPair {
603        self.authority_key_pair.authority_keypair()
604    }
605
606    pub fn protocol_key_pair(&self) -> &NetworkKeyPair {
607        match self.protocol_key_pair.keypair() {
608            IotaKeyPair::Ed25519(kp) => kp,
609            other => {
610                panic!("invalid keypair type: {other:?}, only Ed25519 is allowed for protocol key")
611            }
612        }
613    }
614
615    pub fn network_key_pair(&self) -> &NetworkKeyPair {
616        match self.network_key_pair.keypair() {
617            IotaKeyPair::Ed25519(kp) => kp,
618            other => {
619                panic!("invalid keypair type: {other:?}, only Ed25519 is allowed for network key")
620            }
621        }
622    }
623
624    pub fn authority_public_key(&self) -> AuthorityPublicKeyBytes {
625        self.authority_key_pair().public().into()
626    }
627
628    pub fn db_path(&self) -> PathBuf {
629        self.db_path.join("live")
630    }
631
632    pub fn db_checkpoint_path(&self) -> PathBuf {
633        self.db_path.join("db_checkpoints")
634    }
635
636    pub fn archive_path(&self) -> PathBuf {
637        self.db_path.join("archive")
638    }
639
640    pub fn snapshot_path(&self) -> PathBuf {
641        self.db_path.join("snapshot")
642    }
643
644    pub fn network_address(&self) -> &Multiaddr {
645        &self.network_address
646    }
647
648    pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
649        self.consensus_config.as_ref()
650    }
651
652    pub fn genesis(&self) -> Result<&genesis::Genesis> {
653        self.genesis.genesis()
654    }
655
656    pub fn load_migration_tx_data(&self) -> Result<MigrationTxData> {
657        let Some(location) = &self.migration_tx_data_path else {
658            anyhow::bail!("no file location set");
659        };
660
661        // Load from file
662        let migration_tx_data = MigrationTxData::load(location)?;
663
664        // Validate migration content in order to avoid corrupted or malicious data
665        migration_tx_data.validate_from_genesis(self.genesis.genesis()?)?;
666        Ok(migration_tx_data)
667    }
668
669    pub fn iota_address(&self) -> IotaAddress {
670        (&self.account_key_pair.keypair().public()).into()
671    }
672
673    pub fn archive_reader_config(&self) -> Vec<ArchiveReaderConfig> {
674        self.state_archive_read_config
675            .iter()
676            .flat_map(|config| {
677                config
678                    .object_store_config
679                    .as_ref()
680                    .map(|remote_store_config| ArchiveReaderConfig {
681                        remote_store_config: remote_store_config.clone(),
682                        download_concurrency: NonZeroUsize::new(config.concurrency)
683                            .unwrap_or(NonZeroUsize::new(5).unwrap()),
684                        use_for_pruning_watermark: config.use_for_pruning_watermark,
685                    })
686            })
687            .collect()
688    }
689
690    pub fn jsonrpc_server_type(&self) -> ServerType {
691        self.jsonrpc_server_type.unwrap_or(ServerType::Http)
692    }
693}
694
695#[derive(Debug, Clone, Deserialize, Serialize)]
696pub enum ConsensusProtocol {
697    #[serde(rename = "mysticeti")]
698    Mysticeti,
699    #[serde(rename = "starfish")]
700    Starfish,
701}
702
703#[derive(Debug, Clone, Deserialize, Serialize)]
704#[serde(rename_all = "kebab-case")]
705pub struct ConsensusConfig {
706    // Base consensus DB path for all epochs.
707    pub db_path: PathBuf,
708
709    // The number of epochs for which to retain the consensus DBs. Setting it to 0 will make a
710    // consensus DB getting dropped as soon as system is switched to a new epoch.
711    pub db_retention_epochs: Option<u64>,
712
713    // Pruner will run on every epoch change but it will also check periodically on every
714    // `db_pruner_period_secs` seconds to see if there are any epoch DBs to remove.
715    pub db_pruner_period_secs: Option<u64>,
716
717    /// Maximum number of pending transactions to submit to consensus, including
718    /// those in submission wait.
719    ///
720    /// Default to 20_000 inflight limit, assuming 20_000 txn tps * 1 sec
721    /// consensus latency.
722    pub max_pending_transactions: Option<usize>,
723
724    /// When defined caps the calculated submission position to the
725    /// max_submit_position.
726    ///
727    /// Even if the is elected to submit from a higher
728    /// position than this, it will "reset" to the max_submit_position.
729    pub max_submit_position: Option<usize>,
730
731    /// The submit delay step to consensus defined in milliseconds.
732    ///
733    /// When provided it will override the current back off logic otherwise the
734    /// default backoff logic will be applied based on consensus latency
735    /// estimates.
736    pub submit_delay_step_override_millis: Option<u64>,
737
738    pub parameters: Option<ConsensusParameters>,
739}
740
741impl ConsensusConfig {
742    pub fn db_path(&self) -> &Path {
743        &self.db_path
744    }
745
746    pub fn max_pending_transactions(&self) -> usize {
747        self.max_pending_transactions.unwrap_or(20_000)
748    }
749
750    pub fn submit_delay_step_override(&self) -> Option<Duration> {
751        self.submit_delay_step_override_millis
752            .map(Duration::from_millis)
753    }
754
755    pub fn db_retention_epochs(&self) -> u64 {
756        self.db_retention_epochs.unwrap_or(0)
757    }
758
759    pub fn db_pruner_period(&self) -> Duration {
760        // Default to 1 hour
761        self.db_pruner_period_secs
762            .map(Duration::from_secs)
763            .unwrap_or(Duration::from_secs(3_600))
764    }
765}
766
767#[derive(Clone, Debug, Deserialize, Serialize)]
768#[serde(rename_all = "kebab-case")]
769pub struct CheckpointExecutorConfig {
770    /// Upper bound on the number of checkpoints that can be concurrently
771    /// executed.
772    ///
773    /// If unspecified, this will default to `200`
774    #[serde(default = "default_checkpoint_execution_max_concurrency")]
775    pub checkpoint_execution_max_concurrency: usize,
776
777    /// Number of seconds to wait for effects of a batch of transactions
778    /// before logging a warning. Note that we will continue to retry
779    /// indefinitely.
780    ///
781    /// If unspecified, this will default to `10`.
782    #[serde(default = "default_local_execution_timeout_sec")]
783    pub local_execution_timeout_sec: u64,
784
785    /// Optional directory used for data ingestion pipeline.
786    ///
787    /// When specified, each executed checkpoint will be saved in a local
788    /// directory for post-processing
789    #[serde(default, skip_serializing_if = "Option::is_none")]
790    pub data_ingestion_dir: Option<PathBuf>,
791}
792
793#[derive(Clone, Debug, Default, Deserialize, Serialize)]
794#[serde(rename_all = "kebab-case")]
795pub struct ExpensiveSafetyCheckConfig {
796    /// If enabled, at epoch boundary, we will check that the storage
797    /// fund balance is always identical to the sum of the storage
798    /// rebate of all live objects, and that the total IOTA in the network
799    /// remains the same.
800    #[serde(default)]
801    enable_epoch_iota_conservation_check: bool,
802
803    /// If enabled, we will check that the total IOTA in all input objects of a
804    /// tx (both the Move part and the storage rebate) matches the total IOTA
805    /// in all output objects of the tx + gas fees.
806    #[serde(default)]
807    enable_deep_per_tx_iota_conservation_check: bool,
808
809    /// Disable epoch IOTA conservation check even when we are running in debug
810    /// mode.
811    #[serde(default)]
812    force_disable_epoch_iota_conservation_check: bool,
813
814    /// If enabled, at epoch boundary, we will check that the accumulated
815    /// live object state matches the end of epoch root state digest.
816    #[serde(default)]
817    enable_state_consistency_check: bool,
818
819    /// Disable state consistency check even when we are running in debug mode.
820    #[serde(default)]
821    force_disable_state_consistency_check: bool,
822
823    #[serde(default)]
824    enable_secondary_index_checks: bool,
825    // TODO: Add more expensive checks here
826}
827
828impl ExpensiveSafetyCheckConfig {
829    pub fn new_enable_all() -> Self {
830        Self {
831            enable_epoch_iota_conservation_check: true,
832            enable_deep_per_tx_iota_conservation_check: true,
833            force_disable_epoch_iota_conservation_check: false,
834            enable_state_consistency_check: true,
835            force_disable_state_consistency_check: false,
836            enable_secondary_index_checks: false, // Disable by default for now
837        }
838    }
839
840    pub fn new_disable_all() -> Self {
841        Self {
842            enable_epoch_iota_conservation_check: false,
843            enable_deep_per_tx_iota_conservation_check: false,
844            force_disable_epoch_iota_conservation_check: true,
845            enable_state_consistency_check: false,
846            force_disable_state_consistency_check: true,
847            enable_secondary_index_checks: false,
848        }
849    }
850
851    pub fn force_disable_epoch_iota_conservation_check(&mut self) {
852        self.force_disable_epoch_iota_conservation_check = true;
853    }
854
855    pub fn enable_epoch_iota_conservation_check(&self) -> bool {
856        (self.enable_epoch_iota_conservation_check || cfg!(debug_assertions))
857            && !self.force_disable_epoch_iota_conservation_check
858    }
859
860    pub fn force_disable_state_consistency_check(&mut self) {
861        self.force_disable_state_consistency_check = true;
862    }
863
864    pub fn enable_state_consistency_check(&self) -> bool {
865        (self.enable_state_consistency_check || cfg!(debug_assertions))
866            && !self.force_disable_state_consistency_check
867    }
868
869    pub fn enable_deep_per_tx_iota_conservation_check(&self) -> bool {
870        self.enable_deep_per_tx_iota_conservation_check || cfg!(debug_assertions)
871    }
872
873    pub fn enable_secondary_index_checks(&self) -> bool {
874        self.enable_secondary_index_checks
875    }
876}
877
878fn default_checkpoint_execution_max_concurrency() -> usize {
879    40
880}
881
882fn default_local_execution_timeout_sec() -> u64 {
883    30
884}
885
886impl Default for CheckpointExecutorConfig {
887    fn default() -> Self {
888        Self {
889            checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
890            local_execution_timeout_sec: default_local_execution_timeout_sec(),
891            data_ingestion_dir: None,
892        }
893    }
894}
895
896#[derive(Debug, Clone, Deserialize, Serialize)]
897#[serde(rename_all = "kebab-case")]
898pub struct AuthorityStorePruningConfig {
899    /// number of the latest epoch dbs to retain
900    #[serde(default = "default_num_latest_epoch_dbs_to_retain")]
901    pub num_latest_epoch_dbs_to_retain: usize,
902    /// time interval used by the pruner to determine whether there are any
903    /// epoch DBs to remove
904    #[serde(default = "default_epoch_db_pruning_period_secs")]
905    pub epoch_db_pruning_period_secs: u64,
906    /// number of epochs to keep the latest version of objects for.
907    /// Note that a zero value corresponds to an aggressive pruner.
908    /// This mode is experimental and needs to be used with caution.
909    /// Use `u64::MAX` to disable the pruner for the objects.
910    #[serde(default)]
911    pub num_epochs_to_retain: u64,
912    /// pruner's runtime interval used for aggressive mode
913    #[serde(skip_serializing_if = "Option::is_none")]
914    pub pruning_run_delay_seconds: Option<u64>,
915    /// maximum number of checkpoints in the pruning batch. Can be adjusted to
916    /// increase performance
917    #[serde(default = "default_max_checkpoints_in_batch")]
918    pub max_checkpoints_in_batch: usize,
919    /// maximum number of transaction in the pruning batch
920    #[serde(default = "default_max_transactions_in_batch")]
921    pub max_transactions_in_batch: usize,
922    /// enables periodic background compaction for old SST files whose last
923    /// modified time is older than `periodic_compaction_threshold_days`
924    /// days. That ensures that all sst files eventually go through the
925    /// compaction process
926    #[serde(
927        default = "default_periodic_compaction_threshold_days",
928        skip_serializing_if = "Option::is_none"
929    )]
930    pub periodic_compaction_threshold_days: Option<usize>,
931    /// number of epochs to keep the latest version of transactions and effects
932    /// for
933    #[serde(skip_serializing_if = "Option::is_none")]
934    pub num_epochs_to_retain_for_checkpoints: Option<u64>,
935    #[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
936    pub smooth: bool,
937    /// Enables the compaction filter for pruning the objects table.
938    /// If disabled, a range deletion approach is used instead.
939    /// While it is generally safe to switch between the two modes,
940    /// switching from the compaction filter approach back to range deletion
941    /// may result in some old versions that will never be pruned.
942    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
943    pub enable_compaction_filter: bool,
944}
945
946fn default_num_latest_epoch_dbs_to_retain() -> usize {
947    3
948}
949
950fn default_epoch_db_pruning_period_secs() -> u64 {
951    3600
952}
953
954fn default_max_transactions_in_batch() -> usize {
955    1000
956}
957
958fn default_max_checkpoints_in_batch() -> usize {
959    10
960}
961
962fn default_smoothing() -> bool {
963    cfg!(not(test))
964}
965
966fn default_periodic_compaction_threshold_days() -> Option<usize> {
967    Some(1)
968}
969
970impl Default for AuthorityStorePruningConfig {
971    fn default() -> Self {
972        Self {
973            num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
974            epoch_db_pruning_period_secs: default_epoch_db_pruning_period_secs(),
975            num_epochs_to_retain: 0,
976            pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
977            max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
978            max_transactions_in_batch: default_max_transactions_in_batch(),
979            periodic_compaction_threshold_days: None,
980            num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
981            smooth: true,
982            enable_compaction_filter: cfg!(test) || cfg!(msim),
983        }
984    }
985}
986
987impl AuthorityStorePruningConfig {
988    pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
989        self.num_epochs_to_retain = num_epochs_to_retain;
990    }
991
992    pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
993        self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
994    }
995
996    pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
997        self.num_epochs_to_retain_for_checkpoints
998            // if n less than 2, coerce to 2 and log
999            .map(|n| {
1000                if n < 2 {
1001                    info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
1002                    2
1003                } else {
1004                    n
1005                }
1006            })
1007    }
1008}
1009
1010#[derive(Debug, Clone, Deserialize, Serialize)]
1011#[serde(rename_all = "kebab-case")]
1012pub struct MetricsConfig {
1013    #[serde(skip_serializing_if = "Option::is_none")]
1014    pub push_interval_seconds: Option<u64>,
1015    #[serde(skip_serializing_if = "Option::is_none")]
1016    pub push_url: Option<String>,
1017}
1018
1019#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1020#[serde(rename_all = "kebab-case")]
1021pub struct DBCheckpointConfig {
1022    #[serde(default)]
1023    pub perform_db_checkpoints_at_epoch_end: bool,
1024    #[serde(skip_serializing_if = "Option::is_none")]
1025    pub checkpoint_path: Option<PathBuf>,
1026    #[serde(skip_serializing_if = "Option::is_none")]
1027    pub object_store_config: Option<ObjectStoreConfig>,
1028    #[serde(skip_serializing_if = "Option::is_none")]
1029    pub perform_index_db_checkpoints_at_epoch_end: Option<bool>,
1030    #[serde(skip_serializing_if = "Option::is_none")]
1031    pub prune_and_compact_before_upload: Option<bool>,
1032}
1033
1034#[derive(Debug, Clone)]
1035pub struct ArchiveReaderConfig {
1036    pub remote_store_config: ObjectStoreConfig,
1037    pub download_concurrency: NonZeroUsize,
1038    pub use_for_pruning_watermark: bool,
1039}
1040
1041#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1042#[serde(rename_all = "kebab-case")]
1043pub struct StateArchiveConfig {
1044    #[serde(skip_serializing_if = "Option::is_none")]
1045    pub object_store_config: Option<ObjectStoreConfig>,
1046    pub concurrency: usize,
1047    pub use_for_pruning_watermark: bool,
1048}
1049
1050#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1051#[serde(rename_all = "kebab-case")]
1052pub struct StateSnapshotConfig {
1053    #[serde(skip_serializing_if = "Option::is_none")]
1054    pub object_store_config: Option<ObjectStoreConfig>,
1055    pub concurrency: usize,
1056}
1057
1058#[derive(Default, Debug, Clone, Deserialize, Serialize)]
1059#[serde(rename_all = "kebab-case")]
1060pub struct TransactionKeyValueStoreWriteConfig {
1061    pub aws_access_key_id: String,
1062    pub aws_secret_access_key: String,
1063    pub aws_region: String,
1064    pub table_name: String,
1065    pub bucket_name: String,
1066    pub concurrency: usize,
1067}
1068
1069/// Configuration for the threshold(s) at which we consider the system
1070/// to be overloaded. When one of the threshold is passed, the node may
1071/// stop processing new transactions and/or certificates until the congestion
1072/// resolves.
1073#[derive(Clone, Debug, Deserialize, Serialize)]
1074#[serde(rename_all = "kebab-case")]
1075pub struct AuthorityOverloadConfig {
1076    #[serde(default = "default_max_txn_age_in_queue")]
1077    pub max_txn_age_in_queue: Duration,
1078
1079    // The interval of checking overload signal.
1080    #[serde(default = "default_overload_monitor_interval")]
1081    pub overload_monitor_interval: Duration,
1082
1083    // The execution queueing latency when entering load shedding mode.
1084    #[serde(default = "default_execution_queue_latency_soft_limit")]
1085    pub execution_queue_latency_soft_limit: Duration,
1086
1087    // The execution queueing latency when entering aggressive load shedding mode.
1088    #[serde(default = "default_execution_queue_latency_hard_limit")]
1089    pub execution_queue_latency_hard_limit: Duration,
1090
1091    // The maximum percentage of transactions to shed in load shedding mode.
1092    #[serde(default = "default_max_load_shedding_percentage")]
1093    pub max_load_shedding_percentage: u32,
1094
1095    // When in aggressive load shedding mode, the minimum percentage of
1096    // transactions to shed.
1097    #[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
1098    pub min_load_shedding_percentage_above_hard_limit: u32,
1099
1100    // If transaction ready rate is below this rate, we consider the validator
1101    // is well under used, and will not enter load shedding mode.
1102    #[serde(default = "default_safe_transaction_ready_rate")]
1103    pub safe_transaction_ready_rate: u32,
1104
1105    // When set to true, transaction signing may be rejected when the validator
1106    // is overloaded.
1107    #[serde(default = "default_check_system_overload_at_signing")]
1108    pub check_system_overload_at_signing: bool,
1109
1110    // When set to true, transaction execution may be rejected when the validator
1111    // is overloaded.
1112    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1113    pub check_system_overload_at_execution: bool,
1114
1115    // Reject a transaction if transaction manager queue length is above this threshold.
1116    // 100_000 = 10k TPS * 5s resident time in transaction manager (pending + executing) * 2.
1117    #[serde(default = "default_max_transaction_manager_queue_length")]
1118    pub max_transaction_manager_queue_length: usize,
1119
1120    // Reject a transaction if the number of pending transactions depending on the object
1121    // is above the threshold.
1122    #[serde(default = "default_max_transaction_manager_per_object_queue_length")]
1123    pub max_transaction_manager_per_object_queue_length: usize,
1124}
1125
1126fn default_max_txn_age_in_queue() -> Duration {
1127    Duration::from_millis(500)
1128}
1129
1130fn default_overload_monitor_interval() -> Duration {
1131    Duration::from_secs(10)
1132}
1133
1134fn default_execution_queue_latency_soft_limit() -> Duration {
1135    Duration::from_secs(1)
1136}
1137
1138fn default_execution_queue_latency_hard_limit() -> Duration {
1139    Duration::from_secs(10)
1140}
1141
1142fn default_max_load_shedding_percentage() -> u32 {
1143    95
1144}
1145
1146fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
1147    50
1148}
1149
1150fn default_safe_transaction_ready_rate() -> u32 {
1151    100
1152}
1153
1154fn default_check_system_overload_at_signing() -> bool {
1155    true
1156}
1157
1158fn default_max_transaction_manager_queue_length() -> usize {
1159    100_000
1160}
1161
1162fn default_max_transaction_manager_per_object_queue_length() -> usize {
1163    20
1164}
1165
1166impl Default for AuthorityOverloadConfig {
1167    fn default() -> Self {
1168        Self {
1169            max_txn_age_in_queue: default_max_txn_age_in_queue(),
1170            overload_monitor_interval: default_overload_monitor_interval(),
1171            execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
1172            execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
1173            max_load_shedding_percentage: default_max_load_shedding_percentage(),
1174            min_load_shedding_percentage_above_hard_limit:
1175                default_min_load_shedding_percentage_above_hard_limit(),
1176            safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
1177            check_system_overload_at_signing: true,
1178            check_system_overload_at_execution: false,
1179            max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
1180            max_transaction_manager_per_object_queue_length:
1181                default_max_transaction_manager_per_object_queue_length(),
1182        }
1183    }
1184}
1185
1186fn default_authority_overload_config() -> AuthorityOverloadConfig {
1187    AuthorityOverloadConfig::default()
1188}
1189
1190#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1191pub struct Genesis {
1192    #[serde(flatten)]
1193    location: Option<GenesisLocation>,
1194
1195    #[serde(skip)]
1196    genesis: once_cell::sync::OnceCell<genesis::Genesis>,
1197}
1198
1199impl Genesis {
1200    pub fn new(genesis: genesis::Genesis) -> Self {
1201        Self {
1202            location: Some(GenesisLocation::InPlace {
1203                genesis: Box::new(genesis),
1204            }),
1205            genesis: Default::default(),
1206        }
1207    }
1208
1209    pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
1210        Self {
1211            location: Some(GenesisLocation::File {
1212                genesis_file_location: path.into(),
1213            }),
1214            genesis: Default::default(),
1215        }
1216    }
1217
1218    pub fn new_empty() -> Self {
1219        Self {
1220            location: None,
1221            genesis: Default::default(),
1222        }
1223    }
1224
1225    pub fn genesis(&self) -> Result<&genesis::Genesis> {
1226        match &self.location {
1227            Some(GenesisLocation::InPlace { genesis }) => Ok(genesis),
1228            Some(GenesisLocation::File {
1229                genesis_file_location,
1230            }) => self
1231                .genesis
1232                .get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
1233            None => anyhow::bail!("no genesis location set"),
1234        }
1235    }
1236}
1237
1238#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1239#[serde(untagged)]
1240enum GenesisLocation {
1241    InPlace {
1242        genesis: Box<genesis::Genesis>,
1243    },
1244    File {
1245        #[serde(rename = "genesis-file-location")]
1246        genesis_file_location: PathBuf,
1247    },
1248}
1249
1250/// Wrapper struct for IotaKeyPair that can be deserialized from a file path.
1251/// Used by network, worker, and account keypair.
1252#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1253pub struct KeyPairWithPath {
1254    #[serde(flatten)]
1255    location: KeyPairLocation,
1256
1257    #[serde(skip)]
1258    keypair: OnceCell<Arc<IotaKeyPair>>,
1259}
1260
1261#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1262#[serde(untagged)]
1263enum KeyPairLocation {
1264    InPlace {
1265        #[serde(with = "bech32_formatted_keypair")]
1266        value: Arc<IotaKeyPair>,
1267    },
1268    File {
1269        path: PathBuf,
1270    },
1271}
1272
1273impl KeyPairWithPath {
1274    pub fn new(kp: IotaKeyPair) -> Self {
1275        let cell: OnceCell<Arc<IotaKeyPair>> = OnceCell::new();
1276        let arc_kp = Arc::new(kp);
1277        // OK to unwrap panic because authority should not start without all keypairs
1278        // loaded.
1279        cell.set(arc_kp.clone()).expect("failed to set keypair");
1280        Self {
1281            location: KeyPairLocation::InPlace { value: arc_kp },
1282            keypair: cell,
1283        }
1284    }
1285
1286    pub fn new_from_path(path: PathBuf) -> Self {
1287        let cell: OnceCell<Arc<IotaKeyPair>> = OnceCell::new();
1288        // OK to unwrap panic because authority should not start without all keypairs
1289        // loaded.
1290        cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
1291            |e| panic!("invalid keypair file at path {:?}: {e}", &path),
1292        )))
1293        .expect("failed to set keypair");
1294        Self {
1295            location: KeyPairLocation::File { path },
1296            keypair: cell,
1297        }
1298    }
1299
1300    pub fn keypair(&self) -> &IotaKeyPair {
1301        self.keypair
1302            .get_or_init(|| match &self.location {
1303                KeyPairLocation::InPlace { value } => value.clone(),
1304                KeyPairLocation::File { path } => {
1305                    // OK to unwrap panic because authority should not start without all keypairs
1306                    // loaded.
1307                    Arc::new(
1308                        read_keypair_from_file(path).unwrap_or_else(|e| {
1309                            panic!("invalid keypair file at path {path:?}: {e}")
1310                        }),
1311                    )
1312                }
1313            })
1314            .as_ref()
1315    }
1316}
1317
1318/// Wrapper struct for AuthorityKeyPair that can be deserialized from a file
1319/// path.
1320#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1321pub struct AuthorityKeyPairWithPath {
1322    #[serde(flatten)]
1323    location: AuthorityKeyPairLocation,
1324
1325    #[serde(skip)]
1326    keypair: OnceCell<Arc<AuthorityKeyPair>>,
1327}
1328
1329#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
1330#[serde(untagged)]
1331enum AuthorityKeyPairLocation {
1332    InPlace { value: Arc<AuthorityKeyPair> },
1333    File { path: PathBuf },
1334}
1335
1336impl AuthorityKeyPairWithPath {
1337    pub fn new(kp: AuthorityKeyPair) -> Self {
1338        let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1339        let arc_kp = Arc::new(kp);
1340        // OK to unwrap panic because authority should not start without all keypairs
1341        // loaded.
1342        cell.set(arc_kp.clone())
1343            .expect("failed to set authority keypair");
1344        Self {
1345            location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
1346            keypair: cell,
1347        }
1348    }
1349
1350    pub fn new_from_path(path: PathBuf) -> Self {
1351        let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
1352        // OK to unwrap panic because authority should not start without all keypairs
1353        // loaded.
1354        cell.set(Arc::new(
1355            read_authority_keypair_from_file(&path)
1356                .unwrap_or_else(|_| panic!("invalid authority keypair file at path {:?}", &path)),
1357        ))
1358        .expect("failed to set authority keypair");
1359        Self {
1360            location: AuthorityKeyPairLocation::File { path },
1361            keypair: cell,
1362        }
1363    }
1364
1365    pub fn authority_keypair(&self) -> &AuthorityKeyPair {
1366        self.keypair
1367            .get_or_init(|| match &self.location {
1368                AuthorityKeyPairLocation::InPlace { value } => value.clone(),
1369                AuthorityKeyPairLocation::File { path } => {
1370                    // OK to unwrap panic because authority should not start without all keypairs
1371                    // loaded.
1372                    Arc::new(
1373                        read_authority_keypair_from_file(path).unwrap_or_else(|_| {
1374                            panic!("invalid authority keypair file {:?}", &path)
1375                        }),
1376                    )
1377                }
1378            })
1379            .as_ref()
1380    }
1381}
1382
1383/// Configurations which determine how we dump state debug info.
1384/// Debug info is dumped when a node forks.
1385#[derive(Clone, Debug, Deserialize, Serialize, Default)]
1386#[serde(rename_all = "kebab-case")]
1387pub struct StateDebugDumpConfig {
1388    #[serde(skip_serializing_if = "Option::is_none")]
1389    pub dump_file_directory: Option<PathBuf>,
1390}
1391
1392#[cfg(test)]
1393mod tests {
1394    use std::path::PathBuf;
1395
1396    use fastcrypto::traits::KeyPair;
1397    use iota_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
1398    use iota_types::crypto::{
1399        AuthorityKeyPair, IotaKeyPair, NetworkKeyPair, get_key_pair_from_rng,
1400    };
1401    use rand::{SeedableRng, rngs::StdRng};
1402
1403    use super::Genesis;
1404    use crate::NodeConfig;
1405
1406    #[test]
1407    fn serialize_genesis_from_file() {
1408        let g = Genesis::new_from_file("path/to/file");
1409
1410        let s = serde_yaml::to_string(&g).unwrap();
1411        assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
1412        let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
1413        assert_eq!(g, loaded_genesis);
1414    }
1415
1416    #[test]
1417    fn fullnode_template() {
1418        const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
1419
1420        let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1421    }
1422
1423    #[test]
1424    fn load_key_pairs_to_node_config() {
1425        let authority_key_pair: AuthorityKeyPair =
1426            get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1427        let protocol_key_pair: NetworkKeyPair =
1428            get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1429        let network_key_pair: NetworkKeyPair =
1430            get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
1431
1432        write_authority_keypair_to_file(&authority_key_pair, PathBuf::from("authority.key"))
1433            .unwrap();
1434        write_keypair_to_file(
1435            &IotaKeyPair::Ed25519(protocol_key_pair.copy()),
1436            PathBuf::from("protocol.key"),
1437        )
1438        .unwrap();
1439        write_keypair_to_file(
1440            &IotaKeyPair::Ed25519(network_key_pair.copy()),
1441            PathBuf::from("network.key"),
1442        )
1443        .unwrap();
1444
1445        const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
1446        let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
1447        assert_eq!(
1448            template.authority_key_pair().public(),
1449            authority_key_pair.public()
1450        );
1451        assert_eq!(
1452            template.network_key_pair().public(),
1453            network_key_pair.public()
1454        );
1455        assert_eq!(
1456            template.protocol_key_pair().public(),
1457            protocol_key_pair.public()
1458        );
1459    }
1460}
1461
1462// RunWithRange is used to specify the ending epoch/checkpoint to process.
1463// this is intended for use with disaster recovery debugging and verification
1464// workflows, never in normal operations
1465#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
1466pub enum RunWithRange {
1467    Epoch(EpochId),
1468    Checkpoint(CheckpointSequenceNumber),
1469}
1470
1471impl RunWithRange {
1472    // is epoch_id > RunWithRange::Epoch
1473    pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
1474        matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
1475    }
1476
1477    pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
1478        matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
1479    }
1480
1481    pub fn into_checkpoint_bound(self) -> Option<CheckpointSequenceNumber> {
1482        match self {
1483            RunWithRange::Epoch(_) => None,
1484            RunWithRange::Checkpoint(seq) => Some(seq),
1485        }
1486    }
1487}
1488
1489/// A serde helper module used with #[serde(with = "...")] to change the
1490/// de/serialization format of an `IotaKeyPair` to Bech32 when written to or
1491/// read from a node config.
1492mod bech32_formatted_keypair {
1493    use std::ops::Deref;
1494
1495    use iota_types::crypto::{EncodeDecodeBase64, IotaKeyPair};
1496    use serde::{Deserialize, Deserializer, Serializer};
1497
1498    pub fn serialize<S, T>(kp: &T, serializer: S) -> Result<S::Ok, S::Error>
1499    where
1500        S: Serializer,
1501        T: Deref<Target = IotaKeyPair>,
1502    {
1503        use serde::ser::Error;
1504
1505        // Serialize the keypair to a Bech32 string
1506        let s = kp.encode().map_err(Error::custom)?;
1507
1508        serializer.serialize_str(&s)
1509    }
1510
1511    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
1512    where
1513        D: Deserializer<'de>,
1514        T: From<IotaKeyPair>,
1515    {
1516        use serde::de::Error;
1517
1518        let s = String::deserialize(deserializer)?;
1519
1520        // Try to deserialize the keypair from a Bech32 formatted string
1521        IotaKeyPair::decode(&s)
1522            .or_else(|_| {
1523                // For backwards compatibility try Base64 if Bech32 failed
1524                IotaKeyPair::decode_base64(&s)
1525            })
1526            .map(Into::into)
1527            .map_err(Error::custom)
1528    }
1529}