@@ -12,9 +12,6 @@ use std::sync::{Arc, Mutex};
12
12
/// We include a chain suffix of a certain length for the purpose of robustness.
13
13
const CHAIN_SUFFIX_LENGTH : u32 = 8 ;
14
14
15
- /// Maximum batch size for proof validation requests
16
- const MAX_BATCH_SIZE : usize = 100 ;
17
-
18
15
/// Wrapper around an [`electrum_client::ElectrumApi`] which includes an internal in-memory
19
16
/// transaction cache to avoid re-fetching already downloaded transactions.
20
17
#[ derive( Debug ) ]
@@ -262,15 +259,15 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
262
259
batch_size : usize ,
263
260
pending_anchors : & mut Vec < ( Txid , usize ) > ,
264
261
) -> Result < Option < u32 > , Error > {
265
- let mut unused_spk_count = 0 ;
266
- let mut last_active_index = None ;
262
+ let mut unused_spk_count = 0_usize ;
263
+ let mut last_active_index = Option :: < u32 > :: None ;
267
264
268
- ' batch_loop : loop {
265
+ loop {
269
266
let spks = ( 0 ..batch_size)
270
267
. map_while ( |_| spks_with_expected_txids. next ( ) )
271
268
. collect :: < Vec < _ > > ( ) ;
272
269
if spks. is_empty ( ) {
273
- break ;
270
+ return Ok ( last_active_index ) ;
274
271
}
275
272
276
273
let spk_histories = self
@@ -279,10 +276,10 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
279
276
280
277
for ( ( spk_index, spk) , spk_history) in spks. into_iter ( ) . zip ( spk_histories) {
281
278
if spk_history. is_empty ( ) {
282
- unused_spk_count += 1 ;
283
- if unused_spk_count >= stop_gap {
284
- break ' batch_loop ;
285
- }
279
+ match unused_spk_count . checked_add ( 1 ) {
280
+ Some ( i ) if i < stop_gap => unused_spk_count = i ,
281
+ _ => return Ok ( last_active_index ) ,
282
+ } ;
286
283
} else {
287
284
last_active_index = Some ( spk_index) ;
288
285
unused_spk_count = 0 ;
@@ -509,72 +506,65 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
509
506
}
510
507
}
511
508
512
- // Fetch missing proofs in batches
513
- for chunk in to_fetch. chunks ( MAX_BATCH_SIZE ) {
514
- for & ( txid, height, hash) in chunk {
515
- // Fetch the raw proof.
516
- let proof = self . inner . transaction_get_merkle ( & txid, height) ?;
517
-
518
- // Validate against header, retrying once on stale header.
519
- let mut header = {
520
- let cache = self . block_header_cache . lock ( ) . unwrap ( ) ;
521
- cache[ & ( height as u32 ) ]
522
- } ;
523
- let mut valid = electrum_client:: utils:: validate_merkle_proof (
509
+ // Batch all get_merkle calls.
510
+ let mut batch = electrum_client:: Batch :: default ( ) ;
511
+ for & ( txid, height, _) in & to_fetch {
512
+ batch. raw (
513
+ "blockchain.transaction.get_merkle" . into ( ) ,
514
+ vec ! [
515
+ electrum_client:: Param :: String ( format!( "{:x}" , txid) ) ,
516
+ electrum_client:: Param :: Usize ( height) ,
517
+ ] ,
518
+ ) ;
519
+ }
520
+ let resps = self . inner . batch_call ( & batch) ?;
521
+
522
+ // Validate each proof, retrying once for each stale header.
523
+ for ( ( txid, height, hash) , resp) in to_fetch. into_iter ( ) . zip ( resps. into_iter ( ) ) {
524
+ let proof: electrum_client:: GetMerkleRes = serde_json:: from_value ( resp) ?;
525
+
526
+ let mut header = {
527
+ let cache = self . block_header_cache . lock ( ) . unwrap ( ) ;
528
+ cache
529
+ . get ( & ( height as u32 ) )
530
+ . copied ( )
531
+ . expect ( "header already fetched above" )
532
+ } ;
533
+ let mut valid =
534
+ electrum_client:: utils:: validate_merkle_proof ( & txid, & header. merkle_root , & proof) ;
535
+ if !valid {
536
+ header = self . inner . block_header ( height) ?;
537
+ self . block_header_cache
538
+ . lock ( )
539
+ . unwrap ( )
540
+ . insert ( height as u32 , header) ;
541
+ valid = electrum_client:: utils:: validate_merkle_proof (
524
542
& txid,
525
543
& header. merkle_root ,
526
544
& proof,
527
545
) ;
528
- if !valid {
529
- let new_header = self . inner . block_header ( height) ?;
530
- self . block_header_cache
531
- . lock ( )
532
- . unwrap ( )
533
- . insert ( height as u32 , new_header) ;
534
- header = new_header;
535
- valid = electrum_client:: utils:: validate_merkle_proof (
536
- & txid,
537
- & header. merkle_root ,
538
- & proof,
539
- ) ;
540
- }
546
+ }
541
547
542
- // Build and cache the anchor if merkle proof is valid.
543
- if valid {
544
- let anchor = ConfirmationBlockTime {
545
- confirmation_time : header. time as u64 ,
546
- block_id : BlockId {
547
- height : height as u32 ,
548
- hash,
549
- } ,
550
- } ;
551
- self . anchor_cache
552
- . lock ( )
553
- . unwrap ( )
554
- . insert ( ( txid, hash) , anchor) ;
555
- results. push ( ( txid, anchor) ) ;
556
- }
548
+ // Build and cache the anchor if merkle proof is valid.
549
+ if valid {
550
+ let anchor = ConfirmationBlockTime {
551
+ confirmation_time : header. time as u64 ,
552
+ block_id : BlockId {
553
+ height : height as u32 ,
554
+ hash,
555
+ } ,
556
+ } ;
557
+ self . anchor_cache
558
+ . lock ( )
559
+ . unwrap ( )
560
+ . insert ( ( txid, hash) , anchor) ;
561
+ results. push ( ( txid, anchor) ) ;
557
562
}
558
563
}
559
564
560
565
Ok ( results)
561
566
}
562
567
563
- /// Validate a single transaction’s Merkle proof, cache its confirmation anchor, and update.
564
- #[ allow( dead_code) ]
565
- fn validate_anchor_for_update (
566
- & self ,
567
- tx_update : & mut TxUpdate < ConfirmationBlockTime > ,
568
- txid : Txid ,
569
- confirmation_height : usize ,
570
- ) -> Result < ( ) , Error > {
571
- let anchors = self . batch_fetch_anchors ( & [ ( txid, confirmation_height) ] ) ?;
572
- for ( txid, anchor) in anchors {
573
- tx_update. anchors . insert ( ( anchor, txid) ) ;
574
- }
575
- Ok ( ( ) )
576
- }
577
-
578
568
// Helper function which fetches the `TxOut`s of our relevant transactions' previous
579
569
// transactions, which we do not have by default. This data is needed to calculate the
580
570
// transaction fee.
0 commit comments