Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions Bitkit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = BitkitWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 195;
CURRENT_PROJECT_VERSION = 196;
DEVELOPMENT_TEAM = KYH47R284B;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = BitkitWidget/Info.plist;
Expand All @@ -691,7 +691,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.0;
MARKETING_VERSION = 2.4.1;
PRODUCT_BUNDLE_IDENTIFIER = to.bitkit.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
Expand All @@ -712,7 +712,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = BitkitWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 195;
CURRENT_PROJECT_VERSION = 196;
DEVELOPMENT_TEAM = KYH47R284B;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = BitkitWidget/Info.plist;
Expand All @@ -724,7 +724,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.0;
MARKETING_VERSION = 2.4.1;
PRODUCT_BUNDLE_IDENTIFIER = to.bitkit.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
Expand All @@ -744,7 +744,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = BitkitNotification/BitkitNotification.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 195;
CURRENT_PROJECT_VERSION = 196;
DEVELOPMENT_TEAM = KYH47R284B;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = BitkitNotification/Info.plist;
Expand All @@ -756,7 +756,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.0;
MARKETING_VERSION = 2.4.1;
OTHER_LDFLAGS = (
"-framework",
CoreBluetooth,
Expand All @@ -776,7 +776,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = BitkitNotification/BitkitNotification.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 195;
CURRENT_PROJECT_VERSION = 196;
DEVELOPMENT_TEAM = KYH47R284B;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = BitkitNotification/Info.plist;
Expand All @@ -788,7 +788,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.0;
MARKETING_VERSION = 2.4.1;
OTHER_LDFLAGS = (
"-framework",
CoreBluetooth,
Expand Down Expand Up @@ -926,7 +926,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Bitkit/Bitkit.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 195;
CURRENT_PROJECT_VERSION = 196;
DEVELOPMENT_ASSET_PATHS = "\"Bitkit/Preview Content\"";
DEVELOPMENT_TEAM = KYH47R284B;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -953,7 +953,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 2.4.0;
MARKETING_VERSION = 2.4.1;
OTHER_LDFLAGS = (
"-framework",
CoreBluetooth,
Expand All @@ -975,7 +975,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Bitkit/Bitkit.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 195;
CURRENT_PROJECT_VERSION = 196;
DEVELOPMENT_ASSET_PATHS = "\"Bitkit/Preview Content\"";
DEVELOPMENT_TEAM = KYH47R284B;
ENABLE_HARDENED_RUNTIME = YES;
Expand All @@ -1002,7 +1002,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 2.4.0;
MARKETING_VERSION = 2.4.1;
OTHER_LDFLAGS = (
"-framework",
CoreBluetooth,
Expand Down Expand Up @@ -1176,8 +1176,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/synonymdev/vss-rust-client-ffi";
requirement = {
branch = master;
kind = branch;
kind = exactVersion;
version = 0.5.23;
};
};
4AAB08C82E1FE77600BA63DF /* XCRemoteSwiftPackageReference "lottie-ios" */ = {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 54 additions & 13 deletions Bitkit/Components/ShopWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ struct ShopWebView: UIViewRepresentable {
let url: String
var webView: Binding<WKWebView?>?
var onMessage: ((String) -> Void)?

init(url: String, webView: Binding<WKWebView?>? = nil, onMessage: ((String) -> Void)? = nil) {
var onBlockedNavigation: (() -> Void)?

init(
url: String,
webView: Binding<WKWebView?>? = nil,
onMessage: ((String) -> Void)? = nil,
onBlockedNavigation: (() -> Void)? = nil
) {
self.url = url
self.webView = webView
self.onMessage = onMessage
self.onBlockedNavigation = onBlockedNavigation
}

func makeCoordinator() -> Coordinator {
Expand Down Expand Up @@ -55,28 +62,52 @@ struct ShopWebView: UIViewRepresentable {
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "messageHandler", let body = message.body as? String {
parent.onMessage?(body)
guard message.name == "messageHandler", let body = message.body as? String else { return }
let frameInfo = message.frameInfo
let securityOrigin = frameInfo.securityOrigin
guard ShopOrigin.isAllowedMessageSender(
isMainFrame: frameInfo.isMainFrame,
scheme: securityOrigin.protocol,
host: securityOrigin.host,
port: securityOrigin.port
) else {
Logger.warn(
"Rejected shop payment_intent from untrusted sender '\(securityOrigin.protocol)://\(securityOrigin.host):\(securityOrigin.port)'",
context: "ShopWebView"
)
return
}
parent.onMessage?(body)
}

func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
decisionHandler(.allow)
if navigationAction.targetFrame?.isMainFrame == false {
decisionHandler(.allow)
return
}
if ShopOrigin.shouldAllowMainFrameNavigation(
to: navigationAction.request.url,
initialUrl: parent.url
) {
decisionHandler(.allow)
return
}
Logger.warn(
"Blocked shop navigation to untrusted origin '\(navigationAction.request.url?.absoluteString ?? "")'",
context: "ShopWebView"
)
parent.onBlockedNavigation?()
decisionHandler(.cancel)
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Inject JavaScript to capture postMessage events if message handler is configured
if parent.onMessage != nil {
let script = """
window.addEventListener('message', function(event) {
window.webkit.messageHandlers.messageHandler.postMessage(JSON.stringify(event.data));
});
"""
webView.evaluateJavaScript(script)
webView.evaluateJavaScript(ShopOrigin.messageBridgeScript)
}
}

Expand All @@ -86,9 +117,19 @@ struct ShopWebView: UIViewRepresentable {
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures
) -> WKWebView? {
// Load the navigation request in the current WebView instead of opening a new window
guard ShopOrigin.shouldAllowMainFrameNavigation(
to: navigationAction.request.url,
initialUrl: parent.url
) else {
Logger.warn(
"Blocked shop window navigation to untrusted origin '\(navigationAction.request.url?.absoluteString ?? "")'",
context: "ShopWebView"
)
parent.onBlockedNavigation?()
return nil
}
webView.load(navigationAction.request)
return nil // Return nil to use the current WebView
return nil
}
}
}
1 change: 1 addition & 0 deletions Bitkit/Resources/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@
"other__shop__discover__travel__title" = "Travel";
"other__shop__discover__travel__description" = "Book your ₿ holiday";
"other__shop__main__nav_title" = "Shop";
"other__shop__external_link_blocked" = "This link can’t be opened from the shop.";
"security__backup_wallet" = "Wallet Backup";
"security__backup_title" = "<accent>Safely store</accent> your Bitcoin";
"security__backup_funds" = "Now that you have some funds in your wallet, it is time to back up your money!";
Expand Down
61 changes: 19 additions & 42 deletions Bitkit/Services/LightningService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ class LightningService {

Logger.debug("Checking lightning process lock...")
try await StateLocker.lock(.lightning, wait: 30) // Wait 30 seconds to lock because maybe extension is still running
// Unlock if setup fails before a node exists. After a successful build the node keeps the lock until stop.
var shouldReleaseLightningLock = true
defer {
if shouldReleaseLightningLock {
try? StateLocker.unlock(.lightning)
}
}

guard var mnemonic = try Keychain.loadString(key: .bip39Mnemonic(index: walletIndex)) else {
throw CustomServiceError.mnemonicNotFound
Expand Down Expand Up @@ -136,7 +143,7 @@ class LightningService {
let storeId = try await VssStoreIdProvider.shared.getVssStoreId(walletIndex: walletIndex)

let vssUrl = Env.vssServerUrl
let lnurlAuthServerUrl = Env.lnurlAuthServerUrl
let lnurlAuthServerUrl = Env.lnurlAuthServerUrl.trimmingCharacters(in: .whitespacesAndNewlines)
Logger.debug("Building ldk-node with vssUrl: '\(vssUrl)'")
Logger.debug("Building ldk-node with lnurlAuthServerUrl: '\(lnurlAuthServerUrl)'")

Expand All @@ -147,49 +154,19 @@ class LightningService {

builder.setEntropyBip39Mnemonic(mnemonic: mnemonic, passphrase: passphrase)

try await ServiceQueue.background(.ldk) {
do {
if !lnurlAuthServerUrl.isEmpty {
self.node = try builder.buildWithVssStore(
vssUrl: vssUrl,
storeId: storeId,
lnurlAuthServerUrl: lnurlAuthServerUrl,
fixedHeaders: [:]
)
} else {
self.node = try builder.buildWithVssStoreAndFixedHeaders(
vssUrl: vssUrl,
storeId: storeId,
fixedHeaders: [:]
)
}
} catch let error as BuildError {
guard case .DangerousValue = error else { throw error }
guard !lnurlAuthServerUrl.isEmpty else {
throw CustomServiceError.vssAuthRequired
}

// Stale ChannelMonitor vs ChannelManager — retry with accept_stale to recover.
Logger.warn(
"Build failed with DangerousValue. Retrying with accept_stale_channel_monitors for recovery.",
context: "Recovery"
)
builder.setAcceptStaleChannelMonitors(accept: true)

if !lnurlAuthServerUrl.isEmpty {
self.node = try builder.buildWithVssStore(
vssUrl: vssUrl,
storeId: storeId,
lnurlAuthServerUrl: lnurlAuthServerUrl,
fixedHeaders: [:]
)
} else {
self.node = try builder.buildWithVssStoreAndFixedHeaders(
vssUrl: vssUrl,
storeId: storeId,
fixedHeaders: [:]
)
}
Logger.info("Stale monitor recovery: build succeeded with accept_stale", context: "Recovery")
}
try await ServiceQueue.background(.ldk) {
self.node = try builder.buildWithVssStore(
vssUrl: vssUrl,
storeId: storeId,
lnurlAuthServerUrl: lnurlAuthServerUrl,
fixedHeaders: [:]
)
}
shouldReleaseLightningLock = false

Logger.info("LDK node setup")

Expand Down
13 changes: 12 additions & 1 deletion Bitkit/Services/MigrationsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ enum RNKeychainKey {

// MARK: - Channel Migration Data

struct PendingChannelMigration: Codable {
struct PendingChannelMigration: Codable, Equatable {
let channelManager: Data
let channelMonitors: [Data]
}
Expand Down Expand Up @@ -401,6 +401,17 @@ class MigrationsService: ObservableObject {
set { setCodable(newValue, forKey: Self.rnPendingChannelMigrationKey) }
}

func withPendingChannelMigration(
_ operation: (PendingChannelMigration?) async throws -> Void
) async rethrows {
let migration = pendingChannelMigration
try await operation(migration)

if pendingChannelMigration == migration {
pendingChannelMigration = nil
}
}

/// Stored activity data from RN remote backup for reapplying metadata after sync (persisted)
var pendingRemoteActivityData: [RNActivityItem]? {
get { getCodable(forKey: Self.rnPendingRemoteActivityDataKey) }
Expand Down
Loading
Loading