Compare commits

...

21 Commits

Author SHA1 Message Date
lucretius de6915540c take custom fee not default 5 months ago
lucretius 4f53e65a11 take only fee 5 months ago
lucretius f36053c380 fix fee amount 5 months ago
lucretius 4d5c2e8dc4 add fee to command 5 months ago
lucretius b1c8dfda4b add debug for fee 5 months ago
lucretius 452f2c2b4d add custom fee 5 months ago
lucretius 4ba8b962f2 Merge pull request 'pull hush/dev' (#6) from hush/silentdragonlite-cli:dev into dev 5 months ago
duke 1334c89e0f Merge pull request 'Flag Error 60: better handling of errors, forgotten check for wallettx version' (#27) from lucretius/silentdragonlite-cli:mem into dev 5 months ago
lucretius d8994eec8e less debug 5 months ago
lucretius b3e4162f8a add error warning 5 months ago
lucretius e4658582cb check for version 5 5 months ago
lucretius 8076ee1426 add check for wallet version 5 months ago
lucretius f97cd7d041 Merge pull request 'pull from hush/dev' (#5) from hush/silentdragonlite-cli:dev into dev 5 months ago
duke 86592f64b7 Merge pull request 'Proton files update' (#26) from proto into dev 5 months ago
Deniod 1f184f55d4 less debug 5 months ago
Deniod a307f828c9 less debug, handle error if channel is closed 5 months ago
Deniod 60550982e0 less debug 5 months ago
Deniod c8948af2fc prevent unwrap in get_block_range 5 months ago
Deniod feb26dbdfb prevent unwrap in scan_taddress_txids 5 months ago
Deniod 35ce90eec9 prevent unwrap(), poisoned data 7 months ago
lucretius df65ef8a51 Merge pull request 'Merge upstream dev' (#1) from hush/silentdragonlite-cli:dev into dev 7 months ago
  1. 18
      Cargo.lock
  2. 24
      lib/Cargo.toml
  3. 119
      lib/src/commands.rs
  4. 130
      lib/src/grpcconnector.rs
  5. 99
      lib/src/lightclient.rs
  6. 99
      lib/src/lightwallet.rs
  7. 21
      lib/src/lightwallet/data.rs

18
Cargo.lock

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler32"
version = "1.0.4"
@ -186,7 +188,7 @@ checksum = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2"
[[package]]
name = "bellman"
version = "0.1.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"bit-vec",
"blake2s_simd",
@ -510,7 +512,7 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "ff"
version = "0.4.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"byteorder",
"ff_derive",
@ -520,7 +522,7 @@ dependencies = [
[[package]]
name = "ff_derive"
version = "0.3.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"num-bigint",
"num-integer",
@ -678,7 +680,7 @@ dependencies = [
[[package]]
name = "group"
version = "0.1.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"ff",
"rand 0.7.2",
@ -1158,7 +1160,7 @@ dependencies = [
[[package]]
name = "pairing"
version = "0.14.2"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"byteorder",
"ff",
@ -2830,7 +2832,7 @@ dependencies = [
[[package]]
name = "zcash_client_backend"
version = "0.0.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"bech32",
"bs58",
@ -2846,7 +2848,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.0.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"aes",
"blake2b_simd",
@ -2869,7 +2871,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.0.0"
source = "git+https://git.hush.is/hush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37#1a0204113d487cdaaf183c2967010e5214ff9e37"
source = "git+https://git.hush.is/lucretius/librustzcash.git?rev=368b59f9376d99f9368af8bca5438a45292f62c1#368b59f9376d99f9368af8bca5438a45292f62c1"
dependencies = [
"bellman",
"blake2b_simd",

24
lib/Cargo.toml

@ -42,35 +42,35 @@ webpki = "0.21.0"
webpki-roots = "0.18.0"
[dependencies.bellman]
git = "https://git.hush.is/hush/librustzcash.git"
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
git = "https://git.hush.is/lucretius/librustzcash.git"
rev= "368b59f9376d99f9368af8bca5438a45292f62c1"
default-features = false
features = ["groth16"]
[dependencies.pairing]
git = "https://git.hush.is/hush/librustzcash.git"
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
git = "https://git.hush.is/lucretius/librustzcash.git"
rev= "368b59f9376d99f9368af8bca5438a45292f62c1"
[dependencies.zcash_client_backend]
git = "https://git.hush.is/hush/librustzcash.git"
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
git = "https://git.hush.is/lucretius/librustzcash.git"
rev= "368b59f9376d99f9368af8bca5438a45292f62c1"
default-features = false
[dependencies.zcash_primitives]
git = "https://git.hush.is/hush/librustzcash.git"
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
git = "https://git.hush.is/lucretius/librustzcash.git"
rev= "368b59f9376d99f9368af8bca5438a45292f62c1"
default-features = false
features = ["transparent-inputs"]
[dependencies.zcash_proofs]
git = "https://git.hush.is/hush/librustzcash.git"
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
git = "https://git.hush.is/lucretius/librustzcash.git"
rev= "368b59f9376d99f9368af8bca5438a45292f62c1"
default-features = false
[dependencies.ff]
git = "https://git.hush.is/hush/librustzcash.git"
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
git = "https://git.hush.is/lucretius/librustzcash.git"
rev= "368b59f9376d99f9368af8bca5438a45292f62c1"
features = ["ff_derive"]
[build-dependencies]

119
lib/src/commands.rs

@ -5,7 +5,8 @@ use json::{object};
use crate::lightclient::LightClient;
use crate::lightwallet::LightWallet;
use zcash_primitives::transaction::components::amount::DEFAULT_FEE;
use std::convert::TryInto;
pub trait Command {
fn help(&self) -> String;
@ -499,59 +500,89 @@ impl Command for SendCommand {
let send_args = if args.len() == 1 {
let arg_list = args[0];
let json_args = match json::parse(&arg_list) {
Ok(j) => j,
Err(e) => {
let es = format!("Couldn't understand JSON: {}", e);
return format!("{}\n{}", es, self.help());
}
};
if !json_args.is_array() {
return format!("Couldn't parse argument as array\n{}", self.help());
}
let maybe_send_args = json_args.members().map( |j| {
if !j.has_key("address") || !j.has_key("amount") {
Err(format!("Need 'address' and 'amount'\n"))
} else {
Ok((j["address"].as_str().unwrap().to_string().clone(), j["amount"].as_u64().unwrap(), j["memo"].as_str().map(|s| s.to_string().clone())))
}
}).collect::<Result<Vec<(String, u64, Option<String>)>, String>>();
match maybe_send_args {
Ok(a) => a.clone(),
Err(s) => { return format!("Error: {}\n{}", s, self.help()); }
match json::parse(&arg_list) {
Ok(json_args) => {
// Check if the parsed JSON is an array.
if !json_args.is_array() {
return format!("Couldn't parse argument as array\n{}", self.help());
}
// Map each JSON object to a tuple (address, amount, memo, fee).
let maybe_send_args = json_args.members().map(|j| {
if !j.has_key("address") || !j.has_key("amount") {
Err(format!("Need 'address' and 'amount'\n"))
} else {
let fee = j["fee"].as_u64().unwrap_or(DEFAULT_FEE.try_into().unwrap());
if j.has_key("fee") {
println!("Using custom fee: {:?}", fee);
} else {
println!("Using default fee");
}
Ok((
j["address"].as_str().unwrap().to_string(),
j["amount"].as_u64().unwrap(),
j["memo"].as_str().map(|s| s.to_string()),
fee
))
}
}).collect::<Result<Vec<(String, u64, Option<String>, u64)>, String>>();
// Handle any errors that occurred during mapping.
match maybe_send_args {
Ok(a) => a,
Err(s) => return format!("Error: {}\n{}", s, self.help()),
}
},
Err(e) => return format!("Couldn't understand JSON: {}\n{}", e, self.help()),
}
} else if args.len() == 2 || args.len() == 3 {
} else {
// Handle the case where individual arguments are provided.
let address = args[0].to_string();
// Make sure we can parse the amount
let value = match args[1].parse::<u64>() {
Ok(amt) => amt,
Err(e) => {
return format!("Couldn't parse amount: {}", e);
}
Err(e) => return format!("Couldn't parse amount: {}", e),
};
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
let memo = args.get(2).map(|m| m.to_string());
// Memo should be None if the address is not shielded.
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 {
return self.help()
// Create a vector with a single transaction (address, amount, memo).
vec![(address, value, memo, DEFAULT_FEE.try_into().unwrap())]
};
// Transform transaction data into the required format (String -> &str).
let tos = send_args.iter().map(|(a, v, m, _)| (a.as_str(), *v, m.clone())).collect::<Vec<_>>();
// Calculate the total fee for all transactions.
// This assumes that all transactions have the same fee.
// If they can have different fees, you need to modify this logic.
let default_fee: u64 = DEFAULT_FEE.try_into().unwrap();
let mut fee_found = false;
let mut total_fee = default_fee;
for (_, _, _, fee) in send_args.iter() {
if *fee != default_fee{
total_fee = *fee;
fee_found = true;
break;
}
}
// Convert to the right format. String -> &str.
let tos = send_args.iter().map(|(a, v, m)| (a.as_str(), *v, m.clone()) ).collect::<Vec<_>>();
match lightclient.do_send(tos) {
Ok(txid) => { object!{ "txid" => txid } },
Err(e) => { object!{ "error" => e } }
}.pretty(2)
if fee_found {
println!("Using custom fee: {:?}", total_fee);
} else {
println!("Using default fee: ");
}
// Execute the transaction and handle the result.
match lightclient.do_send(tos, &total_fee) {
Ok(txid) => object!{ "txid" => txid }.pretty(2),
Err(e) => object!{ "error" => e }.pretty(2),
}
}
}

130
lib/src/grpcconnector.rs

@ -97,52 +97,64 @@ pub fn get_coinsupply(uri: http::Uri, no_cert: bool) -> Result<Coinsupply, Strin
let mut rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?;
rt.block_on(get_coinsupply_info(&uri, no_cert)).map_err( |e| e.to_string())
// tokio::runtime::current_thread::Runtime::new().unwrap().block_on(runner)
}
async fn get_block_range<F : 'static + std::marker::Send>(uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, pool: ThreadPool, c: F)
-> Result<(), Box<dyn std::error::Error>>
async fn get_block_range<F : 'static + std::marker::Send>(
uri: &http::Uri,
start_height: u64,
end_height: u64,
no_cert: bool,
pool: ThreadPool,
c: F
) -> Result<(), Box<dyn std::error::Error>>
where F : Fn(&[u8], u64) {
let mut client = get_client(uri, no_cert).await?;
let bs = BlockId{ height: start_height, hash: vec!()};
let be = BlockId{ height: end_height, hash: vec!()};
let bs = BlockId { height: start_height, hash: vec![] };
let be = BlockId { height: end_height, hash: vec![] };
let request = Request::new(BlockRange{ start: Some(bs), end: Some(be) });
let request = Request::new(BlockRange { start: Some(bs), end: Some(be) });
// Channel where the blocks are sent. A None signifies end of all blocks
let (tx, rx) = channel::<Option<CompactBlock>>();
// Channel that the processor signals it is done, so the method can return
let (ftx, frx) = channel();
// The processor runs on a different thread, so that the network calls don't
// block on this
pool.execute(move || {
while let Some(block) = rx.recv().unwrap() {
use prost::Message;
let mut encoded_buf = vec![];
block.encode(&mut encoded_buf).unwrap();
c(&encoded_buf, block.height);
}
ftx.send(Ok(())).unwrap();
});
let (tx, rx) = channel::<Option<CompactBlock>>();
let (ftx, frx) = channel();
pool.execute(move || {
while let Ok(Some(block)) = rx.recv() {
use prost::Message;
let mut encoded_buf = vec![];
if let Err(e) = block.encode(&mut encoded_buf) {
eprintln!("Error encoding block: {:?}", e);
break;
}
c(&encoded_buf, block.height);
}
if let Err(e) = ftx.send(Ok(())) {
eprintln!("Error sending completion signal: {:?}", e);
}
});
let mut response = client.get_block_range(request).await?.into_inner();
//println!("{:?}", response);
while let Some(block) = response.message().await? {
tx.send(Some(block)).unwrap();
if let Err(e) = tx.send(Some(block)) {
eprintln!("Error sending block to channel: {:?}", e);
break;
}
}
if let Err(e) = tx.send(None) {
eprintln!("Error sending end signal to channel: {:?}", e);
}
tx.send(None).unwrap();
// Wait for the processor to exit
frx.iter().take(1).collect::<Result<Vec<()>, String>>()?;
Ok(())
}
pub fn fetch_blocks<F : 'static + std::marker::Send>(uri: &http::Uri, start_height: u64, end_height: u64, no_cert: bool, pool: ThreadPool, c: F) -> Result<(), String>
where F : Fn(&[u8], u64) {
@ -151,7 +163,6 @@ pub fn fetch_blocks<F : 'static + std::marker::Send>(uri: &http::Uri, start_heig
Err(e) => {
let es = format!("Error creating runtime {:?}", e);
error!("{}", es);
eprintln!("{}", e);
return Err(es);
}
};
@ -161,31 +172,49 @@ pub fn fetch_blocks<F : 'static + std::marker::Send>(uri: &http::Uri, start_heig
Err(e) => {
let e = format!("Error fetching blocks {:?}", e);
error!("{}", e);
eprintln!("{}", e);
Err(e)
}
}
}
// get_address_txids GRPC call
async fn get_address_txids<F : 'static + std::marker::Send>(uri: &http::Uri, address: String,
start_height: u64, end_height: u64, no_cert: bool, c: F) -> Result<(), Box<dyn std::error::Error>>
where F : Fn(&[u8], u64) {
async fn get_address_txids<F : 'static + std::marker::Send>(
uri: &http::Uri,
address: String,
start_height: u64,
end_height: u64,
no_cert: bool,
c: F
) -> Result<(), Box<dyn std::error::Error>>
where F : Fn(&[u8], u64) {
let mut client = match get_client(uri, no_cert).await {
Ok(client) => client,
Err(e) => {
eprintln!("Error creating client: {:?}", e);
return Err(e.into());
}
};
let mut client = get_client(uri, no_cert).await?;
let start = Some(BlockId{ height: start_height, hash: vec!()});
let end = Some(BlockId{ height: end_height, hash: vec!()});
let end = Some(BlockId{ height: end_height, hash: vec!()});
let request = Request::new(TransparentAddressBlockFilter{ address, range: Some(BlockRange{start, end}) });
let request = Request::new(TransparentAddressBlockFilter{ address, range: Some(BlockRange{ start, end }) });
let maybe_response = match client.get_address_txids(request).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error getting address txids: {:?}", e);
return Err(e.into());
}
};
let maybe_response = client.get_address_txids(request).await?;
let mut response = maybe_response.into_inner();
while let Some(tx) = response.message().await? {
c(&tx.data, tx.height);
}
Ok(())
}
@ -223,16 +252,21 @@ where
Ok(())
}
pub fn fetch_transparent_txids<F : 'static + std::marker::Send>(uri: &http::Uri, address: String,
start_height: u64, end_height: u64, no_cert: bool, c: F) -> Result<(), String>
where F : Fn(&[u8], u64) {
pub fn fetch_transparent_txids<F : 'static + std::marker::Send>(
uri: &http::Uri,
address: String,
start_height: u64,
end_height: u64,
no_cert: bool,
c: F
) -> Result<(), String>
where F : Fn(&[u8], u64) {
let mut rt = match tokio::runtime::Runtime::new() {
Ok(r) => r,
Err(e) => {
let e = format!("Error creating runtime {:?}", e);
error!("{}", e);
eprintln!("{}", e);
return Err(e);
}
};
@ -242,13 +276,11 @@ pub fn fetch_transparent_txids<F : 'static + std::marker::Send>(uri: &http::Uri,
Err(e) => {
let e = format!("Error with get_address_txids runtime {:?}", e);
error!("{}", e);
eprintln!("{}", e);
Err(e)
return Err(e)
}
}
}
// get_transaction GRPC call
async fn get_transaction(uri: &http::Uri, txid: TxId, no_cert: bool)
-> Result<RawTransaction, Box<dyn std::error::Error>> {
@ -266,7 +298,6 @@ pub fn fetch_full_tx(uri: &http::Uri, txid: TxId, no_cert: bool) -> Result<Vec<u
Err(e) => {
let errstr = format!("Error creating runtime {}", e.to_string());
error!("{}", errstr);
eprintln!("{}", errstr);
return Err(errstr);
}
};
@ -276,7 +307,6 @@ pub fn fetch_full_tx(uri: &http::Uri, txid: TxId, no_cert: bool) -> Result<Vec<u
Err(e) => {
let errstr = format!("Error in get_transaction runtime {}", e.to_string());
error!("{}", errstr);
eprintln!("{}", errstr);
Err(errstr)
}
}

99
lib/src/lightclient.rs

@ -1233,7 +1233,7 @@ pub fn start_mempool_monitor(lc: Arc<LightClient>) -> Result<(), String> {
}
// Sleep exponentially backing off
std::thread::sleep(std::time::Duration::from_secs((2 as u64).pow(retry_count)));
println!("Sync error {}\nRetry count {}", e, retry_count);
// println!("Sync error {}\nRetry count {}", e, retry_count);
}
}
}
@ -1252,6 +1252,7 @@ pub fn start_mempool_monitor(lc: Arc<LightClient>) -> Result<(), String> {
}
let addr = address.or(self.wallet.read().unwrap().get_all_zaddresses().get(0).map(|s| s.clone())).unwrap();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let result = {
let _lock = self.sync_lock.lock().unwrap();
@ -1260,6 +1261,7 @@ pub fn start_mempool_monitor(lc: Arc<LightClient>) -> Result<(), String> {
&self.sapling_spend, &self.sapling_output,
true,
vec![(&addr, tbal - fee, None)],
&fee,
|txbytes| broadcast_raw_tx(&self.get_server_uri(),self.config.no_cert_verification, txbytes)
)
};
@ -1477,47 +1479,83 @@ pub fn start_mempool_monitor(lc: Arc<LightClient>) -> Result<(), String> {
}
}
}
fn scan_taddress_txids(&self, pool: &ThreadPool, block_times: Arc<RwLock<HashMap<u64, u32>>>, start_height: u64, end_height: u64, no_cert: bool) -> Result<Vec<()>, String> {
// 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()
fn scan_taddress_txids(
&self,
pool: &ThreadPool,
block_times: Arc<RwLock<HashMap<u64, u32>>>,
start_height: u64,
end_height: u64,
no_cert: bool
) -> Result<Vec<()>, String> {
let addresses = self.wallet.read()
.map_err(|e| format!("Failed to read wallet: {:?}", e))?
.get_all_taddresses().iter()
.map(|a| a.clone())
.cloned()
.collect::<Vec<String>>();
// Create a channel so the fetch_transparent_txids can send the results back
let (ctx, crx) = channel();
let num_addresses = addresses.len();
for address in addresses {
let address_clone = address.clone();
let wallet = self.wallet.clone();
let pool = pool.clone();
let server_uri = self.get_server_uri();
let ctx = ctx.clone();
let block_times = block_times.clone();
pool.execute(move || {
// Fetch the transparent transactions for this address, and send the results
// via the channel
let r = fetch_transparent_txids(&server_uri, address, start_height, end_height,no_cert,
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.read().unwrap().scan_full_tx(&tx, height as i32, datetime as u64);
});
ctx.send(r).unwrap();
let r = fetch_transparent_txids(
&server_uri,
address,
start_height,
end_height,
no_cert,
move |tx_bytes: &[u8], height: u64| {
let tx_result = Transaction::read(tx_bytes)
.map_err(|e| format!("Failed to read transaction: {:?}", e));
match tx_result {
Ok(tx) => {
let datetime_result = block_times.read()
.map_err(|e| format!("Failed to read block times: {:?}", e))
.and_then(|bt| bt.get(&height).cloned().ok_or_else(|| format!("No datetime for height: {}", height)));
match datetime_result {
Ok(datetime) => {
match wallet.read().map_err(|e| format!("Failed to read wallet: {:?}", e)) {
Ok(w) => {
w.scan_full_tx(&tx, height as i32, datetime as u64);
},
Err(e) => {
println!("Error reading wallet: {}", e);
},
}
},
Err(e) => {
println!("Error processing transaction: {}", e);
},
}
},
Err(e) => {
println!("Error reading transaction: {}", e);
},
}
}
);
match ctx.send(r) {
Ok(_) => info!("Successfully sent data for address: {}", address_clone),
Err(e) => println!("Failed to send data for address: {}: {:?}", address_clone, e),
}
});
}
// Collect all results from the transparent fetches, and make sure everything was OK.
// If it was not, we return an error, which will go back to the retry
crx.iter().take(num_addresses).collect::<Result<Vec<()>, String>>()
}
crx.iter().take(num_addresses).collect()
}
fn scan_fill_fulltxs(&self, pool: &ThreadPool, decoy_txids: Vec<(TxId, i32)>) -> Result<Vec<()>, String> {
// We need to first copy over the Txids from the wallet struct, because
@ -1564,7 +1602,7 @@ pub fn start_mempool_monitor(lc: Arc<LightClient>) -> Result<(), String> {
crx.iter().take(num_fetches).collect::<Result<Vec<()>, String>>()
}
pub fn do_send(&self, addrs: Vec<(&str, u64, Option<String>)>) -> Result<String, String> {
pub fn do_send(&self, addrs: Vec<(&str, u64, Option<String>)>, fee: &u64) -> Result<String, String> {
if !self.wallet.read().unwrap().is_unlocked_for_spending() {
error!("Wallet is locked");
return Err("Wallet is locked".to_string());
@ -1580,6 +1618,7 @@ pub fn start_mempool_monitor(lc: Arc<LightClient>) -> Result<(), String> {
&self.sapling_spend, &self.sapling_output,
false,
addrs,
fee,
|txbytes| broadcast_raw_tx(&self.get_server_uri(), self.config.no_cert_verification, txbytes)
)
};

99
lib/src/lightwallet.rs

@ -39,7 +39,7 @@ use zcash_primitives::{
serialize::{Vector},
transaction::{
builder::{Builder},
components::{Amount, OutPoint, TxOut}, components::amount::DEFAULT_FEE,
components::{Amount, OutPoint, TxOut},
TxId, Transaction,
},
sapling::Node,
@ -1470,26 +1470,43 @@ pub fn scan_full_tx(&self, tx: &Transaction, height: i32, datetime: u64) {
// Do it in a short scope because of the write lock.
{
info!("A sapling output was sent in {}", tx.txid());
let mut txs = self.txs.write().unwrap();
if txs.get(&tx.txid()).unwrap().outgoing_metadata.iter()
.find(|om| om.address == address && om.value == note.value && om.memo == memo)
.is_some() {
warn!("Duplicate outgoing metadata");
continue;
}
match self.txs.write() {
Ok(mut txs) => {
match txs.get(&tx.txid()) {
Some(wtx) => {
if wtx.outgoing_metadata.iter()
.any(|om| om.address == address && om.value == note.value && om.memo == memo)
{
warn!("Duplicate outgoing metadata");
continue;
}
// Write the outgoing metadata
txs.get_mut(&tx.txid()).unwrap()
.outgoing_metadata
.push(OutgoingTxMetadata{
address, value: note.value, memo,
});
// Write the outgoing metadata
txs.get_mut(&tx.txid()).unwrap()
.outgoing_metadata
.push(OutgoingTxMetadata {
address,
value: note.value,
memo,
});
},
None => {
error!("Can not find any entry for txid : {}", tx.txid());
continue;
}
}
},
Err(poisoned) => {
error!("Lock is poisoned: {}", poisoned);
return;
}
}
}
},
None => {}
};
}
}
}
// Mark this Tx as scanned
{
let mut txs = self.txs.write().unwrap();
@ -2177,6 +2194,7 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64
output_params: &[u8],
_transparent_only: bool,
tos: Vec<(&str, u64, Option<String>)>,
fee: &u64,
broadcast_fn: F
) -> Result<(String, Vec<u8>), String>
where F: Fn(Box<[u8]>) -> Result<String, String>
@ -2207,24 +2225,32 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64
total_value, tos.len()
);
// Convert address (str) to RecepientAddress and value to Amount
let recepients = tos.iter().map(|to| {
let ra = match address::RecipientAddress::from_str(to.0,
self.config.hrp_sapling_address(),
self.config.base58_pubkey_address(),
self.config.base58_script_address()) {
Some(to) => to,
None => {
let e = format!("Invalid recipient address: '{}'", to.0);
error!("{}", e);
return Err(e);
}
};
// Convert address (str) to RecipientAddress and value to Amount
let recepients: Result<Vec<(address::RecipientAddress, Amount, Option<String>)>, String> = tos.iter().map(|to| {
// Convert string to RecipientAddress
let ra = match address::RecipientAddress::from_str(
to.0,
self.config.hrp_sapling_address(),
self.config.base58_pubkey_address(),
self.config.base58_script_address()
) {
Some(addr) => addr,
None => {
let e = format!("Invalid recipient address: '{}'", to.0);
error!("{}", e);
return Err(e);
}
};
let value = Amount::from_u64(to.1).unwrap();
// Convert the second tuple element to Amount
let value = Amount::from_u64(to.1).expect("Invalid amount value");
Ok((ra, value, to.2.clone()))
}).collect();
let recepients = recepients?;
Ok((ra, value, to.2.clone()))
}).collect::<Result<Vec<(address::RecipientAddress, Amount, Option<String>)>, String>>()?;
// Target the next block, assuming we are up-to-date.
let (height, anchor_offset) = match self.get_target_height_and_anchor_offset() {
@ -2236,9 +2262,10 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64
}
};
// Select notes to cover the target value
println!("{}: Selecting notes", now() - start_time);
let target_value = Amount::from_u64(total_value).unwrap() + DEFAULT_FEE ;
// Select notes to cover the target value
println!("{}: Selecting notes", now() - start_time);
let target_value = Amount::from_u64(total_value).unwrap() + Amount::from_u64(*fee).unwrap();
// Select the candidate notes that are eligible to be spent
let notes: Vec<_> = self.txs.read().unwrap().iter()
.map(|(txid, tx)| tx.notes.iter().map(move |note| (*txid, note)))
@ -2323,8 +2350,10 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64
return Err(e);
}
let fee_amount = Amount::from_u64(*fee).expect("Invalid fee amount");
builder.set_fee(fee_amount);
// Create the transaction
println!("{}: Adding {} notes and {} utxos", now() - start_time, notes.len(), tinputs.len());
println!("{}: Adding {} notes and {} utxos and fee {:?}", now() - start_time, notes.len(), tinputs.len(), fee_amount);
for selected in notes.iter() {
if let Err(e) = builder.add_sapling_spend(

21
lib/src/lightwallet/data.rs

@ -440,7 +440,7 @@ pub struct WalletTx {
impl WalletTx {
pub fn serialized_version() -> u64 {
return 4;
return 5;
}
pub fn new(height: i32, datetime: u64, txid: &TxId) -> Self {
@ -460,10 +460,9 @@ impl WalletTx {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let version = reader.read_u64::<LittleEndian>()?;
assert!(version <= WalletTx::serialized_version());
assert!(version <= WalletTx::serialized_version(), "Version mismatch. Please restore with your Seed");
let block = reader.read_i32::<LittleEndian>()?;
let datetime = if version >= 4 {
reader.read_u64::<LittleEndian>()?
} else {
@ -472,23 +471,24 @@ impl WalletTx {
let mut txid_bytes = [0u8; 32];
reader.read_exact(&mut txid_bytes)?;
let txid = TxId{0: txid_bytes};
let notes = Vector::read(&mut reader, |r| SaplingNoteData::read(r))?;
let utxos = Vector::read(&mut reader, |r| Utxo::read(r))?;
let total_shielded_value_spent = reader.read_u64::<LittleEndian>()?;
let total_transparent_value_spent = reader.read_u64::<LittleEndian>()?;
// Outgoing metadata was only added in version 2
let outgoing_metadata = Vector::read(&mut reader, |r| OutgoingTxMetadata::read(r))?;
let incoming_metadata = Vector::read(&mut reader, |r| IncomingTxMetadata::read(r))?;
// Read incoming_metadata only if version is 5 or higher
let incoming_metadata = if version >= 5 {
Vector::read(&mut reader, |r| IncomingTxMetadata::read(r))?
} else {
vec![]
};
let full_tx_scanned = reader.read_u8()? > 0;
Ok(WalletTx{
Ok(WalletTx {
block,
datetime,
txid,
@ -502,6 +502,7 @@ impl WalletTx {
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_u64::<LittleEndian>(WalletTx::serialized_version())?;

Loading…
Cancel
Save