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    #[error("Obtained failure status({0}): {1}")]
40    Status(String, String),
41
42    /// Error decoding the response, retry might not help
43    #[error("Malformed Response: {0}")]
44    MalformedResponse(String),
45
46    /// Connection error, retry might help
47    #[error("Could not connect: {0}")]
48    Connection(String),
49
50    /// Timeout error, retry might help
51    #[error("Timeout")]
52    Timeout,
53
54    /// Redirect error, not retryable
55    #[error("HttpRedirect: {0}")]
56    HttpRedirect(String),
57
58    /// Error building the request, unlikely to be recoverable
59    #[error("Could not build request: {0}")]
60    ReqBuilder(String),
61
62    /// Maximum retries exceeded, not retryable
63    #[error("Max retries {0} exceeded")]
64    MaxRetriesExceeded(u8),
65
66    /// General request error, retry might help
67    #[error("Could not create request: {0}")]
68    Request(String),
69
70    /// Wrong network address
71    #[error("Network address: {0}")]
72    WrongNetworkAddress(Network),
73
74    /// Server version is unexpected or incompatible
75    #[error(transparent)]
76    UnexpectedServerVersion(#[from] UnexpectedServerVersionError),
77
78    /// Could not sign raw transaction
79    #[error(transparent)]
80    Sign(#[from] SignRawTransactionWithWalletError),
81
82    /// Could not get a [`Xpriv`](bitcoin::bip32::Xpriv) from the wallet
83    #[error("Could not get xpriv from wallet")]
84    Xpriv,
85
86    /// Unknown error, unlikely to be recoverable
87    #[error("{0}")]
88    Other(String),
89}
90
91impl ClientError {
92    pub fn is_tx_not_found(&self) -> bool {
93        matches!(self, Self::Server(-5, _))
94    }
95
96    pub fn is_block_not_found(&self) -> bool {
97        matches!(self, Self::Server(-5, _))
98    }
99
100    pub fn is_missing_or_invalid_input(&self) -> bool {
101        matches!(self, Self::Server(-26, _)) || matches!(self, Self::Server(-25, _))
102    }
103}
104
105impl From<SerdeJsonError> for ClientError {
106    fn from(value: SerdeJsonError) -> Self {
107        Self::Parse(format!("Could not parse {value}"))
108    }
109}
110
111/// `bitcoind` RPC server error.
112#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
113pub struct BitcoinRpcError {
114    pub code: i32,
115    pub message: String,
116}
117
118impl fmt::Display for BitcoinRpcError {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        write!(f, "RPC error {}: {}", self.code, self.message)
121    }
122}
123
124impl From<BitcoinRpcError> for ClientError {
125    fn from(value: BitcoinRpcError) -> Self {
126        Self::Server(value.code, value.message)
127    }
128}
129
130/// Error returned when signing a raw transaction with a wallet fails.
131#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
132pub struct SignRawTransactionWithWalletError {
133    /// The transaction ID.
134    txid: String,
135    /// The index of the input.
136    vout: u32,
137    /// The script signature.
138    #[serde(rename = "scriptSig")]
139    script_sig: String,
140    /// The sequence number.
141    sequence: u32,
142    /// The error message.
143    error: String,
144}
145
146impl fmt::Display for SignRawTransactionWithWalletError {
147    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148        write!(
149            f,
150            "error signing raw transaction with wallet: {}",
151            self.error
152        )
153    }
154}
155
156/// Error returned when RPC client expects a different version than bitcoind reports.
157#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
158pub struct UnexpectedServerVersionError {
159    /// Version from server.
160    pub got: usize,
161    /// Expected server version.
162    pub expected: Vec<usize>,
163}
164
165impl fmt::Display for UnexpectedServerVersionError {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        let mut expected = String::new();
168        for version in &self.expected {
169            let v = format!(" {version} ");
170            expected.push_str(&v);
171        }
172        write!(
173            f,
174            "unexpected bitcoind version, got: {} expected one of: {}",
175            self.got, expected
176        )
177    }
178}