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.
|
//! Additional functions on the `rust-bitcoin` `PartiallySignedTransaction` structure.
|
||||||
|
|
||||||
use crate::FeeRate;
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bitcoin::psbt::PartiallySignedTransaction as Psbt;
|
use bitcoin::psbt::PartiallySignedTransaction as Psbt;
|
||||||
|
use bitcoin::Amount;
|
||||||
|
use bitcoin::FeeRate;
|
||||||
use bitcoin::TxOut;
|
use bitcoin::TxOut;
|
||||||
|
|
||||||
// TODO upstream the functions here to `rust-bitcoin`?
|
// TODO upstream the functions here to `rust-bitcoin`?
|
||||||
@ -65,7 +66,7 @@ impl PsbtUtils for Psbt {
|
|||||||
let fee_amount = self.fee_amount();
|
let fee_amount = self.fee_amount();
|
||||||
fee_amount.map(|fee| {
|
fee_amount.map(|fee| {
|
||||||
let weight = self.clone().extract_tx().weight();
|
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 alloc::boxed::Box;
|
||||||
use core::convert::AsRef;
|
use core::convert::AsRef;
|
||||||
use core::ops::Sub;
|
|
||||||
|
|
||||||
use bdk_chain::ConfirmationTime;
|
use bdk_chain::ConfirmationTime;
|
||||||
use bitcoin::blockdata::transaction::{OutPoint, Sequence, TxOut};
|
use bitcoin::blockdata::transaction::{OutPoint, Sequence, TxOut};
|
||||||
use bitcoin::{psbt, Weight};
|
use bitcoin::psbt;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
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.
|
/// Trait implemented by types that can be used to measure weight units.
|
||||||
pub trait Vbytes {
|
pub trait Vbytes {
|
||||||
/// Convert weight units to virtual bytes.
|
/// 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,
|
//! &self,
|
||||||
//! required_utxos: Vec<WeightedUtxo>,
|
//! required_utxos: Vec<WeightedUtxo>,
|
||||||
//! optional_utxos: Vec<WeightedUtxo>,
|
//! optional_utxos: Vec<WeightedUtxo>,
|
||||||
//! fee_rate: bdk::FeeRate,
|
//! fee_rate: FeeRate,
|
||||||
//! target_amount: u64,
|
//! target_amount: u64,
|
||||||
//! drain_script: &Script,
|
//! drain_script: &Script,
|
||||||
//! ) -> Result<CoinSelectionResult, coin_selection::Error> {
|
//! ) -> Result<CoinSelectionResult, coin_selection::Error> {
|
||||||
@ -61,7 +61,7 @@
|
|||||||
//! },
|
//! },
|
||||||
//! )
|
//! )
|
||||||
//! .collect::<Vec<_>>();
|
//! .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;
|
//! let amount_needed_with_fees = additional_fees + target_amount;
|
||||||
//! if selected_amount < amount_needed_with_fees {
|
//! if selected_amount < amount_needed_with_fees {
|
||||||
//! return Err(coin_selection::Error::InsufficientFunds {
|
//! return Err(coin_selection::Error::InsufficientFunds {
|
||||||
@ -101,10 +101,10 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::chain::collections::HashSet;
|
use crate::chain::collections::HashSet;
|
||||||
use crate::types::FeeRate;
|
|
||||||
use crate::wallet::utils::IsDust;
|
use crate::wallet::utils::IsDust;
|
||||||
use crate::Utxo;
|
use crate::Utxo;
|
||||||
use crate::WeightedUtxo;
|
use crate::WeightedUtxo;
|
||||||
|
use bitcoin::FeeRate;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bitcoin::consensus::encode::serialize;
|
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 {
|
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)
|
// drain_output_len = size(len(script_pubkey)) + len(script_pubkey) + size(output_value)
|
||||||
let drain_output_len = serialize(drain_script).len() + 8usize;
|
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);
|
let drain_val = remaining_amount.saturating_sub(change_fee);
|
||||||
|
|
||||||
if drain_val.is_dust(drain_script) {
|
if drain_val.is_dust(drain_script) {
|
||||||
@ -344,9 +345,12 @@ fn select_sorted_utxos(
|
|||||||
(&mut selected_amount, &mut fee_amount),
|
(&mut selected_amount, &mut fee_amount),
|
||||||
|(selected_amount, fee_amount), (must_use, weighted_utxo)| {
|
|(selected_amount, fee_amount), (must_use, weighted_utxo)| {
|
||||||
if must_use || **selected_amount < target_amount + **fee_amount {
|
if must_use || **selected_amount < target_amount + **fee_amount {
|
||||||
**fee_amount += fee_rate.fee_wu(Weight::from_wu(
|
**fee_amount += (fee_rate
|
||||||
(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
|
* Weight::from_wu(
|
||||||
));
|
(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
|
||||||
|
))
|
||||||
|
.to_sat();
|
||||||
|
|
||||||
**selected_amount += weighted_utxo.utxo.txout().value;
|
**selected_amount += weighted_utxo.utxo.txout().value;
|
||||||
Some(weighted_utxo.utxo)
|
Some(weighted_utxo.utxo)
|
||||||
} else {
|
} else {
|
||||||
@ -387,9 +391,10 @@ struct OutputGroup {
|
|||||||
|
|
||||||
impl OutputGroup {
|
impl OutputGroup {
|
||||||
fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self {
|
fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self {
|
||||||
let fee = fee_rate.fee_wu(Weight::from_wu(
|
let fee = (fee_rate
|
||||||
(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
|
* 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;
|
let effective_value = weighted_utxo.utxo.txout().value as i64 - fee as i64;
|
||||||
OutputGroup {
|
OutputGroup {
|
||||||
weighted_utxo,
|
weighted_utxo,
|
||||||
@ -456,7 +461,8 @@ impl CoinSelectionAlgorithm for BranchAndBoundCoinSelection {
|
|||||||
.iter()
|
.iter()
|
||||||
.fold(0, |acc, x| acc + x.effective_value);
|
.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
|
// `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
|
// 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_value: i64,
|
||||||
mut curr_available_value: i64,
|
mut curr_available_value: i64,
|
||||||
target_amount: i64,
|
target_amount: i64,
|
||||||
cost_of_change: f32,
|
cost_of_change: u64,
|
||||||
drain_script: &Script,
|
drain_script: &Script,
|
||||||
fee_rate: FeeRate,
|
fee_rate: FeeRate,
|
||||||
) -> Result<CoinSelectionResult, Error> {
|
) -> Result<CoinSelectionResult, Error> {
|
||||||
@ -893,7 +899,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -914,7 +920,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -935,7 +941,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -957,7 +963,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -975,7 +981,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1000.0),
|
FeeRate::from_sat_per_vb_unchecked(1000),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -992,7 +998,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1013,7 +1019,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1034,7 +1040,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1056,7 +1062,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1075,7 +1081,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1000.0),
|
FeeRate::from_sat_per_vb_unchecked(1000),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1096,7 +1102,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1117,7 +1123,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
utxos.clone(),
|
utxos.clone(),
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1138,7 +1144,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1175,7 +1181,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
required,
|
required,
|
||||||
optional,
|
optional,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1197,7 +1203,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1215,7 +1221,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1000.0),
|
FeeRate::from_sat_per_vb_unchecked(1000),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1232,7 +1238,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(1.0),
|
FeeRate::from_sat_per_vb_unchecked(1),
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1258,7 +1264,7 @@ mod test {
|
|||||||
.coin_select(
|
.coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
optional_utxos,
|
optional_utxos,
|
||||||
FeeRate::from_sat_per_vb(0.0),
|
FeeRate::ZERO,
|
||||||
target_amount,
|
target_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)
|
)
|
||||||
@ -1270,7 +1276,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "BnBNoExactMatch")]
|
#[should_panic(expected = "BnBNoExactMatch")]
|
||||||
fn test_bnb_function_no_exact_match() {
|
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()
|
let utxos: Vec<OutputGroup> = get_test_utxos()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| OutputGroup::new(u, fee_rate))
|
.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 curr_available_value = utxos.iter().fold(0, |acc, x| acc + x.effective_value);
|
||||||
|
|
||||||
let size_of_change = 31;
|
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 drain_script = ScriptBuf::default();
|
||||||
let target_amount = 20_000 + FEE_AMOUNT;
|
let target_amount = 20_000 + FEE_AMOUNT;
|
||||||
@ -1300,7 +1306,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "BnBTotalTriesExceeded")]
|
#[should_panic(expected = "BnBTotalTriesExceeded")]
|
||||||
fn test_bnb_function_tries_exceeded() {
|
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)
|
let utxos: Vec<OutputGroup> = generate_same_value_utxos(100_000, 100_000)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| OutputGroup::new(u, fee_rate))
|
.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 curr_available_value = utxos.iter().fold(0, |acc, x| acc + x.effective_value);
|
||||||
|
|
||||||
let size_of_change = 31;
|
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 target_amount = 20_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let drain_script = ScriptBuf::default();
|
let drain_script = ScriptBuf::default();
|
||||||
@ -1331,9 +1337,9 @@ mod test {
|
|||||||
// The match won't be exact but still in the range
|
// The match won't be exact but still in the range
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bnb_function_almost_exact_match_with_fees() {
|
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 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)
|
let utxos: Vec<_> = generate_same_value_utxos(50_000, 10)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -1346,7 +1352,7 @@ mod test {
|
|||||||
|
|
||||||
// 2*(value of 1 utxo) - 2*(1 utxo fees with 1.0sat/vbyte fee rate) -
|
// 2*(value of 1 utxo) - 2*(1 utxo fees with 1.0sat/vbyte fee rate) -
|
||||||
// cost_of_change + 5.
|
// 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();
|
let drain_script = ScriptBuf::default();
|
||||||
|
|
||||||
@ -1371,7 +1377,7 @@ mod test {
|
|||||||
fn test_bnb_function_exact_match_more_utxos() {
|
fn test_bnb_function_exact_match_more_utxos() {
|
||||||
let seed = [0; 32];
|
let seed = [0; 32];
|
||||||
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
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 {
|
for _ in 0..200 {
|
||||||
let optional_utxos: Vec<_> = generate_random_utxos(&mut rng, 40)
|
let optional_utxos: Vec<_> = generate_random_utxos(&mut rng, 40)
|
||||||
@ -1397,7 +1403,7 @@ mod test {
|
|||||||
curr_value,
|
curr_value,
|
||||||
curr_available_value,
|
curr_available_value,
|
||||||
target_amount,
|
target_amount,
|
||||||
0.0,
|
0,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
fee_rate,
|
fee_rate,
|
||||||
)
|
)
|
||||||
@ -1413,7 +1419,7 @@ mod test {
|
|||||||
let mut utxos = generate_random_utxos(&mut rng, 300);
|
let mut utxos = generate_random_utxos(&mut rng, 300);
|
||||||
let target_amount = sum_random_utxos(&mut rng, &mut utxos) + FEE_AMOUNT;
|
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
|
let utxos: Vec<OutputGroup> = utxos
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| OutputGroup::new(u, fee_rate))
|
.map(|u| OutputGroup::new(u, fee_rate))
|
||||||
@ -1442,7 +1448,7 @@ mod test {
|
|||||||
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
||||||
vec![],
|
vec![],
|
||||||
utxos,
|
utxos,
|
||||||
FeeRate::from_sat_per_vb(10.0),
|
FeeRate::from_sat_per_vb_unchecked(10),
|
||||||
500_000,
|
500_000,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
);
|
);
|
||||||
@ -1468,7 +1474,7 @@ mod test {
|
|||||||
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
||||||
required,
|
required,
|
||||||
optional,
|
optional,
|
||||||
FeeRate::from_sat_per_vb(10.0),
|
FeeRate::from_sat_per_vb_unchecked(10),
|
||||||
500_000,
|
500_000,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
);
|
);
|
||||||
@ -1490,7 +1496,7 @@ mod test {
|
|||||||
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
||||||
utxos,
|
utxos,
|
||||||
vec![],
|
vec![],
|
||||||
FeeRate::from_sat_per_vb(10_000.0),
|
FeeRate::from_sat_per_vb_unchecked(10_000),
|
||||||
500_000,
|
500_000,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
);
|
);
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
use crate::descriptor::policy::PolicyError;
|
use crate::descriptor::policy::PolicyError;
|
||||||
use crate::descriptor::DescriptorError;
|
use crate::descriptor::DescriptorError;
|
||||||
use crate::wallet::coin_selection;
|
use crate::wallet::coin_selection;
|
||||||
use crate::{descriptor, FeeRate, KeychainKind};
|
use crate::{descriptor, KeychainKind};
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use bitcoin::{absolute, psbt, OutPoint, Sequence, Txid};
|
use bitcoin::{absolute, psbt, OutPoint, Sequence, Txid};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
@ -83,8 +83,8 @@ pub enum CreateTxError<P> {
|
|||||||
},
|
},
|
||||||
/// When bumping a tx the fee rate requested is lower than required
|
/// When bumping a tx the fee rate requested is lower than required
|
||||||
FeeRateTooLow {
|
FeeRateTooLow {
|
||||||
/// Required fee rate (satoshi/vbyte)
|
/// Required fee rate
|
||||||
required: FeeRate,
|
required: bitcoin::FeeRate,
|
||||||
},
|
},
|
||||||
/// `manually_selected_only` option is selected but no utxo has been passed
|
/// `manually_selected_only` option is selected but no utxo has been passed
|
||||||
NoUtxosSelected,
|
NoUtxosSelected,
|
||||||
@ -168,8 +168,8 @@ where
|
|||||||
CreateTxError::FeeRateTooLow { required } => {
|
CreateTxError::FeeRateTooLow { required } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Fee rate too low: required {} sat/vbyte",
|
"Fee rate too low: required {} sat/kwu",
|
||||||
required.as_sat_per_vb()
|
required.to_sat_per_kwu()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CreateTxError::NoUtxosSelected => {
|
CreateTxError::NoUtxosSelected => {
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
//! # use bdk::signer::SignerOrdering;
|
//! # use bdk::signer::SignerOrdering;
|
||||||
//! # use bdk::wallet::hardwaresigner::HWISigner;
|
//! # use bdk::wallet::hardwaresigner::HWISigner;
|
||||||
//! # use bdk::wallet::AddressIndex::New;
|
//! # use bdk::wallet::AddressIndex::New;
|
||||||
//! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet};
|
//! # use bdk::{KeychainKind, SignOptions, Wallet};
|
||||||
//! # use hwi::HWIClient;
|
//! # use hwi::HWIClient;
|
||||||
//! # use std::sync::Arc;
|
//! # use std::sync::Arc;
|
||||||
//! #
|
//! #
|
||||||
|
@ -33,7 +33,7 @@ use bdk_chain::{
|
|||||||
use bitcoin::secp256k1::{All, Secp256k1};
|
use bitcoin::secp256k1::{All, Secp256k1};
|
||||||
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
||||||
use bitcoin::{
|
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,
|
Txid, Weight, Witness,
|
||||||
};
|
};
|
||||||
use bitcoin::{consensus::encode::serialize, BlockHash};
|
use bitcoin::{consensus::encode::serialize, BlockHash};
|
||||||
@ -986,10 +986,8 @@ impl<D> Wallet<D> {
|
|||||||
/// ```
|
/// ```
|
||||||
/// [`insert_txout`]: Self::insert_txout
|
/// [`insert_txout`]: Self::insert_txout
|
||||||
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
|
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
|
||||||
self.calculate_fee(tx).map(|fee| {
|
self.calculate_fee(tx)
|
||||||
let weight = tx.weight();
|
.map(|fee| bitcoin::Amount::from_sat(fee) / tx.weight())
|
||||||
FeeRate::from_wu(fee, weight)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the `tx`'s sent and received amounts (in satoshis).
|
/// Compute the `tx`'s sent and received amounts (in satoshis).
|
||||||
@ -1432,32 +1430,31 @@ impl<D> Wallet<D> {
|
|||||||
(Some(rbf), _) => rbf.get_value(),
|
(Some(rbf), _) => rbf.get_value(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (fee_rate, mut fee_amount) = match params
|
let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
|
||||||
.fee_policy
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&FeePolicy::FeeRate(FeeRate::default()))
|
|
||||||
{
|
|
||||||
//FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
|
//FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
|
||||||
FeePolicy::FeeAmount(fee) => {
|
FeePolicy::FeeAmount(fee) => {
|
||||||
if let Some(previous_fee) = params.bumping_fee {
|
if let Some(previous_fee) = params.bumping_fee {
|
||||||
if *fee < previous_fee.absolute {
|
if fee < previous_fee.absolute {
|
||||||
return Err(CreateTxError::FeeTooLow {
|
return Err(CreateTxError::FeeTooLow {
|
||||||
required: previous_fee.absolute,
|
required: previous_fee.absolute,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(FeeRate::from_sat_per_vb(0.0), *fee)
|
(FeeRate::ZERO, fee)
|
||||||
}
|
}
|
||||||
FeePolicy::FeeRate(rate) => {
|
FeePolicy::FeeRate(rate) => {
|
||||||
if let Some(previous_fee) = params.bumping_fee {
|
if let Some(previous_fee) = params.bumping_fee {
|
||||||
let required_feerate = FeeRate::from_sat_per_vb(previous_fee.rate + 1.0);
|
let required_feerate = FeeRate::from_sat_per_kwu(
|
||||||
if *rate < required_feerate {
|
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 {
|
return Err(CreateTxError::FeeRateTooLow {
|
||||||
required: required_feerate,
|
required: required_feerate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*rate, 0)
|
(rate, 0)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1500,7 +1497,7 @@ impl<D> Wallet<D> {
|
|||||||
outgoing += value;
|
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,
|
// Segwit transactions' header is 2WU larger than legacy txs' header,
|
||||||
// as they contain a witness marker (1WU) and a witness flag (1WU) (see BIP144).
|
// 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.
|
// 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
|
// If, instead, we undershoot, we may end up with a feerate lower than the requested one
|
||||||
// - we might come up with non broadcastable txs!
|
// - 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
|
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
|
||||||
&& internal_descriptor.is_none()
|
&& internal_descriptor.is_none()
|
||||||
@ -1652,7 +1649,7 @@ impl<D> Wallet<D> {
|
|||||||
/// let mut psbt = {
|
/// let mut psbt = {
|
||||||
/// let mut builder = wallet.build_fee_bump(tx.txid())?;
|
/// let mut builder = wallet.build_fee_bump(tx.txid())?;
|
||||||
/// builder
|
/// builder
|
||||||
/// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0));
|
/// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
|
||||||
/// builder.finish()?
|
/// builder.finish()?
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
@ -1780,7 +1777,7 @@ impl<D> Wallet<D> {
|
|||||||
utxos: original_utxos,
|
utxos: original_utxos,
|
||||||
bumping_fee: Some(tx_builder::PreviousFee {
|
bumping_fee: Some(tx_builder::PreviousFee {
|
||||||
absolute: fee,
|
absolute: fee,
|
||||||
rate: fee_rate.as_sat_per_vb(),
|
rate: fee_rate,
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
//! // Create a transaction with one output to `to_address` of 50_000 satoshi
|
//! // Create a transaction with one output to `to_address` of 50_000 satoshi
|
||||||
//! .add_recipient(to_address.script_pubkey(), 50_000)
|
//! .add_recipient(to_address.script_pubkey(), 50_000)
|
||||||
//! // With a custom fee rate of 5.0 satoshi/vbyte
|
//! // 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
|
//! // Only spend non-change outputs
|
||||||
//! .do_not_spend_change()
|
//! .do_not_spend_change()
|
||||||
//! // Turn on RBF signaling
|
//! // 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::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
||||||
use super::ChangeSet;
|
use super::ChangeSet;
|
||||||
use crate::types::{FeeRate, KeychainKind, LocalOutput, WeightedUtxo};
|
use crate::types::{KeychainKind, LocalOutput, WeightedUtxo};
|
||||||
use crate::wallet::CreateTxError;
|
use crate::wallet::CreateTxError;
|
||||||
use crate::{Utxo, Wallet};
|
use crate::{Utxo, Wallet};
|
||||||
|
use bitcoin::FeeRate;
|
||||||
/// Context in which the [`TxBuilder`] is valid
|
/// Context in which the [`TxBuilder`] is valid
|
||||||
pub trait TxBuilderContext: core::fmt::Debug + Default + Clone {}
|
pub trait TxBuilderContext: core::fmt::Debug + Default + Clone {}
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ pub(crate) struct TxParams {
|
|||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) struct PreviousFee {
|
pub(crate) struct PreviousFee {
|
||||||
pub absolute: u64,
|
pub absolute: u64,
|
||||||
pub rate: f32,
|
pub rate: FeeRate,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@ -174,7 +174,7 @@ pub(crate) enum FeePolicy {
|
|||||||
|
|
||||||
impl Default for FeePolicy {
|
impl Default for FeePolicy {
|
||||||
fn default() -> Self {
|
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
|
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
||||||
impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
||||||
/// Set a custom fee rate
|
/// 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 method sets the mining fee paid by the transaction as a rate on its size.
|
||||||
/// This rate is internally expressed in satoshis-per-virtual-bytes (sats/vB) using FeeRate::from_sat_per_vb, but can also be set by:
|
/// This means that the total fee paid is equal to `fee_rate` times the size
|
||||||
/// * sats/kvB (1000 sats/kvB == 1 sats/vB) using FeeRate::from_sat_per_kvb
|
/// of the transaction. Default is 1 sat/vB in accordance with Bitcoin Core's default
|
||||||
/// * btc/kvB (0.00001000 btc/kvB == 1 sats/vB) using FeeRate::from_btc_per_kvb
|
/// relay policy.
|
||||||
/// * sats/kwu (250 sats/kwu == 1 sats/vB) using FeeRate::from_sat_per_kwu
|
|
||||||
/// Default is 1 sat/vB (see min_relay_fee)
|
|
||||||
///
|
///
|
||||||
/// Note that this is really a minimum feerate -- it's possible to
|
/// Note that this is really a minimum feerate -- it's possible to
|
||||||
/// overshoot it slightly since adding a change output to drain the remaining
|
/// 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()
|
/// .drain_wallet()
|
||||||
/// // Send the excess (which is all the coins minus the fee) to this address.
|
/// // Send the excess (which is all the coins minus the fee) to this address.
|
||||||
/// .drain_to(to_address.script_pubkey())
|
/// .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();
|
/// .enable_rbf();
|
||||||
/// let psbt = tx_builder.finish()?;
|
/// let psbt = tx_builder.finish()?;
|
||||||
/// # Ok::<(), anyhow::Error>(())
|
/// # Ok::<(), anyhow::Error>(())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use bdk::bitcoin::TxIn;
|
use bdk::bitcoin::TxIn;
|
||||||
use bdk::wallet::AddressIndex;
|
use bdk::wallet::AddressIndex;
|
||||||
use bdk::wallet::AddressIndex::New;
|
use bdk::wallet::AddressIndex::New;
|
||||||
use bdk::{psbt, FeeRate, SignOptions};
|
use bdk::{psbt, SignOptions};
|
||||||
use bitcoin::psbt::PartiallySignedTransaction as Psbt;
|
use bitcoin::psbt::PartiallySignedTransaction as Psbt;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
mod common;
|
mod common;
|
||||||
@ -10,6 +10,12 @@ use common::*;
|
|||||||
// from bip 174
|
// from bip 174
|
||||||
const PSBT_STR: &str = "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA";
|
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]
|
#[test]
|
||||||
#[should_panic(expected = "InputIndexOutOfRange")]
|
#[should_panic(expected = "InputIndexOutOfRange")]
|
||||||
fn test_psbt_malformed_psbt_input_legacy() {
|
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() {
|
fn test_psbt_fee_rate_with_witness_utxo() {
|
||||||
use psbt::PsbtUtils;
|
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 (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||||
let addr = wallet.get_address(New);
|
let addr = wallet.get_address(New);
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
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 mut psbt = builder.finish().unwrap();
|
||||||
let fee_amount = psbt.fee_amount();
|
let fee_amount = psbt.fee_amount();
|
||||||
assert!(fee_amount.is_some());
|
assert!(fee_amount.is_some());
|
||||||
@ -99,21 +105,21 @@ fn test_psbt_fee_rate_with_witness_utxo() {
|
|||||||
assert!(finalized);
|
assert!(finalized);
|
||||||
|
|
||||||
let finalized_fee_rate = psbt.fee_rate().unwrap();
|
let finalized_fee_rate = psbt.fee_rate().unwrap();
|
||||||
assert!(finalized_fee_rate.as_sat_per_vb() >= expected_fee_rate);
|
assert!(finalized_fee_rate >= expected_fee_rate);
|
||||||
assert!(finalized_fee_rate.as_sat_per_vb() < unfinalized_fee_rate.as_sat_per_vb());
|
assert!(finalized_fee_rate < unfinalized_fee_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_psbt_fee_rate_with_nonwitness_utxo() {
|
fn test_psbt_fee_rate_with_nonwitness_utxo() {
|
||||||
use psbt::PsbtUtils;
|
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 (mut wallet, _) = get_funded_wallet("pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||||
let addr = wallet.get_address(New);
|
let addr = wallet.get_address(New);
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
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 mut psbt = builder.finish().unwrap();
|
||||||
let fee_amount = psbt.fee_amount();
|
let fee_amount = psbt.fee_amount();
|
||||||
assert!(fee_amount.is_some());
|
assert!(fee_amount.is_some());
|
||||||
@ -123,21 +129,21 @@ fn test_psbt_fee_rate_with_nonwitness_utxo() {
|
|||||||
assert!(finalized);
|
assert!(finalized);
|
||||||
|
|
||||||
let finalized_fee_rate = psbt.fee_rate().unwrap();
|
let finalized_fee_rate = psbt.fee_rate().unwrap();
|
||||||
assert!(finalized_fee_rate.as_sat_per_vb() >= expected_fee_rate);
|
assert!(finalized_fee_rate >= expected_fee_rate);
|
||||||
assert!(finalized_fee_rate.as_sat_per_vb() < unfinalized_fee_rate.as_sat_per_vb());
|
assert!(finalized_fee_rate < unfinalized_fee_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_psbt_fee_rate_with_missing_txout() {
|
fn test_psbt_fee_rate_with_missing_txout() {
|
||||||
use psbt::PsbtUtils;
|
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 (mut wpkh_wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||||
let addr = wpkh_wallet.get_address(New);
|
let addr = wpkh_wallet.get_address(New);
|
||||||
let mut builder = wpkh_wallet.build_tx();
|
let mut builder = wpkh_wallet.build_tx();
|
||||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
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();
|
let mut wpkh_psbt = builder.finish().unwrap();
|
||||||
|
|
||||||
wpkh_psbt.inputs[0].witness_utxo = None;
|
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 addr = pkh_wallet.get_address(New);
|
||||||
let mut builder = pkh_wallet.build_tx();
|
let mut builder = pkh_wallet.build_tx();
|
||||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
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();
|
let mut pkh_psbt = builder.finish().unwrap();
|
||||||
|
|
||||||
pkh_psbt.inputs[0].non_witness_utxo = None;
|
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::tx_builder::AddForeignUtxoError;
|
||||||
use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet};
|
use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet};
|
||||||
use bdk::wallet::{AddressIndex::*, NewError};
|
use bdk::wallet::{AddressIndex::*, NewError};
|
||||||
use bdk::{FeeRate, KeychainKind};
|
use bdk::KeychainKind;
|
||||||
use bdk_chain::COINBASE_MATURITY;
|
use bdk_chain::COINBASE_MATURITY;
|
||||||
use bdk_chain::{BlockId, ConfirmationTime};
|
use bdk_chain::{BlockId, ConfirmationTime};
|
||||||
use bitcoin::hashes::Hash;
|
use bitcoin::hashes::Hash;
|
||||||
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
||||||
|
use bitcoin::Amount;
|
||||||
|
use bitcoin::FeeRate;
|
||||||
use bitcoin::ScriptBuf;
|
use bitcoin::ScriptBuf;
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
absolute, script::PushBytesBuf, taproot::TapNodeHash, Address, OutPoint, Sequence, Transaction,
|
absolute, script::PushBytesBuf, taproot::TapNodeHash, Address, OutPoint, Sequence, Transaction,
|
||||||
@ -21,7 +23,6 @@ use bitcoin::{
|
|||||||
};
|
};
|
||||||
use bitcoin::{psbt, Network};
|
use bitcoin::{psbt, Network};
|
||||||
use bitcoin::{BlockHash, Txid};
|
use bitcoin::{BlockHash, Txid};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
|
||||||
@ -55,6 +56,12 @@ fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
|
|||||||
receive_output(wallet, value, anchor)
|
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 =
|
// 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)
|
// 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
|
// 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
|
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
|
||||||
// sats are the transaction fee.
|
// sats are the transaction fee.
|
||||||
|
|
||||||
// tx weight = 452 bytes, as vbytes = (452+3)/4 = 113
|
// tx weight = 452 wu, as vbytes = (452 + 3) / 4 = 113
|
||||||
// fee rate (sats per vbyte) = fee / vbytes = 1000 / 113 = 8.8495575221 rounded to 8.849558
|
// fee_rate (sats per kwu) = fee / weight = 1000sat / 0.452kwu = 2212
|
||||||
assert_eq!(tx_fee_rate.as_sat_per_vb(), 8.849558);
|
// 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]
|
#[test]
|
||||||
@ -302,11 +311,15 @@ macro_rules! assert_fee_rate {
|
|||||||
|
|
||||||
assert_eq!(fee_amount, $fees);
|
assert_eq!(fee_amount, $fees);
|
||||||
|
|
||||||
let tx_fee_rate = FeeRate::from_wu($fees, tx.weight());
|
let tx_fee_rate = (Amount::from_sat(fee_amount) / tx.weight())
|
||||||
let fee_rate = $fee_rate;
|
.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 {
|
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 {
|
} else {
|
||||||
assert!(tx_fee_rate >= fee_rate, "Expected fee rate of at least {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
|
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 psbt = builder.finish().unwrap();
|
||||||
let fee = check_fee!(wallet, psbt);
|
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]
|
#[test]
|
||||||
@ -657,11 +670,11 @@ fn test_create_tx_custom_fee_rate() {
|
|||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
builder
|
builder
|
||||||
.add_recipient(addr.script_pubkey(), 25_000)
|
.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 psbt = builder.finish().unwrap();
|
||||||
let fee = check_fee!(wallet, psbt);
|
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]
|
#[test]
|
||||||
@ -753,7 +766,7 @@ fn test_create_tx_drain_to_dust_amount() {
|
|||||||
builder
|
builder
|
||||||
.drain_to(addr.script_pubkey())
|
.drain_to(addr.script_pubkey())
|
||||||
.drain_wallet()
|
.drain_wallet()
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(453.0));
|
.fee_rate(FeeRate::from_sat_per_vb_unchecked(454));
|
||||||
builder.finish().unwrap();
|
builder.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1499,7 +1512,7 @@ fn test_bump_fee_low_fee_rate() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(txid).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();
|
builder.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1569,7 +1582,7 @@ fn test_bump_fee_reduce_change() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(txid).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 psbt = builder.finish().unwrap();
|
||||||
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
||||||
let fee = check_fee!(wallet, psbt);
|
let fee = check_fee!(wallet, psbt);
|
||||||
@ -1600,7 +1613,7 @@ fn test_bump_fee_reduce_change() {
|
|||||||
sent_received.1
|
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();
|
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
||||||
builder.fee_absolute(200);
|
builder.fee_absolute(200);
|
||||||
@ -1665,7 +1678,7 @@ fn test_bump_fee_reduce_single_recipient() {
|
|||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
||||||
builder
|
builder
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(2.5))
|
.fee_rate(feerate_unchecked(2.5))
|
||||||
.allow_shrinking(addr.script_pubkey())
|
.allow_shrinking(addr.script_pubkey())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let psbt = builder.finish().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.len(), 1);
|
||||||
assert_eq!(tx.output[0].value + fee.unwrap_or(0), sent_received.0);
|
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]
|
#[test]
|
||||||
@ -1774,7 +1787,7 @@ fn test_bump_fee_drain_wallet() {
|
|||||||
.drain_wallet()
|
.drain_wallet()
|
||||||
.allow_shrinking(addr.script_pubkey())
|
.allow_shrinking(addr.script_pubkey())
|
||||||
.unwrap()
|
.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 psbt = builder.finish().unwrap();
|
||||||
let sent_received = wallet.sent_and_received(&psbt.extract_tx());
|
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();
|
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
||||||
builder
|
builder
|
||||||
.manually_selected_only()
|
.manually_selected_only()
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(255.0));
|
.fee_rate(FeeRate::from_sat_per_vb_unchecked(255));
|
||||||
builder.finish().unwrap();
|
builder.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1878,7 +1891,7 @@ fn test_bump_fee_add_input() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(txid).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 psbt = builder.finish().unwrap();
|
||||||
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
||||||
let fee = check_fee!(wallet, psbt);
|
let fee = check_fee!(wallet, psbt);
|
||||||
@ -1905,7 +1918,7 @@ fn test_bump_fee_add_input() {
|
|||||||
sent_received.1
|
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]
|
#[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
|
// 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
|
// extra input and a change output, and leave the original output untouched
|
||||||
let mut builder = wallet.build_fee_bump(txid).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 psbt = builder.finish().unwrap();
|
||||||
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
||||||
let fee = check_fee!(wallet, psbt);
|
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)
|
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]
|
#[test]
|
||||||
@ -2065,7 +2078,7 @@ fn test_bump_fee_add_input_change_dust() {
|
|||||||
// two inputs (50k, 25k) and one output (45k) - epsilon
|
// two inputs (50k, 25k) and one output (45k) - epsilon
|
||||||
// We use epsilon here to avoid asking for a slightly too high feerate
|
// We use epsilon here to avoid asking for a slightly too high feerate
|
||||||
let fee_abs = 50_000 + 25_000 - 45_000 - 10;
|
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 psbt = builder.finish().unwrap();
|
||||||
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
||||||
let fee = check_fee!(wallet, psbt);
|
let fee = check_fee!(wallet, psbt);
|
||||||
@ -2088,7 +2101,7 @@ fn test_bump_fee_add_input_change_dust() {
|
|||||||
45_000
|
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]
|
#[test]
|
||||||
@ -2119,7 +2132,7 @@ fn test_bump_fee_force_add_input() {
|
|||||||
builder
|
builder
|
||||||
.add_utxo(incoming_op)
|
.add_utxo(incoming_op)
|
||||||
.unwrap()
|
.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 psbt = builder.finish().unwrap();
|
||||||
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
|
||||||
let fee = check_fee!(wallet, psbt);
|
let fee = check_fee!(wallet, psbt);
|
||||||
@ -2147,7 +2160,7 @@ fn test_bump_fee_force_add_input() {
|
|||||||
sent_received.1
|
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]
|
#[test]
|
||||||
@ -2243,7 +2256,7 @@ fn test_bump_fee_unconfirmed_inputs_only() {
|
|||||||
.insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 })
|
.insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 })
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut builder = wallet.build_fee_bump(txid).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();
|
builder.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2278,7 +2291,7 @@ fn test_bump_fee_unconfirmed_input() {
|
|||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
||||||
builder
|
builder
|
||||||
.fee_rate(FeeRate::from_sat_per_vb(15.0))
|
.fee_rate(FeeRate::from_sat_per_vb_unchecked(15))
|
||||||
.allow_shrinking(addr.script_pubkey())
|
.allow_shrinking(addr.script_pubkey())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
builder.finish().unwrap();
|
builder.finish().unwrap();
|
||||||
@ -2298,7 +2311,7 @@ fn test_fee_amount_negative_drain_val() {
|
|||||||
let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.assume_checked();
|
.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 incoming_op = receive_output_in_latest_block(&mut wallet, 8859);
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -3499,7 +3512,7 @@ fn test_fee_rate_sign_no_grinding_high_r() {
|
|||||||
// alright.
|
// alright.
|
||||||
let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||||
let addr = wallet.get_address(New);
|
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 builder = wallet.build_tx();
|
||||||
let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
|
let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
|
||||||
builder
|
builder
|
||||||
@ -3565,7 +3578,7 @@ fn test_fee_rate_sign_grinding_low_r() {
|
|||||||
// signature is 70 bytes.
|
// signature is 70 bytes.
|
||||||
let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||||
let addr = wallet.get_address(New);
|
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 builder = wallet.build_tx();
|
||||||
builder
|
builder
|
||||||
.drain_to(addr.script_pubkey())
|
.drain_to(addr.script_pubkey())
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
//! # use bdk::signer::SignerOrdering;
|
//! # use bdk::signer::SignerOrdering;
|
||||||
//! # use bdk_hwi::HWISigner;
|
//! # use bdk_hwi::HWISigner;
|
||||||
//! # use bdk::wallet::AddressIndex::New;
|
//! # use bdk::wallet::AddressIndex::New;
|
||||||
//! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet};
|
//! # use bdk::{KeychainKind, SignOptions, Wallet};
|
||||||
//! # use hwi::HWIClient;
|
//! # use hwi::HWIClient;
|
||||||
//! # use std::sync::Arc;
|
//! # use std::sync::Arc;
|
||||||
//! #
|
//! #
|
||||||
|
Loading…
x
Reference in New Issue
Block a user