ckb_client/
rpc_client.rs

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// Default implementation of ckb Rpc client
60#[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            /// The transaction.
95            pub transaction: Option<TransactionView>,
96            /// The Transaction status.
97            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}