1use std::convert::TryFrom;
6use std::fmt::{self, Display, Formatter};
7use std::ops::Deref;
8use std::sync::Arc;
9
10use bitcoin::blockdata::block;
11use bitcoin::consensus::encode::deserialize;
12use bitcoin::hashes::{sha256, Hash};
13use bitcoin::hex::{DisplayHex, FromHex};
14use bitcoin::{Script, Txid};
15
16use serde::{de, Deserialize, Serialize};
17
18static JSONRPC_2_0: &str = "2.0";
19
20pub(crate) type Call = (String, Vec<Param>);
21
22#[derive(Serialize, Clone)]
23#[serde(untagged)]
24pub enum Param {
26 U32(u32),
28 Usize(usize),
30 String(String),
32 Bool(bool),
34 Bytes(Vec<u8>),
36}
37
38#[derive(Serialize, Clone)]
39pub struct Request<'a> {
41 jsonrpc: &'static str,
42
43 pub id: usize,
45 pub method: &'a str,
47 pub params: Vec<Param>,
49}
50
51impl<'a> Request<'a> {
52 fn new(method: &'a str, params: Vec<Param>) -> Self {
54 Self {
55 id: 0,
56 jsonrpc: JSONRPC_2_0,
57 method,
58 params,
59 }
60 }
61
62 pub fn new_id(id: usize, method: &'a str, params: Vec<Param>) -> Self {
64 let mut instance = Self::new(method, params);
65 instance.id = id;
66
67 instance
68 }
69}
70
71#[doc(hidden)]
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
73pub struct Hex32Bytes(#[serde(deserialize_with = "from_hex", serialize_with = "to_hex")] [u8; 32]);
74
75impl Deref for Hex32Bytes {
76 type Target = [u8; 32];
77
78 fn deref(&self) -> &Self::Target {
79 &self.0
80 }
81}
82
83impl From<[u8; 32]> for Hex32Bytes {
84 fn from(other: [u8; 32]) -> Hex32Bytes {
85 Hex32Bytes(other)
86 }
87}
88
89impl Hex32Bytes {
90 pub(crate) fn to_hex(self) -> String {
91 self.0.to_lower_hex_string()
92 }
93}
94
95pub type ScriptHash = Hex32Bytes;
98
99pub type ScriptStatus = Hex32Bytes;
102
103pub trait ToElectrumScriptHash {
105 fn to_electrum_scripthash(&self) -> ScriptHash;
107}
108
109impl ToElectrumScriptHash for Script {
110 fn to_electrum_scripthash(&self) -> ScriptHash {
111 let mut result = sha256::Hash::hash(self.as_bytes()).to_byte_array();
112 result.reverse();
113
114 result.into()
115 }
116}
117
118fn from_hex<'de, T, D>(deserializer: D) -> Result<T, D::Error>
119where
120 T: FromHex,
121 D: de::Deserializer<'de>,
122{
123 let s = String::deserialize(deserializer)?;
124 T::from_hex(&s).map_err(de::Error::custom)
125}
126
127fn to_hex<S>(bytes: &[u8], serializer: S) -> std::result::Result<S::Ok, S::Error>
128where
129 S: serde::ser::Serializer,
130{
131 serializer.serialize_str(&bytes.to_lower_hex_string())
132}
133
134fn from_hex_array<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
135where
136 T: FromHex + std::fmt::Debug,
137 D: de::Deserializer<'de>,
138{
139 let arr = Vec::<String>::deserialize(deserializer)?;
140
141 let results: Vec<Result<T, _>> = arr
142 .into_iter()
143 .map(|s| T::from_hex(&s).map_err(de::Error::custom))
144 .collect();
145
146 let mut answer = Vec::new();
147 for x in results.into_iter() {
148 answer.push(x?);
149 }
150
151 Ok(answer)
152}
153
154fn from_hex_header<'de, D>(deserializer: D) -> Result<block::Header, D::Error>
155where
156 D: de::Deserializer<'de>,
157{
158 let vec: Vec<u8> = from_hex(deserializer)?;
159 deserialize(&vec).map_err(de::Error::custom)
160}
161
162#[derive(Clone, Debug, Deserialize)]
164pub struct GetHistoryRes {
165 pub height: i32,
168 pub tx_hash: Txid,
170 pub fee: Option<u64>,
172}
173
174#[derive(Clone, Debug, Deserialize)]
176pub struct ListUnspentRes {
177 pub height: usize,
179 pub tx_hash: Txid,
181 pub tx_pos: usize,
183 pub value: u64,
185}
186
187#[derive(Clone, Debug, Deserialize)]
189pub struct ServerFeaturesRes {
190 pub server_version: String,
192 #[serde(deserialize_with = "from_hex")]
194 pub genesis_hash: [u8; 32],
195 pub protocol_min: String,
197 pub protocol_max: String,
199 pub hash_function: Option<String>,
201 pub pruning: Option<i64>,
203}
204
205#[derive(Clone, Debug, Deserialize)]
207pub struct GetHeadersRes {
208 pub max: usize,
210 pub count: usize,
212 #[serde(rename(deserialize = "hex"), deserialize_with = "from_hex")]
214 pub raw_headers: Vec<u8>,
215 #[serde(skip)]
217 pub headers: Vec<block::Header>,
218}
219
220#[derive(Clone, Debug, Deserialize)]
222pub struct GetBalanceRes {
223 pub confirmed: u64,
225 pub unconfirmed: i64,
229}
230
231#[derive(Clone, Debug, Deserialize)]
233pub struct GetMerkleRes {
234 pub block_height: usize,
236 pub pos: usize,
238 #[serde(deserialize_with = "from_hex_array")]
240 pub merkle: Vec<[u8; 32]>,
241}
242
243#[derive(Clone, Debug, Deserialize)]
246pub struct TxidFromPosRes {
247 pub tx_hash: Txid,
249 #[serde(deserialize_with = "from_hex_array")]
251 pub merkle: Vec<[u8; 32]>,
252}
253
254#[derive(Clone, Debug, Deserialize)]
256pub struct HeaderNotification {
257 pub height: usize,
259 #[serde(rename = "hex", deserialize_with = "from_hex_header")]
261 pub header: block::Header,
262}
263
264#[derive(Clone, Debug, Deserialize)]
266pub struct RawHeaderNotification {
267 pub height: usize,
269 #[serde(rename = "hex", deserialize_with = "from_hex")]
271 pub header: Vec<u8>,
272}
273
274impl TryFrom<RawHeaderNotification> for HeaderNotification {
275 type Error = Error;
276
277 fn try_from(raw: RawHeaderNotification) -> Result<Self, Self::Error> {
278 Ok(HeaderNotification {
279 height: raw.height,
280 header: deserialize(&raw.header)?,
281 })
282 }
283}
284
285#[derive(Clone, Debug, Deserialize)]
287pub struct ScriptNotification {
288 pub scripthash: ScriptHash,
290 pub status: ScriptStatus,
292}
293
294#[derive(Debug)]
296pub enum Error {
297 IOError(std::io::Error),
299 JSON(serde_json::error::Error),
301 Hex(bitcoin::hex::HexToBytesError),
303 Protocol(serde_json::Value),
305 Bitcoin(bitcoin::consensus::encode::Error),
307 AlreadySubscribed(ScriptHash),
309 NotSubscribed(ScriptHash),
311 InvalidResponse(serde_json::Value),
313 Message(String),
315 InvalidDNSNameError(String),
317 MissingDomain,
319 AllAttemptsErrored(Vec<Error>),
321 SharedIOError(Arc<std::io::Error>),
323
324 CouldntLockReader,
327 Mpsc,
329 #[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
330 CouldNotCreateConnection(rustls::Error),
332
333 #[cfg(feature = "use-openssl")]
334 InvalidSslMethod(openssl::error::ErrorStack),
336 #[cfg(feature = "use-openssl")]
337 SslHandshakeError(openssl::ssl::HandshakeError<std::net::TcpStream>),
339}
340
341impl Display for Error {
342 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
343 match self {
344 Error::IOError(e) => Display::fmt(e, f),
345 Error::JSON(e) => Display::fmt(e, f),
346 Error::Hex(e) => Display::fmt(e, f),
347 Error::Bitcoin(e) => Display::fmt(e, f),
348 Error::SharedIOError(e) => Display::fmt(e, f),
349 #[cfg(feature = "use-openssl")]
350 Error::SslHandshakeError(e) => Display::fmt(e, f),
351 #[cfg(feature = "use-openssl")]
352 Error::InvalidSslMethod(e) => Display::fmt(e, f),
353 #[cfg(any(
354 feature = "use-rustls",
355 feature = "use-rustls-ring",
356 ))]
357 Error::CouldNotCreateConnection(e) => Display::fmt(e, f),
358
359 Error::Message(e) => f.write_str(e),
360 Error::InvalidDNSNameError(domain) => write!(f, "Invalid domain name {} not matching SSL certificate", domain),
361 Error::AllAttemptsErrored(errors) => {
362 f.write_str("Made one or multiple attempts, all errored:\n")?;
363 for err in errors {
364 writeln!(f, "\t- {}", err)?;
365 }
366 Ok(())
367 }
368
369 Error::Protocol(e) => write!(f, "Electrum server error: {}", e.clone().take()),
370 Error::InvalidResponse(e) => write!(f, "Error during the deserialization of a response from the server: {}", e.clone().take()),
371
372 Error::AlreadySubscribed(_) => write!(f, "Already subscribed to the notifications of an address"),
374 Error::NotSubscribed(_) => write!(f, "Not subscribed to the notifications of an address"),
375
376 Error::MissingDomain => f.write_str("Missing domain while it was explicitly asked to validate it"),
377 Error::CouldntLockReader => f.write_str("Couldn't take a lock on the reader mutex. This means that there's already another reader thread is running"),
378 Error::Mpsc => f.write_str("Broken IPC communication channel: the other thread probably has exited"),
379 }
380 }
381}
382
383impl std::error::Error for Error {}
384
385macro_rules! impl_error {
386 ( $from:ty, $to:ident ) => {
387 impl std::convert::From<$from> for Error {
388 fn from(err: $from) -> Self {
389 Error::$to(err.into())
390 }
391 }
392 };
393}
394
395impl_error!(std::io::Error, IOError);
396impl_error!(serde_json::Error, JSON);
397impl_error!(bitcoin::hex::HexToBytesError, Hex);
398impl_error!(bitcoin::consensus::encode::Error, Bitcoin);
399
400impl<T> From<std::sync::PoisonError<T>> for Error {
401 fn from(_: std::sync::PoisonError<T>) -> Self {
402 Error::IOError(std::io::Error::from(std::io::ErrorKind::BrokenPipe))
403 }
404}
405
406impl<T> From<std::sync::mpsc::SendError<T>> for Error {
407 fn from(_: std::sync::mpsc::SendError<T>) -> Self {
408 Error::Mpsc
409 }
410}
411
412impl From<std::sync::mpsc::RecvError> for Error {
413 fn from(_: std::sync::mpsc::RecvError) -> Self {
414 Error::Mpsc
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use crate::ScriptStatus;
421
422 #[test]
423 fn script_status_roundtrip() {
424 let script_status: ScriptStatus = [1u8; 32].into();
425 let script_status_json = serde_json::to_string(&script_status).unwrap();
426 let script_status_back = serde_json::from_str(&script_status_json).unwrap();
427 assert_eq!(script_status, script_status_back);
428 }
429}