• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bitcoindevkit / bdk / 10713968110

05 Sep 2024 04:38AM UTC coverage: 82.008% (+0.02%) from 81.984%
10713968110

Pull #1582

github

web-flow
Merge 46e333df5 into 23bae3e8f
Pull Request #1582: refactor(core): Implement generics for `CheckPoint`, `LocalChain`, and `spk_client` types

108 of 116 new or added lines in 3 files covered. (93.1%)

1 existing line in 1 file now uncovered.

11258 of 13728 relevant lines covered (82.01%)

17919.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.33
/crates/core/src/spk_client.rs
1
//! Helper types for spk-based blockchain clients.
2
use crate::{
3
    alloc::{boxed::Box, collections::VecDeque, vec::Vec},
4
    collections::BTreeMap,
5
    CheckPoint, ConfirmationBlockTime, Indexed,
6
};
7
use bitcoin::{BlockHash, OutPoint, Script, ScriptBuf, Txid};
8

9
type InspectSync<I> = dyn FnMut(SyncItem<I>, SyncProgress) + Send + 'static;
10

11
type InspectFullScan<K> = dyn FnMut(K, u32, &Script) + Send + 'static;
12

13
/// An item reported to the [`inspect`](SyncRequestBuilder::inspect) closure of [`SyncRequest`].
14
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
15
pub enum SyncItem<'i, I> {
16
    /// Script pubkey sync item.
17
    Spk(I, &'i Script),
18
    /// Txid sync item.
19
    Txid(Txid),
20
    /// Outpoint sync item.
21
    OutPoint(OutPoint),
22
}
23

24
impl<'i, I: core::fmt::Debug + core::any::Any> core::fmt::Display for SyncItem<'i, I> {
25
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
×
26
        match self {
×
27
            SyncItem::Spk(i, spk) => {
×
28
                if (i as &dyn core::any::Any).is::<()>() {
×
29
                    write!(f, "script '{}'", spk)
×
30
                } else {
31
                    write!(f, "script {:?} '{}'", i, spk)
×
32
                }
33
            }
34
            SyncItem::Txid(txid) => write!(f, "txid '{}'", txid),
×
35
            SyncItem::OutPoint(op) => write!(f, "outpoint '{}'", op),
×
36
        }
37
    }
×
38
}
39

40
/// The progress of [`SyncRequest`].
41
#[derive(Debug, Clone)]
42
pub struct SyncProgress {
43
    /// Script pubkeys consumed by the request.
44
    pub spks_consumed: usize,
45
    /// Script pubkeys remaining in the request.
46
    pub spks_remaining: usize,
47
    /// Txids consumed by the request.
48
    pub txids_consumed: usize,
49
    /// Txids remaining in the request.
50
    pub txids_remaining: usize,
51
    /// Outpoints consumed by the request.
52
    pub outpoints_consumed: usize,
53
    /// Outpoints remaining in the request.
54
    pub outpoints_remaining: usize,
55
}
56

57
impl SyncProgress {
58
    /// Total items, consumed and remaining, of the request.
59
    pub fn total(&self) -> usize {
×
60
        self.total_spks() + self.total_txids() + self.total_outpoints()
×
61
    }
×
62

63
    /// Total script pubkeys, consumed and remaining, of the request.
64
    pub fn total_spks(&self) -> usize {
×
65
        self.spks_consumed + self.spks_remaining
×
66
    }
×
67

68
    /// Total txids, consumed and remaining, of the request.
69
    pub fn total_txids(&self) -> usize {
×
70
        self.txids_consumed + self.txids_remaining
×
71
    }
×
72

73
    /// Total outpoints, consumed and remaining, of the request.
74
    pub fn total_outpoints(&self) -> usize {
×
75
        self.outpoints_consumed + self.outpoints_remaining
×
76
    }
×
77

78
    /// Total consumed items of the request.
79
    pub fn consumed(&self) -> usize {
×
80
        self.spks_consumed + self.txids_consumed + self.outpoints_consumed
×
81
    }
×
82

83
    /// Total remaining items of the request.
84
    pub fn remaining(&self) -> usize {
×
85
        self.spks_remaining + self.txids_remaining + self.outpoints_remaining
×
86
    }
×
87
}
88

89
/// Builds a [`SyncRequest`].
90
#[must_use]
91
pub struct SyncRequestBuilder<I = (), B = BlockHash> {
92
    inner: SyncRequest<I, B>,
93
}
94

95
impl<I, B> Default for SyncRequestBuilder<I, B> {
96
    fn default() -> Self {
×
97
        Self {
×
98
            inner: Default::default(),
×
99
        }
×
100
    }
×
101
}
102

103
impl SyncRequestBuilder<()> {
104
    /// Add [`Script`]s that will be synced against.
105
    pub fn spks(self, spks: impl IntoIterator<Item = ScriptBuf>) -> Self {
16✔
106
        self.spks_with_indexes(spks.into_iter().map(|spk| ((), spk)))
19✔
107
    }
16✔
108
}
109

110
impl<I, B> SyncRequestBuilder<I, B> {
111
    /// Set the initial chain tip for the sync request.
112
    ///
113
    /// This is used to update [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
114
    pub fn chain_tip(mut self, cp: CheckPoint<B>) -> Self {
16✔
115
        self.inner.chain_tip = Some(cp);
16✔
116
        self
16✔
117
    }
16✔
118

119
    /// Add [`Script`]s coupled with associated indexes that will be synced against.
120
    ///
121
    /// # Example
122
    ///
123
    /// Sync revealed script pubkeys obtained from a
124
    /// [`KeychainTxOutIndex`](../../bdk_chain/indexer/keychain_txout/struct.KeychainTxOutIndex.html).
125
    ///
126
    /// ```rust
127
    /// # use bdk_chain::bitcoin::BlockHash;
128
    /// # use bdk_chain::spk_client::SyncRequest;
129
    /// # use bdk_chain::indexer::keychain_txout::KeychainTxOutIndex;
130
    /// # use bdk_chain::miniscript::{Descriptor, DescriptorPublicKey};
131
    /// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
132
    /// # let (descriptor_a,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
133
    /// # let (descriptor_b,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
134
    /// let mut indexer = KeychainTxOutIndex::<&'static str>::default();
135
    /// indexer.insert_descriptor("descriptor_a", descriptor_a)?;
136
    /// indexer.insert_descriptor("descriptor_b", descriptor_b)?;
137
    ///
138
    /// /* Assume that the caller does more mutations to the `indexer` here... */
139
    ///
140
    /// // Reveal spks for "descriptor_a", then build a sync request. Each spk will be indexed with
141
    /// // `u32`, which represents the derivation index of the associated spk from "descriptor_a".
142
    /// let (newly_revealed_spks, _changeset) = indexer
143
    ///     .reveal_to_target("descriptor_a", 21)
144
    ///     .expect("keychain must exist");
145
    /// let _request: SyncRequest<u32, BlockHash> = SyncRequest::builder()
146
    ///     .spks_with_indexes(newly_revealed_spks)
147
    ///     .build();
148
    ///
149
    /// // Sync all revealed spks in the indexer. This time, spks may be derived from different
150
    /// // keychains. Each spk will be indexed with `(&'static str, u32)` where `&'static str` is
151
    /// // the keychain identifier and `u32` is the derivation index.
152
    /// let all_revealed_spks = indexer.revealed_spks(..);
153
    /// let _request: SyncRequest<(&str, u32), BlockHash> = SyncRequest::builder()
154
    ///     .spks_with_indexes(all_revealed_spks)
155
    ///     .build();
156
    /// # Ok::<_, bdk_chain::keychain_txout::InsertDescriptorError<_>>(())
157
    /// ```
158
    pub fn spks_with_indexes(mut self, spks: impl IntoIterator<Item = (I, ScriptBuf)>) -> Self {
16✔
159
        self.inner.spks.extend(spks);
16✔
160
        self
16✔
161
    }
16✔
162

163
    /// Add [`Txid`]s that will be synced against.
164
    pub fn txids(mut self, txids: impl IntoIterator<Item = Txid>) -> Self {
×
165
        self.inner.txids.extend(txids);
×
166
        self
×
167
    }
×
168

169
    /// Add [`OutPoint`]s that will be synced against.
170
    pub fn outpoints(mut self, outpoints: impl IntoIterator<Item = OutPoint>) -> Self {
×
171
        self.inner.outpoints.extend(outpoints);
×
172
        self
×
173
    }
×
174

175
    /// Set the closure that will inspect every sync item visited.
176
    pub fn inspect<F>(mut self, inspect: F) -> Self
×
177
    where
×
178
        F: FnMut(SyncItem<I>, SyncProgress) + Send + 'static,
×
179
    {
×
180
        self.inner.inspect = Box::new(inspect);
×
181
        self
×
182
    }
×
183

184
    /// Build the [`SyncRequest`].
NEW
185
    pub fn build(self) -> SyncRequest<I, B> {
×
186
        self.inner
×
187
    }
×
188
}
189

190
/// Data required to perform a spk-based blockchain client sync.
191
///
192
/// A client sync fetches relevant chain data for a known list of scripts, transaction ids and
193
/// outpoints. The sync process also updates the chain from the given
194
/// [`chain_tip`](SyncRequestBuilder::chain_tip) (if provided).
195
///
196
/// ```rust
197
/// # use bdk_chain::{bitcoin::{hashes::Hash, ScriptBuf}, local_chain::LocalChain};
198
/// # use bdk_chain::spk_client::SyncRequest;
199
/// # let (local_chain, _) = LocalChain::from_genesis_hash(Hash::all_zeros());
200
/// # let scripts = [ScriptBuf::default(), ScriptBuf::default()];
201
/// // Construct a sync request.
202
/// let sync_request = SyncRequest::builder()
203
///     // Provide chain tip of the local wallet.
204
///     .chain_tip(local_chain.tip())
205
///     // Provide list of scripts to scan for transactions against.
206
///     .spks(scripts)
207
///     // This is called for every synced item.
208
///     .inspect(|item, progress| println!("{} (remaining: {})", item, progress.remaining()))
209
///     // Finish constructing the sync request.
210
///     .build();
211
/// ```
212
#[must_use]
213
pub struct SyncRequest<I = (), B = BlockHash> {
214
    chain_tip: Option<CheckPoint<B>>,
215
    spks: VecDeque<(I, ScriptBuf)>,
216
    spks_consumed: usize,
217
    txids: VecDeque<Txid>,
218
    txids_consumed: usize,
219
    outpoints: VecDeque<OutPoint>,
220
    outpoints_consumed: usize,
221
    inspect: Box<InspectSync<I>>,
222
}
223

224
impl<I, B> Default for SyncRequest<I, B> {
225
    fn default() -> Self {
16✔
226
        Self {
16✔
227
            chain_tip: None,
16✔
228
            spks: VecDeque::new(),
16✔
229
            spks_consumed: 0,
16✔
230
            txids: VecDeque::new(),
16✔
231
            txids_consumed: 0,
16✔
232
            outpoints: VecDeque::new(),
16✔
233
            outpoints_consumed: 0,
16✔
234
            inspect: Box::new(|_, _| {}),
19✔
235
        }
16✔
236
    }
16✔
237
}
238

239
impl<I, B> From<SyncRequestBuilder<I, B>> for SyncRequest<I, B> {
240
    fn from(builder: SyncRequestBuilder<I, B>) -> Self {
16✔
241
        builder.inner
16✔
242
    }
16✔
243
}
244

245
impl<I, B> SyncRequest<I, B> {
246
    /// Start building a [`SyncRequest`].
247
    pub fn builder() -> SyncRequestBuilder<I, B> {
16✔
248
        SyncRequestBuilder {
16✔
249
            inner: Default::default(),
16✔
250
        }
16✔
251
    }
16✔
252

253
    /// Get the [`SyncProgress`] of this request.
254
    pub fn progress(&self) -> SyncProgress {
19✔
255
        SyncProgress {
19✔
256
            spks_consumed: self.spks_consumed,
19✔
257
            spks_remaining: self.spks.len(),
19✔
258
            txids_consumed: self.txids_consumed,
19✔
259
            txids_remaining: self.txids.len(),
19✔
260
            outpoints_consumed: self.outpoints_consumed,
19✔
261
            outpoints_remaining: self.outpoints.len(),
19✔
262
        }
19✔
263
    }
19✔
264

265
    /// Get the chain tip [`CheckPoint`] of this request (if any).
266
    pub fn chain_tip(&self) -> Option<CheckPoint<B>> {
16✔
267
        self.chain_tip.clone()
16✔
268
    }
16✔
269

270
    /// Advances the sync request and returns the next [`ScriptBuf`].
271
    ///
272
    /// Returns [`None`] when there are no more scripts remaining in the request.
273
    pub fn next_spk(&mut self) -> Option<ScriptBuf> {
48✔
274
        let (i, spk) = self.spks.pop_front()?;
48✔
275
        self.spks_consumed += 1;
19✔
276
        self._call_inspect(SyncItem::Spk(i, spk.as_script()));
19✔
277
        Some(spk)
19✔
278
    }
48✔
279

280
    /// Advances the sync request and returns the next [`Txid`].
281
    ///
282
    /// Returns [`None`] when there are no more txids remaining in the request.
283
    pub fn next_txid(&mut self) -> Option<Txid> {
16✔
284
        let txid = self.txids.pop_front()?;
16✔
285
        self.txids_consumed += 1;
×
286
        self._call_inspect(SyncItem::Txid(txid));
×
287
        Some(txid)
×
288
    }
16✔
289

290
    /// Advances the sync request and returns the next [`OutPoint`].
291
    ///
292
    /// Returns [`None`] when there are no more outpoints in the request.
293
    pub fn next_outpoint(&mut self) -> Option<OutPoint> {
16✔
294
        let outpoint = self.outpoints.pop_front()?;
16✔
295
        self.outpoints_consumed += 1;
×
296
        self._call_inspect(SyncItem::OutPoint(outpoint));
×
297
        Some(outpoint)
×
298
    }
16✔
299

300
    /// Iterate over [`ScriptBuf`]s contained in this request.
301
    pub fn iter_spks(&mut self) -> impl ExactSizeIterator<Item = ScriptBuf> + '_ {
16✔
302
        SyncIter::<I, B, ScriptBuf>::new(self)
16✔
303
    }
16✔
304

305
    /// Iterate over [`Txid`]s contained in this request.
306
    pub fn iter_txids(&mut self) -> impl ExactSizeIterator<Item = Txid> + '_ {
16✔
307
        SyncIter::<I, B, Txid>::new(self)
16✔
308
    }
16✔
309

310
    /// Iterate over [`OutPoint`]s contained in this request.
311
    pub fn iter_outpoints(&mut self) -> impl ExactSizeIterator<Item = OutPoint> + '_ {
16✔
312
        SyncIter::<I, B, OutPoint>::new(self)
16✔
313
    }
16✔
314

315
    fn _call_inspect(&mut self, item: SyncItem<I>) {
19✔
316
        let progress = self.progress();
19✔
317
        (*self.inspect)(item, progress);
19✔
318
    }
19✔
319
}
320

321
/// Data returned from a spk-based blockchain client sync.
322
///
323
/// See also [`SyncRequest`].
324
#[must_use]
325
#[derive(Debug)]
326
pub struct SyncResult<B = BlockHash, A = ConfirmationBlockTime> {
327
    /// Relevant transaction data discovered during the scan.
328
    pub tx_update: crate::TxUpdate<A>,
329
    /// Changes to the chain discovered during the scan.
330
    pub chain_update: Option<CheckPoint<B>>,
331
}
332

333
impl<B, A> Default for SyncResult<B, A> {
334
    fn default() -> Self {
×
335
        Self {
×
336
            tx_update: Default::default(),
×
337
            chain_update: Default::default(),
×
338
        }
×
339
    }
×
340
}
341

342
/// Builds a [`FullScanRequest`].
343
#[must_use]
344
pub struct FullScanRequestBuilder<K, B = BlockHash> {
345
    inner: FullScanRequest<K, B>,
346
}
347

348
impl<K, B> Default for FullScanRequestBuilder<K, B> {
349
    fn default() -> Self {
×
350
        Self {
×
351
            inner: Default::default(),
×
352
        }
×
353
    }
×
354
}
355

356
impl<K: Ord, B> FullScanRequestBuilder<K, B> {
357
    /// Set the initial chain tip for the full scan request.
358
    ///
359
    /// This is used to update [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
360
    pub fn chain_tip(mut self, tip: CheckPoint<B>) -> Self {
12✔
361
        self.inner.chain_tip = Some(tip);
12✔
362
        self
12✔
363
    }
12✔
364

365
    /// Set the spk iterator for a given `keychain`.
366
    pub fn spks_for_keychain(
12✔
367
        mut self,
12✔
368
        keychain: K,
12✔
369
        spks: impl IntoIterator<IntoIter = impl Iterator<Item = Indexed<ScriptBuf>> + Send + 'static>,
12✔
370
    ) -> Self {
12✔
371
        self.inner
12✔
372
            .spks_by_keychain
12✔
373
            .insert(keychain, Box::new(spks.into_iter()));
12✔
374
        self
12✔
375
    }
12✔
376

377
    /// Set the closure that will inspect every sync item visited.
378
    pub fn inspect<F>(mut self, inspect: F) -> Self
×
379
    where
×
380
        F: FnMut(K, u32, &Script) + Send + 'static,
×
381
    {
×
382
        self.inner.inspect = Box::new(inspect);
×
383
        self
×
384
    }
×
385

386
    /// Build the [`FullScanRequest`].
NEW
387
    pub fn build(self) -> FullScanRequest<K, B> {
×
388
        self.inner
×
389
    }
×
390
}
391

392
/// Data required to perform a spk-based blockchain client full scan.
393
///
394
/// A client full scan iterates through all the scripts for the given keychains, fetching relevant
395
/// data until some stop gap number of scripts is found that have no data. This operation is
396
/// generally only used when importing or restoring previously used keychains in which the list of
397
/// used scripts is not known. The full scan process also updates the chain from the given
398
/// [`chain_tip`](FullScanRequestBuilder::chain_tip) (if provided).
399
#[must_use]
400
pub struct FullScanRequest<K, B = BlockHash> {
401
    chain_tip: Option<CheckPoint<B>>,
402
    spks_by_keychain: BTreeMap<K, Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
403
    inspect: Box<InspectFullScan<K>>,
404
}
405

406
impl<K, B> From<FullScanRequestBuilder<K, B>> for FullScanRequest<K, B> {
407
    fn from(builder: FullScanRequestBuilder<K, B>) -> Self {
12✔
408
        builder.inner
12✔
409
    }
12✔
410
}
411

412
impl<K, B> Default for FullScanRequest<K, B> {
413
    fn default() -> Self {
12✔
414
        Self {
12✔
415
            chain_tip: None,
12✔
416
            spks_by_keychain: Default::default(),
12✔
417
            inspect: Box::new(|_, _, _| {}),
90✔
418
        }
12✔
419
    }
12✔
420
}
421

422
impl<K: Ord + Clone, B> FullScanRequest<K, B> {
423
    /// Start building a [`FullScanRequest`].
424
    pub fn builder() -> FullScanRequestBuilder<K, B> {
12✔
425
        FullScanRequestBuilder {
12✔
426
            inner: Self::default(),
12✔
427
        }
12✔
428
    }
12✔
429

430
    /// Get the chain tip [`CheckPoint`] of this request (if any).
431
    pub fn chain_tip(&self) -> Option<CheckPoint<B>> {
12✔
432
        self.chain_tip.clone()
12✔
433
    }
12✔
434

435
    /// List all keychains contained in this request.
436
    pub fn keychains(&self) -> Vec<K> {
12✔
437
        self.spks_by_keychain.keys().cloned().collect()
12✔
438
    }
12✔
439

440
    /// Advances the full scan request and returns the next indexed [`ScriptBuf`] of the given
441
    /// `keychain`.
442
    pub fn next_spk(&mut self, keychain: K) -> Option<Indexed<ScriptBuf>> {
×
443
        self.iter_spks(keychain).next()
×
444
    }
×
445

446
    /// Iterate over indexed [`ScriptBuf`]s contained in this request of the given `keychain`.
447
    pub fn iter_spks(&mut self, keychain: K) -> impl Iterator<Item = Indexed<ScriptBuf>> + '_ {
12✔
448
        let spks = self.spks_by_keychain.get_mut(&keychain);
12✔
449
        let inspect = &mut self.inspect;
12✔
450
        KeychainSpkIter {
12✔
451
            keychain,
12✔
452
            spks,
12✔
453
            inspect,
12✔
454
        }
12✔
455
    }
12✔
456
}
457

458
/// Data returned from a spk-based blockchain client full scan.
459
///
460
/// See also [`FullScanRequest`].
461
#[must_use]
462
#[derive(Debug)]
463
pub struct FullScanResult<K, B = BlockHash, A = ConfirmationBlockTime> {
464
    /// Relevant transaction data discovered during the scan.
465
    pub tx_update: crate::TxUpdate<A>,
466
    /// Last active indices for the corresponding keychains (`K`). An index is active if it had a
467
    /// transaction associated with the script pubkey at that index.
468
    pub last_active_indices: BTreeMap<K, u32>,
469
    /// Changes to the chain discovered during the scan.
470
    pub chain_update: Option<CheckPoint<B>>,
471
}
472

473
impl<K, B, A> Default for FullScanResult<K, B, A> {
474
    fn default() -> Self {
×
475
        Self {
×
476
            tx_update: Default::default(),
×
477
            chain_update: Default::default(),
×
478
            last_active_indices: Default::default(),
×
479
        }
×
480
    }
×
481
}
482

483
struct KeychainSpkIter<'r, K> {
484
    keychain: K,
485
    spks: Option<&'r mut Box<dyn Iterator<Item = Indexed<ScriptBuf>> + Send>>,
486
    inspect: &'r mut Box<InspectFullScan<K>>,
487
}
488

489
impl<'r, K: Ord + Clone> Iterator for KeychainSpkIter<'r, K> {
490
    type Item = Indexed<ScriptBuf>;
491

492
    fn next(&mut self) -> Option<Self::Item> {
93✔
493
        let (i, spk) = self.spks.as_mut()?.next()?;
93✔
494
        (*self.inspect)(self.keychain.clone(), i, &spk);
90✔
495
        Some((i, spk))
90✔
496
    }
93✔
497
}
498

499
struct SyncIter<'r, I, B, Item> {
500
    request: &'r mut SyncRequest<I, B>,
501
    marker: core::marker::PhantomData<Item>,
502
}
503

504
impl<'r, I, B, Item> SyncIter<'r, I, B, Item> {
505
    fn new(request: &'r mut SyncRequest<I, B>) -> Self {
48✔
506
        Self {
48✔
507
            request,
48✔
508
            marker: core::marker::PhantomData,
48✔
509
        }
48✔
510
    }
48✔
511
}
512

513
impl<'r, I, B, Item> ExactSizeIterator for SyncIter<'r, I, B, Item> where
514
    SyncIter<'r, I, B, Item>: Iterator
515
{
516
}
517

518
impl<'r, I, B> Iterator for SyncIter<'r, I, B, ScriptBuf> {
519
    type Item = ScriptBuf;
520

521
    fn next(&mut self) -> Option<Self::Item> {
48✔
522
        self.request.next_spk()
48✔
523
    }
48✔
524

525
    fn size_hint(&self) -> (usize, Option<usize>) {
×
526
        let remaining = self.request.spks.len();
×
527
        (remaining, Some(remaining))
×
528
    }
×
529
}
530

531
impl<'r, I, B> Iterator for SyncIter<'r, I, B, Txid> {
532
    type Item = Txid;
533

534
    fn next(&mut self) -> Option<Self::Item> {
16✔
535
        self.request.next_txid()
16✔
536
    }
16✔
537

538
    fn size_hint(&self) -> (usize, Option<usize>) {
×
539
        let remaining = self.request.txids.len();
×
540
        (remaining, Some(remaining))
×
541
    }
×
542
}
543

544
impl<'r, I, B> Iterator for SyncIter<'r, I, B, OutPoint> {
545
    type Item = OutPoint;
546

547
    fn next(&mut self) -> Option<Self::Item> {
16✔
548
        self.request.next_outpoint()
16✔
549
    }
16✔
550

551
    fn size_hint(&self) -> (usize, Option<usize>) {
×
552
        let remaining = self.request.outpoints.len();
×
553
        (remaining, Some(remaining))
×
554
    }
×
555
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc