use crate::descriptor::policy::PolicyError;
use crate::descriptor::{DescriptorError, ExtendedDescriptor};
use crate::wallet::coin_selection;
use crate::{descriptor, KeychainKind, LoadWithPersistError};
use alloc::{
boxed::Box,
string::{String, ToString},
};
use bitcoin::{absolute, psbt, Amount, BlockHash, Network, OutPoint, Sequence, Txid};
use core::fmt;
#[derive(Debug, PartialEq)]
pub enum LoadError {
Descriptor(crate::descriptor::DescriptorError),
MissingNetwork,
MissingGenesis,
MissingDescriptor(KeychainKind),
Mismatch(LoadMismatch),
}
impl fmt::Display for LoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LoadError::Descriptor(e) => e.fmt(f),
LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
LoadError::MissingDescriptor(k) => {
write!(f, "loaded data is missing descriptor for {k} keychain")
}
LoadError::Mismatch(e) => write!(f, "{e}"),
}
}
}
impl core::error::Error for LoadError {}
#[derive(Debug, PartialEq)]
pub enum LoadMismatch {
Network {
loaded: Network,
expected: Network,
},
Genesis {
loaded: BlockHash,
expected: BlockHash,
},
Descriptor {
keychain: KeychainKind,
loaded: Option<Box<ExtendedDescriptor>>,
expected: Option<Box<ExtendedDescriptor>>,
},
}
impl fmt::Display for LoadMismatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LoadMismatch::Network { loaded, expected } => {
write!(f, "Network mismatch: loaded {loaded}, expected {expected}")
}
LoadMismatch::Genesis { loaded, expected } => {
write!(
f,
"Genesis hash mismatch: loaded {loaded}, expected {expected}"
)
}
LoadMismatch::Descriptor {
keychain,
loaded,
expected,
} => {
write!(
f,
"Descriptor mismatch for {} keychain: loaded {}, expected {}",
keychain,
loaded
.as_ref()
.map_or("None".to_string(), |d| d.to_string()),
expected
.as_ref()
.map_or("None".to_string(), |d| d.to_string())
)
}
}
}
}
impl<E> From<LoadMismatch> for LoadWithPersistError<E> {
fn from(mismatch: LoadMismatch) -> Self {
Self::InvalidChangeSet(LoadError::Mismatch(mismatch))
}
}
impl From<LoadMismatch> for LoadError {
fn from(mismatch: LoadMismatch) -> Self {
Self::Mismatch(mismatch)
}
}
#[derive(Debug, Clone)]
pub enum MiniscriptPsbtError {
Conversion(miniscript::descriptor::ConversionError),
UtxoUpdate(miniscript::psbt::UtxoUpdateError),
OutputUpdate(miniscript::psbt::OutputUpdateError),
}
impl fmt::Display for MiniscriptPsbtError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Conversion(err) => write!(f, "Conversion error: {err}"),
Self::UtxoUpdate(err) => write!(f, "UTXO update error: {err}"),
Self::OutputUpdate(err) => write!(f, "Output update error: {err}"),
}
}
}
impl core::error::Error for MiniscriptPsbtError {}
#[derive(Debug)]
pub enum CreateTxError {
Descriptor(DescriptorError),
Policy(PolicyError),
SpendingPolicyRequired(KeychainKind),
Version0,
Version1Csv,
LockTime {
requested: absolute::LockTime,
required: absolute::LockTime,
},
RbfSequenceCsv {
sequence: Sequence,
csv: Sequence,
},
FeeTooLow {
required: Amount,
},
FeeRateTooLow {
required: bitcoin::FeeRate,
},
NoUtxosSelected,
OutputBelowDustLimit(usize),
CoinSelection(coin_selection::InsufficientFunds),
NoRecipients,
Psbt(psbt::Error),
MissingKeyOrigin(String),
UnknownUtxo,
MissingNonWitnessUtxo(OutPoint),
MiniscriptPsbt(MiniscriptPsbtError),
}
impl fmt::Display for CreateTxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Descriptor(e) => e.fmt(f),
Self::Policy(e) => e.fmt(f),
CreateTxError::SpendingPolicyRequired(keychain_kind) => {
write!(f, "Spending policy required: {keychain_kind}")
}
CreateTxError::Version0 => {
write!(f, "Invalid version `0`")
}
CreateTxError::Version1Csv => {
write!(
f,
"TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
)
}
CreateTxError::LockTime {
requested,
required,
} => {
write!(f, "TxBuilder requested timelock of `{requested}`, but at least `{required}` is required to spend from this script")
}
CreateTxError::RbfSequenceCsv { sequence, csv } => {
write!(
f,
"Cannot enable RBF with nSequence `{sequence}` given a required OP_CSV of `{csv}`"
)
}
CreateTxError::FeeTooLow { required } => {
write!(f, "Fee to low: required {}", required.display_dynamic())
}
CreateTxError::FeeRateTooLow { required } => {
write!(
f,
"Fee rate too low: required {} sat/vb",
crate::floating_rate!(required)
)
}
CreateTxError::NoUtxosSelected => {
write!(f, "No UTXO selected")
}
CreateTxError::OutputBelowDustLimit(limit) => {
write!(f, "Output below the dust limit: {limit}")
}
CreateTxError::CoinSelection(e) => e.fmt(f),
CreateTxError::NoRecipients => {
write!(f, "Cannot build tx without recipients")
}
CreateTxError::Psbt(e) => e.fmt(f),
CreateTxError::MissingKeyOrigin(err) => {
write!(f, "Missing key origin: {err}")
}
CreateTxError::UnknownUtxo => {
write!(f, "UTXO not found in the internal database")
}
CreateTxError::MissingNonWitnessUtxo(outpoint) => {
write!(f, "Missing non_witness_utxo on foreign utxo {outpoint}")
}
CreateTxError::MiniscriptPsbt(err) => {
write!(f, "Miniscript PSBT error: {err}")
}
}
}
}
impl From<descriptor::error::Error> for CreateTxError {
fn from(err: descriptor::error::Error) -> Self {
CreateTxError::Descriptor(err)
}
}
impl From<PolicyError> for CreateTxError {
fn from(err: PolicyError) -> Self {
CreateTxError::Policy(err)
}
}
impl From<MiniscriptPsbtError> for CreateTxError {
fn from(err: MiniscriptPsbtError) -> Self {
CreateTxError::MiniscriptPsbt(err)
}
}
impl From<psbt::Error> for CreateTxError {
fn from(err: psbt::Error) -> Self {
CreateTxError::Psbt(err)
}
}
impl From<coin_selection::InsufficientFunds> for CreateTxError {
fn from(err: coin_selection::InsufficientFunds) -> Self {
CreateTxError::CoinSelection(err)
}
}
impl core::error::Error for CreateTxError {}
#[derive(Debug)]
pub enum BuildFeeBumpError {
UnknownUtxo(OutPoint),
TransactionNotFound(Txid),
TransactionConfirmed(Txid),
IrreplaceableTransaction(Txid),
FeeRateUnavailable,
InvalidOutputIndex(OutPoint),
}
impl fmt::Display for BuildFeeBumpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownUtxo(outpoint) => write!(
f,
"UTXO not found in the internal database with txid: {}, vout: {}",
outpoint.txid, outpoint.vout
),
Self::TransactionNotFound(txid) => {
write!(
f,
"Transaction not found in the internal database with txid: {txid}"
)
}
Self::TransactionConfirmed(txid) => {
write!(f, "Transaction already confirmed with txid: {txid}")
}
Self::IrreplaceableTransaction(txid) => {
write!(f, "Transaction can't be replaced with txid: {txid}")
}
Self::FeeRateUnavailable => write!(f, "Fee rate unavailable"),
Self::InvalidOutputIndex(op) => {
write!(f, "A txin referenced an invalid output: {op}")
}
}
}
}
impl core::error::Error for BuildFeeBumpError {}