From 0bd1c69fd558143b02d80797114ede26919a6c3a Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 6 Oct 2025 14:29:48 -0500 Subject: [PATCH 1/2] fix: rebuild node service on restart and persist lsp selection --- LDKNodeMonday/App/WalletClient.swift | 19 ++++- .../Service/KeyService/KeyService.swift | 6 +- .../LightningNodeService.swift | 80 +++++++++++++++++-- 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/LDKNodeMonday/App/WalletClient.swift b/LDKNodeMonday/App/WalletClient.swift index 27f483c..2d266f2 100644 --- a/LDKNodeMonday/App/WalletClient.swift +++ b/LDKNodeMonday/App/WalletClient.swift @@ -96,27 +96,38 @@ public class WalletClient { lsp: LightningServiceProvider? = nil ) async { do { + let targetMode = appMode ?? .live + await MainActor.run { self.appState = .loading - switch appMode { + switch targetMode { case .mock: self.appMode = .mock self.keyClient = .mock self.lightningClient = .mock - default: + case .live, @unknown default: self.appMode = .live self.keyClient = .live self.lightningClient = .live } } - try await lightningClient.restart() + + switch targetMode { + case .mock: + try LightningNodeService.stopAndReleaseShared() + case .live: + try LightningNodeService.rebuildShared(keyService: self.keyClient) + } + + try await lightningClient.start() lightningClient.listenForEvents() + await MainActor.run { self.network = newNetwork self.server = newServer self.appState = .wallet - if let lsp = lsp { + if let lsp { self.lsp = lsp } } diff --git a/LDKNodeMonday/Service/KeyService/KeyService.swift b/LDKNodeMonday/Service/KeyService/KeyService.swift index bd4434a..aa64cbe 100644 --- a/LDKNodeMonday/Service/KeyService/KeyService.swift +++ b/LDKNodeMonday/Service/KeyService/KeyService.swift @@ -46,7 +46,8 @@ extension KeyService { let newBackupInfo = BackupInfo( mnemonic: currentBackupInfo.mnemonic, networkString: networkString, - serverURL: currentBackupInfo.serverURL + serverURL: currentBackupInfo.serverURL, + lspString: currentBackupInfo.lspNodeId ) try self.saveBackupInfo(backupInfo: newBackupInfo) } @@ -65,7 +66,8 @@ extension KeyService { let newBackupInfo = BackupInfo( mnemonic: currentBackupInfo.mnemonic, networkString: currentBackupInfo.networkString, - serverURL: url + serverURL: url, + lspString: currentBackupInfo.lspNodeId ) try self.saveBackupInfo(backupInfo: newBackupInfo) } diff --git a/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift b/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift index 9cb6643..9d0790d 100644 --- a/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift +++ b/LDKNodeMonday/Service/Lightning Service/LightningNodeService.swift @@ -34,6 +34,11 @@ class LightningNodeService { var network: Network var server: EsploraServer var lsp: LightningServiceProvider + private var eventListenerTask: Task? + + deinit { + cancelEventListenerTask() + } init( keyService: KeyClient = .live @@ -195,11 +200,59 @@ class LightningNodeService { } } + private static func existingInstance() -> LightningNodeService? { + lock.lock() + let instance = _shared + lock.unlock() + return instance + } + + static func stopAndReleaseShared() throws { + guard let instance = existingInstance() else { return } + do { + try instance.stop() + } catch let error as NodeError { + if case .NotRunning = error { + instance.cancelEventListenerTask() + } else { + throw error + } + } + stopTrackingSharedInstance() + } + + static func rebuildShared(keyService: KeyClient) throws -> LightningNodeService { + if let instance = existingInstance() { + do { + try instance.stop() + } catch let error as NodeError { + if case .NotRunning = error { + instance.cancelEventListenerTask() + } else { + throw error + } + } + } + + let service = LightningNodeService(keyService: keyService) + lock.lock() + _shared = service + lock.unlock() + return service + } + + private static func stopTrackingSharedInstance() { + lock.lock() + _shared = nil + lock.unlock() + } + func start() async throws { try ldkNode.start() } func stop() throws { + cancelEventListenerTask() try ldkNode.stop() } @@ -211,8 +264,11 @@ class LightningNodeService { } func reset() throws { - if LightningNodeService.shared.status().isRunning { - try LightningNodeService.shared.stop() + if let instance = LightningNodeService.existingInstance() { + if instance.status().isRunning { + try instance.stop() + } + instance.cancelEventListenerTask() } // Clean up wallet data to prevent conflicts on next initialization @@ -223,7 +279,7 @@ class LightningNodeService { try? FileManager.default.removeItem(atPath: networkPath) - LightningNodeService._shared = nil + LightningNodeService.stopTrackingSharedInstance() } func nodeId() -> String { @@ -390,14 +446,17 @@ extension LightningNodeService { extension LightningNodeService { func listenForEvents() { - Task { - while true { - let event = await ldkNode.nextEventAsync() + eventListenerTask?.cancel() + eventListenerTask = Task { [weak self] in + guard let self else { return } + while !Task.isCancelled { + let event = await self.ldkNode.nextEventAsync() + if Task.isCancelled { break } NotificationCenter.default.post( name: .ldkEventReceived, object: event ) - try? ldkNode.eventHandled() + try? self.ldkNode.eventHandled() } } } @@ -407,6 +466,13 @@ extension LightningNodeService { } } +extension LightningNodeService { + fileprivate func cancelEventListenerTask() { + eventListenerTask?.cancel() + eventListenerTask = nil + } +} + extension LightningNodeService { func save(mnemonic: Mnemonic) throws { let backupInfo = BackupInfo( From 818d1822023a2f6b09fb32aff9f3f45022d2f95f Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 6 Oct 2025 14:33:50 -0500 Subject: [PATCH 2/2] building --- LDKNodeMonday/App/WalletClient.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/LDKNodeMonday/App/WalletClient.swift b/LDKNodeMonday/App/WalletClient.swift index 2d266f2..4bb464f 100644 --- a/LDKNodeMonday/App/WalletClient.swift +++ b/LDKNodeMonday/App/WalletClient.swift @@ -105,7 +105,11 @@ public class WalletClient { self.appMode = .mock self.keyClient = .mock self.lightningClient = .mock - case .live, @unknown default: + case .live: + self.appMode = .live + self.keyClient = .live + self.lightningClient = .live + @unknown default: self.appMode = .live self.keyClient = .live self.lightningClient = .live @@ -116,7 +120,9 @@ public class WalletClient { case .mock: try LightningNodeService.stopAndReleaseShared() case .live: - try LightningNodeService.rebuildShared(keyService: self.keyClient) + _ = try LightningNodeService.rebuildShared(keyService: self.keyClient) + @unknown default: + _ = try LightningNodeService.rebuildShared(keyService: self.keyClient) } try await lightningClient.start()