Skip to content

Commit 7902df4

Browse files
authored
Merge pull request #249 from synonymdev/restart-fix
feat: ios restart debounce, dropped peer connection handler
2 parents 58713d9 + be4a848 commit 7902df4

File tree

3 files changed

+140
-11
lines changed

3 files changed

+140
-11
lines changed

example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ PODS:
316316
- React-jsinspector (0.72.4)
317317
- React-logger (0.72.4):
318318
- glog
319-
- react-native-ldk (0.0.141):
319+
- react-native-ldk (0.0.143):
320320
- React
321321
- react-native-randombytes (3.6.1):
322322
- React-Core
@@ -621,7 +621,7 @@ SPEC CHECKSUMS:
621621
React-jsiexecutor: c7f826e40fa9cab5d37cab6130b1af237332b594
622622
React-jsinspector: aaed4cf551c4a1c98092436518c2d267b13a673f
623623
React-logger: da1ebe05ae06eb6db4b162202faeafac4b435e77
624-
react-native-ldk: 58b28973bedc64333c350b9074554f814ba3daec
624+
react-native-ldk: 12d78fe1141ad4343a2842340f7ebf8539dcc3b0
625625
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
626626
react-native-tcp-socket: c1b7297619616b4c9caae6889bcb0aba78086989
627627
React-NativeModulesApple: edb5ace14f73f4969df6e7b1f3e41bef0012740f

lib/android/src/main/java/com/reactnativeldk/LdkModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
641641
peerHandler!!.connect(pubKey.hexa(), InetSocketAddress(address, port.toInt()), 3000)
642642
LdkEventEmitter.send(EventTypes.native_log, "Connection to peer $pubKey re-established by handleDroppedPeers().")
643643
} catch (e: Exception) {
644-
LdkEventEmitter.send(EventTypes.native_log, "Error connecting peer $pubKey. Error: $e")
644+
LdkEventEmitter.send(EventTypes.native_log, "Error connecting peer from handleDroppedPeers() $pubKey. Error: $e")
645645
} finally {
646646
currentlyConnectingPeers.remove(pubKey)
647647
}

lib/ios/Ldk.swift

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)