@@ -38,6 +38,7 @@ use fedimint_client::{
3838} ;
3939use fedimint_core:: bitcoin_migration:: bitcoin30_to_bitcoin29_address;
4040use fedimint_core:: config:: ClientConfig ;
41+ use fedimint_core:: core:: LEGACY_HARDCODED_INSTANCE_ID_MINT ;
4142use fedimint_core:: {
4243 api:: InviteCode ,
4344 config:: FederationId ,
@@ -143,6 +144,13 @@ impl From<LnPayState> for HTLCStatus {
143144 }
144145}
145146
147+ #[ derive( Debug , Serialize , Deserialize , Clone , Default ) ]
148+ pub struct ResyncProgress {
149+ pub total : u32 ,
150+ pub complete : u32 ,
151+ pub done : bool ,
152+ }
153+
146154// This is the FederationStorage object saved to the DB
147155#[ derive( Debug , Serialize , Deserialize , Clone , Default ) ]
148156pub struct FederationStorage {
@@ -262,6 +270,7 @@ impl<S: MutinyStorage> FederationClient<S> {
262270 network : Network ,
263271 stop : Arc < AtomicBool > ,
264272 logger : Arc < MutinyLogger > ,
273+ safe_mode : bool ,
265274 ) -> Result < Self , MutinyError > {
266275 log_info ! ( logger, "initializing a new federation client: {uuid}" ) ;
267276
@@ -282,6 +291,10 @@ impl<S: MutinyStorage> FederationClient<S> {
282291
283292 client_builder. with_primary_module ( 1 ) ;
284293
294+ if safe_mode {
295+ client_builder. stopped ( ) ;
296+ }
297+
285298 log_trace ! ( logger, "Building fedimint client db" ) ;
286299 let secret = create_federation_secret ( xprivkey, network) ?;
287300
@@ -482,6 +495,101 @@ impl<S: MutinyStorage> FederationClient<S> {
482495 ) ;
483496 }
484497
498+ /// Starts a resync of the federation
499+ pub async fn start_resync (
500+ federation_code : InviteCode ,
501+ xprivkey : ExtendedPrivKey ,
502+ storage : S ,
503+ network : Network ,
504+ logger : Arc < MutinyLogger > ,
505+ ) -> Result < ( ) , MutinyError > {
506+ let federation_id = federation_code. federation_id ( ) ;
507+
508+ let storage_key = format ! ( "resync_state/{federation_id}" ) ;
509+ storage. set_data ( storage_key. clone ( ) , ResyncProgress :: default ( ) , None ) ?;
510+
511+ log_trace ! ( logger, "Building fedimint client db" ) ;
512+ let fedimint_storage =
513+ FedimintStorage :: new ( storage. clone ( ) , federation_id. to_string ( ) , logger. clone ( ) )
514+ . await ?;
515+ let db = fedimint_storage. clone ( ) . into ( ) ;
516+
517+ let mut client_builder = fedimint_client:: Client :: builder ( db) ;
518+ client_builder. with_module ( WalletClientInit ( None ) ) ;
519+ client_builder. with_module ( MintClientInit ) ;
520+ client_builder. with_module ( LightningClientInit ) ;
521+
522+ client_builder. with_primary_module ( 1 ) ;
523+
524+ log_trace ! ( logger, "Building fedimint client db" ) ;
525+ let secret = create_federation_secret ( xprivkey, network) ?;
526+
527+ // need to use a fresh database for resync
528+ fedimint_storage. delete_store ( ) . await ?;
529+
530+ let config = ClientConfig :: download_from_invite_code ( & federation_code)
531+ . await
532+ . map_err ( |e| {
533+ log_error ! ( logger, "Could not download federation info: {e}" ) ;
534+ e
535+ } ) ?;
536+
537+ let fedimint_client = client_builder
538+ . recover (
539+ get_default_client_secret ( & secret, & federation_id) ,
540+ config,
541+ None ,
542+ )
543+ . await
544+ . map_err ( |e| {
545+ log_error ! ( logger, "Could not open federation client: {e}" ) ;
546+ MutinyError :: FederationConnectionFailed
547+ } ) ?;
548+ let fedimint_client = Arc :: new ( fedimint_client) ;
549+
550+ log_debug ! ( logger, "Built fedimint resync client" ) ;
551+
552+ spawn ( async move {
553+ let mut stream = fedimint_client. subscribe_to_recovery_progress ( ) ;
554+
555+ while let Some ( ( id, progress) ) = stream. next ( ) . await {
556+ // only can rescan mint client, don't care about sync progress of others
557+ if id != LEGACY_HARDCODED_INSTANCE_ID_MINT || progress. is_none ( ) {
558+ continue ;
559+ }
560+
561+ log_debug ! ( logger, "Got recovery progress: {progress:?}" ) ;
562+
563+ // save progress state to storage so frontend can show progress
564+ if let Err ( e) = storage. set_data (
565+ storage_key. clone ( ) ,
566+ ResyncProgress {
567+ total : progress. total ,
568+ complete : progress. complete ,
569+ done : progress. is_done ( ) ,
570+ } ,
571+ None ,
572+ ) {
573+ log_error ! ( logger, "Error saving resync progress: {e}" ) ;
574+ }
575+ }
576+
577+ log_debug ! ( logger, "No more progress, waiting for recoveries" ) ;
578+
579+ // wait for all recoveries to complete just to be sure
580+ if let Err ( e) = fedimint_client. wait_for_all_recoveries ( ) . await {
581+ log_error ! ( logger, "Error waiting for recoveries: {e}" ) ;
582+ }
583+
584+ // can now delete the progress state
585+ if let Err ( e) = storage. delete ( & [ storage_key] ) {
586+ log_error ! ( logger, "Error deleting resync progress state: {e}" ) ;
587+ }
588+ } ) ;
589+
590+ Ok ( ( ) )
591+ }
592+
485593 pub ( crate ) async fn gateway_fee ( & self ) -> Result < GatewayFees , MutinyError > {
486594 let gateway = self . gateway . read ( ) . await ;
487595 Ok ( gateway. as_ref ( ) . map ( |x| x. fees . into ( ) ) . unwrap_or_default ( ) )
0 commit comments