diff --git a/lib/src/commands.rs b/lib/src/commands.rs index 56911dd..7477c93 100644 --- a/lib/src/commands.rs +++ b/lib/src/commands.rs @@ -332,6 +332,44 @@ impl Command for UnlockCommand { } } + +struct LockCommand {} +impl Command for LockCommand { + fn help(&self) -> String { + let mut h = vec![]; + h.push("Lock a wallet that's been temporarily unlocked. You should already have encryption enabled."); + h.push("Note 1: This will remove all spending keys from memory. The wallet remains encrypted on disk"); + h.push("Note 2: If you've forgotten the password, the only way to recover the wallet is to restore"); + h.push(" from the seed phrase."); + h.push("Usage:"); + h.push("lock"); + h.push(""); + h.push("Example:"); + h.push("lock"); + + h.join("\n") + } + + fn short_help(&self) -> String { + "Lock a wallet that's been temporarily unlocked".to_string() + } + + fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + if args.len() != 0 { + return self.help(); + } + + match lightclient.wallet.write().unwrap().lock() { + Ok(_) => object!{ "result" => "success" }, + Err(e) => object!{ + "result" => "error", + "error" => e.to_string() + } + }.pretty(2) + } +} + + struct SendCommand {} impl Command for SendCommand { fn help(&self) -> String { @@ -665,6 +703,7 @@ pub fn get_commands() -> Box>> { map.insert("encrypt".to_string(), Box::new(EncryptCommand{})); map.insert("decrypt".to_string(), Box::new(DecryptCommand{})); map.insert("unlock".to_string(), Box::new(UnlockCommand{})); + map.insert("lock".to_string(), Box::new(LockCommand{})); map.insert("fixbip39bug".to_string(), Box::new(FixBip39BugCommand{})); Box::new(map) diff --git a/lib/src/lightwallet.rs b/lib/src/lightwallet.rs index d5a4abf..f592f0e 100644 --- a/lib/src/lightwallet.rs +++ b/lib/src/lightwallet.rs @@ -596,8 +596,8 @@ impl LightWallet { pub fn encrypt(&mut self, passwd: String) -> io::Result<()> { use sodiumoxide::crypto::secretbox; - if self.encrypted && !self.unlocked { - return Err(io::Error::new(ErrorKind::AlreadyExists, "Wallet is already encrypted and locked")); + if self.encrypted { + return Err(io::Error::new(ErrorKind::AlreadyExists, "Wallet is already encrypted")); } // Get the doublesha256 of the password, which is the right length @@ -617,6 +617,14 @@ impl LightWallet { } pub fn lock(&mut self) -> io::Result<()> { + if !self.encrypted { + return Err(io::Error::new(ErrorKind::AlreadyExists, "Wallet is not encrypted")); + } + + if !self.unlocked { + return Err(io::Error::new(ErrorKind::AlreadyExists, "Wallet is already locked")); + } + // Empty the seed and the secret keys self.seed.copy_from_slice(&[0u8; 32]); self.tkeys = Arc::new(RwLock::new(vec![])); @@ -844,15 +852,11 @@ impl LightWallet { } } - // Scan the full Tx and update memos for incoming shielded transactions + // Scan the full Tx and update memos for incoming shielded transactions. pub fn scan_full_tx(&self, tx: &Transaction, height: i32, datetime: u64) { - // Scan all the inputs to see if we spent any transparent funds in this tx - - // TODO: Save this object - let secp = secp256k1::Secp256k1::new(); - let mut total_transparent_spend: u64 = 0; + // Scan all the inputs to see if we spent any transparent funds in this tx for vin in tx.vin.iter() { // Find the txid in the list of utxos that we have. let txid = TxId {0: vin.prevout.hash}; @@ -892,19 +896,15 @@ impl LightWallet { .total_transparent_value_spent = total_transparent_spend; } - // TODO: Iterate over all transparent addresses. This is currently looking only at - // the first one. // Scan for t outputs - let all_pubkeys = self.tkeys.read().unwrap().iter() - .map(|sk| - secp256k1::PublicKey::from_secret_key(&secp, sk).serialize() - ) - .collect::>(); - for pubkey in all_pubkeys { + let all_taddresses = self.taddresses.read().unwrap().iter() + .map(|a| a.clone()) + .collect::>(); + for address in all_taddresses { for (n, vout) in tx.vout.iter().enumerate() { match vout.script_pubkey.address() { Some(TransparentAddress::PublicKey(hash)) => { - if hash[..] == ripemd160::Ripemd160::digest(&Sha256::digest(&pubkey))[..] { + if address == hash.to_base58check(&self.config.base58_pubkey_address(), &[]) { // This is our address. Add this as an output to the txid self.add_toutput_to_wtx(height, datetime, &tx.txid(), &vout, n as u64); } @@ -1318,6 +1318,8 @@ impl LightWallet { let total_value = tos.iter().map(|to| to.1).sum::(); + // TODO: Check for duplicates in destination addresses + println!( "0: Creating transaction sending {} ztoshis to {} addresses", total_value, tos.len() @@ -3152,6 +3154,10 @@ pub mod tests { let seed = wallet.seed; + // Trying to lock a wallet that's not encrpyted is an error + assert!(wallet.lock().is_err()); + + // Encrypt the wallet wallet.encrypt("somepassword".to_string()).unwrap(); // Encrypting an already encrypted wallet should fail @@ -3196,6 +3202,9 @@ pub mod tests { wallet.lock().unwrap(); wallet.write(&mut vec![]).expect("Serialize wallet"); + // Locking an already locked wallet is an error + assert!(wallet.lock().is_err()); + // Try from a deserialized, locked wallet let mut wallet2 = LightWallet::read(&serialized_data[..], &config).unwrap(); wallet2.unlock("somepassword".to_string()).unwrap(); diff --git a/lib/src/lightwallet/bugs.rs b/lib/src/lightwallet/bugs.rs index 0e9c925..8a88170 100644 --- a/lib/src/lightwallet/bugs.rs +++ b/lib/src/lightwallet/bugs.rs @@ -74,13 +74,13 @@ impl BugBip39Derivation { // Tranfer money // 1. The desination is z address #0 - println!("Sending funds to ourself."); let zaddr = client.do_address()["z_addresses"][0].as_str().unwrap().to_string(); let balance_json = client.do_balance(); let amount: u64 = balance_json["zbalance"].as_u64().unwrap() + balance_json["tbalance"].as_u64().unwrap(); let txid = if amount > 0 { + println!("Sending funds to ourself."); let fee: u64 = DEFAULT_FEE.try_into().unwrap(); match client.do_send(vec![(&zaddr, amount-fee, None)]) { Ok(txid) => txid,