From 6ca2a3c539f79c0feee410aed12ca09e3cd49a7f Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Tue, 1 Apr 2025 17:06:21 +0200 Subject: [PATCH] migrate to v2 - lots to check and do --- nym-wallet/src-tauri/src/config/mod.rs | 272 +- .../src-tauri/src/wallet_storage/mod.rs | 3612 +++++++++-------- 2 files changed, 1949 insertions(+), 1935 deletions(-) diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 1f79075157..5b7dec4099 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -11,6 +11,7 @@ use nym_validator_client::nyxd::AccountId as CosmosAccountId; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; +use tauri::Manager; use url::Url; use nym_config::defaults::{DenomDetailsOwned, NymNetworkDetails, ValidatorDetails}; @@ -104,32 +105,35 @@ impl NetworkConfig { } impl Config { - fn root_directory() -> PathBuf { - tauri::api::path::config_dir().expect("Failed to get config directory") + fn root_directory(app_handle: &tauri::AppHandle) -> PathBuf { + app_handle + .path() + .local_data_dir() + .expect("Failed to get config directory") } - fn config_directory() -> PathBuf { - Self::root_directory().join(CONFIG_DIR_NAME) + fn config_directory(app_handle: &tauri::AppHandle) -> PathBuf { + Self::root_directory(app_handle).join(CONFIG_DIR_NAME) } - fn config_file_path(network: Option) -> PathBuf { + fn config_file_path(app_handle: &tauri::AppHandle, network: Option) -> PathBuf { if let Some(network) = network { let network_filename = format!("{}.toml", network.as_key()); - Self::config_directory().join(network_filename) + Self::config_directory(app_handle).join(network_filename) } else { - Self::config_directory().join(CONFIG_FILENAME) + Self::config_directory(app_handle).join(CONFIG_FILENAME) } } - pub fn save_to_files(&self) -> io::Result<()> { + pub fn save_to_files(&self, app_handle: &tauri::AppHandle) -> io::Result<()> { log::trace!("Config::save_to_file"); // Make sure the whole directory structure actually exists - fs::create_dir_all(Self::config_directory())?; + fs::create_dir_all(Self::config_directory(app_handle))?; // Global config if let Some(global) = &self.global { - let location = Self::config_file_path(None); + let location = Self::config_file_path(app_handle, None); match toml::to_string_pretty(&global) .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) @@ -150,7 +154,7 @@ impl Config { } }; - let location = Self::config_file_path(Some(network)); + let location = Self::config_file_path(app_handle, 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)) @@ -162,10 +166,10 @@ impl Config { Ok(()) } - pub fn load_from_files() -> Self { + pub fn load_from_files(app_handle: &tauri::AppHandle) -> Self { // Global let global = { - let file = Self::config_file_path(None); + let file = Self::config_file_path(app_handle, None); match load_from_file::(file.clone()) { Ok(global) => { log::debug!("Loaded from file {:#?}", file); @@ -181,7 +185,7 @@ impl Config { // One file per network let mut networks = HashMap::new(); for network in WalletNetwork::iter() { - let file = Self::config_file_path(Some(network)); + let file = Self::config_file_path(app_handle, Some(network)); match load_from_file::(file.clone()) { Ok(config) => { log::trace!("Loaded from file {:#?}", file); @@ -261,6 +265,8 @@ impl Config { } } + //////// + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -531,139 +537,139 @@ impl From for NetworkDetails { } } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - fn test_config() -> Config { - let netconfig = NetworkConfig { - selected_nyxd_url: None, - selected_api_url: Some("https://my_api_url.com".parse().unwrap()), +// fn test_config() -> Config { +// let netconfig = NetworkConfig { +// selected_nyxd_url: None, +// selected_api_url: Some("https://my_api_url.com".parse().unwrap()), - nyxd_urls: Some(vec![ - ValidatorConfigEntry { - nyxd_url: "https://foo".parse().unwrap(), - nyxd_name: Some("FooName".to_string()), - api_url: None, - }, - ValidatorConfigEntry { - nyxd_url: "https://bar".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://bar/api".parse().unwrap()), - }, - ValidatorConfigEntry { - nyxd_url: "https://baz".parse().unwrap(), - nyxd_name: None, - api_url: Some("https://baz/api".parse().unwrap()), - }, - ]), - ..NetworkConfig::default() - }; +// nyxd_urls: Some(vec![ +// ValidatorConfigEntry { +// nyxd_url: "https://foo".parse().unwrap(), +// nyxd_name: Some("FooName".to_string()), +// api_url: None, +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://bar".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://bar/api".parse().unwrap()), +// }, +// ValidatorConfigEntry { +// nyxd_url: "https://baz".parse().unwrap(), +// nyxd_name: None, +// api_url: Some("https://baz/api".parse().unwrap()), +// }, +// ]), +// ..NetworkConfig::default() +// }; - Config { - base: Base::default(), - global: Some(GlobalConfig::default()), - networks: [(WalletNetwork::MAINNET.as_key(), netconfig)] - .into_iter() - .collect(), - } - } +// 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(netconfig).unwrap(), - r#"version = 1 -selected_api_url = 'https://my_api_url.com/' +// #[test] +// fn serialize_to_toml() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// assert_eq!( +// toml::to_string_pretty(netconfig).unwrap(), +// r#"version = 1 +// selected_api_url = 'https://my_api_url.com/' -[[nyxd_urls]] -nyxd_url = 'https://foo/' -nyxd_name = 'FooName' +// [[nyxd_urls]] +// nyxd_url = 'https://foo/' +// nyxd_name = 'FooName' -[[nyxd_urls]] -nyxd_url = 'https://bar/' -api_url = 'https://bar/api' +// [[nyxd_urls]] +// nyxd_url = 'https://bar/' +// api_url = 'https://bar/api' -[[nyxd_urls]] -nyxd_url = 'https://baz/' -api_url = 'https://baz/api' -"# - ); - } +// [[nyxd_urls]] +// nyxd_url = 'https://baz/' +// api_url = 'https://baz/api' +// "# +// ); +// } - #[test] - fn serialize_to_json() { - let config = test_config(); - let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; - println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); - assert_eq!( - serde_json::to_string_pretty(netconfig).unwrap(), - r#"{ - "version": 1, - "selected_nyxd_url": null, - "default_nyxd_url": null, - "selected_api_url": "https://my_api_url.com/", - "nyxd_urls": [ - { - "nyxd_url": "https://foo/", - "nyxd_name": "FooName", - "api_url": null - }, - { - "nyxd_url": "https://bar/", - "nyxd_name": null, - "api_url": "https://bar/api" - }, - { - "nyxd_url": "https://baz/", - "nyxd_name": null, - "api_url": "https://baz/api" - } - ] -}"# - ); - } +// #[test] +// fn serialize_to_json() { +// let config = test_config(); +// let netconfig = &config.networks[&WalletNetwork::MAINNET.as_key()]; +// println!("{}", serde_json::to_string_pretty(netconfig).unwrap()); +// assert_eq!( +// serde_json::to_string_pretty(netconfig).unwrap(), +// r#"{ +// "version": 1, +// "selected_nyxd_url": null, +// "default_nyxd_url": null, +// "selected_api_url": "https://my_api_url.com/", +// "nyxd_urls": [ +// { +// "nyxd_url": "https://foo/", +// "nyxd_name": "FooName", +// "api_url": null +// }, +// { +// "nyxd_url": "https://bar/", +// "nyxd_name": null, +// "api_url": "https://bar/api" +// }, +// { +// "nyxd_url": "https://baz/", +// "nyxd_name": null, +// "api_url": "https://baz/api" +// } +// ] +// }"# +// ); +// } - #[test] - fn serialize_and_deserialize_to_toml() { - let config = test_config(); - 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] +// fn serialize_and_deserialize_to_toml() { +// let config = test_config(); +// 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] - fn get_urls_parsed_from_config() { - let config = test_config(); +// #[test] +// fn get_urls_parsed_from_config() { +// let config = test_config(); - let nyxd_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://foo/"); +// let nyxd_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://foo/"); - // The first entry is missing an API URL - let api_url = config - .get_configured_validators(WalletNetwork::MAINNET) - .next() - .and_then(|v| v.api_url); - assert_eq!(api_url, None); - } +// // The first entry is missing an API URL +// let api_url = config +// .get_configured_validators(WalletNetwork::MAINNET) +// .next() +// .and_then(|v| v.api_url); +// assert_eq!(api_url, None); +// } - #[test] - fn get_urls_from_defaults() { - let config = Config::default(); +// #[test] +// fn get_urls_from_defaults() { +// let config = Config::default(); - let nyxd_url = config - .get_base_validators(WalletNetwork::MAINNET) - .next() - .map(|v| v.nyxd_url) - .unwrap(); - assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); +// let nyxd_url = config +// .get_base_validators(WalletNetwork::MAINNET) +// .next() +// .map(|v| v.nyxd_url) +// .unwrap(); +// assert_eq!(nyxd_url.as_ref(), "https://rpc.nymtech.net/"); let api_url = config .get_base_validators(WalletNetwork::MAINNET) diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 115823da4a..faa026452d 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -19,6 +19,7 @@ use nym_validator_client::nyxd::bip32::DerivationPath; use std::ffi::OsString; use std::fs::{self, create_dir_all, OpenOptions}; use std::path::{Path, PathBuf}; +use tauri::Manager; use time::OffsetDateTime; #[cfg(test)] @@ -37,14 +38,18 @@ pub(crate) const DEFAULT_LOGIN_ID: &str = "default"; /// this name. pub(crate) const DEFAULT_FIRST_ACCOUNT_NAME: &str = "Account 1"; -fn get_storage_directory() -> Result { - tauri::api::path::local_data_dir() +fn get_storage_directory(app_handle: &tauri::AppHandle) -> Result { + app_handle + .path() + .local_data_dir() .map(|dir| dir.join(STORAGE_DIR_NAME)) .ok_or(BackendError::UnknownStorageDirectory) } -pub(crate) fn wallet_login_filepath() -> Result { - get_storage_directory().map(|dir| dir.join(WALLET_INFO_FILENAME)) +pub(crate) fn wallet_login_filepath( + app_handle: &tauri::AppHandle, +) -> Result { + get_storage_directory(app_handle).map(|dir| dir.join(WALLET_INFO_FILENAME)) } fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendError> { @@ -59,8 +64,10 @@ fn write_to_file(filepath: &Path, wallet: &StoredWallet) -> Result<(), BackendEr /// Load stored wallet file #[allow(unused)] -pub(crate) fn load_existing_wallet() -> Result { - let store_dir = get_storage_directory()?; +pub(crate) fn load_existing_wallet( + app_handle: &tauri::AppHandle, +) -> Result { + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_wallet_at_file(&filepath) } @@ -77,10 +84,11 @@ fn load_existing_wallet_at_file(filepath: &Path) -> Result Result { - let store_dir = get_storage_directory()?; + let store_dir = get_storage_directory(app_handle)?; let filepath = store_dir.join(WALLET_INFO_FILENAME); load_existing_login_at_file(&filepath, id, password) } @@ -468,1801 +476,1801 @@ fn rename_account_in_login_at_file( write_to_file(filepath, &stored_wallet) } -#[cfg(test)] -mod tests { - use crate::wallet_storage::account_data::WalletAccount; - - use super::*; - use nym_config::defaults::COSMOS_DERIVATION_PATH; - use std::str::FromStr; - use tempfile::tempdir; - - #[test] - fn trying_to_load_nonexistant_wallet_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let id1 = LoginId::new("first".to_string()); - let password = UserPassword::new("password".to_string()); - - assert!(matches!( - load_existing_wallet_at_file(&wallet_file), - Err(BackendError::WalletFileNotFound), - )); - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - remove_login_at_file(&wallet_file, &id1).unwrap_err(); - } - - #[test] - fn store_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - cosmos_hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(stored_wallet.len(), 1); - - let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); - assert_eq!(login.id, id1); - - // some actual ciphertext was saved - assert!(!login.account.ciphertext().is_empty()); - } - - #[test] - fn store_twice_for_the_same_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn store_twice_for_the_same_id_fails_with_multiple() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - // Store the first login - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // and storing the same id again fails - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1, - &password, - ), - Err(BackendError::WalletLoginIdAlreadyExists), - )); - } - - #[test] - fn load_with_wrong_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let 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 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let 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 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - // Trying to load it with wrong password now fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &bad_password), - Err(BackendError::StoreCipherError { .. }), - )); - } - - #[test] - fn load_with_wrong_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn load_with_wrong_id_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Trying to load with the wrong id - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_load_a_single_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - } - - #[test] - fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_and_load_a_single_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); - let accounts = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(accounts.len(), 1); - let account = accounts - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let wrong_password = UserPassword::new("wrong_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) - .unwrap(); - - let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) - .unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - acc1, - hd_path, - id1.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).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 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1, - cosmos_hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_a_second_login_with_a_different_password_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let 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 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1, - &password, - ) - .unwrap(); - - // Can't store a second login if you use different password - assert!(matches!( - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2, - hd_path, - id2, - &bad_password - ), - Err(BackendError::WalletDifferentPasswordDetected), - )); - } - - #[test] - fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); - - let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - let encrypted_blob = &stored_wallet - .get_encrypted_login_by_index(0) - .unwrap() - .account; - - // keep track of salt and iv for future assertion - let original_iv = encrypted_blob.iv().to_vec(); - let original_salt = encrypted_blob.salt().to_vec(); - - // Add an extra account - store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); - - let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); - assert_eq!(loaded_accounts.len(), 2); - let encrypted_blob = &loaded_accounts - .get_encrypted_login_by_index(1) - .unwrap() - .account; - - // fresh IV and salt are used - assert_ne!(original_iv, encrypted_blob.iv()); - assert_ne!(original_salt, encrypted_blob.salt()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Add an extra account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first login with an account - store_login_at_file( - &wallet, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let acc = login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&cosmos_hd_path, acc.hd_path()); - - // Store a second login, also with an account - store_login_at_file( - &wallet, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - } - - #[test] - fn store_one_mnemonic_account_and_one_multi_account() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store the first account - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc.mnemonic()); - assert_eq!(&hd_path, acc.hd_path()); - - // Add an extra account - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_multiple_accounts().unwrap(); - assert_eq!(acc2.len(), 1); - let account = acc2 - .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) - .unwrap(); - assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); - assert_eq!(account.mnemonic(), &account2); - assert_eq!(account.hd_path(), &different_hd_path); - } - - #[test] - fn remove_non_existent_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) - .unwrap(); - - // Fails to delete non-existent id in the wallet - assert!(matches!( - remove_login_at_file(&wallet_file, &id2), - Err(BackendError::WalletNoSuchLoginId), - )); - } - - #[test] - fn store_and_remove_wallet_login_information() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let different_hd_path: DerivationPath = "m".parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - - // Store two accounts with two different passwords - store_login_at_file( - &wallet_file, - account1.clone(), - cosmos_hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - store_login_at_file( - &wallet_file, - account2.clone(), - different_hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Load and compare - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account2, acc2.mnemonic()); - assert_eq!(&different_hd_path, acc2.hd_path()); - - // Delete the second account - remove_login_at_file(&wallet_file, &id2).unwrap(); - - // The first account should be unchanged - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(&account1, acc1.mnemonic()); - assert_eq!(&cosmos_hd_path, acc1.hd_path()); - - // And we can't load the second one anymore - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // Delete the first account - assert!(wallet_file.exists()); - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The file should now be removed - assert!(!wallet_file.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet_file, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn append_account_converts_the_type() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc.mnemonic(), &account1); - assert_eq!(acc.hd_path(), &hd_path); - - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it is now multiple mnemonic type - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), - WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".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()); - - store_login_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Check that it's there as the correct non-multiple type - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let acc2 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc2.mnemonic(), &account2); - assert_eq!(acc2.hd_path(), &hd_path); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let acc1 = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(acc1.mnemonic(), &account1); - assert_eq!(acc1.hd_path(), &hd_path); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_accounts_to_existing_login_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let account4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".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()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet_file, - account4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Check that we can load all four - let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account2, hd_path.clone()), - ), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), - WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let new_password = UserPassword::new("new_password".to_string()); - let login_id = LoginId::new("first".to_string()); - let appended_account = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append a second mnenonic to the same login - append_account_to_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - login_id.clone(), - appended_account.clone(), - &password, - ) - .unwrap(); - - // Update the password - update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); - - // Expect that we can load these 2 accounts with the new password - let loaded_login = - load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Expect that trying to load these 2 accounts with the old password fails - let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); - assert!(matches!(err, BackendError::StoreCipherError { .. })); - } - - #[test] - fn append_the_same_mnemonic_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - assert!(matches!( - append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), - Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn append_the_same_account_name_twice_fails() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let mnemonic1 = Mnemonic::generate(24).unwrap(); - let mnemonic2 = Mnemonic::generate(24).unwrap(); - let mnemonic3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - // The top-level login id. NOTE: the first account id is always set to default. - let login_id = LoginId::new("my_login_id".to_string()); - - // Store the first account under login_id. The first account id is always set to default - // name. - store_login_with_multiple_accounts_at_file( - &wallet_file, - mnemonic1, - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - // Append another account (account2) to the same login (login_id) - let account2 = AccountId::new("account_2".to_string()); - - append_account_to_login_at_file( - &wallet_file, - mnemonic2, - hd_path.clone(), - login_id.clone(), - account2.clone(), - &password, - ) - .unwrap(); - - // Appending the third account, with same account id will fail - assert!(matches!( - append_account_to_login_at_file( - &wallet_file, - mnemonic3, - hd_path, - login_id, - account2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), - )) - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_the_same_account_twice_for_a_login_fails_with_multi() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); - - assert!(matches!( - remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), - Err(BackendError::WalletNoSuchAccountIdInWalletLogin), - )); - } - - #[test] - fn delete_appended_account_doesnt_affect_others() { - let store_dir = tempdir().unwrap(); - let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = LoginId::new("second".to_string()); - let id3 = AccountId::new("third".to_string()); - - store_login_at_file( - &wallet_file, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet_file, - account2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet_file, - account3.clone(), - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_login_at_file(&wallet_file, &id1).unwrap(); - - // The second login one is still there - let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), - WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path, - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // The file should now be removed - assert!(!wallet.exists()); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletFileNotFound), - )); - } - - #[test] - fn remove_all_accounts_for_a_login_removes_that_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let account3 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let id1 = LoginId::new("first".to_string()); - let id2 = AccountId::new("second".to_string()); - let id3 = LoginId::new("third".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1, - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2, - hd_path.clone(), - id1.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account3.clone(), - hd_path.clone(), - id3.clone(), - &password, - ) - .unwrap(); - - remove_account_from_login_at_file( - &wallet, - &id1, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &password, - ) - .unwrap(); - remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); - - // And trying to load when the file is gone fails - assert!(matches!( - load_existing_login_at_file(&wallet, &id1, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The other login is still there - let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); - let acc3 = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account3, hd_path), - )] - .into(); - assert_eq!(acc3, &expected); - } - - #[test] - fn append_accounts_and_remove_appended_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let acc1 = Mnemonic::generate(24).unwrap(); - let acc2 = Mnemonic::generate(24).unwrap(); - let acc3 = Mnemonic::generate(24).unwrap(); - let acc4 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".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()); - - store_login_at_file( - &wallet, - acc1.clone(), - hd_path.clone(), - id1.clone(), - &password, - ) - .unwrap(); - - store_login_at_file( - &wallet, - acc2.clone(), - hd_path.clone(), - id2.clone(), - &password, - ) - .unwrap(); - - // Add a third and fourth mnenonic grouped together with the second one - append_account_to_login_at_file( - &wallet, - acc3, - hd_path.clone(), - id2.clone(), - id3.clone(), - &password, - ) - .unwrap(); - append_account_to_login_at_file( - &wallet, - acc4.clone(), - hd_path.clone(), - id2.clone(), - id4.clone(), - &password, - ) - .unwrap(); - - // Delete the third mnemonic, from the second login entry - remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); - - // Check that we can still load the other accounts - let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - id2.clone().into(), - MnemonicAccount::new(acc2, hd_path.clone()), - ), - WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Delete the second and fourth mnemonic from the second login entry removes the login entry - remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); - remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); - assert!(matches!( - load_existing_login_at_file(&wallet, &id2, &password), - Err(BackendError::WalletNoSuchLoginId), - )); - - // The first login is still available - let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); - let account = loaded_login.as_mnemonic_account().unwrap(); - assert_eq!(account.mnemonic(), &acc1); - assert_eq!(account.hd_path(), &hd_path); - } - - #[test] - fn rename_first_account_in_login() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - - let renamed_account = AccountId::new("new_first".to_string()); - - rename_account_in_login_at_file( - &wallet, - &login_id, - &DEFAULT_FIRST_ACCOUNT_NAME.into(), - &renamed_account, - &password, - ) - .unwrap(); - - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![WalletAccount::new( - renamed_account, - MnemonicAccount::new(account1, hd_path), - )] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_one_account_in_login_with_two_accounts() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = AccountId::new("new_second".to_string()); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to a new name - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - #[test] - fn rename_account_into_existing_account_id_fails() { - let store_dir = tempdir().unwrap(); - let wallet = store_dir.path().join(WALLET_INFO_FILENAME); - let account1 = Mnemonic::generate(24).unwrap(); - let account2 = Mnemonic::generate(24).unwrap(); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password".to_string()); - let login_id = LoginId::new("first".to_string()); - let account_id2 = AccountId::new("second".to_string()); - let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); - - store_login_with_multiple_accounts_at_file( - &wallet, - account1.clone(), - hd_path.clone(), - login_id.clone(), - &password, - ) - .unwrap(); - - append_account_to_login_at_file( - &wallet, - account2.clone(), - hd_path.clone(), - login_id.clone(), - account_id2.clone(), - &password, - ) - .unwrap(); - - // Load and confirm - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1.clone(), hd_path.clone()), - ), - WalletAccount::new( - account_id2.clone(), - MnemonicAccount::new(account2.clone(), hd_path.clone()), - ), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - - // Rename the second account to the name of the first one fails - assert!(matches!( - rename_account_in_login_at_file( - &wallet, - &login_id, - &account_id2, - &renamed_account_id2, - &password, - ), - Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) - )); - - // Load and confirm nothing was changed - let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); - let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); - let expected = vec![ - WalletAccount::new( - DEFAULT_FIRST_ACCOUNT_NAME.into(), - MnemonicAccount::new(account1, hd_path.clone()), - ), - WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), - ] - .into(); - assert_eq!(loaded_accounts, &expected); - } - - // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored - // wallets created with older versions. - #[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 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 = 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)); - - let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); - let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - assert!(matches!(acc2, StoredLogin::Mnemonic(_))); - - let expected_acc1 = 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_acc2 = 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!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - - assert_eq!( - acc2.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc2 - ); - assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_4() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(acc1, StoredLogin::Mnemonic(_))); - - let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - - assert_eq!( - acc1.as_mnemonic_account().unwrap().mnemonic(), - &expected_acc1 - ); - assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); - } - - #[test] - fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { - const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; - let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); - - let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); - - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); - let password = UserPassword::new("password11!".to_string()); - let bad_password = UserPassword::new("bad-password".to_string()); - let login_id = LoginId::new("default".to_string()); - - assert!(!wallet.password_can_decrypt_all(&bad_password)); - assert!(wallet.password_can_decrypt_all(&password)); - - let login = wallet.decrypt_login(&login_id, &password).unwrap(); - - assert!(matches!(login, StoredLogin::Multiple(_))); - - let login = login.as_multiple_accounts().unwrap(); - assert_eq!(login.len(), 4); - - let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); - let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); - let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); - let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); - - let expected = vec![ - WalletAccount::new( - "default".into(), - MnemonicAccount::new(expected_mn1, hd_path.clone()), - ), - WalletAccount::new( - "account2".into(), - MnemonicAccount::new(expected_mn2, hd_path.clone()), - ), - WalletAccount::new( - "foobar".into(), - MnemonicAccount::new(expected_mn3, hd_path.clone()), - ), - WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), - ] - .into(); - - assert_eq!(login, &expected); - } - - #[test] - fn append_filename() { - let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); - let timestamp = OsString::from("42"); - #[cfg(target_family = "unix")] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - "/tmp/saved-wallet-42.json".to_string(), - ); - #[cfg(not(target_family = "unix"))] - assert_eq!( - append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) - .unwrap() - .into_os_string() - .into_string() - .unwrap(), - r"/tmp\saved-wallet-42.json".to_string(), - ); +// #[cfg(test)] +// mod tests { +// use crate::wallet_storage::account_data::WalletAccount; + +// use super::*; +// use nym_config::defaults::COSMOS_DERIVATION_PATH; +// use std::str::FromStr; +// use tempfile::tempdir; + +// #[test] +// fn trying_to_load_nonexistant_wallet_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let id1 = LoginId::new("first".to_string()); +// let password = UserPassword::new("password".to_string()); + +// assert!(matches!( +// load_existing_wallet_at_file(&wallet_file), +// Err(BackendError::WalletFileNotFound), +// )); +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// remove_login_at_file(&wallet_file, &id1).unwrap_err(); +// } + +// #[test] +// fn store_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(stored_wallet.len(), 1); + +// let login = stored_wallet.get_encrypted_login_by_index(0).unwrap(); +// assert_eq!(login.id, id1); + +// // some actual ciphertext was saved +// assert!(!login.account.ciphertext().is_empty()); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password,), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn store_twice_for_the_same_id_fails_with_multiple() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// // Store the first login +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // and storing the same id again fails +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1, +// &password, +// ), +// Err(BackendError::WalletLoginIdAlreadyExists), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let 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 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let 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 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Trying to load it with wrong password now fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &bad_password), +// Err(BackendError::StoreCipherError { .. }), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn load_with_wrong_id_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Trying to load with the wrong id +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_load_a_single_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_identical_pwd_is_noop_but_okay() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_at_file(&wallet_file, account1, hd_path, id1.clone(), &password).unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_and_load_a_single_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &new_password).unwrap(); +// let accounts = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(accounts.len(), 1); +// let account = accounts +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_with_wrong_current_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let wrong_password = UserPassword::new("wrong_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, acc1, hd_path, id1, &password) +// .unwrap(); + +// let err = update_encrypted_logins_at_file(&wallet_file, &wrong_password, &new_password) +// .unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_single_login_with_multi_then_update_pwd_and_load_with_wrong_pwd_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// acc1, +// hd_path, +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// let err = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).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 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// cosmos_hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_at_file(&wallet_file, account2, cosmos_hd_path, id2, &bad_password), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_a_second_login_with_a_different_password_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let 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 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1, +// &password, +// ) +// .unwrap(); + +// // Can't store a second login if you use different password +// assert!(matches!( +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2, +// hd_path, +// id2, +// &bad_password +// ), +// Err(BackendError::WalletDifferentPasswordDetected), +// )); +// } + +// #[test] +// fn store_two_mnemonic_accounts_gives_different_salts_and_iv() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file(&wallet_file, account1, hd_path, id1, &password).unwrap(); + +// let stored_wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); +// let encrypted_blob = &stored_wallet +// .get_encrypted_login_by_index(0) +// .unwrap() +// .account; + +// // keep track of salt and iv for future assertion +// let original_iv = encrypted_blob.iv().to_vec(); +// let original_salt = encrypted_blob.salt().to_vec(); + +// // Add an extra account +// store_login_at_file(&wallet_file, account2, different_hd_path, id2, &password).unwrap(); + +// let loaded_accounts = load_existing_wallet_at_file(&wallet_file).unwrap(); +// assert_eq!(loaded_accounts.len(), 2); +// let encrypted_blob = &loaded_accounts +// .get_encrypted_login_by_index(1) +// .unwrap() +// .account; + +// // fresh IV and salt are used +// assert_ne!(original_iv, encrypted_blob.iv()); +// assert_ne!(original_salt, encrypted_blob.salt()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_two_mnemonic_accounts_using_two_logins_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first login with an account +// store_login_at_file( +// &wallet, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let acc = login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc.hd_path()); + +// // Store a second login, also with an account +// store_login_at_file( +// &wallet, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// update_encrypted_logins_at_file(&wallet, &password, &new_password).unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &new_password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &new_password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); +// } + +// #[test] +// fn store_one_mnemonic_account_and_one_multi_account() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store the first account +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc.mnemonic()); +// assert_eq!(&hd_path, acc.hd_path()); + +// // Add an extra account +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_multiple_accounts().unwrap(); +// assert_eq!(acc2.len(), 1); +// let account = acc2 +// .get_account(&DEFAULT_FIRST_ACCOUNT_NAME.into()) +// .unwrap(); +// assert_eq!(account.id().as_ref(), DEFAULT_FIRST_ACCOUNT_NAME); +// assert_eq!(account.mnemonic(), &account2); +// assert_eq!(account.hd_path(), &different_hd_path); +// } + +// #[test] +// fn remove_non_existent_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file(&wallet_file, account1, hd_path, id1, &password) +// .unwrap(); + +// // Fails to delete non-existent id in the wallet +// assert!(matches!( +// remove_login_at_file(&wallet_file, &id2), +// Err(BackendError::WalletNoSuchLoginId), +// )); +// } + +// #[test] +// fn store_and_remove_wallet_login_information() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let cosmos_hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let different_hd_path: DerivationPath = "m".parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); + +// // Store two accounts with two different passwords +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// cosmos_hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// different_hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and compare +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account2, acc2.mnemonic()); +// assert_eq!(&different_hd_path, acc2.hd_path()); + +// // Delete the second account +// remove_login_at_file(&wallet_file, &id2).unwrap(); + +// // The first account should be unchanged +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(&account1, acc1.mnemonic()); +// assert_eq!(&cosmos_hd_path, acc1.hd_path()); + +// // And we can't load the second one anymore +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // Delete the first account +// assert!(wallet_file.exists()); +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The file should now be removed +// assert!(!wallet_file.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet_file, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn append_account_converts_the_type() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc.mnemonic(), &account1); +// assert_eq!(acc.hd_path(), &hd_path); + +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it is now multiple mnemonic type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id1.into(), MnemonicAccount::new(account1, hd_path.clone())), +// WalletAccount::new(id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".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()); + +// store_login_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that it's there as the correct non-multiple type +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let acc2 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc2.mnemonic(), &account2); +// assert_eq!(acc2.hd_path(), &hd_path); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let acc1 = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(acc1.mnemonic(), &account1); +// assert_eq!(acc1.hd_path(), &hd_path); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_accounts_to_existing_login_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let account4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".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()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet_file, +// account4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Check that we can load all four +// let loaded_login = load_existing_login_at_file(&wallet_file, &id1, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account2, hd_path.clone()), +// ), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path.clone())), +// WalletAccount::new(id4, MnemonicAccount::new(account4, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn append_account_to_existing_login_with_multi_then_update_pwd_and_load() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let new_password = UserPassword::new("new_password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let appended_account = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append a second mnenonic to the same login +// append_account_to_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// appended_account.clone(), +// &password, +// ) +// .unwrap(); + +// // Update the password +// update_encrypted_logins_at_file(&wallet_file, &password, &new_password).unwrap(); + +// // Expect that we can load these 2 accounts with the new password +// let loaded_login = +// load_existing_login_at_file(&wallet_file, &login_id, &new_password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(appended_account, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Expect that trying to load these 2 accounts with the old password fails +// let err = load_existing_login_at_file(&wallet_file, &login_id, &password).unwrap_err(); +// assert!(matches!(err, BackendError::StoreCipherError { .. })); +// } + +// #[test] +// fn append_the_same_mnemonic_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// assert!(matches!( +// append_account_to_login_at_file(&wallet_file, account1, hd_path, id1, id2, &password), +// Err(BackendError::WalletMnemonicAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn append_the_same_account_name_twice_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let mnemonic1 = Mnemonic::generate(24).unwrap(); +// let mnemonic2 = Mnemonic::generate(24).unwrap(); +// let mnemonic3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// // The top-level login id. NOTE: the first account id is always set to default. +// let login_id = LoginId::new("my_login_id".to_string()); + +// // Store the first account under login_id. The first account id is always set to default +// // name. +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// mnemonic1, +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// // Append another account (account2) to the same login (login_id) +// let account2 = AccountId::new("account_2".to_string()); + +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic2, +// hd_path.clone(), +// login_id.clone(), +// account2.clone(), +// &password, +// ) +// .unwrap(); + +// // Appending the third account, with same account id will fail +// assert!(matches!( +// append_account_to_login_at_file( +// &wallet_file, +// mnemonic3, +// hd_path, +// login_id, +// account2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin), +// )) +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_at_file(&wallet, account1, hd_path.clone(), id1.clone(), &password).unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_the_same_account_twice_for_a_login_fails_with_multi() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password).unwrap(); + +// assert!(matches!( +// remove_account_from_login_at_file(&wallet_file, &id1, &id2, &password), +// Err(BackendError::WalletNoSuchAccountIdInWalletLogin), +// )); +// } + +// #[test] +// fn delete_appended_account_doesnt_affect_others() { +// let store_dir = tempdir().unwrap(); +// let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = LoginId::new("second".to_string()); +// let id3 = AccountId::new("third".to_string()); + +// store_login_at_file( +// &wallet_file, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet_file, +// account2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet_file, +// account3.clone(), +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_login_at_file(&wallet_file, &id1).unwrap(); + +// // The second login one is still there +// let loaded_login = load_existing_login_at_file(&wallet_file, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new(id2.into(), MnemonicAccount::new(account2, hd_path.clone())), +// WalletAccount::new(id3, MnemonicAccount::new(account3, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_the_file_when_empty() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path, +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // The file should now be removed +// assert!(!wallet.exists()); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletFileNotFound), +// )); +// } + +// #[test] +// fn remove_all_accounts_for_a_login_removes_that_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let account3 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let id1 = LoginId::new("first".to_string()); +// let id2 = AccountId::new("second".to_string()); +// let id3 = LoginId::new("third".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1, +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2, +// hd_path.clone(), +// id1.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account3.clone(), +// hd_path.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); + +// remove_account_from_login_at_file( +// &wallet, +// &id1, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &password, +// ) +// .unwrap(); +// remove_account_from_login_at_file(&wallet, &id1, &id2, &password).unwrap(); + +// // And trying to load when the file is gone fails +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id1, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The other login is still there +// let loaded_login = load_existing_login_at_file(&wallet, &id3, &password).unwrap(); +// let acc3 = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account3, hd_path), +// )] +// .into(); +// assert_eq!(acc3, &expected); +// } + +// #[test] +// fn append_accounts_and_remove_appended_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let acc1 = Mnemonic::generate(24).unwrap(); +// let acc2 = Mnemonic::generate(24).unwrap(); +// let acc3 = Mnemonic::generate(24).unwrap(); +// let acc4 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".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()); + +// store_login_at_file( +// &wallet, +// acc1.clone(), +// hd_path.clone(), +// id1.clone(), +// &password, +// ) +// .unwrap(); + +// store_login_at_file( +// &wallet, +// acc2.clone(), +// hd_path.clone(), +// id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Add a third and fourth mnenonic grouped together with the second one +// append_account_to_login_at_file( +// &wallet, +// acc3, +// hd_path.clone(), +// id2.clone(), +// id3.clone(), +// &password, +// ) +// .unwrap(); +// append_account_to_login_at_file( +// &wallet, +// acc4.clone(), +// hd_path.clone(), +// id2.clone(), +// id4.clone(), +// &password, +// ) +// .unwrap(); + +// // Delete the third mnemonic, from the second login entry +// remove_account_from_login_at_file(&wallet, &id2, &id3, &password).unwrap(); + +// // Check that we can still load the other accounts +// let loaded_login = load_existing_login_at_file(&wallet, &id2, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// id2.clone().into(), +// MnemonicAccount::new(acc2, hd_path.clone()), +// ), +// WalletAccount::new(id4.clone(), MnemonicAccount::new(acc4, hd_path.clone())), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Delete the second and fourth mnemonic from the second login entry removes the login entry +// remove_account_from_login_at_file(&wallet, &id2, &id2.clone().into(), &password).unwrap(); +// remove_account_from_login_at_file(&wallet, &id2, &id4, &password).unwrap(); +// assert!(matches!( +// load_existing_login_at_file(&wallet, &id2, &password), +// Err(BackendError::WalletNoSuchLoginId), +// )); + +// // The first login is still available +// let loaded_login = load_existing_login_at_file(&wallet, &id1, &password).unwrap(); +// let account = loaded_login.as_mnemonic_account().unwrap(); +// assert_eq!(account.mnemonic(), &acc1); +// assert_eq!(account.hd_path(), &hd_path); +// } + +// #[test] +// fn rename_first_account_in_login() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// let renamed_account = AccountId::new("new_first".to_string()); + +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &DEFAULT_FIRST_ACCOUNT_NAME.into(), +// &renamed_account, +// &password, +// ) +// .unwrap(); + +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![WalletAccount::new( +// renamed_account, +// MnemonicAccount::new(account1, hd_path), +// )] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_one_account_in_login_with_two_accounts() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = AccountId::new("new_second".to_string()); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to a new name +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(renamed_account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// #[test] +// fn rename_account_into_existing_account_id_fails() { +// let store_dir = tempdir().unwrap(); +// let wallet = store_dir.path().join(WALLET_INFO_FILENAME); +// let account1 = Mnemonic::generate(24).unwrap(); +// let account2 = Mnemonic::generate(24).unwrap(); +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password".to_string()); +// let login_id = LoginId::new("first".to_string()); +// let account_id2 = AccountId::new("second".to_string()); +// let renamed_account_id2 = DEFAULT_FIRST_ACCOUNT_NAME.into(); + +// store_login_with_multiple_accounts_at_file( +// &wallet, +// account1.clone(), +// hd_path.clone(), +// login_id.clone(), +// &password, +// ) +// .unwrap(); + +// append_account_to_login_at_file( +// &wallet, +// account2.clone(), +// hd_path.clone(), +// login_id.clone(), +// account_id2.clone(), +// &password, +// ) +// .unwrap(); + +// // Load and confirm +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1.clone(), hd_path.clone()), +// ), +// WalletAccount::new( +// account_id2.clone(), +// MnemonicAccount::new(account2.clone(), hd_path.clone()), +// ), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); + +// // Rename the second account to the name of the first one fails +// assert!(matches!( +// rename_account_in_login_at_file( +// &wallet, +// &login_id, +// &account_id2, +// &renamed_account_id2, +// &password, +// ), +// Err(BackendError::WalletAccountIdAlreadyExistsInWalletLogin) +// )); + +// // Load and confirm nothing was changed +// let loaded_login = load_existing_login_at_file(&wallet, &login_id, &password).unwrap(); +// let loaded_accounts = loaded_login.as_multiple_accounts().unwrap(); +// let expected = vec![ +// WalletAccount::new( +// DEFAULT_FIRST_ACCOUNT_NAME.into(), +// MnemonicAccount::new(account1, hd_path.clone()), +// ), +// WalletAccount::new(account_id2, MnemonicAccount::new(account2, hd_path)), +// ] +// .into(); +// assert_eq!(loaded_accounts, &expected); +// } + +// // Test to that decrypts a stored file from the repo, to make sure we are able to decrypt stored +// // wallets created with older versions. +// #[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 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 = 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)); + +// let acc1 = wallet.decrypt_login(&id1, &password).unwrap(); +// let acc2 = wallet.decrypt_login(&id2, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); +// assert!(matches!(acc2, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = 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_acc2 = 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!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); + +// assert_eq!( +// acc2.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc2 +// ); +// assert_eq!(acc2.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_4() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.4.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let acc1 = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(acc1, StoredLogin::Mnemonic(_))); + +// let expected_acc1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); + +// assert_eq!( +// acc1.as_mnemonic_account().unwrap().mnemonic(), +// &expected_acc1 +// ); +// assert_eq!(acc1.as_mnemonic_account().unwrap().hd_path(), &hd_path,); +// } + +// #[test] +// fn decrypt_stored_wallet_1_0_5_with_multiple_accounts() { +// const SAVED_WALLET: &str = "src/wallet_storage/test-data/saved-wallet-1.0.5.json"; +// let wallet_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SAVED_WALLET); + +// let wallet = load_existing_wallet_at_file(&wallet_file).unwrap(); + +// let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); +// let password = UserPassword::new("password11!".to_string()); +// let bad_password = UserPassword::new("bad-password".to_string()); +// let login_id = LoginId::new("default".to_string()); + +// assert!(!wallet.password_can_decrypt_all(&bad_password)); +// assert!(wallet.password_can_decrypt_all(&password)); + +// let login = wallet.decrypt_login(&login_id, &password).unwrap(); + +// assert!(matches!(login, StoredLogin::Multiple(_))); + +// let login = login.as_multiple_accounts().unwrap(); +// assert_eq!(login.len(), 4); + +// let expected_mn1 = bip39::Mnemonic::from_str("arrow capable abstract industry elevator nominee december piece hotel feed lounge web faint sword veteran bundle hour page actual laptop horror gold test warrior").unwrap(); +// let expected_mn2 = bip39::Mnemonic::from_str("border hurt skull lunar goddess second danger game dismiss exhaust oven thumb dog drama onion false orchard spice tent next predict invite cherry green").unwrap(); +// let expected_mn3 = bip39::Mnemonic::from_str("gentle crowd rule snap girl urge flat jump winner cluster night sand museum stock grunt quick tree acquire traffic major awake tag rack peasant").unwrap(); +// let expected_mn4 = bip39::Mnemonic::from_str("debris blue skin annual inhale text border rigid spatial lesson coconut yard horn crystal control survey version vote hawk neck frame arrive oblige width").unwrap(); + +// let expected = vec![ +// WalletAccount::new( +// "default".into(), +// MnemonicAccount::new(expected_mn1, hd_path.clone()), +// ), +// WalletAccount::new( +// "account2".into(), +// MnemonicAccount::new(expected_mn2, hd_path.clone()), +// ), +// WalletAccount::new( +// "foobar".into(), +// MnemonicAccount::new(expected_mn3, hd_path.clone()), +// ), +// WalletAccount::new("42".into(), MnemonicAccount::new(expected_mn4, hd_path)), +// ] +// .into(); + +// assert_eq!(login, &expected); +// } + +// #[test] +// fn append_filename() { +// let wallet_file = PathBuf::from("/tmp/saved-wallet.json"); +// let timestamp = OsString::from("42"); +// #[cfg(target_family = "unix")] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// "/tmp/saved-wallet-42.json".to_string(), +// ); +// #[cfg(not(target_family = "unix"))] +// assert_eq!( +// append_timestamp_to_filename(wallet_file.clone(), timestamp.clone(), None) +// .unwrap() +// .into_os_string() +// .into_string() +// .unwrap(), +// r"/tmp\saved-wallet-42.json".to_string(), +// ); #[cfg(target_family = "unix")] assert_eq!(