sui_config/
lib.rs

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