zebra_rpc/server/
rpc_call_compatibility.rs1use jsonrpsee::{
7 server::middleware::rpc::{layer::ResponseFuture, RpcServiceT},
8 MethodResponse,
9};
10use jsonrpsee_types::{ErrorObject, ErrorObjectOwned};
11
12#[derive(Clone)]
25pub struct FixRpcResponseMiddleware<S> {
26 service: S,
27}
28
29impl<S> FixRpcResponseMiddleware<S> {
30 pub fn new(service: S) -> Self {
32 Self { service }
33 }
34}
35
36impl<'a, S> RpcServiceT<'a> for FixRpcResponseMiddleware<S>
37where
38 S: RpcServiceT<'a> + Send + Sync + Clone + 'static,
39{
40 type Future = ResponseFuture<futures::future::BoxFuture<'a, jsonrpsee::MethodResponse>>;
41
42 fn call(&self, request: jsonrpsee::types::Request<'a>) -> Self::Future {
43 let service = self.service.clone();
44
45 let id = request.id.clone().into_owned();
48
49 ResponseFuture::future(Box::pin(async move {
50 let response = service.call(request).await;
51
52 if response.as_error_code() != Some(jsonrpsee_types::ErrorCode::InvalidParams.code()) {
53 return response;
54 }
55
56 let new_error_code = crate::server::error::LegacyCode::Misc.into();
57 tracing::debug!(
58 "Replacing RPC error code {} with {new_error_code}",
59 jsonrpsee_types::ErrorCode::InvalidParams.code(),
60 );
61
62 let Some(error) = serde_json::from_str::<serde_json::Value>(response.as_result())
65 .ok()
66 .and_then(|json| {
67 serde_json::from_value::<ErrorObjectOwned>(json.get("error")?.clone()).ok()
68 })
69 else {
70 return response;
71 };
72
73 MethodResponse::error(
74 id,
75 ErrorObject::owned(new_error_code, error.message(), error.data()),
76 )
77 }))
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use jsonrpsee::{
84 server::middleware::rpc::RpcServiceT,
85 types::{Id, Request},
86 MethodResponse,
87 };
88 use jsonrpsee_types::{ErrorCode, ErrorObject};
89
90 use crate::server::error::LegacyCode;
91
92 use super::FixRpcResponseMiddleware;
93
94 #[derive(Clone)]
96 struct ErrorService {
97 code: i32,
98 message: &'static str,
99 data: Option<serde_json::Value>,
100 }
101
102 impl<'a> RpcServiceT<'a> for ErrorService {
103 type Future = std::future::Ready<MethodResponse>;
104
105 fn call(&self, request: Request<'a>) -> Self::Future {
106 let error = ErrorObject::owned(self.code, self.message, self.data.clone());
107 std::future::ready(MethodResponse::error(request.id.into_owned(), error))
108 }
109 }
110
111 fn request() -> Request<'static> {
112 Request::new("getinfo".into(), None, Id::Number(42))
113 }
114
115 #[tokio::test]
118 async fn replaces_invalid_params_code_preserving_id_message_and_data() {
119 let inner = ErrorService {
120 code: ErrorCode::InvalidParams.code(),
121 message: "bad params",
122 data: Some(serde_json::json!({ "detail": "x" })),
123 };
124
125 let response = FixRpcResponseMiddleware::new(inner).call(request()).await;
126
127 assert!(response.is_error());
128 assert_eq!(response.as_error_code(), Some(i32::from(LegacyCode::Misc)));
129
130 let json: serde_json::Value =
131 serde_json::from_str(response.as_result()).expect("response should be valid json");
132 assert_eq!(json["id"], serde_json::json!(42));
133 assert_eq!(
134 json["error"]["code"],
135 serde_json::json!(i32::from(LegacyCode::Misc))
136 );
137 assert_eq!(json["error"]["message"], "bad params");
138 assert_eq!(json["error"]["data"], serde_json::json!({ "detail": "x" }));
139 }
140
141 #[tokio::test]
144 async fn preserves_string_id() {
145 let inner = ErrorService {
146 code: ErrorCode::InvalidParams.code(),
147 message: "bad params",
148 data: None,
149 };
150
151 let request = Request::new("getinfo".into(), None, Id::Str("curltest".into()));
152 let response = FixRpcResponseMiddleware::new(inner).call(request).await;
153
154 assert_eq!(response.as_error_code(), Some(i32::from(LegacyCode::Misc)));
155 let json: serde_json::Value =
156 serde_json::from_str(response.as_result()).expect("response should be valid json");
157 assert_eq!(json["id"], serde_json::json!("curltest"));
158 }
159
160 #[tokio::test]
162 async fn leaves_other_error_codes_unchanged() {
163 let inner = ErrorService {
164 code: ErrorCode::MethodNotFound.code(),
165 message: "nope",
166 data: None,
167 };
168
169 let response = FixRpcResponseMiddleware::new(inner).call(request()).await;
170
171 assert_eq!(
172 response.as_error_code(),
173 Some(ErrorCode::MethodNotFound.code())
174 );
175 let json: serde_json::Value =
176 serde_json::from_str(response.as_result()).expect("response should be valid json");
177 assert_eq!(json["error"]["message"], "nope");
178 }
179}