@@ -3,11 +3,13 @@ use bdk_chain::{BlockId, CheckPoint, ConfirmationBlockTime};
33use bdk_wallet:: event:: WalletEvent ;
44use bdk_wallet:: test_utils:: { get_test_wpkh_and_change_desc, new_wallet_and_funding_update} ;
55use bdk_wallet:: { SignOptions , Update } ;
6+ use bitcoin:: block:: Header ;
67use bitcoin:: hashes:: Hash ;
7- use bitcoin:: { Address , Amount , BlockHash , FeeRate } ;
8+ use bitcoin:: { Address , Amount , Block , BlockHash , FeeRate , Transaction , TxMerkleNode } ;
89use core:: str:: FromStr ;
910use std:: sync:: Arc ;
1011
12+ /// apply_update_events tests.
1113#[ test]
1214fn test_new_confirmed_tx_event ( ) {
1315 let ( desc, change_desc) = get_test_wpkh_and_change_desc ( ) ;
@@ -28,9 +30,8 @@ fn test_new_confirmed_tx_event() {
2830 ) ;
2931 assert ! ( matches!( & events[ 1 ] , WalletEvent :: TxConfirmed { tx, ..} if tx. output. len( ) == 1 ) ) ;
3032 assert ! (
31- matches!( events[ 2 ] , WalletEvent :: TxConfirmed { block_time, ..} if block_time. block_id. height == 2000 )
33+ matches!( & events[ 2 ] , WalletEvent :: TxConfirmed { tx , block_time, ..} if block_time. block_id. height == 2000 && tx . output . len ( ) == 2 )
3234 ) ;
33- assert ! ( matches!( & events[ 2 ] , WalletEvent :: TxConfirmed { tx, ..} if tx. output. len( ) == 2 ) ) ;
3435}
3536
3637#[ test]
@@ -88,7 +89,6 @@ fn test_tx_replaced_event() {
8889 update. tx_update . seen_ats = [ ( orig_txid, 210 ) ] . into ( ) ;
8990 let events = wallet. apply_update_events ( update) . unwrap ( ) ;
9091 assert_eq ! ( events. len( ) , 1 ) ;
91- assert ! ( matches!( events[ 0 ] , WalletEvent :: TxUnconfirmed { .. } ) ) ;
9292 assert ! (
9393 matches!( & events[ 0 ] , WalletEvent :: TxUnconfirmed { tx, ..} if tx. compute_txid( ) == orig_txid)
9494 ) ;
@@ -110,9 +110,8 @@ fn test_tx_replaced_event() {
110110 let events = wallet. apply_update_events ( update) . unwrap ( ) ;
111111 assert_eq ! ( events. len( ) , 2 ) ;
112112 assert ! ( matches!( events[ 0 ] , WalletEvent :: TxUnconfirmed { txid, .. } if txid == rbf_txid) ) ;
113- assert ! ( matches!( events[ 1 ] , WalletEvent :: TxReplaced { txid, ..} if txid == orig_txid) ) ;
114113 assert ! (
115- matches!( & events[ 1 ] , WalletEvent :: TxReplaced { conflicts, ..} if conflicts. len( ) == 1 &&
114+ matches!( & events[ 1 ] , WalletEvent :: TxReplaced { txid , conflicts, ..} if * txid == orig_txid && conflicts. len( ) == 1 &&
116115 conflicts. contains( & ( 0 , rbf_txid) ) )
117116 ) ;
118117}
@@ -143,7 +142,6 @@ fn test_tx_confirmed_event() {
143142 update. tx_update . seen_ats = [ ( new_txid, 210 ) ] . into ( ) ;
144143 let events = wallet. apply_update_events ( update) . unwrap ( ) ;
145144 assert_eq ! ( events. len( ) , 1 ) ;
146- assert ! ( matches!( events[ 0 ] , WalletEvent :: TxUnconfirmed { .. } ) ) ;
147145 assert ! (
148146 matches!( & events[ 0 ] , WalletEvent :: TxUnconfirmed { tx, ..} if tx. compute_txid( ) == new_txid)
149147 ) ;
@@ -201,7 +199,6 @@ fn test_tx_confirmed_new_block_event() {
201199 update. tx_update . seen_ats = [ ( new_txid, 210 ) ] . into ( ) ;
202200 let events = wallet. apply_update_events ( update) . unwrap ( ) ;
203201 assert_eq ! ( events. len( ) , 1 ) ;
204- assert ! ( matches!( events[ 0 ] , WalletEvent :: TxUnconfirmed { .. } ) ) ;
205202 assert ! (
206203 matches!( & events[ 0 ] , WalletEvent :: TxUnconfirmed { tx, ..} if tx. compute_txid( ) == new_txid)
207204 ) ;
@@ -286,7 +283,6 @@ fn test_tx_dropped_event() {
286283 update. tx_update . seen_ats = [ ( new_txid, 210 ) ] . into ( ) ;
287284 let events = wallet. apply_update_events ( update) . unwrap ( ) ;
288285 assert_eq ! ( events. len( ) , 1 ) ;
289- assert ! ( matches!( events[ 0 ] , WalletEvent :: TxUnconfirmed { .. } ) ) ;
290286 assert ! (
291287 matches!( & events[ 0 ] , WalletEvent :: TxUnconfirmed { tx, ..} if tx. compute_txid( ) == new_txid)
292288 ) ;
@@ -299,3 +295,178 @@ fn test_tx_dropped_event() {
299295 assert_eq ! ( events. len( ) , 1 ) ;
300296 assert ! ( matches!( events[ 0 ] , WalletEvent :: TxDropped { txid, .. } if txid == new_txid) ) ;
301297}
298+
299+ // apply_block_events tests.
300+
301+ fn test_block ( prev_blockhash : BlockHash , time : u32 , txdata : Vec < Transaction > ) -> Block {
302+ Block {
303+ header : Header {
304+ version : Default :: default ( ) ,
305+ prev_blockhash,
306+ merkle_root : TxMerkleNode :: all_zeros ( ) ,
307+ time,
308+ bits : Default :: default ( ) ,
309+ nonce : time,
310+ } ,
311+ txdata,
312+ }
313+ }
314+
315+ #[ test]
316+ fn test_apply_block_new_confirmed_tx_event ( ) {
317+ let ( desc, change_desc) = get_test_wpkh_and_change_desc ( ) ;
318+ let ( mut wallet, _, update) = new_wallet_and_funding_update ( desc, Some ( change_desc) ) ;
319+
320+ let genesis = BlockId {
321+ height : 0 ,
322+ hash : wallet. local_chain ( ) . genesis_hash ( ) ,
323+ } ;
324+ // apply empty block
325+ let block1 = test_block ( genesis. hash , 1000 , vec ! [ ] ) ;
326+ let events = wallet. apply_block_events ( & block1, 1 ) . unwrap ( ) ;
327+ assert_eq ! ( events. len( ) , 1 ) ;
328+
329+ // apply funding block
330+ let block2 = test_block (
331+ block1. block_hash ( ) ,
332+ 2000 ,
333+ update. tx_update . txs [ ..1 ]
334+ . iter ( )
335+ . map ( |tx| ( * * tx) . clone ( ) )
336+ . collect ( ) ,
337+ ) ;
338+ let events = wallet. apply_block_events ( & block2, 2 ) . unwrap ( ) ;
339+ assert_eq ! ( events. len( ) , 2 ) ;
340+ let new_tip2 = wallet. local_chain ( ) . tip ( ) . block_id ( ) ;
341+ assert ! (
342+ matches!( events[ 0 ] , WalletEvent :: ChainTipChanged { old_tip, new_tip } if old_tip == ( 1 , block1. block_hash( ) ) . into( ) && new_tip == new_tip2)
343+ ) ;
344+ assert ! (
345+ matches!( & events[ 1 ] , WalletEvent :: TxConfirmed { tx, block_time, ..} if block_time. block_id. height == 2 && tx. output. len( ) == 1 )
346+ ) ;
347+
348+ // apply empty block
349+ let block3 = test_block ( block2. block_hash ( ) , 3000 , vec ! [ ] ) ;
350+ let events = wallet. apply_block_events ( & block3, 3 ) . unwrap ( ) ;
351+ assert_eq ! ( events. len( ) , 1 ) ;
352+
353+ // apply spending block
354+ let block4 = test_block (
355+ block3. block_hash ( ) ,
356+ 4000 ,
357+ update. tx_update . txs [ 1 ..]
358+ . iter ( )
359+ . map ( |tx| ( * * tx) . clone ( ) )
360+ . collect ( ) ,
361+ ) ;
362+ let events = wallet. apply_block_events ( & block4, 4 ) . unwrap ( ) ;
363+ let new_tip3 = wallet. local_chain ( ) . tip ( ) . block_id ( ) ;
364+ assert_eq ! ( events. len( ) , 2 ) ;
365+ assert ! (
366+ matches!( events[ 0 ] , WalletEvent :: ChainTipChanged { old_tip, new_tip } if old_tip == ( 3 , block3. block_hash( ) ) . into( ) && new_tip == new_tip3)
367+ ) ;
368+ assert ! (
369+ matches!( & events[ 1 ] , WalletEvent :: TxConfirmed { tx, block_time, ..} if block_time. block_id. height == 4 && tx. output. len( ) == 2 )
370+ ) ;
371+ }
372+
373+ #[ test]
374+ fn test_apply_block_tx_unconfirmed_event ( ) {
375+ let ( desc, change_desc) = get_test_wpkh_and_change_desc ( ) ;
376+ let ( mut wallet, _, update) = new_wallet_and_funding_update ( desc, Some ( change_desc) ) ;
377+ // apply funding block
378+ let genesis = BlockId {
379+ height : 0 ,
380+ hash : wallet. local_chain ( ) . genesis_hash ( ) ,
381+ } ;
382+ let block1 = test_block (
383+ genesis. hash ,
384+ 1000 ,
385+ update. tx_update . txs [ ..1 ]
386+ . iter ( )
387+ . map ( |tx| ( * * tx) . clone ( ) )
388+ . collect ( ) ,
389+ ) ;
390+ let events = wallet. apply_block_events ( & block1, 1 ) . unwrap ( ) ;
391+ assert_eq ! ( events. len( ) , 2 ) ;
392+
393+ // apply spending block
394+ let block2 = test_block (
395+ block1. block_hash ( ) ,
396+ 2000 ,
397+ update. tx_update . txs [ 1 ..]
398+ . iter ( )
399+ . map ( |tx| ( * * tx) . clone ( ) )
400+ . collect ( ) ,
401+ ) ;
402+ let events = wallet. apply_block_events ( & block2, 2 ) . unwrap ( ) ;
403+ assert_eq ! ( events. len( ) , 2 ) ;
404+ let new_tip2 = wallet. local_chain ( ) . tip ( ) . block_id ( ) ;
405+ assert ! (
406+ matches!( events[ 0 ] , WalletEvent :: ChainTipChanged { old_tip, new_tip } if old_tip == ( 1 , block1. block_hash( ) ) . into( ) && new_tip == new_tip2)
407+ ) ;
408+ assert ! (
409+ matches!( & events[ 1 ] , WalletEvent :: TxConfirmed { block_time, tx, ..} if block_time. block_id. height == 2 && tx. output. len( ) == 2 )
410+ ) ;
411+
412+ // apply reorg of spending block without previously confirmed tx
413+ let reorg_block2 = test_block ( block1. block_hash ( ) , 2100 , vec ! [ ] ) ;
414+ let events = wallet. apply_block_events ( & reorg_block2, 2 ) . unwrap ( ) ;
415+ assert_eq ! ( events. len( ) , 2 ) ;
416+ assert ! (
417+ matches!( events[ 0 ] , WalletEvent :: ChainTipChanged { old_tip, new_tip } if old_tip ==
418+ ( 2 , block2. block_hash( ) ) . into( ) && new_tip == ( 2 , reorg_block2. block_hash( ) ) . into( ) )
419+ ) ;
420+ assert ! (
421+ matches!( & events[ 1 ] , WalletEvent :: TxUnconfirmed { tx, old_block_time, ..} if
422+ tx. output. len( ) == 2 && old_block_time. is_some( ) )
423+ ) ;
424+ }
425+
426+ #[ test]
427+ fn test_apply_block_tx_confirmed_new_block_event ( ) {
428+ let ( desc, change_desc) = get_test_wpkh_and_change_desc ( ) ;
429+ let ( mut wallet, _, update) = new_wallet_and_funding_update ( desc, Some ( change_desc) ) ;
430+ // apply funding block
431+ let genesis = BlockId {
432+ height : 0 ,
433+ hash : wallet. local_chain ( ) . genesis_hash ( ) ,
434+ } ;
435+ let block1 = test_block (
436+ genesis. hash ,
437+ 1000 ,
438+ update. tx_update . txs [ ..1 ]
439+ . iter ( )
440+ . map ( |tx| ( * * tx) . clone ( ) )
441+ . collect ( ) ,
442+ ) ;
443+ let events = wallet. apply_block_events ( & block1, 1 ) . unwrap ( ) ;
444+ assert_eq ! ( events. len( ) , 2 ) ;
445+
446+ // apply spending block
447+ let spending_tx: Transaction = ( * update. tx_update . txs [ 1 ] . clone ( ) ) . clone ( ) ;
448+ let block2 = test_block ( block1. block_hash ( ) , 2000 , vec ! [ spending_tx. clone( ) ] ) ;
449+ let events = wallet. apply_block_events ( & block2, 2 ) . unwrap ( ) ;
450+ assert_eq ! ( events. len( ) , 2 ) ;
451+ let new_tip2 = wallet. local_chain ( ) . tip ( ) . block_id ( ) ;
452+ assert ! (
453+ matches!( events[ 0 ] , WalletEvent :: ChainTipChanged { old_tip, new_tip } if old_tip == ( 1 , block1. block_hash( ) ) . into( ) && new_tip == new_tip2)
454+ ) ;
455+ assert ! (
456+ matches!( events[ 1 ] , WalletEvent :: TxConfirmed { txid, block_time, old_block_time, .. } if
457+ txid == spending_tx. compute_txid( ) && block_time. block_id == ( 2 , block2. block_hash( ) ) . into( ) && old_block_time. is_none( ) )
458+ ) ;
459+
460+ // apply reorg of spending block including the original spending tx
461+ let reorg_block2 = test_block ( block1. block_hash ( ) , 2100 , vec ! [ spending_tx. clone( ) ] ) ;
462+ let events = wallet. apply_block_events ( & reorg_block2, 2 ) . unwrap ( ) ;
463+ assert_eq ! ( events. len( ) , 2 ) ;
464+ assert ! (
465+ matches!( events[ 0 ] , WalletEvent :: ChainTipChanged { old_tip, new_tip } if old_tip ==
466+ ( 2 , block2. block_hash( ) ) . into( ) && new_tip == ( 2 , reorg_block2. block_hash( ) ) . into( ) )
467+ ) ;
468+ assert ! (
469+ matches!( events[ 1 ] , WalletEvent :: TxConfirmed { txid, block_time, old_block_time, .. } if
470+ txid == spending_tx. compute_txid( ) && block_time. block_id == ( 2 , reorg_block2. block_hash( ) ) . into( ) && old_block_time. is_some( ) )
471+ ) ;
472+ }
0 commit comments