diff --git a/crates/sp_client/src/api.rs b/crates/sp_client/src/api.rs index 14c43cc..9d0d59c 100644 --- a/crates/sp_client/src/api.rs +++ b/crates/sp_client/src/api.rs @@ -33,7 +33,7 @@ use crate::images; use crate::silentpayments::{ check_transaction, create_transaction_for_address_with_shared_secret, ScannedTransaction, }; -use crate::user::{lock_connected_users, User, UserWallets, CONNECTED_USERS}; +use crate::user::{lock_connected_user, User, UserWallets, CONNECTED_USER}; use crate::process::Process; @@ -141,9 +141,20 @@ pub fn generate_sp_wallet( } #[wasm_bindgen] -pub fn get_receiving_address(pre_id: String) -> ApiResult { - if let Some(my_wallets) = lock_connected_users()?.get(&pre_id) { - Ok(my_wallets.recover.get_client().get_receiving_address()) +pub fn get_recover_address() -> ApiResult { + if let Ok(my_wallets) = lock_connected_user() { + Ok(my_wallets.try_get_recover()?.get_client().get_receiving_address()) + } else { + Err(ApiError { + message: "Unknown user pre_id".to_owned(), + }) + } +} + +#[wasm_bindgen] +pub fn get_main_address() -> ApiResult { + if let Ok(my_wallets) = lock_connected_user() { + Ok(my_wallets.try_get_main()?.get_client().get_receiving_address()) } else { Err(ApiError { message: "Unknown user pre_id".to_owned(), @@ -168,7 +179,7 @@ pub fn create_user( let user_wallets = UserWallets::new( Some(sp_wallet_main), - sp_wallet_recover, + Some(sp_wallet_recover), Some(sp_wallet_revoke), ); @@ -176,7 +187,9 @@ pub fn create_user( let outputs = user_wallets.get_all_outputs(); - lock_connected_users()?.insert(user.pre_id.clone(), user_wallets); + // Setting CONNECTED_USER to user + let mut connected_user = lock_connected_user()?; + *connected_user = user_wallets; let generate_user = createUserReturn { user, @@ -359,24 +372,22 @@ pub fn parse_network_msg(raw: String) -> ApiResult { #[wasm_bindgen] pub fn get_outpoints_for_user() -> ApiResult { - let connected_users = lock_connected_users()?; - let (_, user) = connected_users.iter().last().ok_or(ApiError { - message: "Can't find user".to_owned(), - })?; - Ok(outputs_list(user.get_all_outputs())) + let connected_user = lock_connected_user()?; + if connected_user.is_not_empty() { + Ok(outputs_list(connected_user.get_all_outputs())) + } else { + Err(ApiError { message: "No user logged in".to_owned() }) + } } #[wasm_bindgen] pub fn is_tx_owned_by_user(pre_id: String, tx: String) -> ApiResult { let transaction = deserialize::(&Vec::from_hex(&tx)?)?; let txid = transaction.txid(); - let connected_users = lock_connected_users()?; - let user = connected_users.get(&pre_id).ok_or(ApiError { - message: "Can't find user".to_owned(), - })?; + let connected_user = lock_connected_user()?; - if let Some(_) = user - .recover + if let Some(_) = connected_user + .try_get_recover()? .get_outputs() .to_outpoints_list() .iter() diff --git a/crates/sp_client/src/silentpayments.rs b/crates/sp_client/src/silentpayments.rs index 1b5d35e..aee9616 100644 --- a/crates/sp_client/src/silentpayments.rs +++ b/crates/sp_client/src/silentpayments.rs @@ -27,7 +27,7 @@ use sp_client::{ }; use tsify::Tsify; -use crate::user::{lock_connected_users, CONNECTED_USERS}; +use crate::user::{lock_connected_user, CONNECTED_USER}; use crate::MutexExt; #[derive(Debug, Serialize, Deserialize, Tsify)] @@ -94,26 +94,19 @@ pub fn create_transaction_for_address_with_shared_secret( sp_address: SilentPaymentAddress, message: String, ) -> Result<(Transaction, SharedSecret)> { - let connected_users = lock_connected_users()?; - - let (_, wallets) = connected_users - .iter() - .last() - .ok_or(Error::msg("Unknown sender"))?; - - debug!("Got user wallets"); + let connected_user = lock_connected_user()?; let sp_wallet = if sp_address.is_testnet() { - &wallets.recover + connected_user.try_get_recover()? } else { - if let Some(main) = &wallets.main { + if let Ok(main) = connected_user.try_get_main() { main } else { return Err(Error::msg("Can't spend on mainnet")); } }; - let available_outpoints = wallets.recover.get_outputs().to_spendable_list(); + let available_outpoints = sp_wallet.get_outputs().to_spendable_list(); // Here we need to add more heuristics about which outpoint we spend // For now let's keep it simple @@ -205,27 +198,19 @@ pub fn check_transaction( return Err(Error::msg(err_msg)); } - let connected_users = lock_connected_users()?; + let mut connected_user = lock_connected_user()?; let txid = tx.txid().to_string(); - // Check the transaction for all connected users - for (pre_id, keys) in connected_users.clone() { - let mut recover = keys.recover; - if recover.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 { - return Ok(txid); - } + if connected_user.try_get_mut_recover()?.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 { + return Ok(txid); + } - if let Some(mut main) = keys.main { - if main.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 { - return Ok(txid); - } - } + if connected_user.try_get_mut_main()?.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 { + return Ok(txid); + } - if let Some(mut revoke) = keys.revoke { - if revoke.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 { - return Ok(txid); - } - } + if connected_user.try_get_mut_revoke()?.update_wallet_with_transaction(tx, blockheight, tweak_data)? > 0 { + return Ok(txid); } return Err(Error::msg("No new outputs found")); diff --git a/crates/sp_client/src/user.rs b/crates/sp_client/src/user.rs index 30a0119..37de212 100644 --- a/crates/sp_client/src/user.rs +++ b/crates/sp_client/src/user.rs @@ -36,24 +36,23 @@ type PreId = String; const MANAGERS_NUMBER: u8 = 10; const QUORUM_SHARD: f32 = 0.8; -type UsersMap = HashMap; -pub static CONNECTED_USERS: OnceLock> = OnceLock::new(); +pub static CONNECTED_USER: OnceLock> = OnceLock::new(); -pub fn lock_connected_users() -> Result> { - CONNECTED_USERS - .get_or_init(|| Mutex::new(HashMap::new())) +pub fn lock_connected_user() -> Result> { + CONNECTED_USER + .get_or_init(|| Mutex::new(UserWallets::default())) .lock_anyhow() } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct UserWallets { - pub main: Option, - pub recover: SpWallet, - pub revoke: Option, + main: Option, + recover: Option, + revoke: Option, } impl UserWallets { - pub fn new(main: Option, recover: SpWallet, revoke: Option) -> Self { + pub fn new(main: Option, recover: Option, revoke: Option) -> Self { Self { main, recover, @@ -61,8 +60,56 @@ impl UserWallets { } } - pub fn try_get_revoke(&self) -> Option<&SpWallet> { - self.revoke.as_ref() + pub fn try_get_revoke(&self) -> Result<&SpWallet> { + if let Some(revoke) = &self.revoke { + Ok(revoke) + } else { + Err(Error::msg("No revoke wallet available")) + } + } + + pub fn try_get_recover(&self) -> Result<&SpWallet> { + if let Some(recover) = &self.recover { + Ok(recover) + } else { + Err(Error::msg("No recover wallet available")) + } + } + + pub fn try_get_main(&self) -> Result<&SpWallet> { + if let Some(main) = &self.main { + Ok(main) + } else { + Err(Error::msg("No main wallet available")) + } + } + + pub fn try_get_mut_revoke(&mut self) -> Result<&mut SpWallet> { + if let Some(revoke) = &mut self.revoke { + Ok(revoke) + } else { + Err(Error::msg("No revoke wallet available")) + } + } + + pub fn try_get_mut_recover(&mut self) -> Result<&mut SpWallet> { + if let Some(recover) = &mut self.recover { + Ok(recover) + } else { + Err(Error::msg("No recover wallet available")) + } + } + + pub fn try_get_mut_main(&mut self) -> Result<&mut SpWallet> { + if let Some(main) = &mut self.main { + Ok(main) + } else { + Err(Error::msg("No main wallet available")) + } + } + + pub(crate) fn is_not_empty(&self) -> bool { + self.get_all_outputs().len() > 0 } pub(crate) fn get_all_outputs(&self) -> Vec { @@ -73,7 +120,9 @@ impl UserWallets { if let Some(revoke) = &self.revoke { res.push(revoke.get_outputs().clone()); } - res.push(self.recover.get_outputs().clone()); + if let Some(recover) = &self.recover { + res.push(recover.get_outputs().clone()); + } res } @@ -93,22 +142,24 @@ pub struct User { impl User { pub fn new(user_wallets: UserWallets, user_password: String, process: String) -> Result { + // if we are already logged in, abort + if lock_connected_user()?.is_not_empty() { + return Err(Error::msg("User already logged in")); + } + let mut rng = thread_rng(); // image revoke // We just take the 2 revoke keys let mut revoke_data = Vec::with_capacity(64); - if let Some(revoke) = user_wallets.try_get_revoke() { - revoke_data.extend_from_slice(revoke.get_client().get_scan_key().as_ref()); - revoke_data.extend_from_slice(revoke.get_client().try_get_secret_spend_key()?.as_ref()); - } else { - return Err(Error::msg("No revoke wallet available")); - } + let revoke = user_wallets.try_get_revoke()?; + revoke_data.extend_from_slice(revoke.get_client().get_scan_key().as_ref()); + revoke_data.extend_from_slice(revoke.get_client().try_get_secret_spend_key()?.as_ref()); // Take the 2 recover keys // split recover spend key let recover_spend_key = user_wallets - .recover + .try_get_recover()? .get_client() .try_get_secret_spend_key()? .clone(); @@ -186,7 +237,7 @@ impl User { let scan_key_encryption = Aes256Encryption::import_key( Purpose::ThirtyTwoBytes, user_wallets - .recover + .try_get_recover()? .get_client() .get_scan_key() .secret_bytes() @@ -200,6 +251,8 @@ impl User { recover_data.extend_from_slice(&cipher_scan_key); + let all_outputs = user_wallets.get_all_outputs(); + Ok(User { pre_id: pre_id.to_string(), processes: vec![process], @@ -207,10 +260,19 @@ impl User { recover_data, revoke_data: Some(revoke_data), shares, - outputs: user_wallets.get_all_outputs(), + outputs: all_outputs, }) } + pub fn logout() -> Result<()> { + if let Ok(mut user) = lock_connected_user() { + *user = UserWallets::default(); + Ok(()) + } else { + Err(Error::msg("Failed to lock CONNECTED_USER")) + } + } + pub fn login( pre_id: PreId, user_password: String, @@ -218,6 +280,11 @@ impl User { shares: &[Vec], outputs: &[OutputList], ) -> Result<()> { + // if we are already logged in, abort + if lock_connected_user()?.is_not_empty() { + return Err(Error::msg("User already logged in")); + } + let mut retrieved_spend_key = [0u8; 32]; let mut retrieved_scan_key = [0u8; 32]; let mut entropy1 = [0u8; 32]; @@ -241,21 +308,6 @@ impl User { return Err(Error::msg("pre_id and recover_data don't match")); } - // If we already have loaded a user with this pre_id, abort - if let Some(current_users) = CONNECTED_USERS.get() { - if current_users - .to_owned() - .lock() - .unwrap() - .contains_key(&pre_id) - { - return Err(Error::msg(format!( - "User with pre_id {} already logged in", - pre_id - ))); - } - } - retrieved_spend_key[..16].copy_from_slice(&Self::recover_part1( &user_password, &entropy1, @@ -290,26 +342,12 @@ impl User { let recover_wallet = SpWallet::new(recover_client, recover_outputs)?; - // Adding user to CONNECTED_USERS - if let Some(current_users) = CONNECTED_USERS.get() { - let mut lock = current_users.to_owned().lock().unwrap(); - if lock.contains_key(&pre_id) { - return Err(Error::msg(format!( - "User with pre_id {} already exists", - pre_id - ))); - } else { - lock.insert(pre_id.clone(), UserWallets::new(None, recover_wallet, None)); - } + let user_wallets = UserWallets::new(None, Some(recover_wallet), None); + + if let Ok(mut user) = lock_connected_user() { + *user = user_wallets; } else { - let mut user_map = HashMap::new(); - user_map.insert(pre_id, UserWallets::new(None, recover_wallet, None)); - let new_value = Mutex::new(user_map); - if let Err(error) = CONNECTED_USERS.set(new_value) { - return Err(Error::msg( - "Failed to set the CONNECTED_USERS static variable", - )); - } + return Err(Error::msg("Failed to lock CONNECTED_USER")); } Ok(()) @@ -456,21 +494,26 @@ mod tests { .unwrap(); let user_wallets = UserWallets::new( Some(SpWallet::new(sp_main, None).unwrap()), - SpWallet::new(sp_recover, None).unwrap(), + Some(SpWallet::new(sp_recover, None).unwrap()), Some(SpWallet::new(sp_revoke, None).unwrap()), ); user_wallets } - // Test 1: Create User #[test] fn test_successful_creation() { let user_wallets = helper_create_user_wallets(); let result = User::new(user_wallets, USER_PASSWORD.to_owned(), PROCESS.to_owned()); assert!(result.is_ok()); - let user = result.unwrap(); + } + + #[test] + fn test_logout() { + let res = User::logout(); + + assert!(res.is_ok()); } #[test] @@ -493,9 +536,9 @@ mod tests { assert!(res.is_ok()); - let connected = CONNECTED_USERS.get().unwrap().lock().unwrap(); + let connected = lock_connected_user().unwrap(); - let recover = &connected.get(&user.pre_id).unwrap().recover; + let recover = connected.try_get_recover().unwrap(); assert!( format!(