bdk/blockchain/esplora/
mod.rs

1//! Esplora
2//!
3//! This module defines a [`EsploraBlockchain`] struct that can query an Esplora
4//! backend populate the wallet's [database](crate::database::Database) by:
5//!
6//! ## Example
7//!
8//! ```no_run
9//! # use bdk::blockchain::esplora::EsploraBlockchain;
10//! let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/api", 20);
11//! # Ok::<(), bdk::Error>(())
12//! ```
13//!
14//! Esplora blockchain can use either `ureq` or `reqwest` for the HTTP client
15//! depending on your needs (blocking or async respectively).
16//!
17//! Please note, to configure the Esplora HTTP client correctly use one of:
18//! Blocking:  --features='use-esplora-blocking'
19//! Async:     --features='async-interface,use-esplora-async' --no-default-features
20
21pub use esplora_client::Error as EsploraError;
22
23#[cfg(feature = "use-esplora-async")]
24mod r#async;
25
26#[cfg(feature = "use-esplora-async")]
27pub use self::r#async::*;
28
29#[cfg(feature = "use-esplora-blocking")]
30mod blocking;
31
32#[cfg(feature = "use-esplora-blocking")]
33pub use self::blocking::*;
34
35/// Configuration for an [`EsploraBlockchain`]
36#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
37pub struct EsploraBlockchainConfig {
38    /// Base URL of the esplora service
39    ///
40    /// eg. `https://blockstream.info/api/`
41    pub base_url: String,
42    /// Optional URL of the proxy to use to make requests to the Esplora server
43    ///
44    /// The string should be formatted as: `<protocol>://<user>:<password>@host:<port>`.
45    ///
46    /// Note that the format of this value and the supported protocols change slightly between the
47    /// sync version of esplora (using `ureq`) and the async version (using `reqwest`). For more
48    /// details check with the documentation of the two crates. Both of them are compiled with
49    /// the `socks` feature enabled.
50    ///
51    /// The proxy is ignored when targeting `wasm32`.
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub proxy: Option<String>,
54    /// Number of parallel requests sent to the esplora service (default: 4)
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub concurrency: Option<u8>,
57    /// Stop searching addresses for transactions after finding an unused gap of this length.
58    pub stop_gap: usize,
59    /// Socket timeout.
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub timeout: Option<u64>,
62}
63
64impl EsploraBlockchainConfig {
65    /// create a config with default values given the base url and stop gap
66    pub fn new(base_url: String, stop_gap: usize) -> Self {
67        Self {
68            base_url,
69            proxy: None,
70            timeout: None,
71            stop_gap,
72            concurrency: None,
73        }
74    }
75}
76
77impl From<esplora_client::BlockTime> for crate::BlockTime {
78    fn from(esplora_client::BlockTime { timestamp, height }: esplora_client::BlockTime) -> Self {
79        Self { timestamp, height }
80    }
81}
82
83#[cfg(test)]
84#[cfg(feature = "test-esplora")]
85crate::bdk_blockchain_tests! {
86    fn test_instance(test_client: &TestClient) -> EsploraBlockchain {
87        EsploraBlockchain::new(&format!("http://{}",test_client.electrsd.esplora_url.as_ref().unwrap()), 20)
88    }
89}
90
91const DEFAULT_CONCURRENT_REQUESTS: u8 = 4;
92
93#[cfg(test)]
94mod test {
95    #[test]
96    #[cfg(feature = "test-esplora")]
97    fn test_esplora_with_variable_configs() {
98        use super::*;
99
100        use crate::testutils::{
101            blockchain_tests::TestClient,
102            configurable_blockchain_tests::ConfigurableBlockchainTester,
103        };
104
105        struct EsploraTester;
106
107        impl ConfigurableBlockchainTester<EsploraBlockchain> for EsploraTester {
108            const BLOCKCHAIN_NAME: &'static str = "Esplora";
109
110            fn config_with_stop_gap(
111                &self,
112                test_client: &mut TestClient,
113                stop_gap: usize,
114            ) -> Option<EsploraBlockchainConfig> {
115                Some(EsploraBlockchainConfig {
116                    base_url: format!(
117                        "http://{}",
118                        test_client.electrsd.esplora_url.as_ref().unwrap()
119                    ),
120                    proxy: None,
121                    concurrency: None,
122                    stop_gap: stop_gap,
123                    timeout: None,
124                })
125            }
126        }
127
128        EsploraTester.run();
129    }
130}