Skip to main content

bitcoin_rs_rpc/
error.rs

1use core::fmt;
2use std::io;
3
4use sonic_rs::{Value, json};
5use thiserror::Error;
6
7/// JSON-RPC 2.0 and Bitcoin Core-compatible RPC errors.
8#[derive(Debug, Error)]
9pub enum RpcError {
10    /// JSON text could not be parsed.
11    #[error("parse error: {0}")]
12    Parse(String),
13    /// Request object is not a valid JSON-RPC call.
14    #[error("invalid request: {0}")]
15    InvalidRequest(&'static str),
16    /// Method name is not supported.
17    #[error("method not found: {0}")]
18    MethodNotFound(String),
19    /// Parameters have the wrong shape.
20    #[error("invalid params: {0}")]
21    InvalidParams(&'static str),
22    /// Parameter value has the wrong JSON type.
23    #[error("invalid type: {0}")]
24    InvalidType(&'static str),
25    /// Requested object was not found.
26    #[error("not found: {0}")]
27    NotFound(&'static str),
28    /// A method is intentionally disabled by policy.
29    #[error("{0}")]
30    MethodDisabled(&'static str),
31    /// Internal server failure.
32    #[error("internal error: {0}")]
33    Internal(String),
34}
35
36impl RpcError {
37    /// Standard JSON-RPC parse error code.
38    pub const PARSE_ERROR: i64 = -32_700;
39    /// Standard JSON-RPC invalid request code.
40    pub const INVALID_REQUEST: i64 = -32_600;
41    /// Standard JSON-RPC unknown method code.
42    pub const METHOD_NOT_FOUND: i64 = -32_601;
43    /// Standard JSON-RPC invalid params code.
44    pub const INVALID_PARAMS: i64 = -32_602;
45    /// Standard JSON-RPC internal error code.
46    pub const INTERNAL_ERROR: i64 = -32_603;
47    /// Bitcoin Core invalid type code.
48    pub const CORE_INVALID_TYPE: i64 = -3;
49    /// Bitcoin Core not-found code.
50    pub const CORE_NOT_FOUND: i64 = -5;
51    /// Bitcoin Core invalid parameter value code.
52    pub const CORE_INVALID_PARAMETER: i64 = -8;
53
54    /// Builds the no-private-keys policy error used by signing RPCs.
55    #[must_use]
56    pub const fn method_disabled(message: &'static str) -> Self {
57        Self::MethodDisabled(message)
58    }
59
60    /// Returns the JSON-RPC numeric error code.
61    #[must_use]
62    pub const fn code(&self) -> i64 {
63        match self {
64            Self::Parse(_) => Self::PARSE_ERROR,
65            Self::InvalidRequest(_) => Self::INVALID_REQUEST,
66            Self::MethodNotFound(_) => Self::METHOD_NOT_FOUND,
67            Self::InvalidParams(_) => Self::INVALID_PARAMS,
68            Self::InvalidType(_) => Self::CORE_INVALID_TYPE,
69            Self::NotFound(_) => Self::CORE_NOT_FOUND,
70            Self::MethodDisabled(_) | Self::Internal(_) => Self::INTERNAL_ERROR,
71        }
72    }
73
74    /// Converts this error into a JSON-RPC response object for `id`.
75    #[must_use]
76    pub fn response(&self, id: &Value) -> Value {
77        json!({
78            "jsonrpc": "2.0",
79            "result": null,
80            "error": {"code": self.code(), "message": self.to_string()},
81            "id": id
82        })
83    }
84}
85
86impl From<sonic_rs::Error> for RpcError {
87    fn from(error: sonic_rs::Error) -> Self {
88        Self::Parse(error.to_string())
89    }
90}
91
92impl From<serde_json::Error> for RpcError {
93    fn from(error: serde_json::Error) -> Self {
94        Self::Internal(error.to_string())
95    }
96}
97
98impl From<io::Error> for RpcError {
99    fn from(error: io::Error) -> Self {
100        Self::Internal(error.to_string())
101    }
102}
103
104impl From<bitcoin::consensus::encode::Error> for RpcError {
105    fn from(_error: bitcoin::consensus::encode::Error) -> Self {
106        Self::InvalidParams("consensus decoding failed")
107    }
108}
109
110impl From<bitcoin::hex::HexToBytesError> for RpcError {
111    fn from(_error: bitcoin::hex::HexToBytesError) -> Self {
112        Self::InvalidParams("hex string is invalid")
113    }
114}
115
116impl From<core::str::Utf8Error> for RpcError {
117    fn from(error: core::str::Utf8Error) -> Self {
118        Self::Parse(error.to_string())
119    }
120}
121
122impl From<fmt::Error> for RpcError {
123    fn from(error: fmt::Error) -> Self {
124        Self::Internal(error.to_string())
125    }
126}