@@ -86,6 +86,9 @@ enum LdkCallbackResponses: String {
8686 case config_init_success = " config_init_success "
8787 case network_graph_init_success = " network_graph_init_success "
8888 case add_peer_success = " add_peer_success "
89+ case add_peer_skipped = " add_peer_skipped "
90+ case peer_already_connected = " peer_already_connected "
91+ case peer_currently_connecting = " peer_currently_connecting "
8992 case chain_sync_success = " chain_sync_success "
9093 case invoice_payment_success = " invoice_payment_success "
9194 case tx_set_confirmed = " tx_set_confirmed "
@@ -143,6 +146,12 @@ class Ldk: NSObject {
143146 var currentBlockchainTipHash : NSString ?
144147 var currentBlockchainHeight : NSInteger ?
145148
149+ //Peer connection checks
150+ var backgroundedAt : Date ? = nil
151+ var addedPeers : [ ( String , Int , String ) ] = [ ]
152+ var currentlyConnectingPeers : [ String ] = [ ]
153+ var droppedPeerTimer : Timer ? = nil
154+
146155 //Static to be accessed from other classes
147156 static var accountStoragePath : URL ?
148157 static var channelStoragePath : URL ?
@@ -495,25 +504,45 @@ class Ldk: NSObject {
495504 currentBlockchainTipHash = blockHash
496505 currentBlockchainHeight = blockHeight
497506 addForegroundObserver ( )
498-
507+ startDroppedPeerTimer ( )
508+
499509 return handleResolve ( resolve, . channel_manager_init_success)
500510 }
501511
502512 func addForegroundObserver( ) {
503513 removeForegroundObserver ( )
514+ backgroundedAt = nil
504515 NotificationCenter . default. addObserver ( self , selector: #selector( restartOnForeground) , name: UIApplication . didBecomeActiveNotification, object: nil )
516+ NotificationCenter . default. addObserver ( self , selector: #selector( onBackground) , name: UIApplication . willResignActiveNotification, object: nil )
505517 }
506518
507519 func removeForegroundObserver( ) {
508520 NotificationCenter . default. removeObserver ( self , name: UIApplication . didBecomeActiveNotification, object: nil )
521+ NotificationCenter . default. removeObserver ( self , name: UIApplication . willResignActiveNotification, object: nil )
509522 }
510523
511524 /// Used by event listener so responses are not handled
512525 @objc
513526 func restartOnForeground( ) {
527+ let secondsSinceBackgrounded = Date ( ) . timeIntervalSince ( backgroundedAt ?? . distantPast)
528+ guard secondsSinceBackgrounded > 5 else {
529+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Skipping restart. App was only backgrounded \( Int ( secondsSinceBackgrounded) ) s ago " )
530+ return
531+ }
532+
533+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Restarting LDK on move to foreground. App was backgrounded \( Int ( secondsSinceBackgrounded) ) s ago " )
534+
535+ backgroundedAt = nil
514536 restart { res in } reject: { code, message, error in }
515537 }
516538
539+ @objc
540+ func onBackground( ) {
541+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " App moved to background " )
542+
543+ backgroundedAt = Date ( )
544+ }
545+
517546 /// Restarts channel manager constructor to get a new TCP peer handler
518547 @objc
519548 func restart( _ resolve: @escaping RCTPromiseResolveBlock , reject: @escaping RCTPromiseRejectBlock ) {
@@ -559,6 +588,7 @@ class Ldk: NSObject {
559588 return handleResolve ( resolve, . ldk_stop)
560589 }
561590
591+ stopStartDroppedPeerTimer ( )
562592 removeForegroundObserver ( ) //LDK was intentionally stopped and we shouldn't attempt a restart
563593 cm. interrupt ( )
564594 channelManagerConstructor = nil
@@ -571,6 +601,7 @@ class Ldk: NSObject {
571601 peerHandler = nil
572602 ldkNetwork = nil
573603 ldkCurrency = nil
604+ backgroundedAt = nil
574605
575606 return handleResolve ( resolve, . ldk_stop)
576607 }
@@ -617,16 +648,114 @@ class Ldk: NSObject {
617648 return handleResolve ( resolve, . chain_sync_success)
618649 }
619650
651+ func startDroppedPeerTimer( ) {
652+ guard droppedPeerTimer == nil else {
653+ return
654+ }
655+
656+ DispatchQueue . main. async { [ weak self] in
657+ guard let self else { return }
658+
659+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Starting timer to check for dropped peers " )
660+
661+ droppedPeerTimer = Timer . scheduledTimer (
662+ timeInterval: 5.0 ,
663+ target: self ,
664+ selector: #selector( handleDroppedPeers) ,
665+ userInfo: nil ,
666+ repeats: true
667+ )
668+ }
669+ }
670+
671+ func stopStartDroppedPeerTimer( ) {
672+ droppedPeerTimer? . invalidate ( )
673+ droppedPeerTimer = nil
674+ }
675+
676+ @objc func handleDroppedPeers( ) {
677+ guard backgroundedAt == nil else {
678+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " App was backgrounded, skipping handleDroppedPeers() " )
679+ return
680+ }
681+
682+ guard channelManagerConstructor != nil else {
683+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " channelManagerConstructor not intialized, skipping handleDroppedPeers() " )
684+ return
685+ }
686+
687+ guard let peerHandler else {
688+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " peerHandler not intialized, skipping handleDroppedPeers() " )
689+ return
690+ }
691+
692+ guard let peerManager else {
693+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " peerManager not intialized, skipping handleDroppedPeers() " )
694+ return
695+ }
696+
697+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Checking for dropped peers " )
698+
699+ let currentList = peerManager. getPeerNodeIds ( ) . map { Data ( $0. 0 ) . hexEncodedString ( ) }
700+
701+ addedPeers. forEach { ( address, port, pubKey) in
702+ guard !currentList. contains ( pubKey) else {
703+ return
704+ }
705+
706+ currentlyConnectingPeers. append ( String ( pubKey) )
707+ let res = peerHandler. connect ( address: String ( address) , port: UInt16 ( port) , theirNodeId: String ( pubKey) . hexaBytes)
708+ currentlyConnectingPeers. removeAll { $0 == String ( pubKey) }
709+
710+ if res {
711+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Connection to peer \( pubKey) re-established by handleDroppedPeers(). " )
712+ } else {
713+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Error connecting peer \( pubKey) from handleDroppedPeers(). " )
714+ }
715+ }
716+ }
717+
620718 @objc
621719 func addPeer( _ address: NSString , port: NSInteger , pubKey: NSString , timeout: NSInteger , resolve: @escaping RCTPromiseResolveBlock , reject: @escaping RCTPromiseRejectBlock ) {
622720 //timeout param not used. Only for android.
623721
624- //Sync ChannelMonitors and ChannelManager to chain tip
722+ guard backgroundedAt == nil else {
723+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " App was backgrounded, skipping addPeer() " )
724+ return handleResolve ( resolve, . add_peer_skipped)
725+ }
726+
625727 guard let peerHandler = peerHandler else {
626728 return handleReject ( reject, . init_peer_handler)
627729 }
628730
731+ guard let peerManager = peerManager else {
732+ return handleReject ( reject, . init_peer_manager)
733+ }
734+
735+ //If peer is already connected don't add again
736+ let currentList = peerManager. getPeerNodeIds ( ) . map { Data ( $0. 0 ) . hexEncodedString ( ) }
737+ guard !currentList. contains ( String ( pubKey) ) else {
738+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Skipping new peer connection, already connected to \( pubKey) " )
739+ return handleResolve ( resolve, . peer_already_connected)
740+ }
741+
742+ guard !currentlyConnectingPeers. contains ( String ( pubKey) ) else {
743+ LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Skipping additional peer connection, already busy connecting to \( pubKey) " )
744+ return handleResolve ( resolve, . peer_currently_connecting)
745+ }
746+
747+ //Add to retry list if peers are dropped
748+
749+ currentlyConnectingPeers. append ( String ( pubKey) )
629750 let res = peerHandler. connect ( address: String ( address) , port: UInt16 ( port) , theirNodeId: String ( pubKey) . hexaBytes)
751+ currentlyConnectingPeers. removeAll { $0 == String ( pubKey) }
752+
753+ if !addedPeers. contains ( where: { ( _, _, pk) in
754+ pk == String ( pubKey)
755+ } ) {
756+ addedPeers. append ( ( String ( address) , Int ( port) , String ( pubKey) ) )
757+ }
758+
630759 if !res {
631760 return handleReject ( reject, . add_peer_fail)
632761 }
@@ -846,7 +975,7 @@ class Ldk: NSObject {
846975 guard !( amountSats > 0 && !isZeroValueInvoice) else {
847976 return handleReject ( reject, . invoice_payment_fail_must_not_specify_amount)
848977 }
849-
978+
850979 let paymentId = invoice. paymentHash ( ) !
851980 let ( paymentHash, recipientOnion, routeParameters) = isZeroValueInvoice ? Bindings . paymentParametersFromZeroAmountInvoice ( invoice: invoice, amountMsat: UInt64 ( amountSats*1000) ) . getValue ( ) ! : Bindings . paymentParametersFromInvoice ( invoice: invoice) . getValue ( ) !
852981
@@ -1038,7 +1167,7 @@ class Ldk: NSObject {
10381167 }
10391168
10401169 let excludeChannelIds = ignoreOpenChannels ? channelManager. listChannels ( ) . map { Data ( $0. getChannelId ( ) ?? [ ] ) . hexEncodedString ( ) } . filter { $0 != " " } : [ ]
1041-
1170+
10421171 let channelFiles = try ! FileManager . default. contentsOfDirectory ( at: channelStoragePath, includingPropertiesForKeys: nil )
10431172
10441173 var result : [ [ String : Any ? ] ] = [ ]
@@ -1261,9 +1390,9 @@ class Ldk: NSObject {
12611390 }
12621391
12631392 let openChannelIds = channelManager. listChannels ( ) . map { Data ( $0. getChannelId ( ) ?? [ ] ) . hexEncodedString ( ) } . filter { $0 != " " }
1264-
1393+
12651394 let channelFiles = try ! FileManager . default. contentsOfDirectory ( at: channelStoragePath, includingPropertiesForKeys: nil )
1266-
1395+
12671396 var txs : [ String ] = [ ]
12681397
12691398 for channelFile in channelFiles {
@@ -1296,7 +1425,7 @@ class Ldk: NSObject {
12961425 LdkEventEmitter . shared. send ( withEvent: . native_log, body: " No spendable outputs found in \( channelId) " )
12971426 continue
12981427 }
1299-
1428+
13001429 let res = keysManager. spendSpendableOutputs (
13011430 descriptors: descriptors,
13021431 outputs: [ ] ,
@@ -1309,7 +1438,7 @@ class Ldk: NSObject {
13091438 LdkEventEmitter . shared. send ( withEvent: . native_log, body: " Failed to spend output from closed channel: \( channelId) . " )
13101439 continue
13111440 }
1312-
1441+
13131442 txs. append ( Data ( res. getValue ( ) !) . hexEncodedString ( ) )
13141443 }
13151444
0 commit comments