bitcoin_bosd/
error.rs

1//! Error types for the Bitcoin BOSD library.
2
3use core::fmt;
4
5use hex::error::HexToBytesError;
6
7use crate::DescriptorType;
8
9#[cfg(feature = "address")]
10use bitcoin::script::witness_program::Error as WitnessProgramError;
11#[cfg(feature = "address")]
12use bitcoin::secp256k1::Error as Secp256k1Error;
13
14/// Errors related to [`Descriptor`](crate::Descriptor).
15#[derive(Debug, PartialEq, Eq)]
16pub enum DescriptorError {
17    /// Missing type tag.
18    MissingTypeTag,
19
20    /// Invalid descriptor type tag.
21    InvalidDescriptorType(u8),
22
23    /// Invalid payload length.
24    InvalidPayloadLength(usize),
25
26    /// Invalid X-only public key.
27    InvalidXOnlyPublicKey,
28
29    /// Hex decoding error.
30    HexDecodingError(HexToBytesError),
31
32    /// Invalid [`Address`](bitcoin::Address) conversion.
33    ///
34    /// Currently only susceptible for `OP_RETURN` descriptors
35    /// being converted to a bitcoin address.
36    #[cfg(feature = "address")]
37    InvalidAddressConversion(DescriptorType),
38
39    /// [`secp256k1`] errors.
40    #[cfg(feature = "address")]
41    Secp256k1Error(Secp256k1Error),
42
43    /// [`WitnessProgram`](bitcoin::WitnessProgram) errors.
44    #[cfg(feature = "address")]
45    WitnessProgramError(WitnessProgramError),
46
47    /// Unsupported witness program.
48    ///
49    /// The witness program is consensus-valid but not representable
50    /// as a standard descriptor (e.g., non-standard v0/v1 lengths,
51    /// future witness versions v2+, or non-P2A 2-byte v1 programs).
52    #[cfg(feature = "address")]
53    UnsupportedWitnessProgram(String),
54}
55
56impl core::fmt::Display for DescriptorError {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match self {
59            Self::MissingTypeTag => write!(f, "missing type tag"),
60            Self::InvalidDescriptorType(tag) => write!(f, "invalid descriptor type tag: {tag}"),
61            Self::InvalidPayloadLength(len) => write!(f, "invalid payload length: {len}"),
62            Self::InvalidXOnlyPublicKey => write!(f, "invalid X-only public key"),
63            Self::HexDecodingError(err) => write!(f, "hex decoding error: {err}"),
64            #[cfg(feature = "address")]
65            Self::InvalidAddressConversion(desc_type) => write!(
66                f,
67                "{desc_type} locking script cannot be converted into a bitcoin address"
68            ),
69            #[cfg(feature = "address")]
70            Self::Secp256k1Error(err) => write!(f, "secp256k1 error: {err}"),
71            #[cfg(feature = "address")]
72            Self::WitnessProgramError(err) => write!(f, "witness program error: {err}"),
73            #[cfg(feature = "address")]
74            Self::UnsupportedWitnessProgram(msg) => write!(f, "unsupported witness program: {msg}"),
75        }
76    }
77}
78
79// TODO: uncomment feature flag when no-std is supported
80//#[cfg(feature = "std")]
81impl std::error::Error for DescriptorError {
82    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
83        match self {
84            Self::HexDecodingError(err) => Some(err),
85            #[cfg(feature = "address")]
86            Self::Secp256k1Error(err) => Some(err),
87            #[cfg(feature = "address")]
88            Self::WitnessProgramError(err) => Some(err),
89            #[cfg(feature = "address")]
90            Self::UnsupportedWitnessProgram(_) => None,
91            _ => None,
92        }
93    }
94}
95
96impl From<HexToBytesError> for DescriptorError {
97    fn from(err: HexToBytesError) -> Self {
98        Self::HexDecodingError(err)
99    }
100}
101
102#[cfg(feature = "address")]
103impl From<Secp256k1Error> for DescriptorError {
104    fn from(err: Secp256k1Error) -> Self {
105        Self::Secp256k1Error(err)
106    }
107}
108
109#[cfg(feature = "address")]
110impl From<WitnessProgramError> for DescriptorError {
111    fn from(err: WitnessProgramError) -> Self {
112        Self::WitnessProgramError(err)
113    }
114}