@@ -12,9 +12,6 @@ use std::sync::{Arc, Mutex};
1212/// We include a chain suffix of a certain length for the purpose of robustness.
1313const CHAIN_SUFFIX_LENGTH : u32 = 8 ;
1414
15- /// Maximum batch size for proof validation requests
16- const MAX_BATCH_SIZE : usize = 100 ;
17-
1815/// Wrapper around an [`electrum_client::ElectrumApi`] which includes an internal in-memory
1916/// transaction cache to avoid re-fetching already downloaded transactions.
2017#[ derive( Debug ) ]
@@ -262,15 +259,15 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
262259 batch_size : usize ,
263260 pending_anchors : & mut Vec < ( Txid , usize ) > ,
264261 ) -> 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 ;
267264
268- ' batch_loop : loop {
265+ loop {
269266 let spks = ( 0 ..batch_size)
270267 . map_while ( |_| spks_with_expected_txids. next ( ) )
271268 . collect :: < Vec < _ > > ( ) ;
272269 if spks. is_empty ( ) {
273- break ;
270+ return Ok ( last_active_index ) ;
274271 }
275272
276273 let spk_histories = self
@@ -279,10 +276,10 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
279276
280277 for ( ( spk_index, spk) , spk_history) in spks. into_iter ( ) . zip ( spk_histories) {
281278 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+ } ;
286283 } else {
287284 last_active_index = Some ( spk_index) ;
288285 unused_spk_count = 0 ;
@@ -509,72 +506,65 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
509506 }
510507 }
511508
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 (
524542 & txid,
525543 & header. merkle_root ,
526544 & proof,
527545 ) ;
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+ }
541547
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) ) ;
557562 }
558563 }
559564
560565 Ok ( results)
561566 }
562567
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-
578568 // Helper function which fetches the `TxOut`s of our relevant transactions' previous
579569 // transactions, which we do not have by default. This data is needed to calculate the
580570 // transaction fee.
0 commit comments