cuprated/
config.rs
1use std::{
3 fmt,
4 fs::{read_to_string, File},
5 io,
6 path::Path,
7 str::FromStr,
8 time::Duration,
9};
10
11use clap::Parser;
12use serde::{Deserialize, Serialize};
13
14use cuprate_consensus::ContextConfig;
15use cuprate_helper::{
16 fs::{CUPRATE_CONFIG_DIR, DEFAULT_CONFIG_FILE_NAME},
17 network::Network,
18};
19use cuprate_p2p::block_downloader::BlockDownloaderConfig;
20use cuprate_p2p_core::ClearNet;
21
22use crate::{
23 constants::{DEFAULT_CONFIG_STARTUP_DELAY, DEFAULT_CONFIG_WARNING},
24 logging::eprintln_red,
25};
26
27mod args;
28mod fs;
29mod p2p;
30mod rayon;
31mod storage;
32mod tokio;
33mod tracing_config;
34
35#[macro_use]
36mod macros;
37
38use fs::FileSystemConfig;
39use p2p::P2PConfig;
40use rayon::RayonConfig;
41use storage::StorageConfig;
42use tokio::TokioConfig;
43use tracing_config::TracingConfig;
44
45const HEADER: &str = r"## ____ _
47## / ___| _ _ __ _ __ __ _| |_ ___
48## | | | | | | '_ \| '__/ _` | __/ _ \
49## | |__| |_| | |_) | | | (_| | || __/
50## \____\__,_| .__/|_| \__,_|\__\___|
51## |_|
52##
53## All these config values can be set to
54## their default by commenting them out with '#'.
55##
56## Some values are already commented out,
57## to set the value remove the '#' at the start of the line.
58##
59## For more documentation, see: <https://user.cuprate.org>.
60
61";
62
63pub fn read_config_and_args() -> Config {
65 let args = args::Args::parse();
66 args.do_quick_requests();
67
68 let config: Config = if let Some(config_file) = &args.config_file {
69 match Config::read_from_path(config_file) {
71 Ok(config) => config,
72 Err(e) => {
73 eprintln_red(&format!("Failed to read config from file: {e}"));
74 std::process::exit(1);
75 }
76 }
77 } else {
78 std::env::current_dir()
80 .map(|path| path.join(DEFAULT_CONFIG_FILE_NAME))
81 .map_err(Into::into)
82 .and_then(Config::read_from_path)
83 .inspect_err(|e| tracing::debug!("Failed to read config from current dir: {e}"))
84 .or_else(|_| {
86 let file = CUPRATE_CONFIG_DIR.join(DEFAULT_CONFIG_FILE_NAME);
87 Config::read_from_path(file)
88 })
89 .inspect_err(|e| {
90 tracing::debug!("Failed to read config from config dir: {e}");
91 if !args.skip_config_warning {
92 eprintln_red(DEFAULT_CONFIG_WARNING);
93 std::thread::sleep(DEFAULT_CONFIG_STARTUP_DELAY);
94 }
95 })
96 .unwrap_or_default()
97 };
98
99 args.apply_args(config)
100}
101
102config_struct! {
103 #[derive(Debug, Deserialize, Serialize, PartialEq)]
105 #[serde(deny_unknown_fields, default)]
106 pub struct Config {
107 pub network: Network,
111
112 pub fast_sync: bool,
122
123 #[child = true]
124 pub tracing: TracingConfig,
128
129 #[child = true]
130 pub tokio: TokioConfig,
134
135 #[child = true]
136 pub rayon: RayonConfig,
140
141 #[child = true]
142 pub p2p: P2PConfig,
144
145 #[child = true]
146 pub storage: StorageConfig,
148
149 #[child = true]
150 pub fs: FileSystemConfig,
152 }
153}
154
155impl Default for Config {
156 fn default() -> Self {
157 Self {
158 network: Default::default(),
159 fast_sync: true,
160 tracing: Default::default(),
161 tokio: Default::default(),
162 rayon: Default::default(),
163 p2p: Default::default(),
164 storage: Default::default(),
165 fs: Default::default(),
166 }
167 }
168}
169
170impl Config {
171 pub fn documented_config() -> String {
173 let str = toml::ser::to_string_pretty(&Self::default()).unwrap();
174 let mut doc = toml_edit::DocumentMut::from_str(&str).unwrap();
175 Self::write_docs(doc.as_table_mut());
176 format!("{HEADER}{doc}")
177 }
178
179 fn read_from_path(file: impl AsRef<Path>) -> Result<Self, anyhow::Error> {
185 let file_text = read_to_string(file.as_ref())?;
186
187 Ok(toml::from_str(&file_text)
188 .inspect(|_| println!("Using config at: {}", file.as_ref().to_string_lossy()))
189 .inspect_err(|e| {
190 eprintln_red(&format!(
191 "Failed to parse config file at: {}",
192 file.as_ref().to_string_lossy()
193 ));
194 eprintln_red(&format!("{e}"));
195 std::process::exit(1);
196 })?)
197 }
198
199 pub const fn network(&self) -> Network {
201 self.network
202 }
203
204 pub fn clearnet_p2p_config(&self) -> cuprate_p2p::P2PConfig<ClearNet> {
206 cuprate_p2p::P2PConfig {
207 network: self.network,
208 seeds: p2p::clear_net_seed_nodes(self.network),
209 outbound_connections: self.p2p.clear_net.general.outbound_connections,
210 extra_outbound_connections: self.p2p.clear_net.general.extra_outbound_connections,
211 max_inbound_connections: self.p2p.clear_net.general.max_inbound_connections,
212 gray_peers_percent: self.p2p.clear_net.general.gray_peers_percent,
213 p2p_port: self.p2p.clear_net.general.p2p_port,
214 rpc_port: 0,
216 address_book_config: self
217 .p2p
218 .clear_net
219 .general
220 .address_book_config(&self.fs.cache_directory, self.network),
221 }
222 }
223
224 pub const fn context_config(&self) -> ContextConfig {
226 match self.network {
227 Network::Mainnet => ContextConfig::main_net(),
228 Network::Stagenet => ContextConfig::stage_net(),
229 Network::Testnet => ContextConfig::test_net(),
230 }
231 }
232
233 pub fn blockchain_config(&self) -> cuprate_blockchain::config::Config {
235 let blockchain = &self.storage.blockchain;
236
237 cuprate_blockchain::config::ConfigBuilder::default()
239 .network(self.network)
240 .data_directory(self.fs.data_directory.clone())
241 .sync_mode(blockchain.shared.sync_mode)
242 .build()
243 }
244
245 pub fn txpool_config(&self) -> cuprate_txpool::config::Config {
247 let txpool = &self.storage.txpool;
248
249 cuprate_txpool::config::ConfigBuilder::default()
251 .network(self.network)
252 .data_directory(self.fs.data_directory.clone())
253 .sync_mode(txpool.shared.sync_mode)
254 .build()
255 }
256
257 pub fn block_downloader_config(&self) -> BlockDownloaderConfig {
259 self.p2p.block_downloader.clone().into()
260 }
261}
262
263impl fmt::Display for Config {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 writeln!(
266 f,
267 "========== CONFIGURATION ==========\n{self:#?}\n==================================="
268 )
269 }
270}
271
272#[cfg(test)]
273mod test {
274 use toml::from_str;
275
276 use super::*;
277
278 #[test]
279 fn documented_config() {
280 let str = Config::documented_config();
281 let conf: Config = from_str(&str).unwrap();
282
283 assert_eq!(conf, Config::default());
284 }
285}