use std::collections::BTreeSet;
use async_trait::async_trait;
+use bdk_chain::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult};
use bdk_chain::Anchor;
use bdk_chain::{
bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
use esplora_client::{Amount, TxStatus};
use futures::{stream::FuturesOrdered, TryStreamExt};
-use crate::{anchor_from_status, FullScanUpdate, SyncUpdate};
+use crate::anchor_from_status;
/// [`esplora_client::Error`]
type Error = Box<esplora_client::Error>;
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
async fn full_scan<K: Ord + Clone + Send>(
&self,
- local_tip: CheckPoint,
- keychain_spks: BTreeMap<
- K,
- impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,
- >,
+ request: FullScanRequest<K>,
stop_gap: usize,
parallel_requests: usize,
- ) -> Result<FullScanUpdate<K>, Error>;
+ ) -> Result<FullScanResult<K>, Error>;
/// Sync a set of scripts with the blockchain (via an Esplora client) for the data
/// specified and return a [`TxGraph`].
/// [`full_scan`]: EsploraAsyncExt::full_scan
async fn sync(
&self,
- local_tip: CheckPoint,
- 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,
+ request: SyncRequest,
parallel_requests: usize,
- ) -> Result<SyncUpdate, Error>;
+ ) -> Result<SyncResult, Error>;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl EsploraAsyncExt for esplora_client::AsyncClient {
async fn full_scan<K: Ord + Clone + Send>(
&self,
- local_tip: CheckPoint,
- keychain_spks: BTreeMap<
- K,
- impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,
- >,
+ request: FullScanRequest<K>,
stop_gap: usize,
parallel_requests: usize,
- ) -> Result<FullScanUpdate<K>, Error> {
+ ) -> Result<FullScanResult<K>, Error> {
let latest_blocks = fetch_latest_blocks(self).await?;
- let (tx_graph, last_active_indices) =
- full_scan_for_index_and_graph(self, keychain_spks, stop_gap, parallel_requests).await?;
- let local_chain =
- chain_update(self, &latest_blocks, &local_tip, tx_graph.all_anchors()).await?;
- Ok(FullScanUpdate {
- local_chain,
- tx_graph,
+ let (graph_update, last_active_indices) = full_scan_for_index_and_graph(
+ self,
+ request.spks_by_keychain,
+ stop_gap,
+ parallel_requests,
+ )
+ .await?;
+ let chain_update = chain_update(
+ self,
+ &latest_blocks,
+ &request.chain_tip,
+ graph_update.all_anchors(),
+ )
+ .await?;
+ Ok(FullScanResult {
+ chain_update,
+ graph_update,
last_active_indices,
})
}
async fn sync(
&self,
- local_tip: CheckPoint,
- 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,
+ request: SyncRequest,
parallel_requests: usize,
- ) -> Result<SyncUpdate, Error> {
+ ) -> Result<SyncResult, Error> {
let latest_blocks = fetch_latest_blocks(self).await?;
- let tx_graph =
- sync_for_index_and_graph(self, misc_spks, txids, outpoints, parallel_requests).await?;
- let local_chain =
- chain_update(self, &latest_blocks, &local_tip, tx_graph.all_anchors()).await?;
- Ok(SyncUpdate {
- tx_graph,
- local_chain,
+ let graph_update = sync_for_index_and_graph(
+ self,
+ request.spks,
+ request.txids,
+ request.outpoints,
+ parallel_requests,
+ )
+ .await?;
+ let chain_update = chain_update(
+ self,
+ &latest_blocks,
+ &request.chain_tip,
+ graph_update.all_anchors(),
+ )
+ .await?;
+ Ok(SyncResult {
+ chain_update,
+ graph_update,
})
}
}
use std::usize;
use bdk_chain::collections::BTreeMap;
+use bdk_chain::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult};
use bdk_chain::Anchor;
use bdk_chain::{
bitcoin::{Amount, BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
use esplora_client::TxStatus;
use crate::anchor_from_status;
-use crate::FullScanUpdate;
-use crate::SyncUpdate;
/// [`esplora_client::Error`]
pub type Error = Box<esplora_client::Error>;
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
fn full_scan<K: Ord + Clone>(
&self,
- local_tip: CheckPoint,
- keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
+ request: FullScanRequest<K>,
stop_gap: usize,
parallel_requests: usize,
- ) -> Result<FullScanUpdate<K>, Error>;
+ ) -> Result<FullScanResult<K>, Error>;
/// Sync a set of scripts with the blockchain (via an Esplora client) for the data
/// specified and return a [`TxGraph`].
///
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
/// [`full_scan`]: EsploraExt::full_scan
- fn sync(
- &self,
- local_tip: CheckPoint,
- misc_spks: impl IntoIterator<Item = ScriptBuf>,
- txids: impl IntoIterator<Item = Txid>,
- outpoints: impl IntoIterator<Item = OutPoint>,
- parallel_requests: usize,
- ) -> Result<SyncUpdate, Error>;
+ fn sync(&self, request: SyncRequest, parallel_requests: usize) -> Result<SyncResult, Error>;
}
impl EsploraExt for esplora_client::BlockingClient {
fn full_scan<K: Ord + Clone>(
&self,
- local_tip: CheckPoint,
- keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
+ request: FullScanRequest<K>,
stop_gap: usize,
parallel_requests: usize,
- ) -> Result<FullScanUpdate<K>, Error> {
+ ) -> Result<FullScanResult<K>, Error> {
let latest_blocks = fetch_latest_blocks(self)?;
- let (tx_graph, last_active_indices) = full_scan_for_index_and_graph_blocking(
+ let (graph_update, last_active_indices) = full_scan_for_index_and_graph_blocking(
self,
- keychain_spks,
+ request.spks_by_keychain,
stop_gap,
parallel_requests,
)?;
- let local_chain = chain_update(self, &latest_blocks, &local_tip, tx_graph.all_anchors())?;
- Ok(FullScanUpdate {
- local_chain,
- tx_graph,
+ let chain_update = chain_update(
+ self,
+ &latest_blocks,
+ &request.chain_tip,
+ graph_update.all_anchors(),
+ )?;
+ Ok(FullScanResult {
+ chain_update,
+ graph_update,
last_active_indices,
})
}
- fn sync(
- &self,
- local_tip: CheckPoint,
- misc_spks: impl IntoIterator<Item = ScriptBuf>,
- txids: impl IntoIterator<Item = Txid>,
- outpoints: impl IntoIterator<Item = OutPoint>,
- parallel_requests: usize,
- ) -> Result<SyncUpdate, Error> {
+ fn sync(&self, request: SyncRequest, parallel_requests: usize) -> Result<SyncResult, Error> {
let latest_blocks = fetch_latest_blocks(self)?;
- let tx_graph = sync_for_index_and_graph_blocking(
+ let graph_update = sync_for_index_and_graph_blocking(
self,
- misc_spks,
- txids,
- outpoints,
+ request.spks,
+ request.txids,
+ request.outpoints,
parallel_requests,
)?;
- let local_chain = chain_update(self, &latest_blocks, &local_tip, tx_graph.all_anchors())?;
- Ok(SyncUpdate {
- local_chain,
- tx_graph,
+ let chain_update = chain_update(
+ self,
+ &latest_blocks,
+ &request.chain_tip,
+ graph_update.all_anchors(),
+ )?;
+ Ok(SyncResult {
+ chain_update,
+ graph_update,
})
}
}
//! [`TxGraph`]: bdk_chain::tx_graph::TxGraph
//! [`example_esplora`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_esplora
-use std::collections::BTreeMap;
-
-use bdk_chain::{local_chain::CheckPoint, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
+use bdk_chain::{BlockId, ConfirmationTimeHeightAnchor};
use esplora_client::TxStatus;
pub use esplora_client;
None
}
}
-
-/// Update returns from a full scan.
-pub struct FullScanUpdate<K> {
- /// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
- pub local_chain: CheckPoint,
- /// The update to apply to the receiving [`TxGraph`].
- pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
- /// Last active indices for the corresponding keychains (`K`).
- pub last_active_indices: BTreeMap<K, u32>,
-}
-
-/// Update returned from a sync.
-pub struct SyncUpdate {
- /// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
- pub local_chain: CheckPoint,
- /// The update to apply to the receiving [`TxGraph`].
- pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
-}
+use bdk_chain::spk_client::{FullScanRequest, SyncRequest};
use bdk_esplora::EsploraAsyncExt;
use electrsd::bitcoind::anyhow;
use electrsd::bitcoind::bitcoincore_rpc::RpcApi;
use esplora_client::{self, Builder};
-use std::collections::{BTreeMap, BTreeSet, HashSet};
+use std::collections::{BTreeSet, HashSet};
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
// use a full checkpoint linked list (since this is not what we are testing)
let cp_tip = env.make_checkpoint_tip();
- let sync_update = client
- .sync(
- cp_tip.clone(),
- misc_spks.into_iter(),
- vec![].into_iter(),
- vec![].into_iter(),
- 1,
- )
- .await?;
+ let sync_update = {
+ let request = SyncRequest::from_chain_tip(cp_tip.clone()).set_spks(misc_spks);
+ client.sync(request, 1).await?
+ };
assert!(
{
let update_cps = sync_update
- .local_chain
+ .chain_update
.iter()
.map(|cp| cp.block_id())
.collect::<BTreeSet<_>>();
"update should not alter original checkpoint tip since we already started with all checkpoints",
);
- let graph_update = sync_update.tx_graph;
+ let graph_update = sync_update.graph_update;
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
for tx in graph_update.full_txs() {
.enumerate()
.map(|(i, addr)| (i as u32, addr.script_pubkey()))
.collect();
- let mut keychains = BTreeMap::new();
- keychains.insert(0, spks);
// Then receive coins on the 4th address.
let txid_4th_addr = env.bitcoind.client.send_to_address(
// A scan with a gap limit of 3 won't find the transaction, but a scan with a gap limit of 4
// will.
- let full_scan_update = client
- .full_scan(cp_tip.clone(), keychains.clone(), 3, 1)
- .await?;
- assert!(full_scan_update.tx_graph.full_txs().next().is_none());
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 3, 1).await?
+ };
+ assert!(full_scan_update.graph_update.full_txs().next().is_none());
assert!(full_scan_update.last_active_indices.is_empty());
- let full_scan_update = client
- .full_scan(cp_tip.clone(), keychains.clone(), 4, 1)
- .await?;
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 4, 1).await?
+ };
assert_eq!(
- full_scan_update.tx_graph.full_txs().next().unwrap().txid,
+ full_scan_update
+ .graph_update
+ .full_txs()
+ .next()
+ .unwrap()
+ .txid,
txid_4th_addr
);
assert_eq!(full_scan_update.last_active_indices[&0], 3);
// A scan with gap limit 5 won't find the second transaction, but a scan with gap limit 6 will.
// The last active indice won't be updated in the first case but will in the second one.
- let full_scan_update = client
- .full_scan(cp_tip.clone(), keychains.clone(), 5, 1)
- .await?;
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 5, 1).await?
+ };
let txs: HashSet<_> = full_scan_update
- .tx_graph
+ .graph_update
.full_txs()
.map(|tx| tx.txid)
.collect();
assert_eq!(txs.len(), 1);
assert!(txs.contains(&txid_4th_addr));
assert_eq!(full_scan_update.last_active_indices[&0], 3);
- let full_scan_update = client.full_scan(cp_tip, keychains, 6, 1).await?;
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 6, 1).await?
+ };
let txs: HashSet<_> = full_scan_update
- .tx_graph
+ .graph_update
.full_txs()
.map(|tx| tx.txid)
.collect();
+use bdk_chain::spk_client::{FullScanRequest, SyncRequest};
use bdk_esplora::EsploraExt;
use electrsd::bitcoind::anyhow;
use electrsd::bitcoind::bitcoincore_rpc::RpcApi;
use esplora_client::{self, Builder};
-use std::collections::{BTreeMap, BTreeSet, HashSet};
+use std::collections::{BTreeSet, HashSet};
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
// use a full checkpoint linked list (since this is not what we are testing)
let cp_tip = env.make_checkpoint_tip();
- let sync_update = client.sync(
- cp_tip.clone(),
- misc_spks.into_iter(),
- vec![].into_iter(),
- vec![].into_iter(),
- 1,
- )?;
+ let sync_update = {
+ let request = SyncRequest::from_chain_tip(cp_tip.clone()).set_spks(misc_spks);
+ client.sync(request, 1)?
+ };
assert!(
{
let update_cps = sync_update
- .local_chain
+ .chain_update
.iter()
.map(|cp| cp.block_id())
.collect::<BTreeSet<_>>();
"update should not alter original checkpoint tip since we already started with all checkpoints",
);
- let graph_update = sync_update.tx_graph;
+ let graph_update = sync_update.graph_update;
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
for tx in graph_update.full_txs() {
.enumerate()
.map(|(i, addr)| (i as u32, addr.script_pubkey()))
.collect();
- let mut keychains = BTreeMap::new();
- keychains.insert(0, spks);
// Then receive coins on the 4th address.
let txid_4th_addr = env.bitcoind.client.send_to_address(
// A scan with a stop_gap of 3 won't find the transaction, but a scan with a gap limit of 4
// will.
- let full_scan_update = client.full_scan(cp_tip.clone(), keychains.clone(), 3, 1)?;
- assert!(full_scan_update.tx_graph.full_txs().next().is_none());
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 3, 1)?
+ };
+ assert!(full_scan_update.graph_update.full_txs().next().is_none());
assert!(full_scan_update.last_active_indices.is_empty());
- let full_scan_update = client.full_scan(cp_tip.clone(), keychains.clone(), 4, 1)?;
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 4, 1)?
+ };
assert_eq!(
- full_scan_update.tx_graph.full_txs().next().unwrap().txid,
+ full_scan_update
+ .graph_update
+ .full_txs()
+ .next()
+ .unwrap()
+ .txid,
txid_4th_addr
);
assert_eq!(full_scan_update.last_active_indices[&0], 3);
// A scan with gap limit 5 won't find the second transaction, but a scan with gap limit 6 will.
// The last active indice won't be updated in the first case but will in the second one.
- let full_scan_update = client.full_scan(cp_tip.clone(), keychains.clone(), 5, 1)?;
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 5, 1)?
+ };
let txs: HashSet<_> = full_scan_update
- .tx_graph
+ .graph_update
.full_txs()
.map(|tx| tx.txid)
.collect();
assert_eq!(txs.len(), 1);
assert!(txs.contains(&txid_4th_addr));
assert_eq!(full_scan_update.last_active_indices[&0], 3);
- let full_scan_update = client.full_scan(cp_tip.clone(), keychains, 6, 1)?;
+ let full_scan_update = {
+ let request =
+ FullScanRequest::from_chain_tip(cp_tip.clone()).set_spks_for_keychain(0, spks.clone());
+ client.full_scan(request, 6, 1)?
+ };
let txs: HashSet<_> = full_scan_update
- .tx_graph
+ .graph_update
.full_txs()
.map(|tx| tx.txid)
.collect();
use std::{
- collections::BTreeMap,
+ collections::BTreeSet,
io::{self, Write},
sync::Mutex,
};
use bdk_chain::{
- bitcoin::{constants::genesis_block, Address, Network, OutPoint, ScriptBuf, Txid},
+ bitcoin::{constants::genesis_block, Address, Network, Txid},
indexed_tx_graph::{self, IndexedTxGraph},
keychain,
local_chain::{self, LocalChain},
+ spk_client::{FullScanRequest, SyncRequest},
Append, ConfirmationTimeHeightAnchor,
};
scan_options,
..
} => {
- let local_tip = chain.lock().expect("mutex must not be poisoned").tip();
- let keychain_spks = graph
- .lock()
- .expect("mutex must not be poisoned")
- .index
- .all_unbounded_spk_iters()
- .into_iter()
- // This `map` is purely for logging.
- .map(|(keychain, iter)| {
- let mut first = true;
- let spk_iter = iter.inspect(move |(i, _)| {
- if first {
- eprint!("\nscanning {}: ", keychain);
- first = false;
+ let request = {
+ let chain_tip = chain.lock().expect("mutex must not be poisoned").tip();
+ let indexed_graph = &*graph.lock().expect("mutex must not be poisoned");
+ FullScanRequest::from_keychain_txout_index(chain_tip, &indexed_graph.index)
+ .inspect_spks_for_all_keychains({
+ let mut once = BTreeSet::<Keychain>::new();
+ move |keychain, spk_i, _| {
+ if once.insert(keychain) {
+ eprint!("\nscanning {}: ", keychain);
+ }
+ eprint!("{} ", spk_i);
+ // Flush early to ensure we print at every iteration.
+ let _ = io::stderr().flush();
}
- eprint!("{} ", i);
- // Flush early to ensure we print at every iteration.
- let _ = io::stderr().flush();
- });
- (keychain, spk_iter)
- })
- .collect::<BTreeMap<_, _>>();
+ })
+ };
// The client scans keychain spks for transaction histories, stopping after `stop_gap`
// is reached. It returns a `TxGraph` update (`graph_update`) and a structure that
// represents the last active spk derivation indices of keychains
// (`keychain_indices_update`).
let mut update = client
- .full_scan(
- local_tip,
- keychain_spks,
- *stop_gap,
- scan_options.parallel_requests,
- )
+ .full_scan(request, *stop_gap, scan_options.parallel_requests)
.context("scanning for transactions")?;
// We want to keep track of the latest time a transaction was seen unconfirmed.
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
- let _ = update.tx_graph.update_last_seen_unconfirmed(now);
+ let _ = update.graph_update.update_last_seen_unconfirmed(now);
let mut graph = graph.lock().expect("mutex must not be poisoned");
let mut chain = chain.lock().expect("mutex must not be poisoned");
// deriviation indices. Usually before a scan you are on a fresh wallet with no
// addresses derived so we need to derive up to last active addresses the scan found
// before adding the transactions.
- (chain.apply_update(update.local_chain)?, {
+ (chain.apply_update(update.chain_update)?, {
let (_, index_changeset) = graph
.index
.reveal_to_target_multi(&update.last_active_indices);
- let mut indexed_tx_graph_changeset = graph.apply_update(update.tx_graph);
+ let mut indexed_tx_graph_changeset = graph.apply_update(update.graph_update);
indexed_tx_graph_changeset.append(index_changeset.into());
indexed_tx_graph_changeset
})
unused_spks = false;
}
- // Spks, outpoints and txids we want updates on will be accumulated here.
- let mut spks: Box<dyn Iterator<Item = ScriptBuf>> = Box::new(core::iter::empty());
- let mut outpoints: Box<dyn Iterator<Item = OutPoint>> = Box::new(core::iter::empty());
- let mut txids: Box<dyn Iterator<Item = Txid>> = Box::new(core::iter::empty());
-
let local_tip = chain.lock().expect("mutex must not be poisoned").tip();
+ // Spks, outpoints and txids we want updates on will be accumulated here.
+ let mut request = SyncRequest::from_chain_tip(local_tip.clone());
// Get a short lock on the structures to get spks, utxos, and txs that we are interested
// in.
.revealed_spks(..)
.map(|(k, i, spk)| (k.to_owned(), i, spk.to_owned()))
.collect::<Vec<_>>();
- spks = Box::new(spks.chain(all_spks.into_iter().map(|(k, i, spk)| {
+ request = request.chain_spks(all_spks.into_iter().map(|(k, i, spk)| {
eprintln!("scanning {}:{}", k, i);
// Flush early to ensure we print at every iteration.
let _ = io::stderr().flush();
spk
- })));
+ }));
}
if unused_spks {
let unused_spks = graph
.unused_spks()
.map(|(k, i, spk)| (k, i, spk.to_owned()))
.collect::<Vec<_>>();
- spks = Box::new(spks.chain(unused_spks.into_iter().map(|(k, i, spk)| {
- eprintln!(
- "Checking if address {} {}:{} has been used",
- Address::from_script(&spk, args.network).unwrap(),
- k,
- i,
- );
- // Flush early to ensure we print at every iteration.
- let _ = io::stderr().flush();
- spk
- })));
+ request =
+ request.chain_spks(unused_spks.into_iter().map(move |(k, i, spk)| {
+ eprintln!(
+ "Checking if address {} {}:{} has been used",
+ Address::from_script(&spk, args.network).unwrap(),
+ k,
+ i,
+ );
+ // Flush early to ensure we print at every iteration.
+ let _ = io::stderr().flush();
+ spk
+ }));
}
if utxos {
// We want to search for whether the UTXO is spent, and spent by which
.filter_chain_unspents(&*chain, local_tip.block_id(), init_outpoints)
.map(|(_, utxo)| utxo)
.collect::<Vec<_>>();
- outpoints = Box::new(
+ request = request.chain_outpoints(
utxos
.into_iter()
.inspect(|utxo| {
.filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed())
.map(|canonical_tx| canonical_tx.tx_node.txid)
.collect::<Vec<Txid>>();
- txids = Box::new(unconfirmed_txids.into_iter().inspect(|txid| {
+ request = request.chain_txids(unconfirmed_txids.into_iter().inspect(|txid| {
eprintln!("Checking if {} is confirmed yet", txid);
// Flush early to ensure we print at every iteration.
let _ = io::stderr().flush();
}
}
- let mut update = client.sync(
- local_tip,
- spks,
- txids,
- outpoints,
- scan_options.parallel_requests,
- )?;
+ let mut update = client.sync(request, scan_options.parallel_requests)?;
// Update last seen unconfirmed
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
- let _ = update.tx_graph.update_last_seen_unconfirmed(now);
+ let _ = update.graph_update.update_last_seen_unconfirmed(now);
(
- chain.lock().unwrap().apply_update(update.local_chain)?,
- graph.lock().unwrap().apply_update(update.tx_graph),
+ chain.lock().unwrap().apply_update(update.chain_update)?,
+ graph.lock().unwrap().apply_update(update.graph_update),
)
}
};
-use std::{io::Write, str::FromStr};
+use std::{collections::BTreeSet, io::Write, str::FromStr};
use bdk::{
- bitcoin::{Address, Network},
- wallet::Update,
+ bitcoin::{Address, Network, Script},
KeychainKind, SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraAsyncExt};
let client =
esplora_client::Builder::new("https://blockstream.info/testnet/api").build_async()?;
- let prev_tip = wallet.latest_checkpoint();
- let keychain_spks = wallet
- .all_unbounded_spk_iters()
- .into_iter()
- .map(|(k, k_spks)| {
- let mut once = Some(());
- let mut stdout = std::io::stdout();
- let k_spks = k_spks
- .inspect(move |(spk_i, _)| match once.take() {
- Some(_) => print!("\nScanning keychain [{:?}]", k),
- None => print!(" {:<3}", spk_i),
- })
- .inspect(move |_| stdout.flush().expect("must flush"));
- (k, k_spks)
+ fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static {
+ let mut once = Some(());
+ let mut stdout = std::io::stdout();
+ move |spk_i, _| {
+ match once.take() {
+ Some(_) => print!("\nScanning keychain [{:?}]", kind),
+ None => print!(" {:<3}", spk_i),
+ };
+ stdout.flush().expect("must flush");
+ }
+ }
+ let request = wallet
+ .start_full_scan()
+ .inspect_spks_for_all_keychains({
+ let mut once = BTreeSet::<KeychainKind>::new();
+ move |keychain, spk_i, _| {
+ match once.insert(keychain) {
+ true => print!("\nScanning keychain [{:?}]", keychain),
+ false => print!(" {:<3}", spk_i),
+ }
+ std::io::stdout().flush().expect("must flush")
+ }
})
- .collect();
+ .inspect_spks_for_keychain(
+ KeychainKind::External,
+ generate_inspect(KeychainKind::External),
+ )
+ .inspect_spks_for_keychain(
+ KeychainKind::Internal,
+ generate_inspect(KeychainKind::Internal),
+ );
let mut update = client
- .full_scan(prev_tip, keychain_spks, STOP_GAP, PARALLEL_REQUESTS)
+ .full_scan(request, STOP_GAP, PARALLEL_REQUESTS)
.await?;
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
- let _ = update.tx_graph.update_last_seen_unconfirmed(now);
+ let _ = update.graph_update.update_last_seen_unconfirmed(now);
- let update = Update {
- last_active_indices: update.last_active_indices,
- graph: update.tx_graph,
- chain: Some(update.local_chain),
- };
wallet.apply_update(update)?;
wallet.commit()?;
println!();
const STOP_GAP: usize = 5;
const PARALLEL_REQUESTS: usize = 1;
-use std::{io::Write, str::FromStr};
+use std::{collections::BTreeSet, io::Write, str::FromStr};
use bdk::{
bitcoin::{Address, Network},
- wallet::Update,
KeychainKind, SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraExt};
let client =
esplora_client::Builder::new("https://blockstream.info/testnet/api").build_blocking();
- let keychain_spks = wallet
- .all_unbounded_spk_iters()
- .into_iter()
- .map(|(k, k_spks)| {
- let mut once = Some(());
- let mut stdout = std::io::stdout();
- let k_spks = k_spks
- .inspect(move |(spk_i, _)| match once.take() {
- Some(_) => print!("\nScanning keychain [{:?}]", k),
- None => print!(" {:<3}", spk_i),
- })
- .inspect(move |_| stdout.flush().expect("must flush"));
- (k, k_spks)
- })
- .collect();
-
- let mut update = client.full_scan(
- wallet.latest_checkpoint(),
- keychain_spks,
- STOP_GAP,
- PARALLEL_REQUESTS,
- )?;
+ let request = wallet.start_full_scan().inspect_spks_for_all_keychains({
+ let mut once = BTreeSet::<KeychainKind>::new();
+ move |keychain, spk_i, _| {
+ match once.insert(keychain) {
+ true => print!("\nScanning keychain [{:?}]", keychain),
+ false => print!(" {:<3}", spk_i),
+ };
+ std::io::stdout().flush().expect("must flush")
+ }
+ });
+
+ let mut update = client.full_scan(request, STOP_GAP, PARALLEL_REQUESTS)?;
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
- let _ = update.tx_graph.update_last_seen_unconfirmed(now);
+ let _ = update.graph_update.update_last_seen_unconfirmed(now);
- wallet.apply_update(Update {
- last_active_indices: update.last_active_indices,
- graph: update.tx_graph,
- chain: Some(update.local_chain),
- })?;
+ wallet.apply_update(update)?;
wallet.commit()?;
println!();