1use std::{
6 fs,
7 io::BufWriter,
8 path::{Path, PathBuf},
9};
10
11use anyhow::{Context, Result};
12use serde::{Serialize, de::DeserializeOwned};
13use tracing::trace;
14
15pub mod certificate_deny_config;
16pub mod genesis;
17pub mod local_ip_utils;
18pub mod migration_tx_data;
19pub mod node;
20pub mod node_config_metrics;
21pub mod object_storage_config;
22pub mod p2p;
23pub mod transaction_deny_config;
24pub mod verifier_signing_config;
25
26use iota_types::multiaddr::Multiaddr;
27pub use node::{
28 ConsensusConfig, ExecutionCacheConfig, ExecutionCacheType, NodeConfig, WritebackCacheConfig,
29};
30
31const IOTA_DIR: &str = ".iota";
32pub const IOTA_CONFIG_DIR: &str = "iota_config";
33pub const IOTA_NETWORK_CONFIG: &str = "network.yaml";
34pub const IOTA_FULLNODE_CONFIG: &str = "fullnode.yaml";
35pub const IOTA_CLIENT_CONFIG: &str = "client.yaml";
36pub const IOTA_KEYSTORE_FILENAME: &str = "iota.keystore";
37pub const IOTA_BENCHMARK_GENESIS_GAS_KEYSTORE_FILENAME: &str = "benchmark.keystore";
38pub const IOTA_GENESIS_FILENAME: &str = "genesis.blob";
39pub const IOTA_GENESIS_MIGRATION_TX_DATA_FILENAME: &str = "migration.blob";
40pub const IOTA_DEV_NET_URL: &str = "https://api.devnet.iota.cafe:443";
41
42pub const AUTHORITIES_DB_NAME: &str = "authorities_db";
43pub const CONSENSUS_DB_NAME: &str = "consensus_db";
44pub const FULL_NODE_DB_PATH: &str = "full_node_db";
45
46pub fn iota_config_dir() -> Result<PathBuf, anyhow::Error> {
47 match std::env::var_os("IOTA_CONFIG_DIR") {
48 Some(config_env) => Ok(config_env.into()),
49 None => match dirs::home_dir() {
50 Some(v) => Ok(v.join(IOTA_DIR).join(IOTA_CONFIG_DIR)),
51 None => anyhow::bail!("cannot obtain home directory path"),
52 },
53 }
54 .and_then(|dir| {
55 if !dir.exists() {
56 fs::create_dir_all(dir.clone())?;
57 }
58 Ok(dir)
59 })
60}
61
62pub fn genesis_blob_exists(config_dir: Option<PathBuf>) -> bool {
65 if let Some(dir) = config_dir {
66 dir.join(IOTA_GENESIS_FILENAME).exists()
67 } else if let Some(config_env) = std::env::var_os("IOTA_CONFIG_DIR") {
68 Path::new(&config_env).join(IOTA_GENESIS_FILENAME).exists()
69 } else if let Some(home) = dirs::home_dir() {
70 let mut config = PathBuf::new();
71 config.push(&home);
72 config.extend([IOTA_DIR, IOTA_CONFIG_DIR, IOTA_GENESIS_FILENAME]);
73 config.exists()
74 } else {
75 false
76 }
77}
78
79pub fn validator_config_file(address: Multiaddr, i: usize) -> String {
80 multiaddr_to_filename(address).unwrap_or(format!("validator-config-{i}.yaml"))
81}
82
83pub fn ssfn_config_file(address: Multiaddr, i: usize) -> String {
84 multiaddr_to_filename(address).unwrap_or(format!("ssfn-config-{i}.yaml"))
85}
86
87fn multiaddr_to_filename(address: Multiaddr) -> Option<String> {
88 if let Some(hostname) = address.hostname() {
89 if let Some(port) = address.port() {
90 return Some(format!("{hostname}-{port}.yaml"));
91 }
92 }
93 None
94}
95
96pub trait Config
97where
98 Self: DeserializeOwned + Serialize,
99{
100 fn persisted(self, path: &Path) -> PersistedConfig<Self> {
101 PersistedConfig {
102 inner: self,
103 path: path.to_path_buf(),
104 }
105 }
106
107 fn load<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
108 let path = path.as_ref();
109 trace!("Reading config from {}", path.display());
110 let reader = fs::File::open(path)
111 .with_context(|| format!("unable to load config from {}", path.display()))?;
112 Ok(serde_yaml::from_reader(reader)?)
113 }
114
115 fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), anyhow::Error> {
116 let path = path.as_ref();
117 trace!("Writing config to {}", path.display());
118 let mut write = BufWriter::new(fs::File::create(path)?);
119 serde_yaml::to_writer(&mut write, &self)
120 .with_context(|| format!("unable to save config to {}", path.display()))?;
121 Ok(())
122 }
123}
124
125pub struct PersistedConfig<C> {
126 inner: C,
127 path: PathBuf,
128}
129
130impl<C> PersistedConfig<C>
131where
132 C: Config,
133{
134 pub fn read(path: &Path) -> Result<C, anyhow::Error> {
135 Config::load(path)
136 }
137
138 pub fn save(&self) -> Result<(), anyhow::Error> {
139 self.inner.save(&self.path)
140 }
141
142 pub fn into_inner(self) -> C {
143 self.inner
144 }
145
146 pub fn path(&self) -> &Path {
147 &self.path
148 }
149}
150
151impl<C> std::ops::Deref for PersistedConfig<C> {
152 type Target = C;
153
154 fn deref(&self) -> &Self::Target {
155 &self.inner
156 }
157}
158
159impl<C> std::ops::DerefMut for PersistedConfig<C> {
160 fn deref_mut(&mut self) -> &mut Self::Target {
161 &mut self.inner
162 }
163}