Browse Source

Support mutable wallets

checkpoints
Aditya Kulkarni 5 years ago
parent
commit
fc15de5687
  1. 233
      lib/src/lightclient.rs
  2. 100
      lib/src/lightwallet.rs

233
lib/src/lightclient.rs

@ -179,7 +179,7 @@ impl LightClientConfig {
}
pub struct LightClient {
pub wallet : Arc<LightWallet>,
pub wallet : Arc<RwLock<LightWallet>>,
pub config : LightClientConfig,
@ -196,7 +196,7 @@ impl LightClient {
let state = self.config.get_initial_state();
match state {
Some((height, hash, tree)) => self.wallet.set_initial_block(height.try_into().unwrap(), hash, tree),
Some((height, hash, tree)) => self.wallet.read().unwrap().set_initial_block(height.try_into().unwrap(), hash, tree),
_ => true,
};
}
@ -213,14 +213,14 @@ impl LightClient {
let wallet = LightWallet::read(&mut file_buffer, config)?;
LightClient {
wallet : Arc::new(wallet),
wallet : Arc::new(RwLock::new(wallet)),
config : config.clone(),
sapling_output : vec![],
sapling_spend : vec![]
}
} else {
let l = LightClient {
wallet : Arc::new(LightWallet::new(seed_phrase, config, latest_block)?),
wallet : Arc::new(RwLock::new(LightWallet::new(seed_phrase, config, latest_block)?)),
config : config.clone(),
sapling_output : vec![],
sapling_spend : vec![]
@ -231,7 +231,7 @@ impl LightClient {
l
};
info!("Read wallet with birthday {}", lc.wallet.get_first_tx_block());
info!("Read wallet with birthday {}", lc.wallet.read().unwrap().get_first_tx_block());
// Read Sapling Params
lc.sapling_output.extend_from_slice(SaplingParams::get("sapling-output.params").unwrap().as_ref());
@ -243,16 +243,16 @@ impl LightClient {
}
pub fn last_scanned_height(&self) -> u64 {
self.wallet.last_scanned_height() as u64
self.wallet.read().unwrap().last_scanned_height() as u64
}
// Export private keys
pub fn do_export(&self, addr: Option<String>) -> JsonValue {
// Clone address so it can be moved into the closure
let address = addr.clone();
let wallet = self.wallet.read().unwrap();
// Go over all z addresses
let z_keys = self.wallet.get_z_private_keys().iter()
let z_keys = wallet.get_z_private_keys().iter()
.filter( move |(addr, _)| address.is_none() || address.as_ref() == Some(addr))
.map( |(addr, pk)|
object!{
@ -265,7 +265,7 @@ impl LightClient {
let address = addr.clone();
// Go over all t addresses
let t_keys = self.wallet.get_t_secret_keys().iter()
let t_keys = wallet.get_t_secret_keys().iter()
.filter( move |(addr, _)| address.is_none() || address.as_ref() == Some(addr))
.map( |(addr, sk)|
object!{
@ -282,15 +282,15 @@ impl LightClient {
}
pub fn do_address(&self) -> JsonValue {
let wallet = self.wallet.read().unwrap();
// Collect z addresses
let z_addresses = self.wallet.address.read().unwrap().iter().map( |ad| {
let z_addresses = wallet.zaddress.read().unwrap().iter().map( |ad| {
encode_payment_address(self.config.hrp_sapling_address(), &ad)
}).collect::<Vec<String>>();
// Collect t addresses
let t_addresses = self.wallet.tkeys.read().unwrap().iter().map( |sk| {
self.wallet.address_from_sk(&sk)
}).collect::<Vec<String>>();
let t_addresses = wallet.taddresses.read().unwrap().iter().map( |a| a.clone() )
.collect::<Vec<String>>();
object!{
"z_addresses" => z_addresses,
@ -299,33 +299,33 @@ impl LightClient {
}
pub fn do_balance(&self) -> JsonValue {
let wallet = self.wallet.read().unwrap();
// Collect z addresses
let z_addresses = self.wallet.address.read().unwrap().iter().map( |ad| {
let z_addresses = wallet.zaddress.read().unwrap().iter().map( |ad| {
let address = encode_payment_address(self.config.hrp_sapling_address(), &ad);
object!{
"address" => address.clone(),
"zbalance" => self.wallet.zbalance(Some(address.clone())),
"verified_zbalance" => self.wallet.verified_zbalance(Some(address)),
"zbalance" => wallet.zbalance(Some(address.clone())),
"verified_zbalance" => wallet.verified_zbalance(Some(address)),
}
}).collect::<Vec<JsonValue>>();
// Collect t addresses
let t_addresses = self.wallet.tkeys.read().unwrap().iter().map( |sk| {
let address = self.wallet.address_from_sk(&sk);
let t_addresses = wallet.taddresses.read().unwrap().iter().map( |address| {
// Get the balance for this address
let balance = self.wallet.tbalance(Some(address.clone()));
let balance = wallet.tbalance(Some(address.clone()));
object!{
"address" => address,
"address" => address.clone(),
"balance" => balance,
}
}).collect::<Vec<JsonValue>>();
object!{
"zbalance" => self.wallet.zbalance(None),
"verified_zbalance" => self.wallet.verified_zbalance(None),
"tbalance" => self.wallet.tbalance(None),
"zbalance" => wallet.zbalance(None),
"verified_zbalance" => wallet.verified_zbalance(None),
"tbalance" => wallet.tbalance(None),
"z_addresses" => z_addresses,
"t_addresses" => t_addresses,
}
@ -336,7 +336,7 @@ impl LightClient {
1_000_000, // 1 MB write buffer
File::create(self.config.get_wallet_path()).unwrap());
match self.wallet.write(&mut file_buffer) {
match self.wallet.write().unwrap().write(&mut file_buffer) {
Ok(_) => {
info!("Saved wallet");
let response = object!{
@ -375,9 +375,10 @@ impl LightClient {
}
pub fn do_seed_phrase(&self) -> JsonValue {
let wallet = self.wallet.read().unwrap();
object!{
"seed" => self.wallet.get_seed_phrase(),
"birthday" => self.wallet.get_birthday()
"seed" => wallet.get_seed_phrase(),
"birthday" => wallet.get_birthday()
}
}
@ -387,69 +388,75 @@ impl LightClient {
let mut spent_notes : Vec<JsonValue> = vec![];
let mut pending_notes: Vec<JsonValue> = vec![];
// Collect Sapling notes
self.wallet.txs.read().unwrap().iter()
.flat_map( |(txid, wtx)| {
wtx.notes.iter().filter_map(move |nd|
if !all_notes && nd.spent.is_some() {
None
{
// Collect Sapling notes
let wallet = self.wallet.read().unwrap();
wallet.txs.read().unwrap().iter()
.flat_map( |(txid, wtx)| {
wtx.notes.iter().filter_map(move |nd|
if !all_notes && nd.spent.is_some() {
None
} else {
Some(object!{
"created_in_block" => wtx.block,
"datetime" => wtx.datetime,
"created_in_txid" => format!("{}", txid),
"value" => nd.note.value,
"is_change" => nd.is_change,
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
"spent" => nd.spent.map(|spent_txid| format!("{}", spent_txid)),
"unconfirmed_spent" => nd.unconfirmed_spent.map(|spent_txid| format!("{}", spent_txid)),
})
}
)
})
.for_each( |note| {
if note["spent"].is_null() && note["unconfirmed_spent"].is_null() {
unspent_notes.push(note);
} else if !note["spent"].is_null() {
spent_notes.push(note);
} else {
Some(object!{
"created_in_block" => wtx.block,
"datetime" => wtx.datetime,
"created_in_txid" => format!("{}", txid),
"value" => nd.note.value,
"is_change" => nd.is_change,
"address" => self.wallet.note_address(nd),
"spent" => nd.spent.map(|spent_txid| format!("{}", spent_txid)),
"unconfirmed_spent" => nd.unconfirmed_spent.map(|spent_txid| format!("{}", spent_txid)),
})
pending_notes.push(note);
}
)
})
.for_each( |note| {
if note["spent"].is_null() && note["unconfirmed_spent"].is_null() {
unspent_notes.push(note);
} else if !note["spent"].is_null() {
spent_notes.push(note);
} else {
pending_notes.push(note);
}
});
});
}
let mut unspent_utxos: Vec<JsonValue> = vec![];
let mut spent_utxos : Vec<JsonValue> = vec![];
let mut pending_utxos: Vec<JsonValue> = vec![];
self.wallet.txs.read().unwrap().iter()
.flat_map( |(txid, wtx)| {
wtx.utxos.iter().filter_map(move |utxo|
if !all_notes && utxo.spent.is_some() {
None
{
let wallet = self.wallet.read().unwrap();
wallet.txs.read().unwrap().iter()
.flat_map( |(txid, wtx)| {
wtx.utxos.iter().filter_map(move |utxo|
if !all_notes && utxo.spent.is_some() {
None
} else {
Some(object!{
"created_in_block" => wtx.block,
"datetime" => wtx.datetime,
"created_in_txid" => format!("{}", txid),
"value" => utxo.value,
"scriptkey" => hex::encode(utxo.script.clone()),
"is_change" => false, // TODO: Identify notes as change if we send change to taddrs
"address" => utxo.address.clone(),
"spent" => utxo.spent.map(|spent_txid| format!("{}", spent_txid)),
"unconfirmed_spent" => utxo.unconfirmed_spent.map(|spent_txid| format!("{}", spent_txid)),
})
}
)
})
.for_each( |utxo| {
if utxo["spent"].is_null() && utxo["unconfirmed_spent"].is_null() {
unspent_utxos.push(utxo);
} else if !utxo["spent"].is_null() {
spent_utxos.push(utxo);
} else {
Some(object!{
"created_in_block" => wtx.block,
"datetime" => wtx.datetime,
"created_in_txid" => format!("{}", txid),
"value" => utxo.value,
"scriptkey" => hex::encode(utxo.script.clone()),
"is_change" => false, // TODO: Identify notes as change if we send change to taddrs
"address" => utxo.address.clone(),
"spent" => utxo.spent.map(|spent_txid| format!("{}", spent_txid)),
"unconfirmed_spent" => utxo.unconfirmed_spent.map(|spent_txid| format!("{}", spent_txid)),
})
pending_utxos.push(utxo);
}
)
})
.for_each( |utxo| {
if utxo["spent"].is_null() && utxo["unconfirmed_spent"].is_null() {
unspent_utxos.push(utxo);
} else if !utxo["spent"].is_null() {
spent_utxos.push(utxo);
} else {
pending_utxos.push(utxo);
}
});
});
}
let mut res = object!{
"unspent_notes" => unspent_notes,
@ -467,8 +474,9 @@ impl LightClient {
}
pub fn do_list_transactions(&self) -> JsonValue {
let wallet = self.wallet.read().unwrap();
// Create a list of TransactionItems
let mut tx_list = self.wallet.txs.read().unwrap().iter()
let mut tx_list = wallet.txs.read().unwrap().iter()
.flat_map(| (_k, v) | {
let mut txns: Vec<JsonValue> = vec![];
@ -512,7 +520,7 @@ impl LightClient {
"datetime" => v.datetime,
"txid" => format!("{}", v.txid),
"amount" => nd.note.value as i64,
"address" => self.wallet.note_address(nd),
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
"memo" => LightWallet::memo_str(&nd.memo),
})
);
@ -547,9 +555,11 @@ impl LightClient {
/// Create a new address, deriving it from the seed.
pub fn do_new_address(&self, addr_type: &str) -> JsonValue {
let wallet = self.wallet.write().unwrap();
let new_address = match addr_type {
"z" => self.wallet.add_zaddr(),
"t" => self.wallet.add_taddr(),
"z" => wallet.add_zaddr(),
"t" => wallet.add_taddr(),
_ => {
let e = format!("Unrecognized address type: {}", addr_type);
error!("{}", e);
@ -565,7 +575,7 @@ impl LightClient {
pub fn do_rescan(&self) -> String {
info!("Rescan starting");
// First, clear the state from the wallet
self.wallet.clear_blocks();
self.wallet.read().unwrap().clear_blocks();
// Then set the initial block
self.set_wallet_initial_state();
@ -583,7 +593,7 @@ impl LightClient {
// 2. Get all the blocks that we don't have
// 3. Find all new Txns that don't have the full Tx, and get them as full transactions
// and scan them, mainly to get the memos
let mut last_scanned_height = self.wallet.last_scanned_height() as u64;
let mut last_scanned_height = self.wallet.read().unwrap().last_scanned_height() as u64;
// This will hold the latest block fetched from the RPC
let latest_block_height = Arc::new(AtomicU64::new(0));
@ -662,7 +672,7 @@ impl LightClient {
Err(_) => {}
}
match local_light_wallet.scan_block(encoded_block) {
match local_light_wallet.read().unwrap().scan_block(encoded_block) {
Ok(block_txns) => {
// Add to global tx list
all_txs.write().unwrap().extend_from_slice(&block_txns.iter().map(|txid| (txid.clone(), height as i32)).collect::<Vec<_>>()[..]);
@ -679,7 +689,7 @@ impl LightClient {
// Check if there was any invalid block, which means we might have to do a reorg
let invalid_height = last_invalid_height.load(Ordering::SeqCst);
if invalid_height > 0 {
total_reorg += self.wallet.invalidate_block(invalid_height);
total_reorg += self.wallet.read().unwrap().invalidate_block(invalid_height);
warn!("Invalidated block at height {}. Total reorg is now {}", invalid_height, total_reorg);
}
@ -705,19 +715,28 @@ impl LightClient {
total_reorg = 0;
// We'll also fetch all the txids that our transparent addresses are involved with
// TODO: Use for all t addresses
let address = self.wallet.address_from_sk(&self.wallet.tkeys.read().unwrap()[0]);
let wallet = self.wallet.clone();
fetch_transparent_txids(&self.get_server_uri(), address, start_height, end_height, self.config.no_cert_verification,
move |tx_bytes: &[u8], height: u64 | {
let tx = Transaction::read(tx_bytes).unwrap();
// Scan this Tx for transparent inputs and outputs
let datetime = block_times.read().unwrap().get(&height).map(|v| *v).unwrap_or(0);
wallet.scan_full_tx(&tx, height as i32, datetime as u64);
{
// Copy over addresses so as to not lock up the wallet, which we'll use inside the callback below.
let addresses = self.wallet.read().unwrap()
.taddresses.read().unwrap().iter().map(|a| a.clone())
.collect::<Vec<String>>();
for address in addresses {
let wallet = self.wallet.clone();
let block_times_inner = block_times.clone();
fetch_transparent_txids(&self.get_server_uri(), address, start_height, end_height, self.config.no_cert_verification,
move |tx_bytes: &[u8], height: u64| {
let tx = Transaction::read(tx_bytes).unwrap();
// Scan this Tx for transparent inputs and outputs
let datetime = block_times_inner.read().unwrap().get(&height).map(|v| *v).unwrap_or(0);
wallet.read().unwrap().scan_full_tx(&tx, height as i32, datetime as u64);
}
);
}
);
}
// Do block height accounting
last_scanned_height = end_height;
end_height = last_scanned_height + 1000;
@ -741,10 +760,10 @@ impl LightClient {
// We need to first copy over the Txids from the wallet struct, because
// we need to free the read lock from here (Because we'll self.wallet.txs later)
let mut txids_to_fetch: Vec<(TxId, i32)> = self.wallet.txs.read().unwrap().values()
.filter(|wtx| wtx.full_tx_scanned == false)
.map(|wtx| (wtx.txid, wtx.block))
.collect::<Vec<(TxId, i32)>>();
let mut txids_to_fetch: Vec<(TxId, i32)> = self.wallet.read().unwrap().txs.read().unwrap().values()
.filter(|wtx| wtx.full_tx_scanned == false)
.map(|wtx| (wtx.txid.clone(), wtx.block))
.collect::<Vec<(TxId, i32)>>();
info!("Fetching {} new txids, total {} with decoy", txids_to_fetch.len(), all_new_txs.read().unwrap().len());
txids_to_fetch.extend_from_slice(&all_new_txs.read().unwrap()[..]);
@ -763,7 +782,7 @@ impl LightClient {
fetch_full_tx(&self.get_server_uri(), txid, self.config.no_cert_verification, move |tx_bytes: &[u8] | {
let tx = Transaction::read(tx_bytes).unwrap();
light_wallet_clone.scan_full_tx(&tx, height, 0);
light_wallet_clone.read().unwrap().scan_full_tx(&tx, height, 0);
});
};
@ -773,7 +792,7 @@ impl LightClient {
pub fn do_send(&self, addrs: Vec<(&str, u64, Option<String>)>) -> String {
info!("Creating transaction");
let rawtx = self.wallet.send_to_address(
let rawtx = self.wallet.write().unwrap().send_to_address(
u32::from_str_radix(&self.config.consensus_branch_id, 16).unwrap(),
&self.sapling_spend, &self.sapling_output,
addrs

100
lib/src/lightwallet.rs

@ -99,10 +99,12 @@ pub struct LightWallet {
extsks: Arc<RwLock<Vec<ExtendedSpendingKey>>>,
extfvks: Arc<RwLock<Vec<ExtendedFullViewingKey>>>,
pub address: Arc<RwLock<Vec<PaymentAddress<Bls12>>>>,
pub zaddress: Arc<RwLock<Vec<PaymentAddress<Bls12>>>>,
// Transparent keys. TODO: Make it not pubic
pub tkeys: Arc<RwLock<Vec<secp256k1::SecretKey>>>,
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
// but the addresses will be present.
tkeys: Arc<RwLock<Vec<secp256k1::SecretKey>>>,
pub taddresses: Arc<RwLock<Vec<String>>>,
blocks: Arc<RwLock<Vec<BlockData>>>,
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
@ -165,8 +167,9 @@ impl LightWallet {
// we need to get the 64 byte bip39 entropy
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&seed_bytes, Language::English).unwrap(), "");
// Derive only the first address
// Derive only the first sk and address
let tpk = LightWallet::get_taddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
let taddr = LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), &tpk);
// TODO: We need to monitor addresses, and always keep 1 "free" address, so
// users can import a seed phrase and automatically get all used addresses
@ -174,16 +177,17 @@ impl LightWallet {
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
Ok(LightWallet {
locked: false,
seed: seed_bytes,
extsks: Arc::new(RwLock::new(vec![extsk])),
extfvks: Arc::new(RwLock::new(vec![extfvk])),
address: Arc::new(RwLock::new(vec![address])),
tkeys: Arc::new(RwLock::new(vec![tpk])),
blocks: Arc::new(RwLock::new(vec![])),
txs: Arc::new(RwLock::new(HashMap::new())),
config: config.clone(),
birthday: latest_block,
locked: false,
seed: seed_bytes,
extsks: Arc::new(RwLock::new(vec![extsk])),
extfvks: Arc::new(RwLock::new(vec![extfvk])),
zaddress: Arc::new(RwLock::new(vec![address])),
tkeys: Arc::new(RwLock::new(vec![tpk])),
taddresses: Arc::new(RwLock::new(vec![taddr])),
blocks: Arc::new(RwLock::new(vec![])),
txs: Arc::new(RwLock::new(HashMap::new())),
config: config.clone(),
birthday: latest_block,
})
}
@ -224,6 +228,14 @@ impl LightWallet {
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
})?;
let taddresses = if version >= 4 {
// Read the addresses
Vector::read(&mut reader, |r| utils::read_string(r))?
} else {
// Calculate the addresses
tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
};
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
let txs_tuples = Vector::read(&mut reader, |r| {
@ -244,15 +256,16 @@ impl LightWallet {
let birthday = reader.read_u64::<LittleEndian>()?;
Ok(LightWallet{
locked: locked,
seed: seed_bytes,
extsks: Arc::new(RwLock::new(extsks)),
extfvks: Arc::new(RwLock::new(extfvks)),
address: Arc::new(RwLock::new(addresses)),
tkeys: Arc::new(RwLock::new(tkeys)),
blocks: Arc::new(RwLock::new(blocks)),
txs: Arc::new(RwLock::new(txs)),
config: config.clone(),
locked: locked,
seed: seed_bytes,
extsks: Arc::new(RwLock::new(extsks)),
extfvks: Arc::new(RwLock::new(extfvks)),
zaddress: Arc::new(RwLock::new(addresses)),
tkeys: Arc::new(RwLock::new(tkeys)),
taddresses: Arc::new(RwLock::new(taddresses)),
blocks: Arc::new(RwLock::new(blocks)),
txs: Arc::new(RwLock::new(txs)),
config: config.clone(),
birthday,
})
}
@ -280,11 +293,16 @@ impl LightWallet {
|w, fvk| fvk.write(w)
)?;
// Write the transparent private key
// Write the transparent private keys
Vector::write(&mut writer, &self.tkeys.read().unwrap(),
|w, pk| w.write_all(&pk[..])
)?;
// Write the transparent addresses
Vector::write(&mut writer, &self.taddresses.read().unwrap(),
|w, a| utils::write_string(w, a)
)?;
Vector::write(&mut writer, &self.blocks.read().unwrap(), |w, b| b.write(w))?;
// The hashmap, write as a set of tuples
@ -302,9 +320,9 @@ impl LightWallet {
Ok(())
}
pub fn note_address(&self, note: &SaplingNoteData) -> Option<String> {
pub fn note_address(hrp: &str, note: &SaplingNoteData) -> Option<String> {
match note.extfvk.fvk.vk.into_payment_address(note.diversifier, &JUBJUB) {
Some(pa) => Some(encode_payment_address(self.config.hrp_sapling_address(), &pa)),
Some(pa) => Some(encode_payment_address(hrp, &pa)),
None => None
}
}
@ -340,7 +358,8 @@ impl LightWallet {
/// Get all t-address private keys. Returns a Vector of (address, secretkey)
pub fn get_t_secret_keys(&self) -> Vec<(String, String)> {
self.tkeys.read().unwrap().iter().map(|sk| {
(self.address_from_sk(sk), sk[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01]))
(self.address_from_sk(sk),
sk[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01]))
}).collect::<Vec<(String, String)>>()
}
@ -355,7 +374,7 @@ impl LightWallet {
let zaddr = encode_payment_address(self.config.hrp_sapling_address(), &address);
self.extsks.write().unwrap().push(extsk);
self.extfvks.write().unwrap().push(extfvk);
self.address.write().unwrap().push(address);
self.zaddress.write().unwrap().push(address);
zaddr
}
@ -365,11 +384,14 @@ impl LightWallet {
/// NOTE: This is not rescan the wallet
pub fn add_taddr(&self) -> String {
let pos = self.tkeys.read().unwrap().len() as u32;
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &self.seed, pos);
let address = self.address_from_sk(&sk);
self.tkeys.write().unwrap().push(sk);
self.taddresses.write().unwrap().push(address.clone());
self.address_from_sk(&sk)
address
}
/// Clears all the downloaded blocks and resets the state back to the initial block.
@ -476,7 +498,7 @@ impl LightWallet {
}
}
pub fn address_from_sk(&self, sk: &secp256k1::SecretKey) -> String {
pub fn address_from_prefix_sk(prefix: &[u8; 2], sk: &secp256k1::SecretKey) -> String {
let secp = secp256k1::Secp256k1::new();
let pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
@ -484,7 +506,11 @@ impl LightWallet {
let mut hash160 = ripemd160::Ripemd160::new();
hash160.input(Sha256::digest(&pk.serialize()[..].to_vec()));
hash160.result().to_base58check(&self.config.base58_pubkey_address(), &[])
hash160.result().to_base58check(prefix, &[])
}
pub fn address_from_sk(&self, sk: &secp256k1::SecretKey) -> String {
LightWallet::address_from_prefix_sk(&self.config.base58_pubkey_address(), sk)
}
pub fn address_from_pubkeyhash(&self, ta: Option<TransparentAddress>) -> Option<String> {
@ -699,8 +725,8 @@ impl LightWallet {
// outgoing metadata
// Collect our t-addresses
let wallet_taddrs = self.tkeys.read().unwrap().iter()
.map(|sk| self.address_from_sk(sk))
let wallet_taddrs = self.taddresses.read().unwrap().iter()
.map(|a| a.clone())
.collect::<HashSet<String>>();
for vout in tx.vout.iter() {
@ -768,7 +794,7 @@ impl LightWallet {
// First, collect all our z addresses, to check for change
// Collect z addresses
let z_addresses = self.address.read().unwrap().iter().map( |ad| {
let z_addresses = self.zaddress.read().unwrap().iter().map( |ad| {
encode_payment_address(self.config.hrp_sapling_address(), &ad)
}).collect::<HashSet<String>>();
@ -1870,7 +1896,7 @@ pub mod tests {
assert_eq!(wallet.extsks.read().unwrap().len(), wallet2.extsks.read().unwrap().len());
assert_eq!(wallet.extsks.read().unwrap()[0], wallet2.extsks.read().unwrap()[0]);
assert_eq!(wallet.extfvks.read().unwrap()[0], wallet2.extfvks.read().unwrap()[0]);
assert_eq!(wallet.address.read().unwrap()[0], wallet2.address.read().unwrap()[0]);
assert_eq!(wallet.zaddress.read().unwrap()[0], wallet2.zaddress.read().unwrap()[0]);
assert_eq!(wallet.tkeys.read().unwrap().len(), wallet2.tkeys.read().unwrap().len());
assert_eq!(wallet.tkeys.read().unwrap()[0], wallet2.tkeys.read().unwrap()[0]);
@ -1939,7 +1965,7 @@ pub mod tests {
assert_eq!(wallet2.tkeys.read().unwrap().len(), 2);
assert_eq!(wallet2.extsks.read().unwrap().len(), 2);
assert_eq!(wallet2.extfvks.read().unwrap().len(), 2);
assert_eq!(wallet2.address.read().unwrap().len(), 2);
assert_eq!(wallet2.zaddress.read().unwrap().len(), 2);
assert_eq!(taddr1, wallet.address_from_sk(&wallet.tkeys.read().unwrap()[0]));
assert_eq!(taddr2, wallet.address_from_sk(&wallet.tkeys.read().unwrap()[1]));
@ -2359,7 +2385,7 @@ pub mod tests {
assert_eq!(txs[&sent_txid].notes[0].extfvk, wallet.extfvks.read().unwrap()[0]);
assert_eq!(txs[&sent_txid].notes[0].note.value, AMOUNT1 - fee);
assert_eq!(wallet.note_address(&txs[&sent_txid].notes[0]), Some(my_address));
assert_eq!(LightWallet::note_address(wallet.config.hrp_sapling_address(), &txs[&sent_txid].notes[0]), Some(my_address));
assert_eq!(LightWallet::memo_str(&txs[&sent_txid].notes[0].memo), Some(memo));
}
}

Loading…
Cancel
Save