From 004957dc2969171b7a37ce92d2cdb0e8f22b6da5 Mon Sep 17 00:00:00 2001 From: vmammal Date: Thu, 16 Nov 2023 18:00:58 -0500 Subject: [PATCH] refactor(bdk)!: drop FeeRate from bdk::types Adopt `bitcoin::FeeRate` throughout --- crates/bdk/src/psbt/mod.rs | 5 +- crates/bdk/src/types.rs | 170 +----------------------- crates/bdk/src/wallet/coin_selection.rs | 92 +++++++------ crates/bdk/src/wallet/error.rs | 10 +- crates/bdk/src/wallet/hardwaresigner.rs | 2 +- crates/bdk/src/wallet/mod.rs | 35 +++-- crates/bdk/src/wallet/tx_builder.rs | 26 ++-- crates/bdk/tests/psbt.rs | 30 +++-- crates/bdk/tests/wallet.rs | 77 ++++++----- crates/hwi/src/lib.rs | 2 +- 10 files changed, 151 insertions(+), 298 deletions(-) diff --git a/crates/bdk/src/psbt/mod.rs b/crates/bdk/src/psbt/mod.rs index 796b8618b..260669175 100644 --- a/crates/bdk/src/psbt/mod.rs +++ b/crates/bdk/src/psbt/mod.rs @@ -11,9 +11,10 @@ //! 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 }) } } diff --git a/crates/bdk/src/types.rs b/crates/bdk/src/types.rs index 5f2173596..3bde290e4 100644 --- a/crates/bdk/src/types.rs +++ b/crates/bdk/src/types.rs @@ -11,11 +11,10 @@ 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); - } -} diff --git a/crates/bdk/src/wallet/coin_selection.rs b/crates/bdk/src/wallet/coin_selection.rs index ac6084cfc..5383e5525 100644 --- a/crates/bdk/src/wallet/coin_selection.rs +++ b/crates/bdk/src/wallet/coin_selection.rs @@ -41,7 +41,7 @@ //! &self, //! required_utxos: Vec, //! optional_utxos: Vec, -//! fee_rate: bdk::FeeRate, +//! fee_rate: FeeRate, //! target_amount: u64, //! drain_script: &Script, //! ) -> Result { @@ -61,7 +61,7 @@ //! }, //! ) //! .collect::>(); -//! 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 { @@ -101,10 +101,10 @@ //! ``` 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 { @@ -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 = 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 = 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 = 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, ); diff --git a/crates/bdk/src/wallet/error.rs b/crates/bdk/src/wallet/error.rs index db58fef06..548dc783f 100644 --- a/crates/bdk/src/wallet/error.rs +++ b/crates/bdk/src/wallet/error.rs @@ -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

{ }, /// 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 => { diff --git a/crates/bdk/src/wallet/hardwaresigner.rs b/crates/bdk/src/wallet/hardwaresigner.rs index aec49297c..8b11e784a 100644 --- a/crates/bdk/src/wallet/hardwaresigner.rs +++ b/crates/bdk/src/wallet/hardwaresigner.rs @@ -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; //! # diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index 4db035fb6..3b133602f 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -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 Wallet { /// ``` /// [`insert_txout`]: Self::insert_txout pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result { - 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 Wallet { (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 Wallet { 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 Wallet { // 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 Wallet { /// 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 Wallet { utxos: original_utxos, bumping_fee: Some(tx_builder::PreviousFee { absolute: fee, - rate: fee_rate.as_sat_per_vb(), + rate: fee_rate, }), ..Default::default() }; diff --git a/crates/bdk/src/wallet/tx_builder.rs b/crates/bdk/src/wallet/tx_builder.rs index 45d215fd8..aa81d1f35 100644 --- a/crates/bdk/src/wallet/tx_builder.rs +++ b/crates/bdk/src/wallet/tx_builder.rs @@ -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>(()) diff --git a/crates/bdk/tests/psbt.rs b/crates/bdk/tests/psbt.rs index 3c4968bfe..e3ace9db0 100644 --- a/crates/bdk/tests/psbt.rs +++ b/crates/bdk/tests/psbt.rs @@ -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; diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index 271b87163..a8a2c9282 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -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()) diff --git a/crates/hwi/src/lib.rs b/crates/hwi/src/lib.rs index 079066878..4e2cd0c0f 100644 --- a/crates/hwi/src/lib.rs +++ b/crates/hwi/src/lib.rs @@ -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; //! #