Skip to content

Commit d5c5fe6

Browse files
committed
FeeRate calculation fixes
Weight units should always be whole units. However, virtual bytes will need decimal places and the conversion from weight unit to vbytes should not be rounded. This commit has the following changes: * vbytes should always be represented in `f32` * conversion between vbytes and weight units should never be rounded * simple calculations (such as those for feerates) should be explicitly defined (instead of using multiple small functions)
1 parent 7de8be4 commit d5c5fe6

File tree

4 files changed

+17
-15
lines changed

4 files changed

+17
-15
lines changed

src/testutils/blockchain_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,7 @@ macro_rules! bdk_blockchain_tests {
10211021
assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
10221022

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

src/types.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,15 @@ impl FeeRate {
9898

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

104106
/// Calculate fee rate from `fee` and `vbytes`.
105-
pub fn from_vb(fee: u64, vbytes: usize) -> FeeRate {
106-
let rate = fee as f32 / vbytes as f32;
107-
Self::from_sat_per_vb(rate)
107+
pub fn from_vb(fee: u64, vbytes: f32) -> FeeRate {
108+
let rate = fee as f32 / vbytes;
109+
Self::new_checked(rate)
108110
}
109111

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

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

120123
/// Calculate absolute fee in Satoshis using size in virtual bytes.
121-
pub fn fee_vb(&self, vbytes: usize) -> u64 {
122-
(self.as_sat_per_vb() * vbytes as f32).ceil() as u64
124+
pub fn fee_vb(&self, vbytes: f32) -> u64 {
125+
(self.0 * vbytes).ceil() as u64
123126
}
124127
}
125128

@@ -140,13 +143,13 @@ impl Sub for FeeRate {
140143
/// Trait implemented by types that can be used to measure weight units.
141144
pub trait Vbytes {
142145
/// Convert weight units to virtual bytes.
143-
fn vbytes(self) -> usize;
146+
fn vbytes(self) -> f32;
144147
}
145148

146149
impl Vbytes for usize {
147-
fn vbytes(self) -> usize {
150+
fn vbytes(self) -> f32 {
148151
// ref: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations
149-
(self as f32 / 4.0).ceil() as usize
152+
self as f32 / 4.0
150153
}
151154
}
152155

src/wallet/coin_selection.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,8 @@ impl<D: Database> CoinSelectionAlgorithm<D> for OldestFirstCoinSelection {
304304
/// - `fee_rate`: required fee rate for the current selection
305305
/// - `drain_script`: script to consider change creation
306306
pub fn decide_change(remaining_amount: u64, fee_rate: FeeRate, drain_script: &Script) -> Excess {
307-
// drain_output_len = size(len(script_pubkey)) + len(script_pubkey) + size(output_value)
308-
let drain_output_len = serialize(drain_script).len() + 8usize;
309-
let change_fee = fee_rate.fee_vb(drain_output_len);
307+
let drain_output_len = serialize(drain_script).len() + 8_usize;
308+
let change_fee = fee_rate.fee_vb(drain_output_len as f32);
310309
let drain_val = remaining_amount.saturating_sub(change_fee);
311310

312311
if drain_val.is_dust(drain_script) {

src/wallet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2649,7 +2649,7 @@ pub(crate) mod test {
26492649
builder
26502650
.drain_to(addr.script_pubkey())
26512651
.drain_wallet()
2652-
.fee_rate(FeeRate::from_sat_per_vb(453.0));
2652+
.fee_rate(FeeRate::from_sat_per_vb(455.0));
26532653
builder.finish().unwrap();
26542654
}
26552655

0 commit comments

Comments
 (0)