esplora_client/
lib.rs

1//! An extensible blocking/async Esplora client
2//!
3//! This library provides an extensible blocking and
4//! async Esplora client to query Esplora's backend.
5//!
6//! The library provides the possibility to build a blocking
7//! client using [`minreq`] and an async client using [`reqwest`].
8//! The library supports communicating to Esplora via a proxy
9//! and also using TLS (SSL) for secure communication.
10//!
11//!
12//! ## Usage
13//!
14//! You can create a blocking client as follows:
15//!
16//! ```no_run
17//! # #[cfg(feature = "blocking")]
18//! # {
19//! use esplora_client::Builder;
20//! let builder = Builder::new("https://blockstream.info/testnet/api");
21//! let blocking_client = builder.build_blocking();
22//! # Ok::<(), esplora_client::Error>(());
23//! # }
24//! ```
25//!
26//! Here is an example of how to create an asynchronous client.
27//!
28//! ```no_run
29//! # #[cfg(all(feature = "async", feature = "tokio"))]
30//! # {
31//! use esplora_client::Builder;
32//! let builder = Builder::new("https://blockstream.info/testnet/api");
33//! let async_client = builder.build_async();
34//! # Ok::<(), esplora_client::Error>(());
35//! # }
36//! ```
37//!
38//! ## Features
39//!
40//! By default the library enables all features. To specify
41//! specific features, set `default-features` to `false` in your `Cargo.toml`
42//! and specify the features you want. This will look like this:
43//!
44//! `esplora-client = { version = "*", default-features = false, features =
45//! ["blocking"] }`
46//!
47//! * `blocking` enables [`minreq`], the blocking client with proxy.
48//! * `blocking-https` enables [`minreq`], the blocking client with proxy and TLS (SSL) capabilities
49//!   using the default [`minreq`] backend.
50//! * `blocking-https-rustls` enables [`minreq`], the blocking client with proxy and TLS (SSL)
51//!   capabilities using the `rustls` backend.
52//! * `blocking-https-native` enables [`minreq`], the blocking client with proxy and TLS (SSL)
53//!   capabilities using the platform's native TLS backend (likely OpenSSL).
54//! * `blocking-https-bundled` enables [`minreq`], the blocking client with proxy and TLS (SSL)
55//!   capabilities using a bundled OpenSSL library backend.
56//! * `async` enables [`reqwest`], the async client with proxy capabilities.
57//! * `async-https` enables [`reqwest`], the async client with support for proxying and TLS (SSL)
58//!   using the default [`reqwest`] TLS backend.
59//! * `async-https-native` enables [`reqwest`], the async client with support for proxying and TLS
60//!   (SSL) using the platform's native TLS backend (likely OpenSSL).
61//! * `async-https-rustls` enables [`reqwest`], the async client with support for proxying and TLS
62//!   (SSL) using the `rustls` TLS backend.
63//! * `async-https-rustls-manual-roots` enables [`reqwest`], the async client with support for
64//!   proxying and TLS (SSL) using the `rustls` TLS backend without using the default root
65//!   certificates.
66//!
67//! [`dont remove this line or cargo doc will break`]: https://example.com
68#![cfg_attr(not(feature = "minreq"), doc = "[`minreq`]: https://docs.rs/minreq")]
69#![cfg_attr(not(feature = "reqwest"), doc = "[`reqwest`]: https://docs.rs/reqwest")]
70#![allow(clippy::result_large_err)]
71#![warn(missing_docs)]
72
73use std::collections::HashMap;
74use std::fmt;
75use std::num::TryFromIntError;
76use std::time::Duration;
77
78#[cfg(feature = "async")]
79pub use r#async::Sleeper;
80
81pub mod api;
82#[cfg(feature = "async")]
83pub mod r#async;
84#[cfg(feature = "blocking")]
85pub mod blocking;
86
87pub use api::*;
88#[cfg(feature = "blocking")]
89pub use blocking::BlockingClient;
90#[cfg(feature = "async")]
91pub use r#async::AsyncClient;
92
93/// Response status codes for which the request may be retried.
94pub const RETRYABLE_ERROR_CODES: [u16; 3] = [
95    429, // TOO_MANY_REQUESTS
96    500, // INTERNAL_SERVER_ERROR
97    503, // SERVICE_UNAVAILABLE
98];
99
100/// Base backoff in milliseconds.
101const BASE_BACKOFF_MILLIS: Duration = Duration::from_millis(256);
102
103/// Default max retries.
104const DEFAULT_MAX_RETRIES: usize = 6;
105
106/// Get a fee value in sats/vbytes from the estimates
107/// that matches the confirmation target set as parameter.
108///
109/// Returns `None` if no feerate estimate is found at or below `target`
110/// confirmations.
111pub fn convert_fee_rate(target: usize, estimates: HashMap<u16, f64>) -> Option<f32> {
112    estimates
113        .into_iter()
114        .filter(|(k, _)| *k as usize <= target)
115        .max_by_key(|(k, _)| *k)
116        .map(|(_, v)| v as f32)
117}
118
119/// A builder for an [`AsyncClient`] or [`BlockingClient`].
120#[derive(Debug, Clone)]
121pub struct Builder {
122    /// The URL of the Esplora server.
123    pub base_url: String,
124    /// Optional URL of the proxy to use to make requests to the Esplora server
125    ///
126    /// The string should be formatted as:
127    /// `<protocol>://<user>:<password>@host:<port>`.
128    ///
129    /// Note that the format of this value and the supported protocols change
130    /// slightly between the blocking version of the client (using `minreq`)
131    /// and the async version (using `reqwest`). For more details check with
132    /// the documentation of the two crates. Both of them are compiled with
133    /// the `socks` feature enabled.
134    ///
135    /// The proxy is ignored when targeting `wasm32`.
136    pub proxy: Option<String>,
137    /// Socket timeout.
138    pub timeout: Option<u64>,
139    /// HTTP headers to set on every request made to Esplora server.
140    pub headers: HashMap<String, String>,
141    /// Max retries
142    pub max_retries: usize,
143}
144
145impl Builder {
146    /// Instantiate a new builder
147    pub fn new(base_url: &str) -> Self {
148        Builder {
149            base_url: base_url.to_string(),
150            proxy: None,
151            timeout: None,
152            headers: HashMap::new(),
153            max_retries: DEFAULT_MAX_RETRIES,
154        }
155    }
156
157    /// Set the proxy of the builder
158    pub fn proxy(mut self, proxy: &str) -> Self {
159        self.proxy = Some(proxy.to_string());
160        self
161    }
162
163    /// Set the timeout of the builder
164    pub fn timeout(mut self, timeout: u64) -> Self {
165        self.timeout = Some(timeout);
166        self
167    }
168
169    /// Add a header to set on each request
170    pub fn header(mut self, key: &str, value: &str) -> Self {
171        self.headers.insert(key.to_string(), value.to_string());
172        self
173    }
174
175    /// Set the maximum number of times to retry a request if the response status
176    /// is one of [`RETRYABLE_ERROR_CODES`].
177    pub fn max_retries(mut self, count: usize) -> Self {
178        self.max_retries = count;
179        self
180    }
181
182    /// Build a blocking client from builder
183    #[cfg(feature = "blocking")]
184    pub fn build_blocking(self) -> BlockingClient {
185        BlockingClient::from_builder(self)
186    }
187
188    /// Build an asynchronous client from builder
189    #[cfg(all(feature = "async", feature = "tokio"))]
190    pub fn build_async(self) -> Result<AsyncClient, Error> {
191        AsyncClient::from_builder(self)
192    }
193
194    /// Build an asynchronous client from builder where the returned client uses a
195    /// user-defined [`Sleeper`].
196    #[cfg(feature = "async")]
197    pub fn build_async_with_sleeper<S: Sleeper>(self) -> Result<AsyncClient<S>, Error> {
198        AsyncClient::from_builder(self)
199    }
200}
201
202/// Errors that can happen during a request to `Esplora` servers.
203#[derive(Debug)]
204pub enum Error {
205    /// Error during `minreq` HTTP request
206    #[cfg(feature = "blocking")]
207    Minreq(::minreq::Error),
208    /// Error during reqwest HTTP request
209    #[cfg(feature = "async")]
210    Reqwest(::reqwest::Error),
211    /// HTTP response error
212    HttpResponse {
213        /// The HTTP status code returned by the server.
214        status: u16,
215        /// The error message content.
216        message: String,
217    },
218    /// Invalid number returned
219    Parsing(std::num::ParseIntError),
220    /// Invalid status code, unable to convert to `u16`
221    StatusCode(TryFromIntError),
222    /// Invalid Bitcoin data returned
223    BitcoinEncoding(bitcoin::consensus::encode::Error),
224    /// Invalid hex data returned (attempting to create an array)
225    HexToArray(bitcoin::hex::HexToArrayError),
226    /// Invalid hex data returned (attempting to create a vector)
227    HexToBytes(bitcoin::hex::HexToBytesError),
228    /// Transaction not found
229    TransactionNotFound(Txid),
230    /// Block Header height not found
231    HeaderHeightNotFound(u32),
232    /// Block Header hash not found
233    HeaderHashNotFound(BlockHash),
234    /// Invalid HTTP Header name specified
235    InvalidHttpHeaderName(String),
236    /// Invalid HTTP Header value specified
237    InvalidHttpHeaderValue(String),
238    /// The server sent an invalid response
239    InvalidResponse,
240}
241
242impl fmt::Display for Error {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        write!(f, "{self:?}")
245    }
246}
247
248macro_rules! impl_error {
249    ( $from:ty, $to:ident ) => {
250        impl_error!($from, $to, Error);
251    };
252    ( $from:ty, $to:ident, $impl_for:ty ) => {
253        impl std::convert::From<$from> for $impl_for {
254            fn from(err: $from) -> Self {
255                <$impl_for>::$to(err)
256            }
257        }
258    };
259}
260
261impl std::error::Error for Error {}
262#[cfg(feature = "blocking")]
263impl_error!(::minreq::Error, Minreq, Error);
264#[cfg(feature = "async")]
265impl_error!(::reqwest::Error, Reqwest, Error);
266impl_error!(std::num::ParseIntError, Parsing, Error);
267impl_error!(bitcoin::consensus::encode::Error, BitcoinEncoding, Error);
268impl_error!(bitcoin::hex::HexToArrayError, HexToArray, Error);
269impl_error!(bitcoin::hex::HexToBytesError, HexToBytes, Error);
270
271#[cfg(test)]
272mod test {
273    use super::*;
274    use electrsd::{corepc_node, ElectrsD};
275    use lazy_static::lazy_static;
276    use std::env;
277    use std::str::FromStr;
278    use tokio::sync::Mutex;
279    #[cfg(all(feature = "blocking", feature = "async"))]
280    use {
281        bitcoin::{hashes::Hash, Amount},
282        corepc_node::AddressType,
283        electrsd::electrum_client::ElectrumApi,
284        std::time::Duration,
285        tokio::sync::OnceCell,
286    };
287
288    lazy_static! {
289        static ref BITCOIND: corepc_node::Node = {
290            let bitcoind_exe = env::var("BITCOIND_EXE")
291                .ok()
292                .or_else(|| corepc_node::downloaded_exe_path().ok())
293                .expect(
294                    "you need to provide an env var BITCOIND_EXE or specify a bitcoind version feature",
295                );
296            let conf = corepc_node::Conf::default();
297            corepc_node::Node::with_conf(bitcoind_exe, &conf).unwrap()
298        };
299        static ref ELECTRSD: ElectrsD = {
300            let electrs_exe = env::var("ELECTRS_EXE")
301                .ok()
302                .or_else(electrsd::downloaded_exe_path)
303                .expect(
304                    "you need to provide env var ELECTRS_EXE or specify an electrsd version feature",
305                );
306            let mut conf = electrsd::Conf::default();
307            conf.http_enabled = true;
308            ElectrsD::with_conf(electrs_exe, &BITCOIND, &conf).unwrap()
309        };
310        static ref MINER: Mutex<()> = Mutex::new(());
311    }
312
313    #[cfg(all(feature = "blocking", feature = "async"))]
314    static PREMINE: OnceCell<()> = OnceCell::const_new();
315
316    #[cfg(all(feature = "blocking", feature = "async"))]
317    async fn setup_clients() -> (BlockingClient, AsyncClient) {
318        setup_clients_with_headers(HashMap::new()).await
319    }
320
321    #[cfg(all(feature = "blocking", feature = "async"))]
322    async fn setup_clients_with_headers(
323        headers: HashMap<String, String>,
324    ) -> (BlockingClient, AsyncClient) {
325        PREMINE
326            .get_or_init(|| async {
327                let _miner = MINER.lock().await;
328                generate_blocks_and_wait(101);
329            })
330            .await;
331
332        let esplora_url = ELECTRSD.esplora_url.as_ref().unwrap();
333
334        let mut builder = Builder::new(&format!("http://{esplora_url}"));
335        if !headers.is_empty() {
336            builder.headers = headers;
337        }
338
339        let blocking_client = builder.build_blocking();
340
341        let builder_async = Builder::new(&format!("http://{esplora_url}"));
342
343        #[cfg(feature = "tokio")]
344        let async_client = builder_async.build_async().unwrap();
345
346        #[cfg(not(feature = "tokio"))]
347        let async_client = builder_async
348            .build_async_with_sleeper::<r#async::DefaultSleeper>()
349            .unwrap();
350
351        (blocking_client, async_client)
352    }
353
354    #[cfg(all(feature = "blocking", feature = "async"))]
355    fn generate_blocks_and_wait(num: usize) {
356        let cur_height = BITCOIND.client.get_block_count().unwrap().0;
357        generate_blocks(num);
358        wait_for_block(cur_height as usize + num);
359    }
360
361    #[cfg(all(feature = "blocking", feature = "async"))]
362    fn generate_blocks(num: usize) {
363        let address = BITCOIND
364            .client
365            .new_address_with_type(AddressType::Legacy)
366            .unwrap();
367        let _block_hashes = BITCOIND.client.generate_to_address(num, &address).unwrap();
368    }
369
370    #[cfg(all(feature = "blocking", feature = "async"))]
371    fn wait_for_block(min_height: usize) {
372        let mut header = ELECTRSD.client.block_headers_subscribe().unwrap();
373        loop {
374            if header.height >= min_height {
375                break;
376            }
377            header = exponential_backoff_poll(|| {
378                ELECTRSD.trigger().unwrap();
379                ELECTRSD.client.ping().unwrap();
380                ELECTRSD.client.block_headers_pop().unwrap()
381            });
382        }
383    }
384
385    #[cfg(all(feature = "blocking", feature = "async"))]
386    fn exponential_backoff_poll<T, F>(mut poll: F) -> T
387    where
388        F: FnMut() -> Option<T>,
389    {
390        let mut delay = Duration::from_millis(64);
391        loop {
392            match poll() {
393                Some(data) => break data,
394                None if delay.as_millis() < 512 => delay = delay.mul_f32(2.0),
395                None => {}
396            }
397
398            std::thread::sleep(delay);
399        }
400    }
401
402    #[test]
403    fn feerate_parsing() {
404        let esplora_fees = serde_json::from_str::<HashMap<u16, f64>>(
405            r#"{
406  "25": 1.015,
407  "5": 2.3280000000000003,
408  "12": 2.0109999999999997,
409  "15": 1.018,
410  "17": 1.018,
411  "11": 2.0109999999999997,
412  "3": 3.01,
413  "2": 4.9830000000000005,
414  "6": 2.2359999999999998,
415  "21": 1.018,
416  "13": 1.081,
417  "7": 2.2359999999999998,
418  "8": 2.2359999999999998,
419  "16": 1.018,
420  "20": 1.018,
421  "22": 1.017,
422  "23": 1.017,
423  "504": 1,
424  "9": 2.2359999999999998,
425  "14": 1.018,
426  "10": 2.0109999999999997,
427  "24": 1.017,
428  "1008": 1,
429  "1": 4.9830000000000005,
430  "4": 2.3280000000000003,
431  "19": 1.018,
432  "144": 1,
433  "18": 1.018
434}
435"#,
436        )
437        .unwrap();
438        assert!(convert_fee_rate(1, HashMap::new()).is_none());
439        assert_eq!(convert_fee_rate(6, esplora_fees.clone()).unwrap(), 2.236);
440        assert_eq!(
441            convert_fee_rate(26, esplora_fees.clone()).unwrap(),
442            1.015,
443            "should inherit from value for 25"
444        );
445        assert!(
446            convert_fee_rate(0, esplora_fees).is_none(),
447            "should not return feerate for 0 target"
448        );
449    }
450
451    #[cfg(all(feature = "blocking", feature = "async"))]
452    #[tokio::test]
453    async fn test_get_tx() {
454        let (blocking_client, async_client) = setup_clients().await;
455
456        let address = BITCOIND
457            .client
458            .new_address_with_type(AddressType::Legacy)
459            .unwrap();
460        let txid = BITCOIND
461            .client
462            .send_to_address(&address, Amount::from_sat(1000))
463            .unwrap()
464            .txid()
465            .unwrap();
466        let _miner = MINER.lock().await;
467        generate_blocks_and_wait(1);
468
469        let tx = blocking_client.get_tx(&txid).unwrap();
470        let tx_async = async_client.get_tx(&txid).await.unwrap();
471        assert_eq!(tx, tx_async);
472    }
473
474    #[cfg(all(feature = "blocking", feature = "async"))]
475    #[tokio::test]
476    async fn test_get_tx_no_opt() {
477        let (blocking_client, async_client) = setup_clients().await;
478
479        let address = BITCOIND
480            .client
481            .new_address_with_type(AddressType::Legacy)
482            .unwrap();
483        let txid = BITCOIND
484            .client
485            .send_to_address(&address, Amount::from_sat(1000))
486            .unwrap()
487            .txid()
488            .unwrap();
489        let _miner = MINER.lock().await;
490        generate_blocks_and_wait(1);
491
492        let tx_no_opt = blocking_client.get_tx_no_opt(&txid).unwrap();
493        let tx_no_opt_async = async_client.get_tx_no_opt(&txid).await.unwrap();
494        assert_eq!(tx_no_opt, tx_no_opt_async);
495    }
496
497    #[cfg(all(feature = "blocking", feature = "async"))]
498    #[tokio::test]
499    async fn test_get_tx_status() {
500        let (blocking_client, async_client) = setup_clients().await;
501
502        let address = BITCOIND
503            .client
504            .new_address_with_type(AddressType::Legacy)
505            .unwrap();
506        let txid = BITCOIND
507            .client
508            .send_to_address(&address, Amount::from_sat(1000))
509            .unwrap()
510            .txid()
511            .unwrap();
512        let _miner = MINER.lock().await;
513        generate_blocks_and_wait(1);
514
515        let tx_status = blocking_client.get_tx_status(&txid).unwrap();
516        let tx_status_async = async_client.get_tx_status(&txid).await.unwrap();
517        assert_eq!(tx_status, tx_status_async);
518        assert!(tx_status.confirmed);
519
520        // Bogus txid returns a TxStatus with false, None, None, None
521        let txid = Txid::hash(b"ayyyy lmao");
522        let tx_status = blocking_client.get_tx_status(&txid).unwrap();
523        let tx_status_async = async_client.get_tx_status(&txid).await.unwrap();
524        assert_eq!(tx_status, tx_status_async);
525        assert!(!tx_status.confirmed);
526        assert!(tx_status.block_height.is_none());
527        assert!(tx_status.block_hash.is_none());
528        assert!(tx_status.block_time.is_none());
529    }
530
531    #[cfg(all(feature = "blocking", feature = "async"))]
532    #[tokio::test]
533    async fn test_get_tx_info() {
534        let (blocking_client, async_client) = setup_clients().await;
535
536        let address = BITCOIND
537            .client
538            .new_address_with_type(AddressType::Legacy)
539            .unwrap();
540        let txid = BITCOIND
541            .client
542            .send_to_address(&address, Amount::from_sat(1000))
543            .unwrap()
544            .txid()
545            .unwrap();
546        let _miner = MINER.lock().await;
547        generate_blocks_and_wait(1);
548
549        let tx_res = BITCOIND
550            .client
551            .get_transaction(txid)
552            .unwrap()
553            .into_model()
554            .unwrap();
555        let tx_exp: Transaction = tx_res.tx;
556        let tx_block_height = BITCOIND
557            .client
558            .get_block_header_verbose(&tx_res.block_hash.unwrap())
559            .unwrap()
560            .into_model()
561            .unwrap()
562            .height;
563
564        let tx_info = blocking_client
565            .get_tx_info(&txid)
566            .unwrap()
567            .expect("must get tx");
568        let tx_info_async = async_client
569            .get_tx_info(&txid)
570            .await
571            .unwrap()
572            .expect("must get tx");
573        assert_eq!(tx_info, tx_info_async);
574        assert_eq!(tx_info.txid, txid);
575        assert_eq!(tx_info.to_tx(), tx_exp);
576        assert_eq!(tx_info.size, tx_exp.total_size());
577        assert_eq!(tx_info.weight(), tx_exp.weight());
578        assert_eq!(tx_info.fee(), tx_res.fee.unwrap().unsigned_abs());
579        assert!(tx_info.status.confirmed);
580        assert_eq!(tx_info.status.block_height, Some(tx_block_height));
581        assert_eq!(tx_info.status.block_hash, tx_res.block_hash);
582        assert_eq!(
583            tx_info.status.block_time,
584            tx_res.block_time.map(|bt| bt as u64)
585        );
586
587        let txid = Txid::hash(b"not exist");
588        assert_eq!(blocking_client.get_tx_info(&txid).unwrap(), None);
589        assert_eq!(async_client.get_tx_info(&txid).await.unwrap(), None);
590    }
591
592    #[cfg(all(feature = "blocking", feature = "async"))]
593    #[tokio::test]
594    async fn test_get_header_by_hash() {
595        let (blocking_client, async_client) = setup_clients().await;
596
597        let block_hash = BITCOIND
598            .client
599            .get_block_hash(23)
600            .unwrap()
601            .block_hash()
602            .unwrap();
603
604        let block_header = blocking_client.get_header_by_hash(&block_hash).unwrap();
605        let block_header_async = async_client.get_header_by_hash(&block_hash).await.unwrap();
606        assert_eq!(block_header, block_header_async);
607    }
608
609    #[cfg(all(feature = "blocking", feature = "async"))]
610    #[tokio::test]
611    async fn test_get_block_status() {
612        let (blocking_client, async_client) = setup_clients().await;
613
614        let block_hash = BITCOIND
615            .client
616            .get_block_hash(21)
617            .unwrap()
618            .block_hash()
619            .unwrap();
620        let next_block_hash = BITCOIND
621            .client
622            .get_block_hash(22)
623            .unwrap()
624            .block_hash()
625            .unwrap();
626
627        let expected = BlockStatus {
628            in_best_chain: true,
629            height: Some(21),
630            next_best: Some(next_block_hash),
631        };
632
633        let block_status = blocking_client.get_block_status(&block_hash).unwrap();
634        let block_status_async = async_client.get_block_status(&block_hash).await.unwrap();
635        assert_eq!(expected, block_status);
636        assert_eq!(expected, block_status_async);
637    }
638
639    #[cfg(all(feature = "blocking", feature = "async"))]
640    #[tokio::test]
641    async fn test_get_non_existing_block_status() {
642        // Esplora returns the same status for orphaned blocks as for non-existing
643        // blocks: non-existing: https://blockstream.info/api/block/0000000000000000000000000000000000000000000000000000000000000000/status
644        // orphaned: https://blockstream.info/api/block/000000000000000000181b1a2354620f66868a723c0c4d5b24e4be8bdfc35a7f/status
645        // (Here the block is cited as orphaned: https://bitcoinchain.com/block_explorer/block/000000000000000000181b1a2354620f66868a723c0c4d5b24e4be8bdfc35a7f/ )
646        // For this reason, we only test for the non-existing case here.
647
648        let (blocking_client, async_client) = setup_clients().await;
649
650        let block_hash = BlockHash::all_zeros();
651
652        let expected = BlockStatus {
653            in_best_chain: false,
654            height: None,
655            next_best: None,
656        };
657
658        let block_status = blocking_client.get_block_status(&block_hash).unwrap();
659        let block_status_async = async_client.get_block_status(&block_hash).await.unwrap();
660        assert_eq!(expected, block_status);
661        assert_eq!(expected, block_status_async);
662    }
663
664    #[cfg(all(feature = "blocking", feature = "async"))]
665    #[tokio::test]
666    async fn test_get_block_by_hash() {
667        let (blocking_client, async_client) = setup_clients().await;
668
669        let block_hash = BITCOIND
670            .client
671            .get_block_hash(21)
672            .unwrap()
673            .block_hash()
674            .unwrap();
675
676        let expected = Some(BITCOIND.client.get_block(block_hash).unwrap());
677
678        let block = blocking_client.get_block_by_hash(&block_hash).unwrap();
679        let block_async = async_client.get_block_by_hash(&block_hash).await.unwrap();
680        assert_eq!(expected, block);
681        assert_eq!(expected, block_async);
682    }
683
684    #[cfg(all(feature = "blocking", feature = "async"))]
685    #[tokio::test]
686    async fn test_that_errors_are_propagated() {
687        let (blocking_client, async_client) = setup_clients().await;
688
689        let address = BITCOIND
690            .client
691            .new_address_with_type(AddressType::Legacy)
692            .unwrap();
693        let txid = BITCOIND
694            .client
695            .send_to_address(&address, Amount::from_sat(1000))
696            .unwrap()
697            .txid()
698            .unwrap();
699        let _miner = MINER.lock().await;
700        generate_blocks_and_wait(1);
701
702        let tx = blocking_client.get_tx(&txid).unwrap();
703        let async_res = async_client.broadcast(tx.as_ref().unwrap()).await;
704        let blocking_res = blocking_client.broadcast(tx.as_ref().unwrap());
705        assert!(async_res.is_err());
706        assert!(matches!(
707            async_res.unwrap_err(),
708            Error::HttpResponse { status: 400, message } if message.contains("-27")
709        ));
710        assert!(blocking_res.is_err());
711        assert!(matches!(
712            blocking_res.unwrap_err(),
713            Error::HttpResponse { status: 400, message } if message.contains("-27")
714        ));
715    }
716
717    #[cfg(all(feature = "blocking", feature = "async"))]
718    #[tokio::test]
719    async fn test_get_block_by_hash_not_existing() {
720        let (blocking_client, async_client) = setup_clients().await;
721
722        let block = blocking_client
723            .get_block_by_hash(&BlockHash::all_zeros())
724            .unwrap();
725        let block_async = async_client
726            .get_block_by_hash(&BlockHash::all_zeros())
727            .await
728            .unwrap();
729        assert!(block.is_none());
730        assert!(block_async.is_none());
731    }
732
733    #[cfg(all(feature = "blocking", feature = "async"))]
734    #[tokio::test]
735    async fn test_get_merkle_proof() {
736        let (blocking_client, async_client) = setup_clients().await;
737
738        let address = BITCOIND
739            .client
740            .new_address_with_type(AddressType::Legacy)
741            .unwrap();
742        let txid = BITCOIND
743            .client
744            .send_to_address(&address, Amount::from_sat(1000))
745            .unwrap()
746            .txid()
747            .unwrap();
748        let _miner = MINER.lock().await;
749        generate_blocks_and_wait(1);
750
751        let merkle_proof = blocking_client.get_merkle_proof(&txid).unwrap().unwrap();
752        let merkle_proof_async = async_client.get_merkle_proof(&txid).await.unwrap().unwrap();
753        assert_eq!(merkle_proof, merkle_proof_async);
754        assert!(merkle_proof.pos > 0);
755    }
756
757    #[cfg(all(feature = "blocking", feature = "async"))]
758    #[tokio::test]
759    async fn test_get_merkle_block() {
760        let (blocking_client, async_client) = setup_clients().await;
761
762        let address = BITCOIND
763            .client
764            .new_address_with_type(AddressType::Legacy)
765            .unwrap();
766        let txid = BITCOIND
767            .client
768            .send_to_address(&address, Amount::from_sat(1000))
769            .unwrap()
770            .txid()
771            .unwrap();
772        let _miner = MINER.lock().await;
773        generate_blocks_and_wait(1);
774
775        let merkle_block = blocking_client.get_merkle_block(&txid).unwrap().unwrap();
776        let merkle_block_async = async_client.get_merkle_block(&txid).await.unwrap().unwrap();
777        assert_eq!(merkle_block, merkle_block_async);
778
779        let mut matches = vec![txid];
780        let mut indexes = vec![];
781        let root = merkle_block
782            .txn
783            .extract_matches(&mut matches, &mut indexes)
784            .unwrap();
785        assert_eq!(root, merkle_block.header.merkle_root);
786        assert_eq!(indexes.len(), 1);
787        assert!(indexes[0] > 0);
788    }
789
790    #[cfg(all(feature = "blocking", feature = "async"))]
791    #[tokio::test]
792    async fn test_get_output_status() {
793        let (blocking_client, async_client) = setup_clients().await;
794
795        let address = BITCOIND
796            .client
797            .new_address_with_type(AddressType::Legacy)
798            .unwrap();
799        let txid = BITCOIND
800            .client
801            .send_to_address(&address, Amount::from_sat(1000))
802            .unwrap()
803            .txid()
804            .unwrap();
805        let _miner = MINER.lock().await;
806        generate_blocks_and_wait(1);
807
808        let output_status = blocking_client
809            .get_output_status(&txid, 1)
810            .unwrap()
811            .unwrap();
812        let output_status_async = async_client
813            .get_output_status(&txid, 1)
814            .await
815            .unwrap()
816            .unwrap();
817
818        assert_eq!(output_status, output_status_async);
819    }
820
821    #[cfg(all(feature = "blocking", feature = "async"))]
822    #[tokio::test]
823    async fn test_get_height() {
824        let (blocking_client, async_client) = setup_clients().await;
825        let block_height = blocking_client.get_height().unwrap();
826        let block_height_async = async_client.get_height().await.unwrap();
827        assert!(block_height > 0);
828        assert_eq!(block_height, block_height_async);
829    }
830
831    #[cfg(all(feature = "blocking", feature = "async"))]
832    #[tokio::test]
833    async fn test_get_tip_hash() {
834        let (blocking_client, async_client) = setup_clients().await;
835        let tip_hash = blocking_client.get_tip_hash().unwrap();
836        let tip_hash_async = async_client.get_tip_hash().await.unwrap();
837        assert_eq!(tip_hash, tip_hash_async);
838    }
839
840    #[cfg(all(feature = "blocking", feature = "async"))]
841    #[tokio::test]
842    async fn test_get_block_hash() {
843        let (blocking_client, async_client) = setup_clients().await;
844
845        let block_hash = BITCOIND
846            .client
847            .get_block_hash(21)
848            .unwrap()
849            .block_hash()
850            .unwrap();
851
852        let block_hash_blocking = blocking_client.get_block_hash(21).unwrap();
853        let block_hash_async = async_client.get_block_hash(21).await.unwrap();
854        assert_eq!(block_hash, block_hash_blocking);
855        assert_eq!(block_hash, block_hash_async);
856    }
857
858    #[cfg(all(feature = "blocking", feature = "async"))]
859    #[tokio::test]
860    async fn test_get_txid_at_block_index() {
861        let (blocking_client, async_client) = setup_clients().await;
862
863        let block_hash = BITCOIND
864            .client
865            .get_block_hash(23)
866            .unwrap()
867            .block_hash()
868            .unwrap();
869
870        let txid_at_block_index = blocking_client
871            .get_txid_at_block_index(&block_hash, 0)
872            .unwrap()
873            .unwrap();
874        let txid_at_block_index_async = async_client
875            .get_txid_at_block_index(&block_hash, 0)
876            .await
877            .unwrap()
878            .unwrap();
879        assert_eq!(txid_at_block_index, txid_at_block_index_async);
880    }
881
882    #[cfg(all(feature = "blocking", feature = "async"))]
883    #[tokio::test]
884    async fn test_get_fee_estimates() {
885        let (blocking_client, async_client) = setup_clients().await;
886        let fee_estimates = blocking_client.get_fee_estimates().unwrap();
887        let fee_estimates_async = async_client.get_fee_estimates().await.unwrap();
888        assert_eq!(fee_estimates.len(), fee_estimates_async.len());
889    }
890
891    #[cfg(all(feature = "blocking", feature = "async"))]
892    #[tokio::test]
893    async fn test_scripthash_txs() {
894        let (blocking_client, async_client) = setup_clients().await;
895
896        let address = BITCOIND
897            .client
898            .new_address_with_type(AddressType::Legacy)
899            .unwrap();
900        let txid = BITCOIND
901            .client
902            .send_to_address(&address, Amount::from_sat(1000))
903            .unwrap()
904            .txid()
905            .unwrap();
906        let _miner = MINER.lock().await;
907        generate_blocks_and_wait(1);
908
909        let expected_tx = BITCOIND
910            .client
911            .get_transaction(txid)
912            .unwrap()
913            .into_model()
914            .unwrap()
915            .tx;
916        let script = &expected_tx.output[0].script_pubkey;
917        let scripthash_txs_txids: Vec<Txid> = blocking_client
918            .scripthash_txs(script, None)
919            .unwrap()
920            .iter()
921            .map(|tx| tx.txid)
922            .collect();
923        let scripthash_txs_txids_async: Vec<Txid> = async_client
924            .scripthash_txs(script, None)
925            .await
926            .unwrap()
927            .iter()
928            .map(|tx| tx.txid)
929            .collect();
930        assert_eq!(scripthash_txs_txids, scripthash_txs_txids_async);
931    }
932
933    #[cfg(all(feature = "blocking", feature = "async"))]
934    #[tokio::test]
935    async fn test_get_block_info() {
936        let (blocking_client, async_client) = setup_clients().await;
937
938        // Genesis block `BlockHash` on regtest.
939        let blockhash_genesis =
940            BlockHash::from_str("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")
941                .unwrap();
942
943        let block_info_blocking = blocking_client.get_block_info(&blockhash_genesis).unwrap();
944        let block_info_async = async_client
945            .get_block_info(&blockhash_genesis)
946            .await
947            .unwrap();
948
949        assert_eq!(block_info_async, block_info_blocking);
950        assert_eq!(block_info_async.id, blockhash_genesis);
951        assert_eq!(block_info_async.height, 0);
952        assert_eq!(block_info_async.previousblockhash, None);
953    }
954
955    #[cfg(all(feature = "blocking", feature = "async"))]
956    #[tokio::test]
957    async fn test_get_block_txids() {
958        let (blocking_client, async_client) = setup_clients().await;
959
960        let address = BITCOIND
961            .client
962            .new_address_with_type(AddressType::Legacy)
963            .unwrap();
964
965        // Create 5 transactions and mine a block.
966        let txids: Vec<_> = (0..5)
967            .map(|_| {
968                BITCOIND
969                    .client
970                    .send_to_address(&address, Amount::from_sat(1000))
971                    .unwrap()
972                    .txid()
973                    .unwrap()
974            })
975            .collect();
976
977        let _miner = MINER.lock().await;
978        generate_blocks_and_wait(1);
979
980        // Get the block hash at the chain's tip.
981        let blockhash = blocking_client.get_tip_hash().unwrap();
982
983        let txids_async = async_client.get_block_txids(&blockhash).await.unwrap();
984        let txids_blocking = blocking_client.get_block_txids(&blockhash).unwrap();
985
986        assert_eq!(txids_async, txids_blocking);
987
988        // Compare expected and received (skipping the coinbase TXID).
989        for expected_txid in txids.iter() {
990            assert!(txids_async.contains(expected_txid));
991        }
992    }
993
994    #[cfg(all(feature = "blocking", feature = "async"))]
995    #[tokio::test]
996    async fn test_get_block_txs() {
997        let (blocking_client, async_client) = setup_clients().await;
998
999        let _miner = MINER.lock().await;
1000        let blockhash = blocking_client.get_tip_hash().unwrap();
1001
1002        let txs_blocking = blocking_client.get_block_txs(&blockhash, None).unwrap();
1003        let txs_async = async_client.get_block_txs(&blockhash, None).await.unwrap();
1004
1005        assert_ne!(txs_blocking.len(), 0);
1006        assert_eq!(txs_blocking.len(), txs_async.len());
1007    }
1008
1009    #[cfg(all(feature = "blocking", feature = "async"))]
1010    #[tokio::test]
1011    async fn test_get_blocks() {
1012        let (blocking_client, async_client) = setup_clients().await;
1013        let start_height = BITCOIND.client.get_block_count().unwrap().0;
1014        let blocks1 = blocking_client.get_blocks(None).unwrap();
1015        let blocks_async1 = async_client.get_blocks(None).await.unwrap();
1016        assert_eq!(blocks1[0].time.height, start_height as u32);
1017        assert_eq!(blocks1, blocks_async1);
1018        generate_blocks_and_wait(10);
1019        let blocks2 = blocking_client.get_blocks(None).unwrap();
1020        let blocks_async2 = async_client.get_blocks(None).await.unwrap();
1021        assert_eq!(blocks2, blocks_async2);
1022        assert_ne!(blocks2, blocks1);
1023        let blocks3 = blocking_client
1024            .get_blocks(Some(start_height as u32))
1025            .unwrap();
1026        let blocks_async3 = async_client
1027            .get_blocks(Some(start_height as u32))
1028            .await
1029            .unwrap();
1030        assert_eq!(blocks3, blocks_async3);
1031        assert_eq!(blocks3[0].time.height, start_height as u32);
1032        assert_eq!(blocks3, blocks1);
1033        let blocks_genesis = blocking_client.get_blocks(Some(0)).unwrap();
1034        let blocks_genesis_async = async_client.get_blocks(Some(0)).await.unwrap();
1035        assert_eq!(blocks_genesis, blocks_genesis_async);
1036    }
1037
1038    #[cfg(all(feature = "blocking", feature = "async"))]
1039    #[tokio::test]
1040    async fn test_get_tx_with_http_header() {
1041        let headers = [(
1042            "Authorization".to_string(),
1043            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_string(),
1044        )]
1045        .into();
1046        let (blocking_client, async_client) = setup_clients_with_headers(headers).await;
1047
1048        let address = BITCOIND
1049            .client
1050            .new_address_with_type(AddressType::Legacy)
1051            .unwrap();
1052        let txid = BITCOIND
1053            .client
1054            .send_to_address(&address, Amount::from_sat(1000))
1055            .unwrap()
1056            .txid()
1057            .unwrap();
1058        let _miner = MINER.lock().await;
1059        generate_blocks_and_wait(1);
1060
1061        let tx = blocking_client.get_tx(&txid).unwrap();
1062        let tx_async = async_client.get_tx(&txid).await.unwrap();
1063        assert_eq!(tx, tx_async);
1064    }
1065
1066    #[cfg(all(feature = "blocking", feature = "async"))]
1067    #[tokio::test]
1068    async fn test_get_address_stats() {
1069        let (blocking_client, async_client) = setup_clients().await;
1070
1071        let address = BITCOIND
1072            .client
1073            .new_address_with_type(AddressType::Legacy)
1074            .unwrap();
1075
1076        let _txid = BITCOIND
1077            .client
1078            .send_to_address(&address, Amount::from_sat(1000))
1079            .unwrap()
1080            .txid()
1081            .unwrap();
1082
1083        let address_stats_blocking = blocking_client.get_address_stats(&address).unwrap();
1084        let address_stats_async = async_client.get_address_stats(&address).await.unwrap();
1085        assert_eq!(address_stats_blocking, address_stats_async);
1086        assert_eq!(address_stats_async.chain_stats.funded_txo_count, 0);
1087
1088        let _miner = MINER.lock().await;
1089        generate_blocks_and_wait(1);
1090
1091        let address_stats_blocking = blocking_client.get_address_stats(&address).unwrap();
1092        let address_stats_async = async_client.get_address_stats(&address).await.unwrap();
1093        assert_eq!(address_stats_blocking, address_stats_async);
1094        assert_eq!(address_stats_async.chain_stats.funded_txo_count, 1);
1095        assert_eq!(address_stats_async.chain_stats.funded_txo_sum, 1000);
1096    }
1097
1098    #[cfg(all(feature = "blocking", feature = "async"))]
1099    #[tokio::test]
1100    async fn test_get_scripthash_stats() {
1101        let (blocking_client, async_client) = setup_clients().await;
1102
1103        // Create an address of each type.
1104        let address_legacy = BITCOIND
1105            .client
1106            .new_address_with_type(AddressType::Legacy)
1107            .unwrap();
1108        let address_p2sh_segwit = BITCOIND
1109            .client
1110            .new_address_with_type(AddressType::P2shSegwit)
1111            .unwrap();
1112        let address_bech32 = BITCOIND
1113            .client
1114            .new_address_with_type(AddressType::Bech32)
1115            .unwrap();
1116        let address_bech32m = BITCOIND
1117            .client
1118            .new_address_with_type(AddressType::Bech32m)
1119            .unwrap();
1120
1121        // Send a transaction to each address.
1122        let _txid = BITCOIND
1123            .client
1124            .send_to_address(&address_legacy, Amount::from_sat(1000))
1125            .unwrap()
1126            .txid()
1127            .unwrap();
1128        let _txid = BITCOIND
1129            .client
1130            .send_to_address(&address_p2sh_segwit, Amount::from_sat(1000))
1131            .unwrap()
1132            .txid()
1133            .unwrap();
1134        let _txid = BITCOIND
1135            .client
1136            .send_to_address(&address_bech32, Amount::from_sat(1000))
1137            .unwrap()
1138            .txid()
1139            .unwrap();
1140        let _txid = BITCOIND
1141            .client
1142            .send_to_address(&address_bech32m, Amount::from_sat(1000))
1143            .unwrap()
1144            .txid()
1145            .unwrap();
1146
1147        let _miner = MINER.lock().await;
1148        generate_blocks_and_wait(1);
1149
1150        // Derive each addresses script.
1151        let script_legacy = address_legacy.script_pubkey();
1152        let script_p2sh_segwit = address_p2sh_segwit.script_pubkey();
1153        let script_bech32 = address_bech32.script_pubkey();
1154        let script_bech32m = address_bech32m.script_pubkey();
1155
1156        // P2PKH
1157        let scripthash_stats_blocking_legacy = blocking_client
1158            .get_scripthash_stats(&script_legacy)
1159            .unwrap();
1160        let scripthash_stats_async_legacy = async_client
1161            .get_scripthash_stats(&script_legacy)
1162            .await
1163            .unwrap();
1164        assert_eq!(
1165            scripthash_stats_blocking_legacy,
1166            scripthash_stats_async_legacy
1167        );
1168        assert_eq!(
1169            scripthash_stats_blocking_legacy.chain_stats.funded_txo_sum,
1170            1000
1171        );
1172        assert_eq!(scripthash_stats_blocking_legacy.chain_stats.tx_count, 1);
1173
1174        // P2SH-P2WSH
1175        let scripthash_stats_blocking_p2sh_segwit = blocking_client
1176            .get_scripthash_stats(&script_p2sh_segwit)
1177            .unwrap();
1178        let scripthash_stats_async_p2sh_segwit = async_client
1179            .get_scripthash_stats(&script_p2sh_segwit)
1180            .await
1181            .unwrap();
1182        assert_eq!(
1183            scripthash_stats_blocking_p2sh_segwit,
1184            scripthash_stats_async_p2sh_segwit
1185        );
1186        assert_eq!(
1187            scripthash_stats_blocking_p2sh_segwit
1188                .chain_stats
1189                .funded_txo_sum,
1190            1000
1191        );
1192        assert_eq!(
1193            scripthash_stats_blocking_p2sh_segwit.chain_stats.tx_count,
1194            1
1195        );
1196
1197        // P2WPKH / P2WSH
1198        let scripthash_stats_blocking_bech32 = blocking_client
1199            .get_scripthash_stats(&script_bech32)
1200            .unwrap();
1201        let scripthash_stats_async_bech32 = async_client
1202            .get_scripthash_stats(&script_bech32)
1203            .await
1204            .unwrap();
1205        assert_eq!(
1206            scripthash_stats_blocking_bech32,
1207            scripthash_stats_async_bech32
1208        );
1209        assert_eq!(
1210            scripthash_stats_blocking_bech32.chain_stats.funded_txo_sum,
1211            1000
1212        );
1213        assert_eq!(scripthash_stats_blocking_bech32.chain_stats.tx_count, 1);
1214
1215        // P2TR
1216        let scripthash_stats_blocking_bech32m = blocking_client
1217            .get_scripthash_stats(&script_bech32m)
1218            .unwrap();
1219        let scripthash_stats_async_bech32m = async_client
1220            .get_scripthash_stats(&script_bech32m)
1221            .await
1222            .unwrap();
1223        assert_eq!(
1224            scripthash_stats_blocking_bech32m,
1225            scripthash_stats_async_bech32m
1226        );
1227        assert_eq!(
1228            scripthash_stats_blocking_bech32m.chain_stats.funded_txo_sum,
1229            1000
1230        );
1231        assert_eq!(scripthash_stats_blocking_bech32m.chain_stats.tx_count, 1);
1232    }
1233
1234    #[cfg(all(feature = "blocking", feature = "async"))]
1235    #[tokio::test]
1236    async fn test_get_address_txs() {
1237        let (blocking_client, async_client) = setup_clients().await;
1238
1239        let address = BITCOIND
1240            .client
1241            .new_address_with_type(AddressType::Legacy)
1242            .unwrap();
1243
1244        let txid = BITCOIND
1245            .client
1246            .send_to_address(&address, Amount::from_sat(1000))
1247            .unwrap()
1248            .txid()
1249            .unwrap();
1250
1251        let _miner = MINER.lock().await;
1252        generate_blocks_and_wait(1);
1253
1254        let address_txs_blocking = blocking_client.get_address_txs(&address, None).unwrap();
1255        let address_txs_async = async_client.get_address_txs(&address, None).await.unwrap();
1256
1257        assert_eq!(address_txs_blocking, address_txs_async);
1258        assert_eq!(address_txs_async[0].txid, txid);
1259    }
1260
1261    #[cfg(all(feature = "blocking", feature = "async"))]
1262    #[tokio::test]
1263    async fn test_get_address_utxos() {
1264        let (blocking_client, async_client) = setup_clients().await;
1265
1266        let address = BITCOIND
1267            .client
1268            .new_address_with_type(AddressType::Legacy)
1269            .unwrap();
1270
1271        let _txid = BITCOIND
1272            .client
1273            .send_to_address(&address, Amount::from_sat(21000))
1274            .unwrap()
1275            .txid()
1276            .unwrap();
1277
1278        let _miner = MINER.lock().await;
1279        generate_blocks_and_wait(1);
1280
1281        let address_utxos_blocking = blocking_client.get_address_utxos(&address).unwrap();
1282        let address_utxos_async = async_client.get_address_utxos(&address).await.unwrap();
1283
1284        assert_ne!(address_utxos_blocking.len(), 0);
1285        assert_ne!(address_utxos_async.len(), 0);
1286        assert_eq!(address_utxos_blocking, address_utxos_async);
1287    }
1288
1289    #[cfg(all(feature = "blocking", feature = "async"))]
1290    #[tokio::test]
1291    async fn test_get_scripthash_utxos() {
1292        let (blocking_client, async_client) = setup_clients().await;
1293
1294        let address = BITCOIND
1295            .client
1296            .new_address_with_type(AddressType::Legacy)
1297            .unwrap();
1298        let script = address.script_pubkey();
1299
1300        let _txid = BITCOIND
1301            .client
1302            .send_to_address(&address, Amount::from_sat(21000))
1303            .unwrap()
1304            .txid()
1305            .unwrap();
1306
1307        let _miner = MINER.lock().await;
1308        generate_blocks_and_wait(1);
1309
1310        let scripthash_utxos_blocking = blocking_client.get_scripthash_utxos(&script).unwrap();
1311        let scripthash_utxos_async = async_client.get_scripthash_utxos(&script).await.unwrap();
1312
1313        assert_ne!(scripthash_utxos_blocking.len(), 0);
1314        assert_ne!(scripthash_utxos_async.len(), 0);
1315        assert_eq!(scripthash_utxos_blocking, scripthash_utxos_async);
1316    }
1317
1318    #[cfg(all(feature = "blocking", feature = "async"))]
1319    #[tokio::test]
1320    async fn test_get_tx_outspends() {
1321        let (blocking_client, async_client) = setup_clients().await;
1322
1323        let address = BITCOIND
1324            .client
1325            .new_address_with_type(AddressType::Legacy)
1326            .unwrap();
1327
1328        let txid = BITCOIND
1329            .client
1330            .send_to_address(&address, Amount::from_sat(21000))
1331            .unwrap()
1332            .txid()
1333            .unwrap();
1334
1335        let _miner = MINER.lock().await;
1336        generate_blocks_and_wait(1);
1337
1338        let outspends_blocking = blocking_client.get_tx_outspends(&txid).unwrap();
1339        let outspends_async = async_client.get_tx_outspends(&txid).await.unwrap();
1340
1341        // Assert that there are 2 outputs: 21K sat and (coinbase - 21K sat).
1342        assert_eq!(outspends_blocking.len(), 2);
1343        assert_eq!(outspends_async.len(), 2);
1344        assert_eq!(outspends_blocking, outspends_async);
1345
1346        // Assert that both outputs are returned as unspent (spent == false).
1347        assert!(outspends_blocking.iter().all(|output| !output.spent));
1348    }
1349
1350    #[cfg(all(feature = "blocking", feature = "async"))]
1351    #[tokio::test]
1352    async fn test_mempool_methods() {
1353        let (blocking_client, async_client) = setup_clients().await;
1354
1355        let address = BITCOIND
1356            .client
1357            .new_address_with_type(AddressType::Legacy)
1358            .unwrap();
1359
1360        for _ in 0..5 {
1361            let _txid = BITCOIND
1362                .client
1363                .send_to_address(&address, Amount::from_sat(1000))
1364                .unwrap()
1365                .txid()
1366                .unwrap();
1367        }
1368
1369        // Wait for transactions to propagate to electrs' mempool.
1370        tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
1371
1372        // Test `get_mempool_stats`
1373        let stats_blocking = blocking_client.get_mempool_stats().unwrap();
1374        let stats_async = async_client.get_mempool_stats().await.unwrap();
1375        assert_eq!(stats_blocking, stats_async);
1376        assert!(stats_blocking.count >= 5);
1377
1378        // Test `get_mempool_recent_txs`
1379        let recent_blocking = blocking_client.get_mempool_recent_txs().unwrap();
1380        let recent_async = async_client.get_mempool_recent_txs().await.unwrap();
1381        assert_eq!(recent_blocking, recent_async);
1382        assert!(recent_blocking.len() <= 10);
1383        assert!(!recent_blocking.is_empty());
1384
1385        // Test `get_mempool_txids`
1386        let txids_blocking = blocking_client.get_mempool_txids().unwrap();
1387        let txids_async = async_client.get_mempool_txids().await.unwrap();
1388        assert_eq!(txids_blocking, txids_async);
1389        assert!(txids_blocking.len() >= 5);
1390
1391        // Test `get_mempool_scripthash_txs`
1392        let script = address.script_pubkey();
1393        let scripthash_txs_blocking = blocking_client.get_mempool_scripthash_txs(&script).unwrap();
1394        let scripthash_txs_async = async_client
1395            .get_mempool_scripthash_txs(&script)
1396            .await
1397            .unwrap();
1398        assert_eq!(scripthash_txs_blocking, scripthash_txs_async);
1399        assert_eq!(scripthash_txs_blocking.len(), 5);
1400
1401        // Test `get_mempool_address_txs`
1402        let mempool_address_txs_blocking =
1403            blocking_client.get_mempool_address_txs(&address).unwrap();
1404        let mempool_address_txs_async = async_client
1405            .get_mempool_address_txs(&address)
1406            .await
1407            .unwrap();
1408        assert_eq!(mempool_address_txs_blocking, mempool_address_txs_async);
1409        assert_eq!(mempool_address_txs_blocking.len(), 5);
1410    }
1411}