Skip to content

Commit 4832f5b

Browse files
committed
refactor(wallet)!: Add BuildFeeBumpError and use as error type for Wallet::build_fee_bump()
1 parent d7287d2 commit 4832f5b

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::*;
@@ -1254,6 +1254,7 @@ impl<D> Wallet<D> {
12541254
/// # use bdk::wallet::ChangeSet;
12551255
/// # use bdk::error::CreateTxError;
12561256
/// # use bdk_chain::PersistBackend;
1257+
/// # use anyhow::Error;
12571258
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
12581259
/// # let mut wallet = doctest_wallet!();
12591260
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -1277,57 +1278,61 @@ impl<D> Wallet<D> {
12771278
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
12781279
/// let fee_bumped_tx = psbt.extract_tx();
12791280
/// // broadcast fee_bumped_tx to replace original
1280-
/// # Ok::<(), bdk::Error>(())
1281+
/// # Ok::<(), anyhow::Error>(())
12811282
/// ```
12821283
// TODO: support for merging multiple transactions while bumping the fees
12831284
pub fn build_fee_bump(
12841285
&mut self,
12851286
txid: Txid,
1286-
) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
1287+
) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, BuildFeeBumpError> {
12871288
let graph = self.indexed_graph.graph();
12881289
let txout_index = &self.indexed_graph.index;
12891290
let chain_tip = self.chain.tip().map(|cp| cp.block_id()).unwrap_or_default();
12901291

12911292
let mut tx = graph
12921293
.get_tx(txid)
1293-
.ok_or(Error::TransactionNotFound)?
1294+
.ok_or(BuildFeeBumpError::TransactionNotFound { txid })?
12941295
.clone();
12951296

12961297
let pos = graph
12971298
.get_chain_position(&self.chain, chain_tip, txid)
1298-
.ok_or(Error::TransactionNotFound)?;
1299+
.ok_or(BuildFeeBumpError::TransactionNotFound { txid })?;
12991300
if let ChainPosition::Confirmed(_) = pos {
1300-
return Err(Error::TransactionConfirmed);
1301+
return Err(BuildFeeBumpError::TransactionConfirmed { txid });
13011302
}
13021303

13031304
if !tx
13041305
.input
13051306
.iter()
13061307
.any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
13071308
{
1308-
return Err(Error::IrreplaceableTransaction);
1309+
return Err(BuildFeeBumpError::IrreplaceableTransaction { txid: tx.txid() });
13091310
}
13101311

13111312
let fee = self
13121313
.calculate_fee(&tx)
1313-
.map_err(|_| Error::FeeRateUnavailable)?;
1314+
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
13141315
let fee_rate = self
13151316
.calculate_fee_rate(&tx)
1316-
.map_err(|_| Error::FeeRateUnavailable)?;
1317+
.map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
13171318

13181319
// remove the inputs from the tx and process them
13191320
let original_txin = tx.input.drain(..).collect::<Vec<_>>();
13201321
let original_utxos = original_txin
13211322
.iter()
1322-
.map(|txin| -> Result<_, Error> {
1323-
let prev_tx = graph
1324-
.get_tx(txin.previous_output.txid)
1325-
.ok_or(Error::UnknownUtxo)?;
1323+
.map(|txin| -> Result<_, BuildFeeBumpError> {
1324+
let prev_tx = graph.get_tx(txin.previous_output.txid).ok_or(
1325+
BuildFeeBumpError::UnknownUtxo {
1326+
outpoint: txin.previous_output,
1327+
},
1328+
)?;
13261329
let txout = &prev_tx.output[txin.previous_output.vout as usize];
13271330

13281331
let confirmation_time: ConfirmationTime = graph
13291332
.get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
1330-
.ok_or(Error::UnknownUtxo)?
1333+
.ok_or(BuildFeeBumpError::UnknownUtxo {
1334+
outpoint: txin.previous_output,
1335+
})?
13311336
.cloned()
13321337
.into();
13331338

0 commit comments

Comments
 (0)