@@ -9,7 +9,7 @@ use bdk_chain::{
99 indexed_tx_graph:: { self , IndexedTxGraph } ,
1010 indexer:: keychain_txout:: KeychainTxOutIndex ,
1111 local_chain:: LocalChain ,
12- tx_graph, Balance , ChainPosition , ConfirmationBlockTime , DescriptorExt ,
12+ tx_graph, Balance , ChainPosition , ConfirmationBlockTime , DescriptorExt , Indexer ,
1313} ;
1414use bdk_testenv:: {
1515 block_id, hash,
@@ -99,6 +99,97 @@ fn insert_relevant_txs() {
9999 assert_eq ! ( graph. initial_changeset( ) , initial_changeset) ;
100100}
101101
102+ /// Ensure that [`IndexedTxGraph::batch_insert_relevant`] adds transactions that are direct
103+ /// conflicts with transactions in our graph but are not directly relevant to it.
104+ ///
105+ /// The graph contains three transactions (A, B, and conflicting_tx), with A and B being relevant
106+ /// because B spends from A. This test verifies that `conflicting_tx` is inserted into the graph
107+ /// solely because it is directly conflicting with B.
108+ #[ test]
109+ fn insert_relevant_conflicting_txs ( ) {
110+ use bdk_chain:: indexer:: keychain_txout;
111+ let ( descriptor, _) = Descriptor :: parse_descriptor ( & Secp256k1 :: signing_only ( ) , DESCRIPTORS [ 0 ] )
112+ . expect ( "must be valid" ) ;
113+ let spk_0 = descriptor. at_derivation_index ( 0 ) . unwrap ( ) . script_pubkey ( ) ;
114+ let spk_1 = descriptor. at_derivation_index ( 9 ) . unwrap ( ) . script_pubkey ( ) ;
115+
116+ let mut graph = IndexedTxGraph :: < ConfirmationBlockTime , KeychainTxOutIndex < ( ) > > :: new (
117+ KeychainTxOutIndex :: new ( 10 ) ,
118+ ) ;
119+ let _ = graph
120+ . index
121+ . insert_descriptor ( ( ) , descriptor. clone ( ) )
122+ . unwrap ( ) ;
123+
124+ let conflicting_prev_tx = Transaction {
125+ output : vec ! [ TxOut {
126+ value: Amount :: from_sat( 10_000 ) ,
127+ script_pubkey: spk_0,
128+ } ] ,
129+ ..new_tx ( 0 )
130+ } ;
131+
132+ let tx_a = Transaction {
133+ output : vec ! [ TxOut {
134+ value: Amount :: from_sat( 10_000 ) ,
135+ script_pubkey: spk_1,
136+ } ] ,
137+ ..new_tx ( 1 )
138+ } ;
139+
140+ let tx_b = Transaction {
141+ input : vec ! [
142+ TxIn {
143+ previous_output: OutPoint :: new( conflicting_prev_tx. compute_txid( ) , 0 ) ,
144+ ..Default :: default ( )
145+ } ,
146+ TxIn {
147+ previous_output: OutPoint :: new( tx_a. compute_txid( ) , 0 ) ,
148+ ..Default :: default ( )
149+ } ,
150+ ] ,
151+ ..new_tx ( 2 )
152+ } ;
153+
154+ let conflicting_tx = Transaction {
155+ input : vec ! [ TxIn {
156+ previous_output: OutPoint :: new( conflicting_prev_tx. compute_txid( ) , 0 ) ,
157+ ..Default :: default ( )
158+ } ] ,
159+ ..new_tx ( 3 )
160+ } ;
161+
162+ // We add only `tx_a`, `tx_b`, and `conflicting_tx` into the graph. `tx_a` and `tx_b` are
163+ // relevant to our graph, because `tx_b` spends from `tx_a`. `conflicting_tx` is not directly
164+ // relevant to our graph because it does not spend from `tx_a`. However, `tx_b` and
165+ // `conflicting_tx` conflict, because they both spend from the same output of
166+ // `conflicting_prev_tx`.
167+ let txs = [ tx_a, tx_b, conflicting_tx. clone ( ) ] ;
168+
169+ let changeset = graph. batch_insert_relevant ( txs. iter ( ) . cloned ( ) . map ( |tx| ( tx, None ) ) ) ;
170+
171+ // Confirm that `conflicting_tx` is added to `ChangeSet` only due to it being a conflicting
172+ // transaction, and not because it is directly relevant to our graph.
173+ assert ! ( !graph. index. is_tx_relevant( & conflicting_tx) ) ;
174+ assert ! ( graph
175+ . graph( )
176+ . direct_conflicts( & conflicting_tx)
177+ . next( )
178+ . is_some( ) ) ;
179+
180+ let expected_changeset = indexed_tx_graph:: ChangeSet {
181+ tx_graph : tx_graph:: ChangeSet :: < ConfirmationBlockTime > {
182+ txs : txs. iter ( ) . cloned ( ) . map ( Arc :: new) . collect ( ) ,
183+ ..Default :: default ( )
184+ } ,
185+ indexer : keychain_txout:: ChangeSet {
186+ last_revealed : [ ( descriptor. descriptor_id ( ) , 9_u32 ) ] . into ( ) ,
187+ } ,
188+ } ;
189+
190+ assert_eq ! ( changeset, expected_changeset) ;
191+ }
192+
102193/// Ensure consistency IndexedTxGraph list_* and balance methods. These methods lists
103194/// relevant txouts and utxos from the information fetched from a ChainOracle (here a LocalChain).
104195///
0 commit comments