eth_jsonrpc_lib/rpc_request/
request.rs

1// Copyright Rivtower Technologies LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14#![allow(non_camel_case_types)]
15
16use crate::internals::construct_params;
17use crate::rpc_types::ethereum_types::{
18    EthBlock, EthCallRequest, EthFilter, EthLog, EthReceipt, EthRpcTransaction,
19    EthTransactionRequest,
20};
21use crate::rpc_types::{
22    Block, BlockNumber, Boolean, CallRequest, CallResult, CensorAddrs, Data, Data20, Data32,
23    Filter, FilterChanges, Id, Integer, LicenseInfo, Log, MetaData, OneItemTupleTrick, PeersInfo,
24    PoolTxNum, Quantity, Receipt, RpcTransaction, SoftwareVersion, TxResponse, Version,
25};
26/// JSON-RPC Request.
27use serde_json;
28
29pub type Logs = Vec<Log>;
30pub type EthLogs = Vec<EthLog>;
31pub type Accounts = Vec<Data20>;
32
33#[derive(Debug, Clone, PartialEq)]
34pub struct RequestInfo {
35    pub jsonrpc: Option<Version>,
36    pub id: Id,
37}
38
39impl RequestInfo {
40    pub fn new(jsonrpc: Option<Version>, id: Id) -> Self {
41        RequestInfo { jsonrpc, id }
42    }
43    pub fn null() -> Self {
44        RequestInfo {
45            jsonrpc: None,
46            id: Id::Null,
47        }
48    }
49}
50
51impl Default for RequestInfo {
52    fn default() -> Self {
53        RequestInfo::new(Some(Version::default()), Id::default())
54    }
55}
56
57/// JSON-RPC 2.0 Request object (http://www.jsonrpc.org/specification#request_object)
58#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
59pub struct Request {
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub jsonrpc: Option<Version>,
62    #[serde(default, skip_serializing_if = "Id::is_null")]
63    pub id: Id,
64    /// Contain method and params.
65    #[serde(flatten)]
66    pub call: Call,
67}
68
69impl Request {
70    pub fn new(jsonrpc: Option<Version>, id: Id, call: Call) -> Self {
71        Request { jsonrpc, id, call }
72    }
73    pub fn get_method(&self) -> &str {
74        self.call.get_method()
75    }
76    pub fn get_info(&self) -> RequestInfo {
77        RequestInfo::new(self.jsonrpc.clone(), self.id.clone())
78    }
79}
80
81impl Into<String> for Request {
82    fn into(self) -> String {
83        serde_json::to_string(&self).unwrap()
84    }
85}
86
87#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
88pub struct PartialRequest {
89    pub jsonrpc: Option<Version>,
90    pub id: Id,
91    /// Contain method and params.
92    #[serde(flatten)]
93    pub call: Option<PartialCall>,
94}
95
96impl PartialRequest {
97    pub fn get_info(&self) -> RequestInfo {
98        RequestInfo::new(self.jsonrpc.clone(), self.id.clone())
99    }
100}
101
102macro_rules! define_call {
103    ($( ($enum_name:ident, $params_name:ident: $params_list:expr, $result_type:ident) ),+ ,) => {
104        define_call!($( ($enum_name, $params_name: $params_list, $result_type) ),+);
105    };
106    ($( ($enum_name:ident, $params_name:ident: $params_list:expr, $result_type:ident) ),+ ) => {
107
108        $(
109            construct_params!($params_name: $params_list, $result_type);
110        )+
111
112
113        #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
114        #[serde(untagged)]
115        #[allow(clippy::large_enum_variant)]   // TODO
116        pub enum ResponseResult {
117            #[serde(rename = "null")]
118            Null,
119            $(
120                $enum_name($result_type),
121            )+
122        }
123
124        impl Default for ResponseResult {
125            fn default() -> Self {
126                ResponseResult::Null
127            }
128        }
129
130        #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
131        #[serde(tag = "method", rename_all = "camelCase")]
132        pub enum Call {
133            $(
134                $enum_name { params: $params_name},
135            )+
136        }
137
138        #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
139        #[serde(tag = "method", rename_all = "camelCase")]
140        pub enum PartialCall {
141            $(
142                $enum_name {
143                    params: Option<serde_json::Value>
144                },
145            )+
146        }
147
148        impl Call {
149            pub fn get_method(&self) -> &str {
150                match self {
151                    $(
152                        Call::$enum_name { ref params } => params.method_name(),
153                    )+
154                }
155            }
156            pub fn into_request(self, id: u64) -> Request {
157                Request::new(
158                    Some(Version::default()),
159                    Id::Num(id),
160                    self,
161                )
162            }
163        }
164
165        $(
166            impl Into<$params_name> for Call {
167                fn into(self) -> $params_name{
168                    if let Call::$enum_name{ params } = self {
169                        params
170                    } else {
171                        // IMHO, in Rust, no static check can do this.
172                        // If https://github.com/rust-lang/rfcs/pull/1450 merged,
173                        // I think I can remove this panic.
174                        panic!("The method and params are one to one correspondence.")
175                    }
176                }
177            }
178
179            impl From<$params_name> for Call {
180                fn from(params: $params_name) -> Call {
181                    Call::$enum_name{ params }
182                }
183            }
184
185            impl $params_name {
186                pub fn into_request(self, id: u64) -> Request {
187                    Request::new(
188                        Some(Version::default()),
189                        Id::Num(id),
190                        self.into(),
191                    )
192                }
193            }
194        )+
195    };
196}
197
198pub trait JsonRpcRequest {
199    type Response;
200    fn required_len() -> usize;
201    fn valid_len() -> usize;
202    fn method_name(&self) -> &'static str;
203    fn value_vec(self) -> Vec<serde_json::Value>;
204}
205
206// Q. How to add a JSON-RPC method?
207//
208// A.
209//  First, add a tuple into the follow macro.
210//
211//    - The 1st item in tuple is a enum name used in `Call` / `PartialCall`.
212//      The enum name will used to generate the JSON-RPC method name.
213//      The enum name is PascalCase and JSON-RPC method name is camelCase.
214//
215//    - The 2st item is params type name and it's structure.
216//      The params type has some methods, such as `new()` and `method_name()`.
217//      More details can found in the definition of `construct_params`.
218//
219//    - The 3rd item is the type of result of Response object on success.
220//
221//  Second, implement `TryInto<ProtoRequest>` for the new params type.
222//
223//  DONE!
224#[macro_export]
225macro_rules! impl_for_each_jsonrpc_requests {
226    ($macro:ident) => {
227        $macro!(
228            (BlockNumber, BlockNumberParams: [], Quantity),
229            (PeerCount, PeerCountParams: [], Quantity),
230            (SendRawTransaction, SendRawTransactionParams: [Data], TxResponse),
231            (SendTransaction, SendTransactionParams: [Data], TxResponse),
232            (GetBlockByHash, GetBlockByHashParams: [Data32, Boolean], Block),
233            (GetBlockByNumber, GetBlockByNumberParams: [BlockNumber, Boolean], Block),
234            (GetTransactionReceipt, GetTransactionReceiptParams: [Data32], Receipt),
235            (GetLogs, GetLogsParams: [Filter], Logs),
236            (GetTransactionCount, GetTransactionCountParams: [Data20, BlockNumber], Quantity),
237            (GetCode, GetCodeParams: [Data20, BlockNumber], Data),
238            (GetAbi, GetAbiParams: [Data20, BlockNumber], Data),
239            (GetBalance, GetBalanceParams: [Data20, BlockNumber], Quantity),
240            (NewFilter, NewFilterParams: [Filter], Quantity),
241            (NewBlockFilter, NewBlockFilterParams: [], Quantity),
242            (UninstallFilter, UninstallFilterParams: [Quantity], Boolean),
243            (GetFilterChanges, GetFilterChangesParams: [Quantity], FilterChanges),
244            (GetFilterLogs, GetFilterLogsParams: [Quantity], Logs),
245            (GetTransactionProof, GetTransactionProofParams: [Data32], Data),
246            (GetMetaData, GetMetaDataParams: [BlockNumber], MetaData),
247            (GetStateProof, GetStateProofParams: [Data20, Data32, BlockNumber], Data),
248            (GetBlockHeader, GetBlockHeaderParams: [BlockNumber], Data),
249            (GetStorageAt, GetStorageKeyParams: [Data20, Data32, BlockNumber], Data),
250            (GetVersion, GetVersionParams: [], SoftwareVersion),
251            (EstimateQuota, EstimateQuotaParams: [CallRequest, BlockNumber], Quantity),
252            (LicenseInfo, LicenseInfoParams: [], LicenseInfo),
253            (GetPoolTxNum, GetPoolTxNumParams: [], PoolTxNum),
254            (OpCensoredAddress, OpCensoredAddressParams: [Integer, Data20], Boolean),
255            (PeersInfo, PeersInfoParams: [
256                #[serde(default)]
257                Boolean
258            ], PeersInfo),
259            (GetTransaction, GetTransactionParams: [
260                Data32,
261                #[serde(default)]
262                Boolean
263            ], RpcTransaction),
264            (Call, CallParams: [
265                CallRequest,
266                BlockNumber,
267                #[serde(default)]
268                Boolean
269            ], CallResult),
270            (GetCensoredAddrs, GetCensoredAddrsParams: [], CensorAddrs),
271            // ethereum jsonrpc
272            (eth_blockNumber, eth_blockNumberParams: [], Quantity),
273            (eth_chainId, eth_chainIdParams: [], Quantity),
274            (eth_getBlockByHash, eth_getBlockByHashParams: [Data32, Boolean], EthBlock),
275            (eth_getBlockByNumber, eth_getBlockByNumberParams: [BlockNumber, Boolean], EthBlock),
276            (eth_getTransactionByHash, eth_getTransactionByHashParams: [Data32], EthRpcTransaction),
277            (eth_getTransactionByBlockHashAndIndex, eth_getTransactionByBlockHashAndIndexParams: [Data32, Integer], EthRpcTransaction),
278            (eth_getTransactionByBlockNumberAndIndex, eth_getTransactionByBlockNumberAndIndexParams: [BlockNumber, Integer], EthRpcTransaction),
279            (eth_getBlockTransactionCountByHash, eth_getBlockTransactionCountByHashParams: [Data32], Integer),
280            (eth_getBlockTransactionCountByNumber, eth_getBlockTransactionCountByNumberParams: [BlockNumber], Integer),
281            (eth_getTransactionReceipt, eth_getTransactionReceiptParams: [Data32], EthReceipt),
282            (eth_getBalance, eth_getBalanceParams: [Data20, BlockNumber], Quantity),
283            (eth_syncing, eth_syncingParams: [], Boolean),
284            (eth_getStorageAt, eth_getStorageAtParams: [Data20, Quantity, BlockNumber], Data),
285            (eth_getCode, eth_getCodeParams: [Data20, BlockNumber], Data),
286            (eth_getTransactionCount, eth_getTransactionCountParams: [Data20, BlockNumber], Quantity),
287            (eth_getLogs, eth_getLogsParams: [EthFilter], EthLogs),
288            (eth_call, eth_callParams: [EthCallRequest, BlockNumber], Data),
289            (eth_estimateGas, eth_estimateGasParams: [
290                EthCallRequest,
291                #[serde(default)]
292                BlockNumber
293            ], Quantity),
294            (eth_gasPrice, eth_gasPriceParams: [], Quantity),
295            (eth_maxPriorityFeePerGas, eth_maxPriorityFeePerGasParams: [], Quantity),
296            (eth_sendTransaction, eth_sendTransactionParams: [EthTransactionRequest], Data32),
297            (eth_sendRawTransaction, eth_sendRawTransactionParams: [Data], Data32),
298            (eth_accounts, eth_accountsParams: [], Accounts),
299            // net jsonrpc
300            (net_version, net_versionParams: [], Integer),
301        );
302    };
303}
304
305impl_for_each_jsonrpc_requests!(define_call);