diff --git a/lib/src/commands.rs b/lib/src/commands.rs index b6dc870..c052ff4 100644 --- a/lib/src/commands.rs +++ b/lib/src/commands.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use json::{object}; use crate::lightclient::LightClient; +use crate::lightwallet::LightWallet; pub trait Command { fn help(&self) -> String; @@ -487,6 +488,8 @@ impl Command for SendCommand { Err(s) => { return format!("Error: {}\n{}", s, self.help()); } } } else if args.len() == 2 || args.len() == 3 { + let address = args[0].to_string(); + // Make sure we can parse the amount let value = match args[1].parse::() { Ok(amt) => amt, @@ -495,7 +498,12 @@ impl Command for SendCommand { } }; - let memo = if args.len() == 3 { Some(args[2].to_string()) } else {None}; + let memo = if args.len() == 3 { Some(args[2].to_string()) } else { None }; + + // Memo has to be None if not sending to a shileded address + if memo.is_some() && !LightWallet::is_shielded_address(&address, &lightclient.config) { + return format!("Can't send a memo to the non-shielded address {}", address); + } vec![(args[0].to_string(), value, memo)] } else { diff --git a/lib/src/lightwallet.rs b/lib/src/lightwallet.rs index ec1f91c..809ac21 100644 --- a/lib/src/lightwallet.rs +++ b/lib/src/lightwallet.rs @@ -166,6 +166,16 @@ impl LightWallet { (extsk, extfvk, address) } + pub fn is_shielded_address(addr: &String, config: &LightClientConfig) -> bool { + match address::RecipientAddress::from_str(addr, + config.hrp_sapling_address(), + config.base58_pubkey_address(), + config.base58_script_address()) { + Some(address::RecipientAddress::Shielded(_)) => true, + _ => false, + } + } + pub fn new(seed_phrase: Option, config: &LightClientConfig, latest_block: u64) -> io::Result { // This is the source entropy that corresponds to the 24-word seed phrase let mut seed_bytes = [0u8; 32]; @@ -1572,7 +1582,14 @@ impl LightWallet { value: *amt, memo: match maybe_memo { None => Memo::default(), - Some(s) => Memo::from_str(&s).unwrap(), + Some(s) => { + // If the address is not a z-address, then drop the memo + if LightWallet::is_shielded_address(&addr.to_string(), &self.config) { + Memo::from_str(s).unwrap() + } else { + Memo::default() + } + } }, } }).collect::>(); diff --git a/lib/src/lightwallet/tests.rs b/lib/src/lightwallet/tests.rs index ac8b5ff..8a1e9ba 100644 --- a/lib/src/lightwallet/tests.rs +++ b/lib/src/lightwallet/tests.rs @@ -922,7 +922,6 @@ fn test_z_spend_to_taddr() { cb3.add_tx(&sent_tx); wallet.scan_block(&cb3.as_bytes()).unwrap(); - // Now this new Spent tx should be in, so the note should be marked confirmed spent { let txs = wallet.txs.read().unwrap(); @@ -950,6 +949,38 @@ fn test_z_spend_to_taddr() { assert_eq!(txs[&sent_txid].outgoing_metadata[0].value, AMOUNT_SENT); assert_eq!(txs[&sent_txid].total_shielded_value_spent, AMOUNT1); } + + // Create a new Tx, but this time with a memo. + let raw_tx = wallet.send_to_address(branch_id, &ss, &so, + vec![(&taddr, AMOUNT_SENT, Some("T address memo".to_string()))]).unwrap(); + let sent_tx = Transaction::read(&raw_tx[..]).unwrap(); + let sent_txid2 = sent_tx.txid(); + + // There should be a mempool Tx, but the memo should be dropped, because it was sent to a + // t address + { + let txs = wallet.mempool_txs.read().unwrap(); + + assert_eq!(txs[&sent_txid2].outgoing_metadata.len(), 1); + assert_eq!(txs[&sent_txid2].outgoing_metadata[0].address, taddr); + assert_eq!(txs[&sent_txid2].outgoing_metadata[0].value, AMOUNT_SENT); + assert_eq!(LightWallet::memo_str(&Some(txs[&sent_txid2].outgoing_metadata[0].memo.clone())), None); + } + + // Now add the block + let mut cb4 = FakeCompactBlock::new(3, cb3.hash()); + cb4.add_tx(&sent_tx); + wallet.scan_block(&cb4.as_bytes()).unwrap(); + wallet.scan_full_tx(&sent_tx, 3, 0); + + // Check Outgoing Metadata for t address, but once again there should be no memo + { + let txs = wallet.txs.read().unwrap(); + assert_eq!(txs[&sent_txid2].outgoing_metadata.len(), 1); + assert_eq!(txs[&sent_txid2].outgoing_metadata[0].address, taddr); + assert_eq!(txs[&sent_txid2].outgoing_metadata[0].value, AMOUNT_SENT); + assert_eq!(LightWallet::memo_str(&Some(txs[&sent_txid2].outgoing_metadata[0].memo.clone())), None); + } } #[test]