wallet: additional tidy
This commit is contained in:
@@ -70,7 +70,6 @@ impl StoredWallet {
|
||||
.accounts
|
||||
.iter_mut()
|
||||
.find(|account| &account.id == id)
|
||||
//.map(|account| &mut account.account)
|
||||
.ok_or(BackendError::NoSuchIdInWallet)
|
||||
}
|
||||
|
||||
@@ -132,6 +131,19 @@ pub(crate) struct EncryptedLogin {
|
||||
pub account: EncryptedData<StoredLogin>,
|
||||
}
|
||||
|
||||
impl EncryptedLogin {
|
||||
pub(crate) fn encrypt(
|
||||
id: LoginId,
|
||||
login: &StoredLogin,
|
||||
password: &UserPassword,
|
||||
) -> Result<Self, BackendError> {
|
||||
Ok(EncryptedLogin {
|
||||
id,
|
||||
account: super::encryption::encrypt_struct(login, password)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A stored login is either a account, such as a mnemonic, or a list of multiple accounts where
|
||||
/// each has an inner id. Future proofed for having private key backed accounts.
|
||||
#[derive(Serialize, Deserialize, Debug, Zeroize)]
|
||||
@@ -144,17 +156,6 @@ pub(crate) enum StoredLogin {
|
||||
}
|
||||
|
||||
impl StoredLogin {
|
||||
pub(crate) fn new_mnemonic_backed_account(
|
||||
mnemonic: bip39::Mnemonic,
|
||||
hd_path: DerivationPath,
|
||||
) -> Self {
|
||||
Self::Mnemonic(MnemonicAccount { mnemonic, hd_path })
|
||||
}
|
||||
|
||||
pub(crate) fn new_multiple_login() -> Self {
|
||||
Self::Multiple(MultipleAccounts::empty())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn as_mnemonic_account(&self) -> Option<&MnemonicAccount> {
|
||||
match self {
|
||||
@@ -171,64 +172,19 @@ impl StoredLogin {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the login as multiple accounts, and if there is only a single mnemonic backed account,
|
||||
// return a set containing only the single account paired with the account id passed as function
|
||||
// argument.
|
||||
pub(crate) fn unwrap_into_multiple_accounts(self, id: AccountId) -> MultipleAccounts {
|
||||
match self {
|
||||
StoredLogin::Mnemonic(ref account) => account.clone().into_multiple(id),
|
||||
StoredLogin::Mnemonic(ref account) => {
|
||||
vec![WalletAccount::from_mnemonic_account(id, account.clone())].into()
|
||||
}
|
||||
StoredLogin::Multiple(ref accounts) => accounts.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An account backed by a unique mnemonic.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct MnemonicAccount {
|
||||
mnemonic: bip39::Mnemonic,
|
||||
#[serde(with = "display_hd_path")]
|
||||
hd_path: DerivationPath,
|
||||
}
|
||||
|
||||
impl MnemonicAccount {
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
&self.mnemonic
|
||||
}
|
||||
|
||||
pub(crate) fn hd_path(&self) -> &DerivationPath {
|
||||
&self.hd_path
|
||||
}
|
||||
|
||||
pub(crate) fn into_wallet_account(self, id: AccountId) -> WalletAccount {
|
||||
WalletAccount {
|
||||
id,
|
||||
account: self.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_multiple(self, id: AccountId) -> MultipleAccounts {
|
||||
MultipleAccounts::new(self.into_wallet_account(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for MnemonicAccount {
|
||||
fn zeroize(&mut self) {
|
||||
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
|
||||
// and the memory would have been filled with zeroes.
|
||||
//
|
||||
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
|
||||
// of overwriting it with a fresh mnemonic that was never used before
|
||||
//
|
||||
// note: this function can only fail on an invalid word count, which clearly is not the case here
|
||||
self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap();
|
||||
|
||||
// further note: we don't really care about the hd_path, there's nothing secret about it.
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MnemonicAccount {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple stored accounts, each entry having an id and a data field.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
|
||||
pub(crate) struct MultipleAccounts {
|
||||
@@ -236,18 +192,12 @@ pub(crate) struct MultipleAccounts {
|
||||
}
|
||||
|
||||
impl MultipleAccounts {
|
||||
pub(crate) fn empty() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
MultipleAccounts {
|
||||
accounts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(account: WalletAccount) -> Self {
|
||||
MultipleAccounts {
|
||||
accounts: vec![account],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_accounts(&self) -> impl Iterator<Item = &WalletAccount> {
|
||||
self.accounts.iter()
|
||||
}
|
||||
@@ -317,11 +267,20 @@ impl WalletAccount {
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
account: AccountData::new_mnemonic_backed_account(mnemonic, hd_path),
|
||||
account: AccountData::Mnemonic(MnemonicAccount::new(mnemonic, hd_path)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_mnemonic_account(id: AccountId, mnemonic_account: MnemonicAccount) -> Self {
|
||||
Self {
|
||||
id,
|
||||
account: AccountData::Mnemonic(mnemonic_account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An account usually is a mnemonic account, but in the future it might be backed by a private
|
||||
/// key.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
#[zeroize(drop)]
|
||||
@@ -331,13 +290,6 @@ pub(crate) enum AccountData {
|
||||
}
|
||||
|
||||
impl AccountData {
|
||||
pub(crate) fn new_mnemonic_backed_account(
|
||||
mnemonic: bip39::Mnemonic,
|
||||
hd_path: DerivationPath,
|
||||
) -> AccountData {
|
||||
AccountData::Mnemonic(MnemonicAccount { mnemonic, hd_path })
|
||||
}
|
||||
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
match self {
|
||||
AccountData::Mnemonic(account) => account.mnemonic(),
|
||||
@@ -352,9 +304,47 @@ impl AccountData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MnemonicAccount> for AccountData {
|
||||
fn from(mnemonic_account: MnemonicAccount) -> Self {
|
||||
AccountData::Mnemonic(mnemonic_account)
|
||||
/// An account backed by a unique mnemonic.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct MnemonicAccount {
|
||||
mnemonic: bip39::Mnemonic,
|
||||
#[serde(with = "display_hd_path")]
|
||||
hd_path: DerivationPath,
|
||||
}
|
||||
|
||||
impl MnemonicAccount {
|
||||
pub(crate) fn new(mnemonic: bip39::Mnemonic, hd_path: DerivationPath) -> Self {
|
||||
Self { mnemonic, hd_path }
|
||||
}
|
||||
|
||||
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
|
||||
&self.mnemonic
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn hd_path(&self) -> &DerivationPath {
|
||||
&self.hd_path
|
||||
}
|
||||
}
|
||||
|
||||
impl Zeroize for MnemonicAccount {
|
||||
fn zeroize(&mut self) {
|
||||
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
|
||||
// and the memory would have been filled with zeroes.
|
||||
//
|
||||
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
|
||||
// of overwriting it with a fresh mnemonic that was never used before
|
||||
//
|
||||
// note: this function can only fail on an invalid word count, which clearly is not the case here
|
||||
self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap();
|
||||
|
||||
// further note: we don't really care about the hd_path, there's nothing secret about it.
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MnemonicAccount {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ pub(crate) use crate::wallet_storage::password::{AccountId, LoginId, UserPasswor
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME};
|
||||
use crate::wallet_storage::encryption::encrypt_struct;
|
||||
use cosmrs::bip32::DerivationPath;
|
||||
use std::fs::{self, create_dir_all, OpenOptions};
|
||||
use std::path::PathBuf;
|
||||
@@ -108,12 +107,9 @@ fn store_wallet_login_at_file(
|
||||
return Err(BackendError::WalletDifferentPasswordDetected);
|
||||
}
|
||||
|
||||
let new_account = StoredLogin::new_mnemonic_backed_account(mnemonic, hd_path);
|
||||
let new_encrypted_account = EncryptedLogin {
|
||||
id,
|
||||
account: encrypt_struct(&new_account, password)?,
|
||||
};
|
||||
|
||||
let new_account = account_data::MnemonicAccount::new(mnemonic, hd_path);
|
||||
let new_login = StoredLogin::Mnemonic(new_account);
|
||||
let new_encrypted_account = EncryptedLogin::encrypt(id, &new_login, password)?;
|
||||
stored_wallet.add_encrypted_login(new_encrypted_account)?;
|
||||
|
||||
let file = OpenOptions::new()
|
||||
@@ -157,18 +153,10 @@ fn store_wallet_login_with_multiple_accounts_at_file(
|
||||
return Err(BackendError::WalletDifferentPasswordDetected);
|
||||
}
|
||||
|
||||
let mut new_accounts = account_data::MultipleAccounts::empty();
|
||||
new_accounts.add(
|
||||
DEFAULT_FIRST_ACCOUNT_NAME.to_string().into(),
|
||||
mnemonic,
|
||||
hd_path,
|
||||
)?;
|
||||
|
||||
let mut new_accounts = account_data::MultipleAccounts::new();
|
||||
new_accounts.add(DEFAULT_FIRST_ACCOUNT_NAME.into(), mnemonic, hd_path)?;
|
||||
let new_login = StoredLogin::Multiple(new_accounts);
|
||||
let new_encrypted_login = EncryptedLogin {
|
||||
id,
|
||||
account: encrypt_struct(&new_login, password)?,
|
||||
};
|
||||
let new_encrypted_login = EncryptedLogin::encrypt(id, &new_login, password)?;
|
||||
|
||||
stored_wallet.add_encrypted_login(new_encrypted_login)?;
|
||||
|
||||
@@ -222,10 +210,7 @@ fn append_account_to_wallet_login_at_file(
|
||||
let mut accounts = decrypted_login.unwrap_into_multiple_accounts(first_id_when_converting);
|
||||
accounts.add(inner_id, mnemonic, hd_path)?;
|
||||
|
||||
let encrypted_accounts = EncryptedLogin {
|
||||
id,
|
||||
account: encrypt_struct(&StoredLogin::Multiple(accounts), password)?,
|
||||
};
|
||||
let encrypted_accounts = EncryptedLogin::encrypt(id, &StoredLogin::Multiple(accounts), password)?;
|
||||
|
||||
stored_wallet.replace_encrypted_login(encrypted_accounts)?;
|
||||
|
||||
@@ -305,6 +290,7 @@ fn remove_account_from_wallet_login_at_file(
|
||||
|
||||
let mut decrypted_login = stored_wallet.decrypt_login(id, password)?;
|
||||
|
||||
// Remove the account
|
||||
let is_empty = match decrypted_login {
|
||||
StoredLogin::Mnemonic(_) => {
|
||||
log::warn!("Encountered mnemonic login instead of list of accounts, aborting");
|
||||
@@ -316,19 +302,17 @@ fn remove_account_from_wallet_login_at_file(
|
||||
}
|
||||
};
|
||||
|
||||
// Remove the login, or encrypt the new updated login
|
||||
if is_empty {
|
||||
stored_wallet
|
||||
.remove_encrypted_login(id)
|
||||
.ok_or(BackendError::NoSuchIdInWallet)?;
|
||||
} else {
|
||||
// Replace the encrypted login with the pruned one.
|
||||
let encrypted_accounts = EncryptedLogin {
|
||||
id: id.clone(),
|
||||
account: encrypt_struct(&decrypted_login, password)?,
|
||||
};
|
||||
let encrypted_accounts = EncryptedLogin::encrypt(id.clone(), &decrypted_login, password)?;
|
||||
stored_wallet.replace_encrypted_login(encrypted_accounts)?;
|
||||
}
|
||||
|
||||
// Remove the file, or write the new file
|
||||
if stored_wallet.is_empty() {
|
||||
log::info!("Removing file: {:#?}", filepath);
|
||||
Ok(fs::remove_file(filepath)?)
|
||||
@@ -785,7 +769,7 @@ mod tests {
|
||||
#[test]
|
||||
fn store_two_mnemonic_accounts_using_two_logins() {
|
||||
let store_dir = tempdir().unwrap();
|
||||
let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME);
|
||||
let wallet = store_dir.path().join(WALLET_INFO_FILENAME);
|
||||
|
||||
let dummy_account1 = bip39::Mnemonic::generate(24).unwrap();
|
||||
let dummy_account2 = bip39::Mnemonic::generate(24).unwrap();
|
||||
@@ -799,7 +783,7 @@ mod tests {
|
||||
|
||||
// Store the first account
|
||||
store_wallet_login_at_file(
|
||||
wallet_file.clone(),
|
||||
wallet.clone(),
|
||||
dummy_account1.clone(),
|
||||
cosmos_hd_path.clone(),
|
||||
id1.clone(),
|
||||
@@ -807,15 +791,14 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let loaded_account =
|
||||
load_existing_wallet_login_at_file(wallet_file.clone(), &id1, &password).unwrap();
|
||||
let acc = loaded_account.as_mnemonic_account().unwrap();
|
||||
let login = load_existing_wallet_login_at_file(wallet.clone(), &id1, &password).unwrap();
|
||||
let acc = login.as_mnemonic_account().unwrap();
|
||||
assert_eq!(&dummy_account1, acc.mnemonic());
|
||||
assert_eq!(&cosmos_hd_path, acc.hd_path());
|
||||
|
||||
// Add an extra account
|
||||
store_wallet_login_at_file(
|
||||
wallet_file.clone(),
|
||||
wallet.clone(),
|
||||
dummy_account2.clone(),
|
||||
different_hd_path.clone(),
|
||||
id2.clone(),
|
||||
@@ -825,12 +808,12 @@ mod tests {
|
||||
|
||||
// first account should be unchanged
|
||||
let loaded_account =
|
||||
load_existing_wallet_login_at_file(wallet_file.clone(), &id1, &password).unwrap();
|
||||
load_existing_wallet_login_at_file(wallet.clone(), &id1, &password).unwrap();
|
||||
let acc1 = loaded_account.as_mnemonic_account().unwrap();
|
||||
assert_eq!(&dummy_account1, acc1.mnemonic());
|
||||
assert_eq!(&cosmos_hd_path, acc1.hd_path());
|
||||
|
||||
let loaded_account = load_existing_wallet_login_at_file(wallet_file, &id2, &password).unwrap();
|
||||
let loaded_account = load_existing_wallet_login_at_file(wallet, &id2, &password).unwrap();
|
||||
let acc2 = loaded_account.as_mnemonic_account().unwrap();
|
||||
assert_eq!(&dummy_account2, acc2.mnemonic());
|
||||
assert_eq!(&different_hd_path, acc2.hd_path());
|
||||
|
||||
Reference in New Issue
Block a user