Skip to content

Commit de54e71

Browse files
committed
refactor(esplora_ext): rename scan_txs to sync and scan_txs_with_keychains to full_scan
removed txids and outpoints params from full_scan
1 parent 95d3485 commit de54e71

File tree

9 files changed

+124
-147
lines changed

9 files changed

+124
-147
lines changed

crates/chain/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! This crate is a collection of core structures for [Bitcoin Dev Kit] (alpha release).
1+
//! This crate is a collection of core structures for [Bitcoin Dev Kit].
22
//!
33
//! The goal of this crate is to give wallets the mechanisms needed to:
44
//!

crates/esplora/src/async_ext.rs

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -36,58 +36,45 @@ pub trait EsploraAsyncExt {
3636
request_heights: impl IntoIterator<IntoIter = impl Iterator<Item = u32> + Send> + Send,
3737
) -> Result<local_chain::Update, Error>;
3838

39-
/// Scan Esplora for the data specified and return a [`TxGraph`] and a map of last active
40-
/// indices.
39+
/// Full scan the keychain scripts specified with the blockchain (via an Esplora client) and
40+
/// returns a [`TxGraph`] and a map of last active indices.
4141
///
4242
/// * `keychain_spks`: keychains that we want to scan transactions for
43-
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
44-
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
45-
/// want to include in the update
4643
///
47-
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
44+
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
4845
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
4946
/// parallel.
5047
#[allow(clippy::result_large_err)]
51-
async fn scan_txs_with_keychains<K: Ord + Clone + Send>(
48+
async fn full_scan<K: Ord + Clone + Send>(
5249
&self,
5350
keychain_spks: BTreeMap<
5451
K,
5552
impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,
5653
>,
57-
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
58-
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
5954
stop_gap: usize,
6055
parallel_requests: usize,
6156
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error>;
6257

63-
/// Convenience method to call [`scan_txs_with_keychains`] without requiring a keychain.
58+
/// Sync a set of scripts with the blockchain (via an Esplora client) for the data
59+
/// specified and return a [`TxGraph`].
6460
///
65-
/// [`scan_txs_with_keychains`]: EsploraAsyncExt::scan_txs_with_keychains
61+
/// * `misc_spks`: scripts that we want to sync transactions for
62+
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
63+
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
64+
/// want to include in the update
65+
///
66+
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
67+
/// may include scripts that have been used, use [`full_scan`] with the keychain.
68+
///
69+
/// [`full_scan`]: EsploraAsyncExt::full_scan
6670
#[allow(clippy::result_large_err)]
67-
async fn scan_txs(
71+
async fn sync(
6872
&self,
6973
misc_spks: impl IntoIterator<IntoIter = impl Iterator<Item = ScriptBuf> + Send> + Send,
7074
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
7175
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
7276
parallel_requests: usize,
73-
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
74-
self.scan_txs_with_keychains(
75-
[(
76-
(),
77-
misc_spks
78-
.into_iter()
79-
.enumerate()
80-
.map(|(i, spk)| (i as u32, spk)),
81-
)]
82-
.into(),
83-
txids,
84-
outpoints,
85-
usize::MAX,
86-
parallel_requests,
87-
)
88-
.await
89-
.map(|(g, _)| g)
90-
}
77+
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error>;
9178
}
9279

9380
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -199,14 +186,12 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
199186
})
200187
}
201188

202-
async fn scan_txs_with_keychains<K: Ord + Clone + Send>(
189+
async fn full_scan<K: Ord + Clone + Send>(
203190
&self,
204191
keychain_spks: BTreeMap<
205192
K,
206193
impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,
207194
>,
208-
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
209-
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
210195
stop_gap: usize,
211196
parallel_requests: usize,
212197
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error> {
@@ -275,6 +260,32 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
275260
}
276261
}
277262

263+
Ok((graph, last_active_indexes))
264+
}
265+
266+
async fn sync(
267+
&self,
268+
misc_spks: impl IntoIterator<IntoIter = impl Iterator<Item = ScriptBuf> + Send> + Send,
269+
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
270+
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
271+
parallel_requests: usize,
272+
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
273+
let mut graph = self
274+
.full_scan(
275+
[(
276+
(),
277+
misc_spks
278+
.into_iter()
279+
.enumerate()
280+
.map(|(i, spk)| (i as u32, spk)),
281+
)]
282+
.into(),
283+
usize::MAX,
284+
parallel_requests,
285+
)
286+
.await
287+
.map(|(g, _)| g)?;
288+
278289
let mut txids = txids.into_iter();
279290
loop {
280291
let handles = txids
@@ -323,7 +334,6 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
323334
}
324335
}
325336
}
326-
327-
Ok((graph, last_active_indexes))
337+
Ok(graph)
328338
}
329339
}

crates/esplora/src/blocking_ext.rs

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use crate::{anchor_from_status, ASSUME_FINAL_DEPTH};
1919
pub trait EsploraExt {
2020
/// Prepare an [`LocalChain`] update with blocks fetched from Esplora.
2121
///
22-
/// * `prev_tip` is the previous tip of [`LocalChain::tip`].
23-
/// * `get_heights` is the block heights that we are interested in fetching from Esplora.
22+
/// * `local_tip` is the previous tip of [`LocalChain::tip`].
23+
/// * `request_heights` is the block heights that we are interested in fetching from Esplora.
2424
///
2525
/// The result of this method can be applied to [`LocalChain::apply_update`].
2626
///
@@ -34,54 +34,42 @@ pub trait EsploraExt {
3434
request_heights: impl IntoIterator<Item = u32>,
3535
) -> Result<local_chain::Update, Error>;
3636

37-
/// Scan Esplora for the data specified and return a [`TxGraph`] and a map of last active
38-
/// indices.
37+
/// Full scan the keychain scripts specified with the blockchain (via an Esplora client) and
38+
/// returns a [`TxGraph`] and a map of last active indices.
3939
///
4040
/// * `keychain_spks`: keychains that we want to scan transactions for
41-
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
42-
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
43-
/// want to include in the update
4441
///
45-
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
42+
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
4643
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
4744
/// parallel.
4845
#[allow(clippy::result_large_err)]
49-
fn scan_txs_with_keychains<K: Ord + Clone>(
46+
fn full_scan<K: Ord + Clone>(
5047
&self,
5148
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
52-
txids: impl IntoIterator<Item = Txid>,
53-
outpoints: impl IntoIterator<Item = OutPoint>,
5449
stop_gap: usize,
5550
parallel_requests: usize,
5651
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error>;
5752

58-
/// Convenience method to call [`scan_txs_with_keychains`] without requiring a keychain.
53+
/// Sync a set of scripts with the blockchain (via an Esplora client) for the data
54+
/// specified and return a [`TxGraph`].
5955
///
60-
/// [`scan_txs_with_keychains`]: EsploraExt::scan_txs_with_keychains
56+
/// * `misc_spks`: scripts that we want to sync transactions for
57+
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
58+
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
59+
/// want to include in the update
60+
///
61+
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
62+
/// may include scripts that have been used, use [`full_scan`] with the keychain.
63+
///
64+
/// [`full_scan`]: EsploraExt::full_scan
6165
#[allow(clippy::result_large_err)]
62-
fn scan_txs(
66+
fn sync(
6367
&self,
6468
misc_spks: impl IntoIterator<Item = ScriptBuf>,
6569
txids: impl IntoIterator<Item = Txid>,
6670
outpoints: impl IntoIterator<Item = OutPoint>,
6771
parallel_requests: usize,
68-
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
69-
self.scan_txs_with_keychains(
70-
[(
71-
(),
72-
misc_spks
73-
.into_iter()
74-
.enumerate()
75-
.map(|(i, spk)| (i as u32, spk)),
76-
)]
77-
.into(),
78-
txids,
79-
outpoints,
80-
usize::MAX,
81-
parallel_requests,
82-
)
83-
.map(|(g, _)| g)
84-
}
72+
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error>;
8573
}
8674

8775
impl EsploraExt for esplora_client::BlockingClient {
@@ -190,11 +178,9 @@ impl EsploraExt for esplora_client::BlockingClient {
190178
})
191179
}
192180

193-
fn scan_txs_with_keychains<K: Ord + Clone>(
181+
fn full_scan<K: Ord + Clone>(
194182
&self,
195183
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
196-
txids: impl IntoIterator<Item = Txid>,
197-
outpoints: impl IntoIterator<Item = OutPoint>,
198184
stop_gap: usize,
199185
parallel_requests: usize,
200186
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error> {
@@ -266,6 +252,31 @@ impl EsploraExt for esplora_client::BlockingClient {
266252
}
267253
}
268254

255+
Ok((graph, last_active_indexes))
256+
}
257+
258+
fn sync(
259+
&self,
260+
misc_spks: impl IntoIterator<Item = ScriptBuf>,
261+
txids: impl IntoIterator<Item = Txid>,
262+
outpoints: impl IntoIterator<Item = OutPoint>,
263+
parallel_requests: usize,
264+
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
265+
let mut graph = self
266+
.full_scan(
267+
[(
268+
(),
269+
misc_spks
270+
.into_iter()
271+
.enumerate()
272+
.map(|(i, spk)| (i as u32, spk)),
273+
)]
274+
.into(),
275+
usize::MAX,
276+
parallel_requests,
277+
)
278+
.map(|(g, _)| g)?;
279+
269280
let mut txids = txids.into_iter();
270281
loop {
271282
let handles = txids
@@ -292,7 +303,7 @@ impl EsploraExt for esplora_client::BlockingClient {
292303
}
293304
}
294305

295-
for op in outpoints.into_iter() {
306+
for op in outpoints {
296307
if graph.get_tx(op.txid).is_none() {
297308
if let Some(tx) = self.get_tx(&op.txid)? {
298309
let _ = graph.insert_tx(tx);
@@ -317,7 +328,6 @@ impl EsploraExt for esplora_client::BlockingClient {
317328
}
318329
}
319330
}
320-
321-
Ok((graph, last_active_indexes))
331+
Ok(graph)
322332
}
323333
}

crates/esplora/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
11
#![doc = include_str!("../README.md")]
2+
3+
//! This crate is used for updating structures of [`bdk_chain`] with data from an Esplora server.
4+
//!
5+
//! The two primary methods are [`EsploraExt::sync`] and [`EsploraExt::full_scan`]. In most cases
6+
//! [`EsploraExt::sync`] is used to sync the transaction histories of scripts that the application
7+
//! cares about, for example the scripts for all the receive addresses of a Wallet's keychain that it
8+
//! has shown a user. [`EsploraExt::full_scan`] is meant to be used when importing or restoring a
9+
//! keychain where the range of possibly used scripts is not known. In this case it is necessary to
10+
//! scan all keychain scripts until a number (the "stop gap") of unused scripts is discovered. For a
11+
//! sync or full scan the user receives relevant blockchain data and output updates for [`bdk_chain`]
12+
//! via a new [`TxGraph`] to be appended to any existing [`TxGraph`] data.
13+
//!
14+
//! Refer to [`example_esplora`] for a complete example.
15+
//!
16+
//! [`TxGraph`]: bdk_chain::tx_graph::TxGraph
17+
//! [`example_esplora`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_esplora
18+
219
use bdk_chain::{BlockId, ConfirmationTimeHeightAnchor};
320
use esplora_client::TxStatus;
421

crates/esplora/tests/async_ext.rs

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
101101

102102
let graph_update = env
103103
.client
104-
.scan_txs(
104+
.sync(
105105
misc_spks.into_iter(),
106106
vec![].into_iter(),
107107
vec![].into_iter(),
@@ -166,28 +166,10 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
166166

167167
// A scan with a gap limit of 2 won't find the transaction, but a scan with a gap limit of 3
168168
// will.
169-
let (graph_update, active_indices) = env
170-
.client
171-
.scan_txs_with_keychains(
172-
keychains.clone(),
173-
vec![].into_iter(),
174-
vec![].into_iter(),
175-
2,
176-
1,
177-
)
178-
.await?;
169+
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 2, 1).await?;
179170
assert!(graph_update.full_txs().next().is_none());
180171
assert!(active_indices.is_empty());
181-
let (graph_update, active_indices) = env
182-
.client
183-
.scan_txs_with_keychains(
184-
keychains.clone(),
185-
vec![].into_iter(),
186-
vec![].into_iter(),
187-
3,
188-
1,
189-
)
190-
.await?;
172+
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 3, 1).await?;
191173
assert_eq!(graph_update.full_txs().next().unwrap().txid, txid_4th_addr);
192174
assert_eq!(active_indices[&0], 3);
193175

@@ -209,24 +191,12 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
209191

210192
// A scan with gap limit 4 won't find the second transaction, but a scan with gap limit 5 will.
211193
// The last active indice won't be updated in the first case but will in the second one.
212-
let (graph_update, active_indices) = env
213-
.client
214-
.scan_txs_with_keychains(
215-
keychains.clone(),
216-
vec![].into_iter(),
217-
vec![].into_iter(),
218-
4,
219-
1,
220-
)
221-
.await?;
194+
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 4, 1).await?;
222195
let txs: HashSet<_> = graph_update.full_txs().map(|tx| tx.txid).collect();
223196
assert_eq!(txs.len(), 1);
224197
assert!(txs.contains(&txid_4th_addr));
225198
assert_eq!(active_indices[&0], 3);
226-
let (graph_update, active_indices) = env
227-
.client
228-
.scan_txs_with_keychains(keychains, vec![].into_iter(), vec![].into_iter(), 5, 1)
229-
.await?;
199+
let (graph_update, active_indices) = env.client.full_scan(keychains, 5, 1).await?;
230200
let txs: HashSet<_> = graph_update.full_txs().map(|tx| tx.txid).collect();
231201
assert_eq!(txs.len(), 2);
232202
assert!(txs.contains(&txid_4th_addr) && txs.contains(&txid_last_addr));

0 commit comments

Comments
 (0)