reth_rpc_server_types/
result.rs

1//! Additional helpers for converting errors.
2
3use std::fmt;
4
5use alloy_eips::BlockId;
6use alloy_rpc_types_engine::PayloadError;
7use jsonrpsee_core::RpcResult;
8use reth_errors::ConsensusError;
9
10/// Helper trait to easily convert various `Result` types into [`RpcResult`]
11pub trait ToRpcResult<Ok, Err>: Sized {
12    /// Converts result to [`RpcResult`] by converting error variant to
13    /// [`jsonrpsee_types::error::ErrorObject`]
14    fn to_rpc_result(self) -> RpcResult<Ok>
15    where
16        Err: fmt::Display,
17    {
18        self.map_internal_err(|err| err.to_string())
19    }
20
21    /// Converts this type into an [`RpcResult`]
22    fn map_rpc_err<'a, F, M>(self, op: F) -> RpcResult<Ok>
23    where
24        F: FnOnce(Err) -> (i32, M, Option<&'a [u8]>),
25        M: Into<String>;
26
27    /// Converts this type into an [`RpcResult`] with the
28    /// [`jsonrpsee_types::error::INTERNAL_ERROR_CODE`] and the given message.
29    fn map_internal_err<F, M>(self, op: F) -> RpcResult<Ok>
30    where
31        F: FnOnce(Err) -> M,
32        M: Into<String>;
33
34    /// Converts this type into an [`RpcResult`] with the
35    /// [`jsonrpsee_types::error::INTERNAL_ERROR_CODE`] and given message and data.
36    fn map_internal_err_with_data<'a, F, M>(self, op: F) -> RpcResult<Ok>
37    where
38        F: FnOnce(Err) -> (M, &'a [u8]),
39        M: Into<String>;
40
41    /// Adds a message to the error variant and returns an internal Error.
42    ///
43    /// This is shorthand for `Self::map_internal_err(|err| format!("{msg}: {err}"))`.
44    fn with_message(self, msg: &str) -> RpcResult<Ok>;
45}
46
47/// A macro that implements the `ToRpcResult` for a specific error type
48#[macro_export]
49macro_rules! impl_to_rpc_result {
50    ($err:ty) => {
51        impl<Ok> ToRpcResult<Ok, $err> for Result<Ok, $err> {
52            #[inline]
53            fn map_rpc_err<'a, F, M>(self, op: F) -> jsonrpsee_core::RpcResult<Ok>
54            where
55                F: FnOnce($err) -> (i32, M, Option<&'a [u8]>),
56                M: Into<String>,
57            {
58                match self {
59                    Ok(t) => Ok(t),
60                    Err(err) => {
61                        let (code, msg, data) = op(err);
62                        Err($crate::result::rpc_err(code, msg, data))
63                    }
64                }
65            }
66
67            #[inline]
68            fn map_internal_err<'a, F, M>(self, op: F) -> jsonrpsee_core::RpcResult<Ok>
69            where
70                F: FnOnce($err) -> M,
71                M: Into<String>,
72            {
73                self.map_err(|err| $crate::result::internal_rpc_err(op(err)))
74            }
75
76            #[inline]
77            fn map_internal_err_with_data<'a, F, M>(self, op: F) -> jsonrpsee_core::RpcResult<Ok>
78            where
79                F: FnOnce($err) -> (M, &'a [u8]),
80                M: Into<String>,
81            {
82                match self {
83                    Ok(t) => Ok(t),
84                    Err(err) => {
85                        let (msg, data) = op(err);
86                        Err($crate::result::internal_rpc_err_with_data(msg, data))
87                    }
88                }
89            }
90
91            #[inline]
92            fn with_message(self, msg: &str) -> jsonrpsee_core::RpcResult<Ok> {
93                match self {
94                    Ok(t) => Ok(t),
95                    Err(err) => {
96                        let msg = format!("{msg}: {err}");
97                        Err($crate::result::internal_rpc_err(msg))
98                    }
99                }
100            }
101        }
102    };
103}
104
105impl_to_rpc_result!(PayloadError);
106impl_to_rpc_result!(ConsensusError);
107impl_to_rpc_result!(reth_errors::RethError);
108impl_to_rpc_result!(reth_errors::ProviderError);
109impl_to_rpc_result!(reth_network_api::NetworkError);
110
111/// Constructs an invalid params JSON-RPC error.
112pub fn invalid_params_rpc_err(
113    msg: impl Into<String>,
114) -> jsonrpsee_types::error::ErrorObject<'static> {
115    rpc_err(jsonrpsee_types::error::INVALID_PARAMS_CODE, msg, None)
116}
117
118/// Constructs an internal JSON-RPC error.
119pub fn internal_rpc_err(msg: impl Into<String>) -> jsonrpsee_types::error::ErrorObject<'static> {
120    rpc_err(jsonrpsee_types::error::INTERNAL_ERROR_CODE, msg, None)
121}
122
123/// Constructs an internal JSON-RPC error with data
124pub fn internal_rpc_err_with_data(
125    msg: impl Into<String>,
126    data: &[u8],
127) -> jsonrpsee_types::error::ErrorObject<'static> {
128    rpc_err(jsonrpsee_types::error::INTERNAL_ERROR_CODE, msg, Some(data))
129}
130
131/// Constructs an internal JSON-RPC error with code and message
132pub fn rpc_error_with_code(
133    code: i32,
134    msg: impl Into<String>,
135) -> jsonrpsee_types::error::ErrorObject<'static> {
136    rpc_err(code, msg, None)
137}
138
139/// Constructs a JSON-RPC error, consisting of `code`, `message` and optional `data`.
140pub fn rpc_err(
141    code: i32,
142    msg: impl Into<String>,
143    data: Option<&[u8]>,
144) -> jsonrpsee_types::error::ErrorObject<'static> {
145    jsonrpsee_types::error::ErrorObject::owned(
146        code,
147        msg.into(),
148        data.map(|data| {
149            jsonrpsee_core::to_json_raw_value(&alloy_primitives::hex::encode_prefixed(data))
150                .expect("serializing String can't fail")
151        }),
152    )
153}
154
155/// Formats a [`BlockId`] into an error message.
156pub fn block_id_to_str(id: BlockId) -> String {
157    match id {
158        BlockId::Hash(h) => {
159            if h.require_canonical == Some(true) {
160                format!("canonical hash {}", h.block_hash)
161            } else {
162                format!("hash {}", h.block_hash)
163            }
164        }
165        BlockId::Number(n) => format!("{n}"),
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172    use reth_errors::{RethError, RethResult};
173
174    const fn assert_rpc_result<T, E, TRR: ToRpcResult<T, E>>() {}
175
176    #[test]
177    fn can_convert_rpc() {
178        assert_rpc_result::<(), RethError, RethResult<()>>();
179
180        let res = RethResult::Ok(100);
181        let rpc_res = res.map_internal_err(|_| "This is a message");
182        let val = rpc_res.unwrap();
183        assert_eq!(val, 100);
184    }
185}