1use jsonrpsee::types::{
9 ErrorObject, ErrorObjectOwned,
10 error::{INTERNAL_ERROR_CODE, INTERNAL_ERROR_MSG, INVALID_PARAMS_CODE},
11};
12
13use crate::BlockProducerError;
14
15pub type RpcResult<T> = Result<T, RpcError>;
17
18#[derive(Debug, thiserror::Error)]
24pub enum RpcError {
25 #[error(transparent)]
27 Arb(#[from] arb_errors::ArbError),
28
29 #[error(transparent)]
31 BlockProducer(#[from] BlockProducerError),
32
33 #[error(transparent)]
35 Provider(#[from] reth_storage_errors::provider::ProviderError),
36
37 #[error("invalid params: {0}")]
39 InvalidParams(String),
40
41 #[error("base64 decode failed: {0}")]
43 Base64Decode(String),
44
45 #[error("resource not found: {0}")]
47 NotFound(String),
48
49 #[error("internal error: {0}")]
52 Internal(String),
53}
54
55impl RpcError {
56 pub fn invalid_params(msg: impl core::fmt::Display) -> Self {
58 Self::InvalidParams(msg.to_string())
59 }
60
61 pub fn base64_decode(msg: impl core::fmt::Display) -> Self {
63 Self::Base64Decode(msg.to_string())
64 }
65
66 pub fn not_found(msg: impl core::fmt::Display) -> Self {
68 Self::NotFound(msg.to_string())
69 }
70
71 pub fn internal(msg: impl core::fmt::Display) -> Self {
73 Self::Internal(msg.to_string())
74 }
75}
76
77impl From<RpcError> for ErrorObjectOwned {
78 fn from(err: RpcError) -> Self {
79 match err {
80 RpcError::InvalidParams(msg) => {
81 ErrorObject::owned(INVALID_PARAMS_CODE, msg, None::<()>)
82 }
83 RpcError::Base64Decode(msg) => ErrorObject::owned(
84 INVALID_PARAMS_CODE,
85 format!("base64 decode: {msg}"),
86 None::<()>,
87 ),
88 RpcError::NotFound(msg) => ErrorObject::owned(INVALID_PARAMS_CODE, msg, None::<()>),
89 RpcError::Arb(_)
90 | RpcError::BlockProducer(_)
91 | RpcError::Provider(_)
92 | RpcError::Internal(_) => {
93 tracing::warn!(target: "arb::rpc", error = %err, "RPC internal error");
94 ErrorObject::owned(INTERNAL_ERROR_CODE, INTERNAL_ERROR_MSG, None::<()>)
95 }
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use jsonrpsee::types::error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE};
103
104 use super::*;
105
106 fn into_obj(err: RpcError) -> ErrorObjectOwned {
107 err.into()
108 }
109
110 #[test]
111 fn invalid_params_maps_to_invalid_params_code() {
112 let obj = into_obj(RpcError::invalid_params("bad msg"));
113 assert_eq!(obj.code(), INVALID_PARAMS_CODE);
114 assert_eq!(obj.message(), "bad msg");
115 }
116
117 #[test]
118 fn base64_decode_maps_to_invalid_params_code() {
119 let obj = into_obj(RpcError::base64_decode("bad char"));
120 assert_eq!(obj.code(), INVALID_PARAMS_CODE);
121 assert!(obj.message().contains("base64 decode"));
122 }
123
124 #[test]
125 fn not_found_maps_to_invalid_params_code() {
126 let obj = into_obj(RpcError::not_found("block 7"));
127 assert_eq!(obj.code(), INVALID_PARAMS_CODE);
128 assert_eq!(obj.message(), "block 7");
129 }
130
131 #[test]
132 fn internal_redacts_source() {
133 let obj = into_obj(RpcError::internal("db connection lost"));
134 assert_eq!(obj.code(), INTERNAL_ERROR_CODE);
135 assert!(!obj.message().contains("db connection lost"));
136 }
137
138 #[test]
139 fn provider_redacts_source() {
140 use reth_storage_errors::provider::ProviderError;
141 let obj = into_obj(RpcError::Provider(ProviderError::BestBlockNotFound));
142 assert_eq!(obj.code(), INTERNAL_ERROR_CODE);
143 assert!(!obj.message().contains("Best block"));
144 }
145}