1use std::{collections::HashMap, fmt::Display, str::FromStr};
2
3use super::{method::Method, params::VersionKind, request::Request, types::ScriptHash, Error};
4use miniscript::bitcoin::Txid;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8#[derive(Debug, PartialEq, Clone)]
9pub enum Response {
10 HeaderNotif(HeaderNotification),
11 BatchHeaderNotif(BatchHeaderNotif),
12 SHNotification(SHNotification),
13 Ping(PingResponse),
14 Banner(BannerResponse),
15 Header(HeaderResponse),
16 Headers(HeadersResponse),
17 Version(VersionResponse),
18 TxGet(TxGetResponse),
19 SHSubscribe(SHSubscribeResponse),
20 SHUnsubscribe(SHUnsubscribeResponse),
21 SHGetBalance(SHGetBalanceResponse),
22 SHGetHistory(SHGetHistoryResponse),
23 SHGetMempool(SHGetMempoolResponse),
24 SHListUnspent(SHListUnspentResponse),
25 Error(ErrorResponse),
26 Features(FeaturesResponse),
27 TxBroadcast(TxBroadcastResponse),
28 Donation(DonationResponse),
29 EstimateFee(EstimateFeeResponse),
30 FeeHistogram(FeeHistogramResponse),
31 RelayFee(RelayFeeResponse),
32 TxGetMerkle(TxGetMerkleResponse),
33 TxFromposition(TxFromPositionResponse),
34 ListPeers(ListPeersResponse),
35}
36
37impl From<Response> for Vec<Response> {
38 fn from(val: Response) -> Self {
39 vec![val]
40 }
41}
42
43impl Response {
44 pub fn id(&self) -> Option<usize> {
45 match self {
46 Response::Ping(PingResponse { id, .. }) => Some(*id),
47 Response::Banner(BannerResponse { id, .. }) => Some(*id),
48 Response::Header(HeaderResponse { id, .. }) => Some(*id),
49 Response::Headers(HeadersResponse { id, .. }) => Some(*id),
50 Response::Version(VersionResponse { id, .. }) => Some(*id),
51 Response::TxGet(TxGetResponse { id, .. }) => Some(*id),
52 Response::SHSubscribe(SHSubscribeResponse { id, .. }) => Some(*id),
53 Response::SHUnsubscribe(SHUnsubscribeResponse { id, .. }) => Some(*id),
54 Response::SHGetBalance(SHGetBalanceResponse { id, .. }) => Some(*id),
55 Response::SHGetHistory(SHGetHistoryResponse { id, .. }) => Some(*id),
56 Response::SHGetMempool(SHGetMempoolResponse { id, .. }) => Some(*id),
57 Response::SHListUnspent(SHListUnspentResponse { id, .. }) => Some(*id),
58 Response::Error(ErrorResponse { id, .. }) => Some(*id),
59 Response::Features(FeaturesResponse { id, .. }) => Some(*id),
60 Response::TxBroadcast(TxBroadcastResponse { id, .. }) => Some(*id),
61 Response::Donation(DonationResponse { id, .. }) => Some(*id),
62 Response::EstimateFee(EstimateFeeResponse { id, .. }) => Some(*id),
63 Response::FeeHistogram(FeeHistogramResponse { id, .. }) => Some(*id),
64 Response::RelayFee(RelayFeeResponse { id, .. }) => Some(*id),
65 Response::TxGetMerkle(TxGetMerkleResponse { id, .. }) => Some(*id),
66 Response::TxFromposition(TxFromPositionResponse { id, .. }) => Some(*id),
67 Response::ListPeers(ListPeersResponse { id, .. }) => Some(*id),
68 _ => None,
69 }
70 }
71}
72
73pub struct ResponseBatch {
74 pub batch: Vec<Response>,
75}
76
77pub fn parse_str_response(
78 raw: &str,
79 index: &HashMap<usize, Request>,
80) -> Result<Vec<Response>, Error> {
81 let batch = ResponseBatch::from_str(raw, index)?;
83 if let Some(b) = batch {
84 return Ok(b.batch);
85 }
86 Ok(Response::try_parse(raw, index)?.into())
88}
89
90impl ResponseBatch {
91 pub fn from_str(s: &str, index: &HashMap<usize, Request>) -> Result<Option<Self>, Error> {
92 let parsed: Result<Vec<Value>, _> = serde_json::from_str(s);
93 if let Ok(parsed) = parsed {
94 let mut batch = Vec::<Response>::new();
95 for response in parsed {
96 let raw =
97 serde_json::to_string(&response).expect("parsing Value to string do not fail!");
98 batch.push(Response::try_parse(&raw, index)?);
99 }
100 Ok(Some(ResponseBatch { batch }))
101 } else {
102 Ok(None)
103 }
104 }
105}
106
107macro_rules! parse {
108 ($method:ident, $response_type:ty, $raw:expr) => {{
109 let r: $response_type =
110 serde_json::from_str($raw).map_err(|_| Error::ResponseParsing($raw.into()))?;
111 Ok(Self::$method(r))
112 }};
113}
114
115impl Response {
116 pub fn parse(raw: &str, index: &HashMap<usize, Request>) -> Response {
117 Self::try_parse(raw, index).unwrap()
118 }
119
120 pub fn try_parse(raw: &str, index: &HashMap<usize, Request>) -> Result<Response, Error> {
121 log::debug!("Response::try_parse() {raw}");
122 let error: Result<ErrorResponse, _> = serde_json::from_str(raw);
124 if let Ok(e) = error {
125 return Ok(Response::Error(e));
126 }
127
128 let header_notif: Result<BatchHeaderNotif, _> = serde_json::from_str(raw);
130 if let Ok(n) = header_notif {
131 return Ok(Response::BatchHeaderNotif(n));
132 }
133
134 let sh_notif: Result<SHNotification, _> = serde_json::from_str(raw);
136 if let Ok(n) = sh_notif {
137 return Ok(Response::SHNotification(n));
138 }
139
140 let rr: RawResponse = serde_json::from_str(raw)
142 .map_err(|e| Error::RawResponseParsing(format!("Fail to parse `{}`: {:?}", raw, e)))?;
143 let request = index.get(&rr.id).ok_or(Error::ResponseId(rr.id))?;
144 match request.method {
145 Method::Ping => parse!(Ping, PingResponse, raw),
146 Method::Banner => parse!(Banner, BannerResponse, raw),
147 Method::HeadersSubscribe => parse!(HeaderNotif, HeaderNotification, raw),
148 Method::BlockHeader => parse!(Header, HeaderResponse, raw),
149 Method::BlockHeaders => parse!(Headers, HeadersResponse, raw),
150 Method::Version => parse!(Version, VersionResponse, raw),
151 Method::TransactionGet => parse!(TxGet, TxGetResponse, raw),
152 Method::ScriptHashSubscribe => parse!(SHSubscribe, SHSubscribeResponse, raw),
153 Method::ScriptHashUnsubscribe => parse!(SHUnsubscribe, SHUnsubscribeResponse, raw),
154 Method::ScriptHashGetBalance => parse!(SHGetBalance, SHGetBalanceResponse, raw),
155 Method::ScriptHashGetHistory => parse!(SHGetHistory, SHGetHistoryResponse, raw),
156 Method::ScriptHashListUnspent => parse!(SHListUnspent, SHListUnspentResponse, raw),
157 Method::Features => parse!(Features, FeaturesResponse, raw),
160 Method::Donation => parse!(Donation, DonationResponse, raw),
161 Method::EstimateFee => parse!(EstimateFee, EstimateFeeResponse, raw),
162 Method::FeeHistogram => parse!(FeeHistogram, FeeHistogramResponse, raw),
163 Method::RelayFee => parse!(RelayFee, RelayFeeResponse, raw),
164 Method::TransactionGetMerkle => parse!(TxGetMerkle, TxGetMerkleResponse, raw),
165 Method::TransactionFromPosition => parse!(TxFromposition, TxFromPositionResponse, raw),
166 Method::TransactionBroadcast => parse!(TxBroadcast, TxBroadcastResponse, raw),
167 Method::ListPeers => todo!(),
168 }
169 }
170}
171
172#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
173pub struct ErrorResult {
174 pub code: usize,
175 pub message: String,
176}
177
178impl Display for ErrorResponse {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 write!(
181 f,
182 "Electrum error {}: {}",
183 self.error.code, self.error.message
184 )
185 }
186}
187
188#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
189pub struct ErrorResponse {
190 pub id: usize,
191 pub error: ErrorResult,
192}
193
194#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
195pub struct SHNotification {
196 pub method: Method,
197 #[serde(rename = "params")]
198 pub status: (ScriptHash, Option<String>),
199}
200
201impl FromStr for SHNotification {
202 type Err = Error;
203 fn from_str(value: &str) -> Result<Self, Error> {
204 let notif: Self =
205 serde_json::from_str(value).map_err(|_| Error::ResponseParsing(value.into()))?;
206 if let Method::ScriptHashSubscribe = notif.method {
207 Ok(notif)
208 } else {
209 Err(Error::WrongMethod)
210 }
211 }
212}
213
214#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
215pub struct RawResponse {
216 jsonrpc: String,
217 pub id: usize,
218}
219
220#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
221pub struct BannerResponse {
222 pub id: usize,
223 pub result: String,
224}
225
226#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
227pub struct Header {
228 pub height: usize,
229 #[serde(rename = "hex")]
230 pub raw_header: String,
231}
232
233#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
234pub struct SingleHeaderNotif {
235 pub id: usize,
236 #[serde(rename = "result")]
237 pub header: Header,
238}
239
240#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
241pub struct BatchHeaderNotif {
242 pub method: Method,
243 #[serde(rename = "params")]
244 pub headers: Vec<Header>,
245}
246
247#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
248#[serde(untagged)]
249pub enum HeaderNotification {
250 Single(SingleHeaderNotif),
251 Batch(BatchHeaderNotif),
252}
253
254#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
255pub struct HeaderResponse {
256 pub id: usize,
257 #[serde(rename = "result")]
258 pub raw_header: String,
259}
260
261#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
262pub struct Headers {
263 pub count: usize,
264 #[serde(rename = "hex")]
265 pub raw_headers: String,
266 pub max: usize,
267}
268
269#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
270pub struct HeadersResponse {
271 pub id: usize,
272 #[serde(rename = "result")]
273 pub headers: Headers,
274}
275
276#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
277pub struct TxBroadcastResponse {
278 pub id: usize,
279 #[serde(rename = "result")]
280 pub txid: Txid,
281}
282
283#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
284pub struct DonationResponse {
285 pub id: usize,
286 #[serde(rename = "result")]
287 pub address: Option<String>,
288}
289
290#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
291pub struct EstimateFeeResponse {
292 pub id: usize,
293 #[serde(rename = "result")]
294 pub fee: OptionalFee,
295}
296
297#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
298#[serde(untagged)]
299pub enum Port {
300 String(String),
301 U16(u16),
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
305pub struct Host {
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub tcp_port: Option<Port>,
308 #[serde(skip_serializing_if = "Option::is_none")]
309 pub ssl_port: Option<Port>,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
313#[serde(untagged)]
314pub enum Hosts {
315 Single(Host),
316 Map(HashMap<String, Host>),
317}
318
319#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
320pub struct FeaturesResult {
321 #[serde(rename = "genesis_hash")]
322 pub genesis: String,
323 pub hosts: Hosts,
324 pub protocol_max: String,
325 pub protocol_min: String,
326 pub pruning: Option<usize>,
327 pub server_version: String,
328 pub hash_function: String,
329 #[serde(skip_serializing_if = "Option::is_none")]
330 pub services: Option<Vec<String>>,
331}
332
333#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
334pub struct FeaturesResponse {
335 pub id: usize,
336 #[serde(rename = "result")]
337 pub features: FeaturesResult,
338}
339
340#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
341pub struct FeeHistogramResponse {
342 pub id: usize,
343 #[serde(rename = "result")]
344 pub histogram: Vec<(usize, usize)>,
345}
346
347#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
348pub struct PingResponse {
349 pub id: usize,
350 pub result: Option<String>,
352}
353
354#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
355#[serde(untagged)]
356pub enum OptionalFee {
357 Fee(f64),
358 None(i64),
359}
360
361#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
362pub struct RelayFeeResponse {
363 pub id: usize,
364 #[serde(rename = "result")]
365 pub fee: OptionalFee,
367}
368
369#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
370pub struct SHSubscribeResponse {
371 pub id: usize,
372 pub result: Option<String>,
373}
374
375#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
376pub struct SHUnsubscribeResponse {
377 pub id: usize,
378 pub result: bool,
379}
380
381#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
382pub struct BalanceResult {
383 pub confirmed: i64,
384 pub unconfirmed: i64,
385}
386
387#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
388pub struct SHGetBalanceResponse {
389 pub id: usize,
390 #[serde(rename = "result")]
391 pub balance: BalanceResult,
392}
393
394#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
395pub struct HistoryResult {
396 pub height: i128,
400 #[serde(rename = "tx_hash")]
401 pub txid: Txid,
402 pub fee: Option<usize>,
403}
404
405#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
406pub struct SHGetHistoryResponse {
407 pub id: usize,
408 #[serde(rename = "result")]
409 pub history: Vec<HistoryResult>,
410}
411
412#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
413pub struct SHGetMempoolResponse {
414 pub id: usize,
415 #[serde(rename = "result")]
416 pub mempool: Vec<HistoryResult>,
417}
418
419#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
420pub struct UtxoResult {
421 pub height: usize,
422 #[serde(rename = "tx_hash")]
423 pub txid: Txid,
424 #[serde(rename = "tx_pos")]
425 pub vout: usize,
426 pub value: usize,
427}
428
429#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
430pub struct SHListUnspentResponse {
431 pub id: usize,
432 #[serde(rename = "result")]
433 pub unspent: Vec<UtxoResult>,
434}
435
436#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
437pub struct VerboseTx {
438 pub blockhash: String,
439 pub blocktime: usize,
440 pub confirmations: usize,
441 pub locktime: usize,
442 pub size: usize,
443 pub time: usize,
444 pub version: usize,
445 pub txid: String,
446 #[serde(rename = "hex")]
447 pub raw_tx: String,
448 pub vin: Value,
450 pub vout: Value,
451}
452
453#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
454#[serde(untagged)]
455pub enum TxGetResult {
456 Raw(String),
457 Verbose(Box<VerboseTx>),
458}
459#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
460pub struct TxGetResponse {
461 pub id: usize,
462 pub result: TxGetResult,
463}
464
465#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
466pub struct GetMerkleResult {
467 merkle: Vec<String>,
468 block_height: usize,
469 #[serde(rename = "pos")]
470 tx_pos: usize,
471}
472
473#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
474pub struct TxGetMerkleResponse {
475 pub id: usize,
476 pub result: GetMerkleResult,
477}
478
479#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
480#[serde(untagged)]
481pub enum TxfromPosResult {
482 Simple(Txid),
483 WithMerkle {
484 #[serde(rename = "tx_hash")]
485 txid: Txid,
486 merkle: Vec<String>,
487 },
488}
489
490#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
491pub struct TxFromPositionResponse {
492 pub id: usize,
493 #[serde(rename = "result")]
494 pub tx: TxfromPosResult,
495}
496
497#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
498pub struct Peer(
499 (
500 String, String, Vec<String>, ),
504);
505
506#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
507pub struct ListPeersResponse {
508 pub id: usize,
509 #[serde(rename = "result")]
510 pub peers: Vec<Peer>,
511}
512
513#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
514pub struct ResultVersion((String, VersionKind));
515
516#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
517pub struct VersionResponse {
518 pub id: usize,
519 #[serde(rename = "result")]
520 pub version: ResultVersion,
521}
522
523#[cfg(test)]
524mod tests {
525 use miniscript::bitcoin::{OutPoint, Script};
526
527 use super::*;
528
529 #[test]
530 fn parse_block_header_subscribe_response_a() {
531 let response = r#"{"id":3,"jsonrpc":"2.0","result":{"height":119367,"hex":"00000020835fdbdeeadd23463fad98b4e21aaa8519afde89eecd0eb224001317421cbb5f5e636df02303e51280b586bc596ee9326bc849bbb5993e121a8cab7e6b60e8ab593fe166ffff7f2000000000"}}"#;
532
533 let parsed: HeaderNotification = serde_json::from_str(response).unwrap();
534 let expected = HeaderNotification::Single(SingleHeaderNotif { id: 3, header: Header { height: 119367, raw_header: "00000020835fdbdeeadd23463fad98b4e21aaa8519afde89eecd0eb224001317421cbb5f5e636df02303e51280b586bc596ee9326bc849bbb5993e121a8cab7e6b60e8ab593fe166ffff7f2000000000".into() }});
535 assert_eq!(parsed, expected)
536 }
537
538 #[test]
539 fn parse_header_response() {
540 let response = r#"{"id":0,"jsonrpc":"2.0","result":"000000206e59d4b0d8d5b9daa4d3ad3093975b0f2a18a6909533350cbfb4b7a04adc6f5f380884ecf7425e488e7f2b249de516e839a5b2d48bcc9b65d45387ce5081c1e8563fe166ffff7f2001000000"}"#;
541
542 let parsed: HeaderResponse = serde_json::from_str(response).unwrap();
543 assert_eq!(
544 parsed,
545 HeaderResponse {
546 id: 0,
547 raw_header: "000000206e59d4b0d8d5b9daa4d3ad3093975b0f2a18a6909533350cbfb4b7a04adc6f5f380884ecf7425e488e7f2b249de516e839a5b2d48bcc9b65d45387ce5081c1e8563fe166ffff7f2001000000".into()
548 }
549 )
550 }
551
552 #[test]
553 fn parse_headers_response() {
554 let response = r#"{"id":0,"jsonrpc":"2.0","result":{"count":5,"hex":"000000206e59d4b0d8d5b9daa4d3ad3093975b0f2a18a6909533350cbfb4b7a04adc6f5f380884ecf7425e488e7f2b249de516e839a5b2d48bcc9b65d45387ce5081c1e8563fe166ffff7f200100000000000020e4a9efb184a77e3b3d75c374823a808f437c5d04fc322f6585c1682ea859a379874002727ca2397cbf8b45bffbd0463c1a8e4f52c23af48b3d8e30c0c4556bd1563fe166ffff7f200100000000000020d02dd6842a2be3611748c75b423d0199f86599a7f565de283ee09ffe3527cf49d2e107eae3f796827fb71fc950ee32f5c45c58704cd0f6de8c5125dfe18d0005573fe166ffff7f20000000000000002007e28823c56f2b29644eaa8060f1e62e622733fbb796a429119963f6318e4d012833a1ec146ca836cbd22f3be596ee73f00134c1edafaeb1178623cf480e554c573fe166ffff7f200600000000000020a7cc866c5522c258d4d08cf78aaf6dec40df9cba90c51b4fb63577dab6000b4805c639b49ecb0ddb0d6e922047310faefc6d69316e137084386a24238d1152ba573fe166ffff7f2000000000","max":2016}}"#;
555
556 let parsed: HeadersResponse = serde_json::from_str(response).unwrap();
557 assert_eq!(
558 parsed,
559 HeadersResponse {
560 id: 0,
561 headers: Headers {
562 count: 5,
563 raw_headers: "000000206e59d4b0d8d5b9daa4d3ad3093975b0f2a18a6909533350cbfb4b7a04adc6f5f380884ecf7425e488e7f2b249de516e839a5b2d48bcc9b65d45387ce5081c1e8563fe166ffff7f200100000000000020e4a9efb184a77e3b3d75c374823a808f437c5d04fc322f6585c1682ea859a379874002727ca2397cbf8b45bffbd0463c1a8e4f52c23af48b3d8e30c0c4556bd1563fe166ffff7f200100000000000020d02dd6842a2be3611748c75b423d0199f86599a7f565de283ee09ffe3527cf49d2e107eae3f796827fb71fc950ee32f5c45c58704cd0f6de8c5125dfe18d0005573fe166ffff7f20000000000000002007e28823c56f2b29644eaa8060f1e62e622733fbb796a429119963f6318e4d012833a1ec146ca836cbd22f3be596ee73f00134c1edafaeb1178623cf480e554c573fe166ffff7f200600000000000020a7cc866c5522c258d4d08cf78aaf6dec40df9cba90c51b4fb63577dab6000b4805c639b49ecb0ddb0d6e922047310faefc6d69316e137084386a24238d1152ba573fe166ffff7f2000000000".into(),
564 max: 2016
565 }
566 }
567 )
568 }
569
570 #[test]
571 fn version() {
572 let response = r#"{"id":0,"jsonrpc":"2.0","result":["electrs/0.10.5","1.4"]}"#;
573 let response: VersionResponse = serde_json::from_str(response).unwrap();
574 if let VersionResponse {
575 id,
576 version: ResultVersion((server_name, VersionKind::Single(version))),
577 } = response
578 {
579 assert_eq!(id, 0);
580 assert_eq!(server_name, "electrs/0.10.5");
581 assert_eq!(version, "1.4");
582 } else {
583 panic!("wrong response")
584 }
585
586 let response = r#"{"id":0,"jsonrpc":"2.0","result":["electrs/0.10.5",["1.1","1.4"]]}"#;
587 let response: VersionResponse = serde_json::from_str(response).unwrap();
588 if let VersionResponse {
589 id,
590 version: ResultVersion((server_name, VersionKind::MinMax(min, max))),
591 } = response
592 {
593 assert_eq!(id, 0);
594 assert_eq!(server_name, "electrs/0.10.5");
595 assert_eq!(min, "1.1");
596 assert_eq!(max, "1.4");
597 } else {
598 panic!("wrong response")
599 }
600 }
601
602 #[test]
603 fn hash_subscribe_response() {
604 let response = r#"{"id":14,"jsonrpc":"2.0","result":"1c8606707de065bef7474d719b76fb41cdff0090fffb78ca6b640c66ba9a9542"}"#;
605
606 let response: SHSubscribeResponse = serde_json::from_str(response).unwrap();
607 assert_eq!(response.id, 14);
608 assert_eq!(
609 response.result,
610 Some("1c8606707de065bef7474d719b76fb41cdff0090fffb78ca6b640c66ba9a9542".to_string())
611 )
612 }
613
614 #[test]
615 fn batch_sh_subscribe_response() {
616 let script = Script::from_bytes(&[0x00]);
617 let req = Request::subscribe_sh(script);
618
619 let mut index = HashMap::new();
621 for i in 14..35usize {
622 let mut r = req.clone();
623 r.id = i;
624 index.insert(i, r);
625 }
626
627 let response = r#"[{"id":14,"jsonrpc":"2.0","result":"1c8606707de065bef7474d719b76fb41cdff0090fffb78ca6b640c66ba9a9542"},{"id":15,"jsonrpc":"2.0","result":null},{"id":16,"jsonrpc":"2.0","result":null},{"id":17,"jsonrpc":"2.0","result":null},{"id":18,"jsonrpc":"2.0","result":null},{"id":19,"jsonrpc":"2.0","result":null},{"id":20,"jsonrpc":"2.0","result":null},{"id":21,"jsonrpc":"2.0","result":null},{"id":22,"jsonrpc":"2.0","result":null},{"id":23,"jsonrpc":"2.0","result":null},{"id":24,"jsonrpc":"2.0","result":null},{"id":25,"jsonrpc":"2.0","result":null},{"id":26,"jsonrpc":"2.0","result":null},{"id":27,"jsonrpc":"2.0","result":null},{"id":28,"jsonrpc":"2.0","result":null},{"id":29,"jsonrpc":"2.0","result":null},{"id":30,"jsonrpc":"2.0","result":null},{"id":31,"jsonrpc":"2.0","result":null},{"id":32,"jsonrpc":"2.0","result":null},{"id":33,"jsonrpc":"2.0","result":null},{"id":34,"jsonrpc":"2.0","result":null}]"#;
628
629 let batch = ResponseBatch::from_str(response, &index).unwrap().unwrap();
630 assert_eq!(batch.batch.len(), 21);
631 let resp = &batch.batch[5];
632 if let Response::SHSubscribe(SHSubscribeResponse { id, result }) = resp {
633 assert_eq!(*id, 19);
634 assert_eq!(*result, None);
635 } else {
636 panic!("wrong response");
637 }
638 }
639
640 #[test]
641 fn error_response() {
642 let response = r#"{"error":{"code":1,"message":"unsupported request Single(\"0.4\") by smart"},"id":0,"jsonrpc":"2.0"}"#;
643
644 let response: ErrorResponse = serde_json::from_str(response).unwrap();
645 assert_eq!(response.error.code, 1);
646 assert_eq!(response.id, 0);
647 assert_eq!(
648 response.error.message,
649 r#"unsupported request Single("0.4") by smart"#
650 );
651 }
652
653 #[test]
654 fn sh_unsubscribe_response() {
655 let response = r#"{"id":0,"jsonrpc":"2.0","result":false}"#;
656 let response: SHUnsubscribeResponse = serde_json::from_str(response).unwrap();
657 assert_eq!(response.id, 0);
658 assert!(!response.result);
659 }
660
661 #[test]
662 fn sh_subscribe_response() {
663 let response = r#"{"id":1,"jsonrpc":"2.0","result":null}"#;
664 let response: SHSubscribeResponse = serde_json::from_str(response).unwrap();
665 assert_eq!(response.id, 1);
666 assert_eq!(response.result, None);
667
668 let response = r#"{"id":1,"jsonrpc":"2.0","result":"some_garbage_string"}"#;
669 let response: SHSubscribeResponse = serde_json::from_str(response).unwrap();
670 assert_eq!(response.id, 1);
671 assert_eq!(response.result, Some("some_garbage_string".into()));
672 }
673
674 #[test]
675 fn sh_notification() {
676 let response = r#" {"jsonrpc":"2.0","method":"blockchain.scripthash.subscribe","params":["95ebd95e7c0763b785d12b1d20d9f548fa5bb809f120afb0dd11276fa1ce8352","9bf1d98ff899eafd048290199144aed63e3d7ccbc8925e8351a4c1e8af2137f4"]}"#;
677
678 let _: SHNotification = serde_json::from_str(response).unwrap();
679 let response = SHNotification::from_str(response).unwrap();
680
681 assert_eq!(response.method, Method::ScriptHashSubscribe);
682 assert!(response.status.1.is_some());
683 assert_eq!(
684 response.status.1,
685 Some("9bf1d98ff899eafd048290199144aed63e3d7ccbc8925e8351a4c1e8af2137f4".into())
686 );
687 }
688
689 #[test]
690 fn sh_list_unspent() {
691 let response = r#"{"jsonrpc": "2.0", "result": [{"tx_hash": "b14edd61d6902890932be0d4386c79ca64a8dea345e9b9c95b2e8a825316cfc0", "tx_pos": 1, "height": 861250, "value": 566888}], "id": 0}"#;
692 let response: SHListUnspentResponse = serde_json::from_str(response).unwrap();
693 assert_eq!(response.id, 0);
694 assert_eq!(response.unspent.len(), 1);
695 assert_eq!(
696 response.unspent[0].txid,
697 Txid::from_str("b14edd61d6902890932be0d4386c79ca64a8dea345e9b9c95b2e8a825316cfc0")
698 .unwrap()
699 );
700 assert_eq!(response.unspent[0].vout, 1);
701 assert_eq!(response.unspent[0].height, 861250);
702 assert_eq!(response.unspent[0].value, 566888);
703 }
704
705 #[test]
706 fn sh_get_balance() {
707 let response =
708 r#"{"jsonrpc": "2.0", "result": {"confirmed": 566888, "unconfirmed": 0}, "id": 0}"#;
709 let response: SHGetBalanceResponse = serde_json::from_str(response).unwrap();
710 assert_eq!(response.id, 0);
711 assert_eq!(response.balance.confirmed, 566888);
712 assert_eq!(response.balance.unconfirmed, 0);
713 }
714
715 #[test]
716 fn sh_get_history() {
717 let response = r#"{"jsonrpc": "2.0", "result": [{"tx_hash": "b14edd61d6902890932be0d4386c79ca64a8dea345e9b9c95b2e8a825316cfc0", "height": 861250}], "id": 0}"#;
718 let response: SHGetHistoryResponse = serde_json::from_str(response).unwrap();
719 assert_eq!(response.id, 0);
720 assert_eq!(response.history.len(), 1);
721 assert_eq!(
722 response.history[0].txid,
723 Txid::from_str("b14edd61d6902890932be0d4386c79ca64a8dea345e9b9c95b2e8a825316cfc0")
724 .unwrap()
725 );
726 assert_eq!(response.history[0].height, 861250);
727 }
728
729 #[test]
730 fn features() {
731 let response = r#"{"jsonrpc": "2.0", "result": {"hosts": {}, "pruning": null, "server_version": "ElectrumX 1.15.0", "protocol_min": "1.4", "protocol_max": "1.4.2", "genesis_hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", "hash_function": "sha256", "services": []}, "id": 0}"#;
732
733 let response: FeaturesResponse = serde_json::from_str(response).unwrap();
734 assert_eq!(response.id, 0);
735 assert!(response.features.pruning.is_none());
736 assert_eq!(response.features.server_version, "ElectrumX 1.15.0");
737 assert_eq!(response.features.protocol_min, "1.4");
738 assert_eq!(response.features.protocol_max, "1.4.2");
739 assert_eq!(
740 response.features.genesis,
741 "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
742 );
743 assert_eq!(response.features.hash_function, "sha256");
744 assert!(response.features.services.is_some());
745 assert!(response.features.services.unwrap().is_empty());
746
747 let response = r#"{
748 "id": 0,
749 "jsonrpc": "2.0",
750 "result": {
751 "genesis_hash": "abc",
752 "hash_function": "sha256",
753 "hosts": {
754 "tcp_port": 46771
755 },
756 "protocol_max": "1.4",
757 "protocol_min": "1.4",
758 "pruning": null,
759 "server_version": "toto"
760 }
761 }"#;
762
763 let response: FeaturesResponse = serde_json::from_str(response).unwrap();
764
765 let expected = FeaturesResponse {
766 id: 0,
767 features: FeaturesResult {
768 genesis: "abc".into(),
769 hosts: Hosts::Single(Host {
770 tcp_port: Some(Port::U16(46771)),
771 ssl_port: None,
772 }),
773 protocol_max: "1.4".into(),
774 protocol_min: "1.4".into(),
775 pruning: None,
776 server_version: "toto".into(),
777 hash_function: "sha256".into(),
778 services: None,
779 },
780 };
781 assert_eq!(response, expected);
782 }
783
784 #[test]
785 fn donation() {
786 let response = r#"{"jsonrpc": "2.0", "result": "make_me_rich", "id": 0}"#;
787
788 let response: DonationResponse = serde_json::from_str(response).unwrap();
789 assert_eq!(response.id, 0);
790 assert_eq!(response.address, Some("make_me_rich".into()));
791 }
792
793 #[test]
794 fn estimate_fee() {
795 let response = r#"{"jsonrpc": "2.0", "result": 3.006e-05, "id": 0}"#;
796
797 let response: EstimateFeeResponse = serde_json::from_str(response).unwrap();
798 assert_eq!(response.id, 0);
799 assert_eq!(response.fee, OptionalFee::Fee(0.00003006));
800 }
801
802 #[test]
803 fn get_fee_histogram() {
804 let response = r#"{"jsonrpc": "2.0", "result": [[5, 103673], [3, 238053], [2, 12058673], [1, 34188435]], "id": 0}"#;
805
806 let response: FeeHistogramResponse = serde_json::from_str(response).unwrap();
807 assert_eq!(response.id, 0);
808 assert_eq!(response.histogram.len(), 4);
809 assert_eq!(response.histogram[1].0, 3);
810 assert_eq!(response.histogram[2].1, 12058673);
811 let expected = FeeHistogramResponse {
812 id: 0,
813 histogram: vec![(5, 103673), (3, 238053), (2, 12058673), (1, 34188435)],
814 };
815 assert_eq!(response, expected);
816 }
817
818 #[test]
819 fn relay_fee() {
820 let response = r#"{"jsonrpc": "2.0", "result": 1e-05, "id": 0}"#;
821
822 let response: RelayFeeResponse = serde_json::from_str(response).unwrap();
823 assert_eq!(response.id, 0);
824 assert_eq!(response.fee, OptionalFee::Fee(0.00001));
825 }
826
827 #[test]
828 fn tx_get_merkle() {
829 let response = r#"{"jsonrpc": "2.0", "result": {"block_height": 200000, "merkle": ["ffa0267c8f2af736858894d6f3e5081a05e2ec16dc98f78a80f376ce35077491", "d0039b6be844e631698f57fa02bbfbfb5e8b680f3ebb17646631e6ec9f91f6e6", "bbe3063ce3d04c2e3f18e494a287867f81ad1182b62a1ecb3e1ea2686edcea20", "1d15a2423f52d4aa281a2ac389c0a5a601ed08bdf814494ddf7697196860b801", "b63e58ec9f5ee2e268f1540af8bb0e5b8fd0ce7cd6877a174e6178c676d6b574", "7407724b98c77cdbf070f3fe297839de2bef50fead98b452883f0f3a4643cde2", "d029f17725e71e3c025bd7d0505006dc859af5450d0b6dd092ee88c0d98f9a25", "e4df974d81ab4fdf35f635024a01f20aa88af9f520215708b339dbc5bceddf63", "20f4202f18666483306f175e1c9c521741845afcf2710f0b0d42602ac72c5fd6"], "pos": 2}, "id": 0}"#;
830
831 let response: TxGetMerkleResponse = serde_json::from_str(response).unwrap();
832 assert_eq!(response.id, 0);
833 let expected = TxGetMerkleResponse {
834 id: 0,
835 result: GetMerkleResult {
836 merkle: vec![
837 "ffa0267c8f2af736858894d6f3e5081a05e2ec16dc98f78a80f376ce35077491".into(),
838 "d0039b6be844e631698f57fa02bbfbfb5e8b680f3ebb17646631e6ec9f91f6e6".into(),
839 "bbe3063ce3d04c2e3f18e494a287867f81ad1182b62a1ecb3e1ea2686edcea20".into(),
840 "1d15a2423f52d4aa281a2ac389c0a5a601ed08bdf814494ddf7697196860b801".into(),
841 "b63e58ec9f5ee2e268f1540af8bb0e5b8fd0ce7cd6877a174e6178c676d6b574".into(),
842 "7407724b98c77cdbf070f3fe297839de2bef50fead98b452883f0f3a4643cde2".into(),
843 "d029f17725e71e3c025bd7d0505006dc859af5450d0b6dd092ee88c0d98f9a25".into(),
844 "e4df974d81ab4fdf35f635024a01f20aa88af9f520215708b339dbc5bceddf63".into(),
845 "20f4202f18666483306f175e1c9c521741845afcf2710f0b0d42602ac72c5fd6".into(),
846 ],
847 block_height: 200_000,
848 tx_pos: 2,
849 },
850 };
851 assert_eq!(expected, response);
852 }
853
854 #[test]
855 fn tx_from_pos() {
856 let response = r#"{"jsonrpc": "2.0", "result": "ffa0267c8f2af736858894d6f3e5081a05e2ec16dc98f78a80f376ce35077491", "id": 0}"#;
857
858 let outpoint = OutPoint::from_str(
859 "ffa0267c8f2af736858894d6f3e5081a05e2ec16dc98f78a80f376ce35077491:0",
860 )
861 .unwrap();
862
863 let response: TxFromPositionResponse = serde_json::from_str(response).unwrap();
864 let expected = TxFromPositionResponse {
865 id: 0,
866 tx: TxfromPosResult::Simple(outpoint.txid),
867 };
868 assert_eq!(response, expected);
869
870 let response = r#"{"jsonrpc": "2.0", "result": {"tx_hash": "9cc064bbce74a2c56ce12b0b59fc7267a2618a35e1d8c66f642efd6d033a9681", "merkle": ["e48b08df0afa01a7339335fb6b6964100d11985765cbc6afcde990fd65856a9b", "12a6c68b6c033d6704bda3437370b3e7d65bec81b2e3f4eafb17632197f0b6c7", "c0dbecba7c7990f3bfbe727dd9a7371225852600dc0a0f07e68b3ec7c4fd629e", "8e351c5bac49e6dbf08bc67cc1f57fb4dbea0383336d0ee2c38fefc8736b18eb", "aa7171ca4f639d14050101ac602f3f526abec753414b3b4648071b252434e38e", "583b92abff3481905c686d3ff594c4a1d6a00bab25deb3397369b9e49adf11ae", "6a4d797a4d3e162a951ccd142fe6ca86e12006145f1670c5d1aa5e7bfcc05fa3", "c6dd553f393d1b7694ae168e8f5efeba8db4c3b000c2d9bf5205dd19f96c08a8"]}, "id": 1}"#;
871
872 let response: TxFromPositionResponse = serde_json::from_str(response).unwrap();
873 let outpoint = OutPoint::from_str(
874 "9cc064bbce74a2c56ce12b0b59fc7267a2618a35e1d8c66f642efd6d033a9681:0",
875 )
876 .unwrap();
877
878 let expected = TxFromPositionResponse {
879 id: 1,
880 tx: TxfromPosResult::WithMerkle {
881 txid: outpoint.txid,
882 merkle: vec![
883 "e48b08df0afa01a7339335fb6b6964100d11985765cbc6afcde990fd65856a9b".into(),
884 "12a6c68b6c033d6704bda3437370b3e7d65bec81b2e3f4eafb17632197f0b6c7".into(),
885 "c0dbecba7c7990f3bfbe727dd9a7371225852600dc0a0f07e68b3ec7c4fd629e".into(),
886 "8e351c5bac49e6dbf08bc67cc1f57fb4dbea0383336d0ee2c38fefc8736b18eb".into(),
887 "aa7171ca4f639d14050101ac602f3f526abec753414b3b4648071b252434e38e".into(),
888 "583b92abff3481905c686d3ff594c4a1d6a00bab25deb3397369b9e49adf11ae".into(),
889 "6a4d797a4d3e162a951ccd142fe6ca86e12006145f1670c5d1aa5e7bfcc05fa3".into(),
890 "c6dd553f393d1b7694ae168e8f5efeba8db4c3b000c2d9bf5205dd19f96c08a8".into(),
891 ],
892 },
893 };
894 assert_eq!(response, expected);
895 }
896}