bitcoind_async_client/
error.rs

1//! Error types for the RPC client.
2use std::fmt;
3
4use bitcoin::Network;
5use serde::{Deserialize, Serialize};
6use serde_json::Error as SerdeJsonError;
7use thiserror::Error;
8
9/// The error type for errors produced in this library.
10#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub enum ClientError {
12    /// Missing username or password for the RPC server
13    #[error("Missing username or password")]
14    MissingUserPassword,
15
16    /// RPC server returned an error
17    ///
18    /// # Note
19    ///
20    /// These errors are ABSOLUTELY UNDOCUMENTED.
21    /// Check
22    /// <https://github.com/bitcoin/bitcoin/blob/96b0a8f858ab24f3672360b8c830553b963de726/src/rpc/protocol.h#L24>
23    /// and good luck!
24    #[error("RPC server returned error '{1}' (code {0})")]
25    Server(i32, String),
26
27    #[error("Error parsing rpc response: {0}")]
28    Parse(String),
29
30    /// Error creating the RPC request, retry might help
31    #[error("Could not create RPC Param")]
32    Param(String),
33
34    /// Body error, unlikely to be recoverable by retrying
35    #[error("{0}")]
36    Body(String),
37
38    /// HTTP status error, not retryable
39    // NOTE: Using u16 instead of reqwest::StatusCode because StatusCode doesn't implement Serialize/Deserialize
40    #[error("Obtained failure status({0}): {1}")]
41    Status(u16, String),
42
43    /// Error decoding the response, retry might not help
44    #[error("Malformed Response: {0}")]
45    MalformedResponse(String),
46
47    /// Connection error, retry might help
48    #[error("Could not connect: {0}")]
49    Connection(String),
50
51    /// Timeout error, retry might help
52    #[error("Timeout")]
53    Timeout,
54
55    /// Redirect error, not retryable
56    #[error("HttpRedirect: {0}")]
57    HttpRedirect(String),
58
59    /// Error building the request, unlikely to be recoverable
60    #[error("Could not build request: {0}")]
61    ReqBuilder(String),
62
63    /// Maximum retries exceeded, not retryable
64    #[error("Max retries {0} exceeded")]
65    MaxRetriesExceeded(u8),
66
67    /// General request error, retry might help
68    #[error("Could not create request: {0}")]
69    Request(String),
70
71    /// Wrong network address
72    #[error("Network address: {0}")]
73    WrongNetworkAddress(Network),
74
75    /// Server version is unexpected or incompatible
76    #[error(transparent)]
77    UnexpectedServerVersion(#[from] UnexpectedServerVersionError),
78
79    /// Could not sign raw transaction
80    #[error(transparent)]
81    Sign(#[from] SignRawTransactionWithWalletError),
82
83    /// Could not get a [`Xpriv`](bitcoin::bip32::Xpriv) from the wallet
84    #[error("Could not get xpriv from wallet")]
85    Xpriv,
86
87    /// Unknown error, unlikely to be recoverable
88    #[error("{0}")]
89    Other(String),
90}
91
92impl ClientError {
93    pub fn is_tx_not_found(&self) -> bool {
94        matches!(self, Self::Server(-5, _))
95    }
96
97    pub fn is_block_not_found(&self) -> bool {
98        matches!(self, Self::Server(-5, _))
99    }
100
101    pub fn is_missing_or_invalid_input(&self) -> bool {
102        matches!(self, Self::Server(-26, _)) || matches!(self, Self::Server(-25, _))
103    }
104}
105
106impl From<SerdeJsonError> for ClientError {
107    fn from(value: SerdeJsonError) -> Self {
108        Self::Parse(format!("Could not parse {value}"))
109    }
110}
111
112/// `bitcoind` RPC server error.
113#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
114pub struct BitcoinRpcError {
115    pub code: i32,
116    pub message: String,
117}
118
119impl fmt::Display for BitcoinRpcError {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        write!(f, "RPC error {}: {}", self.code, self.message)
122    }
123}
124
125impl From<BitcoinRpcError> for ClientError {
126    fn from(value: BitcoinRpcError) -> Self {
127        Self::Server(value.code, value.message)
128    }
129}
130
131/// Error returned when signing a raw transaction with a wallet fails.
132#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
133pub struct SignRawTransactionWithWalletError {
134    /// The transaction ID.
135    txid: String,
136    /// The index of the input.
137    vout: u32,
138    /// The script signature.
139    #[serde(rename = "scriptSig")]
140    script_sig: String,
141    /// The sequence number.
142    sequence: u32,
143    /// The error message.
144    error: String,
145}
146
147impl fmt::Display for SignRawTransactionWithWalletError {
148    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        write!(
150            f,
151            "error signing raw transaction with wallet: {}",
152            self.error
153        )
154    }
155}
156
157/// Error returned when RPC client expects a different version than bitcoind reports.
158#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub struct UnexpectedServerVersionError {
160    /// Version from server.
161    pub got: usize,
162    /// Expected server version.
163    pub expected: Vec<usize>,
164}
165
166impl fmt::Display for UnexpectedServerVersionError {
167    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168        let mut expected = String::new();
169        for version in &self.expected {
170            let v = format!(" {version} ");
171            expected.push_str(&v);
172        }
173        write!(
174            f,
175            "unexpected bitcoind version, got: {} expected one of: {}",
176            self.got, expected
177        )
178    }
179}