1
1
use bdk_chain:: {
2
2
bitcoin:: { hashes:: Hash , Address , Amount , ScriptBuf , Txid , WScriptHash } ,
3
3
local_chain:: LocalChain ,
4
- spk_client:: { FullScanRequest , SyncRequest } ,
4
+ spk_client:: { FullScanRequest , SyncRequest , SyncResult } ,
5
5
spk_txout:: SpkTxOutIndex ,
6
- Balance , ConfirmationBlockTime , IndexedTxGraph ,
6
+ Balance , ConfirmationBlockTime , IndexedTxGraph , Indexer , Merge ,
7
7
} ;
8
8
use bdk_electrum:: BdkElectrumClient ;
9
9
use bdk_testenv:: { anyhow, bitcoincore_rpc:: RpcApi , TestEnv } ;
10
+ use core:: time:: Duration ;
10
11
use std:: collections:: { BTreeSet , HashSet } ;
11
12
use std:: str:: FromStr ;
12
13
14
+ // Batch size for `sync_with_electrum`.
15
+ const BATCH_SIZE : usize = 5 ;
16
+
13
17
fn get_balance (
14
18
recv_chain : & LocalChain ,
15
19
recv_graph : & IndexedTxGraph < ConfirmationBlockTime , SpkTxOutIndex < ( ) > > ,
@@ -22,6 +26,39 @@ fn get_balance(
22
26
Ok ( balance)
23
27
}
24
28
29
+ fn sync_with_electrum < I , Spks > (
30
+ client : & BdkElectrumClient < electrum_client:: Client > ,
31
+ spks : Spks ,
32
+ chain : & mut LocalChain ,
33
+ graph : & mut IndexedTxGraph < ConfirmationBlockTime , I > ,
34
+ ) -> anyhow:: Result < SyncResult >
35
+ where
36
+ I : Indexer ,
37
+ I :: ChangeSet : Default + Merge ,
38
+ Spks : IntoIterator < Item = ScriptBuf > ,
39
+ Spks :: IntoIter : ExactSizeIterator + Send + ' static ,
40
+ {
41
+ let mut update = client. sync (
42
+ SyncRequest :: from_chain_tip ( chain. tip ( ) ) . chain_spks ( spks) ,
43
+ BATCH_SIZE ,
44
+ true ,
45
+ ) ?;
46
+
47
+ // Update `last_seen` to be able to calculate balance for unconfirmed transactions.
48
+ let now = std:: time:: UNIX_EPOCH
49
+ . elapsed ( )
50
+ . expect ( "must get time" )
51
+ . as_secs ( ) ;
52
+ let _ = update. graph_update . update_last_seen_unconfirmed ( now) ;
53
+
54
+ let _ = chain
55
+ . apply_update ( update. chain_update . clone ( ) )
56
+ . map_err ( |err| anyhow:: anyhow!( "LocalChain update error: {:?}" , err) ) ?;
57
+ let _ = graph. apply_update ( update. graph_update . clone ( ) ) ;
58
+
59
+ Ok ( update)
60
+ }
61
+
25
62
#[ test]
26
63
pub fn test_update_tx_graph_without_keychain ( ) -> anyhow:: Result < ( ) > {
27
64
let env = TestEnv :: new ( ) ?;
@@ -60,7 +97,7 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
60
97
None ,
61
98
) ?;
62
99
env. mine_blocks ( 1 , None ) ?;
63
- env. wait_until_electrum_sees_block ( ) ?;
100
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
64
101
65
102
// use a full checkpoint linked list (since this is not what we are testing)
66
103
let cp_tip = env. make_checkpoint_tip ( ) ;
@@ -162,7 +199,7 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> {
162
199
None ,
163
200
) ?;
164
201
env. mine_blocks ( 1 , None ) ?;
165
- env. wait_until_electrum_sees_block ( ) ?;
202
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
166
203
167
204
// use a full checkpoint linked list (since this is not what we are testing)
168
205
let cp_tip = env. make_checkpoint_tip ( ) ;
@@ -204,7 +241,7 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> {
204
241
None ,
205
242
) ?;
206
243
env. mine_blocks ( 1 , None ) ?;
207
- env. wait_until_electrum_sees_block ( ) ?;
244
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
208
245
209
246
// A scan with gap limit 5 won't find the second transaction, but a scan with gap limit 6 will.
210
247
// The last active indice won't be updated in the first case but will in the second one.
@@ -238,14 +275,11 @@ pub fn test_update_tx_graph_stop_gap() -> anyhow::Result<()> {
238
275
Ok ( ( ) )
239
276
}
240
277
241
- /// Ensure that [`ElectrumExt`] can sync properly.
242
- ///
243
- /// 1. Mine 101 blocks.
244
- /// 2. Send a tx.
245
- /// 3. Mine extra block to confirm sent tx.
246
- /// 4. Check [`Balance`] to ensure tx is confirmed.
278
+ /// Ensure that [`BdkElectrumClient::sync`] can confirm previously unconfirmed transactions in both
279
+ /// reorg and no-reorg situations. After the transaction is confirmed after reorg, check if floating
280
+ /// txouts for previous outputs were inserted for transaction fee calculation.
247
281
#[ test]
248
- fn scan_detects_confirmed_tx ( ) -> anyhow:: Result < ( ) > {
282
+ fn test_sync ( ) -> anyhow:: Result < ( ) > {
249
283
const SEND_AMOUNT : Amount = Amount :: from_sat ( 10_000 ) ;
250
284
251
285
let env = TestEnv :: new ( ) ?;
@@ -271,35 +305,88 @@ fn scan_detects_confirmed_tx() -> anyhow::Result<()> {
271
305
272
306
// Mine some blocks.
273
307
env. mine_blocks ( 101 , Some ( addr_to_mine) ) ?;
308
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
274
309
275
- // Create transaction that is tracked by our receiver.
276
- env. send ( & addr_to_track, SEND_AMOUNT ) ?;
310
+ // Broadcast transaction to mempool.
311
+ let txid = env. send ( & addr_to_track, SEND_AMOUNT ) ?;
312
+ env. wait_until_electrum_sees_txid ( txid, Duration :: from_secs ( 6 ) ) ?;
277
313
278
- // Mine a block to confirm sent tx.
314
+ sync_with_electrum (
315
+ & client,
316
+ [ spk_to_track. clone ( ) ] ,
317
+ & mut recv_chain,
318
+ & mut recv_graph,
319
+ ) ?;
320
+
321
+ // Check for unconfirmed balance when transaction exists only in mempool.
322
+ assert_eq ! (
323
+ get_balance( & recv_chain, & recv_graph) ?,
324
+ Balance {
325
+ trusted_pending: SEND_AMOUNT ,
326
+ ..Balance :: default ( )
327
+ } ,
328
+ "balance must be correct" ,
329
+ ) ;
330
+
331
+ // Mine block to confirm transaction.
279
332
env. mine_blocks ( 1 , None ) ?;
333
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
280
334
281
- // Sync up to tip.
282
- env. wait_until_electrum_sees_block ( ) ?;
283
- let update = client. sync (
284
- SyncRequest :: from_chain_tip ( recv_chain. tip ( ) ) . chain_spks ( core:: iter:: once ( spk_to_track) ) ,
285
- 5 ,
286
- true ,
335
+ sync_with_electrum (
336
+ & client,
337
+ [ spk_to_track. clone ( ) ] ,
338
+ & mut recv_chain,
339
+ & mut recv_graph,
287
340
) ?;
288
341
289
- let _ = recv_chain
290
- . apply_update ( update. chain_update )
291
- . map_err ( |err| anyhow:: anyhow!( "LocalChain update error: {:?}" , err) ) ?;
292
- let _ = recv_graph. apply_update ( update. graph_update ) ;
342
+ // Check if balance is correct when transaction is confirmed.
343
+ assert_eq ! (
344
+ get_balance( & recv_chain, & recv_graph) ?,
345
+ Balance {
346
+ confirmed: SEND_AMOUNT ,
347
+ ..Balance :: default ( )
348
+ } ,
349
+ "balance must be correct" ,
350
+ ) ;
351
+
352
+ // Perform reorg on block with confirmed transaction.
353
+ env. reorg_empty_blocks ( 1 ) ?;
354
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
355
+
356
+ sync_with_electrum (
357
+ & client,
358
+ [ spk_to_track. clone ( ) ] ,
359
+ & mut recv_chain,
360
+ & mut recv_graph,
361
+ ) ?;
293
362
294
- // Check to see if tx is confirmed.
363
+ // Check if balance is correct when transaction returns to mempool.
364
+ assert_eq ! (
365
+ get_balance( & recv_chain, & recv_graph) ?,
366
+ Balance {
367
+ trusted_pending: SEND_AMOUNT ,
368
+ ..Balance :: default ( )
369
+ } ,
370
+ ) ;
371
+
372
+ // Mine block to confirm transaction again.
373
+ env. mine_blocks ( 1 , None ) ?;
374
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
375
+
376
+ sync_with_electrum ( & client, [ spk_to_track] , & mut recv_chain, & mut recv_graph) ?;
377
+
378
+ // Check if balance is correct once transaction is confirmed again.
295
379
assert_eq ! (
296
380
get_balance( & recv_chain, & recv_graph) ?,
297
381
Balance {
298
382
confirmed: SEND_AMOUNT ,
299
383
..Balance :: default ( )
300
384
} ,
385
+ "balance must be correct" ,
301
386
) ;
302
387
388
+ // Check to see if we have the floating txouts available from our transactions' previous outputs
389
+ // in order to calculate transaction fees.
303
390
for tx in recv_graph. graph ( ) . full_txs ( ) {
304
391
// Retrieve the calculated fee from `TxGraph`, which will panic if we do not have the
305
392
// floating txouts available from the transaction's previous outputs.
@@ -371,18 +458,14 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
371
458
}
372
459
373
460
// Sync up to tip.
374
- env. wait_until_electrum_sees_block ( ) ?;
375
- let update = client. sync (
376
- SyncRequest :: from_chain_tip ( recv_chain. tip ( ) ) . chain_spks ( [ spk_to_track. clone ( ) ] ) ,
377
- 5 ,
378
- false ,
461
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
462
+ let update = sync_with_electrum (
463
+ & client,
464
+ [ spk_to_track. clone ( ) ] ,
465
+ & mut recv_chain,
466
+ & mut recv_graph,
379
467
) ?;
380
468
381
- let _ = recv_chain
382
- . apply_update ( update. chain_update )
383
- . map_err ( |err| anyhow:: anyhow!( "LocalChain update error: {:?}" , err) ) ?;
384
- let _ = recv_graph. apply_update ( update. graph_update . clone ( ) ) ;
385
-
386
469
// Retain a snapshot of all anchors before reorg process.
387
470
let initial_anchors = update. graph_update . all_anchors ( ) ;
388
471
let anchors: Vec < _ > = initial_anchors. iter ( ) . cloned ( ) . collect ( ) ;
@@ -407,24 +490,21 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
407
490
for depth in 1 ..=REORG_COUNT {
408
491
env. reorg_empty_blocks ( depth) ?;
409
492
410
- env. wait_until_electrum_sees_block ( ) ?;
411
- let update = client. sync (
412
- SyncRequest :: from_chain_tip ( recv_chain. tip ( ) ) . chain_spks ( [ spk_to_track. clone ( ) ] ) ,
413
- 5 ,
414
- false ,
493
+ env. wait_until_electrum_sees_block ( Duration :: from_secs ( 6 ) ) ?;
494
+ let update = sync_with_electrum (
495
+ & client,
496
+ [ spk_to_track. clone ( ) ] ,
497
+ & mut recv_chain,
498
+ & mut recv_graph,
415
499
) ?;
416
500
417
- let _ = recv_chain
418
- . apply_update ( update. chain_update )
419
- . map_err ( |err| anyhow:: anyhow!( "LocalChain update error: {:?}" , err) ) ?;
420
-
421
501
// Check that no new anchors are added during current reorg.
422
502
assert ! ( initial_anchors. is_superset( update. graph_update. all_anchors( ) ) ) ;
423
- let _ = recv_graph. apply_update ( update. graph_update ) ;
424
503
425
504
assert_eq ! (
426
505
get_balance( & recv_chain, & recv_graph) ?,
427
506
Balance {
507
+ trusted_pending: SEND_AMOUNT * depth as u64 ,
428
508
confirmed: SEND_AMOUNT * ( REORG_COUNT - depth) as u64 ,
429
509
..Balance :: default ( )
430
510
} ,
0 commit comments