1use std::convert::TryFrom;
8use std::fmt::{self, Display, Formatter};
9use std::ops::Deref;
10use std::sync::Arc;
11
12use bitcoin::blockdata::block;
13use bitcoin::consensus::encode::deserialize;
14use bitcoin::hashes::{sha256, Hash};
15use bitcoin::hex::{DisplayHex, FromHex};
16use bitcoin::{Script, Txid};
17
18use serde::{de, Deserialize, Serialize};
19
20static JSONRPC_2_0: &str = "2.0";
21
22pub(crate) type Call = (String, Vec<Param>);
23
24#[derive(Serialize, Clone)]
25#[serde(untagged)]
26pub enum Param {
28 U32(u32),
30 Usize(usize),
32 String(String),
34 Bool(bool),
36 Bytes(Vec<u8>),
38 StringVec(Vec<String>),
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum EstimationMode {
48 Conservative,
52 Economical,
54}
55
56impl Display for EstimationMode {
57 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
58 match self {
59 EstimationMode::Conservative => write!(f, "CONSERVATIVE"),
60 EstimationMode::Economical => write!(f, "ECONOMICAL"),
61 }
62 }
63}
64
65#[derive(Serialize, Clone)]
66pub struct Request<'a> {
68 jsonrpc: &'static str,
69
70 pub id: usize,
72 pub method: &'a str,
74 pub params: Vec<Param>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 authorization: Option<String>,
80}
81
82impl<'a> Request<'a> {
83 fn new(method: &'a str, params: Vec<Param>) -> Self {
85 Self {
86 id: 0,
87 jsonrpc: JSONRPC_2_0,
88 method,
89 params,
90 authorization: None,
91 }
92 }
93
94 pub fn new_id(id: usize, method: &'a str, params: Vec<Param>) -> Self {
96 let mut instance = Self::new(method, params);
97 instance.id = id;
98
99 instance
100 }
101
102 pub fn with_auth(mut self, authorization: Option<String>) -> Self {
104 self.authorization = authorization;
105 self
106 }
107}
108
109#[doc(hidden)]
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
111pub struct Hex32Bytes(#[serde(deserialize_with = "from_hex", serialize_with = "to_hex")] [u8; 32]);
112
113impl Deref for Hex32Bytes {
114 type Target = [u8; 32];
115
116 fn deref(&self) -> &Self::Target {
117 &self.0
118 }
119}
120
121impl From<[u8; 32]> for Hex32Bytes {
122 fn from(other: [u8; 32]) -> Hex32Bytes {
123 Hex32Bytes(other)
124 }
125}
126
127impl Hex32Bytes {
128 pub(crate) fn to_hex(self) -> String {
129 self.0.to_lower_hex_string()
130 }
131}
132
133pub type ScriptHash = Hex32Bytes;
136
137pub type ScriptStatus = Hex32Bytes;
140
141pub trait ToElectrumScriptHash {
143 fn to_electrum_scripthash(&self) -> ScriptHash;
145}
146
147impl ToElectrumScriptHash for Script {
148 fn to_electrum_scripthash(&self) -> ScriptHash {
149 let mut result = sha256::Hash::hash(self.as_bytes()).to_byte_array();
150 result.reverse();
151
152 result.into()
153 }
154}
155
156fn from_hex<'de, T, D>(deserializer: D) -> Result<T, D::Error>
157where
158 T: FromHex,
159 D: de::Deserializer<'de>,
160{
161 let s = String::deserialize(deserializer)?;
162 T::from_hex(&s).map_err(de::Error::custom)
163}
164
165fn to_hex<S>(bytes: &[u8], serializer: S) -> std::result::Result<S::Ok, S::Error>
166where
167 S: serde::ser::Serializer,
168{
169 serializer.serialize_str(&bytes.to_lower_hex_string())
170}
171
172fn from_hex_array<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
173where
174 T: FromHex + std::fmt::Debug,
175 D: de::Deserializer<'de>,
176{
177 let arr = Vec::<String>::deserialize(deserializer)?;
178
179 let results: Vec<Result<T, _>> = arr
180 .into_iter()
181 .map(|s| T::from_hex(&s).map_err(de::Error::custom))
182 .collect();
183
184 let mut answer = Vec::new();
185 for x in results.into_iter() {
186 answer.push(x?);
187 }
188
189 Ok(answer)
190}
191
192fn from_hex_header<'de, D>(deserializer: D) -> Result<block::Header, D::Error>
193where
194 D: de::Deserializer<'de>,
195{
196 let vec: Vec<u8> = from_hex(deserializer)?;
197 deserialize(&vec).map_err(de::Error::custom)
198}
199
200#[derive(Clone, Debug, Deserialize)]
202pub struct GetHistoryRes {
203 pub height: i32,
206 pub tx_hash: Txid,
208 pub fee: Option<u64>,
210}
211
212#[derive(Clone, Debug, Deserialize)]
214pub struct ListUnspentRes {
215 pub height: usize,
217 pub tx_hash: Txid,
219 pub tx_pos: usize,
221 pub value: u64,
223}
224
225#[derive(Clone, Debug, Deserialize)]
227pub struct ServerFeaturesRes {
228 pub server_version: String,
230 #[serde(deserialize_with = "from_hex")]
232 pub genesis_hash: [u8; 32],
233 pub protocol_min: String,
235 pub protocol_max: String,
237 pub hash_function: Option<String>,
239 pub pruning: Option<i64>,
241}
242
243#[derive(Clone, Debug, Deserialize)]
248#[serde(from = "(String, String)")]
249pub struct ServerVersionRes {
250 pub server_software_version: String,
252 pub protocol_version: String,
254}
255
256impl From<(String, String)> for ServerVersionRes {
257 fn from((server_software_version, protocol_version): (String, String)) -> Self {
258 Self {
259 server_software_version,
260 protocol_version,
261 }
262 }
263}
264
265#[derive(Clone, Debug, Deserialize)]
269pub struct MempoolInfoRes {
270 pub mempoolminfee: f64,
273 pub minrelaytxfee: f64,
275 pub incrementalrelayfee: f64,
278}
279
280#[derive(Clone, Debug, Deserialize)]
284pub(crate) struct GetHeadersResLegacy {
285 pub max: usize,
287 pub count: usize,
289 #[serde(rename(deserialize = "hex"), deserialize_with = "from_hex")]
291 pub raw_headers: Vec<u8>,
292}
293
294#[derive(Clone, Debug, Deserialize)]
296pub struct GetHeadersRes {
297 pub max: usize,
299 pub count: usize,
301 #[serde(default, rename(deserialize = "headers"))]
303 pub(crate) header_hexes: Vec<String>,
304 #[serde(skip)]
306 pub headers: Vec<block::Header>,
307}
308
309#[derive(Clone, Debug, Deserialize)]
311pub struct GetBalanceRes {
312 pub confirmed: u64,
314 pub unconfirmed: i64,
318}
319
320#[derive(Clone, Debug, Deserialize)]
322pub struct GetMerkleRes {
323 pub block_height: usize,
325 pub pos: usize,
327 #[serde(deserialize_with = "from_hex_array")]
329 pub merkle: Vec<[u8; 32]>,
330}
331
332#[derive(Clone, Debug, Deserialize)]
335pub struct TxidFromPosRes {
336 pub tx_hash: Txid,
338 #[serde(deserialize_with = "from_hex_array")]
340 pub merkle: Vec<[u8; 32]>,
341}
342
343#[derive(Clone, Debug, Deserialize)]
345pub struct BroadcastPackageError {
346 pub txid: Txid,
348 pub error: String,
350}
351
352#[derive(Clone, Debug, Deserialize)]
357pub struct BroadcastPackageRes {
358 pub success: bool,
360 #[serde(default)]
363 pub errors: Vec<BroadcastPackageError>,
364}
365
366#[derive(Clone, Debug, Deserialize)]
368pub struct HeaderNotification {
369 pub height: usize,
371 #[serde(rename = "hex", deserialize_with = "from_hex_header")]
373 pub header: block::Header,
374}
375
376#[derive(Clone, Debug, Deserialize)]
378pub struct RawHeaderNotification {
379 pub height: usize,
381 #[serde(rename = "hex", deserialize_with = "from_hex")]
383 pub header: Vec<u8>,
384}
385
386impl TryFrom<RawHeaderNotification> for HeaderNotification {
387 type Error = Error;
388
389 fn try_from(raw: RawHeaderNotification) -> Result<Self, Self::Error> {
390 Ok(HeaderNotification {
391 height: raw.height,
392 header: deserialize(&raw.header)?,
393 })
394 }
395}
396
397#[derive(Clone, Debug, Deserialize)]
399pub struct ScriptNotification {
400 pub scripthash: ScriptHash,
402 pub status: ScriptStatus,
404}
405
406#[derive(Debug)]
408pub enum Error {
409 IOError(std::io::Error),
411 JSON(serde_json::error::Error),
413 Hex(bitcoin::hex::HexToBytesError),
415 Protocol(serde_json::Value),
417 Bitcoin(bitcoin::consensus::encode::Error),
419 AlreadySubscribed(ScriptHash),
421 NotSubscribed(ScriptHash),
423 InvalidResponse(serde_json::Value),
425 Message(String),
427 InvalidDNSNameError(String),
429 MissingDomain,
431 AllAttemptsErrored(Vec<Error>),
433 SharedIOError(Arc<std::io::Error>),
435
436 CouldntLockReader,
439 Mpsc,
441 #[cfg(any(feature = "rustls", feature = "rustls-ring"))]
442 CouldNotCreateConnection(rustls::Error),
444
445 #[cfg(feature = "openssl")]
446 InvalidSslMethod(openssl::error::ErrorStack),
448 #[cfg(feature = "openssl")]
449 SslHandshakeError(openssl::ssl::HandshakeError<std::net::TcpStream>),
451}
452
453impl Display for Error {
454 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
455 match self {
456 Error::IOError(e) => Display::fmt(e, f),
457 Error::JSON(e) => Display::fmt(e, f),
458 Error::Hex(e) => Display::fmt(e, f),
459 Error::Bitcoin(e) => Display::fmt(e, f),
460 Error::SharedIOError(e) => Display::fmt(e, f),
461 #[cfg(feature = "openssl")]
462 Error::SslHandshakeError(e) => Display::fmt(e, f),
463 #[cfg(feature = "openssl")]
464 Error::InvalidSslMethod(e) => Display::fmt(e, f),
465 #[cfg(any(
466 feature = "rustls",
467 feature = "rustls-ring",
468 ))]
469 Error::CouldNotCreateConnection(e) => Display::fmt(e, f),
470
471 Error::Message(e) => f.write_str(e),
472 Error::InvalidDNSNameError(domain) => write!(f, "Invalid domain name {} not matching SSL certificate", domain),
473 Error::AllAttemptsErrored(errors) => {
474 f.write_str("Made one or multiple attempts, all errored:\n")?;
475 for err in errors {
476 writeln!(f, "\t- {}", err)?;
477 }
478 Ok(())
479 }
480
481 Error::Protocol(e) => write!(f, "Electrum server error: {}", e.clone().take()),
482 Error::InvalidResponse(e) => write!(f, "Error during the deserialization of a response from the server: {}", e.clone().take()),
483
484 Error::AlreadySubscribed(_) => write!(f, "Already subscribed to the notifications of an address"),
486 Error::NotSubscribed(_) => write!(f, "Not subscribed to the notifications of an address"),
487
488 Error::MissingDomain => f.write_str("Missing domain while it was explicitly asked to validate it"),
489 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"),
490 Error::Mpsc => f.write_str("Broken IPC communication channel: the other thread probably has exited"),
491 }
492 }
493}
494
495impl std::error::Error for Error {}
496
497macro_rules! impl_error {
498 ( $from:ty, $to:ident ) => {
499 impl std::convert::From<$from> for Error {
500 fn from(err: $from) -> Self {
501 Error::$to(err.into())
502 }
503 }
504 };
505}
506
507impl_error!(std::io::Error, IOError);
508impl_error!(serde_json::Error, JSON);
509impl_error!(bitcoin::hex::HexToBytesError, Hex);
510impl_error!(bitcoin::consensus::encode::Error, Bitcoin);
511
512impl<T> From<std::sync::PoisonError<T>> for Error {
513 fn from(_: std::sync::PoisonError<T>) -> Self {
514 Error::IOError(std::io::Error::from(std::io::ErrorKind::BrokenPipe))
515 }
516}
517
518impl<T> From<std::sync::mpsc::SendError<T>> for Error {
519 fn from(_: std::sync::mpsc::SendError<T>) -> Self {
520 Error::Mpsc
521 }
522}
523
524impl From<std::sync::mpsc::RecvError> for Error {
525 fn from(_: std::sync::mpsc::RecvError) -> Self {
526 Error::Mpsc
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use crate::ScriptStatus;
533
534 use super::{Param, Request};
535
536 #[test]
537 fn script_status_roundtrip() {
538 let script_status: ScriptStatus = [1u8; 32].into();
539 let script_status_json = serde_json::to_string(&script_status).unwrap();
540 let script_status_back = serde_json::from_str(&script_status_json).unwrap();
541 assert_eq!(script_status, script_status_back);
542 }
543
544 #[test]
545 fn test_request_serialization_without_authorization() {
546 let req = Request::new_id(1, "server.version", vec![]);
547
548 let json = serde_json::to_string(&req).unwrap();
549 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
550
551 assert!(parsed.get("authorization").is_none());
553 assert!(!json.contains("authorization"));
554 assert_eq!(parsed["jsonrpc"], "2.0");
555 assert_eq!(parsed["method"], "server.version");
556 assert_eq!(parsed["id"], 1);
557 }
558
559 #[test]
560 fn test_request_serialization_with_authorization() {
561 let mut req = Request::new_id(1, "server.version", vec![]);
562 req.authorization = Some("Bearer test-jwt-token".to_string());
563
564 let json = serde_json::to_string(&req).unwrap();
565 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
566
567 assert_eq!(
569 parsed["authorization"],
570 serde_json::Value::String("Bearer test-jwt-token".to_string())
571 );
572 assert_eq!(parsed["jsonrpc"], "2.0");
573 assert_eq!(parsed["method"], "server.version");
574 assert_eq!(parsed["id"], 1);
575 }
576
577 #[test]
578 fn test_request_with_params_and_authorization() {
579 let mut req = Request::new_id(
580 42,
581 "blockchain.scripthash.get_balance",
582 vec![Param::String("test-scripthash".to_string())],
583 );
584 req.authorization = Some("Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9".to_string());
585
586 let json = serde_json::to_string(&req).unwrap();
587 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
588
589 assert_eq!(parsed["id"], 42);
590 assert_eq!(parsed["method"], "blockchain.scripthash.get_balance");
591 assert_eq!(
592 parsed["authorization"],
593 "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
594 );
595 assert!(parsed["params"].is_array());
596 assert_eq!(parsed["params"][0], "test-scripthash");
597 }
598}