hopr_chain_rpc/
helper.rs

1//! Private helper types for JSON RPC operation with an RPC endpoint.
2//!
3//! Most of these types were taken as-is from <https://github.com/gakonst/ethers-rs>, because
4//! they are not exposed as public from the `ethers` crate.
5use ethers::providers::JsonRpcError;
6use serde::de::{MapAccess, Unexpected, Visitor};
7use serde::{de, Deserialize, Serialize};
8use serde_json::value::RawValue;
9use std::fmt;
10
11fn is_zst<T>(_t: &T) -> bool {
12    std::mem::size_of::<T>() == 0
13}
14
15/// A JSON-RPC request
16#[derive(Serialize, Deserialize, Debug)]
17pub struct Request<'a, T> {
18    id: u64,
19    jsonrpc: &'a str,
20    method: &'a str,
21    #[serde(skip_serializing_if = "is_zst")]
22    params: T,
23}
24
25impl<'a, T> Request<'a, T> {
26    /// Creates a new JSON RPC request
27    pub fn new(id: u64, method: &'a str, params: T) -> Self {
28        Self {
29            id,
30            jsonrpc: "2.0",
31            method,
32            params,
33        }
34    }
35}
36
37/// A JSON-RPC response
38#[derive(Debug)]
39#[allow(dead_code)] // not dead code, conforming to the trait
40pub enum Response<'a> {
41    Success { id: u64, result: &'a RawValue },
42    Error { id: u64, error: JsonRpcError },
43    Notification { method: &'a str, params: Params<'a> },
44}
45
46/// JSON-RPC request parameters.
47#[derive(Deserialize, Debug)]
48#[allow(dead_code)] // not dead code, conforming to the trait
49pub struct Params<'a> {
50    pub subscription: ethers::types::U256,
51    #[serde(borrow)]
52    pub result: &'a RawValue,
53}
54
55// FIXME: ideally, this could be auto-derived as an untagged enum, but due to
56// https://github.com/serde-rs/serde/issues/1183 this currently fails
57impl<'de: 'a, 'a> Deserialize<'de> for Response<'a> {
58    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59    where
60        D: serde::Deserializer<'de>,
61    {
62        #[allow(dead_code)] // not dead code, conforming to the trait
63        struct ResponseVisitor<'a>(&'a ());
64        impl<'de: 'a, 'a> Visitor<'de> for ResponseVisitor<'a> {
65            type Value = Response<'a>;
66
67            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
68                formatter.write_str("a valid jsonrpc 2.0 response object")
69            }
70
71            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
72            where
73                A: MapAccess<'de>,
74            {
75                let mut jsonrpc = false;
76
77                // response & error
78                let mut id = None;
79                // only response
80                let mut result = None;
81                // only error
82                let mut error = None;
83                // only notification
84                let mut method = None;
85                let mut params = None;
86
87                while let Some(key) = map.next_key()? {
88                    match key {
89                        "jsonrpc" => {
90                            if jsonrpc {
91                                return Err(de::Error::duplicate_field("jsonrpc"));
92                            }
93
94                            let value = map.next_value()?;
95                            if value != "2.0" {
96                                return Err(de::Error::invalid_value(Unexpected::Str(value), &"2.0"));
97                            }
98
99                            jsonrpc = true;
100                        }
101                        "id" => {
102                            if id.is_some() {
103                                return Err(de::Error::duplicate_field("id"));
104                            }
105
106                            let value: u64 = map.next_value()?;
107                            id = Some(value);
108                        }
109                        "result" => {
110                            if result.is_some() {
111                                return Err(de::Error::duplicate_field("result"));
112                            }
113
114                            let value: &RawValue = map.next_value()?;
115                            result = Some(value);
116                        }
117                        "error" => {
118                            if error.is_some() {
119                                return Err(de::Error::duplicate_field("error"));
120                            }
121
122                            let value: JsonRpcError = map.next_value()?;
123                            error = Some(value);
124                        }
125                        "method" => {
126                            if method.is_some() {
127                                return Err(de::Error::duplicate_field("method"));
128                            }
129
130                            let value: &str = map.next_value()?;
131                            method = Some(value);
132                        }
133                        "params" => {
134                            if params.is_some() {
135                                return Err(de::Error::duplicate_field("params"));
136                            }
137
138                            let value: Params = map.next_value()?;
139                            params = Some(value);
140                        }
141                        key => {
142                            return Err(de::Error::unknown_field(
143                                key,
144                                &["id", "jsonrpc", "result", "error", "params", "method"],
145                            ))
146                        }
147                    }
148                }
149
150                // jsonrpc version must be present in all responses
151                if !jsonrpc {
152                    return Err(de::Error::missing_field("jsonrpc"));
153                }
154
155                match (id, result, error, method, params) {
156                    (Some(id), Some(result), None, None, None) => Ok(Response::Success { id, result }),
157                    (Some(id), None, Some(error), None, None) => Ok(Response::Error { id, error }),
158                    (None, None, None, Some(method), Some(params)) => Ok(Response::Notification { method, params }),
159                    _ => Err(de::Error::custom(
160                        "response must be either a success/error or notification object",
161                    )),
162                }
163            }
164        }
165
166        deserializer.deserialize_map(ResponseVisitor(&()))
167    }
168}