From 3bfce128a6ef1897a4142afbecb09e7141247f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 16 May 2022 23:39:08 +0200 Subject: [PATCH] wallet: store id instead of mnemonic in state --- CHANGELOG.md | 1 + Cargo.lock | 1 + nym-wallet/src-tauri/src/main.rs | 3 +- .../src/operations/mixnet/account.rs | 116 ++++++++++++------ nym-wallet/src-tauri/src/state.rs | 14 ++- 5 files changed, 93 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e4b58981..c7824f88ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added +- wallet: require password to switch accounts - wallet: added support for multiple accounts ([#1265]) - wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint. - mixnet-contract: Replace all naked `-` with `saturating_sub`. diff --git a/Cargo.lock b/Cargo.lock index 225174005d..5f2b2e0634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3178,6 +3178,7 @@ dependencies = [ "proxy-helpers", "publicsuffix", "rand 0.7.3", + "rocket", "serde", "socks5-requests", "sqlx", diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 1fd7a69817..cb49a3974a 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -43,13 +43,12 @@ fn main() { mixnet::account::does_password_file_exist, mixnet::account::get_balance, mixnet::account::list_accounts, - mixnet::account::sign_in_decrypted_account, mixnet::account::logout, mixnet::account::remove_account_for_password, mixnet::account::remove_password, mixnet::account::show_mnemonic_for_account_in_password, - mixnet::account::sign_in_decrypted_account, mixnet::account::sign_in_with_password, + mixnet::account::sign_in_with_password_and_account_id, mixnet::account::switch_network, mixnet::account::validate_mnemonic, mixnet::admin::get_contract_settings, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index c2b4e1eff8..79a9f2ddb0 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -4,7 +4,7 @@ use crate::error::BackendError; use crate::network::Network as WalletNetwork; use crate::network_config; use crate::nymd_client; -use crate::state::State; +use crate::state::{State, WalletAccountIds}; use crate::wallet_storage::{self, DEFAULT_LOGIN_ID}; use bip39::{Language, Mnemonic}; @@ -229,6 +229,10 @@ async fn _connect_with_mnemonic( }; // Register all the clients + { + let mut w_state = state.write().await; + w_state.logout(); + } for client in clients { let network: WalletNetwork = client.network.into(); let mut w_state = state.write().await; @@ -395,9 +399,9 @@ pub async fn sign_in_with_password( } fn extract_first_mnemonic( - stored_account: &wallet_storage::StoredLogin, + stored_login: &wallet_storage::StoredLogin, ) -> Result { - let mnemonic = match stored_account { + let mnemonic = match stored_login { wallet_storage::StoredLogin::Mnemonic(ref account) => account.mnemonic().clone(), wallet_storage::StoredLogin::Multiple(ref accounts) => { // Login using the first account in the list @@ -413,6 +417,44 @@ fn extract_first_mnemonic( Ok(mnemonic) } +#[tauri::command] +pub async fn sign_in_with_password_and_account_id( + account_id: &str, + password: &str, + state: tauri::State<'_, Arc>>, +) -> Result { + log::info!("Signing in with password"); + + // Currently we only support a single, default, id in the wallet + let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); + let account_id = wallet_storage::AccountId::new(account_id.to_string()); + let password = wallet_storage::UserPassword::new(password.to_string()); + let stored_login = wallet_storage::load_existing_login(&login_id, &password)?; + + let mnemonic = extract_mnemonic(&stored_login, &account_id)?; + let first_login_id_when_converting = login_id.into(); + set_state_with_all_accounts(stored_login, first_login_id_when_converting, state.clone()).await?; + + _connect_with_mnemonic(mnemonic, state).await +} + +fn extract_mnemonic( + stored_login: &wallet_storage::StoredLogin, + account_id: &wallet_storage::AccountId, +) -> Result { + let mnemonic = match stored_login { + wallet_storage::StoredLogin::Mnemonic(_) => { + return Err(BackendError::WalletNoSuchAccountIdInWalletLogin); + } + wallet_storage::StoredLogin::Multiple(ref accounts) => accounts + .get_account(account_id) + .ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)? + .mnemonic() + .clone(), + }; + Ok(mnemonic) +} + #[tauri::command] pub fn remove_password() -> Result<(), BackendError> { log::info!("Removing password"); @@ -479,8 +521,28 @@ async fn set_state_with_all_accounts( log::trace!("account: {:?}", account); } + let all_account_ids: Vec = all_accounts + .iter() + .map(|account| { + let mnemonic = account.mnemonic(); + let addresses: HashMap = WalletNetwork::iter() + .map(|network| { + let config_network: Network = network.into(); + ( + network, + derive_address(mnemonic.clone(), config_network.bech32_prefix()).unwrap(), + ) + }) + .collect(); + WalletAccountIds { + id: account.id().clone(), + addresses, + } + }) + .collect(); + let mut w_state = state.write().await; - w_state.set_all_accounts(all_accounts); + w_state.set_all_accounts(all_account_ids); Ok(()) } @@ -523,16 +585,13 @@ pub async fn list_accounts( ) -> Result, BackendError> { log::trace!("Listing accounts"); let state = state.read().await; - let network: Network = state.current_network().into(); - let prefix = network.bech32_prefix(); + let network = state.current_network(); let all_accounts = state .get_all_accounts() .map(|account| AccountEntry { - id: account.id().to_string(), - address: derive_address(account.mnemonic().clone(), prefix) - .unwrap() - .to_string(), + id: account.id.to_string(), + address: account.addresses[&network].to_string(), }) .map(|account| { log::trace!("{:?}", account); @@ -552,8 +611,16 @@ pub fn show_mnemonic_for_account_in_password( let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let account_id = wallet_storage::AccountId::new(account_id); let password = wallet_storage::UserPassword::new(password); - let stored_account = wallet_storage::load_existing_login(&login_id, &password)?; + let mnemonic = _show_mnemonic_for_account_in_password(&login_id, &account_id, &password)?; + Ok(mnemonic.to_string()) +} +fn _show_mnemonic_for_account_in_password( + login_id: &wallet_storage::LoginId, + account_id: &wallet_storage::AccountId, + password: &wallet_storage::UserPassword, +) -> Result { + let stored_account = wallet_storage::load_existing_login(login_id, password)?; let mnemonic = match stored_account { wallet_storage::StoredLogin::Mnemonic(ref account) => account.mnemonic().clone(), wallet_storage::StoredLogin::Multiple(ref accounts) => { @@ -561,36 +628,13 @@ pub fn show_mnemonic_for_account_in_password( log::debug!("{:?}", account); } accounts - .get_account(&account_id) + .get_account(account_id) .ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)? .mnemonic() .clone() } }; - - Ok(mnemonic.to_string()) -} - -#[tauri::command] -pub async fn sign_in_decrypted_account( - account_id: &str, - state: tauri::State<'_, Arc>>, -) -> Result { - log::info!("Signing in to already decrypted account: {account_id}"); - let mnemonic = { - let state = state.read().await; - let account = &state - .get_all_accounts() - .find(|a| a.id().as_ref() == account_id) - .ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)?; - account.mnemonic().clone() - }; - - { - state.write().await.logout(); - } - - _connect_with_mnemonic(mnemonic, state).await + Ok(mnemonic) } #[cfg(test)] diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index c5d06ef8f4..a94a599a89 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -1,6 +1,5 @@ use crate::error::BackendError; use crate::network::Network; -use crate::wallet_storage::account_data::WalletAccount; use crate::{config, network_config}; use strum::IntoEnumIterator; @@ -49,7 +48,7 @@ pub struct State { // All the accounts the we get from decrypting the wallet. We hold on to these for being able to // switch accounts on-the-fly - all_accounts: Vec, + all_accounts: Vec, /// Validators that have been fetched dynamically, probably during startup. fetched_validators: config::OptionalValidators, @@ -58,6 +57,13 @@ pub struct State { validator_metadata: HashMap, } +pub(crate) struct WalletAccountIds { + // The wallet account id + pub id: crate::wallet_storage::AccountId, + // The set of corresponding network identities derived from the mnemonic + pub addresses: HashMap, +} + impl State { pub fn client(&self, network: Network) -> Result<&Client, BackendError> { self @@ -117,11 +123,11 @@ impl State { self.current_network } - pub(crate) fn set_all_accounts(&mut self, all_accounts: Vec) { + pub(crate) fn set_all_accounts(&mut self, all_accounts: Vec) { self.all_accounts = all_accounts } - pub(crate) fn get_all_accounts(&self) -> impl Iterator { + pub(crate) fn get_all_accounts(&self) -> impl Iterator { self.all_accounts.iter() }