Merge pull request #1195 from nymtech/feature/expose-more-validator-in-wallet

wallet: expose additional validator configuration functionality to the frontend
This commit is contained in:
Jon Häggblad
2022-04-11 11:21:08 +02:00
committed by GitHub
6 changed files with 246 additions and 71 deletions
+63 -16
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::network_config;
use crate::platform_constants::{CONFIG_DIR_NAME, CONFIG_FILENAME};
use crate::{error::BackendError, network::Network as WalletNetwork};
use config::defaults::all::Network;
@@ -18,6 +19,9 @@ use url::Url;
pub const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str =
"https://nymtech.net/.wellknown/wallet/validators.json";
const CURRENT_GLOBAL_CONFIG_VERSION: u32 = 1;
const CURRENT_NETWORK_CONFIG_VERSION: u32 = 1;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Config {
// Base configuration is not part of the configuration file as it's not intended to be changed.
@@ -36,27 +40,25 @@ struct Base {
networks: SupportedNetworks,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct GlobalConfig {
version: Option<u32>,
// TODO: there are no global settings (yet)
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct NetworkConfig {
version: Option<u32>,
// User selected urls
selected_nymd_url: Option<Url>,
selected_api_url: Option<Url>,
// Additional user provided validators
// Additional user provided validators.
// It is an option for the purpuse of file serialization.
validator_urls: Option<Vec<ValidatorUrl>>,
}
impl NetworkConfig {
fn validators(&self) -> impl Iterator<Item = &ValidatorUrl> {
self.validator_urls.iter().flat_map(|v| v.iter())
}
}
impl Default for Base {
fn default() -> Self {
let networks = WalletNetwork::iter().map(Into::into).collect();
@@ -66,6 +68,31 @@ impl Default for Base {
}
}
impl Default for GlobalConfig {
fn default() -> Self {
Self {
version: Some(CURRENT_GLOBAL_CONFIG_VERSION),
}
}
}
impl Default for NetworkConfig {
fn default() -> Self {
Self {
version: Some(CURRENT_NETWORK_CONFIG_VERSION),
selected_nymd_url: None,
selected_api_url: None,
validator_urls: None,
}
}
}
impl NetworkConfig {
fn validators(&self) -> impl Iterator<Item = &ValidatorUrl> {
self.validator_urls.iter().flat_map(|v| v.iter())
}
}
impl Config {
fn root_directory() -> PathBuf {
tauri::api::path::config_dir().expect("Failed to get config directory")
@@ -259,11 +286,11 @@ impl Config {
}
pub fn add_validator_url(&mut self, url: ValidatorUrl, network: WalletNetwork) {
if let Some(net) = self.networks.get_mut(&network.as_key()) {
if let Some(ref mut urls) = net.validator_urls {
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);
} else {
net.validator_urls = Some(vec![url]);
network_config.validator_urls = Some(vec![url]);
}
} else {
self.networks.insert(
@@ -276,9 +303,13 @@ impl Config {
}
}
#[allow(unused)]
pub fn remove_validator_url(&mut self, _url: ValidatorUrl, _network: WalletNetwork) {
todo!();
pub fn remove_validator_url(&mut self, url: ValidatorUrl, 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
urls.retain(|existing_url| existing_url != &url);
}
}
}
}
@@ -312,6 +343,20 @@ impl TryFrom<ValidatorDetails> for ValidatorUrl {
}
}
impl TryFrom<network_config::Validator> for ValidatorUrl {
type Error = BackendError;
fn try_from(validator: network_config::Validator) -> 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,
},
})
}
}
impl fmt::Display for ValidatorUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s1 = format!("nymd_url: {}", self.nymd_url);
@@ -389,6 +434,7 @@ mod tests {
api_url: Some("https://baz/api".parse().unwrap()),
},
]),
..NetworkConfig::default()
};
Config {
@@ -406,7 +452,8 @@ mod tests {
let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()];
assert_eq!(
toml::to_string_pretty(netconfig).unwrap(),
r#"selected_api_url = 'https://my_api_url.com/'
r#"version = 1
selected_api_url = 'https://my_api_url.com/'
[[validator_urls]]
nymd_url = 'https://foo/'
+12 -6
View File
@@ -14,6 +14,7 @@ mod config;
mod error;
mod menu;
mod network;
mod network_config;
mod operations;
mod platform_constants;
mod state;
@@ -35,19 +36,15 @@ fn main() {
.manage(Arc::new(RwLock::new(State::default())))
.invoke_handler(tauri::generate_handler![
mixnet::account::connect_with_mnemonic,
mixnet::account::validate_mnemonic,
mixnet::account::create_new_account,
mixnet::account::create_new_mnemonic,
mixnet::account::create_password,
mixnet::account::does_password_file_exist,
mixnet::account::get_balance,
mixnet::account::get_validator_nymd_urls,
mixnet::account::get_validator_api_urls,
mixnet::account::logout,
mixnet::account::remove_password,
mixnet::account::sign_in_with_password,
mixnet::account::switch_network,
mixnet::account::update_validator_urls,
mixnet::account::validate_mnemonic,
mixnet::admin::get_contract_settings,
mixnet::admin::update_contract_settings,
@@ -61,11 +58,20 @@ fn main() {
mixnet::bond::update_mixnode,
mixnet::delegate::delegate_to_mixnode,
mixnet::delegate::get_delegator_rewards,
mixnet::delegate::get_pending_delegation_events,
mixnet::delegate::get_reverse_mix_delegations_paged,
mixnet::delegate::undelegate_from_mixnode,
mixnet::delegate::get_pending_delegation_events,
mixnet::epoch::get_current_epoch,
mixnet::send::send,
network_config::add_validator,
network_config::get_validator_api_urls,
network_config::get_validator_nymd_urls,
network_config::remove_validator,
network_config::select_validator_api_url,
network_config::select_validator_nymd_url,
network_config::update_validator_urls,
state::load_config_from_files,
state::save_config_to_files,
utils::major_to_minor,
utils::minor_to_major,
utils::outdated_get_approximate_fee,
@@ -81,8 +87,8 @@ fn main() {
vesting::bond::vesting_bond_mixnode,
vesting::bond::vesting_unbond_gateway,
vesting::bond::vesting_unbond_mixnode,
vesting::bond::withdraw_vested_coins,
vesting::bond::vesting_update_mixnode,
vesting::bond::withdraw_vested_coins,
vesting::delegate::vesting_delegate_to_mixnode,
vesting::delegate::vesting_undelegate_from_mixnode,
vesting::queries::delegated_free,
+122
View File
@@ -0,0 +1,122 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::BackendError;
use crate::network::Network as WalletNetwork;
use crate::state::State;
use serde::{Deserialize, Serialize};
use std::{fmt, sync::Arc};
use tokio::sync::RwLock;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
#[derive(Serialize, Deserialize)]
pub struct ValidatorUrls {
pub urls: Vec<String>,
}
#[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 api_url: Option<String>,
}
impl fmt::Display for Validator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let nymd_url = format!("nymd_url: {}", self.nymd_url);
let api_url = self
.api_url
.as_ref()
.map(|api_url| format!(", api_url: {}", api_url))
.unwrap_or_default();
write!(f, "{nymd_url}{api_url}")
}
}
#[tauri::command]
pub async fn get_validator_nymd_urls(
network: WalletNetwork,
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();
Ok(ValidatorUrls { urls })
}
#[tauri::command]
pub async fn get_validator_api_urls(
network: WalletNetwork,
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();
Ok(ValidatorUrls { urls })
}
#[tauri::command]
pub async fn select_validator_nymd_url(
url: &str,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
log::debug!("Selecting new validator nymd_url for {network}: {url}");
state
.write()
.await
.select_validator_nymd_url(url, network)?;
Ok(())
}
#[tauri::command]
pub async fn select_validator_api_url(
url: &str,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
log::debug!("Selecting new validator api_url for {network}: {url}");
state.write().await.select_validator_api_url(url, network)?;
Ok(())
}
#[tauri::command]
pub async fn add_validator(
validator: Validator,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
log::debug!("Add validator for {network}: {validator}");
let url = validator.try_into()?;
state.write().await.add_validator_url(url, network);
Ok(())
}
#[tauri::command]
pub async fn remove_validator(
validator: Validator,
network: WalletNetwork,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
log::debug!("Remove validator for {network}: {validator}");
let url = validator.try_into()?;
state.write().await.remove_validator_url(url, network);
Ok(())
}
// Update the list of validators by fecthing additional ones remotely. If it fails, just ignore.
#[tauri::command]
pub async fn update_validator_urls(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
let mut w_state = state.write().await;
let _r = w_state.fetch_updated_validator_urls().await;
Ok(())
}
@@ -2,6 +2,7 @@ use crate::coin::{Coin, Denom};
use crate::config::Config;
use crate::error::BackendError;
use crate::network::Network as WalletNetwork;
use crate::network_config;
use crate::nymd_client;
use crate::state::State;
use crate::wallet_storage::{self, DEFAULT_WALLET_ACCOUNT_ID};
@@ -58,13 +59,6 @@ pub struct Balance {
printable_balance: String,
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))]
#[derive(Serialize, Deserialize)]
pub struct ValidatorUrls {
urls: Vec<String>,
}
#[tauri::command]
pub async fn connect_with_mnemonic(
mnemonic: String,
@@ -113,7 +107,7 @@ pub async fn create_new_account(
}
#[tauri::command]
pub async fn create_new_mnemonic() -> Result<String, BackendError> {
pub fn create_new_mnemonic() -> Result<String, BackendError> {
let rand_mnemonic = random_mnemonic();
Ok(rand_mnemonic.to_string())
}
@@ -157,42 +151,6 @@ fn random_mnemonic() -> Mnemonic {
Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap()
}
#[tauri::command]
pub async fn update_validator_urls(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
// Update the list of validators by fecthing additional ones remotely. If it fails, just ignore.
let mut w_state = state.write().await;
let _r = w_state.fetch_updated_validator_urls().await;
Ok(())
}
#[tauri::command]
pub async fn get_validator_nymd_urls(
network: WalletNetwork,
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();
Ok(ValidatorUrls { urls })
}
#[tauri::command]
pub async fn get_validator_api_urls(
network: WalletNetwork,
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();
Ok(ValidatorUrls { urls })
}
async fn _connect_with_mnemonic(
mnemonic: Mnemonic,
state: tauri::State<'_, Arc<RwLock<State>>>,
@@ -202,7 +160,7 @@ async fn _connect_with_mnemonic(
w_state.load_config_files();
}
update_validator_urls(state.clone()).await?;
network_config::update_validator_urls(state.clone()).await?;
let config = {
let state = state.read().await;
+41 -4
View File
@@ -7,11 +7,28 @@ use validator_client::nymd::SigningNymdClient;
use validator_client::Client;
use itertools::Itertools;
use tokio::sync::RwLock;
use url::Url;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
#[tauri::command]
pub async fn load_config_from_files(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
state.write().await.load_config_files();
Ok(())
}
#[tauri::command]
pub async fn save_config_to_files(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<(), BackendError> {
state.read().await.save_config_files()
}
#[derive(Default)]
pub struct State {
config: Config,
@@ -30,6 +47,16 @@ impl State {
.ok_or(BackendError::ClientNotInitialized)
}
pub fn client_mut(
&mut self,
network: Network,
) -> Result<&mut Client<SigningNymdClient>, BackendError> {
self
.signing_clients
.get_mut(&network)
.ok_or(BackendError::ClientNotInitialized)
}
pub fn current_client(&self) -> Result<&Client<SigningNymdClient>, BackendError> {
self
.signing_clients
@@ -37,6 +64,14 @@ impl State {
.ok_or(BackendError::ClientNotInitialized)
}
#[allow(unused)]
pub fn current_client_mut(&mut self) -> Result<&mut Client<SigningNymdClient>, BackendError> {
self
.signing_clients
.get_mut(&self.current_network)
.ok_or(BackendError::ClientNotInitialized)
}
pub fn config(&self) -> &Config {
&self.config
}
@@ -124,32 +159,34 @@ impl State {
Ok(())
}
#[allow(unused)]
pub fn select_validator_nymd_url(
&mut self,
url: &str,
network: Network,
) -> Result<(), BackendError> {
self.config.select_validator_nymd_url(url.parse()?, network);
if let Ok(client) = self.client_mut(network) {
client.change_nymd(url.parse()?)?;
}
Ok(())
}
#[allow(unused)]
pub fn select_validator_api_url(
&mut self,
url: &str,
network: Network,
) -> Result<(), BackendError> {
self.config.select_validator_api_url(url.parse()?, network);
if let Ok(client) = self.client_mut(network) {
client.change_validator_api(url.parse()?);
}
Ok(())
}
#[allow(unused)]
pub fn add_validator_url(&mut self, url: ValidatorUrl, network: Network) {
self.config.add_validator_url(url, network);
}
#[allow(unused)]
pub fn remove_validator_url(&mut self, url: ValidatorUrl, network: Network) {
self.config.remove_validator_url(url, network)
}
@@ -1,3 +1,8 @@
export interface ValidatorUrls {
urls: Array<string>;
}
export interface Validator {
nymd_url: string;
api_url: string | null;
}