diff --git a/Cargo.lock b/Cargo.lock index 38544ca2f..9b8d40c3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5779,6 +5779,7 @@ dependencies = [ "humantime", "indexmap 2.0.1", "insta", + "itertools 0.11.0", "lazy_static", "once_cell", "owo-colors", diff --git a/README.md b/README.md index 48476db85..52cec8703 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - [Getting Started](#getting-started) - [Docker](#docker) - [Building Zebra](#building-zebra) - - [Optional Features](#optional-features) + - [Optional Configs & Features](#optional-features) - [Known Issues](#known-issues) - [Future Work](#future-work) - [Documentation](#documentation) @@ -116,13 +116,24 @@ zebrad start See the [Installing Zebra](https://zebra.zfnd.org/user/install.html) and [Running Zebra](https://zebra.zfnd.org/user/run.html) sections in the book for more details. -#### Optional Features +#### Optional Configs & Features + +##### Configuring Progress Bars + +Configure `tracing.progress_bar` in your `zebrad.toml` to +[show key metrics in the terminal using progress bars](https://zfnd.org/experimental-zebra-progress-bars/). +When progress bars are active, Zebra automatically sends logs to a file. + +In future releases, the `progress_bar = "summary"` config will show a few key metrics, +and the "detailed" config will show all available metrics. Please let us know which metrics are +important to you! + +##### Custom Build Features You can also build Zebra with additional [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options): - `getblocktemplate-rpcs` for [mining support](https://zebra.zfnd.org/user/mining.html) - `prometheus` for [Prometheus metrics](https://zebra.zfnd.org/user/metrics.html) -- `progress-bar` [experimental progress bars](https://zfnd.org/experimental-zebra-progress-bars/) - `sentry` for [Sentry monitoring](https://zebra.zfnd.org/user/tracing.html#sentry-production-monitoring) - `elasticsearch` for [experimental Elasticsearch support](https://zebra.zfnd.org/user/elasticsearch.html) diff --git a/zebra-test/Cargo.toml b/zebra-test/Cargo.toml index ceb6a5b4e..3417e7726 100644 --- a/zebra-test/Cargo.toml +++ b/zebra-test/Cargo.toml @@ -19,6 +19,7 @@ hex = "0.4.3" indexmap = "2.0.1" lazy_static = "1.4.0" insta = "1.33.0" +itertools = "0.11.0" proptest = "1.3.1" once_cell = "1.18.0" rand = "0.8.5" diff --git a/zebra-test/src/command.rs b/zebra-test/src/command.rs index 75f45fc6d..d65d43830 100644 --- a/zebra-test/src/command.rs +++ b/zebra-test/src/command.rs @@ -21,10 +21,11 @@ use tracing::instrument; #[macro_use] mod arguments; + pub mod to_regex; pub use self::arguments::Arguments; -use self::to_regex::{CollectRegexSet, ToRegex, ToRegexSet}; +use self::to_regex::{CollectRegexSet, ToRegexSet}; /// A super-trait for [`Iterator`] + [`Debug`]. pub trait IteratorDebug: Iterator + Debug {} @@ -791,7 +792,7 @@ impl TestChild { #[allow(clippy::unwrap_in_result)] pub fn expect_stdout_line_matches(&mut self, success_regex: R) -> Result where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { self.apply_failure_regexes_to_outputs(); @@ -823,7 +824,7 @@ impl TestChild { #[allow(clippy::unwrap_in_result)] pub fn expect_stderr_line_matches(&mut self, success_regex: R) -> Result where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { self.apply_failure_regexes_to_outputs(); @@ -855,7 +856,7 @@ impl TestChild { #[allow(clippy::unwrap_in_result)] pub fn expect_stdout_line_matches_silent(&mut self, success_regex: R) -> Result where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { self.apply_failure_regexes_to_outputs(); @@ -887,7 +888,7 @@ impl TestChild { #[allow(clippy::unwrap_in_result)] pub fn expect_stderr_line_matches_silent(&mut self, success_regex: R) -> Result where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { self.apply_failure_regexes_to_outputs(); @@ -1246,9 +1247,9 @@ impl TestOutput { #[allow(clippy::unwrap_in_result)] pub fn stdout_matches(&self, regex: R) -> Result<&Self> where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { - let re = regex.to_regex().expect("regex must be valid"); + let re = regex.to_regex_set().expect("regex must be valid"); self.output_check( |stdout| re.is_match(stdout), @@ -1270,9 +1271,9 @@ impl TestOutput { #[allow(clippy::unwrap_in_result)] pub fn stdout_line_matches(&self, regex: R) -> Result<&Self> where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { - let re = regex.to_regex().expect("regex must be valid"); + let re = regex.to_regex_set().expect("regex must be valid"); self.any_output_line( |line| re.is_match(line), @@ -1300,9 +1301,9 @@ impl TestOutput { #[allow(clippy::unwrap_in_result)] pub fn stderr_matches(&self, regex: R) -> Result<&Self> where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { - let re = regex.to_regex().expect("regex must be valid"); + let re = regex.to_regex_set().expect("regex must be valid"); self.output_check( |stderr| re.is_match(stderr), @@ -1324,9 +1325,9 @@ impl TestOutput { #[allow(clippy::unwrap_in_result)] pub fn stderr_line_matches(&self, regex: R) -> Result<&Self> where - R: ToRegex + Debug, + R: ToRegexSet + Debug, { - let re = regex.to_regex().expect("regex must be valid"); + let re = regex.to_regex_set().expect("regex must be valid"); self.any_output_line( |line| re.is_match(line), diff --git a/zebra-test/src/command/to_regex.rs b/zebra-test/src/command/to_regex.rs index 04979ce6d..66e00c874 100644 --- a/zebra-test/src/command/to_regex.rs +++ b/zebra-test/src/command/to_regex.rs @@ -2,6 +2,7 @@ use std::iter; +use itertools::Itertools; use regex::{Error, Regex, RegexBuilder, RegexSet, RegexSetBuilder}; /// A trait for converting a value to a [`Regex`]. @@ -135,15 +136,17 @@ pub trait CollectRegexSet { impl CollectRegexSet for I where I: IntoIterator, - I::Item: ToRegex, + I::Item: ToRegexSet, { fn collect_regex_set(self) -> Result { - let regexes: Result, Error> = - self.into_iter().map(|item| item.to_regex()).collect(); + let regexes: Result, Error> = self + .into_iter() + .map(|item| item.to_regex_set()) + .try_collect(); let regexes = regexes?; // This conversion discards flags and limits from Regex and RegexBuilder. - let regexes = regexes.iter().map(|regex| regex.as_str()); + let regexes = regexes.iter().flat_map(|regex_set| regex_set.patterns()); RegexSet::new(regexes) } diff --git a/zebrad/Cargo.toml b/zebrad/Cargo.toml index 646c61e28..ff38f3f5b 100644 --- a/zebrad/Cargo.toml +++ b/zebrad/Cargo.toml @@ -52,7 +52,7 @@ features = [ [features] # In release builds, don't compile debug logging code, to improve performance. -default = ["release_max_level_info"] +default = ["release_max_level_info", "progress-bar"] # Default features for official ZF binary release builds default-release-binaries = ["default", "sentry"] diff --git a/zebrad/src/application.rs b/zebrad/src/application.rs index 133465ffa..d8df9d3f0 100644 --- a/zebrad/src/application.rs +++ b/zebrad/src/application.rs @@ -434,6 +434,7 @@ impl Application for ZebradApp { // Override the default tracing filter based on the command-line verbosity. tracing_config.filter = tracing_config .filter + .clone() .or_else(|| Some(default_filter.to_owned())); } else { // Don't apply the configured filter for short-lived commands. diff --git a/zebrad/src/components/tracing.rs b/zebrad/src/components/tracing.rs index 439c5052e..6b3560045 100644 --- a/zebrad/src/components/tracing.rs +++ b/zebrad/src/components/tracing.rs @@ -1,6 +1,10 @@ //! Tracing and logging infrastructure for Zebra. -use std::{net::SocketAddr, path::PathBuf}; +use std::{ + net::SocketAddr, + ops::{Deref, DerefMut}, + path::PathBuf, +}; use serde::{Deserialize, Serialize}; @@ -16,10 +20,59 @@ pub use endpoint::TracingEndpoint; #[cfg(feature = "flamegraph")] pub use flame::{layer, Grapher}; -/// Tracing configuration section. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields, default)] +/// Tracing configuration section: outer config after cross-field defaults are applied. +/// +/// This is a wrapper type that dereferences to the inner config type. +/// +// +// TODO: replace with serde's finalizer attribute when that feature is implemented. +// we currently use the recommended workaround of a wrapper struct with from/into attributes. +// https://github.com/serde-rs/serde/issues/642#issuecomment-525432907 +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[serde( + deny_unknown_fields, + default, + from = "InnerConfig", + into = "InnerConfig" +)] pub struct Config { + inner: InnerConfig, +} + +impl Deref for Config { + type Target = InnerConfig; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Config { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl From for Config { + fn from(mut inner: InnerConfig) -> Self { + inner.log_file = runtime_default_log_file(inner.log_file, inner.progress_bar); + + Self { inner } + } +} + +impl From for InnerConfig { + fn from(mut config: Config) -> Self { + config.log_file = disk_default_log_file(config.log_file.clone(), config.progress_bar); + + config.inner + } +} + +/// Tracing configuration section: inner config used to deserialize and apply cross-field defaults. +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[serde(deny_unknown_fields, default)] +pub struct InnerConfig { /// Whether to use colored terminal output, if available. /// /// Colored terminal output is automatically disabled if an output stream @@ -108,10 +161,16 @@ pub struct Config { /// replaced with `.folded` and `.svg` for the respective files. pub flamegraph: Option, + /// Shows progress bars for block syncing, and mempool transactions, and peer networking. + /// Also sends logs to the default log file path. + /// + /// This config field is ignored unless the `progress-bar` feature is enabled. + pub progress_bar: Option, + /// If set to a path, write the tracing logs to that path. /// /// By default, logs are sent to the terminal standard output. - /// But if the `progress-bar` feature is activated, logs are sent to the standard log file path: + /// But if the `progress_bar` config is activated, logs are sent to the standard log file path: /// - Linux: `$XDG_STATE_HOME/zebrad.log` or `$HOME/.local/state/zebrad.log` /// - macOS: `$HOME/Library/Application Support/zebrad.log` /// - Windows: `%LOCALAPPDATA%\zebrad.log` or `C:\Users\%USERNAME%\AppData\Local\zebrad.log` @@ -131,6 +190,21 @@ pub struct Config { pub use_journald: bool, } +/// The progress bars that Zebra will show while running. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum ProgressConfig { + /// Show a lot of progress bars. + Detailed, + + /// Show a few important progress bars. + // + // TODO: actually hide some progress bars in this mode. + #[default] + #[serde(other)] + Summary, +} + impl Config { /// Returns `true` if standard output should use color escapes. /// Automatically checks if Zebra is running in a terminal. @@ -152,12 +226,10 @@ impl Config { } } -impl Default for Config { +impl Default for InnerConfig { fn default() -> Self { - #[cfg(feature = "progress-bar")] - let default_log_file = dirs::state_dir() - .or_else(dirs::data_local_dir) - .map(|dir| dir.join("zebrad.log")); + // TODO: enable progress bars by default once they have been tested + let progress_bar = None; Self { use_color: true, @@ -166,11 +238,50 @@ impl Default for Config { buffer_limit: 128_000, endpoint_addr: None, flamegraph: None, - #[cfg(not(feature = "progress-bar"))] - log_file: None, - #[cfg(feature = "progress-bar")] - log_file: default_log_file, + progress_bar, + log_file: runtime_default_log_file(None, progress_bar), use_journald: false, } } } + +/// Returns the runtime default log file path based on the `log_file` and `progress_bar` configs. +fn runtime_default_log_file( + log_file: Option, + progress_bar: Option, +) -> Option { + if let Some(log_file) = log_file { + return Some(log_file); + } + + // If the progress bar is active, we want to use a log file regardless of the config. + // (Logging to a terminal erases parts of the progress bars, making both unreadable.) + if progress_bar.is_some() { + return default_log_file(); + } + + None +} + +/// Returns the configured log file path using the runtime `log_file` and `progress_bar` config. +/// +/// This is the inverse of [`runtime_default_log_file()`]. +fn disk_default_log_file( + log_file: Option, + progress_bar: Option, +) -> Option { + // If the progress bar is active, and we've likely substituted the default log file path, + // don't write that substitute to the config on disk. + if progress_bar.is_some() && log_file == default_log_file() { + return None; + } + + log_file +} + +/// Returns the default log file path. +fn default_log_file() -> Option { + dirs::state_dir() + .or_else(dirs::data_local_dir) + .map(|dir| dir.join("zebrad.log")) +} diff --git a/zebrad/src/components/tracing/component.rs b/zebrad/src/components/tracing/component.rs index 745315ae1..1129cc64b 100644 --- a/zebrad/src/components/tracing/component.rs +++ b/zebrad/src/components/tracing/component.rs @@ -79,14 +79,14 @@ impl Tracing { /// and the Zebra logo on startup. (If the terminal supports it.) // // This method should only print to stderr, because stdout is for tracing logs. - #[allow(clippy::print_stderr, clippy::unwrap_in_result)] + #[allow(clippy::print_stdout, clippy::print_stderr, clippy::unwrap_in_result)] pub fn new(network: Network, config: Config, uses_intro: bool) -> Result { // Only use color if tracing output is being sent to a terminal or if it was explicitly // forced to. let use_color = config.use_color_stdout(); let use_color_stderr = config.use_color_stderr(); - let filter = config.filter.unwrap_or_default(); + let filter = config.filter.clone().unwrap_or_default(); let flame_root = &config.flamegraph; // Only show the intro for user-focused node server commands like `start` @@ -139,7 +139,8 @@ impl Tracing { } if uses_intro { - eprintln!("Sending logs to {log_file:?}..."); + // We want this to appear on stdout instead of the usual log messages. + println!("Sending logs to {log_file:?}..."); } let log_file = File::options().append(true).create(true).open(log_file)?; Box::new(log_file) as BoxWrite @@ -305,7 +306,7 @@ impl Tracing { // // TODO: move this to its own module? #[cfg(feature = "progress-bar")] - { + if let Some(progress_bar_config) = config.progress_bar.as_ref() { use howudoin::consumers::TermLine; use std::time::Duration; @@ -315,7 +316,11 @@ impl Tracing { let terminal_consumer = TermLine::with_debounce(PROGRESS_BAR_DEBOUNCE); howudoin::init(terminal_consumer); - info!("activated progress bar"); + info!(?progress_bar_config, "activated progress bars"); + } else { + info!( + "set 'tracing.progress_bar =\"summary\"' in zebrad.toml to activate progress bars" + ); } Ok(Self { diff --git a/zebrad/src/lib.rs b/zebrad/src/lib.rs index 1995e8f3e..9ebe9b3ff 100644 --- a/zebrad/src/lib.rs +++ b/zebrad/src/lib.rs @@ -61,9 +61,10 @@ //! //! ### Metrics //! -//! * `prometheus`: export metrics to prometheus. -//! * `progress-bar`: shows key metrics in the terminal using progress bars, +//! * configuring a `tracing.progress_bar`: shows key metrics in the terminal using progress bars, //! and automatically configures Zebra to send logs to a file. +//! (The `progress-bar` feature is activated by default.) +//! * `prometheus`: export metrics to prometheus. //! //! Read the [metrics](https://zebra.zfnd.org/user/metrics.html) section of the book //! for more details. diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 15611ee74..c4fbdc707 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -161,7 +161,12 @@ use zebra_network::constants::PORT_IN_USE_ERROR; use zebra_node_services::rpc_client::RpcRequestClient; use zebra_state::{constants::LOCK_FILE_ERROR, database_format_version_in_code}; -use zebra_test::{args, command::ContextFrom, net::random_known_port, prelude::*}; +use zebra_test::{ + args, + command::{to_regex::CollectRegexSet, ContextFrom}, + net::random_known_port, + prelude::*, +}; mod common; @@ -605,7 +610,7 @@ fn config_tests() -> Result<()> { // Check that Zebra's previous configurations still work stored_configs_work()?; - // Runs `zebrad` serially to avoid potential port conflicts + // We run the `zebrad` app test after the config tests, to avoid potential port conflicts app_no_args()?; Ok(()) @@ -619,6 +624,8 @@ fn app_no_args() -> Result<()> { // start caches state, so run one of the start tests with persistent state let testdir = testdir()?.with_config(&mut persistent_test_config()?)?; + tracing::info!(?testdir, "running zebrad with no config (default settings)"); + let mut child = testdir.spawn_child(args![])?; // Run the program and kill it after a few seconds @@ -651,6 +658,8 @@ fn valid_generated_config(command: &str, expect_stdout_line_contains: &str) -> R // Add a config file name to tempdir path let generated_config_path = testdir.path().join("zebrad.toml"); + tracing::info!(?generated_config_path, "generating valid config"); + // Generate configuration in temp dir path let child = testdir.spawn_child(args!["generate", "-o": generated_config_path.to_str().unwrap()])?; @@ -664,6 +673,8 @@ fn valid_generated_config(command: &str, expect_stdout_line_contains: &str) -> R "generated config file not found" ); + tracing::info!(?generated_config_path, "testing valid config parsing"); + // Run command using temp dir and kill it after a few seconds let mut child = testdir.spawn_child(args![command])?; std::thread::sleep(LAUNCH_DELAY); @@ -702,6 +713,8 @@ fn last_config_is_stored() -> Result<()> { // Add a config file name to tempdir path let generated_config_path = testdir.path().join("zebrad.toml"); + tracing::info!(?generated_config_path, "generated current config"); + // Generate configuration in temp dir path let child = testdir.spawn_child(args!["generate", "-o": generated_config_path.to_str().unwrap()])?; @@ -715,6 +728,11 @@ fn last_config_is_stored() -> Result<()> { "generated config file not found" ); + tracing::info!( + ?generated_config_path, + "testing current config is in stored configs" + ); + // Get the contents of the generated config file let generated_content = fs::read_to_string(generated_config_path).expect("Should have been able to read the file"); @@ -815,6 +833,11 @@ fn invalid_generated_config() -> Result<()> { // Add a config file name to tempdir path. let config_path = testdir.path().join("zebrad.toml"); + tracing::info!( + ?config_path, + "testing invalid config parsing: generating valid config" + ); + // Generate a valid config file in the temp dir. let child = testdir.spawn_child(args!["generate", "-o": config_path.to_str().unwrap()])?; @@ -849,10 +872,14 @@ fn invalid_generated_config() -> Result<()> { secs = 3600 "; + tracing::info!(?config_path, "writing invalid config"); + // Write the altered config file so that Zebra can pick it up. fs::write(config_path.to_str().unwrap(), config_file.as_bytes()) .expect("Could not write the altered config file."); + tracing::info!(?config_path, "testing invalid config parsing"); + // Run Zebra in a temp dir so that it loads the config. let mut child = testdir.spawn_child(args!["start"])?; @@ -883,6 +910,8 @@ fn invalid_generated_config() -> Result<()> { fn stored_configs_work() -> Result<()> { let old_configs_dir = configs_dir(); + tracing::info!(?old_configs_dir, "testing older config parsing"); + for config_file in old_configs_dir .read_dir() .expect("read_dir call failed") @@ -892,10 +921,10 @@ fn stored_configs_work() -> Result<()> { let config_file_name = config_file_path .file_name() .expect("config files must have a file name") - .to_string_lossy(); + .to_str() + .expect("config file names are valid unicode"); - if config_file_name.as_ref().starts_with('.') || config_file_name.as_ref().starts_with('#') - { + if config_file_name.starts_with('.') || config_file_name.starts_with('#') { // Skip editor files and other invalid config paths tracing::info!( ?config_file_path, @@ -907,10 +936,7 @@ fn stored_configs_work() -> Result<()> { // ignore files starting with getblocktemplate prefix // if we were not built with the getblocktemplate-rpcs feature. #[cfg(not(feature = "getblocktemplate-rpcs"))] - if config_file_name - .as_ref() - .starts_with(GET_BLOCK_TEMPLATE_CONFIG_PREFIX) - { + if config_file_name.starts_with(GET_BLOCK_TEMPLATE_CONFIG_PREFIX) { tracing::info!( ?config_file_path, "skipping getblocktemplate-rpcs config file path" @@ -921,12 +947,41 @@ fn stored_configs_work() -> Result<()> { let run_dir = testdir()?; let stored_config_path = config_file_full_path(config_file.path()); + tracing::info!( + ?stored_config_path, + "testing old config can be parsed by current zebrad" + ); + // run zebra with stored config let mut child = run_dir.spawn_child(args!["-c", stored_config_path.to_str().unwrap(), "start"])?; - // zebra was able to start with the stored config - child.expect_stdout_line_matches("Starting zebrad".to_string())?; + let success_regexes = [ + // When logs are sent to the terminal, we see the config loading message and path. + format!( + "loaded zebrad config.*config_path.*=.*{}", + regex::escape(config_file_name) + ), + // If they are sent to a file, we see a log file message on stdout, + // and a logo, welcome message, and progress bar on stderr. + "Sending logs to".to_string(), + // TODO: add expect_stdout_or_stderr_line_matches() and check for this instead: + //"Thank you for running a mainnet zebrad".to_string(), + ]; + + tracing::info!( + ?stored_config_path, + ?success_regexes, + "waiting for zebrad to parse config and start logging" + ); + + let success_regexes = success_regexes + .iter() + .collect_regex_set() + .expect("regexes are valid"); + + // Zebra was able to start with the stored config. + child.expect_stdout_line_matches(success_regexes)?; // finish child.kill(false)?; diff --git a/zebrad/tests/common/config.rs b/zebrad/tests/common/config.rs index 884f205db..b6eead94f 100644 --- a/zebrad/tests/common/config.rs +++ b/zebrad/tests/common/config.rs @@ -58,10 +58,9 @@ pub fn default_test_config() -> Result { env::var("ZEBRA_FORCE_USE_COLOR"), Err(env::VarError::NotPresent) ); - let tracing = tracing::Config { - force_use_color, - ..tracing::Config::default() - }; + + let mut tracing = tracing::Config::default(); + tracing.force_use_color = force_use_color; let mut state = zebra_state::Config::ephemeral(); state.debug_validity_check_interval = Some(DATABASE_FORMAT_CHECK_INTERVAL); diff --git a/zebrad/tests/common/configs/v1.3.0-progress-bars.toml b/zebrad/tests/common/configs/v1.3.0-progress-bars.toml new file mode 100644 index 000000000..82da5039f --- /dev/null +++ b/zebrad/tests/common/configs/v1.3.0-progress-bars.toml @@ -0,0 +1,73 @@ +# Default configuration for zebrad. +# +# This file can be used as a skeleton for custom configs. +# +# Unspecified fields use default values. Optional fields are Some(field) if the +# field is present and None if it is absent. +# +# This file is generated as an example using zebrad's current defaults. +# You should set only the config options you want to keep, and delete the rest. +# Only a subset of fields are present in the skeleton, since optional values +# whose default is None are omitted. +# +# The config format (including a complete list of sections and fields) is +# documented here: +# https://doc.zebra.zfnd.org/zebrad/config/struct.ZebradConfig.html +# +# zebrad attempts to load configs in the following order: +# +# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`; +# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent); +# 3. The default config. + +[consensus] +checkpoint_sync = true +debug_skip_parameter_preload = false + +[mempool] +eviction_memory_time = "1h" +tx_cost_limit = 80000000 + +[metrics] + +[network] +cache_dir = true +crawl_new_peer_interval = "1m 1s" +initial_mainnet_peers = [ + "dnsseed.z.cash:8233", + "dnsseed.str4d.xyz:8233", + "mainnet.seeder.zfnd.org:8233", + "mainnet.is.yolo.money:8233", +] +initial_testnet_peers = [ + "dnsseed.testnet.z.cash:18233", + "testnet.seeder.zfnd.org:18233", + "testnet.is.yolo.money:18233", +] +listen_addr = "0.0.0.0:8233" +max_connections_per_ip = 1 +network = "Mainnet" +peerset_initial_target_size = 25 + +[rpc] +debug_force_finished_sync = false +parallel_cpu_threads = 1 + +[state] +cache_dir = "cache_dir" +delete_old_database = true +ephemeral = false + +[sync] +checkpoint_verify_concurrency_limit = 1000 +download_concurrency_limit = 50 +full_verify_concurrency_limit = 20 +parallel_cpu_threads = 0 + +[tracing] +buffer_limit = 128000 +force_use_color = false +progress_bar = "summary" +use_color = true +use_journald = false +