From 750bb38f5aa4fe2f6b68114aed03e9b5c68ddfe0 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Wed, 18 Mar 2026 07:34:19 -0300 Subject: [PATCH 1/2] fix: bump ldk-node to v0.7.0-rc.33 which includes the migration-aware write protection --- Bitkit.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Bitkit.xcodeproj/project.pbxproj b/Bitkit.xcodeproj/project.pbxproj index fe07deab..452308d0 100644 --- a/Bitkit.xcodeproj/project.pbxproj +++ b/Bitkit.xcodeproj/project.pbxproj @@ -928,7 +928,7 @@ repositoryURL = "https://github.com/synonymdev/ldk-node"; requirement = { kind = revision; - revision = c5698d00066e0e50f33696afc562d71023da2373; + revision = 1531af727811f03ec0542aec3befb5a26198b192; }; }; 96DEA0382DE8BBA1009932BF /* XCRemoteSwiftPackageReference "bitkit-core" */ = { diff --git a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e14c1eae..284ed91c 100644 --- a/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,7 +24,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/synonymdev/ldk-node", "state" : { - "revision" : "c5698d00066e0e50f33696afc562d71023da2373" + "revision" : "1531af727811f03ec0542aec3befb5a26198b192" } }, { From fe63cfeca823b9258667e683d87ecfe0ee2b220b Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Wed, 18 Mar 2026 08:16:26 -0300 Subject: [PATCH 2/2] fix: filter out already migrated monitors --- Bitkit/Services/MigrationsService.swift | 35 ++++++++++++++++++++++--- Bitkit/ViewModels/WalletViewModel.swift | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Bitkit/Services/MigrationsService.swift b/Bitkit/Services/MigrationsService.swift index aaea3c21..13cd2d24 100644 --- a/Bitkit/Services/MigrationsService.swift +++ b/Bitkit/Services/MigrationsService.swift @@ -2098,9 +2098,10 @@ extension MigrationsService { } /// Fetches channel manager and monitors from RN remote backup. + /// When `walletIndex` is provided, filters out monitors that already exist in local LDK storage. /// Returns `true` if all monitors were successfully retrieved (or none exist), `false` if some failed. @discardableResult - func fetchRNRemoteLdkData() async -> Bool { + func fetchRNRemoteLdkData(walletIndex: Int? = nil) async -> Bool { do { let files = try await RNBackupClient.shared.listFiles(fileGroup: "ldk") @@ -2109,10 +2110,38 @@ extension MigrationsService { return true } - let expectedCount = files.channel_monitors.count + // Filter out monitors that already exist locally (previously migrated) + var localChannelIds: Set = [] + if let walletIndex { + let ldkPath = Env.ldkStorage(walletIndex: walletIndex) + let channelsPath = ldkPath.appendingPathComponent("channels") + let monitorsPath = ldkPath.appendingPathComponent("monitors") + let monitorDir = FileManager.default.fileExists(atPath: channelsPath.path) ? channelsPath : monitorsPath + + if FileManager.default.fileExists(atPath: monitorDir.path), + let localFiles = try? FileManager.default.contentsOfDirectory(atPath: monitorDir.path) + { + localChannelIds = Set(localFiles.filter { $0.hasSuffix(".bin") }.map { $0.replacingOccurrences(of: ".bin", with: "") }) + } + } + + let remoteMonitorFiles = files.channel_monitors.filter { file in + let channelId = file.replacingOccurrences(of: ".bin", with: "") + return !localChannelIds.contains(channelId) + } + + if !localChannelIds.isEmpty { + let skipped = files.channel_monitors.count - remoteMonitorFiles.count + Logger.info( + "Filtered \(skipped) already-migrated monitors, \(remoteMonitorFiles.count) orphaned remaining", + context: "Migration" + ) + } + + let expectedCount = remoteMonitorFiles.count let monitorResults = await withTaskGroup(of: (String, Data?).self) { group in var results: [(String, Data?)] = [] - for monitorFile in files.channel_monitors { + for monitorFile in remoteMonitorFiles { let channelId = monitorFile.replacingOccurrences(of: ".bin", with: "") group.addTask { await (channelId, self.retrieveChannelMonitorWithRetry(channelId: channelId)) diff --git a/Bitkit/ViewModels/WalletViewModel.swift b/Bitkit/ViewModels/WalletViewModel.swift index 6ea9d86b..50bf9587 100644 --- a/Bitkit/ViewModels/WalletViewModel.swift +++ b/Bitkit/ViewModels/WalletViewModel.swift @@ -275,7 +275,7 @@ class WalletViewModel: ObservableObject { RNBackupClient.shared.reset() try await RNBackupClient.shared.setup(mnemonic: mnemonic, passphrase: passphrase) - let allRetrieved = await migrations.fetchRNRemoteLdkData() + let allRetrieved = await migrations.fetchRNRemoteLdkData(walletIndex: walletIndex) if let migration = migrations.pendingChannelMigration { Logger.info(