Skip to content

Commit 481e477

Browse files
authored
Merge branch 'master' into feat/multiple-addresses-types
2 parents b77ba6f + 9196ea2 commit 481e477

5 files changed

Lines changed: 123 additions & 16 deletions

File tree

Bitkit.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@
500500
buildSettings = {
501501
CODE_SIGN_ENTITLEMENTS = BitkitNotification/BitkitNotification.entitlements;
502502
CODE_SIGN_STYLE = Automatic;
503-
CURRENT_PROJECT_VERSION = 177;
503+
CURRENT_PROJECT_VERSION = 178;
504504
DEVELOPMENT_TEAM = KYH47R284B;
505505
GENERATE_INFOPLIST_FILE = YES;
506506
INFOPLIST_FILE = BitkitNotification/Info.plist;
@@ -512,7 +512,7 @@
512512
"@executable_path/Frameworks",
513513
"@executable_path/../../Frameworks",
514514
);
515-
MARKETING_VERSION = 2.0.4;
515+
MARKETING_VERSION = 2.0.5;
516516
PRODUCT_BUNDLE_IDENTIFIER = to.bitkit.notification;
517517
PRODUCT_NAME = "$(TARGET_NAME)";
518518
SDKROOT = iphoneos;
@@ -528,7 +528,7 @@
528528
buildSettings = {
529529
CODE_SIGN_ENTITLEMENTS = BitkitNotification/BitkitNotification.entitlements;
530530
CODE_SIGN_STYLE = Automatic;
531-
CURRENT_PROJECT_VERSION = 177;
531+
CURRENT_PROJECT_VERSION = 178;
532532
DEVELOPMENT_TEAM = KYH47R284B;
533533
GENERATE_INFOPLIST_FILE = YES;
534534
INFOPLIST_FILE = BitkitNotification/Info.plist;
@@ -540,7 +540,7 @@
540540
"@executable_path/Frameworks",
541541
"@executable_path/../../Frameworks",
542542
);
543-
MARKETING_VERSION = 2.0.4;
543+
MARKETING_VERSION = 2.0.5;
544544
PRODUCT_BUNDLE_IDENTIFIER = to.bitkit.notification;
545545
PRODUCT_NAME = "$(TARGET_NAME)";
546546
SDKROOT = iphoneos;
@@ -674,7 +674,7 @@
674674
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
675675
CODE_SIGN_ENTITLEMENTS = Bitkit/Bitkit.entitlements;
676676
CODE_SIGN_STYLE = Automatic;
677-
CURRENT_PROJECT_VERSION = 177;
677+
CURRENT_PROJECT_VERSION = 178;
678678
DEVELOPMENT_ASSET_PATHS = "\"Bitkit/Preview Content\"";
679679
DEVELOPMENT_TEAM = KYH47R284B;
680680
ENABLE_HARDENED_RUNTIME = YES;
@@ -699,7 +699,7 @@
699699
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
700700
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
701701
MACOSX_DEPLOYMENT_TARGET = 14.0;
702-
MARKETING_VERSION = 2.0.4;
702+
MARKETING_VERSION = 2.0.5;
703703
PRODUCT_BUNDLE_IDENTIFIER = to.bitkit;
704704
PRODUCT_NAME = "$(TARGET_NAME)";
705705
SDKROOT = auto;
@@ -717,7 +717,7 @@
717717
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
718718
CODE_SIGN_ENTITLEMENTS = Bitkit/Bitkit.entitlements;
719719
CODE_SIGN_STYLE = Automatic;
720-
CURRENT_PROJECT_VERSION = 177;
720+
CURRENT_PROJECT_VERSION = 178;
721721
DEVELOPMENT_ASSET_PATHS = "\"Bitkit/Preview Content\"";
722722
DEVELOPMENT_TEAM = KYH47R284B;
723723
ENABLE_HARDENED_RUNTIME = YES;
@@ -742,7 +742,7 @@
742742
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
743743
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
744744
MACOSX_DEPLOYMENT_TARGET = 14.0;
745-
MARKETING_VERSION = 2.0.4;
745+
MARKETING_VERSION = 2.0.5;
746746
PRODUCT_BUNDLE_IDENTIFIER = to.bitkit;
747747
PRODUCT_NAME = "$(TARGET_NAME)";
748748
SDKROOT = auto;

Bitkit/Constants/Env.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ enum Env {
177177
return [
178178
.init(nodeId: "039b8b4dd1d88c2c5db374290cda397a8f5d79f312d6ea5d5bfdfc7c6ff363eae3", host: "34.65.111.104", port: 9735),
179179
.init(nodeId: "03816141f1dce7782ec32b66a300783b1d436b19777e7c686ed00115bd4b88ff4b", host: "34.65.191.64", port: 9735),
180-
.init(nodeId: "02a371038863605300d0b3fc9de0cf5ccb57728b7f8906535709a831b16e311187", host: "34.65.186.40", port: 9735),
180+
.init(nodeId: "02a371038863605300d0b3fc9de0cf5ccb57728b7f8906535709a831b16e311187", host: "34.65.153.174", port: 9735),
181181
]
182182
case .signet:
183183
return []

Bitkit/Services/LightningService.swift

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,20 +309,61 @@ class LightningService {
309309
Logger.info("Deleted network graph cache at: \(graphPath.path)")
310310
}
311311

312-
func connectToTrustedPeers() async throws {
312+
func connectToTrustedPeers(remotePeers: [LnPeer]? = nil) async throws {
313313
guard let node else {
314314
throw AppError(serviceError: .nodeNotSetup)
315315
}
316316

317+
let peers: [LnPeer]
318+
let usingRemotePeers: Bool
319+
if let remotePeers, !remotePeers.isEmpty {
320+
Logger.info("Using \(remotePeers.count) trusted peers from Blocktank API")
321+
peers = remotePeers
322+
usingRemotePeers = true
323+
} else {
324+
Logger.warn("No remote peers available, falling back to preconfigured env peers")
325+
peers = Env.trustedLnPeers
326+
usingRemotePeers = false
327+
}
328+
317329
try await ServiceQueue.background(.ldk) {
318-
for peer in Env.trustedLnPeers {
330+
for peer in peers {
319331
do {
320332
try node.connect(nodeId: peer.nodeId, address: peer.address, persist: true)
321333
Logger.info("Connected to trusted peer: \(peer.nodeId)")
322334
} catch {
323335
Logger.error(error, context: "Peer: \(peer.nodeId)")
324336
}
325337
}
338+
339+
if usingRemotePeers {
340+
self.verifyTrustedPeersOrFallback(node: node, trustedPeers: peers)
341+
}
342+
}
343+
}
344+
345+
private func verifyTrustedPeersOrFallback(node: Node, trustedPeers: [LnPeer]) {
346+
let connectedPeerIds = Set(node.listPeers().filter(\.isConnected).map(\.nodeId))
347+
let trustedConnected = trustedPeers.filter { connectedPeerIds.contains($0.nodeId) }.count
348+
let trustedPeerIds = Set(trustedPeers.map(\.nodeId))
349+
350+
if trustedConnected == 0, !trustedPeers.isEmpty {
351+
let fallbackPeers = Env.trustedLnPeers.filter { !trustedPeerIds.contains($0.nodeId) }
352+
if fallbackPeers.isEmpty {
353+
Logger.warn("No trusted peers connected. All preconfigured env peers overlap with API peers (not retrying with stale addresses).")
354+
} else {
355+
Logger.warn("No trusted peers connected, trying \(fallbackPeers.count) preconfigured env peer(s) not in API list")
356+
for peer in fallbackPeers {
357+
do {
358+
try node.connect(nodeId: peer.nodeId, address: peer.address, persist: true)
359+
Logger.info("Connected to fallback peer: \(peer.nodeId)")
360+
} catch {
361+
Logger.error(error, context: "Fallback peer: \(peer.nodeId)")
362+
}
363+
}
364+
}
365+
} else {
366+
Logger.info("Connected to \(trustedConnected)/\(trustedPeers.count) trusted peers")
326367
}
327368
}
328369

Bitkit/ViewModels/WalletViewModel.swift

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ class WalletViewModel: ObservableObject {
5252
private let transferService: TransferService
5353
private let sheetViewModel: SheetViewModel
5454

55+
enum BlocktankPeerSimulation: String, CaseIterable {
56+
case none = "None"
57+
case apiFailure = "API Failure"
58+
case unreachablePeers = "Unreachable Peers"
59+
}
60+
61+
static var peerSimulation: BlocktankPeerSimulation = .none
62+
5563
@Published var isRestoringWallet = false
5664
@Published var balanceInTransferToSavings: Int = 0
5765
@Published var balanceInTransferToSpending: Int = 0
@@ -206,11 +214,7 @@ class WalletViewModel: ObservableObject {
206214

207215
syncState()
208216

209-
do {
210-
try await lightningService.connectToTrustedPeers()
211-
} catch {
212-
Logger.error("Failed to connect to trusted peers")
213-
}
217+
await reconnectTrustedPeers()
214218

215219
// Migration only: fetch peers from remote backup (once) and persist in ldk-node
216220
let peerUris = await MigrationsService.shared.tryFetchMigrationPeersFromBackup(walletIndex: walletIndex)
@@ -233,6 +237,52 @@ class WalletViewModel: ObservableObject {
233237
}
234238
}
235239

240+
private func fetchTrustedPeersFromBlocktank() async -> [LnPeer]? {
241+
switch Self.peerSimulation {
242+
case .apiFailure:
243+
Logger.warn("⚠️ [DEBUG] Simulating Blocktank API failure — returning nil")
244+
return nil
245+
case .unreachablePeers:
246+
Logger.warn("⚠️ [DEBUG] Simulating unreachable API peers")
247+
return [
248+
LnPeer(nodeId: "000000000000000000000000000000000000000000000000000000000000000001",
249+
host: "192.0.2.1", port: 9735),
250+
]
251+
case .none:
252+
break
253+
}
254+
255+
256+
var info: IBtInfo?
257+
do {
258+
info = try await coreService.blocktank.info(refresh: true)
259+
} catch {
260+
Logger.warn("Blocktank API refresh failed, trying cache: \(error)")
261+
}
262+
if info == nil {
263+
info = try? await coreService.blocktank.info(refresh: false)
264+
}
265+
guard let nodes = info?.nodes, !nodes.isEmpty else { return nil }
266+
let peers = nodes.compactMap { node -> LnPeer? in
267+
guard let connString = node.connectionStrings.first else { return nil }
268+
let address = connString.contains("@") ? String(connString.split(separator: "@").last ?? "") : connString
269+
let parts = address.split(separator: ":")
270+
guard parts.count == 2, let port = UInt16(parts[1]) else { return nil }
271+
return LnPeer(nodeId: node.pubkey, host: String(parts[0]), port: port)
272+
}
273+
Logger.info("Fetched \(peers.count) trusted peers from Blocktank API")
274+
return peers.isEmpty ? nil : peers
275+
}
276+
277+
func reconnectTrustedPeers() async {
278+
do {
279+
let remotePeers = await fetchTrustedPeersFromBlocktank()
280+
try await lightningService.connectToTrustedPeers(remotePeers: remotePeers)
281+
} catch {
282+
Logger.error("Failed to connect to trusted peers")
283+
}
284+
}
285+
236286
func stopLightningNode(clearEventCallback: Bool = false) async throws {
237287
nodeLifecycleState = .stopping
238288
try await lightningService.stop(clearEventCallback: clearEventCallback)

Bitkit/Views/Settings/LdkDebugScreen.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ struct LdkDebugScreen: View {
6666
}
6767
}
6868
}
69+
70+
// Peer Simulation
71+
VStack(alignment: .leading, spacing: 8) {
72+
CaptionMText("Peer Simulation")
73+
74+
Picker("Peer Simulation", selection: Binding(
75+
get: { WalletViewModel.peerSimulation },
76+
set: { WalletViewModel.peerSimulation = $0 }
77+
)) {
78+
ForEach(WalletViewModel.BlocktankPeerSimulation.allCases, id: \.self) { mode in
79+
Text(mode.rawValue).tag(mode)
80+
}
81+
}
82+
.pickerStyle(.segmented)
83+
}
6984
}
7085
}
7186
}
@@ -109,6 +124,7 @@ struct LdkDebugScreen: View {
109124
isRestartingNode = true
110125
let lightningService = LightningService.shared
111126
try await lightningService.restart()
127+
await wallet.reconnectTrustedPeers()
112128
app.toast(type: .success, title: "Node Restarted", description: "Node restarted successfully")
113129
} catch {
114130
Logger.error("Failed to restart node: \(error)")

0 commit comments

Comments
 (0)