bdk_chain/
tx_graph.rs

1//! Module for structures that store and traverse transactions.
2//!
3//! [`TxGraph`] contains transactions and indexes them so you can easily traverse the graph of
4//! those transactions. `TxGraph` is *monotone* in that you can always insert a transaction -- it
5//! does not care whether that transaction is in the current best chain or whether it conflicts with
6//! any of the existing transactions or what order you insert the transactions. This means that you
7//! can always combine two [`TxGraph`]s together, without resulting in inconsistencies. Furthermore,
8//! there is currently no way to delete a transaction.
9//!
10//! Transactions can be either whole or partial (i.e., transactions for which we only know some
11//! outputs, which we usually call "floating outputs"; these are usually inserted using the
12//! [`insert_txout`] method.).
13//!
14//! The graph contains transactions in the form of [`TxNode`]s. Each node contains the txid, the
15//! transaction (whole or partial), the blocks that it is anchored to (see the [`Anchor`]
16//! documentation for more details), and the timestamp of the last time we saw the transaction as
17//! unconfirmed.
18//!
19//! # Canonicalization
20//!
21//! Conflicting transactions are allowed to coexist within a [`TxGraph`]. A process called
22//! canonicalization is required to get a conflict-free view of transactions.
23//!
24//! * [`list_canonical_txs`](TxGraph::list_canonical_txs) lists canonical transactions.
25//! * [`filter_chain_txouts`](TxGraph::filter_chain_txouts) filters out canonical outputs from a
26//!   list of outpoints.
27//! * [`filter_chain_unspents`](TxGraph::filter_chain_unspents) filters out canonical unspent
28//!   outputs from a list of outpoints.
29//! * [`balance`](TxGraph::balance) gets the total sum of unspent outputs filtered from a list of
30//!   outpoints.
31//! * [`canonical_iter`](TxGraph::canonical_iter) returns the [`CanonicalIter`] which contains all
32//!   of the canonicalization logic.
33//!
34//! All these methods require a `chain` and `chain_tip` argument. The `chain` must be a
35//! [`ChainOracle`] implementation (such as [`LocalChain`](crate::local_chain::LocalChain)) which
36//! identifies which blocks exist under a given `chain_tip`.
37//!
38//! The canonicalization algorithm uses the following associated data to determine which
39//! transactions have precedence over others:
40//!
41//! * [`Anchor`] - This bit of data represents that a transaction is anchored in a given block. If
42//!   the transaction is anchored in chain of `chain_tip`, or is an ancestor of a transaction
43//!   anchored in chain of `chain_tip`, then the transaction must be canonical.
44//! * `last_seen` - This is the timestamp of when a transaction is last-seen in the mempool. This
45//!   value is updated by [`insert_seen_at`](TxGraph::insert_seen_at) and
46//!   [`apply_update`](TxGraph::apply_update). Transactions that are seen later have higher priority
47//!   than those that are seen earlier. `last_seen` values are transitive. This means that the
48//!   actual `last_seen` value of a transaction is the max of all the `last_seen` values from it's
49//!   descendants.
50//! * `last_evicted` - This is the timestamp of when a transaction last went missing from the
51//!   mempool. If this value is equal to or higher than the transaction's `last_seen` value, then it
52//!   will not be considered canonical.
53//!
54//! # Graph traversal
55//!
56//! You can use [`TxAncestors`]/[`TxDescendants`] to traverse ancestors and descendants of a given
57//! transaction, respectively.
58//!
59//! # Applying changes
60//!
61//! The [`ChangeSet`] reports changes made to a [`TxGraph`]; it can be used to either save to
62//! persistent storage, or to be applied to another [`TxGraph`].
63//!
64//! Methods that change the state of [`TxGraph`] will return [`ChangeSet`]s.
65//!
66//! # Generics
67//!
68//! Anchors are represented as generics within `TxGraph<A>`. To make use of all functionality of the
69//! `TxGraph`, anchors (`A`) should implement [`Anchor`].
70//!
71//! Anchors are made generic so that different types of data can be stored with how a transaction is
72//! *anchored* to a given block. An example of this is storing a merkle proof of the transaction to
73//! the confirmation block - this can be done with a custom [`Anchor`] type. The minimal [`Anchor`]
74//! type would just be a [`BlockId`] which just represents the height and hash of the block which
75//! the transaction is contained in. Note that a transaction can be contained in multiple
76//! conflicting blocks (by nature of the Bitcoin network).
77//!
78//! ```
79//! # use bdk_chain::BlockId;
80//! # use bdk_chain::tx_graph::TxGraph;
81//! # use bdk_chain::example_utils::*;
82//! # use bitcoin::Transaction;
83//! # let tx_a = tx_from_hex(RAW_TX_1);
84//! let mut tx_graph: TxGraph = TxGraph::default();
85//!
86//! // insert a transaction
87//! let changeset = tx_graph.insert_tx(tx_a);
88//!
89//! // We can restore the state of the `tx_graph` by applying all
90//! // the changesets obtained by mutating the original (the order doesn't matter).
91//! let mut restored_tx_graph: TxGraph = TxGraph::default();
92//! restored_tx_graph.apply_changeset(changeset);
93//!
94//! assert_eq!(tx_graph, restored_tx_graph);
95//! ```
96//!
97//! A [`TxGraph`] can also be updated with another [`TxGraph`] which merges them together.
98//!
99//! ```
100//! # use bdk_chain::{Merge, BlockId};
101//! # use bdk_chain::tx_graph::{self, TxGraph};
102//! # use bdk_chain::example_utils::*;
103//! # use bitcoin::Transaction;
104//! # use std::sync::Arc;
105//! # let tx_a = tx_from_hex(RAW_TX_1);
106//! # let tx_b = tx_from_hex(RAW_TX_2);
107//! let mut graph: TxGraph = TxGraph::default();
108//!
109//! let mut update = tx_graph::TxUpdate::default();
110//! update.txs.push(Arc::new(tx_a));
111//! update.txs.push(Arc::new(tx_b));
112//!
113//! // apply the update graph
114//! let changeset = graph.apply_update(update.clone());
115//!
116//! // if we apply it again, the resulting changeset will be empty
117//! let changeset = graph.apply_update(update);
118//! assert!(changeset.is_empty());
119//! ```
120//! [`insert_txout`]: TxGraph::insert_txout
121
122use crate::collections::*;
123use crate::spk_txout::SpkTxOutIndex;
124use crate::BlockId;
125use crate::CanonicalIter;
126use crate::CanonicalReason;
127use crate::CanonicalizationParams;
128use crate::ObservedIn;
129use crate::{Anchor, Balance, ChainOracle, ChainPosition, FullTxOut, Merge};
130use alloc::collections::vec_deque::VecDeque;
131use alloc::sync::Arc;
132use alloc::vec::Vec;
133use bdk_core::ConfirmationBlockTime;
134pub use bdk_core::TxUpdate;
135use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
136use core::fmt::{self, Formatter};
137use core::ops::RangeBounds;
138use core::{
139    convert::Infallible,
140    ops::{Deref, RangeInclusive},
141};
142
143impl<A: Ord> From<TxGraph<A>> for TxUpdate<A> {
144    fn from(graph: TxGraph<A>) -> Self {
145        let mut tx_update = TxUpdate::default();
146        tx_update.txs = graph.full_txs().map(|tx_node| tx_node.tx).collect();
147        tx_update.txouts = graph
148            .floating_txouts()
149            .map(|(op, txo)| (op, txo.clone()))
150            .collect();
151        tx_update.anchors = graph
152            .anchors
153            .into_iter()
154            .flat_map(|(txid, anchors)| anchors.into_iter().map(move |a| (a, txid)))
155            .collect();
156        tx_update.seen_ats = graph.last_seen.into_iter().collect();
157        tx_update.evicted_ats = graph.last_evicted.into_iter().collect();
158        tx_update
159    }
160}
161
162impl<A: Anchor> From<TxUpdate<A>> for TxGraph<A> {
163    fn from(update: TxUpdate<A>) -> Self {
164        let mut graph = TxGraph::<A>::default();
165        let _ = graph.apply_update(update);
166        graph
167    }
168}
169
170/// A graph of transactions and spends.
171///
172/// See the [module-level documentation] for more.
173///
174/// [module-level documentation]: crate::tx_graph
175#[derive(Clone, Debug, PartialEq)]
176pub struct TxGraph<A = ConfirmationBlockTime> {
177    txs: HashMap<Txid, TxNodeInternal>,
178    spends: BTreeMap<OutPoint, HashSet<Txid>>,
179    anchors: HashMap<Txid, BTreeSet<A>>,
180    first_seen: HashMap<Txid, u64>,
181    last_seen: HashMap<Txid, u64>,
182    last_evicted: HashMap<Txid, u64>,
183
184    txs_by_highest_conf_heights: BTreeSet<(u32, Txid)>,
185    txs_by_last_seen: BTreeSet<(u64, Txid)>,
186
187    // The following fields exist so that methods can return references to empty sets.
188    // FIXME: This can be removed once `HashSet::new` and `BTreeSet::new` are const fns.
189    empty_outspends: HashSet<Txid>,
190    empty_anchors: BTreeSet<A>,
191}
192
193impl<A> Default for TxGraph<A> {
194    fn default() -> Self {
195        Self {
196            txs: Default::default(),
197            spends: Default::default(),
198            anchors: Default::default(),
199            first_seen: Default::default(),
200            last_seen: Default::default(),
201            last_evicted: Default::default(),
202            txs_by_highest_conf_heights: Default::default(),
203            txs_by_last_seen: Default::default(),
204            empty_outspends: Default::default(),
205            empty_anchors: Default::default(),
206        }
207    }
208}
209
210/// A transaction node in the [`TxGraph`].
211#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
212pub struct TxNode<'a, T, A> {
213    /// Txid of the transaction.
214    pub txid: Txid,
215    /// A partial or full representation of the transaction.
216    pub tx: T,
217    /// The blocks that the transaction is "anchored" in.
218    pub anchors: &'a BTreeSet<A>,
219    /// The first-seen unix timestamp of the transaction as unconfirmed.
220    pub first_seen: Option<u64>,
221    /// The last-seen unix timestamp of the transaction as unconfirmed.
222    pub last_seen: Option<u64>,
223}
224
225impl<T, A> Deref for TxNode<'_, T, A> {
226    type Target = T;
227
228    fn deref(&self) -> &Self::Target {
229        &self.tx
230    }
231}
232
233/// Internal representation of a transaction node of a [`TxGraph`].
234///
235/// This can either be a whole transaction, or a partial transaction (where we only have select
236/// outputs).
237#[derive(Clone, Debug, PartialEq)]
238enum TxNodeInternal {
239    Whole(Arc<Transaction>),
240    Partial(BTreeMap<u32, TxOut>),
241}
242
243impl Default for TxNodeInternal {
244    fn default() -> Self {
245        Self::Partial(BTreeMap::new())
246    }
247}
248
249/// A transaction that is deemed to be part of the canonical history.
250#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
251pub struct CanonicalTx<'a, T, A> {
252    /// How the transaction is observed in the canonical chain (confirmed or unconfirmed).
253    pub chain_position: ChainPosition<A>,
254    /// The transaction node (as part of the graph).
255    pub tx_node: TxNode<'a, T, A>,
256}
257
258impl<'a, T, A> From<CanonicalTx<'a, T, A>> for Txid {
259    fn from(tx: CanonicalTx<'a, T, A>) -> Self {
260        tx.tx_node.txid
261    }
262}
263
264impl<'a, A> From<CanonicalTx<'a, Arc<Transaction>, A>> for Arc<Transaction> {
265    fn from(tx: CanonicalTx<'a, Arc<Transaction>, A>) -> Self {
266        tx.tx_node.tx
267    }
268}
269
270/// Errors returned by `TxGraph::calculate_fee`.
271#[derive(Debug, PartialEq, Eq)]
272pub enum CalculateFeeError {
273    /// Missing `TxOut` for one or more of the inputs of the tx
274    MissingTxOut(Vec<OutPoint>),
275    /// When the transaction is invalid according to the graph it has a negative fee
276    NegativeFee(SignedAmount),
277}
278
279impl fmt::Display for CalculateFeeError {
280    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
281        match self {
282            CalculateFeeError::MissingTxOut(outpoints) => write!(
283                f,
284                "missing `TxOut` for one or more of the inputs of the tx: {:?}",
285                outpoints
286            ),
287            CalculateFeeError::NegativeFee(fee) => write!(
288                f,
289                "transaction is invalid according to the graph and has negative fee: {}",
290                fee.display_dynamic()
291            ),
292        }
293    }
294}
295
296#[cfg(feature = "std")]
297impl std::error::Error for CalculateFeeError {}
298
299impl<A> TxGraph<A> {
300    /// Iterate over all tx outputs known by [`TxGraph`].
301    ///
302    /// This includes txouts of both full transactions as well as floating transactions.
303    pub fn all_txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
304        self.txs.iter().flat_map(|(txid, tx)| match tx {
305            TxNodeInternal::Whole(tx) => tx
306                .as_ref()
307                .output
308                .iter()
309                .enumerate()
310                .map(|(vout, txout)| (OutPoint::new(*txid, vout as _), txout))
311                .collect::<Vec<_>>(),
312            TxNodeInternal::Partial(txouts) => txouts
313                .iter()
314                .map(|(vout, txout)| (OutPoint::new(*txid, *vout as _), txout))
315                .collect::<Vec<_>>(),
316        })
317    }
318
319    /// Iterate over floating txouts known by [`TxGraph`].
320    ///
321    /// Floating txouts are txouts that do not have the residing full transaction contained in the
322    /// graph.
323    pub fn floating_txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
324        self.txs
325            .iter()
326            .filter_map(|(txid, tx_node)| match tx_node {
327                TxNodeInternal::Whole(_) => None,
328                TxNodeInternal::Partial(txouts) => Some(
329                    txouts
330                        .iter()
331                        .map(|(&vout, txout)| (OutPoint::new(*txid, vout), txout)),
332                ),
333            })
334            .flatten()
335    }
336
337    /// Iterate over all full transactions in the graph.
338    pub fn full_txs(&self) -> impl Iterator<Item = TxNode<'_, Arc<Transaction>, A>> {
339        self.txs.iter().filter_map(|(&txid, tx)| match tx {
340            TxNodeInternal::Whole(tx) => Some(TxNode {
341                txid,
342                tx: tx.clone(),
343                anchors: self.anchors.get(&txid).unwrap_or(&self.empty_anchors),
344                first_seen: self.first_seen.get(&txid).copied(),
345                last_seen: self.last_seen.get(&txid).copied(),
346            }),
347            TxNodeInternal::Partial(_) => None,
348        })
349    }
350
351    /// Iterate over graph transactions with no anchors or last-seen.
352    pub fn txs_with_no_anchor_or_last_seen(
353        &self,
354    ) -> impl Iterator<Item = TxNode<'_, Arc<Transaction>, A>> {
355        self.full_txs().filter_map(|tx| {
356            if tx.anchors.is_empty() && tx.last_seen.is_none() {
357                Some(tx)
358            } else {
359                None
360            }
361        })
362    }
363
364    /// Get a transaction by txid. This only returns `Some` for full transactions.
365    ///
366    /// Refer to [`get_txout`] for getting a specific [`TxOut`].
367    ///
368    /// [`get_txout`]: Self::get_txout
369    pub fn get_tx(&self, txid: Txid) -> Option<Arc<Transaction>> {
370        self.get_tx_node(txid).map(|n| n.tx)
371    }
372
373    /// Get a transaction node by txid. This only returns `Some` for full transactions.
374    pub fn get_tx_node(&self, txid: Txid) -> Option<TxNode<'_, Arc<Transaction>, A>> {
375        match &self.txs.get(&txid)? {
376            TxNodeInternal::Whole(tx) => Some(TxNode {
377                txid,
378                tx: tx.clone(),
379                anchors: self.anchors.get(&txid).unwrap_or(&self.empty_anchors),
380                first_seen: self.first_seen.get(&txid).copied(),
381                last_seen: self.last_seen.get(&txid).copied(),
382            }),
383            _ => None,
384        }
385    }
386
387    /// Obtains a single tx output (if any) at the specified outpoint.
388    pub fn get_txout(&self, outpoint: OutPoint) -> Option<&TxOut> {
389        match &self.txs.get(&outpoint.txid)? {
390            TxNodeInternal::Whole(tx) => tx.as_ref().output.get(outpoint.vout as usize),
391            TxNodeInternal::Partial(txouts) => txouts.get(&outpoint.vout),
392        }
393    }
394
395    /// Returns known outputs of a given `txid`.
396    ///
397    /// Returns a [`BTreeMap`] of vout to output of the provided `txid`.
398    pub fn tx_outputs(&self, txid: Txid) -> Option<BTreeMap<u32, &TxOut>> {
399        Some(match &self.txs.get(&txid)? {
400            TxNodeInternal::Whole(tx) => tx
401                .as_ref()
402                .output
403                .iter()
404                .enumerate()
405                .map(|(vout, txout)| (vout as u32, txout))
406                .collect::<BTreeMap<_, _>>(),
407            TxNodeInternal::Partial(txouts) => txouts
408                .iter()
409                .map(|(vout, txout)| (*vout, txout))
410                .collect::<BTreeMap<_, _>>(),
411        })
412    }
413
414    /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase
415    /// transaction. Returns `OK(_)` if we have all the [`TxOut`]s being spent by `tx` in the
416    /// graph (either as the full transactions or individual txouts).
417    ///
418    /// To calculate the fee for a [`Transaction`] that depends on foreign [`TxOut`] values you must
419    /// first manually insert the foreign TxOuts into the tx graph using the [`insert_txout`]
420    /// function. Only insert TxOuts you trust the values for!
421    ///
422    /// Note `tx` does not have to be in the graph for this to work.
423    ///
424    /// [`insert_txout`]: Self::insert_txout
425    pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
426        if tx.is_coinbase() {
427            return Ok(Amount::ZERO);
428        }
429
430        let (inputs_sum, missing_outputs) = tx.input.iter().fold(
431            (SignedAmount::ZERO, Vec::new()),
432            |(mut sum, mut missing_outpoints), txin| match self.get_txout(txin.previous_output) {
433                None => {
434                    missing_outpoints.push(txin.previous_output);
435                    (sum, missing_outpoints)
436                }
437                Some(txout) => {
438                    sum += txout.value.to_signed().expect("valid `SignedAmount`");
439                    (sum, missing_outpoints)
440                }
441            },
442        );
443        if !missing_outputs.is_empty() {
444            return Err(CalculateFeeError::MissingTxOut(missing_outputs));
445        }
446
447        let outputs_sum = tx
448            .output
449            .iter()
450            .map(|txout| txout.value.to_signed().expect("valid `SignedAmount`"))
451            .sum::<SignedAmount>();
452
453        let fee = inputs_sum - outputs_sum;
454        fee.to_unsigned()
455            .map_err(|_| CalculateFeeError::NegativeFee(fee))
456    }
457
458    /// The transactions spending from this output.
459    ///
460    /// [`TxGraph`] allows conflicting transactions within the graph. Obviously the transactions in
461    /// the returned set will never be in the same active-chain.
462    pub fn outspends(&self, outpoint: OutPoint) -> &HashSet<Txid> {
463        self.spends.get(&outpoint).unwrap_or(&self.empty_outspends)
464    }
465
466    /// Iterates over the transactions spending from `txid`.
467    ///
468    /// The iterator item is a union of `(vout, txid-set)` where:
469    ///
470    /// - `vout` is the provided `txid`'s outpoint that is being spent
471    /// - `txid-set` is the set of txids spending the `vout`.
472    pub fn tx_spends(
473        &self,
474        txid: Txid,
475    ) -> impl DoubleEndedIterator<Item = (u32, &HashSet<Txid>)> + '_ {
476        let start = OutPoint::new(txid, 0);
477        let end = OutPoint::new(txid, u32::MAX);
478        self.spends
479            .range(start..=end)
480            .map(|(outpoint, spends)| (outpoint.vout, spends))
481    }
482}
483
484impl<A: Clone + Ord> TxGraph<A> {
485    /// Creates an iterator that filters and maps ancestor transactions.
486    ///
487    /// The iterator starts with the ancestors of the supplied `tx` (ancestor transactions of `tx`
488    /// are transactions spent by `tx`). The supplied transaction is excluded from the iterator.
489    ///
490    /// The supplied closure takes in two inputs `(depth, ancestor_tx)`:
491    ///
492    /// * `depth` is the distance between the starting `Transaction` and the `ancestor_tx`. I.e., if
493    ///   the `Transaction` is spending an output of the `ancestor_tx` then `depth` will be 1.
494    /// * `ancestor_tx` is the `Transaction`'s ancestor which we are considering to walk.
495    ///
496    /// The supplied closure returns an `Option<T>`, allowing the caller to map each `Transaction`
497    /// it visits and decide whether to visit ancestors.
498    pub fn walk_ancestors<'g, T, F, O>(&'g self, tx: T, walk_map: F) -> TxAncestors<'g, A, F, O>
499    where
500        T: Into<Arc<Transaction>>,
501        F: FnMut(usize, Arc<Transaction>) -> Option<O> + 'g,
502    {
503        TxAncestors::new_exclude_root(self, tx, walk_map)
504    }
505
506    /// Creates an iterator that filters and maps descendants from the starting `txid`.
507    ///
508    /// The supplied closure takes in two inputs `(depth, descendant_txid)`:
509    ///
510    /// * `depth` is the distance between the starting `txid` and the `descendant_txid`. I.e., if
511    ///   the descendant is spending an output of the starting `txid` then `depth` will be 1.
512    /// * `descendant_txid` is the descendant's txid which we are considering to walk.
513    ///
514    /// The supplied closure returns an `Option<T>`, allowing the caller to map each node it visits
515    /// and decide whether to visit descendants.
516    pub fn walk_descendants<'g, F, O>(
517        &'g self,
518        txid: Txid,
519        walk_map: F,
520    ) -> TxDescendants<'g, A, F, O>
521    where
522        F: FnMut(usize, Txid) -> Option<O> + 'g,
523    {
524        TxDescendants::new_exclude_root(self, txid, walk_map)
525    }
526}
527
528impl<A> TxGraph<A> {
529    /// Creates an iterator that both filters and maps conflicting transactions (this includes
530    /// descendants of directly-conflicting transactions, which are also considered conflicts).
531    ///
532    /// Refer to [`Self::walk_descendants`] for `walk_map` usage.
533    pub fn walk_conflicts<'g, F, O>(
534        &'g self,
535        tx: &'g Transaction,
536        walk_map: F,
537    ) -> TxDescendants<'g, A, F, O>
538    where
539        F: FnMut(usize, Txid) -> Option<O> + 'g,
540    {
541        let txids = self.direct_conflicts(tx).map(|(_, txid)| txid);
542        TxDescendants::from_multiple_include_root(self, txids, walk_map)
543    }
544
545    /// Given a transaction, return an iterator of txids that directly conflict with the given
546    /// transaction's inputs (spends). The conflicting txids are returned with the given
547    /// transaction's vin (in which it conflicts).
548    ///
549    /// Note that this only returns directly conflicting txids and won't include:
550    /// - descendants of conflicting transactions (which are technically also conflicting)
551    /// - transactions conflicting with the given transaction's ancestors
552    pub fn direct_conflicts<'g>(
553        &'g self,
554        tx: &'g Transaction,
555    ) -> impl Iterator<Item = (usize, Txid)> + 'g {
556        let txid = tx.compute_txid();
557        tx.input
558            .iter()
559            .enumerate()
560            .filter_map(move |(vin, txin)| self.spends.get(&txin.previous_output).zip(Some(vin)))
561            .flat_map(|(spends, vin)| core::iter::repeat(vin).zip(spends.iter().cloned()))
562            .filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
563    }
564
565    /// Get all transaction anchors known by [`TxGraph`].
566    pub fn all_anchors(&self) -> &HashMap<Txid, BTreeSet<A>> {
567        &self.anchors
568    }
569
570    /// Whether the graph has any transactions or outputs in it.
571    pub fn is_empty(&self) -> bool {
572        self.txs.is_empty()
573    }
574}
575
576impl<A: Anchor> TxGraph<A> {
577    /// Transform the [`TxGraph`] to have [`Anchor`]s of another type.
578    ///
579    /// This takes in a closure of signature `FnMut(A) -> A2` which is called for each [`Anchor`] to
580    /// transform it.
581    pub fn map_anchors<A2: Anchor, F>(self, f: F) -> TxGraph<A2>
582    where
583        F: FnMut(A) -> A2,
584    {
585        let mut new_graph = TxGraph::<A2>::default();
586        new_graph.apply_changeset(self.initial_changeset().map_anchors(f));
587        new_graph
588    }
589
590    /// Construct a new [`TxGraph`] from a list of transactions.
591    pub fn new(txs: impl IntoIterator<Item = Transaction>) -> Self {
592        let mut new = Self::default();
593        for tx in txs.into_iter() {
594            let _ = new.insert_tx(tx);
595        }
596        new
597    }
598
599    /// Inserts the given [`TxOut`] at [`OutPoint`].
600    ///
601    /// Inserting floating txouts are useful for determining fee/feerate of transactions we care
602    /// about.
603    ///
604    /// The [`ChangeSet`] result will be empty if the `outpoint` (or a full transaction containing
605    /// the `outpoint`) already existed in `self`.
606    ///
607    /// [`apply_changeset`]: Self::apply_changeset
608    pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) -> ChangeSet<A> {
609        let mut changeset = ChangeSet::<A>::default();
610        let tx_node = self.txs.entry(outpoint.txid).or_default();
611        match tx_node {
612            TxNodeInternal::Whole(_) => {
613                // ignore this txout we have the full one already.
614                // NOTE: You might think putting a debug_assert! here to check the output being
615                // replaced was actually correct is a good idea but the tests have already been
616                // written assuming this never panics.
617            }
618            TxNodeInternal::Partial(partial_tx) => {
619                match partial_tx.insert(outpoint.vout, txout.clone()) {
620                    Some(old_txout) => {
621                        debug_assert_eq!(
622                            txout, old_txout,
623                            "txout of the same outpoint should never change"
624                        );
625                    }
626                    None => {
627                        changeset.txouts.insert(outpoint, txout);
628                    }
629                }
630            }
631        }
632        changeset
633    }
634
635    /// Insert the given transaction into [`TxGraph`].
636    ///
637    /// The [`ChangeSet`] returned will be empty if no changes are made to the graph.
638    ///
639    /// # Updating Existing Transactions
640    ///
641    /// An unsigned transaction can be inserted first and have it's witness fields updated with
642    /// further transaction insertions (given that the newly introduced transaction shares the same
643    /// txid as the original transaction).
644    ///
645    /// The witnesses of the newly introduced transaction will be merged with the witnesses of the
646    /// original transaction in a way where:
647    ///
648    /// * A non-empty witness has precedence over an empty witness.
649    /// * A smaller witness has precedence over a larger witness.
650    /// * If the witness sizes are the same, we prioritize the two witnesses with lexicographical
651    ///   order.
652    pub fn insert_tx<T: Into<Arc<Transaction>>>(&mut self, tx: T) -> ChangeSet<A> {
653        // This returns `Some` only if the merged tx is different to the `original_tx`.
654        fn _merge_tx_witnesses(
655            original_tx: &Arc<Transaction>,
656            other_tx: &Arc<Transaction>,
657        ) -> Option<Arc<Transaction>> {
658            debug_assert_eq!(
659                original_tx.input.len(),
660                other_tx.input.len(),
661                "tx input count must be the same"
662            );
663            let merged_input = Iterator::zip(original_tx.input.iter(), other_tx.input.iter())
664                .map(|(original_txin, other_txin)| {
665                    let original_key = core::cmp::Reverse((
666                        original_txin.witness.is_empty(),
667                        original_txin.witness.size(),
668                        &original_txin.witness,
669                    ));
670                    let other_key = core::cmp::Reverse((
671                        other_txin.witness.is_empty(),
672                        other_txin.witness.size(),
673                        &other_txin.witness,
674                    ));
675                    if original_key > other_key {
676                        original_txin.clone()
677                    } else {
678                        other_txin.clone()
679                    }
680                })
681                .collect::<Vec<_>>();
682            if merged_input == original_tx.input {
683                return None;
684            }
685            if merged_input == other_tx.input {
686                return Some(other_tx.clone());
687            }
688            Some(Arc::new(Transaction {
689                input: merged_input,
690                ..(**original_tx).clone()
691            }))
692        }
693
694        let tx: Arc<Transaction> = tx.into();
695        let txid = tx.compute_txid();
696        let mut changeset = ChangeSet::<A>::default();
697
698        let tx_node = self.txs.entry(txid).or_default();
699        match tx_node {
700            TxNodeInternal::Whole(existing_tx) => {
701                if existing_tx.as_ref() != tx.as_ref() {
702                    // Allowing updating witnesses of txs.
703                    if let Some(merged_tx) = _merge_tx_witnesses(existing_tx, &tx) {
704                        *existing_tx = merged_tx.clone();
705                        changeset.txs.insert(merged_tx);
706                    }
707                }
708            }
709            partial_tx => {
710                for txin in &tx.input {
711                    // this means the tx is coinbase so there is no previous output
712                    if txin.previous_output.is_null() {
713                        continue;
714                    }
715                    self.spends
716                        .entry(txin.previous_output)
717                        .or_default()
718                        .insert(txid);
719                }
720                *partial_tx = TxNodeInternal::Whole(tx.clone());
721                changeset.txs.insert(tx);
722            }
723        }
724
725        changeset
726    }
727
728    /// Batch insert unconfirmed transactions.
729    ///
730    /// Items of `txs` are tuples containing the transaction and a *last seen* timestamp. The
731    /// *last seen* communicates when the transaction is last seen in mempool which is used for
732    /// conflict-resolution (refer to [`TxGraph::insert_seen_at`] for details).
733    pub fn batch_insert_unconfirmed<T: Into<Arc<Transaction>>>(
734        &mut self,
735        txs: impl IntoIterator<Item = (T, u64)>,
736    ) -> ChangeSet<A> {
737        let mut changeset = ChangeSet::<A>::default();
738        for (tx, seen_at) in txs {
739            let tx: Arc<Transaction> = tx.into();
740            changeset.merge(self.insert_seen_at(tx.compute_txid(), seen_at));
741            changeset.merge(self.insert_tx(tx));
742        }
743        changeset
744    }
745
746    /// Inserts the given `anchor` into [`TxGraph`].
747    ///
748    /// The [`ChangeSet`] returned will be empty if graph already knows that `txid` exists in
749    /// `anchor`.
750    pub fn insert_anchor(&mut self, txid: Txid, anchor: A) -> ChangeSet<A> {
751        // These two variables are used to determine how to modify the `txid`'s entry in
752        // `txs_by_highest_conf_heights`.
753        // We want to remove `(old_top_h?, txid)` and insert `(new_top_h?, txid)`.
754        let mut old_top_h = None;
755        let mut new_top_h = anchor.confirmation_height_upper_bound();
756
757        let is_changed = match self.anchors.entry(txid) {
758            hash_map::Entry::Occupied(mut e) => {
759                old_top_h = e
760                    .get()
761                    .iter()
762                    .last()
763                    .map(Anchor::confirmation_height_upper_bound);
764                if let Some(old_top_h) = old_top_h {
765                    if old_top_h > new_top_h {
766                        new_top_h = old_top_h;
767                    }
768                }
769                let is_changed = e.get_mut().insert(anchor.clone());
770                is_changed
771            }
772            hash_map::Entry::Vacant(e) => {
773                e.insert(core::iter::once(anchor.clone()).collect());
774                true
775            }
776        };
777
778        let mut changeset = ChangeSet::<A>::default();
779        if is_changed {
780            let new_top_is_changed = match old_top_h {
781                None => true,
782                Some(old_top_h) if old_top_h != new_top_h => true,
783                _ => false,
784            };
785            if new_top_is_changed {
786                if let Some(prev_top_h) = old_top_h {
787                    self.txs_by_highest_conf_heights.remove(&(prev_top_h, txid));
788                }
789                self.txs_by_highest_conf_heights.insert((new_top_h, txid));
790            }
791            changeset.anchors.insert((anchor, txid));
792        }
793        changeset
794    }
795
796    /// Updates the first-seen and last-seen timestamps for a given `txid` in the [`TxGraph`].
797    ///
798    /// This method records the time a transaction was observed by updating both:
799    /// - the **first-seen** timestamp, which only changes if `seen_at` is earlier than the current
800    ///   value, and
801    /// - the **last-seen** timestamp, which only changes if `seen_at` is later than the current
802    ///   value.
803    ///
804    /// `seen_at` is a UNIX timestamp in seconds.
805    ///
806    /// Returns a [`ChangeSet`] representing any changes applied.
807    pub fn insert_seen_at(&mut self, txid: Txid, seen_at: u64) -> ChangeSet<A> {
808        let mut changeset_first_seen = self.update_first_seen(txid, seen_at);
809        let changeset_last_seen = self.update_last_seen(txid, seen_at);
810        changeset_first_seen.merge(changeset_last_seen);
811        changeset_first_seen
812    }
813
814    /// Updates `first_seen` given a new `seen_at`.
815    fn update_first_seen(&mut self, txid: Txid, seen_at: u64) -> ChangeSet<A> {
816        let is_changed = match self.first_seen.entry(txid) {
817            hash_map::Entry::Occupied(mut e) => {
818                let first_seen = e.get_mut();
819                let change = *first_seen > seen_at;
820                if change {
821                    *first_seen = seen_at;
822                }
823                change
824            }
825            hash_map::Entry::Vacant(e) => {
826                e.insert(seen_at);
827                true
828            }
829        };
830
831        let mut changeset = ChangeSet::<A>::default();
832        if is_changed {
833            changeset.first_seen.insert(txid, seen_at);
834        }
835        changeset
836    }
837
838    /// Updates `last_seen` given a new `seen_at`.
839    fn update_last_seen(&mut self, txid: Txid, seen_at: u64) -> ChangeSet<A> {
840        let mut old_last_seen = None;
841        let is_changed = match self.last_seen.entry(txid) {
842            hash_map::Entry::Occupied(mut e) => {
843                let last_seen = e.get_mut();
844                old_last_seen = Some(*last_seen);
845                let change = *last_seen < seen_at;
846                if change {
847                    *last_seen = seen_at;
848                }
849                change
850            }
851            hash_map::Entry::Vacant(e) => {
852                e.insert(seen_at);
853                true
854            }
855        };
856
857        let mut changeset = ChangeSet::<A>::default();
858        if is_changed {
859            if let Some(old_last_seen) = old_last_seen {
860                self.txs_by_last_seen.remove(&(old_last_seen, txid));
861            }
862            self.txs_by_last_seen.insert((seen_at, txid));
863            changeset.last_seen.insert(txid, seen_at);
864        }
865        changeset
866    }
867
868    /// Inserts the given `evicted_at` for `txid` into [`TxGraph`].
869    ///
870    /// The `evicted_at` timestamp represents the last known time when the transaction was observed
871    /// to be missing from the mempool. If `txid` was previously recorded with an earlier
872    /// `evicted_at` value, it is updated only if the new value is greater.
873    pub fn insert_evicted_at(&mut self, txid: Txid, evicted_at: u64) -> ChangeSet<A> {
874        let is_changed = match self.last_evicted.entry(txid) {
875            hash_map::Entry::Occupied(mut e) => {
876                let last_evicted = e.get_mut();
877                let change = *last_evicted < evicted_at;
878                if change {
879                    *last_evicted = evicted_at;
880                }
881                change
882            }
883            hash_map::Entry::Vacant(e) => {
884                e.insert(evicted_at);
885                true
886            }
887        };
888
889        let mut changeset = ChangeSet::<A>::default();
890        if is_changed {
891            changeset.last_evicted.insert(txid, evicted_at);
892        }
893        changeset
894    }
895
896    /// Batch inserts `(txid, evicted_at)` pairs into [`TxGraph`] for `txid`s that the graph is
897    /// tracking.
898    ///
899    /// The `evicted_at` timestamp represents the last known time when the transaction was observed
900    /// to be missing from the mempool. If `txid` was previously recorded with an earlier
901    /// `evicted_at` value, it is updated only if the new value is greater.
902    pub fn batch_insert_relevant_evicted_at(
903        &mut self,
904        evicted_ats: impl IntoIterator<Item = (Txid, u64)>,
905    ) -> ChangeSet<A> {
906        let mut changeset = ChangeSet::default();
907        for (txid, evicted_at) in evicted_ats {
908            // Only record evictions for transactions the graph is tracking.
909            if self.txs.contains_key(&txid) {
910                changeset.merge(self.insert_evicted_at(txid, evicted_at));
911            }
912        }
913        changeset
914    }
915
916    /// Extends this graph with the given `update`.
917    ///
918    /// The returned [`ChangeSet`] is the set difference between `update` and `self` (transactions
919    /// that exist in `update` but not in `self`).
920    pub fn apply_update(&mut self, update: TxUpdate<A>) -> ChangeSet<A> {
921        let mut changeset = ChangeSet::<A>::default();
922        for tx in update.txs {
923            changeset.merge(self.insert_tx(tx));
924        }
925        for (outpoint, txout) in update.txouts {
926            changeset.merge(self.insert_txout(outpoint, txout));
927        }
928        for (anchor, txid) in update.anchors {
929            changeset.merge(self.insert_anchor(txid, anchor));
930        }
931        for (txid, seen_at) in update.seen_ats {
932            changeset.merge(self.insert_seen_at(txid, seen_at));
933        }
934        for (txid, evicted_at) in update.evicted_ats {
935            changeset.merge(self.insert_evicted_at(txid, evicted_at));
936        }
937        changeset
938    }
939
940    /// Determines the [`ChangeSet`] between `self` and an empty [`TxGraph`].
941    pub fn initial_changeset(&self) -> ChangeSet<A> {
942        ChangeSet {
943            txs: self.full_txs().map(|tx_node| tx_node.tx).collect(),
944            txouts: self
945                .floating_txouts()
946                .map(|(op, txout)| (op, txout.clone()))
947                .collect(),
948            anchors: self
949                .anchors
950                .iter()
951                .flat_map(|(txid, anchors)| anchors.iter().map(|a| (a.clone(), *txid)))
952                .collect(),
953            first_seen: self.first_seen.iter().map(|(&k, &v)| (k, v)).collect(),
954            last_seen: self.last_seen.iter().map(|(&k, &v)| (k, v)).collect(),
955            last_evicted: self.last_evicted.iter().map(|(&k, &v)| (k, v)).collect(),
956        }
957    }
958
959    /// Applies [`ChangeSet`] to [`TxGraph`].
960    pub fn apply_changeset(&mut self, changeset: ChangeSet<A>) {
961        for tx in changeset.txs {
962            let _ = self.insert_tx(tx);
963        }
964        for (outpoint, txout) in changeset.txouts {
965            let _ = self.insert_txout(outpoint, txout);
966        }
967        for (anchor, txid) in changeset.anchors {
968            let _ = self.insert_anchor(txid, anchor);
969        }
970        for (txid, seen_at) in changeset.last_seen {
971            let _ = self.insert_seen_at(txid, seen_at);
972        }
973        for (txid, evicted_at) in changeset.last_evicted {
974            let _ = self.insert_evicted_at(txid, evicted_at);
975        }
976    }
977}
978
979impl<A: Anchor> TxGraph<A> {
980    /// List graph transactions that are in `chain` with `chain_tip`.
981    ///
982    /// Each transaction is represented as a [`CanonicalTx`] that contains where the transaction is
983    /// observed in-chain, and the [`TxNode`].
984    ///
985    /// # Error
986    ///
987    /// If the [`ChainOracle`] implementation (`chain`) fails, an error will be returned with the
988    /// returned item.
989    ///
990    /// If the [`ChainOracle`] is infallible, [`list_canonical_txs`] can be used instead.
991    ///
992    /// [`list_canonical_txs`]: Self::list_canonical_txs
993    pub fn try_list_canonical_txs<'a, C: ChainOracle + 'a>(
994        &'a self,
995        chain: &'a C,
996        chain_tip: BlockId,
997        params: CanonicalizationParams,
998    ) -> impl Iterator<Item = Result<CanonicalTx<'a, Arc<Transaction>, A>, C::Error>> {
999        fn find_direct_anchor<A: Anchor, C: ChainOracle>(
1000            tx_node: &TxNode<'_, Arc<Transaction>, A>,
1001            chain: &C,
1002            chain_tip: BlockId,
1003        ) -> Result<Option<A>, C::Error> {
1004            tx_node
1005                .anchors
1006                .iter()
1007                .find_map(|a| -> Option<Result<A, C::Error>> {
1008                    match chain.is_block_in_chain(a.anchor_block(), chain_tip) {
1009                        Ok(Some(true)) => Some(Ok(a.clone())),
1010                        Ok(Some(false)) | Ok(None) => None,
1011                        Err(err) => Some(Err(err)),
1012                    }
1013                })
1014                .transpose()
1015        }
1016        self.canonical_iter(chain, chain_tip, params)
1017            .flat_map(move |res| {
1018                res.map(|(txid, _, canonical_reason)| {
1019                    let tx_node = self.get_tx_node(txid).expect("must contain tx");
1020                    let chain_position = match canonical_reason {
1021                        CanonicalReason::Assumed { descendant } => match descendant {
1022                            Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
1023                                Some(anchor) => ChainPosition::Confirmed {
1024                                    anchor,
1025                                    transitively: None,
1026                                },
1027                                None => ChainPosition::Unconfirmed {
1028                                    first_seen: tx_node.first_seen,
1029                                    last_seen: tx_node.last_seen,
1030                                },
1031                            },
1032                            None => ChainPosition::Unconfirmed {
1033                                first_seen: tx_node.first_seen,
1034                                last_seen: tx_node.last_seen,
1035                            },
1036                        },
1037                        CanonicalReason::Anchor { anchor, descendant } => match descendant {
1038                            Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
1039                                Some(anchor) => ChainPosition::Confirmed {
1040                                    anchor,
1041                                    transitively: None,
1042                                },
1043                                None => ChainPosition::Confirmed {
1044                                    anchor,
1045                                    transitively: descendant,
1046                                },
1047                            },
1048                            None => ChainPosition::Confirmed {
1049                                anchor,
1050                                transitively: None,
1051                            },
1052                        },
1053                        CanonicalReason::ObservedIn { observed_in, .. } => match observed_in {
1054                            ObservedIn::Mempool(last_seen) => ChainPosition::Unconfirmed {
1055                                first_seen: tx_node.first_seen,
1056                                last_seen: Some(last_seen),
1057                            },
1058                            ObservedIn::Block(_) => ChainPosition::Unconfirmed {
1059                                first_seen: tx_node.first_seen,
1060                                last_seen: None,
1061                            },
1062                        },
1063                    };
1064                    Ok(CanonicalTx {
1065                        chain_position,
1066                        tx_node,
1067                    })
1068                })
1069            })
1070    }
1071
1072    /// List graph transactions that are in `chain` with `chain_tip`.
1073    ///
1074    /// This is the infallible version of [`try_list_canonical_txs`].
1075    ///
1076    /// [`try_list_canonical_txs`]: Self::try_list_canonical_txs
1077    pub fn list_canonical_txs<'a, C: ChainOracle<Error = Infallible> + 'a>(
1078        &'a self,
1079        chain: &'a C,
1080        chain_tip: BlockId,
1081        params: CanonicalizationParams,
1082    ) -> impl Iterator<Item = CanonicalTx<'a, Arc<Transaction>, A>> {
1083        self.try_list_canonical_txs(chain, chain_tip, params)
1084            .map(|res| res.expect("infallible"))
1085    }
1086
1087    /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
1088    /// `chain_tip`.
1089    ///
1090    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
1091    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
1092    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
1093    ///
1094    /// Floating outputs (i.e., outputs for which we don't have the full transaction in the graph)
1095    /// are ignored.
1096    ///
1097    /// # Error
1098    ///
1099    /// An [`Iterator::Item`] can be an [`Err`] if the [`ChainOracle`] implementation (`chain`)
1100    /// fails.
1101    ///
1102    /// If the [`ChainOracle`] implementation is infallible, [`filter_chain_txouts`] can be used
1103    /// instead.
1104    ///
1105    /// [`filter_chain_txouts`]: Self::filter_chain_txouts
1106    pub fn try_filter_chain_txouts<'a, C: ChainOracle + 'a, OI: Clone + 'a>(
1107        &'a self,
1108        chain: &'a C,
1109        chain_tip: BlockId,
1110        params: CanonicalizationParams,
1111        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
1112    ) -> Result<impl Iterator<Item = (OI, FullTxOut<A>)> + 'a, C::Error> {
1113        let mut canon_txs = HashMap::<Txid, CanonicalTx<Arc<Transaction>, A>>::new();
1114        let mut canon_spends = HashMap::<OutPoint, Txid>::new();
1115        for r in self.try_list_canonical_txs(chain, chain_tip, params) {
1116            let canonical_tx = r?;
1117            let txid = canonical_tx.tx_node.txid;
1118
1119            if !canonical_tx.tx_node.tx.is_coinbase() {
1120                for txin in &canonical_tx.tx_node.tx.input {
1121                    let _res = canon_spends.insert(txin.previous_output, txid);
1122                    assert!(
1123                        _res.is_none(),
1124                        "tried to replace {:?} with {:?}",
1125                        _res,
1126                        txid
1127                    );
1128                }
1129            }
1130            canon_txs.insert(txid, canonical_tx);
1131        }
1132        Ok(outpoints.into_iter().filter_map(move |(spk_i, outpoint)| {
1133            let canon_tx = canon_txs.get(&outpoint.txid)?;
1134            let txout = canon_tx
1135                .tx_node
1136                .tx
1137                .output
1138                .get(outpoint.vout as usize)
1139                .cloned()?;
1140            let chain_position = canon_tx.chain_position.clone();
1141            let spent_by = canon_spends.get(&outpoint).map(|spend_txid| {
1142                let spend_tx = canon_txs
1143                    .get(spend_txid)
1144                    .cloned()
1145                    .expect("must be canonical");
1146                (spend_tx.chain_position, *spend_txid)
1147            });
1148            let is_on_coinbase = canon_tx.tx_node.is_coinbase();
1149            Some((
1150                spk_i,
1151                FullTxOut {
1152                    outpoint,
1153                    txout,
1154                    chain_position,
1155                    spent_by,
1156                    is_on_coinbase,
1157                },
1158            ))
1159        }))
1160    }
1161
1162    /// List txids by descending anchor height order.
1163    ///
1164    /// If multiple anchors exist for a txid, the highest anchor height will be used. Transactions
1165    /// without anchors are excluded.
1166    pub fn txids_by_descending_anchor_height(
1167        &self,
1168    ) -> impl ExactSizeIterator<Item = (u32, Txid)> + '_ {
1169        self.txs_by_highest_conf_heights.iter().copied().rev()
1170    }
1171
1172    /// List txids by descending last-seen order.
1173    ///
1174    /// Transactions without last-seens are excluded. Transactions with a last-evicted timestamp
1175    /// equal or higher than it's last-seen timestamp are excluded.
1176    pub fn txids_by_descending_last_seen(&self) -> impl Iterator<Item = (u64, Txid)> + '_ {
1177        self.txs_by_last_seen
1178            .iter()
1179            .copied()
1180            .rev()
1181            .filter(|(last_seen, txid)| match self.last_evicted.get(txid) {
1182                Some(last_evicted) => last_evicted < last_seen,
1183                None => true,
1184            })
1185    }
1186
1187    /// Returns a [`CanonicalIter`].
1188    pub fn canonical_iter<'a, C: ChainOracle>(
1189        &'a self,
1190        chain: &'a C,
1191        chain_tip: BlockId,
1192        params: CanonicalizationParams,
1193    ) -> CanonicalIter<'a, A, C> {
1194        CanonicalIter::new(self, chain, chain_tip, params)
1195    }
1196
1197    /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
1198    /// `chain_tip`.
1199    ///
1200    /// This is the infallible version of [`try_filter_chain_txouts`].
1201    ///
1202    /// [`try_filter_chain_txouts`]: Self::try_filter_chain_txouts
1203    pub fn filter_chain_txouts<'a, C: ChainOracle<Error = Infallible> + 'a, OI: Clone + 'a>(
1204        &'a self,
1205        chain: &'a C,
1206        chain_tip: BlockId,
1207        params: CanonicalizationParams,
1208        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
1209    ) -> impl Iterator<Item = (OI, FullTxOut<A>)> + 'a {
1210        self.try_filter_chain_txouts(chain, chain_tip, params, outpoints)
1211            .expect("oracle is infallible")
1212    }
1213
1214    /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in
1215    /// `chain` with `chain_tip`.
1216    ///
1217    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
1218    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
1219    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
1220    ///
1221    /// Floating outputs are ignored.
1222    ///
1223    /// # Error
1224    ///
1225    /// An [`Iterator::Item`] can be an [`Err`] if the [`ChainOracle`] implementation (`chain`)
1226    /// fails.
1227    ///
1228    /// If the [`ChainOracle`] implementation is infallible, [`filter_chain_unspents`] can be used
1229    /// instead.
1230    ///
1231    /// [`filter_chain_unspents`]: Self::filter_chain_unspents
1232    pub fn try_filter_chain_unspents<'a, C: ChainOracle + 'a, OI: Clone + 'a>(
1233        &'a self,
1234        chain: &'a C,
1235        chain_tip: BlockId,
1236        params: CanonicalizationParams,
1237        outpoints: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
1238    ) -> Result<impl Iterator<Item = (OI, FullTxOut<A>)> + 'a, C::Error> {
1239        Ok(self
1240            .try_filter_chain_txouts(chain, chain_tip, params, outpoints)?
1241            .filter(|(_, full_txo)| full_txo.spent_by.is_none()))
1242    }
1243
1244    /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in
1245    /// `chain` with `chain_tip`.
1246    ///
1247    /// This is the infallible version of [`try_filter_chain_unspents`].
1248    ///
1249    /// [`try_filter_chain_unspents`]: Self::try_filter_chain_unspents
1250    pub fn filter_chain_unspents<'a, C: ChainOracle<Error = Infallible> + 'a, OI: Clone + 'a>(
1251        &'a self,
1252        chain: &'a C,
1253        chain_tip: BlockId,
1254        params: CanonicalizationParams,
1255        txouts: impl IntoIterator<Item = (OI, OutPoint)> + 'a,
1256    ) -> impl Iterator<Item = (OI, FullTxOut<A>)> + 'a {
1257        self.try_filter_chain_unspents(chain, chain_tip, params, txouts)
1258            .expect("oracle is infallible")
1259    }
1260
1261    /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`.
1262    ///
1263    /// The output of `trust_predicate` should return `true` for scripts that we trust.
1264    ///
1265    /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier
1266    /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
1267    /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
1268    ///
1269    /// If the provided [`ChainOracle`] implementation (`chain`) is infallible, [`balance`] can be
1270    /// used instead.
1271    ///
1272    /// [`balance`]: Self::balance
1273    pub fn try_balance<C: ChainOracle, OI: Clone>(
1274        &self,
1275        chain: &C,
1276        chain_tip: BlockId,
1277        params: CanonicalizationParams,
1278        outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
1279        mut trust_predicate: impl FnMut(&OI, ScriptBuf) -> bool,
1280    ) -> Result<Balance, C::Error> {
1281        let mut immature = Amount::ZERO;
1282        let mut trusted_pending = Amount::ZERO;
1283        let mut untrusted_pending = Amount::ZERO;
1284        let mut confirmed = Amount::ZERO;
1285
1286        for (spk_i, txout) in self.try_filter_chain_unspents(chain, chain_tip, params, outpoints)? {
1287            match &txout.chain_position {
1288                ChainPosition::Confirmed { .. } => {
1289                    if txout.is_confirmed_and_spendable(chain_tip.height) {
1290                        confirmed += txout.txout.value;
1291                    } else if !txout.is_mature(chain_tip.height) {
1292                        immature += txout.txout.value;
1293                    }
1294                }
1295                ChainPosition::Unconfirmed { .. } => {
1296                    if trust_predicate(&spk_i, txout.txout.script_pubkey) {
1297                        trusted_pending += txout.txout.value;
1298                    } else {
1299                        untrusted_pending += txout.txout.value;
1300                    }
1301                }
1302            }
1303        }
1304
1305        Ok(Balance {
1306            immature,
1307            trusted_pending,
1308            untrusted_pending,
1309            confirmed,
1310        })
1311    }
1312
1313    /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`.
1314    ///
1315    /// This is the infallible version of [`try_balance`].
1316    ///
1317    /// [`try_balance`]: Self::try_balance
1318    pub fn balance<C: ChainOracle<Error = Infallible>, OI: Clone>(
1319        &self,
1320        chain: &C,
1321        chain_tip: BlockId,
1322        params: CanonicalizationParams,
1323        outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
1324        trust_predicate: impl FnMut(&OI, ScriptBuf) -> bool,
1325    ) -> Balance {
1326        self.try_balance(chain, chain_tip, params, outpoints, trust_predicate)
1327            .expect("oracle is infallible")
1328    }
1329
1330    /// List txids that are expected to exist under the given spks.
1331    ///
1332    /// This is used to fill
1333    /// [`SyncRequestBuilder::expected_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_spk_txids).
1334    ///
1335    ///
1336    /// The spk index range can be constrained with `range`.
1337    ///
1338    /// # Error
1339    ///
1340    /// If the [`ChainOracle`] implementation (`chain`) fails, an error will be returned with the
1341    /// returned item.
1342    ///
1343    /// If the [`ChainOracle`] is infallible,
1344    /// [`list_expected_spk_txids`](Self::list_expected_spk_txids) can be used instead.
1345    pub fn try_list_expected_spk_txids<'a, C, I>(
1346        &'a self,
1347        chain: &'a C,
1348        chain_tip: BlockId,
1349        indexer: &'a impl AsRef<SpkTxOutIndex<I>>,
1350        spk_index_range: impl RangeBounds<I> + 'a,
1351    ) -> impl Iterator<Item = Result<(ScriptBuf, Txid), C::Error>> + 'a
1352    where
1353        C: ChainOracle,
1354        I: fmt::Debug + Clone + Ord + 'a,
1355    {
1356        let indexer = indexer.as_ref();
1357        self.try_list_canonical_txs(chain, chain_tip, CanonicalizationParams::default())
1358            .flat_map(move |res| -> Vec<Result<(ScriptBuf, Txid), C::Error>> {
1359                let range = &spk_index_range;
1360                let c_tx = match res {
1361                    Ok(c_tx) => c_tx,
1362                    Err(err) => return vec![Err(err)],
1363                };
1364                let relevant_spks = indexer.relevant_spks_of_tx(&c_tx.tx_node);
1365                relevant_spks
1366                    .into_iter()
1367                    .filter(|(i, _)| range.contains(i))
1368                    .map(|(_, spk)| Ok((spk, c_tx.tx_node.txid)))
1369                    .collect()
1370            })
1371    }
1372
1373    /// List txids that are expected to exist under the given spks.
1374    ///
1375    /// This is the infallible version of
1376    /// [`try_list_expected_spk_txids`](Self::try_list_expected_spk_txids).
1377    pub fn list_expected_spk_txids<'a, C, I>(
1378        &'a self,
1379        chain: &'a C,
1380        chain_tip: BlockId,
1381        indexer: &'a impl AsRef<SpkTxOutIndex<I>>,
1382        spk_index_range: impl RangeBounds<I> + 'a,
1383    ) -> impl Iterator<Item = (ScriptBuf, Txid)> + 'a
1384    where
1385        C: ChainOracle<Error = Infallible>,
1386        I: fmt::Debug + Clone + Ord + 'a,
1387    {
1388        self.try_list_expected_spk_txids(chain, chain_tip, indexer, spk_index_range)
1389            .map(|r| r.expect("infallible"))
1390    }
1391
1392    /// Construct a `TxGraph` from a `changeset`.
1393    pub fn from_changeset(changeset: ChangeSet<A>) -> Self {
1394        let mut graph = Self::default();
1395        graph.apply_changeset(changeset);
1396        graph
1397    }
1398}
1399
1400/// The [`ChangeSet`] represents changes to a [`TxGraph`].
1401///
1402/// Since [`TxGraph`] is monotone, the "changeset" can only contain transactions to be added and
1403/// not removed.
1404///
1405/// Refer to [module-level documentation] for more.
1406///
1407/// [module-level documentation]: crate::tx_graph
1408#[derive(Debug, Clone, PartialEq)]
1409#[cfg_attr(
1410    feature = "serde",
1411    derive(serde::Deserialize, serde::Serialize),
1412    serde(bound(
1413        deserialize = "A: Ord + serde::Deserialize<'de>",
1414        serialize = "A: Ord + serde::Serialize",
1415    ))
1416)]
1417#[must_use]
1418pub struct ChangeSet<A = ()> {
1419    /// Added transactions.
1420    pub txs: BTreeSet<Arc<Transaction>>,
1421    /// Added txouts.
1422    pub txouts: BTreeMap<OutPoint, TxOut>,
1423    /// Added anchors.
1424    pub anchors: BTreeSet<(A, Txid)>,
1425    /// Added last-seen unix timestamps of transactions.
1426    pub last_seen: BTreeMap<Txid, u64>,
1427    /// Added timestamps of when a transaction is last evicted from the mempool.
1428    #[cfg_attr(feature = "serde", serde(default))]
1429    pub last_evicted: BTreeMap<Txid, u64>,
1430    /// Added first-seen unix timestamps of transactions.
1431    #[cfg_attr(feature = "serde", serde(default))]
1432    pub first_seen: BTreeMap<Txid, u64>,
1433}
1434
1435impl<A> Default for ChangeSet<A> {
1436    fn default() -> Self {
1437        Self {
1438            txs: Default::default(),
1439            txouts: Default::default(),
1440            anchors: Default::default(),
1441            first_seen: Default::default(),
1442            last_seen: Default::default(),
1443            last_evicted: Default::default(),
1444        }
1445    }
1446}
1447
1448impl<A> ChangeSet<A> {
1449    /// Iterates over all outpoints contained within [`ChangeSet`].
1450    pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
1451        self.txs
1452            .iter()
1453            .flat_map(|tx| {
1454                tx.output
1455                    .iter()
1456                    .enumerate()
1457                    .map(move |(vout, txout)| (OutPoint::new(tx.compute_txid(), vout as _), txout))
1458            })
1459            .chain(self.txouts.iter().map(|(op, txout)| (*op, txout)))
1460    }
1461
1462    /// Iterates over the heights of that the new transaction anchors in this changeset.
1463    ///
1464    /// This is useful if you want to find which heights you need to fetch data about in order to
1465    /// confirm or exclude these anchors.
1466    pub fn anchor_heights(&self) -> impl Iterator<Item = u32> + '_
1467    where
1468        A: Anchor,
1469    {
1470        let mut dedup = None;
1471        self.anchors
1472            .iter()
1473            .map(|(a, _)| a.anchor_block().height)
1474            .filter(move |height| {
1475                let duplicate = dedup == Some(*height);
1476                dedup = Some(*height);
1477                !duplicate
1478            })
1479    }
1480}
1481
1482impl<A: Ord> Merge for ChangeSet<A> {
1483    fn merge(&mut self, other: Self) {
1484        // We use `extend` instead of `BTreeMap::append` due to performance issues with `append`.
1485        // Refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420
1486        self.txs.extend(other.txs);
1487        self.txouts.extend(other.txouts);
1488        self.anchors.extend(other.anchors);
1489
1490        // first_seen timestamps should only decrease
1491        self.first_seen.extend(
1492            other
1493                .first_seen
1494                .into_iter()
1495                .filter(|(txid, update_fs)| match self.first_seen.get(txid) {
1496                    Some(existing) => update_fs < existing,
1497                    None => true,
1498                })
1499                .collect::<Vec<_>>(),
1500        );
1501
1502        // last_seen timestamps should only increase
1503        self.last_seen.extend(
1504            other
1505                .last_seen
1506                .into_iter()
1507                .filter(|(txid, update_ls)| self.last_seen.get(txid) < Some(update_ls))
1508                .collect::<Vec<_>>(),
1509        );
1510        // last_evicted timestamps should only increase
1511        self.last_evicted.extend(
1512            other
1513                .last_evicted
1514                .into_iter()
1515                .filter(|(txid, update_lm)| self.last_evicted.get(txid) < Some(update_lm))
1516                .collect::<Vec<_>>(),
1517        );
1518    }
1519
1520    fn is_empty(&self) -> bool {
1521        self.txs.is_empty()
1522            && self.txouts.is_empty()
1523            && self.anchors.is_empty()
1524            && self.first_seen.is_empty()
1525            && self.last_seen.is_empty()
1526            && self.last_evicted.is_empty()
1527    }
1528}
1529
1530impl<A: Ord> ChangeSet<A> {
1531    /// Transform the [`ChangeSet`] to have [`Anchor`]s of another type.
1532    ///
1533    /// This takes in a closure of signature `FnMut(A) -> A2` which is called for each [`Anchor`] to
1534    /// transform it.
1535    pub fn map_anchors<A2: Ord, F>(self, mut f: F) -> ChangeSet<A2>
1536    where
1537        F: FnMut(A) -> A2,
1538    {
1539        ChangeSet {
1540            txs: self.txs,
1541            txouts: self.txouts,
1542            anchors: BTreeSet::<(A2, Txid)>::from_iter(
1543                self.anchors.into_iter().map(|(a, txid)| (f(a), txid)),
1544            ),
1545            first_seen: self.first_seen,
1546            last_seen: self.last_seen,
1547            last_evicted: self.last_evicted,
1548        }
1549    }
1550}
1551
1552impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
1553    fn as_ref(&self) -> &TxGraph<A> {
1554        self
1555    }
1556}
1557
1558/// An iterator that traverses ancestors of a given root transaction.
1559///
1560/// The iterator excludes partial transactions.
1561///
1562/// Returned by the [`walk_ancestors`] method of [`TxGraph`].
1563///
1564/// [`walk_ancestors`]: TxGraph::walk_ancestors
1565pub struct TxAncestors<'g, A, F, O>
1566where
1567    F: FnMut(usize, Arc<Transaction>) -> Option<O>,
1568{
1569    graph: &'g TxGraph<A>,
1570    visited: HashSet<Txid>,
1571    queue: VecDeque<(usize, Arc<Transaction>)>,
1572    filter_map: F,
1573}
1574
1575impl<'g, A, F, O> TxAncestors<'g, A, F, O>
1576where
1577    F: FnMut(usize, Arc<Transaction>) -> Option<O>,
1578{
1579    /// Creates a `TxAncestors` that includes the starting `Transaction` when iterating.
1580    pub(crate) fn new_include_root(
1581        graph: &'g TxGraph<A>,
1582        tx: impl Into<Arc<Transaction>>,
1583        filter_map: F,
1584    ) -> Self {
1585        Self {
1586            graph,
1587            visited: Default::default(),
1588            queue: [(0, tx.into())].into(),
1589            filter_map,
1590        }
1591    }
1592
1593    /// Creates a `TxAncestors` that excludes the starting `Transaction` when iterating.
1594    pub(crate) fn new_exclude_root(
1595        graph: &'g TxGraph<A>,
1596        tx: impl Into<Arc<Transaction>>,
1597        filter_map: F,
1598    ) -> Self {
1599        let mut ancestors = Self {
1600            graph,
1601            visited: Default::default(),
1602            queue: Default::default(),
1603            filter_map,
1604        };
1605        ancestors.populate_queue(1, tx.into());
1606        ancestors
1607    }
1608
1609    /// Creates a `TxAncestors` from multiple starting `Transaction`s that includes the starting
1610    /// `Transaction`s when iterating.
1611    #[allow(unused)]
1612    pub(crate) fn from_multiple_include_root<I>(
1613        graph: &'g TxGraph<A>,
1614        txs: I,
1615        filter_map: F,
1616    ) -> Self
1617    where
1618        I: IntoIterator,
1619        I::Item: Into<Arc<Transaction>>,
1620    {
1621        Self {
1622            graph,
1623            visited: Default::default(),
1624            queue: txs.into_iter().map(|tx| (0, tx.into())).collect(),
1625            filter_map,
1626        }
1627    }
1628
1629    /// Creates a `TxAncestors` from multiple starting `Transaction`s that excludes the starting
1630    /// `Transaction`s when iterating.
1631    #[allow(unused)]
1632    pub(crate) fn from_multiple_exclude_root<I>(
1633        graph: &'g TxGraph<A>,
1634        txs: I,
1635        filter_map: F,
1636    ) -> Self
1637    where
1638        I: IntoIterator,
1639        I::Item: Into<Arc<Transaction>>,
1640    {
1641        let mut ancestors = Self {
1642            graph,
1643            visited: Default::default(),
1644            queue: Default::default(),
1645            filter_map,
1646        };
1647        for tx in txs {
1648            ancestors.populate_queue(1, tx.into());
1649        }
1650        ancestors
1651    }
1652
1653    /// Traverse all ancestors that are not filtered out by the provided closure.
1654    pub fn run_until_finished(self) {
1655        self.for_each(|_| {})
1656    }
1657
1658    fn populate_queue(&mut self, depth: usize, tx: Arc<Transaction>) {
1659        let ancestors = tx
1660            .input
1661            .iter()
1662            .map(|txin| txin.previous_output.txid)
1663            .filter(|&prev_txid| self.visited.insert(prev_txid))
1664            .filter_map(|prev_txid| self.graph.get_tx(prev_txid))
1665            .map(|tx| (depth, tx));
1666        self.queue.extend(ancestors);
1667    }
1668}
1669
1670impl<A, F, O> Iterator for TxAncestors<'_, A, F, O>
1671where
1672    F: FnMut(usize, Arc<Transaction>) -> Option<O>,
1673{
1674    type Item = O;
1675
1676    fn next(&mut self) -> Option<Self::Item> {
1677        loop {
1678            // we have exhausted all paths when queue is empty
1679            let (ancestor_depth, tx) = self.queue.pop_front()?;
1680            // ignore paths when user filters them out
1681            let item = match (self.filter_map)(ancestor_depth, tx.clone()) {
1682                Some(item) => item,
1683                None => continue,
1684            };
1685            self.populate_queue(ancestor_depth + 1, tx);
1686            return Some(item);
1687        }
1688    }
1689}
1690
1691/// An iterator that traverses transaction descendants.
1692///
1693/// Returned by the [`walk_descendants`] method of [`TxGraph`].
1694///
1695/// [`walk_descendants`]: TxGraph::walk_descendants
1696pub struct TxDescendants<'g, A, F, O>
1697where
1698    F: FnMut(usize, Txid) -> Option<O>,
1699{
1700    graph: &'g TxGraph<A>,
1701    visited: HashSet<Txid>,
1702    queue: VecDeque<(usize, Txid)>,
1703    filter_map: F,
1704}
1705
1706impl<'g, A, F, O> TxDescendants<'g, A, F, O>
1707where
1708    F: FnMut(usize, Txid) -> Option<O>,
1709{
1710    /// Creates a `TxDescendants` that includes the starting `txid` when iterating.
1711    #[allow(unused)]
1712    pub(crate) fn new_include_root(graph: &'g TxGraph<A>, txid: Txid, filter_map: F) -> Self {
1713        Self {
1714            graph,
1715            visited: Default::default(),
1716            queue: [(0, txid)].into(),
1717            filter_map,
1718        }
1719    }
1720
1721    /// Creates a `TxDescendants` that excludes the starting `txid` when iterating.
1722    pub(crate) fn new_exclude_root(graph: &'g TxGraph<A>, txid: Txid, filter_map: F) -> Self {
1723        let mut descendants = Self {
1724            graph,
1725            visited: Default::default(),
1726            queue: Default::default(),
1727            filter_map,
1728        };
1729        descendants.populate_queue(1, txid);
1730        descendants
1731    }
1732
1733    /// Creates a `TxDescendants` from multiple starting transactions that includes the starting
1734    /// `txid`s when iterating.
1735    pub(crate) fn from_multiple_include_root<I>(
1736        graph: &'g TxGraph<A>,
1737        txids: I,
1738        filter_map: F,
1739    ) -> Self
1740    where
1741        I: IntoIterator<Item = Txid>,
1742    {
1743        Self {
1744            graph,
1745            visited: Default::default(),
1746            queue: txids.into_iter().map(|txid| (0, txid)).collect(),
1747            filter_map,
1748        }
1749    }
1750
1751    /// Creates a `TxDescendants` from multiple starting transactions that excludes the starting
1752    /// `txid`s when iterating.
1753    #[allow(unused)]
1754    pub(crate) fn from_multiple_exclude_root<I>(
1755        graph: &'g TxGraph<A>,
1756        txids: I,
1757        filter_map: F,
1758    ) -> Self
1759    where
1760        I: IntoIterator<Item = Txid>,
1761    {
1762        let mut descendants = Self {
1763            graph,
1764            visited: Default::default(),
1765            queue: Default::default(),
1766            filter_map,
1767        };
1768        for txid in txids {
1769            descendants.populate_queue(1, txid);
1770        }
1771        descendants
1772    }
1773
1774    /// Traverse all descendants that are not filtered out by the provided closure.
1775    pub fn run_until_finished(self) {
1776        self.for_each(|_| {})
1777    }
1778
1779    fn populate_queue(&mut self, depth: usize, txid: Txid) {
1780        let spend_paths = self
1781            .graph
1782            .spends
1783            .range(tx_outpoint_range(txid))
1784            .flat_map(|(_, spends)| spends)
1785            .map(|&txid| (depth, txid));
1786        self.queue.extend(spend_paths);
1787    }
1788}
1789
1790impl<A, F, O> Iterator for TxDescendants<'_, A, F, O>
1791where
1792    F: FnMut(usize, Txid) -> Option<O>,
1793{
1794    type Item = O;
1795
1796    fn next(&mut self) -> Option<Self::Item> {
1797        let (op_spends, txid, item) = loop {
1798            // we have exhausted all paths when queue is empty
1799            let (op_spends, txid) = self.queue.pop_front()?;
1800            // we do not want to visit the same transaction twice
1801            if self.visited.insert(txid) {
1802                // ignore paths when user filters them out
1803                if let Some(item) = (self.filter_map)(op_spends, txid) {
1804                    break (op_spends, txid, item);
1805                }
1806            }
1807        };
1808
1809        self.populate_queue(op_spends + 1, txid);
1810        Some(item)
1811    }
1812}
1813
1814fn tx_outpoint_range(txid: Txid) -> RangeInclusive<OutPoint> {
1815    OutPoint::new(txid, u32::MIN)..=OutPoint::new(txid, u32::MAX)
1816}