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

bitcoindevkit / bdk / 5582722505

pending completion
5582722505

Pull #1002

github

web-flow
Merge 98a52d0cb into 81c761339
Pull Request #1002: Implement linked-list `LocalChain` and add rpc-chain module/example

945 of 945 new or added lines in 10 files covered. (100.0%)

8019 of 10332 relevant lines covered (77.61%)

5036.23 hits per line

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

85.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, local_chain::LocalChain, Anchor, Append, BlockId,
60
    ChainOracle, ChainPosition, 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 {
717✔
88
        Self {
717✔
89
            txs: Default::default(),
717✔
90
            spends: Default::default(),
717✔
91
            anchors: Default::default(),
717✔
92
            empty_outspends: Default::default(),
717✔
93
        }
717✔
94
    }
717✔
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 {
6✔
114
        self.tx
6✔
115
    }
6✔
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 {
455✔
130
        Self::Partial(BTreeMap::new())
455✔
131
    }
455✔
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>> {
10✔
182
        self.txs
10✔
183
            .iter()
10✔
184
            .filter_map(|(&txid, (tx, anchors, last_seen))| match tx {
16✔
185
                TxNodeInternal::Whole(tx) => Some(TxNode {
14✔
186
                    txid,
14✔
187
                    tx,
14✔
188
                    anchors,
14✔
189
                    last_seen_unconfirmed: *last_seen,
14✔
190
                }),
14✔
191
                TxNodeInternal::Partial(_) => None,
2✔
192
            })
16✔
193
    }
10✔
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> {
468✔
201
        self.get_tx_node(txid).map(|n| n.tx)
468✔
202
    }
468✔
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>> {
729✔
206
        match &self.txs.get(&txid)? {
729✔
207
            (TxNodeInternal::Whole(tx), anchors, last_seen) => Some(TxNode {
560✔
208
                txid,
560✔
209
                tx,
560✔
210
                anchors,
560✔
211
                last_seen_unconfirmed: *last_seen,
560✔
212
            }),
560✔
213
            _ => None,
×
214
        }
215
    }
729✔
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>(
165✔
323
        &'g self,
165✔
324
        tx: &'g Transaction,
165✔
325
        walk_map: F,
165✔
326
    ) -> TxDescendants<A, F>
165✔
327
    where
165✔
328
        F: FnMut(usize, Txid) -> Option<O> + 'g,
165✔
329
    {
165✔
330
        let txids = self.direct_conflicts_of_tx(tx).map(|(_, txid)| txid);
165✔
331
        TxDescendants::from_multiple_include_root(self, txids, walk_map)
165✔
332
    }
165✔
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>(
165✔
341
        &'g self,
165✔
342
        tx: &'g Transaction,
165✔
343
    ) -> impl Iterator<Item = (usize, Txid)> + '_ {
165✔
344
        let txid = tx.txid();
165✔
345
        tx.input
165✔
346
            .iter()
165✔
347
            .enumerate()
165✔
348
            .filter_map(move |(vin, txin)| self.spends.get(&txin.previous_output).zip(Some(vin)))
165✔
349
            .flat_map(|(spends, vin)| core::iter::repeat(vin).zip(spends.iter().cloned()))
165✔
350
            .filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
169✔
351
    }
165✔
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 {
1✔
367
        let mut new = Self::default();
1✔
368
        for tx in txs.into_iter() {
3✔
369
            let _ = new.insert_tx(tx);
3✔
370
        }
3✔
371
        new
1✔
372
    }
1✔
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> {
209✔
412
        let mut update = Self::default();
209✔
413
        update
209✔
414
            .txs
209✔
415
            .insert(tx.txid(), (TxNodeInternal::Whole(tx), BTreeSet::new(), 0));
209✔
416
        self.determine_additions(&update)
209✔
417
    }
209✔
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> {
209✔
423
        let additions = self.insert_tx_preview(tx);
209✔
424
        self.apply_additions(additions.clone());
209✔
425
        additions
209✔
426
    }
209✔
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> {
30✔
453
        let mut update = Self::default();
30✔
454
        let (_, _, update_last_seen) = update.txs.entry(txid).or_default();
30✔
455
        *update_last_seen = seen_at;
30✔
456
        self.determine_additions(&update)
30✔
457
    }
30✔
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> {
30✔
467
        let additions = self.insert_seen_at_preview(txid, seen_at);
30✔
468
        self.apply_additions(additions.clone());
30✔
469
        additions
30✔
470
    }
30✔
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> {
170✔
478
        let additions = self.determine_additions(&update);
170✔
479
        self.apply_additions(additions.clone());
170✔
480
        additions
170✔
481
    }
170✔
482

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

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

381✔
498
            match self.txs.get_mut(&txid) {
381✔
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 => {
380✔
510
                    self.txs
380✔
511
                        .insert(txid, (TxNodeInternal::Whole(tx), BTreeSet::new(), 0));
380✔
512
                }
380✔
513
            }
514
        }
515

516
        for (outpoint, txout) in additions.txouts {
707✔
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 {
989✔
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 {
709✔
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
    }
701✔
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> {
563✔
551
        let mut additions = Additions::default();
563✔
552

553
        for (&txid, (update_tx_node, _, update_last_seen)) in &update.txs {
983✔
554
            let prev_last_seen: u64 = match (self.txs.get(&txid), update_tx_node) {
420✔
555
                (None, TxNodeInternal::Whole(update_tx)) => {
380✔
556
                    additions.txs.insert(update_tx.clone());
380✔
557
                    0
380✔
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,
31✔
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 {
420✔
590
                additions.last_seen.insert(txid, *update_last_seen);
8✔
591
            }
412✔
592
        }
593

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

563✔
596
        additions
563✔
597
    }
563✔
598
}
599

600
impl<A: Anchor> TxGraph<A> {
601
    /// Find missing block heights of `chain`.
602
    ///
603
    /// This works by scanning through anchors, and seeing whether the anchor block of the anchor
604
    /// exists in the [`LocalChain`].
605
    pub fn missing_blocks<'a>(&'a self, chain: &'a LocalChain) -> impl Iterator<Item = u32> + 'a {
×
606
        self.anchors
×
607
            .iter()
×
608
            .map(|(a, _)| a.anchor_block())
×
609
            .filter({
×
610
                let mut last_block = Option::<BlockId>::None;
×
611
                move |block| {
×
612
                    if last_block.as_ref() == Some(block) {
×
613
                        false
×
614
                    } else {
615
                        last_block = Some(*block);
×
616
                        true
×
617
                    }
618
                }
×
619
            })
×
620
            .filter_map(|block| match chain.heights().get(&block.height) {
×
621
                Some(chain_hash) if *chain_hash == block.hash => None,
×
622
                _ => Some(block.height),
×
623
            })
×
624
    }
×
625

626
    /// Get the position of the transaction in `chain` with tip `chain_tip`.
627
    ///
628
    /// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is
629
    /// returned.
630
    ///
631
    /// # Error
632
    ///
633
    /// An error will occur if the [`ChainOracle`] implementation (`chain`) fails. If the
634
    /// [`ChainOracle`] is infallible, [`get_chain_position`] can be used instead.
635
    ///
636
    /// [`get_chain_position`]: Self::get_chain_position
637
    pub fn try_get_chain_position<C: ChainOracle>(
759✔
638
        &self,
759✔
639
        chain: &C,
759✔
640
        chain_tip: BlockId,
759✔
641
        txid: Txid,
759✔
642
    ) -> Result<Option<ChainPosition<&A>>, C::Error> {
759✔
643
        let (tx_node, anchors, last_seen) = match self.txs.get(&txid) {
759✔
644
            Some(v) => v,
756✔
645
            None => return Ok(None),
3✔
646
        };
647

648
        for anchor in anchors {
780✔
649
            match chain.is_block_in_chain(anchor.anchor_block(), chain_tip)? {
616✔
650
                Some(true) => return Ok(Some(ChainPosition::Confirmed(anchor))),
592✔
651
                _ => continue,
24✔
652
            }
653
        }
654

655
        // The tx is not anchored to a block which is in the best chain, let's check whether we can
656
        // ignore it by checking conflicts!
657
        let tx = match tx_node {
164✔
658
            TxNodeInternal::Whole(tx) => tx,
164✔
659
            TxNodeInternal::Partial(_) => {
660
                // Partial transactions (outputs only) cannot have conflicts.
661
                return Ok(None);
×
662
            }
663
        };
664

665
        // If a conflicting tx is in the best chain, or has `last_seen` higher than this tx, then
666
        // this tx cannot exist in the best chain
667
        for conflicting_tx in self.walk_conflicts(tx, |_, txid| self.get_tx_node(txid)) {
164✔
668
            for block in conflicting_tx.anchors.iter().map(A::anchor_block) {
4✔
669
                if chain.is_block_in_chain(block, chain_tip)? == Some(true) {
1✔
670
                    // conflicting tx is in best chain, so the current tx cannot be in best chain!
671
                    return Ok(None);
1✔
672
                }
×
673
            }
674
            if conflicting_tx.last_seen_unconfirmed > *last_seen {
3✔
675
                return Ok(None);
1✔
676
            }
2✔
677
        }
678

679
        Ok(Some(ChainPosition::Unconfirmed(*last_seen)))
162✔
680
    }
759✔
681

682
    /// Get the position of the transaction in `chain` with tip `chain_tip`.
683
    ///
684
    /// This is the infallible version of [`try_get_chain_position`].
685
    ///
686
    /// [`try_get_chain_position`]: Self::try_get_chain_position
687
    pub fn get_chain_position<C: ChainOracle<Error = Infallible>>(
208✔
688
        &self,
208✔
689
        chain: &C,
208✔
690
        chain_tip: BlockId,
208✔
691
        txid: Txid,
208✔
692
    ) -> Option<ChainPosition<&A>> {
208✔
693
        self.try_get_chain_position(chain, chain_tip, txid)
208✔
694
            .expect("error is infallible")
208✔
695
    }
208✔
696

697
    /// Get the txid of the spending transaction and where the spending transaction is observed in
698
    /// the `chain` of `chain_tip`.
699
    ///
700
    /// If no in-chain transaction spends `outpoint`, `None` will be returned.
701
    ///
702
    /// # Error
703
    ///
704
    /// An error will occur only if the [`ChainOracle`] implementation (`chain`) fails.
705
    ///
706
    /// If the [`ChainOracle`] is infallible, [`get_chain_spend`] can be used instead.
707
    ///
708
    /// [`get_chain_spend`]: Self::get_chain_spend
709
    pub fn try_get_chain_spend<C: ChainOracle>(
257✔
710
        &self,
257✔
711
        chain: &C,
257✔
712
        chain_tip: BlockId,
257✔
713
        outpoint: OutPoint,
257✔
714
    ) -> Result<Option<(ChainPosition<&A>, Txid)>, C::Error> {
257✔
715
        if self
257✔
716
            .try_get_chain_position(chain, chain_tip, outpoint.txid)?
257✔
717
            .is_none()
257✔
718
        {
719
            return Ok(None);
×
720
        }
257✔
721
        if let Some(spends) = self.spends.get(&outpoint) {
257✔
722
            for &txid in spends {
34✔
723
                if let Some(observed_at) = self.try_get_chain_position(chain, chain_tip, txid)? {
34✔
724
                    return Ok(Some((observed_at, txid)));
34✔
725
                }
×
726
            }
727
        }
223✔
728
        Ok(None)
223✔
729
    }
257✔
730

731
    /// Get the txid of the spending transaction and where the spending transaction is observed in
732
    /// the `chain` of `chain_tip`.
733
    ///
734
    /// This is the infallible version of [`try_get_chain_spend`]
735
    ///
736
    /// [`try_get_chain_spend`]: Self::try_get_chain_spend
737
    pub fn get_chain_spend<C: ChainOracle<Error = Infallible>>(
4✔
738
        &self,
4✔
739
        chain: &C,
4✔
740
        static_block: BlockId,
4✔
741
        outpoint: OutPoint,
4✔
742
    ) -> Option<(ChainPosition<&A>, Txid)> {
4✔
743
        self.try_get_chain_spend(chain, static_block, outpoint)
4✔
744
            .expect("error is infallible")
4✔
745
    }
4✔
746

747
    /// List graph transactions that are in `chain` with `chain_tip`.
748
    ///
749
    /// Each transaction is represented as a [`CanonicalTx`] that contains where the transaction is
750
    /// observed in-chain, and the [`TxNode`].
751
    ///
752
    /// # Error
753
    ///
754
    /// If the [`ChainOracle`] implementation (`chain`) fails, an error will be returned with the
755
    /// returned item.
756
    ///
757
    /// If the [`ChainOracle`] is infallible, [`list_chain_txs`] can be used instead.
758
    ///
759
    /// [`list_chain_txs`]: Self::list_chain_txs
760
    pub fn try_list_chain_txs<'a, C: ChainOracle + 'a>(
7✔
761
        &'a self,
7✔
762
        chain: &'a C,
7✔
763
        chain_tip: BlockId,
7✔
764
    ) -> impl Iterator<Item = Result<CanonicalTx<'a, Transaction, A>, C::Error>> {
7✔
765
        self.full_txs().filter_map(move |tx| {
7✔
766
            self.try_get_chain_position(chain, chain_tip, tx.txid)
7✔
767
                .map(|v| {
7✔
768
                    v.map(|observed_in| CanonicalTx {
7✔
769
                        observed_as: observed_in,
7✔
770
                        node: tx,
7✔
771
                    })
7✔
772
                })
7✔
773
                .transpose()
7✔
774
        })
7✔
775
    }
7✔
776

777
    /// List graph transactions that are in `chain` with `chain_tip`.
778
    ///
779
    /// This is the infallible version of [`try_list_chain_txs`].
780
    ///
781
    /// [`try_list_chain_txs`]: Self::try_list_chain_txs
782
    pub fn list_chain_txs<'a, C: ChainOracle + 'a>(
7✔
783
        &'a self,
7✔
784
        chain: &'a C,
7✔
785
        chain_tip: BlockId,
7✔
786
    ) -> impl Iterator<Item = CanonicalTx<'a, Transaction, A>> {
7✔
787
        self.try_list_chain_txs(chain, chain_tip)
7✔
788
            .map(|r| r.expect("oracle is infallible"))
7✔
789
    }
7✔
790

791
    /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
792
    /// `chain_tip`.
793
    ///
794
    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
795
    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
796
    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
797
    ///
798
    /// Floating outputs are ignored.
799
    ///
800
    /// # Error
801
    ///
802
    /// An [`Iterator::Item`] can be an [`Err`] if the [`ChainOracle`] implementation (`chain`)
803
    /// fails.
804
    ///
805
    /// If the [`ChainOracle`] implementation is infallible, [`filter_chain_txouts`] can be used
806
    /// instead.
807
    ///
808
    /// [`filter_chain_txouts`]: Self::filter_chain_txouts
809
    pub fn try_filter_chain_txouts<'a, C: ChainOracle + 'a, OI: Clone + 'a>(
164✔
810
        &'a self,
164✔
811
        chain: &'a C,
164✔
812
        chain_tip: BlockId,
164✔
813
        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
164✔
814
    ) -> impl Iterator<Item = Result<(OI, FullTxOut<A>), C::Error>> + 'a {
164✔
815
        outpoints
164✔
816
            .into_iter()
164✔
817
            .map(
164✔
818
                move |(spk_i, op)| -> Result<Option<(OI, FullTxOut<_>)>, C::Error> {
164✔
819
                    let tx_node = match self.get_tx_node(op.txid) {
253✔
820
                        Some(n) => n,
253✔
821
                        None => return Ok(None),
×
822
                    };
823

824
                    let txout = match tx_node.tx.output.get(op.vout as usize) {
253✔
825
                        Some(txout) => txout.clone(),
253✔
826
                        None => return Ok(None),
×
827
                    };
828

829
                    let chain_position =
253✔
830
                        match self.try_get_chain_position(chain, chain_tip, op.txid)? {
253✔
831
                            Some(pos) => pos.cloned(),
253✔
832
                            None => return Ok(None),
×
833
                        };
834

835
                    let spent_by = self
253✔
836
                        .try_get_chain_spend(chain, chain_tip, op)?
253✔
837
                        .map(|(a, txid)| (a.cloned(), txid));
253✔
838

253✔
839
                    Ok(Some((
253✔
840
                        spk_i,
253✔
841
                        FullTxOut {
253✔
842
                            outpoint: op,
253✔
843
                            txout,
253✔
844
                            chain_position,
253✔
845
                            spent_by,
253✔
846
                            is_on_coinbase: tx_node.tx.is_coin_base(),
253✔
847
                        },
253✔
848
                    )))
253✔
849
                },
253✔
850
            )
164✔
851
            .filter_map(Result::transpose)
164✔
852
    }
164✔
853

854
    /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
855
    /// `chain_tip`.
856
    ///
857
    /// This is the infallible version of [`try_filter_chain_txouts`].
858
    ///
859
    /// [`try_filter_chain_txouts`]: Self::try_filter_chain_txouts
860
    pub fn filter_chain_txouts<'a, C: ChainOracle<Error = Infallible> + 'a, OI: Clone + 'a>(
5✔
861
        &'a self,
5✔
862
        chain: &'a C,
5✔
863
        chain_tip: BlockId,
5✔
864
        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
5✔
865
    ) -> impl Iterator<Item = (OI, FullTxOut<A>)> + 'a {
5✔
866
        self.try_filter_chain_txouts(chain, chain_tip, outpoints)
5✔
867
            .map(|r| r.expect("oracle is infallible"))
25✔
868
    }
5✔
869

870
    /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in
871
    /// `chain` with `chain_tip`.
872
    ///
873
    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
874
    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
875
    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
876
    ///
877
    /// Floating outputs are ignored.
878
    ///
879
    /// # Error
880
    ///
881
    /// An [`Iterator::Item`] can be an [`Err`] if the [`ChainOracle`] implementation (`chain`)
882
    /// fails.
883
    ///
884
    /// If the [`ChainOracle`] implementation is infallible, [`filter_chain_unspents`] can be used
885
    /// instead.
886
    ///
887
    /// [`filter_chain_unspents`]: Self::filter_chain_unspents
888
    pub fn try_filter_chain_unspents<'a, C: ChainOracle + 'a, OI: Clone + 'a>(
159✔
889
        &'a self,
159✔
890
        chain: &'a C,
159✔
891
        chain_tip: BlockId,
159✔
892
        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
159✔
893
    ) -> impl Iterator<Item = Result<(OI, FullTxOut<A>), C::Error>> + 'a {
159✔
894
        self.try_filter_chain_txouts(chain, chain_tip, outpoints)
159✔
895
            .filter(|r| match r {
228✔
896
                // keep unspents, drop spents
897
                Ok((_, full_txo)) => full_txo.spent_by.is_none(),
228✔
898
                // keep errors
899
                Err(_) => true,
×
900
            })
228✔
901
    }
159✔
902

903
    /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in
904
    /// `chain` with `chain_tip`.
905
    ///
906
    /// This is the infallible version of [`try_filter_chain_unspents`].
907
    ///
908
    /// [`try_filter_chain_unspents`]: Self::try_filter_chain_unspents
909
    pub fn filter_chain_unspents<'a, C: ChainOracle<Error = Infallible> + 'a, OI: Clone + 'a>(
151✔
910
        &'a self,
151✔
911
        chain: &'a C,
151✔
912
        chain_tip: BlockId,
151✔
913
        txouts: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
151✔
914
    ) -> impl Iterator<Item = (OI, FullTxOut<A>)> + 'a {
151✔
915
        self.try_filter_chain_unspents(chain, chain_tip, txouts)
151✔
916
            .map(|r| r.expect("oracle is infallible"))
180✔
917
    }
151✔
918

919
    /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`.
920
    ///
921
    /// The output of `trust_predicate` should return `true` for scripts that we trust.
922
    ///
923
    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
924
    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
925
    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
926
    ///
927
    /// If the provided [`ChainOracle`] implementation (`chain`) is infallible, [`balance`] can be
928
    /// used instead.
929
    ///
930
    /// [`balance`]: Self::balance
931
    pub fn try_balance<C: ChainOracle, OI: Clone>(
8✔
932
        &self,
8✔
933
        chain: &C,
8✔
934
        chain_tip: BlockId,
8✔
935
        outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
8✔
936
        mut trust_predicate: impl FnMut(&OI, &Script) -> bool,
8✔
937
    ) -> Result<Balance, C::Error> {
8✔
938
        let mut immature = 0;
8✔
939
        let mut trusted_pending = 0;
8✔
940
        let mut untrusted_pending = 0;
8✔
941
        let mut confirmed = 0;
8✔
942

943
        for res in self.try_filter_chain_unspents(chain, chain_tip, outpoints) {
23✔
944
            let (spk_i, txout) = res?;
23✔
945

946
            match &txout.chain_position {
23✔
947
                ChainPosition::Confirmed(_) => {
948
                    if txout.is_confirmed_and_spendable(chain_tip.height) {
11✔
949
                        confirmed += txout.txout.value;
6✔
950
                    } else if !txout.is_mature(chain_tip.height) {
6✔
951
                        immature += txout.txout.value;
5✔
952
                    }
5✔
953
                }
954
                ChainPosition::Unconfirmed(_) => {
955
                    if trust_predicate(&spk_i, &txout.txout.script_pubkey) {
12✔
956
                        trusted_pending += txout.txout.value;
7✔
957
                    } else {
7✔
958
                        untrusted_pending += txout.txout.value;
5✔
959
                    }
5✔
960
                }
961
            }
962
        }
963

964
        Ok(Balance {
8✔
965
            immature,
8✔
966
            trusted_pending,
8✔
967
            untrusted_pending,
8✔
968
            confirmed,
8✔
969
        })
8✔
970
    }
8✔
971

972
    /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`.
973
    ///
974
    /// This is the infallible version of [`try_balance`].
975
    ///
976
    /// [`try_balance`]: Self::try_balance
977
    pub fn balance<C: ChainOracle<Error = Infallible>, OI: Clone>(
8✔
978
        &self,
8✔
979
        chain: &C,
8✔
980
        chain_tip: BlockId,
8✔
981
        outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
8✔
982
        trust_predicate: impl FnMut(&OI, &Script) -> bool,
8✔
983
    ) -> Balance {
8✔
984
        self.try_balance(chain, chain_tip, outpoints, trust_predicate)
8✔
985
            .expect("oracle is infallible")
8✔
986
    }
8✔
987
}
988

989
/// A structure that represents changes to a [`TxGraph`].
990
///
991
/// It is named "additions" because [`TxGraph`] is monotone, so transactions can only be added and
992
/// not removed.
993
///
994
/// Refer to [module-level documentation] for more.
995
///
996
/// [module-level documentation]: crate::tx_graph
997
#[derive(Debug, Clone, PartialEq)]
562✔
998
#[cfg_attr(
999
    feature = "serde",
1000
    derive(serde::Deserialize, serde::Serialize),
×
1001
    serde(
1002
        crate = "serde_crate",
1003
        bound(
1004
            deserialize = "A: Ord + serde::Deserialize<'de>",
1005
            serialize = "A: Ord + serde::Serialize",
1006
        )
1007
    )
1008
)]
1009
#[must_use]
1010
pub struct Additions<A = ()> {
1011
    /// Added transactions.
1012
    pub txs: BTreeSet<Transaction>,
1013
    /// Added txouts.
1014
    pub txouts: BTreeMap<OutPoint, TxOut>,
1015
    /// Added anchors.
1016
    pub anchors: BTreeSet<(A, Txid)>,
1017
    /// Added last-seen unix timestamps of transactions.
1018
    pub last_seen: BTreeMap<Txid, u64>,
1019
}
1020

1021
impl<A> Default for Additions<A> {
1022
    fn default() -> Self {
2,074✔
1023
        Self {
2,074✔
1024
            txs: Default::default(),
2,074✔
1025
            txouts: Default::default(),
2,074✔
1026
            anchors: Default::default(),
2,074✔
1027
            last_seen: Default::default(),
2,074✔
1028
        }
2,074✔
1029
    }
2,074✔
1030
}
1031

1032
impl<A> Additions<A> {
1033
    /// Returns true if the [`Additions`] is empty (no transactions or txouts).
1034
    pub fn is_empty(&self) -> bool {
374✔
1035
        self.txs.is_empty() && self.txouts.is_empty()
374✔
1036
    }
374✔
1037

1038
    /// Iterates over all outpoints contained within [`Additions`].
1039
    pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
×
1040
        self.txs
×
1041
            .iter()
×
1042
            .flat_map(|tx| {
×
1043
                tx.output
×
1044
                    .iter()
×
1045
                    .enumerate()
×
1046
                    .map(move |(vout, txout)| (OutPoint::new(tx.txid(), vout as _), txout))
×
1047
            })
×
1048
            .chain(self.txouts.iter().map(|(op, txout)| (*op, txout)))
×
1049
    }
×
1050
}
1051

1052
impl<A: Ord> Append for Additions<A> {
1053
    fn append(&mut self, mut other: Self) {
628✔
1054
        self.txs.append(&mut other.txs);
628✔
1055
        self.txouts.append(&mut other.txouts);
628✔
1056
        self.anchors.append(&mut other.anchors);
628✔
1057

628✔
1058
        // last_seen timestamps should only increase
628✔
1059
        self.last_seen.extend(
628✔
1060
            other
628✔
1061
                .last_seen
628✔
1062
                .into_iter()
628✔
1063
                .filter(|(txid, update_ls)| self.last_seen.get(txid) < Some(update_ls))
628✔
1064
                .collect::<Vec<_>>(),
628✔
1065
        );
628✔
1066
    }
628✔
1067

1068
    fn is_empty(&self) -> bool {
×
1069
        self.txs.is_empty()
×
1070
            && self.txouts.is_empty()
×
1071
            && self.anchors.is_empty()
×
1072
            && self.last_seen.is_empty()
×
1073
    }
×
1074
}
1075

1076
impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
1077
    fn as_ref(&self) -> &TxGraph<A> {
×
1078
        self
×
1079
    }
×
1080
}
1081

1082
impl<A> ForEachTxOut for Additions<A> {
1083
    fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
×
1084
        self.txouts().for_each(f)
×
1085
    }
×
1086
}
1087

1088
impl<A> ForEachTxOut for TxGraph<A> {
1089
    fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
×
1090
        self.all_txouts().for_each(f)
×
1091
    }
×
1092
}
1093

1094
/// An iterator that traverses transaction descendants.
1095
///
1096
/// This `struct` is created by the [`walk_descendants`] method of [`TxGraph`].
1097
///
1098
/// [`walk_descendants`]: TxGraph::walk_descendants
1099
pub struct TxDescendants<'g, A, F> {
1100
    graph: &'g TxGraph<A>,
1101
    visited: HashSet<Txid>,
1102
    stack: Vec<(usize, Txid)>,
1103
    filter_map: F,
1104
}
1105

1106
impl<'g, A, F> TxDescendants<'g, A, F> {
1107
    /// Creates a `TxDescendants` that includes the starting `txid` when iterating.
1108
    #[allow(unused)]
1109
    pub(crate) fn new_include_root(graph: &'g TxGraph<A>, txid: Txid, filter_map: F) -> Self {
×
1110
        Self {
×
1111
            graph,
×
1112
            visited: Default::default(),
×
1113
            stack: [(0, txid)].into(),
×
1114
            filter_map,
×
1115
        }
×
1116
    }
×
1117

1118
    /// Creates a `TxDescendants` that excludes the starting `txid` when iterating.
1119
    pub(crate) fn new_exclude_root(graph: &'g TxGraph<A>, txid: Txid, filter_map: F) -> Self {
1✔
1120
        let mut descendants = Self {
1✔
1121
            graph,
1✔
1122
            visited: Default::default(),
1✔
1123
            stack: Default::default(),
1✔
1124
            filter_map,
1✔
1125
        };
1✔
1126
        descendants.populate_stack(1, txid);
1✔
1127
        descendants
1✔
1128
    }
1✔
1129

1130
    /// Creates a `TxDescendants` from multiple starting transactions that include the starting
1131
    /// `txid`s when iterating.
1132
    pub(crate) fn from_multiple_include_root<I>(
165✔
1133
        graph: &'g TxGraph<A>,
165✔
1134
        txids: I,
165✔
1135
        filter_map: F,
165✔
1136
    ) -> Self
165✔
1137
    where
165✔
1138
        I: IntoIterator<Item = Txid>,
165✔
1139
    {
165✔
1140
        Self {
165✔
1141
            graph,
165✔
1142
            visited: Default::default(),
165✔
1143
            stack: txids.into_iter().map(|txid| (0, txid)).collect(),
165✔
1144
            filter_map,
165✔
1145
        }
165✔
1146
    }
165✔
1147

1148
    /// Creates a `TxDescendants` from multiple starting transactions that excludes the starting
1149
    /// `txid`s when iterating.
1150
    #[allow(unused)]
1151
    pub(crate) fn from_multiple_exclude_root<I>(
×
1152
        graph: &'g TxGraph<A>,
×
1153
        txids: I,
×
1154
        filter_map: F,
×
1155
    ) -> Self
×
1156
    where
×
1157
        I: IntoIterator<Item = Txid>,
×
1158
    {
×
1159
        let mut descendants = Self {
×
1160
            graph,
×
1161
            visited: Default::default(),
×
1162
            stack: Default::default(),
×
1163
            filter_map,
×
1164
        };
×
1165
        for txid in txids {
×
1166
            descendants.populate_stack(1, txid);
×
1167
        }
×
1168
        descendants
×
1169
    }
×
1170
}
1171

1172
impl<'g, A, F> TxDescendants<'g, A, F> {
1173
    fn populate_stack(&mut self, depth: usize, txid: Txid) {
14✔
1174
        let spend_paths = self
14✔
1175
            .graph
14✔
1176
            .spends
14✔
1177
            .range(tx_outpoint_range(txid))
14✔
1178
            .flat_map(|(_, spends)| spends)
14✔
1179
            .map(|&txid| (depth, txid));
14✔
1180
        self.stack.extend(spend_paths);
14✔
1181
    }
14✔
1182
}
1183

1184
impl<'g, A, F, O> Iterator for TxDescendants<'g, A, F>
1185
where
1186
    F: FnMut(usize, Txid) -> Option<O>,
1187
{
1188
    type Item = O;
1189

1190
    fn next(&mut self) -> Option<Self::Item> {
177✔
1191
        let (op_spends, txid, item) = loop {
13✔
1192
            // we have exhausted all paths when stack is empty
1193
            let (op_spends, txid) = self.stack.pop()?;
178✔
1194
            // we do not want to visit the same transaction twice
1195
            if self.visited.insert(txid) {
14✔
1196
                // ignore paths when user filters them out
1197
                if let Some(item) = (self.filter_map)(op_spends, txid) {
13✔
1198
                    break (op_spends, txid, item);
13✔
1199
                }
×
1200
            }
1✔
1201
        };
1202

1203
        self.populate_stack(op_spends + 1, txid);
13✔
1204
        Some(item)
13✔
1205
    }
177✔
1206
}
1207

1208
fn tx_outpoint_range(txid: Txid) -> RangeInclusive<OutPoint> {
182✔
1209
    OutPoint::new(txid, u32::MIN)..=OutPoint::new(txid, u32::MAX)
182✔
1210
}
182✔
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