@@ -15,12 +15,14 @@ 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}
@@ -35,14 +37,6 @@ impl<A, I: Default> Default for IndexedTxGraph<A, I> {
3537}
3638
3739impl < 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-
4640 /// Get a reference of the internal transaction graph.
4741 pub fn graph ( & self ) -> & TxGraph < A > {
4842 & self . graph
@@ -79,6 +73,87 @@ impl<A: Anchor, I: Indexer> IndexedTxGraph<A, I>
7973where
8074 I :: ChangeSet : Default + Merge ,
8175{
76+ /// Create a new, empty [`IndexedTxGraph`].
77+ ///
78+ /// The underlying `TxGraph` is initialized with `TxGraph::default()`, and the provided
79+ /// `index`er is used as‐is (since there are no existing transactions to process).
80+ pub fn new ( index : I ) -> Self {
81+ Self {
82+ index,
83+ graph : TxGraph :: default ( ) ,
84+ }
85+ }
86+
87+ /// Reconstruct an [`IndexedTxGraph`] from persisted graph + indexer state.
88+ ///
89+ /// 1. Rebuilds the `TxGraph` from `changeset.tx_graph`.
90+ /// 2. Calls your `indexer_from_changeset` closure on `changeset.indexer` to restore any state
91+ /// your indexer needs beyond its raw changeset.
92+ /// 3. Runs a full `.reindex()`, returning its `ChangeSet` to describe any additional updates
93+ /// applied.
94+ ///
95+ /// # Errors
96+ ///
97+ /// Returns `Err(E)` if `indexer_from_changeset` fails.
98+ ///
99+ /// # Examples
100+ ///
101+ /// ```rust,no_run
102+ /// use bdk_chain::IndexedTxGraph;
103+ /// # use bdk_chain::indexed_tx_graph::ChangeSet;
104+ /// # use bdk_chain::indexer::keychain_txout::{KeychainTxOutIndex, DEFAULT_LOOKAHEAD};
105+ /// # use bdk_core::BlockId;
106+ /// # use bdk_testenv::anyhow;
107+ /// # use miniscript::{Descriptor, DescriptorPublicKey};
108+ /// # use std::str::FromStr;
109+ /// # let persisted_changeset = ChangeSet::<BlockId, _>::default();
110+ /// # let persisted_desc = Some(Descriptor::<DescriptorPublicKey>::from_str("")?);
111+ /// # let persisted_change_desc = Some(Descriptor::<DescriptorPublicKey>::from_str("")?);
112+ ///
113+ /// let (graph, reindex_cs) =
114+ /// IndexedTxGraph::from_changeset(persisted_changeset, move |idx_cs| -> anyhow::Result<_> {
115+ /// // e.g. KeychainTxOutIndex needs descriptors that weren’t in its CS
116+ /// let mut idx = KeychainTxOutIndex::from_changeset(DEFAULT_LOOKAHEAD, true, idx_cs);
117+ /// if let Some(desc) = persisted_desc {
118+ /// idx.insert_descriptor("external", desc)?;
119+ /// }
120+ /// if let Some(desc) = persisted_change_desc {
121+ /// idx.insert_descriptor("internal", desc)?;
122+ /// }
123+ /// Ok(idx)
124+ /// })?;
125+ /// # Ok::<(), anyhow::Error>(())
126+ /// ```
127+ pub fn from_changeset < F , E > (
128+ changeset : ChangeSet < A , I :: ChangeSet > ,
129+ indexer_from_changeset : F ,
130+ ) -> Result < ( Self , ChangeSet < A , I :: ChangeSet > ) , E >
131+ where
132+ F : FnOnce ( I :: ChangeSet ) -> Result < I , E > ,
133+ {
134+ let graph = TxGraph :: < A > :: from_changeset ( changeset. tx_graph ) ;
135+ let index = indexer_from_changeset ( changeset. indexer ) ?;
136+ let mut out = Self { graph, index } ;
137+ let out_changeset = out. reindex ( ) ;
138+ Ok ( ( out, out_changeset) )
139+ }
140+
141+ /// Synchronizes the indexer to reflect every entry in the transaction graph.
142+ ///
143+ /// Iterates over **all** full transactions and floating outputs in `self.graph`, passing each
144+ /// into `self.index`. Any indexer-side changes produced (via `index_tx` or `index_txout`) are
145+ /// merged into a fresh `ChangeSet`, which is then returned.
146+ pub fn reindex ( & mut self ) -> ChangeSet < A , I :: ChangeSet > {
147+ let mut changeset = ChangeSet :: < A , I :: ChangeSet > :: default ( ) ;
148+ for tx in self . graph . full_txs ( ) {
149+ changeset. indexer . merge ( self . index . index_tx ( & tx) ) ;
150+ }
151+ for ( op, txout) in self . graph . floating_txouts ( ) {
152+ changeset. indexer . merge ( self . index . index_txout ( op, txout) ) ;
153+ }
154+ changeset
155+ }
156+
82157 fn index_tx_graph_changeset (
83158 & mut self ,
84159 tx_graph_changeset : & tx_graph:: ChangeSet < A > ,
@@ -443,6 +518,12 @@ impl<A, IA: Default> From<tx_graph::ChangeSet<A>> for ChangeSet<A, IA> {
443518 }
444519}
445520
521+ impl < A , IA > From < ( tx_graph:: ChangeSet < A > , IA ) > for ChangeSet < A , IA > {
522+ fn from ( ( tx_graph, indexer) : ( tx_graph:: ChangeSet < A > , IA ) ) -> Self {
523+ Self { tx_graph, indexer }
524+ }
525+ }
526+
446527#[ cfg( feature = "miniscript" ) ]
447528impl < A > From < crate :: keychain_txout:: ChangeSet > for ChangeSet < A , crate :: keychain_txout:: ChangeSet > {
448529 fn from ( indexer : crate :: keychain_txout:: ChangeSet ) -> Self {
0 commit comments