@@ -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 ;
@@ -499,72 +496,65 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
499496 }
500497 }
501498
502- // Fetch missing proofs in batches
503- for chunk in to_fetch. chunks ( MAX_BATCH_SIZE ) {
504- for & ( txid, height, hash) in chunk {
505- // Fetch the raw proof.
506- let proof = self . inner . transaction_get_merkle ( & txid, height) ?;
507-
508- // Validate against header, retrying once on stale header.
509- let mut header = {
510- let cache = self . block_header_cache . lock ( ) . unwrap ( ) ;
511- cache[ & ( height as u32 ) ]
512- } ;
513- let mut valid = electrum_client:: utils:: validate_merkle_proof (
499+ // Batch all get_merkle calls.
500+ let mut batch = electrum_client:: Batch :: default ( ) ;
501+ for & ( txid, height, _) in & to_fetch {
502+ batch. raw (
503+ "blockchain.transaction.get_merkle" . into ( ) ,
504+ vec ! [
505+ electrum_client:: Param :: String ( format!( "{:x}" , txid) ) ,
506+ electrum_client:: Param :: Usize ( height) ,
507+ ] ,
508+ ) ;
509+ }
510+ let resps = self . inner . batch_call ( & batch) ?;
511+
512+ // Validate each proof, retrying once for each stale header.
513+ for ( ( txid, height, hash) , resp) in to_fetch. into_iter ( ) . zip ( resps. into_iter ( ) ) {
514+ let proof: electrum_client:: GetMerkleRes = serde_json:: from_value ( resp) ?;
515+
516+ let mut header = {
517+ let cache = self . block_header_cache . lock ( ) . unwrap ( ) ;
518+ cache
519+ . get ( & ( height as u32 ) )
520+ . copied ( )
521+ . expect ( "header already fetched above" )
522+ } ;
523+ let mut valid =
524+ electrum_client:: utils:: validate_merkle_proof ( & txid, & header. merkle_root , & proof) ;
525+ if !valid {
526+ header = self . inner . block_header ( height) ?;
527+ self . block_header_cache
528+ . lock ( )
529+ . unwrap ( )
530+ . insert ( height as u32 , header) ;
531+ valid = electrum_client:: utils:: validate_merkle_proof (
514532 & txid,
515533 & header. merkle_root ,
516534 & proof,
517535 ) ;
518- if !valid {
519- let new_header = self . inner . block_header ( height) ?;
520- self . block_header_cache
521- . lock ( )
522- . unwrap ( )
523- . insert ( height as u32 , new_header) ;
524- header = new_header;
525- valid = electrum_client:: utils:: validate_merkle_proof (
526- & txid,
527- & header. merkle_root ,
528- & proof,
529- ) ;
530- }
536+ }
531537
532- // Build and cache the anchor if merkle proof is valid.
533- if valid {
534- let anchor = ConfirmationBlockTime {
535- confirmation_time : header. time as u64 ,
536- block_id : BlockId {
537- height : height as u32 ,
538- hash,
539- } ,
540- } ;
541- self . anchor_cache
542- . lock ( )
543- . unwrap ( )
544- . insert ( ( txid, hash) , anchor) ;
545- results. push ( ( txid, anchor) ) ;
546- }
538+ // Build and cache the anchor if merkle proof is valid.
539+ if valid {
540+ let anchor = ConfirmationBlockTime {
541+ confirmation_time : header. time as u64 ,
542+ block_id : BlockId {
543+ height : height as u32 ,
544+ hash,
545+ } ,
546+ } ;
547+ self . anchor_cache
548+ . lock ( )
549+ . unwrap ( )
550+ . insert ( ( txid, hash) , anchor) ;
551+ results. push ( ( txid, anchor) ) ;
547552 }
548553 }
549554
550555 Ok ( results)
551556 }
552557
553- /// Validate a single transaction’s Merkle proof, cache its confirmation anchor, and update.
554- #[ allow( dead_code) ]
555- fn validate_anchor_for_update (
556- & self ,
557- tx_update : & mut TxUpdate < ConfirmationBlockTime > ,
558- txid : Txid ,
559- confirmation_height : usize ,
560- ) -> Result < ( ) , Error > {
561- let anchors = self . batch_fetch_anchors ( & [ ( txid, confirmation_height) ] ) ?;
562- for ( txid, anchor) in anchors {
563- tx_update. anchors . insert ( ( anchor, txid) ) ;
564- }
565- Ok ( ( ) )
566- }
567-
568558 // Helper function which fetches the `TxOut`s of our relevant transactions' previous
569559 // transactions, which we do not have by default. This data is needed to calculate the
570560 // transaction fee.
0 commit comments