@@ -35,7 +35,7 @@ use bdk_sp_oracles::{
3535 TrustedPeer , UnboundedReceiver , Warning ,
3636 } ,
3737 filters:: kyoto:: { FilterEvent , FilterSubscriber } ,
38- frigate:: { FrigateClient , History , SubscribeRequest } ,
38+ frigate:: { FrigateClient , History , SubscribeRequest , UnsubscribeRequest } ,
3939 tweaks:: blindbit:: { BlindbitSubscriber , TweakEvent } ,
4040} ;
4141use bdk_sp_wallet:: {
@@ -583,7 +583,11 @@ async fn main() -> anyhow::Result<()> {
583583 // We are doing a one time scanning only. So instead of calling `blockchain.scripthash.subscribe` on each script from the wallet,
584584 // we just subscribe and read the scanning result from the stream. On each result received we update the wallet state and once scanning progress reaches 1.0 (100%) we stop.
585585
586- let mut client = FrigateClient :: connect ( & rpc_args. url ) . await . unwrap ( ) ;
586+ let mut client = FrigateClient :: connect ( & rpc_args. url )
587+ . await
588+ . unwrap ( )
589+ . with_timeout ( tokio:: time:: Duration :: from_secs ( 600 ) ) ;
590+
587591 let labels = wallet
588592 . indexer ( )
589593 . index ( )
@@ -597,88 +601,120 @@ async fn main() -> anyhow::Result<()> {
597601 None
598602 } ;
599603
600- // send a subscribe request
601604 let subscribe_params = SubscribeRequest {
602605 scan_priv_key : * wallet. indexer ( ) . scan_sk ( ) ,
603606 spend_pub_key : * wallet. indexer ( ) . spend_pk ( ) ,
604607 start_height : start,
605608 labels,
606609 } ;
607610
608- client. version ( ) . await . unwrap ( ) ;
609- client. subscribe ( & subscribe_params) . await . unwrap ( ) ;
611+ // Attempt to subscribe; any timeout will trigger unsubscribe automatically.
612+ match client. subscribe_with_timeout ( & subscribe_params) . await {
613+ Ok ( Some ( ( histories, progress) ) ) => {
614+ tracing:: info!(
615+ "Initial subscription result: {} histories, progress {}" ,
616+ histories. len( ) ,
617+ progress
618+ ) ;
619+ }
620+ Ok ( None ) => {
621+ tracing:: info!( "Subscription acknowledged, awaiting notifications" ) ;
622+ }
623+ Err ( e) => {
624+ tracing:: error!( "Subscribe failed: {}" , e) ;
625+ return Err ( e. into ( ) ) ;
626+ }
627+ }
610628
611629 tracing:: info!( "Starting frigate scanning loop..." ) ;
612630 loop {
613- let subscribe_result = client. read_from_stream ( 4096 ) . await . unwrap ( ) ;
614-
615- if subscribe_result[ "params" ] . is_object ( ) {
616- let histories: Vec < History > =
617- serde_json:: from_value ( subscribe_result[ "params" ] [ "history" ] . clone ( ) ) ?;
618- let progress = subscribe_result[ "params" ] [ "progress" ]
619- . as_f64 ( )
620- . unwrap_or ( 0.0 ) as f32 ;
621-
622- // Group by block height, then iterate over by fetching block details and apply
623- let mut secrets_by_height: HashMap < u32 , HashMap < Txid , PublicKey > > =
624- HashMap :: new ( ) ;
625-
626- tracing:: debug!( "Received history {:#?}" , histories) ;
627-
628- histories. iter ( ) . for_each ( |h| {
629- secrets_by_height
630- . entry ( h. height )
631- . and_modify ( |v| {
632- v. insert ( h. tx_hash , h. tweak_key ) ;
633- } )
634- . or_insert ( HashMap :: from ( [ ( h. tx_hash , h. tweak_key ) ] ) ) ;
635- } ) ;
636-
637- // Filter when the height is 0, because that would mean mempool transaction
638- for secret in secrets_by_height. into_iter ( ) . filter ( |v| v. 0 > 0 ) {
639- // Since frigate doesn't provide a blockchain.getblock we will mimick that here
640- // By constructing a block from the block header and the list of transactions
641- // received from the scan request
642- let mut raw_blk = client. get_block_header ( secret. 0 ) . await . unwrap ( ) ;
643- raw_blk. push_str ( "00" ) ;
644-
645- // Push dummy coinbase
646- let dummy_coinbase = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b03951a0604f15ccf5609013803062b9b5a0100072f425443432f20000000000000000000" ;
647- let coinbase: Transaction =
648- deserialize ( & Vec :: < u8 > :: from_hex ( dummy_coinbase) . unwrap ( ) ) . unwrap ( ) ;
649- let mut block: Block =
650- deserialize ( & Vec :: < u8 > :: from_hex ( & raw_blk) . unwrap ( ) ) . unwrap ( ) ;
651-
652- let mut blockhash = BlockHash :: all_zeros ( ) ;
653-
654- let mut txs: Vec < Transaction > = vec ! [ coinbase] ;
655- for key in secret. 1 . keys ( ) {
656- let tx_result = client. get_transaction ( key. to_string ( ) ) . await . unwrap ( ) ;
657- let tx: Transaction =
658- deserialize ( & Vec :: < u8 > :: from_hex ( & tx_result. 1 ) . unwrap ( ) ) . unwrap ( ) ;
659- txs. push ( tx) ;
660-
661- blockhash = BlockHash :: from_str ( & tx_result. 0 ) ?;
662- }
631+ match client. read_from_stream ( 4096 ) . await {
632+ Ok ( subscribe_result) => {
633+ if subscribe_result[ "params" ] . is_object ( ) {
634+ let histories: Vec < History > = serde_json:: from_value (
635+ subscribe_result[ "params" ] [ "history" ] . clone ( ) ,
636+ ) ?;
637+ let progress = subscribe_result[ "params" ] [ "progress" ]
638+ . as_f64 ( )
639+ . unwrap_or ( 0.0 ) as f32 ;
640+
641+ let mut secrets_by_height: HashMap < u32 , HashMap < Txid , PublicKey > > =
642+ HashMap :: new ( ) ;
643+
644+ tracing:: debug!( "Received history {:#?}" , histories) ;
645+
646+ histories. iter ( ) . for_each ( |h| {
647+ secrets_by_height
648+ . entry ( h. height )
649+ . and_modify ( |v| {
650+ v. insert ( h. tx_hash , h. tweak_key ) ;
651+ } )
652+ . or_insert ( HashMap :: from ( [ ( h. tx_hash , h. tweak_key ) ] ) ) ;
653+ } ) ;
654+
655+ // Filter when the height is 0, because that would mean mempool transaction
656+ for secret in secrets_by_height. into_iter ( ) . filter ( |v| v. 0 > 0 ) {
657+ // Since frigate doesn't provide a blockchain.getblock we will mimick that here
658+ // By constructing a block from the block header and the list of transactions
659+ // received from the scan request
660+ let mut raw_blk = client. get_block_header ( secret. 0 ) . await . unwrap ( ) ;
661+ raw_blk. push_str ( "00" ) ;
662+
663+ // Push dummy coinbase
664+ let dummy_coinbase = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1b03951a0604f15ccf5609013803062b9b5a0100072f425443432f20000000000000000000" ;
665+ let coinbase: Transaction =
666+ deserialize ( & Vec :: < u8 > :: from_hex ( dummy_coinbase) . unwrap ( ) )
667+ . unwrap ( ) ;
668+ let mut block: Block =
669+ deserialize ( & Vec :: < u8 > :: from_hex ( & raw_blk) . unwrap ( ) ) . unwrap ( ) ;
670+
671+ let mut blockhash = BlockHash :: all_zeros ( ) ;
672+
673+ let mut txs: Vec < Transaction > = vec ! [ coinbase] ;
674+ for key in secret. 1 . keys ( ) {
675+ let tx_result =
676+ client. get_transaction ( key. to_string ( ) ) . await . unwrap ( ) ;
677+ let tx: Transaction =
678+ deserialize ( & Vec :: < u8 > :: from_hex ( & tx_result. 1 ) . unwrap ( ) )
679+ . unwrap ( ) ;
680+ txs. push ( tx) ;
681+
682+ blockhash = BlockHash :: from_str ( & tx_result. 0 ) . unwrap ( ) ;
683+ }
663684
664- block. txdata = txs;
665- tracing:: debug!( "Final block {:?}" , block) ;
666- wallet. apply_block_relevant ( & block, secret. 1 , secret. 0 ) ;
685+ block. txdata = txs;
686+ tracing:: debug!( "Final block {:?}" , block) ;
687+ wallet. apply_block_relevant ( & block, secret. 1 , secret. 0 ) ;
667688
668- tracing:: debug!( "Checkpoint hash {blockhash:?}" ) ;
669- let checkpoint = wallet. chain ( ) . tip ( ) . insert ( BlockId {
670- height : secret. 0 ,
671- hash : blockhash,
672- } ) ;
673- wallet. update_chain ( checkpoint) ;
674- }
689+ tracing:: debug!( "Checkpoint hash {blockhash:?}" ) ;
690+ let checkpoint = wallet. chain ( ) . tip ( ) . insert ( BlockId {
691+ height : secret. 0 ,
692+ hash : blockhash,
693+ } ) ;
694+ wallet. update_chain ( checkpoint) ;
695+ }
675696
676- tracing:: info!( "Progress {progress}" ) ;
677- // Check the progress
678- if progress >= 1.0 {
679- tracing:: info!( "Scanning completed" ) ;
697+ tracing:: info!( "Progress {progress}" ) ;
698+ // Check the progress
699+ if progress >= 1.0 {
700+ tracing:: info!( "Scanning completed" ) ;
701+ break ;
702+ }
703+ }
704+ }
705+ Err ( e) if e. to_string ( ) . contains ( "timed out" ) => {
706+ tracing:: warn!( "read_from_stream timeout, exiting scan" ) ;
707+ let unsubscribe_request = UnsubscribeRequest {
708+ scan_privkey : * wallet. indexer ( ) . scan_sk ( ) ,
709+ spend_pubkey : * wallet. indexer ( ) . spend_pk ( ) ,
710+ } ;
711+ let _ = client. unsubscribe ( & unsubscribe_request) . await ;
680712 break ;
681713 }
714+ Err ( e) => {
715+ tracing:: error!( "read_from_stream error: {}" , e) ;
716+ return Err ( e. into ( ) ) ;
717+ }
682718 }
683719 }
684720 }
0 commit comments