Skip to content

Commit 9e01dee

Browse files
committed
refactor!(bdk): Update wallet start_sync, start_scan to return new SyncRequest, ScanRequest structs
Update esplora async_ext sync and scan to use new SyncRequest and ScanRequest
1 parent c97ddef commit 9e01dee

File tree

5 files changed

+119
-58
lines changed

5 files changed

+119
-58
lines changed

crates/bdk/src/wallet/mod.rs

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use core::fmt;
4040
use core::ops::Deref;
4141
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
4242

43+
use bdk_chain::request::{ScanRequest, SyncRequest};
4344
use bdk_chain::tx_graph::CalculateFeeError;
4445
#[allow(unused_imports)]
4546
use log::{debug, error, info, trace};
@@ -113,9 +114,20 @@ pub struct Update {
113114
pub chain: Option<local_chain::Update>,
114115
}
115116

116-
impl From<(BTreeMap<KeychainKind, u32>, TxGraph<ConfirmationTimeAnchor>, local_chain::Update)> for Update
117+
impl
118+
From<(
119+
BTreeMap<KeychainKind, u32>,
120+
TxGraph<ConfirmationTimeAnchor>,
121+
local_chain::Update,
122+
)> for Update
117123
{
118-
fn from((last_active_indices, graph, chain): (BTreeMap<KeychainKind, u32>, TxGraph<ConfirmationTimeAnchor>, local_chain::Update)) -> Self {
124+
fn from(
125+
(last_active_indices, graph, chain): (
126+
BTreeMap<KeychainKind, u32>,
127+
TxGraph<ConfirmationTimeAnchor>,
128+
local_chain::Update,
129+
),
130+
) -> Self {
119131
Self {
120132
last_active_indices,
121133
graph,
@@ -124,8 +136,7 @@ impl From<(BTreeMap<KeychainKind, u32>, TxGraph<ConfirmationTimeAnchor>, local_c
124136
}
125137
}
126138

127-
impl From<(TxGraph<ConfirmationTimeAnchor>, local_chain::Update)> for Update
128-
{
139+
impl From<(TxGraph<ConfirmationTimeAnchor>, local_chain::Update)> for Update {
129140
fn from((graph, chain): (TxGraph<ConfirmationTimeAnchor>, local_chain::Update)) -> Self {
130141
Self {
131142
graph,
@@ -2013,54 +2024,59 @@ impl<D> Wallet<D> {
20132024
///
20142025
/// Collect the wallet keychain script pub keys, local chain, and previous chain tip data needed
20152026
/// to start a blockchain scan.
2016-
pub fn start_scan(&self) -> (BTreeMap<KeychainKind, impl Iterator<Item = (u32, ScriptBuf)> + Clone>,
2017-
&LocalChain, Option<CheckPoint>) {
2018-
2019-
let keychain_spks = self.spks_of_all_keychains();
2020-
let local_chain = self.local_chain();
2021-
let prev_tip = self.latest_checkpoint();
2027+
pub fn start_scan(
2028+
&self,
2029+
) -> ScanRequest<KeychainKind, impl Iterator<Item = (u32, ScriptBuf)> + Clone> {
2030+
let spks_by_keychain = self.spks_of_all_keychains();
2031+
let checkpoint = self.latest_checkpoint();
20222032

2023-
(keychain_spks, local_chain, prev_tip)
2033+
ScanRequest {
2034+
spks_by_keychain,
2035+
checkpoint,
2036+
}
20242037
}
20252038

20262039
/// Get data needed to start a wallet sync
20272040
///
20282041
/// Collect the wallet keychain script pub keys, local chain, previous chain tip, UTXOs, and
20292042
/// unconfirmed transaction id data needed to start a blockchain sync.
2030-
pub fn start_sync(&self, unused_spks_only: bool) -> (Vec<ScriptBuf>, &LocalChain, Option<CheckPoint>, Vec<OutPoint>, Vec<Txid>) {
2031-
2032-
let local_chain = self.local_chain();
2033-
let prev_tip = self.latest_checkpoint();
2043+
pub fn start_sync(&self, unused_spks_only: bool) -> SyncRequest {
2044+
let checkpoint = self.latest_checkpoint();
20342045

20352046
// Sync only unused SPKs
20362047
let spks = if unused_spks_only {
2037-
self.spk_index()
2038-
.unused_spks(..)
2039-
.map(|((_keychain, _index), script)| ScriptBuf::from(script))
2040-
.collect::<Vec<ScriptBuf>>()
2041-
}
2042-
// Sync all SPKs
2043-
else {
2044-
self.spk_index()
2045-
.all_spks()
2046-
.into_iter()
2047-
.map(|((_keychain, _index), script)| (*script).clone())
2048-
.collect::<Vec<ScriptBuf>>()
2049-
};
2048+
self.spk_index()
2049+
.unused_spks(..)
2050+
.map(|((_keychain, _index), script)| ScriptBuf::from(script))
2051+
.collect::<Vec<ScriptBuf>>()
2052+
}
2053+
// Sync all SPKs
2054+
else {
2055+
self.spk_index()
2056+
.all_spks()
2057+
.into_iter()
2058+
.map(|((_keychain, _index), script)| (*script).clone())
2059+
.collect::<Vec<ScriptBuf>>()
2060+
};
20502061

2051-
// Sync UTXOs
2052-
// We want to search for whether our UTXOs are spent, and spent by which transaction.
2053-
let outpoints: Vec<OutPoint> = self.list_unspent().map(|utxo| utxo.outpoint).collect();
2062+
// Sync UTXOs
2063+
// We want to search for whether our UTXOs are spent, and spent by which transaction.
2064+
let outpoints: Vec<OutPoint> = self.list_unspent().map(|utxo| utxo.outpoint).collect();
20542065

2055-
// Sync unconfirmed TX
2056-
// We want to search for whether our unconfirmed transactions are now confirmed.
2057-
let txids: Vec<Txid> = self
2058-
.transactions()
2059-
.filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed())
2060-
.map(|canonical_tx| canonical_tx.tx_node.txid)
2061-
.collect();
2066+
// Sync unconfirmed TX
2067+
// We want to search for whether our unconfirmed transactions are now confirmed.
2068+
let txids: Vec<Txid> = self
2069+
.transactions()
2070+
.filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed())
2071+
.map(|canonical_tx| canonical_tx.tx_node.txid)
2072+
.collect();
20622073

2063-
(spks, local_chain, prev_tip, outpoints, txids)
2074+
SyncRequest {
2075+
spks,
2076+
txids,
2077+
outpoints,
2078+
checkpoint,
2079+
}
20642080
}
20652081
}
20662082

crates/chain/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ mod spk_iter;
5353
#[cfg(feature = "miniscript")]
5454
pub use spk_iter::*;
5555

56+
/// Structures for requesting data needed to sync or scan the blockchain for chain related data.
57+
pub mod request;
58+
5659
#[allow(unused_imports)]
5760
#[macro_use]
5861
extern crate alloc;

crates/chain/src/request.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use crate::local_chain::CheckPoint;
2+
use alloc::vec::Vec;
3+
use bitcoin::{OutPoint, ScriptBuf, Txid};
4+
use std::collections::BTreeMap;
5+
6+
/// A list of blockchain entities for which we want to receive any related transaction data.
7+
pub struct SyncRequest {
8+
/// transactions that spend from or two these script pubkeys
9+
pub spks: Vec<ScriptBuf>,
10+
/// Transactions with these txids
11+
pub txids: Vec<Txid>,
12+
/// Transactions with these outpoints or spend from these outpoints
13+
pub outpoints: Vec<OutPoint>,
14+
/// The local chain checkpoint. The sync process will return a new chain that extends this one.
15+
pub checkpoint: Option<CheckPoint>,
16+
}
17+
18+
/// Script pubkeys indexed by their keychain.
19+
pub struct ScanRequest<K, I> {
20+
/// Iterators of script pubkeys indexed by the keychain index
21+
pub spks_by_keychain: BTreeMap<K, I>,
22+
/// The local chain checkpoint. The scan process will return a new chain that extends this one.
23+
pub checkpoint: Option<CheckPoint>,
24+
}

crates/esplora/src/async_ext.rs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use async_trait::async_trait;
22
use bdk_chain::collections::btree_map;
3+
use bdk_chain::local_chain::LocalChain;
4+
use bdk_chain::request::{ScanRequest, SyncRequest};
35
use bdk_chain::{
46
bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid},
57
collections::{BTreeMap, BTreeSet},
@@ -8,7 +10,6 @@ use bdk_chain::{
810
};
911
use esplora_client::{Error, TxStatus};
1012
use futures::{stream::FuturesOrdered, TryStreamExt};
11-
use bdk_chain::local_chain::LocalChain;
1213

1314
use crate::{anchor_from_status, ASSUME_FINAL_DEPTH};
1415

@@ -33,29 +34,37 @@ pub trait EsploraAsyncExt {
3334
/// which is used to update a wallets [`KeychainTxOutIndex`].
3435
/// * graph_update, contains an update to a wallet's internal [`TxGraph`].
3536
/// * chain_update, contains an update to a wallet's internal [`LocalChain`].
36-
async fn scan<K: Clone + Ord + Send>(
37+
async fn scan<K: Clone + Ord + Send, I: Iterator<Item = (u32, ScriptBuf)> + Send>(
3738
&self,
38-
start_scan: (BTreeMap<K, impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,>,
39-
&LocalChain,
40-
Option<CheckPoint>),
39+
scan_request: ScanRequest<K, I>,
4140
stop_gap: usize,
4241
parallel_requests: usize,
43-
) -> Result<(BTreeMap<K, u32>, TxGraph<ConfirmationTimeAnchor>, local_chain::Update), Error> {
44-
let (keychain_spks, local_chain, local_tip) = start_scan;
42+
) -> Result<
43+
(
44+
BTreeMap<K, u32>,
45+
TxGraph<ConfirmationTimeAnchor>,
46+
local_chain::Update,
47+
),
48+
Error,
49+
> {
4550
let (graph_update, last_active_indices) = self
4651
.scan_txs_with_keychains(
47-
keychain_spks,
52+
scan_request.spks_by_keychain,
4853
core::iter::empty(),
4954
core::iter::empty(),
5055
stop_gap,
5156
parallel_requests,
5257
)
5358
.await?;
5459

55-
let missing_heights = graph_update.missing_heights(local_chain);
60+
let local_chain = scan_request
61+
.checkpoint
62+
.map(|cp| LocalChain::from_tip(cp))
63+
.unwrap_or_default();
64+
let missing_heights = graph_update.missing_heights(&local_chain);
5665

5766
let chain_update = self
58-
.update_local_chain(local_tip, missing_heights)
67+
.update_local_chain(local_chain.tip(), missing_heights)
5968
.await?;
6069

6170
Ok((last_active_indices, graph_update, chain_update))
@@ -69,16 +78,23 @@ pub trait EsploraAsyncExt {
6978
/// * chain_update, contains an update to a wallet's internal [`LocalChain`].
7079
async fn sync(
7180
&self,
72-
start_sync: (Vec<ScriptBuf>, &LocalChain, Option<CheckPoint>, Vec<OutPoint>, Vec<Txid>),
81+
sync_request: SyncRequest,
7382
parallel_requests: usize,
7483
) -> Result<(TxGraph<ConfirmationTimeAnchor>, local_chain::Update), Error> {
75-
let (spks, local_chain, local_tip, outpoints, txids) = start_sync;
7684
let graph_update = self
77-
.scan_txs(spks.into_iter(), txids.into_iter(), outpoints.into_iter(), parallel_requests)
85+
.scan_txs(
86+
sync_request.spks.into_iter(),
87+
sync_request.txids.into_iter(),
88+
sync_request.outpoints.into_iter(),
89+
parallel_requests,
90+
)
7891
.await?;
7992

80-
let missing_heights = graph_update.missing_heights(local_chain);
81-
let chain_update = self.update_local_chain(local_tip, missing_heights).await?;
93+
let local_chain = LocalChain::from_tip(sync_request.checkpoint.unwrap());
94+
let missing_heights = graph_update.missing_heights(&local_chain);
95+
let chain_update = self
96+
.update_local_chain(local_chain.tip(), missing_heights)
97+
.await?;
8298

8399
Ok((graph_update, chain_update))
84100
}

example-crates/wallet_esplora_async/src/main.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4242
// wallet restoration. It is a special case. Applications should use "sync" style updates
4343
// after an initial scan.
4444
if prompt("Scan wallet") {
45-
let start_scan = wallet.start_scan();
46-
let wallet_update = client.scan(start_scan, STOP_GAP, PARALLEL_REQUESTS).await?;
45+
let scan_request = wallet.start_scan();
46+
let wallet_update = client
47+
.scan(scan_request, STOP_GAP, PARALLEL_REQUESTS)
48+
.await?;
4749
wallet.apply_update(wallet_update.into())?;
4850
wallet.commit()?;
4951
println!("Scan completed.");
@@ -52,8 +54,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
5254
// status or fetch missing transactions.
5355
else {
5456
let unused_spks_only = prompt("Sync only unused SPKs");
55-
let start_sync = wallet.start_sync(unused_spks_only);
56-
let wallet_update = client.sync(start_sync, PARALLEL_REQUESTS).await?;
57+
let sync_request = wallet.start_sync(unused_spks_only);
58+
let wallet_update = client.sync(sync_request, PARALLEL_REQUESTS).await?;
5759
wallet.apply_update(wallet_update.into())?;
5860
wallet.commit()?;
5961
println!("Sync completed.");

0 commit comments

Comments
 (0)