diff --git a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/CbfClient+Extensions.swift b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/CbfClient+Extensions.swift index 4a5ec154..dec41988 100644 --- a/BDKSwiftExampleWallet/Extensions/BDK+Extensions/CbfClient+Extensions.swift +++ b/BDKSwiftExampleWallet/Extensions/BDK+Extensions/CbfClient+Extensions.swift @@ -128,7 +128,8 @@ extension CbfClient { if Task.isCancelled { break } do { let warning = try await self.nextWarning() - if case .needConnections = warning { + switch warning { + case .needConnections: await MainActor.run { NotificationCenter.default.post( name: NSNotification.Name("KyotoConnectionUpdate"), @@ -136,6 +137,15 @@ extension CbfClient { userInfo: ["connected": false] ) } + case let .transactionRejected(wtxid, reason): + BDKService.shared.handleKyotoRejectedTransaction(wtxidHex: wtxid) + if let reason { + print("Kyoto rejected tx \(wtxid): \(reason)") + } else { + print("Kyoto rejected tx \(wtxid)") + } + default: + break } } catch is CancellationError { break diff --git a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift index 39670b34..cdd6ab80 100644 --- a/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift +++ b/BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift @@ -98,7 +98,7 @@ extension BlockchainClient { } } -private class BDKService { +final class BDKService { static let shared: BDKService = BDKService() private var balance: Balance? @@ -110,6 +110,8 @@ private class BDKService { private(set) var network: Network private var blockchainURL: String internal private(set) var wallet: Wallet? + private var kyotoPendingTxs: [Wtxid: Txid] = [:] + private let kyotoPendingTxQueue = DispatchQueue(label: "bdk.service.kyoto.pending") init(keyClient: KeyClient = .live) { self.keyClient = keyClient @@ -532,6 +534,7 @@ private class BDKService { try? keyClient.deleteEsplora() needsFullScan = true + clearKyotoTrackedTransactions() } func getBackupInfo() throws -> BackupInfo { @@ -576,6 +579,7 @@ private class BDKService { try await self.blockchainClient.broadcast(transaction) if self.clientType == .kyoto { + trackKyotoBroadcast(transaction) let lastSeen = UInt64(Date().timeIntervalSince1970) let unconfirmedTx = UnconfirmedTx(tx: transaction, lastSeen: lastSeen) wallet.applyUnconfirmedTxs(unconfirmedTxs: [unconfirmedTx]) @@ -589,6 +593,40 @@ private class BDKService { } } + private func trackKyotoBroadcast(_ transaction: Transaction) { + let wtxid = transaction.computeWtxid() + let txid = transaction.computeTxid() + kyotoPendingTxQueue.sync { + kyotoPendingTxs[wtxid] = txid + } + } + + private func takeKyotoTx(for wtxid: Wtxid) -> Txid? { + kyotoPendingTxQueue.sync { + kyotoPendingTxs.removeValue(forKey: wtxid) + } + } + + private func clearKyotoTrackedTransactions() { + kyotoPendingTxQueue.sync { + kyotoPendingTxs.removeAll() + } + } + + func handleKyotoRejectedTransaction(wtxidHex: String) { + guard let wtxid = try? Wtxid.fromString(hex: wtxidHex.lowercased()) else { return } + guard let txid = takeKyotoTx(for: wtxid) else { return } + guard let wallet = self.wallet else { return } + let evictedTx = EvictedTx( + txid: txid, + evictedAt: UInt64(Date().timeIntervalSince1970) + ) + wallet.applyEvictedTxs(evictedTxs: [evictedTx]) + if let persister = self.persister { + try? wallet.persist(persister: persister) + } + } + func syncWithInspector(inspector: SyncScriptInspector) async throws { guard let wallet = self.wallet else { throw WalletError.walletNotFound } let syncRequest = try wallet.startSyncWithRevealedSpks()