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