]> Untitled Git - bdk/commitdiff
refactor(electrum_ext): rename scan_without_keychain to sync and scan to full_scan
authorSteve Myers <steve@notmandatory.org>
Thu, 7 Dec 2023 03:14:16 +0000 (21:14 -0600)
committerSteve Myers <steve@notmandatory.org>
Fri, 5 Jan 2024 21:31:12 +0000 (15:31 -0600)
removed txids and outpoints params from full_scan

crates/electrum/README.md
crates/electrum/src/electrum_ext.rs
crates/electrum/src/lib.rs
example-crates/example_electrum/src/main.rs
example-crates/wallet_electrum/src/main.rs

index d3ada695e6913758fa38cdd2d60c79f1bddd4078..1bafe04eb4b888e838891111091714f4299f7166 100644 (file)
@@ -1,3 +1,7 @@
 # BDK Electrum
 
-BDK Electrum client library for updating the keychain tracker.
+BDK Electrum extends [`electrum-client`] to update [`bdk_chain`] structures
+from an Electrum server.
+
+[`electrum-client`]: https://docs.rs/electrum-client/
+[`bdk_chain`]: https://docs.rs/bdk-chain/
index 216755a1364b7cd656246c7fb17a016e2a45628a..1c9ce9890ddec11638d9d6400a6195e3023befaa 100644 (file)
@@ -134,64 +134,54 @@ pub struct ElectrumUpdate {
 
 /// 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> {
@@ -201,9 +191,6 @@ impl ElectrumExt for Client {
             .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();
@@ -242,15 +229,6 @@ impl ElectrumExt for Client {
                 }
             }
 
-            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 {
@@ -284,6 +262,41 @@ impl ElectrumExt for Client {
 
         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`.
@@ -405,7 +418,7 @@ fn populate_with_outpoints(
     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 {
@@ -466,7 +479,7 @@ fn populate_with_txids(
     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) {
index 1e4805379c63a319f9b27261b049582876d85d6a..87c0e46188cb7fad41f210392366529313c438fd 100644 (file)
@@ -1,26 +1,26 @@
-//! 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)]
 
index a96378f64750257b045c65047c209f255e0b8273..0041a20c8ab9d3238a28db5fcdbc3191f0967922 100644 (file)
@@ -172,14 +172,7 @@ fn main() -> anyhow::Result<()> {
             };
 
             client
-                .scan(
-                    tip,
-                    keychain_spks,
-                    core::iter::empty(),
-                    core::iter::empty(),
-                    stop_gap,
-                    scan_options.batch_size,
-                )
+                .full_scan(tip, keychain_spks, stop_gap, scan_options.batch_size)
                 .context("scanning the blockchain")?
         }
         ElectrumCommands::Sync {
@@ -279,7 +272,7 @@ fn main() -> anyhow::Result<()> {
             drop((graph, chain));
 
             let electrum_update = client
-                .scan_without_keychain(tip, spks, txids, outpoints, scan_options.batch_size)
+                .sync(tip, spks, txids, outpoints, scan_options.batch_size)
                 .context("scanning the blockchain")?;
             (electrum_update, BTreeMap::new())
         }
index 9d4c6c5a4f27759ce4a6a90f485849fa2792ba81..e5c6b146627a00d305eeccaec2952edff34bc34c 100644 (file)
@@ -61,7 +61,7 @@ fn main() -> Result<(), anyhow::Error> {
             relevant_txids,
         },
         keychain_update,
-    ) = client.scan(prev_tip, keychain_spks, None, None, STOP_GAP, BATCH_SIZE)?;
+    ) = client.full_scan(prev_tip, keychain_spks, STOP_GAP, BATCH_SIZE)?;
 
     println!();