diff --git a/Cargo.lock b/Cargo.lock index b446bf6d..b7eb7a48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,7 @@ checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstyle", "clap_lex", + "terminal_size", ] [[package]] @@ -790,6 +791,7 @@ dependencies = [ "libc", "monero-serai", "rayon", + "serde", "tokio", "windows", ] @@ -1035,7 +1037,6 @@ dependencies = [ "cuprate-consensus", "cuprate-consensus-context", "cuprate-consensus-rules", - "cuprate-constants", "cuprate-cryptonight", "cuprate-dandelion-tower", "cuprate-database", @@ -1077,6 +1078,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", + "toml", "tower 0.5.1 (git+https://github.com/Cuprate/tower.git?rev=6c7faf0)", "tracing", "tracing-subscriber", @@ -2616,6 +2618,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2833,6 +2844,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.66" @@ -2964,11 +2985,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -2977,6 +3013,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 0f460e8f..7a4b1fc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ members = [ ] [profile.release] +panic = "abort" lto = true # Build with LTO strip = "none" # Keep panic stack traces codegen-units = 1 # Optimize for binary speed over compile times @@ -119,6 +120,7 @@ tokio-util = { version = "0.7", default-features = false } tokio-stream = { version = "0.1", default-features = false } tokio = { version = "1", default-features = false } tower = { git = "https://github.com/Cuprate/tower.git", rev = "6c7faf0", default-features = false } # +toml = { version = "0.8", default-features = false } tracing-subscriber = { version = "0.3", default-features = false } tracing = { version = "0.1", default-features = false } diff --git a/Cuprated.toml b/Cuprated.toml new file mode 100644 index 00000000..a0446251 --- /dev/null +++ b/Cuprated.toml @@ -0,0 +1,67 @@ +# ____ _ +# / ___| _ _ __ _ __ __ _| |_ ___ +# | | | | | | '_ \| '__/ _` | __/ _ \ +# | |__| |_| | |_) | | | (_| | || __/ +# \____\__,_| .__/|_| \__,_|\__\___| +# |_| +# + +## The network to run on, valid values: "Mainnet", "Testnet", "Stagenet". +network = "Mainnet" + +## Tracing config. +[tracing] +## The minimum level for log events to be displayed. +level = "info" + +## Clear-net config. +[p2p.clear_net] +## The number of outbound connections we should make and maintain. +outbound_connections = 64 +## The number of extra connections we should make under load from the rest of Cuprate, i.e. when syncing. +extra_outbound_connections = 8 +## The maximum number of incoming we should allow. +max_inbound_connections = 128 +## The percent of outbound connections that should be to nodes we have not connected to before. +gray_peers_percent = 0.7 +## The port to accept connections on, if left `0` no connections will be accepted. +p2p_port = 0 +## The IP address to listen to connections on. +listen_on = "0.0.0.0" + +## The Clear-net addressbook config. +[p2p.clear_net.address_book_config] +## The size of the white peer list, which contains peers we have made a connection to before. +max_white_list_length = 1_000 +## The size of the gray peer list, which contains peers we have not made a connection to before. +max_gray_list_length = 5_000 +## The amount of time between address book saves. +peer_save_period = { secs = 90, nanos = 0 } + +## The block downloader config. +[p2p.block_downloader] +## The size of the buffer of sequential blocks waiting to be verified and added to the chain (bytes). +buffer_size = 50_000_000 +## The size of the queue of blocks which are waiting for a parent block to be downloaded (bytes). +in_progress_queue_size = 50_000_000 +## The target size of a batch of blocks (bytes), must not exceed 100MB. +target_batch_size = 5_000_000 +## The amount of time between checking the pool of connected peers for free peers to download blocks. +check_client_pool_interval = { secs = 30, nanos = 0 } + +## Storage config +[storage] +## The amount of reader threads to spawn. +reader_threads = "OnePerThread" + +## Txpool storage config. +[storage.txpool] +## The database sync mode for the txpool. +sync_mode = "Async" +## The maximum size of all the txs in the pool (bytes). +max_txpool_byte_size = 100_000_000 + +## Blockchain storage config. +[storage.blockchain] +## The database sync mode for the blockchain. +sync_mode = "Async" diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index 9ebdd780..acf8827b 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -2,7 +2,7 @@ name = "cuprated" version = "0.0.1" edition = "2021" -description = "The Cuprate Monero Rust node." +description = "The Cuprate Rust Monero node." license = "AGPL-3.0-only" authors = ["Boog900", "hinto-janai", "SyntheticBird45"] repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated" @@ -12,29 +12,29 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated" cuprate-consensus = { workspace = true } cuprate-fast-sync = { workspace = true } cuprate-consensus-context = { workspace = true } -cuprate-consensus-rules = { workspace = true } -cuprate-constants = { workspace = true } -cuprate-cryptonight = { workspace = true } -cuprate-helper = { workspace = true } -cuprate-epee-encoding = { workspace = true } -cuprate-fixed-bytes = { workspace = true } -cuprate-levin = { workspace = true } -cuprate-wire = { workspace = true } -cuprate-p2p = { workspace = true } -cuprate-p2p-core = { workspace = true } -cuprate-dandelion-tower = { workspace = true, features = ["txpool"] } -cuprate-async-buffer = { workspace = true } -cuprate-address-book = { workspace = true } -cuprate-blockchain = { workspace = true } -cuprate-database-service = { workspace = true } -cuprate-txpool = { workspace = true } -cuprate-database = { workspace = true } -cuprate-pruning = { workspace = true } -cuprate-test-utils = { workspace = true } -cuprate-types = { workspace = true } -cuprate-json-rpc = { workspace = true } -cuprate-rpc-interface = { workspace = true } -cuprate-rpc-types = { workspace = true } +cuprate-consensus-rules = { workspace = true } +cuprate-cryptonight = { workspace = true } +cuprate-helper = { workspace = true, features = ["serde"] } +cuprate-epee-encoding = { workspace = true } +cuprate-fixed-bytes = { workspace = true } +cuprate-levin = { workspace = true } +cuprate-wire = { workspace = true } +cuprate-p2p = { workspace = true } +cuprate-p2p-core = { workspace = true } +cuprate-dandelion-tower = { workspace = true, features = ["txpool"] } +cuprate-async-buffer = { workspace = true } +cuprate-address-book = { workspace = true } +cuprate-blockchain = { workspace = true } +cuprate-database-service = { workspace = true, features = ["serde"] } +cuprate-txpool = { workspace = true } +cuprate-database = { workspace = true, features = ["serde"] } +cuprate-pruning = { workspace = true } +cuprate-test-utils = { workspace = true } +cuprate-types = { workspace = true } +cuprate-json-rpc = { workspace = true } +cuprate-rpc-interface = { workspace = true } +cuprate-rpc-types = { workspace = true } + # TODO: after v1.0.0, remove unneeded dependencies. anyhow = { workspace = true } @@ -44,7 +44,7 @@ borsh = { workspace = true } bytemuck = { workspace = true } bytes = { workspace = true } cfg-if = { workspace = true } -clap = { workspace = true, features = ["cargo"] } +clap = { workspace = true, features = ["cargo", "help", "wrap_help"] } chrono = { workspace = true } crypto-bigint = { workspace = true } crossbeam = { workspace = true } @@ -71,15 +71,10 @@ thread_local = { workspace = true } tokio-util = { workspace = true } tokio-stream = { workspace = true } tokio = { workspace = true } +toml = { workspace = true, features = ["parse", "display"]} tower = { workspace = true } tracing-subscriber = { workspace = true, features = ["std", "fmt", "default"] } -tracing = { workspace = true } +tracing = { workspace = true, features = ["default"] } [lints] workspace = true - -[profile.dev] -panic = "abort" - -[profile.release] -panic = "abort" diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs index d613c1fc..6b1229f8 100644 --- a/binaries/cuprated/src/config.rs +++ b/binaries/cuprated/src/config.rs @@ -1 +1,159 @@ //! cuprated config +use std::{ + fs::{read_to_string, File}, + io, + path::Path, + time::Duration, +}; + +use clap::Parser; +use serde::{Deserialize, Serialize}; + +use cuprate_consensus::ContextConfig; +use cuprate_helper::{ + fs::{CUPRATE_CONFIG_DIR, DEFAULT_CONFIG_FILE_NAME}, + network::Network, +}; +use cuprate_p2p::block_downloader::BlockDownloaderConfig; +use cuprate_p2p_core::{ClearNet, ClearNetServerCfg}; + +mod args; +mod default; +mod fs; +mod p2p; +mod storage; +mod tracing_config; + +use crate::config::fs::FileSystemConfig; +use p2p::P2PConfig; +use storage::StorageConfig; +use tracing_config::TracingConfig; + +/// Reads the args & config file, returning a [`Config`]. +pub fn read_config_and_args() -> Config { + let args = args::Args::parse(); + + let config: Config = if let Some(config_file) = &args.config_file { + // If a config file was set in the args try to read it and exit if we can't. + match Config::read_from_path(config_file) { + Ok(config) => config, + Err(e) => { + tracing::error!("Failed to read config from file: {e}"); + std::process::exit(1); + } + } + } else { + // First attempt to read the config file from the current directory. + std::env::current_dir() + .map(|path| path.join(DEFAULT_CONFIG_FILE_NAME)) + .map_err(Into::into) + .and_then(Config::read_from_path) + .inspect_err(|e| tracing::debug!("Failed to read config from current dir: {e}")) + // otherwise try the main config directory. + .or_else(|_| { + let file = CUPRATE_CONFIG_DIR.join(DEFAULT_CONFIG_FILE_NAME); + Config::read_from_path(file) + }) + .inspect_err(|e| { + tracing::debug!("Failed to read config from config dir: {e}"); + println!("Failed to find/read config file, using default config."); + }) + .unwrap_or_default() + }; + + args.apply_args(config) +} + +/// The config for all of Cuprate. +#[derive(Default, Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct Config { + /// The network we should run on. + network: Network, + + /// [`tracing`] config. + tracing: TracingConfig, + + /// The P2P network config. + p2p: P2PConfig, + + /// The storage config. + storage: StorageConfig, + + fs: FileSystemConfig, +} + +impl Config { + /// Attempts to read a config file in [`toml`] format from the given [`Path`]. + /// + /// # Errors + /// + /// Will return an [`Err`] if the file cannot be read or if the file is not a valid [`toml`] config. + fn read_from_path(file: impl AsRef) -> Result { + let file_text = read_to_string(file.as_ref())?; + + Ok(toml::from_str(&file_text) + .inspect(|_| println!("Using config at: {}", file.as_ref().to_string_lossy())) + .inspect_err(|e| { + println!("{e}"); + println!( + "Failed to parse config file at: {}", + file.as_ref().to_string_lossy() + ); + })?) + } + + /// Returns the current [`Network`] we are running on. + pub const fn network(&self) -> Network { + self.network + } + + /// The [`ClearNet`], [`cuprate_p2p::P2PConfig`]. + pub fn clearnet_p2p_config(&self) -> cuprate_p2p::P2PConfig { + cuprate_p2p::P2PConfig { + network: self.network, + seeds: p2p::clear_net_seed_nodes(self.network), + outbound_connections: self.p2p.clear_net.general.outbound_connections, + extra_outbound_connections: self.p2p.clear_net.general.extra_outbound_connections, + max_inbound_connections: self.p2p.clear_net.general.max_inbound_connections, + gray_peers_percent: self.p2p.clear_net.general.gray_peers_percent, + server_config: Some(ClearNetServerCfg { + ip: self.p2p.clear_net.listen_on, + }), + p2p_port: self.p2p.clear_net.general.p2p_port, + // TODO: set this if a public RPC server is set. + rpc_port: 0, + address_book_config: self + .p2p + .clear_net + .general + .address_book_config(&self.fs.cache_directory, self.network), + } + } + + /// The [`ContextConfig`]. + pub const fn context_config(&self) -> ContextConfig { + match self.network { + Network::Mainnet => ContextConfig::main_net(), + Network::Stagenet => ContextConfig::stage_net(), + Network::Testnet => ContextConfig::test_net(), + } + } + + /// The [`cuprate_blockchain`] config. + pub fn blockchain_config(&self) -> cuprate_blockchain::config::Config { + let blockchain = &self.storage.blockchain; + + // We don't set reader threads as we manually make the reader threadpool. + cuprate_blockchain::config::ConfigBuilder::default() + .network(self.network) + .data_directory(self.fs.data_directory.clone()) + .sync_mode(blockchain.shared.sync_mode) + .build() + } + + /// The [`BlockDownloaderConfig`]. + pub fn block_downloader_config(&self) -> BlockDownloaderConfig { + self.p2p.block_downloader.clone().into() + } +} diff --git a/binaries/cuprated/src/config/args.rs b/binaries/cuprated/src/config/args.rs new file mode 100644 index 00000000..c2cd6233 --- /dev/null +++ b/binaries/cuprated/src/config/args.rs @@ -0,0 +1,50 @@ +use std::{io::Write, path::PathBuf}; + +use clap::builder::TypedValueParser; + +use cuprate_helper::network::Network; + +use crate::config::{default::create_default_config_file, Config}; + +/// Cuprate Args. +#[derive(clap::Parser, Debug)] +#[command(version, about)] +pub struct Args { + /// The network to run on. + #[arg( + long, + default_value_t = Network::Mainnet, + value_parser = clap::builder::PossibleValuesParser::new(["mainnet", "testnet", "stagenet"]) + .map(|s| s.parse::().unwrap()), + )] + pub network: Network, + /// The amount of outbound clear-net connections to maintain. + #[arg(long)] + pub outbound_connections: Option, + /// The PATH of the `cuprated` config file. + #[arg(long)] + pub config_file: Option, + /// Generate a config file and place it in the given PATH. + #[arg(long)] + pub generate_config: Option, +} + +impl Args { + /// Apply the [`Args`] to the given [`Config`]. + /// + /// This may exit the program if a config value was set that requires an early exit. + pub fn apply_args(&self, mut config: Config) -> Config { + if let Some(config_folder) = self.generate_config.as_ref() { + // This will create the config file and exit. + create_default_config_file(config_folder) + }; + + config.network = self.network; + + if let Some(outbound_connections) = self.outbound_connections { + config.p2p.clear_net.general.outbound_connections = outbound_connections; + } + + config + } +} diff --git a/binaries/cuprated/src/config/default.rs b/binaries/cuprated/src/config/default.rs new file mode 100644 index 00000000..8c9642b8 --- /dev/null +++ b/binaries/cuprated/src/config/default.rs @@ -0,0 +1,44 @@ +use std::{ + io::Write, + path::{Path, PathBuf}, + str::from_utf8, +}; + +use cuprate_helper::fs::{CUPRATE_CACHE_DIR, DEFAULT_CONFIG_FILE_NAME}; + +use crate::constants::EXAMPLE_CONFIG; + +/// Creates a config file which will be named [`DEFAULT_CONFIG_FILE_NAME`] in the directory given in [`Path`]. +/// +/// This will always terminate the program, on success and failure. +pub fn create_default_config_file(path: &Path) -> ! { + let config_file = path.join(DEFAULT_CONFIG_FILE_NAME); + + tracing::info!("Attempting to create new config file here: {config_file:?}"); + + let mut file = match std::fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(&config_file) + { + Ok(file) => file, + Err(e) => { + tracing::error!("Failed to create config file, got error: {e}"); + std::process::exit(1); + } + }; + + let config = EXAMPLE_CONFIG; + file.write_all(config.as_bytes()).unwrap(); + + std::process::exit(0); +} + +#[cfg(test)] +mod tests { + use crate::{config::Config, constants::EXAMPLE_CONFIG}; + #[test] + fn generate_config_text_is_valid() { + let config: Config = toml::from_str(EXAMPLE_CONFIG).unwrap(); + } +} diff --git a/binaries/cuprated/src/config/fs.rs b/binaries/cuprated/src/config/fs.rs new file mode 100644 index 00000000..f8f61307 --- /dev/null +++ b/binaries/cuprated/src/config/fs.rs @@ -0,0 +1,21 @@ +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +use cuprate_helper::fs::{CUPRATE_CACHE_DIR, CUPRATE_DATA_DIR}; + +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct FileSystemConfig { + pub data_directory: PathBuf, + pub cache_directory: PathBuf, +} + +impl Default for FileSystemConfig { + fn default() -> Self { + Self { + data_directory: CUPRATE_DATA_DIR.to_path_buf(), + cache_directory: CUPRATE_CACHE_DIR.to_path_buf(), + } + } +} diff --git a/binaries/cuprated/src/config/p2p.rs b/binaries/cuprated/src/config/p2p.rs new file mode 100644 index 00000000..aaf7dda7 --- /dev/null +++ b/binaries/cuprated/src/config/p2p.rs @@ -0,0 +1,178 @@ +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, + path::Path, + time::Duration, +}; + +use serde::{Deserialize, Serialize}; + +use cuprate_helper::{fs::addressbook_path, network::Network}; + +/// P2P config. +#[derive(Default, Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct P2PConfig { + /// Clear-net config. + pub clear_net: ClearNetConfig, + /// Block downloader config. + pub block_downloader: BlockDownloaderConfig, +} + +#[derive(Clone, Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct BlockDownloaderConfig { + /// The size in bytes of the buffer between the block downloader and the place which + /// is consuming the downloaded blocks. + pub buffer_size: usize, + /// The size of the in progress queue (in bytes) at which we stop requesting more blocks. + pub in_progress_queue_size: usize, + /// The [`Duration`] between checking the client pool for free peers. + pub check_client_pool_interval: Duration, + /// The target size of a single batch of blocks (in bytes). + pub target_batch_size: usize, +} + +impl From for cuprate_p2p::block_downloader::BlockDownloaderConfig { + fn from(value: BlockDownloaderConfig) -> Self { + Self { + buffer_size: value.buffer_size, + in_progress_queue_size: value.in_progress_queue_size, + check_client_pool_interval: value.check_client_pool_interval, + target_batch_size: value.target_batch_size, + initial_batch_len: 1, + } + } +} + +impl Default for BlockDownloaderConfig { + fn default() -> Self { + Self { + buffer_size: 50_000_000, + in_progress_queue_size: 50_000_000, + check_client_pool_interval: Duration::from_secs(30), + target_batch_size: 5_000_000, + } + } +} + +/// The config values for P2P clear-net. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct ClearNetConfig { + /// The server config. + pub listen_on: IpAddr, + #[serde(flatten)] + pub general: SharedNetConfig, +} + +impl Default for ClearNetConfig { + fn default() -> Self { + Self { + listen_on: IpAddr::V4(Ipv4Addr::UNSPECIFIED), + general: Default::default(), + } + } +} + +/// Network config values shared between all network zones. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct SharedNetConfig { + /// The number of outbound connections to make and try keep. + pub outbound_connections: usize, + /// The amount of extra connections we can make if we are under load from the rest of Cuprate. + pub extra_outbound_connections: usize, + /// The maximum amount of inbound connections + pub max_inbound_connections: usize, + /// The percent of connections that should be to peers we haven't connected to before. + pub gray_peers_percent: f64, + /// port to use to accept p2p connections. + pub p2p_port: u16, + /// The address book config. + address_book_config: AddressBookConfig, +} + +impl SharedNetConfig { + /// Returns the [`AddressBookConfig`]. + pub fn address_book_config( + &self, + cache_dir: &Path, + network: Network, + ) -> cuprate_address_book::AddressBookConfig { + cuprate_address_book::AddressBookConfig { + max_white_list_length: self.address_book_config.max_white_list_length, + max_gray_list_length: self.address_book_config.max_gray_list_length, + peer_store_directory: addressbook_path(cache_dir, network), + peer_save_period: self.address_book_config.peer_save_period, + } + } +} + +impl Default for SharedNetConfig { + fn default() -> Self { + Self { + outbound_connections: 64, + extra_outbound_connections: 8, + max_inbound_connections: 128, + gray_peers_percent: 0.7, + p2p_port: 0, + address_book_config: AddressBookConfig::default(), + } + } +} + +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct AddressBookConfig { + max_white_list_length: usize, + max_gray_list_length: usize, + peer_save_period: Duration, +} + +impl Default for AddressBookConfig { + fn default() -> Self { + Self { + max_white_list_length: 1_000, + max_gray_list_length: 5_000, + peer_save_period: Duration::from_secs(30), + } + } +} + +/// Seed nodes for [`ClearNet`](cuprate_p2p_core::ClearNet). +pub fn clear_net_seed_nodes(network: Network) -> Vec { + let seeds = match network { + Network::Mainnet => [ + "176.9.0.187:18080", + "88.198.163.90:18080", + "66.85.74.134:18080", + "51.79.173.165:18080", + "192.99.8.110:18080", + "37.187.74.171:18080", + "77.172.183.193:18080", + ] + .as_slice(), + Network::Stagenet => [ + "176.9.0.187:38080", + "51.79.173.165:38080", + "192.99.8.110:38080", + "37.187.74.171:38080", + "77.172.183.193:38080", + ] + .as_slice(), + Network::Testnet => [ + "176.9.0.187:28080", + "51.79.173.165:28080", + "192.99.8.110:28080", + "37.187.74.171:28080", + "77.172.183.193:28080", + ] + .as_slice(), + }; + + seeds + .iter() + .map(|s| s.parse()) + .collect::>() + .unwrap() +} diff --git a/binaries/cuprated/src/config/storage.rs b/binaries/cuprated/src/config/storage.rs new file mode 100644 index 00000000..b3e3c1f4 --- /dev/null +++ b/binaries/cuprated/src/config/storage.rs @@ -0,0 +1,67 @@ +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +use cuprate_database::config::SyncMode; +use cuprate_database_service::ReaderThreads; +use cuprate_helper::fs::CUPRATE_DATA_DIR; + +/// The storage config. +#[derive(Default, Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct StorageConfig { + /// The amount of reader threads to spawn between the tx-pool and blockchain. + pub reader_threads: ReaderThreads, + /// The tx-pool config. + pub txpool: TxpoolConfig, + /// The blockchain config. + pub blockchain: BlockchainConfig, +} + +/// The blockchain config. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct BlockchainConfig { + #[serde(flatten)] + pub shared: SharedStorageConfig, +} + +impl Default for BlockchainConfig { + fn default() -> Self { + Self { + shared: SharedStorageConfig { + sync_mode: SyncMode::Async, + }, + } + } +} + +/// The tx-pool config. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct TxpoolConfig { + #[serde(flatten)] + pub shared: SharedStorageConfig, + + /// The maximum size of the tx-pool. + pub max_txpool_byte_size: usize, +} + +impl Default for TxpoolConfig { + fn default() -> Self { + Self { + shared: SharedStorageConfig { + sync_mode: SyncMode::Async, + }, + max_txpool_byte_size: 100_000_000, + } + } +} + +/// Config values shared between the tx-pool and blockchain. +#[derive(Default, Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct SharedStorageConfig { + /// The [`SyncMode`] of the database. + pub sync_mode: SyncMode, +} diff --git a/binaries/cuprated/src/config/tracing_config.rs b/binaries/cuprated/src/config/tracing_config.rs new file mode 100644 index 00000000..859d516a --- /dev/null +++ b/binaries/cuprated/src/config/tracing_config.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; +use tracing::level_filters::LevelFilter; + +/// [`tracing`] config. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct TracingConfig { + /// The default minimum log level. + #[serde(with = "level_filter_serde")] + level: LevelFilter, +} + +impl Default for TracingConfig { + fn default() -> Self { + Self { + level: LevelFilter::INFO, + } + } +} + +mod level_filter_serde { + use std::str::FromStr; + + use serde::{Deserialize, Deserializer, Serializer}; + use tracing::level_filters::LevelFilter; + + #[expect(clippy::trivially_copy_pass_by_ref, reason = "serde")] + pub fn serialize(level_filter: &LevelFilter, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(&level_filter.to_string()) + } + + pub fn deserialize<'de, D>(d: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(d)?; + LevelFilter::from_str(&s).map_err(serde::de::Error::custom) + } +} diff --git a/binaries/cuprated/src/constants.rs b/binaries/cuprated/src/constants.rs index 2f3c7bb6..a42248b6 100644 --- a/binaries/cuprated/src/constants.rs +++ b/binaries/cuprated/src/constants.rs @@ -18,6 +18,8 @@ pub const VERSION_BUILD: &str = if cfg!(debug_assertions) { pub const PANIC_CRITICAL_SERVICE_ERROR: &str = "A service critical to Cuprate's function returned an unexpected error."; +pub const EXAMPLE_CONFIG: &str = include_str!("../../../Cuprated.toml"); + #[cfg(test)] mod test { use super::*; diff --git a/binaries/cuprated/src/main.rs b/binaries/cuprated/src/main.rs index d5c832e2..617c5b61 100644 --- a/binaries/cuprated/src/main.rs +++ b/binaries/cuprated/src/main.rs @@ -29,6 +29,8 @@ fn main() { // Initialize global static `LazyLock` data. statics::init_lazylock_statics(); + let _config = config::read_config_and_args(); + // TODO: everything else. todo!() } diff --git a/helper/Cargo.toml b/helper/Cargo.toml index 1b3158f1..716beb34 100644 --- a/helper/Cargo.toml +++ b/helper/Cargo.toml @@ -35,6 +35,8 @@ futures = { workspace = true, optional = true, features = ["std"] } monero-serai = { workspace = true, optional = true } rayon = { workspace = true, optional = true } +serde = { workspace = true, optional = true, features = ["derive"] } + # This is kinda a stupid work around. # [thread] needs to activate one of these libs (windows|libc) # although it depends on what target we're building for. diff --git a/helper/src/fs.rs b/helper/src/fs.rs index 5d62a644..60d61a37 100644 --- a/helper/src/fs.rs +++ b/helper/src/fs.rs @@ -28,7 +28,12 @@ //! - //---------------------------------------------------------------------------------------------------- Use -use std::{path::PathBuf, sync::LazyLock}; +use std::{ + path::{Path, PathBuf}, + sync::LazyLock, +}; + +use crate::network::Network; //---------------------------------------------------------------------------------------------------- Const /// Cuprate's main directory. @@ -58,6 +63,9 @@ pub const CUPRATE_DIR: &str = { } }; +/// The default name of Cuprate's config file. +pub const DEFAULT_CONFIG_FILE_NAME: &str = "Cuprated.toml"; + //---------------------------------------------------------------------------------------------------- Directories /// Create a `LazyLock` for common PATHs used by Cuprate. /// @@ -150,32 +158,61 @@ impl_path_lazylock! { CUPRATE_DATA_DIR, data_dir, "", +} - /// Cuprate's blockchain directory. - /// - /// This is the PATH used for any Cuprate blockchain files. - /// - /// | OS | PATH | - /// |---------|----------------------------------------------------------------| - /// | Windows | `C:\Users\Alice\AppData\Roaming\Cuprate\blockchain\` | - /// | macOS | `/Users/Alice/Library/Application Support/Cuprate/blockchain/` | - /// | Linux | `/home/alice/.local/share/cuprate/blockchain/` | - CUPRATE_BLOCKCHAIN_DIR, - data_dir, - "blockchain", +/// Joins the [`Network`] to the [`Path`]. +/// +/// This will keep the path the same for [`Network::Mainnet`]. +fn path_with_network(path: &Path, network: Network) -> PathBuf { + match network { + Network::Mainnet => path.to_path_buf(), + network => path.join(network.to_string()), + } +} - /// Cuprate's transaction pool directory. - /// - /// This is the PATH used for any Cuprate txpool files. - /// - /// | OS | PATH | - /// |---------|------------------------------------------------------------| - /// | Windows | `C:\Users\Alice\AppData\Roaming\Cuprate\txpool\` | - /// | macOS | `/Users/Alice/Library/Application Support/Cuprate/txpool/` | - /// | Linux | `/home/alice/.local/share/cuprate/txpool/` | - CUPRATE_TXPOOL_DIR, - data_dir, - "txpool", +/// Cuprate's blockchain directory. +/// +/// This is the PATH used for any Cuprate blockchain files. +/// +/// ```rust +/// use cuprate_helper::{network::Network, fs::{CUPRATE_DATA_DIR, blockchain_path}}; +/// +/// assert_eq!(blockchain_path(&**CUPRATE_DATA_DIR, Network::Mainnet).as_path(), CUPRATE_DATA_DIR.join("blockchain")); +/// assert_eq!(blockchain_path(&**CUPRATE_DATA_DIR, Network::Stagenet).as_path(), CUPRATE_DATA_DIR.join(Network::Stagenet.to_string()).join("blockchain")); +/// assert_eq!(blockchain_path(&**CUPRATE_DATA_DIR, Network::Testnet).as_path(), CUPRATE_DATA_DIR.join(Network::Testnet.to_string()).join("blockchain")); +/// ``` +pub fn blockchain_path(data_dir: &Path, network: Network) -> PathBuf { + path_with_network(data_dir, network).join("blockchain") +} + +/// Cuprate's txpool directory. +/// +/// This is the PATH used for any Cuprate txpool files. +/// +/// ```rust +/// use cuprate_helper::{network::Network, fs::{CUPRATE_DATA_DIR, txpool_path}}; +/// +/// assert_eq!(txpool_path(&**CUPRATE_DATA_DIR, Network::Mainnet).as_path(), CUPRATE_DATA_DIR.join("txpool")); +/// assert_eq!(txpool_path(&**CUPRATE_DATA_DIR, Network::Stagenet).as_path(), CUPRATE_DATA_DIR.join(Network::Stagenet.to_string()).join("txpool")); +/// assert_eq!(txpool_path(&**CUPRATE_DATA_DIR, Network::Testnet).as_path(), CUPRATE_DATA_DIR.join(Network::Testnet.to_string()).join("txpool")); +/// ``` +pub fn txpool_path(data_dir: &Path, network: Network) -> PathBuf { + path_with_network(data_dir, network).join("txpool") +} + +/// Cuprate's address-book directory. +/// +/// This is the PATH used for any Cuprate address-book files. +/// +/// ```rust +/// use cuprate_helper::{network::Network, fs::{CUPRATE_CACHE_DIR, addressbook_path}}; +/// +/// assert_eq!(addressbook_path(&**CUPRATE_CACHE_DIR, Network::Mainnet).as_path(), CUPRATE_CACHE_DIR.join("addressbook")); +/// assert_eq!(addressbook_path(&**CUPRATE_CACHE_DIR, Network::Stagenet).as_path(), CUPRATE_CACHE_DIR.join(Network::Stagenet.to_string()).join("addressbook")); +/// assert_eq!(addressbook_path(&**CUPRATE_CACHE_DIR, Network::Testnet).as_path(), CUPRATE_CACHE_DIR.join(Network::Testnet.to_string()).join("addressbook")); +/// ``` +pub fn addressbook_path(cache_dir: &Path, network: Network) -> PathBuf { + path_with_network(cache_dir, network).join("addressbook") } //---------------------------------------------------------------------------------------------------- Tests @@ -197,29 +234,21 @@ mod test { (&*CUPRATE_CACHE_DIR, ""), (&*CUPRATE_CONFIG_DIR, ""), (&*CUPRATE_DATA_DIR, ""), - (&*CUPRATE_BLOCKCHAIN_DIR, ""), - (&*CUPRATE_TXPOOL_DIR, ""), ]; if cfg!(target_os = "windows") { array[0].1 = r"AppData\Local\Cuprate"; array[1].1 = r"AppData\Roaming\Cuprate"; array[2].1 = r"AppData\Roaming\Cuprate"; - array[3].1 = r"AppData\Roaming\Cuprate\blockchain"; - array[4].1 = r"AppData\Roaming\Cuprate\txpool"; } else if cfg!(target_os = "macos") { array[0].1 = "Library/Caches/Cuprate"; array[1].1 = "Library/Application Support/Cuprate"; array[2].1 = "Library/Application Support/Cuprate"; - array[3].1 = "Library/Application Support/Cuprate/blockchain"; - array[4].1 = "Library/Application Support/Cuprate/txpool"; } else { // Assumes Linux. array[0].1 = ".cache/cuprate"; array[1].1 = ".config/cuprate"; array[2].1 = ".local/share/cuprate"; - array[3].1 = ".local/share/cuprate/blockchain"; - array[4].1 = ".local/share/cuprate/txpool"; }; for (path, expected) in array { diff --git a/helper/src/network.rs b/helper/src/network.rs index f3224b33..d2961170 100644 --- a/helper/src/network.rs +++ b/helper/src/network.rs @@ -5,6 +5,12 @@ //! into it's own crate. //! //! `#[no_std]` compatible. +// TODO: move to types crate. + +use core::{ + fmt::{Display, Formatter}, + str::FromStr, +}; const MAINNET_NETWORK_ID: [u8; 16] = [ 0x12, 0x30, 0xF1, 0x71, 0x61, 0x04, 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10, @@ -17,7 +23,8 @@ const STAGENET_NETWORK_ID: [u8; 16] = [ ]; /// An enum representing every Monero network. -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, Default, Ord, PartialOrd, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Network { /// Mainnet #[default] @@ -38,3 +45,28 @@ impl Network { } } } + +#[derive(Debug, PartialEq, Eq)] +pub struct ParseNetworkError; + +impl FromStr for Network { + type Err = ParseNetworkError; + + fn from_str(s: &str) -> Result { + match s { + "mainnet" | "Mainnet" => Ok(Self::Mainnet), + "testnet" | "Testnet" => Ok(Self::Testnet), + "stagenet" | "Stagenet" => Ok(Self::Stagenet), + _ => Err(ParseNetworkError), + } + } +} +impl Display for Network { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.write_str(match self { + Self::Mainnet => "mainnet", + Self::Testnet => "testnet", + Self::Stagenet => "stagenet", + }) + } +} diff --git a/p2p/address-book/Cargo.toml b/p2p/address-book/Cargo.toml index a88819f7..d57cfded 100644 --- a/p2p/address-book/Cargo.toml +++ b/p2p/address-book/Cargo.toml @@ -23,7 +23,7 @@ indexmap = { workspace = true, features = ["std"] } rand = { workspace = true, features = ["std", "std_rng"] } -borsh = { workspace = true, features = ["derive", "std"]} +borsh = { workspace = true, features = ["derive", "std"] } [dev-dependencies] cuprate-test-utils = { workspace = true } diff --git a/p2p/address-book/src/book/tests.rs b/p2p/address-book/src/book/tests.rs index 216fcfac..b2c4c493 100644 --- a/p2p/address-book/src/book/tests.rs +++ b/p2p/address-book/src/book/tests.rs @@ -15,7 +15,7 @@ fn test_cfg() -> AddressBookConfig { AddressBookConfig { max_white_list_length: 100, max_gray_list_length: 500, - peer_store_file: PathBuf::new(), + peer_store_directory: PathBuf::new(), peer_save_period: Duration::from_secs(60), } } diff --git a/p2p/address-book/src/lib.rs b/p2p/address-book/src/lib.rs index c0903485..054be462 100644 --- a/p2p/address-book/src/lib.rs +++ b/p2p/address-book/src/lib.rs @@ -29,8 +29,8 @@ pub struct AddressBookConfig { /// /// Gray peers are peers we are yet to make a connection to. pub max_gray_list_length: usize, - /// The location to store the address book. - pub peer_store_file: PathBuf, + /// The location to store the peer store files. + pub peer_store_directory: PathBuf, /// The amount of time between saving the address book to disk. pub peer_save_period: Duration, } @@ -63,11 +63,6 @@ pub enum AddressBookError { pub async fn init_address_book( cfg: AddressBookConfig, ) -> Result, std::io::Error> { - tracing::info!( - "Loading peers from file: {} ", - cfg.peer_store_file.display() - ); - let (white_list, gray_list) = match store::read_peers_from_disk::(&cfg).await { Ok(res) => res, Err(e) if e.kind() == ErrorKind::NotFound => (vec![], vec![]), diff --git a/p2p/address-book/src/store.rs b/p2p/address-book/src/store.rs index 9abf0c3e..47994ae5 100644 --- a/p2p/address-book/src/store.rs +++ b/p2p/address-book/src/store.rs @@ -39,7 +39,9 @@ pub(crate) fn save_peers_to_disk( }) .unwrap(); - let file = cfg.peer_store_file.clone(); + let file = cfg + .peer_store_directory + .join(format!("{}_p2p_state", Z::NAME)); spawn_blocking(move || fs::write(&file, &data)) } @@ -52,7 +54,12 @@ pub(crate) async fn read_peers_from_disk( ), std::io::Error, > { - let file = cfg.peer_store_file.clone(); + let file = cfg + .peer_store_directory + .join(format!("{}_p2p_state", Z::NAME)); + + tracing::info!("Loading peers from file: {} ", file.display()); + let data = spawn_blocking(move || fs::read(file)).await.unwrap()?; let de_ser: DeserPeerDataV1 = from_slice(&data)?; diff --git a/p2p/p2p/src/block_downloader.rs b/p2p/p2p/src/block_downloader.rs index fcc9eb65..5ebcedc0 100644 --- a/p2p/p2p/src/block_downloader.rs +++ b/p2p/p2p/src/block_downloader.rs @@ -70,7 +70,7 @@ pub struct BlockDownloaderConfig { /// The target size of a single batch of blocks (in bytes). pub target_batch_size: usize, /// The initial amount of blocks to request (in number of blocks) - pub initial_batch_size: usize, + pub initial_batch_len: usize, } /// An error that occurred in the [`BlockDownloader`]. @@ -243,7 +243,7 @@ where Self { client_pool, our_chain_svc, - amount_of_blocks_to_request: config.initial_batch_size, + amount_of_blocks_to_request: config.initial_batch_len, amount_of_blocks_to_request_updated_at: 0, amount_of_empty_chain_entries: 0, block_download_tasks: JoinSet::new(), diff --git a/p2p/p2p/src/block_downloader/tests.rs b/p2p/p2p/src/block_downloader/tests.rs index 83dd417c..dd07cce9 100644 --- a/p2p/p2p/src/block_downloader/tests.rs +++ b/p2p/p2p/src/block_downloader/tests.rs @@ -69,7 +69,7 @@ proptest! { in_progress_queue_size: 10_000, check_client_pool_interval: Duration::from_secs(5), target_batch_size: 5_000, - initial_batch_size: 1, + initial_batch_len: 1, }); let blocks = stream.map(|blocks| blocks.blocks).concat().await; diff --git a/storage/blockchain/Cargo.toml b/storage/blockchain/Cargo.toml index 6fd973cd..92b4374b 100644 --- a/storage/blockchain/Cargo.toml +++ b/storage/blockchain/Cargo.toml @@ -15,7 +15,7 @@ default = ["heed"] heed = ["cuprate-database/heed"] redb = ["cuprate-database/redb"] redb-memory = ["cuprate-database/redb-memory"] -serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/serde"] +serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/serde", "cuprate-helper/serde"] [dependencies] cuprate-database = { workspace = true } diff --git a/storage/blockchain/README.md b/storage/blockchain/README.md index 3f97a3d6..8ab8b432 100644 --- a/storage/blockchain/README.md +++ b/storage/blockchain/README.md @@ -76,7 +76,7 @@ use cuprate_blockchain::{ let tmp_dir = tempfile::tempdir()?; let db_dir = tmp_dir.path().to_owned(); let config = ConfigBuilder::new() - .db_directory(db_dir.into()) + .data_directory(db_dir.into()) .build(); // Initialize the database environment. diff --git a/storage/blockchain/src/config.rs b/storage/blockchain/src/config.rs index e4b76068..4bef2cb0 100644 --- a/storage/blockchain/src/config.rs +++ b/storage/blockchain/src/config.rs @@ -25,7 +25,7 @@ //! //! let config = ConfigBuilder::new() //! // Use a custom database directory. -//! .db_directory(db_dir.into()) +//! .data_directory(db_dir.into()) //! // Use as many reader threads as possible (when using `service`). //! .reader_threads(ReaderThreads::OnePerThread) //! // Use the fastest sync mode. @@ -41,13 +41,16 @@ //! ``` //---------------------------------------------------------------------------------------------------- Import -use std::{borrow::Cow, path::Path}; +use std::{borrow::Cow, path::PathBuf}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use cuprate_database::{config::SyncMode, resize::ResizeAlgorithm}; -use cuprate_helper::fs::CUPRATE_BLOCKCHAIN_DIR; +use cuprate_helper::{ + fs::{blockchain_path, CUPRATE_DATA_DIR}, + network::Network, +}; // re-exports pub use cuprate_database_service::ReaderThreads; @@ -59,8 +62,9 @@ pub use cuprate_database_service::ReaderThreads; #[derive(Debug, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ConfigBuilder { - /// [`Config::db_directory`]. - db_directory: Option>, + network: Network, + + data_dir: Option, /// [`Config::cuprate_database_config`]. db_config: cuprate_database::config::ConfigBuilder, @@ -76,10 +80,12 @@ impl ConfigBuilder { /// after this function to use default values. pub fn new() -> Self { Self { - db_directory: None, - db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed( - &*CUPRATE_BLOCKCHAIN_DIR, - )), + network: Network::default(), + data_dir: None, + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(blockchain_path( + &CUPRATE_DATA_DIR, + Network::Mainnet, + ))), reader_threads: None, } } @@ -87,21 +93,21 @@ impl ConfigBuilder { /// Build into a [`Config`]. /// /// # Default values - /// If [`ConfigBuilder::db_directory`] was not called, - /// the default [`CUPRATE_BLOCKCHAIN_DIR`] will be used. + /// If [`ConfigBuilder::data_directory`] was not called, + /// [`blockchain_path`] with [`CUPRATE_DATA_DIR`] [`Network::Mainnet`] will be used. /// /// For all other values, [`Default::default`] is used. pub fn build(self) -> Config { // INVARIANT: all PATH safety checks are done // in `helper::fs`. No need to do them here. - let db_directory = self - .db_directory - .unwrap_or_else(|| Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR)); + let data_dir = self + .data_dir + .unwrap_or_else(|| CUPRATE_DATA_DIR.to_path_buf()); let reader_threads = self.reader_threads.unwrap_or_default(); let db_config = self .db_config - .db_directory(db_directory) + .db_directory(Cow::Owned(blockchain_path(&data_dir, self.network))) .reader_threads(reader_threads.as_threads()) .build(); @@ -111,10 +117,17 @@ impl ConfigBuilder { } } - /// Set a custom database directory (and file) [`Path`]. + /// Change the network this blockchain database is for. #[must_use] - pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self { - self.db_directory = Some(db_directory); + pub const fn network(mut self, network: Network) -> Self { + self.network = network; + self + } + + /// Set a custom database directory (and file) [`PathBuf`]. + #[must_use] + pub fn data_directory(mut self, db_directory: PathBuf) -> Self { + self.data_dir = Some(db_directory); self } @@ -145,9 +158,7 @@ impl ConfigBuilder { /// Good default for testing, and resource-available machines. #[must_use] pub fn fast(mut self) -> Self { - self.db_config = - cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR)) - .fast(); + self.db_config = self.db_config.fast(); self.reader_threads = Some(ReaderThreads::OnePerThread); self @@ -159,9 +170,7 @@ impl ConfigBuilder { /// Good default for resource-limited machines, e.g. a cheap VPS. #[must_use] pub fn low_power(mut self) -> Self { - self.db_config = - cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR)) - .low_power(); + self.db_config = self.db_config.low_power(); self.reader_threads = Some(ReaderThreads::One); self @@ -170,10 +179,13 @@ impl ConfigBuilder { impl Default for ConfigBuilder { fn default() -> Self { - let db_directory = Cow::Borrowed(&**CUPRATE_BLOCKCHAIN_DIR); Self { - db_directory: Some(db_directory.clone()), - db_config: cuprate_database::config::ConfigBuilder::new(db_directory), + network: Network::default(), + data_dir: Some(CUPRATE_DATA_DIR.to_path_buf()), + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(blockchain_path( + &CUPRATE_DATA_DIR, + Network::default(), + ))), reader_threads: Some(ReaderThreads::default()), } } @@ -201,7 +213,7 @@ impl Config { /// Create a new [`Config`] with sane default settings. /// /// The [`cuprate_database::config::Config::db_directory`] - /// will be set to [`CUPRATE_BLOCKCHAIN_DIR`]. + /// will be set to [`blockchain_path`] with [`CUPRATE_DATA_DIR`] [`Network::Mainnet`]. /// /// All other values will be [`Default::default`]. /// @@ -213,14 +225,14 @@ impl Config { /// resize::ResizeAlgorithm, /// DATABASE_DATA_FILENAME, /// }; - /// use cuprate_helper::fs::*; + /// use cuprate_helper::{fs::*, network::Network}; /// /// use cuprate_blockchain::config::*; /// /// let config = Config::new(); /// - /// assert_eq!(config.db_config.db_directory(), &*CUPRATE_BLOCKCHAIN_DIR); - /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_BLOCKCHAIN_DIR)); + /// assert_eq!(config.db_config.db_directory().as_ref(), blockchain_path(&CUPRATE_DATA_DIR, Network::Mainnet).as_path()); + /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_DATA_DIR)); /// assert!(config.db_config.db_file().ends_with(DATABASE_DATA_FILENAME)); /// assert_eq!(config.db_config.sync_mode, SyncMode::default()); /// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default()); diff --git a/storage/blockchain/src/ops/mod.rs b/storage/blockchain/src/ops/mod.rs index 285aa244..96b25aff 100644 --- a/storage/blockchain/src/ops/mod.rs +++ b/storage/blockchain/src/ops/mod.rs @@ -71,7 +71,7 @@ //! let tmp_dir = tempfile::tempdir()?; //! let db_dir = tmp_dir.path().to_owned(); //! let config = ConfigBuilder::new() -//! .db_directory(db_dir.into()) +//! .data_directory(db_dir.into()) //! .build(); //! //! // Initialize the database environment. diff --git a/storage/blockchain/src/service/mod.rs b/storage/blockchain/src/service/mod.rs index c5eb80c2..d6a811bd 100644 --- a/storage/blockchain/src/service/mod.rs +++ b/storage/blockchain/src/service/mod.rs @@ -77,7 +77,7 @@ //! let tmp_dir = tempfile::tempdir()?; //! let db_dir = tmp_dir.path().to_owned(); //! let config = ConfigBuilder::new() -//! .db_directory(db_dir.into()) +//! .data_directory(db_dir.into()) //! .build(); //! //! // Initialize the database thread-pool. diff --git a/storage/blockchain/src/service/tests.rs b/storage/blockchain/src/service/tests.rs index 719f3613..38db6652 100644 --- a/storage/blockchain/src/service/tests.rs +++ b/storage/blockchain/src/service/tests.rs @@ -7,7 +7,6 @@ //---------------------------------------------------------------------------------------------------- Use use std::{ - borrow::Cow, collections::{HashMap, HashSet}, sync::Arc, }; @@ -46,7 +45,7 @@ fn init_service() -> ( ) { let tempdir = tempfile::tempdir().unwrap(); let config = ConfigBuilder::new() - .db_directory(Cow::Owned(tempdir.path().into())) + .data_directory(tempdir.path().into()) .low_power() .build(); let (reader, writer, env) = init(config).unwrap(); diff --git a/storage/blockchain/src/tests.rs b/storage/blockchain/src/tests.rs index 1fe20637..4192f812 100644 --- a/storage/blockchain/src/tests.rs +++ b/storage/blockchain/src/tests.rs @@ -5,7 +5,7 @@ //! - only used internally //---------------------------------------------------------------------------------------------------- Import -use std::{borrow::Cow, fmt::Debug}; +use std::fmt::Debug; use pretty_assertions::assert_eq; @@ -74,7 +74,7 @@ impl AssertTableLen { pub(crate) fn tmp_concrete_env() -> (impl Env, tempfile::TempDir) { let tempdir = tempfile::tempdir().unwrap(); let config = ConfigBuilder::new() - .db_directory(Cow::Owned(tempdir.path().into())) + .data_directory(tempdir.path().into()) .low_power() .build(); let env = crate::open(config).unwrap(); diff --git a/storage/txpool/Cargo.toml b/storage/txpool/Cargo.toml index c9082655..0fb43b24 100644 --- a/storage/txpool/Cargo.toml +++ b/storage/txpool/Cargo.toml @@ -15,7 +15,7 @@ default = ["heed"] heed = ["cuprate-database/heed"] redb = ["cuprate-database/redb"] redb-memory = ["cuprate-database/redb-memory"] -serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/serde"] +serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/serde", "cuprate-helper/serde"] [dependencies] cuprate-database = { workspace = true, features = ["heed"] } diff --git a/storage/txpool/README.md b/storage/txpool/README.md index d14f445b..ca4f737c 100644 --- a/storage/txpool/README.md +++ b/storage/txpool/README.md @@ -78,7 +78,7 @@ use cuprate_txpool::{ let tmp_dir = tempfile::tempdir()?; let db_dir = tmp_dir.path().to_owned(); let config = ConfigBuilder::new() - .db_directory(db_dir.into()) + .data_directory(db_dir.into()) .build(); // Initialize the database environment. diff --git a/storage/txpool/src/config.rs b/storage/txpool/src/config.rs index 1ef0d734..724ae21a 100644 --- a/storage/txpool/src/config.rs +++ b/storage/txpool/src/config.rs @@ -1,15 +1,18 @@ //! The transaction pool [`Config`]. -use std::{borrow::Cow, path::Path}; +use std::{borrow::Cow, path::PathBuf}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use cuprate_database::{ config::{Config as DbConfig, SyncMode}, resize::ResizeAlgorithm, }; use cuprate_database_service::ReaderThreads; -use cuprate_helper::fs::CUPRATE_TXPOOL_DIR; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use cuprate_helper::{ + fs::{txpool_path, CUPRATE_DATA_DIR}, + network::Network, +}; /// The default transaction pool weight limit. const DEFAULT_TXPOOL_WEIGHT_LIMIT: usize = 600 * 1024 * 1024; @@ -21,8 +24,9 @@ const DEFAULT_TXPOOL_WEIGHT_LIMIT: usize = 600 * 1024 * 1024; #[derive(Debug, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ConfigBuilder { - /// [`Config::db_directory`]. - db_directory: Option>, + network: Network, + + data_dir: Option, /// [`Config::cuprate_database_config`]. db_config: cuprate_database::config::ConfigBuilder, @@ -41,10 +45,12 @@ impl ConfigBuilder { /// after this function to use default values. pub fn new() -> Self { Self { - db_directory: None, - db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed( - &*CUPRATE_TXPOOL_DIR, - )), + network: Network::default(), + data_dir: None, + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(txpool_path( + &CUPRATE_DATA_DIR, + Network::Mainnet, + ))), reader_threads: None, max_txpool_weight: None, } @@ -53,16 +59,16 @@ impl ConfigBuilder { /// Build into a [`Config`]. /// /// # Default values - /// If [`ConfigBuilder::db_directory`] was not called, - /// the default [`CUPRATE_TXPOOL_DIR`] will be used. + /// If [`ConfigBuilder::data_directory`] was not called, + /// [`txpool_path`] with [`CUPRATE_DATA_DIR`] and [`Network::Mainnet`] will be used. /// /// For all other values, [`Default::default`] is used. pub fn build(self) -> Config { // INVARIANT: all PATH safety checks are done // in `helper::fs`. No need to do them here. - let db_directory = self - .db_directory - .unwrap_or_else(|| Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)); + let data_dir = self + .data_dir + .unwrap_or_else(|| CUPRATE_DATA_DIR.to_path_buf()); let reader_threads = self.reader_threads.unwrap_or_default(); @@ -72,7 +78,7 @@ impl ConfigBuilder { let db_config = self .db_config - .db_directory(db_directory) + .db_directory(Cow::Owned(txpool_path(&data_dir, self.network))) .reader_threads(reader_threads.as_threads()) .build(); @@ -83,6 +89,13 @@ impl ConfigBuilder { } } + /// Change the network this database is for. + #[must_use] + pub const fn network(mut self, network: Network) -> Self { + self.network = network; + self + } + /// Sets a new maximum weight for the transaction pool. #[must_use] pub const fn max_txpool_weight(mut self, max_txpool_weight: usize) -> Self { @@ -90,10 +103,10 @@ impl ConfigBuilder { self } - /// Set a custom database directory (and file) [`Path`]. + /// Set a custom data directory [`PathBuf`]. #[must_use] - pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self { - self.db_directory = Some(db_directory); + pub fn data_directory(mut self, db_directory: PathBuf) -> Self { + self.data_dir = Some(db_directory); self } @@ -124,9 +137,7 @@ impl ConfigBuilder { /// Good default for testing, and resource-available machines. #[must_use] pub fn fast(mut self) -> Self { - self.db_config = - cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)) - .fast(); + self.db_config = self.db_config.fast(); self.reader_threads = Some(ReaderThreads::OnePerThread); self @@ -138,9 +149,7 @@ impl ConfigBuilder { /// Good default for resource-limited machines, e.g. a cheap VPS. #[must_use] pub fn low_power(mut self) -> Self { - self.db_config = - cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)) - .low_power(); + self.db_config = self.db_config.low_power(); self.reader_threads = Some(ReaderThreads::One); self @@ -149,10 +158,13 @@ impl ConfigBuilder { impl Default for ConfigBuilder { fn default() -> Self { - let db_directory = Cow::Borrowed(CUPRATE_TXPOOL_DIR.as_path()); Self { - db_directory: Some(db_directory.clone()), - db_config: cuprate_database::config::ConfigBuilder::new(db_directory), + network: Network::default(), + data_dir: Some(CUPRATE_DATA_DIR.to_path_buf()), + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(txpool_path( + &CUPRATE_DATA_DIR, + Network::Mainnet, + ))), reader_threads: Some(ReaderThreads::default()), max_txpool_weight: Some(DEFAULT_TXPOOL_WEIGHT_LIMIT), } @@ -184,7 +196,7 @@ impl Config { /// Create a new [`Config`] with sane default settings. /// /// The [`DbConfig::db_directory`] - /// will be set to [`CUPRATE_TXPOOL_DIR`]. + /// will be set to [`txpool_path`] with [`CUPRATE_DATA_DIR`] and [`Network::Mainnet`]. /// /// All other values will be [`Default::default`]. /// @@ -197,25 +209,21 @@ impl Config { /// DATABASE_DATA_FILENAME, /// }; /// use cuprate_database_service::ReaderThreads; - /// use cuprate_helper::fs::*; + /// use cuprate_helper::{fs::*, network::Network}; /// /// use cuprate_txpool::Config; /// /// let config = Config::new(); /// - /// assert_eq!(config.db_config.db_directory(), &*CUPRATE_TXPOOL_DIR); - /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_TXPOOL_DIR)); + /// assert_eq!(config.db_config.db_directory(), txpool_path(&CUPRATE_DATA_DIR, Network::Mainnet).as_path()); + /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_DATA_DIR)); /// assert!(config.db_config.db_file().ends_with(DATABASE_DATA_FILENAME)); /// assert_eq!(config.db_config.sync_mode, SyncMode::default()); /// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default()); /// assert_eq!(config.reader_threads, ReaderThreads::default()); /// ``` pub fn new() -> Self { - Self { - db_config: DbConfig::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)), - reader_threads: ReaderThreads::default(), - max_txpool_weight: 0, - } + ConfigBuilder::new().build() } } diff --git a/storage/txpool/src/ops.rs b/storage/txpool/src/ops.rs index 289a8bbf..badc4f6d 100644 --- a/storage/txpool/src/ops.rs +++ b/storage/txpool/src/ops.rs @@ -51,7 +51,7 @@ //! let tmp_dir = tempfile::tempdir()?; //! let db_dir = tmp_dir.path().to_owned(); //! let config = ConfigBuilder::new() -//! .db_directory(db_dir.into()) +//! .data_directory(db_dir.into()) //! .build(); //! //! // Initialize the database environment. diff --git a/storage/txpool/src/service.rs b/storage/txpool/src/service.rs index a82de5bf..03ce2f03 100644 --- a/storage/txpool/src/service.rs +++ b/storage/txpool/src/service.rs @@ -83,7 +83,7 @@ //! let tmp_dir = tempfile::tempdir()?; //! let db_dir = tmp_dir.path().to_owned(); //! let config = ConfigBuilder::new() -//! .db_directory(db_dir.into()) +//! .data_directory(db_dir.into()) //! .build(); //! //! // Initialize the database thread-pool.