diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 2efe8128..a2fcb08a 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -433,6 +433,33 @@ impl Transaction { Ok(()) } + /// Calculate transaction weight + pub fn tx_weight(&self) -> u32 { + Transaction::weight( + self.inputs.len(), + self.outputs.len(), + self.input_proofs_count(), + ) + } + + /// Collect input's Merkle proofs + pub fn input_proofs_count(&self) -> usize { + self.inputs + .iter() + .filter(|i| i.merkle_proof.is_some()) + .count() + } + + /// Calculate transaction weight from transaction details + pub fn weight(input_len: usize, output_len: usize, proof_len: usize) -> u32 { + let mut tx_weight = + -1 * (input_len as i32) + (4 * output_len as i32) + (proof_len as i32) + 1; + if tx_weight < 1 { + tx_weight = 1; + } + tx_weight as u32 + } + fn verify_sorted(&self) -> Result<(), Error> { self.inputs.verify_sort_order()?; self.outputs.verify_sort_order()?; diff --git a/pool/src/pool.rs b/pool/src/pool.rs index f5d10a30..070eac86 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -786,11 +786,7 @@ where // (-1 * 1) + (4 * 2) + 1 = 8 // 8 * 10 = 80 if self.config.accept_fee_base > 0 { - let mut tx_weight = -1 * (tx.inputs.len() as i32) + (4 * tx.outputs.len() as i32) + 1; - if tx_weight < 1 { - tx_weight = 1; - } - let threshold = (tx_weight as u64) * self.config.accept_fee_base; + let threshold = (tx.tx_weight() as u64) * self.config.accept_fee_base; if tx.fee() < threshold { return Err(PoolError::LowFeeTransaction(threshold)); } diff --git a/wallet/src/receiver.rs b/wallet/src/receiver.rs index 87be2f8e..22b1f185 100644 --- a/wallet/src/receiver.rs +++ b/wallet/src/receiver.rs @@ -63,7 +63,12 @@ fn handle_sender_initiation( // double check the fee amount included in the partial tx // we don't necessarily want to just trust the sender // we could just overwrite the fee here (but we won't) due to the ecdsa sig - let fee = tx_fee(tx.inputs.len(), tx.outputs.len() + 1, None); + let fee = tx_fee( + tx.inputs.len(), + tx.outputs.len() + 1, + tx.input_proofs_count(), + None, + ); if fee != tx.fee() { return Err(ErrorKind::FeeDispute { sender_fee: tx.fee(), @@ -403,7 +408,12 @@ fn build_final_transaction( // double check the fee amount included in the partial tx // we don't necessarily want to just trust the sender // we could just overwrite the fee here (but we won't) due to the ecdsa sig - let fee = tx_fee(tx.inputs.len(), tx.outputs.len() + 1, None); + let fee = tx_fee( + tx.inputs.len(), + tx.outputs.len() + 1, + tx.input_proofs_count(), + None, + ); if fee != tx.fee() { return Err(ErrorKind::FeeDispute { sender_fee: tx.fee(), diff --git a/wallet/src/sender.rs b/wallet/src/sender.rs index e4318829..a01840e4 100644 --- a/wallet/src/sender.rs +++ b/wallet/src/sender.rs @@ -263,7 +263,7 @@ fn build_send_tx( // 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 fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); let mut total: u64 = coins.iter().map(|c| c.value).sum(); let mut amount_with_fee = amount + fee; @@ -286,7 +286,7 @@ fn build_send_tx( selection_strategy_is_use_all, )) })?; - fee = tx_fee(coins.len(), 2, None); + fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); total = coins.iter().map(|c| c.value).sum(); amount_with_fee = amount + fee; } @@ -303,6 +303,10 @@ fn build_send_tx( Ok((tx, blind, coins, change_key, amount_with_fee)) } +fn coins_proof_count(coins: &Vec) -> usize { + coins.iter().filter(|c| c.merkle_proof.is_some()).count() +} + pub fn issue_burn_tx( config: &WalletConfig, keychain: &Keychain, @@ -333,7 +337,7 @@ pub fn issue_burn_tx( debug!(LOGGER, "selected some coins - {}", coins.len()); - let fee = tx_fee(coins.len(), 2, None); + let fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None); let (mut parts, _) = inputs_and_change(&coins, config, keychain, amount, fee)?; // add burn output and fees diff --git a/wallet/src/types.rs b/wallet/src/types.rs index a56dec3b..572bcb43 100644 --- a/wallet/src/types.rs +++ b/wallet/src/types.rs @@ -53,17 +53,13 @@ const SEED_FILE: &'static str = "wallet.seed"; const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN; /// Transaction fee calculation -pub fn tx_fee(input_len: usize, output_len: usize, base_fee: Option) -> u64 { +pub fn tx_fee(input_len: usize, output_len: usize, proof_len: usize, base_fee: Option) -> u64 { let use_base_fee = match base_fee { Some(bf) => bf, None => DEFAULT_BASE_FEE, }; - let mut tx_weight = -1 * (input_len as i32) + 4 * (output_len as i32) + 1; - if tx_weight < 1 { - tx_weight = 1; - } - (tx_weight as u64) * use_base_fee + (Transaction::weight(input_len, output_len, proof_len) as u64) * use_base_fee } #[derive(Debug)] @@ -74,13 +70,11 @@ pub struct Error { /// Wallet errors, mostly wrappers around underlying crypto or I/O errors. #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] pub enum ErrorKind { - #[fail(display = "Not enough funds")] NotEnoughFunds(u64), + #[fail(display = "Not enough funds")] + NotEnoughFunds(u64), #[fail(display = "Fee dispute: sender fee {}, recipient fee {}", sender_fee, recipient_fee)] - FeeDispute { - sender_fee: u64, - recipient_fee: u64, - }, + FeeDispute { sender_fee: u64, recipient_fee: u64 }, #[fail(display = "Fee exceeds amount: sender amount {}, recipient fee {}", sender_amount, recipient_fee)] @@ -89,19 +83,24 @@ pub enum ErrorKind { recipient_fee: u64, }, - #[fail(display = "Keychain error")] Keychain, + #[fail(display = "Keychain error")] + Keychain, - #[fail(display = "Transaction error")] Transaction, + #[fail(display = "Transaction error")] + Transaction, - #[fail(display = "Secp error")] Secp, + #[fail(display = "Secp error")] + Secp, - #[fail(display = "Wallet data error: {}", _0)] WalletData(&'static str), + #[fail(display = "Wallet data error: {}", _0)] + WalletData(&'static str), /// An error in the format of the JSON structures exchanged by the wallet #[fail(display = "JSON format error")] Format, - #[fail(display = "I/O error")] IO, + #[fail(display = "I/O error")] + IO, /// Error when contacting a node through its API #[fail(display = "Node API error")] @@ -115,7 +114,8 @@ pub enum ErrorKind { #[fail(display = "Uri parsing error")] Uri, - #[fail(display = "Signature error")] Signature(&'static str), + #[fail(display = "Signature error")] + Signature(&'static str), /// Attempt to use duplicate transaction id in separate transactions #[fail(display = "Duplicate transaction ID error")] @@ -129,7 +129,8 @@ pub enum ErrorKind { #[fail(display = "Wallet seed doesn't exist error")] WalletSeedDoesntExist, - #[fail(display = "Generic error: {}", _0)] GenericError(&'static str), + #[fail(display = "Generic error: {}", _0)] + GenericError(&'static str), } impl Fail for Error {