add custom fee
This commit is contained in:
18
Cargo.lock
generated
18
Cargo.lock
generated
@@ -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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
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/hush/librustzcash.git?rev=acff1444ec373e9c3e37b47ca95bfd358e45255b#acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
dependencies = [
|
||||
"bellman",
|
||||
"blake2b_simd",
|
||||
|
||||
@@ -43,34 +43,34 @@ webpki-roots = "0.18.0"
|
||||
|
||||
[dependencies.bellman]
|
||||
git = "https://git.hush.is/hush/librustzcash.git"
|
||||
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
|
||||
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
default-features = false
|
||||
features = ["groth16"]
|
||||
|
||||
[dependencies.pairing]
|
||||
git = "https://git.hush.is/hush/librustzcash.git"
|
||||
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
|
||||
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
|
||||
[dependencies.zcash_client_backend]
|
||||
git = "https://git.hush.is/hush/librustzcash.git"
|
||||
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
|
||||
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
|
||||
default-features = false
|
||||
|
||||
[dependencies.zcash_primitives]
|
||||
git = "https://git.hush.is/hush/librustzcash.git"
|
||||
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
|
||||
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
default-features = false
|
||||
features = ["transparent-inputs"]
|
||||
|
||||
[dependencies.zcash_proofs]
|
||||
git = "https://git.hush.is/hush/librustzcash.git"
|
||||
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
|
||||
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
default-features = false
|
||||
|
||||
[dependencies.ff]
|
||||
git = "https://git.hush.is/hush/librustzcash.git"
|
||||
rev= "1a0204113d487cdaaf183c2967010e5214ff9e37"
|
||||
rev= "acff1444ec373e9c3e37b47ca95bfd358e45255b"
|
||||
features = ["ff_derive"]
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -5,6 +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;
|
||||
@@ -489,69 +491,82 @@ impl Command for SendCommand {
|
||||
// Parse the args. There are two argument types.
|
||||
// 1 - A set of 2(+1 optional) arguments for a single address send representing address, value, memo?
|
||||
// 2 - A single argument in the form of a JSON string that is "[{address: address, value: value, memo: memo},...]"
|
||||
|
||||
// 1 - Destination address. T or Z address
|
||||
if args.len() < 1 || args.len() > 3 {
|
||||
return self.help();
|
||||
}
|
||||
|
||||
// Check for a single argument that can be parsed as JSON
|
||||
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());
|
||||
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());
|
||||
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()),
|
||||
}
|
||||
|
||||
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()); }
|
||||
}
|
||||
} 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 total_fee = default_fee;
|
||||
|
||||
// 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)
|
||||
for (_, _, _, fee) in send_args.iter() {
|
||||
if *fee != default_fee{
|
||||
total_fee = *fee;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
};
|
||||
@@ -1600,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());
|
||||
@@ -1616,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)
|
||||
)
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
@@ -2194,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>
|
||||
@@ -2224,24 +2225,31 @@ 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 value = Amount::from_u64(to.1).unwrap();
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
Ok((ra, value, to.2.clone()))
|
||||
}).collect::<Result<Vec<(address::RecipientAddress, Amount, Option<String>)>, String>>()?;
|
||||
// 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?;
|
||||
|
||||
// Target the next block, assuming we are up-to-date.
|
||||
let (height, anchor_offset) = match self.get_target_height_and_anchor_offset() {
|
||||
@@ -2254,8 +2262,9 @@ 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)))
|
||||
@@ -2340,8 +2349,11 @@ 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(
|
||||
|
||||
Reference in New Issue
Block a user