bdk_wallet/wallet/
error.rs

1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12//! Errors that can be thrown by the [`Wallet`](crate::wallet::Wallet)
13
14use crate::descriptor::policy::PolicyError;
15use crate::descriptor::DescriptorError;
16use crate::wallet::coin_selection;
17use crate::{descriptor, KeychainKind};
18use alloc::string::String;
19use bitcoin::{absolute, psbt, Amount, OutPoint, Sequence, Txid};
20use core::fmt;
21
22/// Errors returned by miniscript when updating inconsistent PSBTs
23#[derive(Debug, Clone)]
24pub enum MiniscriptPsbtError {
25    /// Descriptor key conversion error
26    Conversion(miniscript::descriptor::ConversionError),
27    /// Return error type for PsbtExt::update_input_with_descriptor
28    UtxoUpdate(miniscript::psbt::UtxoUpdateError),
29    /// Return error type for PsbtExt::update_output_with_descriptor
30    OutputUpdate(miniscript::psbt::OutputUpdateError),
31}
32
33impl fmt::Display for MiniscriptPsbtError {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            Self::Conversion(err) => write!(f, "Conversion error: {err}"),
37            Self::UtxoUpdate(err) => write!(f, "UTXO update error: {err}"),
38            Self::OutputUpdate(err) => write!(f, "Output update error: {err}"),
39        }
40    }
41}
42
43#[cfg(feature = "std")]
44impl std::error::Error for MiniscriptPsbtError {}
45
46#[derive(Debug)]
47/// Error returned from [`TxBuilder::finish`]
48///
49/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
50pub enum CreateTxError {
51    /// There was a problem with the descriptors passed in
52    Descriptor(DescriptorError),
53    /// There was a problem while extracting and manipulating policies
54    Policy(PolicyError),
55    /// Spending policy is not compatible with this [`KeychainKind`]
56    SpendingPolicyRequired(KeychainKind),
57    /// Requested invalid transaction version '0'
58    Version0,
59    /// Requested transaction version `1`, but at least `2` is needed to use OP_CSV
60    Version1Csv,
61    /// Requested `LockTime` is less than is required to spend from this script
62    LockTime {
63        /// Requested `LockTime`
64        requested: absolute::LockTime,
65        /// Required `LockTime`
66        required: absolute::LockTime,
67    },
68    /// Cannot enable RBF with `Sequence` given a required OP_CSV
69    RbfSequenceCsv {
70        /// Given RBF `Sequence`
71        sequence: Sequence,
72        /// Required OP_CSV `Sequence`
73        csv: Sequence,
74    },
75    /// When bumping a tx the absolute fee requested is lower than replaced tx absolute fee
76    FeeTooLow {
77        /// Required fee absolute value [`Amount`]
78        required: Amount,
79    },
80    /// When bumping a tx the fee rate requested is lower than required
81    FeeRateTooLow {
82        /// Required fee rate
83        required: bitcoin::FeeRate,
84    },
85    /// `manually_selected_only` option is selected but no utxo has been passed
86    NoUtxosSelected,
87    /// Output created is under the dust limit, 546 satoshis
88    OutputBelowDustLimit(usize),
89    /// There was an error with coin selection
90    CoinSelection(coin_selection::InsufficientFunds),
91    /// Cannot build a tx without recipients
92    NoRecipients,
93    /// Partially signed bitcoin transaction error
94    Psbt(psbt::Error),
95    /// In order to use the [`TxBuilder::add_global_xpubs`] option every extended
96    /// key in the descriptor must either be a master key itself (having depth = 0) or have an
97    /// explicit origin provided
98    ///
99    /// [`TxBuilder::add_global_xpubs`]: crate::wallet::tx_builder::TxBuilder::add_global_xpubs
100    MissingKeyOrigin(String),
101    /// Happens when trying to spend an UTXO that is not in the internal database
102    UnknownUtxo,
103    /// Missing non_witness_utxo on foreign utxo for given `OutPoint`
104    MissingNonWitnessUtxo(OutPoint),
105    /// Miniscript PSBT error
106    MiniscriptPsbt(MiniscriptPsbtError),
107}
108
109impl fmt::Display for CreateTxError {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            Self::Descriptor(e) => e.fmt(f),
113            Self::Policy(e) => e.fmt(f),
114            CreateTxError::SpendingPolicyRequired(keychain_kind) => {
115                write!(f, "Spending policy required: {keychain_kind}")
116            }
117            CreateTxError::Version0 => {
118                write!(f, "Invalid version `0`")
119            }
120            CreateTxError::Version1Csv => {
121                write!(
122                    f,
123                    "TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
124                )
125            }
126            CreateTxError::LockTime {
127                requested,
128                required,
129            } => {
130                write!(f, "TxBuilder requested timelock of `{requested}`, but at least `{required}` is required to spend from this script")
131            }
132            CreateTxError::RbfSequenceCsv { sequence, csv } => {
133                write!(
134                    f,
135                    "Cannot enable RBF with nSequence `{sequence}` given a required OP_CSV of `{csv}`"
136                )
137            }
138            CreateTxError::FeeTooLow { required } => {
139                write!(f, "Fee to low: required {}", required.display_dynamic())
140            }
141            CreateTxError::FeeRateTooLow { required } => {
142                write!(
143                    f,
144                    // Note: alternate fmt as sat/vb (ceil) available in bitcoin-0.31
145                    //"Fee rate too low: required {required:#}"
146                    "Fee rate too low: required {} sat/vb",
147                    crate::floating_rate!(required)
148                )
149            }
150            CreateTxError::NoUtxosSelected => {
151                write!(f, "No UTXO selected")
152            }
153            CreateTxError::OutputBelowDustLimit(limit) => {
154                write!(f, "Output below the dust limit: {limit}")
155            }
156            CreateTxError::CoinSelection(e) => e.fmt(f),
157            CreateTxError::NoRecipients => {
158                write!(f, "Cannot build tx without recipients")
159            }
160            CreateTxError::Psbt(e) => e.fmt(f),
161            CreateTxError::MissingKeyOrigin(err) => {
162                write!(f, "Missing key origin: {err}")
163            }
164            CreateTxError::UnknownUtxo => {
165                write!(f, "UTXO not found in the internal database")
166            }
167            CreateTxError::MissingNonWitnessUtxo(outpoint) => {
168                write!(f, "Missing non_witness_utxo on foreign utxo {outpoint}")
169            }
170            CreateTxError::MiniscriptPsbt(err) => {
171                write!(f, "Miniscript PSBT error: {err}")
172            }
173        }
174    }
175}
176
177impl From<descriptor::error::Error> for CreateTxError {
178    fn from(err: descriptor::error::Error) -> Self {
179        CreateTxError::Descriptor(err)
180    }
181}
182
183impl From<PolicyError> for CreateTxError {
184    fn from(err: PolicyError) -> Self {
185        CreateTxError::Policy(err)
186    }
187}
188
189impl From<MiniscriptPsbtError> for CreateTxError {
190    fn from(err: MiniscriptPsbtError) -> Self {
191        CreateTxError::MiniscriptPsbt(err)
192    }
193}
194
195impl From<psbt::Error> for CreateTxError {
196    fn from(err: psbt::Error) -> Self {
197        CreateTxError::Psbt(err)
198    }
199}
200
201impl From<coin_selection::InsufficientFunds> for CreateTxError {
202    fn from(err: coin_selection::InsufficientFunds) -> Self {
203        CreateTxError::CoinSelection(err)
204    }
205}
206
207#[cfg(feature = "std")]
208impl std::error::Error for CreateTxError {}
209
210#[derive(Debug)]
211/// Error returned from [`Wallet::build_fee_bump`]
212///
213/// [`Wallet::build_fee_bump`]: super::Wallet::build_fee_bump
214pub enum BuildFeeBumpError {
215    /// Happens when trying to spend an UTXO that is not in the internal database
216    UnknownUtxo(OutPoint),
217    /// Thrown when a tx is not found in the internal database
218    TransactionNotFound(Txid),
219    /// Happens when trying to bump a transaction that is already confirmed
220    TransactionConfirmed(Txid),
221    /// Trying to replace a tx that has a sequence >= `0xFFFFFFFE`
222    IrreplaceableTransaction(Txid),
223    /// Node doesn't have data to estimate a fee rate
224    FeeRateUnavailable,
225    /// Input references an invalid output index in the previous transaction
226    InvalidOutputIndex(OutPoint),
227}
228
229impl fmt::Display for BuildFeeBumpError {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        match self {
232            Self::UnknownUtxo(outpoint) => write!(
233                f,
234                "UTXO not found in the internal database with txid: {}, vout: {}",
235                outpoint.txid, outpoint.vout
236            ),
237            Self::TransactionNotFound(txid) => {
238                write!(
239                    f,
240                    "Transaction not found in the internal database with txid: {txid}"
241                )
242            }
243            Self::TransactionConfirmed(txid) => {
244                write!(f, "Transaction already confirmed with txid: {txid}")
245            }
246            Self::IrreplaceableTransaction(txid) => {
247                write!(f, "Transaction can't be replaced with txid: {txid}")
248            }
249            Self::FeeRateUnavailable => write!(f, "Fee rate unavailable"),
250            Self::InvalidOutputIndex(op) => {
251                write!(f, "A txin referenced an invalid output: {op}")
252            }
253        }
254    }
255}
256
257#[cfg(feature = "std")]
258impl std::error::Error for BuildFeeBumpError {}