cuprated: config & args (#304)

* init config

* split sections

* finish initial config.

* fix clap

* misc changes

* fix doc

* fix test & clippy

* fix test 2

* try fix windows

* testing

* testing 2

* fix windows test

* fix windows: the remix.

* review comments

* fix imports

* rename & fix default config file

* fix cargo hack

* enable serde on `cuprate-helper`

* changes from matrix chats

* fix ci

* fix doc

* fix doc test

* move Cuprated.toml

* remove default.rs

* `size` -> `bytes`

* `addressbook_path` -> `address_book_path`

* fix config output

* fix ci

* Update binaries/cuprated/src/config/args.rs

Co-authored-by: hinto-janai <hinto.janai@protonmail.com>

---------

Co-authored-by: hinto-janai <hinto.janai@protonmail.com>
This commit is contained in:
Boog900 2024-12-03 15:17:21 +00:00 committed by GitHub
parent 38541dbfda
commit ecd077b402
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 888 additions and 171 deletions

40
Cargo.lock generated
View file

@ -446,6 +446,7 @@ checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"terminal_size",
] ]
[[package]] [[package]]
@ -933,6 +934,7 @@ dependencies = [
"libc", "libc",
"monero-serai", "monero-serai",
"rayon", "rayon",
"serde",
"tokio", "tokio",
"windows", "windows",
] ]
@ -1188,7 +1190,6 @@ dependencies = [
"cuprate-consensus", "cuprate-consensus",
"cuprate-consensus-context", "cuprate-consensus-context",
"cuprate-consensus-rules", "cuprate-consensus-rules",
"cuprate-constants",
"cuprate-cryptonight", "cuprate-cryptonight",
"cuprate-dandelion-tower", "cuprate-dandelion-tower",
"cuprate-database", "cuprate-database",
@ -1230,6 +1231,7 @@ dependencies = [
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util",
"toml",
"tower 0.5.1 (git+https://github.com/Cuprate/tower.git?rev=6c7faf0)", "tower 0.5.1 (git+https://github.com/Cuprate/tower.git?rev=6c7faf0)",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -2904,6 +2906,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@ -3121,6 +3132,16 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.66" version = "1.0.66"
@ -3262,11 +3283,26 @@ dependencies = [
"tracing", "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]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.8" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
@ -3275,6 +3311,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde",
"serde_spanned",
"toml_datetime", "toml_datetime",
"winnow", "winnow",
] ]

View file

@ -55,6 +55,7 @@ members = [
] ]
[profile.release] [profile.release]
panic = "abort"
lto = true # Build with LTO lto = true # Build with LTO
strip = "none" # Keep panic stack traces strip = "none" # Keep panic stack traces
codegen-units = 1 # Optimize for binary speed over compile times codegen-units = 1 # Optimize for binary speed over compile times
@ -144,6 +145,7 @@ tokio-util = { version = "0.7", default-features = false }
tokio-stream = { version = "0.1", default-features = false } tokio-stream = { version = "0.1", default-features = false }
tokio = { version = "1", default-features = false } tokio = { version = "1", default-features = false }
tower = { git = "https://github.com/Cuprate/tower.git", rev = "6c7faf0", default-features = false } # <https://github.com/tower-rs/tower/pull/796> tower = { git = "https://github.com/Cuprate/tower.git", rev = "6c7faf0", default-features = false } # <https://github.com/tower-rs/tower/pull/796>
toml = { version = "0.8", default-features = false }
tracing-subscriber = { version = "0.3", default-features = false } tracing-subscriber = { version = "0.3", default-features = false }
tracing = { version = "0.1", default-features = false } tracing = { version = "0.1", default-features = false }

View file

@ -2,7 +2,7 @@
name = "cuprated" name = "cuprated"
version = "0.0.1" version = "0.0.1"
edition = "2021" edition = "2021"
description = "The Cuprate Monero Rust node." description = "The Cuprate Rust Monero node."
license = "AGPL-3.0-only" license = "AGPL-3.0-only"
authors = ["Boog900", "hinto-janai", "SyntheticBird45"] authors = ["Boog900", "hinto-janai", "SyntheticBird45"]
repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated" 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-consensus = { workspace = true }
cuprate-fast-sync = { workspace = true } cuprate-fast-sync = { workspace = true }
cuprate-consensus-context = { workspace = true } cuprate-consensus-context = { workspace = true }
cuprate-consensus-rules = { workspace = true } cuprate-consensus-rules = { workspace = true }
cuprate-constants = { workspace = true } cuprate-cryptonight = { workspace = true }
cuprate-cryptonight = { workspace = true } cuprate-helper = { workspace = true, features = ["serde"] }
cuprate-helper = { workspace = true } cuprate-epee-encoding = { workspace = true }
cuprate-epee-encoding = { workspace = true } cuprate-fixed-bytes = { workspace = true }
cuprate-fixed-bytes = { workspace = true } cuprate-levin = { workspace = true }
cuprate-levin = { workspace = true } cuprate-wire = { workspace = true }
cuprate-wire = { workspace = true } cuprate-p2p = { workspace = true }
cuprate-p2p = { workspace = true } cuprate-p2p-core = { workspace = true }
cuprate-p2p-core = { workspace = true } cuprate-dandelion-tower = { workspace = true, features = ["txpool"] }
cuprate-dandelion-tower = { workspace = true, features = ["txpool"] } cuprate-async-buffer = { workspace = true }
cuprate-async-buffer = { workspace = true } cuprate-address-book = { workspace = true }
cuprate-address-book = { workspace = true } cuprate-blockchain = { workspace = true }
cuprate-blockchain = { workspace = true } cuprate-database-service = { workspace = true, features = ["serde"] }
cuprate-database-service = { workspace = true } cuprate-txpool = { workspace = true }
cuprate-txpool = { workspace = true } cuprate-database = { workspace = true, features = ["serde"] }
cuprate-database = { workspace = true } cuprate-pruning = { workspace = true }
cuprate-pruning = { workspace = true } cuprate-test-utils = { workspace = true }
cuprate-test-utils = { workspace = true } cuprate-types = { workspace = true }
cuprate-types = { workspace = true } cuprate-json-rpc = { workspace = true }
cuprate-json-rpc = { workspace = true } cuprate-rpc-interface = { workspace = true }
cuprate-rpc-interface = { workspace = true } cuprate-rpc-types = { workspace = true }
cuprate-rpc-types = { workspace = true }
# TODO: after v1.0.0, remove unneeded dependencies. # TODO: after v1.0.0, remove unneeded dependencies.
anyhow = { workspace = true } anyhow = { workspace = true }
@ -44,7 +44,7 @@ borsh = { workspace = true }
bytemuck = { workspace = true } bytemuck = { workspace = true }
bytes = { workspace = true } bytes = { workspace = true }
cfg-if = { workspace = true } cfg-if = { workspace = true }
clap = { workspace = true, features = ["cargo"] } clap = { workspace = true, features = ["cargo", "help", "wrap_help"] }
chrono = { workspace = true } chrono = { workspace = true }
crypto-bigint = { workspace = true } crypto-bigint = { workspace = true }
crossbeam = { workspace = true } crossbeam = { workspace = true }
@ -71,15 +71,10 @@ thread_local = { workspace = true }
tokio-util = { workspace = true } tokio-util = { workspace = true }
tokio-stream = { workspace = true } tokio-stream = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
toml = { workspace = true, features = ["parse", "display"]}
tower = { workspace = true } tower = { workspace = true }
tracing-subscriber = { workspace = true, features = ["std", "fmt", "default"] } tracing-subscriber = { workspace = true, features = ["std", "fmt", "default"] }
tracing = { workspace = true } tracing = { workspace = true, features = ["default"] }
[lints] [lints]
workspace = true workspace = true
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"

View file

@ -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_bytes = 50_000_000
## The size of the queue of blocks which are waiting for a parent block to be downloaded (bytes).
in_progress_queue_bytes = 50_000_000
## The target size of a batch of blocks (bytes), must not exceed 100MB.
target_batch_bytes= 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"

View file

@ -1 +1,159 @@
//! cuprated config //! 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 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();
args.do_quick_requests();
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) => {
eprintln!("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}");
eprintln!("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<Path>) -> Result<Self, anyhow::Error> {
let file_text = read_to_string(file.as_ref())?;
Ok(toml::from_str(&file_text)
.inspect(|_| eprintln!("Using config at: {}", file.as_ref().to_string_lossy()))
.inspect_err(|e| {
eprintln!("{e}");
eprintln!(
"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<ClearNet> {
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()
}
}

View file

@ -0,0 +1,55 @@
use std::{io::Write, path::PathBuf, process::exit};
use clap::builder::TypedValueParser;
use cuprate_helper::network::Network;
use crate::{config::Config, constants::EXAMPLE_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::<Network>().unwrap()),
)]
pub network: Network,
/// The amount of outbound clear-net connections to maintain.
#[arg(long)]
pub outbound_connections: Option<usize>,
/// The PATH of the `cuprated` config file.
#[arg(long)]
pub config_file: Option<PathBuf>,
/// Generate a config file and print it to stdout.
#[arg(long)]
pub generate_config: bool,
}
impl Args {
/// Complete any quick requests asked for in [`Args`].
///
/// May cause the process to [`exit`].
pub fn do_quick_requests(&self) {
if self.generate_config {
println!("{EXAMPLE_CONFIG}");
exit(0);
}
}
/// Apply the [`Args`] to the given [`Config`].
///
/// This may exit the program if a config value was set that requires an early exit.
pub const fn apply_args(&self, mut config: Config) -> Config {
config.network = self.network;
if let Some(outbound_connections) = self.outbound_connections {
config.p2p.clear_net.general.outbound_connections = outbound_connections;
}
config
}
}

View file

@ -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(),
}
}
}

View file

@ -0,0 +1,178 @@
use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
path::Path,
time::Duration,
};
use serde::{Deserialize, Serialize};
use cuprate_helper::{fs::address_book_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_bytes: usize,
/// The size of the in progress queue (in bytes) at which we stop requesting more blocks.
pub in_progress_queue_bytes: 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_bytes: usize,
}
impl From<BlockDownloaderConfig> for cuprate_p2p::block_downloader::BlockDownloaderConfig {
fn from(value: BlockDownloaderConfig) -> Self {
Self {
buffer_bytes: value.buffer_bytes,
in_progress_queue_bytes: value.in_progress_queue_bytes,
check_client_pool_interval: value.check_client_pool_interval,
target_batch_bytes: value.target_batch_bytes,
initial_batch_len: 1,
}
}
}
impl Default for BlockDownloaderConfig {
fn default() -> Self {
Self {
buffer_bytes: 50_000_000,
in_progress_queue_bytes: 50_000_000,
check_client_pool_interval: Duration::from_secs(30),
target_batch_bytes: 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: address_book_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<SocketAddr> {
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::<Result<_, _>>()
.unwrap()
}

View file

@ -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,
}

View file

@ -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<S>(level_filter: &LevelFilter, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&level_filter.to_string())
}
pub fn deserialize<'de, D>(d: D) -> Result<LevelFilter, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(d)?;
LevelFilter::from_str(&s).map_err(serde::de::Error::custom)
}
}

View file

@ -18,9 +18,12 @@ pub const VERSION_BUILD: &str = if cfg!(debug_assertions) {
pub const PANIC_CRITICAL_SERVICE_ERROR: &str = pub const PANIC_CRITICAL_SERVICE_ERROR: &str =
"A service critical to Cuprate's function returned an unexpected error."; "A service critical to Cuprate's function returned an unexpected error.";
pub const EXAMPLE_CONFIG: &str = include_str!("../Cuprated.toml");
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::config::Config;
#[test] #[test]
fn version() { fn version() {
@ -35,4 +38,9 @@ mod test {
assert_eq!(VERSION_BUILD, "0.0.1-release"); assert_eq!(VERSION_BUILD, "0.0.1-release");
} }
} }
#[test]
fn generate_config_text_is_valid() {
let config: Config = toml::from_str(EXAMPLE_CONFIG).unwrap();
}
} }

View file

@ -29,6 +29,8 @@ fn main() {
// Initialize global static `LazyLock` data. // Initialize global static `LazyLock` data.
statics::init_lazylock_statics(); statics::init_lazylock_statics();
let _config = config::read_config_and_args();
// TODO: everything else. // TODO: everything else.
todo!() todo!()
} }

View file

@ -35,6 +35,8 @@ futures = { workspace = true, optional = true, features = ["std"] }
monero-serai = { workspace = true, optional = true } monero-serai = { workspace = true, optional = true }
rayon = { workspace = true, optional = true } rayon = { workspace = true, optional = true }
serde = { workspace = true, optional = true, features = ["derive"] }
# This is kinda a stupid work around. # This is kinda a stupid work around.
# [thread] needs to activate one of these libs (windows|libc) # [thread] needs to activate one of these libs (windows|libc)
# although it depends on what target we're building for. # although it depends on what target we're building for.

View file

@ -28,7 +28,12 @@
//! - <https://docs.rs/dirs> //! - <https://docs.rs/dirs>
//---------------------------------------------------------------------------------------------------- Use //---------------------------------------------------------------------------------------------------- Use
use std::{path::PathBuf, sync::LazyLock}; use std::{
path::{Path, PathBuf},
sync::LazyLock,
};
use crate::network::Network;
//---------------------------------------------------------------------------------------------------- Const //---------------------------------------------------------------------------------------------------- Const
/// Cuprate's main directory. /// 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 //---------------------------------------------------------------------------------------------------- Directories
/// Create a `LazyLock` for common PATHs used by Cuprate. /// Create a `LazyLock` for common PATHs used by Cuprate.
/// ///
@ -150,32 +158,61 @@ impl_path_lazylock! {
CUPRATE_DATA_DIR, CUPRATE_DATA_DIR,
data_dir, data_dir,
"", "",
}
/// Cuprate's blockchain directory. /// Joins the [`Network`] to the [`Path`].
/// ///
/// This is the PATH used for any Cuprate blockchain files. /// This will keep the path the same for [`Network::Mainnet`].
/// fn path_with_network(path: &Path, network: Network) -> PathBuf {
/// | OS | PATH | match network {
/// |---------|----------------------------------------------------------------| Network::Mainnet => path.to_path_buf(),
/// | Windows | `C:\Users\Alice\AppData\Roaming\Cuprate\blockchain\` | network => path.join(network.to_string()),
/// | macOS | `/Users/Alice/Library/Application Support/Cuprate/blockchain/` | }
/// | Linux | `/home/alice/.local/share/cuprate/blockchain/` | }
CUPRATE_BLOCKCHAIN_DIR,
data_dir,
"blockchain",
/// Cuprate's transaction pool directory. /// Cuprate's blockchain directory.
/// ///
/// This is the PATH used for any Cuprate txpool files. /// This is the PATH used for any Cuprate blockchain files.
/// ///
/// | OS | PATH | /// ```rust
/// |---------|------------------------------------------------------------| /// use cuprate_helper::{network::Network, fs::{CUPRATE_DATA_DIR, blockchain_path}};
/// | Windows | `C:\Users\Alice\AppData\Roaming\Cuprate\txpool\` | ///
/// | macOS | `/Users/Alice/Library/Application Support/Cuprate/txpool/` | /// assert_eq!(blockchain_path(&**CUPRATE_DATA_DIR, Network::Mainnet).as_path(), CUPRATE_DATA_DIR.join("blockchain"));
/// | Linux | `/home/alice/.local/share/cuprate/txpool/` | /// assert_eq!(blockchain_path(&**CUPRATE_DATA_DIR, Network::Stagenet).as_path(), CUPRATE_DATA_DIR.join(Network::Stagenet.to_string()).join("blockchain"));
CUPRATE_TXPOOL_DIR, /// assert_eq!(blockchain_path(&**CUPRATE_DATA_DIR, Network::Testnet).as_path(), CUPRATE_DATA_DIR.join(Network::Testnet.to_string()).join("blockchain"));
data_dir, /// ```
"txpool", 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, address_book_path}};
///
/// assert_eq!(address_book_path(&**CUPRATE_CACHE_DIR, Network::Mainnet).as_path(), CUPRATE_CACHE_DIR.join("addressbook"));
/// assert_eq!(address_book_path(&**CUPRATE_CACHE_DIR, Network::Stagenet).as_path(), CUPRATE_CACHE_DIR.join(Network::Stagenet.to_string()).join("addressbook"));
/// assert_eq!(address_book_path(&**CUPRATE_CACHE_DIR, Network::Testnet).as_path(), CUPRATE_CACHE_DIR.join(Network::Testnet.to_string()).join("addressbook"));
/// ```
pub fn address_book_path(cache_dir: &Path, network: Network) -> PathBuf {
path_with_network(cache_dir, network).join("addressbook")
} }
//---------------------------------------------------------------------------------------------------- Tests //---------------------------------------------------------------------------------------------------- Tests
@ -197,29 +234,21 @@ mod test {
(&*CUPRATE_CACHE_DIR, ""), (&*CUPRATE_CACHE_DIR, ""),
(&*CUPRATE_CONFIG_DIR, ""), (&*CUPRATE_CONFIG_DIR, ""),
(&*CUPRATE_DATA_DIR, ""), (&*CUPRATE_DATA_DIR, ""),
(&*CUPRATE_BLOCKCHAIN_DIR, ""),
(&*CUPRATE_TXPOOL_DIR, ""),
]; ];
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
array[0].1 = r"AppData\Local\Cuprate"; array[0].1 = r"AppData\Local\Cuprate";
array[1].1 = r"AppData\Roaming\Cuprate"; array[1].1 = r"AppData\Roaming\Cuprate";
array[2].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") { } else if cfg!(target_os = "macos") {
array[0].1 = "Library/Caches/Cuprate"; array[0].1 = "Library/Caches/Cuprate";
array[1].1 = "Library/Application Support/Cuprate"; array[1].1 = "Library/Application Support/Cuprate";
array[2].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 { } else {
// Assumes Linux. // Assumes Linux.
array[0].1 = ".cache/cuprate"; array[0].1 = ".cache/cuprate";
array[1].1 = ".config/cuprate"; array[1].1 = ".config/cuprate";
array[2].1 = ".local/share/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 { for (path, expected) in array {

View file

@ -5,6 +5,12 @@
//! into it's own crate. //! into it's own crate.
//! //!
//! `#[no_std]` compatible. //! `#[no_std]` compatible.
// TODO: move to types crate.
use core::{
fmt::{Display, Formatter},
str::FromStr,
};
const MAINNET_NETWORK_ID: [u8; 16] = [ const MAINNET_NETWORK_ID: [u8; 16] = [
0x12, 0x30, 0xF1, 0x71, 0x61, 0x04, 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10, 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. /// 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 { pub enum Network {
/// Mainnet /// Mainnet
#[default] #[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<Self, Self::Err> {
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",
})
}
}

View file

@ -23,7 +23,7 @@ indexmap = { workspace = true, features = ["std"] }
rand = { workspace = true, features = ["std", "std_rng"] } rand = { workspace = true, features = ["std", "std_rng"] }
borsh = { workspace = true, features = ["derive", "std"]} borsh = { workspace = true, features = ["derive", "std"] }
[dev-dependencies] [dev-dependencies]
cuprate-test-utils = { workspace = true } cuprate-test-utils = { workspace = true }

View file

@ -15,7 +15,7 @@ fn test_cfg() -> AddressBookConfig {
AddressBookConfig { AddressBookConfig {
max_white_list_length: 100, max_white_list_length: 100,
max_gray_list_length: 500, max_gray_list_length: 500,
peer_store_file: PathBuf::new(), peer_store_directory: PathBuf::new(),
peer_save_period: Duration::from_secs(60), peer_save_period: Duration::from_secs(60),
} }
} }

View file

@ -29,8 +29,8 @@ pub struct AddressBookConfig {
/// ///
/// Gray peers are peers we are yet to make a connection to. /// Gray peers are peers we are yet to make a connection to.
pub max_gray_list_length: usize, pub max_gray_list_length: usize,
/// The location to store the address book. /// The location to store the peer store files.
pub peer_store_file: PathBuf, pub peer_store_directory: PathBuf,
/// The amount of time between saving the address book to disk. /// The amount of time between saving the address book to disk.
pub peer_save_period: Duration, pub peer_save_period: Duration,
} }
@ -63,11 +63,6 @@ pub enum AddressBookError {
pub async fn init_address_book<Z: BorshNetworkZone>( pub async fn init_address_book<Z: BorshNetworkZone>(
cfg: AddressBookConfig, cfg: AddressBookConfig,
) -> Result<book::AddressBook<Z>, std::io::Error> { ) -> Result<book::AddressBook<Z>, 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::<Z>(&cfg).await { let (white_list, gray_list) = match store::read_peers_from_disk::<Z>(&cfg).await {
Ok(res) => res, Ok(res) => res,
Err(e) if e.kind() == ErrorKind::NotFound => (vec![], vec![]), Err(e) if e.kind() == ErrorKind::NotFound => (vec![], vec![]),

View file

@ -39,7 +39,9 @@ pub(crate) fn save_peers_to_disk<Z: BorshNetworkZone>(
}) })
.unwrap(); .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)) spawn_blocking(move || fs::write(&file, &data))
} }
@ -52,7 +54,12 @@ pub(crate) async fn read_peers_from_disk<Z: BorshNetworkZone>(
), ),
std::io::Error, 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 data = spawn_blocking(move || fs::read(file)).await.unwrap()?;
let de_ser: DeserPeerDataV1<Z::Addr> = from_slice(&data)?; let de_ser: DeserPeerDataV1<Z::Addr> = from_slice(&data)?;

View file

@ -62,15 +62,15 @@ pub struct BlockBatch {
pub struct BlockDownloaderConfig { pub struct BlockDownloaderConfig {
/// The size in bytes of the buffer between the block downloader and the place which /// The size in bytes of the buffer between the block downloader and the place which
/// is consuming the downloaded blocks. /// is consuming the downloaded blocks.
pub buffer_size: usize, pub buffer_bytes: usize,
/// The size of the in progress queue (in bytes) at which we stop requesting more blocks. /// The size of the in progress queue (in bytes) at which we stop requesting more blocks.
pub in_progress_queue_size: usize, pub in_progress_queue_bytes: usize,
/// The [`Duration`] between checking the client pool for free peers. /// The [`Duration`] between checking the client pool for free peers.
pub check_client_pool_interval: Duration, pub check_client_pool_interval: Duration,
/// The target size of a single batch of blocks (in bytes). /// The target size of a single batch of blocks (in bytes).
pub target_batch_size: usize, pub target_batch_bytes: usize,
/// The initial amount of blocks to request (in number of blocks) /// 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`]. /// An error that occurred in the [`BlockDownloader`].
@ -145,7 +145,7 @@ where
+ 'static, + 'static,
C::Future: Send + 'static, C::Future: Send + 'static,
{ {
let (buffer_appender, buffer_stream) = cuprate_async_buffer::new_buffer(config.buffer_size); let (buffer_appender, buffer_stream) = cuprate_async_buffer::new_buffer(config.buffer_bytes);
let block_downloader = BlockDownloader::new(peer_set, our_chain_svc, buffer_appender, config); let block_downloader = BlockDownloader::new(peer_set, our_chain_svc, buffer_appender, config);
@ -242,7 +242,7 @@ where
Self { Self {
peer_set, peer_set,
our_chain_svc, 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_blocks_to_request_updated_at: 0,
amount_of_empty_chain_entries: 0, amount_of_empty_chain_entries: 0,
block_download_tasks: JoinSet::new(), block_download_tasks: JoinSet::new(),
@ -381,7 +381,7 @@ where
} }
// If our ready queue is too large send duplicate requests for the blocks we are waiting on. // If our ready queue is too large send duplicate requests for the blocks we are waiting on.
if self.block_queue.size() >= self.config.in_progress_queue_size { if self.block_queue.size() >= self.config.in_progress_queue_bytes {
return self.request_inflight_batch_again(client); return self.request_inflight_batch_again(client);
} }
@ -565,7 +565,7 @@ where
self.amount_of_blocks_to_request = calculate_next_block_batch_size( self.amount_of_blocks_to_request = calculate_next_block_batch_size(
block_batch.size, block_batch.size,
block_batch.blocks.len(), block_batch.blocks.len(),
self.config.target_batch_size, self.config.target_batch_bytes,
); );
tracing::debug!( tracing::debug!(

View file

@ -66,11 +66,11 @@ proptest! {
genesis: *blockchain.blocks.first().unwrap().0 genesis: *blockchain.blocks.first().unwrap().0
}, },
BlockDownloaderConfig { BlockDownloaderConfig {
buffer_size: 1_000, buffer_bytes: 1_000,
in_progress_queue_size: 10_000, in_progress_queue_bytes: 10_000,
check_client_pool_interval: Duration::from_secs(5), check_client_pool_interval: Duration::from_secs(5),
target_batch_size: 5_000, target_batch_bytes: 5_000,
initial_batch_size: 1, initial_batch_len: 1,
}); });
let blocks = stream.map(|blocks| blocks.blocks).concat().await; let blocks = stream.map(|blocks| blocks.blocks).concat().await;

View file

@ -15,7 +15,7 @@ default = ["heed"]
heed = ["cuprate-database/heed"] heed = ["cuprate-database/heed"]
redb = ["cuprate-database/redb"] redb = ["cuprate-database/redb"]
redb-memory = ["cuprate-database/redb-memory"] 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] [dependencies]
cuprate-database = { workspace = true } cuprate-database = { workspace = true }

View file

@ -76,7 +76,7 @@ use cuprate_blockchain::{
let tmp_dir = tempfile::tempdir()?; let tmp_dir = tempfile::tempdir()?;
let db_dir = tmp_dir.path().to_owned(); let db_dir = tmp_dir.path().to_owned();
let config = ConfigBuilder::new() let config = ConfigBuilder::new()
.db_directory(db_dir.into()) .data_directory(db_dir.into())
.build(); .build();
// Initialize the database environment. // Initialize the database environment.

View file

@ -25,7 +25,7 @@
//! //!
//! let config = ConfigBuilder::new() //! let config = ConfigBuilder::new()
//! // Use a custom database directory. //! // 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`). //! // Use as many reader threads as possible (when using `service`).
//! .reader_threads(ReaderThreads::OnePerThread) //! .reader_threads(ReaderThreads::OnePerThread)
//! // Use the fastest sync mode. //! // Use the fastest sync mode.
@ -41,13 +41,16 @@
//! ``` //! ```
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use std::{borrow::Cow, path::Path}; use std::{borrow::Cow, path::PathBuf};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use cuprate_database::{config::SyncMode, resize::ResizeAlgorithm}; 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 // re-exports
pub use cuprate_database_service::ReaderThreads; pub use cuprate_database_service::ReaderThreads;
@ -59,8 +62,9 @@ pub use cuprate_database_service::ReaderThreads;
#[derive(Debug, Clone, PartialEq, PartialOrd)] #[derive(Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ConfigBuilder { pub struct ConfigBuilder {
/// [`Config::db_directory`]. network: Network,
db_directory: Option<Cow<'static, Path>>,
data_dir: Option<PathBuf>,
/// [`Config::cuprate_database_config`]. /// [`Config::cuprate_database_config`].
db_config: cuprate_database::config::ConfigBuilder, db_config: cuprate_database::config::ConfigBuilder,
@ -76,10 +80,12 @@ impl ConfigBuilder {
/// after this function to use default values. /// after this function to use default values.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
db_directory: None, network: Network::default(),
db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed( data_dir: None,
&*CUPRATE_BLOCKCHAIN_DIR, db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(blockchain_path(
)), &CUPRATE_DATA_DIR,
Network::Mainnet,
))),
reader_threads: None, reader_threads: None,
} }
} }
@ -87,21 +93,21 @@ impl ConfigBuilder {
/// Build into a [`Config`]. /// Build into a [`Config`].
/// ///
/// # Default values /// # Default values
/// If [`ConfigBuilder::db_directory`] was not called, /// If [`ConfigBuilder::data_directory`] was not called,
/// the default [`CUPRATE_BLOCKCHAIN_DIR`] will be used. /// [`blockchain_path`] with [`CUPRATE_DATA_DIR`] [`Network::Mainnet`] will be used.
/// ///
/// For all other values, [`Default::default`] is used. /// For all other values, [`Default::default`] is used.
pub fn build(self) -> Config { pub fn build(self) -> Config {
// INVARIANT: all PATH safety checks are done // INVARIANT: all PATH safety checks are done
// in `helper::fs`. No need to do them here. // in `helper::fs`. No need to do them here.
let db_directory = self let data_dir = self
.db_directory .data_dir
.unwrap_or_else(|| Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR)); .unwrap_or_else(|| CUPRATE_DATA_DIR.to_path_buf());
let reader_threads = self.reader_threads.unwrap_or_default(); let reader_threads = self.reader_threads.unwrap_or_default();
let db_config = self let db_config = self
.db_config .db_config
.db_directory(db_directory) .db_directory(Cow::Owned(blockchain_path(&data_dir, self.network)))
.reader_threads(reader_threads.as_threads()) .reader_threads(reader_threads.as_threads())
.build(); .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] #[must_use]
pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self { pub const fn network(mut self, network: Network) -> Self {
self.db_directory = Some(db_directory); 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 self
} }
@ -145,9 +158,7 @@ impl ConfigBuilder {
/// Good default for testing, and resource-available machines. /// Good default for testing, and resource-available machines.
#[must_use] #[must_use]
pub fn fast(mut self) -> Self { pub fn fast(mut self) -> Self {
self.db_config = self.db_config = self.db_config.fast();
cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR))
.fast();
self.reader_threads = Some(ReaderThreads::OnePerThread); self.reader_threads = Some(ReaderThreads::OnePerThread);
self self
@ -159,9 +170,7 @@ impl ConfigBuilder {
/// Good default for resource-limited machines, e.g. a cheap VPS. /// Good default for resource-limited machines, e.g. a cheap VPS.
#[must_use] #[must_use]
pub fn low_power(mut self) -> Self { pub fn low_power(mut self) -> Self {
self.db_config = self.db_config = self.db_config.low_power();
cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_BLOCKCHAIN_DIR))
.low_power();
self.reader_threads = Some(ReaderThreads::One); self.reader_threads = Some(ReaderThreads::One);
self self
@ -170,10 +179,13 @@ impl ConfigBuilder {
impl Default for ConfigBuilder { impl Default for ConfigBuilder {
fn default() -> Self { fn default() -> Self {
let db_directory = Cow::Borrowed(&**CUPRATE_BLOCKCHAIN_DIR);
Self { Self {
db_directory: Some(db_directory.clone()), network: Network::default(),
db_config: cuprate_database::config::ConfigBuilder::new(db_directory), 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()), reader_threads: Some(ReaderThreads::default()),
} }
} }
@ -201,7 +213,7 @@ impl Config {
/// Create a new [`Config`] with sane default settings. /// Create a new [`Config`] with sane default settings.
/// ///
/// The [`cuprate_database::config::Config::db_directory`] /// 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`]. /// All other values will be [`Default::default`].
/// ///
@ -213,14 +225,14 @@ impl Config {
/// resize::ResizeAlgorithm, /// resize::ResizeAlgorithm,
/// DATABASE_DATA_FILENAME, /// DATABASE_DATA_FILENAME,
/// }; /// };
/// use cuprate_helper::fs::*; /// use cuprate_helper::{fs::*, network::Network};
/// ///
/// use cuprate_blockchain::config::*; /// use cuprate_blockchain::config::*;
/// ///
/// let config = Config::new(); /// let config = Config::new();
/// ///
/// assert_eq!(config.db_config.db_directory(), &*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_BLOCKCHAIN_DIR)); /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_DATA_DIR));
/// assert!(config.db_config.db_file().ends_with(DATABASE_DATA_FILENAME)); /// 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.sync_mode, SyncMode::default());
/// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default()); /// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default());

View file

@ -71,7 +71,7 @@
//! let tmp_dir = tempfile::tempdir()?; //! let tmp_dir = tempfile::tempdir()?;
//! let db_dir = tmp_dir.path().to_owned(); //! let db_dir = tmp_dir.path().to_owned();
//! let config = ConfigBuilder::new() //! let config = ConfigBuilder::new()
//! .db_directory(db_dir.into()) //! .data_directory(db_dir.into())
//! .build(); //! .build();
//! //!
//! // Initialize the database environment. //! // Initialize the database environment.

View file

@ -77,7 +77,7 @@
//! let tmp_dir = tempfile::tempdir()?; //! let tmp_dir = tempfile::tempdir()?;
//! let db_dir = tmp_dir.path().to_owned(); //! let db_dir = tmp_dir.path().to_owned();
//! let config = ConfigBuilder::new() //! let config = ConfigBuilder::new()
//! .db_directory(db_dir.into()) //! .data_directory(db_dir.into())
//! .build(); //! .build();
//! //!
//! // Initialize the database thread-pool. //! // Initialize the database thread-pool.

View file

@ -7,7 +7,6 @@
//---------------------------------------------------------------------------------------------------- Use //---------------------------------------------------------------------------------------------------- Use
use std::{ use std::{
borrow::Cow,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
sync::Arc, sync::Arc,
}; };
@ -46,7 +45,7 @@ fn init_service() -> (
) { ) {
let tempdir = tempfile::tempdir().unwrap(); let tempdir = tempfile::tempdir().unwrap();
let config = ConfigBuilder::new() let config = ConfigBuilder::new()
.db_directory(Cow::Owned(tempdir.path().into())) .data_directory(tempdir.path().into())
.low_power() .low_power()
.build(); .build();
let (reader, writer, env) = init(config).unwrap(); let (reader, writer, env) = init(config).unwrap();

View file

@ -5,7 +5,7 @@
//! - only used internally //! - only used internally
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use std::{borrow::Cow, fmt::Debug}; use std::fmt::Debug;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -74,7 +74,7 @@ impl AssertTableLen {
pub(crate) fn tmp_concrete_env() -> (impl Env, tempfile::TempDir) { pub(crate) fn tmp_concrete_env() -> (impl Env, tempfile::TempDir) {
let tempdir = tempfile::tempdir().unwrap(); let tempdir = tempfile::tempdir().unwrap();
let config = ConfigBuilder::new() let config = ConfigBuilder::new()
.db_directory(Cow::Owned(tempdir.path().into())) .data_directory(tempdir.path().into())
.low_power() .low_power()
.build(); .build();
let env = crate::open(config).unwrap(); let env = crate::open(config).unwrap();

View file

@ -15,7 +15,7 @@ default = ["heed"]
heed = ["cuprate-database/heed"] heed = ["cuprate-database/heed"]
redb = ["cuprate-database/redb"] redb = ["cuprate-database/redb"]
redb-memory = ["cuprate-database/redb-memory"] 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] [dependencies]
cuprate-database = { workspace = true, features = ["heed"] } cuprate-database = { workspace = true, features = ["heed"] }

View file

@ -78,7 +78,7 @@ use cuprate_txpool::{
let tmp_dir = tempfile::tempdir()?; let tmp_dir = tempfile::tempdir()?;
let db_dir = tmp_dir.path().to_owned(); let db_dir = tmp_dir.path().to_owned();
let config = ConfigBuilder::new() let config = ConfigBuilder::new()
.db_directory(db_dir.into()) .data_directory(db_dir.into())
.build(); .build();
// Initialize the database environment. // Initialize the database environment.

View file

@ -1,15 +1,18 @@
//! The transaction pool [`Config`]. //! 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::{ use cuprate_database::{
config::{Config as DbConfig, SyncMode}, config::{Config as DbConfig, SyncMode},
resize::ResizeAlgorithm, resize::ResizeAlgorithm,
}; };
use cuprate_database_service::ReaderThreads; use cuprate_database_service::ReaderThreads;
use cuprate_helper::fs::CUPRATE_TXPOOL_DIR; use cuprate_helper::{
fs::{txpool_path, CUPRATE_DATA_DIR},
#[cfg(feature = "serde")] network::Network,
use serde::{Deserialize, Serialize}; };
/// The default transaction pool weight limit. /// The default transaction pool weight limit.
const DEFAULT_TXPOOL_WEIGHT_LIMIT: usize = 600 * 1024 * 1024; 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)] #[derive(Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ConfigBuilder { pub struct ConfigBuilder {
/// [`Config::db_directory`]. network: Network,
db_directory: Option<Cow<'static, Path>>,
data_dir: Option<PathBuf>,
/// [`Config::cuprate_database_config`]. /// [`Config::cuprate_database_config`].
db_config: cuprate_database::config::ConfigBuilder, db_config: cuprate_database::config::ConfigBuilder,
@ -41,10 +45,12 @@ impl ConfigBuilder {
/// after this function to use default values. /// after this function to use default values.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
db_directory: None, network: Network::default(),
db_config: cuprate_database::config::ConfigBuilder::new(Cow::Borrowed( data_dir: None,
&*CUPRATE_TXPOOL_DIR, db_config: cuprate_database::config::ConfigBuilder::new(Cow::Owned(txpool_path(
)), &CUPRATE_DATA_DIR,
Network::Mainnet,
))),
reader_threads: None, reader_threads: None,
max_txpool_weight: None, max_txpool_weight: None,
} }
@ -53,16 +59,16 @@ impl ConfigBuilder {
/// Build into a [`Config`]. /// Build into a [`Config`].
/// ///
/// # Default values /// # Default values
/// If [`ConfigBuilder::db_directory`] was not called, /// If [`ConfigBuilder::data_directory`] was not called,
/// the default [`CUPRATE_TXPOOL_DIR`] will be used. /// [`txpool_path`] with [`CUPRATE_DATA_DIR`] and [`Network::Mainnet`] will be used.
/// ///
/// For all other values, [`Default::default`] is used. /// For all other values, [`Default::default`] is used.
pub fn build(self) -> Config { pub fn build(self) -> Config {
// INVARIANT: all PATH safety checks are done // INVARIANT: all PATH safety checks are done
// in `helper::fs`. No need to do them here. // in `helper::fs`. No need to do them here.
let db_directory = self let data_dir = self
.db_directory .data_dir
.unwrap_or_else(|| Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)); .unwrap_or_else(|| CUPRATE_DATA_DIR.to_path_buf());
let reader_threads = self.reader_threads.unwrap_or_default(); let reader_threads = self.reader_threads.unwrap_or_default();
@ -72,7 +78,7 @@ impl ConfigBuilder {
let db_config = self let db_config = self
.db_config .db_config
.db_directory(db_directory) .db_directory(Cow::Owned(txpool_path(&data_dir, self.network)))
.reader_threads(reader_threads.as_threads()) .reader_threads(reader_threads.as_threads())
.build(); .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. /// Sets a new maximum weight for the transaction pool.
#[must_use] #[must_use]
pub const fn max_txpool_weight(mut self, max_txpool_weight: usize) -> Self { pub const fn max_txpool_weight(mut self, max_txpool_weight: usize) -> Self {
@ -90,10 +103,10 @@ impl ConfigBuilder {
self self
} }
/// Set a custom database directory (and file) [`Path`]. /// Set a custom data directory [`PathBuf`].
#[must_use] #[must_use]
pub fn db_directory(mut self, db_directory: Cow<'static, Path>) -> Self { pub fn data_directory(mut self, db_directory: PathBuf) -> Self {
self.db_directory = Some(db_directory); self.data_dir = Some(db_directory);
self self
} }
@ -124,9 +137,7 @@ impl ConfigBuilder {
/// Good default for testing, and resource-available machines. /// Good default for testing, and resource-available machines.
#[must_use] #[must_use]
pub fn fast(mut self) -> Self { pub fn fast(mut self) -> Self {
self.db_config = self.db_config = self.db_config.fast();
cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR))
.fast();
self.reader_threads = Some(ReaderThreads::OnePerThread); self.reader_threads = Some(ReaderThreads::OnePerThread);
self self
@ -138,9 +149,7 @@ impl ConfigBuilder {
/// Good default for resource-limited machines, e.g. a cheap VPS. /// Good default for resource-limited machines, e.g. a cheap VPS.
#[must_use] #[must_use]
pub fn low_power(mut self) -> Self { pub fn low_power(mut self) -> Self {
self.db_config = self.db_config = self.db_config.low_power();
cuprate_database::config::ConfigBuilder::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR))
.low_power();
self.reader_threads = Some(ReaderThreads::One); self.reader_threads = Some(ReaderThreads::One);
self self
@ -149,10 +158,13 @@ impl ConfigBuilder {
impl Default for ConfigBuilder { impl Default for ConfigBuilder {
fn default() -> Self { fn default() -> Self {
let db_directory = Cow::Borrowed(CUPRATE_TXPOOL_DIR.as_path());
Self { Self {
db_directory: Some(db_directory.clone()), network: Network::default(),
db_config: cuprate_database::config::ConfigBuilder::new(db_directory), 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()), reader_threads: Some(ReaderThreads::default()),
max_txpool_weight: Some(DEFAULT_TXPOOL_WEIGHT_LIMIT), max_txpool_weight: Some(DEFAULT_TXPOOL_WEIGHT_LIMIT),
} }
@ -184,7 +196,7 @@ impl Config {
/// Create a new [`Config`] with sane default settings. /// Create a new [`Config`] with sane default settings.
/// ///
/// The [`DbConfig::db_directory`] /// 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`]. /// All other values will be [`Default::default`].
/// ///
@ -197,25 +209,21 @@ impl Config {
/// DATABASE_DATA_FILENAME, /// DATABASE_DATA_FILENAME,
/// }; /// };
/// use cuprate_database_service::ReaderThreads; /// use cuprate_database_service::ReaderThreads;
/// use cuprate_helper::fs::*; /// use cuprate_helper::{fs::*, network::Network};
/// ///
/// use cuprate_txpool::Config; /// use cuprate_txpool::Config;
/// ///
/// let config = Config::new(); /// let config = Config::new();
/// ///
/// assert_eq!(config.db_config.db_directory(), &*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_TXPOOL_DIR)); /// assert!(config.db_config.db_file().starts_with(&*CUPRATE_DATA_DIR));
/// assert!(config.db_config.db_file().ends_with(DATABASE_DATA_FILENAME)); /// 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.sync_mode, SyncMode::default());
/// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default()); /// assert_eq!(config.db_config.resize_algorithm, ResizeAlgorithm::default());
/// assert_eq!(config.reader_threads, ReaderThreads::default()); /// assert_eq!(config.reader_threads, ReaderThreads::default());
/// ``` /// ```
pub fn new() -> Self { pub fn new() -> Self {
Self { ConfigBuilder::new().build()
db_config: DbConfig::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)),
reader_threads: ReaderThreads::default(),
max_txpool_weight: 0,
}
} }
} }

View file

@ -51,7 +51,7 @@
//! let tmp_dir = tempfile::tempdir()?; //! let tmp_dir = tempfile::tempdir()?;
//! let db_dir = tmp_dir.path().to_owned(); //! let db_dir = tmp_dir.path().to_owned();
//! let config = ConfigBuilder::new() //! let config = ConfigBuilder::new()
//! .db_directory(db_dir.into()) //! .data_directory(db_dir.into())
//! .build(); //! .build();
//! //!
//! // Initialize the database environment. //! // Initialize the database environment.

View file

@ -83,7 +83,7 @@
//! let tmp_dir = tempfile::tempdir()?; //! let tmp_dir = tempfile::tempdir()?;
//! let db_dir = tmp_dir.path().to_owned(); //! let db_dir = tmp_dir.path().to_owned();
//! let config = ConfigBuilder::new() //! let config = ConfigBuilder::new()
//! .db_directory(db_dir.into()) //! .data_directory(db_dir.into())
//! .build(); //! .build();
//! //!
//! // Initialize the database thread-pool. //! // Initialize the database thread-pool.