wallet: add backend support for validator name (#1262)
* wallet: add support for validator nymd name * changelog: add entry for wallt validator name * rustfmt * wallet: keep nymd_name entirely on wallet side * wallet: lint fixes
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
### Added
|
||||
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- validator-api: add Swagger to document the REST API ([#1249]).
|
||||
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
|
||||
|
||||
@@ -101,6 +101,13 @@ impl ValidatorDetails {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_name(nymd_url: &str, api_url: Option<&str>) -> Self {
|
||||
ValidatorDetails {
|
||||
nymd_url: nymd_url.to_string(),
|
||||
api_url: api_url.map(ToString::to_string),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nymd_url(&self) -> Url {
|
||||
self.nymd_url
|
||||
.parse()
|
||||
|
||||
@@ -21,6 +21,6 @@ pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hh
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://rpc.nyx.nodes.guru/",
|
||||
Some("https://validator.nymtech.net/api"),
|
||||
Some("https://validator.nymtech.net/api/"),
|
||||
)]
|
||||
}
|
||||
|
||||
Generated
+1
@@ -2965,6 +2965,7 @@ dependencies = [
|
||||
"itertools",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"once_cell",
|
||||
"pretty_env_logger",
|
||||
"rand 0.6.5",
|
||||
"reqwest",
|
||||
|
||||
@@ -28,6 +28,7 @@ eyre = "0.6.5"
|
||||
futures = "0.3.15"
|
||||
itertools = "0.10"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
pretty_env_logger = "0.4"
|
||||
rand = "0.6.5"
|
||||
reqwest = "0.11.9"
|
||||
|
||||
@@ -57,7 +57,7 @@ pub struct NetworkConfig {
|
||||
|
||||
// Additional user provided validators.
|
||||
// It is an option for the purpuse of file serialization.
|
||||
validator_urls: Option<Vec<ValidatorUrl>>,
|
||||
validator_urls: Option<Vec<ValidatorConfigEntry>>,
|
||||
}
|
||||
|
||||
impl Default for Base {
|
||||
@@ -89,7 +89,7 @@ impl Default for NetworkConfig {
|
||||
}
|
||||
|
||||
impl NetworkConfig {
|
||||
fn validators(&self) -> impl Iterator<Item = &ValidatorUrl> {
|
||||
fn validators(&self) -> impl Iterator<Item = &ValidatorConfigEntry> {
|
||||
self.validator_urls.iter().flat_map(|v| v.iter())
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ impl Config {
|
||||
pub fn get_base_validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = ValidatorUrl> + '_ {
|
||||
) -> impl Iterator<Item = ValidatorConfigEntry> + '_ {
|
||||
self.base.networks.validators(network.into()).map(|v| {
|
||||
v.clone()
|
||||
.try_into()
|
||||
@@ -203,7 +203,7 @@ impl Config {
|
||||
pub fn get_configured_validators(
|
||||
&self,
|
||||
network: WalletNetwork,
|
||||
) -> impl Iterator<Item = ValidatorUrl> + '_ {
|
||||
) -> impl Iterator<Item = ValidatorConfigEntry> + '_ {
|
||||
self
|
||||
.networks
|
||||
.get(&network.as_key())
|
||||
@@ -272,7 +272,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_selected_validator_nymd_url(&self, network: &WalletNetwork) -> Option<Url> {
|
||||
pub fn get_selected_validator_nymd_url(&self, network: WalletNetwork) -> Option<Url> {
|
||||
self
|
||||
.networks
|
||||
.get(&network.as_key())
|
||||
@@ -286,7 +286,7 @@ impl Config {
|
||||
.and_then(|config| config.selected_api_url.clone())
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: ValidatorUrl, network: WalletNetwork) {
|
||||
pub fn add_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
urls.push(url);
|
||||
@@ -304,7 +304,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorUrl, network: WalletNetwork) {
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorConfigEntry, network: WalletNetwork) {
|
||||
if let Some(network_config) = self.networks.get_mut(&network.as_key()) {
|
||||
if let Some(ref mut urls) = network_config.validator_urls {
|
||||
// Removes duplicates too if there are any
|
||||
@@ -325,17 +325,19 @@ where
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct ValidatorUrl {
|
||||
pub struct ValidatorConfigEntry {
|
||||
pub nymd_url: Url,
|
||||
pub nymd_name: Option<String>,
|
||||
pub api_url: Option<Url>,
|
||||
}
|
||||
|
||||
impl TryFrom<ValidatorDetails> for ValidatorUrl {
|
||||
impl TryFrom<ValidatorDetails> for ValidatorConfigEntry {
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(validator: ValidatorDetails) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorUrl {
|
||||
Ok(ValidatorConfigEntry {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
nymd_name: None,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
@@ -344,12 +346,13 @@ impl TryFrom<ValidatorDetails> for ValidatorUrl {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<network_config::Validator> for ValidatorUrl {
|
||||
impl TryFrom<network_config::Validator> for ValidatorConfigEntry {
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(validator: network_config::Validator) -> Result<Self, Self::Error> {
|
||||
Ok(ValidatorUrl {
|
||||
Ok(ValidatorConfigEntry {
|
||||
nymd_url: validator.nymd_url.parse()?,
|
||||
nymd_name: validator.nymd_name,
|
||||
api_url: match &validator.api_url {
|
||||
Some(url) => Some(url.parse()?),
|
||||
None => None,
|
||||
@@ -358,14 +361,21 @@ impl TryFrom<network_config::Validator> for ValidatorUrl {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ValidatorUrl {
|
||||
impl fmt::Display for ValidatorConfigEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let s1 = format!("nymd_url: {}", self.nymd_url);
|
||||
let name = self.nymd_name.as_ref().map(|name| format!(" ({})", name));
|
||||
let s2 = self
|
||||
.api_url
|
||||
.as_ref()
|
||||
.map(|url| format!(", api_url: {}", url));
|
||||
write!(f, " {}{},", s1, s2.unwrap_or_default())
|
||||
write!(
|
||||
f,
|
||||
" {}{}{},",
|
||||
s1,
|
||||
name.unwrap_or_default(),
|
||||
s2.unwrap_or_default()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,13 +384,13 @@ impl fmt::Display for ValidatorUrl {
|
||||
pub 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<ValidatorUrl>>,
|
||||
sandbox: Option<Vec<ValidatorUrl>>,
|
||||
qa: Option<Vec<ValidatorUrl>>,
|
||||
mainnet: Option<Vec<ValidatorConfigEntry>>,
|
||||
sandbox: Option<Vec<ValidatorConfigEntry>>,
|
||||
qa: Option<Vec<ValidatorConfigEntry>>,
|
||||
}
|
||||
|
||||
impl OptionalValidators {
|
||||
pub fn validators(&self, network: WalletNetwork) -> impl Iterator<Item = &ValidatorUrl> {
|
||||
pub fn validators(&self, network: WalletNetwork) -> impl Iterator<Item = &ValidatorConfigEntry> {
|
||||
match network {
|
||||
WalletNetwork::MAINNET => self.mainnet.as_ref(),
|
||||
WalletNetwork::SANDBOX => self.sandbox.as_ref(),
|
||||
@@ -422,16 +432,19 @@ mod tests {
|
||||
selected_api_url: Some("https://my_api_url.com".parse().unwrap()),
|
||||
|
||||
validator_urls: Some(vec![
|
||||
ValidatorUrl {
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://foo".parse().unwrap(),
|
||||
nymd_name: Some("FooName".to_string()),
|
||||
api_url: None,
|
||||
},
|
||||
ValidatorUrl {
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://bar".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: Some("https://bar/api".parse().unwrap()),
|
||||
},
|
||||
ValidatorUrl {
|
||||
ValidatorConfigEntry {
|
||||
nymd_url: "https://baz".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: Some("https://baz/api".parse().unwrap()),
|
||||
},
|
||||
]),
|
||||
@@ -458,6 +471,7 @@ selected_api_url = 'https://my_api_url.com/'
|
||||
|
||||
[[validator_urls]]
|
||||
nymd_url = 'https://foo/'
|
||||
nymd_name = 'FooName'
|
||||
|
||||
[[validator_urls]]
|
||||
nymd_url = 'https://bar/'
|
||||
@@ -469,6 +483,39 @@ api_url = 'https://baz/api'
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_to_json() {
|
||||
let config = test_config();
|
||||
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
|
||||
println!("{}", serde_json::to_string_pretty(netconfig).unwrap());
|
||||
assert_eq!(
|
||||
serde_json::to_string_pretty(netconfig).unwrap(),
|
||||
r#"{
|
||||
"version": 1,
|
||||
"selected_nymd_url": null,
|
||||
"selected_api_url": "https://my_api_url.com/",
|
||||
"validator_urls": [
|
||||
{
|
||||
"nymd_url": "https://foo/",
|
||||
"nymd_name": "FooName",
|
||||
"api_url": null
|
||||
},
|
||||
{
|
||||
"nymd_url": "https://bar/",
|
||||
"nymd_name": null,
|
||||
"api_url": "https://bar/api"
|
||||
},
|
||||
{
|
||||
"nymd_url": "https://baz/",
|
||||
"nymd_name": null,
|
||||
"api_url": "https://baz/api"
|
||||
}
|
||||
]
|
||||
}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_and_deserialize_to_toml() {
|
||||
let config = test_config();
|
||||
@@ -513,6 +560,6 @@ api_url = 'https://baz/api'
|
||||
.next()
|
||||
.and_then(|v| v.api_url)
|
||||
.unwrap();
|
||||
assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api",);
|
||||
assert_eq!(api_url.as_ref(), "https://validator.nymtech.net/api/",);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,18 +9,30 @@ use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
// When the UI queries validator urls we use this type
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ValidatorUrls {
|
||||
pub urls: Vec<String>,
|
||||
pub urls: Vec<ValidatorUrl>,
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurl.ts"))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ValidatorUrl {
|
||||
pub url: String,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
// The type used when adding or removing validators, effectively the input.
|
||||
// NOTE: we should consider if we want to split this up
|
||||
#[cfg_attr(test, derive(ts_rs::TS))]
|
||||
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Validator {
|
||||
pub nymd_url: String,
|
||||
pub nymd_name: Option<String>,
|
||||
pub api_url: Option<String>,
|
||||
}
|
||||
|
||||
@@ -42,10 +54,7 @@ pub async fn get_validator_nymd_urls(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<String> = state
|
||||
.get_nymd_urls(network)
|
||||
.map(|url| url.to_string())
|
||||
.collect();
|
||||
let urls: Vec<ValidatorUrl> = state.get_nymd_urls(network).collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
@@ -55,10 +64,7 @@ pub async fn get_validator_api_urls(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<ValidatorUrls, BackendError> {
|
||||
let state = state.read().await;
|
||||
let urls: Vec<String> = state
|
||||
.get_api_urls(network)
|
||||
.map(|url| url.to_string())
|
||||
.collect();
|
||||
let urls: Vec<ValidatorUrl> = state.get_api_urls(network).collect();
|
||||
Ok(ValidatorUrls { urls })
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ async fn _connect_with_mnemonic(
|
||||
for network in WalletNetwork::iter() {
|
||||
log::debug!(
|
||||
"List of validators for {network}: [\n{}\n]",
|
||||
state.get_validators(network).format(",\n")
|
||||
state.get_config_validator_entries(network).format(",\n")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ fn create_clients(
|
||||
) -> Result<Vec<Client<SigningNymdClient>>, BackendError> {
|
||||
let mut clients = Vec::new();
|
||||
for network in WalletNetwork::iter() {
|
||||
let nymd_url = if let Some(url) = config.get_selected_validator_nymd_url(&network) {
|
||||
let nymd_url = if let Some(url) = config.get_selected_validator_nymd_url(network) {
|
||||
log::debug!("Using selected nymd_url for {network}: {url}");
|
||||
url.clone()
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::config::{Config, OptionalValidators, ValidatorUrl};
|
||||
use crate::error::BackendError;
|
||||
use crate::network::Network;
|
||||
use crate::{config, network_config};
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
use validator_client::Client;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::sync::RwLock;
|
||||
use url::Url;
|
||||
|
||||
@@ -14,6 +15,16 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
// Some hardcoded metadata overrides
|
||||
static METADATA_OVERRIDES: Lazy<Vec<(Url, ValidatorMetadata)>> = Lazy::new(|| {
|
||||
vec![(
|
||||
"https://rpc.nyx.nodes.guru/".parse().unwrap(),
|
||||
ValidatorMetadata {
|
||||
name: Some("Nodes.Guru".to_string()),
|
||||
},
|
||||
)]
|
||||
});
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn load_config_from_files(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
@@ -31,12 +42,15 @@ pub async fn save_config_to_files(
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
config: Config,
|
||||
config: config::Config,
|
||||
signing_clients: HashMap<Network, Client<SigningNymdClient>>,
|
||||
current_network: Network,
|
||||
|
||||
/// Validators that have been fetched dynamically, probably during startup.
|
||||
fetched_validators: OptionalValidators,
|
||||
fetched_validators: config::OptionalValidators,
|
||||
|
||||
/// We fetch (and cache) some metadata, such as names, when available
|
||||
validator_metadata: HashMap<Url, ValidatorMetadata>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -72,13 +86,13 @@ impl State {
|
||||
.ok_or(BackendError::ClientNotInitialized)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &Config {
|
||||
pub fn config(&self) -> &config::Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Load configuration from files. If unsuccessful we just log it and move on.
|
||||
pub fn load_config_files(&mut self) {
|
||||
self.config = Config::load_from_files();
|
||||
self.config = config::Config::load_from_files();
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@@ -106,37 +120,98 @@ impl State {
|
||||
/// 1. from the configuration file
|
||||
/// 2. provided remotely
|
||||
/// 3. hardcoded fallback
|
||||
pub fn get_validators(&self, network: Network) -> impl Iterator<Item = ValidatorUrl> + '_ {
|
||||
/// The format is the config backend format, which is flat due to serialization preference.
|
||||
pub fn get_config_validator_entries(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = config::ValidatorConfigEntry> + '_ {
|
||||
let validators_in_config = self.config.get_configured_validators(network);
|
||||
let fetched_validators = self.fetched_validators.validators(network).cloned();
|
||||
let default_validators = self.config.get_base_validators(network);
|
||||
|
||||
validators_in_config
|
||||
// All the validators, in decending list of priority
|
||||
let validators = validators_in_config
|
||||
.chain(fetched_validators)
|
||||
.chain(default_validators)
|
||||
.unique()
|
||||
.unique_by(|v| (v.nymd_url.clone(), v.api_url.clone()));
|
||||
|
||||
// Annotate with dynamic metadata
|
||||
validators.map(|v| {
|
||||
let metadata = self.validator_metadata.get(&v.nymd_url);
|
||||
let name = v
|
||||
.nymd_name
|
||||
.or_else(|| metadata.and_then(|m| m.name.clone()));
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: v.nymd_url,
|
||||
nymd_name: name,
|
||||
api_url: v.api_url,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_nymd_urls(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self.get_validators(network).into_iter().map(|v| v.nymd_url)
|
||||
}
|
||||
|
||||
pub fn get_api_urls(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
pub fn get_nymd_urls_only(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self
|
||||
.get_validators(network)
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.map(|v| v.nymd_url)
|
||||
}
|
||||
|
||||
pub fn get_api_urls_only(&self, network: Network) -> impl Iterator<Item = Url> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.filter_map(|v| v.api_url)
|
||||
}
|
||||
|
||||
/// Get the list of validator nymd urls in the network config format, suitable for passing on to
|
||||
/// the UI
|
||||
pub fn get_nymd_urls(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = network_config::ValidatorUrl> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.map(|v| network_config::ValidatorUrl {
|
||||
url: v.nymd_url.to_string(),
|
||||
name: v.nymd_name,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the list of validator-api urls in the network config format, suitable for passing on to
|
||||
/// the UI
|
||||
pub fn get_api_urls(
|
||||
&self,
|
||||
network: Network,
|
||||
) -> impl Iterator<Item = network_config::ValidatorUrl> + '_ {
|
||||
self
|
||||
.get_config_validator_entries(network)
|
||||
.into_iter()
|
||||
.filter_map(|v| {
|
||||
v.api_url.map(|u| network_config::ValidatorUrl {
|
||||
url: u.to_string(),
|
||||
name: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_all_nymd_urls(&self) -> HashMap<Network, Vec<Url>> {
|
||||
Network::iter()
|
||||
.flat_map(|network| self.get_nymd_urls(network).map(move |url| (network, url)))
|
||||
.flat_map(|network| {
|
||||
self
|
||||
.get_nymd_urls_only(network)
|
||||
.map(move |url| (network, url))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
|
||||
pub fn get_all_api_urls(&self) -> HashMap<Network, Vec<Url>> {
|
||||
Network::iter()
|
||||
.flat_map(|network| self.get_api_urls(network).map(move |url| (network, url)))
|
||||
.flat_map(|network| {
|
||||
self
|
||||
.get_api_urls_only(network)
|
||||
.map(move |url| (network, url))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
|
||||
@@ -154,11 +229,71 @@ impl State {
|
||||
.get(crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
self.fetched_validators = serde_json::from_str(&response.text().await?)?;
|
||||
log::debug!("Received validator urls: \n{}", self.fetched_validators);
|
||||
|
||||
self.refresh_validator_status().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn refresh_validator_status(&mut self) -> Result<(), BackendError> {
|
||||
log::debug!("Refreshing validator status");
|
||||
|
||||
// All urls for all networks
|
||||
let nymd_urls = self
|
||||
.get_all_nymd_urls()
|
||||
.into_iter()
|
||||
.flat_map(|(_, urls)| urls.into_iter());
|
||||
|
||||
// Fetch status for all urls
|
||||
let responses = fetch_status_for_urls(nymd_urls).await?;
|
||||
|
||||
// Update the stored metadata
|
||||
self.apply_responses(responses)?;
|
||||
|
||||
// Override some overrides for usability
|
||||
self.apply_metadata_override(METADATA_OVERRIDES.to_vec());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_responses(
|
||||
&mut self,
|
||||
responses: Vec<Result<(Url, String), reqwest::Error>>,
|
||||
) -> Result<(), BackendError> {
|
||||
for response in responses.into_iter().flatten() {
|
||||
let json: serde_json::Value = serde_json::from_str(&response.1)?;
|
||||
let moniker = &json["result"]["node_info"]["moniker"];
|
||||
log::debug!("Fetched moniker for: {}: {}", response.0, moniker);
|
||||
|
||||
// Insert into metadata map
|
||||
if let Some(ref mut m) = self.validator_metadata.get_mut(&response.0) {
|
||||
m.name = Some(moniker.to_string());
|
||||
} else {
|
||||
self.validator_metadata.insert(
|
||||
response.0,
|
||||
ValidatorMetadata {
|
||||
name: Some(moniker.to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_metadata_override(&mut self, metadata_overrides: Vec<(Url, ValidatorMetadata)>) {
|
||||
for (url, metadata) in metadata_overrides {
|
||||
log::debug!("Overriding (some) metadata for: {url}");
|
||||
if let Some(m) = self.validator_metadata.get_mut(&url) {
|
||||
m.name = metadata.name;
|
||||
} else {
|
||||
self.validator_metadata.insert(url, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_validator_nymd_url(
|
||||
&mut self,
|
||||
url: &str,
|
||||
@@ -183,15 +318,41 @@ impl State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_validator_url(&mut self, url: ValidatorUrl, network: Network) {
|
||||
pub fn add_validator_url(&mut self, url: config::ValidatorConfigEntry, network: Network) {
|
||||
self.config.add_validator_url(url, network);
|
||||
}
|
||||
|
||||
pub fn remove_validator_url(&mut self, url: ValidatorUrl, network: Network) {
|
||||
pub fn remove_validator_url(&mut self, url: config::ValidatorConfigEntry, network: Network) {
|
||||
self.config.remove_validator_url(url, network)
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_status_for_urls(
|
||||
nymd_urls: impl Iterator<Item = Url>,
|
||||
) -> Result<Vec<Result<(Url, String), reqwest::Error>>, BackendError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
|
||||
let responses = futures::future::join_all(nymd_urls.into_iter().map(|url| {
|
||||
let client = &client;
|
||||
let status_url = url.join("status").unwrap_or_else(|_| url.clone());
|
||||
async move {
|
||||
let resp = client.get(status_url).send().await?;
|
||||
resp.text().await.map(|text| (url, text))
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
|
||||
Ok(responses)
|
||||
}
|
||||
|
||||
// Validator metadata that can by dynamically populated
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ValidatorMetadata {
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! client {
|
||||
($state:ident) => {
|
||||
@@ -223,31 +384,36 @@ mod tests {
|
||||
let _api_urls = state.get_api_urls(Network::MAINNET).collect::<Vec<_>>();
|
||||
|
||||
state.add_validator_url(
|
||||
ValidatorUrl {
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://nymd_url.com".parse().unwrap(),
|
||||
nymd_name: Some("NymdUrl".to_string()),
|
||||
api_url: Some("http://nymd_url.com/api".parse().unwrap()),
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
|
||||
state.add_validator_url(
|
||||
ValidatorUrl {
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://foo.com".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: None,
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
|
||||
state.add_validator_url(
|
||||
ValidatorUrl {
|
||||
config::ValidatorConfigEntry {
|
||||
nymd_url: "http://bar.com".parse().unwrap(),
|
||||
nymd_name: None,
|
||||
api_url: None,
|
||||
},
|
||||
Network::MAINNET,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
state.get_nymd_urls(Network::MAINNET).collect::<Vec<_>>(),
|
||||
state
|
||||
.get_nymd_urls_only(Network::MAINNET)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
@@ -256,10 +422,12 @@ mod tests {
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_api_urls(Network::MAINNET).collect::<Vec<_>>(),
|
||||
state
|
||||
.get_api_urls_only(Network::MAINNET)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"http://nymd_url.com/api".parse().unwrap(),
|
||||
"https://validator.nymtech.net/api".parse().unwrap(),
|
||||
"https://validator.nymtech.net/api/".parse().unwrap(),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ValidatorUrl {
|
||||
url: string;
|
||||
name: string | null;
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
export interface ValidatorUrls {
|
||||
urls: Array<string>;
|
||||
}
|
||||
import type { ValidatorUrl } from './validatorurl';
|
||||
|
||||
export interface Validator {
|
||||
nymd_url: string;
|
||||
api_url: string | null;
|
||||
export interface ValidatorUrls {
|
||||
urls: Array<ValidatorUrl>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user