starknet_devnet_server/rpc_core/
request.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use starknet_types::messaging::MessageToL2;
5
6use crate::error::Error;
7
8/// A JSON-RPC request object, a method call
9#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(deny_unknown_fields)]
11pub struct RpcMethodCall {
12    /// The version of the protocol
13    pub jsonrpc: Version,
14    /// The name of the method to execute
15    pub method: String,
16    /// An array or object containing the parameters to be passed to the function.
17    #[serde(default = "no_params")]
18    pub params: RequestParams,
19    /// The identifier for this request issued by the client,
20    /// An [Id] must be a String, null or a number.
21    /// If missing it's considered a notification in [Version::V2]
22    pub id: Id,
23}
24
25impl RpcMethodCall {
26    pub fn id(&self) -> Id {
27        self.id.clone()
28    }
29}
30
31impl TryFrom<MessageToL2> for RpcMethodCall {
32    type Error = Error;
33
34    fn try_from(message: MessageToL2) -> Result<Self, Self::Error> {
35        (&message).try_into()
36    }
37}
38
39impl TryFrom<&MessageToL2> for RpcMethodCall {
40    type Error = Error;
41
42    fn try_from(message: &MessageToL2) -> Result<Self, Self::Error> {
43        let message_serialized =
44            serde_json::to_value(message).map_err(|e| Error::ConversionError(e.to_string()))?;
45
46        let params = message_serialized
47            .as_object()
48            .ok_or(Error::ConversionError("Message not convertible to JSON object".into()))?;
49
50        Ok(Self {
51            jsonrpc: Version::V2,
52            method: "devnet_postmanSendMessageToL2".into(),
53            params: RequestParams::Object(params.clone()),
54            id: Id::Number(0),
55        })
56    }
57}
58
59/// Represents a JSON-RPC request which is considered a notification (missing [Id] optional
60/// [Version])
61#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(deny_unknown_fields)]
63pub struct RpcNotification {
64    pub jsonrpc: Option<Version>,
65    pub method: String,
66    #[serde(default = "no_params")]
67    pub params: RequestParams,
68}
69
70/// Representation of a single JSON-RPC call
71#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(untagged)]
73pub enum RpcCall {
74    /// the RPC method to invoke
75    MethodCall(RpcMethodCall),
76    /// A notification (no [Id] provided)
77    Notification(RpcNotification),
78    /// Invalid call
79    Invalid {
80        /// id or [Id::Null]
81        #[serde(default = "null_id")]
82        id: Id,
83    },
84}
85
86/// Represents a JSON-RPC request.
87#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
88#[cfg_attr(test, derive(Serialize))]
89#[serde(deny_unknown_fields)]
90#[serde(untagged)]
91pub enum Request {
92    /// single json rpc request [RpcCall]
93    Single(RpcCall),
94    /// batch of several requests
95    Batch(Vec<RpcCall>),
96}
97
98/// Request parameters
99#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(untagged, deny_unknown_fields)]
101pub enum RequestParams {
102    /// no parameters provided
103    None,
104    /// An array of JSON values
105    Array(Vec<serde_json::Value>),
106    /// a map of JSON values
107    Object(serde_json::Map<String, serde_json::Value>),
108}
109
110impl From<RequestParams> for serde_json::Value {
111    fn from(params: RequestParams) -> Self {
112        match params {
113            RequestParams::None => serde_json::Value::Null,
114            RequestParams::Array(arr) => arr.into(),
115            RequestParams::Object(obj) => obj.into(),
116        }
117    }
118}
119
120fn no_params() -> RequestParams {
121    RequestParams::None
122}
123
124/// Represents the version of the RPC protocol
125#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
126pub enum Version {
127    #[serde(rename = "2.0")]
128    V2,
129    #[serde(rename = "1.0")]
130    V1,
131}
132
133#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
134#[serde(untagged)]
135pub enum Id {
136    String(String),
137    Number(i64),
138    Null,
139}
140
141impl fmt::Display for Id {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        match self {
144            Id::String(s) => s.fmt(f),
145            Id::Number(n) => n.fmt(f),
146            Id::Null => f.write_str("null"),
147        }
148    }
149}
150
151fn null_id() -> Id {
152    Id::Null
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn can_serialize_batch() {
161        let batch = Request::Batch(vec![
162            RpcCall::MethodCall(RpcMethodCall {
163                jsonrpc: Version::V2,
164                method: "eth_method".to_owned(),
165                params: RequestParams::Array(vec![
166                    serde_json::Value::from(999),
167                    serde_json::Value::from(1337),
168                ]),
169                id: Id::Number(1),
170            }),
171            RpcCall::Notification(RpcNotification {
172                jsonrpc: Some(Version::V2),
173                method: "eth_method".to_owned(),
174                params: RequestParams::Array(vec![serde_json::Value::from(999)]),
175            }),
176        ]);
177
178        let obj = serde_json::to_string(&batch).unwrap();
179        assert_eq!(
180            obj,
181            r#"[{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1},{"jsonrpc":"2.0","method":"eth_method","params":[999]}]"#
182        );
183    }
184
185    #[test]
186    fn can_deserialize_batch() {
187        let s = r#"[{}, {"jsonrpc": "2.0", "method": "eth_call", "params": [1337,420], "id": 1},{"jsonrpc": "2.0", "method": "notify", "params": [999]}]"#;
188        let obj: Request = serde_json::from_str(s).unwrap();
189        assert_eq!(
190            obj,
191            Request::Batch(vec![
192                RpcCall::Invalid { id: Id::Null },
193                RpcCall::MethodCall(RpcMethodCall {
194                    jsonrpc: Version::V2,
195                    method: "eth_call".to_owned(),
196                    params: RequestParams::Array(vec![
197                        serde_json::Value::from(1337),
198                        serde_json::Value::from(420)
199                    ]),
200                    id: Id::Number(1)
201                }),
202                RpcCall::Notification(RpcNotification {
203                    jsonrpc: Some(Version::V2),
204                    method: "notify".to_owned(),
205                    params: RequestParams::Array(vec![serde_json::Value::from(999)])
206                })
207            ])
208        )
209    }
210
211    #[test]
212    fn can_serialize_method() {
213        let m = RpcMethodCall {
214            jsonrpc: Version::V2,
215            method: "eth_method".to_owned(),
216            params: RequestParams::Array(vec![
217                serde_json::Value::from(999),
218                serde_json::Value::from(1337),
219            ]),
220            id: Id::Number(1),
221        };
222
223        let obj = serde_json::to_string(&m).unwrap();
224        assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337],"id":1}"#);
225    }
226
227    #[test]
228    fn can_serialize_call_notification() {
229        let n = RpcCall::Notification(RpcNotification {
230            jsonrpc: Some(Version::V2),
231            method: "eth_method".to_owned(),
232            params: RequestParams::Array(vec![serde_json::Value::from(999)]),
233        });
234        let obj = serde_json::to_string(&n).unwrap();
235        assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999]}"#);
236    }
237
238    #[test]
239    fn can_serialize_notification() {
240        let n = RpcNotification {
241            jsonrpc: Some(Version::V2),
242            method: "eth_method".to_owned(),
243            params: RequestParams::Array(vec![
244                serde_json::Value::from(999),
245                serde_json::Value::from(1337),
246            ]),
247        };
248        let obj = serde_json::to_string(&n).unwrap();
249        assert_eq!(obj, r#"{"jsonrpc":"2.0","method":"eth_method","params":[999,1337]}"#);
250    }
251
252    #[test]
253    fn can_deserialize_notification() {
254        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337]}"#;
255        let obj: RpcNotification = serde_json::from_str(s).unwrap();
256
257        assert_eq!(
258            obj,
259            RpcNotification {
260                jsonrpc: Some(Version::V2),
261                method: "eth_method".to_owned(),
262                params: RequestParams::Array(vec![
263                    serde_json::Value::from(999),
264                    serde_json::Value::from(1337)
265                ])
266            }
267        );
268        let s = r#"{"jsonrpc": "2.0", "method": "foobar"}"#;
269        let obj: RpcNotification = serde_json::from_str(s).unwrap();
270        assert_eq!(
271            obj,
272            RpcNotification {
273                jsonrpc: Some(Version::V2),
274                method: "foobar".to_owned(),
275                params: RequestParams::None,
276            }
277        );
278        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999,1337], "id": 1}"#;
279        let obj: Result<RpcNotification, _> = serde_json::from_str(s);
280        assert!(obj.is_err());
281    }
282
283    #[test]
284    fn can_deserialize_call() {
285        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999]}"#;
286        let obj: RpcCall = serde_json::from_str(s).unwrap();
287        assert_eq!(
288            obj,
289            RpcCall::Notification(RpcNotification {
290                jsonrpc: Some(Version::V2),
291                method: "eth_method".to_owned(),
292                params: RequestParams::Array(vec![serde_json::Value::from(999)])
293            })
294        );
295
296        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [999], "id": 1}"#;
297        let obj: RpcCall = serde_json::from_str(s).unwrap();
298        assert_eq!(
299            obj,
300            RpcCall::MethodCall(RpcMethodCall {
301                jsonrpc: Version::V2,
302                method: "eth_method".to_owned(),
303                params: RequestParams::Array(vec![serde_json::Value::from(999)]),
304                id: Id::Number(1)
305            })
306        );
307
308        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": [], "id": 1}"#;
309        let obj: RpcCall = serde_json::from_str(s).unwrap();
310        assert_eq!(
311            obj,
312            RpcCall::MethodCall(RpcMethodCall {
313                jsonrpc: Version::V2,
314                method: "eth_method".to_owned(),
315                params: RequestParams::Array(vec![]),
316                id: Id::Number(1)
317            })
318        );
319
320        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "params": null, "id": 1}"#;
321        let obj: RpcCall = serde_json::from_str(s).unwrap();
322        assert_eq!(
323            obj,
324            RpcCall::MethodCall(RpcMethodCall {
325                jsonrpc: Version::V2,
326                method: "eth_method".to_owned(),
327                params: RequestParams::None,
328                id: Id::Number(1)
329            })
330        );
331
332        let s = r#"{"jsonrpc": "2.0", "method": "eth_method", "id": 1}"#;
333        let obj: RpcCall = serde_json::from_str(s).unwrap();
334        assert_eq!(
335            obj,
336            RpcCall::MethodCall(RpcMethodCall {
337                jsonrpc: Version::V2,
338                method: "eth_method".to_owned(),
339                params: RequestParams::None,
340                id: Id::Number(1)
341            })
342        );
343    }
344}