1use jsonrpc_core::IoHandler;
3use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
4
5use tendermint_light_client::supervisor::Handle;
6
7use crate::error;
8
9pub use sealed::{Client, Rpc, Server};
10
11pub fn run<H>(server: Server<H>, addr: &str) -> Result<(), error::Error>
18where
19 H: Handle + Send + Sync + 'static,
20{
21 let mut io = IoHandler::new();
22 io.extend_with(server.to_delegate());
23
24 let srv = ServerBuilder::new(io)
25 .cors(DomainsValidation::AllowOnly(vec![
26 AccessControlAllowOrigin::Any,
27 ]))
28 .start_http(&addr.parse().map_err(error::Kind::from)?)
29 .map_err(|e| error::Kind::Io.context(e))?;
30
31 srv.wait();
32
33 Ok(())
34}
35
36mod sealed {
37 use jsonrpc_core::futures;
38 use jsonrpc_core::types::Error;
39 use jsonrpc_core::{BoxFuture, Result};
40 use jsonrpc_derive::rpc;
41
42 use tendermint_light_client::supervisor::Handle;
43 use tendermint_light_client::types::LatestStatus;
44 use tendermint_light_client::types::LightBlock;
45
46 #[rpc]
47 pub trait Rpc {
48 #[rpc(name = "state")]
50 fn state(&self) -> BoxFuture<Result<Option<LightBlock>>>;
51
52 #[rpc(name = "status")]
54 fn status(&self) -> BoxFuture<Result<LatestStatus>>;
55 }
56
57 pub use self::rpc_impl_Rpc::gen_client::Client;
58
59 pub struct Server<H>
60 where
61 H: Handle + Send + Sync,
62 {
63 handle: H,
64 }
65
66 impl<H> Server<H>
67 where
68 H: Handle + Send + Sync,
69 {
70 pub fn new(handle: H) -> Self {
71 Self { handle }
72 }
73 }
74
75 impl<H> Rpc for Server<H>
76 where
77 H: Handle + Send + Sync + 'static,
78 {
79 fn state(&self) -> BoxFuture<Result<Option<LightBlock>>> {
80 let res = self.handle.latest_trusted().map_err(|e| {
81 let mut err = Error::internal_error();
82 err.message = e.to_string();
83 err.data = serde_json::to_value(e.kind()).ok();
84 err
85 });
86
87 Box::pin(futures::future::ready(res))
88 }
89
90 fn status(&self) -> BoxFuture<Result<LatestStatus>> {
91 let res = self.handle.latest_status().map_err(|e| {
92 let mut err = Error::internal_error();
93 err.message = e.to_string();
94 err.data = serde_json::to_value(e.kind()).ok();
95 err
96 });
97
98 Box::pin(futures::future::ready(res))
99 }
100 }
101}
102
103#[cfg(test)]
104mod test {
105 use jsonrpc_core::IoHandler;
106 use jsonrpc_core_client::transports::local;
107 use pretty_assertions::assert_eq;
108
109 use tendermint_light_client::errors::Error;
110 use tendermint_light_client::supervisor::Handle;
111 use tendermint_light_client::types::LatestStatus;
112 use tendermint_light_client::types::LightBlock;
113
114 use super::{Client, Rpc as _, Server};
115
116 #[tokio::test]
117 async fn state() {
118 let server = Server::new(MockHandle {});
119 let have = {
120 let mut io = IoHandler::new();
121 io.extend_with(server.to_delegate());
122 let (client, server) = local::connect::<Client, _, _>(io);
123 tokio::select! {
124 result = client.state() => result.unwrap(),
125 _ = server => panic!("server terminated before client state request completed"),
126 }
127 };
128 let want = serde_json::from_str(LIGHTBLOCK_JSON).unwrap();
129
130 assert_eq!(have, want);
131 }
132
133 #[tokio::test]
134 async fn status() {
135 let server = Server::new(MockHandle {});
136 let have = {
137 let mut io = IoHandler::new();
138 io.extend_with(server.to_delegate());
139 let (client, server) = local::connect::<Client, _, _>(io);
140 tokio::select! {
141 result = client.status() => result.unwrap(),
142 _ = server => panic!("server terminated before client status request completed"),
143 }
144 };
145 let want = serde_json::from_str(STATUS_JSON).unwrap();
146
147 assert_eq!(have, want);
148 }
149
150 struct MockHandle;
151
152 impl Handle for MockHandle {
153 fn latest_trusted(&self) -> Result<Option<LightBlock>, Error> {
154 let block: LightBlock = serde_json::from_str(LIGHTBLOCK_JSON).unwrap();
155
156 Ok(Some(block))
157 }
158
159 fn latest_status(&self) -> Result<LatestStatus, Error> {
160 let status: LatestStatus = serde_json::from_str(STATUS_JSON).unwrap();
161
162 Ok(status)
163 }
164
165 fn verify_to_highest(&self) -> Result<LightBlock, Error> {
166 todo!()
167 }
168
169 fn verify_to_target(
170 &self,
171 _height: tendermint::block::Height,
172 ) -> Result<LightBlock, Error> {
173 todo!()
174 }
175
176 fn terminate(&self) -> Result<(), Error> {
177 todo!()
178 }
179 }
180
181 const LIGHTBLOCK_JSON: &str = r#"
182{
183 "signed_header": {
184 "header": {
185 "version": {
186 "block": "0",
187 "app": "0"
188 },
189 "chain_id": "test-chain-01",
190 "height": "1",
191 "time": "2019-11-02T15:04:00Z",
192 "last_block_id": {
193 "hash": "",
194 "part_set_header": {
195 "total": 0,
196 "hash": ""
197 }
198 },
199 "last_commit_hash": "",
200 "data_hash": "",
201 "validators_hash": "ADAE23D9D908638F3866C11A39E31CE4399AE6DE8EC8EBBCB1916B90C46EDDE3",
202 "next_validators_hash": "ADAE23D9D908638F3866C11A39E31CE4399AE6DE8EC8EBBCB1916B90C46EDDE3",
203 "consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
204 "app_hash": "6170705F68617368",
205 "last_results_hash": "",
206 "evidence_hash": "",
207 "proposer_address": "01F527D77D3FFCC4FCFF2DDC2952EEA5414F2A33"
208 },
209 "commit": {
210 "height": "1",
211 "round": 1,
212 "block_id": {
213 "hash": "76B0FB738138A2C934300D7B23C280B65965D7427DA4D5414B41C75EBC4AD4C3",
214 "part_set_header": {
215 "total": 1,
216 "hash": "073CE26981DF93820595E602CE63B810BC8F1003D6BB28DEDFF5B2F4F09811A1"
217 }
218 },
219 "signatures": [
220 {
221 "block_id_flag": 2,
222 "validator_address": "01F527D77D3FFCC4FCFF2DDC2952EEA5414F2A33",
223 "timestamp": "2019-11-02T15:04:10Z",
224 "signature": "NaNXQhv7SgBtcq+iHwItxlYUMGHP5MeFpTbyNsnLtzwM6P/EAAAexUH94+osvRDoiahUOoQrRlTiZrYGfahWBw=="
225 },
226 {
227 "block_id_flag": 2,
228 "validator_address": "026CC7B6F3E62F789DBECEC59766888B5464737D",
229 "timestamp": "2019-11-02T15:04:10Z",
230 "signature": "tw0csJ1L1vkBG/71BMjrFEcA6VWjOx29WMwkg1cmDn82XBjRFz+HJu7amGoIj6WLL2p26pO25yQR49crsYQ+AA=="
231 }
232 ]
233 }
234 },
235 "validator_set": {
236 "validators": [
237 {
238 "address": "01F527D77D3FFCC4FCFF2DDC2952EEA5414F2A33",
239 "pub_key": {
240 "type": "tendermint/PubKeyEd25519",
241 "value": "OAaNq3DX/15fGJP2MI6bujt1GRpvjwrqIevChirJsbc="
242 },
243 "voting_power": "50",
244 "proposer_priority": "-50"
245 },
246 {
247 "address": "026CC7B6F3E62F789DBECEC59766888B5464737D",
248 "pub_key": {
249 "type": "tendermint/PubKeyEd25519",
250 "value": "+vlsKpn6ojn+UoTZl+w+fxeqm6xvUfBokTcKfcG3au4="
251 },
252 "voting_power": "50",
253 "proposer_priority": "50"
254 }
255 ],
256 "proposer": {
257 "address": "01F527D77D3FFCC4FCFF2DDC2952EEA5414F2A33",
258 "pub_key": {
259 "type": "tendermint/PubKeyEd25519",
260 "value": "OAaNq3DX/15fGJP2MI6bujt1GRpvjwrqIevChirJsbc="
261 },
262 "voting_power": "50",
263 "proposer_priority": "-50"
264 },
265 "total_voting_power": "0"
266 },
267 "next_validator_set": {
268 "validators": [
269 {
270 "address": "01F527D77D3FFCC4FCFF2DDC2952EEA5414F2A33",
271 "pub_key": {
272 "type": "tendermint/PubKeyEd25519",
273 "value": "OAaNq3DX/15fGJP2MI6bujt1GRpvjwrqIevChirJsbc="
274 },
275 "voting_power": "50",
276 "proposer_priority": "0"
277 },
278 {
279 "address": "026CC7B6F3E62F789DBECEC59766888B5464737D",
280 "pub_key": {
281 "type": "tendermint/PubKeyEd25519",
282 "value": "+vlsKpn6ojn+UoTZl+w+fxeqm6xvUfBokTcKfcG3au4="
283 },
284 "voting_power": "50",
285 "proposer_priority": "0"
286 }
287 ],
288 "proposer": {
289 "address": "026CC7B6F3E62F789DBECEC59766888B5464737D",
290 "pub_key": {
291 "type": "tendermint/PubKeyEd25519",
292 "value": "+vlsKpn6ojn+UoTZl+w+fxeqm6xvUfBokTcKfcG3au4="
293 },
294 "voting_power": "50",
295 "proposer_priority": "0"
296 },
297 "total_voting_power": "0"
298 },
299 "provider": "9D61B19DEFFD5A60BA844AF492EC2CC44449C569"
300}
301"#;
302 const STATUS_JSON: &str = r#"
303{
304 "block_hash": "5A55D7AF2DF9AE4BF4B46FDABBBAD1B66D37B5E044A4843AB0FB0EBEC3E0422C",
305 "connected_nodes": [
306 "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE",
307 "CEFEEDBADFADAD0C0CEEFACADE0ADEADBEEFC0FF"
308 ],
309 "height": 1565,
310 "valset_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3"
311}"#;
312}