diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 40925ea2be..ed93d1da46 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -100,25 +100,31 @@ impl Config { /// 1. from the configuration file /// 2. provided remotely /// 3. hardcoded fallback - pub fn get_validators( - &self, - network: WalletNetwork, - ) -> impl Iterator + '_ { + pub fn get_validators(&self, network: WalletNetwork) -> impl Iterator + '_ { + // The base validators are (currently) stored as strings + let base_validators = self.base.networks.validators(network.into()).map(|v| { + v.clone() + .try_into() + .expect("The hardcoded validators are assumed to be valid urls") + }); + self .base .fetched_validators .validators(network) .chain(self.network.validators(network)) - .chain(self.base.networks.validators(network.into())) + .cloned() + .chain(base_validators) } pub fn get_validators_with_api_endpoint( &self, network: WalletNetwork, - ) -> impl Iterator + '_ { + ) -> impl Iterator + '_ { self .get_validators(network) - .filter_map(|validator| ValidatorWithApiEndpoint::try_from(validator.clone()).ok()) + .into_iter() + .filter_map(|validator| ValidatorUrlWithApiEndpoint::try_from(validator).ok()) } pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option { @@ -169,13 +175,13 @@ impl Config { pub async fn check_validator_health( &self, network: WalletNetwork, - ) -> Result, BackendError> { + ) -> Result, BackendError> { // Limit the number of validators we query let max_validators = 200_usize; - let validators_to_query = || self.get_validators(network).take(max_validators).cloned(); + let validators_to_query = || self.get_validators(network).take(max_validators); let validator_urls = validators_to_query().map(|v| { - let mut health_url = v.nymd_url(); + let mut health_url = v.nymd_url.clone(); health_url.set_path("health"); (v, health_url) }); @@ -199,7 +205,7 @@ impl Config { #[allow(unused)] pub async fn check_validator_health_for_all_networks( &self, - ) -> Result>, BackendError> { + ) -> Result>, BackendError> { let validator_health_requests = WalletNetwork::iter().map(|network| self.check_validator_health(network)); @@ -217,21 +223,39 @@ impl Config { } } -// Unlike `ValidatorDetails` this represents validators which are always supposed to have a -// validator-api endpoint running. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct ValidatorUrl { + pub nymd_url: Url, + pub api_url: Option, +} + +impl TryFrom for ValidatorUrl { + type Error = BackendError; + + fn try_from(validator: ValidatorDetails) -> Result { + Ok(ValidatorUrl { + nymd_url: validator.nymd_url.parse()?, + api_url: match &validator.api_url { + Some(url) => Some(url.parse()?), + None => None, + }, + }) + } +} + #[derive(Clone, Debug)] -pub struct ValidatorWithApiEndpoint { +pub struct ValidatorUrlWithApiEndpoint { pub nymd_url: Url, pub api_url: Url, } -impl TryFrom for ValidatorWithApiEndpoint { +impl TryFrom for ValidatorUrlWithApiEndpoint { type Error = BackendError; - fn try_from(validator: ValidatorDetails) -> Result { - match validator.api_url() { - Some(api_url) => Ok(ValidatorWithApiEndpoint { - nymd_url: validator.nymd_url(), + fn try_from(validator: ValidatorUrl) -> Result { + match validator.api_url { + Some(api_url) => Ok(ValidatorUrlWithApiEndpoint { + nymd_url: validator.nymd_url, api_url, }), None => Err(BackendError::NoValidatorApiUrlConfigured), @@ -244,13 +268,13 @@ impl TryFrom for ValidatorWithApiEndpoint { struct OptionalValidators { // User supplied additional validator urls in addition to the hardcoded ones. // These are separate fields, rather than a map, to force the serialization order. - mainnet: Option>, - sandbox: Option>, - qa: Option>, + mainnet: Option>, + sandbox: Option>, + qa: Option>, } impl OptionalValidators { - fn validators(&self, network: WalletNetwork) -> impl Iterator { + fn validators(&self, network: WalletNetwork) -> impl Iterator { match network { WalletNetwork::MAINNET => self.mainnet.as_ref(), WalletNetwork::SANDBOX => self.sandbox.as_ref(), @@ -273,15 +297,17 @@ mod tests { ValidatorDetails { nymd_url: "https://foo".to_string(), api_url: None, - }, - ValidatorDetails { - nymd_url: "https://baz".to_string(), - api_url: Some("https://baz/api".to_string()), + } + .try_into() + .unwrap(), + ValidatorUrl { + nymd_url: "https://baz".parse().unwrap(), + api_url: Some("https://baz/api".parse().unwrap()), }, ]), - sandbox: Some(vec![ValidatorDetails { - nymd_url: "https://bar".to_string(), - api_url: Some("https://bar/api".to_string()), + sandbox: Some(vec![ValidatorUrl { + nymd_url: "https://bar".parse().unwrap(), + api_url: Some("https://bar/api".parse().unwrap()), }]), qa: None, }, @@ -293,14 +319,14 @@ mod tests { assert_eq!( toml::to_string_pretty(&test_config()).unwrap(), r#"[[network.mainnet]] -nymd_url = 'https://foo' +nymd_url = 'https://foo/' [[network.mainnet]] -nymd_url = 'https://baz' +nymd_url = 'https://baz/' api_url = 'https://baz/api' [[network.sandbox]] -nymd_url = 'https://bar' +nymd_url = 'https://bar/' api_url = 'https://bar/api' "# ); @@ -320,15 +346,15 @@ api_url = 'https://bar/api' let nymd_url = config .get_validators(WalletNetwork::MAINNET) .next() - .map(ValidatorDetails::nymd_url) + .map(|v| v.nymd_url) .unwrap(); - assert_eq!(nymd_url.to_string(), "https://foo/".to_string()); + assert_eq!(nymd_url.as_ref(), "https://foo/"); // The first entry is missing an API URL let api_url = config .get_validators(WalletNetwork::MAINNET) .next() - .and_then(ValidatorDetails::api_url); + .and_then(|v| v.api_url); assert_eq!(api_url, None); } @@ -339,21 +365,15 @@ api_url = 'https://bar/api' let nymd_url = config .get_validators(WalletNetwork::MAINNET) .next() - .map(ValidatorDetails::nymd_url) + .map(|v| v.nymd_url) .unwrap(); - assert_eq!( - nymd_url.to_string(), - "https://rpc.nyx.nodes.guru/".to_string() - ); + assert_eq!(nymd_url.as_ref(), "https://rpc.nyx.nodes.guru/"); let api_url = config .get_validators(WalletNetwork::MAINNET) .next() - .and_then(ValidatorDetails::api_url) + .and_then(|v| v.api_url) .unwrap(); - assert_eq!( - api_url.to_string(), - "https://api.nyx.nodes.guru/".to_string() - ); + assert_eq!(api_url.as_ref(), "https://api.nyx.nodes.guru/",); } } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 334136f6f2..a390fecb3d 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -1,5 +1,5 @@ use crate::coin::{Coin, Denom}; -use crate::config::{Config, ValidatorWithApiEndpoint}; +use crate::config::{Config, ValidatorUrlWithApiEndpoint}; use crate::error::BackendError; use crate::network::Network; use crate::nymd_client; @@ -181,7 +181,7 @@ async fn _connect_with_mnemonic( } fn create_clients( - validators: &HashMap, + validators: &HashMap, mnemonic: &Mnemonic, config: &Config, ) -> Result>, BackendError> { @@ -206,7 +206,7 @@ fn create_clients( async fn choose_validators( mnemonic: Mnemonic, state: &tauri::State<'_, Arc>>, -) -> Result, BackendError> { +) -> Result, BackendError> { let config = state.read().await.config(); // Try to connect to validators on all networks @@ -236,7 +236,7 @@ async fn choose_validators( async fn select_responding_validators( config: &Config, mnemonic: &Mnemonic, -) -> Result, BackendError> { +) -> Result, BackendError> { use tokio::time::timeout; let validators = futures::future::join_all(Network::iter().map(|network| { timeout( @@ -264,11 +264,11 @@ async fn select_responding_validators( } async fn try_connect_to_validators( - validators: impl Iterator, + validators: impl Iterator, config: &Config, network: Network, mnemonic: Mnemonic, -) -> Result, BackendError> { +) -> Result, BackendError> { for validator in validators { if let Some(responding_validator) = try_connect_to_validator(&validator, config, network, mnemonic.clone()).await? @@ -281,11 +281,11 @@ async fn try_connect_to_validators( } async fn try_connect_to_validator( - validator: &ValidatorWithApiEndpoint, + validator: &ValidatorUrlWithApiEndpoint, config: &Config, network: Network, mnemonic: Mnemonic, -) -> Result, BackendError> { +) -> Result, BackendError> { let client = validator_client::Client::new_signing( validator_client::Config::new( network.into(),