Skip to content

wallet: use Urls rather than Strings for validator urls #1148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 66 additions & 46 deletions nym-wallet/src-tauri/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = &ValidatorDetails> + '_ {
pub fn get_validators(&self, network: WalletNetwork) -> impl Iterator<Item = ValidatorUrl> + '_ {
// 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<Item = ValidatorWithApiEndpoint> + '_ {
) -> impl Iterator<Item = ValidatorUrlWithApiEndpoint> + '_ {
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<cosmrs::AccountId> {
Expand Down Expand Up @@ -169,13 +175,13 @@ impl Config {
pub async fn check_validator_health(
&self,
network: WalletNetwork,
) -> Result<Vec<(ValidatorDetails, StatusCode)>, BackendError> {
) -> Result<Vec<(ValidatorUrl, StatusCode)>, 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)
});
Expand All @@ -199,7 +205,7 @@ impl Config {
#[allow(unused)]
pub async fn check_validator_health_for_all_networks(
&self,
) -> Result<HashMap<WalletNetwork, Vec<(ValidatorDetails, StatusCode)>>, BackendError> {
) -> Result<HashMap<WalletNetwork, Vec<(ValidatorUrl, StatusCode)>>, BackendError> {
let validator_health_requests =
WalletNetwork::iter().map(|network| self.check_validator_health(network));

Expand All @@ -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<Url>,
}

impl TryFrom<ValidatorDetails> for ValidatorUrl {
type Error = BackendError;

fn try_from(validator: ValidatorDetails) -> Result<Self, Self::Error> {
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<ValidatorDetails> for ValidatorWithApiEndpoint {
impl TryFrom<ValidatorUrl> for ValidatorUrlWithApiEndpoint {
type Error = BackendError;

fn try_from(validator: ValidatorDetails) -> Result<Self, Self::Error> {
match validator.api_url() {
Some(api_url) => Ok(ValidatorWithApiEndpoint {
nymd_url: validator.nymd_url(),
fn try_from(validator: ValidatorUrl) -> Result<Self, Self::Error> {
match validator.api_url {
Some(api_url) => Ok(ValidatorUrlWithApiEndpoint {
nymd_url: validator.nymd_url,
api_url,
}),
None => Err(BackendError::NoValidatorApiUrlConfigured),
Expand All @@ -244,13 +268,13 @@ impl TryFrom<ValidatorDetails> 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<Vec<ValidatorDetails>>,
sandbox: Option<Vec<ValidatorDetails>>,
qa: Option<Vec<ValidatorDetails>>,
mainnet: Option<Vec<ValidatorUrl>>,
sandbox: Option<Vec<ValidatorUrl>>,
qa: Option<Vec<ValidatorUrl>>,
}

impl OptionalValidators {
fn validators(&self, network: WalletNetwork) -> impl Iterator<Item = &ValidatorDetails> {
fn validators(&self, network: WalletNetwork) -> impl Iterator<Item = &ValidatorUrl> {
match network {
WalletNetwork::MAINNET => self.mainnet.as_ref(),
WalletNetwork::SANDBOX => self.sandbox.as_ref(),
Expand All @@ -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,
},
Expand All @@ -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'
"#
);
Expand All @@ -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);
}

Expand All @@ -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/",);
}
}
16 changes: 8 additions & 8 deletions nym-wallet/src-tauri/src/operations/mixnet/account.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -181,7 +181,7 @@ async fn _connect_with_mnemonic(
}

fn create_clients(
validators: &HashMap<Network, ValidatorWithApiEndpoint>,
validators: &HashMap<Network, ValidatorUrlWithApiEndpoint>,
mnemonic: &Mnemonic,
config: &Config,
) -> Result<Vec<Client<SigningNymdClient>>, BackendError> {
Expand All @@ -206,7 +206,7 @@ fn create_clients(
async fn choose_validators(
mnemonic: Mnemonic,
state: &tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<HashMap<Network, ValidatorWithApiEndpoint>, BackendError> {
) -> Result<HashMap<Network, ValidatorUrlWithApiEndpoint>, BackendError> {
let config = state.read().await.config();

// Try to connect to validators on all networks
Expand Down Expand Up @@ -236,7 +236,7 @@ async fn choose_validators(
async fn select_responding_validators(
config: &Config,
mnemonic: &Mnemonic,
) -> Result<HashMap<Network, ValidatorWithApiEndpoint>, BackendError> {
) -> Result<HashMap<Network, ValidatorUrlWithApiEndpoint>, BackendError> {
use tokio::time::timeout;
let validators = futures::future::join_all(Network::iter().map(|network| {
timeout(
Expand Down Expand Up @@ -264,11 +264,11 @@ async fn select_responding_validators(
}

async fn try_connect_to_validators(
validators: impl Iterator<Item = ValidatorWithApiEndpoint>,
validators: impl Iterator<Item = ValidatorUrlWithApiEndpoint>,
config: &Config,
network: Network,
mnemonic: Mnemonic,
) -> Result<Option<(Network, ValidatorWithApiEndpoint)>, BackendError> {
) -> Result<Option<(Network, ValidatorUrlWithApiEndpoint)>, BackendError> {
for validator in validators {
if let Some(responding_validator) =
try_connect_to_validator(&validator, config, network, mnemonic.clone()).await?
Expand All @@ -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<Option<(Network, ValidatorWithApiEndpoint)>, BackendError> {
) -> Result<Option<(Network, ValidatorUrlWithApiEndpoint)>, BackendError> {
let client = validator_client::Client::new_signing(
validator_client::Config::new(
network.into(),
Expand Down