diff --git a/Cargo.lock b/Cargo.lock index 39305f62..b17a322a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,6 +798,7 @@ dependencies = [ "rand", "rand_distr", "rayon", + "serde", "thiserror", "tokio", "tokio-stream", @@ -1018,6 +1019,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", + "toml", "tower 0.5.1", "tracing", "tracing-subscriber", @@ -2552,6 +2554,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" @@ -2911,11 +2922,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" @@ -2924,6 +2950,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index c458302b..4fe3a650 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -13,20 +13,20 @@ cuprate-consensus = { path = "../../consensus" } cuprate-fast-sync = { path = "../../consensus/fast-sync" } cuprate-consensus-rules = { path = "../../consensus/rules" } cuprate-cryptonight = { path = "../../cryptonight" } -cuprate-helper = { path = "../../helper" } +cuprate-helper = { path = "../../helper", features = ["serde"] } cuprate-epee-encoding = { path = "../../net/epee-encoding" } cuprate-fixed-bytes = { path = "../../net/fixed-bytes" } cuprate-levin = { path = "../../net/levin" } cuprate-wire = { path = "../../net/wire" } -cuprate-p2p = { path = "../../p2p/p2p" } +cuprate-p2p = { path = "../../p2p/p2p", features = ["serde"] } cuprate-p2p-core = { path = "../../p2p/p2p-core", features = ["serde"] } cuprate-dandelion-tower = { path = "../../p2p/dandelion-tower" } cuprate-async-buffer = { path = "../../p2p/async-buffer" } cuprate-address-book = { path = "../../p2p/address-book", features = ["serde_config"] } cuprate-blockchain = { path = "../../storage/blockchain", features = ["service"] } -cuprate-database-service = { path = "../../storage/service" } +cuprate-database-service = { path = "../../storage/service", features = ["serde"] } cuprate-txpool = { path = "../../storage/txpool" } -cuprate-database = { path = "../../storage/database" } +cuprate-database = { path = "../../storage/database", features = ["serde"] } cuprate-pruning = { path = "../../pruning" } cuprate-test-utils = { path = "../../test-utils" } cuprate-types = { path = "../../types" } @@ -71,7 +71,8 @@ tokio-stream = { workspace = true } tokio = { workspace = true } tower = { workspace = true } tracing-subscriber = { workspace = true, features = ["std", "fmt", "default"] } -tracing = { workspace = true } +tracing = { workspace = true, features = ["default"] } +toml = "0.8" [lints] workspace = true diff --git a/binaries/cuprated/src/config.rs b/binaries/cuprated/src/config.rs index 2a55bebb..290f5c27 100644 --- a/binaries/cuprated/src/config.rs +++ b/binaries/cuprated/src/config.rs @@ -1,52 +1,124 @@ //! 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::network::Network; +use cuprate_helper::{fs::CUPRATE_CONFIG_DIR, network::Network}; use cuprate_p2p::block_downloader::BlockDownloaderConfig; use cuprate_p2p_core::ClearNet; -use serde::{Deserialize, Serialize}; -use std::time::Duration; +mod args; +mod default; mod p2p; mod storage; +mod tracing_config; use p2p::P2PConfig; use storage::StorageConfig; +use tracing_config::TracingConfig; -pub fn config() -> Config { - Config::default() +/// The default name of Cuprate's config file. +const DEFAULT_CONFIG_FILE_NAME: &str = "Cuprate.toml"; + +/// 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 read it and exit if we can't. + match Config::read_from_file(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_err(Into::into) + .and_then(Config::read_from_file) + .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_file(file) + }) + .inspect_err(|e| { + tracing::debug!("Failed to read config from config dir: {e}"); + tracing::warn!("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, } impl Config { - pub fn network(&self) -> Network { + /// 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_file(file: impl AsRef) -> Result { + let file_text = read_to_string(file.as_ref())?; + + Ok(toml::from_str(&file_text).inspect_err(|_| { + tracing::warn!( + "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(self.p2p.clear_net.server.clone()), 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.clone(), } } - pub fn context_config(&self) -> ContextConfig { + /// The [`ContextConfig`]. + pub const fn context_config(&self) -> ContextConfig { match self.network { Network::Mainnet => ContextConfig::main_net(), Network::Stagenet => ContextConfig::stage_net(), @@ -54,17 +126,20 @@ impl Config { } } + /// The [`cuprate_blockchain`] config. pub fn blockchain_config(&self) -> cuprate_blockchain::config::Config { - self.storage.blockchain.clone() + 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) + .db_directory(blockchain.shared.path.clone()) + .sync_mode(blockchain.shared.sync_mode) + .build() } - pub fn block_downloader_config(&self) -> BlockDownloaderConfig { - BlockDownloaderConfig { - 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, - initial_batch_size: 1, - } + /// The [`BlockDownloaderConfig`]. + pub const fn block_downloader_config(&self) -> BlockDownloaderConfig { + self.p2p.block_downloader } } diff --git a/binaries/cuprated/src/config/Cuprate.toml b/binaries/cuprated/src/config/Cuprate.toml new file mode 100644 index 00000000..aa8031f8 --- /dev/null +++ b/binaries/cuprated/src/config/Cuprate.toml @@ -0,0 +1,75 @@ +# ____ _ +# / ___| _ _ __ _ __ __ _| |_ ___ +# | | | | | | '_ \| '__/ _` | __/ _ \ +# | |__| |_| | |_) | | | (_| | || __/ +# \____\__,_| .__/|_| \__,_|\__\___| +# |_| +# + +## 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. +server.ip = "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 folder to store the address book. +peer_store_folder = "{cache}" +## 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 number of blocks in the first bacth (you probably shouldn't change this). +initial_batch_len = 1 +## 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 txpool storage location. +path = "{txpool}" +## The database sync mode for the txpool. +sync_mode = "Async" +## The maximum size of all the txs in the pool (bytes). +max_txpool_size = 100_000_000 + +## Blockchain storage config. +[storage.blockchain] +## The blockchain storage location. +path = "{blockchain}" +## The database sync mode for the blockchain. +sync_mode = "Async" diff --git a/binaries/cuprated/src/config/args.rs b/binaries/cuprated/src/config/args.rs new file mode 100644 index 00000000..c0b89694 --- /dev/null +++ b/binaries/cuprated/src/config/args.rs @@ -0,0 +1,45 @@ +use std::{io::Write, path::PathBuf}; + +use clap::builder::TypedValueParser; + +use cuprate_helper::network::Network; + +use crate::config::{default::create_default_config_file, Config, DEFAULT_CONFIG_FILE_NAME}; + +#[derive(clap::Parser, Debug)] +pub struct Args { + /// The network we should 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. + pub outbound_connections: Option, + /// The location of the Cuprate config file. + pub config_file: Option, + /// Generate a config file and place it in the given folder. + 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..d1438c89 --- /dev/null +++ b/binaries/cuprated/src/config/default.rs @@ -0,0 +1,69 @@ +use std::{ + io::Write, + path::{Path, PathBuf}, +}; + +use cuprate_helper::fs::{CUPRATE_BLOCKCHAIN_DIR, CUPRATE_CACHE_DIR, CUPRATE_TXPOOL_DIR}; + +use crate::config::DEFAULT_CONFIG_FILE_NAME; + +/// 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 = generate_config_text(); + file.write_all(config.as_bytes()).unwrap(); + + std::process::exit(0); +} + +/// Generates the text of the default config file. +fn generate_config_text() -> String { + format!( + include_str!("Cuprate.toml"), + cache = CUPRATE_CACHE_DIR.to_string_lossy(), + txpool = CUPRATE_TXPOOL_DIR.to_string_lossy(), + blockchain = CUPRATE_BLOCKCHAIN_DIR.to_string_lossy() + ) +} + +#[cfg(test)] +mod tests { + use crate::config::{default::generate_config_text, Config}; + + #[test] + fn generate_config_text_covers_all_values() { + let text = generate_config_text(); + + let table: toml::Table = toml::from_str(&text).unwrap(); + + let full_config = Config::default(); + let full_config_table: toml::Table = + toml::from_str(&toml::to_string(&full_config).unwrap()).unwrap(); + + assert_eq!(full_config_table, table); + } + + #[test] + fn generate_config_text_is_valid() { + let text = generate_config_text(); + + let config: Config = toml::from_str(&text).unwrap(); + } +} diff --git a/binaries/cuprated/src/config/p2p.rs b/binaries/cuprated/src/config/p2p.rs index f89b2460..64b5c353 100644 --- a/binaries/cuprated/src/config/p2p.rs +++ b/binaries/cuprated/src/config/p2p.rs @@ -1,21 +1,33 @@ -use cuprate_address_book::AddressBookConfig; -use cuprate_p2p_core::ClearNetServerCfg; +use std::net::SocketAddr; + use serde::{Deserialize, Serialize}; +use cuprate_address_book::AddressBookConfig; +use cuprate_helper::network::Network; +use cuprate_p2p::block_downloader::BlockDownloaderConfig; +use cuprate_p2p_core::ClearNetServerCfg; + +/// 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, } +/// The config values for P2P clear-net. #[derive(Default, Deserialize, Serialize)] #[serde(deny_unknown_fields, default)] pub struct ClearNetConfig { + /// The server config. pub server: ClearNetServerCfg, #[serde(flatten)] pub general: SharedNetConfig, } +/// Network config values shared between all network zones. #[derive(Deserialize, Serialize)] #[serde(deny_unknown_fields, default)] pub struct SharedNetConfig { @@ -25,6 +37,7 @@ pub struct SharedNetConfig { 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, @@ -39,8 +52,46 @@ impl Default for SharedNetConfig { extra_outbound_connections: 8, max_inbound_connections: 128, gray_peers_percent: 0.7, - p2p_port: 18080, + p2p_port: 0, address_book_config: AddressBookConfig::default(), } } } + +/// 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| str::parse(s)) + .collect::>() + .unwrap() +} diff --git a/binaries/cuprated/src/config/storage.rs b/binaries/cuprated/src/config/storage.rs index de913949..ba86c48e 100644 --- a/binaries/cuprated/src/config/storage.rs +++ b/binaries/cuprated/src/config/storage.rs @@ -1,8 +1,70 @@ use serde::{Deserialize, Serialize}; +use cuprate_database::config::SyncMode; +use cuprate_database_service::ReaderThreads; +use cuprate_helper::fs::{CUPRATE_BLOCKCHAIN_DIR, CUPRATE_TXPOOL_DIR}; + +/// The storage config. #[derive(Default, Deserialize, Serialize)] #[serde(deny_unknown_fields, default)] pub struct StorageConfig { - pub blockchain: cuprate_blockchain::config::Config, - pub txpool: cuprate_txpool::Config, + /// 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 { + path: CUPRATE_BLOCKCHAIN_DIR.to_path_buf(), + 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 (bytes). + pub max_txpool_size: usize, +} + +impl Default for TxpoolConfig { + fn default() -> Self { + Self { + shared: SharedStorageConfig { + path: CUPRATE_TXPOOL_DIR.to_path_buf(), + sync_mode: SyncMode::Async, + }, + max_txpool_size: 100_000_000, + } + } +} + +/// Config values shared between the tx-pool and blockchain. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct SharedStorageConfig { + /// The path to the database storage. + pub path: std::path::PathBuf, + /// The [`SyncMode`] of the database. + #[serde(default)] + 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/main.rs b/binaries/cuprated/src/main.rs index d3fe1f56..51d3bce6 100644 --- a/binaries/cuprated/src/main.rs +++ b/binaries/cuprated/src/main.rs @@ -25,6 +25,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/src/fs.rs b/helper/src/fs.rs index 5d62a644..a145ea0e 100644 --- a/helper/src/fs.rs +++ b/helper/src/fs.rs @@ -28,6 +28,8 @@ //! - //---------------------------------------------------------------------------------------------------- Use +use crate::network::Network; +use std::path::Path; use std::{path::PathBuf, sync::LazyLock}; //---------------------------------------------------------------------------------------------------- Const @@ -178,6 +180,10 @@ impl_path_lazylock! { "txpool", } +pub fn path_with_network(path: &Path, network: Network) -> PathBuf { + path.join(network.to_string()) +} + //---------------------------------------------------------------------------------------------------- Tests #[cfg(test)] mod test { diff --git a/helper/src/network.rs b/helper/src/network.rs index 02574752..6d7740b0 100644 --- a/helper/src/network.rs +++ b/helper/src/network.rs @@ -7,6 +7,9 @@ //! `#[no_std]` compatible. // TODO: move to types crate. +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + const MAINNET_NETWORK_ID: [u8; 16] = [ 0x12, 0x30, 0xF1, 0x71, 0x61, 0x04, 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10, ]; @@ -18,7 +21,7 @@ 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 @@ -40,3 +43,29 @@ impl Network { } } } + +#[derive(Debug, PartialEq, Eq)] +pub struct ParseNetworkError; + +impl FromStr for Network { + type Err = ParseNetworkError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "mainnet" => Ok(Self::Mainnet), + "testnet" => Ok(Self::Testnet), + "stagenet" => Ok(Self::Stagenet), + _ => Err(ParseNetworkError), + } + } +} + +impl Display for Network { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Mainnet => "mainnet", + Self::Testnet => "testnet", + Self::Stagenet => "stagenet", + }) + } +} diff --git a/net/wire/src/p2p/common.rs b/net/wire/src/p2p/common.rs index d95a6203..5beb26bd 100644 --- a/net/wire/src/p2p/common.rs +++ b/net/wire/src/p2p/common.rs @@ -51,6 +51,8 @@ impl<'a> From<&'a PeerSupportFlags> for &'a u32 { } } +//15515542498767257178 + /// Basic Node Data, information on the connected peer #[derive(Debug, Clone, PartialEq, Eq)] pub struct BasicNodeData { diff --git a/p2p/p2p-core/src/network_zones/clear.rs b/p2p/p2p-core/src/network_zones/clear.rs index ecc6c0ef..bff7b1ed 100644 --- a/p2p/p2p-core/src/network_zones/clear.rs +++ b/p2p/p2p-core/src/network_zones/clear.rs @@ -1,5 +1,5 @@ use std::{ - net::{IpAddr, SocketAddr}, + net::{IpAddr, Ipv4Addr, SocketAddr}, pin::Pin, task::{Context, Poll}, }; diff --git a/p2p/p2p/Cargo.toml b/p2p/p2p/Cargo.toml index 3444b5ef..41dda2e2 100644 --- a/p2p/p2p/Cargo.toml +++ b/p2p/p2p/Cargo.toml @@ -34,6 +34,8 @@ rand_distr = { workspace = true, features = ["std"] } tracing = { workspace = true, features = ["std", "attributes"] } borsh = { workspace = true, features = ["derive", "std"] } +serde = { workspace = true, features = ["std", "derive"], optional = true } + [dev-dependencies] cuprate-test-utils = { path = "../../test-utils" } indexmap = { workspace = true } diff --git a/p2p/p2p/src/block_downloader.rs b/p2p/p2p/src/block_downloader.rs index fcc9eb65..d5d7fcce 100644 --- a/p2p/p2p/src/block_downloader.rs +++ b/p2p/p2p/src/block_downloader.rs @@ -59,6 +59,8 @@ pub struct BlockBatch { /// The block downloader config. #[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", 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. @@ -70,7 +72,19 @@ 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, +} + +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, + initial_batch_len: 1, + } + } } /// An error that occurred in the [`BlockDownloader`]. @@ -243,7 +257,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 00579110..e7ff955b 100644 --- a/storage/blockchain/Cargo.toml +++ b/storage/blockchain/Cargo.toml @@ -16,6 +16,7 @@ heed = ["cuprate-database/heed"] redb = ["cuprate-database/redb"] redb-memory = ["cuprate-database/redb-memory"] service = ["dep:thread_local", "dep:rayon", "cuprate-helper/thread"] +serde = ["dep:serde", "cuprate-helper/serde", "cuprate-database-service/serde", "cuprate-database/serde"] [dependencies] cuprate-database = { path = "../database" } diff --git a/storage/blockchain/src/config.rs b/storage/blockchain/src/config.rs index e4b76068..e4b2f4ca 100644 --- a/storage/blockchain/src/config.rs +++ b/storage/blockchain/src/config.rs @@ -41,16 +41,17 @@ //! ``` //---------------------------------------------------------------------------------------------------- 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::{path_with_network, CUPRATE_BLOCKCHAIN_DIR}; // re-exports pub use cuprate_database_service::ReaderThreads; +use cuprate_helper::network::Network; //---------------------------------------------------------------------------------------------------- ConfigBuilder /// Builder for [`Config`]. @@ -59,8 +60,10 @@ pub use cuprate_database_service::ReaderThreads; #[derive(Debug, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ConfigBuilder { + network: Network, + /// [`Config::db_directory`]. - db_directory: Option>, + db_directory: Option, /// [`Config::cuprate_database_config`]. db_config: cuprate_database::config::ConfigBuilder, @@ -76,10 +79,12 @@ impl ConfigBuilder { /// after this function to use default values. pub fn new() -> Self { Self { + network: Network::default(), db_directory: None, - db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed( - &*CUPRATE_BLOCKCHAIN_DIR, - )), + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(path_with_network( + &CUPRATE_BLOCKCHAIN_DIR, + Network::default(), + ))), reader_threads: None, } } @@ -96,12 +101,12 @@ impl ConfigBuilder { // in `helper::fs`. No need to do them here. let db_directory = self .db_directory - .unwrap_or_else(|| Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR)); + .unwrap_or_else(|| CUPRATE_BLOCKCHAIN_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(path_with_network(&db_directory, self.network))) .reader_threads(reader_threads.as_threads()) .build(); @@ -111,9 +116,16 @@ impl ConfigBuilder { } } + /// Change the network this blockchain database is for. + #[must_use] + pub const fn network(mut self, network: Network) -> Self { + self.network = network; + self + } + /// Set a custom database directory (and file) [`Path`]. #[must_use] - pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self { + pub fn db_directory(mut self, db_directory: PathBuf) -> Self { self.db_directory = Some(db_directory); self } @@ -170,10 +182,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(), + db_directory: Some(CUPRATE_BLOCKCHAIN_DIR.to_path_buf()), + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(path_with_network( + &CUPRATE_BLOCKCHAIN_DIR, + Network::default(), + ))), reader_threads: Some(ReaderThreads::default()), } } diff --git a/storage/txpool/Cargo.toml b/storage/txpool/Cargo.toml index 70211d9e..21ac915d 100644 --- a/storage/txpool/Cargo.toml +++ b/storage/txpool/Cargo.toml @@ -16,7 +16,7 @@ heed = ["cuprate-database/heed"] redb = ["cuprate-database/redb"] redb-memory = ["cuprate-database/redb-memory"] service = ["dep:tower", "dep:rayon", "dep:cuprate-database-service"] -serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/serde"] +serde = ["dep:serde", "cuprate-helper/serde", "cuprate-database-service/serde", "cuprate-database/serde"] [dependencies] cuprate-database = { path = "../database", features = ["heed"] } diff --git a/storage/txpool/src/config.rs b/storage/txpool/src/config.rs index 1ef0d734..5aa7334d 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::{path_with_network, CUPRATE_TXPOOL_DIR}, + network::Network, +}; /// The default transaction pool weight limit. const DEFAULT_TXPOOL_WEIGHT_LIMIT: usize = 600 * 1024 * 1024; @@ -21,8 +24,10 @@ const DEFAULT_TXPOOL_WEIGHT_LIMIT: usize = 600 * 1024 * 1024; #[derive(Debug, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ConfigBuilder { + network: Network, + /// [`Config::db_directory`]. - db_directory: Option>, + db_directory: Option, /// [`Config::cuprate_database_config`]. db_config: cuprate_database::config::ConfigBuilder, @@ -41,10 +46,12 @@ impl ConfigBuilder { /// after this function to use default values. pub fn new() -> Self { Self { + network: Network::default(), db_directory: None, - db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed( - &*CUPRATE_TXPOOL_DIR, - )), + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(path_with_network( + &CUPRATE_TXPOOL_DIR, + Network::default(), + ))), reader_threads: None, max_txpool_weight: None, } @@ -62,7 +69,7 @@ impl ConfigBuilder { // in `helper::fs`. No need to do them here. let db_directory = self .db_directory - .unwrap_or_else(|| Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)); + .unwrap_or_else(|| CUPRATE_TXPOOL_DIR.to_path_buf()); let reader_threads = self.reader_threads.unwrap_or_default(); @@ -72,7 +79,7 @@ impl ConfigBuilder { let db_config = self .db_config - .db_directory(db_directory) + .db_directory(Cow::Owned(path_with_network(&db_directory, self.network))) .reader_threads(reader_threads.as_threads()) .build(); @@ -83,6 +90,13 @@ impl ConfigBuilder { } } + /// Change the network this blockchain 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 { @@ -92,7 +106,7 @@ impl ConfigBuilder { /// Set a custom database directory (and file) [`Path`]. #[must_use] - pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self { + pub fn db_directory(mut self, db_directory: PathBuf) -> Self { self.db_directory = Some(db_directory); self } @@ -149,10 +163,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(), + db_directory: Some(CUPRATE_TXPOOL_DIR.to_path_buf()), + db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(path_with_network( + &CUPRATE_TXPOOL_DIR, + Network::default(), + ))), reader_threads: Some(ReaderThreads::default()), max_txpool_weight: Some(DEFAULT_TXPOOL_WEIGHT_LIMIT), }