From ceb5f090cf3c148c2c7fce4d3254b435e8fc4049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 4 Apr 2022 10:14:28 +0200 Subject: [PATCH 1/5] wallet: expose select, add and remove validators --- nym-wallet/src-tauri/src/config/mod.rs | 15 +++ nym-wallet/src-tauri/src/main.rs | 17 ++- nym-wallet/src-tauri/src/network_config.rs | 106 ++++++++++++++++++ .../src/operations/mixnet/account.rs | 48 +------- nym-wallet/src/types/rust/validatorurls.ts | 5 + 5 files changed, 140 insertions(+), 51 deletions(-) create mode 100644 nym-wallet/src-tauri/src/network_config.rs diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index c0972a77fa..2bcd2050b2 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -1,6 +1,7 @@ // Copyright 2021 - Nym Technologies SA // 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; @@ -312,6 +313,20 @@ impl TryFrom for ValidatorUrl { } } +impl TryFrom for ValidatorUrl { + type Error = BackendError; + + fn try_from(validator: network_config::Validator) -> Result { + 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); diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 61346a19cf..d8e107b29f 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -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,16 @@ 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::account::validate_mnemonic, mixnet::admin::get_contract_settings, mixnet::admin::update_contract_settings, @@ -61,11 +59,18 @@ 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, utils::major_to_minor, utils::minor_to_major, utils::outdated_get_approximate_fee, @@ -81,8 +86,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, diff --git a/nym-wallet/src-tauri/src/network_config.rs b/nym-wallet/src-tauri/src/network_config.rs new file mode 100644 index 0000000000..8205d079c8 --- /dev/null +++ b/nym-wallet/src-tauri/src/network_config.rs @@ -0,0 +1,106 @@ +// Copyright 2021 - Nym Technologies SA +// 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::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, +} + +#[cfg_attr(test, derive(ts_rs::TS))] +#[cfg_attr(test, ts(export, export_to = "../src/types/rust/validatorurls.ts"))] +#[derive(Serialize, Deserialize)] +pub struct Validator { + pub nymd_url: String, + pub api_url: Option, +} + +#[tauri::command] +pub async fn get_validator_nymd_urls( + network: WalletNetwork, + state: tauri::State<'_, Arc>>, +) -> Result { + let state = state.read().await; + let urls: Vec = 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>>, +) -> Result { + let state = state.read().await; + let urls: Vec = 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>>, +) -> Result<(), BackendError> { + 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>>, +) -> Result<(), BackendError> { + 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>>, +) -> Result<(), BackendError> { + 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>>, +) -> Result<(), BackendError> { + 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>>, +) -> Result<(), BackendError> { + let mut w_state = state.write().await; + let _r = w_state.fetch_updated_validator_urls().await; + Ok(()) +} diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 12901cc046..a79a6c92c3 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -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, -} - #[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 { +pub fn create_new_mnemonic() -> Result { 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>>, -) -> 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>>, -) -> Result { - let state = state.read().await; - let urls: Vec = 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>>, -) -> Result { - let state = state.read().await; - let urls: Vec = 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>>, @@ -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; diff --git a/nym-wallet/src/types/rust/validatorurls.ts b/nym-wallet/src/types/rust/validatorurls.ts index c4b4a7624e..c3c6bbe580 100644 --- a/nym-wallet/src/types/rust/validatorurls.ts +++ b/nym-wallet/src/types/rust/validatorurls.ts @@ -1,3 +1,8 @@ export interface ValidatorUrls { urls: Array; } + +export interface Validator { + nymd_url: string; + api_url: string | null; +} From baba5ed212a6d78a788903bef8eaf71f652d9f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 5 Apr 2022 11:44:00 +0200 Subject: [PATCH 2/5] wallet: additional backend logic for selecting validators --- nym-wallet/src-tauri/src/config/mod.rs | 19 +++++---- nym-wallet/src-tauri/src/main.rs | 2 + nym-wallet/src-tauri/src/network_config.rs | 20 +++++++++- nym-wallet/src-tauri/src/state.rs | 45 ++++++++++++++++++++-- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 2bcd2050b2..c896b027fd 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -48,7 +48,8 @@ pub struct NetworkConfig { selected_nymd_url: Option, selected_api_url: Option, - // Additional user provided validators + // Additional user provided validators. + // It is an option for the purpuse of file serialization. validator_urls: Option>, } @@ -260,11 +261,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( @@ -277,9 +278,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); + } + } } } diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index d8e107b29f..4b3bb89a01 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -71,6 +71,8 @@ fn main() { 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, diff --git a/nym-wallet/src-tauri/src/network_config.rs b/nym-wallet/src-tauri/src/network_config.rs index 8205d079c8..df80e299a7 100644 --- a/nym-wallet/src-tauri/src/network_config.rs +++ b/nym-wallet/src-tauri/src/network_config.rs @@ -6,7 +6,7 @@ use crate::network::Network as WalletNetwork; use crate::state::State; use serde::{Deserialize, Serialize}; -use std::sync::Arc; +use std::{fmt, sync::Arc}; use tokio::sync::RwLock; #[cfg_attr(test, derive(ts_rs::TS))] @@ -18,12 +18,24 @@ pub struct ValidatorUrls { #[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 Validator { pub nymd_url: String, pub api_url: Option, } +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, @@ -56,6 +68,7 @@ pub async fn select_validator_nymd_url( network: WalletNetwork, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { + log::debug!("Selecting new validator nymd_url for {network}: {url}"); state .write() .await @@ -69,6 +82,7 @@ pub async fn select_validator_api_url( network: WalletNetwork, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { + log::debug!("Selecting new validator api_url for {network}: {url}"); state.write().await.select_validator_api_url(url, network)?; Ok(()) } @@ -79,6 +93,7 @@ pub async fn add_validator( network: WalletNetwork, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { + log::debug!("Add validator for {network}: {validator}"); let url = validator.try_into()?; state.write().await.add_validator_url(url, network); Ok(()) @@ -90,6 +105,7 @@ pub async fn remove_validator( network: WalletNetwork, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { + log::debug!("Remove validator for {network}: {validator}"); let url = validator.try_into()?; state.write().await.remove_validator_url(url, network); Ok(()) diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index 63e5a961c5..2a3242d3d7 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -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>>, +) -> Result<(), BackendError> { + state.write().await.load_config_files(); + Ok(()) +} + +#[tauri::command] +pub async fn save_config_to_files( + state: tauri::State<'_, Arc>>, +) -> 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, BackendError> { + self + .signing_clients + .get_mut(&network) + .ok_or(BackendError::ClientNotInitialized) + } + pub fn current_client(&self) -> Result<&Client, 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, 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) } From 9d2d670990f35b31a8f8e5bee141a6253019ce6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 5 Apr 2022 12:19:09 +0200 Subject: [PATCH 3/5] wallet: remove duplicate handler entries --- nym-wallet/src-tauri/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 4b3bb89a01..75ee64719c 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -46,7 +46,6 @@ fn main() { mixnet::account::sign_in_with_password, mixnet::account::switch_network, mixnet::account::validate_mnemonic, - mixnet::account::validate_mnemonic, mixnet::admin::get_contract_settings, mixnet::admin::update_contract_settings, mixnet::bond::bond_gateway, From 46a4991c12027f1e941dbcf5eac9c8268e2e017e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 5 Apr 2022 13:08:19 +0200 Subject: [PATCH 4/5] wallet: add version number to network config files --- nym-wallet/src-tauri/src/config/mod.rs | 45 ++++++++++++++++++++------ 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index c896b027fd..71012940f6 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -19,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. @@ -37,13 +40,16 @@ struct Base { networks: SupportedNetworks, } -#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct GlobalConfig { + version: 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: u32, + // User selected urls selected_nymd_url: Option, selected_api_url: Option, @@ -53,12 +59,6 @@ pub struct NetworkConfig { validator_urls: Option>, } -impl NetworkConfig { - fn validators(&self) -> impl Iterator { - self.validator_urls.iter().flat_map(|v| v.iter()) - } -} - impl Default for Base { fn default() -> Self { let networks = WalletNetwork::iter().map(Into::into).collect(); @@ -68,6 +68,31 @@ impl Default for Base { } } +impl Default for GlobalConfig { + fn default() -> Self { + Self { + version: CURRENT_GLOBAL_CONFIG_VERSION, + } + } +} + +impl Default for NetworkConfig { + fn default() -> Self { + Self { + version: CURRENT_NETWORK_CONFIG_VERSION, + selected_nymd_url: None, + selected_api_url: None, + validator_urls: None, + } + } +} + +impl NetworkConfig { + fn validators(&self) -> impl Iterator { + 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") @@ -409,6 +434,7 @@ mod tests { api_url: Some("https://baz/api".parse().unwrap()), }, ]), + ..NetworkConfig::default() }; Config { @@ -426,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/' From 1e13d4124523cb1d8347291bd7f5c44fcf98917d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 11 Apr 2022 10:48:46 +0200 Subject: [PATCH 5/5] wallet: make config file version optional --- nym-wallet/src-tauri/src/config/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 71012940f6..7510d80612 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -42,13 +42,13 @@ struct Base { #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct GlobalConfig { - version: u32, + version: Option, // TODO: there are no global settings (yet) } #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct NetworkConfig { - version: u32, + version: Option, // User selected urls selected_nymd_url: Option, @@ -71,7 +71,7 @@ impl Default for Base { impl Default for GlobalConfig { fn default() -> Self { Self { - version: CURRENT_GLOBAL_CONFIG_VERSION, + version: Some(CURRENT_GLOBAL_CONFIG_VERSION), } } } @@ -79,7 +79,7 @@ impl Default for GlobalConfig { impl Default for NetworkConfig { fn default() -> Self { Self { - version: CURRENT_NETWORK_CONFIG_VERSION, + version: Some(CURRENT_NETWORK_CONFIG_VERSION), selected_nymd_url: None, selected_api_url: None, validator_urls: None,