From bce86235c7042a95423dec0c0fdc5bbfe9cb19f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 12 May 2022 11:56:00 +0200 Subject: [PATCH] wallet: create LoginId type to distinguish --- .../src/operations/mixnet/account.rs | 49 ++++-- .../src/wallet_storage/account_data.rs | 18 +- .../src-tauri/src/wallet_storage/mod.rs | 162 +++++++++--------- .../src-tauri/src/wallet_storage/password.rs | 51 +++++- 4 files changed, 166 insertions(+), 114 deletions(-) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 0f17c2880c..3b7336ad0c 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -5,7 +5,7 @@ use crate::network::Network as WalletNetwork; use crate::network_config; use crate::nymd_client; use crate::state::State; -use crate::wallet_storage::{self, StoredLogin, DEFAULT_WALLET_ACCOUNT_ID}; +use crate::wallet_storage::{self, StoredLogin, DEFAULT_LOGIN_ID}; use bip39::{Language, Mnemonic}; use config::defaults::all::Network; @@ -370,7 +370,7 @@ pub fn create_password(mnemonic: &str, password: String) -> Result<(), BackendEr let mnemonic = Mnemonic::from_str(mnemonic)?; let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); // Currently we only support a single, default, id in the wallet - let id = wallet_storage::AccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + let id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let password = wallet_storage::UserPassword::new(password); wallet_storage::store_wallet_login_multiple_information(mnemonic, hd_path, id, &password) } @@ -383,12 +383,13 @@ pub async fn sign_in_with_password( log::info!("Signing in with password"); // Currently we only support a single, default, id in the wallet - let id = wallet_storage::AccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + let id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let password = wallet_storage::UserPassword::new(password); let stored_login = wallet_storage::load_existing_wallet_login_information(&id, &password)?; let mnemonic = extract_first_mnemonic(&stored_login)?; - set_state_with_all_accounts(stored_login, id, state.clone()).await?; + let first_id_when_converting = id.into(); + set_state_with_all_accounts(stored_login, first_id_when_converting, state.clone()).await?; _connect_with_mnemonic(mnemonic, state).await } @@ -414,7 +415,7 @@ fn extract_first_mnemonic(stored_account: &StoredLogin) -> Result Result<(), BackendError> { log::info!("Removing password"); - let id = wallet_storage::AccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + let id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); wallet_storage::remove_wallet_login_information(&id) } @@ -429,11 +430,10 @@ pub async fn add_account_for_password( let mnemonic = Mnemonic::from_str(mnemonic)?; let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); // Currently we only support a single, default, id in the wallet - let id = wallet_storage::AccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + let id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let inner_id = wallet_storage::AccountId::new(inner_id.to_string()); let password = wallet_storage::UserPassword::new(password.to_string()); - // Creating the returned account entry could fail, so do it before attempting to store to wallet wallet_storage::append_account_to_wallet_login_information( mnemonic.clone(), hd_path, @@ -451,7 +451,10 @@ pub async fn add_account_for_password( // Re-read all the acccounts from the wallet to reset the state, rather than updating it // incrementally let stored_login = wallet_storage::load_existing_wallet_login_information(&id, &password)?; - set_state_with_all_accounts(stored_login, id, state).await?; + // NOTE: since we are appending, this id shouldn't be needed, but setting the state is supposed + // to be a general function + let first_id_when_converting = id.into(); + set_state_with_all_accounts(stored_login, first_id_when_converting, state).await?; Ok(AccountEntry { id: inner_id.to_string(), @@ -459,14 +462,15 @@ pub async fn add_account_for_password( }) } +// The first `AccoundId` when converting is the `LoginId` for the entry that was loaded. async fn set_state_with_all_accounts( stored_login: StoredLogin, - id: wallet_storage::AccountId, + first_id_when_converting: wallet_storage::AccountId, state: tauri::State<'_, Arc>>, ) -> Result<(), BackendError> { log::trace!("Set state with accounts:"); let all_accounts: Vec<_> = stored_login - .unwrap_into_multiple_accounts(id.clone()) + .unwrap_into_multiple_accounts(first_id_when_converting) .into_accounts() .collect(); @@ -487,14 +491,17 @@ pub async fn remove_account_for_password( ) -> Result<(), BackendError> { log::info!("Removing account: {inner_id}"); // Currently we only support a single, default, id in the wallet - let id = wallet_storage::AccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + let id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let inner_id = wallet_storage::AccountId::new(inner_id.to_string()); let password = wallet_storage::UserPassword::new(password.to_string()); wallet_storage::remove_account_from_wallet_login(&id, &inner_id, &password)?; // Load to reset the internal state let stored_login = wallet_storage::load_existing_wallet_login_information(&id, &password)?; - set_state_with_all_accounts(stored_login, id, state).await + // NOTE: Since we removed from a multi-account login, this id shouldn't be needed, but setting + // the state is supposed to be a general function + let first_id_when_converting = id.into(); + set_state_with_all_accounts(stored_login, first_id_when_converting, state).await } fn derive_address( @@ -541,7 +548,7 @@ pub fn show_mnemonic_for_account_in_password( password: String, ) -> Result { log::info!("Getting mnemonic for: {account_id}"); - let id = wallet_storage::AccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string()); + let 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_wallet_login_information(&id, &password)?; @@ -601,20 +608,24 @@ mod tests { fn decrypt_stored_wallet_for_sign_in() { 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 id = wallet_storage::AccountId::new("first".to_string()); + let login_id = wallet_storage::LoginId::new("first".to_string()); + let account_id = wallet_storage::AccountId::new("first".to_string()); let password = wallet_storage::UserPassword::new("password".to_string()); let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let stored_login = - wallet_storage::load_existing_wallet_login_information_at_file(wallet_file, &id, &password) - .unwrap(); + let stored_login = wallet_storage::load_existing_wallet_login_information_at_file( + wallet_file, + &login_id, + &password, + ) + .unwrap(); let mnemonic = extract_first_mnemonic(&stored_login).unwrap(); let expected_mnemonic = 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(); assert_eq!(mnemonic, expected_mnemonic); let all_accounts: Vec<_> = stored_login - .unwrap_into_multiple_accounts(id.clone()) + .unwrap_into_multiple_accounts(account_id.clone()) .into_accounts() .collect(); @@ -622,7 +633,7 @@ mod tests { all_accounts, vec![ wallet_storage::account_data::WalletAccount::new_mnemonic_backed_account( - id, + account_id, expected_mnemonic, hd_path ) 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 418be34433..148e088e5b 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/account_data.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/account_data.rs @@ -21,7 +21,7 @@ use zeroize::Zeroize; use crate::error::BackendError; use super::encryption::EncryptedData; -use super::password::AccountId; +use super::password::{AccountId, LoginId}; use super::UserPassword; const CURRENT_WALLET_FILE_VERSION: u32 = 1; @@ -56,10 +56,7 @@ impl StoredWallet { Ok(()) } - fn get_encrypted_login( - &self, - id: &AccountId, - ) -> Result<&EncryptedData, BackendError> { + fn get_encrypted_login(&self, id: &LoginId) -> Result<&EncryptedData, BackendError> { self .accounts .iter() @@ -68,10 +65,7 @@ impl StoredWallet { .ok_or(BackendError::NoSuchIdInWallet) } - fn get_encrypted_login_mut( - &mut self, - id: &AccountId, - ) -> Result<&mut EncryptedLogin, BackendError> { + fn get_encrypted_login_mut(&mut self, id: &LoginId) -> Result<&mut EncryptedLogin, BackendError> { self .accounts .iter_mut() @@ -91,7 +85,7 @@ impl StoredWallet { Ok(()) } - pub fn remove_encrypted_login(&mut self, id: &AccountId) -> Option { + pub fn remove_encrypted_login(&mut self, id: &LoginId) -> 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)) @@ -103,7 +97,7 @@ impl StoredWallet { pub fn decrypt_login( &self, - id: &AccountId, + id: &LoginId, password: &UserPassword, ) -> Result { self.get_encrypted_login(id)?.decrypt_struct(password) @@ -134,7 +128,7 @@ impl Default for StoredWallet { /// Each entry in the stored wallet file. An id field in plaintext and an encrypted stored login. #[derive(Serialize, Deserialize, Debug)] pub(crate) struct EncryptedLogin { - pub id: AccountId, + pub id: LoginId, pub account: EncryptedData, } diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 731547ee39..9b80994422 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub(crate) use crate::wallet_storage::account_data::StoredLogin; -pub(crate) use crate::wallet_storage::password::{AccountId, UserPassword}; +pub(crate) use crate::wallet_storage::password::{AccountId, LoginId, UserPassword}; use crate::error::BackendError; use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME}; @@ -19,11 +19,11 @@ pub(crate) mod encryption; mod password; /// The default wallet (top-level) login id. -pub(crate) const DEFAULT_WALLET_ACCOUNT_ID: &str = "default"; +pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// When converting a single account entry to one that contains many, the first account will use /// this name. -pub(crate) const DEFAULT_NAME_FIRST_ACCOUNT: &str = "Account 1"; +pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; fn get_storage_directory() -> Result { tauri::api::path::local_data_dir() @@ -55,7 +55,7 @@ fn load_existing_wallet_at_file(filepath: PathBuf) -> Result Result { let store_dir = get_storage_directory()?; @@ -65,7 +65,7 @@ pub(crate) fn load_existing_wallet_login_information( pub(crate) fn load_existing_wallet_login_information_at_file( filepath: PathBuf, - id: &AccountId, + id: &LoginId, password: &UserPassword, ) -> Result { load_existing_wallet_at_file(filepath)?.decrypt_login(id, password) @@ -79,7 +79,7 @@ pub(crate) fn load_existing_wallet_login_information_at_file( pub(crate) fn store_wallet_login_information( mnemonic: bip39::Mnemonic, hd_path: DerivationPath, - id: AccountId, + id: LoginId, password: &UserPassword, ) -> Result<(), BackendError> { // make sure the entire directory structure exists @@ -94,7 +94,7 @@ fn store_wallet_login_information_at_file( filepath: PathBuf, mnemonic: bip39::Mnemonic, hd_path: DerivationPath, - id: AccountId, + id: LoginId, password: &UserPassword, ) -> Result<(), BackendError> { let mut stored_wallet = match load_existing_wallet_at_file(filepath.clone()) { @@ -128,7 +128,7 @@ fn store_wallet_login_information_at_file( pub(crate) fn store_wallet_login_multiple_information( mnemonic: bip39::Mnemonic, hd_path: DerivationPath, - id: AccountId, + id: LoginId, password: &UserPassword, ) -> Result<(), BackendError> { // make sure the entire directory structure exists @@ -143,7 +143,7 @@ fn store_wallet_login_multiple_information_at_file( filepath: PathBuf, mnemonic: bip39::Mnemonic, hd_path: DerivationPath, - id: AccountId, + id: LoginId, password: &UserPassword, ) -> Result<(), BackendError> { let mut stored_wallet = match load_existing_wallet_at_file(filepath.clone()) { @@ -159,7 +159,7 @@ fn store_wallet_login_multiple_information_at_file( let mut new_accounts = account_data::MultipleAccounts::empty(); new_accounts.add( - DEFAULT_NAME_FIRST_ACCOUNT.to_string().into(), + DEFAULT_FIRST_ACCOUNT_NAME.to_string().into(), mnemonic, hd_path, )?; @@ -188,7 +188,7 @@ fn store_wallet_login_multiple_information_at_file( pub(crate) fn append_account_to_wallet_login_information( mnemonic: bip39::Mnemonic, hd_path: DerivationPath, - id: AccountId, + id: LoginId, inner_id: AccountId, password: &UserPassword, ) -> Result<(), BackendError> { @@ -206,7 +206,7 @@ fn append_account_to_wallet_login_information_at_file( filepath: PathBuf, mnemonic: bip39::Mnemonic, hd_path: DerivationPath, - id: AccountId, + id: LoginId, inner_id: AccountId, password: &UserPassword, ) -> Result<(), BackendError> { @@ -220,7 +220,8 @@ fn append_account_to_wallet_login_information_at_file( // Add accounts to the inner structure. // Note that in case we only have single account entry, without an inner_id, we convert to // multiple accounts and we set the first inner_id to id. - let mut accounts = decrypted_login.unwrap_into_multiple_accounts(id.clone()); + let first_id_when_converting = id.clone().into(); + let mut accounts = decrypted_login.unwrap_into_multiple_accounts(first_id_when_converting); accounts.add(inner_id, mnemonic, hd_path)?; let encrypted_accounts = EncryptedLogin { @@ -242,7 +243,7 @@ fn append_account_to_wallet_login_information_at_file( /// Remove the entire encrypted login entry for the given `id`. This means potentially removing all /// associated accounts! /// If this was the last entry in the file, the file is removed. -pub(crate) fn remove_wallet_login_information(id: &AccountId) -> Result<(), BackendError> { +pub(crate) fn remove_wallet_login_information(id: &LoginId) -> Result<(), BackendError> { let store_dir = get_storage_directory()?; let filepath = store_dir.join(WALLET_INFO_FILENAME); remove_wallet_login_information_at_file(filepath, id) @@ -250,7 +251,7 @@ pub(crate) fn remove_wallet_login_information(id: &AccountId) -> Result<(), Back fn remove_wallet_login_information_at_file( filepath: PathBuf, - id: &AccountId, + id: &LoginId, ) -> Result<(), BackendError> { log::warn!("Removing wallet account with id: {id}. This includes all associated accounts!"); let mut stored_wallet = match load_existing_wallet_at_file(filepath.clone()) { @@ -286,7 +287,7 @@ fn remove_wallet_login_information_at_file( /// - If it is the last associated account with that login, the encrypted login will be removed. /// - If this was the last encrypted login in the file, it will be removed. pub(crate) fn remove_account_from_wallet_login( - id: &AccountId, + id: &LoginId, inner_id: &AccountId, password: &UserPassword, ) -> Result<(), BackendError> { @@ -297,7 +298,7 @@ pub(crate) fn remove_account_from_wallet_login( fn remove_account_from_wallet_login_at_file( filepath: PathBuf, - id: &AccountId, + id: &LoginId, inner_id: &AccountId, password: &UserPassword, ) -> Result<(), BackendError> { @@ -360,7 +361,7 @@ mod tests { fn trying_to_load_nonexistant_wallet_fails() { let store_dir = tempdir().unwrap(); let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); let password = UserPassword::new("password".to_string()); assert!(matches!( @@ -382,13 +383,13 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); store_wallet_login_information_at_file( wallet_file.clone(), dummy_account1, cosmos_hd_path, - id1, + id1.clone(), &password, ) .unwrap(); @@ -397,7 +398,7 @@ mod tests { assert_eq!(stored_wallet.len(), 1); let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, AccountId::new("first".to_string())); + assert_eq!(login.id, id1); // some actual ciphertext was saved assert!(!login.account.ciphertext().is_empty()); @@ -411,13 +412,13 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); store_wallet_login_multiple_information_at_file( wallet_file.clone(), dummy_account1, cosmos_hd_path, - id1, + id1.clone(), &password, ) .unwrap(); @@ -426,7 +427,7 @@ mod tests { assert_eq!(stored_wallet.len(), 1); let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, AccountId::new("first".to_string())); + assert_eq!(login.id, id1); // some actual ciphertext was saved assert!(!login.account.ciphertext().is_empty()); @@ -440,7 +441,7 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); // Store the first login store_wallet_login_information_at_file( @@ -473,7 +474,7 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); // Store the first login store_wallet_login_multiple_information_at_file( @@ -507,7 +508,7 @@ mod tests { 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 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); store_wallet_login_information_at_file( wallet_file.clone(), @@ -534,7 +535,7 @@ mod tests { 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 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); store_wallet_login_multiple_information_at_file( wallet_file.clone(), @@ -560,8 +561,8 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); store_wallet_login_information_at_file( wallet_file.clone(), @@ -587,8 +588,8 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); store_wallet_login_multiple_information_at_file( wallet_file.clone(), @@ -614,7 +615,7 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); store_wallet_login_information_at_file( wallet_file.clone(), @@ -640,7 +641,7 @@ mod tests { let dummy_account1 = bip39::Mnemonic::generate(24).unwrap(); let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); store_wallet_login_multiple_information_at_file( wallet_file.clone(), @@ -656,9 +657,9 @@ mod tests { let accounts = loaded_login.as_multiple_accounts().unwrap(); assert_eq!(accounts.len(), 1); let account = accounts - .get_account(&DEFAULT_NAME_FIRST_ACCOUNT.into()) + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) .unwrap(); - assert_eq!(account.id, DEFAULT_NAME_FIRST_ACCOUNT.into()); + assert_eq!(account.id, DEFAULT_FIRST_ACCOUNT_NAME.into()); assert_eq!(account.account.mnemonic(), &dummy_account1); assert_eq!(account.account.hd_path(), &cosmos_hd_path); } @@ -675,8 +676,8 @@ mod tests { let password = UserPassword::new("password".to_string()); let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); store_wallet_login_information_at_file( wallet_file.clone(), @@ -712,8 +713,8 @@ mod tests { let password = UserPassword::new("password".to_string()); let bad_password = UserPassword::new("bad-password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); store_wallet_login_multiple_information_at_file( wallet_file.clone(), @@ -749,8 +750,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); // Store the first account store_wallet_login_information_at_file( @@ -806,8 +807,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); // Store the first account store_wallet_login_information_at_file( @@ -861,8 +862,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); // Store the first account store_wallet_login_information_at_file( @@ -902,9 +903,9 @@ mod tests { let acc2 = loaded_account.as_multiple_accounts().unwrap(); assert_eq!(acc2.len(), 1); let account = acc2 - .get_account(&DEFAULT_NAME_FIRST_ACCOUNT.into()) + .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) .unwrap(); - assert_eq!(account.id, DEFAULT_NAME_FIRST_ACCOUNT.into()); + assert_eq!(account.id, DEFAULT_FIRST_ACCOUNT_NAME.into()); assert_eq!(account.account.mnemonic(), &dummy_account2); assert_eq!(account.account.hd_path(), &different_hd_path); } @@ -919,8 +920,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); store_wallet_login_multiple_information_at_file( wallet_file.clone(), @@ -950,8 +951,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); // Store two accounts with two different passwords store_wallet_login_information_at_file( @@ -1025,7 +1026,7 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); let id2 = AccountId::new("second".to_string()); store_wallet_login_information_at_file( @@ -1059,7 +1060,7 @@ mod tests { load_existing_wallet_login_information_at_file(wallet_file, &id1, &password).unwrap(); let accounts = loaded_accounts.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new_mnemonic_backed_account(id1, dummy_account1, hd_path.clone()), + WalletAccount::new_mnemonic_backed_account(id1.into(), dummy_account1, hd_path.clone()), WalletAccount::new_mnemonic_backed_account(id2, dummy_account2, hd_path), ] .into(); @@ -1079,8 +1080,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); let id3 = AccountId::new("third".to_string()); let id4 = AccountId::new("fourth".to_string()); @@ -1140,7 +1141,7 @@ mod tests { load_existing_wallet_login_information_at_file(wallet_file, &id2, &password).unwrap(); let accounts = loaded_accounts.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new_mnemonic_backed_account(id2, dummy_account2, hd_path.clone()), + WalletAccount::new_mnemonic_backed_account(id2.into(), dummy_account2, hd_path.clone()), WalletAccount::new_mnemonic_backed_account(id3, dummy_account3, hd_path.clone()), WalletAccount::new_mnemonic_backed_account(id4, dummy_account4, hd_path), ] @@ -1161,8 +1162,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); let id3 = AccountId::new("third".to_string()); let id4 = AccountId::new("fourth".to_string()); @@ -1209,7 +1210,7 @@ mod tests { load_existing_wallet_login_information_at_file(wallet_file.clone(), &id1, &password).unwrap(); let accounts = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![WalletAccount::new_mnemonic_backed_account( - DEFAULT_NAME_FIRST_ACCOUNT.into(), + DEFAULT_FIRST_ACCOUNT_NAME.into(), dummy_account1, hd_path.clone(), )] @@ -1221,7 +1222,7 @@ mod tests { let accounts = loaded_login.as_multiple_accounts().unwrap(); let expected = vec![ WalletAccount::new_mnemonic_backed_account( - DEFAULT_NAME_FIRST_ACCOUNT.into(), + DEFAULT_FIRST_ACCOUNT_NAME.into(), dummy_account2, hd_path.clone(), ), @@ -1243,7 +1244,7 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); let id2 = AccountId::new("second".to_string()); store_wallet_login_information_at_file( @@ -1284,7 +1285,7 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); let id2 = AccountId::new("second".to_string()); store_wallet_login_multiple_information_at_file( @@ -1326,8 +1327,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); let id3 = AccountId::new("third".to_string()); store_wallet_login_information_at_file( @@ -1365,7 +1366,7 @@ mod tests { load_existing_wallet_login_information_at_file(wallet_file, &id2, &password).unwrap(); let accounts = loaded_accounts.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new_mnemonic_backed_account(id2, dummy_account2, hd_path.clone()), + WalletAccount::new_mnemonic_backed_account(id2.into(), dummy_account2, hd_path.clone()), WalletAccount::new_mnemonic_backed_account(id3, dummy_account3, hd_path), ] .into(); @@ -1383,7 +1384,7 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); let id2 = AccountId::new("second".to_string()); store_wallet_login_multiple_information_at_file( @@ -1408,7 +1409,7 @@ mod tests { remove_account_from_wallet_login_at_file( wallet.clone(), &id1, - &DEFAULT_NAME_FIRST_ACCOUNT.into(), + &DEFAULT_FIRST_ACCOUNT_NAME.into(), &password, ) .unwrap(); @@ -1436,9 +1437,9 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); + let id1 = LoginId::new("first".to_string()); let id2 = AccountId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); + let id3 = LoginId::new("third".to_string()); store_wallet_login_multiple_information_at_file( wallet.clone(), @@ -1471,7 +1472,7 @@ mod tests { remove_account_from_wallet_login_at_file( wallet.clone(), &id1, - &DEFAULT_NAME_FIRST_ACCOUNT.into(), + &DEFAULT_FIRST_ACCOUNT_NAME.into(), &password, ) .unwrap(); @@ -1488,7 +1489,7 @@ mod tests { load_existing_wallet_login_information_at_file(wallet, &id3, &password).unwrap(); let acc3 = loaded_account.as_multiple_accounts().unwrap(); let expected = vec![WalletAccount::new_mnemonic_backed_account( - DEFAULT_NAME_FIRST_ACCOUNT.into(), + DEFAULT_FIRST_ACCOUNT_NAME.into(), dummy_account3, cosmos_hd_path, )] @@ -1509,8 +1510,8 @@ mod tests { let password = UserPassword::new("password".to_string()); - let id1 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); let id3 = AccountId::new("third".to_string()); let id4 = AccountId::new("fourth".to_string()); @@ -1560,14 +1561,19 @@ mod tests { load_existing_wallet_login_information_at_file(wallet.clone(), &id2, &password).unwrap(); let accounts = loaded_accounts.as_multiple_accounts().unwrap(); let expected = vec![ - WalletAccount::new_mnemonic_backed_account(id2.clone(), dummy_account2, hd_path.clone()), + WalletAccount::new_mnemonic_backed_account( + id2.clone().into(), + dummy_account2, + hd_path.clone(), + ), WalletAccount::new_mnemonic_backed_account(id4.clone(), dummy_account4, hd_path.clone()), ] .into(); assert_eq!(accounts, &expected); // Delete the second and fourth mnemonic from the second login entry removes the login entry - remove_account_from_wallet_login_at_file(wallet.clone(), &id2, &id2, &password).unwrap(); + remove_account_from_wallet_login_at_file(wallet.clone(), &id2, &id2.clone().into(), &password) + .unwrap(); remove_account_from_wallet_login_at_file(wallet.clone(), &id2, &id4, &password).unwrap(); assert!(matches!( load_existing_wallet_login_information_at_file(wallet.clone(), &id2, &password), @@ -1594,8 +1600,8 @@ mod tests { 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 = AccountId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); + let id1 = LoginId::new("first".to_string()); + let id2 = LoginId::new("second".to_string()); assert!(!wallet.password_can_decrypt_all(&bad_password)); assert!(wallet.password_can_decrypt_all(&password)); diff --git a/nym-wallet/src-tauri/src/wallet_storage/password.rs b/nym-wallet/src-tauri/src/wallet_storage/password.rs index 920032a5da..4e47215e50 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/password.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/password.rs @@ -6,6 +6,41 @@ use std::fmt; use serde::{Deserialize, Serialize}; use zeroize::Zeroize; +// The `LoginId` is the top level id in the wallet file, and is not stored encrypted +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Zeroize)] +pub(crate) struct LoginId(String); + +impl LoginId { + pub(crate) fn new(id: String) -> LoginId { + LoginId(id) + } +} + +impl AsRef for LoginId { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl From for LoginId { + fn from(id: String) -> Self { + Self::new(id) + } +} + +impl From<&str> for LoginId { + fn from(id: &str) -> Self { + Self::new(id.to_string()) + } +} + +impl fmt::Display for LoginId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +// For each encrypted login, we can have multiple encrypted accounts. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Zeroize)] pub(crate) struct AccountId(String); @@ -22,14 +57,20 @@ impl AsRef for AccountId { } impl From for AccountId { - fn from(id: String) -> Self { - Self::new(id) - } + fn from(id: String) -> Self { + Self::new(id) + } } impl From<&str> for AccountId { - fn from(id: &str) -> Self { - Self::new(id.to_string()) + fn from(id: &str) -> Self { + Self::new(id.to_string()) + } +} + +impl From for AccountId { + fn from(login_id: LoginId) -> Self { + Self::new(login_id.0) } }