]> Untitled Git - bdk/commitdiff
refactor(bdk)!: drop FeeRate from bdk::types
authorvmammal <valuedmammal@protonmail.com>
Thu, 16 Nov 2023 23:00:58 +0000 (18:00 -0500)
committervmammal <valuedmammal@protonmail.com>
Fri, 22 Mar 2024 03:32:00 +0000 (23:32 -0400)
Adopt `bitcoin::FeeRate` throughout

crates/bdk/src/psbt/mod.rs
crates/bdk/src/types.rs
crates/bdk/src/wallet/coin_selection.rs
crates/bdk/src/wallet/error.rs
crates/bdk/src/wallet/hardwaresigner.rs
crates/bdk/src/wallet/mod.rs
crates/bdk/src/wallet/tx_builder.rs
crates/bdk/tests/psbt.rs
crates/bdk/tests/wallet.rs
crates/hwi/src/lib.rs

index 796b8618b3b2b0dfb1822ee1d6eb402fae1e004c..2606691752146e3ea478fdf33f6d203264e9ebc6 100644 (file)
 
 //! Additional functions on the `rust-bitcoin` `PartiallySignedTransaction` structure.
 
-use crate::FeeRate;
 use alloc::vec::Vec;
 use bitcoin::psbt::PartiallySignedTransaction as Psbt;
+use bitcoin::Amount;
+use bitcoin::FeeRate;
 use bitcoin::TxOut;
 
 // TODO upstream the functions here to `rust-bitcoin`?
@@ -65,7 +66,7 @@ impl PsbtUtils for Psbt {
         let fee_amount = self.fee_amount();
         fee_amount.map(|fee| {
             let weight = self.clone().extract_tx().weight();
-            FeeRate::from_wu(fee, weight)
+            Amount::from_sat(fee) / weight
         })
     }
 }
index 5f2173596ebba59f77ead95167b912ba19e1dffd..3bde290e4b04b6a4c3a80fa4070bff6a65ebde39 100644 (file)
 
 use alloc::boxed::Box;
 use core::convert::AsRef;
-use core::ops::Sub;
 
 use bdk_chain::ConfirmationTime;
 use bitcoin::blockdata::transaction::{OutPoint, Sequence, TxOut};
-use bitcoin::{psbt, Weight};
+use bitcoin::psbt;
 
 use serde::{Deserialize, Serialize};
 
@@ -47,103 +46,6 @@ impl AsRef<[u8]> for KeychainKind {
     }
 }
 
-/// Fee rate
-#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
-// Internally stored as satoshi/vbyte
-pub struct FeeRate(f32);
-
-impl FeeRate {
-    /// Create a new instance checking the value provided
-    ///
-    /// ## Panics
-    ///
-    /// Panics if the value is not [normal](https://doc.rust-lang.org/std/primitive.f32.html#method.is_normal) (except if it's a positive zero) or negative.
-    fn new_checked(value: f32) -> Self {
-        assert!(value.is_normal() || value == 0.0);
-        assert!(value.is_sign_positive());
-
-        FeeRate(value)
-    }
-
-    /// Create a new instance of [`FeeRate`] given a float fee rate in sats/kwu
-    pub fn from_sat_per_kwu(sat_per_kwu: f32) -> Self {
-        FeeRate::new_checked(sat_per_kwu / 250.0_f32)
-    }
-
-    /// Create a new instance of [`FeeRate`] given a float fee rate in sats/kvb
-    pub fn from_sat_per_kvb(sat_per_kvb: f32) -> Self {
-        FeeRate::new_checked(sat_per_kvb / 1000.0_f32)
-    }
-
-    /// Create a new instance of [`FeeRate`] given a float fee rate in btc/kvbytes
-    ///
-    /// ## Panics
-    ///
-    /// Panics if the value is not [normal](https://doc.rust-lang.org/std/primitive.f32.html#method.is_normal) (except if it's a positive zero) or negative.
-    pub fn from_btc_per_kvb(btc_per_kvb: f32) -> Self {
-        FeeRate::new_checked(btc_per_kvb * 1e5)
-    }
-
-    /// Create a new instance of [`FeeRate`] given a float fee rate in satoshi/vbyte
-    ///
-    /// ## Panics
-    ///
-    /// Panics if the value is not [normal](https://doc.rust-lang.org/std/primitive.f32.html#method.is_normal) (except if it's a positive zero) or negative.
-    pub fn from_sat_per_vb(sat_per_vb: f32) -> Self {
-        FeeRate::new_checked(sat_per_vb)
-    }
-
-    /// Create a new [`FeeRate`] with the default min relay fee value
-    pub const fn default_min_relay_fee() -> Self {
-        FeeRate(1.0)
-    }
-
-    /// Calculate fee rate from `fee` and weight units (`wu`).
-    pub fn from_wu(fee: u64, wu: Weight) -> FeeRate {
-        Self::from_vb(fee, wu.to_vbytes_ceil() as usize)
-    }
-
-    /// 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)
-    }
-
-    /// Return the value as satoshi/vbyte
-    pub fn as_sat_per_vb(&self) -> f32 {
-        self.0
-    }
-
-    /// Return the value as satoshi/kwu
-    pub fn sat_per_kwu(&self) -> f32 {
-        self.0 * 250.0_f32
-    }
-
-    /// Calculate absolute fee in Satoshis using size in weight units.
-    pub fn fee_wu(&self, wu: Weight) -> u64 {
-        self.fee_vb(wu.to_vbytes_ceil() as usize)
-    }
-
-    /// 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
-    }
-}
-
-impl Default for FeeRate {
-    fn default() -> Self {
-        FeeRate::default_min_relay_fee()
-    }
-}
-
-impl Sub for FeeRate {
-    type Output = Self;
-
-    fn sub(self, other: FeeRate) -> Self::Output {
-        FeeRate(self.0 - other.0)
-    }
-}
-
 /// Trait implemented by types that can be used to measure weight units.
 pub trait Vbytes {
     /// Convert weight units to virtual bytes.
@@ -244,73 +146,3 @@ impl Utxo {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn can_store_feerate_in_const() {
-        const _MIN_RELAY: FeeRate = FeeRate::default_min_relay_fee();
-    }
-
-    #[test]
-    #[should_panic]
-    fn test_invalid_feerate_neg_zero() {
-        let _ = FeeRate::from_sat_per_vb(-0.0);
-    }
-
-    #[test]
-    #[should_panic]
-    fn test_invalid_feerate_neg_value() {
-        let _ = FeeRate::from_sat_per_vb(-5.0);
-    }
-
-    #[test]
-    #[should_panic]
-    fn test_invalid_feerate_nan() {
-        let _ = FeeRate::from_sat_per_vb(f32::NAN);
-    }
-
-    #[test]
-    #[should_panic]
-    fn test_invalid_feerate_inf() {
-        let _ = FeeRate::from_sat_per_vb(f32::INFINITY);
-    }
-
-    #[test]
-    fn test_valid_feerate_pos_zero() {
-        let _ = FeeRate::from_sat_per_vb(0.0);
-    }
-
-    #[test]
-    fn test_fee_from_btc_per_kvb() {
-        let fee = FeeRate::from_btc_per_kvb(1e-5);
-        assert!((fee.as_sat_per_vb() - 1.0).abs() < f32::EPSILON);
-    }
-
-    #[test]
-    fn test_fee_from_sat_per_vbyte() {
-        let fee = FeeRate::from_sat_per_vb(1.0);
-        assert!((fee.as_sat_per_vb() - 1.0).abs() < f32::EPSILON);
-    }
-
-    #[test]
-    fn test_fee_default_min_relay_fee() {
-        let fee = FeeRate::default_min_relay_fee();
-        assert!((fee.as_sat_per_vb() - 1.0).abs() < f32::EPSILON);
-    }
-
-    #[test]
-    fn test_fee_from_sat_per_kvb() {
-        let fee = FeeRate::from_sat_per_kvb(1000.0);
-        assert!((fee.as_sat_per_vb() - 1.0).abs() < f32::EPSILON);
-    }
-
-    #[test]
-    fn test_fee_from_sat_per_kwu() {
-        let fee = FeeRate::from_sat_per_kwu(250.0);
-        assert!((fee.as_sat_per_vb() - 1.0).abs() < f32::EPSILON);
-        assert_eq!(fee.sat_per_kwu(), 250.0);
-    }
-}
index ac6084cfc4c2216a235d7cb01951b28eb42cc718..5383e552553a6ce6c4977610c3d026afb51189db 100644 (file)
@@ -41,7 +41,7 @@
 //!         &self,
 //!         required_utxos: Vec<WeightedUtxo>,
 //!         optional_utxos: Vec<WeightedUtxo>,
-//!         fee_rate: bdk::FeeRate,
+//!         fee_rate: FeeRate,
 //!         target_amount: u64,
 //!         drain_script: &Script,
 //!     ) -> Result<CoinSelectionResult, coin_selection::Error> {
@@ -61,7 +61,7 @@
 //!                 },
 //!             )
 //!             .collect::<Vec<_>>();
-//!         let additional_fees = fee_rate.fee_wu(additional_weight);
+//!         let additional_fees = (fee_rate * additional_weight).to_sat();
 //!         let amount_needed_with_fees = additional_fees + target_amount;
 //!         if selected_amount < amount_needed_with_fees {
 //!             return Err(coin_selection::Error::InsufficientFunds {
 //! ```
 
 use crate::chain::collections::HashSet;
-use crate::types::FeeRate;
 use crate::wallet::utils::IsDust;
 use crate::Utxo;
 use crate::WeightedUtxo;
+use bitcoin::FeeRate;
 
 use alloc::vec::Vec;
 use bitcoin::consensus::encode::serialize;
@@ -313,7 +313,8 @@ impl CoinSelectionAlgorithm for OldestFirstCoinSelection {
 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 change_fee =
+        (fee_rate * Weight::from_vb(drain_output_len as u64).expect("overflow occurred")).to_sat();
     let drain_val = remaining_amount.saturating_sub(change_fee);
 
     if drain_val.is_dust(drain_script) {
@@ -344,9 +345,12 @@ fn select_sorted_utxos(
             (&mut selected_amount, &mut fee_amount),
             |(selected_amount, fee_amount), (must_use, weighted_utxo)| {
                 if must_use || **selected_amount < target_amount + **fee_amount {
-                    **fee_amount += fee_rate.fee_wu(Weight::from_wu(
-                        (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
-                    ));
+                    **fee_amount += (fee_rate
+                        * Weight::from_wu(
+                            (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
+                        ))
+                    .to_sat();
+
                     **selected_amount += weighted_utxo.utxo.txout().value;
                     Some(weighted_utxo.utxo)
                 } else {
@@ -387,9 +391,10 @@ struct OutputGroup {
 
 impl OutputGroup {
     fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self {
-        let fee = fee_rate.fee_wu(Weight::from_wu(
-            (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
-        ));
+        let fee = (fee_rate
+            * Weight::from_wu((TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64))
+        .to_sat();
+
         let effective_value = weighted_utxo.utxo.txout().value as i64 - fee as i64;
         OutputGroup {
             weighted_utxo,
@@ -456,7 +461,8 @@ impl CoinSelectionAlgorithm for BranchAndBoundCoinSelection {
             .iter()
             .fold(0, |acc, x| acc + x.effective_value);
 
-        let cost_of_change = self.size_of_change as f32 * fee_rate.as_sat_per_vb();
+        let cost_of_change =
+            (Weight::from_vb(self.size_of_change).expect("overflow occurred") * fee_rate).to_sat();
 
         // `curr_value` and `curr_available_value` are both the sum of *effective_values* of
         // the UTXOs. For the optional UTXOs (curr_available_value) we filter out UTXOs with
@@ -547,7 +553,7 @@ impl BranchAndBoundCoinSelection {
         mut curr_value: i64,
         mut curr_available_value: i64,
         target_amount: i64,
-        cost_of_change: f32,
+        cost_of_change: u64,
         drain_script: &Script,
         fee_rate: FeeRate,
     ) -> Result<CoinSelectionResult, Error> {
@@ -893,7 +899,7 @@ mod test {
             .coin_select(
                 utxos,
                 vec![],
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -914,7 +920,7 @@ mod test {
             .coin_select(
                 utxos,
                 vec![],
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -935,7 +941,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -957,7 +963,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -975,7 +981,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1000.0),
+                FeeRate::from_sat_per_vb_unchecked(1000),
                 target_amount,
                 &drain_script,
             )
@@ -992,7 +998,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1013,7 +1019,7 @@ mod test {
             .coin_select(
                 utxos,
                 vec![],
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1034,7 +1040,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1056,7 +1062,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1075,7 +1081,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1000.0),
+                FeeRate::from_sat_per_vb_unchecked(1000),
                 target_amount,
                 &drain_script,
             )
@@ -1096,7 +1102,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1117,7 +1123,7 @@ mod test {
             .coin_select(
                 utxos.clone(),
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1138,7 +1144,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1175,7 +1181,7 @@ mod test {
             .coin_select(
                 required,
                 optional,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1197,7 +1203,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1215,7 +1221,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1000.0),
+                FeeRate::from_sat_per_vb_unchecked(1000),
                 target_amount,
                 &drain_script,
             )
@@ -1232,7 +1238,7 @@ mod test {
             .coin_select(
                 vec![],
                 utxos,
-                FeeRate::from_sat_per_vb(1.0),
+                FeeRate::from_sat_per_vb_unchecked(1),
                 target_amount,
                 &drain_script,
             )
@@ -1258,7 +1264,7 @@ mod test {
                 .coin_select(
                     vec![],
                     optional_utxos,
-                    FeeRate::from_sat_per_vb(0.0),
+                    FeeRate::ZERO,
                     target_amount,
                     &drain_script,
                 )
@@ -1270,7 +1276,7 @@ mod test {
     #[test]
     #[should_panic(expected = "BnBNoExactMatch")]
     fn test_bnb_function_no_exact_match() {
-        let fee_rate = FeeRate::from_sat_per_vb(10.0);
+        let fee_rate = FeeRate::from_sat_per_vb_unchecked(10);
         let utxos: Vec<OutputGroup> = get_test_utxos()
             .into_iter()
             .map(|u| OutputGroup::new(u, fee_rate))
@@ -1279,7 +1285,7 @@ mod test {
         let curr_available_value = utxos.iter().fold(0, |acc, x| acc + x.effective_value);
 
         let size_of_change = 31;
-        let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
+        let cost_of_change = (Weight::from_vb_unchecked(size_of_change) * fee_rate).to_sat();
 
         let drain_script = ScriptBuf::default();
         let target_amount = 20_000 + FEE_AMOUNT;
@@ -1300,7 +1306,7 @@ mod test {
     #[test]
     #[should_panic(expected = "BnBTotalTriesExceeded")]
     fn test_bnb_function_tries_exceeded() {
-        let fee_rate = FeeRate::from_sat_per_vb(10.0);
+        let fee_rate = FeeRate::from_sat_per_vb_unchecked(10);
         let utxos: Vec<OutputGroup> = generate_same_value_utxos(100_000, 100_000)
             .into_iter()
             .map(|u| OutputGroup::new(u, fee_rate))
@@ -1309,7 +1315,7 @@ mod test {
         let curr_available_value = utxos.iter().fold(0, |acc, x| acc + x.effective_value);
 
         let size_of_change = 31;
-        let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
+        let cost_of_change = (Weight::from_vb_unchecked(size_of_change) * fee_rate).to_sat();
         let target_amount = 20_000 + FEE_AMOUNT;
 
         let drain_script = ScriptBuf::default();
@@ -1331,9 +1337,9 @@ mod test {
     // The match won't be exact but still in the range
     #[test]
     fn test_bnb_function_almost_exact_match_with_fees() {
-        let fee_rate = FeeRate::from_sat_per_vb(1.0);
+        let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
         let size_of_change = 31;
-        let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
+        let cost_of_change = (Weight::from_vb_unchecked(size_of_change) * fee_rate).to_sat();
 
         let utxos: Vec<_> = generate_same_value_utxos(50_000, 10)
             .into_iter()
@@ -1346,7 +1352,7 @@ mod test {
 
         // 2*(value of 1 utxo)  - 2*(1 utxo fees with 1.0sat/vbyte fee rate) -
         // cost_of_change + 5.
-        let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.ceil() as i64 + 5;
+        let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change as i64 + 5;
 
         let drain_script = ScriptBuf::default();
 
@@ -1371,7 +1377,7 @@ mod test {
     fn test_bnb_function_exact_match_more_utxos() {
         let seed = [0; 32];
         let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let fee_rate = FeeRate::from_sat_per_vb(0.0);
+        let fee_rate = FeeRate::ZERO;
 
         for _ in 0..200 {
             let optional_utxos: Vec<_> = generate_random_utxos(&mut rng, 40)
@@ -1397,7 +1403,7 @@ mod test {
                     curr_value,
                     curr_available_value,
                     target_amount,
-                    0.0,
+                    0,
                     &drain_script,
                     fee_rate,
                 )
@@ -1413,7 +1419,7 @@ mod test {
         let mut utxos = generate_random_utxos(&mut rng, 300);
         let target_amount = sum_random_utxos(&mut rng, &mut utxos) + FEE_AMOUNT;
 
-        let fee_rate = FeeRate::from_sat_per_vb(1.0);
+        let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
         let utxos: Vec<OutputGroup> = utxos
             .into_iter()
             .map(|u| OutputGroup::new(u, fee_rate))
@@ -1442,7 +1448,7 @@ mod test {
         let selection = BranchAndBoundCoinSelection::default().coin_select(
             vec![],
             utxos,
-            FeeRate::from_sat_per_vb(10.0),
+            FeeRate::from_sat_per_vb_unchecked(10),
             500_000,
             &drain_script,
         );
@@ -1468,7 +1474,7 @@ mod test {
         let selection = BranchAndBoundCoinSelection::default().coin_select(
             required,
             optional,
-            FeeRate::from_sat_per_vb(10.0),
+            FeeRate::from_sat_per_vb_unchecked(10),
             500_000,
             &drain_script,
         );
@@ -1490,7 +1496,7 @@ mod test {
         let selection = BranchAndBoundCoinSelection::default().coin_select(
             utxos,
             vec![],
-            FeeRate::from_sat_per_vb(10_000.0),
+            FeeRate::from_sat_per_vb_unchecked(10_000),
             500_000,
             &drain_script,
         );
index db58fef06fd13c1db4aaa8888a5de0021f97dd5d..548dc783fa30ab29b6feb60f44629dd904593c9c 100644 (file)
@@ -14,7 +14,7 @@
 use crate::descriptor::policy::PolicyError;
 use crate::descriptor::DescriptorError;
 use crate::wallet::coin_selection;
-use crate::{descriptor, FeeRate, KeychainKind};
+use crate::{descriptor, KeychainKind};
 use alloc::string::String;
 use bitcoin::{absolute, psbt, OutPoint, Sequence, Txid};
 use core::fmt;
@@ -83,8 +83,8 @@ pub enum CreateTxError<P> {
     },
     /// When bumping a tx the fee rate requested is lower than required
     FeeRateTooLow {
-        /// Required fee rate (satoshi/vbyte)
-        required: FeeRate,
+        /// Required fee rate
+        required: bitcoin::FeeRate,
     },
     /// `manually_selected_only` option is selected but no utxo has been passed
     NoUtxosSelected,
@@ -168,8 +168,8 @@ where
             CreateTxError::FeeRateTooLow { required } => {
                 write!(
                     f,
-                    "Fee rate too low: required {} sat/vbyte",
-                    required.as_sat_per_vb()
+                    "Fee rate too low: required {} sat/kwu",
+                    required.to_sat_per_kwu()
                 )
             }
             CreateTxError::NoUtxosSelected => {
index aec49297c28be6890cab39fb5b85045bd2c15089..8b11e784a8abac916293967cbc7da0d73d18d588 100644 (file)
@@ -18,7 +18,7 @@
 //! # use bdk::signer::SignerOrdering;
 //! # use bdk::wallet::hardwaresigner::HWISigner;
 //! # use bdk::wallet::AddressIndex::New;
-//! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet};
+//! # use bdk::{KeychainKind, SignOptions, Wallet};
 //! # use hwi::HWIClient;
 //! # use std::sync::Arc;
 //! #
index 4db035fb6c4e2a6af529c34176db0e65abce693d..3b133602f7a7d21bf4b4e5a719f7cbe30473e95e 100644 (file)
@@ -33,7 +33,7 @@ use bdk_chain::{
 use bitcoin::secp256k1::{All, Secp256k1};
 use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
 use bitcoin::{
-    absolute, Address, Block, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxOut,
+    absolute, Address, Block, FeeRate, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxOut,
     Txid, Weight, Witness,
 };
 use bitcoin::{consensus::encode::serialize, BlockHash};
@@ -986,10 +986,8 @@ impl<D> Wallet<D> {
     /// ```
     /// [`insert_txout`]: Self::insert_txout
     pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
-        self.calculate_fee(tx).map(|fee| {
-            let weight = tx.weight();
-            FeeRate::from_wu(fee, weight)
-        })
+        self.calculate_fee(tx)
+            .map(|fee| bitcoin::Amount::from_sat(fee) / tx.weight())
     }
 
     /// Compute the `tx`'s sent and received amounts (in satoshis).
@@ -1432,32 +1430,31 @@ impl<D> Wallet<D> {
             (Some(rbf), _) => rbf.get_value(),
         };
 
-        let (fee_rate, mut fee_amount) = match params
-            .fee_policy
-            .as_ref()
-            .unwrap_or(&FeePolicy::FeeRate(FeeRate::default()))
-        {
+        let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
             //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
             FeePolicy::FeeAmount(fee) => {
                 if let Some(previous_fee) = params.bumping_fee {
-                    if *fee < previous_fee.absolute {
+                    if fee < previous_fee.absolute {
                         return Err(CreateTxError::FeeTooLow {
                             required: previous_fee.absolute,
                         });
                     }
                 }
-                (FeeRate::from_sat_per_vb(0.0), *fee)
+                (FeeRate::ZERO, fee)
             }
             FeePolicy::FeeRate(rate) => {
                 if let Some(previous_fee) = params.bumping_fee {
-                    let required_feerate = FeeRate::from_sat_per_vb(previous_fee.rate + 1.0);
-                    if *rate < required_feerate {
+                    let required_feerate = FeeRate::from_sat_per_kwu(
+                        previous_fee.rate.to_sat_per_kwu()
+                            + FeeRate::BROADCAST_MIN.to_sat_per_kwu(), // +1 sat/vb
+                    );
+                    if rate < required_feerate {
                         return Err(CreateTxError::FeeRateTooLow {
                             required: required_feerate,
                         });
                     }
                 }
-                (*rate, 0)
+                (rate, 0)
             }
         };
 
@@ -1500,7 +1497,7 @@ impl<D> Wallet<D> {
             outgoing += value;
         }
 
-        fee_amount += fee_rate.fee_wu(tx.weight());
+        fee_amount += (fee_rate * tx.weight()).to_sat();
 
         // Segwit transactions' header is 2WU larger than legacy txs' header,
         // as they contain a witness marker (1WU) and a witness flag (1WU) (see BIP144).
@@ -1511,7 +1508,7 @@ impl<D> Wallet<D> {
         // end up with a transaction with a slightly higher fee rate than the requested one.
         // If, instead, we undershoot, we may end up with a feerate lower than the requested one
         // - we might come up with non broadcastable txs!
-        fee_amount += fee_rate.fee_wu(Weight::from_wu(2));
+        fee_amount += (fee_rate * Weight::from_wu(2)).to_sat();
 
         if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
             && internal_descriptor.is_none()
@@ -1652,7 +1649,7 @@ impl<D> Wallet<D> {
     /// let mut psbt =  {
     ///     let mut builder = wallet.build_fee_bump(tx.txid())?;
     ///     builder
-    ///         .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0));
+    ///         .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
     ///     builder.finish()?
     /// };
     ///
@@ -1780,7 +1777,7 @@ impl<D> Wallet<D> {
             utxos: original_utxos,
             bumping_fee: Some(tx_builder::PreviousFee {
                 absolute: fee,
-                rate: fee_rate.as_sat_per_vb(),
+                rate: fee_rate,
             }),
             ..Default::default()
         };
index 45d215fd82d044fc15969989fbcff9ddc0a5f363..aa81d1f3520a082f6cbe2b576712cad4087227df 100644 (file)
@@ -31,7 +31,7 @@
 //!     // Create a transaction with one output to `to_address` of 50_000 satoshi
 //!     .add_recipient(to_address.script_pubkey(), 50_000)
 //!     // With a custom fee rate of 5.0 satoshi/vbyte
-//!     .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0))
+//!     .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"))
 //!     // Only spend non-change outputs
 //!     .do_not_spend_change()
 //!     // Turn on RBF signaling
@@ -53,10 +53,10 @@ use bitcoin::{absolute, script::PushBytes, OutPoint, ScriptBuf, Sequence, Transa
 
 use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
 use super::ChangeSet;
-use crate::types::{FeeRate, KeychainKind, LocalOutput, WeightedUtxo};
+use crate::types::{KeychainKind, LocalOutput, WeightedUtxo};
 use crate::wallet::CreateTxError;
 use crate::{Utxo, Wallet};
-
+use bitcoin::FeeRate;
 /// Context in which the [`TxBuilder`] is valid
 pub trait TxBuilderContext: core::fmt::Debug + Default + Clone {}
 
@@ -163,7 +163,7 @@ pub(crate) struct TxParams {
 #[derive(Clone, Copy, Debug)]
 pub(crate) struct PreviousFee {
     pub absolute: u64,
-    pub rate: f32,
+    pub rate: FeeRate,
 }
 
 #[derive(Debug, Clone, Copy)]
@@ -174,7 +174,7 @@ pub(crate) enum FeePolicy {
 
 impl Default for FeePolicy {
     fn default() -> Self {
-        FeePolicy::FeeRate(FeeRate::default_min_relay_fee())
+        FeePolicy::FeeRate(FeeRate::BROADCAST_MIN)
     }
 }
 
@@ -191,14 +191,12 @@ impl<'a, D, Cs: Clone, Ctx> Clone for TxBuilder<'a, D, Cs, Ctx> {
 
 // methods supported by both contexts, for any CoinSelectionAlgorithm
 impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
-    /// Set a custom fee rate
-    /// The fee_rate method sets the mining fee paid by the transaction as a rate on its size.
-    /// This means that the total fee paid is equal to this rate * size of the transaction in virtual Bytes (vB) or Weight Unit (wu).
-    /// This rate is internally expressed in satoshis-per-virtual-bytes (sats/vB) using FeeRate::from_sat_per_vb, but can also be set by:
-    /// * sats/kvB (1000 sats/kvB == 1 sats/vB) using FeeRate::from_sat_per_kvb
-    /// * btc/kvB (0.00001000 btc/kvB == 1 sats/vB) using FeeRate::from_btc_per_kvb
-    /// * sats/kwu (250 sats/kwu == 1 sats/vB) using FeeRate::from_sat_per_kwu
-    /// Default is 1 sat/vB (see min_relay_fee)
+    /// Set a custom fee rate.
+    ///
+    /// This method sets the mining fee paid by the transaction as a rate on its size.
+    /// This means that the total fee paid is equal to `fee_rate` times the size
+    /// of the transaction. Default is 1 sat/vB in accordance with Bitcoin Core's default
+    /// relay policy.
     ///
     /// Note that this is really a minimum feerate -- it's possible to
     /// overshoot it slightly since adding a change output to drain the remaining
@@ -781,7 +779,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
     ///     .drain_wallet()
     ///     // Send the excess (which is all the coins minus the fee) to this address.
     ///     .drain_to(to_address.script_pubkey())
-    ///     .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0))
+    ///     .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"))
     ///     .enable_rbf();
     /// let psbt = tx_builder.finish()?;
     /// # Ok::<(), anyhow::Error>(())
index 3c4968bfebf72647bece380d8a407bb2125892a3..e3ace9db0028020382eba8093257707a2546aac6 100644 (file)
@@ -1,7 +1,7 @@
 use bdk::bitcoin::TxIn;
 use bdk::wallet::AddressIndex;
 use bdk::wallet::AddressIndex::New;
-use bdk::{psbt, FeeRate, SignOptions};
+use bdk::{psbt, SignOptions};
 use bitcoin::psbt::PartiallySignedTransaction as Psbt;
 use core::str::FromStr;
 mod common;
@@ -10,6 +10,12 @@ use common::*;
 // from bip 174
 const PSBT_STR: &str = "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA";
 
+fn feerate_unchecked(sat_vb: f64) -> bitcoin::FeeRate {
+    // 1 sat_vb / 4wu_vb * 1000kwu_wu = 250 sat_kwu
+    let sat_kwu = (sat_vb * 250.0).ceil() as u64;
+    bitcoin::FeeRate::from_sat_per_kwu(sat_kwu)
+}
+
 #[test]
 #[should_panic(expected = "InputIndexOutOfRange")]
 fn test_psbt_malformed_psbt_input_legacy() {
@@ -82,13 +88,13 @@ fn test_psbt_sign_with_finalized() {
 fn test_psbt_fee_rate_with_witness_utxo() {
     use psbt::PsbtUtils;
 
-    let expected_fee_rate = 1.2345;
+    let expected_fee_rate = feerate_unchecked(1.2345);
 
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
     let addr = wallet.get_address(New);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(FeeRate::from_sat_per_vb(expected_fee_rate));
+    builder.fee_rate(expected_fee_rate);
     let mut psbt = builder.finish().unwrap();
     let fee_amount = psbt.fee_amount();
     assert!(fee_amount.is_some());
@@ -99,21 +105,21 @@ fn test_psbt_fee_rate_with_witness_utxo() {
     assert!(finalized);
 
     let finalized_fee_rate = psbt.fee_rate().unwrap();
-    assert!(finalized_fee_rate.as_sat_per_vb() >= expected_fee_rate);
-    assert!(finalized_fee_rate.as_sat_per_vb() < unfinalized_fee_rate.as_sat_per_vb());
+    assert!(finalized_fee_rate >= expected_fee_rate);
+    assert!(finalized_fee_rate < unfinalized_fee_rate);
 }
 
 #[test]
 fn test_psbt_fee_rate_with_nonwitness_utxo() {
     use psbt::PsbtUtils;
 
-    let expected_fee_rate = 1.2345;
+    let expected_fee_rate = feerate_unchecked(1.2345);
 
     let (mut wallet, _) = get_funded_wallet("pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
     let addr = wallet.get_address(New);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(FeeRate::from_sat_per_vb(expected_fee_rate));
+    builder.fee_rate(expected_fee_rate);
     let mut psbt = builder.finish().unwrap();
     let fee_amount = psbt.fee_amount();
     assert!(fee_amount.is_some());
@@ -123,21 +129,21 @@ fn test_psbt_fee_rate_with_nonwitness_utxo() {
     assert!(finalized);
 
     let finalized_fee_rate = psbt.fee_rate().unwrap();
-    assert!(finalized_fee_rate.as_sat_per_vb() >= expected_fee_rate);
-    assert!(finalized_fee_rate.as_sat_per_vb() < unfinalized_fee_rate.as_sat_per_vb());
+    assert!(finalized_fee_rate >= expected_fee_rate);
+    assert!(finalized_fee_rate < unfinalized_fee_rate);
 }
 
 #[test]
 fn test_psbt_fee_rate_with_missing_txout() {
     use psbt::PsbtUtils;
 
-    let expected_fee_rate = 1.2345;
+    let expected_fee_rate = feerate_unchecked(1.2345);
 
     let (mut wpkh_wallet,  _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
     let addr = wpkh_wallet.get_address(New);
     let mut builder = wpkh_wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(FeeRate::from_sat_per_vb(expected_fee_rate));
+    builder.fee_rate(expected_fee_rate);
     let mut wpkh_psbt = builder.finish().unwrap();
 
     wpkh_psbt.inputs[0].witness_utxo = None;
@@ -149,7 +155,7 @@ fn test_psbt_fee_rate_with_missing_txout() {
     let addr = pkh_wallet.get_address(New);
     let mut builder = pkh_wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(FeeRate::from_sat_per_vb(expected_fee_rate));
+    builder.fee_rate(expected_fee_rate);
     let mut pkh_psbt = builder.finish().unwrap();
 
     pkh_psbt.inputs[0].non_witness_utxo = None;
index 271b871632dfea655e2c99c89038ae3f63c9b668..a8a2c9282e1becbdf6de5196d50590e63c5e0e42 100644 (file)
@@ -9,11 +9,13 @@ use bdk::wallet::error::CreateTxError;
 use bdk::wallet::tx_builder::AddForeignUtxoError;
 use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet};
 use bdk::wallet::{AddressIndex::*, NewError};
-use bdk::{FeeRate, KeychainKind};
+use bdk::KeychainKind;
 use bdk_chain::COINBASE_MATURITY;
 use bdk_chain::{BlockId, ConfirmationTime};
 use bitcoin::hashes::Hash;
 use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
+use bitcoin::Amount;
+use bitcoin::FeeRate;
 use bitcoin::ScriptBuf;
 use bitcoin::{
     absolute, script::PushBytesBuf, taproot::TapNodeHash, Address, OutPoint, Sequence, Transaction,
@@ -21,7 +23,6 @@ use bitcoin::{
 };
 use bitcoin::{psbt, Network};
 use bitcoin::{BlockHash, Txid};
-
 mod common;
 use common::*;
 
@@ -55,6 +56,12 @@ fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
     receive_output(wallet, value, anchor)
 }
 
+fn feerate_unchecked(sat_vb: f64) -> FeeRate {
+    // 1 sat_vb / 4wu_vb * 1000kwu_wu = 250 sat_kwu
+    let sat_kwu = (sat_vb * 250.0).ceil() as u64;
+    FeeRate::from_sat_per_kwu(sat_kwu)
+}
+
 // The satisfaction size of a P2WPKH is 112 WU =
 // 1 (elements in witness) + 1 (OP_PUSH) + 33 (pk) + 1 (OP_PUSH) + 72 (signature + sighash) + 1*4 (script len)
 // On the witness itself, we have to push once for the pk (33WU) and once for signature + sighash (72WU), for
@@ -246,9 +253,11 @@ fn test_get_funded_wallet_tx_fee_rate() {
     // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
     // sats are the transaction fee.
 
-    // tx weight = 452 bytes, as vbytes = (452+3)/4 = 113
-    // fee rate (sats per vbyte) = fee / vbytes = 1000 / 113 = 8.8495575221 rounded to 8.849558
-    assert_eq!(tx_fee_rate.as_sat_per_vb(), 8.849558);
+    // tx weight = 452 wu, as vbytes = (452 + 3) / 4 = 113
+    // fee_rate (sats per kwu) = fee / weight = 1000sat / 0.452kwu = 2212
+    // fee_rate (sats per vbyte ceil) = fee / vsize = 1000sat / 113vb = 9
+    assert_eq!(tx_fee_rate.to_sat_per_kwu(), 2212);
+    assert_eq!(tx_fee_rate.to_sat_per_vb_ceil(), 9);
 }
 
 #[test]
@@ -302,11 +311,15 @@ macro_rules! assert_fee_rate {
 
         assert_eq!(fee_amount, $fees);
 
-        let tx_fee_rate = FeeRate::from_wu($fees, tx.weight());
-        let fee_rate = $fee_rate;
+        let tx_fee_rate = (Amount::from_sat(fee_amount) / tx.weight())
+            .to_sat_per_kwu();
+        let fee_rate = $fee_rate.to_sat_per_kwu();
+        let half_default = FeeRate::BROADCAST_MIN.checked_div(2)
+            .unwrap()
+            .to_sat_per_kwu();
 
         if !dust_change {
-            assert!(tx_fee_rate >= fee_rate && (tx_fee_rate - fee_rate).as_sat_per_vb().abs() < 0.5, "Expected fee rate of {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
+            assert!(tx_fee_rate >= fee_rate && tx_fee_rate - fee_rate < half_default, "Expected fee rate of {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
         } else {
             assert!(tx_fee_rate >= fee_rate, "Expected fee rate of at least {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
         }
@@ -647,7 +660,7 @@ fn test_create_tx_default_fee_rate() {
     let psbt = builder.finish().unwrap();
     let fee = check_fee!(wallet, psbt);
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::default(), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::BROADCAST_MIN, @add_signature);
 }
 
 #[test]
@@ -657,11 +670,11 @@ fn test_create_tx_custom_fee_rate() {
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), 25_000)
-        .fee_rate(FeeRate::from_sat_per_vb(5.0));
+        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
     let psbt = builder.finish().unwrap();
     let fee = check_fee!(wallet, psbt);
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
 }
 
 #[test]
@@ -753,7 +766,7 @@ fn test_create_tx_drain_to_dust_amount() {
     builder
         .drain_to(addr.script_pubkey())
         .drain_wallet()
-        .fee_rate(FeeRate::from_sat_per_vb(453.0));
+        .fee_rate(FeeRate::from_sat_per_vb_unchecked(454));
     builder.finish().unwrap();
 }
 
@@ -1499,7 +1512,7 @@ fn test_bump_fee_low_fee_rate() {
         .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb(1.0));
+    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(1));
     builder.finish().unwrap();
 }
 
@@ -1569,7 +1582,7 @@ fn test_bump_fee_reduce_change() {
         .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb(2.5)).enable_rbf();
+    builder.fee_rate(feerate_unchecked(2.5)).enable_rbf();
     let psbt = builder.finish().unwrap();
     let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
     let fee = check_fee!(wallet, psbt);
@@ -1600,7 +1613,7 @@ fn test_bump_fee_reduce_change() {
         sent_received.1
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), feerate_unchecked(2.5), @add_signature);
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_absolute(200);
@@ -1665,7 +1678,7 @@ fn test_bump_fee_reduce_single_recipient() {
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder
-        .fee_rate(FeeRate::from_sat_per_vb(2.5))
+        .fee_rate(feerate_unchecked(2.5))
         .allow_shrinking(addr.script_pubkey())
         .unwrap();
     let psbt = builder.finish().unwrap();
@@ -1679,7 +1692,7 @@ fn test_bump_fee_reduce_single_recipient() {
     assert_eq!(tx.output.len(), 1);
     assert_eq!(tx.output[0].value + fee.unwrap_or(0), sent_received.0);
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), feerate_unchecked(2.5), @add_signature);
 }
 
 #[test]
@@ -1774,7 +1787,7 @@ fn test_bump_fee_drain_wallet() {
         .drain_wallet()
         .allow_shrinking(addr.script_pubkey())
         .unwrap()
-        .fee_rate(FeeRate::from_sat_per_vb(5.0));
+        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
     let psbt = builder.finish().unwrap();
     let sent_received = wallet.sent_and_received(&psbt.extract_tx());
 
@@ -1837,7 +1850,7 @@ fn test_bump_fee_remove_output_manually_selected_only() {
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder
         .manually_selected_only()
-        .fee_rate(FeeRate::from_sat_per_vb(255.0));
+        .fee_rate(FeeRate::from_sat_per_vb_unchecked(255));
     builder.finish().unwrap();
 }
 
@@ -1878,7 +1891,7 @@ fn test_bump_fee_add_input() {
         .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
+    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50));
     let psbt = builder.finish().unwrap();
     let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
     let fee = check_fee!(wallet, psbt);
@@ -1905,7 +1918,7 @@ fn test_bump_fee_add_input() {
         sent_received.1
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
 }
 
 #[test]
@@ -1988,7 +2001,7 @@ fn test_bump_fee_no_change_add_input_and_change() {
     // now bump the fees without using `allow_shrinking`. the wallet should add an
     // extra input and a change output, and leave the original output untouched
     let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
+    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50));
     let psbt = builder.finish().unwrap();
     let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
     let fee = check_fee!(wallet, psbt);
@@ -2020,7 +2033,7 @@ fn test_bump_fee_no_change_add_input_and_change() {
         75_000 - original_send_all_amount - fee.unwrap_or(0)
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
 }
 
 #[test]
@@ -2065,7 +2078,7 @@ fn test_bump_fee_add_input_change_dust() {
     // two inputs (50k, 25k) and one output (45k) - epsilon
     // We use epsilon here to avoid asking for a slightly too high feerate
     let fee_abs = 50_000 + 25_000 - 45_000 - 10;
-    builder.fee_rate(FeeRate::from_wu(fee_abs, new_tx_weight));
+    builder.fee_rate(Amount::from_sat(fee_abs) / new_tx_weight);
     let psbt = builder.finish().unwrap();
     let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
     let fee = check_fee!(wallet, psbt);
@@ -2088,7 +2101,7 @@ fn test_bump_fee_add_input_change_dust() {
         45_000
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(140), @dust_change, @add_signature);
 }
 
 #[test]
@@ -2119,7 +2132,7 @@ fn test_bump_fee_force_add_input() {
     builder
         .add_utxo(incoming_op)
         .unwrap()
-        .fee_rate(FeeRate::from_sat_per_vb(5.0));
+        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
     let psbt = builder.finish().unwrap();
     let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
     let fee = check_fee!(wallet, psbt);
@@ -2147,7 +2160,7 @@ fn test_bump_fee_force_add_input() {
         sent_received.1
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
 }
 
 #[test]
@@ -2243,7 +2256,7 @@ fn test_bump_fee_unconfirmed_inputs_only() {
         .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 })
         .unwrap();
     let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb(25.0));
+    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(25));
     builder.finish().unwrap();
 }
 
@@ -2278,7 +2291,7 @@ fn test_bump_fee_unconfirmed_input() {
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder
-        .fee_rate(FeeRate::from_sat_per_vb(15.0))
+        .fee_rate(FeeRate::from_sat_per_vb_unchecked(15))
         .allow_shrinking(addr.script_pubkey())
         .unwrap();
     builder.finish().unwrap();
@@ -2298,7 +2311,7 @@ fn test_fee_amount_negative_drain_val() {
     let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
         .unwrap()
         .assume_checked();
-    let fee_rate = FeeRate::from_sat_per_vb(2.01);
+    let fee_rate = feerate_unchecked(2.01);
     let incoming_op = receive_output_in_latest_block(&mut wallet, 8859);
 
     let mut builder = wallet.build_tx();
@@ -3499,7 +3512,7 @@ fn test_fee_rate_sign_no_grinding_high_r() {
     // alright.
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
     let addr = wallet.get_address(New);
-    let fee_rate = FeeRate::from_sat_per_vb(1.0);
+    let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
     let mut builder = wallet.build_tx();
     let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
     builder
@@ -3565,7 +3578,7 @@ fn test_fee_rate_sign_grinding_low_r() {
     // signature is 70 bytes.
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
     let addr = wallet.get_address(New);
-    let fee_rate = FeeRate::from_sat_per_vb(1.0);
+    let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
index 079066878470dd0b997e5f6b801596f241e29ad7..4e2cd0c0f5a7a1d69c99ea491ca88bf312dd0bd8 100644 (file)
@@ -7,7 +7,7 @@
 //! # use bdk::signer::SignerOrdering;
 //! # use bdk_hwi::HWISigner;
 //! # use bdk::wallet::AddressIndex::New;
-//! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet};
+//! # use bdk::{KeychainKind, SignOptions, Wallet};
 //! # use hwi::HWIClient;
 //! # use std::sync::Arc;
 //! #