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

bitcoindevkit / bdk / 5119600689

pending completion
5119600689

Pull #976

github

web-flow
Merge 250458ae0 into 9bc7fe855
Pull Request #976: Reimplement `Wallet`, `ElectrumExt` and `Esplora{Async}Ext` with redesigned structures.

909 of 909 new or added lines in 13 files covered. (100.0%)

7569 of 9556 relevant lines covered (79.21%)

5314.36 hits per line

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

86.05
/crates/chain/src/tx_graph.rs
1
//! Module for structures that store and traverse transactions.
2
//!
3
//! [`TxGraph`] is a monotone structure that inserts transactions and indexes the spends. The
4
//! [`Additions`] structure reports changes of [`TxGraph`] but can also be applied to a
5
//! [`TxGraph`] as well. Lastly, [`TxDescendants`] is an [`Iterator`] that traverses descendants of
6
//! a given transaction.
7
//!
8
//! Conflicting transactions are allowed to coexist within a [`TxGraph`]. This is useful for
9
//! identifying and traversing conflicts and descendants of a given transaction.
10
//!
11
//! # Previewing and applying changes
12
//!
13
//! Methods that either preview or apply changes to [`TxGraph`] will return [`Additions`].
14
//! [`Additions`] can be applied back to a [`TxGraph`] or be used to inform persistent storage
15
//! of the changes to [`TxGraph`].
16
//!
17
//! ```
18
//! # use bdk_chain::BlockId;
19
//! # use bdk_chain::tx_graph::TxGraph;
20
//! # use bdk_chain::example_utils::*;
21
//! # use bitcoin::Transaction;
22
//! # let tx_a = tx_from_hex(RAW_TX_1);
23
//! # let tx_b = tx_from_hex(RAW_TX_2);
24
//! let mut graph: TxGraph = TxGraph::default();
25
//!
26
//! // preview a transaction insertion (not actually inserted)
27
//! let additions = graph.insert_tx_preview(tx_a);
28
//! // apply the insertion
29
//! graph.apply_additions(additions);
30
//!
31
//! // you can also insert a transaction directly
32
//! let already_applied_additions = graph.insert_tx(tx_b);
33
//! ```
34
//!
35
//! A [`TxGraph`] can also be updated with another [`TxGraph`].
36
//!
37
//! ```
38
//! # use bdk_chain::BlockId;
39
//! # use bdk_chain::tx_graph::TxGraph;
40
//! # use bdk_chain::example_utils::*;
41
//! # use bitcoin::Transaction;
42
//! # let tx_a = tx_from_hex(RAW_TX_1);
43
//! # let tx_b = tx_from_hex(RAW_TX_2);
44
//! let mut graph: TxGraph = TxGraph::default();
45
//! let update = TxGraph::new(vec![tx_a, tx_b]);
46
//!
47
//! // preview additions as the result of the update
48
//! let additions = graph.determine_additions(&update);
49
//! // apply the additions
50
//! graph.apply_additions(additions);
51
//!
52
//! // we can also apply the update graph directly
53
//! // the additions will be empty as we have already applied the same update above
54
//! let additions = graph.apply_update(update);
55
//! assert!(additions.is_empty());
56
//! ```
57

58
use crate::{
59
    collections::*, keychain::Balance, Anchor, Append, BlockId, ChainOracle, ChainPosition,
60
    ForEachTxOut, FullTxOut,
61
};
62
use alloc::vec::Vec;
63
use bitcoin::{OutPoint, Script, Transaction, TxOut, Txid};
64
use core::{
65
    convert::Infallible,
66
    ops::{Deref, RangeInclusive},
67
};
68

69
/// A graph of transactions and spends.
70
///
71
/// See the [module-level documentation] for more.
72
///
73
/// [module-level documentation]: crate::tx_graph
74
#[derive(Clone, Debug, PartialEq)]
×
75
pub struct TxGraph<A = ()> {
76
    // all transactions that the graph is aware of in format: `(tx_node, tx_anchors, tx_last_seen)`
77
    txs: HashMap<Txid, (TxNodeInternal, BTreeSet<A>, u64)>,
78
    spends: BTreeMap<OutPoint, HashSet<Txid>>,
79
    anchors: BTreeSet<(A, Txid)>,
80

81
    // This atrocity exists so that `TxGraph::outspends()` can return a reference.
82
    // FIXME: This can be removed once `HashSet::new` is a const fn.
83
    empty_outspends: HashSet<Txid>,
84
}
85

86
impl<A> Default for TxGraph<A> {
87
    fn default() -> Self {
706✔
88
        Self {
706✔
89
            txs: Default::default(),
706✔
90
            spends: Default::default(),
706✔
91
            anchors: Default::default(),
706✔
92
            empty_outspends: Default::default(),
706✔
93
        }
706✔
94
    }
706✔
95
}
96

97
/// An outward-facing view of a (transaction) node in the [`TxGraph`].
98
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
×
99
pub struct TxNode<'a, T, A> {
100
    /// Txid of the transaction.
101
    pub txid: Txid,
102
    /// A partial or full representation of the transaction.
103
    pub tx: &'a T,
104
    /// The blocks that the transaction is "anchored" in.
105
    pub anchors: &'a BTreeSet<A>,
106
    /// The last-seen unix timestamp of the transaction as unconfirmed.
107
    pub last_seen_unconfirmed: u64,
108
}
109

110
impl<'a, T, A> Deref for TxNode<'a, T, A> {
111
    type Target = T;
112

113
    fn deref(&self) -> &Self::Target {
×
114
        self.tx
×
115
    }
×
116
}
117

118
/// Internal representation of a transaction node of a [`TxGraph`].
119
///
120
/// This can either be a whole transaction, or a partial transaction (where we only have select
121
/// outputs).
122
#[derive(Clone, Debug, PartialEq)]
×
123
enum TxNodeInternal {
124
    Whole(Transaction),
125
    Partial(BTreeMap<u32, TxOut>),
126
}
127

128
impl Default for TxNodeInternal {
129
    fn default() -> Self {
429✔
130
        Self::Partial(BTreeMap::new())
429✔
131
    }
429✔
132
}
133

134
/// An outwards-facing view of a transaction that is part of the *best chain*'s history.
135
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
×
136
pub struct CanonicalTx<'a, T, A> {
137
    /// How the transaction is observed as (confirmed or unconfirmed).
138
    pub observed_as: ChainPosition<&'a A>,
139
    /// The transaction node (as part of the graph).
140
    pub node: TxNode<'a, T, A>,
141
}
142

143
impl<A> TxGraph<A> {
144
    /// Iterate over all tx outputs known by [`TxGraph`].
145
    ///
146
    /// This includes txouts of both full transactions as well as floating transactions.
147
    pub fn all_txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
1✔
148
        self.txs.iter().flat_map(|(txid, (tx, _, _))| match tx {
3✔
149
            TxNodeInternal::Whole(tx) => tx
1✔
150
                .output
1✔
151
                .iter()
1✔
152
                .enumerate()
1✔
153
                .map(|(vout, txout)| (OutPoint::new(*txid, vout as _), txout))
1✔
154
                .collect::<Vec<_>>(),
1✔
155
            TxNodeInternal::Partial(txouts) => txouts
2✔
156
                .iter()
2✔
157
                .map(|(vout, txout)| (OutPoint::new(*txid, *vout as _), txout))
3✔
158
                .collect::<Vec<_>>(),
2✔
159
        })
3✔
160
    }
1✔
161

162
    /// Iterate over floating txouts known by [`TxGraph`].
163
    ///
164
    /// Floating txouts are txouts that do not have the residing full transaction contained in the
165
    /// graph.
166
    pub fn floating_txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
1✔
167
        self.txs
1✔
168
            .iter()
1✔
169
            .filter_map(|(txid, (tx_node, _, _))| match tx_node {
3✔
170
                TxNodeInternal::Whole(_) => None,
1✔
171
                TxNodeInternal::Partial(txouts) => Some(
2✔
172
                    txouts
2✔
173
                        .iter()
2✔
174
                        .map(|(&vout, txout)| (OutPoint::new(*txid, vout), txout)),
3✔
175
                ),
2✔
176
            })
3✔
177
            .flatten()
1✔
178
    }
1✔
179

180
    /// Iterate over all full transactions in the graph.
181
    pub fn full_txs(&self) -> impl Iterator<Item = TxNode<'_, Transaction, A>> {
8✔
182
        self.txs
8✔
183
            .iter()
8✔
184
            .filter_map(|(&txid, (tx, anchors, last_seen))| match tx {
10✔
185
                TxNodeInternal::Whole(tx) => Some(TxNode {
8✔
186
                    txid,
8✔
187
                    tx,
8✔
188
                    anchors,
8✔
189
                    last_seen_unconfirmed: *last_seen,
8✔
190
                }),
8✔
191
                TxNodeInternal::Partial(_) => None,
2✔
192
            })
10✔
193
    }
8✔
194

195
    /// Get a transaction by txid. This only returns `Some` for full transactions.
196
    ///
197
    /// Refer to [`get_txout`] for getting a specific [`TxOut`].
198
    ///
199
    /// [`get_txout`]: Self::get_txout
200
    pub fn get_tx(&self, txid: Txid) -> Option<&Transaction> {
469✔
201
        self.get_tx_node(txid).map(|n| n.tx)
469✔
202
    }
469✔
203

204
    /// Get a transaction node by txid. This only returns `Some` for full transactions.
205
    pub fn get_tx_node(&self, txid: Txid) -> Option<TxNode<'_, Transaction, A>> {
731✔
206
        match &self.txs.get(&txid)? {
731✔
207
            (TxNodeInternal::Whole(tx), anchors, last_seen) => Some(TxNode {
562✔
208
                txid,
562✔
209
                tx,
562✔
210
                anchors,
562✔
211
                last_seen_unconfirmed: *last_seen,
562✔
212
            }),
562✔
213
            _ => None,
×
214
        }
215
    }
731✔
216

217
    /// Obtains a single tx output (if any) at the specified outpoint.
218
    pub fn get_txout(&self, outpoint: OutPoint) -> Option<&TxOut> {
156✔
219
        match &self.txs.get(&outpoint.txid)?.0 {
156✔
220
            TxNodeInternal::Whole(tx) => tx.output.get(outpoint.vout as usize),
154✔
221
            TxNodeInternal::Partial(txouts) => txouts.get(&outpoint.vout),
1✔
222
        }
223
    }
156✔
224

225
    /// Returns known outputs of a given `txid`.
226
    ///
227
    /// Returns a [`BTreeMap`] of vout to output of the provided `txid`.
228
    pub fn tx_outputs(&self, txid: Txid) -> Option<BTreeMap<u32, &TxOut>> {
2✔
229
        Some(match &self.txs.get(&txid)?.0 {
2✔
230
            TxNodeInternal::Whole(tx) => tx
1✔
231
                .output
1✔
232
                .iter()
1✔
233
                .enumerate()
1✔
234
                .map(|(vout, txout)| (vout as u32, txout))
1✔
235
                .collect::<BTreeMap<_, _>>(),
1✔
236
            TxNodeInternal::Partial(txouts) => txouts
1✔
237
                .iter()
1✔
238
                .map(|(vout, txout)| (*vout, txout))
2✔
239
                .collect::<BTreeMap<_, _>>(),
1✔
240
        })
241
    }
2✔
242

243
    /// Calculates the fee of a given transaction. Returns 0 if `tx` is a coinbase transaction.
244
    /// Returns `Some(_)` if we have all the `TxOut`s being spent by `tx` in the graph (either as
245
    /// the full transactions or individual txouts). If the returned value is negative, then the
246
    /// transaction is invalid according to the graph.
247
    ///
248
    /// Returns `None` if we're missing an input for the tx in the graph.
249
    ///
250
    /// Note `tx` does not have to be in the graph for this to work.
251
    pub fn calculate_fee(&self, tx: &Transaction) -> Option<i64> {
21✔
252
        if tx.is_coin_base() {
21✔
253
            return Some(0);
1✔
254
        }
20✔
255
        let inputs_sum = tx
20✔
256
            .input
20✔
257
            .iter()
20✔
258
            .map(|txin| {
26✔
259
                self.get_txout(txin.previous_output)
26✔
260
                    .map(|txout| txout.value as i64)
26✔
261
            })
26✔
262
            .sum::<Option<i64>>()?;
20✔
263

264
        let outputs_sum = tx
19✔
265
            .output
19✔
266
            .iter()
19✔
267
            .map(|txout| txout.value as i64)
29✔
268
            .sum::<i64>();
19✔
269

19✔
270
        Some(inputs_sum - outputs_sum)
19✔
271
    }
21✔
272

273
    /// The transactions spending from this output.
274
    ///
275
    /// `TxGraph` allows conflicting transactions within the graph. Obviously the transactions in
276
    /// the returned set will never be in the same active-chain.
277
    pub fn outspends(&self, outpoint: OutPoint) -> &HashSet<Txid> {
4✔
278
        self.spends.get(&outpoint).unwrap_or(&self.empty_outspends)
4✔
279
    }
4✔
280

281
    /// Iterates over the transactions spending from `txid`.
282
    ///
283
    /// The iterator item is a union of `(vout, txid-set)` where:
284
    ///
285
    /// - `vout` is the provided `txid`'s outpoint that is being spent
286
    /// - `txid-set` is the set of txids spending the `vout`.
287
    pub fn tx_spends(
1✔
288
        &self,
1✔
289
        txid: Txid,
1✔
290
    ) -> impl DoubleEndedIterator<Item = (u32, &HashSet<Txid>)> + '_ {
1✔
291
        let start = OutPoint { txid, vout: 0 };
1✔
292
        let end = OutPoint {
1✔
293
            txid,
1✔
294
            vout: u32::MAX,
1✔
295
        };
1✔
296
        self.spends
1✔
297
            .range(start..=end)
1✔
298
            .map(|(outpoint, spends)| (outpoint.vout, spends))
1✔
299
    }
1✔
300

301
    /// Creates an iterator that filters and maps descendants from the starting `txid`.
302
    ///
303
    /// The supplied closure takes in two inputs `(depth, descendant_txid)`:
304
    ///
305
    /// * `depth` is the distance between the starting `txid` and the `descendant_txid`. I.e., if the
306
    ///     descendant is spending an output of the starting `txid`; the `depth` will be 1.
307
    /// * `descendant_txid` is the descendant's txid which we are considering to walk.
308
    ///
309
    /// The supplied closure returns an `Option<T>`, allowing the caller to map each node it vists
310
    /// and decide whether to visit descendants.
311
    pub fn walk_descendants<'g, F, O>(&'g self, txid: Txid, walk_map: F) -> TxDescendants<A, F>
1✔
312
    where
1✔
313
        F: FnMut(usize, Txid) -> Option<O> + 'g,
1✔
314
    {
1✔
315
        TxDescendants::new_exclude_root(self, txid, walk_map)
1✔
316
    }
1✔
317

318
    /// Creates an iterator that both filters and maps conflicting transactions (this includes
319
    /// descendants of directly-conflicting transactions, which are also considered conflicts).
320
    ///
321
    /// Refer to [`Self::walk_descendants`] for `walk_map` usage.
322
    pub fn walk_conflicts<'g, F, O>(
166✔
323
        &'g self,
166✔
324
        tx: &'g Transaction,
166✔
325
        walk_map: F,
166✔
326
    ) -> TxDescendants<A, F>
166✔
327
    where
166✔
328
        F: FnMut(usize, Txid) -> Option<O> + 'g,
166✔
329
    {
166✔
330
        let txids = self.direct_conflicts_of_tx(tx).map(|(_, txid)| txid);
166✔
331
        TxDescendants::from_multiple_include_root(self, txids, walk_map)
166✔
332
    }
166✔
333

334
    /// Given a transaction, return an iterator of txids that directly conflict with the given
335
    /// transaction's inputs (spends). The conflicting txids are returned with the given
336
    /// transaction's vin (in which it conflicts).
337
    ///
338
    /// Note that this only returns directly conflicting txids and does not include descendants of
339
    /// those txids (which are technically also conflicting).
340
    pub fn direct_conflicts_of_tx<'g>(
166✔
341
        &'g self,
166✔
342
        tx: &'g Transaction,
166✔
343
    ) -> impl Iterator<Item = (usize, Txid)> + '_ {
166✔
344
        let txid = tx.txid();
166✔
345
        tx.input
166✔
346
            .iter()
166✔
347
            .enumerate()
166✔
348
            .filter_map(move |(vin, txin)| self.spends.get(&txin.previous_output).zip(Some(vin)))
166✔
349
            .flat_map(|(spends, vin)| core::iter::repeat(vin).zip(spends.iter().cloned()))
166✔
350
            .filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
171✔
351
    }
166✔
352

353
    /// Get all transaction anchors known by [`TxGraph`].
354
    pub fn all_anchors(&self) -> &BTreeSet<(A, Txid)> {
×
355
        &self.anchors
×
356
    }
×
357

358
    /// Whether the graph has any transactions or outputs in it.
359
    pub fn is_empty(&self) -> bool {
×
360
        self.txs.is_empty()
×
361
    }
×
362
}
363

364
impl<A: Clone + Ord> TxGraph<A> {
365
    /// Construct a new [`TxGraph`] from a list of transactions.
366
    pub fn new(txs: impl IntoIterator<Item = Transaction>) -> Self {
×
367
        let mut new = Self::default();
×
368
        for tx in txs.into_iter() {
×
369
            let _ = new.insert_tx(tx);
×
370
        }
×
371
        new
×
372
    }
×
373

374
    /// Returns the resultant [`Additions`] if the given `txout` is inserted at `outpoint`. Does not
375
    /// mutate `self`.
376
    ///
377
    /// Inserting floating txouts are useful for determining fee/feerate of transactions we care
378
    /// about.
379
    ///
380
    /// The [`Additions`] result will be empty if the `outpoint` (or a full transaction containing
381
    /// the `outpoint`) already existed in `self`.
382
    pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> Additions<A> {
8✔
383
        let mut update = Self::default();
8✔
384
        update.txs.insert(
8✔
385
            outpoint.txid,
8✔
386
            (
8✔
387
                TxNodeInternal::Partial([(outpoint.vout, txout)].into()),
8✔
388
                BTreeSet::new(),
8✔
389
                0,
8✔
390
            ),
8✔
391
        );
8✔
392
        self.determine_additions(&update)
8✔
393
    }
8✔
394

395
    /// Inserts the given [`TxOut`] at [`OutPoint`].
396
    ///
397
    /// This is equivalent to calling [`insert_txout_preview`] and [`apply_additions`] in sequence.
398
    ///
399
    /// [`insert_txout_preview`]: Self::insert_txout_preview
400
    /// [`apply_additions`]: Self::apply_additions
401
    pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) -> Additions<A> {
8✔
402
        let additions = self.insert_txout_preview(outpoint, txout);
8✔
403
        self.apply_additions(additions.clone());
8✔
404
        additions
8✔
405
    }
8✔
406

407
    /// Returns the resultant [`Additions`] if the given transaction is inserted. Does not actually
408
    /// mutate [`Self`].
409
    ///
410
    /// The [`Additions`] result will be empty if `tx` already exists in `self`.
411
    pub fn insert_tx_preview(&self, tx: Transaction) -> Additions<A> {
204✔
412
        let mut update = Self::default();
204✔
413
        update
204✔
414
            .txs
204✔
415
            .insert(tx.txid(), (TxNodeInternal::Whole(tx), BTreeSet::new(), 0));
204✔
416
        self.determine_additions(&update)
204✔
417
    }
204✔
418

419
    /// Inserts the given transaction into [`TxGraph`].
420
    ///
421
    /// The [`Additions`] returned will be empty if `tx` already exists.
422
    pub fn insert_tx(&mut self, tx: Transaction) -> Additions<A> {
204✔
423
        let additions = self.insert_tx_preview(tx);
204✔
424
        self.apply_additions(additions.clone());
204✔
425
        additions
204✔
426
    }
204✔
427

428
    /// Returns the resultant [`Additions`] if the `txid` is set in `anchor`.
429
    pub fn insert_anchor_preview(&self, txid: Txid, anchor: A) -> Additions<A> {
145✔
430
        let mut update = Self::default();
145✔
431
        update.anchors.insert((anchor, txid));
145✔
432
        self.determine_additions(&update)
145✔
433
    }
145✔
434

435
    /// Inserts the given `anchor` into [`TxGraph`].
436
    ///
437
    /// This is equivalent to calling [`insert_anchor_preview`] and [`apply_additions`] in sequence.
438
    /// The [`Additions`] returned will be empty if graph already knows that `txid` exists in
439
    /// `anchor`.
440
    ///
441
    /// [`insert_anchor_preview`]: Self::insert_anchor_preview
442
    /// [`apply_additions`]: Self::apply_additions
443
    pub fn insert_anchor(&mut self, txid: Txid, anchor: A) -> Additions<A> {
145✔
444
        let additions = self.insert_anchor_preview(txid, anchor);
145✔
445
        self.apply_additions(additions.clone());
145✔
446
        additions
145✔
447
    }
145✔
448

449
    /// Returns the resultant [`Additions`] if the `txid` is set to `seen_at`.
450
    ///
451
    /// Note that [`TxGraph`] only keeps track of the lastest `seen_at`.
452
    pub fn insert_seen_at_preview(&self, txid: Txid, seen_at: u64) -> Additions<A> {
28✔
453
        let mut update = Self::default();
28✔
454
        let (_, _, update_last_seen) = update.txs.entry(txid).or_default();
28✔
455
        *update_last_seen = seen_at;
28✔
456
        self.determine_additions(&update)
28✔
457
    }
28✔
458

459
    /// Inserts the given `seen_at` into [`TxGraph`].
460
    ///
461
    /// This is equivalent to calling [`insert_seen_at_preview`] and [`apply_additions`] in
462
    /// sequence.
463
    ///
464
    /// [`insert_seen_at_preview`]: Self::insert_seen_at_preview
465
    /// [`apply_additions`]: Self::apply_additions
466
    pub fn insert_seen_at(&mut self, txid: Txid, seen_at: u64) -> Additions<A> {
28✔
467
        let additions = self.insert_seen_at_preview(txid, seen_at);
28✔
468
        self.apply_additions(additions.clone());
28✔
469
        additions
28✔
470
    }
28✔
471

472
    /// Extends this graph with another so that `self` becomes the union of the two sets of
473
    /// transactions.
474
    ///
475
    /// The returned [`Additions`] is the set difference between `update` and `self` (transactions that
476
    /// exist in `update` but not in `self`).
477
    pub fn apply_update(&mut self, update: TxGraph<A>) -> Additions<A> {
168✔
478
        let additions = self.determine_additions(&update);
168✔
479
        self.apply_additions(additions.clone());
168✔
480
        additions
168✔
481
    }
168✔
482

483
    /// Applies [`Additions`] to [`TxGraph`].
484
    pub fn apply_additions(&mut self, additions: Additions<A>) {
693✔
485
        for tx in additions.txs {
1,067✔
486
            let txid = tx.txid();
375✔
487

375✔
488
            tx.input
375✔
489
                .iter()
375✔
490
                .map(|txin| txin.previous_output)
375✔
491
                // coinbase spends are not to be counted
375✔
492
                .filter(|outpoint| !outpoint.is_null())
375✔
493
                // record spend as this tx has spent this outpoint
375✔
494
                .for_each(|outpoint| {
375✔
495
                    self.spends.entry(outpoint).or_default().insert(txid);
70✔
496
                });
375✔
497

375✔
498
            match self.txs.get_mut(&txid) {
375✔
499
                Some((tx_node @ TxNodeInternal::Partial(_), _, _)) => {
1✔
500
                    *tx_node = TxNodeInternal::Whole(tx);
1✔
501
                }
1✔
502
                Some((TxNodeInternal::Whole(tx), _, _)) => {
×
503
                    debug_assert_eq!(
504
                        tx.txid(),
×
505
                        txid,
506
                        "tx should produce txid that is same as key"
×
507
                    );
508
                }
509
                None => {
373✔
510
                    self.txs
373✔
511
                        .insert(txid, (TxNodeInternal::Whole(tx), BTreeSet::new(), 0));
373✔
512
                }
373✔
513
            }
514
        }
515

516
        for (outpoint, txout) in additions.txouts {
698✔
517
            let tx_entry = self
6✔
518
                .txs
6✔
519
                .entry(outpoint.txid)
6✔
520
                .or_insert_with(Default::default);
6✔
521

6✔
522
            match tx_entry {
6✔
523
                (TxNodeInternal::Whole(_), _, _) => { /* do nothing since we already have full tx */
×
524
                }
×
525
                (TxNodeInternal::Partial(txouts), _, _) => {
6✔
526
                    txouts.insert(outpoint.vout, txout);
6✔
527
                }
6✔
528
            }
529
        }
530

531
        for (anchor, txid) in additions.anchors {
980✔
532
            if self.anchors.insert((anchor.clone(), txid)) {
288✔
533
                let (_, anchors, _) = self.txs.entry(txid).or_insert_with(Default::default);
288✔
534
                anchors.insert(anchor);
288✔
535
            }
288✔
536
        }
537

538
        for (txid, new_last_seen) in additions.last_seen {
700✔
539
            let (_, _, last_seen) = self.txs.entry(txid).or_insert_with(Default::default);
8✔
540
            if new_last_seen > *last_seen {
8✔
541
                *last_seen = new_last_seen;
8✔
542
            }
8✔
543
        }
544
    }
692✔
545

546
    /// Previews the resultant [`Additions`] when [`Self`] is updated against the `update` graph.
547
    ///
548
    /// The [`Additions`] would be the set difference between `update` and `self` (transactions that
549
    /// exist in `update` but not in `self`).
550
    pub fn determine_additions(&self, update: &TxGraph<A>) -> Additions<A> {
554✔
551
        let mut additions = Additions::default();
554✔
552

553
        for (&txid, (update_tx_node, _, update_last_seen)) in &update.txs {
965✔
554
            let prev_last_seen: u64 = match (self.txs.get(&txid), update_tx_node) {
411✔
555
                (None, TxNodeInternal::Whole(update_tx)) => {
373✔
556
                    additions.txs.insert(update_tx.clone());
373✔
557
                    0
373✔
558
                }
559
                (None, TxNodeInternal::Partial(update_txos)) => {
5✔
560
                    additions.txouts.extend(
5✔
561
                        update_txos
5✔
562
                            .iter()
5✔
563
                            .map(|(&vout, txo)| (OutPoint::new(txid, vout), txo.clone())),
5✔
564
                    );
5✔
565
                    0
5✔
566
                }
567
                (Some((TxNodeInternal::Whole(_), _, last_seen)), _) => *last_seen,
29✔
568
                (
569
                    Some((TxNodeInternal::Partial(_), _, last_seen)),
1✔
570
                    TxNodeInternal::Whole(update_tx),
1✔
571
                ) => {
1✔
572
                    additions.txs.insert(update_tx.clone());
1✔
573
                    *last_seen
1✔
574
                }
575
                (
576
                    Some((TxNodeInternal::Partial(txos), _, last_seen)),
3✔
577
                    TxNodeInternal::Partial(update_txos),
3✔
578
                ) => {
3✔
579
                    additions.txouts.extend(
3✔
580
                        update_txos
3✔
581
                            .iter()
3✔
582
                            .filter(|(vout, _)| !txos.contains_key(*vout))
3✔
583
                            .map(|(&vout, txo)| (OutPoint::new(txid, vout), txo.clone())),
3✔
584
                    );
3✔
585
                    *last_seen
3✔
586
                }
587
            };
588

589
            if *update_last_seen > prev_last_seen {
411✔
590
                additions.last_seen.insert(txid, *update_last_seen);
8✔
591
            }
403✔
592
        }
593

594
        additions.anchors = update.anchors.difference(&self.anchors).cloned().collect();
555✔
595

555✔
596
        additions
555✔
597
    }
555✔
598
}
599

600
impl<A: Anchor> TxGraph<A> {
601
    /// Get the position of the transaction in `chain` with tip `chain_tip`.
602
    ///
603
    /// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is
604
    /// returned.
605
    ///
606
    /// # Error
607
    ///
608
    /// An error will occur if the [`ChainOracle`] implementation (`chain`) fails. If the
609
    /// [`ChainOracle`] is infallible, [`get_chain_position`] can be used instead.
610
    ///
611
    /// [`get_chain_position`]: Self::get_chain_position
612
    pub fn try_get_chain_position<C: ChainOracle>(
760✔
613
        &self,
760✔
614
        chain: &C,
760✔
615
        chain_tip: BlockId,
760✔
616
        txid: Txid,
760✔
617
    ) -> Result<Option<ChainPosition<&A>>, C::Error> {
760✔
618
        let (tx_node, anchors, last_seen) = match self.txs.get(&txid) {
760✔
619
            Some(v) => v,
757✔
620
            None => return Ok(None),
3✔
621
        };
622

623
        for anchor in anchors {
781✔
624
            match chain.is_block_in_chain(anchor.anchor_block(), chain_tip)? {
616✔
625
                Some(true) => return Ok(Some(ChainPosition::Confirmed(anchor))),
592✔
626
                _ => continue,
24✔
627
            }
628
        }
629

630
        // The tx is not anchored to a block which is in the best chain, let's check whether we can
631
        // ignore it by checking conflicts!
632
        let tx = match tx_node {
165✔
633
            TxNodeInternal::Whole(tx) => tx,
165✔
634
            TxNodeInternal::Partial(_) => {
635
                // Partial transactions (outputs only) cannot have conflicts.
636
                return Ok(None);
×
637
            }
638
        };
639

640
        // If a conflicting tx is in the best chain, or has `last_seen` higher than this tx, then
641
        // this tx cannot exist in the best chain
642
        for conflicting_tx in self.walk_conflicts(tx, |_, txid| self.get_tx_node(txid)) {
165✔
643
            for block in conflicting_tx.anchors.iter().map(A::anchor_block) {
5✔
644
                if chain.is_block_in_chain(block, chain_tip)? == Some(true) {
1✔
645
                    // conflicting tx is in best chain, so the current tx cannot be in best chain!
646
                    return Ok(None);
1✔
647
                }
×
648
            }
649
            if conflicting_tx.last_seen_unconfirmed > *last_seen {
4✔
650
                return Ok(None);
2✔
651
            }
2✔
652
        }
653

654
        Ok(Some(ChainPosition::Unconfirmed(*last_seen)))
162✔
655
    }
760✔
656

657
    /// Get the position of the transaction in `chain` with tip `chain_tip`.
658
    ///
659
    /// This is the infallible version of [`try_get_chain_position`].
660
    ///
661
    /// [`try_get_chain_position`]: Self::try_get_chain_position
662
    pub fn get_chain_position<C: ChainOracle<Error = Infallible>>(
208✔
663
        &self,
208✔
664
        chain: &C,
208✔
665
        chain_tip: BlockId,
208✔
666
        txid: Txid,
208✔
667
    ) -> Option<ChainPosition<&A>> {
208✔
668
        self.try_get_chain_position(chain, chain_tip, txid)
208✔
669
            .expect("error is infallible")
208✔
670
    }
208✔
671

672
    /// Get the txid of the spending transaction and where the spending transaction is observed in
673
    /// the `chain` of `chain_tip`.
674
    ///
675
    /// If no in-chain transaction spends `outpoint`, `None` will be returned.
676
    ///
677
    /// # Error
678
    ///
679
    /// An error will occur only if the [`ChainOracle`] implementation (`chain`) fails.
680
    ///
681
    /// If the [`ChainOracle`] is infallible, [`get_chain_spend`] can be used instead.
682
    ///
683
    /// [`get_chain_spend`]: Self::get_chain_spend
684
    pub fn try_get_chain_spend<C: ChainOracle>(
257✔
685
        &self,
257✔
686
        chain: &C,
257✔
687
        chain_tip: BlockId,
257✔
688
        outpoint: OutPoint,
257✔
689
    ) -> Result<Option<(ChainPosition<&A>, Txid)>, C::Error> {
257✔
690
        if self
257✔
691
            .try_get_chain_position(chain, chain_tip, outpoint.txid)?
257✔
692
            .is_none()
257✔
693
        {
694
            return Ok(None);
×
695
        }
257✔
696
        if let Some(spends) = self.spends.get(&outpoint) {
257✔
697
            for &txid in spends {
35✔
698
                if let Some(observed_at) = self.try_get_chain_position(chain, chain_tip, txid)? {
35✔
699
                    return Ok(Some((observed_at, txid)));
34✔
700
                }
1✔
701
            }
702
        }
223✔
703
        Ok(None)
223✔
704
    }
257✔
705

706
    /// Get the txid of the spending transaction and where the spending transaction is observed in
707
    /// the `chain` of `chain_tip`.
708
    ///
709
    /// This is the infallible version of [`try_get_chain_spend`]
710
    ///
711
    /// [`try_get_chain_spend`]: Self::try_get_chain_spend
712
    pub fn get_chain_spend<C: ChainOracle<Error = Infallible>>(
4✔
713
        &self,
4✔
714
        chain: &C,
4✔
715
        static_block: BlockId,
4✔
716
        outpoint: OutPoint,
4✔
717
    ) -> Option<(ChainPosition<&A>, Txid)> {
4✔
718
        self.try_get_chain_spend(chain, static_block, outpoint)
4✔
719
            .expect("error is infallible")
4✔
720
    }
4✔
721

722
    /// List graph transactions that are in `chain` with `chain_tip`.
723
    ///
724
    /// Each transaction is represented as a [`CanonicalTx`] that contains where the transaction is
725
    /// observed in-chain, and the [`TxNode`].
726
    ///
727
    /// # Error
728
    ///
729
    /// If the [`ChainOracle`] implementation (`chain`) fails, an error will be returned with the
730
    /// returned item.
731
    ///
732
    /// If the [`ChainOracle`] is infallible, [`list_chain_txs`] can be used instead.
733
    ///
734
    /// [`list_chain_txs`]: Self::list_chain_txs
735
    pub fn try_list_chain_txs<'a, C: ChainOracle + 'a>(
7✔
736
        &'a self,
7✔
737
        chain: &'a C,
7✔
738
        chain_tip: BlockId,
7✔
739
    ) -> impl Iterator<Item = Result<CanonicalTx<'a, Transaction, A>, C::Error>> {
7✔
740
        self.full_txs().filter_map(move |tx| {
7✔
741
            self.try_get_chain_position(chain, chain_tip, tx.txid)
7✔
742
                .map(|v| {
7✔
743
                    v.map(|observed_in| CanonicalTx {
7✔
744
                        observed_as: observed_in,
7✔
745
                        node: tx,
7✔
746
                    })
7✔
747
                })
7✔
748
                .transpose()
7✔
749
        })
7✔
750
    }
7✔
751

752
    /// List graph transactions that are in `chain` with `chain_tip`.
753
    ///
754
    /// This is the infallible version of [`try_list_chain_txs`].
755
    ///
756
    /// [`try_list_chain_txs`]: Self::try_list_chain_txs
757
    pub fn list_chain_txs<'a, C: ChainOracle + 'a>(
7✔
758
        &'a self,
7✔
759
        chain: &'a C,
7✔
760
        chain_tip: BlockId,
7✔
761
    ) -> impl Iterator<Item = CanonicalTx<'a, Transaction, A>> {
7✔
762
        self.try_list_chain_txs(chain, chain_tip)
7✔
763
            .map(|r| r.expect("oracle is infallible"))
7✔
764
    }
7✔
765

766
    /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
767
    /// `chain_tip`.
768
    ///
769
    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
770
    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
771
    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
772
    ///
773
    /// Floating outputs are ignored.
774
    ///
775
    /// # Error
776
    ///
777
    /// An [`Iterator::Item`] can be an [`Err`] if the [`ChainOracle`] implementation (`chain`)
778
    /// fails.
779
    ///
780
    /// If the [`ChainOracle`] implementation is infallible, [`filter_chain_txouts`] can be used
781
    /// instead.
782
    ///
783
    /// [`filter_chain_txouts`]: Self::filter_chain_txouts
784
    pub fn try_filter_chain_txouts<'a, C: ChainOracle + 'a, OI: Clone + 'a>(
164✔
785
        &'a self,
164✔
786
        chain: &'a C,
164✔
787
        chain_tip: BlockId,
164✔
788
        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
164✔
789
    ) -> impl Iterator<Item = Result<(OI, FullTxOut<A>), C::Error>> + 'a {
164✔
790
        outpoints
164✔
791
            .into_iter()
164✔
792
            .map(
164✔
793
                move |(spk_i, op)| -> Result<Option<(OI, FullTxOut<_>)>, C::Error> {
164✔
794
                    let tx_node = match self.get_tx_node(op.txid) {
253✔
795
                        Some(n) => n,
253✔
796
                        None => return Ok(None),
×
797
                    };
798

799
                    let txout = match tx_node.tx.output.get(op.vout as usize) {
253✔
800
                        Some(txout) => txout.clone(),
253✔
801
                        None => return Ok(None),
×
802
                    };
803

804
                    let chain_position =
253✔
805
                        match self.try_get_chain_position(chain, chain_tip, op.txid)? {
253✔
806
                            Some(pos) => pos.cloned(),
253✔
807
                            None => return Ok(None),
×
808
                        };
809

810
                    let spent_by = self
253✔
811
                        .try_get_chain_spend(chain, chain_tip, op)?
253✔
812
                        .map(|(a, txid)| (a.cloned(), txid));
253✔
813

253✔
814
                    Ok(Some((
253✔
815
                        spk_i,
253✔
816
                        FullTxOut {
253✔
817
                            outpoint: op,
253✔
818
                            txout,
253✔
819
                            chain_position,
253✔
820
                            spent_by,
253✔
821
                            is_on_coinbase: tx_node.tx.is_coin_base(),
253✔
822
                        },
253✔
823
                    )))
253✔
824
                },
253✔
825
            )
164✔
826
            .filter_map(Result::transpose)
164✔
827
    }
164✔
828

829
    /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
830
    /// `chain_tip`.
831
    ///
832
    /// This is the infallible version of [`try_filter_chain_txouts`].
833
    ///
834
    /// [`try_filter_chain_txouts`]: Self::try_filter_chain_txouts
835
    pub fn filter_chain_txouts<'a, C: ChainOracle<Error = Infallible> + 'a, OI: Clone + 'a>(
5✔
836
        &'a self,
5✔
837
        chain: &'a C,
5✔
838
        chain_tip: BlockId,
5✔
839
        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
5✔
840
    ) -> impl Iterator<Item = (OI, FullTxOut<A>)> + 'a {
5✔
841
        self.try_filter_chain_txouts(chain, chain_tip, outpoints)
5✔
842
            .map(|r| r.expect("oracle is infallible"))
25✔
843
    }
5✔
844

845
    /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in
846
    /// `chain` with `chain_tip`.
847
    ///
848
    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
849
    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
850
    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
851
    ///
852
    /// Floating outputs are ignored.
853
    ///
854
    /// # Error
855
    ///
856
    /// An [`Iterator::Item`] can be an [`Err`] if the [`ChainOracle`] implementation (`chain`)
857
    /// fails.
858
    ///
859
    /// If the [`ChainOracle`] implementation is infallible, [`filter_chain_unspents`] can be used
860
    /// instead.
861
    ///
862
    /// [`filter_chain_unspents`]: Self::filter_chain_unspents
863
    pub fn try_filter_chain_unspents<'a, C: ChainOracle + 'a, OI: Clone + 'a>(
159✔
864
        &'a self,
159✔
865
        chain: &'a C,
159✔
866
        chain_tip: BlockId,
159✔
867
        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
159✔
868
    ) -> impl Iterator<Item = Result<(OI, FullTxOut<A>), C::Error>> + 'a {
159✔
869
        self.try_filter_chain_txouts(chain, chain_tip, outpoints)
159✔
870
            .filter(|r| match r {
228✔
871
                // keep unspents, drop spents
872
                Ok((_, full_txo)) => full_txo.spent_by.is_none(),
228✔
873
                // keep errors
874
                Err(_) => true,
×
875
            })
228✔
876
    }
159✔
877

878
    /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in
879
    /// `chain` with `chain_tip`.
880
    ///
881
    /// This is the infallible version of [`try_filter_chain_unspents`].
882
    ///
883
    /// [`try_filter_chain_unspents`]: Self::try_filter_chain_unspents
884
    pub fn filter_chain_unspents<'a, C: ChainOracle<Error = Infallible> + 'a, OI: Clone + 'a>(
151✔
885
        &'a self,
151✔
886
        chain: &'a C,
151✔
887
        chain_tip: BlockId,
151✔
888
        txouts: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
151✔
889
    ) -> impl Iterator<Item = (OI, FullTxOut<A>)> + 'a {
151✔
890
        self.try_filter_chain_unspents(chain, chain_tip, txouts)
151✔
891
            .map(|r| r.expect("oracle is infallible"))
180✔
892
    }
151✔
893

894
    /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`.
895
    ///
896
    /// The output of `trust_predicate` should return `true` for scripts that we trust.
897
    ///
898
    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
899
    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
900
    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
901
    ///
902
    /// If the provided [`ChainOracle`] implementation (`chain`) is infallible, [`balance`] can be
903
    /// used instead.
904
    ///
905
    /// [`balance`]: Self::balance
906
    pub fn try_balance<C: ChainOracle, OI: Clone>(
8✔
907
        &self,
8✔
908
        chain: &C,
8✔
909
        chain_tip: BlockId,
8✔
910
        outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
8✔
911
        mut trust_predicate: impl FnMut(&OI, &Script) -> bool,
8✔
912
    ) -> Result<Balance, C::Error> {
8✔
913
        let mut immature = 0;
8✔
914
        let mut trusted_pending = 0;
8✔
915
        let mut untrusted_pending = 0;
8✔
916
        let mut confirmed = 0;
8✔
917

918
        for res in self.try_filter_chain_unspents(chain, chain_tip, outpoints) {
23✔
919
            let (spk_i, txout) = res?;
23✔
920

921
            match &txout.chain_position {
23✔
922
                ChainPosition::Confirmed(_) => {
923
                    if txout.is_confirmed_and_spendable(chain_tip.height) {
11✔
924
                        confirmed += txout.txout.value;
6✔
925
                    } else if !txout.is_mature(chain_tip.height) {
6✔
926
                        immature += txout.txout.value;
5✔
927
                    }
5✔
928
                }
929
                ChainPosition::Unconfirmed(_) => {
930
                    if trust_predicate(&spk_i, &txout.txout.script_pubkey) {
12✔
931
                        trusted_pending += txout.txout.value;
7✔
932
                    } else {
7✔
933
                        untrusted_pending += txout.txout.value;
5✔
934
                    }
5✔
935
                }
936
            }
937
        }
938

939
        Ok(Balance {
8✔
940
            immature,
8✔
941
            trusted_pending,
8✔
942
            untrusted_pending,
8✔
943
            confirmed,
8✔
944
        })
8✔
945
    }
8✔
946

947
    /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`.
948
    ///
949
    /// This is the infallible version of [`try_balance`].
950
    ///
951
    /// [`try_balance`]: Self::try_balance
952
    pub fn balance<C: ChainOracle<Error = Infallible>, OI: Clone>(
8✔
953
        &self,
8✔
954
        chain: &C,
8✔
955
        chain_tip: BlockId,
8✔
956
        outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
8✔
957
        trust_predicate: impl FnMut(&OI, &Script) -> bool,
8✔
958
    ) -> Balance {
8✔
959
        self.try_balance(chain, chain_tip, outpoints, trust_predicate)
8✔
960
            .expect("oracle is infallible")
8✔
961
    }
8✔
962
}
963

964
/// A structure that represents changes to a [`TxGraph`].
965
///
966
/// It is named "additions" because [`TxGraph`] is monotone, so transactions can only be added and
967
/// not removed.
968
///
969
/// Refer to [module-level documentation] for more.
970
///
971
/// [module-level documentation]: crate::tx_graph
972
#[derive(Debug, Clone, PartialEq)]
554✔
973
#[cfg_attr(
974
    feature = "serde",
975
    derive(serde::Deserialize, serde::Serialize),
×
976
    serde(
977
        crate = "serde_crate",
978
        bound(
979
            deserialize = "A: Ord + serde::Deserialize<'de>",
980
            serialize = "A: Ord + serde::Serialize",
981
        )
982
    )
983
)]
984
#[must_use]
985
pub struct Additions<A = ()> {
986
    /// Added transactions.
987
    pub txs: BTreeSet<Transaction>,
988
    /// Added txouts.
989
    pub txouts: BTreeMap<OutPoint, TxOut>,
990
    /// Added anchors.
991
    pub anchors: BTreeSet<(A, Txid)>,
992
    /// Added last-seen unix timestamps of transactions.
993
    pub last_seen: BTreeMap<Txid, u64>,
994
}
995

996
impl<A> Default for Additions<A> {
997
    fn default() -> Self {
2,061✔
998
        Self {
2,061✔
999
            txs: Default::default(),
2,061✔
1000
            txouts: Default::default(),
2,061✔
1001
            anchors: Default::default(),
2,061✔
1002
            last_seen: Default::default(),
2,061✔
1003
        }
2,061✔
1004
    }
2,061✔
1005
}
1006

1007
impl<A> Additions<A> {
1008
    /// Returns true if the [`Additions`] is empty (no transactions or txouts).
1009
    pub fn is_empty(&self) -> bool {
373✔
1010
        self.txs.is_empty() && self.txouts.is_empty()
373✔
1011
    }
373✔
1012

1013
    /// Iterates over all outpoints contained within [`Additions`].
1014
    pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
×
1015
        self.txs
×
1016
            .iter()
×
1017
            .flat_map(|tx| {
×
1018
                tx.output
×
1019
                    .iter()
×
1020
                    .enumerate()
×
1021
                    .map(move |(vout, txout)| (OutPoint::new(tx.txid(), vout as _), txout))
×
1022
            })
×
1023
            .chain(self.txouts.iter().map(|(op, txout)| (*op, txout)))
×
1024
    }
×
1025
}
1026

1027
impl<A: Ord> Append for Additions<A> {
1028
    fn append(&mut self, mut other: Self) {
630✔
1029
        self.txs.append(&mut other.txs);
630✔
1030
        self.txouts.append(&mut other.txouts);
630✔
1031
        self.anchors.append(&mut other.anchors);
630✔
1032

630✔
1033
        // last_seen timestamps should only increase
630✔
1034
        self.last_seen.extend(
630✔
1035
            other
630✔
1036
                .last_seen
630✔
1037
                .into_iter()
630✔
1038
                .filter(|(txid, update_ls)| self.last_seen.get(txid) < Some(update_ls))
630✔
1039
                .collect::<Vec<_>>(),
630✔
1040
        );
630✔
1041
    }
630✔
1042

1043
    fn is_empty(&self) -> bool {
×
1044
        self.txs.is_empty()
×
1045
            && self.txouts.is_empty()
×
1046
            && self.anchors.is_empty()
×
1047
            && self.last_seen.is_empty()
×
1048
    }
×
1049
}
1050

1051
impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
1052
    fn as_ref(&self) -> &TxGraph<A> {
×
1053
        self
×
1054
    }
×
1055
}
1056

1057
impl<A> ForEachTxOut for Additions<A> {
1058
    fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
×
1059
        self.txouts().for_each(f)
×
1060
    }
×
1061
}
1062

1063
impl<A> ForEachTxOut for TxGraph<A> {
1064
    fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
×
1065
        self.all_txouts().for_each(f)
×
1066
    }
×
1067
}
1068

1069
/// An iterator that traverses transaction descendants.
1070
///
1071
/// This `struct` is created by the [`walk_descendants`] method of [`TxGraph`].
1072
///
1073
/// [`walk_descendants`]: TxGraph::walk_descendants
1074
pub struct TxDescendants<'g, A, F> {
1075
    graph: &'g TxGraph<A>,
1076
    visited: HashSet<Txid>,
1077
    stack: Vec<(usize, Txid)>,
1078
    filter_map: F,
1079
}
1080

1081
impl<'g, A, F> TxDescendants<'g, A, F> {
1082
    /// Creates a `TxDescendants` that includes the starting `txid` when iterating.
1083
    #[allow(unused)]
1084
    pub(crate) fn new_include_root(graph: &'g TxGraph<A>, txid: Txid, filter_map: F) -> Self {
×
1085
        Self {
×
1086
            graph,
×
1087
            visited: Default::default(),
×
1088
            stack: [(0, txid)].into(),
×
1089
            filter_map,
×
1090
        }
×
1091
    }
×
1092

1093
    /// Creates a `TxDescendants` that excludes the starting `txid` when iterating.
1094
    pub(crate) fn new_exclude_root(graph: &'g TxGraph<A>, txid: Txid, filter_map: F) -> Self {
1✔
1095
        let mut descendants = Self {
1✔
1096
            graph,
1✔
1097
            visited: Default::default(),
1✔
1098
            stack: Default::default(),
1✔
1099
            filter_map,
1✔
1100
        };
1✔
1101
        descendants.populate_stack(1, txid);
1✔
1102
        descendants
1✔
1103
    }
1✔
1104

1105
    /// Creates a `TxDescendants` from multiple starting transactions that include the starting
1106
    /// `txid`s when iterating.
1107
    pub(crate) fn from_multiple_include_root<I>(
166✔
1108
        graph: &'g TxGraph<A>,
166✔
1109
        txids: I,
166✔
1110
        filter_map: F,
166✔
1111
    ) -> Self
166✔
1112
    where
166✔
1113
        I: IntoIterator<Item = Txid>,
166✔
1114
    {
166✔
1115
        Self {
166✔
1116
            graph,
166✔
1117
            visited: Default::default(),
166✔
1118
            stack: txids.into_iter().map(|txid| (0, txid)).collect(),
166✔
1119
            filter_map,
166✔
1120
        }
166✔
1121
    }
166✔
1122

1123
    /// Creates a `TxDescendants` from multiple starting transactions that excludes the starting
1124
    /// `txid`s when iterating.
1125
    #[allow(unused)]
1126
    pub(crate) fn from_multiple_exclude_root<I>(
×
1127
        graph: &'g TxGraph<A>,
×
1128
        txids: I,
×
1129
        filter_map: F,
×
1130
    ) -> Self
×
1131
    where
×
1132
        I: IntoIterator<Item = Txid>,
×
1133
    {
×
1134
        let mut descendants = Self {
×
1135
            graph,
×
1136
            visited: Default::default(),
×
1137
            stack: Default::default(),
×
1138
            filter_map,
×
1139
        };
×
1140
        for txid in txids {
×
1141
            descendants.populate_stack(1, txid);
×
1142
        }
×
1143
        descendants
×
1144
    }
×
1145
}
1146

1147
impl<'g, A, F> TxDescendants<'g, A, F> {
1148
    fn populate_stack(&mut self, depth: usize, txid: Txid) {
15✔
1149
        let spend_paths = self
15✔
1150
            .graph
15✔
1151
            .spends
15✔
1152
            .range(tx_outpoint_range(txid))
15✔
1153
            .flat_map(|(_, spends)| spends)
15✔
1154
            .map(|&txid| (depth, txid));
15✔
1155
        self.stack.extend(spend_paths);
15✔
1156
    }
15✔
1157
}
1158

1159
impl<'g, A, F, O> Iterator for TxDescendants<'g, A, F>
1160
where
1161
    F: FnMut(usize, Txid) -> Option<O>,
1162
{
1163
    type Item = O;
1164

1165
    fn next(&mut self) -> Option<Self::Item> {
178✔
1166
        let (op_spends, txid, item) = loop {
14✔
1167
            // we have exhausted all paths when stack is empty
1168
            let (op_spends, txid) = self.stack.pop()?;
179✔
1169
            // we do not want to visit the same transaction twice
1170
            if self.visited.insert(txid) {
15✔
1171
                // ignore paths when user filters them out
1172
                if let Some(item) = (self.filter_map)(op_spends, txid) {
14✔
1173
                    break (op_spends, txid, item);
14✔
1174
                }
×
1175
            }
1✔
1176
        };
1177

1178
        self.populate_stack(op_spends + 1, txid);
14✔
1179
        Some(item)
14✔
1180
    }
178✔
1181
}
1182

1183
fn tx_outpoint_range(txid: Txid) -> RangeInclusive<OutPoint> {
195✔
1184
    OutPoint::new(txid, u32::MIN)..=OutPoint::new(txid, u32::MAX)
195✔
1185
}
195✔
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