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