1use base64::Engine;
4use hex::decode;
5use http_body_util::{BodyExt, Full};
6use hyper::{
7 body::Bytes,
8 header::{AUTHORIZATION, CONTENT_TYPE},
9 Request,
10};
11use hyper_util::{
12 client::legacy::{connect::HttpConnector, Client},
13 rt::TokioExecutor,
14};
15use serde::{Deserialize, Serialize};
16use serde_json::json;
17use stratum_core::bitcoin::{consensus::encode::deserialize as consensus_decode, Transaction};
18
19use super::BlockHash;
20
21#[derive(Clone, Debug)]
22pub struct MiniRpcClient {
23 client: Client<HttpConnector, Full<Bytes>>,
24 url: hyper::Uri,
25 auth: Auth,
26}
27
28impl MiniRpcClient {
29 pub fn new(url: hyper::Uri, auth: Auth) -> MiniRpcClient {
30 let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
31 MiniRpcClient { client, url, auth }
32 }
33
34 pub async fn get_raw_transaction(
35 &self,
36 txid: &String,
37 block_hash: Option<&BlockHash>,
38 ) -> Result<Transaction, RpcError> {
39 let response = match block_hash {
40 Some(hash) => {
41 self.send_json_rpc_request("getrawtransaction", json!([txid, false, hash]))
42 }
43 None => self.send_json_rpc_request("getrawtransaction", json!([txid, false])),
44 }
45 .await;
46 match response {
47 Ok(result_hex) => {
48 let result_deserialized: JsonRpcResult<String> = serde_json::from_str(&result_hex)
49 .map_err(|e| {
50 RpcError::Deserialization(e.to_string()) })?;
52 let transaction_hex: String = result_deserialized
53 .result
54 .ok_or_else(|| RpcError::Other("Result not found".to_string()))?;
55 let transaction_bytes = decode(transaction_hex).expect("Decoding failed");
56 Ok(consensus_decode(&transaction_bytes).expect("Deserialization failed"))
57 }
58 Err(error) => Err(error),
59 }
60 }
61
62 pub async fn get_raw_mempool(&self) -> Result<Vec<String>, RpcError> {
63 let response = self.send_json_rpc_request("getrawmempool", json!([])).await;
64 match response {
65 Ok(result_hex) => {
66 let result_deserialized: JsonRpcResult<Vec<String>> =
67 serde_json::from_str(&result_hex).map_err(|e| {
68 RpcError::Deserialization(e.to_string()) })?;
70 let mempool: Vec<String> = result_deserialized
71 .result
72 .ok_or_else(|| RpcError::Other("Result not found".to_string()))?;
73 Ok(mempool)
74 }
75 Err(error) => Err(error),
76 }
77 }
78
79 pub async fn submit_block(&self, block_hex: String) -> Result<(), RpcError> {
80 let response = self
81 .send_json_rpc_request("submitblock", json!([block_hex]))
82 .await;
83
84 match response {
85 Ok(_) => Ok(()),
86 Err(error) => Err(error),
87 }
88 }
89
90 pub async fn health(&self) -> Result<(), RpcError> {
93 let response = self
94 .send_json_rpc_request("getblockchaininfo", json!([]))
95 .await;
96 match response {
97 Ok(_) => Ok(()),
98 Err(error) => Err(error),
99 }
100 }
101
102 async fn send_json_rpc_request(
103 &self,
104 method: &str,
105 params: serde_json::Value,
106 ) -> Result<String, RpcError> {
107 let client = &self.client;
108 let (username, password) = self.auth.clone().get_user_pass();
109 let request = JsonRpcRequest {
110 jsonrpc: "2.0".to_string(),
111 method: method.to_string(),
112 params,
113 id: 1, };
115
116 let request_body = match serde_json::to_string(&request) {
117 Ok(body) => body,
118 Err(e) => return Err(RpcError::Serialization(e.to_string())),
119 };
120
121 let req = Request::builder()
122 .method("POST")
123 .uri(self.url.clone())
124 .header(CONTENT_TYPE, "application/json")
125 .header(
126 AUTHORIZATION,
127 format!(
128 "Basic {}",
129 base64::engine::general_purpose::STANDARD
130 .encode(format!("{username}:{password}"))
131 ),
132 )
133 .body(Full::<Bytes>::from(request_body))
134 .map_err(|e| RpcError::Http(e.to_string()))?;
135
136 let response = client
137 .request(req)
138 .await
139 .map_err(|e| RpcError::Http(e.to_string()))?;
140
141 let status = response.status();
142 let body = response
143 .into_body()
144 .collect()
145 .await
146 .map_err(|e| RpcError::Http(e.to_string()))?
147 .to_bytes()
148 .to_vec();
149
150 if status.is_success() {
151 String::from_utf8(body).map_err(|e| {
152 RpcError::Deserialization(e.to_string()) })
154 } else {
155 let error_result: Result<JsonRpcResult<_>, _> = serde_json::from_slice(&body);
156 match error_result {
157 Ok(error_response) => Err(error_response.into()),
158 Err(e) => Err(RpcError::Deserialization(e.to_string())),
159 }
160 }
161 }
162}
163
164#[derive(Clone, Debug)]
165pub struct Auth {
166 username: String,
167 password: String,
168}
169
170impl Auth {
171 pub fn get_user_pass(self) -> (String, String) {
172 (self.username, self.password)
173 }
174 pub fn new(username: String, password: String) -> Auth {
175 Auth { username, password }
176 }
177}
178
179#[derive(Debug, Serialize)]
180struct JsonRpcRequest {
181 jsonrpc: String,
182 method: String,
183 params: serde_json::Value,
184 id: u64,
185}
186
187#[derive(Debug, Deserialize)]
188pub struct JsonRpcResult<T> {
189 result: Option<T>,
190 pub error: Option<JsonRpcError>,
191 pub id: u64,
192}
193
194#[derive(Debug, Deserialize, Clone)]
195pub struct JsonRpcError {
196 pub code: i32,
197 pub message: String,
198}
199
200#[derive(Debug, Deserialize)]
201pub enum RpcError {
202 JsonRpc(JsonRpcResult<JsonRpcError>),
205 Deserialization(String),
206 Serialization(String),
207 Http(String),
208 Other(String),
209}
210
211impl From<JsonRpcResult<JsonRpcError>> for RpcError {
212 fn from(error: JsonRpcResult<JsonRpcError>) -> Self {
213 Self::JsonRpc(error)
214 }
215}