From 225db642400ff8ac181bdef8c2a0c2aad5af3983 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Wed, 9 Oct 2019 11:38:19 -0700 Subject: [PATCH] Fetch all txns in a block --- Cargo.toml | 5 ++--- src/grpcconnector.rs | 6 +++--- src/lightclient.rs | 28 +++++++++++++++++++++------- src/lightwallet.rs | 42 ++++++++++++++++++++++++++++-------------- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 80151bd..2a749da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ hex = "0.3" protobuf = "2" rustyline = "5.0.2" byteorder = "1" -rand = "0.5.6" json = "0.12.0" shellwords = "1.0.0" tiny-bip39 = "0.6.2" @@ -37,6 +36,7 @@ webpki = "0.19.1" webpki-roots = "0.16.0" tower-h2 = { git = "https://github.com/tower-rs/tower-h2" } rust-embed = "5.1.0" +rand = "0.7.2" [dependencies.bellman] git = "https://github.com/adityapk00/librustzcash.git" @@ -72,8 +72,7 @@ features = ["ff_derive"] [build-dependencies] tower-grpc-build = { git = "https://github.com/tower-rs/tower-grpc", features = ["tower-hyper"] } -[dev-dependencies] -rand_core = "0.5.1" + [profile.release] debug = false \ No newline at end of file diff --git a/src/grpcconnector.rs b/src/grpcconnector.rs index 8052ad1..66a0f2d 100644 --- a/src/grpcconnector.rs +++ b/src/grpcconnector.rs @@ -171,8 +171,8 @@ pub fn get_info(uri: http::Uri, no_cert: bool) -> Result { } -pub fn fetch_blocks(uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, c: F) - where F : Fn(&[u8]) { +pub fn fetch_blocks(uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, mut c: F) + where F : FnMut(&[u8], u64) { let runner = make_grpc_client!(uri.scheme_str().unwrap(), uri.host().unwrap(), uri.port_part().unwrap(), no_cert) .and_then(move |mut client| { let bs = BlockId{ height: start_height, hash: vec!()}; @@ -191,7 +191,7 @@ pub fn fetch_blocks(uri: &http::Uri, start_heig let mut encoded_buf = vec![]; b.encode(&mut encoded_buf).unwrap(); - c(&encoded_buf); + c(&encoded_buf, b.height); Ok(()) }) diff --git a/src/lightclient.rs b/src/lightclient.rs index c432bfe..0f554e5 100644 --- a/src/lightclient.rs +++ b/src/lightclient.rs @@ -2,7 +2,9 @@ use crate::lightwallet::LightWallet; use log::{info, warn, error}; -use std::sync::{Arc}; +use rand::{rngs::OsRng, seq::SliceRandom}; + +use std::sync::{Arc, RwLock}; use std::sync::atomic::{AtomicU64, AtomicI32, AtomicUsize, Ordering}; use std::path::Path; use std::fs::File; @@ -583,6 +585,11 @@ impl LightClient { let mut total_reorg = 0; + // Collect all txns in blocks that we have a tx in. We'll fetch all these + // txs along with our own, so that the server doesn't learn which ones + // belong to us. + let all_new_txs = Arc::new(RwLock::new(vec![])); + // Fetch CompactBlocks in increments loop { let local_light_wallet = self.wallet.clone(); @@ -599,23 +606,26 @@ impl LightClient { // Fetch compact blocks info!("Fetching blocks {}-{}", start_height, end_height); - + let all_txs = all_new_txs.clone(); + let last_invalid_height = Arc::new(AtomicI32::new(0)); let last_invalid_height_inner = last_invalid_height.clone(); fetch_blocks(&self.get_server_uri(), start_height, end_height, self.config.no_cert_verification, - move |encoded_block: &[u8]| { + move |encoded_block: &[u8], height: u64| { // Process the block only if there were no previous errors if last_invalid_height_inner.load(Ordering::SeqCst) > 0 { return; } match local_light_wallet.scan_block(encoded_block) { - Ok(_) => {}, + Ok(block_txns) => { + all_txs.write().unwrap().copy_from_slice(&block_txns.iter().map(|txid| (txid.clone(), height as i32)).collect::>()[..]); + }, Err(invalid_height) => { // Block at this height seems to be invalid, so invalidate up till that point last_invalid_height_inner.store(invalid_height, Ordering::SeqCst); } - } + }; local_bytes_downloaded.fetch_add(encoded_block.len(), Ordering::SeqCst); }); @@ -683,12 +693,16 @@ 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 txids_to_fetch: Vec<(TxId, i32)> = self.wallet.txs.read().unwrap().values() + 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::>(); - info!("Fetching {} new txids", txids_to_fetch.len()); + info!("Fetching {} new txids, along with {} decoy", txids_to_fetch.len(), all_new_txs.read().unwrap().len()); + txids_to_fetch.extend_from_slice(&all_new_txs.read().unwrap()[..]); + + let mut rng = OsRng; + txids_to_fetch.shuffle(&mut rng); // And go and fetch the txids, getting the full transaction, so we can // read the memos diff --git a/src/lightwallet.rs b/src/lightwallet.rs index e674949..1818dd2 100644 --- a/src/lightwallet.rs +++ b/src/lightwallet.rs @@ -5,6 +5,8 @@ use std::collections::{HashMap, HashSet}; use std::sync::{Arc, RwLock}; use std::io::{Error, ErrorKind}; +use rand::{Rng, rngs::OsRng}; + use log::{info, warn, error}; use protobuf::parse_from_bytes; @@ -165,14 +167,12 @@ impl LightWallet { } pub fn new(seed_phrase: Option, config: &LightClientConfig, latest_block: u64) -> io::Result { - use rand::{FromEntropy, ChaChaRng, Rng}; - // This is the source entropy that corresponds to the 24-word seed phrase let mut seed_bytes = [0u8; 32]; if seed_phrase.is_none() { // Create a random seed. - let mut system_rng = ChaChaRng::from_entropy(); + let mut system_rng = OsRng; system_rng.fill(&mut seed_bytes); } else { seed_bytes.copy_from_slice(&Mnemonic::from_phrase(seed_phrase.expect("should have a seed phrase"), @@ -881,8 +881,8 @@ impl LightWallet { } // Scan a block. Will return an error with the block height that failed to scan - pub fn scan_block(&self, block: &[u8]) -> Result<(), i32> { - let block: CompactBlock = match parse_from_bytes(block) { + pub fn scan_block(&self, block_bytes: &[u8]) -> Result, i32> { + let block: CompactBlock = match parse_from_bytes(block_bytes) { Ok(block) => block, Err(e) => { error!("Could not parse CompactBlock from bytes: {}", e); @@ -900,7 +900,7 @@ impl LightWallet { return Err(height); } } - return Ok(()) + return Ok(vec![]); } else if height != (self.last_scanned_height() + 1) { error!( "Block is not height-sequential (expected {}, found {})", @@ -978,7 +978,7 @@ impl LightWallet { .collect(); scan_block( - block, + block.clone(), &self.extfvks.read().unwrap(), &nf_refs[..], &mut block_data.tree, @@ -986,6 +986,18 @@ impl LightWallet { ) }; + // If this block had any new Txs, return the list of ALL txids in this block, + // so the wallet can fetch them all as a decoy. + let all_txs = if !new_txs.is_empty() { + block.vtx.iter().map(|vtx| { + let mut t = [0u8; 32]; + t.copy_from_slice(&vtx.hash[..]); + TxId{0: t} + }).collect::>() + } else { + vec![] + }; + for tx in new_txs { // Mark notes as spent. let mut total_shielded_value_spent: u64 = 0; @@ -1023,9 +1035,7 @@ impl LightWallet { tx_entry.total_shielded_value_spent = total_shielded_value_spent; // Save notes. - for output in tx - .shielded_outputs - .into_iter() + for output in tx.shielded_outputs { info!("Received sapling output"); @@ -1059,7 +1069,8 @@ impl LightWallet { } } - Ok(()) + + Ok(all_txs) } pub fn send_to_address( @@ -1284,9 +1295,10 @@ impl LightWallet { pub mod tests { use std::convert::TryInto; use std::io::{Error}; + use rand::{RngCore, rngs::OsRng}; + use ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::bls12_381::Bls12; - use rand_core::{RngCore, OsRng}; use protobuf::{Message, UnknownFields, CachedSize, RepeatedField}; use zcash_client_backend::{encoding::encode_payment_address, proto::compact_formats::{ @@ -1938,7 +1950,8 @@ pub mod tests { chain_name: "test".to_string(), sapling_activation_height: 0, consensus_branch_id: "000000".to_string(), - anchor_offset: 0 + anchor_offset: 0, + no_cert_verification: false, } } @@ -2860,7 +2873,8 @@ pub mod tests { chain_name: "main".to_string(), sapling_activation_height: 0, consensus_branch_id: "000000".to_string(), - anchor_offset: 1 + anchor_offset: 1, + no_cert_verification: false, }; let seed_phrase = Some("chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise".to_string());