Skip to content

Commit 9cc0332

Browse files
committed
Merge #1235: Refactor/rename electrum_ext and esplora_ext to have sync and full_scan functions
de54e71 refactor(esplora_ext): rename scan_txs to sync and scan_txs_with_keychains to full_scan (Steve Myers) 95d3485 refactor(electrum_ext): rename scan_without_keychain to sync and scan to full_scan (Steve Myers) Pull request description: ### Description fixes #1112 Simple function renaming plus updated docs: 1. electrum_ext: rename functions `scan_without_keychain` to `sync` and `scan` to `full_scan` 2. esplora_ext: rename functions `scan_txs` to `sync` and `scan_txs_with_keychains` to `full_scan` ### Notes to the reviewers The esplora_ext changes were partially fixed in #1070 but I renamed again so the functions match names ~~suggested in #1112~~ agreed on in discord poll, `sync` and `full_scan`. ### Changelog notice Changed - electrum_ext: rename functions scan_without_keychain to sync and scan to full_scan - esplora_ext: rename functions scan_txs to sync and scan_txs_with_keychains to full_scan ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing Top commit has no ACKs. Tree-SHA512: d34516ecc513a194b679f73a1260d0cbc3d12b6a2e162d822e7381da0b3250aff319e85ed2fadec506e36f95a78a5cd79d0ab972da2b02928c074be17664da08
2 parents 7eff024 + de54e71 commit 9cc0332

File tree

14 files changed

+208
-221
lines changed

14 files changed

+208
-221
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/electrum/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
# BDK Electrum
22

3-
BDK Electrum client library for updating the keychain tracker.
3+
BDK Electrum extends [`electrum-client`] to update [`bdk_chain`] structures
4+
from an Electrum server.
5+
6+
[`electrum-client`]: https://docs.rs/electrum-client/
7+
[`bdk_chain`]: https://docs.rs/bdk-chain/

crates/electrum/src/electrum_ext.rs

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -134,64 +134,54 @@ pub struct ElectrumUpdate {
134134

135135
/// Trait to extend [`Client`] functionality.
136136
pub trait ElectrumExt {
137-
/// Scan the blockchain (via electrum) for the data specified and returns updates for
138-
/// [`bdk_chain`] data structures.
137+
/// Full scan the keychain scripts specified with the blockchain (via an Electrum client) and
138+
/// returns updates for [`bdk_chain`] data structures.
139139
///
140140
/// - `prev_tip`: the most recent blockchain tip present locally
141141
/// - `keychain_spks`: keychains that we want to scan transactions for
142-
/// - `txids`: transactions for which we want updated [`Anchor`]s
143-
/// - `outpoints`: transactions associated with these outpoints (residing, spending) that we
144-
/// want to included in the update
145142
///
146-
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
143+
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
147144
/// transactions. `batch_size` specifies the max number of script pubkeys to request for in a
148145
/// single batch request.
149-
fn scan<K: Ord + Clone>(
146+
fn full_scan<K: Ord + Clone>(
150147
&self,
151148
prev_tip: CheckPoint,
152149
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
153-
txids: impl IntoIterator<Item = Txid>,
154-
outpoints: impl IntoIterator<Item = OutPoint>,
155150
stop_gap: usize,
156151
batch_size: usize,
157152
) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error>;
158153

159-
/// Convenience method to call [`scan`] without requiring a keychain.
154+
/// Sync a set of scripts with the blockchain (via an Electrum client) for the data specified
155+
/// and returns updates for [`bdk_chain`] data structures.
160156
///
161-
/// [`scan`]: ElectrumExt::scan
162-
fn scan_without_keychain(
157+
/// - `prev_tip`: the most recent blockchain tip present locally
158+
/// - `misc_spks`: an iterator of scripts we want to sync transactions for
159+
/// - `txids`: transactions for which we want updated [`Anchor`]s
160+
/// - `outpoints`: transactions associated with these outpoints (residing, spending) that we
161+
/// want to include in the update
162+
///
163+
/// `batch_size` specifies the max number of script pubkeys to request for in a single batch
164+
/// request.
165+
///
166+
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
167+
/// may include scripts that have been used, use [`full_scan`] with the keychain.
168+
///
169+
/// [`full_scan`]: ElectrumExt::full_scan
170+
fn sync(
163171
&self,
164172
prev_tip: CheckPoint,
165173
misc_spks: impl IntoIterator<Item = ScriptBuf>,
166174
txids: impl IntoIterator<Item = Txid>,
167175
outpoints: impl IntoIterator<Item = OutPoint>,
168176
batch_size: usize,
169-
) -> Result<ElectrumUpdate, Error> {
170-
let spk_iter = misc_spks
171-
.into_iter()
172-
.enumerate()
173-
.map(|(i, spk)| (i as u32, spk));
174-
175-
let (electrum_update, _) = self.scan(
176-
prev_tip,
177-
[((), spk_iter)].into(),
178-
txids,
179-
outpoints,
180-
usize::MAX,
181-
batch_size,
182-
)?;
183-
184-
Ok(electrum_update)
185-
}
177+
) -> Result<ElectrumUpdate, Error>;
186178
}
187179

188180
impl ElectrumExt for Client {
189-
fn scan<K: Ord + Clone>(
181+
fn full_scan<K: Ord + Clone>(
190182
&self,
191183
prev_tip: CheckPoint,
192184
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
193-
txids: impl IntoIterator<Item = Txid>,
194-
outpoints: impl IntoIterator<Item = OutPoint>,
195185
stop_gap: usize,
196186
batch_size: usize,
197187
) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error> {
@@ -201,9 +191,6 @@ impl ElectrumExt for Client {
201191
.collect::<BTreeMap<K, _>>();
202192
let mut scanned_spks = BTreeMap::<(K, u32), (ScriptBuf, bool)>::new();
203193

204-
let txids = txids.into_iter().collect::<Vec<_>>();
205-
let outpoints = outpoints.into_iter().collect::<Vec<_>>();
206-
207194
let (electrum_update, keychain_update) = loop {
208195
let (tip, _) = construct_update_tip(self, prev_tip.clone())?;
209196
let mut relevant_txids = RelevantTxids::default();
@@ -242,15 +229,6 @@ impl ElectrumExt for Client {
242229
}
243230
}
244231

245-
populate_with_txids(self, &cps, &mut relevant_txids, &mut txids.iter().cloned())?;
246-
247-
let _txs = populate_with_outpoints(
248-
self,
249-
&cps,
250-
&mut relevant_txids,
251-
&mut outpoints.iter().cloned(),
252-
)?;
253-
254232
// check for reorgs during scan process
255233
let server_blockhash = self.block_header(tip.height() as usize)?.block_hash();
256234
if tip.hash() != server_blockhash {
@@ -284,6 +262,41 @@ impl ElectrumExt for Client {
284262

285263
Ok((electrum_update, keychain_update))
286264
}
265+
266+
fn sync(
267+
&self,
268+
prev_tip: CheckPoint,
269+
misc_spks: impl IntoIterator<Item = ScriptBuf>,
270+
txids: impl IntoIterator<Item = Txid>,
271+
outpoints: impl IntoIterator<Item = OutPoint>,
272+
batch_size: usize,
273+
) -> Result<ElectrumUpdate, Error> {
274+
let spk_iter = misc_spks
275+
.into_iter()
276+
.enumerate()
277+
.map(|(i, spk)| (i as u32, spk));
278+
279+
let (mut electrum_update, _) = self.full_scan(
280+
prev_tip.clone(),
281+
[((), spk_iter)].into(),
282+
usize::MAX,
283+
batch_size,
284+
)?;
285+
286+
let (tip, _) = construct_update_tip(self, prev_tip)?;
287+
let cps = tip
288+
.iter()
289+
.take(10)
290+
.map(|cp| (cp.height(), cp))
291+
.collect::<BTreeMap<u32, CheckPoint>>();
292+
293+
populate_with_txids(self, &cps, &mut electrum_update.relevant_txids, txids)?;
294+
295+
let _txs =
296+
populate_with_outpoints(self, &cps, &mut electrum_update.relevant_txids, outpoints)?;
297+
298+
Ok(electrum_update)
299+
}
287300
}
288301

289302
/// Return a [`CheckPoint`] of the latest tip, that connects with `prev_tip`.
@@ -405,7 +418,7 @@ fn populate_with_outpoints(
405418
client: &Client,
406419
cps: &BTreeMap<u32, CheckPoint>,
407420
relevant_txids: &mut RelevantTxids,
408-
outpoints: &mut impl Iterator<Item = OutPoint>,
421+
outpoints: impl IntoIterator<Item = OutPoint>,
409422
) -> Result<HashMap<Txid, Transaction>, Error> {
410423
let mut full_txs = HashMap::new();
411424
for outpoint in outpoints {
@@ -466,7 +479,7 @@ fn populate_with_txids(
466479
client: &Client,
467480
cps: &BTreeMap<u32, CheckPoint>,
468481
relevant_txids: &mut RelevantTxids,
469-
txids: &mut impl Iterator<Item = Txid>,
482+
txids: impl IntoIterator<Item = Txid>,
470483
) -> Result<(), Error> {
471484
for txid in txids {
472485
let tx = match client.transaction_get(&txid) {

crates/electrum/src/lib.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
//! This crate is used for updating structures of the [`bdk_chain`] crate with data from electrum.
1+
//! This crate is used for updating structures of [`bdk_chain`] with data from an Electrum server.
22
//!
3-
//! The star of the show is the [`ElectrumExt::scan`] method, which scans for relevant blockchain
4-
//! data (via electrum) and outputs updates for [`bdk_chain`] structures as a tuple of form:
3+
//! The two primary methods are [`ElectrumExt::sync`] and [`ElectrumExt::full_scan`]. In most cases
4+
//! [`ElectrumExt::sync`] is used to sync the transaction histories of scripts that the application
5+
//! cares about, for example the scripts for all the receive addresses of a Wallet's keychain that it
6+
//! has shown a user. [`ElectrumExt::full_scan`] is meant to be used when importing or restoring a
7+
//! keychain where the range of possibly used scripts is not known. In this case it is necessary to
8+
//! scan all keychain scripts until a number (the "stop gap") of unused scripts is discovered. For a
9+
//! sync or full scan the user receives relevant blockchain data and output updates for
10+
//! [`bdk_chain`] including [`RelevantTxids`].
511
//!
6-
//! ([`bdk_chain::local_chain::Update`], [`RelevantTxids`], `keychain_update`)
12+
//! The [`RelevantTxids`] only includes `txid`s and not full transactions. The caller is responsible
13+
//! for obtaining full transactions before applying new data to their [`bdk_chain`]. This can be
14+
//! done with these steps:
715
//!
8-
//! An [`RelevantTxids`] only includes `txid`s and no full transactions. The caller is
9-
//! responsible for obtaining full transactions before applying. This can be done with
10-
//! these steps:
16+
//! 1. Determine which full transactions are missing. Use [`RelevantTxids::missing_full_txs`].
1117
//!
12-
//! 1. Determine which full transactions are missing. The method [`missing_full_txs`] of
13-
//! [`RelevantTxids`] can be used.
18+
//! 2. Obtaining the full transactions. To do this via electrum use [`ElectrumApi::batch_transaction_get`].
1419
//!
15-
//! 2. Obtaining the full transactions. To do this via electrum, the method
16-
//! [`batch_transaction_get`] can be used.
20+
//! Refer to [`example_electrum`] for a complete example.
1721
//!
18-
//! Refer to [`bdk_electrum_example`] for a complete example.
19-
//!
20-
//! [`ElectrumClient::scan`]: electrum_client::ElectrumClient::scan
21-
//! [`missing_full_txs`]: RelevantTxids::missing_full_txs
22-
//! [`batch_transaction_get`]: electrum_client::ElectrumApi::batch_transaction_get
23-
//! [`bdk_electrum_example`]: https://github.com/LLFourn/bdk_core_staging/tree/master/bdk_electrum_example
22+
//! [`ElectrumApi::batch_transaction_get`]: electrum_client::ElectrumApi::batch_transaction_get
23+
//! [`example_electrum`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_electrum
2424
2525
#![warn(missing_docs)]
2626

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
}

0 commit comments

Comments
 (0)