diff --git a/Cargo.lock b/Cargo.lock index 2b0a50c68b..12c836ef05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3564,6 +3564,7 @@ dependencies = [ "rustyline", "rustyline-derive", "serde", + "serde_json", "strum", "tari_common", "tari_common_types", @@ -6557,6 +6558,7 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", + "toml 0.5.11", "tower", "trust-dns-client", "webpki", diff --git a/applications/minotari_app_utilities/src/common_cli_args.rs b/applications/minotari_app_utilities/src/common_cli_args.rs index d440bf65ce..b88cc1e97b 100644 --- a/applications/minotari_app_utilities/src/common_cli_args.rs +++ b/applications/minotari_app_utilities/src/common_cli_args.rs @@ -50,7 +50,12 @@ pub struct CommonCliArgs { #[clap(long, env = "TARI_NETWORK")] pub network: Option, - /// Overrides for properties in the config file, e.g. -p base_node.network=esmeralda + /// Overrides for properties in the config file (use the fully qualified key name!!), e.g. + /// -p base_node.network=esmeralda + /// -p base_node.grpc_server_allow_methods="get_tokens_in_circulation, get_sync_progress, get_mempool_stats" + /// -p esmeralda.p2p.seeds.peer_seeds="::", "::", or, + /// -p base_node.grpc_server_allow_methods="" + /// -p esmeralda.p2p.seeds.peer_seeds="" #[clap(short = 'p', parse(try_from_str = parse_key_val), multiple_occurrences(true))] pub config_property_overrides: Vec<(String, String)>, } @@ -105,7 +110,7 @@ impl CommonCliArgs { } impl ConfigOverrideProvider for CommonCliArgs { - fn get_config_property_overrides(&self, _network: &mut Network) -> Vec<(String, String)> { + fn get_config_property_overrides(&self, _network: &Network) -> Vec<(String, String)> { let mut overrides = self.config_property_overrides.clone(); overrides.push(( "common.base_path".to_string(), diff --git a/applications/minotari_console_wallet/src/cli.rs b/applications/minotari_console_wallet/src/cli.rs index cdf77e336c..6adba55561 100644 --- a/applications/minotari_console_wallet/src/cli.rs +++ b/applications/minotari_console_wallet/src/cli.rs @@ -96,18 +96,24 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(network); - *network = self.common.network.unwrap_or(*network); - overrides.push(("wallet.network".to_string(), network.to_string())); + /// Get the configuration property overrides for the given network. In case of duplicates, the final override + /// added to the list will have preference. + fn get_config_property_overrides(&self, network: &Network) -> Vec<(String, String)> { + // Config file overrides + let mut overrides = vec![("wallet.network".to_string(), network.to_string())]; overrides.push(("wallet.override_from".to_string(), network.to_string())); overrides.push(("p2p.seeds.override_from".to_string(), network.to_string())); - // Either of these configs enable grpc + // Command-line overrides + let command_line_overrides = self.common.get_config_property_overrides(network); + command_line_overrides.iter().for_each(|(k, v)| { + replace_or_add_override(&mut overrides, k, v); + }); + // Logical overrides based on command-line flags - Either of these configs enable grpc if let Some(ref addr) = self.grpc_address { - overrides.push(("wallet.grpc_enabled".to_string(), "true".to_string())); - overrides.push(("wallet.grpc_address".to_string(), addr.clone())); + replace_or_add_override(&mut overrides, "wallet.grpc_enabled", "true"); + replace_or_add_override(&mut overrides, "wallet.grpc_address", addr); } else if self.grpc_enabled { - overrides.push(("wallet.grpc_enabled".to_string(), "true".to_string())); + replace_or_add_override(&mut overrides, "wallet.grpc_enabled", "true"); } else { // GRPC is disabled } @@ -115,6 +121,13 @@ impl ConfigOverrideProvider for Cli { } } +fn replace_or_add_override(overrides: &mut Vec<(String, String)>, key: &str, value: &str) { + if let Some(index) = overrides.iter().position(|(k, _)| k == key) { + overrides.remove(index); + } + overrides.push((key.to_string(), value.to_string())); +} + #[allow(clippy::large_enum_variant)] #[derive(Debug, Subcommand, Clone)] pub enum CliCommands { diff --git a/applications/minotari_console_wallet/src/main.rs b/applications/minotari_console_wallet/src/main.rs index e7fd79457e..853c9aa82a 100644 --- a/applications/minotari_console_wallet/src/main.rs +++ b/applications/minotari_console_wallet/src/main.rs @@ -24,6 +24,7 @@ use std::process; use clap::Parser; use log::*; +use minotari_app_utilities::consts; use minotari_console_wallet::{run_wallet_with_cli, ApplicationConfig, Cli}; use tari_common::{ configuration::bootstrap::{grpc_default_port, ApplicationType}, @@ -69,14 +70,25 @@ fn main() { fn main_inner() -> Result<(), ExitError> { let cli = Cli::parse(); - - let cfg = load_configuration(cli.common.config_path(), true, cli.non_interactive_mode, &cli)?; let base_path = cli.common.get_base_path(); initialize_logging( &cli.common.log_config_path("wallet"), cli.common.log_path.as_ref().unwrap_or(&base_path), include_str!("../log4rs_sample.yml"), )?; + info!( + target: LOG_TARGET, + "Starting Minotari Console Wallet version: {}", + consts::APP_VERSION + ); + + let cfg = load_configuration( + cli.common.config_path(), + true, + cli.non_interactive_mode, + &cli, + cli.common.network, + )?; if cli.profile_with_tokio_console { // Uncomment to enable tokio tracing via tokio-console diff --git a/applications/minotari_merge_mining_proxy/src/cli.rs b/applications/minotari_merge_mining_proxy/src/cli.rs index 14e42769cd..4493321619 100644 --- a/applications/minotari_merge_mining_proxy/src/cli.rs +++ b/applications/minotari_merge_mining_proxy/src/cli.rs @@ -35,11 +35,24 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(network); - *network = self.common.network.unwrap_or(*network); - overrides.push(("merge_mining_proxy.override_from".to_string(), network.to_string())); + /// Get the configuration property overrides for the given network. In case of duplicates, the final override + /// added to the list will have preference. + fn get_config_property_overrides(&self, network: &Network) -> Vec<(String, String)> { + // Config file overrides + let mut overrides = vec![("merge_mining_proxy.override_from".to_string(), network.to_string())]; overrides.push(("merge_mining_proxy.network".to_string(), network.to_string())); + // Command-line overrides + let command_line_overrides = self.common.get_config_property_overrides(network); + command_line_overrides.iter().for_each(|(k, v)| { + replace_or_add_override(&mut overrides, k, v); + }); overrides } } + +fn replace_or_add_override(overrides: &mut Vec<(String, String)>, key: &str, value: &str) { + if let Some(index) = overrides.iter().position(|(k, _)| k == key) { + overrides.remove(index); + } + overrides.push((key.to_string(), value.to_string())); +} diff --git a/applications/minotari_merge_mining_proxy/src/main.rs b/applications/minotari_merge_mining_proxy/src/main.rs index 98f7684afa..b09dc15743 100644 --- a/applications/minotari_merge_mining_proxy/src/main.rs +++ b/applications/minotari_merge_mining_proxy/src/main.rs @@ -59,6 +59,11 @@ async fn main() -> Result<(), anyhow::Error> { cli.common.log_path.as_ref().unwrap_or(&base_path), include_str!("../log4rs_sample.yml"), )?; + info!( + target: LOG_TARGET, + "Starting Minotari Merge Mining Proxy version: {}", + consts::APP_VERSION + ); match run_merge_miner::start_merge_miner(cli).await { Ok(_) => Ok(()), Err(err) => { diff --git a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs index a23a4ca592..f9b14f63e5 100644 --- a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs +++ b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs @@ -54,7 +54,7 @@ const LOG_TARGET: &str = "minotari_mm_proxy::proxy"; pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { let config_path = cli.common.config_path(); - let cfg = load_configuration(&config_path, true, cli.non_interactive_mode, &cli)?; + let cfg = load_configuration(&config_path, true, cli.non_interactive_mode, &cli, cli.common.network)?; let mut config = MergeMiningProxyConfig::load_from(&cfg)?; config.set_base_path(cli.common.get_base_path()); if config.use_dynamic_fail_data { diff --git a/applications/minotari_miner/src/cli.rs b/applications/minotari_miner/src/cli.rs index d8ef4382d0..b568104c6c 100644 --- a/applications/minotari_miner/src/cli.rs +++ b/applications/minotari_miner/src/cli.rs @@ -42,10 +42,23 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(network); - *network = self.common.network.unwrap_or(*network); - overrides.push(("miner.network".to_string(), network.to_string())); + /// Get the configuration property overrides for the given network. In case of duplicates, the final override + /// added to the list will have preference. + fn get_config_property_overrides(&self, network: &Network) -> Vec<(String, String)> { + // Config file overrides + let mut overrides = vec![("miner.network".to_string(), network.to_string())]; + // Command-line overrides + let command_line_overrides = self.common.get_config_property_overrides(network); + command_line_overrides.iter().for_each(|(k, v)| { + replace_or_add_override(&mut overrides, k, v); + }); overrides } } + +fn replace_or_add_override(overrides: &mut Vec<(String, String)>, key: &str, value: &str) { + if let Some(index) = overrides.iter().position(|(k, _)| k == key) { + overrides.remove(index); + } + overrides.push((key.to_string(), value.to_string())); +} diff --git a/applications/minotari_miner/src/main.rs b/applications/minotari_miner/src/main.rs index 80691e1a60..5de3bad3b0 100644 --- a/applications/minotari_miner/src/main.rs +++ b/applications/minotari_miner/src/main.rs @@ -67,5 +67,10 @@ async fn main_inner() -> Result<(), ExitError> { &cli.common.get_base_path(), include_str!("../log4rs_sample.yml"), )?; + info!( + target: LOG_TARGET, + "Starting Minotari Miner version: {}", + consts::APP_VERSION + ); start_miner(cli).await } diff --git a/applications/minotari_miner/src/run_miner.rs b/applications/minotari_miner/src/run_miner.rs index 39a0e07ae7..72742b6896 100644 --- a/applications/minotari_miner/src/run_miner.rs +++ b/applications/minotari_miner/src/run_miner.rs @@ -81,7 +81,13 @@ pub const LOG_TARGET_FILE: &str = "minotari::logging::miner::main"; #[allow(clippy::too_many_lines)] pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { let config_path = cli.common.config_path(); - let cfg = load_configuration(config_path.as_path(), true, cli.non_interactive_mode, &cli)?; + let cfg = load_configuration( + config_path.as_path(), + true, + cli.non_interactive_mode, + &cli, + cli.common.network, + )?; let mut config = MinerConfig::load_from(&cfg).expect("Failed to load config"); config.set_base_path(cli.common.get_base_path()); diff --git a/applications/minotari_node/Cargo.toml b/applications/minotari_node/Cargo.toml index a34eef954b..85bc543c08 100644 --- a/applications/minotari_node/Cargo.toml +++ b/applications/minotari_node/Cargo.toml @@ -45,7 +45,8 @@ log4rs = { version = "1.3.0", default-features = false, features = ["config_pars nom = "7.1" rustyline = "9.0" rustyline-derive = "0.5" -serde = "1.0.136" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" strum = { version = "0.22", features = ["derive"] } thiserror = "^1.0.26" tokio = { version = "1.36", features = ["signal"] } @@ -71,3 +72,4 @@ ignored = [ [dev-dependencies] toml = { version = "0.5" } +serde_json = "1.0.108" diff --git a/applications/minotari_node/src/cli.rs b/applications/minotari_node/src/cli.rs index f4eb4da533..3105dd4574 100644 --- a/applications/minotari_node/src/cli.rs +++ b/applications/minotari_node/src/cli.rs @@ -54,25 +54,39 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(network); - *network = self.common.network.unwrap_or(*network); - overrides.push(("base_node.network".to_string(), network.to_string())); + /// Get the configuration property overrides for the given network. In case of duplicates, the final override + /// added to the list will have preference. + fn get_config_property_overrides(&self, network: &Network) -> Vec<(String, String)> { + // Config file overrides + let mut overrides = vec![("base_node.network".to_string(), network.to_string())]; overrides.push(("base_node.override_from".to_string(), network.to_string())); overrides.push(("p2p.seeds.override_from".to_string(), network.to_string())); overrides.push(("auto_update.override_from".to_string(), network.to_string())); overrides.push(("metrics.override_from".to_string(), network.to_string())); + // Command-line overrides + let command_line_overrides = self.common.get_config_property_overrides(network); + command_line_overrides.iter().for_each(|(k, v)| { + replace_or_add_override(&mut overrides, k, v); + }); + // Logical overrides based on command-line flags if self.grpc_enabled { - overrides.push(("base_node.grpc_enabled".to_string(), "true".to_string())); + replace_or_add_override(&mut overrides, "base_node.grpc_enabled", "true"); } if self.mining_enabled { - overrides.push(("base_node.grpc_enabled".to_string(), "true".to_string())); - overrides.push(("base_node.mining_enabled".to_string(), "true".to_string())); + replace_or_add_override(&mut overrides, "base_node.grpc_enabled", "true"); + replace_or_add_override(&mut overrides, "base_node.mining_enabled", "true"); } if self.second_layer_grpc_enabled { - overrides.push(("base_node.grpc_enabled".to_string(), "true".to_string())); - overrides.push(("base_node.second_layer_grpc_enabled".to_string(), "true".to_string())); + replace_or_add_override(&mut overrides, "base_node.grpc_enabled", "true"); + replace_or_add_override(&mut overrides, "base_node.second_layer_grpc_enabled", "true"); } overrides } } + +fn replace_or_add_override(overrides: &mut Vec<(String, String)>, key: &str, value: &str) { + if let Some(index) = overrides.iter().position(|(k, _)| k == key) { + overrides.remove(index); + } + overrides.push((key.to_string(), value.to_string())); +} diff --git a/applications/minotari_node/src/config.rs b/applications/minotari_node/src/config.rs index ad0fe955ec..01de596230 100644 --- a/applications/minotari_node/src/config.rs +++ b/applications/minotari_node/src/config.rs @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::{ - fmt, path::{Path, PathBuf}, time::Duration, }; @@ -29,7 +28,7 @@ use std::{ use config::Config; use serde::{Deserialize, Serialize}; use tari_common::{ - configuration::{serializers, CommonConfig, Network, StringList}, + configuration::{serializers, CommonConfig, ConfigList, Network, StringList}, ConfigurationError, DefaultConfigLoader, SubConfigPath, @@ -44,6 +43,7 @@ use tari_core::{ use tari_p2p::{auto_update::AutoUpdateConfig, P2pConfig, PeerSeedsConfig}; use tari_storage::lmdb_store::LMDBConfig; +use crate::grpc_method::GrpcMethod; #[cfg(feature = "metrics")] use crate::metrics::MetricsConfig; @@ -88,8 +88,8 @@ pub struct BaseNodeConfig { pub grpc_enabled: bool, /// GRPC address of base node pub grpc_address: Option, - /// GRPC server config - which methods are active and which not - pub grpc_server_allow_methods: Vec, + /// GRPC server config - which methods are active and which not, only active when `grpc_enabled = true`. + pub grpc_server_allow_methods: ConfigList, /// GRPC authentication mode pub grpc_authentication: GrpcAuthentication, /// GRPC tls enabled @@ -155,7 +155,7 @@ impl Default for BaseNodeConfig { network: Network::default(), grpc_enabled: true, grpc_address: None, - grpc_server_allow_methods: vec![GrpcMethod::GetVersion], + grpc_server_allow_methods: vec![GrpcMethod::GetVersion].into(), grpc_authentication: GrpcAuthentication::default(), grpc_tls_enabled: false, mining_enabled: false, @@ -216,93 +216,3 @@ impl BaseNodeConfig { pub enum DatabaseType { Lmdb, } - -/// A list of all the GRPC methods that can be enabled/disabled -#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum GrpcMethod { - ListHeaders, - GetHeaderByHash, - GetBlocks, - GetBlockTiming, - GetConstants, - GetBlockSize, - GetBlockFees, - GetVersion, - CheckForUpdates, - GetTokensInCirculation, - GetNetworkDifficulty, - GetNewBlockTemplate, - GetNewBlock, - GetNewBlockWithCoinbases, - GetNewBlockTemplateWithCoinbases, - GetNewBlockBlob, - SubmitBlock, - SubmitBlockBlob, - SubmitTransaction, - GetSyncInfo, - GetSyncProgress, - GetTipInfo, - SearchKernels, - SearchUtxos, - FetchMatchingUtxos, - GetPeers, - GetMempoolTransactions, - TransactionState, - Identify, - GetNetworkStatus, - ListConnectedPeers, - GetMempoolStats, - GetActiveValidatorNodes, - GetShardKey, - GetTemplateRegistrations, - GetSideChainUtxos, -} - -impl fmt::Display for GrpcMethod { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -#[cfg(test)] -mod tests { - use serde::{Deserialize, Serialize}; - - use crate::config::GrpcMethod; - - #[derive(Clone, Serialize, Deserialize, Debug)] - #[allow(clippy::struct_excessive_bools)] - struct TestConfig { - name: String, - inner_config: TestInnerConfig, - } - - #[derive(Clone, Serialize, Deserialize, Debug)] - #[allow(clippy::struct_excessive_bools)] - struct TestInnerConfig { - allow_methods: Vec, - } - - #[test] - fn it_deserializes_enums() { - let config_str = r#" - name = "blockchain champion" - inner_config.allow_methods = [ - "list_headers", - "get_constants", - # "get_blocks" - "identify", - # "get_shard_key" - ] - "#; - let config = toml::from_str::(config_str).unwrap(); - - // Enums in the config - assert!(config.inner_config.allow_methods.contains(&GrpcMethod::ListHeaders)); - assert!(config.inner_config.allow_methods.contains(&GrpcMethod::GetConstants)); - assert!(!config.inner_config.allow_methods.contains(&GrpcMethod::GetBlocks)); // commented out in the config - assert!(config.inner_config.allow_methods.contains(&GrpcMethod::Identify)); - assert!(!config.inner_config.allow_methods.contains(&GrpcMethod::GetShardKey)); // commented out in the config - } -} diff --git a/applications/minotari_node/src/grpc/base_node_grpc_server.rs b/applications/minotari_node/src/grpc/base_node_grpc_server.rs index d6245767f6..f831a02e46 100644 --- a/applications/minotari_node/src/grpc/base_node_grpc_server.rs +++ b/applications/minotari_node/src/grpc/base_node_grpc_server.rs @@ -76,12 +76,12 @@ use tonic::{Request, Response, Status}; use crate::{ builder::BaseNodeContext, - config::GrpcMethod, grpc::{ blocks::{block_fees, block_heights, block_size, GET_BLOCKS_MAX_HEIGHTS, GET_BLOCKS_PAGE_SIZE}, hash_rate::HashRateMovingAverage, helpers::{mean, median}, }, + grpc_method::GrpcMethod, BaseNodeConfig, }; @@ -170,7 +170,7 @@ impl BaseNodeGrpcServer { if self.config.second_layer_grpc_enabled && second_layer_methods.contains(&grpc_method) { return true; } - self.config.grpc_server_allow_methods.contains(&grpc_method) + self.config.grpc_server_allow_methods.to_vec().contains(&grpc_method) } fn check_method_enabled(&self, method: GrpcMethod) -> Result<(), Status> { diff --git a/applications/minotari_node/src/grpc_method.rs b/applications/minotari_node/src/grpc_method.rs new file mode 100644 index 0000000000..ff512e2a19 --- /dev/null +++ b/applications/minotari_node/src/grpc_method.rs @@ -0,0 +1,278 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{fmt, str::FromStr}; + +use serde::{Deserialize, Serialize}; + +/// A list of all the GRPC methods that can be enabled/disabled +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum GrpcMethod { + ListHeaders, + GetHeaderByHash, + GetBlocks, + GetBlockTiming, + GetConstants, + GetBlockSize, + GetBlockFees, + GetVersion, + CheckForUpdates, + GetTokensInCirculation, + GetNetworkDifficulty, + GetNewBlockTemplate, + GetNewBlock, + GetNewBlockWithCoinbases, + GetNewBlockTemplateWithCoinbases, + GetNewBlockBlob, + SubmitBlock, + SubmitBlockBlob, + SubmitTransaction, + GetSyncInfo, + GetSyncProgress, + GetTipInfo, + SearchKernels, + SearchUtxos, + FetchMatchingUtxos, + GetPeers, + GetMempoolTransactions, + TransactionState, + Identify, + GetNetworkStatus, + ListConnectedPeers, + GetMempoolStats, + GetActiveValidatorNodes, + GetShardKey, + GetTemplateRegistrations, + GetSideChainUtxos, +} + +impl Default for GrpcMethod { + fn default() -> Self { + GrpcMethod::GetVersion + } +} + +impl GrpcMethod { + /// All the GRPC methods as a fixed array + pub const ALL_VARIANTS: [GrpcMethod; 36] = [ + GrpcMethod::ListHeaders, + GrpcMethod::GetHeaderByHash, + GrpcMethod::GetBlocks, + GrpcMethod::GetBlockTiming, + GrpcMethod::GetConstants, + GrpcMethod::GetBlockSize, + GrpcMethod::GetBlockFees, + GrpcMethod::GetVersion, + GrpcMethod::CheckForUpdates, + GrpcMethod::GetTokensInCirculation, + GrpcMethod::GetNetworkDifficulty, + GrpcMethod::GetNewBlockTemplate, + GrpcMethod::GetNewBlock, + GrpcMethod::GetNewBlockWithCoinbases, + GrpcMethod::GetNewBlockTemplateWithCoinbases, + GrpcMethod::GetNewBlockBlob, + GrpcMethod::SubmitBlock, + GrpcMethod::SubmitBlockBlob, + GrpcMethod::SubmitTransaction, + GrpcMethod::GetSyncInfo, + GrpcMethod::GetSyncProgress, + GrpcMethod::GetTipInfo, + GrpcMethod::SearchKernels, + GrpcMethod::SearchUtxos, + GrpcMethod::FetchMatchingUtxos, + GrpcMethod::GetPeers, + GrpcMethod::GetMempoolTransactions, + GrpcMethod::TransactionState, + GrpcMethod::Identify, + GrpcMethod::GetNetworkStatus, + GrpcMethod::ListConnectedPeers, + GrpcMethod::GetMempoolStats, + GrpcMethod::GetActiveValidatorNodes, + GrpcMethod::GetShardKey, + GrpcMethod::GetTemplateRegistrations, + GrpcMethod::GetSideChainUtxos, + ]; +} + +impl IntoIterator for GrpcMethod { + type IntoIter = std::array::IntoIter; + type Item = GrpcMethod; + + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(Self::ALL_VARIANTS) + } +} + +impl FromStr for GrpcMethod { + type Err = String; + + fn from_str(s: &str) -> Result { + // Cater for 'serde_json' converted strings as well + let binding = s.to_string().replace("\"", ""); + match binding.as_str() { + "list_headers" => Ok(GrpcMethod::ListHeaders), + "get_header_by_hash" => Ok(GrpcMethod::GetHeaderByHash), + "get_blocks" => Ok(GrpcMethod::GetBlocks), + "get_block_timing" => Ok(GrpcMethod::GetBlockTiming), + "get_constants" => Ok(GrpcMethod::GetConstants), + "get_block_size" => Ok(GrpcMethod::GetBlockSize), + "get_block_fees" => Ok(GrpcMethod::GetBlockFees), + "get_version" => Ok(GrpcMethod::GetVersion), + "check_for_updates" => Ok(GrpcMethod::CheckForUpdates), + "get_tokens_in_circulation" => Ok(GrpcMethod::GetTokensInCirculation), + "get_network_difficulty" => Ok(GrpcMethod::GetNetworkDifficulty), + "get_new_block_template" => Ok(GrpcMethod::GetNewBlockTemplate), + "get_new_block" => Ok(GrpcMethod::GetNewBlock), + "get_new_block_with_coinbases" => Ok(GrpcMethod::GetNewBlockWithCoinbases), + "get_new_block_template_with_coinbases" => Ok(GrpcMethod::GetNewBlockTemplateWithCoinbases), + "get_new_block_blob" => Ok(GrpcMethod::GetNewBlockBlob), + "submit_block" => Ok(GrpcMethod::SubmitBlock), + "submit_block_blob" => Ok(GrpcMethod::SubmitBlockBlob), + "submit_transaction" => Ok(GrpcMethod::SubmitTransaction), + "get_sync_info" => Ok(GrpcMethod::GetSyncInfo), + "get_sync_progress" => Ok(GrpcMethod::GetSyncProgress), + "get_tip_info" => Ok(GrpcMethod::GetTipInfo), + "search_kernels" => Ok(GrpcMethod::SearchKernels), + "search_utxos" => Ok(GrpcMethod::SearchUtxos), + "fetch_matching_utxos" => Ok(GrpcMethod::FetchMatchingUtxos), + "get_peers" => Ok(GrpcMethod::GetPeers), + "get_mempool_transactions" => Ok(GrpcMethod::GetMempoolTransactions), + "transaction_state" => Ok(GrpcMethod::TransactionState), + "identify" => Ok(GrpcMethod::Identify), + "get_network_status" => Ok(GrpcMethod::GetNetworkStatus), + "list_connected_peers" => Ok(GrpcMethod::ListConnectedPeers), + "get_mempool_stats" => Ok(GrpcMethod::GetMempoolStats), + "get_active_validator_nodes" => Ok(GrpcMethod::GetActiveValidatorNodes), + "get_shard_key" => Ok(GrpcMethod::GetShardKey), + "get_template_registrations" => Ok(GrpcMethod::GetTemplateRegistrations), + "get_side_chain_utxos" => Ok(GrpcMethod::GetSideChainUtxos), + _ => Err(format!("'{}' not supported", s)), + } + } +} + +impl fmt::Display for GrpcMethod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use serde::{Deserialize, Serialize}; + + use crate::grpc_method::GrpcMethod; + + #[derive(Clone, Serialize, Deserialize, Debug)] + #[allow(clippy::struct_excessive_bools)] + struct TestConfig { + name: String, + inner_config: TestInnerConfig, + } + + #[derive(Clone, Serialize, Deserialize, Debug)] + #[allow(clippy::struct_excessive_bools)] + struct TestInnerConfig { + allow_methods: Vec, + } + + #[test] + fn it_deserializes_enums() { + let config_str = r#" + name = "blockchain champion" + inner_config.allow_methods = [ + "list_headers", + "get_constants", + # "get_blocks" + "identify", + # "get_shard_key" + ] + "#; + let config = toml::from_str::(config_str).unwrap(); + + // Enums in the config + assert!(config.inner_config.allow_methods.contains(&GrpcMethod::ListHeaders)); + assert!(config.inner_config.allow_methods.contains(&GrpcMethod::GetConstants)); + assert!(!config.inner_config.allow_methods.contains(&GrpcMethod::GetBlocks)); // commented out in the config + assert!(config.inner_config.allow_methods.contains(&GrpcMethod::Identify)); + assert!(!config.inner_config.allow_methods.contains(&GrpcMethod::GetShardKey)); // commented out in the config + } + + #[test] + fn grpc_method_into_iter_is_exhaustive() { + let mut count = 0; + for method in GrpcMethod::ALL_VARIANTS.iter() { + match method { + GrpcMethod::ListHeaders => count += 1, + GrpcMethod::GetHeaderByHash => count += 1, + GrpcMethod::GetBlocks => count += 1, + GrpcMethod::GetBlockTiming => count += 1, + GrpcMethod::GetConstants => count += 1, + GrpcMethod::GetBlockSize => count += 1, + GrpcMethod::GetBlockFees => count += 1, + GrpcMethod::GetVersion => count += 1, + GrpcMethod::CheckForUpdates => count += 1, + GrpcMethod::GetTokensInCirculation => count += 1, + GrpcMethod::GetNetworkDifficulty => count += 1, + GrpcMethod::GetNewBlockTemplate => count += 1, + GrpcMethod::GetNewBlock => count += 1, + GrpcMethod::GetNewBlockWithCoinbases => count += 1, + GrpcMethod::GetNewBlockTemplateWithCoinbases => count += 1, + GrpcMethod::GetNewBlockBlob => count += 1, + GrpcMethod::SubmitBlock => count += 1, + GrpcMethod::SubmitBlockBlob => count += 1, + GrpcMethod::SubmitTransaction => count += 1, + GrpcMethod::GetSyncInfo => count += 1, + GrpcMethod::GetSyncProgress => count += 1, + GrpcMethod::GetTipInfo => count += 1, + GrpcMethod::SearchKernels => count += 1, + GrpcMethod::SearchUtxos => count += 1, + GrpcMethod::FetchMatchingUtxos => count += 1, + GrpcMethod::GetPeers => count += 1, + GrpcMethod::GetMempoolTransactions => count += 1, + GrpcMethod::TransactionState => count += 1, + GrpcMethod::Identify => count += 1, + GrpcMethod::GetNetworkStatus => count += 1, + GrpcMethod::ListConnectedPeers => count += 1, + GrpcMethod::GetMempoolStats => count += 1, + GrpcMethod::GetActiveValidatorNodes => count += 1, + GrpcMethod::GetShardKey => count += 1, + GrpcMethod::GetTemplateRegistrations => count += 1, + GrpcMethod::GetSideChainUtxos => count += 1, + } + } + assert_eq!(count, GrpcMethod::ALL_VARIANTS.len()); + } + + #[test] + fn it_converts_from_serde_json_str_to_enum() { + // Iterate over all the enum variants and convert them to a string + for method in GrpcMethod::ALL_VARIANTS.iter() { + let method_str = serde_json::to_string(&method).unwrap(); + let method_from_str = GrpcMethod::from_str(&method_str).unwrap(); + assert_eq!(method, &method_from_str); + } + } +} diff --git a/applications/minotari_node/src/lib.rs b/applications/minotari_node/src/lib.rs index e675183518..99ebebf22b 100644 --- a/applications/minotari_node/src/lib.rs +++ b/applications/minotari_node/src/lib.rs @@ -32,15 +32,16 @@ pub mod cli; mod commands; pub mod config; mod grpc; +mod grpc_method; #[cfg(feature = "metrics")] mod metrics; mod recovery; mod utils; - use std::{process, sync::Arc}; use commands::{cli_loop::CliLoop, command::CommandContext}; use futures::FutureExt; +pub use grpc_method::GrpcMethod; use log::*; use minotari_app_grpc::{authentication::ServerAuthenticationInterceptor, tls::identity::read_identity}; use minotari_app_utilities::common_cli_args::CommonCliArgs; diff --git a/applications/minotari_node/src/main.rs b/applications/minotari_node/src/main.rs index 18cdfa5267..3f76c71abd 100644 --- a/applications/minotari_node/src/main.rs +++ b/applications/minotari_node/src/main.rs @@ -103,25 +103,26 @@ fn main() { fn main_inner() -> Result<(), ExitError> { let cli = Cli::parse(); - let config_path = cli.common.config_path(); - let cfg = load_configuration(config_path, true, cli.non_interactive_mode, &cli)?; - - if cli.profile_with_tokio_console { - console_subscriber::init(); - } - let base_path = cli.common.get_base_path(); initialize_logging( &cli.common.log_config_path("base_node"), cli.common.log_path.as_ref().unwrap_or(&base_path), include_str!("../log4rs_sample.yml"), )?; + info!( target: LOG_TARGET, "Starting Minotari Base Node version: {}", consts::APP_VERSION ); + let config_path = cli.common.config_path(); + let cfg = load_configuration(config_path, true, cli.non_interactive_mode, &cli, cli.common.network)?; + + if cli.profile_with_tokio_console { + console_subscriber::init(); + } + #[cfg(all(unix, feature = "libtor"))] let mut config = ApplicationConfig::load_from(&cfg)?; #[cfg(not(all(unix, feature = "libtor")))] diff --git a/base_layer/chat_ffi/src/application_config.rs b/base_layer/chat_ffi/src/application_config.rs index de4a6c6349..bedadef836 100644 --- a/base_layer/chat_ffi/src/application_config.rs +++ b/base_layer/chat_ffi/src/application_config.rs @@ -27,8 +27,8 @@ use tari_chat_client::{ config::{ApplicationConfig, ChatClientConfig}, networking::Multiaddr, }; -use tari_common::configuration::{MultiaddrList, Network, StringList}; -use tari_p2p::{PeerSeedsConfig, TransportConfig, TransportType, DEFAULT_DNS_NAME_SERVER}; +use tari_common::configuration::{name_server::DEFAULT_DNS_NAME_SERVER, MultiaddrList, Network, StringList}; +use tari_p2p::{PeerSeedsConfig, TransportConfig, TransportType}; use crate::error::{InterfaceError, LibChatError}; diff --git a/base_layer/p2p/Cargo.toml b/base_layer/p2p/Cargo.toml index 98279f38ea..030ce99fe8 100644 --- a/base_layer/p2p/Cargo.toml +++ b/base_layer/p2p/Cargo.toml @@ -40,6 +40,7 @@ webpki = "0.22" [dev-dependencies] tari_test_utils = { path = "../../infrastructure/test_utils" } +toml = "0.5.11" config = "0.14.0" clap = "3.2" diff --git a/base_layer/p2p/src/auto_update/dns.rs b/base_layer/p2p/src/auto_update/dns.rs index f91add59f7..ce3556d26c 100644 --- a/base_layer/p2p/src/auto_update/dns.rs +++ b/base_layer/p2p/src/auto_update/dns.rs @@ -244,15 +244,16 @@ mod test { mod dns_software_update { use std::time::Duration; + use tari_common::configuration::name_server::DEFAULT_DNS_NAME_SERVER; + use super::*; - use crate::DEFAULT_DNS_NAME_SERVER; impl AutoUpdateConfig { fn get_test_defaults() -> Self { Self { override_from: None, name_server: DEFAULT_DNS_NAME_SERVER.parse().unwrap(), - update_uris: vec!["test.local".to_string()], + update_uris: vec!["test.local".to_string()].into(), use_dnssec: true, download_base_url: "https://tari-binaries.s3.amazonaws.com/latest".to_string(), hashes_url: "https://raw.githubusercontent.com/tari-project/tari/development/meta/hashes.txt" diff --git a/base_layer/p2p/src/auto_update/mod.rs b/base_layer/p2p/src/auto_update/mod.rs index dc9e52614e..29a48f4f98 100644 --- a/base_layer/p2p/src/auto_update/mod.rs +++ b/base_layer/p2p/src/auto_update/mod.rs @@ -47,6 +47,7 @@ use tari_common::{ bootstrap::ApplicationType, serializers::optional_seconds, utils::{deserialize_string_or_struct, serialize_string}, + StringList, }, DnsNameServer, SubConfigPath, @@ -67,7 +68,7 @@ pub struct AutoUpdateConfig { serialize_with = "serialize_string" )] pub name_server: DnsNameServer, - pub update_uris: Vec, + pub update_uris: StringList, pub use_dnssec: bool, pub download_base_url: String, pub hashes_url: String, @@ -81,7 +82,7 @@ impl Default for AutoUpdateConfig { Self { override_from: None, name_server: DnsNameServer::from_str("1.1.1.1:53/cloudflare.net").unwrap(), - update_uris: vec![], + update_uris: vec![].into(), use_dnssec: false, download_base_url: String::new(), hashes_url: String::new(), @@ -267,7 +268,7 @@ download_base_url ="http://test.com" config.name_server, DnsNameServer::from_str("127.0.0.1:80/localtest").unwrap(), ); - assert_eq!(config.update_uris, Vec::::new()); + assert_eq!(config.update_uris.into_vec(), Vec::::new()); assert_eq!(config.download_base_url, "http://test.com"); // update_uris = // pub update_uris: Vec, @@ -287,7 +288,7 @@ download_base_url ="http://test.com" config.name_server, DnsNameServer::from_str("127.0.0.1:80/localtest2").unwrap(), ); - assert_eq!(config.update_uris, vec!["http://none", "http://local"]); + assert_eq!(config.update_uris.into_vec(), vec!["http://none", "http://local"]); assert!(config.use_dnssec); } #[test] @@ -299,7 +300,7 @@ download_base_url ="http://test.com" config.name_server, DnsNameServer::from_str("127.0.0.1:80/localtest2").unwrap(), ); - assert_eq!(config.update_uris, vec!["http://none", "http://local"]); + assert_eq!(config.update_uris.into_vec(), vec!["http://none", "http://local"]); assert!(config.use_dnssec); } diff --git a/base_layer/p2p/src/auto_update/service.rs b/base_layer/p2p/src/auto_update/service.rs index b7b3e761fb..da8216be6e 100644 --- a/base_layer/p2p/src/auto_update/service.rs +++ b/base_layer/p2p/src/auto_update/service.rs @@ -140,7 +140,7 @@ impl SoftwareUpdaterService { log::info!( target: LOG_TARGET, "Checking for updates ({})...", - self.config.update_uris.join(", ") + self.config.update_uris.as_slice().join(", ") ); if !self.config.is_update_enabled() { warn!( diff --git a/base_layer/p2p/src/config.rs b/base_layer/p2p/src/config.rs index 601a71ec5f..1ee266def7 100644 --- a/base_layer/p2p/src/config.rs +++ b/base_layer/p2p/src/config.rs @@ -28,9 +28,11 @@ use std::{ use serde::{Deserialize, Serialize}; use tari_common::{ configuration::{ + name_server::DEFAULT_DNS_NAME_SERVER, serializers, utils::{deserialize_string_or_struct, serialize_string}, MultiaddrList, + Network, StringList, }, DnsNameServer, @@ -39,7 +41,7 @@ use tari_common::{ use tari_comms::multiaddr::Multiaddr; use tari_comms_dht::{DbConnectionUrl, DhtConfig}; -use crate::{transport::TransportConfig, DEFAULT_DNS_NAME_SERVER}; +use crate::transport::TransportConfig; /// Peer seed configuration #[derive(Clone, Debug, Serialize, Deserialize)] @@ -47,17 +49,21 @@ use crate::{transport::TransportConfig, DEFAULT_DNS_NAME_SERVER}; pub struct PeerSeedsConfig { pub override_from: Option, /// Custom specified peer seed nodes + #[serde(default)] pub peer_seeds: StringList, /// DNS seeds hosts. The DNS TXT records are queried from these hosts and the resulting peers added to the comms /// peer list. + #[serde(default)] pub dns_seeds: StringList, #[serde( + default, deserialize_with = "deserialize_string_or_struct", serialize_with = "serialize_string" )] /// DNS name server to use for DNS seeds. pub dns_seeds_name_server: DnsNameServer, /// All DNS seed records must pass DNSSEC validation + #[serde(default)] pub dns_seeds_use_dnssec: bool, } @@ -66,7 +72,11 @@ impl Default for PeerSeedsConfig { Self { override_from: None, peer_seeds: StringList::default(), - dns_seeds: StringList::default(), + dns_seeds: vec![format!( + "seeds.{}.tari.com", + Network::get_current_or_user_setting_or_default().as_key_str() + )] + .into(), dns_seeds_name_server: DEFAULT_DNS_NAME_SERVER.parse().unwrap(), dns_seeds_use_dnssec: false, } @@ -168,3 +178,77 @@ impl P2pConfig { self.dht.set_base_path(base_path) } } + +#[cfg(test)] +mod test { + use crate::PeerSeedsConfig; + + #[test] + fn it_deserializes_from_toml() { + // No empty fields, no omitted fields + let config_str = r#" + dns_seeds = ["seeds.esmeralda.tari.com"] + peer_seeds = ["20605a28047938f851e3d0cd3f0ff771b2fb23036f0ab8eaa57947dccc834d15::/onion3/e4dsii6vc5f7frao23syonalgikd5kcd7fddrdjhab6bdo3cu47n3kyd:18141"] + dns_seeds_name_server = "1.1.1.1:853/cloudflare-dns.com" + dns_seeds_use_dnssec = false + "#; + let config = toml::from_str::(config_str).unwrap(); + assert_eq!(config.dns_seeds.into_vec(), vec!["seeds.esmeralda.tari.com"]); + assert_eq!(config.peer_seeds.into_vec(), vec![ + "20605a28047938f851e3d0cd3f0ff771b2fb23036f0ab8eaa57947dccc834d15::/onion3/\ + e4dsii6vc5f7frao23syonalgikd5kcd7fddrdjhab6bdo3cu47n3kyd:18141" + ]); + assert_eq!( + config.dns_seeds_name_server.to_string(), + "1.1.1.1:853/cloudflare-dns.com".to_string() + ); + assert!(!config.dns_seeds_use_dnssec); + + // 'dns_seeds_name_server' parse error handled + let config_str = r#" + dns_seeds = ["seeds.esmeralda.tari.com"] + peer_seeds = ["20605a28047938f851e3d0cd3f0ff771b2fb23036f0ab8eaa57947dccc834d15::/onion3/e4dsii6vc5f7frao23syonalgikd5kcd7fddrdjhab6bdo3cu47n3kyd:18141"] + dns_seeds_name_server = "" + #dns_seeds_use_dnssec = false + "#; + match toml::from_str::(config_str) { + Ok(_) => panic!("Should fail"), + Err(e) => assert_eq!( + e.to_string(), + "failed to parse DNS name server 'dns_name' for key `dns_seeds_name_server` at line 4 column 37" + ), + } + + // Empty config list fields + let config_str = r#" + dns_seeds = [] + peer_seeds = [] + dns_seeds_name_server = "1.1.1.1:853/cloudflare-dns.com" + dns_seeds_use_dnssec = false + "#; + let config = toml::from_str::(config_str).unwrap(); + assert_eq!(config.dns_seeds.into_vec(), Vec::::new()); + assert_eq!(config.peer_seeds.into_vec(), Vec::::new()); + assert_eq!( + config.dns_seeds_name_server.to_string(), + "1.1.1.1:853/cloudflare-dns.com".to_string() + ); + assert!(!config.dns_seeds_use_dnssec); + + // Omitted config fields + let config_str = r#" + #dns_seeds = [] + #peer_seeds = [] + #dns_seeds_name_server = "1.1.1.1:853/cloudflare-dns.com" + #dns_seeds_use_dnssec = false + "#; + let config = toml::from_str::(config_str).unwrap(); + assert_eq!(config.dns_seeds.into_vec(), Vec::::new()); + assert_eq!(config.peer_seeds.into_vec(), Vec::::new()); + assert_eq!( + config.dns_seeds_name_server.to_string(), + "1.1.1.1:853/cloudflare-dns.com".to_string() + ); + assert!(!config.dns_seeds_use_dnssec); + } +} diff --git a/base_layer/p2p/src/lib.rs b/base_layer/p2p/src/lib.rs index 5763213f5e..f5c3a0a976 100644 --- a/base_layer/p2p/src/lib.rs +++ b/base_layer/p2p/src/lib.rs @@ -49,9 +49,6 @@ pub use transport::{Socks5TransportConfig, TcpTransportConfig, TorTransportConfi pub use self::config::{P2pConfig, PeerSeedsConfig}; -/// Default DNS resolver set to cloudflare's private 1.1.1.1 resolver -pub const DEFAULT_DNS_NAME_SERVER: &str = "1.1.1.1:853/cloudflare-dns.com"; - /// Major network version. Peers will refuse connections if this value differs pub const MAJOR_NETWORK_VERSION: u8 = 0; /// Minor network version. This should change with each time the network protocol has changed in a backward-compatible diff --git a/base_layer/p2p/src/peer_seeds.rs b/base_layer/p2p/src/peer_seeds.rs index 0e1311e431..7582bf6afc 100644 --- a/base_layer/p2p/src/peer_seeds.rs +++ b/base_layer/p2p/src/peer_seeds.rs @@ -225,6 +225,7 @@ mod test { } mod peer_seed_resolver { + use tari_common::configuration::name_server::DEFAULT_DNS_NAME_SERVER; use trust_dns_client::{ proto::{ op::Query, @@ -235,7 +236,7 @@ mod test { }; use super::*; - use crate::{dns::mock, DEFAULT_DNS_NAME_SERVER}; + use crate::dns::mock; #[tokio::test] #[ignore = "Useful for developer testing but will fail unless the DNS has TXT records setup correctly."] diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 895163c93c..6e9f3e9b43 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -114,7 +114,7 @@ use minotari_wallet::{ use num_traits::FromPrimitive; use rand::rngs::OsRng; use tari_common::{ - configuration::{MultiaddrList, StringList}, + configuration::{name_server::DEFAULT_DNS_NAME_SERVER, MultiaddrList, StringList}, network_check::set_network_if_choice_valid, }; use tari_common_types::{ @@ -171,7 +171,6 @@ use tari_p2p::{ TorTransportConfig, TransportConfig, TransportType, - DEFAULT_DNS_NAME_SERVER, }; use tari_script::TariScript; use tari_shutdown::Shutdown; diff --git a/common/config/presets/b_peer_seeds.toml b/common/config/presets/b_peer_seeds.toml index 62cc16fbf7..08dada19cc 100644 --- a/common/config/presets/b_peer_seeds.toml +++ b/common/config/presets/b_peer_seeds.toml @@ -5,58 +5,30 @@ # # ######################################################################################################################## -[peer_seeds] -# DNS seeds hosts - DNS TXT records are queried from these hosts and the resulting peers added to the comms peer list. -#dns_seeds = [] -# Custom specified peer seed nodes -#peer_seeds = [] -# DNS name server to use for DNS seeds -#dns_seeds_name_server = "1.1.1.1:853/cloudflare-dns.com" -# All DNS seed records must pass DNSSEC validation -#dns_seeds_use_dnssec = false - [nextnet.p2p.seeds] # DNS seeds hosts - DNS TXT records are queried from these hosts and the resulting peers added to the comms peer list. +# (Default: peer_seeds = []) dns_seeds = ["seeds.nextnet.tari.com"] -# Custom specified peer seed nodes -peer_seeds = [ - #"a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/onion3/rhoqxfbzz3uidp23erxu4mkwwexc2gg4q45rcxfpbhb35ycdv4ex2fid:18141", - #"a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189", -] +# Custom specified peer seed nodes (Default: peer_seeds = []) +#peer_seeds = ["a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189"] [stagenet.p2p.seeds] # DNS seeds hosts - DNS TXT records are queried from these hosts and the resulting peers added to the comms peer list. +# (Default: peer_seeds = []) dns_seeds = ["seeds.stagenet.tari.com"] -# Custom specified peer seed nodes -peer_seeds = [ - #"a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/onion3/rhoqxfbzz3uidp23erxu4mkwwexc2gg4q45rcxfpbhb35ycdv4ex2fid:18141", - #"a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189", -] +# Custom specified peer seed nodes (Default: peer_seeds = []) +#peer_seeds = ["a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189"] [esmeralda.p2p.seeds] # DNS seeds hosts - DNS TXT records are queried from these hosts and the resulting peers added to the comms peer list. +# (Default: peer_seeds = []) dns_seeds = ["seeds.esmeralda.tari.com"] -# Custom specified peer seed nodes -peer_seeds = [ - #"a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/onion3/rhoqxfbzz3uidp23erxu4mkwwexc2gg4q45rcxfpbhb35ycdv4ex2fid:18141", - #"a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189", -] - +# Custom specified peer seed nodes (Default: peer_seeds = []) +#peer_seeds = ["a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189"] [igor.p2p.seeds] -# Custom specified peer seed nodes -peer_seeds = [ - # 2222bb888618d65d7b16e355f9 - "8e7eb81e512f3d6347bf9b1ca9cd67d2c8e29f2836fc5bd608206505cc72af34::/ip6/fd56:2026:93c0:0:9e:96fb:e119:d8ec/tcp/18189", - # aaaaf562a6ef070706fb9a7244 - "40a9d8573745072534bce7d0ecafe882b1c79570375a69841c08a98dee9ecb5f::/ip6/fd56:2026:93c0:0:9e:96fb:e119:d8ec/tcp/18189", - # dddd69c587a10c41ef2bf51397 - "126c7ee64f71aca36398b977dd31fbbe9f9dad615df96473fb655bef5709c540::/ip6/fd56:2026:93c0:0:9e:96fb:e119:d8ec/tcp/18189", - - - # Local DAN Seeds, Stringhandler - # da100065d065f839dab6b6fb4f - "f24a6ed54362cee25c8e08e92bcd33e4d8ab2b733862948f863c982040d0d447::/onion3/s7sto2fd6cqf3wak2ec23gygb3d77p2ro7pcl2vesk6notgedjhy4nyd:18141", - # da4000fb6a031eced4ce65fe31 - "9c127e9451d6721bfbe2b75434fcc19f6c7ab23523d4dacf7f5f5d601d2c8840::/onion3/kfh6trtkccp6mdbob42sb3sd464lzorn2ufys4zglnqhoxzsa4souaqd:18141" -] +# DNS seeds hosts - DNS TXT records are queried from these hosts and the resulting peers added to the comms peer list. +# (Default: peer_seeds = []) +dns_seeds = ["seeds.igor.tari.com"] +# Custom specified peer seed nodes (Default: peer_seeds = []) +#peer_seeds = ["a062ae2345b0db0df9fb1504b99511e23d98f8513f9b5503efcc6dad8eca7e47::/ip4/54.77.66.39/tcp/18189"] diff --git a/common/src/configuration/config_list.rs b/common/src/configuration/config_list.rs new file mode 100644 index 0000000000..f7e1fbbb33 --- /dev/null +++ b/common/src/configuration/config_list.rs @@ -0,0 +1,308 @@ +// Copyright 2022 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{ + fmt::{Display, Formatter}, + ops::Deref, + slice, + str::FromStr, +}; + +use serde::{ + de::{self, SeqAccess, Visitor}, + Deserialize, + Deserializer, + Serialize, +}; + +// Define a new type ConfigList +#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +pub struct ConfigList(Vec); + +impl ConfigList { + /// Create a new ConfigList + pub fn new() -> Self { + Self(vec![]) + } + + /// Create a new ConfigList with a specified capacity + pub fn with_capacity(size: usize) -> Self { + ConfigList(Vec::with_capacity(size)) + } + + /// Convert ConfigList into a Vec + pub fn into_vec(self) -> Vec { + self.0 + } + + /// Get a reference to the inner Vec + pub fn as_slice(&self) -> &[T] { + &self.0 + } + + /// Get the length of the inner Vec + pub fn len(&self) -> usize { + self.0.len() + } + + /// Get the length of the inner Vec + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Get an iterator over the inner Vec + pub fn iter(&self) -> slice::Iter<'_, T> { + self.0.iter() + } +} + +impl Display for ConfigList +where T: Display +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.0 + .iter() + .map(|item| item.to_string()) + .collect::>() + .join(", ") + ) + } +} + +impl Default for ConfigList { + fn default() -> Self { + Self(vec![]) + } +} + +// Implement Deref for ConfigList +impl Deref for ConfigList { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +// Implement AsRef<[T]> for ConfigList +impl AsRef<[T]> for ConfigList { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +// Implement From> for ConfigList +impl From> for ConfigList { + fn from(v: Vec) -> Self { + ConfigList(v) + } +} + +// Implement IntoIterator for ConfigList +impl IntoIterator for ConfigList { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +// Implement IntoIterator for &ConfigList +impl<'a, T> IntoIterator for &'a ConfigList { + type IntoIter = slice::Iter<'a, T>; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + self.as_slice().iter() + } +} + +// Implement Deserialize<'de> for ConfigList +impl<'de, T> Deserialize<'de> for ConfigList +where + T: FromStr + Deserialize<'de>, + ::Err: Display, +{ + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + struct ConfigListVisitor(std::marker::PhantomData); + + impl<'de, T> Visitor<'de> for ConfigListVisitor + where + T: FromStr + Deserialize<'de>, + ::Err: Display, + { + type Value = ConfigList; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "a comma delimited string or multiple string elements") + } + + fn visit_str(self, v: &str) -> Result + where E: de::Error { + if v.trim().is_empty() { + return Ok(ConfigList::new()); + } + let strings = v + .split(',') + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .collect::>(); + let parsed: Result, _> = strings + .into_iter() + .map(|item| T::from_str(item).map_err(E::custom)) + .collect(); + Ok(ConfigList(parsed.map_err(E::custom)?)) + } + + fn visit_seq(self, mut seq: A) -> Result + where A: SeqAccess<'de> { + let mut buf = seq.size_hint().map(Vec::with_capacity).unwrap_or_default(); + while let Some(v) = seq.next_element::()? { + buf.push(v) + } + Ok(ConfigList::from(buf)) + } + } + + if deserializer.is_human_readable() { + deserializer.deserialize_seq(ConfigListVisitor(std::marker::PhantomData)) + } else { + deserializer.deserialize_newtype_struct("ConfigList", ConfigListVisitor(std::marker::PhantomData)) + } + } +} + +#[cfg(test)] +mod test_config_list_general { + use config::Config; + use serde::Deserialize; + + use crate::configuration::ConfigList; + + type TestList = ConfigList; + + #[derive(Debug, Deserialize, PartialEq)] + struct Test { + something: TestList, + } + + #[test] + fn with_capacity_test() { + let new_str_lst = TestList::with_capacity(3); + assert_eq!(new_str_lst.into_vec().capacity(), 3); + } + + #[test] + fn from_vec_string_list() { + let vec_string = vec![String::from("Tari is cool!")]; + let string_lst = TestList::from(vec_string); + assert_eq!(string_lst.into_vec(), vec![String::from("Tari is cool!")]); + } + + #[test] + fn as_ref_string_list() { + let vec_string = vec![String::from("Tari")]; + let vec_as_ref: &[String] = vec_string.as_ref(); + let string_lst = TestList::from(vec![String::from("Tari")]); + assert_eq!(string_lst.as_ref(), vec_as_ref); + } + + #[test] + fn into_iter_string_list() { + let vec_string = vec![ + String::from("Tari"), + String::from("Project"), + String::from("let's mine it!"), + ]; + let string_lst = TestList::from(vec_string); + let mut res_iter = string_lst.into_iter(); + + assert_eq!(Some(String::from("Tari")), res_iter.next()); + assert_eq!(Some(String::from("Project")), res_iter.next()); + assert_eq!(Some(String::from("let's mine it!")), res_iter.next()); + assert_eq!(None, res_iter.next()); + } + + #[test] + fn it_deserializes_from_toml() { + let config_str = r#"something = ["a","b","c"]"#; + let test = toml::from_str::(config_str).unwrap(); + assert_eq!(test.something.0, vec!["a", "b", "c"]); + } + + #[test] + fn it_deserializes_from_config_comma_delimited() { + let config = Config::builder() + .set_override("something", "a, b, c,") + .unwrap() + .build() + .unwrap(); + let test = config.try_deserialize::().unwrap(); + assert_eq!(test.something.0, vec!["a", "b", "c"]); + } +} + +#[cfg(test)] +mod test_config_list_for_toml { + use std::str::FromStr; + + use serde::Deserialize; + + use crate::configuration::{ConfigList, Multiaddr}; + + #[derive(Debug, Deserialize, PartialEq)] + struct Test { + #[serde(default)] + u32_list: ConfigList, + #[serde(default)] + string_list: ConfigList, + #[serde(default)] + multiaddr_list: ConfigList, + } + + #[test] + fn it_deserializes_from_toml() { + // No empty fields, no omitted fields + let config_str = r#" + # u32 + u32_list = [1, 2, 3, 4, 5] + # String + string_list = ["1", "2", "3", "4", "5"] + # Multiaddr + multiaddr_list = ["/ip4/127.0.150.1/tcp/18500", "/ip4/127.0.0.1/udt/sctp/5678", "/ip4/127.0.0.0/tcp/18189"] + "#; + let config = toml::from_str::(config_str).unwrap(); + let item_vec_u32 = config.u32_list.into_vec(); + assert_eq!(item_vec_u32, vec![1, 2, 3, 4, 5]); + let item_vec_string = config.string_list.into_vec(); + assert_eq!(item_vec_string, vec!["1", "2", "3", "4", "5"]); + let item_vec_multiaddr = config.multiaddr_list.into_vec(); + assert_eq!(item_vec_multiaddr, vec![ + Multiaddr::from_str("/ip4/127.0.150.1/tcp/18500").unwrap(), + Multiaddr::from_str("/ip4/127.0.0.1/udt/sctp/5678").unwrap(), + Multiaddr::from_str("/ip4/127.0.0.0/tcp/18189").unwrap(), + ]); + + // Empty fields, omitted fields, filled fields + let config_str = r#" + # u32 + u32_list = [1, 2, 3, 4, 5] + # String + string_list = [] + # Multiaddr + #multiaddr_list = [] + "#; + let config = toml::from_str::(config_str).unwrap(); + let item_vec_u32 = config.u32_list.into_vec(); + assert_eq!(item_vec_u32, vec![1, 2, 3, 4, 5]); + let item_vec_string = config.string_list.into_vec(); + assert_eq!(item_vec_string, Vec::::new()); + let item_vec_multiaddr = config.multiaddr_list.into_vec(); + assert_eq!(item_vec_multiaddr, Vec::::new()); + } +} diff --git a/common/src/configuration/mod.rs b/common/src/configuration/mod.rs index bcb57ab324..40b710b224 100644 --- a/common/src/configuration/mod.rs +++ b/common/src/configuration/mod.rs @@ -43,15 +43,16 @@ pub mod loader; mod network; pub use network::Network; mod common_config; +mod config_list; mod multiaddr_list; pub mod name_server; pub mod serializers; mod string_list; pub mod utils; - use std::{iter::FromIterator, net::SocketAddr}; pub use common_config::CommonConfig; +pub use config_list::ConfigList; use multiaddr::{Error, Multiaddr, Protocol}; pub use multiaddr_list::MultiaddrList; pub use string_list::StringList; @@ -69,13 +70,13 @@ pub fn socket_or_multi(addr: &str) -> Result { /// Implement this trait to specify custom configuration overrides for a network when loading the config pub trait ConfigOverrideProvider { - fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)>; + fn get_config_property_overrides(&self, network: &Network) -> Vec<(String, String)>; } pub struct NoConfigOverrides; impl ConfigOverrideProvider for NoConfigOverrides { - fn get_config_property_overrides(&self, _network: &mut Network) -> Vec<(String, String)> { + fn get_config_property_overrides(&self, _network: &Network) -> Vec<(String, String)> { Vec::new() } } diff --git a/common/src/configuration/multiaddr_list.rs b/common/src/configuration/multiaddr_list.rs index 321b755b70..1517a4d819 100644 --- a/common/src/configuration/multiaddr_list.rs +++ b/common/src/configuration/multiaddr_list.rs @@ -20,129 +20,22 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{fmt, ops::Deref, slice, str::FromStr, vec}; - use multiaddr::Multiaddr; -use serde::{ - de, - de::{SeqAccess, Visitor}, - Deserialize, - Deserializer, - Serialize, -}; - -/// Supports deserialization from a sequence of strings or comma-delimited strings -#[derive(Debug, Default, Clone, Serialize, PartialEq, Eq)] -pub struct MultiaddrList(Vec); - -impl MultiaddrList { - pub fn new() -> Self { - Self(vec![]) - } - - pub fn with_capacity(size: usize) -> Self { - Self(Vec::with_capacity(size)) - } - - pub fn into_vec(self) -> Vec { - self.0 - } - - pub fn as_slice(&self) -> &[Multiaddr] { - self.0.as_slice() - } -} - -impl Deref for MultiaddrList { - type Target = [Multiaddr]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl AsRef<[Multiaddr]> for MultiaddrList { - fn as_ref(&self) -> &[Multiaddr] { - self.0.as_ref() - } -} +use crate::configuration::ConfigList; -impl From> for MultiaddrList { - fn from(v: Vec) -> Self { - Self(v) - } -} - -impl IntoIterator for MultiaddrList { - type IntoIter = as IntoIterator>::IntoIter; - type Item = as IntoIterator>::Item; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a MultiaddrList { - type IntoIter = slice::Iter<'a, Multiaddr>; - type Item = ::Item; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'de> Deserialize<'de> for MultiaddrList { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - struct MultiaddrListVisitor; - - impl<'de> Visitor<'de> for MultiaddrListVisitor { - type Value = MultiaddrList; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "a comma delimited multiaddr or multiple multiaddr elements") - } - - fn visit_str(self, v: &str) -> Result - where E: de::Error { - Ok(MultiaddrList( - v.split(',') - .map(|s| s.trim()) - .filter(|s| !s.is_empty()) - .map(Multiaddr::from_str) - .collect::, _>>() - .map_err(|e| E::invalid_value(de::Unexpected::Str(e.to_string().as_str()), &self))?, - )) - } - - fn visit_newtype_struct(self, deserializer: D) -> Result - where D: Deserializer<'de> { - deserializer.deserialize_seq(MultiaddrListVisitor) - } - - fn visit_seq(self, mut seq: A) -> Result - where A: SeqAccess<'de> { - let mut buf = seq.size_hint().map(Vec::with_capacity).unwrap_or_default(); - while let Some(v) = seq.next_element::()? { - buf.push(v) - } - Ok(MultiaddrList(buf)) - } - } - - if deserializer.is_human_readable() { - deserializer.deserialize_seq(MultiaddrListVisitor) - } else { - deserializer.deserialize_newtype_struct("MultiaddrList", MultiaddrListVisitor) - } - } -} +/// Supports deserialization from a sequence of strings or comma-delimited strings +pub type MultiaddrList = ConfigList; #[cfg(test)] mod tests { + use std::{str::FromStr, vec}; + use config::Config; + use multiaddr::Multiaddr; + use serde::Deserialize; - use super::*; + use crate::configuration::MultiaddrList; #[derive(Deserialize)] struct Test { @@ -203,7 +96,7 @@ mod tests { let config_str = r#"something = ["/ip4/127.0.0.1/tcp/1234","/ip4/192.168.0.1/tcp/1234","/ip4/10.0.0.1/tcp/1234"]"#; let test = toml::from_str::(config_str).unwrap(); - assert_eq!(test.something.0, vec![ + assert_eq!(test.something.into_vec(), vec![ Multiaddr::from_str("/ip4/127.0.0.1/tcp/1234").unwrap(), Multiaddr::from_str("/ip4/192.168.0.1/tcp/1234").unwrap(), Multiaddr::from_str("/ip4/10.0.0.1/tcp/1234").unwrap() @@ -227,7 +120,7 @@ mod tests { .build() .unwrap(); let test = config.try_deserialize::().unwrap(); - assert_eq!(test.something.0, vec![ + assert_eq!(test.something.into_vec(), vec![ Multiaddr::from_str("/ip4/127.0.0.1/tcp/1234").unwrap(), Multiaddr::from_str("/ip4/192.168.0.1/tcp/1234").unwrap(), Multiaddr::from_str("/ip4/10.0.0.1/tcp/1234").unwrap() diff --git a/common/src/configuration/name_server.rs b/common/src/configuration/name_server.rs index d432d0c405..10d77e3fd9 100644 --- a/common/src/configuration/name_server.rs +++ b/common/src/configuration/name_server.rs @@ -29,6 +29,9 @@ use std::{ use anyhow::anyhow; use serde::Deserialize; +/// Default DNS resolver set to cloudflare's private 1.1.1.1 resolver +pub const DEFAULT_DNS_NAME_SERVER: &str = "1.1.1.1:853/cloudflare-dns.com"; + #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] pub struct DnsNameServer { pub addr: SocketAddr, @@ -41,6 +44,12 @@ impl DnsNameServer { } } +impl Default for DnsNameServer { + fn default() -> Self { + Self::from_str(DEFAULT_DNS_NAME_SERVER).expect("is valid") + } +} + impl Display for DnsNameServer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}/{}", self.addr, self.dns_name) @@ -52,8 +61,12 @@ impl FromStr for DnsNameServer { fn from_str(s: &str) -> Result { let mut split = s.splitn(2, '/'); - let addr = split.next().ok_or_else(|| anyhow!("failed to parse DNS name server"))?; - let dns_name = split.next().ok_or_else(|| anyhow!("failed to parse name server"))?; + let addr = split + .next() + .ok_or_else(|| anyhow!("failed to parse DNS name server 'addr'"))?; + let dns_name = split + .next() + .ok_or_else(|| anyhow!("failed to parse DNS name server 'dns_name'"))?; Ok(Self { addr: addr.parse()?, dns_name: dns_name.to_string(), @@ -81,5 +94,11 @@ mod test { // from str let new_dns = DnsNameServer::from_str("127.0.0.1:8080/my_dns").unwrap(); assert_eq!(new_dns, dns); + + // default + assert_eq!( + DnsNameServer::default(), + DnsNameServer::from_str(DEFAULT_DNS_NAME_SERVER).unwrap() + ); } } diff --git a/common/src/configuration/string_list.rs b/common/src/configuration/string_list.rs index 7487e46681..c3787a6b52 100644 --- a/common/src/configuration/string_list.rs +++ b/common/src/configuration/string_list.rs @@ -20,127 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{fmt, ops::Deref, slice, vec}; - -use serde::{ - de, - de::{SeqAccess, Visitor}, - Deserialize, - Deserializer, - Serialize, -}; +use crate::configuration::ConfigList; /// Supports deserialization from a sequence of strings or comma-delimited strings -#[derive(Debug, Default, Clone, Serialize, PartialEq, Eq)] -pub struct StringList(Vec); - -impl StringList { - pub fn new() -> Self { - Self(vec![]) - } - - pub fn with_capacity(size: usize) -> Self { - Self(Vec::with_capacity(size)) - } - - pub fn into_vec(self) -> Vec { - self.0 - } - - pub fn as_slice(&self) -> &[String] { - self.0.as_slice() - } -} - -impl Deref for StringList { - type Target = [String]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl AsRef<[String]> for StringList { - fn as_ref(&self) -> &[String] { - self.0.as_ref() - } -} - -impl From> for StringList { - fn from(v: Vec) -> Self { - Self(v) - } -} - -impl IntoIterator for StringList { - type IntoIter = as IntoIterator>::IntoIter; - type Item = as IntoIterator>::Item; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a StringList { - type IntoIter = slice::Iter<'a, String>; - type Item = ::Item; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'de> Deserialize<'de> for StringList { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - struct StringListVisitor; - - impl<'de> Visitor<'de> for StringListVisitor { - type Value = StringList; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "a comma delimited string or multiple string elements") - } - - fn visit_str(self, v: &str) -> Result - where E: de::Error { - Ok(StringList( - v.split(',') - .map(|s| s.trim()) - .filter(|s| !s.is_empty()) - .map(ToString::to_string) - .collect(), - )) - } - - fn visit_newtype_struct(self, deserializer: D) -> Result - where D: Deserializer<'de> { - deserializer.deserialize_seq(StringListVisitor) - } - - fn visit_seq(self, mut seq: A) -> Result - where A: SeqAccess<'de> { - let mut buf = seq.size_hint().map(Vec::with_capacity).unwrap_or_default(); - while let Some(v) = seq.next_element::()? { - buf.push(v) - } - Ok(StringList(buf)) - } - } - - if deserializer.is_human_readable() { - deserializer.deserialize_seq(StringListVisitor) - } else { - deserializer.deserialize_newtype_struct("StringList", StringListVisitor) - } - } -} +pub type StringList = ConfigList; #[cfg(test)] mod tests { + use std::vec; + use config::Config; + use serde::Deserialize; - use super::*; + use crate::configuration::StringList; #[derive(Deserialize)] struct Test { @@ -188,7 +80,7 @@ mod tests { fn it_deserializes_from_toml() { let config_str = r#"something = ["a","b","c"]"#; let test = toml::from_str::(config_str).unwrap(); - assert_eq!(test.something.0, vec!["a", "b", "c"]); + assert_eq!(test.something.into_vec(), vec!["a", "b", "c"]); } #[test] @@ -199,6 +91,6 @@ mod tests { .build() .unwrap(); let test = config.try_deserialize::().unwrap(); - assert_eq!(test.something.0, vec!["a", "b", "c"]); + assert_eq!(test.something.into_vec(), vec!["a", "b", "c"]); } } diff --git a/common/src/configuration/utils.rs b/common/src/configuration/utils.rs index cacd4cc0a6..816b2a88c0 100644 --- a/common/src/configuration/utils.rs +++ b/common/src/configuration/utils.rs @@ -4,7 +4,7 @@ use std::{fmt, fmt::Display, fs, fs::File, io::Write, marker::PhantomData, path::Path, str::FromStr}; use config::Config; -use log::{debug, info}; +use log::{debug, info, trace}; use serde::{ de::{self, MapAccess, Visitor}, Deserialize, @@ -28,29 +28,37 @@ pub fn load_configuration, TOverride: ConfigOverrideProvider>( create_if_not_exists: bool, non_interactive: bool, overrides: &TOverride, + cli_network: Option, ) -> Result { - debug!( - target: LOG_TARGET, - "Loading configuration file from {}", - config_path.as_ref().display() - ); - if !config_path.as_ref().exists() && create_if_not_exists { + if config_path.as_ref().exists() { + debug!( + target: LOG_TARGET, + "Using existing configuration file {}", + config_path.as_ref().display() + ); + } else if create_if_not_exists { let sources = if non_interactive { get_default_config(false) } else { prompt_default_config() }; + debug!( + target: LOG_TARGET, + "Created new configuration file {}", + config_path.as_ref().display() + ); write_config_to(&config_path, &sources) .map_err(|io| ConfigError::new("Could not create default config", Some(io.to_string())))?; } - load_configuration_with_overrides(config_path, overrides) + load_configuration_with_overrides(config_path, overrides, cli_network) } /// Loads the config at the given path applying all overrides. pub fn load_configuration_with_overrides, TOverride: ConfigOverrideProvider>( config_path: P, overrides: &TOverride, + cli_network: Option, ) -> Result { let filename = config_path .as_ref() @@ -66,24 +74,29 @@ pub fn load_configuration_with_overrides, TOverride: ConfigOverri .build() .map_err(|ce| ConfigError::new("Could not build config", Some(ce.to_string())))?; - let mut network = match cfg.get_string("network") { - Ok(network) => { - Network::from_str(&network).map_err(|e| ConfigError::new("Invalid network", Some(e.to_string())))? - }, - Err(config::ConfigError::NotFound(_)) => { - debug!(target: LOG_TARGET, "No network configuration found. Using default."); - Network::default() - }, - Err(e) => { - return Err(ConfigError::new( - "Could not get network configuration", - Some(e.to_string()), - )); + let network = match cli_network { + Some(val) => val, + None => match cfg.get_string("network") { + Ok(network) => { + Network::from_str(&network).map_err(|e| ConfigError::new("Invalid network", Some(e.to_string())))? + }, + Err(config::ConfigError::NotFound(_)) => { + debug!(target: LOG_TARGET, "No network configuration found. Using default."); + Network::default() + }, + Err(e) => { + return Err(ConfigError::new( + "Could not get network configuration", + Some(e.to_string()), + )); + }, }, }; info!(target: LOG_TARGET, "Configuration file loaded."); - let overrides = overrides.get_config_property_overrides(&mut network); + let overrides = overrides.get_config_property_overrides(&network); + trace!(target: LOG_TARGET, "Config property overrides: {:?}", overrides); + // Set the static network variable according to the user chosen network (for use with // `get_current_or_user_setting_or_default()`) - set_network_if_choice_valid(network)?; @@ -94,6 +107,7 @@ pub fn load_configuration_with_overrides, TOverride: ConfigOverri let mut cfg = Config::builder().add_source(cfg); for (key, value) in overrides { + trace!(target: LOG_TARGET, "Set override: ({}, {})", key, value); cfg = cfg .set_override(key.as_str(), value.as_str()) .map_err(|ce| ConfigError::new("Could not override config property", Some(ce.to_string())))?; @@ -182,7 +196,10 @@ where fn visit_str(self, value: &str) -> Result where E: de::Error { - Ok(FromStr::from_str(value).unwrap()) + match FromStr::from_str(value) { + Ok(val) => Ok(val), + Err(e) => Err(de::Error::custom(e)), + } } fn visit_map(self, map: M) -> Result diff --git a/integration_tests/src/base_node_process.rs b/integration_tests/src/base_node_process.rs index 14b9849e05..5168efd819 100644 --- a/integration_tests/src/base_node_process.rs +++ b/integration_tests/src/base_node_process.rs @@ -31,7 +31,7 @@ use std::{ }; use minotari_app_utilities::identity_management::save_as_json; -use minotari_node::{config::GrpcMethod, run_base_node, BaseNodeConfig, MetricsConfig}; +use minotari_node::{run_base_node, BaseNodeConfig, GrpcMethod, MetricsConfig}; use minotari_node_grpc_client::BaseNodeGrpcClient; use rand::rngs::OsRng; use tari_common::{ @@ -190,44 +190,7 @@ pub async fn spawn_base_node_with_config( if base_node_config.base_node.storage.pruning_horizon != 0 { base_node_config.base_node.storage.pruning_interval = 1; }; - base_node_config.base_node.grpc_server_allow_methods = vec![ - GrpcMethod::ListHeaders, - GrpcMethod::GetHeaderByHash, - GrpcMethod::GetBlocks, - GrpcMethod::GetBlockTiming, - GrpcMethod::GetConstants, - GrpcMethod::GetBlockSize, - GrpcMethod::GetBlockFees, - GrpcMethod::GetVersion, - GrpcMethod::CheckForUpdates, - GrpcMethod::GetTokensInCirculation, - GrpcMethod::GetNetworkDifficulty, - GrpcMethod::GetNewBlockTemplate, - GrpcMethod::GetNewBlock, - GrpcMethod::GetNewBlockWithCoinbases, - GrpcMethod::GetNewBlockTemplateWithCoinbases, - GrpcMethod::GetNewBlockBlob, - GrpcMethod::SubmitBlock, - GrpcMethod::SubmitBlockBlob, - GrpcMethod::SubmitTransaction, - GrpcMethod::GetSyncInfo, - GrpcMethod::GetSyncProgress, - GrpcMethod::GetTipInfo, - GrpcMethod::SearchKernels, - GrpcMethod::SearchUtxos, - GrpcMethod::FetchMatchingUtxos, - GrpcMethod::GetPeers, - GrpcMethod::GetMempoolTransactions, - GrpcMethod::TransactionState, - GrpcMethod::Identify, - GrpcMethod::GetNetworkStatus, - GrpcMethod::ListConnectedPeers, - GrpcMethod::GetMempoolStats, - GrpcMethod::GetActiveValidatorNodes, - GrpcMethod::GetShardKey, - GrpcMethod::GetTemplateRegistrations, - GrpcMethod::GetSideChainUtxos, - ]; + base_node_config.base_node.grpc_server_allow_methods = GrpcMethod::ALL_VARIANTS.to_vec().into(); // Hierarchically set the base path for all configs base_node_config.base_node.set_base_path(temp_dir_path.clone());