/// Trait to extend [`Client`] functionality.
pub trait ElectrumExt {
- /// Scan the blockchain (via electrum) for the data specified and returns updates for
- /// [`bdk_chain`] data structures.
+ /// Full scan the keychain scripts specified with the blockchain (via an Electrum client) and
+ /// returns updates for [`bdk_chain`] data structures.
///
/// - `prev_tip`: the most recent blockchain tip present locally
/// - `keychain_spks`: keychains that we want to scan transactions for
- /// - `txids`: transactions for which we want updated [`Anchor`]s
- /// - `outpoints`: transactions associated with these outpoints (residing, spending) that we
- /// want to included 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. `batch_size` specifies the max number of script pubkeys to request for in a
/// single batch request.
- fn scan<K: Ord + Clone>(
+ fn full_scan<K: Ord + Clone>(
&self,
prev_tip: CheckPoint,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
- txids: impl IntoIterator<Item = Txid>,
- outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize,
batch_size: usize,
) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error>;
- /// Convenience method to call [`scan`] without requiring a keychain.
+ /// Sync a set of scripts with the blockchain (via an Electrum client) for the data specified
+ /// and returns updates for [`bdk_chain`] data structures.
///
- /// [`scan`]: ElectrumExt::scan
- fn scan_without_keychain(
+ /// - `prev_tip`: the most recent blockchain tip present locally
+ /// - `misc_spks`: an iterator of scripts we want to sync transactions for
+ /// - `txids`: transactions for which we want updated [`Anchor`]s
+ /// - `outpoints`: transactions associated with these outpoints (residing, spending) that we
+ /// want to include in the update
+ ///
+ /// `batch_size` specifies the max number of script pubkeys to request for in a single batch
+ /// request.
+ ///
+ /// 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.
+ ///
+ /// [`full_scan`]: ElectrumExt::full_scan
+ fn sync(
&self,
prev_tip: CheckPoint,
misc_spks: impl IntoIterator<Item = ScriptBuf>,
txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>,
batch_size: usize,
- ) -> Result<ElectrumUpdate, Error> {
- let spk_iter = misc_spks
- .into_iter()
- .enumerate()
- .map(|(i, spk)| (i as u32, spk));
-
- let (electrum_update, _) = self.scan(
- prev_tip,
- [((), spk_iter)].into(),
- txids,
- outpoints,
- usize::MAX,
- batch_size,
- )?;
-
- Ok(electrum_update)
- }
+ ) -> Result<ElectrumUpdate, Error>;
}
impl ElectrumExt for Client {
- fn scan<K: Ord + Clone>(
+ fn full_scan<K: Ord + Clone>(
&self,
prev_tip: CheckPoint,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
- txids: impl IntoIterator<Item = Txid>,
- outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize,
batch_size: usize,
) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error> {
.collect::<BTreeMap<K, _>>();
let mut scanned_spks = BTreeMap::<(K, u32), (ScriptBuf, bool)>::new();
- let txids = txids.into_iter().collect::<Vec<_>>();
- let outpoints = outpoints.into_iter().collect::<Vec<_>>();
-
let (electrum_update, keychain_update) = loop {
let (tip, _) = construct_update_tip(self, prev_tip.clone())?;
let mut relevant_txids = RelevantTxids::default();
}
}
- populate_with_txids(self, &cps, &mut relevant_txids, &mut txids.iter().cloned())?;
-
- let _txs = populate_with_outpoints(
- self,
- &cps,
- &mut relevant_txids,
- &mut outpoints.iter().cloned(),
- )?;
-
// check for reorgs during scan process
let server_blockhash = self.block_header(tip.height() as usize)?.block_hash();
if tip.hash() != server_blockhash {
Ok((electrum_update, keychain_update))
}
+
+ fn sync(
+ &self,
+ prev_tip: CheckPoint,
+ misc_spks: impl IntoIterator<Item = ScriptBuf>,
+ txids: impl IntoIterator<Item = Txid>,
+ outpoints: impl IntoIterator<Item = OutPoint>,
+ batch_size: usize,
+ ) -> Result<ElectrumUpdate, Error> {
+ let spk_iter = misc_spks
+ .into_iter()
+ .enumerate()
+ .map(|(i, spk)| (i as u32, spk));
+
+ let (mut electrum_update, _) = self.full_scan(
+ prev_tip.clone(),
+ [((), spk_iter)].into(),
+ usize::MAX,
+ batch_size,
+ )?;
+
+ let (tip, _) = construct_update_tip(self, prev_tip)?;
+ let cps = tip
+ .iter()
+ .take(10)
+ .map(|cp| (cp.height(), cp))
+ .collect::<BTreeMap<u32, CheckPoint>>();
+
+ populate_with_txids(self, &cps, &mut electrum_update.relevant_txids, txids)?;
+
+ let _txs =
+ populate_with_outpoints(self, &cps, &mut electrum_update.relevant_txids, outpoints)?;
+
+ Ok(electrum_update)
+ }
}
/// Return a [`CheckPoint`] of the latest tip, that connects with `prev_tip`.
client: &Client,
cps: &BTreeMap<u32, CheckPoint>,
relevant_txids: &mut RelevantTxids,
- outpoints: &mut impl Iterator<Item = OutPoint>,
+ outpoints: impl IntoIterator<Item = OutPoint>,
) -> Result<HashMap<Txid, Transaction>, Error> {
let mut full_txs = HashMap::new();
for outpoint in outpoints {
client: &Client,
cps: &BTreeMap<u32, CheckPoint>,
relevant_txids: &mut RelevantTxids,
- txids: &mut impl Iterator<Item = Txid>,
+ txids: impl IntoIterator<Item = Txid>,
) -> Result<(), Error> {
for txid in txids {
let tx = match client.transaction_get(&txid) {
-//! This crate is used for updating structures of the [`bdk_chain`] crate with data from electrum.
+//! This crate is used for updating structures of [`bdk_chain`] with data from an Electrum server.
//!
-//! The star of the show is the [`ElectrumExt::scan`] method, which scans for relevant blockchain
-//! data (via electrum) and outputs updates for [`bdk_chain`] structures as a tuple of form:
+//! The two primary methods are [`ElectrumExt::sync`] and [`ElectrumExt::full_scan`]. In most cases
+//! [`ElectrumExt::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. [`ElectrumExt::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`] including [`RelevantTxids`].
//!
-//! ([`bdk_chain::local_chain::Update`], [`RelevantTxids`], `keychain_update`)
+//! The [`RelevantTxids`] only includes `txid`s and not full transactions. The caller is responsible
+//! for obtaining full transactions before applying new data to their [`bdk_chain`]. This can be
+//! done with these steps:
//!
-//! An [`RelevantTxids`] only includes `txid`s and no full transactions. The caller is
-//! responsible for obtaining full transactions before applying. This can be done with
-//! these steps:
+//! 1. Determine which full transactions are missing. Use [`RelevantTxids::missing_full_txs`].
//!
-//! 1. Determine which full transactions are missing. The method [`missing_full_txs`] of
-//! [`RelevantTxids`] can be used.
+//! 2. Obtaining the full transactions. To do this via electrum use [`ElectrumApi::batch_transaction_get`].
//!
-//! 2. Obtaining the full transactions. To do this via electrum, the method
-//! [`batch_transaction_get`] can be used.
+//! Refer to [`example_electrum`] for a complete example.
//!
-//! Refer to [`bdk_electrum_example`] for a complete example.
-//!
-//! [`ElectrumClient::scan`]: electrum_client::ElectrumClient::scan
-//! [`missing_full_txs`]: RelevantTxids::missing_full_txs
-//! [`batch_transaction_get`]: electrum_client::ElectrumApi::batch_transaction_get
-//! [`bdk_electrum_example`]: https://github.com/LLFourn/bdk_core_staging/tree/master/bdk_electrum_example
+//! [`ElectrumApi::batch_transaction_get`]: electrum_client::ElectrumApi::batch_transaction_get
+//! [`example_electrum`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_electrum
#![warn(missing_docs)]