Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Bitkit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1200,7 +1200,7 @@
repositoryURL = "https://github.com/synonymdev/bitkit-core";
requirement = {
kind = exactVersion;
version = 0.1.58;
version = 0.1.64;
};
};
96E20CD22CB6D91A00C24149 /* XCRemoteSwiftPackageReference "CodeScanner" */ = {
Expand Down

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

1 change: 1 addition & 0 deletions Bitkit/MainNavView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ struct MainNavView: View {
case .ldkDebug: LdkDebugScreen()
case .vssDebug: VssDebugScreen()
case .probingTool: ProbingToolScreen()
case .legacyRnRecovery: LegacyRnRecoveryScreen()
case .orders: ChannelOrders()
case .logs: LogView()
case .trezor: TrezorRootView()
Expand Down
55 changes: 55 additions & 0 deletions Bitkit/Services/CoreService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1916,6 +1916,61 @@ class UtilityService {
self.coreService = coreService
}

private func recoveryWalletCredentials(walletIndex: Int) throws -> (mnemonic: String, passphrase: String?) {
guard let mnemonic = try Keychain.loadString(key: .bip39Mnemonic(index: walletIndex)) else {
throw AppError(message: "Mnemonic not found", debugMessage: "Unable to load mnemonic for wallet index \(walletIndex)")
}

let passphrase = try Keychain.loadString(key: .bip39Passphrase(index: walletIndex))
return (mnemonic, passphrase)
}

func scanLegacyRnNativeSegwitRecoveryFunds(
walletIndex: Int = 0,
indexLimit: UInt32,
electrumUrl: String = Env.electrumServerUrl
) async throws -> LegacyRnCloseRecoveryScanResult {
try await ServiceQueue.background(.core) {
let credentials = try self.recoveryWalletCredentials(walletIndex: walletIndex)

return try await BitkitCore.scanLegacyRnNativeSegwitRecoveryFunds(
mnemonicPhrase: credentials.mnemonic,
network: Env.bitkitCoreNetwork,
electrumUrl: electrumUrl,
indexLimit: indexLimit,
bip39Passphrase: credentials.passphrase
)
}
}

func prepareLegacyRnNativeSegwitRecoverySweep(
destinationAddress: String,
feeRateSatsPerVbyte: UInt32?,
walletIndex: Int = 0,
indexLimit: UInt32,
electrumUrl: String = Env.electrumServerUrl
) async throws -> LegacyRnCloseRecoverySweepPreview {
try await ServiceQueue.background(.core) {
let credentials = try self.recoveryWalletCredentials(walletIndex: walletIndex)

return try await BitkitCore.prepareLegacyRnNativeSegwitRecoverySweep(
mnemonicPhrase: credentials.mnemonic,
network: Env.bitkitCoreNetwork,
electrumUrl: electrumUrl,
destinationAddress: destinationAddress,
feeRateSatsPerVbyte: feeRateSatsPerVbyte,
indexLimit: indexLimit,
bip39Passphrase: credentials.passphrase
)
}
}

func broadcastRawTx(txHex: String, electrumUrl: String = Env.electrumServerUrl) async throws -> String {
try await ServiceQueue.background(.core) {
return try await onchainBroadcastRawTx(serializedTx: txHex, electrumUrl: electrumUrl)
}
}

func getAccountAddresses(
walletIndex: Int = 0,
isChange: Bool? = nil,
Expand Down
2 changes: 1 addition & 1 deletion Bitkit/Services/Trezor/TrezorService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class TrezorService {
func connect(deviceId: String) async throws -> TrezorFeatures {
try await ServiceQueue.background(.core) { [self] in
ensureCallbacksRegistered()
return try await trezorConnect(deviceId: deviceId)
return try await trezorConnect(deviceId: deviceId, selection: .standard)
}
}

Expand Down
20 changes: 14 additions & 6 deletions Bitkit/Services/Trezor/TrezorUiHandler.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BitkitCore
import Combine

Check warning on line 2 in Bitkit/Services/Trezor/TrezorUiHandler.swift

View workflow job for this annotation

GitHub Actions / Run Tests

add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Combine'

Check warning on line 2 in Bitkit/Services/Trezor/TrezorUiHandler.swift

View workflow job for this annotation

GitHub Actions / Run Integration Tests

add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Combine'
import Foundation

/// Implementation of TrezorUiCallback protocol for PIN and passphrase handling.
Expand All @@ -11,9 +11,9 @@
// MARK: - PIN Handling

/// Publisher to notify UI when PIN entry is needed
let needsPinPublisher = PassthroughSubject<Void, Never>()

Check warning on line 14 in Bitkit/Services/Trezor/TrezorUiHandler.swift

View workflow job for this annotation

GitHub Actions / Run Tests

stored property 'needsPinPublisher' of 'Sendable'-conforming class 'TrezorUiHandler' has non-sendable type 'PassthroughSubject<Void, Never>'; this is an error in the Swift 6 language mode

Check warning on line 14 in Bitkit/Services/Trezor/TrezorUiHandler.swift

View workflow job for this annotation

GitHub Actions / Run Integration Tests

stored property 'needsPinPublisher' of 'Sendable'-conforming class 'TrezorUiHandler' has non-sendable type 'PassthroughSubject<Void, Never>'; this is an error in the Swift 6 language mode

private var submittedPin: String = ""

Check warning on line 16 in Bitkit/Services/Trezor/TrezorUiHandler.swift

View workflow job for this annotation

GitHub Actions / Run Tests

stored property 'submittedPin' of 'Sendable'-conforming class 'TrezorUiHandler' is mutable; this is an error in the Swift 6 language mode

Check warning on line 16 in Bitkit/Services/Trezor/TrezorUiHandler.swift

View workflow job for this annotation

GitHub Actions / Run Integration Tests

stored property 'submittedPin' of 'Sendable'-conforming class 'TrezorUiHandler' is mutable; this is an error in the Swift 6 language mode
private let pinLock = NSLock()
private let pinSemaphore = DispatchSemaphore(value: 0)

Expand All @@ -24,6 +24,7 @@
let needsPassphrasePublisher = PassthroughSubject<Bool, Never>()

private var submittedPassphrase: String = ""
private var didCancelPassphrase = false
private let passphraseLock = NSLock()
private let passphraseSemaphore = DispatchSemaphore(value: 0)

Expand Down Expand Up @@ -75,11 +76,12 @@
return pin
}

func onPassphraseRequest(onDevice: Bool) -> String {
func onPassphraseRequest(onDevice: Bool) -> PassphraseResponse {
debugLog("onPassphraseRequest: onDevice=\(onDevice), waiting for user input...")

passphraseLock.lock()
submittedPassphrase = ""
didCancelPassphrase = false
passphraseLock.unlock()

awaitingLock.lock()
Expand All @@ -101,21 +103,26 @@

if result == .timedOut {
debugLog("onPassphraseRequest: timed out")
return ""
return .cancel
}

if onDevice {
// For on-device entry, return any non-empty string to acknowledge
debugLog("onPassphraseRequest(onDevice): acknowledged")
return "ok"
return .onDevice
}

passphraseLock.lock()
let passphrase = submittedPassphrase
let wasCancelled = didCancelPassphrase
passphraseLock.unlock()

debugLog("onPassphraseRequest: \(passphrase.isEmpty ? "cancelled" : "received")")
return passphrase
if wasCancelled {
debugLog("onPassphraseRequest: cancelled")
return .cancel
}

debugLog("onPassphraseRequest: \(passphrase.isEmpty ? "standard wallet" : "received")")
return passphrase.isEmpty ? .standard : .hidden(value: passphrase)
}

// MARK: - UI Submit/Cancel Methods
Expand Down Expand Up @@ -152,6 +159,7 @@
debugLog("cancelPassphrase")
passphraseLock.lock()
submittedPassphrase = ""
didCancelPassphrase = true
passphraseLock.unlock()
passphraseSemaphore.signal()
}
Expand Down
1 change: 1 addition & 0 deletions Bitkit/ViewModels/NavigationViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ enum Route: Hashable {
case ldkDebug
case vssDebug
case probingTool
case legacyRnRecovery
case orders
case logs
case trezor
Expand Down
Loading
Loading