1use amplify::hex;
6use amplify::hex::{FromHex, ToHex};
7use bp::{BlockHash, BlockHeader, ConsensusDecode, ConsensusDecodeError, ScriptPubkey, Tx, Txid};
8use std::convert::TryFrom;
9use std::fmt::{self, Display, Formatter};
10use std::ops::Deref;
11use std::sync::Arc;
12
13use serde::{de, Deserialize, Serialize};
14use sha2::Digest;
15
16static JSONRPC_2_0: &str = "2.0";
17
18pub(crate) type Call = (String, Vec<Param>);
19
20#[derive(Serialize, Clone)]
21#[serde(untagged)]
22pub enum Param {
24 U32(u32),
26 Usize(usize),
28 String(String),
30 Bool(bool),
32 Bytes(Vec<u8>),
34}
35
36#[derive(Serialize, Clone)]
37pub struct Request<'a> {
39 jsonrpc: &'static str,
40
41 pub id: usize,
43 pub method: &'a str,
45 pub params: Vec<Param>,
47}
48
49impl<'a> Request<'a> {
50 fn new(method: &'a str, params: Vec<Param>) -> Self {
52 Self {
53 id: 0,
54 jsonrpc: JSONRPC_2_0,
55 method,
56 params,
57 }
58 }
59
60 pub fn new_id(id: usize, method: &'a str, params: Vec<Param>) -> Self {
62 let mut instance = Self::new(method, params);
63 instance.id = id;
64
65 instance
66 }
67}
68
69#[doc(hidden)]
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
71pub struct Hex32Bytes(#[serde(deserialize_with = "from_hex", serialize_with = "to_hex")] [u8; 32]);
72
73impl Deref for Hex32Bytes {
74 type Target = [u8; 32];
75
76 fn deref(&self) -> &Self::Target {
77 &self.0
78 }
79}
80
81impl From<[u8; 32]> for Hex32Bytes {
82 fn from(other: [u8; 32]) -> Hex32Bytes {
83 Hex32Bytes(other)
84 }
85}
86
87impl Hex32Bytes {
88 pub(crate) fn as_hex(&self) -> String {
89 self.0.to_hex()
90 }
91}
92
93pub type ScriptHash = Hex32Bytes;
96
97pub type ScriptStatus = Hex32Bytes;
100
101pub trait ToElectrumScriptHash {
103 fn to_electrum_scripthash(&self) -> ScriptHash;
105}
106
107impl ToElectrumScriptHash for ScriptPubkey {
108 fn to_electrum_scripthash(&self) -> ScriptHash {
109 let mut result: [u8; 32] = sha2::Sha256::digest(self.as_slice()).into();
110 result.reverse();
111
112 result.into()
113 }
114}
115
116fn from_hex<'de, T, D>(deserializer: D) -> Result<T, D::Error>
117where
118 T: FromHex,
119 D: de::Deserializer<'de>,
120{
121 let s = String::deserialize(deserializer)?;
122 T::from_hex(&s).map_err(de::Error::custom)
123}
124
125fn to_hex<S>(bytes: &[u8], serializer: S) -> std::result::Result<S::Ok, S::Error>
126where
127 S: serde::ser::Serializer,
128{
129 serializer.serialize_str(&bytes.to_hex())
130}
131
132fn from_hex_array<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
133where
134 T: FromHex + std::fmt::Debug,
135 D: de::Deserializer<'de>,
136{
137 let arr = Vec::<String>::deserialize(deserializer)?;
138
139 let results: Vec<Result<T, _>> = arr
140 .into_iter()
141 .map(|s| T::from_hex(&s).map_err(de::Error::custom))
142 .collect();
143
144 let mut answer = Vec::new();
145 for x in results.into_iter() {
146 answer.push(x?);
147 }
148
149 Ok(answer)
150}
151
152fn from_hex_header<'de, D>(deserializer: D) -> Result<BlockHeader, D::Error>
153where
154 D: de::Deserializer<'de>,
155{
156 let vec: Vec<u8> = from_hex(deserializer)?;
157 BlockHeader::consensus_deserialize(&vec).map_err(de::Error::custom)
158}
159
160#[derive(Clone, Debug, Deserialize)]
162pub struct GetHistoryRes {
163 pub height: i32,
166 pub tx_hash: Txid,
168 pub fee: Option<u64>,
170}
171
172#[derive(Clone, Debug, Deserialize)]
174pub struct ListUnspentRes {
175 pub height: usize,
177 pub tx_hash: Txid,
179 pub tx_pos: usize,
181 pub value: u64,
183}
184
185#[derive(Clone, Debug, Deserialize)]
187pub struct GetMempoolRes {
188 pub height: i32,
190 pub tx_hash: Txid,
192 pub fee: u64,
194}
195
196#[derive(Clone, Debug, Deserialize)]
198pub struct TxRes {
199 pub confirmations: u32,
201 pub block_hash: Option<BlockHash>,
203 pub time: Option<u64>,
205 pub tx: Tx,
207}
208
209#[derive(Clone, Debug, Deserialize)]
211pub struct ServerFeaturesRes {
212 pub server_version: String,
214 #[serde(deserialize_with = "from_hex")]
216 pub genesis_hash: [u8; 32],
217 pub protocol_min: String,
219 pub protocol_max: String,
221 pub hash_function: Option<String>,
223 pub pruning: Option<i64>,
225}
226
227#[derive(Clone, Debug, Deserialize)]
229pub struct GetHeadersRes {
230 pub max: usize,
232 pub count: usize,
234 #[serde(rename(deserialize = "hex"), deserialize_with = "from_hex")]
236 pub raw_headers: Vec<u8>,
237 #[serde(skip)]
239 pub headers: Vec<BlockHeader>,
240}
241
242#[derive(Clone, Debug, Deserialize)]
244pub struct GetBalanceRes {
245 pub confirmed: u64,
247 pub unconfirmed: i64,
251}
252
253#[derive(Clone, Debug, Deserialize)]
255pub struct GetMerkleRes {
256 pub block_height: usize,
258 pub pos: usize,
260 #[serde(deserialize_with = "from_hex_array")]
262 pub merkle: Vec<[u8; 32]>,
263}
264
265#[derive(Clone, Debug, Deserialize)]
268pub struct TxidFromPosRes {
269 pub tx_hash: Txid,
271 #[serde(deserialize_with = "from_hex_array")]
273 pub merkle: Vec<[u8; 32]>,
274}
275
276#[derive(Clone, Debug, Deserialize)]
278pub struct HeaderNotification {
279 pub height: usize,
281 #[serde(rename = "hex", deserialize_with = "from_hex_header")]
283 pub header: BlockHeader,
284}
285
286#[derive(Clone, Debug, Deserialize)]
288pub struct RawHeaderNotification {
289 pub height: usize,
291 #[serde(rename = "hex", deserialize_with = "from_hex")]
293 pub header: Vec<u8>,
294}
295
296impl TryFrom<RawHeaderNotification> for HeaderNotification {
297 type Error = Error;
298
299 fn try_from(raw: RawHeaderNotification) -> Result<Self, Self::Error> {
300 Ok(HeaderNotification {
301 height: raw.height,
302 header: BlockHeader::consensus_deserialize(&raw.header)?,
303 })
304 }
305}
306
307#[derive(Clone, Debug, Deserialize)]
309pub struct ScriptNotification {
310 pub scripthash: ScriptHash,
312 pub status: ScriptStatus,
314}
315
316#[derive(Clone, Debug, Deserialize)]
318pub struct ProtocolError {
319 pub code: i16,
321 pub message: String,
323}
324
325#[derive(Debug)]
327pub enum Error {
328 IOError(std::io::Error),
330 JSON(serde_json::error::Error),
332 Hex(hex::Error),
334 JSONRpc(String),
336 Protocol(ProtocolError),
338 Bitcoin(ConsensusDecodeError),
340 AlreadySubscribed(ScriptHash),
342 NotSubscribed(ScriptHash),
344 InvalidResponse(serde_json::Value),
346 InvalidDNSNameError(String),
348 MissingDomain,
350 AllAttemptsErrored(Vec<Error>),
352 SharedIOError(Arc<std::io::Error>),
354
355 CouldntLockReader,
358 Mpsc,
360 #[cfg(any(feature = "use-rustls", feature = "use-rustls-ring"))]
361 CouldNotCreateConnection(rustls::Error),
363
364 #[cfg(feature = "use-openssl")]
365 InvalidSslMethod(openssl::error::ErrorStack),
367 #[cfg(feature = "use-openssl")]
368 SslHandshakeError(openssl::ssl::HandshakeError<std::net::TcpStream>),
370}
371
372impl Display for Error {
373 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
374 match self {
375 Error::IOError(e) => Display::fmt(e, f),
376 Error::JSON(e) => Display::fmt(e, f),
377 Error::Hex(e) => Display::fmt(e, f),
378 Error::Bitcoin(e) => Display::fmt(e, f),
379 Error::SharedIOError(e) => Display::fmt(e, f),
380 #[cfg(feature = "use-openssl")]
381 Error::SslHandshakeError(e) => Display::fmt(e, f),
382 #[cfg(feature = "use-openssl")]
383 Error::InvalidSslMethod(e) => Display::fmt(e, f),
384 #[cfg(any(
385 feature = "use-rustls",
386 feature = "use-rustls-ring",
387 ))]
388 Error::CouldNotCreateConnection(e) => Display::fmt(e, f),
389
390 Error::InvalidDNSNameError(domain) => write!(f, "Invalid domain name {} not matching SSL certificate", domain),
391 Error::AllAttemptsErrored(errors) => {
392 f.write_str("Made one or multiple attempts, all errored:\n")?;
393 for err in errors {
394 writeln!(f, "\t- {}", err)?;
395 }
396 Ok(())
397 }
398
399 Error::JSONRpc(msg) => write!(f, "Invalid use of Electrum JSON-RPC: {msg}"),
400 Error::Protocol(err) => write!(f, "Electrum server returned an error: ({}) {}", err.code, err.message),
401 Error::InvalidResponse(e) => write!(f, "Error during the deserialization of a response from the server: {e}"),
402
403 Error::AlreadySubscribed(_) => write!(f, "Already subscribed to the notifications of an address"),
405 Error::NotSubscribed(_) => write!(f, "Not subscribed to the notifications of an address"),
406
407 Error::MissingDomain => f.write_str("Missing domain while it was explicitly asked to validate it"),
408 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"),
409 Error::Mpsc => f.write_str("Broken IPC communication channel: the other thread probably has exited"),
410 }
411 }
412}
413
414impl std::error::Error for Error {}
415
416macro_rules! impl_error {
417 ( $from:ty, $to:ident ) => {
418 impl std::convert::From<$from> for Error {
419 fn from(err: $from) -> Self {
420 Error::$to(err.into())
421 }
422 }
423 };
424}
425
426impl_error!(std::io::Error, IOError);
427impl_error!(serde_json::Error, JSON);
428impl_error!(hex::Error, Hex);
429impl_error!(ConsensusDecodeError, Bitcoin);
430
431impl<T> From<std::sync::PoisonError<T>> for Error {
432 fn from(_: std::sync::PoisonError<T>) -> Self {
433 Error::IOError(std::io::Error::from(std::io::ErrorKind::BrokenPipe))
434 }
435}
436
437impl<T> From<std::sync::mpsc::SendError<T>> for Error {
438 fn from(_: std::sync::mpsc::SendError<T>) -> Self {
439 Error::Mpsc
440 }
441}
442
443impl From<std::sync::mpsc::RecvError> for Error {
444 fn from(_: std::sync::mpsc::RecvError) -> Self {
445 Error::Mpsc
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use crate::ScriptStatus;
452
453 #[test]
454 fn script_status_roundtrip() {
455 let script_status: ScriptStatus = [1u8; 32].into();
456 let script_status_json = serde_json::to_string(&script_status).unwrap();
457 let script_status_back = serde_json::from_str(&script_status_json).unwrap();
458 assert_eq!(script_status, script_status_back);
459 }
460}