Skip to content

Commit

Permalink
refactor(esplora_ext): rename scan_txs to sync and scan_txs_with_keyc…
Browse files Browse the repository at this point in the history
…hains to full_scan
  • Loading branch information
notmandatory committed Dec 12, 2023
1 parent 348d117 commit b9cbcc3
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 39 deletions.
2 changes: 1 addition & 1 deletion crates/chain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! This crate is a collection of core structures for [Bitcoin Dev Kit] (alpha release).
//! This crate is a collection of core structures for [Bitcoin Dev Kit].
//!
//! The goal of this crate is to give wallets the mechanisms needed to:
//!
Expand Down
31 changes: 22 additions & 9 deletions crates/esplora/src/async_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ pub trait EsploraAsyncExt {
request_heights: impl IntoIterator<IntoIter = impl Iterator<Item = u32> + Send> + Send,
) -> Result<local_chain::Update, Error>;

/// Scan Esplora for the data specified and return a [`TxGraph`] and a map of last active
/// indices.
/// Full scan the keychain scripts specified with the blockchain (via an Esplora client) and
/// returns a [`TxGraph`] and a map of last active indices.
///
/// * `keychain_spks`: keychains that we want to scan transactions for
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
/// want to include in the update
///
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
/// parallel.
#[allow(clippy::result_large_err)]
async fn scan_txs_with_keychains<K: Ord + Clone + Send>(
async fn full_scan<K: Ord + Clone + Send>(
&self,
keychain_spks: BTreeMap<
K,
Expand All @@ -60,18 +60,31 @@ pub trait EsploraAsyncExt {
parallel_requests: usize,
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error>;

/// Convenience method to call [`scan_txs_with_keychains`] without requiring a keychain.
/// Sync a set of scripts with with the blockchain (via an Esplora client) for the data
/// specified and return a [`TxGraph`] and a map of last active indices.
///
/// * `misc_spks`: scripts that we want to sync transactions for
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
/// want to include in the update
///
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
/// parallel.
///
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
/// may include scripts that have been used, use [`full_scan`] with the keychain.
///
/// [`scan_txs_with_keychains`]: EsploraAsyncExt::scan_txs_with_keychains
/// [`full_scan`]: EsploraAsyncExt::full_scan
#[allow(clippy::result_large_err)]
async fn scan_txs(
async fn sync(
&self,
misc_spks: impl IntoIterator<IntoIter = impl Iterator<Item = ScriptBuf> + Send> + Send,
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
parallel_requests: usize,
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
self.scan_txs_with_keychains(
self.full_scan(
[(
(),
misc_spks
Expand Down Expand Up @@ -199,7 +212,7 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
})
}

async fn scan_txs_with_keychains<K: Ord + Clone + Send>(
async fn full_scan<K: Ord + Clone + Send>(
&self,
keychain_spks: BTreeMap<
K,
Expand Down
31 changes: 22 additions & 9 deletions crates/esplora/src/blocking_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ pub trait EsploraExt {
request_heights: impl IntoIterator<Item = u32>,
) -> Result<local_chain::Update, Error>;

/// Scan Esplora for the data specified and return a [`TxGraph`] and a map of last active
/// indices.
/// Full scan the keychain scripts specified with the blockchain (via an Esplora client) and
/// returns a [`TxGraph`] and a map of last active indices.
///
/// * `keychain_spks`: keychains that we want to scan transactions for
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
/// want to include in the update
///
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
/// parallel.
#[allow(clippy::result_large_err)]
fn scan_txs_with_keychains<K: Ord + Clone>(
fn full_scan<K: Ord + Clone>(
&self,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
txids: impl IntoIterator<Item = Txid>,
Expand All @@ -55,18 +55,31 @@ pub trait EsploraExt {
parallel_requests: usize,
) -> Result<(TxGraph<ConfirmationTimeHeightAnchor>, BTreeMap<K, u32>), Error>;

/// Convenience method to call [`scan_txs_with_keychains`] without requiring a keychain.
/// Sync a set of scripts with with the blockchain (via an Esplora client) for the data
/// specified and return a [`TxGraph`] and a map of last active indices.
///
/// * `misc_spks`: scripts that we want to sync transactions for
/// * `txids`: transactions for which we want updated [`ConfirmationTimeHeightAnchor`]s
/// * `outpoints`: transactions associated with these outpoints (residing, spending) that we
/// want to include in the update
///
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
/// parallel.
///
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
/// may include scripts that have been used, use [`full_scan`] with the keychain.
///
/// [`scan_txs_with_keychains`]: EsploraExt::scan_txs_with_keychains
/// [`full_scan`]: EsploraExt::full_scan
#[allow(clippy::result_large_err)]
fn scan_txs(
fn sync(
&self,
misc_spks: impl IntoIterator<Item = ScriptBuf>,
txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>,
parallel_requests: usize,
) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
self.scan_txs_with_keychains(
self.full_scan(
[(
(),
misc_spks
Expand Down Expand Up @@ -190,7 +203,7 @@ impl EsploraExt for esplora_client::BlockingClient {
})
}

fn scan_txs_with_keychains<K: Ord + Clone>(
fn full_scan<K: Ord + Clone>(
&self,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
txids: impl IntoIterator<Item = Txid>,
Expand Down
17 changes: 17 additions & 0 deletions crates/esplora/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
#![doc = include_str!("../README.md")]

//! This crate is used for updating structures of [`bdk_chain`] with data from an Esplora server.
//!
//! The two primary methods are [`EsploraExt::sync`] and [`EsploraExt::full_scan`]. In most cases
//! [`EsploraExt::sync`] is used to sync the transaction histories of scripts that the application
//! cares about, for example the scripts for all the receive addresses of a Wallet's keychain that it
//! has shown a user. [`EsploraExt::full_scan`] is meant to be used when importing or restoring a
//! keychain where the range of possibly used scripts is not known. In this case it is necessary to
//! scan all keychain scripts until a number (the "stop gap") of unused scripts is discovered. For a
//! sync or full scan the user receives relevant blockchain data and output updates for [`bdk_chain`]
//! via a new [`TxGraph`] to be appended to any existing [`TxGraph`] data.
//!
//! Refer to [`example_esplora`] for a complete example.
//!
//! [`TxGraph`]: bdk_chain::tx_graph::TxGraph
//! [`example_esplora`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_esplora
use bdk_chain::{BlockId, ConfirmationTimeHeightAnchor};
use esplora_client::TxStatus;

Expand Down
10 changes: 5 additions & 5 deletions crates/esplora/tests/async_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {

let graph_update = env
.client
.scan_txs(
.sync(
misc_spks.into_iter(),
vec![].into_iter(),
vec![].into_iter(),
Expand Down Expand Up @@ -168,7 +168,7 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
// will.
let (graph_update, active_indices) = env
.client
.scan_txs_with_keychains(
.full_scan(
keychains.clone(),
vec![].into_iter(),
vec![].into_iter(),
Expand All @@ -180,7 +180,7 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
assert!(active_indices.is_empty());
let (graph_update, active_indices) = env
.client
.scan_txs_with_keychains(
.full_scan(
keychains.clone(),
vec![].into_iter(),
vec![].into_iter(),
Expand Down Expand Up @@ -211,7 +211,7 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
// The last active indice won't be updated in the first case but will in the second one.
let (graph_update, active_indices) = env
.client
.scan_txs_with_keychains(
.full_scan(
keychains.clone(),
vec![].into_iter(),
vec![].into_iter(),
Expand All @@ -225,7 +225,7 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
assert_eq!(active_indices[&0], 3);
let (graph_update, active_indices) = env
.client
.scan_txs_with_keychains(keychains, vec![].into_iter(), vec![].into_iter(), 5, 1)
.full_scan(keychains, vec![].into_iter(), vec![].into_iter(), 5, 1)
.await?;
let txs: HashSet<_> = graph_update.full_txs().map(|tx| tx.txid).collect();
assert_eq!(txs.len(), 2);
Expand Down
18 changes: 7 additions & 11 deletions crates/esplora/tests/blocking_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
sleep(Duration::from_millis(10))
}

let graph_update = env.client.scan_txs(
let graph_update = env.client.sync(
misc_spks.into_iter(),
vec![].into_iter(),
vec![].into_iter(),
Expand Down Expand Up @@ -164,7 +164,7 @@ pub fn test_update_tx_graph_gap_limit() -> anyhow::Result<()> {

// A scan with a gap limit of 2 won't find the transaction, but a scan with a gap limit of 3
// will.
let (graph_update, active_indices) = env.client.scan_txs_with_keychains(
let (graph_update, active_indices) = env.client.full_scan(
keychains.clone(),
vec![].into_iter(),
vec![].into_iter(),
Expand All @@ -173,7 +173,7 @@ pub fn test_update_tx_graph_gap_limit() -> anyhow::Result<()> {
)?;
assert!(graph_update.full_txs().next().is_none());
assert!(active_indices.is_empty());
let (graph_update, active_indices) = env.client.scan_txs_with_keychains(
let (graph_update, active_indices) = env.client.full_scan(
keychains.clone(),
vec![].into_iter(),
vec![].into_iter(),
Expand Down Expand Up @@ -201,7 +201,7 @@ pub fn test_update_tx_graph_gap_limit() -> anyhow::Result<()> {

// A scan with gap limit 4 won't find the second transaction, but a scan with gap limit 5 will.
// The last active indice won't be updated in the first case but will in the second one.
let (graph_update, active_indices) = env.client.scan_txs_with_keychains(
let (graph_update, active_indices) = env.client.full_scan(
keychains.clone(),
vec![].into_iter(),
vec![].into_iter(),
Expand All @@ -212,13 +212,9 @@ pub fn test_update_tx_graph_gap_limit() -> anyhow::Result<()> {
assert_eq!(txs.len(), 1);
assert!(txs.contains(&txid_4th_addr));
assert_eq!(active_indices[&0], 3);
let (graph_update, active_indices) = env.client.scan_txs_with_keychains(
keychains,
vec![].into_iter(),
vec![].into_iter(),
5,
1,
)?;
let (graph_update, active_indices) =
env.client
.full_scan(keychains, vec![].into_iter(), vec![].into_iter(), 5, 1)?;
let txs: HashSet<_> = graph_update.full_txs().map(|tx| tx.txid).collect();
assert_eq!(txs.len(), 2);
assert!(txs.contains(&txid_4th_addr) && txs.contains(&txid_last_addr));
Expand Down
4 changes: 2 additions & 2 deletions example-crates/example_esplora/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ fn main() -> anyhow::Result<()> {
// represents the last active spk derivation indices of keychains
// (`keychain_indices_update`).
let (graph_update, last_active_indices) = client
.scan_txs_with_keychains(
.full_scan(
keychain_spks,
core::iter::empty(),
core::iter::empty(),
Expand Down Expand Up @@ -312,7 +312,7 @@ fn main() -> anyhow::Result<()> {
}

let graph_update =
client.scan_txs(spks, txids, outpoints, scan_options.parallel_requests)?;
client.sync(spks, txids, outpoints, scan_options.parallel_requests)?;

graph.lock().unwrap().apply_update(graph_update)
}
Expand Down
2 changes: 1 addition & 1 deletion example-crates/wallet_esplora_async/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async fn main() -> Result<(), anyhow::Error> {
})
.collect();
let (update_graph, last_active_indices) = client
.scan_txs_with_keychains(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)
.full_scan(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)
.await?;
let missing_heights = update_graph.missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
Expand Down
2 changes: 1 addition & 1 deletion example-crates/wallet_esplora_blocking/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn main() -> Result<(), anyhow::Error> {
.collect();

let (update_graph, last_active_indices) =
client.scan_txs_with_keychains(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)?;
client.full_scan(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)?;
let missing_heights = update_graph.missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights)?;
let update = Update {
Expand Down

0 comments on commit b9cbcc3

Please sign in to comment.