/home/runner/work/creditcoin/creditcoin/pallets/creditcoin/src/ocw/rpc.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use self::errors::RpcError; |
2 | | |
3 | | use super::OffchainResult; |
4 | | use alloc::string::String; |
5 | | use core::{convert::TryFrom, fmt}; |
6 | | use ethereum_types::{H160, H256, U256, U64}; |
7 | | use serde::{ |
8 | | de::{Error, Unexpected, Visitor}, |
9 | | Deserialize, Deserializer, Serialize, Serializer, |
10 | | }; |
11 | | use sp_runtime::offchain::{http, Duration, Timestamp}; |
12 | | use sp_std::{prelude::*, vec::Vec}; |
13 | | |
14 | | use crate::ExternalTxId; |
15 | | |
16 | | pub mod errors { |
17 | | use super::JsonRpcError; |
18 | | use crate::ocw::errors::impl_from_error; |
19 | | use sp_runtime::offchain::{http::PendingRequest, HttpError}; |
20 | | |
21 | 1 | #[derive(Debug)] |
22 | | pub enum RpcError { |
23 | | NoResult, |
24 | | FailureResponse(JsonRpcError), |
25 | | SerdeError(serde_json::Error), |
26 | | HttpError(HttpError), |
27 | | RequestError(sp_runtime::offchain::http::Error), |
28 | | InvalidArgument(&'static str), |
29 | | Timeout(PendingRequest), |
30 | | } |
31 | | |
32 | | impl_from_error!( |
33 | | RpcError, |
34 | | JsonRpcError => FailureResponse, |
35 | | serde_json::Error => SerdeError, |
36 | | HttpError => HttpError, |
37 | | sp_runtime::offchain::http::Error => RequestError, |
38 | | PendingRequest => Timeout |
39 | | ); |
40 | | } |
41 | | |
42 | | #[repr(transparent)] |
43 | 0 | #[derive(Clone, Debug)] |
44 | | pub struct VecString(Vec<u8>, ()); |
45 | | |
46 | | impl TryFrom<&[u8]> for VecString { |
47 | | type Error = core::str::Utf8Error; |
48 | | fn try_from(value: &[u8]) -> Result<Self, Self::Error> { |
49 | 0 | let _ = core::str::from_utf8(value)?; |
50 | 0 | Ok(VecString(Vec::from(value), ())) |
51 | 0 | } |
52 | | } |
53 | | |
54 | | impl From<&str> for VecString { |
55 | 432 | fn from(s: &str) -> Self { |
56 | 432 | VecString(Vec::from(s.as_bytes()), ()) |
57 | 432 | } |
58 | | } |
59 | | |
60 | | impl serde::Serialize for VecString { |
61 | 567 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
62 | 567 | where |
63 | 567 | S: serde::Serializer, |
64 | 567 | { |
65 | 567 | serde::Serialize::serialize( |
66 | 567 | core::str::from_utf8(&self.0) |
67 | 567 | .expect("vecstrings cannot be constructed without validating utf8; qed"), |
68 | 567 | serializer, |
69 | 567 | ) |
70 | 567 | } |
71 | | } |
72 | | |
73 | | impl<'de> serde::Deserialize<'de> for VecString { |
74 | 87 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
75 | 87 | where |
76 | 87 | D: serde::Deserializer<'de>, |
77 | 87 | { |
78 | 87 | let s: &str = serde::Deserialize::deserialize(deserializer)?0 ; |
79 | 87 | Ok(VecString(Vec::from(s.as_bytes()), ())) |
80 | 87 | } |
81 | | } |
82 | | |
83 | | /// Raw bytes wrapper |
84 | 18 | #[derive(Clone16 , Debug0 , Default, PartialEq0 , Eq, Hash0 )] |
85 | | pub struct Bytes(pub Vec<u8>); |
86 | | |
87 | | impl<T: Into<Vec<u8>>> From<T> for Bytes { |
88 | 1 | fn from(data: T) -> Self { |
89 | 1 | Bytes(data.into()) |
90 | 1 | } |
91 | | } |
92 | | |
93 | | impl Serialize for Bytes { |
94 | 0 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
95 | 0 | where |
96 | 0 | S: Serializer, |
97 | 0 | { |
98 | 0 | let mut serialized = String::from("0x"); |
99 | 0 | serialized.push_str(&hex::encode(&self.0)); |
100 | 0 | serializer.serialize_str(serialized.as_ref()) |
101 | 0 | } |
102 | | } |
103 | | |
104 | | impl<'a> Deserialize<'a> for Bytes { |
105 | 22 | fn deserialize<D>(deserializer: D) -> Result<Bytes, D::Error> |
106 | 22 | where |
107 | 22 | D: Deserializer<'a>, |
108 | 22 | { |
109 | 22 | deserializer.deserialize_identifier(BytesVisitor) |
110 | 22 | } |
111 | | } |
112 | | |
113 | | pub type Address = H160; |
114 | | |
115 | | struct BytesVisitor; |
116 | | |
117 | | impl<'a> Visitor<'a> for BytesVisitor { |
118 | | type Value = Bytes; |
119 | | |
120 | 0 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
121 | 0 | write!(formatter, "a 0x-prefixed hex-encoded vector of bytes") |
122 | 0 | } |
123 | | |
124 | 22 | fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> |
125 | 22 | where |
126 | 22 | E: Error, |
127 | 22 | { |
128 | 22 | if value.len() >= 2 && &value[0..2] == "0x" { |
129 | 22 | let bytes = hex::decode(&value[2..]) |
130 | 22 | .map_err(|e| Error::custom(alloc::format!("Invalid hex: {}", e))0 )?0 ; |
131 | 22 | Ok(Bytes(bytes)) |
132 | | } else { |
133 | 0 | Err(Error::invalid_value(Unexpected::Str(value), &"0x prefix")) |
134 | | } |
135 | 22 | } |
136 | | |
137 | 0 | fn visit_string<E>(self, value: String) -> Result<Self::Value, E> |
138 | 0 | where |
139 | 0 | E: Error, |
140 | 0 | { |
141 | 0 | self.visit_str(value.as_ref()) |
142 | 0 | } |
143 | | } |
144 | | |
145 | 212 | #[derive(serde::Serialize, s0 erde::Deserializ0 e0 , Clone0 , Debug0 )] |
146 | | pub struct JsonRpcRequest { |
147 | | jsonrpc: VecString, |
148 | | method: VecString, |
149 | | params: Vec<serde_json::Value>, |
150 | | id: u64, |
151 | | } |
152 | | |
153 | | const REQUEST_TIMEOUT_PERIOD: Duration = Duration::from_millis(5000); |
154 | | |
155 | 77 | fn timeout() -> Timestamp { |
156 | 77 | sp_io::offchain::timestamp().add(REQUEST_TIMEOUT_PERIOD) |
157 | 77 | } |
158 | | |
159 | | impl JsonRpcRequest { |
160 | | #[allow(dead_code)] |
161 | 0 | pub fn with_method(method: impl Into<VecString>) -> Self { |
162 | 0 | let method = method.into(); |
163 | 0 | Self { jsonrpc: VecString::from("2.0"), method, params: Vec::new(), id: 1 } |
164 | 0 | } |
165 | 212 | pub fn new( |
166 | 212 | method: impl Into<VecString>, |
167 | 212 | params: impl IntoIterator<Item = serde_json::Value>, |
168 | 212 | ) -> Self { |
169 | 212 | Self { |
170 | 212 | jsonrpc: VecString::from("2.0"), |
171 | 212 | method: method.into(), |
172 | 212 | params: params.into_iter().collect(), |
173 | 212 | id: 1, |
174 | 212 | } |
175 | 212 | } |
176 | | |
177 | | #[allow(dead_code)] |
178 | 0 | pub fn param(&mut self, param: serde_json::Value) -> &mut Self { |
179 | 0 | self.params.push(param); |
180 | 0 | self |
181 | 0 | } |
182 | | |
183 | | #[allow(dead_code)] |
184 | 135 | pub fn to_bytes(&self) -> Vec<u8> { |
185 | 135 | serde_json::to_vec(self).expect("serialization cannot fail; qed") |
186 | 135 | } |
187 | | |
188 | 77 | pub fn send<T: for<'de> serde::Deserialize<'de>>( |
189 | 77 | self, |
190 | 77 | rpc_url: &str, |
191 | 77 | ) -> OffchainResult<T, RpcError> { |
192 | 77 | let rpc_bytes = serde_json::to_vec(&self).map_err(RpcError::SerdeError)?0 ; |
193 | 77 | let timeout = timeout(); |
194 | 77 | let response = http::Request::post(rpc_url, vec![rpc_bytes]) |
195 | 77 | .add_header("Content-Type", "application/json") |
196 | 77 | .send()?0 |
197 | 77 | .try_wait(timeout)?0 ?0 ; |
198 | 77 | let body: Vec<u8> = response.body().collect(); |
199 | 77 | let rpc_response: JsonRpcResponse<T> = serde_json::from_slice(&body)?0 ; |
200 | 77 | rpc_response.result() |
201 | 77 | } |
202 | | } |
203 | | |
204 | 763 | #[derive(s338 erde::Deserializ87 e, serd143 e::Serializ143 e143 , Clone0 , Debug0 )] |
205 | | pub struct JsonRpcResponse<T> { |
206 | | #[allow(dead_code)] |
207 | | pub jsonrpc: VecString, |
208 | | #[allow(dead_code)] |
209 | | pub id: u64, |
210 | | pub error: Option<JsonRpcError>, |
211 | | pub result: Option<T>, |
212 | | } |
213 | | |
214 | | impl<T> JsonRpcResponse<T> { |
215 | | pub fn result(self) -> Result<T, RpcError> { |
216 | 77 | if let Some(err1 ) = self.error { |
217 | 1 | return Err(err.into()); |
218 | 76 | } |
219 | 76 | if let Some(result71 ) = self.result { |
220 | 71 | Ok(result) |
221 | | } else { |
222 | 5 | Err(RpcError::NoResult) |
223 | | } |
224 | 77 | } |
225 | | } |
226 | | |
227 | | #[allow(dead_code)] |
228 | 5 | #[derive(s2 erde::Deserializ0 e, serd1 e::Serializ1 e1 , Clone0 , Debug1 )] |
229 | | pub struct JsonRpcError { |
230 | | pub code: i32, |
231 | | pub message: String, |
232 | | } |
233 | | |
234 | 572 | #[derive(s418 erde::Deserializ22 e, Clone16 , Debug0 , Default18 )] |
235 | | pub struct EthTransaction { |
236 | | /// Hash |
237 | | pub hash: H256, |
238 | | /// Block number. None when pending. |
239 | | #[serde(rename = "blockNumber")] |
240 | | pub block_number: Option<U64>, |
241 | | /// Sender |
242 | | #[serde(default, skip_serializing_if = "Option::is_none")] |
243 | | pub from: Option<Address>, |
244 | | /// Recipient (None when contract creation) |
245 | | pub to: Option<Address>, |
246 | | /// Transfered value |
247 | | pub value: U256, |
248 | | /// Input data |
249 | | input: Bytes, |
250 | | } |
251 | | |
252 | | impl EthTransaction { |
253 | 9 | pub fn selector(&self) -> &[u8] { |
254 | 9 | &self.input.0[..4] |
255 | 9 | } |
256 | | |
257 | 10 | pub fn is_input_empty(&self) -> bool { |
258 | 10 | self.input.0.len() <= 4 |
259 | 10 | } |
260 | | |
261 | 22 | pub fn input(&self) -> &[u8] { |
262 | 22 | &self.input.0[4..] |
263 | 22 | } |
264 | | } |
265 | | |
266 | | #[cfg(test)] |
267 | | impl EthTransaction { |
268 | 21 | pub fn set_input(&mut self, input: &[u8]) { |
269 | 21 | self.input.0 = input.to_vec(); |
270 | 21 | } |
271 | | } |
272 | | |
273 | 420 | #[derive(s294 erde::Deserializ21 e, Clone0 , Debug0 , Default29 )] |
274 | | pub struct EthTransactionReceipt { |
275 | | /// Transaction hash. |
276 | | #[serde(rename = "transactionHash")] |
277 | | pub transaction_hash: H256, |
278 | | /// Number of the block this transaction was included within. |
279 | | #[serde(rename = "blockNumber")] |
280 | | pub block_number: Option<U64>, |
281 | | /// Sender |
282 | | /// Note: default address if the client did not return this value |
283 | | /// (maintains backwards compatibility for <= 0.7.0 when this field was missing) |
284 | | #[serde(default)] |
285 | | pub from: Address, |
286 | | /// Recipient (None when contract creation) |
287 | | /// Note: Also `None` if the client did not return this value |
288 | | /// (maintains backwards compatibility for <= 0.7.0 when this field was missing) |
289 | | #[serde(default)] |
290 | | pub to: Option<Address>, |
291 | | /// Status: either 1 (success) or 0 (failure). |
292 | | pub status: Option<U64>, |
293 | | } |
294 | | |
295 | 115 | #[derive(s110 erde::Deserialize, Clone0 , Debug0 , Default0 )] |
296 | | pub struct EthBlock { |
297 | | /// Timestamp of the block's collation. |
298 | | pub timestamp: U64, |
299 | | } |
300 | | |
301 | | impl EthTransactionReceipt { |
302 | | pub fn is_success(&self) -> bool { |
303 | 41 | if let Some(status) = self.status { |
304 | 41 | !status.is_zero() |
305 | | } else { |
306 | 0 | false |
307 | | } |
308 | 41 | } |
309 | | } |
310 | | |
311 | 50 | fn to_json_hex(bytes: &[u8]) -> String { |
312 | 50 | use core::ops::Not; |
313 | 50 | let hex = hex::encode(bytes); |
314 | 50 | if hex.is_empty().not() { |
315 | 49 | let mut buf = String::with_capacity(hex.len() + 2); |
316 | 49 | buf.push_str("0x"); |
317 | 49 | buf.push_str(&hex); |
318 | 49 | buf |
319 | | } else { |
320 | 1 | hex |
321 | | } |
322 | 50 | } |
323 | | |
324 | 7 | fn format_as_hex<T: sp_std::fmt::LowerHex>(value: T) -> String { |
325 | 7 | alloc::format!("0x{:x}", value) |
326 | 7 | } |
327 | | |
328 | 25 | pub fn eth_get_transaction( |
329 | 25 | tx_id: &ExternalTxId, |
330 | 25 | rpc_url: &str, |
331 | 25 | ) -> OffchainResult<EthTransaction, RpcError> { |
332 | 25 | let rpc_req = JsonRpcRequest::new( |
333 | 25 | "eth_getTransactionByHash", |
334 | 25 | Some(serde_json::Value::String(to_json_hex(tx_id.as_ref()))), |
335 | 25 | ); |
336 | 25 | rpc_req.send(rpc_url) |
337 | 25 | } |
338 | | |
339 | 22 | pub fn eth_get_transaction_receipt( |
340 | 22 | tx_id: &ExternalTxId, |
341 | 22 | rpc_url: &str, |
342 | 22 | ) -> OffchainResult<EthTransactionReceipt, RpcError> { |
343 | 22 | let rpc_req = JsonRpcRequest::new( |
344 | 22 | "eth_getTransactionReceipt", |
345 | 22 | Some(serde_json::Value::String(to_json_hex(tx_id.as_ref()))), |
346 | 22 | ); |
347 | 22 | rpc_req.send(rpc_url) |
348 | 22 | } |
349 | | |
350 | 21 | pub fn eth_get_block_number(rpc_url: &str) -> OffchainResult<U64, RpcError> { |
351 | 21 | let rpc_req = JsonRpcRequest::new("eth_blockNumber", None); |
352 | 21 | rpc_req.send(rpc_url) |
353 | 21 | } |
354 | | |
355 | 6 | pub fn eth_get_block_by_number( |
356 | 6 | block_number: U64, |
357 | 6 | rpc_url: &str, |
358 | 6 | ) -> OffchainResult<EthBlock, RpcError> { |
359 | 6 | let rpc_req = JsonRpcRequest::new( |
360 | 6 | "eth_getBlockByNumber", |
361 | 6 | [serde_json::Value::String(format_as_hex(block_number)), serde_json::Value::Bool(false)], |
362 | 6 | ); |
363 | 6 | rpc_req.send(rpc_url) |
364 | 6 | } |
365 | | |
366 | 3 | pub fn eth_chain_id(rpc_url: &str) -> OffchainResult<U64, RpcError> { |
367 | 3 | let rpc_req = JsonRpcRequest::new("eth_chainId", None); |
368 | 3 | rpc_req.send(rpc_url) |
369 | 3 | } |
370 | | |
371 | | #[cfg(test)] |
372 | | mod tests { |
373 | 1 | #[test] |
374 | 1 | fn to_json_hex_works() { |
375 | 1 | assert_eq!(super::to_json_hex(&[0x01, 0x02, 0x03]), "0x010203"); |
376 | 1 | assert_eq!(super::to_json_hex(&[0x01, 0x00, 0x03]), "0x010003"); |
377 | 1 | assert_eq!(super::to_json_hex(&[]), ""); |
378 | 1 | } |
379 | | |
380 | 1 | #[test] |
381 | 1 | fn format_as_hex_works() { |
382 | 1 | assert_eq!(super::format_as_hex(0x123456789abcdefu64), "0x123456789abcdef"); |
383 | 1 | } |
384 | | } |