1use anyhow::Context;
5use anyhow::Result;
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8use std::fs;
9use std::path::{Path, PathBuf};
10use tracing::trace;
11
12pub mod certificate_deny_config;
13pub mod dynamic_transaction_signing_checks;
14pub mod genesis;
15pub mod local_ip_utils;
16pub mod node;
17pub mod node_config_metrics;
18pub mod object_storage_config;
19pub mod p2p;
20pub mod rpc_config;
21pub mod transaction_deny_config;
22pub mod validator_client_monitor_config;
23pub mod verifier_signing_config;
24
25pub use node::{ConsensusConfig, ExecutionCacheConfig, NodeConfig};
26pub use rpc_config::{RpcConfig, RpcIndexInitConfig, RpcTlsConfig};
27use sui_types::multiaddr::Multiaddr;
28use tracing::debug;
29
30const SUI_DIR: &str = ".sui";
31pub const SUI_CONFIG_DIR: &str = "sui_config";
32pub const SUI_NETWORK_CONFIG: &str = "network.yaml";
33pub const SUI_FULLNODE_CONFIG: &str = "fullnode.yaml";
34pub const SUI_CLIENT_CONFIG: &str = "client.yaml";
35pub const SUI_KEYSTORE_FILENAME: &str = "sui.keystore";
36pub const SUI_KEYSTORE_ALIASES_FILENAME: &str = "sui.aliases";
37pub const SUI_BENCHMARK_GENESIS_GAS_KEYSTORE_FILENAME: &str = "benchmark.keystore";
38pub const SUI_GENESIS_FILENAME: &str = "genesis.blob";
39pub const SUI_DEV_NET_URL: &str = "https://fullnode.devnet.sui.io:443";
40
41pub const AUTHORITIES_DB_NAME: &str = "authorities_db";
42pub const CONSENSUS_DB_NAME: &str = "consensus_db";
43pub const FULL_NODE_DB_PATH: &str = "full_node_db";
44
45pub fn sui_config_dir() -> Result<PathBuf, anyhow::Error> {
46 match std::env::var_os("SUI_CONFIG_DIR") {
47 Some(config_env) => Ok(config_env.into()),
48 None => match dirs::home_dir() {
49 Some(v) => Ok(v.join(SUI_DIR).join(SUI_CONFIG_DIR)),
50 None => anyhow::bail!("Cannot obtain home directory path"),
51 },
52 }
53 .and_then(|dir| {
54 if !dir.exists() {
55 fs::create_dir_all(dir.clone())?;
56 }
57 Ok(dir)
58 })
59}
60
61pub fn genesis_blob_exists(config_dir: Option<PathBuf>) -> bool {
63 if let Some(dir) = config_dir {
64 dir.join(SUI_GENESIS_FILENAME).exists()
65 } else if let Some(config_env) = std::env::var_os("SUI_CONFIG_DIR") {
66 Path::new(&config_env).join(SUI_GENESIS_FILENAME).exists()
67 } else if let Some(home) = dirs::home_dir() {
68 let mut config = PathBuf::new();
69 config.push(&home);
70 config.extend([SUI_DIR, SUI_CONFIG_DIR, SUI_GENESIS_FILENAME]);
71 config.exists()
72 } else {
73 false
74 }
75}
76
77pub fn validator_config_file(address: Multiaddr, i: usize) -> String {
78 multiaddr_to_filename(address).unwrap_or(format!("validator-config-{}.yaml", i))
79}
80
81pub fn ssfn_config_file(address: Multiaddr, i: usize) -> String {
82 multiaddr_to_filename(address).unwrap_or(format!("ssfn-config-{}.yaml", i))
83}
84
85fn multiaddr_to_filename(address: Multiaddr) -> Option<String> {
86 if let Some(hostname) = address.hostname()
87 && let Some(port) = address.port()
88 {
89 return Some(format!("{}-{}.yaml", hostname, port));
90 }
91 None
92}
93
94pub trait Config
95where
96 Self: DeserializeOwned + Serialize,
97{
98 fn persisted(self, path: &Path) -> PersistedConfig<Self> {
99 PersistedConfig {
100 inner: self,
101 path: path.to_path_buf(),
102 }
103 }
104
105 fn load<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
106 let path = path.as_ref();
107 trace!("Reading config from {}", path.display());
108 let reader = fs::File::open(path)
109 .with_context(|| format!("Unable to load config from {}", path.display()))?;
110 Ok(serde_yaml::from_reader(reader)?)
111 }
112
113 fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), anyhow::Error> {
114 let path = path.as_ref();
115 trace!("Writing config to {}", path.display());
116 let config = serde_yaml::to_string(&self)?;
117 fs::write(path, config)
118 .with_context(|| format!("Unable to save config to {}", path.display()))?;
119 Ok(())
120 }
121
122 fn load_with_lock<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
124 let path = path.as_ref();
125 debug!("Reading config with lock from {}", path.display());
126 let file = fs::File::open(path)
127 .with_context(|| format!("Unable to load config from {}", path.display()))?;
128 file.lock_shared()?;
129 let config: Self = serde_yaml::from_reader(&file)?;
130 file.unlock()?;
131 Ok(config)
132 }
133
134 fn save_with_lock<P: AsRef<Path>>(&self, path: P) -> Result<(), anyhow::Error> {
137 let path = path.as_ref();
138 debug!("Writing config with lock to {}", path.display());
139 let config_str = serde_yaml::to_string(&self)?;
140
141 let file = fs::OpenOptions::new()
142 .write(true)
143 .create(true)
144 .truncate(true)
145 .open(path)
146 .with_context(|| {
147 format!(
148 "Unable to open config file for writing at {}",
149 path.display()
150 )
151 })?;
152
153 file.lock()
154 .with_context(|| format!("Unable to acquire exclusive lock on {}", path.display()))?;
155
156 fs::write(path, config_str)
157 .with_context(|| format!("Unable to save config to {}", path.display()))?;
158
159 file.unlock()?;
160 Ok(())
161 }
162}
163
164pub struct PersistedConfig<C> {
165 inner: C,
166 path: PathBuf,
167}
168
169impl<C> PersistedConfig<C>
170where
171 C: Config,
172{
173 pub fn read(path: &Path) -> Result<C, anyhow::Error> {
174 Config::load(path)
175 }
176
177 pub fn save(&self) -> Result<(), anyhow::Error> {
178 self.inner.save(&self.path)
179 }
180
181 pub fn save_with_lock(&self) -> Result<(), anyhow::Error> {
182 self.inner.save_with_lock(&self.path)
183 }
184
185 pub fn into_inner(self) -> C {
186 self.inner
187 }
188
189 pub fn path(&self) -> &Path {
190 &self.path
191 }
192}
193
194impl<C> std::ops::Deref for PersistedConfig<C> {
195 type Target = C;
196
197 fn deref(&self) -> &Self::Target {
198 &self.inner
199 }
200}
201
202impl<C> std::ops::DerefMut for PersistedConfig<C> {
203 fn deref_mut(&mut self) -> &mut Self::Target {
204 &mut self.inner
205 }
206}