zebra_rpc/server/
error.rs

1//! RPC error codes & their handling.
2use jsonrpsee_types::{ErrorCode, ErrorObject, ErrorObjectOwned};
3
4/// Bitcoin RPC error codes
5///
6/// Drawn from <https://github.com/zcash/zcash/blob/99ad6fdc3a549ab510422820eea5e5ce9f60a5fd/src/rpc/protocol.h#L32-L80>.
7///
8/// ## Notes
9///
10/// - All explicit discriminants fit within `i64`.
11#[derive(Default, Debug)]
12pub enum LegacyCode {
13    // General application defined errors
14    /// `std::exception` thrown in command handling
15    #[default]
16    Misc = -1,
17    /// Server is in safe mode, and command is not allowed in safe mode
18    ForbiddenBySafeMode = -2,
19    /// Unexpected type was passed as parameter
20    Type = -3,
21    /// Invalid address or key
22    InvalidAddressOrKey = -5,
23    /// Ran out of memory during operation
24    OutOfMemory = -7,
25    /// Invalid, missing or duplicate parameter
26    InvalidParameter = -8,
27    /// Database error
28    Database = -20,
29    /// Error parsing or validating structure in raw format
30    Deserialization = -22,
31    /// General error during transaction or block submission
32    Verify = -25,
33    /// Transaction or block was rejected by network rules
34    VerifyRejected = -26,
35    /// Transaction already in chain
36    VerifyAlreadyInChain = -27,
37    /// Client still warming up
38    InWarmup = -28,
39
40    // P2P client errors
41    /// Bitcoin is not connected
42    ClientNotConnected = -9,
43    /// Still downloading initial blocks
44    ClientInInitialDownload = -10,
45    /// Node is already added
46    ClientNodeAlreadyAdded = -23,
47    /// Node has not been added before
48    ClientNodeNotAdded = -24,
49    /// Node to disconnect not found in connected nodes
50    ClientNodeNotConnected = -29,
51    /// Invalid IP/Subnet
52    ClientInvalidIpOrSubnet = -30,
53}
54
55impl From<LegacyCode> for ErrorCode {
56    fn from(code: LegacyCode) -> Self {
57        Self::ServerError(code as i32)
58    }
59}
60
61impl From<LegacyCode> for i32 {
62    fn from(code: LegacyCode) -> Self {
63        code as i32
64    }
65}
66
67/// A trait for mapping errors to [`jsonrpsee_types::ErrorObjectOwned`].
68pub(crate) trait MapError<T>: Sized {
69    /// Maps errors to [`jsonrpsee_types::ErrorObjectOwned`] with a specific error code.
70    fn map_error(self, code: impl Into<ErrorCode>) -> std::result::Result<T, ErrorObjectOwned>;
71
72    /// Maps errors to [`jsonrpsee_types::ErrorObjectOwned`] with a prefixed message and a specific error code.
73    fn map_error_with_prefix(
74        self,
75        code: impl Into<ErrorCode>,
76        msg_prefix: impl ToString,
77    ) -> Result<T, ErrorObjectOwned>;
78
79    /// Maps errors to [`jsonrpsee_types::ErrorObjectOwned`] with a [`LegacyCode::Misc`] error code.
80    fn map_misc_error(self) -> std::result::Result<T, ErrorObjectOwned> {
81        self.map_error(LegacyCode::Misc)
82    }
83}
84
85/// A trait for conditionally converting a value into a `Result<T, jsonrpc_core::Error>`.
86pub(crate) trait OkOrError<T>: Sized {
87    /// Converts the implementing type to `Result<T, jsonrpc_core::Error>`, using an error code and
88    /// message if conversion is to `Err`.
89    fn ok_or_error(
90        self,
91        code: impl Into<ErrorCode>,
92        message: impl ToString,
93    ) -> std::result::Result<T, ErrorObjectOwned>;
94
95    /// Converts the implementing type to `Result<T, jsonrpc_core::Error>`, using a [`LegacyCode::Misc`] error code.
96    fn ok_or_misc_error(self, message: impl ToString) -> std::result::Result<T, ErrorObjectOwned> {
97        self.ok_or_error(LegacyCode::Misc, message)
98    }
99}
100
101impl<T, E> MapError<T> for Result<T, E>
102where
103    E: ToString,
104{
105    fn map_error(self, code: impl Into<ErrorCode>) -> Result<T, ErrorObjectOwned> {
106        self.map_err(|error| ErrorObject::owned(code.into().code(), error.to_string(), None::<()>))
107    }
108
109    fn map_error_with_prefix(
110        self,
111        code: impl Into<ErrorCode>,
112        msg_prefix: impl ToString,
113    ) -> Result<T, ErrorObjectOwned> {
114        self.map_err(|error| {
115            ErrorObject::owned(
116                code.into().code(),
117                format!("{}: {}", msg_prefix.to_string(), error.to_string()),
118                None::<()>,
119            )
120        })
121    }
122}
123
124impl<T> OkOrError<T> for Option<T> {
125    fn ok_or_error(
126        self,
127        code: impl Into<ErrorCode>,
128        message: impl ToString,
129    ) -> Result<T, ErrorObjectOwned> {
130        self.ok_or(ErrorObject::owned(
131            code.into().code(),
132            message.to_string(),
133            None::<()>,
134        ))
135    }
136}