blockchain_client/providers/
jsonrpc.rs1use async_trait::async_trait;
2use reqwest::Client;
3use serde_json::json;
4use std::sync::Arc;
5
6use crate::client::{JsonRpcRequest, JsonRpcResponse, RpcConfig};
7use crate::error::{Result, RpcError};
8use crate::traits::BlockchainClient;
9use crate::types::*;
10
11#[derive(Clone)]
12pub struct JsonRpcClient {
13 config: Arc<RpcConfig>,
14 http_client: Client,
15 chain: Chain,
16 network: Network,
17}
18
19impl JsonRpcClient {
20 pub fn new(config: RpcConfig, chain: Chain, network: Network) -> Result<Self> {
21 let http_client = Client::builder()
22 .timeout(config.timeout())
23 .pool_max_idle_per_host(config.pool_max_idle_per_host)
24 .pool_idle_timeout(config.pool_idle_timeout())
25 .tcp_keepalive(config.tcp_keepalive())
26 .build()?;
27
28 Ok(Self {
29 config: Arc::new(config),
30 http_client,
31 chain,
32 network,
33 })
34 }
35
36 pub fn chain(&self) -> Chain {
37 self.chain
38 }
39
40 pub fn network(&self) -> Network {
41 self.network
42 }
43
44 async fn call<T: serde::de::DeserializeOwned>(
45 &self,
46 method: &str,
47 params: serde_json::Value,
48 ) -> Result<T> {
49 let request = JsonRpcRequest::new(method.to_string(), params);
50
51 let response = self
52 .http_client
53 .post(&self.config.url)
54 .basic_auth(&self.config.username, Some(&self.config.password))
55 .json(&request)
56 .send()
57 .await?;
58
59 let status = response.status();
60
61 if !status.is_success() {
62 let body = response.text().await.unwrap_or_default();
63 return Err(RpcError::RequestFailed(format!(
64 "http {}: {}",
65 status, body
66 )));
67 }
68
69 let text = response.text().await?;
70 let rpc_response: JsonRpcResponse<T> = serde_json::from_str(&text)?;
71
72 if let Some(error) = rpc_response.error {
73 return Err(RpcError::RpcResponseError {
74 code: error.code,
75 message: error.message,
76 });
77 }
78
79 rpc_response
80 .result
81 .ok_or_else(|| RpcError::InvalidResponse("missing result field".to_string()))
82 }
83}
84
85#[async_trait]
86impl BlockchainClient for JsonRpcClient {
87 async fn get_blockchain_info(&self) -> Result<BlockchainInfo> {
88 self.call("getblockchaininfo", json!([])).await
89 }
90
91 async fn get_raw_transaction_with_block(
92 &self,
93 txid: &str,
94 verbose: bool,
95 blockhash: Option<&str>,
96 ) -> Result<RawTransaction> {
97 let params = if let Some(hash) = blockhash {
98 json!([txid, if verbose { 1 } else { 0 }, hash])
99 } else {
100 json!([txid, if verbose { 1 } else { 0 }])
101 };
102
103 match self
104 .call::<RawTransaction>("getrawtransaction", params)
105 .await
106 {
107 Ok(tx) => Ok(tx),
108
109 Err(RpcError::RpcResponseError { code: -5, .. }) => {
110 let wallet_tx: serde_json::Value =
112 self.call("gettransaction", json!([txid])).await?;
113
114 if let Some(hex) = wallet_tx.get("hex").and_then(|h| h.as_str()) {
115 self.call("decoderawtransaction", json!([hex])).await
116 } else {
117 Err(RpcError::InvalidResponse(
118 "gettransaction response missing hex field".to_string(),
119 ))
120 }
121 }
122
123 Err(e) => Err(e),
124 }
125 }
126
127 async fn list_unspent(
128 &self,
129 min_conf: Option<u32>,
130 max_conf: Option<u32>,
131 addresses: Option<&[String]>,
132 ) -> Result<Vec<Utxo>> {
133 let params = json!([
134 min_conf.unwrap_or(1),
135 max_conf.unwrap_or(9_999_999),
136 addresses.unwrap_or(&[])
137 ]);
138
139 self.call("listunspent", params).await
140 }
141
142 async fn list_transactions(
143 &self,
144 label: Option<&str>,
145 count: Option<usize>,
146 skip: Option<usize>,
147 include_watchonly: bool,
148 ) -> Result<Vec<TransactionListItem>> {
149 let params = json!([
150 label.unwrap_or("*"),
151 count.unwrap_or(10),
152 skip.unwrap_or(0),
153 include_watchonly
154 ]);
155
156 self.call("listtransactions", params).await
157 }
158
159 async fn get_received_by_address(&self, address: &str, min_conf: Option<u32>) -> Result<f64> {
160 let params = json!([address, min_conf.unwrap_or(1)]);
161 self.call("getreceivedbyaddress", params).await
162 }
163
164 async fn list_received_by_address(
165 &self,
166 min_conf: Option<u32>,
167 include_empty: bool,
168 include_watchonly: bool,
169 ) -> Result<Vec<ReceivedByAddress>> {
170 let params = json!([min_conf.unwrap_or(1), include_empty, include_watchonly]);
171 self.call("listreceivedbyaddress", params).await
172 }
173
174 async fn is_address_watched(&self, address: &str) -> Result<bool> {
175 let validation = self.validate_address(address).await?;
176
177 if !validation.isvalid {
178 return Ok(false);
179 }
180
181 if validation.ismine == Some(true) || validation.iswatchonly == Some(true) {
182 return Ok(true);
183 }
184
185 let received = self.list_received_by_address(Some(0), true, true).await?;
186
187 Ok(received.iter().any(|r| r.address == address))
188 }
189
190 async fn import_address(&self, address: &str, label: Option<&str>, rescan: bool) -> Result<()> {
191 let params = json!([address, label.unwrap_or(""), rescan]);
192 self.call::<serde_json::Value>("importaddress", params)
193 .await?;
194 Ok(())
195 }
196
197 async fn validate_address(&self, address: &str) -> Result<AddressValidation> {
198 self.call("validateaddress", json!([address])).await
199 }
200
201 async fn get_transaction(&self, txid: &str) -> Result<serde_json::Value> {
202 self.call("gettransaction", json!([txid])).await
203 }
204
205 async fn get_block_count(&self) -> Result<u64> {
206 self.call("getblockcount", json!([])).await
207 }
208
209 async fn get_best_block_hash(&self) -> Result<String> {
210 self.call("getbestblockhash", json!([])).await
211 }
212}