]> Untitled Git - bdk/commitdiff
feat!: move `spk_client`s to `bdk_core`
author志宇 <hello@evanlinjin.me>
Fri, 23 Aug 2024 18:12:46 +0000 (18:12 +0000)
committer志宇 <hello@evanlinjin.me>
Sat, 24 Aug 2024 16:17:47 +0000 (16:17 +0000)
Also introduced extension trait for builder methods that take in a
`KeychainTxOutIndex`.

`Indexed<T>` and `KeychainIndexed<T>` are also moved to `bdk_core`.

crates/chain/src/indexer/keychain_txout.rs
crates/chain/src/lib.rs
crates/chain/src/spk_client.rs [deleted file]
crates/core/Cargo.toml
crates/core/src/lib.rs
crates/core/src/spk_client.rs [new file with mode: 0644]
crates/testenv/src/lib.rs
crates/wallet/src/wallet/mod.rs
example-crates/example_esplora/src/main.rs

index ce9707c7b52e14cbb7daf0e87e991231287fbaf8..c432080957891d25de8f08eb6ea2aadd9f42b30c 100644 (file)
@@ -4,6 +4,7 @@
 use crate::{
     collections::*,
     miniscript::{Descriptor, DescriptorPublicKey},
+    spk_client::{FullScanRequestBuilder, SyncRequestBuilder},
     spk_iter::BIP32_MAX_INDEX,
     spk_txout::SpkTxOutIndex,
     DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
@@ -875,3 +876,43 @@ impl Merge for ChangeSet {
         self.last_revealed.is_empty()
     }
 }
+
+/// Trait to extend [`SyncRequestBuilder`].
+pub trait SyncRequestBuilderExt<K> {
+    /// Add [`Script`](bitcoin::Script)s that are revealed by the `indexer` of the given `spk_range`
+    /// that will be synced against.
+    fn revealed_spks_from_indexer<R>(self, indexer: &KeychainTxOutIndex<K>, spk_range: R) -> Self
+    where
+        R: core::ops::RangeBounds<K>;
+
+    /// Add [`Script`](bitcoin::Script)s that are revealed by the `indexer` but currently unused.
+    fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self;
+}
+
+impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequestBuilder<(K, u32)> {
+    fn revealed_spks_from_indexer<R>(self, indexer: &KeychainTxOutIndex<K>, spk_range: R) -> Self
+    where
+        R: core::ops::RangeBounds<K>,
+    {
+        self.spks_with_indexes(indexer.revealed_spks(spk_range))
+    }
+
+    fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self {
+        self.spks_with_indexes(indexer.unused_spks())
+    }
+}
+
+/// Trait to extend [`FullScanRequestBuilder`].
+pub trait FullScanRequestBuilderExt<K> {
+    /// Add spk iterators for each keychain tracked in `indexer`.
+    fn spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self;
+}
+
+impl<K: Clone + Ord + core::fmt::Debug> FullScanRequestBuilderExt<K> for FullScanRequestBuilder<K> {
+    fn spks_from_indexer(mut self, indexer: &KeychainTxOutIndex<K>) -> Self {
+        for (keychain, spks) in indexer.all_unbounded_spk_iters() {
+            self = self.spks_for_keychain(keychain, spks);
+        }
+        self
+    }
+}
index c1c555961a1d60d81ab1190c4db25872c14f725b..9667bb5498f7c0799a60c39477ec350f3cffff14 100644 (file)
@@ -61,7 +61,6 @@ pub use indexer::keychain_txout;
 pub use spk_iter::*;
 #[cfg(feature = "rusqlite")]
 pub mod rusqlite_impl;
-pub mod spk_client;
 
 pub extern crate bdk_core;
 pub use bdk_core::*;
@@ -81,11 +80,6 @@ extern crate std;
 /// How many confirmations are needed f or a coinbase output to be spent.
 pub const COINBASE_MATURITY: u32 = 100;
 
-/// A tuple of keychain index and `T` representing the indexed value.
-pub type Indexed<T> = (u32, T);
-/// A tuple of keychain `K`, derivation index (`u32`) and a `T` associated with them.
-pub type KeychainIndexed<K, T> = ((K, u32), T);
-
 /// A wrapper that we use to impl remote traits for types in our crate or dependency crates.
 pub struct Impl<T>(pub T);
 
diff --git a/crates/chain/src/spk_client.rs b/crates/chain/src/spk_client.rs
deleted file mode 100644 (file)
index e31b431..0000000
+++ /dev/null
@@ -1,586 +0,0 @@
-//! Helper types for spk-based blockchain clients.
-use crate::{
-    alloc::{boxed::Box, collections::VecDeque, vec::Vec},
-    collections::BTreeMap,
-    local_chain::CheckPoint,
-    ConfirmationBlockTime, Indexed,
-};
-use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
-
-type InspectSync<I> = dyn FnMut(SyncItem<I>, SyncProgress) + Send + 'static;
-
-type InspectFullScan<K> = dyn FnMut(K, u32, &Script) + Send + 'static;
-
-/// An item reported to the [`inspect`](SyncRequestBuilder::inspect) closure of [`SyncRequest`].
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum SyncItem<'i, I> {
-    /// Script pubkey sync item.
-    Spk(I, &'i Script),
-    /// Txid sync item.
-    Txid(Txid),
-    /// Outpoint sync item.
-    OutPoint(OutPoint),
-}
-
-impl<'i, I: core::fmt::Debug + core::any::Any> core::fmt::Display for SyncItem<'i, I> {
-    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        match self {
-            SyncItem::Spk(i, spk) => {
-                if (i as &dyn core::any::Any).is::<()>() {
-                    write!(f, "script '{}'", spk)
-                } else {
-                    write!(f, "script {:?} '{}'", i, spk)
-                }
-            }
-            SyncItem::Txid(txid) => write!(f, "txid '{}'", txid),
-            SyncItem::OutPoint(op) => write!(f, "outpoint '{}'", op),
-        }
-    }
-}
-
-/// The progress of [`SyncRequest`].
-#[derive(Debug, Clone)]
-pub struct SyncProgress {
-    /// Script pubkeys consumed by the request.
-    pub spks_consumed: usize,
-    /// Script pubkeys remaining in the request.
-    pub spks_remaining: usize,
-    /// Txids consumed by the request.
-    pub txids_consumed: usize,
-    /// Txids remaining in the request.
-    pub txids_remaining: usize,
-    /// Outpoints consumed by the request.
-    pub outpoints_consumed: usize,
-    /// Outpoints remaining in the request.
-    pub outpoints_remaining: usize,
-}
-
-impl SyncProgress {
-    /// Total items, consumed and remaining, of the request.
-    pub fn total(&self) -> usize {
-        self.total_spks() + self.total_txids() + self.total_outpoints()
-    }
-
-    /// Total script pubkeys, consumed and remaining, of the request.
-    pub fn total_spks(&self) -> usize {
-        self.spks_consumed + self.spks_remaining
-    }
-
-    /// Total txids, consumed and remaining, of the request.
-    pub fn total_txids(&self) -> usize {
-        self.txids_consumed + self.txids_remaining
-    }
-
-    /// Total outpoints, consumed and remaining, of the request.
-    pub fn total_outpoints(&self) -> usize {
-        self.outpoints_consumed + self.outpoints_remaining
-    }
-
-    /// Total consumed items of the request.
-    pub fn consumed(&self) -> usize {
-        self.spks_consumed + self.txids_consumed + self.outpoints_consumed
-    }
-
-    /// Total remaining items of the request.
-    pub fn remaining(&self) -> usize {
-        self.spks_remaining + self.txids_remaining + self.outpoints_remaining
-    }
-}
-
-/// Builds a [`SyncRequest`].
-#[must_use]
-pub struct SyncRequestBuilder<I = ()> {
-    inner: SyncRequest<I>,
-}
-
-impl<I> Default for SyncRequestBuilder<I> {
-    fn default() -> Self {
-        Self {
-            inner: Default::default(),
-        }
-    }
-}
-
-#[cfg(feature = "miniscript")]
-impl<K: Clone + Ord + core::fmt::Debug + Send + Sync> SyncRequestBuilder<(K, u32)> {
-    /// Add [`Script`]s that are revealed by the `indexer` of the given `spk_range` that will be
-    /// synced against.
-    pub fn revealed_spks_from_indexer(
-        self,
-        indexer: &crate::indexer::keychain_txout::KeychainTxOutIndex<K>,
-        spk_range: impl core::ops::RangeBounds<K>,
-    ) -> Self {
-        self.spks_with_indexes(indexer.revealed_spks(spk_range))
-    }
-
-    /// Add [`Script`]s that are revealed by the `indexer` but currently unused.
-    pub fn unused_spks_from_indexer(
-        self,
-        indexer: &crate::indexer::keychain_txout::KeychainTxOutIndex<K>,
-    ) -> Self {
-        self.spks_with_indexes(indexer.unused_spks())
-    }
-}
-
-impl SyncRequestBuilder<()> {
-    /// Add [`Script`]s that will be synced against.
-    pub fn spks(self, spks: impl IntoIterator<Item = ScriptBuf>) -> Self {
-        self.spks_with_indexes(spks.into_iter().map(|spk| ((), spk)))
-    }
-}
-
-impl<I> SyncRequestBuilder<I> {
-    /// Set the initial chain tip for the sync request.
-    ///
-    /// This is used to update [`LocalChain`](crate::local_chain::LocalChain).
-    pub fn chain_tip(mut self, cp: CheckPoint) -> Self {
-        self.inner.chain_tip = Some(cp);
-        self
-    }
-
-    /// Add [`Script`]s coupled with associated indexes that will be synced against.
-    ///
-    /// # Example
-    ///
-    /// Sync revealed script pubkeys obtained from a
-    /// [`KeychainTxOutIndex`](crate::keychain_txout::KeychainTxOutIndex).
-    ///
-    /// ```rust
-    /// # use bdk_chain::spk_client::SyncRequest;
-    /// # use bdk_chain::indexer::keychain_txout::KeychainTxOutIndex;
-    /// # use bdk_chain::miniscript::{Descriptor, DescriptorPublicKey};
-    /// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
-    /// # let (descriptor_a,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
-    /// # let (descriptor_b,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
-    /// let mut indexer = KeychainTxOutIndex::<&'static str>::default();
-    /// indexer.insert_descriptor("descriptor_a", descriptor_a)?;
-    /// indexer.insert_descriptor("descriptor_b", descriptor_b)?;
-    ///
-    /// /* Assume that the caller does more mutations to the `indexer` here... */
-    ///
-    /// // Reveal spks for "descriptor_a", then build a sync request. Each spk will be indexed with
-    /// // `u32`, which represents the derivation index of the associated spk from "descriptor_a".
-    /// let (newly_revealed_spks, _changeset) = indexer
-    ///     .reveal_to_target("descriptor_a", 21)
-    ///     .expect("keychain must exist");
-    /// let _request = SyncRequest::builder()
-    ///     .spks_with_indexes(newly_revealed_spks)
-    ///     .build();
-    ///
-    /// // Sync all revealed spks in the indexer. This time, spks may be derived from different
-    /// // keychains. Each spk will be indexed with `(&'static str, u32)` where `&'static str` is
-    /// // the keychain identifier and `u32` is the derivation index.
-    /// let all_revealed_spks = indexer.revealed_spks(..);
-    /// let _request = SyncRequest::builder()
-    ///     .spks_with_indexes(all_revealed_spks)
-    ///     .build();
-    /// # Ok::<_, bdk_chain::keychain_txout::InsertDescriptorError<_>>(())
-    /// ```
-    pub fn spks_with_indexes(mut self, spks: impl IntoIterator<Item = (I, ScriptBuf)>) -> Self {
-        self.inner.spks.extend(spks);
-        self
-    }
-
-    /// Add [`Txid`]s that will be synced against.
-    pub fn txids(mut self, txids: impl IntoIterator<Item = Txid>) -> Self {
-        self.inner.txids.extend(txids);
-        self
-    }
-
-    /// Add [`OutPoint`]s that will be synced against.
-    pub fn outpoints(mut self, outpoints: impl IntoIterator<Item = OutPoint>) -> Self {
-        self.inner.outpoints.extend(outpoints);
-        self
-    }
-
-    /// Set the closure that will inspect every sync item visited.
-    pub fn inspect<F>(mut self, inspect: F) -> Self
-    where
-        F: FnMut(SyncItem<I>, SyncProgress) + Send + 'static,
-    {
-        self.inner.inspect = Box::new(inspect);
-        self
-    }
-
-    /// Build the [`SyncRequest`].
-    pub fn build(self) -> SyncRequest<I> {
-        self.inner
-    }
-}
-
-/// Data required to perform a spk-based blockchain client sync.
-///
-/// A client sync fetches relevant chain data for a known list of scripts, transaction ids and
-/// outpoints. The sync process also updates the chain from the given
-/// [`chain_tip`](SyncRequestBuilder::chain_tip) (if provided).
-///
-/// ```rust
-/// # use bdk_chain::{bitcoin::{hashes::Hash, ScriptBuf}, local_chain::LocalChain};
-/// # let (local_chain, _) = LocalChain::from_genesis_hash(Hash::all_zeros());
-/// # let scripts = [ScriptBuf::default(), ScriptBuf::default()];
-/// # use bdk_chain::spk_client::SyncRequest;
-/// // Construct a sync request.
-/// let sync_request = SyncRequest::builder()
-///     // Provide chain tip of the local wallet.
-///     .chain_tip(local_chain.tip())
-///     // Provide list of scripts to scan for transactions against.
-///     .spks(scripts)
-///     // This is called for every synced item.
-///     .inspect(|item, progress| println!("{} (remaining: {})", item, progress.remaining()))
-///     // Finish constructing the sync request.
-///     .build();
-/// ```
-#[must_use]
-pub struct SyncRequest<I = ()> {
-    chain_tip: Option<CheckPoint>,
-    spks: VecDeque<(I, ScriptBuf)>,
-    spks_consumed: usize,
-    txids: VecDeque<Txid>,
-    txids_consumed: usize,
-    outpoints: VecDeque<OutPoint>,
-    outpoints_consumed: usize,
-    inspect: Box<InspectSync<I>>,
-}
-
-impl<I> Default for SyncRequest<I> {
-    fn default() -> Self {
-        Self {
-            chain_tip: None,
-            spks: VecDeque::new(),
-            spks_consumed: 0,
-            txids: VecDeque::new(),
-            txids_consumed: 0,
-            outpoints: VecDeque::new(),
-            outpoints_consumed: 0,
-            inspect: Box::new(|_, _| {}),
-        }
-    }
-}
-
-impl<I> From<SyncRequestBuilder<I>> for SyncRequest<I> {
-    fn from(builder: SyncRequestBuilder<I>) -> Self {
-        builder.inner
-    }
-}
-
-impl<I> SyncRequest<I> {
-    /// Start building a [`SyncRequest`].
-    pub fn builder() -> SyncRequestBuilder<I> {
-        SyncRequestBuilder {
-            inner: Default::default(),
-        }
-    }
-
-    /// Get the [`SyncProgress`] of this request.
-    pub fn progress(&self) -> SyncProgress {
-        SyncProgress {
-            spks_consumed: self.spks_consumed,
-            spks_remaining: self.spks.len(),
-            txids_consumed: self.txids_consumed,
-            txids_remaining: self.txids.len(),
-            outpoints_consumed: self.outpoints_consumed,
-            outpoints_remaining: self.outpoints.len(),
-        }
-    }
-
-    /// Get the chain tip [`CheckPoint`] of this request (if any).
-    pub fn chain_tip(&self) -> Option<CheckPoint> {
-        self.chain_tip.clone()
-    }
-
-    /// Advances the sync request and returns the next [`ScriptBuf`].
-    ///
-    /// Returns [`None`] when there are no more scripts remaining in the request.
-    pub fn next_spk(&mut self) -> Option<ScriptBuf> {
-        let (i, spk) = self.spks.pop_front()?;
-        self.spks_consumed += 1;
-        self._call_inspect(SyncItem::Spk(i, spk.as_script()));
-        Some(spk)
-    }
-
-    /// Advances the sync request and returns the next [`Txid`].
-    ///
-    /// Returns [`None`] when there are no more txids remaining in the request.
-    pub fn next_txid(&mut self) -> Option<Txid> {
-        let txid = self.txids.pop_front()?;
-        self.txids_consumed += 1;
-        self._call_inspect(SyncItem::Txid(txid));
-        Some(txid)
-    }
-
-    /// Advances the sync request and returns the next [`OutPoint`].
-    ///
-    /// Returns [`None`] when there are no more outpoints in the request.
-    pub fn next_outpoint(&mut self) -> Option<OutPoint> {
-        let outpoint = self.outpoints.pop_front()?;
-        self.outpoints_consumed += 1;
-        self._call_inspect(SyncItem::OutPoint(outpoint));
-        Some(outpoint)
-    }
-
-    /// Iterate over [`ScriptBuf`]s contained in this request.
-    pub fn iter_spks(&mut self) -> impl ExactSizeIterator<Item = ScriptBuf> + '_ {
-        SyncIter::<I, ScriptBuf>::new(self)
-    }
-
-    /// Iterate over [`Txid`]s contained in this request.
-    pub fn iter_txids(&mut self) -> impl ExactSizeIterator<Item = Txid> + '_ {
-        SyncIter::<I, Txid>::new(self)
-    }
-
-    /// Iterate over [`OutPoint`]s contained in this request.
-    pub fn iter_outpoints(&mut self) -> impl ExactSizeIterator<Item = OutPoint> + '_ {
-        SyncIter::<I, OutPoint>::new(self)
-    }
-
-    fn _call_inspect(&mut self, item: SyncItem<I>) {
-        let progress = self.progress();
-        (*self.inspect)(item, progress);
-    }
-}
-
-/// Data returned from a spk-based blockchain client sync.
-///
-/// See also [`SyncRequest`].
-#[must_use]
-#[derive(Debug)]
-pub struct SyncResult<A = ConfirmationBlockTime> {
-    /// The update to apply to the receiving [`TxGraph`](crate::tx_graph::TxGraph).
-    pub graph_update: crate::tx_graph::Update<A>,
-    /// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain).
-    pub chain_update: Option<CheckPoint>,
-}
-
-impl<A> Default for SyncResult<A> {
-    fn default() -> Self {
-        Self {
-            graph_update: Default::default(),
-            chain_update: Default::default(),
-        }
-    }
-}
-
-/// Builds a [`FullScanRequest`].
-#[must_use]
-pub struct FullScanRequestBuilder<K> {
-    inner: FullScanRequest<K>,
-}
-
-impl<K> Default for FullScanRequestBuilder<K> {
-    fn default() -> Self {
-        Self {
-            inner: Default::default(),
-        }
-    }
-}
-
-#[cfg(feature = "miniscript")]
-impl<K: Ord + Clone + core::fmt::Debug> FullScanRequestBuilder<K> {
-    /// Add spk iterators for each keychain tracked in `indexer`.
-    pub fn spks_from_indexer(
-        mut self,
-        indexer: &crate::indexer::keychain_txout::KeychainTxOutIndex<K>,
-    ) -> Self {
-        for (keychain, spks) in indexer.all_unbounded_spk_iters() {
-            self = self.spks_for_keychain(keychain, spks);
-        }
-        self
-    }
-}
-
-impl<K: Ord> FullScanRequestBuilder<K> {
-    /// Set the initial chain tip for the full scan request.
-    ///
-    /// This is used to update [`LocalChain`](crate::local_chain::LocalChain).
-    pub fn chain_tip(mut self, tip: CheckPoint) -> Self {
-        self.inner.chain_tip = Some(tip);
-        self
-    }
-
-    /// Set the spk iterator for a given `keychain`.
-    pub fn spks_for_keychain(
-        mut self,
-        keychain: K,
-        spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
-    ) -> Self {
-        self.inner
-            .spks_by_keychain
-            .insert(keychain, Box::new(spks.into_iter()));
-        self
-    }
-
-    /// Set the closure that will inspect every sync item visited.
-    pub fn inspect<F>(mut self, inspect: F) -> Self
-    where
-        F: FnMut(K, u32, &Script) + Send + 'static,
-    {
-        self.inner.inspect = Box::new(inspect);
-        self
-    }
-
-    /// Build the [`FullScanRequest`].
-    pub fn build(self) -> FullScanRequest<K> {
-        self.inner
-    }
-}
-
-/// Data required to perform a spk-based blockchain client full scan.
-///
-/// A client full scan iterates through all the scripts for the given keychains, fetching relevant
-/// data until some stop gap number of scripts is found that have no data. This operation is
-/// generally only used when importing or restoring previously used keychains in which the list of
-/// used scripts is not known. The full scan process also updates the chain from the given
-/// [`chain_tip`](FullScanRequestBuilder::chain_tip) (if provided).
-#[must_use]
-pub struct FullScanRequest<K> {
-    chain_tip: Option<CheckPoint>,
-    spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
-    inspect: Box<InspectFullScan<K>>,
-}
-
-impl<K> From<FullScanRequestBuilder<K>> for FullScanRequest<K> {
-    fn from(builder: FullScanRequestBuilder<K>) -> Self {
-        builder.inner
-    }
-}
-
-impl<K> Default for FullScanRequest<K> {
-    fn default() -> Self {
-        Self {
-            chain_tip: None,
-            spks_by_keychain: Default::default(),
-            inspect: Box::new(|_, _, _| {}),
-        }
-    }
-}
-
-impl<K: Ord + Clone> FullScanRequest<K> {
-    /// Start building a [`FullScanRequest`].
-    pub fn builder() -> FullScanRequestBuilder<K> {
-        FullScanRequestBuilder {
-            inner: Self::default(),
-        }
-    }
-
-    /// Get the chain tip [`CheckPoint`] of this request (if any).
-    pub fn chain_tip(&self) -> Option<CheckPoint> {
-        self.chain_tip.clone()
-    }
-
-    /// List all keychains contained in this request.
-    pub fn keychains(&self) -> Vec<K> {
-        self.spks_by_keychain.keys().cloned().collect()
-    }
-
-    /// Advances the full scan request and returns the next indexed [`ScriptBuf`] of the given
-    /// `keychain`.
-    pub fn next_spk(&mut self, keychain: K) -> Option<Indexed<ScriptBuf>> {
-        self.iter_spks(keychain).next()
-    }
-
-    /// Iterate over indexed [`ScriptBuf`]s contained in this request of the given `keychain`.
-    pub fn iter_spks(&mut self, keychain: K) -> impl Iterator<Item = Indexed<ScriptBuf>> + '_ {
-        let spks = self.spks_by_keychain.get_mut(&keychain);
-        let inspect = &mut self.inspect;
-        KeychainSpkIter {
-            keychain,
-            spks,
-            inspect,
-        }
-    }
-}
-
-/// Data returned from a spk-based blockchain client full scan.
-///
-/// See also [`FullScanRequest`].
-#[must_use]
-#[derive(Debug)]
-pub struct FullScanResult<K, A = ConfirmationBlockTime> {
-    /// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain).
-    pub graph_update: crate::tx_graph::Update<A>,
-    /// The update to apply to the receiving [`TxGraph`](crate::tx_graph::TxGraph).
-    pub chain_update: Option<CheckPoint>,
-    /// Last active indices for the corresponding keychains (`K`).
-    pub last_active_indices: BTreeMap<K, u32>,
-}
-
-impl<K, A> Default for FullScanResult<K, A> {
-    fn default() -> Self {
-        Self {
-            graph_update: Default::default(),
-            chain_update: Default::default(),
-            last_active_indices: Default::default(),
-        }
-    }
-}
-
-struct KeychainSpkIter<'r, K> {
-    keychain: K,
-    spks: Option<&'r mut Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
-    inspect: &'r mut Box<InspectFullScan<K>>,
-}
-
-impl<'r, K: Ord + Clone> Iterator for KeychainSpkIter<'r, K> {
-    type Item = Indexed<ScriptBuf>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let (i, spk) = self.spks.as_mut()?.next()?;
-        (*self.inspect)(self.keychain.clone(), i, &spk);
-        Some((i, spk))
-    }
-}
-
-struct SyncIter<'r, I, Item> {
-    request: &'r mut SyncRequest<I>,
-    marker: core::marker::PhantomData<Item>,
-}
-
-impl<'r, I, Item> SyncIter<'r, I, Item> {
-    fn new(request: &'r mut SyncRequest<I>) -> Self {
-        Self {
-            request,
-            marker: core::marker::PhantomData,
-        }
-    }
-}
-
-impl<'r, I, Item> ExactSizeIterator for SyncIter<'r, I, Item> where SyncIter<'r, I, Item>: Iterator {}
-
-impl<'r, I> Iterator for SyncIter<'r, I, ScriptBuf> {
-    type Item = ScriptBuf;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.request.next_spk()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let remaining = self.request.spks.len();
-        (remaining, Some(remaining))
-    }
-}
-
-impl<'r, I> Iterator for SyncIter<'r, I, Txid> {
-    type Item = Txid;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.request.next_txid()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let remaining = self.request.txids.len();
-        (remaining, Some(remaining))
-    }
-}
-
-impl<'r, I> Iterator for SyncIter<'r, I, OutPoint> {
-    type Item = OutPoint;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.request.next_outpoint()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let remaining = self.request.outpoints.len();
-        (remaining, Some(remaining))
-    }
-}
index d74cf906c9c2924b263314cc33e447bb558f80f5..80741b07476c6190294c6a361b8e23bac79fe3ee 100644 (file)
@@ -19,3 +19,6 @@ hashbrown = { version = "0.9.1", optional = true }
 default = ["std"]
 std = ["bitcoin/std"]
 serde = ["dep:serde", "bitcoin/serde", "hashbrown?/serde"]
+
+[dev-dependencies]
+bdk_chain = { version = "0.17.0", path = "../chain" }
index bed9da4a116fc9532187eb65db508c636fd713e7..038bee621639be3e2aad998cefae3a0dcb63ed07 100644 (file)
@@ -54,12 +54,19 @@ pub mod collections {
     pub use hashbrown::hash_map;
 }
 
+/// A tuple of keychain index and `T` representing the indexed value.
+pub type Indexed<T> = (u32, T);
+/// A tuple of keychain `K`, derivation index (`u32`) and a `T` associated with them.
+pub type KeychainIndexed<K, T> = ((K, u32), T);
+
 mod chain_data;
 pub use chain_data::*;
 
 mod checkpoint;
 pub use checkpoint::*;
 
+pub mod spk_client;
+
 /// Core structures for [`TxGraph`].
 ///
 /// [`TxGraph`]: https://docs.rs/bdk_chain/latest/bdk_chain/tx_graph/struct.TxGraph.html
diff --git a/crates/core/src/spk_client.rs b/crates/core/src/spk_client.rs
new file mode 100644 (file)
index 0000000..e54849f
--- /dev/null
@@ -0,0 +1,553 @@
+//! Helper types for spk-based blockchain clients.
+use crate::{
+    alloc::{boxed::Box, collections::VecDeque, vec::Vec},
+    collections::BTreeMap,
+    CheckPoint, ConfirmationBlockTime, Indexed,
+};
+use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
+
+type InspectSync<I> = dyn FnMut(SyncItem<I>, SyncProgress) + Send + 'static;
+
+type InspectFullScan<K> = dyn FnMut(K, u32, &Script) + Send + 'static;
+
+/// An item reported to the [`inspect`](SyncRequestBuilder::inspect) closure of [`SyncRequest`].
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum SyncItem<'i, I> {
+    /// Script pubkey sync item.
+    Spk(I, &'i Script),
+    /// Txid sync item.
+    Txid(Txid),
+    /// Outpoint sync item.
+    OutPoint(OutPoint),
+}
+
+impl<'i, I: core::fmt::Debug + core::any::Any> core::fmt::Display for SyncItem<'i, I> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            SyncItem::Spk(i, spk) => {
+                if (i as &dyn core::any::Any).is::<()>() {
+                    write!(f, "script '{}'", spk)
+                } else {
+                    write!(f, "script {:?} '{}'", i, spk)
+                }
+            }
+            SyncItem::Txid(txid) => write!(f, "txid '{}'", txid),
+            SyncItem::OutPoint(op) => write!(f, "outpoint '{}'", op),
+        }
+    }
+}
+
+/// The progress of [`SyncRequest`].
+#[derive(Debug, Clone)]
+pub struct SyncProgress {
+    /// Script pubkeys consumed by the request.
+    pub spks_consumed: usize,
+    /// Script pubkeys remaining in the request.
+    pub spks_remaining: usize,
+    /// Txids consumed by the request.
+    pub txids_consumed: usize,
+    /// Txids remaining in the request.
+    pub txids_remaining: usize,
+    /// Outpoints consumed by the request.
+    pub outpoints_consumed: usize,
+    /// Outpoints remaining in the request.
+    pub outpoints_remaining: usize,
+}
+
+impl SyncProgress {
+    /// Total items, consumed and remaining, of the request.
+    pub fn total(&self) -> usize {
+        self.total_spks() + self.total_txids() + self.total_outpoints()
+    }
+
+    /// Total script pubkeys, consumed and remaining, of the request.
+    pub fn total_spks(&self) -> usize {
+        self.spks_consumed + self.spks_remaining
+    }
+
+    /// Total txids, consumed and remaining, of the request.
+    pub fn total_txids(&self) -> usize {
+        self.txids_consumed + self.txids_remaining
+    }
+
+    /// Total outpoints, consumed and remaining, of the request.
+    pub fn total_outpoints(&self) -> usize {
+        self.outpoints_consumed + self.outpoints_remaining
+    }
+
+    /// Total consumed items of the request.
+    pub fn consumed(&self) -> usize {
+        self.spks_consumed + self.txids_consumed + self.outpoints_consumed
+    }
+
+    /// Total remaining items of the request.
+    pub fn remaining(&self) -> usize {
+        self.spks_remaining + self.txids_remaining + self.outpoints_remaining
+    }
+}
+
+/// Builds a [`SyncRequest`].
+#[must_use]
+pub struct SyncRequestBuilder<I = ()> {
+    inner: SyncRequest<I>,
+}
+
+impl<I> Default for SyncRequestBuilder<I> {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
+impl SyncRequestBuilder<()> {
+    /// Add [`Script`]s that will be synced against.
+    pub fn spks(self, spks: impl IntoIterator<Item = ScriptBuf>) -> Self {
+        self.spks_with_indexes(spks.into_iter().map(|spk| ((), spk)))
+    }
+}
+
+impl<I> SyncRequestBuilder<I> {
+    /// Set the initial chain tip for the sync request.
+    ///
+    /// This is used to update [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
+    pub fn chain_tip(mut self, cp: CheckPoint) -> Self {
+        self.inner.chain_tip = Some(cp);
+        self
+    }
+
+    /// Add [`Script`]s coupled with associated indexes that will be synced against.
+    ///
+    /// # Example
+    ///
+    /// Sync revealed script pubkeys obtained from a
+    /// [`KeychainTxOutIndex`](../../bdk_chain/indexer/keychain_txout/struct.KeychainTxOutIndex.html).
+    ///
+    /// ```rust
+    /// # use bdk_chain::spk_client::SyncRequest;
+    /// # use bdk_chain::indexer::keychain_txout::KeychainTxOutIndex;
+    /// # use bdk_chain::miniscript::{Descriptor, DescriptorPublicKey};
+    /// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
+    /// # let (descriptor_a,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
+    /// # let (descriptor_b,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
+    /// let mut indexer = KeychainTxOutIndex::<&'static str>::default();
+    /// indexer.insert_descriptor("descriptor_a", descriptor_a)?;
+    /// indexer.insert_descriptor("descriptor_b", descriptor_b)?;
+    ///
+    /// /* Assume that the caller does more mutations to the `indexer` here... */
+    ///
+    /// // Reveal spks for "descriptor_a", then build a sync request. Each spk will be indexed with
+    /// // `u32`, which represents the derivation index of the associated spk from "descriptor_a".
+    /// let (newly_revealed_spks, _changeset) = indexer
+    ///     .reveal_to_target("descriptor_a", 21)
+    ///     .expect("keychain must exist");
+    /// let _request = SyncRequest::builder()
+    ///     .spks_with_indexes(newly_revealed_spks)
+    ///     .build();
+    ///
+    /// // Sync all revealed spks in the indexer. This time, spks may be derived from different
+    /// // keychains. Each spk will be indexed with `(&'static str, u32)` where `&'static str` is
+    /// // the keychain identifier and `u32` is the derivation index.
+    /// let all_revealed_spks = indexer.revealed_spks(..);
+    /// let _request = SyncRequest::builder()
+    ///     .spks_with_indexes(all_revealed_spks)
+    ///     .build();
+    /// # Ok::<_, bdk_chain::keychain_txout::InsertDescriptorError<_>>(())
+    /// ```
+    pub fn spks_with_indexes(mut self, spks: impl IntoIterator<Item = (I, ScriptBuf)>) -> Self {
+        self.inner.spks.extend(spks);
+        self
+    }
+
+    /// Add [`Txid`]s that will be synced against.
+    pub fn txids(mut self, txids: impl IntoIterator<Item = Txid>) -> Self {
+        self.inner.txids.extend(txids);
+        self
+    }
+
+    /// Add [`OutPoint`]s that will be synced against.
+    pub fn outpoints(mut self, outpoints: impl IntoIterator<Item = OutPoint>) -> Self {
+        self.inner.outpoints.extend(outpoints);
+        self
+    }
+
+    /// Set the closure that will inspect every sync item visited.
+    pub fn inspect<F>(mut self, inspect: F) -> Self
+    where
+        F: FnMut(SyncItem<I>, SyncProgress) + Send + 'static,
+    {
+        self.inner.inspect = Box::new(inspect);
+        self
+    }
+
+    /// Build the [`SyncRequest`].
+    pub fn build(self) -> SyncRequest<I> {
+        self.inner
+    }
+}
+
+/// Data required to perform a spk-based blockchain client sync.
+///
+/// A client sync fetches relevant chain data for a known list of scripts, transaction ids and
+/// outpoints. The sync process also updates the chain from the given
+/// [`chain_tip`](SyncRequestBuilder::chain_tip) (if provided).
+///
+/// ```rust
+/// # use bdk_chain::{bitcoin::{hashes::Hash, ScriptBuf}, local_chain::LocalChain};
+/// # use bdk_chain::spk_client::SyncRequest;
+/// # let (local_chain, _) = LocalChain::from_genesis_hash(Hash::all_zeros());
+/// # let scripts = [ScriptBuf::default(), ScriptBuf::default()];
+/// // Construct a sync request.
+/// let sync_request = SyncRequest::builder()
+///     // Provide chain tip of the local wallet.
+///     .chain_tip(local_chain.tip())
+///     // Provide list of scripts to scan for transactions against.
+///     .spks(scripts)
+///     // This is called for every synced item.
+///     .inspect(|item, progress| println!("{} (remaining: {})", item, progress.remaining()))
+///     // Finish constructing the sync request.
+///     .build();
+/// ```
+#[must_use]
+pub struct SyncRequest<I = ()> {
+    chain_tip: Option<CheckPoint>,
+    spks: VecDeque<(I, ScriptBuf)>,
+    spks_consumed: usize,
+    txids: VecDeque<Txid>,
+    txids_consumed: usize,
+    outpoints: VecDeque<OutPoint>,
+    outpoints_consumed: usize,
+    inspect: Box<InspectSync<I>>,
+}
+
+impl<I> Default for SyncRequest<I> {
+    fn default() -> Self {
+        Self {
+            chain_tip: None,
+            spks: VecDeque::new(),
+            spks_consumed: 0,
+            txids: VecDeque::new(),
+            txids_consumed: 0,
+            outpoints: VecDeque::new(),
+            outpoints_consumed: 0,
+            inspect: Box::new(|_, _| {}),
+        }
+    }
+}
+
+impl<I> From<SyncRequestBuilder<I>> for SyncRequest<I> {
+    fn from(builder: SyncRequestBuilder<I>) -> Self {
+        builder.inner
+    }
+}
+
+impl<I> SyncRequest<I> {
+    /// Start building a [`SyncRequest`].
+    pub fn builder() -> SyncRequestBuilder<I> {
+        SyncRequestBuilder {
+            inner: Default::default(),
+        }
+    }
+
+    /// Get the [`SyncProgress`] of this request.
+    pub fn progress(&self) -> SyncProgress {
+        SyncProgress {
+            spks_consumed: self.spks_consumed,
+            spks_remaining: self.spks.len(),
+            txids_consumed: self.txids_consumed,
+            txids_remaining: self.txids.len(),
+            outpoints_consumed: self.outpoints_consumed,
+            outpoints_remaining: self.outpoints.len(),
+        }
+    }
+
+    /// Get the chain tip [`CheckPoint`] of this request (if any).
+    pub fn chain_tip(&self) -> Option<CheckPoint> {
+        self.chain_tip.clone()
+    }
+
+    /// Advances the sync request and returns the next [`ScriptBuf`].
+    ///
+    /// Returns [`None`] when there are no more scripts remaining in the request.
+    pub fn next_spk(&mut self) -> Option<ScriptBuf> {
+        let (i, spk) = self.spks.pop_front()?;
+        self.spks_consumed += 1;
+        self._call_inspect(SyncItem::Spk(i, spk.as_script()));
+        Some(spk)
+    }
+
+    /// Advances the sync request and returns the next [`Txid`].
+    ///
+    /// Returns [`None`] when there are no more txids remaining in the request.
+    pub fn next_txid(&mut self) -> Option<Txid> {
+        let txid = self.txids.pop_front()?;
+        self.txids_consumed += 1;
+        self._call_inspect(SyncItem::Txid(txid));
+        Some(txid)
+    }
+
+    /// Advances the sync request and returns the next [`OutPoint`].
+    ///
+    /// Returns [`None`] when there are no more outpoints in the request.
+    pub fn next_outpoint(&mut self) -> Option<OutPoint> {
+        let outpoint = self.outpoints.pop_front()?;
+        self.outpoints_consumed += 1;
+        self._call_inspect(SyncItem::OutPoint(outpoint));
+        Some(outpoint)
+    }
+
+    /// Iterate over [`ScriptBuf`]s contained in this request.
+    pub fn iter_spks(&mut self) -> impl ExactSizeIterator<Item = ScriptBuf> + '_ {
+        SyncIter::<I, ScriptBuf>::new(self)
+    }
+
+    /// Iterate over [`Txid`]s contained in this request.
+    pub fn iter_txids(&mut self) -> impl ExactSizeIterator<Item = Txid> + '_ {
+        SyncIter::<I, Txid>::new(self)
+    }
+
+    /// Iterate over [`OutPoint`]s contained in this request.
+    pub fn iter_outpoints(&mut self) -> impl ExactSizeIterator<Item = OutPoint> + '_ {
+        SyncIter::<I, OutPoint>::new(self)
+    }
+
+    fn _call_inspect(&mut self, item: SyncItem<I>) {
+        let progress = self.progress();
+        (*self.inspect)(item, progress);
+    }
+}
+
+/// Data returned from a spk-based blockchain client sync.
+///
+/// See also [`SyncRequest`].
+#[must_use]
+#[derive(Debug)]
+pub struct SyncResult<A = ConfirmationBlockTime> {
+    /// The update to apply to the receiving
+    /// [`TxGraph`](../../bdk_chain/tx_graph/struct.TxGraph.html).
+    pub graph_update: crate::tx_graph::Update<A>,
+    /// The update to apply to the receiving
+    /// [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
+    pub chain_update: Option<CheckPoint>,
+}
+
+impl<A> Default for SyncResult<A> {
+    fn default() -> Self {
+        Self {
+            graph_update: Default::default(),
+            chain_update: Default::default(),
+        }
+    }
+}
+
+/// Builds a [`FullScanRequest`].
+#[must_use]
+pub struct FullScanRequestBuilder<K> {
+    inner: FullScanRequest<K>,
+}
+
+impl<K> Default for FullScanRequestBuilder<K> {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
+impl<K: Ord> FullScanRequestBuilder<K> {
+    /// Set the initial chain tip for the full scan request.
+    ///
+    /// This is used to update [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
+    pub fn chain_tip(mut self, tip: CheckPoint) -> Self {
+        self.inner.chain_tip = Some(tip);
+        self
+    }
+
+    /// Set the spk iterator for a given `keychain`.
+    pub fn spks_for_keychain(
+        mut self,
+        keychain: K,
+        spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
+    ) -> Self {
+        self.inner
+            .spks_by_keychain
+            .insert(keychain, Box::new(spks.into_iter()));
+        self
+    }
+
+    /// Set the closure that will inspect every sync item visited.
+    pub fn inspect<F>(mut self, inspect: F) -> Self
+    where
+        F: FnMut(K, u32, &Script) + Send + 'static,
+    {
+        self.inner.inspect = Box::new(inspect);
+        self
+    }
+
+    /// Build the [`FullScanRequest`].
+    pub fn build(self) -> FullScanRequest<K> {
+        self.inner
+    }
+}
+
+/// Data required to perform a spk-based blockchain client full scan.
+///
+/// A client full scan iterates through all the scripts for the given keychains, fetching relevant
+/// data until some stop gap number of scripts is found that have no data. This operation is
+/// generally only used when importing or restoring previously used keychains in which the list of
+/// used scripts is not known. The full scan process also updates the chain from the given
+/// [`chain_tip`](FullScanRequestBuilder::chain_tip) (if provided).
+#[must_use]
+pub struct FullScanRequest<K> {
+    chain_tip: Option<CheckPoint>,
+    spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
+    inspect: Box<InspectFullScan<K>>,
+}
+
+impl<K> From<FullScanRequestBuilder<K>> for FullScanRequest<K> {
+    fn from(builder: FullScanRequestBuilder<K>) -> Self {
+        builder.inner
+    }
+}
+
+impl<K> Default for FullScanRequest<K> {
+    fn default() -> Self {
+        Self {
+            chain_tip: None,
+            spks_by_keychain: Default::default(),
+            inspect: Box::new(|_, _, _| {}),
+        }
+    }
+}
+
+impl<K: Ord + Clone> FullScanRequest<K> {
+    /// Start building a [`FullScanRequest`].
+    pub fn builder() -> FullScanRequestBuilder<K> {
+        FullScanRequestBuilder {
+            inner: Self::default(),
+        }
+    }
+
+    /// Get the chain tip [`CheckPoint`] of this request (if any).
+    pub fn chain_tip(&self) -> Option<CheckPoint> {
+        self.chain_tip.clone()
+    }
+
+    /// List all keychains contained in this request.
+    pub fn keychains(&self) -> Vec<K> {
+        self.spks_by_keychain.keys().cloned().collect()
+    }
+
+    /// Advances the full scan request and returns the next indexed [`ScriptBuf`] of the given
+    /// `keychain`.
+    pub fn next_spk(&mut self, keychain: K) -> Option<Indexed<ScriptBuf>> {
+        self.iter_spks(keychain).next()
+    }
+
+    /// Iterate over indexed [`ScriptBuf`]s contained in this request of the given `keychain`.
+    pub fn iter_spks(&mut self, keychain: K) -> impl Iterator<Item = Indexed<ScriptBuf>> + '_ {
+        let spks = self.spks_by_keychain.get_mut(&keychain);
+        let inspect = &mut self.inspect;
+        KeychainSpkIter {
+            keychain,
+            spks,
+            inspect,
+        }
+    }
+}
+
+/// Data returned from a spk-based blockchain client full scan.
+///
+/// See also [`FullScanRequest`].
+#[must_use]
+#[derive(Debug)]
+pub struct FullScanResult<K, A = ConfirmationBlockTime> {
+    /// The update to apply to the receiving
+    /// [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
+    pub graph_update: crate::tx_graph::Update<A>,
+    /// The update to apply to the receiving [`TxGraph`](../../bdk_chain/tx_graph/struct.TxGraph.html).
+    pub chain_update: Option<CheckPoint>,
+    /// Last active indices for the corresponding keychains (`K`).
+    pub last_active_indices: BTreeMap<K, u32>,
+}
+
+impl<K, A> Default for FullScanResult<K, A> {
+    fn default() -> Self {
+        Self {
+            graph_update: Default::default(),
+            chain_update: Default::default(),
+            last_active_indices: Default::default(),
+        }
+    }
+}
+
+struct KeychainSpkIter<'r, K> {
+    keychain: K,
+    spks: Option<&'r mut Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
+    inspect: &'r mut Box<InspectFullScan<K>>,
+}
+
+impl<'r, K: Ord + Clone> Iterator for KeychainSpkIter<'r, K> {
+    type Item = Indexed<ScriptBuf>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let (i, spk) = self.spks.as_mut()?.next()?;
+        (*self.inspect)(self.keychain.clone(), i, &spk);
+        Some((i, spk))
+    }
+}
+
+struct SyncIter<'r, I, Item> {
+    request: &'r mut SyncRequest<I>,
+    marker: core::marker::PhantomData<Item>,
+}
+
+impl<'r, I, Item> SyncIter<'r, I, Item> {
+    fn new(request: &'r mut SyncRequest<I>) -> Self {
+        Self {
+            request,
+            marker: core::marker::PhantomData,
+        }
+    }
+}
+
+impl<'r, I, Item> ExactSizeIterator for SyncIter<'r, I, Item> where SyncIter<'r, I, Item>: Iterator {}
+
+impl<'r, I> Iterator for SyncIter<'r, I, ScriptBuf> {
+    type Item = ScriptBuf;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.request.next_spk()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let remaining = self.request.spks.len();
+        (remaining, Some(remaining))
+    }
+}
+
+impl<'r, I> Iterator for SyncIter<'r, I, Txid> {
+    type Item = Txid;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.request.next_txid()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let remaining = self.request.txids.len();
+        (remaining, Some(remaining))
+    }
+}
+
+impl<'r, I> Iterator for SyncIter<'r, I, OutPoint> {
+    type Item = OutPoint;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.request.next_outpoint()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let remaining = self.request.outpoints.len();
+        (remaining, Some(remaining))
+    }
+}
index 747acc4487affee6a4504415ae4a4d6eb5cd9f2d..6d169bdce3d4193e8d37558fff5050d6a656d41d 100644 (file)
@@ -4,7 +4,8 @@ use bdk_chain::{
         secp256k1::rand::random, transaction, Address, Amount, Block, BlockHash, CompactTarget,
         ScriptBuf, ScriptHash, Transaction, TxIn, TxOut, Txid,
     },
-    BlockId, local_chain::CheckPoint,
+    local_chain::CheckPoint,
+    BlockId,
 };
 use bitcoincore_rpc::{
     bitcoincore_rpc_json::{GetBlockTemplateModes, GetBlockTemplateRules},
index 638bb575795f9b3f4e1fc0fdb97f66dd924c7716..72c42ddfed4e4f08cd4bb669b3e350558e21291e 100644 (file)
@@ -2472,6 +2472,7 @@ impl Wallet {
     /// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
     /// start a blockchain sync with a spk based blockchain client.
     pub fn start_sync_with_revealed_spks(&self) -> SyncRequestBuilder<(KeychainKind, u32)> {
+        use bdk_chain::keychain_txout::SyncRequestBuilderExt;
         SyncRequest::builder()
             .chain_tip(self.chain.tip())
             .revealed_spks_from_indexer(&self.indexed_graph.index, ..)
@@ -2486,6 +2487,7 @@ impl Wallet {
     /// This operation is generally only used when importing or restoring a previously used wallet
     /// in which the list of used scripts is not known.
     pub fn start_full_scan(&self) -> FullScanRequestBuilder<KeychainKind> {
+        use bdk_chain::keychain_txout::FullScanRequestBuilderExt;
         FullScanRequest::builder()
             .chain_tip(self.chain.tip())
             .spks_from_indexer(&self.indexed_graph.index)
index d4692e35c333c456dce98a411440d71a758b0ad4..7a40058787631884c3f9688bc931d526ddd2da4a 100644 (file)
@@ -6,6 +6,7 @@ use std::{
 
 use bdk_chain::{
     bitcoin::Network,
+    keychain_txout::FullScanRequestBuilderExt,
     spk_client::{FullScanRequest, SyncRequest},
     Merge,
 };