From 45b41d9e202d794a4ebc69177e2baaa3449b4df0 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Fri, 1 Apr 2022 10:07:02 +0100 Subject: [PATCH 01/23] Update README.md --- explorer/docs/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/explorer/docs/README.md b/explorer/docs/README.md index 6f56ce3f14..bb0d0be99a 100644 --- a/explorer/docs/README.md +++ b/explorer/docs/README.md @@ -18,6 +18,8 @@ We use the following: ## Development mode +Copy the `.env.prod` file to `.env` to configure your environment. Using the live sandbox Explorer API is the best way to do development, so the prod settings are good. + Run the following: ``` From 53292ceca9e3e3e56415df352bb05896fb081bff Mon Sep 17 00:00:00 2001 From: gala1234 Date: Fri, 1 Apr 2022 12:37:24 +0200 Subject: [PATCH 02/23] enable discord icon --- explorer/src/components/Socials.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/explorer/src/components/Socials.tsx b/explorer/src/components/Socials.tsx index b37ee78351..298c8c6c4e 100644 --- a/explorer/src/components/Socials.tsx +++ b/explorer/src/components/Socials.tsx @@ -22,11 +22,9 @@ export const Socials: React.FC<{ isFooter?: boolean }> = ({ isFooter }) => { - {false && ( - - - - )} + + + From 09155fbf1227e367e0c1c4f3d2db22658e7e5025 Mon Sep 17 00:00:00 2001 From: gala1234 Date: Fri, 1 Apr 2022 12:42:17 +0200 Subject: [PATCH 03/23] update discord url --- explorer/src/components/Socials.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/explorer/src/components/Socials.tsx b/explorer/src/components/Socials.tsx index 298c8c6c4e..f84aa7bcc3 100644 --- a/explorer/src/components/Socials.tsx +++ b/explorer/src/components/Socials.tsx @@ -10,7 +10,7 @@ import { DiscordIcon } from '../icons/socials/DiscordIcon'; export const TELEGRAM_LINK = 'https://t.me/nymchan'; export const TWITTER_LINK = 'https://twitter.com/nymproject'; export const GITHUB_LINK = 'https://github.com/nymtech'; -export const DISCORD_LINK = 'https://discord.gg/jUqJYGB5'; +export const DISCORD_LINK = 'https://discord.gg/ggxrUpbNnn'; export const Socials: React.FC<{ isFooter?: boolean }> = ({ isFooter }) => { const theme = useTheme(); From 23fb34f5645777cccea545e6e6d8cbf739af7b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Fri, 1 Apr 2022 14:41:20 +0200 Subject: [PATCH 04/23] wallet: support removing accounts from the wallet file --- nym-wallet/src-tauri/src/main.rs | 1 + .../src/operations/mixnet/account.rs | 7 ++ .../src/wallet_storage/account_data.rs | 10 +++ .../src-tauri/src/wallet_storage/mod.rs | 72 +++++++++++++++++-- .../src-tauri/src/wallet_storage/password.rs | 8 +++ 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 47490e17ae..8f17331dc4 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -43,6 +43,7 @@ fn main() { mixnet::account::does_password_file_exist, mixnet::account::get_balance, mixnet::account::logout, + mixnet::account::remove_password, mixnet::account::sign_in_with_password, mixnet::account::switch_network, mixnet::account::update_validator_urls, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 56a051ae34..9083cd20bb 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -336,3 +336,10 @@ pub async fn sign_in_with_password( let stored_account = wallet_storage::load_existing_wallet_login_information(&id, &password)?; _connect_with_mnemonic(stored_account.mnemonic().clone(), state).await } + +#[tauri::command] +pub fn remove_password() -> Result<(), BackendError> { + log::info!("Removing password"); + let id = wallet_storage::WalletAccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + wallet_storage::remove_wallet_login_information(&id) +} diff --git a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs index 7f1f18e0f8..dd5f9405e8 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs @@ -35,6 +35,16 @@ impl StoredWallet { self.accounts.len() } + pub fn remove_account(&mut self, id: &WalletAccountId) -> Option { + if let Some(index) = self.accounts.iter().position(|account| &account.id == id) { + log::info!("Removing from wallet file: {id}"); + Some(self.accounts.remove(index)) + } else { + log::debug!("Tried to remove non-existent id from wallet: {id}"); + None + } + } + pub fn encrypted_account_by_index(&self, index: usize) -> Option<&EncryptedAccount> { self.accounts.get(index) } diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 1ac9c14084..9fcfc09660 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -9,8 +9,10 @@ use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME}; use crate::wallet_storage::account_data::StoredAccount; use crate::wallet_storage::encryption::{encrypt_struct, EncryptedData}; use cosmrs::bip32::DerivationPath; +use cosmwasm_std::BankMsg; use serde::{Deserialize, Serialize}; -use std::fs::{create_dir_all, OpenOptions}; +use std::fs::{self, create_dir_all, OpenOptions}; +use std::os::unix::prelude::OpenOptionsExt; use std::path::PathBuf; use self::account_data::{EncryptedAccount, StoredWallet}; @@ -112,10 +114,43 @@ fn store_wallet_login_information_at_file( Ok(serde_json::to_writer_pretty(file, &stored_wallet)?) } -// this function should probably exist, but I guess we need to discuss how it should behave in the context of the UX -// pub(crate) fn remove_wallet_login_information( -// -// ) +pub(crate) fn remove_wallet_login_information(id: &WalletAccountId) -> Result<(), BackendError> { + let store_dir = get_storage_directory()?; + let filepath = store_dir.join(WALLET_INFO_FILENAME); + remove_wallet_login_information_at_file(filepath, id) +} + +pub(crate) fn remove_wallet_login_information_at_file( + filepath: PathBuf, + id: &WalletAccountId, +) -> Result<(), BackendError> { + let mut stored_wallet = match load_existing_wallet_at_file(filepath.clone()) { + Err(BackendError::WalletFileNotFound) => StoredWallet::default(), + result => result?, + }; + + if stored_wallet.is_empty() { + log::info!("Removing file: {:#?}", filepath); + return Ok(fs::remove_file(filepath)?); + } + + stored_wallet + .remove_account(id) + .ok_or(BackendError::NoSuchIdInWallet)?; + + if stored_wallet.is_empty() { + log::info!("Removing file: {:#?}", filepath); + Ok(fs::remove_file(filepath)?) + } else { + let file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(filepath)?; + + Ok(serde_json::to_writer_pretty(file, &stored_wallet)?) + } +} #[cfg(test)] mod tests { @@ -127,6 +162,7 @@ mod tests { // I'm not 100% sure how to feel about having to touch the file system at all #[test] + #[allow(clippy::too_many_lines)] fn storing_wallet_information() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); @@ -248,9 +284,33 @@ mod tests { assert_eq!(&cosmos_hd_path, acc1.hd_path()); let loaded_account = - load_existing_wallet_login_information_at_file(wallet_file, &id2, &password).unwrap(); + load_existing_wallet_login_information_at_file(wallet_file.clone(), &id2, &password).unwrap(); let StoredAccount::Mnemonic(ref acc2) = loaded_account; assert_eq!(&dummy_account2, acc2.mnemonic()); assert_eq!(&different_hd_path, acc2.hd_path()); + + // Fails to delete non-existent id in the wallet + let id3 = WalletAccountId::new("phony".to_string()); + assert!(matches!( + remove_wallet_login_information_at_file(wallet_file.clone(), &id3), + Err(BackendError::NoSuchIdInWallet), + )); + + // Delete the second account + remove_wallet_login_information_at_file(wallet_file.clone(), &id2).unwrap(); + + // The first account should be unchanged + let loaded_account = + load_existing_wallet_login_information_at_file(wallet_file.clone(), &id1, &password).unwrap(); + let StoredAccount::Mnemonic(ref acc1) = loaded_account; + assert_eq!(&dummy_account1, acc1.mnemonic()); + assert_eq!(&cosmos_hd_path, acc1.hd_path()); + + // Delete the first account + assert!(wallet_file.exists()); + remove_wallet_login_information_at_file(wallet_file.clone(), &id1).unwrap(); + + // The file should now be removed + assert!(!wallet_file.exists()); } } diff --git a/nym-wallet/src-tauri/src/wallet_storage/password.rs b/nym-wallet/src-tauri/src/wallet_storage/password.rs index f81b5bf7c1..894576f4b4 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/password.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/password.rs @@ -1,6 +1,8 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use std::fmt; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; use zeroize::Zeroize; @@ -19,6 +21,12 @@ impl AsRef for WalletAccountId { } } +impl fmt::Display for WalletAccountId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + // simple wrapper for String that will get zeroized on drop #[derive(Zeroize)] #[zeroize(drop)] From efe6df12c960be53a1fbe2ac6f08308aaed4f18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Fri, 1 Apr 2022 14:46:44 +0200 Subject: [PATCH 05/23] wallet: remove unused --- nym-wallet/src-tauri/src/wallet_storage/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 9fcfc09660..11fe6f07ce 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -9,7 +9,6 @@ use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME}; use crate::wallet_storage::account_data::StoredAccount; use crate::wallet_storage::encryption::{encrypt_struct, EncryptedData}; use cosmrs::bip32::DerivationPath; -use cosmwasm_std::BankMsg; use serde::{Deserialize, Serialize}; use std::fs::{self, create_dir_all, OpenOptions}; use std::os::unix::prelude::OpenOptionsExt; From e106390e1d6ed56b870fa49fe31fdf6eaf084936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Fri, 1 Apr 2022 15:09:22 +0200 Subject: [PATCH 06/23] wallet: add validate_mnemonic --- nym-wallet/src-tauri/src/main.rs | 1 + nym-wallet/src-tauri/src/operations/mixnet/account.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 8f17331dc4..0c8f2bbd7b 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -47,6 +47,7 @@ fn main() { 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, mixnet::bond::bond_gateway, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 9083cd20bb..6429024bd4 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -113,6 +113,12 @@ pub async fn create_new_mnemonic() -> Result { Ok(rand_mnemonic.to_string()) } +#[tauri::command] +pub fn validate_mnemonic(mnemonic: &str) -> Result<(), BackendError> { + Mnemonic::from_str(mnemonic)?; + Ok(()) +} + #[tauri::command] pub async fn switch_network( state: tauri::State<'_, Arc>>, From 4116aa18a8b76eaad84ccb28c37313a2644cd479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Fri, 1 Apr 2022 11:22:26 +0200 Subject: [PATCH 07/23] wallet: config backend for validators --- Cargo.lock | 1 + .../validator-client/src/connection_tester.rs | 6 +- common/config/Cargo.toml | 3 +- common/config/src/lib.rs | 8 +- contracts/Cargo.lock | 1 + nym-wallet/Cargo.lock | 1 + nym-wallet/src-tauri/src/config/mod.rs | 370 ++++++++++++------ nym-wallet/src-tauri/src/network.rs | 4 + .../src/operations/mixnet/account.rs | 195 +++++---- .../src-tauri/src/platform_constants.rs | 8 + nym-wallet/src-tauri/src/state.rs | 175 ++++++++- 11 files changed, 566 insertions(+), 206 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2853c9d980..e417e0bc27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -623,6 +623,7 @@ version = "0.1.0" dependencies = [ "handlebars", "humantime-serde", + "log", "network-defaults", "serde", "toml", diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index ebf156d7ca..1a8154cd80 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -93,7 +93,11 @@ async fn test_nymd_connection( { Ok(Err(NymdError::TendermintError(e))) => { // If we get a tendermint-rpc error, we classify the node as not contactable - log::debug!("Checking: nymd_url: {network}: {url}: failed: {}", e); + log::debug!( + "Checking: nymd_url: {network}: {url}: {}: {}", + "failed".red(), + e + ); false } Ok(Err(NymdError::AbciError(code, log))) => { diff --git a/common/config/Cargo.toml b/common/config/Cargo.toml index 2455b32f8a..bbdc13e620 100644 --- a/common/config/Cargo.toml +++ b/common/config/Cargo.toml @@ -9,8 +9,9 @@ edition = "2021" [dependencies] handlebars = "3.0.1" humantime-serde = "1.0" +log = "0.4" serde = { version = "1.0", features = ["derive"] } toml = "0.5.6" url = "2.2" -network-defaults = { path = "../network-defaults" } \ No newline at end of file +network-defaults = { path = "../network-defaults" } diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 3d00921eef..61e95e9ca9 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -13,6 +13,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { fn template() -> &'static str; fn config_file_name() -> String { + log::trace!("NymdConfig::config_file_name"); "config.toml".to_string() } @@ -20,6 +21,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { // default, most probable, implementations; can be easily overridden where required fn default_config_directory(id: Option<&str>) -> PathBuf { + log::trace!("NymdConfig::default_config_directory"); if let Some(id) = id { Self::default_root_directory().join(id).join("config") } else { @@ -28,6 +30,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { } fn default_data_directory(id: Option<&str>) -> PathBuf { + log::trace!("NymdConfig::default_data_path"); if let Some(id) = id { Self::default_root_directory().join(id).join("data") } else { @@ -36,6 +39,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { } fn default_config_file_path(id: Option<&str>) -> PathBuf { + log::trace!("NymdConfig::default_config_file_path"); Self::default_config_directory(id).join(Self::config_file_name()) } @@ -68,7 +72,9 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { } fn load_from_file(id: Option<&str>) -> io::Result { - let config_contents = fs::read_to_string(Self::default_config_file_path(id))?; + let file = Self::default_config_file_path(id); + log::trace!("Loading from file: {:#?}", file); + let config_contents = fs::read_to_string(file)?; toml::from_str(&config_contents) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 9e5ee6baee..0efb879812 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -219,6 +219,7 @@ version = "0.1.0" dependencies = [ "handlebars", "humantime-serde", + "log", "network-defaults", "serde", "toml", diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 3f5a26d260..fd97a2b32b 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -568,6 +568,7 @@ version = "0.1.0" dependencies = [ "handlebars", "humantime-serde", + "log", "network-defaults", "serde", "toml", diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index c35c80cb10..c0972a77fa 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -1,39 +1,60 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::platform_constants::{CONFIG_DIR_NAME, CONFIG_FILENAME}; use crate::{error::BackendError, network::Network as WalletNetwork}; +use config::defaults::all::Network; use config::defaults::{all::SupportedNetworks, ValidatorDetails}; -use config::NymConfig; use core::fmt; use itertools::Itertools; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::collections::HashMap; +use std::str::FromStr; use std::{fs, io, path::PathBuf}; use strum::IntoEnumIterator; use url::Url; -const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str = +pub const REMOTE_SOURCE_OF_VALIDATOR_URLS: &str = "https://nymtech.net/.wellknown/wallet/validators.json"; -#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] -#[serde(deny_unknown_fields)] +#[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. - #[serde(skip)] base: Base, - // Network level configuration - network: OptionalValidators, + // Global configuration file + global: Option, + + // One configuration file per network + networks: HashMap, } #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] -#[serde(deny_unknown_fields)] struct Base { /// Information on all the networks that the wallet connects to. networks: SupportedNetworks, +} - /// Validators that have been fetched dynamically, probably during startup. - fetched_validators: OptionalValidators, +#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] +pub struct GlobalConfig { + // TODO: there are no global settings (yet) +} + +#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] +pub struct NetworkConfig { + // User selected urls + selected_nymd_url: Option, + selected_api_url: Option, + + // Additional user provided validators + validator_urls: Option>, +} + +impl NetworkConfig { + fn validators(&self) -> impl Iterator { + self.validator_urls.iter().flat_map(|v| v.iter()) + } } impl Default for Base { @@ -41,91 +62,125 @@ impl Default for Base { let networks = WalletNetwork::iter().map(Into::into).collect(); Base { networks: SupportedNetworks::new(networks), - fetched_validators: OptionalValidators::default(), } } } -impl NymConfig for Config { - fn template() -> &'static str { - // For now we're not using a template - unimplemented!(); +impl Config { + fn root_directory() -> PathBuf { + tauri::api::path::config_dir().expect("Failed to get config directory") } - fn default_root_directory() -> PathBuf { - dirs::home_dir() - .expect("Failed to evaluate $HOME value") - .join(".nym") - .join("wallet") + fn config_directory() -> PathBuf { + Self::root_directory().join(CONFIG_DIR_NAME) } - fn root_directory(&self) -> PathBuf { - Self::default_root_directory() + fn config_file_path(network: Option) -> PathBuf { + if let Some(network) = network { + let network_filename = format!("{}.toml", network.as_key()); + Self::config_directory().join(network_filename) + } else { + Self::config_directory().join(CONFIG_FILENAME) + } } - fn config_directory(&self) -> PathBuf { - self.root_directory().join("config") - } - - fn data_directory(&self) -> PathBuf { - self.root_directory().join("data") - } - - fn save_to_file(&self, custom_location: Option) -> io::Result<()> { - let config_toml = toml::to_string_pretty(&self) - .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))?; + pub fn save_to_files(&self) -> io::Result<()> { + log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - match custom_location.clone() { - Some(loc) => { - if let Some(parent_dir) = loc.parent() { - fs::create_dir_all(parent_dir) - } else { - Ok(()) + fs::create_dir_all(Self::config_directory())?; + + // Global config + if let Some(global) = &self.global { + let location = Self::config_file_path(None); + + match toml::to_string_pretty(&global) + .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) + .map(|toml| fs::write(location.clone(), toml)) + { + Ok(_) => log::debug!("Writing to: {:#?}", location), + Err(err) => log::warn!("Failed to write to {:#?}: {err}", location), + } + } + + // One file per network + for (network, config) in &self.networks { + let network = match Network::from_str(network).map(Into::into) { + Ok(network) => network, + Err(err) => { + log::warn!("Unexpected name for network configuration, not saving: {err}"); + break; + } + }; + + let location = Self::config_file_path(Some(network)); + match toml::to_string_pretty(config) + .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) + .map(|toml| fs::write(location.clone(), toml)) + { + Ok(_) => log::debug!("Writing to: {:#?}", location), + Err(err) => log::warn!("Failed to write to {:#?}: {err}", location), + } + } + Ok(()) + } + + pub fn load_from_files() -> Self { + // Global + let global = { + let file = Self::config_file_path(None); + match load_from_file::(file.clone()) { + Ok(global) => { + log::debug!("Loaded from file {:#?}", file); + Some(global) + } + Err(err) => { + log::trace!("Not loading {:#?}: {}", file, err); + None } } - None => fs::create_dir_all(self.config_directory()), - }?; + }; - fs::write( - custom_location.unwrap_or_else(|| self.config_directory().join(Self::config_file_name())), - config_toml, - ) + // One file per network + let mut networks = HashMap::new(); + for network in WalletNetwork::iter() { + let file = Self::config_file_path(Some(network)); + match load_from_file::(file.clone()) { + Ok(config) => { + log::trace!("Loaded from file {:#?}", file); + networks.insert(network.as_key(), config); + } + Err(err) => log::trace!("Not loading {:#?}: {}", file, err), + }; + } + + Self { + base: Base::default(), + global, + networks, + } } -} -impl Config { - /// Get the available validators in the order - /// 1. from the configuration file - /// 2. provided remotely - /// 3. hardcoded fallback - pub fn get_validators(&self, network: WalletNetwork) -> impl Iterator + '_ { - // The base validators are (currently) stored as strings - let base_validators = self.base.networks.validators(network.into()).map(|v| { + pub fn get_base_validators( + &self, + network: WalletNetwork, + ) -> impl Iterator + '_ { + 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)) - .cloned() - .chain(base_validators) - .unique() + }) } - pub fn get_nymd_urls(&self, network: WalletNetwork) -> impl Iterator + '_ { - self.get_validators(network).into_iter().map(|v| v.nymd_url) - } - - pub fn get_api_urls(&self, network: WalletNetwork) -> impl Iterator + '_ { + pub fn get_configured_validators( + &self, + network: WalletNetwork, + ) -> impl Iterator + '_ { self - .get_validators(network) + .networks + .get(&network.as_key()) .into_iter() - .filter_map(|v| v.api_url) + .flat_map(|c| c.validators().cloned()) } pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option { @@ -161,25 +216,80 @@ impl Config { .ok() } - pub async fn fetch_updated_validator_urls(&mut self) -> Result<(), BackendError> { - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(3)) - .build()?; - log::debug!( - "Fetching validator urls from: {}", - REMOTE_SOURCE_OF_VALIDATOR_URLS - ); - let response = client - .get(REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string()) - .send() - .await?; - self.base.fetched_validators = serde_json::from_str(&response.text().await?)?; - log::debug!( - "Received validator urls: \n{}", - self.base.fetched_validators - ); - Ok(()) + pub fn select_validator_nymd_url(&mut self, nymd_url: Url, network: WalletNetwork) { + if let Some(net) = self.networks.get_mut(&network.as_key()) { + net.selected_nymd_url = Some(nymd_url); + } else { + self.networks.insert( + network.as_key(), + NetworkConfig { + selected_nymd_url: Some(nymd_url), + ..NetworkConfig::default() + }, + ); + } } + + pub fn select_validator_api_url(&mut self, api_url: Url, network: WalletNetwork) { + if let Some(net) = self.networks.get_mut(&network.as_key()) { + net.selected_api_url = Some(api_url); + } else { + self.networks.insert( + network.as_key(), + NetworkConfig { + selected_nymd_url: Some(api_url), + ..NetworkConfig::default() + }, + ); + } + } + + pub fn get_selected_validator_nymd_url(&self, network: &WalletNetwork) -> Option { + self + .networks + .get(&network.as_key()) + .and_then(|config| config.selected_nymd_url.clone()) + } + + pub fn get_selected_validator_api_url(&self, network: &WalletNetwork) -> Option { + self + .networks + .get(&network.as_key()) + .and_then(|config| config.selected_api_url.clone()) + } + + 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 { + urls.push(url); + } else { + net.validator_urls = Some(vec![url]); + } + } else { + self.networks.insert( + network.as_key(), + NetworkConfig { + validator_urls: Some(vec![url]), + ..NetworkConfig::default() + }, + ); + } + } + + #[allow(unused)] + pub fn remove_validator_url(&mut self, _url: ValidatorUrl, _network: WalletNetwork) { + todo!(); + } +} + +fn load_from_file(file: PathBuf) -> Result +where + T: DeserializeOwned, +{ + fs::read_to_string(file).and_then(|contents| { + toml::from_str::(&contents) + .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) + }) } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] @@ -215,7 +325,7 @@ impl fmt::Display for ValidatorUrl { #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] #[serde(deny_unknown_fields)] -struct OptionalValidators { +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>, @@ -224,7 +334,7 @@ struct OptionalValidators { } impl OptionalValidators { - fn validators(&self, network: WalletNetwork) -> impl Iterator { + pub fn validators(&self, network: WalletNetwork) -> impl Iterator { match network { WalletNetwork::MAINNET => self.mainnet.as_ref(), WalletNetwork::SANDBOX => self.sandbox.as_ref(), @@ -261,53 +371,63 @@ mod tests { use super::*; fn test_config() -> Config { - Config { - base: Base::default(), - network: OptionalValidators { - mainnet: Some(vec![ - ValidatorDetails { - nymd_url: "https://foo".to_string(), - api_url: None, - } - .try_into() - .unwrap(), - ValidatorUrl { - nymd_url: "https://baz".parse().unwrap(), - api_url: Some("https://baz/api".parse().unwrap()), - }, - ]), - sandbox: Some(vec![ValidatorUrl { + let netconfig = NetworkConfig { + selected_nymd_url: None, + selected_api_url: Some("https://my_api_url.com".parse().unwrap()), + + validator_urls: Some(vec![ + ValidatorUrl { + nymd_url: "https://foo".parse().unwrap(), + api_url: None, + }, + ValidatorUrl { nymd_url: "https://bar".parse().unwrap(), api_url: Some("https://bar/api".parse().unwrap()), - }]), - qa: None, - }, + }, + ValidatorUrl { + nymd_url: "https://baz".parse().unwrap(), + api_url: Some("https://baz/api".parse().unwrap()), + }, + ]), + }; + + Config { + base: Base::default(), + global: Some(GlobalConfig::default()), + networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] + .into_iter() + .collect(), } } #[test] fn serialize_to_toml() { + let config = test_config(); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; assert_eq!( - toml::to_string_pretty(&test_config()).unwrap(), - r#"[[network.mainnet]] + toml::to_string_pretty(netconfig).unwrap(), + r#"selected_api_url = 'https://my_api_url.com/' + +[[validator_urls]] nymd_url = 'https://foo/' -[[network.mainnet]] -nymd_url = 'https://baz/' -api_url = 'https://baz/api' - -[[network.sandbox]] +[[validator_urls]] nymd_url = 'https://bar/' api_url = 'https://bar/api' + +[[validator_urls]] +nymd_url = 'https://baz/' +api_url = 'https://baz/api' "# ); } #[test] fn serialize_and_deserialize_to_toml() { let config = test_config(); - let config_str = toml::to_string_pretty(&config).unwrap(); - let config_from_toml = toml::from_str(&config_str).unwrap(); - assert_eq!(config, config_from_toml); + let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; + let config_str = toml::to_string_pretty(netconfig).unwrap(); + let config_from_toml: NetworkConfig = toml::from_str(&config_str).unwrap(); + assert_eq!(netconfig, &config_from_toml); } #[test] @@ -315,7 +435,7 @@ api_url = 'https://bar/api' let config = test_config(); let nymd_url = config - .get_validators(WalletNetwork::MAINNET) + .get_configured_validators(WalletNetwork::MAINNET) .next() .map(|v| v.nymd_url) .unwrap(); @@ -323,7 +443,7 @@ api_url = 'https://bar/api' // The first entry is missing an API URL let api_url = config - .get_validators(WalletNetwork::MAINNET) + .get_configured_validators(WalletNetwork::MAINNET) .next() .and_then(|v| v.api_url); assert_eq!(api_url, None); @@ -334,14 +454,14 @@ api_url = 'https://bar/api' let config = Config::default(); let nymd_url = config - .get_validators(WalletNetwork::MAINNET) + .get_base_validators(WalletNetwork::MAINNET) .next() .map(|v| v.nymd_url) .unwrap(); assert_eq!(nymd_url.as_ref(), "https://rpc.nyx.nodes.guru/"); let api_url = config - .get_validators(WalletNetwork::MAINNET) + .get_base_validators(WalletNetwork::MAINNET) .next() .and_then(|v| v.api_url) .unwrap(); diff --git a/nym-wallet/src-tauri/src/network.rs b/nym-wallet/src-tauri/src/network.rs index 5202b70d52..59574c744e 100644 --- a/nym-wallet/src-tauri/src/network.rs +++ b/nym-wallet/src-tauri/src/network.rs @@ -21,6 +21,10 @@ pub enum Network { } impl Network { + pub fn as_key(&self) -> String { + self.to_string().to_lowercase() + } + pub fn denom(&self) -> Denom { match self { // network defaults should be correctly formatted diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 6429024bd4..3fc7d1380e 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -21,9 +21,7 @@ use strum::IntoEnumIterator; use tokio::sync::RwLock; use url::Url; -use validator_client::{ - connection_tester::run_validator_connection_test, nymd::SigningNymdClient, Client, -}; +use validator_client::{nymd::SigningNymdClient, Client}; #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export, export_to = "../src/types/rust/account.ts"))] @@ -167,36 +165,54 @@ async fn _connect_with_mnemonic( mnemonic: Mnemonic, state: tauri::State<'_, Arc>>, ) -> Result { - update_validator_urls(state.clone()).await?; - let config = state.read().await.config(); - - for network in WalletNetwork::iter() { - log::debug!( - "List of validators for {network}: [\n{}\n]", - config.get_validators(network).format(",\n") - ); + { + let mut w_state = state.write().await; + w_state.load_config_files(); } - // Run connection tests on all nymd and validator-api endpoints - let (nymd_urls, api_urls) = { - let mixnet_contract_address = WalletNetwork::iter() - .map(|network| (network.into(), config.get_mixnet_contract_address(network))) - .collect::>(); - let nymd_urls = WalletNetwork::iter().flat_map(|network| { - config - .get_nymd_urls(network) - .map(move |url| (network.into(), url)) - }); - let api_urls = WalletNetwork::iter().flat_map(|network| { - config - .get_api_urls(network) - .map(move |url| (network.into(), url)) - }); + update_validator_urls(state.clone()).await?; - run_validator_connection_test(nymd_urls, api_urls, mixnet_contract_address).await + let config = { + let state = state.read().await; + + // Take the oppertunity to list all the known validators while we have the state. + for network in WalletNetwork::iter() { + log::debug!( + "List of validators for {network}: [\n{}\n]", + state.get_validators(network).format(",\n") + ); + } + + state.config().clone() }; - let clients = create_clients(&nymd_urls, &api_urls, &mnemonic, &config)?; + // Get all the urls needed for the connection test + let (untested_nymd_urls, untested_api_urls) = { + let state = state.read().await; + (state.get_all_nymd_urls(), state.get_all_api_urls()) + }; + let default_nymd_urls: HashMap = untested_nymd_urls + .iter() + .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) + .collect(); + let default_api_urls: HashMap = untested_api_urls + .iter() + .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) + .collect(); + + // Run connection tests on all nymd and validator-api endpoints + let (nymd_urls, api_urls) = + run_connection_test(untested_nymd_urls, untested_api_urls, &config).await; + + // Create clients for all networks + let clients = create_clients( + &nymd_urls, + &api_urls, + &default_nymd_urls, + &default_api_urls, + &config, + &mnemonic, + )?; // Set the default account let default_network: WalletNetwork = config::defaults::DEFAULT_NETWORK.into(); @@ -224,63 +240,69 @@ async fn _connect_with_mnemonic( account_for_default_network } -fn select_random_responding_nymd_url( - nymd_urls: &HashMap>, - network: WalletNetwork, +async fn run_connection_test( + untested_nymd_urls: HashMap>, + untested_api_urls: HashMap>, config: &Config, -) -> Url { - // We pick a randon responding nymd url, and if not, fall back on the first one in the list. - nymd_urls - .get(&network.into()) - .and_then(|urls| { - let nymd_urls: Vec<_> = urls - .iter() - .filter_map(|(url, result)| if *result { Some(url.clone()) } else { None }) - .collect(); - nymd_urls.choose(&mut rand::thread_rng()).cloned() - }) - .unwrap_or_else(|| { - log::debug!("No passing nymd_urls for {network}: using default"); - config - .get_nymd_urls(network) - .next() - .expect("Expected at least one hardcoded nymd url") - }) -} +) -> ( + HashMap>, + HashMap>, +) { + let mixnet_contract_address = WalletNetwork::iter() + .map(|network| (network.into(), config.get_mixnet_contract_address(network))) + .collect::>(); -fn select_first_responding_api_url( - api_urls: &HashMap>, - network: WalletNetwork, - config: &Config, -) -> Url { - // We pick the first API url among the responding ones. If none exists, fall back on the first - // one in the list. - api_urls - .get(&network.into()) - .and_then(|urls| { - urls - .iter() - .find_map(|(url, result)| if *result { Some(url.clone()) } else { None }) - }) - .unwrap_or_else(|| { - log::debug!("No passing api_urls for {network}: using default"); - config - .get_api_urls(network) - .next() - .expect("Expected at least one hardcoded api url") - }) + let untested_nymd_urls = untested_nymd_urls + .into_iter() + .flat_map(|(net, urls)| urls.into_iter().map(move |url| (net.into(), url))); + + let untested_api_urls = untested_api_urls + .into_iter() + .flat_map(|(net, urls)| urls.into_iter().map(move |url| (net.into(), url))); + + validator_client::connection_tester::run_validator_connection_test( + untested_nymd_urls, + untested_api_urls, + mixnet_contract_address, + ) + .await } fn create_clients( nymd_urls: &HashMap>, api_urls: &HashMap>, - mnemonic: &Mnemonic, + default_nymd_urls: &HashMap, + default_api_urls: &HashMap, config: &Config, + mnemonic: &Mnemonic, ) -> Result>, BackendError> { let mut clients = Vec::new(); for network in WalletNetwork::iter() { - let nymd_url = select_random_responding_nymd_url(nymd_urls, network, config); - let api_url = select_first_responding_api_url(api_urls, network, config); + 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 { + let default_nymd_url = default_nymd_urls + .get(&network) + .expect("Expected at least one nymd_url"); + select_random_responding_url(nymd_urls, network).unwrap_or_else(|| { + log::debug!("No successful nymd_urls for {network}: using default: {default_nymd_url}"); + default_nymd_url.clone() + }) + }; + + let api_url = if let Some(url) = config.get_selected_validator_api_url(&network) { + log::debug!("Using selected api_url for {network}: {url}"); + url.clone() + } else { + let default_api_url = default_api_urls + .get(&network) + .expect("Expected at least one api url"); + select_first_responding_url(api_urls, network).unwrap_or_else(|| { + log::debug!("No passing api_urls for {network}: using default: {default_api_url}"); + default_api_url.clone() + }) + }; log::info!("Connecting to: nymd_url: {nymd_url} for {network}"); log::info!("Connecting to: api_url: {api_url} for {network}"); @@ -301,6 +323,31 @@ fn create_clients( Ok(clients) } +fn select_random_responding_url( + urls: &HashMap>, + network: WalletNetwork, +) -> Option { + urls.get(&network.into()).and_then(|urls| { + let urls: Vec<_> = urls + .iter() + .filter_map(|(url, result)| if *result { Some(url.clone()) } else { None }) + .collect(); + urls.choose(&mut rand::thread_rng()).cloned() + }) +} + +fn select_first_responding_url( + urls: &HashMap>, + network: WalletNetwork, + //config: &Config, +) -> Option { + urls.get(&network.into()).and_then(|urls| { + urls + .iter() + .find_map(|(url, result)| if *result { Some(url.clone()) } else { None }) + }) +} + #[tauri::command] pub fn does_password_file_exist() -> Result { log::info!("Checking wallet file"); diff --git a/nym-wallet/src-tauri/src/platform_constants.rs b/nym-wallet/src-tauri/src/platform_constants.rs index 309a5fee7c..583c00e7d3 100644 --- a/nym-wallet/src-tauri/src/platform_constants.rs +++ b/nym-wallet/src-tauri/src/platform_constants.rs @@ -6,16 +6,24 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { + pub const CONFIG_DIR_NAME: &str = "nym-wallet"; + pub const CONFIG_FILENAME: &str = "config.toml"; pub const STORAGE_DIR_NAME: &str = "nym-wallet"; pub const WALLET_INFO_FILENAME: &str = "saved-wallet.json"; } else if #[cfg(taret_os = "macos")] { + pub const CONFIG_DIR_NAME: &str = "nym-wallet"; + pub const CONFIG_FILENAME: &str = "config.toml"; pub const STORAGE_DIR_NAME: &str = "nym-wallet"; pub const WALLET_INFO_FILENAME: &str = "saved-wallet.json"; } else if #[cfg(taret_os = "windows")] { + pub const CONFIG_DIR_NAME: &str = "NymWallet"; + pub const CONFIG_FILENAME: &str = "Config.toml"; pub const STORAGE_DIR_NAME: &str = "NymWallet"; pub const WALLET_INFO_FILENAME: &str = "saved_wallet.json"; } else { // This case is likely to be a unix-y system + pub const CONFIG_DIR_NAME: &str = "nym-wallet"; + pub const CONFIG_FILENAME: &str = "config.toml"; pub const STORAGE_DIR_NAME: &str = "nym-wallet"; pub const WALLET_INFO_FILENAME: &str = "saved-wallet.json"; } diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index 82da416be5..63e5a961c5 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -1,16 +1,25 @@ -use crate::config::Config; +use crate::config::{Config, OptionalValidators, ValidatorUrl}; use crate::error::BackendError; use crate::network::Network; + +use strum::IntoEnumIterator; use validator_client::nymd::SigningNymdClient; use validator_client::Client; +use itertools::Itertools; +use url::Url; + use std::collections::HashMap; +use std::time::Duration; #[derive(Default)] pub struct State { config: Config, signing_clients: HashMap>, current_network: Network, + + /// Validators that have been fetched dynamically, probably during startup. + fetched_validators: OptionalValidators, } impl State { @@ -28,8 +37,18 @@ impl State { .ok_or(BackendError::ClientNotInitialized) } - pub fn config(&self) -> Config { - self.config.clone() + pub fn config(&self) -> &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(); + } + + #[allow(unused)] + pub fn save_config_files(&self) -> Result<(), BackendError> { + Ok(self.config.save_to_files()?) } pub fn add_client(&mut self, network: Network, client: Client) { @@ -48,8 +67,91 @@ impl State { self.signing_clients = HashMap::new(); } + /// Get the available validators in the order + /// 1. from the configuration file + /// 2. provided remotely + /// 3. hardcoded fallback + pub fn get_validators(&self, network: Network) -> impl Iterator + '_ { + 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 + .chain(fetched_validators) + .chain(default_validators) + .unique() + } + + pub fn get_nymd_urls(&self, network: Network) -> impl Iterator + '_ { + self.get_validators(network).into_iter().map(|v| v.nymd_url) + } + + pub fn get_api_urls(&self, network: Network) -> impl Iterator + '_ { + self + .get_validators(network) + .into_iter() + .filter_map(|v| v.api_url) + } + + pub fn get_all_nymd_urls(&self) -> HashMap> { + Network::iter() + .flat_map(|network| self.get_nymd_urls(network).map(move |url| (network, url))) + .into_group_map() + } + + pub fn get_all_api_urls(&self) -> HashMap> { + Network::iter() + .flat_map(|network| self.get_api_urls(network).map(move |url| (network, url))) + .into_group_map() + } + + /// Fetch validator urls remotely. These are used to in addition to the base ones, and the user + /// configured ones. pub async fn fetch_updated_validator_urls(&mut self) -> Result<(), BackendError> { - self.config.fetch_updated_validator_urls().await + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(3)) + .build()?; + log::debug!( + "Fetching validator urls from: {}", + crate::config::REMOTE_SOURCE_OF_VALIDATOR_URLS + ); + let response = client + .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); + 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); + 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); + 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) } } @@ -73,3 +175,68 @@ macro_rules! api_client { $state.read().await.current_client()?.validator_api }; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn adding_validators_urls_prepends() { + let mut state = State::default(); + let _api_urls = state.get_api_urls(Network::MAINNET).collect::>(); + + state.add_validator_url( + ValidatorUrl { + nymd_url: "http://nymd_url.com".parse().unwrap(), + api_url: Some("http://nymd_url.com/api".parse().unwrap()), + }, + Network::MAINNET, + ); + + state.add_validator_url( + ValidatorUrl { + nymd_url: "http://foo.com".parse().unwrap(), + api_url: None, + }, + Network::MAINNET, + ); + + state.add_validator_url( + ValidatorUrl { + nymd_url: "http://bar.com".parse().unwrap(), + api_url: None, + }, + Network::MAINNET, + ); + + assert_eq!( + state.get_nymd_urls(Network::MAINNET).collect::>(), + vec![ + "http://nymd_url.com/".parse().unwrap(), + "http://foo.com".parse().unwrap(), + "http://bar.com".parse().unwrap(), + "https://rpc.nyx.nodes.guru".parse().unwrap(), + ], + ); + assert_eq!( + state.get_api_urls(Network::MAINNET).collect::>(), + vec![ + "http://nymd_url.com/api".parse().unwrap(), + "https://api.nyx.nodes.guru".parse().unwrap(), + ], + ); + assert_eq!( + state + .get_all_nymd_urls() + .get(&Network::MAINNET) + .unwrap() + .clone(), + vec![ + "http://nymd_url.com/".parse().unwrap(), + "http://foo.com".parse().unwrap(), + "http://bar.com".parse().unwrap(), + "https://rpc.nyx.nodes.guru".parse().unwrap(), + ], + ) + } +} From 2779b5d28ab23efd75da32f54f674792155329a4 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Fri, 1 Apr 2022 18:44:44 +0100 Subject: [PATCH 08/23] Password for wallet with routes (#1187) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new password flow * update global error and load state from children * fix linting * dont load account when creating mnemonic * wallets: provide placeholder functions for ui password * wallet: platform_constants * wallet: swap println to log * UI for existing mnemonic to be use * wallet: inline encryption of wallet file * wallet: tweak error enum names * wallet: general wallet_storage tidy * wallet: tweak some type names * create sign-in context * update sign in functions * move state to context * update pages * connect new rust methods with frontend * update components * remove non-existent method * add separate sign in pages for mnemonic and password * add a hook for clipboard copy * fix workmark svg sizing issue * create step component * use new sign in pages * reorder pages * use clipboard lib directly * ui tweaks * use login type selector * update password strength test + use autofocus prop for password input * start adding routes * restructure with routes * wip * more wip * more wip * reset state where required * wallet: remove unused rust use statements * fix unbond page Co-authored-by: fmtabbara Co-authored-by: Jon Häggblad Co-authored-by: Tommy Verrall --- assets/logo/logo-wordmark.svg | 2 +- nym-wallet/package.json | 1 + nym-wallet/src-tauri/src/main.rs | 3 +- .../src/operations/mixnet/account.rs | 5 +- .../src/wallet_storage/account_data.rs | 9 ++- .../src/wallet_storage/encryption.rs | 10 ++- .../src-tauri/src/wallet_storage/mod.rs | 8 +- .../src-tauri/src/wallet_storage/password.rs | 8 +- nym-wallet/src/context/main.tsx | 28 +++++-- nym-wallet/src/index.tsx | 11 ++- nym-wallet/src/pages/index.ts | 2 +- .../src/pages/send/SendConfirmation.tsx | 3 + nym-wallet/src/pages/send/SendWizard.tsx | 21 +++-- .../src/pages/settings/system-variables.tsx | 10 +-- .../src/pages/sign-in/components/error.tsx | 8 ++ .../components/heading.tsx | 2 +- .../{welcome => sign-in}/components/index.ts | 4 + .../pages/sign-in/components/page-layout.tsx | 33 ++++++++ .../components/password-strength.tsx | 37 +++++++-- .../components/render-page.tsx | 0 .../src/pages/sign-in/components/step.tsx | 30 +++++++ .../pages/sign-in/components/textfields.tsx | 80 ++++++++++++++++++ .../components/word-tiles.tsx | 9 ++- .../src/pages/sign-in/context/index.tsx | 76 +++++++++++++++++ nym-wallet/src/pages/sign-in/index.tsx | 1 + .../pages/sign-in/pages/confirm-mnemonic.tsx | 51 ++++++++++++ .../pages/sign-in/pages/connect-password.tsx | 75 +++++++++++++++++ .../pages/sign-in/pages/create-mnemonic.tsx | 69 ++++++++++++++++ .../pages/sign-in/pages/create-password.tsx | 74 +++++++++++++++++ .../pages/sign-in/pages/existing-account.tsx | 32 ++++++++ nym-wallet/src/pages/sign-in/pages/index.ts | 8 ++ .../pages/sign-in/pages/signin-mnemonic.tsx | 50 ++++++++++++ .../pages/sign-in/pages/signin-password.tsx | 56 +++++++++++++ .../pages/verify-mnemonic.tsx | 40 ++++----- .../src/pages/sign-in/pages/welcome.tsx | 36 +++++++++ .../src/pages/{welcome => sign-in}/types.ts | 8 +- nym-wallet/src/pages/unbond/index.tsx | 24 ++++-- .../pages/welcome/_legacy_create-account.tsx | 56 ------------- .../src/pages/welcome/_legacy_sign-in.tsx | 66 --------------- nym-wallet/src/pages/welcome/index.tsx | 81 ------------------- .../pages/welcome/pages/create-password.tsx | 60 -------------- .../pages/welcome/pages/existing-account.tsx | 55 ------------- nym-wallet/src/pages/welcome/pages/index.ts | 5 -- .../pages/welcome/pages/mnemonic-words.tsx | 34 -------- .../pages/welcome/pages/welcome-content.tsx | 42 ---------- nym-wallet/src/requests/account.ts | 22 +++-- nym-wallet/src/routes/app.tsx | 32 ++++++++ nym-wallet/src/routes/index.tsx | 35 +------- nym-wallet/src/routes/sign-in.tsx | 48 +++++++++++ nym-wallet/src/theme/index.tsx | 1 + 50 files changed, 935 insertions(+), 526 deletions(-) create mode 100644 nym-wallet/src/pages/sign-in/components/error.tsx rename nym-wallet/src/pages/{welcome => sign-in}/components/heading.tsx (93%) rename nym-wallet/src/pages/{welcome => sign-in}/components/index.ts (53%) create mode 100644 nym-wallet/src/pages/sign-in/components/page-layout.tsx rename nym-wallet/src/pages/{welcome => sign-in}/components/password-strength.tsx (69%) rename nym-wallet/src/pages/{welcome => sign-in}/components/render-page.tsx (100%) create mode 100644 nym-wallet/src/pages/sign-in/components/step.tsx create mode 100644 nym-wallet/src/pages/sign-in/components/textfields.tsx rename nym-wallet/src/pages/{welcome => sign-in}/components/word-tiles.tsx (92%) create mode 100644 nym-wallet/src/pages/sign-in/context/index.tsx create mode 100644 nym-wallet/src/pages/sign-in/index.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/confirm-mnemonic.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/connect-password.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/create-mnemonic.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/create-password.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/existing-account.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/index.ts create mode 100644 nym-wallet/src/pages/sign-in/pages/signin-mnemonic.tsx create mode 100644 nym-wallet/src/pages/sign-in/pages/signin-password.tsx rename nym-wallet/src/pages/{welcome => sign-in}/pages/verify-mnemonic.tsx (75%) create mode 100644 nym-wallet/src/pages/sign-in/pages/welcome.tsx rename nym-wallet/src/pages/{welcome => sign-in}/types.ts (71%) delete mode 100644 nym-wallet/src/pages/welcome/_legacy_create-account.tsx delete mode 100644 nym-wallet/src/pages/welcome/_legacy_sign-in.tsx delete mode 100644 nym-wallet/src/pages/welcome/index.tsx delete mode 100644 nym-wallet/src/pages/welcome/pages/create-password.tsx delete mode 100644 nym-wallet/src/pages/welcome/pages/existing-account.tsx delete mode 100644 nym-wallet/src/pages/welcome/pages/index.ts delete mode 100644 nym-wallet/src/pages/welcome/pages/mnemonic-words.tsx delete mode 100644 nym-wallet/src/pages/welcome/pages/welcome-content.tsx create mode 100644 nym-wallet/src/routes/app.tsx create mode 100644 nym-wallet/src/routes/sign-in.tsx diff --git a/assets/logo/logo-wordmark.svg b/assets/logo/logo-wordmark.svg index 14e0208e4a..52512c2665 100644 --- a/assets/logo/logo-wordmark.svg +++ b/assets/logo/logo-wordmark.svg @@ -1,4 +1,4 @@ - + diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 299b34b39f..7d055fb77e 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -42,6 +42,7 @@ "react-hook-form": "^7.14.2", "react-router-dom": "^5.2.0", "semver": "^6.3.0", + "use-clipboard-copy": "^0.2.0", "yup": "^0.32.9" }, "devDependencies": { diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 0c8f2bbd7b..566d09afeb 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -18,8 +18,6 @@ mod operations; mod platform_constants; mod state; mod utils; -// temporarily until it is actually used -#[allow(unused)] mod wallet_storage; use crate::menu::AddDefaultSubmenus; @@ -37,6 +35,7 @@ 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, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 3fc7d1380e..2e7424103a 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -112,9 +112,8 @@ pub async fn create_new_mnemonic() -> Result { } #[tauri::command] -pub fn validate_mnemonic(mnemonic: &str) -> Result<(), BackendError> { - Mnemonic::from_str(mnemonic)?; - Ok(()) +pub fn validate_mnemonic(mnemonic: &str) -> bool { + Mnemonic::from_str(mnemonic).is_ok() } #[tauri::command] diff --git a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs index dd5f9405e8..760208b6b8 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs @@ -2,11 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use cosmrs::bip32::DerivationPath; -use serde::de::Visitor; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::Formatter; +use serde::{Deserialize, Serialize}; use zeroize::Zeroize; -use zeroize::Zeroizing; use crate::error::BackendError; @@ -23,6 +20,7 @@ pub(crate) struct StoredWallet { } impl StoredWallet { + #[allow(unused)] pub fn version(&self) -> u32 { self.version } @@ -31,6 +29,7 @@ impl StoredWallet { self.accounts.is_empty() } + #[allow(unused)] pub fn len(&self) -> usize { self.accounts.len() } @@ -45,6 +44,7 @@ impl StoredWallet { } } + #[allow(unused)] pub fn encrypted_account_by_index(&self, index: usize) -> Option<&EncryptedAccount> { self.accounts.get(index) } @@ -146,6 +146,7 @@ impl MnemonicAccount { &self.mnemonic } + #[allow(unused)] pub(crate) fn hd_path(&self) -> &DerivationPath { &self.hd_path } diff --git a/nym-wallet/src-tauri/src/wallet_storage/encryption.rs b/nym-wallet/src-tauri/src/wallet_storage/encryption.rs index a8aa27fe84..101dde0417 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/encryption.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/encryption.rs @@ -4,14 +4,13 @@ use super::password::UserPassword; use crate::error::BackendError; use aes_gcm::aead::generic_array::ArrayLength; -use aes_gcm::aead::{Aead, NewAead, Payload}; +use aes_gcm::aead::{Aead, NewAead}; use aes_gcm::{Aes256Gcm, Key, Nonce}; use argon2::{ password_hash::rand_core::{OsRng, RngCore}, Algorithm, Argon2, Params, Version, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::convert::TryFrom; +use serde::{Deserialize, Serialize}; use std::marker::PhantomData; use zeroize::Zeroize; @@ -78,6 +77,7 @@ mod base64 { } impl EncryptedData { + #[allow(unused)] pub(crate) fn encrypt_struct(data: &T, password: &UserPassword) -> Result where T: Serialize, @@ -94,10 +94,12 @@ impl EncryptedData { } impl EncryptedData> { + #[allow(unused)] pub(crate) fn encrypt_data(data: &[u8], password: &UserPassword) -> Result { encrypt_data(data, password) } + #[allow(unused)] pub(crate) fn decrypt_data(&self, password: &UserPassword) -> Result, BackendError> { decrypt_data(self, password) } @@ -159,6 +161,7 @@ fn decrypt( .map_err(|_| BackendError::DecryptionError) } +#[allow(unused)] pub(crate) fn encrypt_data( data: &[u8], password: &UserPassword, @@ -194,6 +197,7 @@ where }) } +#[allow(unused)] pub(crate) fn decrypt_data( encrypted_data: &EncryptedData>, password: &UserPassword, diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 11fe6f07ce..092fe6dc83 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -4,14 +4,11 @@ pub(crate) use crate::wallet_storage::password::{UserPassword, WalletAccountId}; use crate::error::BackendError; -use crate::operations::mixnet::account::create_new_account; use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME}; use crate::wallet_storage::account_data::StoredAccount; -use crate::wallet_storage::encryption::{encrypt_struct, EncryptedData}; +use crate::wallet_storage::encryption::encrypt_struct; use cosmrs::bip32::DerivationPath; -use serde::{Deserialize, Serialize}; use std::fs::{self, create_dir_all, OpenOptions}; -use std::os::unix::prelude::OpenOptionsExt; use std::path::PathBuf; use self::account_data::{EncryptedAccount, StoredWallet}; @@ -33,6 +30,7 @@ pub(crate) fn wallet_login_filepath() -> Result { get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) } +#[allow(unused)] pub(crate) fn load_existing_wallet(password: &UserPassword) -> Result { let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); @@ -154,9 +152,7 @@ pub(crate) fn remove_wallet_login_information_at_file( #[cfg(test)] mod tests { use super::*; - use crate::wallet_storage::encryption::encrypt_data; use config::defaults::COSMOS_DERIVATION_PATH; - use std::path::Path; use tempfile::tempdir; // I'm not 100% sure how to feel about having to touch the file system at all diff --git a/nym-wallet/src-tauri/src/wallet_storage/password.rs b/nym-wallet/src-tauri/src/wallet_storage/password.rs index 894576f4b4..04b85277a1 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/password.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/password.rs @@ -3,7 +3,7 @@ use std::fmt; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use zeroize::Zeroize; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -22,9 +22,9 @@ impl AsRef for WalletAccountId { } impl fmt::Display for WalletAccountId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } } // simple wrapper for String that will get zeroized on drop diff --git a/nym-wallet/src/context/main.tsx b/nym-wallet/src/context/main.tsx index 0209364494..dc6091c8de 100644 --- a/nym-wallet/src/context/main.tsx +++ b/nym-wallet/src/context/main.tsx @@ -1,10 +1,11 @@ import React, { useMemo, createContext, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { useSnackbar } from 'notistack'; +import { TLoginType } from 'src/pages/sign-in/types'; import { Account, Network, TCurrency, TMixnodeBondDetails } from '../types'; import { TUseuserBalance, useGetBalance } from '../hooks/useGetBalance'; import { config } from '../../config'; -import { getMixnodeBondDetails, selectNetwork, signInWithMnemonic, signOut } from '../requests'; +import { getMixnodeBondDetails, selectNetwork, signInWithMnemonic, signInWithPassword, signOut } from '../requests'; import { currencyMap } from '../utils'; import { Console } from '../utils/console'; @@ -32,11 +33,14 @@ type TClientContext = { currency?: TCurrency; isLoading: boolean; error?: string; + setIsLoading: (isLoading: boolean) => void; + setError: (value?: string) => void; switchNetwork: (network: Network) => void; getBondDetails: () => Promise; handleShowSettings: () => void; handleShowAdmin: () => void; - logIn: (mnemonic: string) => void; + logIn: (opts: { type: 'mnemonic' | 'password'; value: string }) => void; + signInWithPassword: (password: string) => void; logOut: () => void; }; @@ -90,17 +94,24 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode refreshAccount(); }, [network]); - const logIn = async (mnemonic: string) => { + const logIn = async ({ type, value }: { type: TLoginType; value: string }) => { + if (value.length === 0) { + setError(`A ${type} must be provided`); + return; + } try { setIsLoading(true); - await signInWithMnemonic(mnemonic || ''); - await getBondDetails(); + if (type === 'mnemonic') { + await signInWithMnemonic(value); + } else { + await signInWithPassword(value); + } setNetwork('MAINNET'); + history.push('/balance'); } catch (e) { - setIsLoading(false); setError(e as string); } finally { - history.push('/balance'); + setIsLoading(false); } }; @@ -131,6 +142,9 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode showSettings, network, currency, + setIsLoading, + setError, + signInWithPassword, switchNetwork, getBondDetails, handleShowSettings, diff --git a/nym-wallet/src/index.tsx b/nym-wallet/src/index.tsx index f05b529d07..2400a8b00a 100644 --- a/nym-wallet/src/index.tsx +++ b/nym-wallet/src/index.tsx @@ -3,13 +3,14 @@ import ReactDOM from 'react-dom'; import { ErrorBoundary } from 'react-error-boundary'; import { BrowserRouter as Router } from 'react-router-dom'; import { SnackbarProvider } from 'notistack'; -import { Routes } from './routes'; +import { AppRoutes, SignInRoutes } from './routes'; import { ClientContext, ClientContextProvider } from './context/main'; import { ApplicationLayout } from './layouts'; -import { Admin, Welcome, Settings } from './pages'; +import { Admin, Settings } from './pages'; import { ErrorFallback } from './components'; import { NymWalletTheme, WelcomeTheme } from './theme'; import { maximizeWindow } from './utils'; +import { SignInProvider } from './pages/sign-in/context'; const App = () => { const { clientDetails } = useContext(ClientContext); @@ -20,14 +21,16 @@ const App = () => { return !clientDetails ? ( - + + + ) : ( - + ); diff --git a/nym-wallet/src/pages/index.ts b/nym-wallet/src/pages/index.ts index dc00ac2bb2..6bc5c9fdbc 100644 --- a/nym-wallet/src/pages/index.ts +++ b/nym-wallet/src/pages/index.ts @@ -5,7 +5,7 @@ export * from './delegate'; export * from './internal-docs'; export * from './receive'; export * from './send'; -export * from './welcome'; +export * from './sign-in'; export * from './settings'; export * from './unbond'; export * from './undelegate'; diff --git a/nym-wallet/src/pages/send/SendConfirmation.tsx b/nym-wallet/src/pages/send/SendConfirmation.tsx index 560136bc21..fb2d77fb27 100644 --- a/nym-wallet/src/pages/send/SendConfirmation.tsx +++ b/nym-wallet/src/pages/send/SendConfirmation.tsx @@ -16,6 +16,9 @@ export const SendConfirmation = ({ isLoading: boolean; }) => { const { userBalance, currency, network } = useContext(ClientContext); + + if (!data && !error && !isLoading) return null; + return ( { px: 3, }} > - {activeStep === 0 ? ( - - ) : activeStep === 1 ? ( - - ) : ( - - )} + {activeStep === 0 && } + {activeStep === 1 && } + { color="primary" disableElevation data-testid="button" - onClick={activeStep === 0 ? handleNextStep : activeStep === 1 ? handleSend : handleFinish} + onClick={() => { + switch (activeStep) { + case 0: + return handleNextStep(); + case 1: + return handleSend(); + default: + return handleFinish(); + } + }} disabled={!!(methods.formState.errors.amount || methods.formState.errors.to || isLoading)} size="large" > diff --git a/nym-wallet/src/pages/settings/system-variables.tsx b/nym-wallet/src/pages/settings/system-variables.tsx index 11bf6c0218..a0276b4733 100644 --- a/nym-wallet/src/pages/settings/system-variables.tsx +++ b/nym-wallet/src/pages/settings/system-variables.tsx @@ -139,13 +139,13 @@ export const SystemVariables = ({ pt: 0, }} > - {nodeUpdateResponse === 'success' ? ( + {nodeUpdateResponse === 'success' && ( Node successfully updated - ) : nodeUpdateResponse === 'failed' ? ( - Node update failed - ) : ( - )} + {nodeUpdateResponse === 'failed' && ( + Node update failed + )} + {!nodeUpdateResponse && } - - - - - {inputError && ( - - - {inputError} - - - )} - - - ); -}; diff --git a/nym-wallet/src/pages/welcome/index.tsx b/nym-wallet/src/pages/welcome/index.tsx deleted file mode 100644 index c23444dbdf..0000000000 --- a/nym-wallet/src/pages/welcome/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React, { useContext, useState } from 'react'; -import { NymLogo } from '@nymproject/react'; -import { CircularProgress, Stack, Box } from '@mui/material'; -import { ExistingAccount, WelcomeContent } from './pages'; -import { TPages } from './types'; -import { RenderPage } from './components'; -import { CreateAccountContent } from './_legacy_create-account'; -import { ClientContext } from '../../context/main'; - -// const testMnemonic = -// 'futuristic big receptive caption saw hug odd spoon internal dime bike rake helpless left distribution gusty eyes beg enormous word influence trashy pets curl'; -// -// const mnemonicToArray = (mnemonic: string): TMnemonicWords => -// mnemonic -// .split(' ') -// .reduce((a, c: string, index) => [...a, { name: c, index: index + 1, disabled: false }], [] as TMnemonicWords); - -export const Welcome = () => { - const [page, setPage] = useState('welcome'); - // const [mnemonicWords, setMnemonicWords] = useState(); - - const { isLoading } = useContext(ClientContext); - - // useEffect(() => { - // const mnemonicArray = mnemonicToArray(testMnemonic) - // setMnemonicWords(mnemonicArray) - // }, []) - - return ( - - - {isLoading ? ( - - ) : ( - - - - setPage('existing account')} - onCreateAccountComplete={() => setPage('legacy create account')} - page="welcome" - /> - - setPage('existing account')} /> - {/* setPage('verify mnemonic')} - onPrev={() => setPage('welcome')} - page="create account" - /> - setPage('create password')} - page="verify mnemonic" - /> - */} - setPage('welcome')} page="existing account" /> - - - )} - - - ); -}; diff --git a/nym-wallet/src/pages/welcome/pages/create-password.tsx b/nym-wallet/src/pages/welcome/pages/create-password.tsx deleted file mode 100644 index 9d1ee759e5..0000000000 --- a/nym-wallet/src/pages/welcome/pages/create-password.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useState } from 'react'; -import { Button, FormControl, Grid, IconButton, Stack, TextField } from '@mui/material'; -import { VisibilityOff, Visibility } from '@mui/icons-material'; -import { Subtitle, Title, PasswordStrength } from '../components'; - -export const CreatePassword = () => { - const [password, setPassword] = useState(''); - const [confirmedPassword, setConfirmedPassword] = useState(); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmedPassword, setShowConfirmedPassword] = useState(false); - - return ( - <> - - - - - - - setPassword(e.target.value)} - type={showPassword ? 'input' : 'password'} - InputProps={{ - endAdornment: ( - setShowPassword((show) => !show)}> - {showPassword ? : } - - ), - }} - /> - - setConfirmedPassword(e.target.value)} - type={showConfirmedPassword ? 'input' : 'password'} - InputProps={{ - endAdornment: ( - setShowConfirmedPassword((show) => !show)}> - {showConfirmedPassword ? : } - - ), - }} - /> - - - - - - - ); -}; diff --git a/nym-wallet/src/pages/welcome/pages/existing-account.tsx b/nym-wallet/src/pages/welcome/pages/existing-account.tsx deleted file mode 100644 index e0a4800cd8..0000000000 --- a/nym-wallet/src/pages/welcome/pages/existing-account.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* eslint-disable react/no-unused-prop-types */ -import React, { useContext, useState } from 'react'; -import { Alert, Button, Stack, TextField } from '@mui/material'; -import { Subtitle } from '../components'; -import { ClientContext } from '../../../context/main'; -import { TPages } from '../types'; - -export const ExistingAccount: React.FC<{ page: TPages; onPrev: () => void }> = ({ onPrev }) => { - const [mnemonic, setMnemonic] = useState(''); - - const { logIn, error } = useContext(ClientContext); - - const handleSignIn = async () => { - await logIn(mnemonic); - }; - - const handleSignInOnEnter = ({ key }: React.KeyboardEvent) => { - if (key.toLowerCase() === 'enter') { - logIn(mnemonic); - } - }; - - return ( - - - setMnemonic(e.target.value)} - multiline - rows={5} - fullWidth - onKeyDown={handleSignInOnEnter} - /> - {error && ( - - {error} - - )} - - - - - ); -}; diff --git a/nym-wallet/src/pages/welcome/pages/index.ts b/nym-wallet/src/pages/welcome/pages/index.ts deleted file mode 100644 index 0553bb50f3..0000000000 --- a/nym-wallet/src/pages/welcome/pages/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './welcome-content'; -export * from './mnemonic-words'; -export * from './verify-mnemonic'; -export * from './create-password'; -export * from './existing-account'; diff --git a/nym-wallet/src/pages/welcome/pages/mnemonic-words.tsx b/nym-wallet/src/pages/welcome/pages/mnemonic-words.tsx deleted file mode 100644 index efad61b414..0000000000 --- a/nym-wallet/src/pages/welcome/pages/mnemonic-words.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { Alert, Button, Typography } from '@mui/material'; -import { WordTiles } from '../components'; -import { TMnemonicWords } from '../types'; - -export const MnemonicWords = ({ - mnemonicWords, - onNext, - onPrev, -}: { - mnemonicWords?: TMnemonicWords; - onNext: () => void; - onPrev: () => void; -}) => ( - <> - Write down your mnemonic - - Please store your mnemonic in a safe place. This is the only way to access your wallet! - - - - - -); diff --git a/nym-wallet/src/pages/welcome/pages/welcome-content.tsx b/nym-wallet/src/pages/welcome/pages/welcome-content.tsx deleted file mode 100644 index dbbfe7a0fd..0000000000 --- a/nym-wallet/src/pages/welcome/pages/welcome-content.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable react/no-unused-prop-types */ -import React from 'react'; -import { Button, Stack } from '@mui/material'; -import { SubtitleSlick, Title } from '../components'; -import { TPages } from '../types'; - -export const WelcomeContent: React.FC<{ - page: TPages; - onUseExisting: () => void; - onCreateAccountComplete: () => void; -}> = ({ onUseExisting, onCreateAccountComplete }) => ( - <> - - - - - - - -); diff --git a/nym-wallet/src/requests/account.ts b/nym-wallet/src/requests/account.ts index f8bf6cfc61..f6d75e5bf6 100644 --- a/nym-wallet/src/requests/account.ts +++ b/nym-wallet/src/requests/account.ts @@ -1,14 +1,10 @@ import { invoke } from '@tauri-apps/api'; -import { Account, TCreateAccount } from '../types'; +import { Account } from '../types'; -export const createAccount = async (): Promise => { - const res: TCreateAccount = await invoke('create_new_account'); - return res; -}; +export const createMnemonic = async (): Promise => invoke('create_new_mnemonic'); -export const createMnemonic = async (): Promise => { - const res: string = await invoke('create_new_mnemonic'); - return res; +export const createPassword = async ({ mnemonic, password }: { mnemonic: string; password: string }): Promise => { + await invoke('create_password', { mnemonic, password }); }; export const signInWithMnemonic = async (mnemonic: string): Promise => { @@ -16,6 +12,16 @@ export const signInWithMnemonic = async (mnemonic: string): Promise => return res; }; +export const validateMnemonic = async (mnemonic: string): Promise => { + const res: boolean = await invoke('validate_mnemonic', { mnemonic }); + return res; +}; + +export const signInWithPassword = async (password: string): Promise => { + const res: Account = await invoke('sign_in_with_password', { password }); + return res; +}; + export const signOut = async (): Promise => { await invoke('logout'); }; diff --git a/nym-wallet/src/routes/app.tsx b/nym-wallet/src/routes/app.tsx new file mode 100644 index 0000000000..3b393bb8e4 --- /dev/null +++ b/nym-wallet/src/routes/app.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Switch, Route } from 'react-router-dom'; +import { Bond, Balance, Delegate, InternalDocs, Receive, Send, Unbond, Undelegate } from '../pages'; + +export const AppRoutes = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/nym-wallet/src/routes/index.tsx b/nym-wallet/src/routes/index.tsx index 039e3a447d..d2037fa0e4 100644 --- a/nym-wallet/src/routes/index.tsx +++ b/nym-wallet/src/routes/index.tsx @@ -1,33 +1,2 @@ -import React from 'react'; -import { Switch, Route } from 'react-router-dom'; -import { Balance } from '../pages/balance'; -import { Bond, Delegate, InternalDocs, Receive, Send, Unbond, Undelegate } from '../pages'; - -export const Routes = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - -); +export * from './app'; +export * from './sign-in'; diff --git a/nym-wallet/src/routes/sign-in.tsx b/nym-wallet/src/routes/sign-in.tsx new file mode 100644 index 0000000000..2d161641cd --- /dev/null +++ b/nym-wallet/src/routes/sign-in.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Switch, Route } from 'react-router-dom'; +import { PageLayout } from 'src/pages/sign-in/components'; +import { + CreateMnemonic, + CreatePassword, + ExistingAccount, + SignInMnemonic, + SignInPassword, + VerifyMnemonic, + WelcomeContent, + ConnectPassword, +} from 'src/pages/sign-in/pages'; +import { ConfirmMnemonic } from 'src/pages/sign-in/pages/confirm-mnemonic'; + +export const SignInRoutes = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/nym-wallet/src/theme/index.tsx b/nym-wallet/src/theme/index.tsx index 70470845e6..3d59f21892 100644 --- a/nym-wallet/src/theme/index.tsx +++ b/nym-wallet/src/theme/index.tsx @@ -8,6 +8,7 @@ import { NymWalletThemeWithMode } from './NymWalletTheme'; /** * Provides the theme for the Network Explorer by reacting to the light/dark mode choice stored in the app context. */ + export const NymWalletTheme: React.FC = ({ children }) => { const { mode } = useContext(ClientContext); return {children}; From 2b9ea90e1620ed92ad7f63213d0a5d39420d87c4 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Fri, 1 Apr 2022 18:50:16 +0100 Subject: [PATCH 09/23] Nym Wallet version 1.0.2 --- nym-wallet/src-tauri/Cargo.toml | 2 +- nym-wallet/src-tauri/tauri.conf.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 55714076cb..bd2d45e059 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym_wallet" -version = "1.0.1" +version = "1.0.2" description = "Nym Native Wallet" authors = ["Nym Technologies SA"] license = "" diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 929536616f..126d4014db 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "package": { "productName": "nym-wallet", - "version": "1.0.1" + "version": "1.0.2" }, "build": { "distDir": "../dist", From 5bb516471edea95fb411a806fbb9d248020948d5 Mon Sep 17 00:00:00 2001 From: Max Hampshire Date: Sun, 3 Apr 2022 20:26:25 +0200 Subject: [PATCH 10/23] updated mainnet bandwidthgen contract address --- .../contractAddresses.json | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/contracts/basic-bandwidth-generation/contractAddresses.json b/contracts/basic-bandwidth-generation/contractAddresses.json index 2517fbf035..3d94f9b469 100644 --- a/contracts/basic-bandwidth-generation/contractAddresses.json +++ b/contracts/basic-bandwidth-generation/contractAddresses.json @@ -1,14 +1,12 @@ { - "rinkeby":{ - "NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B", - "BANDWIDTH_GENERATOR":"0x5FbDB2315678afecb367f032d93F642f64180aa3", - "GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B" - }, - "mainnet":{ - "NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1", - "NYMT": "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F", - "BANDWIDTH_GENERATOR":"", + "rinkeby": + {"NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B", + "BANDWIDTH_GENERATOR":"0xfa2714Bf14EB5Bb887e4A54984C6F7A7e3E6c84b", + "GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"}, + "mainnet": + {"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1", + "NYMT":"0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F", + "BANDWIDTH_GENERATOR":"0x3FfEb99acca159A182f35F9944dAf3BF41Ae8165", "BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be", - "GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906" - } + "GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"} } \ No newline at end of file From 58fd40fb8e3660ec304bf480b723873f66e50159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Sun, 3 Apr 2022 20:49:12 +0200 Subject: [PATCH 11/23] Fix clippy warnings --- common/credentials/src/coconut/bandwidth.rs | 2 +- validator-api/src/coconut/deposit.rs | 2 +- validator-api/src/coconut/tests.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/credentials/src/coconut/bandwidth.rs b/common/credentials/src/coconut/bandwidth.rs index 8cc95f3123..d25187950a 100644 --- a/common/credentials/src/coconut/bandwidth.rs +++ b/common/credentials/src/coconut/bandwidth.rs @@ -215,7 +215,7 @@ mod test { )); assert!(!BandwidthVoucher::verify_against_plain( &voucher.get_public_attributes(), - &vec![], + &[], )); assert!(!BandwidthVoucher::verify_against_plain( &voucher.get_public_attributes(), diff --git a/validator-api/src/coconut/deposit.rs b/validator-api/src/coconut/deposit.rs index 7c8f2c218b..f773b60151 100644 --- a/validator-api/src/coconut/deposit.rs +++ b/validator-api/src/coconut/deposit.rs @@ -114,7 +114,7 @@ mod test { ¶ms, "1234".to_string(), VOUCHER_INFO.to_string(), - tx_hash.clone(), + tx_hash, identity::PrivateKey::from_base58_string( identity::KeyPair::new(&mut rng) .private_key() diff --git a/validator-api/src/coconut/tests.rs b/validator-api/src/coconut/tests.rs index 56c95572bc..4e5aaa5315 100644 --- a/validator-api/src/coconut/tests.rs +++ b/validator-api/src/coconut/tests.rs @@ -139,7 +139,7 @@ async fn signed_before() { ¶ms, "1234".to_string(), VOUCHER_INFO.to_string(), - tx_hash.clone(), + tx_hash, identity::PrivateKey::from_base58_string( identity::KeyPair::new(&mut rng) .private_key() @@ -335,7 +335,7 @@ async fn blind_sign_correct() { ¶ms, "1234".to_string(), VOUCHER_INFO.to_string(), - tx_hash.clone(), + tx_hash, identity::PrivateKey::from_base58_string( identity::KeyPair::new(&mut rng) .private_key() From 11b22ce2c1215cec5ed30db366d0ac98c732a90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Sun, 3 Apr 2022 21:04:47 +0200 Subject: [PATCH 12/23] wallet: add test for decrypting file --- .../src-tauri/src/wallet_storage/mod.rs | 35 +++++++++++++++++++ .../test-data/saved-wallet.json | 21 +++++++++++ 2 files changed, 56 insertions(+) create mode 100644 nym-wallet/src-tauri/src/wallet_storage/test-data/saved-wallet.json diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 092fe6dc83..862e23e682 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -153,6 +153,7 @@ pub(crate) fn remove_wallet_login_information_at_file( mod tests { use super::*; use config::defaults::COSMOS_DERIVATION_PATH; + use std::str::FromStr; use tempfile::tempdir; // I'm not 100% sure how to feel about having to touch the file system at all @@ -308,4 +309,38 @@ mod tests { // The file should now be removed assert!(!wallet_file.exists()); } + + #[test] + fn decrypt_stored_wallet() { + const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet.json"; + let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + + let wallet = load_existing_wallet_at_file(wallet_file).unwrap(); + + let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let password = UserPassword::new("password".to_string()); + let bad_password = UserPassword::new("bad-password".to_string()); + let id1 = WalletAccountId::new("first".to_string()); + let id2 = WalletAccountId::new("second".to_string()); + + assert!(!wallet.password_can_decrypt_all(&bad_password)); + assert!(wallet.password_can_decrypt_all(&password)); + + let account1 = wallet.decrypt_account(&id1, &password).unwrap(); + let account2 = wallet.decrypt_account(&id2, &password).unwrap(); + + let expected_account1 = bip39::Mnemonic::from_str("country mean universe text phone begin deputy reject result good cram illness common cluster proud swamp digital patrol spread bar face december base kick").unwrap(); + let expected_account2 = bip39::Mnemonic::from_str("home mansion start quiz dress decide hint second dragon sunny juice always steak real minimum art rival skin draw total pulp foot goddess agent").unwrap(); + + assert_eq!(account1.mnemonic(), &expected_account1); + assert_eq!(account2.mnemonic(), &expected_account2); + + let StoredAccount::Mnemonic(ref mnemonic1) = account1; + assert_eq!(mnemonic1.mnemonic(), &expected_account1); + assert_eq!(mnemonic1.hd_path(), &cosmos_hd_path); + + let StoredAccount::Mnemonic(ref mnemonic2) = account2; + assert_eq!(mnemonic2.mnemonic(), &expected_account2); + assert_eq!(mnemonic2.hd_path(), &cosmos_hd_path); + } } diff --git a/nym-wallet/src-tauri/src/wallet_storage/test-data/saved-wallet.json b/nym-wallet/src-tauri/src/wallet_storage/test-data/saved-wallet.json new file mode 100644 index 0000000000..2cf0288fbc --- /dev/null +++ b/nym-wallet/src-tauri/src/wallet_storage/test-data/saved-wallet.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "accounts": [ + { + "id": "first", + "account": { + "ciphertext": "icnpxLmr/H7FIIOaEf7DYNLuM6uhh7poEppXpYCllQD33TjY+8eLtVvhEQmjX60IQeFOd+1JCcrHa2B12vlBAYlfM4gBxA6d2ZJ8+Dw/vNvBNyChiyUx2euV3vPGOs22r/XDBsmEeF40XZcXftQZa2kzYaPnkbP+eiMOIWkcY4FYOEHwx5SxT4VBPZIrVTC3iDalJLWybVbbw/Bc2zbzEXI1ckg4Ccydj95SMil9BiyDpALfZqwlai7I97S+BjmcVxSCsYqFjTkRUHVMjrEr7fWHKU4DIOM=", + "salt": "CtnbfkxTybqz0U4cPHW2jQ==", + "iv": "77ZROU6dAMttEWwS" + } + }, + { + "id": "second", + "account": { + "ciphertext": "nsqZHdQFlskglc5izKgnr8sBwdMmd82h2Rnjdos9EUca3cqkUdFYEjZDsK8OGR3GZ9alLTNt/1U97Rvvr2HPAWbzl23FW2YXaLTA6yj6ZwQK5w0MYE061NYbcxNHuzT9f5aQWkGULAk4RWb5t8eUX7y/NdJr3tA5xuGOLhooTfBB98/4RpupDsYGZp1DPC/GMFppOA3GmKs9bacZm805Bhfq5mwhXab1SjJQpFHMHisCMhxo/oLqulKML1tQMetBdqDTjJmPpdUnd1mi", + "salt": "J2TMLjKv4dkZ/kXso9FGhg==", + "iv": "CTqqoMa4LetvBKCP" + } + } + ] +} \ No newline at end of file From d81967189a6aa40cbb37fbdb2e6b4612c188d451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 4 Apr 2022 08:41:12 +0200 Subject: [PATCH 13/23] Fix clippy warnings for beta toolchain --- validator-api/src/rewarded_set_updater/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator-api/src/rewarded_set_updater/mod.rs b/validator-api/src/rewarded_set_updater/mod.rs index a358c78be7..88a14ffc6a 100644 --- a/validator-api/src/rewarded_set_updater/mod.rs +++ b/validator-api/src/rewarded_set_updater/mod.rs @@ -139,7 +139,7 @@ impl RewardedSetUpdater { .insert_rewarding_report(rewarding_report) .await?; - Ok(self.generate_reward_messages(&to_reward).await?) + self.generate_reward_messages(&to_reward).await } #[allow(unused_variables)] From 7ddde50ffa402f0f6ad788735b22c3cb8bdf43c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Wed, 30 Mar 2022 11:51:41 +0200 Subject: [PATCH 14/23] wallet: expose validator urls to the frontend --- nym-wallet/src-tauri/src/main.rs | 2 ++ .../src/operations/mixnet/account.rs | 33 +++++++++++++++++++ nym-wallet/src/types/rust/validatorurls.ts | 3 ++ 3 files changed, 38 insertions(+) create mode 100644 nym-wallet/src/types/rust/validatorurls.ts diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 566d09afeb..61346a19cf 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -41,6 +41,8 @@ fn main() { 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, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 2e7424103a..8cb2d4fee7 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -58,6 +58,13 @@ 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, @@ -160,6 +167,32 @@ pub async fn update_validator_urls( Ok(()) } +#[tauri::command] +pub async fn get_validator_nymd_urls( + network: WalletNetwork, + state: tauri::State<'_, Arc>>, +) -> Result { + let config = state.read().await.config(); + let urls: Vec = config + .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 config = state.read().await.config(); + let urls: Vec = config + .get_api_urls(network) + .map(|url| url.to_string()) + .collect(); + Ok(ValidatorUrls { urls }) +} + async fn _connect_with_mnemonic( mnemonic: Mnemonic, state: tauri::State<'_, Arc>>, diff --git a/nym-wallet/src/types/rust/validatorurls.ts b/nym-wallet/src/types/rust/validatorurls.ts new file mode 100644 index 0000000000..c4b4a7624e --- /dev/null +++ b/nym-wallet/src/types/rust/validatorurls.ts @@ -0,0 +1,3 @@ +export interface ValidatorUrls { + urls: Array; +} From b5eddb691943bba0379e053f9d1f7fb5702cf4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 4 Apr 2022 09:19:00 +0200 Subject: [PATCH 15/23] wallet: fix get urls function call --- nym-wallet/src-tauri/src/operations/mixnet/account.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 8cb2d4fee7..12901cc046 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -172,8 +172,8 @@ pub async fn get_validator_nymd_urls( network: WalletNetwork, state: tauri::State<'_, Arc>>, ) -> Result { - let config = state.read().await.config(); - let urls: Vec = config + let state = state.read().await; + let urls: Vec = state .get_nymd_urls(network) .map(|url| url.to_string()) .collect(); @@ -185,8 +185,8 @@ pub async fn get_validator_api_urls( network: WalletNetwork, state: tauri::State<'_, Arc>>, ) -> Result { - let config = state.read().await.config(); - let urls: Vec = config + let state = state.read().await; + let urls: Vec = state .get_api_urls(network) .map(|url| url.to_string()) .collect(); From 0d343eb82d2c82b32beb4ba6d21225f109b06054 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 11:13:44 +0100 Subject: [PATCH 16/23] Update GitHub Actions for wallet to upload the signature for the auto-updater --- .github/workflows/nym-wallet-publish-macos.yml | 4 +++- .github/workflows/nym-wallet-publish-ubuntu.yml | 4 +++- .github/workflows/nym-wallet-publish-windows10.yml | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml index 7cd484a7ee..3f59acaed1 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -69,7 +69,9 @@ jobs: - name: Upload to release based on tag name uses: softprops/action-gh-release@v1 with: - files: nym-wallet/target/release/bundle/dmg/*.dmg + files: | + nym-wallet/target/release/bundle/dmg/*.dmg + nym-wallet/target/release/bundle/dmg/*.dmg.sig - name: Clean up keychain if: ${{ always() }} diff --git a/.github/workflows/nym-wallet-publish-ubuntu.yml b/.github/workflows/nym-wallet-publish-ubuntu.yml index 3b2c37ef20..17adf129dc 100644 --- a/.github/workflows/nym-wallet-publish-ubuntu.yml +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -43,4 +43,6 @@ jobs: - name: Upload to release based on tag name uses: softprops/action-gh-release@v1 with: - files: nym-wallet/target/release/bundle/appimage/*.AppImage + files: | + nym-wallet/target/release/bundle/appimage/*.AppImage + nym-wallet/target/release/bundle/appimage/*.AppImage.sig diff --git a/.github/workflows/nym-wallet-publish-windows10.yml b/.github/workflows/nym-wallet-publish-windows10.yml index c9e9e1ba83..939f345493 100644 --- a/.github/workflows/nym-wallet-publish-windows10.yml +++ b/.github/workflows/nym-wallet-publish-windows10.yml @@ -68,4 +68,6 @@ jobs: - name: Upload to release based on tag name uses: softprops/action-gh-release@v1 with: - files: nym-wallet/target/release/bundle/msi/*.msi + files: | + nym-wallet/target/release/bundle/msi/*.msi + nym-wallet/target/release/bundle/msi/*.msi.sig From 15b552fa6202212a850d03f067f416e0c684258f Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 12:07:30 +0100 Subject: [PATCH 17/23] GitHub Actions wallet publish - add missing env vars for updater signing --- .github/workflows/nym-wallet-publish-macos.yml | 2 ++ .github/workflows/nym-wallet-publish-ubuntu.yml | 10 +++++++--- .github/workflows/nym-wallet-publish-windows10.yml | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml index 3f59acaed1..baf3cbf22a 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -64,6 +64,8 @@ jobs: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} run: yarn && yarn build - name: Upload to release based on tag name diff --git a/.github/workflows/nym-wallet-publish-ubuntu.yml b/.github/workflows/nym-wallet-publish-ubuntu.yml index 17adf129dc..a908a5f511 100644 --- a/.github/workflows/nym-wallet-publish-ubuntu.yml +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -37,9 +37,13 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: stable - - name: Install app dependencies and build it - run: yarn && yarn build - + - name: Install app dependencies + run: yarn + - name: Build app + run: yarn build + env: + TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} - name: Upload to release based on tag name uses: softprops/action-gh-release@v1 with: diff --git a/.github/workflows/nym-wallet-publish-windows10.yml b/.github/workflows/nym-wallet-publish-windows10.yml index 939f345493..3ea4ba1583 100644 --- a/.github/workflows/nym-wallet-publish-windows10.yml +++ b/.github/workflows/nym-wallet-publish-windows10.yml @@ -63,6 +63,8 @@ jobs: ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} + TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} run: yarn build - name: Upload to release based on tag name From de45ab8995afcd0e79a505abeb62a1498451942f Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 13:47:01 +0100 Subject: [PATCH 18/23] GitHub Actions: nym wallet build uploads updater zip and signature to release --- .github/workflows/nym-wallet-publish-macos.yml | 2 +- .github/workflows/nym-wallet-publish-ubuntu.yml | 2 +- .github/workflows/nym-wallet-publish-windows10.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml index baf3cbf22a..8355cbc98a 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -73,7 +73,7 @@ jobs: with: files: | nym-wallet/target/release/bundle/dmg/*.dmg - nym-wallet/target/release/bundle/dmg/*.dmg.sig + nym-wallet/target/release/bundle/dmg/*.dmg.tar.gz* - name: Clean up keychain if: ${{ always() }} diff --git a/.github/workflows/nym-wallet-publish-ubuntu.yml b/.github/workflows/nym-wallet-publish-ubuntu.yml index a908a5f511..36893ba388 100644 --- a/.github/workflows/nym-wallet-publish-ubuntu.yml +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -49,4 +49,4 @@ jobs: with: files: | nym-wallet/target/release/bundle/appimage/*.AppImage - nym-wallet/target/release/bundle/appimage/*.AppImage.sig + nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz* diff --git a/.github/workflows/nym-wallet-publish-windows10.yml b/.github/workflows/nym-wallet-publish-windows10.yml index 3ea4ba1583..132cb71df6 100644 --- a/.github/workflows/nym-wallet-publish-windows10.yml +++ b/.github/workflows/nym-wallet-publish-windows10.yml @@ -72,4 +72,4 @@ jobs: with: files: | nym-wallet/target/release/bundle/msi/*.msi - nym-wallet/target/release/bundle/msi/*.msi.sig + nym-wallet/target/release/bundle/msi/*.msi.zip* From 1f6d4153a7472a821742d918daaa4b49b28542d3 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 14:25:55 +0100 Subject: [PATCH 19/23] GitHub Actions: fix wallet updater path for upload to release --- .github/workflows/nym-wallet-publish-macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml index 8355cbc98a..b00c8b4902 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -73,7 +73,7 @@ jobs: with: files: | nym-wallet/target/release/bundle/dmg/*.dmg - nym-wallet/target/release/bundle/dmg/*.dmg.tar.gz* + nym-wallet/target/release/bundle/dmg/*.app.tar.gz* - name: Clean up keychain if: ${{ always() }} From bed709b155a1333f71f95b82ed2fcd77146702e5 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 15:02:36 +0100 Subject: [PATCH 20/23] Fix wallet lock file version --- nym-wallet/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index fd97a2b32b..06e51a7772 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -2759,7 +2759,7 @@ dependencies = [ [[package]] name = "nym_wallet" -version = "1.0.1" +version = "1.0.2" dependencies = [ "aes-gcm", "argon2", From 64ee03112eaa5702b25f0068124ff069c062fb9c Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 15:03:01 +0100 Subject: [PATCH 21/23] GitHub Actions: fix wallet updater location for MacOS --- .github/workflows/nym-wallet-publish-macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml index b00c8b4902..23044a79f6 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -73,7 +73,7 @@ jobs: with: files: | nym-wallet/target/release/bundle/dmg/*.dmg - nym-wallet/target/release/bundle/dmg/*.app.tar.gz* + nym-wallet/target/release/bundle/macos/*.app.tar.gz* - name: Clean up keychain if: ${{ always() }} From 021b542a4a582eff57aecb4b5c5e2e89a986a7f5 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 11:13:21 +0100 Subject: [PATCH 22/23] Add updater config to `tauri.conf.json` --- nym-wallet/src-tauri/tauri.conf.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 126d4014db..e10f56ba29 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -46,7 +46,12 @@ } }, "updater": { - "active": false + "active": true, + "endpoints": [ + "https://nymtech.net/.wellknown/wallet/updater.json" + ], + "dialog": true, + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=" }, "allowlist": { "window": { From 5deafaa27b011c6b8621cf71bd56dc4516313a69 Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Mon, 4 Apr 2022 11:31:29 +0100 Subject: [PATCH 23/23] Add tauri updater feature --- nym-wallet/Cargo.lock | 378 ++++++++++++++++++++++++++++++++ nym-wallet/src-tauri/Cargo.toml | 2 +- 2 files changed, 379 insertions(+), 1 deletion(-) diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 06e51a7772..9ca3f04426 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -102,6 +102,101 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "ashpd" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eea0a7a98b3bd2832eb087e1078f6f58db5a54447574d3007cdac6309c1e9f1" +dependencies = [ + "enumflags2", + "futures", + "rand 0.8.5", + "serde", + "serde_repr", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b" +dependencies = [ + "easy-parallel", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-recursion" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-task" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" + [[package]] name = "async-trait" version = "0.1.52" @@ -137,6 +232,24 @@ dependencies = [ "system-deps 6.0.2", ] +[[package]] +name = "attohttpc" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69e13a99a7e6e070bb114f7ff381e58c7ccc188630121fc4c2fe4bcf24cd072" +dependencies = [ + "flate2", + "http", + "log", + "native-tls", + "openssl", + "serde", + "serde_json", + "serde_urlencoded", + "url", + "wildmatch", +] + [[package]] name = "atty" version = "0.2.14" @@ -398,6 +511,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "cairo-rs" version = "0.15.6" @@ -562,6 +681,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + [[package]] name = "config" version = "0.1.0" @@ -1035,6 +1163,17 @@ dependencies = [ "const-oid 0.7.1", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1151,6 +1290,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" +[[package]] +name = "easy-parallel" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" + [[package]] name = "ecdsa" version = "0.12.4" @@ -1265,6 +1410,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enumflags2" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b3ab37dc79652c9d85f1f7b6070d77d321d2467f5fe7b00d6b7a86c57b092ae" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -1278,6 +1444,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "event-listener" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" + [[package]] name = "eyre" version = "0.6.7" @@ -2514,6 +2686,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minisign-verify" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -2661,6 +2839,19 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2828,6 +3019,17 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_id" version = "0.1.1" @@ -2898,6 +3100,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-stream" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "pairing" version = "0.20.0" @@ -3252,6 +3464,25 @@ dependencies = [ "miniz_oxide 0.3.7", ] +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + [[package]] name = "polyval" version = "0.5.3" @@ -3729,6 +3960,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rfd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aaf1d71ccd44689f7c2c72da1117fd8db71f72a76fe9b5c5dbb17ab903007e0" +dependencies = [ + "ashpd", + "block", + "dispatch", + "glib-sys 0.15.6", + "gobject-sys 0.15.5", + "gtk-sys", + "js-sys", + "lazy_static", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows", +] + [[package]] name = "ring" version = "0.16.20" @@ -4116,6 +4373,21 @@ dependencies = [ "digest 0.10.3", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -4498,6 +4770,8 @@ version = "1.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3571de0bcfd1f4f7599cbb3fe42f28ea9f52c3e89914ff04eaba68b5ee36bb51" dependencies = [ + "attohttpc", + "base64", "bincode", "cfg_aliases", "dirs-next", @@ -4511,12 +4785,14 @@ dependencies = [ "gtk", "http", "ignore", + "minisign-verify", "once_cell", "open", "percent-encoding", "rand 0.8.5", "raw-window-handle", "regex", + "rfd", "semver 1.0.6", "serde", "serde_json", @@ -5423,6 +5699,21 @@ dependencies = [ "windows-bindgen", ] +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "wildmatch" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c48bd20df7e4ced539c12f570f937c6b4884928a87fee70a479d72f031d4e0" + [[package]] name = "winapi" version = "0.3.9" @@ -5619,6 +5910,67 @@ dependencies = [ "libc", ] +[[package]] +name = "zbus" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb86f3d4592e26a48b2719742aec94f8ae6238ebde20d98183ee185d1275e9a" +dependencies = [ + "async-broadcast", + "async-channel", + "async-executor", + "async-io", + "async-lock", + "async-recursion", + "async-task", + "async-trait", + "byteorder", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "lazy_static", + "nix", + "once_cell", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "winapi", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36823cc10fddc3c6b19f048903262dacaf8274170e9a255784bdd8b4570a8040" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "zbus_names" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45dfcdcf87b71dad505d30cc27b1b7b88a64b6d1c435648f48f9dbc1fdc4b7e1" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeroize" version = "1.4.3" @@ -5682,3 +6034,29 @@ dependencies = [ "cc", "libc", ] + +[[package]] +name = "zvariant" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ea5dc38b2058fae6a5b79009388143dadce1e91c26a67f984a0fc0381c8033" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2cecc5a61c2a053f7f653a24cd15b3b0195d7f7ddb5042c837fb32e161fb7a" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn", +] diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index bd2d45e059..1df0beb7b2 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -34,7 +34,7 @@ reqwest = "0.11.9" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" strum = { version = "0.23", features = ["derive"] } -tauri = { version = "=1.0.0-rc.2", features = ["clipboard-all", "shell-open", "window-maximize"] } +tauri = { version = "=1.0.0-rc.2", features = ["clipboard-all", "shell-open", "updater", "window-maximize"] } tendermint-rpc = "0.23.0" thiserror = "1.0" tokio = { version = "1.10", features = ["sync", "time"] }