From 9d7947efdcc11ff6c880ee35810ccb9bd13db57b Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Sun, 22 Feb 2026 21:37:42 +0100 Subject: [PATCH 1/2] fix(settings): add peer simulation picker to LDK Debug screen Add BlocktankPeerSimulation enum to WalletViewModel with simulation modes (None, API Failure, Unreachable Peers) and a segmented picker in the LDK Debug screen for testing Blocktank peer connection edge cases. Dev Settings is already UI-gated via @AppStorage, so no compile-time guards are needed. Co-Authored-By: Claude Opus 4.6 --- Bitkit/ViewModels/WalletViewModel.swift | 23 ++++++++++++++++++++++ Bitkit/Views/Settings/LdkDebugScreen.swift | 15 ++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/Bitkit/ViewModels/WalletViewModel.swift b/Bitkit/ViewModels/WalletViewModel.swift index 86f5f094..28e58a13 100644 --- a/Bitkit/ViewModels/WalletViewModel.swift +++ b/Bitkit/ViewModels/WalletViewModel.swift @@ -51,6 +51,14 @@ class WalletViewModel: ObservableObject { private let transferService: TransferService private let sheetViewModel: SheetViewModel + enum BlocktankPeerSimulation: String, CaseIterable { + case none = "None" + case apiFailure = "API Failure" + case unreachablePeers = "Unreachable Peers" + } + + static var peerSimulation: BlocktankPeerSimulation = .none + @Published var isRestoringWallet = false @Published var balanceInTransferToSavings: Int = 0 @Published var balanceInTransferToSpending: Int = 0 @@ -234,6 +242,21 @@ class WalletViewModel: ObservableObject { } private func fetchTrustedPeersFromBlocktank() async -> [LnPeer]? { + switch Self.peerSimulation { + case .apiFailure: + Logger.warn("⚠️ [DEBUG] Simulating Blocktank API failure — returning nil") + return nil + case .unreachablePeers: + Logger.warn("⚠️ [DEBUG] Simulating unreachable API peers") + return [ + LnPeer(nodeId: "000000000000000000000000000000000000000000000000000000000000000001", + host: "192.0.2.1", port: 9735), + ] + case .none: + break + } + + var info: IBtInfo? do { info = try await coreService.blocktank.info(refresh: true) diff --git a/Bitkit/Views/Settings/LdkDebugScreen.swift b/Bitkit/Views/Settings/LdkDebugScreen.swift index d9932fde..2dc43561 100644 --- a/Bitkit/Views/Settings/LdkDebugScreen.swift +++ b/Bitkit/Views/Settings/LdkDebugScreen.swift @@ -66,6 +66,21 @@ struct LdkDebugScreen: View { } } } + + // Peer Simulation + VStack(alignment: .leading, spacing: 8) { + CaptionMText("Peer Simulation") + + Picker("Peer Simulation", selection: Binding( + get: { WalletViewModel.peerSimulation }, + set: { WalletViewModel.peerSimulation = $0 } + )) { + ForEach(WalletViewModel.BlocktankPeerSimulation.allCases, id: \.self) { mode in + Text(mode.rawValue).tag(mode) + } + } + .pickerStyle(.segmented) + } } } } From 70c25aa8230f7bc442ebdd12d6410eadce52f359 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Mon, 23 Feb 2026 00:11:12 +0100 Subject: [PATCH 2/2] fix(settings): reconnect trusted peers after LDK restart in debug screen Extract reconnectTrustedPeers() from WalletViewModel.start() and call it after lightningService.restart() in LdkDebugScreen so the peer simulation picker actually takes effect on restart. Co-Authored-By: Claude Opus 4.6 --- Bitkit/ViewModels/WalletViewModel.swift | 16 ++++++++++------ Bitkit/Views/Settings/LdkDebugScreen.swift | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Bitkit/ViewModels/WalletViewModel.swift b/Bitkit/ViewModels/WalletViewModel.swift index 28e58a13..95c9033f 100644 --- a/Bitkit/ViewModels/WalletViewModel.swift +++ b/Bitkit/ViewModels/WalletViewModel.swift @@ -213,12 +213,7 @@ class WalletViewModel: ObservableObject { syncState() - do { - let remotePeers = await fetchTrustedPeersFromBlocktank() - try await lightningService.connectToTrustedPeers(remotePeers: remotePeers) - } catch { - Logger.error("Failed to connect to trusted peers") - } + await reconnectTrustedPeers() // Migration only: fetch peers from remote backup (once) and persist in ldk-node let peerUris = await MigrationsService.shared.tryFetchMigrationPeersFromBackup(walletIndex: walletIndex) @@ -278,6 +273,15 @@ class WalletViewModel: ObservableObject { return peers.isEmpty ? nil : peers } + func reconnectTrustedPeers() async { + do { + let remotePeers = await fetchTrustedPeersFromBlocktank() + try await lightningService.connectToTrustedPeers(remotePeers: remotePeers) + } catch { + Logger.error("Failed to connect to trusted peers") + } + } + func stopLightningNode(clearEventCallback: Bool = false) async throws { nodeLifecycleState = .stopping try await lightningService.stop(clearEventCallback: clearEventCallback) diff --git a/Bitkit/Views/Settings/LdkDebugScreen.swift b/Bitkit/Views/Settings/LdkDebugScreen.swift index 2dc43561..69d1556c 100644 --- a/Bitkit/Views/Settings/LdkDebugScreen.swift +++ b/Bitkit/Views/Settings/LdkDebugScreen.swift @@ -124,6 +124,7 @@ struct LdkDebugScreen: View { isRestartingNode = true let lightningService = LightningService.shared try await lightningService.restart() + await wallet.reconnectTrustedPeers() app.toast(type: .success, title: "Node Restarted", description: "Node restarted successfully") } catch { Logger.error("Failed to restart node: \(error)")