Skip to content

Commit f883ed5

Browse files
committed
refactor(wallet)!: Add BuildFeeBumpError and use as error type for Wallet::build_fee_bump()
1 parent 351c727 commit f883ed5

File tree

2 files changed

+83
-26
lines changed

2 files changed

+83
-26
lines changed

crates/bdk/src/error.rs

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::descriptor::DescriptorError;
1616
use crate::wallet::coin_selection;
1717
use crate::{descriptor, wallet, FeeRate, KeychainKind};
1818
use alloc::string::String;
19-
use bitcoin::{absolute, psbt, OutPoint, Sequence};
19+
use bitcoin::{absolute, psbt, OutPoint, Sequence, Txid};
2020
use core::fmt;
2121

2222
/// Old catch-all errors enum that can be thrown by the [`Wallet`](crate::wallet::Wallet)
@@ -26,12 +26,6 @@ pub enum Error {
2626
Generic(String),
2727
/// Happens when trying to spend an UTXO that is not in the internal database
2828
UnknownUtxo,
29-
/// Thrown when a tx is not found in the internal database
30-
TransactionNotFound,
31-
/// Happens when trying to bump a transaction that is already confirmed
32-
TransactionConfirmed,
33-
/// Trying to replace a tx that has a sequence >= `0xFFFFFFFE`
34-
IrreplaceableTransaction,
3529
/// Node doesn't have data to estimate a fee rate
3630
FeeRateUnavailable,
3731
/// Error while working with [`keys`](crate::keys)
@@ -84,11 +78,6 @@ impl fmt::Display for Error {
8478
match self {
8579
Self::Generic(err) => write!(f, "Generic error: {}", err),
8680
Self::UnknownUtxo => write!(f, "UTXO not found in the internal database"),
87-
Self::TransactionNotFound => {
88-
write!(f, "Transaction not found in the internal database")
89-
}
90-
Self::TransactionConfirmed => write!(f, "Transaction already confirmed"),
91-
Self::IrreplaceableTransaction => write!(f, "Transaction can't be replaced"),
9281
Self::FeeRateUnavailable => write!(f, "Fee rate unavailable"),
9382
Self::Key(err) => write!(f, "Key error: {}", err),
9483
Self::ChecksumMismatch => write!(f, "Descriptor checksum mismatch"),
@@ -144,6 +133,8 @@ impl_error!(bitcoin::psbt::Error, Psbt);
144133

145134
#[derive(Debug)]
146135
/// Error returned from [`TxBuilder::finish`]
136+
///
137+
/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
147138
pub enum CreateTxError<P> {
148139
/// There was a problem with the descriptors passed in
149140
Descriptor(DescriptorError),
@@ -342,3 +333,64 @@ impl<P> From<coin_selection::Error> for CreateTxError<P> {
342333

343334
#[cfg(feature = "std")]
344335
impl<P: core::fmt::Display + core::fmt::Debug> std::error::Error for CreateTxError<P> {}
336+
337+
//
338+
339+
#[derive(Debug)]
340+
/// Error returned from [`Wallet::build_fee_bump`]
341+
///
342+
/// [`Wallet::build_fee_bump`]: wallet::Wallet::build_fee_bump
343+
pub enum BuildFeeBumpError {
344+
/// Happens when trying to spend an UTXO that is not in the internal database
345+
UnknownUtxo {
346+
/// The outpoint of the missing UTXO
347+
outpoint: OutPoint,
348+
},
349+
/// Thrown when a tx is not found in the internal database
350+
TransactionNotFound {
351+
/// The txid of the missing transaction
352+
txid: Txid,
353+
},
354+
/// Happens when trying to bump a transaction that is already confirmed
355+
TransactionConfirmed {
356+
/// The txid of the already confirmed transaction
357+
txid: Txid,
358+
},
359+
/// Trying to replace a tx that has a sequence >= `0xFFFFFFFE`
360+
IrreplaceableTransaction {
361+
/// The txid of the irreplaceable transaction
362+
txid: Txid,
363+
},
364+
/// Node doesn't have data to estimate a fee rate
365+
FeeRateUnavailable,
366+
}
367+
368+
#[cfg(feature = "std")]
369+
impl fmt::Display for BuildFeeBumpError {
370+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371+
match self {
372+
Self::UnknownUtxo { outpoint } => write!(
373+
f,
374+
"UTXO not found in the internal database with txid: {}, vout: {}",
375+
outpoint.txid, outpoint.vout
376+
),
377+
Self::TransactionNotFound { txid } => {
378+
write!(
379+
f,
380+
"Transaction not found in the internal database with txid: {}",
381+
txid
382+
)
383+
}
384+
Self::TransactionConfirmed { txid } => {
385+
write!(f, "Transaction already confirmed with txid: {}", txid)
386+
}
387+
Self::IrreplaceableTransaction { txid } => {
388+
write!(f, "Transaction can't be replaced with txid: {}", txid)
389+
}
390+
Self::FeeRateUnavailable => write!(f, "Fee rate unavailable"),
391+
}
392+
}
393+
}
394+
395+
#[cfg(feature = "std")]
396+
impl std::error::Error for BuildFeeBumpError {}

crates/bdk/src/wallet/mod.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use crate::descriptor::{
6767
calc_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DescriptorMeta,
6868
ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
6969
};
70-
use crate::error::{CreateTxError, Error, MiniscriptPsbtError};
70+
use crate::error::{BuildFeeBumpError, CreateTxError, Error, MiniscriptPsbtError};
7171
use crate::psbt::PsbtUtils;
7272
use crate::signer::SignerError;
7373
use crate::types::*;
@@ -1526,6 +1526,7 @@ impl<D> Wallet<D> {
15261526
/// # use bdk::wallet::ChangeSet;
15271527
/// # use bdk::error::CreateTxError;
15281528
/// # use bdk_chain::PersistBackend;
1529+
/// # use anyhow::Error;
15291530
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
15301531
/// # let mut wallet = doctest_wallet!();
15311532
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -1549,57 +1550,61 @@ impl<D> Wallet<D> {
15491550
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
15501551
/// let fee_bumped_tx = psbt.extract_tx();
15511552
/// // broadcast fee_bumped_tx to replace original
1552-
/// # Ok::<(), bdk::Error>(())
1553+
/// # Ok::<(), anyhow::Error>(())
15531554
/// ```
15541555
// TODO: support for merging multiple transactions while bumping the fees
15551556
pub fn build_fee_bump(
15561557
&mut self,
15571558
txid: Txid,
1558-
) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
1559+
) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, BuildFeeBumpError> {
15591560
let graph = self.indexed_graph.graph();
15601561
let txout_index = &self.indexed_graph.index;
15611562
let chain_tip = self.chain.tip().block_id();
15621563

15631564
let mut tx = graph
15641565
.get_tx(txid)
1565-
.ok_or(Error::TransactionNotFound)?
1566+
.ok_or(BuildFeeBumpError::TransactionNotFound { txid })?
15661567
.clone();
15671568

15681569
let pos = graph
15691570
.get_chain_position(&self.chain, chain_tip, txid)
1570-
.ok_or(Error::TransactionNotFound)?;
1571+
.ok_or(BuildFeeBumpError::TransactionNotFound { txid })?;
15711572
if let ChainPosition::Confirmed(_) = pos {
1572-
return Err(Error::TransactionConfirmed);
1573+
return Err(BuildFeeBumpError::TransactionConfirmed { txid });
15731574
}
15741575

15751576
if !tx
15761577
.input
15771578
.iter()
15781579
.any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
15791580
{
1580-
return Err(Error::IrreplaceableTransaction);
1581+
return Err(BuildFeeBumpError::IrreplaceableTransaction { txid: tx.txid() });
15811582
}
15821583

15831584
let fee = self
15841585
.calculate_fee(&tx)
1585-
.map_err(|_| Error::FeeRateUnavailable)?;
1586+
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
15861587
let fee_rate = self
15871588
.calculate_fee_rate(&tx)
1588-
.map_err(|_| Error::FeeRateUnavailable)?;
1589+
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
15891590

15901591
// remove the inputs from the tx and process them
15911592
let original_txin = tx.input.drain(..).collect::<Vec<_>>();
15921593
let original_utxos = original_txin
15931594
.iter()
1594-
.map(|txin| -> Result<_, Error> {
1595-
let prev_tx = graph
1596-
.get_tx(txin.previous_output.txid)
1597-
.ok_or(Error::UnknownUtxo)?;
1595+
.map(|txin| -> Result<_, BuildFeeBumpError> {
1596+
let prev_tx = graph.get_tx(txin.previous_output.txid).ok_or(
1597+
BuildFeeBumpError::UnknownUtxo {
1598+
outpoint: txin.previous_output,
1599+
},
1600+
)?;
15981601
let txout = &prev_tx.output[txin.previous_output.vout as usize];
15991602

16001603
let confirmation_time: ConfirmationTime = graph
16011604
.get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
1602-
.ok_or(Error::UnknownUtxo)?
1605+
.ok_or(BuildFeeBumpError::UnknownUtxo {
1606+
outpoint: txin.previous_output,
1607+
})?
16031608
.cloned()
16041609
.into();
16051610

0 commit comments

Comments
 (0)