wallet: store id instead of mnemonic in state
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
### Added
|
||||
|
||||
- wallet: require password to switch accounts
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
|
||||
Generated
+1
@@ -3178,6 +3178,7 @@ dependencies = [
|
||||
"proxy-helpers",
|
||||
"publicsuffix",
|
||||
"rand 0.7.3",
|
||||
"rocket",
|
||||
"serde",
|
||||
"socks5-requests",
|
||||
"sqlx",
|
||||
|
||||
@@ -43,13 +43,12 @@ fn main() {
|
||||
mixnet::account::does_password_file_exist,
|
||||
mixnet::account::get_balance,
|
||||
mixnet::account::list_accounts,
|
||||
mixnet::account::sign_in_decrypted_account,
|
||||
mixnet::account::logout,
|
||||
mixnet::account::remove_account_for_password,
|
||||
mixnet::account::remove_password,
|
||||
mixnet::account::show_mnemonic_for_account_in_password,
|
||||
mixnet::account::sign_in_decrypted_account,
|
||||
mixnet::account::sign_in_with_password,
|
||||
mixnet::account::sign_in_with_password_and_account_id,
|
||||
mixnet::account::switch_network,
|
||||
mixnet::account::validate_mnemonic,
|
||||
mixnet::admin::get_contract_settings,
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::error::BackendError;
|
||||
use crate::network::Network as WalletNetwork;
|
||||
use crate::network_config;
|
||||
use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use crate::state::{State, WalletAccountIds};
|
||||
use crate::wallet_storage::{self, DEFAULT_LOGIN_ID};
|
||||
|
||||
use bip39::{Language, Mnemonic};
|
||||
@@ -229,6 +229,10 @@ async fn _connect_with_mnemonic(
|
||||
};
|
||||
|
||||
// Register all the clients
|
||||
{
|
||||
let mut w_state = state.write().await;
|
||||
w_state.logout();
|
||||
}
|
||||
for client in clients {
|
||||
let network: WalletNetwork = client.network.into();
|
||||
let mut w_state = state.write().await;
|
||||
@@ -395,9 +399,9 @@ pub async fn sign_in_with_password(
|
||||
}
|
||||
|
||||
fn extract_first_mnemonic(
|
||||
stored_account: &wallet_storage::StoredLogin,
|
||||
stored_login: &wallet_storage::StoredLogin,
|
||||
) -> Result<Mnemonic, BackendError> {
|
||||
let mnemonic = match stored_account {
|
||||
let mnemonic = match stored_login {
|
||||
wallet_storage::StoredLogin::Mnemonic(ref account) => account.mnemonic().clone(),
|
||||
wallet_storage::StoredLogin::Multiple(ref accounts) => {
|
||||
// Login using the first account in the list
|
||||
@@ -413,6 +417,44 @@ fn extract_first_mnemonic(
|
||||
Ok(mnemonic)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sign_in_with_password_and_account_id(
|
||||
account_id: &str,
|
||||
password: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Account, BackendError> {
|
||||
log::info!("Signing in with password");
|
||||
|
||||
// Currently we only support a single, default, id in the wallet
|
||||
let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string());
|
||||
let account_id = wallet_storage::AccountId::new(account_id.to_string());
|
||||
let password = wallet_storage::UserPassword::new(password.to_string());
|
||||
let stored_login = wallet_storage::load_existing_login(&login_id, &password)?;
|
||||
|
||||
let mnemonic = extract_mnemonic(&stored_login, &account_id)?;
|
||||
let first_login_id_when_converting = login_id.into();
|
||||
set_state_with_all_accounts(stored_login, first_login_id_when_converting, state.clone()).await?;
|
||||
|
||||
_connect_with_mnemonic(mnemonic, state).await
|
||||
}
|
||||
|
||||
fn extract_mnemonic(
|
||||
stored_login: &wallet_storage::StoredLogin,
|
||||
account_id: &wallet_storage::AccountId,
|
||||
) -> Result<Mnemonic, BackendError> {
|
||||
let mnemonic = match stored_login {
|
||||
wallet_storage::StoredLogin::Mnemonic(_) => {
|
||||
return Err(BackendError::WalletNoSuchAccountIdInWalletLogin);
|
||||
}
|
||||
wallet_storage::StoredLogin::Multiple(ref accounts) => accounts
|
||||
.get_account(account_id)
|
||||
.ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)?
|
||||
.mnemonic()
|
||||
.clone(),
|
||||
};
|
||||
Ok(mnemonic)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn remove_password() -> Result<(), BackendError> {
|
||||
log::info!("Removing password");
|
||||
@@ -479,8 +521,28 @@ async fn set_state_with_all_accounts(
|
||||
log::trace!("account: {:?}", account);
|
||||
}
|
||||
|
||||
let all_account_ids: Vec<WalletAccountIds> = all_accounts
|
||||
.iter()
|
||||
.map(|account| {
|
||||
let mnemonic = account.mnemonic();
|
||||
let addresses: HashMap<WalletNetwork, cosmrs::AccountId> = WalletNetwork::iter()
|
||||
.map(|network| {
|
||||
let config_network: Network = network.into();
|
||||
(
|
||||
network,
|
||||
derive_address(mnemonic.clone(), config_network.bech32_prefix()).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
WalletAccountIds {
|
||||
id: account.id().clone(),
|
||||
addresses,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut w_state = state.write().await;
|
||||
w_state.set_all_accounts(all_accounts);
|
||||
w_state.set_all_accounts(all_account_ids);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -523,16 +585,13 @@ pub async fn list_accounts(
|
||||
) -> Result<Vec<AccountEntry>, BackendError> {
|
||||
log::trace!("Listing accounts");
|
||||
let state = state.read().await;
|
||||
let network: Network = state.current_network().into();
|
||||
let prefix = network.bech32_prefix();
|
||||
let network = state.current_network();
|
||||
|
||||
let all_accounts = state
|
||||
.get_all_accounts()
|
||||
.map(|account| AccountEntry {
|
||||
id: account.id().to_string(),
|
||||
address: derive_address(account.mnemonic().clone(), prefix)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
id: account.id.to_string(),
|
||||
address: account.addresses[&network].to_string(),
|
||||
})
|
||||
.map(|account| {
|
||||
log::trace!("{:?}", account);
|
||||
@@ -552,8 +611,16 @@ pub fn show_mnemonic_for_account_in_password(
|
||||
let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string());
|
||||
let account_id = wallet_storage::AccountId::new(account_id);
|
||||
let password = wallet_storage::UserPassword::new(password);
|
||||
let stored_account = wallet_storage::load_existing_login(&login_id, &password)?;
|
||||
let mnemonic = _show_mnemonic_for_account_in_password(&login_id, &account_id, &password)?;
|
||||
Ok(mnemonic.to_string())
|
||||
}
|
||||
|
||||
fn _show_mnemonic_for_account_in_password(
|
||||
login_id: &wallet_storage::LoginId,
|
||||
account_id: &wallet_storage::AccountId,
|
||||
password: &wallet_storage::UserPassword,
|
||||
) -> Result<bip39::Mnemonic, BackendError> {
|
||||
let stored_account = wallet_storage::load_existing_login(login_id, password)?;
|
||||
let mnemonic = match stored_account {
|
||||
wallet_storage::StoredLogin::Mnemonic(ref account) => account.mnemonic().clone(),
|
||||
wallet_storage::StoredLogin::Multiple(ref accounts) => {
|
||||
@@ -561,36 +628,13 @@ pub fn show_mnemonic_for_account_in_password(
|
||||
log::debug!("{:?}", account);
|
||||
}
|
||||
accounts
|
||||
.get_account(&account_id)
|
||||
.get_account(account_id)
|
||||
.ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)?
|
||||
.mnemonic()
|
||||
.clone()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(mnemonic.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sign_in_decrypted_account(
|
||||
account_id: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Account, BackendError> {
|
||||
log::info!("Signing in to already decrypted account: {account_id}");
|
||||
let mnemonic = {
|
||||
let state = state.read().await;
|
||||
let account = &state
|
||||
.get_all_accounts()
|
||||
.find(|a| a.id().as_ref() == account_id)
|
||||
.ok_or(BackendError::WalletNoSuchAccountIdInWalletLogin)?;
|
||||
account.mnemonic().clone()
|
||||
};
|
||||
|
||||
{
|
||||
state.write().await.logout();
|
||||
}
|
||||
|
||||
_connect_with_mnemonic(mnemonic, state).await
|
||||
Ok(mnemonic)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::network::Network;
|
||||
use crate::wallet_storage::account_data::WalletAccount;
|
||||
use crate::{config, network_config};
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
@@ -49,7 +48,7 @@ pub struct State {
|
||||
|
||||
// All the accounts the we get from decrypting the wallet. We hold on to these for being able to
|
||||
// switch accounts on-the-fly
|
||||
all_accounts: Vec<WalletAccount>,
|
||||
all_accounts: Vec<WalletAccountIds>,
|
||||
|
||||
/// Validators that have been fetched dynamically, probably during startup.
|
||||
fetched_validators: config::OptionalValidators,
|
||||
@@ -58,6 +57,13 @@ pub struct State {
|
||||
validator_metadata: HashMap<Url, ValidatorMetadata>,
|
||||
}
|
||||
|
||||
pub(crate) struct WalletAccountIds {
|
||||
// The wallet account id
|
||||
pub id: crate::wallet_storage::AccountId,
|
||||
// The set of corresponding network identities derived from the mnemonic
|
||||
pub addresses: HashMap<Network, cosmrs::AccountId>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn client(&self, network: Network) -> Result<&Client<SigningNymdClient>, BackendError> {
|
||||
self
|
||||
@@ -117,11 +123,11 @@ impl State {
|
||||
self.current_network
|
||||
}
|
||||
|
||||
pub(crate) fn set_all_accounts(&mut self, all_accounts: Vec<WalletAccount>) {
|
||||
pub(crate) fn set_all_accounts(&mut self, all_accounts: Vec<WalletAccountIds>) {
|
||||
self.all_accounts = all_accounts
|
||||
}
|
||||
|
||||
pub(crate) fn get_all_accounts(&self) -> impl Iterator<Item = &WalletAccount> {
|
||||
pub(crate) fn get_all_accounts(&self) -> impl Iterator<Item = &WalletAccountIds> {
|
||||
self.all_accounts.iter()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user