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 rpc;
32mod storage;
33mod tokio;
34mod tracing_config;
35
36#[macro_use]
37mod macros;
38
39use fs::FileSystemConfig;
40use p2p::P2PConfig;
41use rayon::RayonConfig;
42pub use rpc::RpcConfig;
43use storage::StorageConfig;
44use tokio::TokioConfig;
45use tracing_config::TracingConfig;
46
47const HEADER: &str = r"## ____ _
49## / ___| _ _ __ _ __ __ _| |_ ___
50## | | | | | | '_ \| '__/ _` | __/ _ \
51## | |__| |_| | |_) | | | (_| | || __/
52## \____\__,_| .__/|_| \__,_|\__\___|
53## |_|
54##
55## All these config values can be set to
56## their default by commenting them out with '#'.
57##
58## Some values are already commented out,
59## to set the value remove the '#' at the start of the line.
60##
61## For more documentation, see: <https://user.cuprate.org>.
62
63";
64
65pub fn read_config_and_args() -> Config {
67 let args = args::Args::parse();
68 args.do_quick_requests();
69
70 let config: Config = if let Some(config_file) = &args.config_file {
71 match Config::read_from_path(config_file) {
73 Ok(config) => config,
74 Err(e) => {
75 eprintln_red(&format!("Failed to read config from file: {e}"));
76 std::process::exit(1);
77 }
78 }
79 } else {
80 std::env::current_dir()
82 .map(|path| path.join(DEFAULT_CONFIG_FILE_NAME))
83 .map_err(Into::into)
84 .and_then(Config::read_from_path)
85 .inspect_err(|e| tracing::debug!("Failed to read config from current dir: {e}"))
86 .or_else(|_| {
88 let file = CUPRATE_CONFIG_DIR.join(DEFAULT_CONFIG_FILE_NAME);
89 Config::read_from_path(file)
90 })
91 .inspect_err(|e| {
92 tracing::debug!("Failed to read config from config dir: {e}");
93 if !args.skip_config_warning {
94 eprintln_red(DEFAULT_CONFIG_WARNING);
95 std::thread::sleep(DEFAULT_CONFIG_STARTUP_DELAY);
96 }
97 })
98 .unwrap_or_default()
99 };
100
101 args.apply_args(config)
102}
103
104config_struct! {
105 #[derive(Debug, Deserialize, Serialize, PartialEq)]
107 #[serde(deny_unknown_fields, default)]
108 pub struct Config {
109 pub network: Network,
113
114 pub fast_sync: bool,
124
125 #[child = true]
126 pub tracing: TracingConfig,
130
131 #[child = true]
132 pub tokio: TokioConfig,
136
137 #[child = true]
138 pub rayon: RayonConfig,
142
143 #[child = true]
144 pub p2p: P2PConfig,
146
147 #[child = true]
148 pub rpc: RpcConfig,
150
151 #[child = true]
152 pub storage: StorageConfig,
154
155 #[child = true]
156 pub fs: FileSystemConfig,
158 }
159}
160
161impl Default for Config {
162 fn default() -> Self {
163 Self {
164 network: Default::default(),
165 fast_sync: true,
166 tracing: Default::default(),
167 tokio: Default::default(),
168 rayon: Default::default(),
169 p2p: Default::default(),
170 rpc: Default::default(),
171 storage: Default::default(),
172 fs: Default::default(),
173 }
174 }
175}
176
177impl Config {
178 pub fn documented_config() -> String {
180 let str = toml::ser::to_string_pretty(&Self::default()).unwrap();
181 let mut doc = toml_edit::DocumentMut::from_str(&str).unwrap();
182 Self::write_docs(doc.as_table_mut());
183 format!("{HEADER}{doc}")
184 }
185
186 fn read_from_path(file: impl AsRef<Path>) -> Result<Self, anyhow::Error> {
192 let file_text = read_to_string(file.as_ref())?;
193
194 Ok(toml::from_str(&file_text)
195 .inspect(|_| println!("Using config at: {}", file.as_ref().to_string_lossy()))
196 .inspect_err(|e| {
197 eprintln_red(&format!(
198 "Failed to parse config file at: {}",
199 file.as_ref().to_string_lossy()
200 ));
201 eprintln_red(&format!("{e}"));
202 std::process::exit(1);
203 })?)
204 }
205
206 pub const fn network(&self) -> Network {
208 self.network
209 }
210
211 pub fn clearnet_p2p_config(&self) -> cuprate_p2p::P2PConfig<ClearNet> {
213 cuprate_p2p::P2PConfig {
214 network: self.network,
215 seeds: p2p::clear_net_seed_nodes(self.network),
216 outbound_connections: self.p2p.clear_net.outbound_connections,
217 extra_outbound_connections: self.p2p.clear_net.extra_outbound_connections,
218 max_inbound_connections: self.p2p.clear_net.max_inbound_connections,
219 gray_peers_percent: self.p2p.clear_net.gray_peers_percent,
220 p2p_port: self.p2p.clear_net.p2p_port,
221 rpc_port: self.rpc.restricted.port_for_p2p(),
222 address_book_config: self.p2p.clear_net.address_book_config.address_book_config(
223 &self.fs.cache_directory,
224 self.network,
225 None,
226 ),
227 }
228 }
229
230 pub const fn context_config(&self) -> ContextConfig {
232 match self.network {
233 Network::Mainnet => ContextConfig::main_net(),
234 Network::Stagenet => ContextConfig::stage_net(),
235 Network::Testnet => ContextConfig::test_net(),
236 }
237 }
238
239 pub fn blockchain_config(&self) -> cuprate_blockchain::config::Config {
241 let blockchain = &self.storage.blockchain;
242
243 cuprate_blockchain::config::ConfigBuilder::default()
245 .network(self.network)
246 .data_directory(self.fs.data_directory.clone())
247 .sync_mode(blockchain.sync_mode)
248 .build()
249 }
250
251 pub fn txpool_config(&self) -> cuprate_txpool::config::Config {
253 let txpool = &self.storage.txpool;
254
255 cuprate_txpool::config::ConfigBuilder::default()
257 .network(self.network)
258 .data_directory(self.fs.data_directory.clone())
259 .sync_mode(txpool.sync_mode)
260 .build()
261 }
262
263 pub fn block_downloader_config(&self) -> BlockDownloaderConfig {
265 self.p2p.block_downloader.clone().into()
266 }
267}
268
269impl fmt::Display for Config {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 writeln!(
272 f,
273 "========== CONFIGURATION ==========\n{self:#?}\n==================================="
274 )
275 }
276}
277
278#[cfg(test)]
279mod test {
280 use pretty_assertions::assert_eq;
281 use toml::from_str;
282
283 use super::*;
284
285 #[test]
286 fn documented_config() {
287 let str = Config::documented_config();
288 let conf: Config = from_str(&str).unwrap();
289
290 assert_eq!(conf, Config::default());
291 }
292}