-
Notifications
You must be signed in to change notification settings - Fork 121
/
Copy pathrpc_client.rs
152 lines (134 loc) · 4.62 KB
/
rpc_client.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use js_sys::JSON::stringify;
use std::fmt::Debug;
use std::fmt::Display;
use thiserror::Error;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::Response;
use namada::ledger::queries::{Client, EncodedResponseQuery};
use namada::tendermint::abci::Code;
use namada::tendermint_rpc::{Response as RpcResponse, SimpleRequest};
use namada::types::storage::BlockHeight;
use namada::{tendermint, tendermint_rpc::error::Error as TendermintRpcError};
#[wasm_bindgen(module = "/src/rpc_client.js")]
extern "C" {
#[wasm_bindgen(catch, js_name = "wasmFetch")]
async fn wasmFetch(url: JsValue, method: JsValue, body: JsValue) -> Result<JsValue, JsValue>;
}
#[derive(Clone, Error, Debug)]
pub struct RpcError(String);
impl RpcError {
pub fn new(msg: &str) -> Self {
RpcError(String::from(msg))
}
}
impl Display for RpcError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl From<std::io::Error> for RpcError {
fn from(error: std::io::Error) -> Self {
RpcError::new(&error.to_string())
}
}
impl From<namada::tendermint::Error> for RpcError {
fn from(error: namada::tendermint::Error) -> Self {
RpcError::new(&error.to_string())
}
}
impl From<namada::tendermint_rpc::Error> for RpcError {
fn from(error: namada::tendermint_rpc::Error) -> Self {
RpcError::new(&error.to_string())
}
}
pub struct HttpClient {
url: String,
}
/// HttpClient implementation using `window.fetch` API.
impl HttpClient {
pub fn new(url: String) -> HttpClient {
HttpClient { url }
}
async fn fetch(&self, url: &str, method: &str, body: &str) -> Result<JsValue, JsValue> {
let resp_value = wasmFetch(
JsValue::from_str(url),
JsValue::from_str(method),
JsValue::from_str(body),
)
.await?;
let resp: Response = resp_value.dyn_into()?;
JsFuture::from(resp.json().unwrap()).await
}
}
#[async_trait::async_trait(?Send)]
impl Client for HttpClient {
/// Implementation of the `Client` trait for the `HttpClient` struct.
/// It's used by the Sdk to perform queries to the blockchain.
type Error = RpcError;
/// Wrapper for tendermint specific abci_query.
///
/// # Arguments
///
/// * `path` - path of the resource in the storage.
/// * `data` - query params in the form of bytearray.
/// * `height` - height of the specific blockchain block that we are quering information from.
/// 0 means the latest block.
/// * `prove` - include proofs of the transactions inclusion in the block
async fn request(
&self,
path: String,
data: Option<Vec<u8>>,
height: Option<BlockHeight>,
prove: bool,
) -> Result<EncodedResponseQuery, Self::Error> {
let data = data.unwrap_or_default();
let height = height
.map(|height| tendermint::block::Height::try_from(height.0))
.transpose()?;
let response = self
.abci_query(
Some(std::str::FromStr::from_str(&path).unwrap()),
data,
height,
prove,
)
.await?;
let response = response.clone();
let code = Code::from(response.code);
match code {
Code::Ok => Ok(EncodedResponseQuery {
data: response.value,
info: response.info,
proof: response.proof,
}),
Code::Err(code) => Err(RpcError::new(&format!("Error code {}", code))),
}
}
/// Performas request using fetch API. Maps returned JS object to the `RpcResponse` struct.
///
/// # Arguments
///
/// * `request` - request type to be performed. Check `Client` trait for avaialble requests.
async fn perform<R>(&self, request: R) -> Result<R::Response, TendermintRpcError>
where
R: SimpleRequest,
{
let request_body = request.into_json();
let response = self
.fetch(&self.url[..], "POST", &request_body)
.await
.map_err(|e| {
let e = stringify(&e).expect("Error to be serializable");
let e_str: String = e.into();
// There is no "generic" RpcError, so we have to pick
// one with error msg as an argument.
TendermintRpcError::server(e_str)
})?;
let response_json: String = stringify(&response)
.expect("JS object to be serializable")
.into();
R::Response::from_string(&response_json)
}
}