| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::{
- fmt,
- path::{Path, PathBuf},
- str::FromStr,
- };
- use serde::Deserialize;
- use structopt::StructOpt;
- use tracing::{debug, error};
- use url::Url;
- use darkfi::{rpc::settings::RpcSettingsOpt, util::file::load_file, Error, Result};
- /// Represents an explorer configuration
- #[derive(Clone, Debug, Deserialize, StructOpt)]
- pub struct ExplorerConfig {
- /// Current active network
- #[allow(dead_code)] // Part of the config file
- pub network: String,
- /// Supported network configurations
- pub network_config: NetworkConfigs,
- /// Path to the configuration if read from a file
- pub path: Option<String>,
- }
- impl ExplorerConfig {
- /// Creates a new configuration from a given file path.
- /// If the file cannot be loaded or parsed, an error is returned.
- pub fn new(config_path: String) -> Result<Self> {
- // Load the configuration file from the specified path
- let config_content = load_file(Path::new(&config_path)).map_err(|err| {
- Error::ConfigError(format!(
- "Failed to read the configuration file {config_path}: {err:?}"
- ))
- })?;
- // Parse the loaded content into a configuration instance
- let mut config = toml::from_str::<Self>(&config_content).map_err(|e| {
- error!(target: "explorerd::config", "Failed parsing TOML config: {e}");
- Error::ConfigError(format!("Failed to parse the configuration file {config_path}"))
- })?;
- // Set the configuration path
- config.path = Some(config_path);
- debug!(target: "explorerd::config", "Successfully loaded configuration: {config:?}");
- Ok(config)
- }
- /// Returns the currently active network configuration.
- #[allow(dead_code)] // Test case currently using
- pub fn active_network_config(&self) -> Option<ExplorerNetworkConfig> {
- self.get_network_config(self.network.as_str())
- }
- /// Returns the network configuration for specified network.
- pub fn get_network_config(&self, network: &str) -> Option<ExplorerNetworkConfig> {
- match network {
- "localnet" => self.network_config.localnet.clone(),
- "testnet" => self.network_config.testnet.clone(),
- "mainnet" => self.network_config.mainnet.clone(),
- _ => None,
- }
- }
- }
- /// Provides a default `ExplorerConfig` configuration using the `testnet` network.
- impl Default for ExplorerConfig {
- fn default() -> Self {
- Self {
- network: String::from("testnet"),
- network_config: NetworkConfigs::default(),
- path: None,
- }
- }
- }
- /// Attempts to convert a [`PathBuff`] to an [`ExplorerConfig`] by loading and parsing from specified file path.
- impl TryFrom<&PathBuf> for ExplorerConfig {
- type Error = Error;
- fn try_from(path: &PathBuf) -> Result<Self> {
- let path_str = path.to_str().ok_or_else(|| {
- Error::ConfigError("Unable to convert PathBuf to a valid UTF-8 path string".to_string())
- })?;
- // Create configuration and return
- ExplorerConfig::new(path_str.to_string())
- }
- }
- /// Deserializes a `&str` containing explorer content in TOML format into an [`ExplorerConfig`] instance.
- impl FromStr for ExplorerConfig {
- type Err = String;
- fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
- let config: ExplorerConfig =
- toml::from_str(s).map_err(|e| format!("Failed to parse ExplorerdConfig: {e}"))?;
- Ok(config)
- }
- }
- /// Represents network configurations for localnet, testnet, and mainnet.
- #[derive(Debug, Clone, Deserialize, StructOpt)]
- pub struct NetworkConfigs {
- /// Local network configuration
- pub localnet: Option<ExplorerNetworkConfig>,
- /// Testnet network configuration
- pub testnet: Option<ExplorerNetworkConfig>,
- /// Mainnet network configuration
- pub mainnet: Option<ExplorerNetworkConfig>,
- }
- /// Provides a default `NetworkConfigs` configuration using the `testnet` network.
- impl Default for NetworkConfigs {
- fn default() -> Self {
- NetworkConfigs {
- localnet: None,
- testnet: Some(ExplorerNetworkConfig::default()),
- mainnet: None,
- }
- }
- }
- /// Deserializes a `&str` containing network configs content in TOML format into an [`NetworkConfigs`] instance.
- impl FromStr for NetworkConfigs {
- type Err = String;
- fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
- let config: NetworkConfigs =
- toml::from_str(s).map_err(|e| format!("Failed to parse NetworkConfigs: {e}"))?;
- Ok(config)
- }
- }
- /// Struct representing the configuration for an explorer network.
- #[derive(Clone, Deserialize, StructOpt)]
- #[structopt()]
- #[serde(default)]
- pub struct ExplorerNetworkConfig {
- #[structopt(flatten)]
- /// JSON-RPC settings used to set up a server that the explorer listens on for incoming RPC requests.
- pub rpc: RpcSettingsOpt,
- #[structopt(long, default_value = "~/.local/share/darkfi/explorerd/testnet")]
- /// Path to the explorer's database.
- pub database: String,
- #[structopt(short, long, default_value = "tcp://127.0.0.1:8340")]
- /// Endpoint of the DarkFi node JSON-RPC server to sync with.
- pub endpoint: Url,
- }
- /// Attempts to convert a tuple `(PathBuf, &str)` representing a configuration file path
- /// and network name into an `ExplorerNetworkConfig`.
- impl TryFrom<(&PathBuf, &String)> for ExplorerNetworkConfig {
- type Error = Error;
- fn try_from(path_and_network: (&PathBuf, &String)) -> Result<Self> {
- // Load the ExplorerConfig from the given file path
- let config: ExplorerConfig = path_and_network.0.try_into()?;
- // Retrieve the network configuration for the specified network
- match config.get_network_config(path_and_network.1) {
- Some(config) => Ok(config),
- None => Err(Error::ConfigError(format!(
- "Failed to retrieve network configuration for network: {}",
- path_and_network.1
- ))),
- }
- }
- }
- /// Provides a default `ExplorerNetworkConfig` instance using `structopt` default values defined
- /// in the `ExplorerNetworkConfig` struct.
- impl Default for ExplorerNetworkConfig {
- fn default() -> Self {
- Self::from_iter(&[""])
- }
- }
- /// Provides a user-friendly debug view of the `ExplorerdNetworkConfig` configuration.
- impl fmt::Debug for ExplorerNetworkConfig {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut debug_struct = f.debug_struct("ExplorerdConfig");
- debug_struct
- .field("rpc_listen", &self.rpc.rpc_listen.to_string().trim_end_matches('/'))
- .field("db_path", &self.database)
- .field("endpoint", &self.endpoint.to_string().trim_end_matches('/'));
- debug_struct.finish()
- }
- }
- /// Deserializes a `&str` containing network config content in TOML format into an [`ExplorerNetworkConfig`] instance.
- impl FromStr for ExplorerNetworkConfig {
- type Err = String;
- fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
- let config: ExplorerNetworkConfig = toml::from_str(s)
- .map_err(|e| format!("Failed to parse ExplorerdNetworkConfig: {e}"))?;
- Ok(config)
- }
- }
- #[cfg(test)]
- /// Contains test cases for validating the functionality and correctness of the `ExplorerConfig`
- /// and related components using a configuration loaded from a TOML file.
- mod tests {
- use std::path::Path;
- use super::*;
- use crate::test_utils::init_logger;
- /// Validates the functionality of initializing and interacting with `ExplorerConfig`
- /// loaded from a TOML file, ensuring correctness of the network-specific configurations.
- #[test]
- fn test_explorerd_config_from_file() {
- // Constants for expected configurations
- const CONFIG_PATH: &str = "explorerd_config.toml";
- const ACTIVE_NETWORK: &str = "testnet";
- const NETWORK_CONFIGS: &[(&str, &str, &str, &str)] = &[
- (
- "localnet",
- "~/.local/share/darkfi/explorerd/localnet",
- "tcp://127.0.0.1:8240/",
- "tcp://127.0.0.1:14567/",
- ),
- (
- "testnet",
- "~/.local/share/darkfi/explorerd/testnet",
- "tcp://127.0.0.1:8340/",
- "tcp://127.0.0.1:14667/",
- ),
- (
- "mainnet",
- "~/.local/share/darkfi/explorerd/mainnet",
- "tcp://127.0.0.1:8440/",
- "tcp://127.0.0.1:14767/",
- ),
- ];
- init_logger(simplelog::LevelFilter::Info, vec!["sled", "runtime", "net"]);
- // Ensure the configuration file exists
- assert!(Path::new(CONFIG_PATH).exists());
- // Load the configuration
- let config = ExplorerConfig::new(CONFIG_PATH.to_string())
- .expect("Failed to load configuration from file");
- // Validate the expected network
- assert_eq!(config.network, ACTIVE_NETWORK);
- // Validate the path is correctly set
- assert_eq!(config.path.as_deref(), Some(CONFIG_PATH));
- // Validate that `active_network_config` correctly retrieves the testnet configuration
- let active_config = config.active_network_config();
- assert!(active_config.is_some(), "Active network configuration should not be None.");
- let active_config = active_config.unwrap();
- assert_eq!(active_config.database, NETWORK_CONFIGS[1].1); // Testnet database
- assert_eq!(active_config.endpoint.to_string(), NETWORK_CONFIGS[1].2);
- assert_eq!(&active_config.rpc.rpc_listen.to_string(), NETWORK_CONFIGS[1].3);
- // Validate all network configurations values (localnet, testnet, mainnet)
- for &(network, expected_db, expected_endpoint, expected_rpc) in NETWORK_CONFIGS {
- let network_config = config.get_network_config(network);
- if let Some(config) = network_config {
- assert_eq!(config.database, expected_db);
- assert_eq!(config.endpoint.to_string(), expected_endpoint);
- assert_eq!(config.rpc.rpc_listen.to_string(), expected_rpc);
- } else {
- assert!(network_config.is_none(), "{network} configuration is missing");
- }
- }
- // Validate (path, network).try_into()
- let config_path_buf = &PathBuf::from(CONFIG_PATH);
- let mainnet_string = &String::from("mainnet");
- let mainnet_config: ExplorerNetworkConfig = (config_path_buf, mainnet_string)
- .try_into()
- .expect("Failed to load explorer network config");
- assert_eq!(mainnet_config.database, NETWORK_CONFIGS[2].1); // Mainnet database
- assert_eq!(mainnet_config.endpoint.to_string(), NETWORK_CONFIGS[2].2);
- assert_eq!(&mainnet_config.rpc.rpc_listen.to_string(), NETWORK_CONFIGS[2].3);
- }
- }
|