refactor(bdk)!: drop FeeRate from bdk::types

Adopt `bitcoin::FeeRate` throughout
This commit is contained in:
vmammal 2023-11-16 18:00:58 -05:00
parent fc637a7bcc
commit 004957dc29
No known key found for this signature in database
10 changed files with 151 additions and 298 deletions

View File

@ -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
})
}
}

View File

@ -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);
}
}

View 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 {
@ -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(
**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,
);

View 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 => {

View 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;
//! #

View 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()
};

View 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>(())

View 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;

View 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())

View 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;
//! #