refactor(bdk)!: drop FeeRate from bdk::types
Adopt `bitcoin::FeeRate` throughout
This commit is contained in:
parent
fc637a7bcc
commit
004957dc29
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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 => {
|
||||
|
@ -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;
|
||||
//! #
|
||||
|
@ -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()
|
||||
};
|
||||
|
@ -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>(())
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
//! #
|
||||
|
Loading…
x
Reference in New Issue
Block a user