Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eecd685c77 |
Generated
+1
@@ -2851,6 +2851,7 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"eyre",
|
"eyre",
|
||||||
"futures",
|
"futures",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"mixnet-contract-common",
|
"mixnet-contract-common",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ bip39 = "1.0"
|
|||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
eyre = "0.6.5"
|
eyre = "0.6.5"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
|
itertools = "0.10"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.4"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::{error::BackendError, network::Network as WalletNetwork};
|
use crate::{error::BackendError, network::Network as WalletNetwork};
|
||||||
use config::defaults::{all::SupportedNetworks, ValidatorDetails};
|
use config::defaults::{all::SupportedNetworks, ValidatorDetails};
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
|
use itertools::Itertools;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -115,8 +116,10 @@ impl Config {
|
|||||||
.chain(self.network.validators(network))
|
.chain(self.network.validators(network))
|
||||||
.cloned()
|
.cloned()
|
||||||
.chain(base_validators)
|
.chain(base_validators)
|
||||||
|
.unique()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub fn get_validators_with_api_endpoint(
|
pub fn get_validators_with_api_endpoint(
|
||||||
&self,
|
&self,
|
||||||
network: WalletNetwork,
|
network: WalletNetwork,
|
||||||
@@ -164,6 +167,10 @@ impl Config {
|
|||||||
let client = reqwest::Client::builder()
|
let client = reqwest::Client::builder()
|
||||||
.timeout(Duration::from_secs(3))
|
.timeout(Duration::from_secs(3))
|
||||||
.build()?;
|
.build()?;
|
||||||
|
log::debug!(
|
||||||
|
"Fetching validator urls from: {}",
|
||||||
|
REMOTE_SOURCE_OF_VALIDATOR_URLS
|
||||||
|
);
|
||||||
let response = client
|
let response = client
|
||||||
.get(REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string())
|
.get(REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string())
|
||||||
.send()
|
.send()
|
||||||
@@ -183,6 +190,7 @@ impl Config {
|
|||||||
let validator_urls = validators_to_query().map(|v| {
|
let validator_urls = validators_to_query().map(|v| {
|
||||||
let mut health_url = v.nymd_url.clone();
|
let mut health_url = v.nymd_url.clone();
|
||||||
health_url.set_path("health");
|
health_url.set_path("health");
|
||||||
|
log::debug!("Checking health of: {health_url}");
|
||||||
(v, health_url)
|
(v, health_url)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -193,13 +201,18 @@ impl Config {
|
|||||||
let requests = validator_urls.map(|(_, url)| client.get(url).send());
|
let requests = validator_urls.map(|(_, url)| client.get(url).send());
|
||||||
let responses = futures::future::join_all(requests).await;
|
let responses = futures::future::join_all(requests).await;
|
||||||
|
|
||||||
let validators_responding_success =
|
let validators_responding_success = zip(validators_to_query(), responses)
|
||||||
zip(validators_to_query(), responses).filter_map(|(v, r)| match r {
|
.filter_map(|(v, r)| match r {
|
||||||
Ok(r) if r.status().is_success() => Some((v, r.status())),
|
Ok(r) if r.status().is_success() => Some((v, r.status())),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(validators_responding_success.collect::<Vec<_>>())
|
for validator in &validators_responding_success {
|
||||||
|
log::debug!("Returned success: {}", validator.0.nymd_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(validators_responding_success)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -223,7 +236,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct ValidatorUrl {
|
pub struct ValidatorUrl {
|
||||||
pub nymd_url: Url,
|
pub nymd_url: Url,
|
||||||
pub api_url: Option<Url>,
|
pub api_url: Option<Url>,
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
use crate::coin::{Coin, Denom};
|
use crate::coin::{Coin, Denom};
|
||||||
use crate::config::{Config, ValidatorUrlWithApiEndpoint};
|
use crate::config::{Config, ValidatorUrl};
|
||||||
use crate::error::BackendError;
|
use crate::error::BackendError;
|
||||||
use crate::network::Network;
|
use crate::network::Network;
|
||||||
use crate::nymd_client;
|
use crate::nymd_client;
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
|
||||||
use bip39::{Language, Mnemonic};
|
use bip39::{Language, Mnemonic};
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use reqwest::StatusCode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use validator_client::nymd::error::NymdError;
|
use url::Url;
|
||||||
use validator_client::nymd::SigningNymdClient;
|
use validator_client::nymd::SigningNymdClient;
|
||||||
use validator_client::Client;
|
use validator_client::Client;
|
||||||
|
|
||||||
@@ -101,7 +102,7 @@ pub async fn create_new_account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn create_new_mnemonic() -> Result<String, BackendError> {
|
pub fn create_new_mnemonic() -> Result<String, BackendError> {
|
||||||
let rand_mnemonic = random_mnemonic();
|
let rand_mnemonic = random_mnemonic();
|
||||||
Ok(rand_mnemonic.to_string())
|
Ok(rand_mnemonic.to_string())
|
||||||
}
|
}
|
||||||
@@ -155,9 +156,9 @@ async fn _connect_with_mnemonic(
|
|||||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
) -> Result<Account, BackendError> {
|
) -> Result<Account, BackendError> {
|
||||||
update_validator_urls(state.clone()).await?;
|
update_validator_urls(state.clone()).await?;
|
||||||
let validators = choose_validators(mnemonic.clone(), &state).await?;
|
|
||||||
|
|
||||||
let config = state.read().await.config();
|
let config = state.read().await.config().clone();
|
||||||
|
let validators = config.check_validator_health_for_all_networks().await?;
|
||||||
let clients = create_clients(&validators, &mnemonic, &config)?;
|
let clients = create_clients(&validators, &mnemonic, &config)?;
|
||||||
|
|
||||||
// Set the default account
|
// Set the default account
|
||||||
@@ -187,17 +188,22 @@ async fn _connect_with_mnemonic(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_clients(
|
fn create_clients(
|
||||||
validators: &HashMap<Network, ValidatorUrlWithApiEndpoint>,
|
validators: &HashMap<Network, Vec<(ValidatorUrl, StatusCode)>>,
|
||||||
mnemonic: &Mnemonic,
|
mnemonic: &Mnemonic,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Result<Vec<Client<SigningNymdClient>>, BackendError> {
|
) -> Result<Vec<Client<SigningNymdClient>>, BackendError> {
|
||||||
let mut clients = Vec::new();
|
let mut clients = Vec::new();
|
||||||
for network in Network::iter() {
|
for network in Network::iter() {
|
||||||
|
let nymd_url = select_validator_nymd_url(network, validators)?;
|
||||||
|
let api_url = select_validator_api_url(network, validators)?;
|
||||||
|
log::info!("{network}: nymd_url: connecting to {nymd_url}");
|
||||||
|
log::info!("{network}: api_url: connecting to {api_url}");
|
||||||
|
|
||||||
let client = validator_client::Client::new_signing(
|
let client = validator_client::Client::new_signing(
|
||||||
validator_client::Config::new(
|
validator_client::Config::new(
|
||||||
network.into(),
|
network.into(),
|
||||||
validators[&network].nymd_url.clone(),
|
nymd_url,
|
||||||
validators[&network].api_url.clone(),
|
api_url,
|
||||||
config.get_mixnet_contract_address(network),
|
config.get_mixnet_contract_address(network),
|
||||||
config.get_vesting_contract_address(network),
|
config.get_vesting_contract_address(network),
|
||||||
config.get_bandwidth_claim_contract_address(network),
|
config.get_bandwidth_claim_contract_address(network),
|
||||||
@@ -209,116 +215,24 @@ fn create_clients(
|
|||||||
Ok(clients)
|
Ok(clients)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn choose_validators(
|
fn select_validator_nymd_url(
|
||||||
mnemonic: Mnemonic,
|
|
||||||
state: &tauri::State<'_, Arc<RwLock<State>>>,
|
|
||||||
) -> Result<HashMap<Network, ValidatorUrlWithApiEndpoint>, BackendError> {
|
|
||||||
let config = state.read().await.config();
|
|
||||||
|
|
||||||
// Try to connect to validators on all networks
|
|
||||||
let mut validators = select_responding_validators(&config, &mnemonic).await?;
|
|
||||||
|
|
||||||
// If for a network we didn't manage to connect to any validators, just go ahead and try with the
|
|
||||||
// first in the list
|
|
||||||
for network in Network::iter() {
|
|
||||||
validators.entry(network).or_insert_with(|| {
|
|
||||||
let default_validator = config
|
|
||||||
.get_validators_with_api_endpoint(network)
|
|
||||||
.next()
|
|
||||||
// We always have at least one hardcoded default validator
|
|
||||||
.unwrap();
|
|
||||||
println!(
|
|
||||||
"Using default for {network}: {}, {}",
|
|
||||||
default_validator.nymd_url, default_validator.api_url,
|
|
||||||
);
|
|
||||||
default_validator
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(validators)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each network, try the list of available validators one by one and use the first responding
|
|
||||||
// one.
|
|
||||||
async fn select_responding_validators(
|
|
||||||
config: &Config,
|
|
||||||
mnemonic: &Mnemonic,
|
|
||||||
) -> Result<HashMap<Network, ValidatorUrlWithApiEndpoint>, BackendError> {
|
|
||||||
use tokio::time::timeout;
|
|
||||||
let validators = futures::future::join_all(Network::iter().map(|network| {
|
|
||||||
timeout(
|
|
||||||
Duration::from_millis(3000),
|
|
||||||
try_connect_to_validators(
|
|
||||||
config.get_validators_with_api_endpoint(network),
|
|
||||||
config,
|
|
||||||
network,
|
|
||||||
mnemonic.clone(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Drop networks that failed the global timeout
|
|
||||||
let validators = validators.into_iter().filter_map(Result::ok);
|
|
||||||
|
|
||||||
// Rewrap to return any errors during client creation
|
|
||||||
let validators = validators.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
// Filter out networks where we exhausted all listed validators
|
|
||||||
let validators = validators.into_iter().flatten();
|
|
||||||
|
|
||||||
Ok(validators.collect::<HashMap<_, _>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn try_connect_to_validators(
|
|
||||||
validators: impl Iterator<Item = ValidatorUrlWithApiEndpoint>,
|
|
||||||
config: &Config,
|
|
||||||
network: Network,
|
network: Network,
|
||||||
mnemonic: Mnemonic,
|
validators: &HashMap<Network, Vec<(ValidatorUrl, StatusCode)>>,
|
||||||
) -> Result<Option<(Network, ValidatorUrlWithApiEndpoint)>, BackendError> {
|
) -> Result<Url, BackendError> {
|
||||||
for validator in validators {
|
// For the nymd url we pick one at random
|
||||||
if let Some(responding_validator) =
|
validators[&network]
|
||||||
try_connect_to_validator(&validator, config, network, mnemonic.clone()).await?
|
.choose(&mut rand::thread_rng())
|
||||||
{
|
.map(|v| v.0.nymd_url.clone())
|
||||||
// Pick the first successful one
|
.ok_or(BackendError::NoNymdValidatorConfigured)
|
||||||
return Ok(Some(responding_validator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_connect_to_validator(
|
fn select_validator_api_url(
|
||||||
validator: &ValidatorUrlWithApiEndpoint,
|
|
||||||
config: &Config,
|
|
||||||
network: Network,
|
network: Network,
|
||||||
mnemonic: Mnemonic,
|
validators: &HashMap<Network, Vec<(ValidatorUrl, StatusCode)>>,
|
||||||
) -> Result<Option<(Network, ValidatorUrlWithApiEndpoint)>, BackendError> {
|
) -> Result<Url, BackendError> {
|
||||||
let client = validator_client::Client::new_signing(
|
// For the validator api we always just pick the first one
|
||||||
validator_client::Config::new(
|
validators[&network]
|
||||||
network.into(),
|
.get(0)
|
||||||
validator.nymd_url.clone(),
|
.and_then(|v| v.0.api_url.clone())
|
||||||
validator.api_url.clone(),
|
.ok_or(BackendError::NoValidatorApiUrlConfigured)
|
||||||
config.get_mixnet_contract_address(network),
|
|
||||||
config.get_vesting_contract_address(network),
|
|
||||||
config.get_bandwidth_claim_contract_address(network),
|
|
||||||
),
|
|
||||||
mnemonic,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if is_validator_connection_ok(&client).await {
|
|
||||||
println!(
|
|
||||||
"Connection ok for {network}: {}, {}",
|
|
||||||
validator.nymd_url, validator.api_url
|
|
||||||
);
|
|
||||||
Ok(Some((network, validator.clone())))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The criteria used to determina if a validator endpoint is to be used
|
|
||||||
async fn is_validator_connection_ok(client: &Client<SigningNymdClient>) -> bool {
|
|
||||||
match client.get_mixnet_contract_version().await {
|
|
||||||
Err(NymdError::TendermintError(_)) => false,
|
|
||||||
Err(_) | Ok(_) => true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ impl State {
|
|||||||
.ok_or(BackendError::ClientNotInitialized)
|
.ok_or(BackendError::ClientNotInitialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config(&self) -> Config {
|
pub fn config(&self) -> &Config {
|
||||||
self.config.clone()
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_client(&mut self, network: Network, client: Client<SigningNymdClient>) {
|
pub fn add_client(&mut self, network: Network, client: Client<SigningNymdClient>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user