Skip to content

FeeRate calculation fixes #774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/testutils/blockchain_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ macro_rules! bdk_blockchain_tests {
assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");

let mut builder = wallet.build_fee_bump(details.txid).unwrap();
builder.fee_rate(FeeRate::from_sat_per_vb(123.0));
builder.fee_rate(FeeRate::from_sat_per_vb(124.0));
let (mut new_psbt, new_details) = builder.finish().unwrap();
println!("{:#?}", new_details);

Expand Down
27 changes: 15 additions & 12 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,15 @@ impl FeeRate {

/// Calculate fee rate from `fee` and weight units (`wu`).
pub fn from_wu(fee: u64, wu: usize) -> FeeRate {
Self::from_vb(fee, wu.vbytes())
let vbytes = wu as f32 / 4.0;
let rate = fee as f32 / vbytes;
Self::new_checked(rate)
}

/// Calculate fee rate from `fee` and `vbytes`.
pub fn from_vb(fee: u64, vbytes: usize) -> FeeRate {
let rate = fee as f32 / vbytes as f32;
Self::from_sat_per_vb(rate)
pub fn from_vb(fee: u64, vbytes: f32) -> FeeRate {
let rate = fee as f32 / vbytes;
Self::new_checked(rate)
}

/// Return the value as satoshi/vbyte
Expand All @@ -114,12 +116,13 @@ impl FeeRate {

/// Calculate absolute fee in Satoshis using size in weight units.
pub fn fee_wu(&self, wu: usize) -> u64 {
self.fee_vb(wu.vbytes())
let vbytes = wu as f32 / 4.0;
(self.0 * vbytes).ceil() as u64
}

/// Calculate absolute fee in Satoshis using size in virtual bytes.
pub fn fee_vb(&self, vbytes: usize) -> u64 {
(self.as_sat_per_vb() * vbytes as f32).ceil() as u64
pub fn fee_vb(&self, vbytes: f32) -> u64 {
(self.0 * vbytes).ceil() as u64
}
}

Expand All @@ -138,15 +141,15 @@ impl Sub for FeeRate {
}

/// Trait implemented by types that can be used to measure weight units.
pub trait Vbytes {
pub trait WeightUnits {
/// Convert weight units to virtual bytes.
fn vbytes(self) -> usize;
fn to_vbytes(self) -> f32;
}

impl Vbytes for usize {
fn vbytes(self) -> usize {
impl WeightUnits for usize {
fn to_vbytes(self) -> f32 {
// ref: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations
(self as f32 / 4.0).ceil() as usize
self as f32 / 4.0
}
}

Expand Down
9 changes: 4 additions & 5 deletions src/wallet/coin_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,8 @@ impl<D: Database> CoinSelectionAlgorithm<D> for OldestFirstCoinSelection {
/// - `fee_rate`: required fee rate for the current selection
/// - `drain_script`: script to consider change creation
pub fn decide_change(remaining_amount: u64, fee_rate: FeeRate, drain_script: &Script) -> Excess {
// drain_output_len = size(len(script_pubkey)) + len(script_pubkey) + size(output_value)
let drain_output_len = serialize(drain_script).len() + 8usize;
let change_fee = fee_rate.fee_vb(drain_output_len);
let drain_output_len = serialize(drain_script).len() + 8_usize;
let change_fee = fee_rate.fee_vb(drain_output_len as f32);
let drain_val = remaining_amount.saturating_sub(change_fee);

if drain_val.is_dust(drain_script) {
Expand Down Expand Up @@ -728,7 +727,7 @@ mod test {
use super::*;
use crate::database::{BatchOperations, MemoryDatabase};
use crate::types::*;
use crate::wallet::Vbytes;
use crate::wallet::WeightUnits;

use rand::rngs::StdRng;
use rand::seq::SliceRandom;
Expand Down Expand Up @@ -1319,7 +1318,7 @@ mod test {

assert_eq!(result.selected.len(), 1);
assert_eq!(result.selected_amount(), 100_000);
let input_size = (TXIN_BASE_WEIGHT + P2WPKH_SATISFACTION_SIZE).vbytes();
let input_size = (TXIN_BASE_WEIGHT + P2WPKH_SATISFACTION_SIZE).to_vbytes();
// the final fee rate should be exactly the same as the fee rate given
assert!((1.0 - (result.fee_amount as f32 / input_size as f32)).abs() < f32::EPSILON);
}
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2649,7 +2649,7 @@ pub(crate) mod test {
builder
.drain_to(addr.script_pubkey())
.drain_wallet()
.fee_rate(FeeRate::from_sat_per_vb(453.0));
.fee_rate(FeeRate::from_sat_per_vb(455.0));
builder.finish().unwrap();
}

Expand Down