Sender pays the fee. Fix #274 (#694)

This commit is contained in:
Quentin Le Sceller 2018-02-09 00:33:24 -05:00 committed by Ignotus Peverell
parent 623bdb696b
commit 06bd78faed
2 changed files with 57 additions and 22 deletions

View file

@ -121,7 +121,7 @@ fn basic_wallet_transactions() {
let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed); let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed);
println!("Recipient wallet info: {:?}", recipient_info); println!("Recipient wallet info: {:?}", recipient_info);
assert!(recipient_info.data_confirmed && recipient_info.amount_currently_spendable==49992000000); assert!(recipient_info.data_confirmed && recipient_info.amount_currently_spendable==50000000000);
warn!(LOGGER, "Sending many small transactions to recipient wallet"); warn!(LOGGER, "Sending many small transactions to recipient wallet");
for _ in 0..10 { for _ in 0..10 {
@ -132,7 +132,7 @@ fn basic_wallet_transactions() {
let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed); let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed);
println!("Recipient wallet info post little sends: {:?}", recipient_info); println!("Recipient wallet info post little sends: {:?}", recipient_info);
assert!(recipient_info.data_confirmed && recipient_info.amount_currently_spendable==59912000000); assert!(recipient_info.data_confirmed && recipient_info.amount_currently_spendable==60000000000);
//send some cash right back //send some cash right back
LocalServerContainer::send_amount_to(&recp_wallet_config, "25.00", 1, "all", "http://127.0.0.1:10002"); LocalServerContainer::send_amount_to(&recp_wallet_config, "25.00", 1, "all", "http://127.0.0.1:10002");

View file

@ -45,7 +45,7 @@ pub fn issue_send_tx(
// proof of concept - set lock_height on the tx // proof of concept - set lock_height on the tx
let lock_height = chain_tip.height; let lock_height = chain_tip.height;
let (tx, blind_sum, coins, change_key) = build_send_tx( let (tx, blind_sum, coins, change_key, amount_with_fee) = build_send_tx(
config, config,
keychain, keychain,
amount, amount,
@ -64,7 +64,7 @@ pub fn issue_send_tx(
// Create a new aggsig context // Create a new aggsig context
let tx_id = Uuid::new_v4(); let tx_id = Uuid::new_v4();
let _ = keychain.aggsig_create_context(&tx_id, blind_sum.secret_key()); let _ = keychain.aggsig_create_context(&tx_id, blind_sum.secret_key());
let partial_tx = build_partial_tx(&tx_id, keychain, amount, None, tx); let partial_tx = build_partial_tx(&tx_id, keychain, amount_with_fee, None, tx);
// Closure to acquire wallet lock and lock the coins being spent // Closure to acquire wallet lock and lock the coins being spent
// so we avoid accidental double spend attempt. // so we avoid accidental double spend attempt.
@ -127,7 +127,7 @@ pub fn issue_send_tx(
let sig_part=keychain.aggsig_calculate_partial_sig(&tx_id, &recp_pub_nonce, tx.fee, tx.lock_height).unwrap(); let sig_part=keychain.aggsig_calculate_partial_sig(&tx_id, &recp_pub_nonce, tx.fee, tx.lock_height).unwrap();
// Build the next stage, containing sS (and our pubkeys again, for the recipient's convenience) // Build the next stage, containing sS (and our pubkeys again, for the recipient's convenience)
let mut partial_tx = build_partial_tx(&tx_id, keychain, amount, Some(sig_part), tx); let mut partial_tx = build_partial_tx(&tx_id, keychain, amount_with_fee, Some(sig_part), tx);
partial_tx.phase = PartialTxPhase::SenderConfirmation; partial_tx.phase = PartialTxPhase::SenderConfirmation;
// And send again // And send again
@ -163,11 +163,11 @@ fn build_send_tx(
lock_height: u64, lock_height: u64,
max_outputs: usize, max_outputs: usize,
selection_strategy_is_use_all: bool, selection_strategy_is_use_all: bool,
) -> Result<(Transaction, BlindingFactor, Vec<OutputData>, Identifier), Error> { ) -> Result<(Transaction, BlindingFactor, Vec<OutputData>, Identifier, u64), Error> {
let key_id = keychain.clone().root_key_id(); let key_id = keychain.clone().root_key_id();
// select some spendable coins from the wallet // select some spendable coins from the wallet
let coins = WalletData::read_wallet(&config.data_file_dir, |wallet_data| { let mut coins = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
wallet_data.select_coins( wallet_data.select_coins(
key_id.clone(), key_id.clone(),
amount, amount,
@ -178,8 +178,51 @@ fn build_send_tx(
) )
})?; })?;
// Get the maximum number of outputs in the wallet
let max_outputs = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
wallet_data.select_coins(
key_id.clone(),
amount,
current_height,
minimum_confirmations,
max_outputs,
true,
)
})?.len();
// sender is responsible for setting the fee on the partial tx
// recipient should double check the fee calculation and not blindly trust the
// sender
let mut fee = tx_fee(coins.len(), 2, None);
let mut total: u64 = coins.iter().map(|c| c.value).sum();
let mut amount_with_fee = amount + fee;
// Here check if we have enough outputs for the amount including fee otherwise look for other
// outputs and check again
while total <= amount_with_fee {
// End the loop if we have selected all the outputs and still not enough funds
if coins.len() == max_outputs {
return Err(Error::NotEnoughFunds(total as u64));
}
// select some spendable coins from the wallet
coins = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
wallet_data.select_coins(
key_id.clone(),
amount_with_fee,
current_height,
minimum_confirmations,
max_outputs,
selection_strategy_is_use_all,
)
})?;
fee = tx_fee(coins.len(), 2, None);
total = coins.iter().map(|c| c.value).sum();
amount_with_fee = amount + fee;
}
// build transaction skeleton with inputs and change // build transaction skeleton with inputs and change
let (mut parts, change_key) = inputs_and_change(&coins, config, keychain, amount)?; let (mut parts, change_key) = inputs_and_change(&coins, config, keychain, amount, fee)?;
// This is more proof of concept than anything but here we set lock_height // This is more proof of concept than anything but here we set lock_height
// on tx being sent (based on current chain height via api). // on tx being sent (based on current chain height via api).
@ -187,7 +230,7 @@ fn build_send_tx(
let (tx, blind) = build::transaction(parts, &keychain)?; let (tx, blind) = build::transaction(parts, &keychain)?;
Ok((tx, blind, coins, change_key)) Ok((tx, blind, coins, change_key, amount_with_fee))
} }
pub fn issue_burn_tx( pub fn issue_burn_tx(
@ -220,10 +263,10 @@ pub fn issue_burn_tx(
debug!(LOGGER, "selected some coins - {}", coins.len()); debug!(LOGGER, "selected some coins - {}", coins.len());
let (mut parts, _) = inputs_and_change(&coins, config, keychain, amount)?; let fee = tx_fee(coins.len(), 2, None);
let (mut parts, _) = inputs_and_change(&coins, config, keychain, amount, fee)?;
// add burn output and fees // add burn output and fees
let fee = tx_fee(coins.len(), 2, None);
parts.push(build::output(amount - fee, Identifier::zero())); parts.push(build::output(amount - fee, Identifier::zero()));
// finalize the burn transaction and send // finalize the burn transaction and send
@ -242,26 +285,18 @@ fn inputs_and_change(
config: &WalletConfig, config: &WalletConfig,
keychain: &Keychain, keychain: &Keychain,
amount: u64, amount: u64,
fee: u64,
) -> Result<(Vec<Box<build::Append>>, Identifier), Error> { ) -> Result<(Vec<Box<build::Append>>, Identifier), Error> {
let mut parts = vec![]; let mut parts = vec![];
// calculate the total across all inputs, and how much is left // calculate the total across all inputs, and how much is left
let total: u64 = coins.iter().map(|c| c.value).sum(); let total: u64 = coins.iter().map(|c| c.value).sum();
if total < amount {
return Err(Error::NotEnoughFunds(total as u64));
}
// sender is responsible for setting the fee on the partial tx
// recipient should double check the fee calculation and not blindly trust the
// sender
let fee = tx_fee(coins.len(), 2, None);
parts.push(build::with_fee(fee)); parts.push(build::with_fee(fee));
// if we are spending 10,000 coins to send 1,000 then our change will be 9,000 // if we are spending 10,000 coins to send 1,000 then our change will be 9,000
// the fee will come out of the amount itself // if the fee is 80 then the recipient will receive 1000 and our change will be 8,920
// if the fee is 80 then the recipient will only receive 920 let change = total - amount - fee;
// but our change will still be 9,000
let change = total - amount;
// build inputs using the appropriate derived key_ids // build inputs using the appropriate derived key_ids
for coin in coins { for coin in coins {