diff --git a/lib/src/commands.rs b/lib/src/commands.rs index 3d853fa..732235c 100644 --- a/lib/src/commands.rs +++ b/lib/src/commands.rs @@ -475,7 +475,7 @@ impl Command for SendCommand { h.push("OR"); h.push("send '[{'address':
, 'amount': , 'memo': }, ...]'"); h.push(""); - h.push("NOTE: The fee required to send this transaction (currently HUSH 0.0001) is additionally detected from your balance."); + h.push("NOTE: The fee required to send this transaction (currently HUSH 0.0001) is additionally deducted from your balance."); h.push("Example:"); h.push("send ztestsapling1x65nq4dgp0qfywgxcwk9n0fvm4fysmapgr2q00p85ju252h6l7mmxu2jg9cqqhtvzd69jwhgv8d 200000 \"Hello from the command line\""); h.push(""); @@ -548,17 +548,12 @@ impl Command for SendCommand { return self.help() }; - match lightclient.do_sync(true) { - Ok(_) => { - // Convert to the right format. String -> &str. - let tos = send_args.iter().map(|(a, v, m)| (a.as_str(), *v, m.clone()) ).collect::>(); - match lightclient.do_send(tos) { - Ok(txid) => { object!{ "txid" => txid } }, - Err(e) => { object!{ "error" => e } } - }.pretty(2) - }, - Err(e) => e - } + // Convert to the right format. String -> &str. + let tos = send_args.iter().map(|(a, v, m)| (a.as_str(), *v, m.clone()) ).collect::>(); + match lightclient.do_send(tos) { + Ok(txid) => { object!{ "txid" => txid } }, + Err(e) => { object!{ "error" => e } } + }.pretty(2) } } @@ -673,30 +668,12 @@ impl Command for ImportCommand { } let key = args[0]; - - - let rescan = if args.len() == 3 { - if args[2] == "norescan" || args[2] == "false" || args[2] == "no" { - false - } else { - return format!("Couldn't undestand the argument '{}'. Please pass 'norescan' to prevent rescanning the wallet", args[2]); - } - } else { - true - }; let r = match lightclient.do_import_key(key.to_string(), 0) { Ok(r) => r.pretty(2), Err(e) => return format!("Error: {}", e), }; - if rescan { - match lightclient.do_rescan() { - Ok(_) => {}, - Err(e) => return format!("Error: Rescan failed: {}", e), - }; - } - return r; } } @@ -719,21 +696,53 @@ impl Command for TImportCommand { } fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { - + if args.len() < 1 || args.len() > 2 { + return format!("Insufficient arguments\n\n{}", self.help()); + } + let key = args[0]; - let r = match lightclient.do_import_tk(key.to_string()){ + let r = match lightclient.do_import_tk(key.to_string(), 0) { Ok(r) => r.pretty(2), Err(e) => return format!("Error: {}", e), }; - match lightclient.do_rescan() { - Ok(_) => {}, - Err(e) => return format!("Error: Rescan failed: {}", e), - }; + return r; + } +} +struct ShieldCommand {} +impl Command for ShieldCommand { + fn help(&self) -> String { + let mut h = vec![]; + h.push("Shield all your transparent funds"); + h.push("Usage:"); + h.push("shield [optional address]"); + h.push(""); + h.push("NOTE: The fee required to send this transaction (currently HUSH 0.0001) is additionally deducted from your balance."); + h.push("Example:"); + h.push("shield"); + h.push(""); - return r; + h.join("\n") + } + + fn short_help(&self) -> String { + "Shield your transparent HUSH into a sapling address".to_string() + } + + fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + // Parse the address or amount + let address = if args.len() > 0 { + Some(args[0].to_string()) + } else { + None + }; + + match lightclient.do_shield(address) { + Ok(txid) => { object!{ "txid" => txid } }, + Err(e) => { object!{ "error" => e } } + }.pretty(2) } } @@ -905,6 +914,7 @@ pub fn get_commands() -> Box>> { map.insert("info".to_string(), Box::new(InfoCommand{})); map.insert("coinsupply".to_string(), Box::new(CoinsupplyCommand{})); map.insert("send".to_string(), Box::new(SendCommand{})); + map.insert("shield".to_string(), Box::new(ShieldCommand{})); map.insert("save".to_string(), Box::new(SaveCommand{})); map.insert("quit".to_string(), Box::new(QuitCommand{})); map.insert("list".to_string(), Box::new(TransactionsCommand{})); diff --git a/lib/src/lightclient.rs b/lib/src/lightclient.rs index 38c48ea..fa181a1 100644 --- a/lib/src/lightclient.rs +++ b/lib/src/lightclient.rs @@ -1011,16 +1011,16 @@ impl LightClient { } /// Import a new private key - pub fn do_import_tk(&self, sk: String) -> Result { + pub fn do_import_tk(&self, sk: String, birthday: u64) -> Result { if !self.wallet.read().unwrap().is_unlocked_for_spending() { error!("Wallet is locked"); return Err("Wallet is locked".to_string()); } let new_address = { - let wallet = self.wallet.write().unwrap(); + let mut wallet = self.wallet.write().unwrap(); - let addr = wallet.import_taddr(sk); + let addr = wallet.import_taddr(sk, birthday); if addr.starts_with("Error") { let e = format!("Error creating new address{}", addr); error!("{}", e); @@ -1166,6 +1166,34 @@ impl LightClient { } } + pub fn do_shield(&self, address: Option) -> Result { + use zcash_primitives::transaction::components::amount::DEFAULT_FEE; + use std::convert::TryInto; + + let fee = DEFAULT_FEE.try_into().unwrap(); + let tbal = self.wallet.read().unwrap().tbalance(None); + + // Make sure there is a balance, and it is greated than the amount + if tbal <= fee { + return Err(format!("Not enough transparent balance to shield. Have {} puposhis, need more than {} puposhis to cover tx fee", tbal, fee)); + } + + let addr = address.or(self.wallet.read().unwrap().get_all_zaddresses().get(0).map(|s| s.clone())).unwrap(); + + let result = { + let _lock = self.sync_lock.lock().unwrap(); + self.wallet.read().unwrap().send_to_address( + u32::from_str_radix(&self.config.consensus_branch_id, 16).unwrap(), + &self.sapling_spend, &self.sapling_output, + true, + vec![(&addr, tbal - fee, None)], + |txbytes| broadcast_raw_tx(&self.get_server_uri(),self.config.no_cert_verification, txbytes) + ) + }; + + result.map(|(txid, _)| txid) + } + fn do_sync_internal(&self, print_updates: bool, retry_count: u32) -> Result { // We can only do one sync at a time because we sync blocks in serial order // If we allow multiple syncs, they'll all get jumbled up. @@ -1477,6 +1505,7 @@ impl LightClient { self.wallet.write().unwrap().send_to_address( u32::from_str_radix(&self.config.consensus_branch_id, 16).unwrap(), &self.sapling_spend, &self.sapling_output, + false, addrs, |txbytes| broadcast_raw_tx(&self.get_server_uri(), self.config.no_cert_verification, txbytes) ) diff --git a/lib/src/lightwallet.rs b/lib/src/lightwallet.rs index 083f20e..9c66639 100644 --- a/lib/src/lightwallet.rs +++ b/lib/src/lightwallet.rs @@ -628,7 +628,7 @@ impl LightWallet { address } - pub fn import_taddr(&self, sk: String) -> String { + pub fn import_taddr(&mut self, sk: String, birthday: u64) -> String { if !self.unlocked { return "Error: Can't add key while wallet is locked".to_string(); } @@ -657,6 +657,11 @@ impl LightWallet { //// Add to tkeys self.tkeys.write().unwrap().push(WalletTKey::import_hdkey(sk_raw , address.clone())); + // Adjust wallet birthday + if birthday < self.birthday { + self.birthday = if birthday < self.config.sapling_activation_height {self.config.sapling_activation_height} else {birthday}; + } + address } @@ -1992,6 +1997,7 @@ impl LightWallet { consensus_branch_id: u32, spend_params: &[u8], output_params: &[u8], + transparent_only: bool, tos: Vec<(&str, u64, Option)>, broadcast_fn: F ) -> Result<(String, Vec), String>