@@ -15,34 +15,19 @@ use crate::{
1515 Anchor , BlockId , ChainOracle , Indexer , Merge , TxPosInBlock ,
1616} ;
1717
18- /// The [`IndexedTxGraph`] combines a [`TxGraph`] and an [`Indexer`] implementation.
18+ /// A [`TxGraph<A>`] paired with an indexer `I`, enforcing that every insertion into the graph is
19+ /// simultaneously fed through the indexer.
1920///
20- /// It ensures that [`TxGraph`] and [`Indexer`] are updated atomically.
21+ /// This guarantees that `tx_graph` and `index` remain in sync: any transaction or floating txout
22+ /// you add to `tx_graph` has already been processed by `index`.
2123#[ derive( Debug , Clone ) ]
2224pub struct IndexedTxGraph < A , I > {
23- /// Transaction index .
25+ /// The indexer used for filtering transactions and floating txouts that we are interested in .
2426 pub index : I ,
2527 graph : TxGraph < A > ,
2628}
2729
28- impl < A , I : Default > Default for IndexedTxGraph < A , I > {
29- fn default ( ) -> Self {
30- Self {
31- graph : Default :: default ( ) ,
32- index : Default :: default ( ) ,
33- }
34- }
35- }
36-
3730impl < A , I > IndexedTxGraph < A , I > {
38- /// Construct a new [`IndexedTxGraph`] with a given `index`.
39- pub fn new ( index : I ) -> Self {
40- Self {
41- index,
42- graph : TxGraph :: default ( ) ,
43- }
44- }
45-
4631 /// Get a reference of the internal transaction graph.
4732 pub fn graph ( & self ) -> & TxGraph < A > {
4833 & self . graph
@@ -79,6 +64,87 @@ impl<A: Anchor, I: Indexer> IndexedTxGraph<A, I>
7964where
8065 I :: ChangeSet : Default + Merge ,
8166{
67+ /// Create a new, empty [`IndexedTxGraph`].
68+ ///
69+ /// The underlying `TxGraph` is initialized with `TxGraph::default()`, and the provided
70+ /// `index`er is used as‐is (since there are no existing transactions to process).
71+ pub fn new ( index : I ) -> Self {
72+ Self {
73+ index,
74+ graph : TxGraph :: default ( ) ,
75+ }
76+ }
77+
78+ /// Reconstruct an [`IndexedTxGraph`] from persisted graph + indexer state.
79+ ///
80+ /// 1. Rebuilds the `TxGraph` from `changeset.tx_graph`.
81+ /// 2. Calls your `indexer_from_changeset` closure on `changeset.indexer` to restore any state
82+ /// your indexer needs beyond its raw changeset.
83+ /// 3. Runs a full `.reindex()`, returning its `ChangeSet` to describe any additional updates
84+ /// applied.
85+ ///
86+ /// # Errors
87+ ///
88+ /// Returns `Err(E)` if `indexer_from_changeset` fails.
89+ ///
90+ /// # Examples
91+ ///
92+ /// ```rust,no_run
93+ /// use bdk_chain::IndexedTxGraph;
94+ /// # use bdk_chain::indexed_tx_graph::ChangeSet;
95+ /// # use bdk_chain::indexer::keychain_txout::{KeychainTxOutIndex, DEFAULT_LOOKAHEAD};
96+ /// # use bdk_core::BlockId;
97+ /// # use bdk_testenv::anyhow;
98+ /// # use miniscript::{Descriptor, DescriptorPublicKey};
99+ /// # use std::str::FromStr;
100+ /// # let persisted_changeset = ChangeSet::<BlockId, _>::default();
101+ /// # let persisted_desc = Some(Descriptor::<DescriptorPublicKey>::from_str("")?);
102+ /// # let persisted_change_desc = Some(Descriptor::<DescriptorPublicKey>::from_str("")?);
103+ ///
104+ /// let (graph, reindex_cs) =
105+ /// IndexedTxGraph::from_changeset(persisted_changeset, move |idx_cs| -> anyhow::Result<_> {
106+ /// // e.g. KeychainTxOutIndex needs descriptors that weren’t in its CS
107+ /// let mut idx = KeychainTxOutIndex::from_changeset(DEFAULT_LOOKAHEAD, true, idx_cs);
108+ /// if let Some(desc) = persisted_desc {
109+ /// idx.insert_descriptor("external", desc)?;
110+ /// }
111+ /// if let Some(desc) = persisted_change_desc {
112+ /// idx.insert_descriptor("internal", desc)?;
113+ /// }
114+ /// Ok(idx)
115+ /// })?;
116+ /// # Ok::<(), anyhow::Error>(())
117+ /// ```
118+ pub fn from_changeset < F , E > (
119+ changeset : ChangeSet < A , I :: ChangeSet > ,
120+ indexer_from_changeset : F ,
121+ ) -> Result < ( Self , ChangeSet < A , I :: ChangeSet > ) , E >
122+ where
123+ F : FnOnce ( I :: ChangeSet ) -> Result < I , E > ,
124+ {
125+ let graph = TxGraph :: < A > :: from_changeset ( changeset. tx_graph ) ;
126+ let index = indexer_from_changeset ( changeset. indexer ) ?;
127+ let mut out = Self { graph, index } ;
128+ let out_changeset = out. reindex ( ) ;
129+ Ok ( ( out, out_changeset) )
130+ }
131+
132+ /// Synchronizes the indexer to reflect every entry in the transaction graph.
133+ ///
134+ /// Iterates over **all** full transactions and floating outputs in `self.graph`, passing each
135+ /// into `self.index`. Any indexer-side changes produced (via `index_tx` or `index_txout`) are
136+ /// merged into a fresh `ChangeSet`, which is then returned.
137+ pub fn reindex ( & mut self ) -> ChangeSet < A , I :: ChangeSet > {
138+ let mut changeset = ChangeSet :: < A , I :: ChangeSet > :: default ( ) ;
139+ for tx in self . graph . full_txs ( ) {
140+ changeset. indexer . merge ( self . index . index_tx ( & tx) ) ;
141+ }
142+ for ( op, txout) in self . graph . floating_txouts ( ) {
143+ changeset. indexer . merge ( self . index . index_txout ( op, txout) ) ;
144+ }
145+ changeset
146+ }
147+
82148 fn index_tx_graph_changeset (
83149 & mut self ,
84150 tx_graph_changeset : & tx_graph:: ChangeSet < A > ,
@@ -443,6 +509,12 @@ impl<A, IA: Default> From<tx_graph::ChangeSet<A>> for ChangeSet<A, IA> {
443509 }
444510}
445511
512+ impl < A , IA > From < ( tx_graph:: ChangeSet < A > , IA ) > for ChangeSet < A , IA > {
513+ fn from ( ( tx_graph, indexer) : ( tx_graph:: ChangeSet < A > , IA ) ) -> Self {
514+ Self { tx_graph, indexer }
515+ }
516+ }
517+
446518#[ cfg( feature = "miniscript" ) ]
447519impl < A > From < crate :: keychain_txout:: ChangeSet > for ChangeSet < A , crate :: keychain_txout:: ChangeSet > {
448520 fn from ( indexer : crate :: keychain_txout:: ChangeSet ) -> Self {
0 commit comments