Skip to content

Commit a9b1c3c

Browse files
committed
kyoto: breaking changes
1 parent 511091f commit a9b1c3c

File tree

5 files changed

+53
-86
lines changed

5 files changed

+53
-86
lines changed

BDKSwiftExampleWallet/Extensions/BDK+Extensions/CbfClient+Extensions.swift

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,26 @@ extension CbfClient {
1212
// Track monitoring tasks per client for clean cancellation
1313
private static var monitoringTasks: [ObjectIdentifier: Task<Void, Never>] = [:]
1414
private static var warningTasks: [ObjectIdentifier: Task<Void, Never>] = [:]
15-
private static var logTasks: [ObjectIdentifier: Task<Void, Never>] = [:]
1615
private static var heartbeatTasks: [ObjectIdentifier: Task<Void, Never>] = [:]
1716
private static var lastInfoAt: [ObjectIdentifier: Date] = [:]
1817
private static let monitoringTasksQueue = DispatchQueue(label: "cbf.monitoring.tasks")
1918

20-
static func createComponents(wallet: Wallet) -> (client: CbfClient, node: CbfNode) {
19+
static func createComponents(
20+
wallet: Wallet,
21+
scanType: ScanType,
22+
peers: [Peer]
23+
) -> (client: CbfClient, node: CbfNode) {
2124
do {
25+
let network = wallet.network()
26+
let dataDir = Constants.Config.Kyoto.dbPath
27+
print(
28+
"[Kyoto] Preparing CBF components – network: \(network), dataDir: \(dataDir), peers: \(peers.count), scanType: \(scanType)"
29+
)
2230

2331
let components = try CbfBuilder()
24-
.logLevel(logLevel: .debug)
25-
.scanType(scanType: .sync)
26-
.dataDir(dataDir: Constants.Config.Kyoto.dbPath)
27-
.peers(peers: Constants.Networks.Signet.Regular.kyotoPeers)
32+
.scanType(scanType: scanType)
33+
.dataDir(dataDir: dataDir)
34+
.peers(peers: peers)
2835
.build(wallet: wallet)
2936

3037
components.node.run()
@@ -47,34 +54,29 @@ extension CbfClient {
4754
let info = try await self.nextInfo()
4855
CbfClient.monitoringTasksQueue.sync { Self.lastInfoAt[id] = Date() }
4956
switch info {
50-
case .progress(let progress):
57+
case .progress(let chainHeight, let filtersDownloadedPercent):
5158
await MainActor.run {
5259
NotificationCenter.default.post(
5360
name: NSNotification.Name("KyotoProgressUpdate"),
5461
object: nil,
55-
userInfo: ["progress": progress]
62+
userInfo: [
63+
"progress": filtersDownloadedPercent,
64+
"height": Int(chainHeight),
65+
]
5666
)
57-
}
58-
case .newChainHeight(let height):
59-
await MainActor.run {
6067
NotificationCenter.default.post(
6168
name: NSNotification.Name("KyotoChainHeightUpdate"),
6269
object: nil,
63-
userInfo: ["height": height]
70+
userInfo: ["height": Int(chainHeight)]
6471
)
6572
NotificationCenter.default.post(
6673
name: NSNotification.Name("KyotoConnectionUpdate"),
6774
object: nil,
6875
userInfo: ["connected": true]
6976
)
7077
}
71-
case .stateUpdate(let nodeState):
78+
case .blockReceived(_):
7279
await MainActor.run {
73-
NotificationCenter.default.post(
74-
name: NSNotification.Name("KyotoStateUpdate"),
75-
object: nil,
76-
userInfo: ["state": nodeState]
77-
)
7880
NotificationCenter.default.post(
7981
name: NSNotification.Name("KyotoConnectionUpdate"),
8082
object: nil,
@@ -89,8 +91,6 @@ extension CbfClient {
8991
userInfo: ["connected": true]
9092
)
9193
}
92-
default:
93-
break
9494
}
9595
} catch is CancellationError {
9696
break
@@ -149,23 +149,6 @@ extension CbfClient {
149149
Self.warningTasks[id] = warnings
150150
}
151151

152-
// Log listener for detailed debugging
153-
let logs = Task { [self] in
154-
while true {
155-
if Task.isCancelled { break }
156-
do {
157-
let log = try await self.nextLog()
158-
} catch is CancellationError {
159-
break
160-
} catch {
161-
// ignore
162-
}
163-
}
164-
}
165-
166-
Self.monitoringTasksQueue.sync {
167-
Self.logTasks[id] = logs
168-
}
169152
}
170153

171154
func stopBackgroundMonitoring() {
@@ -175,7 +158,6 @@ extension CbfClient {
175158
task.cancel()
176159
if let hb = Self.heartbeatTasks.removeValue(forKey: id) { hb.cancel() }
177160
if let wt = Self.warningTasks.removeValue(forKey: id) { wt.cancel() }
178-
if let lt = Self.logTasks.removeValue(forKey: id) { lt.cancel() }
179161
Self.lastInfoAt.removeValue(forKey: id)
180162
}
181163
}
@@ -184,11 +166,9 @@ extension CbfClient {
184166
Self.monitoringTasksQueue.sync {
185167
for (_, task) in Self.monitoringTasks { task.cancel() }
186168
for (_, wt) in Self.warningTasks { wt.cancel() }
187-
for (_, lt) in Self.logTasks { lt.cancel() }
188169
for (_, hb) in Self.heartbeatTasks { hb.cancel() }
189170
Self.monitoringTasks.removeAll()
190171
Self.warningTasks.removeAll()
191-
Self.logTasks.removeAll()
192172
Self.heartbeatTasks.removeAll()
193173
Self.lastInfoAt.removeAll()
194174
}

BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ enum BlockchainClientType: String, CaseIterable {
1717
struct BlockchainClient {
1818
let sync: @Sendable (SyncRequest, UInt64) async throws -> Update
1919
let fullScan: @Sendable (FullScanRequest, UInt64, UInt64) async throws -> Update
20-
let broadcast: @Sendable (Transaction) throws -> Void
20+
let broadcast: @Sendable (Transaction) async throws -> Void
2121
let getUrl: @Sendable () -> String
2222
let getType: @Sendable () -> BlockchainClientType
2323
let supportsFullScan: @Sendable () -> Bool = { true }
@@ -55,7 +55,24 @@ extension BlockchainClient {
5555

5656
try FileManager.default.ensureDirectoryExists(at: Constants.Config.Kyoto.dbDirectoryURL)
5757

58-
let components = CbfClient.createComponents(wallet: wallet)
58+
let scanType: ScanType
59+
if BDKService.shared.needsFullScanOfWallet() {
60+
let addressType = BDKService.shared.getAddressType()
61+
let checkpoint: RecoveryPoint =
62+
addressType == .bip86 ? .taprootActivation : .segwitActivation
63+
scanType = .recovery(
64+
usedScriptIndex: 1000,
65+
checkpoint: checkpoint
66+
)
67+
} else {
68+
scanType = .sync
69+
}
70+
71+
let components = CbfClient.createComponents(
72+
wallet: wallet,
73+
scanType: scanType,
74+
peers: Constants.Networks.Signet.Regular.kyotoPeers
75+
)
5976
cbfComponents = components
6077
return components
6178
}
@@ -73,7 +90,7 @@ extension BlockchainClient {
7390
},
7491
broadcast: { tx in
7592
let components = try getOrCreateComponents()
76-
try components.client.broadcast(transaction: tx)
93+
try await components.client.broadcast(transaction: tx)
7794
},
7895
getUrl: { peer },
7996
getType: { .kyoto }
@@ -556,7 +573,7 @@ private class BDKService {
556573
let isSigned = try wallet.sign(psbt: psbt)
557574
if isSigned {
558575
let transaction = try psbt.extractTx()
559-
try self.blockchainClient.broadcast(transaction)
576+
try await self.blockchainClient.broadcast(transaction)
560577

561578
if self.clientType == .kyoto {
562579
let lastSeen = UInt64(Date().timeIntervalSince1970)

BDKSwiftExampleWallet/View Model/WalletViewModel.swift

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ class WalletViewModel {
4747
}
4848
var isKyotoConnected: Bool = false
4949
var currentBlockHeight: UInt32 = 0
50-
var kyotoNodeState: NodeState?
5150

5251
private var updateProgress: @Sendable (UInt64, UInt64) -> Void {
5352
{ [weak self] inspected, total in
@@ -105,6 +104,9 @@ class WalletViewModel {
105104
if self.bdkClient.getClientType() != .kyoto { return }
106105
if let progress = notification.userInfo?["progress"] as? Float {
107106
self.updateKyotoProgress(progress)
107+
if let height = notification.userInfo?["height"] as? Int {
108+
self.currentBlockHeight = UInt32(max(height, 0))
109+
}
108110
// Consider any progress update as evidence of an active connection
109111
// so the UI does not falsely show a red disconnected indicator while syncing.
110112
if progress > 0 {
@@ -148,8 +150,8 @@ class WalletViewModel {
148150
guard let self else { return }
149151
// Ignore Kyoto updates unless client type is Kyoto
150152
if self.bdkClient.getClientType() != .kyoto { return }
151-
if let height = notification.userInfo?["height"] as? UInt32 {
152-
self.currentBlockHeight = height
153+
if let height = notification.userInfo?["height"] as? Int {
154+
self.currentBlockHeight = UInt32(max(height, 0))
153155
// Receiving chain height implies we have peer connectivity
154156
self.isKyotoConnected = true
155157
// Ensure UI reflects syncing as soon as we see chain activity
@@ -162,23 +164,6 @@ class WalletViewModel {
162164
}
163165
}
164166
}
165-
166-
NotificationCenter.default.addObserver(
167-
forName: NSNotification.Name("KyotoStateUpdate"),
168-
object: nil,
169-
queue: .main
170-
) { [weak self] notification in
171-
guard let self else { return }
172-
if self.bdkClient.getClientType() != .kyoto { return }
173-
if let nodeState = notification.userInfo?["state"] as? NodeState {
174-
self.kyotoNodeState = nodeState
175-
if nodeState == .transactionsSynced {
176-
self.walletSyncState = .synced
177-
} else {
178-
self.walletSyncState = .syncing
179-
}
180-
}
181-
}
182167
}
183168

184169
private func fullScanWithProgress() async {

BDKSwiftExampleWallet/View/Home/ActivityHomeHeaderView.swift

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// Created by Rubens Machion on 24/04/25.
66
//
77

8-
import BitcoinDevKit
98
import SwiftUI
109

1110
struct ActivityHomeHeaderView: View {
@@ -18,7 +17,6 @@ struct ActivityHomeHeaderView: View {
1817
let isKyotoClient: Bool
1918
let isKyotoConnected: Bool
2019
let currentBlockHeight: UInt32
21-
let kyotoNodeState: NodeState?
2220

2321
let showAllTransactions: () -> Void
2422

@@ -209,25 +207,13 @@ struct ActivityHomeHeaderView: View {
209207

210208
extension ActivityHomeHeaderView {
211209
fileprivate var kyotoStatusText: String? {
212-
guard isKyotoClient, let kyotoNodeState else { return nil }
213-
// Kyoto's NodeState reflects the next stage it will enter, so describe upcoming work.
214-
switch kyotoNodeState {
215-
case .behind:
216-
// Still acquiring header tips, so call out the header sync explicitly.
217-
return "Getting headers..."
218-
case .headersSynced:
219-
// Kyoto reports this once headers are already finished, so surface the next
220-
// actionable phase the node is entering rather than the completed step.
221-
return "Preparing filters..."
222-
case .filterHeadersSynced:
223-
// Filter headers are ready; actual filter scanning starts next.
224-
return "Scanning filters..."
225-
case .filtersSynced:
226-
// Filters are exhausted; the node now gossips for matching blocks/txs.
227-
return "Fetching matches..."
228-
case .transactionsSynced:
229-
// No further phases—fall back to showing percent + standard synced UI.
210+
guard isKyotoClient else { return nil }
211+
if walletSyncState == .synced || progress >= 100 {
230212
return nil
231213
}
214+
if progress <= 0 {
215+
return isKyotoConnected ? "Getting headers..." : "Connecting..."
216+
}
217+
return "Scanning filters..."
232218
}
233219
}

BDKSwiftExampleWallet/View/WalletView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ struct WalletView: View {
5151
needsFullScan: viewModel.needsFullScan,
5252
isKyotoClient: viewModel.isKyotoClient,
5353
isKyotoConnected: viewModel.isKyotoConnected,
54-
currentBlockHeight: viewModel.currentBlockHeight,
55-
kyotoNodeState: viewModel.kyotoNodeState
54+
currentBlockHeight: viewModel.currentBlockHeight
5655
) {
5756
showAllTransactions = true
5857
}

0 commit comments

Comments
 (0)