1#![allow(dead_code)]
2
3use ckb_jsonrpc_types::{
4 BlockNumber, BlockView, CellWithStatus, HeaderView, JsonBytes, OutPoint, TransactionView, TxStatus, Uint32
5};
6use ckb_types::H256;
7use reqwest::{Client, Url};
8use serde::{Deserialize, Serialize};
9use async_trait::async_trait;
10
11use std::{
12 future::Future,
13 io,
14 sync::{
15 atomic::{AtomicU64, Ordering},
16 Arc,
17 },
18};
19
20use crate::{types::{Cell, CellsCapacity, IndexerTip, Order, Pagination, SearchKey, Tx},
21 Rpc,
22};
23
24macro_rules! jsonrpc {
25 ($method:expr, $self:ident, $return:ty$(, $params:ident$(,)?)*) => {{
26 let old = $self.id.fetch_add(1, Ordering::AcqRel);
27 let data = format!(
28 r#"{{"id": {}, "jsonrpc": "2.0", "method": "{}", "params": {}}}"#,
29 old,
30 $method,
31 serde_json::to_value(($($params,)*)).unwrap()
32 );
33
34 let req_json: serde_json::Value = serde_json::from_str(&data).unwrap();
35
36 let c = $self.raw.post($self.ckb_uri.clone()).json(&req_json);
37 async {
38 let resp = c
39 .send()
40 .await
41 .map_err::<io::Error, _>(|e| io::Error::new(io::ErrorKind::ConnectionAborted, format!("{:?}", e)))?;
42 let output = resp
43 .json::<jsonrpc_core::response::Output>()
44 .await
45 .map_err::<io::Error, _>(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{:?}", e)))?;
46
47 match output {
48 jsonrpc_core::response::Output::Success(success) => {
49 Ok(serde_json::from_value::<$return>(success.result).unwrap())
50 }
51 jsonrpc_core::response::Output::Failure(e) => {
52 Err(io::Error::new(io::ErrorKind::InvalidData, format!("{:?}", e)))
53 }
54 }
55 }
56 }}
57}
58
59#[derive(Clone)]
61pub struct RpcClient {
62 raw: Client,
63 ckb_uri: Url,
64 id: Arc<AtomicU64>,
65}
66
67impl RpcClient {
68 pub fn new(ckb_uri: &str) -> Self {
69 let ckb_uri = Url::parse(ckb_uri).expect("ckb uri, e.g. \"http://127.0.0.1:8114\"");
70
71 RpcClient {
72 raw: Client::new(),
73 ckb_uri,
74 id: Arc::new(AtomicU64::new(0)),
75 }
76 }
77
78 pub fn new_with_client(ckb_uri: &str, raw: Client) -> Self {
79 let ckb_uri = Url::parse(ckb_uri).expect("ckb uri, e.g. \"http://127.0.0.1:8114\"");
80
81 RpcClient {
82 raw,
83 ckb_uri,
84 id: Arc::new(AtomicU64::new(0)),
85 }
86 }
87
88 pub fn get_transaction(
89 &self,
90 hash: &H256,
91 ) -> impl Future<Output = Result<Option<TransactionView>, io::Error>> {
92 #[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
93 struct TransactionWithStatusResponse {
94 pub transaction: Option<TransactionView>,
96 pub tx_status: TxStatus,
98 }
99 let task = jsonrpc!("get_transaction", self, TransactionWithStatusResponse, hash);
100 async {
101 let res = task.await?;
102 Ok(res.transaction)
103 }
104 }
105
106 pub fn get_header_by_number(
107 &self,
108 number: BlockNumber,
109 ) -> impl Future<Output = Result<HeaderView, io::Error>> {
110 jsonrpc!("get_header_by_number", self, HeaderView, number)
111 }
112
113 pub fn get_block_by_number(
114 &self,
115 number: BlockNumber,
116 ) -> impl Future<Output = Result<BlockView, io::Error>> {
117 jsonrpc!("get_block_by_number", self, BlockView, number)
118 }
119
120 pub fn get_header(
121 &self,
122 hash: H256,
123 ) -> impl Future<Output = Result<Option<HeaderView>, io::Error>> {
124 jsonrpc!("get_header", self, Option<HeaderView>, hash)
125 }
126
127 pub fn get_indexer_tip(&self) -> impl Future<Output = Result<IndexerTip, io::Error>> {
128 jsonrpc!("get_indexer_tip", self, IndexerTip)
129 }
130
131 pub fn get_transactions(
132 &self,
133 search_key: SearchKey,
134 order: Order,
135 limit: Uint32,
136 after: Option<JsonBytes>,
137 ) -> impl Future<Output = Result<Pagination<Tx>, io::Error>> {
138 jsonrpc!(
139 "get_transactions",
140 self,
141 Pagination<Tx>,
142 search_key,
143 order,
144 limit,
145 after
146 )
147 }
148
149 pub fn get_cells(
150 &self,
151 search_key: SearchKey,
152 order: Order,
153 limit: Uint32,
154 after: Option<JsonBytes>,
155 ) -> impl Future<Output = Result<Pagination<Cell>, io::Error>> {
156 jsonrpc!(
157 "get_cells",
158 self,
159 Pagination<Cell>,
160 search_key,
161 order,
162 limit,
163 after
164 )
165 }
166
167 pub fn get_live_cell(
168 &self,
169 out_point: OutPoint,
170 with_data: bool,
171 ) -> impl Future<Output = Result<CellWithStatus, io::Error>> {
172 jsonrpc!(
173 "get_live_cell",
174 self,
175 CellWithStatus,
176 out_point,
177 with_data,
178 )
179 }
180
181 pub fn get_cells_capacity(
182 &self,
183 search_key: SearchKey,
184 ) -> impl Future<Output = Result<Option<CellsCapacity>, io::Error>> {
185 jsonrpc!(
186 "get_cells_capacity",
187 self,
188 Option<CellsCapacity>,
189 search_key,
190 )
191 }
192}
193
194#[async_trait]
195impl Rpc for RpcClient {
196 async fn get_transactions(
197 &self,
198 search_key: SearchKey,
199 order: Order,
200 limit: Uint32,
201 after: Option<JsonBytes>,
202 ) -> Result<Pagination<Tx>, io::Error> {
203 self.get_transactions(search_key, order, limit, after).await
204 }
205
206 async fn get_transaction(&self, hash: &H256) -> Result<Option<TransactionView>, io::Error> {
207 self.get_transaction(hash).await
208 }
209
210 async fn get_header_by_number(&self, number: BlockNumber) -> Result<HeaderView, io::Error> {
211 self.get_header_by_number(number).await
212 }
213
214 async fn get_indexer_tip(&self) -> Result<IndexerTip, io::Error> {
215 self.get_indexer_tip().await
216 }
217
218 async fn get_block_by_number(&self, number: BlockNumber) -> Result<BlockView, std::io::Error> {
219 self.get_block_by_number(number).await
220 }
221}