cashweb_bitcoin_client/
lib.rs1#![warn(
2 missing_debug_implementations,
3 missing_docs,
4 rust_2018_idioms,
5 unreachable_pub
6)]
7
8use hex::FromHexError;
12use hyper::{
13 client::HttpConnector, Body, Client as HyperClient, Error as HyperError,
14 Request as HttpRequest, Response as HttpResponse,
15};
16use hyper_tls::HttpsConnector;
17use json_rpc::{
18 clients::{
19 http::{Client as JsonClient, ConnectionError},
20 Error as RpcCallError,
21 },
22 prelude::{JsonError, RequestFactory, RpcError},
23};
24use serde_json::Value;
25use thiserror::Error;
26use tower_service::Service;
27
28pub type HttpClient = HyperClient<HttpConnector>;
30
31pub type HttpsClient = HyperClient<HttpsConnector<HttpConnector>>;
33
34pub type HttpError = NodeError<HyperError>;
36
37#[derive(Clone, Debug)]
39pub struct BitcoinClient<S>(JsonClient<S>);
40
41impl<S> BitcoinClient<S> {
42 pub fn from_service(service: S, endpoint: String, username: String, password: String) -> Self {
44 BitcoinClient(JsonClient::from_service(
45 service,
46 endpoint,
47 Some(username),
48 Some(password),
49 ))
50 }
51}
52
53impl BitcoinClient<HyperClient<HttpConnector>> {
54 pub fn new(endpoint: String, username: String, password: String) -> Self {
56 BitcoinClient(JsonClient::new(endpoint, Some(username), Some(password)))
57 }
58}
59
60impl BitcoinClient<HyperClient<HttpsConnector<HttpConnector>>> {
61 pub fn new_tls(endpoint: String, username: String, password: String) -> Self {
63 BitcoinClient(JsonClient::new_tls(
64 endpoint,
65 Some(username),
66 Some(password),
67 ))
68 }
69}
70
71impl<C> std::ops::Deref for BitcoinClient<C> {
72 type Target = JsonClient<C>;
73
74 fn deref(&self) -> &Self::Target {
75 &self.0
76 }
77}
78
79#[derive(Debug, Error)]
81pub enum NodeError<E: std::fmt::Debug + std::fmt::Display + 'static> {
82 #[error(transparent)]
84 Http(RpcCallError<ConnectionError<E>>),
85 #[error("{0:?}")]
87 Rpc(RpcError),
88 #[error(transparent)]
90 Json(JsonError),
91 #[error("empty response")]
93 EmptyResponse,
94 #[error(transparent)]
96 HexDecode(#[from] FromHexError),
97}
98
99impl<S> BitcoinClient<S>
100where
101 S: Service<HttpRequest<Body>, Response = HttpResponse<Body>> + Clone,
102 S::Error: std::fmt::Debug + std::fmt::Display + 'static,
103 S::Future: Send + 'static,
104{
105 pub async fn get_new_addr(&self) -> Result<String, NodeError<S::Error>> {
107 let request = self
108 .build_request()
109 .method("getnewaddress")
110 .finish()
111 .unwrap();
112 let response = self.send(request).await.map_err(NodeError::Http)?;
113 if response.is_error() {
114 return Err(NodeError::Rpc(response.error().unwrap()));
115 }
116 response
117 .into_result()
118 .ok_or(NodeError::EmptyResponse)?
119 .map_err(NodeError::Json)
120 }
121
122 pub async fn send_tx(&self, raw_tx: &[u8]) -> Result<String, NodeError<S::Error>> {
124 let request = self
125 .build_request()
126 .method("sendrawtransaction")
127 .params(vec![Value::String(hex::encode(raw_tx))])
128 .finish()
129 .unwrap();
130 let response = self.send(request).await.map_err(NodeError::Http)?;
131 if response.is_error() {
132 let err = response.error().unwrap();
133 return Err(NodeError::Rpc(err));
134 }
135 response
136 .into_result()
137 .ok_or(NodeError::EmptyResponse)?
138 .map_err(NodeError::Json)
139 }
140
141 pub async fn get_raw_transaction(&self, tx_id: &[u8]) -> Result<Vec<u8>, NodeError<S::Error>> {
143 let request = self
144 .build_request()
145 .method("getrawtransaction")
146 .params(vec![Value::String(hex::encode(tx_id))])
147 .finish()
148 .unwrap();
149 let response = self.send(request).await.map_err(NodeError::Http)?;
150 if response.is_error() {
151 return Err(NodeError::Rpc(response.error().unwrap()));
152 }
153 let tx_hex: String = response
154 .into_result()
155 .ok_or(NodeError::EmptyResponse)?
156 .map_err(NodeError::Json)?;
157 hex::decode(tx_hex).map_err(Into::into)
158 }
159}