tendermint_light_node/
rpc.rs

1//! JSON-RPC Server and Client for the light-node RPC endpoint.
2use 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
11/// Run the given [`Server`] on the given address and blocks until closed.
12///
13/// n.b. The underlying server has semantics to close on drop. Also it does not offer any way
14/// to get the underlying Future to await, so we are left with this rather rudimentary way to
15/// control the lifecycle. Should we be interested in a more controlled way to close the server we
16/// can expose a handle in the future.
17pub 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        /// Returns the latest trusted block.
49        #[rpc(name = "state")]
50        fn state(&self) -> BoxFuture<Result<Option<LightBlock>>>;
51
52        /// Returns the latest status.
53        #[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}