@@ -15,12 +15,14 @@ use crate::{
15
15
Anchor , BlockId , ChainOracle , Indexer , Merge , TxPosInBlock ,
16
16
} ;
17
17
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.
19
20
///
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`.
21
23
#[ derive( Debug , Clone ) ]
22
24
pub struct IndexedTxGraph < A , I > {
23
- /// Transaction index .
25
+ /// The indexer used for filtering transactions and floating txouts that we are interested in .
24
26
pub index : I ,
25
27
graph : TxGraph < A > ,
26
28
}
@@ -35,14 +37,6 @@ impl<A, I: Default> Default for IndexedTxGraph<A, I> {
35
37
}
36
38
37
39
impl < 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
-
46
40
/// Get a reference of the internal transaction graph.
47
41
pub fn graph ( & self ) -> & TxGraph < A > {
48
42
& self . graph
@@ -79,6 +73,87 @@ impl<A: Anchor, I: Indexer> IndexedTxGraph<A, I>
79
73
where
80
74
I :: ChangeSet : Default + Merge ,
81
75
{
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
+
82
157
fn index_tx_graph_changeset (
83
158
& mut self ,
84
159
tx_graph_changeset : & tx_graph:: ChangeSet < A > ,
@@ -443,6 +518,12 @@ impl<A, IA: Default> From<tx_graph::ChangeSet<A>> for ChangeSet<A, IA> {
443
518
}
444
519
}
445
520
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
+
446
527
#[ cfg( feature = "miniscript" ) ]
447
528
impl < A > From < crate :: keychain_txout:: ChangeSet > for ChangeSet < A , crate :: keychain_txout:: ChangeSet > {
448
529
fn from ( indexer : crate :: keychain_txout:: ChangeSet ) -> Self {
0 commit comments