Skip to content

Commit 45acbc6

Browse files
committed
wip client
1 parent 22ec08b commit 45acbc6

File tree

3 files changed

+129
-32
lines changed

3 files changed

+129
-32
lines changed

BDKSwiftExampleWallet/Service/BDK Service/BDKService.swift

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,64 @@
88
import BitcoinDevKit
99
import Foundation
1010

11+
enum BlockchainClientType: String, CaseIterable {
12+
case esplora = "esplora"
13+
case kyoto = "kyoto" // future
14+
case electrum = "electrum" // future
15+
}
16+
17+
struct BlockchainClient {
18+
let sync: @Sendable (SyncRequest, UInt64) throws -> Update
19+
let fullScan: @Sendable (FullScanRequest, UInt64, UInt64) throws -> Update
20+
let broadcast: @Sendable (Transaction) throws -> Void
21+
let getUrl: @Sendable () -> String
22+
let getType: @Sendable () -> BlockchainClientType
23+
let supportsFullScan: @Sendable () -> Bool = { true }
24+
}
25+
26+
extension BlockchainClient {
27+
static func esplora(url: String) -> Self {
28+
let client = EsploraClient(url: url)
29+
return Self(
30+
sync: { request, parallel in
31+
try client.sync(request: request, parallelRequests: parallel)
32+
},
33+
fullScan: { request, stopGap, parallel in
34+
try client.fullScan(request: request, stopGap: stopGap, parallelRequests: parallel)
35+
},
36+
broadcast: { tx in
37+
try client.broadcast(transaction: tx)
38+
},
39+
getUrl: { url },
40+
getType: { .esplora }
41+
)
42+
}
43+
}
44+
1145
private class BDKService {
1246
static var shared: BDKService = BDKService()
1347

1448
private var balance: Balance?
1549
private var persister: Persister?
16-
private var esploraClient: EsploraClient
50+
private var blockchainClient: BlockchainClient
51+
internal private(set) var clientType: BlockchainClientType
1752
private let keyClient: KeyClient
1853
private var needsFullScan: Bool = false
1954
private(set) var network: Network
20-
private(set) var esploraURL: String
55+
private var blockchainURL: String
2156
private var wallet: Wallet?
2257

2358
init(keyClient: KeyClient = .live) {
2459
self.keyClient = keyClient
2560
let storedNetworkString = try? keyClient.getNetwork() ?? Network.signet.description
2661
self.network = Network(stringValue: storedNetworkString ?? "") ?? .signet
2762

28-
self.esploraURL = (try? keyClient.getEsploraURL()) ?? self.network.url
29-
30-
self.esploraClient = EsploraClient(url: self.esploraURL)
63+
let storedClientType = try? keyClient.getClientType()
64+
self.clientType = storedClientType ?? .esplora
65+
66+
self.blockchainURL = (try? keyClient.getEsploraURL()) ?? self.network.url
67+
self.blockchainClient = BlockchainClient.esplora(url: self.blockchainURL)
68+
updateBlockchainClient()
3169
}
3270

3371
func updateNetwork(_ newNetwork: Network) {
@@ -40,20 +78,33 @@ private class BDKService {
4078
try? keyClient.saveNetwork(newNetwork.description)
4179

4280
let newURL = newNetwork.url
43-
updateEsploraURL(newURL)
81+
updateBlockchainURL(newURL)
4482
}
4583
}
4684

47-
func updateEsploraURL(_ newURL: String) {
48-
if newURL != self.esploraURL {
49-
self.esploraURL = newURL
50-
try? keyClient.saveEsploraURL(newURL)
51-
updateEsploraClient()
85+
func updateBlockchainURL(_ newURL: String) {
86+
if newURL != self.blockchainURL {
87+
self.blockchainURL = newURL
88+
try? keyClient.saveEsploraURL(newURL) // TODO: Future - saveURL(newURL, for: clientType)
89+
updateBlockchainClient()
5290
}
5391
}
5492

55-
private func updateEsploraClient() {
56-
self.esploraClient = EsploraClient(url: self.esploraURL)
93+
internal func updateBlockchainClient() {
94+
do {
95+
switch clientType {
96+
case .esplora:
97+
self.blockchainClient = .esplora(url: self.blockchainURL)
98+
case .kyoto:
99+
throw WalletError.backendNotImplemented
100+
case .electrum:
101+
throw WalletError.backendNotImplemented
102+
}
103+
} catch {
104+
// Fallback to esplora if selected backend not implemented
105+
self.clientType = .esplora
106+
self.blockchainClient = .esplora(url: self.blockchainURL)
107+
}
57108
}
58109

59110
private func getCurrentAddressType() -> AddressType {
@@ -211,8 +262,8 @@ private class BDKService {
211262
try keyClient.saveBackupInfo(backupInfo)
212263
try keyClient.saveNetwork(self.network.description)
213264
try keyClient.saveEsploraURL(baseUrl)
214-
self.esploraURL = baseUrl
215-
updateEsploraClient()
265+
self.blockchainURL = baseUrl
266+
updateBlockchainClient()
216267

217268
let wallet = try Wallet(
218269
descriptor: descriptor,
@@ -313,8 +364,8 @@ private class BDKService {
313364
try keyClient.saveBackupInfo(backupInfo)
314365
try keyClient.saveNetwork(self.network.description)
315366
try keyClient.saveEsploraURL(baseUrl)
316-
self.esploraURL = baseUrl
317-
updateEsploraClient()
367+
self.blockchainURL = baseUrl
368+
updateBlockchainClient()
318369

319370
let wallet = try Wallet(
320371
descriptor: descriptor,
@@ -441,22 +492,20 @@ private class BDKService {
441492
let isSigned = try wallet.sign(psbt: psbt)
442493
if isSigned {
443494
let transaction = try psbt.extractTx()
444-
let client = self.esploraClient
445-
try client.broadcast(transaction: transaction)
495+
try self.blockchainClient.broadcast(transaction)
446496
} else {
447497
throw WalletError.notSigned
448498
}
449499
}
450500

451501
func syncWithInspector(inspector: SyncScriptInspector) async throws {
452502
guard let wallet = self.wallet else { throw WalletError.walletNotFound }
453-
let esploraClient = self.esploraClient
454503
let syncRequest = try wallet.startSyncWithRevealedSpks()
455504
.inspectSpks(inspector: inspector)
456505
.build()
457-
let update = try esploraClient.sync(
458-
request: syncRequest,
459-
parallelRequests: UInt64(5)
506+
let update = try self.blockchainClient.sync(
507+
syncRequest,
508+
UInt64(5)
460509
)
461510
let _ = try wallet.applyUpdate(update: update)
462511
guard let persister = self.persister else {
@@ -467,16 +516,18 @@ private class BDKService {
467516

468517
func fullScanWithInspector(inspector: FullScanScriptInspector) async throws {
469518
guard let wallet = self.wallet else { throw WalletError.walletNotFound }
470-
let esploraClient = esploraClient
519+
guard self.blockchainClient.supportsFullScan() else {
520+
throw WalletError.fullScanUnsupported
521+
}
471522
let fullScanRequest = try wallet.startFullScan()
472523
.inspectSpksForAllKeychains(inspector: inspector)
473524
.build()
474-
let update = try esploraClient.fullScan(
475-
request: fullScanRequest,
525+
let update = try self.blockchainClient.fullScan(
526+
fullScanRequest,
476527
// using https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit
477-
stopGap: UInt64(20),
528+
UInt64(20),
478529
// using https://github.com/bitcoindevkit/bdk/blob/master/example-crates/example_wallet_esplora_blocking/src/main.rs
479-
parallelRequests: UInt64(5)
530+
UInt64(5)
480531
)
481532
let _ = try wallet.applyUpdate(update: update)
482533
guard let persister = self.persister else {
@@ -534,6 +585,20 @@ extension BDKService {
534585
func updateAddressType(_ newAddressType: AddressType) {
535586
try? keyClient.saveAddressType(newAddressType.description)
536587
}
588+
589+
func updateClientType(_ newType: BlockchainClientType) {
590+
self.clientType = newType
591+
try? keyClient.saveClientType(newType)
592+
updateBlockchainClient()
593+
}
594+
595+
var esploraURL: String {
596+
return blockchainURL
597+
}
598+
599+
func updateEsploraURL(_ newURL: String) {
600+
updateBlockchainURL(newURL)
601+
}
537602
}
538603

539604
struct BDKClient {
@@ -563,6 +628,8 @@ struct BDKClient {
563628
let updateEsploraURL: (String) -> Void
564629
let getAddressType: () -> AddressType
565630
let updateAddressType: (AddressType) -> Void
631+
let getClientType: () -> BlockchainClientType
632+
let updateClientType: (BlockchainClientType) -> Void
566633
}
567634

568635
extension BDKClient {
@@ -622,6 +689,10 @@ extension BDKClient {
622689
},
623690
updateAddressType: { newAddressType in
624691
BDKService.shared.updateAddressType(newAddressType)
692+
},
693+
getClientType: { BDKService.shared.clientType },
694+
updateClientType: { newType in
695+
BDKService.shared.updateClientType(newType)
625696
}
626697
)
627698
}
@@ -681,7 +752,9 @@ extension BDKClient {
681752
updateNetwork: { _ in },
682753
updateEsploraURL: { _ in },
683754
getAddressType: { .bip86 },
684-
updateAddressType: { _ in }
755+
updateAddressType: { _ in },
756+
getClientType: { .esplora },
757+
updateClientType: { _ in }
685758
)
686759
}
687760
#endif

BDKSwiftExampleWallet/Service/BDK Service/BDKSwiftExampleWalletError.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ enum WalletError: Error {
1212
case dbNotFound
1313
case notSigned
1414
case walletNotFound
15+
case fullScanUnsupported
16+
case backendNotImplemented
1517
}

BDKSwiftExampleWallet/Service/Key Service/KeyService.swift

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ private struct KeyService {
7070
func saveAddressType(addressType: String) throws {
7171
keychain[string: "SelectedAddressType"] = addressType
7272
}
73+
74+
func getClientType() throws -> String? {
75+
return keychain[string: "SelectedClientType"]
76+
}
77+
78+
func saveClientType(_ clientType: String) throws {
79+
keychain[string: "SelectedClientType"] = clientType
80+
}
7381
}
7482

7583
struct KeyClient {
@@ -84,6 +92,8 @@ struct KeyClient {
8492
let saveBackupInfo: (BackupInfo) throws -> Void
8593
let saveNetwork: (String) throws -> Void
8694
let saveAddressType: (String) throws -> Void
95+
let getClientType: () throws -> BlockchainClientType
96+
let saveClientType: (BlockchainClientType) throws -> Void
8797

8898
private init(
8999
deleteBackupInfo: @escaping () throws -> Void,
@@ -96,7 +106,9 @@ struct KeyClient {
96106
saveBackupInfo: @escaping (BackupInfo) throws -> Void,
97107
saveEsploraURL: @escaping (String) throws -> Void,
98108
saveNetwork: @escaping (String) throws -> Void,
99-
saveAddressType: @escaping (String) throws -> Void
109+
saveAddressType: @escaping (String) throws -> Void,
110+
getClientType: @escaping () throws -> BlockchainClientType,
111+
saveClientType: @escaping (BlockchainClientType) throws -> Void
100112
) {
101113
self.deleteBackupInfo = deleteBackupInfo
102114
self.deleteEsplora = deleteEsplora
@@ -109,6 +121,8 @@ struct KeyClient {
109121
self.saveEsploraURL = saveEsploraURL
110122
self.saveNetwork = saveNetwork
111123
self.saveAddressType = saveAddressType
124+
self.getClientType = getClientType
125+
self.saveClientType = saveClientType
112126
}
113127
}
114128

@@ -124,7 +138,13 @@ extension KeyClient {
124138
saveBackupInfo: { backupInfo in try KeyService().saveBackupInfo(backupInfo: backupInfo) },
125139
saveEsploraURL: { url in try KeyService().saveEsploraURL(url: url) },
126140
saveNetwork: { network in try KeyService().saveNetwork(network: network) },
127-
saveAddressType: { addressType in try KeyService().saveAddressType(addressType: addressType)
141+
saveAddressType: { addressType in try KeyService().saveAddressType(addressType: addressType) },
142+
getClientType: {
143+
let raw = try KeyService().getClientType()
144+
return BlockchainClientType(rawValue: raw ?? "") ?? .esplora
145+
},
146+
saveClientType: { type in
147+
try KeyService().saveClientType(type.rawValue)
128148
}
129149
)
130150
}
@@ -167,7 +187,9 @@ extension KeyClient {
167187
saveBackupInfo: { _ in },
168188
saveEsploraURL: { _ in },
169189
saveNetwork: { _ in },
170-
saveAddressType: { _ in }
190+
saveAddressType: { _ in },
191+
getClientType: { .esplora },
192+
saveClientType: { _ in }
171193
)
172194
}
173195
#endif

0 commit comments

Comments
 (0)