react-native-zcash 0.6.9 → 0.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2440000.json +8 -0
  3. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2450000.json +8 -0
  4. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2460000.json +8 -0
  5. package/ios/ZCashLightClientKit/Block/CompactBlockProcessor.swift +937 -425
  6. package/ios/ZCashLightClientKit/Block/Download/BlockDownloader.swift +17 -31
  7. package/ios/ZCashLightClientKit/Block/Download/BlockDownloaderService.swift +2 -2
  8. package/ios/ZCashLightClientKit/Block/Enhance/BlockEnhancer.swift +15 -46
  9. package/ios/ZCashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +15 -4
  10. package/ios/ZCashLightClientKit/Block/FilesystemStorage/FSCompactBlockRepository.swift +4 -4
  11. package/ios/ZCashLightClientKit/Block/Scan/BlockScanner.swift +35 -10
  12. package/ios/ZCashLightClientKit/Block/Utils/InternalSyncProgress.swift +200 -0
  13. package/ios/ZCashLightClientKit/Block/Validate/BlockValidator.swift +51 -0
  14. package/ios/ZCashLightClientKit/ClosureSynchronizer.swift +2 -1
  15. package/ios/ZCashLightClientKit/CombineSynchronizer.swift +5 -2
  16. package/ios/ZCashLightClientKit/Constants/ZcashSDK.swift +26 -13
  17. package/ios/ZCashLightClientKit/DAO/BlockDao.swift +112 -0
  18. package/ios/ZCashLightClientKit/DAO/TransactionDao.swift +42 -40
  19. package/ios/ZCashLightClientKit/DAO/UnspentTransactionOutputDao.swift +4 -13
  20. package/ios/ZCashLightClientKit/Entity/AccountEntity.swift +0 -9
  21. package/ios/ZCashLightClientKit/Entity/BlockProgress.swift +24 -0
  22. package/ios/ZCashLightClientKit/Entity/TransactionEntity.swift +10 -7
  23. package/ios/ZCashLightClientKit/Error/Sourcery/generateErrorCode.sh +1 -1
  24. package/ios/ZCashLightClientKit/Error/ZcashError.swift +12 -121
  25. package/ios/ZCashLightClientKit/Error/ZcashErrorCode.swift +5 -43
  26. package/ios/ZCashLightClientKit/Error/ZcashErrorCodeDefinition.swift +6 -72
  27. package/ios/ZCashLightClientKit/Initializer.swift +26 -47
  28. package/ios/ZCashLightClientKit/Metrics/SDKMetrics.swift +12 -0
  29. package/ios/ZCashLightClientKit/Model/Checkpoint.swift +0 -12
  30. package/ios/ZCashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +0 -15
  31. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/compact_formats.pb.swift +46 -150
  32. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto +16 -30
  33. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/service.proto +6 -32
  34. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.grpc.swift +22 -259
  35. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.pb.swift +7 -193
  36. package/ios/ZCashLightClientKit/Modules/Service/LightWalletService.swift +0 -8
  37. package/ios/ZCashLightClientKit/Providers/LatestBlocksDataProvider.swift +28 -18
  38. package/ios/ZCashLightClientKit/Repository/CompactBlockRepository.swift +1 -1
  39. package/ios/ZCashLightClientKit/Repository/TransactionRepository.swift +6 -2
  40. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2440000.json +8 -0
  41. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2450000.json +8 -0
  42. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2460000.json +8 -0
  43. package/ios/ZCashLightClientKit/Rust/ZcashRustBackend.swift +158 -293
  44. package/ios/ZCashLightClientKit/Rust/ZcashRustBackendWelding.swift +64 -58
  45. package/ios/ZCashLightClientKit/Rust/zcashlc.h +513 -619
  46. package/ios/ZCashLightClientKit/Synchronizer/ClosureSDKSynchronizer.swift +8 -2
  47. package/ios/ZCashLightClientKit/Synchronizer/CombineSDKSynchronizer.swift +15 -3
  48. package/ios/ZCashLightClientKit/Synchronizer/Dependencies.swift +30 -11
  49. package/ios/ZCashLightClientKit/Synchronizer/SDKSynchronizer.swift +50 -41
  50. package/ios/ZCashLightClientKit/Synchronizer.swift +65 -51
  51. package/ios/ZCashLightClientKit/Transaction/TransactionEncoder.swift +2 -2
  52. package/ios/ZCashLightClientKit/Transaction/WalletTransactionEncoder.swift +7 -7
  53. package/ios/ZCashLightClientKit/Utils/OSLogger.swift +3 -3
  54. package/ios/libzcashlc.xcframework/Info.plist +0 -4
  55. package/ios/libzcashlc.xcframework/ios-arm64/libzcashlc.a +0 -0
  56. package/ios/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.a +0 -0
  57. package/package.json +1 -1
  58. package/ios/ZCashLightClientKit/Block/Actions/Action.swift +0 -98
  59. package/ios/ZCashLightClientKit/Block/Actions/ClearAlreadyScannedBlocksAction.swift +0 -35
  60. package/ios/ZCashLightClientKit/Block/Actions/ClearCacheAction.swift +0 -30
  61. package/ios/ZCashLightClientKit/Block/Actions/DownloadAction.swift +0 -67
  62. package/ios/ZCashLightClientKit/Block/Actions/EnhanceAction.swift +0 -97
  63. package/ios/ZCashLightClientKit/Block/Actions/FetchUTXOsAction.swift +0 -33
  64. package/ios/ZCashLightClientKit/Block/Actions/MigrateLegacyCacheDBAction.swift +0 -70
  65. package/ios/ZCashLightClientKit/Block/Actions/ProcessSuggestedScanRangesAction.swift +0 -60
  66. package/ios/ZCashLightClientKit/Block/Actions/RewindAction.swift +0 -48
  67. package/ios/ZCashLightClientKit/Block/Actions/SaplingParamsAction.swift +0 -33
  68. package/ios/ZCashLightClientKit/Block/Actions/ScanAction.swift +0 -95
  69. package/ios/ZCashLightClientKit/Block/Actions/UpdateChainTipAction.swift +0 -55
  70. package/ios/ZCashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift +0 -58
  71. package/ios/ZCashLightClientKit/Block/Actions/ValidateServerAction.swift +0 -60
  72. package/ios/ZCashLightClientKit/Block/Utils/CompactBlockProgress.swift +0 -24
  73. package/ios/ZCashLightClientKit/Block/Utils/SyncControlData.swift +0 -25
  74. package/ios/ZCashLightClientKit/Extensions/Bool+ToData.swift +0 -15
  75. package/ios/ZCashLightClientKit/Extensions/Data+ToOtherTypes.swift +0 -18
  76. package/ios/ZCashLightClientKit/Extensions/Int+ToData.swift +0 -15
  77. package/ios/ZCashLightClientKit/Model/ScanProgress.swift +0 -29
  78. package/ios/ZCashLightClientKit/Model/ScanRange.swift +0 -31
  79. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2092500.json +0 -8
  80. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2095000.json +0 -8
  81. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2097500.json +0 -8
  82. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2102500.json +0 -8
  83. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2105000.json +0 -8
  84. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2107500.json +0 -8
  85. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2112500.json +0 -8
  86. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2115000.json +0 -8
  87. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2117500.json +0 -8
  88. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2122500.json +0 -8
  89. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2125000.json +0 -8
  90. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2127500.json +0 -8
  91. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2132500.json +0 -8
  92. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2135000.json +0 -8
  93. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2137500.json +0 -8
  94. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2142500.json +0 -8
  95. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2145000.json +0 -8
  96. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2147500.json +0 -8
  97. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2152500.json +0 -8
  98. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2155000.json +0 -8
  99. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2157500.json +0 -8
  100. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2162500.json +0 -8
  101. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2165000.json +0 -8
  102. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2167500.json +0 -8
  103. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2172500.json +0 -8
  104. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2175000.json +0 -8
  105. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2177500.json +0 -8
  106. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2182500.json +0 -8
  107. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2185000.json +0 -8
  108. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2187500.json +0 -8
  109. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2192500.json +0 -8
  110. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2195000.json +0 -8
  111. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2197500.json +0 -8
  112. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2202500.json +0 -8
  113. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2205000.json +0 -8
  114. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2207500.json +0 -8
  115. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2212500.json +0 -8
  116. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2215000.json +0 -8
  117. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2217500.json +0 -8
  118. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2222500.json +0 -8
  119. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2225000.json +0 -8
  120. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2227500.json +0 -8
  121. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2232500.json +0 -8
  122. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2235000.json +0 -8
  123. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2237500.json +0 -8
  124. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2242500.json +0 -8
  125. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2245000.json +0 -8
  126. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2247500.json +0 -8
  127. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2252500.json +0 -8
  128. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2255000.json +0 -8
  129. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2257500.json +0 -8
  130. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2262500.json +0 -8
  131. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2265000.json +0 -8
  132. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2267500.json +0 -8
  133. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2350000.json +0 -8
  134. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2360000.json +0 -8
  135. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2370000.json +0 -8
  136. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2380000.json +0 -8
  137. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2390000.json +0 -8
  138. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2400000.json +0 -8
  139. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2410000.json +0 -8
  140. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2420000.json +0 -8
  141. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2430000.json +0 -8
  142. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2440000.json +0 -8
  143. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2450000.json +0 -8
  144. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2460000.json +0 -8
  145. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2470000.json +0 -8
  146. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2480000.json +0 -8
  147. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2490000.json +0 -8
  148. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2500000.json +0 -8
  149. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2510000.json +0 -8
  150. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2520000.json +0 -8
  151. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2530000.json +0 -8
  152. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2540000.json +0 -8
  153. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2550000.json +0 -8
  154. package/ios/ZCashLightClientKit/Utils/ZcashFileManager.swift +0 -16
@@ -52,14 +52,6 @@ protocol BlockDownloader {
52
52
  /// called before then nothing is downloaded.
53
53
  /// - Parameter range: Wait until blocks from `range` are downloaded.
54
54
  func waitUntilRequestedBlocksAreDownloaded(in range: CompactBlockRange) async throws
55
-
56
- /// Updates the internal in memory value of latest downloaded block height. This way the `BlockDownloader` works with the current latest height and can
57
- /// continue on parallel downloading of next batch.
58
- func update(latestDownloadedBlockHeight: BlockHeight, force: Bool) async
59
- /// Provides the value of latest downloaded height.
60
- func latestDownloadedBlockHeight() async -> BlockHeight
61
- /// In case rewind is needed, the latestDownloadedBlockHeight is rewritten forcefully.
62
- func rewind(latestDownloadedBlockHeight: BlockHeight?) async
63
55
  }
64
56
 
65
57
  actor BlockDownloaderImpl {
@@ -70,9 +62,9 @@ actor BlockDownloaderImpl {
70
62
  let service: LightWalletService
71
63
  let downloaderService: BlockDownloaderService
72
64
  let storage: CompactBlockRepository
65
+ let internalSyncProgress: InternalSyncProgress
73
66
  let metrics: SDKMetrics
74
67
  let logger: Logger
75
- var latestDownloadedBlockHeight: BlockHeight = -1
76
68
 
77
69
  private var downloadStreamCreatedAtRange: CompactBlockRange = 0...0
78
70
  private var downloadStream: BlockDownloaderStream?
@@ -88,12 +80,14 @@ actor BlockDownloaderImpl {
88
80
  service: LightWalletService,
89
81
  downloaderService: BlockDownloaderService,
90
82
  storage: CompactBlockRepository,
83
+ internalSyncProgress: InternalSyncProgress,
91
84
  metrics: SDKMetrics,
92
85
  logger: Logger
93
86
  ) {
94
87
  self.service = service
95
88
  self.downloaderService = downloaderService
96
89
  self.storage = storage
90
+ self.internalSyncProgress = internalSyncProgress
97
91
  self.metrics = metrics
98
92
  self.logger = logger
99
93
  }
@@ -106,6 +100,8 @@ actor BlockDownloaderImpl {
106
100
  throw ZcashError.blockDownloadSyncRangeNotSet
107
101
  }
108
102
 
103
+ let latestDownloadedBlockHeight = await internalSyncProgress.load(.latestDownloadedBlockHeight)
104
+
109
105
  let downloadFrom = max(syncRange.lowerBound, latestDownloadedBlockHeight + 1)
110
106
  let downloadTo = min(downloadToHeight, syncRange.upperBound)
111
107
 
@@ -199,8 +195,13 @@ actor BlockDownloaderImpl {
199
195
  var counter = 0
200
196
  var lastDownloadedBlockHeight = -1
201
197
 
202
- let pushMetrics: (BlockHeight, Date, Date) -> Void = { [metrics] _, startTime, finishTime in
198
+ let pushMetrics: (BlockHeight, Date, Date) -> Void = { [metrics] lastDownloadedBlockHeight, startTime, finishTime in
203
199
  metrics.pushProgressReport(
200
+ progress: BlockProgress(
201
+ startHeight: totalProgressRange.lowerBound,
202
+ targetHeight: totalProgressRange.upperBound,
203
+ progressHeight: Int(lastDownloadedBlockHeight)
204
+ ),
204
205
  start: startTime,
205
206
  end: finishTime,
206
207
  batchSize: maxBlockBufferSize,
@@ -219,7 +220,7 @@ actor BlockDownloaderImpl {
219
220
  if buffer.count >= maxBlockBufferSize {
220
221
  let finishTime = Date()
221
222
  try await storage.write(blocks: buffer)
222
- try await blocksBufferWritten(buffer)
223
+ await blocksBufferWritten(buffer)
223
224
  buffer.removeAll(keepingCapacity: true)
224
225
 
225
226
  pushMetrics(block.height, startTime, finishTime)
@@ -234,36 +235,21 @@ actor BlockDownloaderImpl {
234
235
  }
235
236
 
236
237
  try await storage.write(blocks: buffer)
237
- try await blocksBufferWritten(buffer)
238
+ await blocksBufferWritten(buffer)
238
239
  }
239
240
 
240
- private func blocksBufferWritten(_ buffer: [ZcashCompactBlock]) async throws {
241
+ private func blocksBufferWritten(_ buffer: [ZcashCompactBlock]) async {
241
242
  guard let lastBlock = buffer.last else { return }
242
- latestDownloadedBlockHeight = lastBlock.height
243
+ await internalSyncProgress.set(lastBlock.height, .latestDownloadedBlockHeight)
243
244
  }
244
245
  }
245
246
 
246
247
  extension BlockDownloaderImpl: BlockDownloader {
247
- func rewind(latestDownloadedBlockHeight: BlockHeight?) async {
248
- self.latestDownloadedBlockHeight = latestDownloadedBlockHeight ?? -1
249
- }
250
-
251
- func update(latestDownloadedBlockHeight: BlockHeight, force: Bool = false) async {
252
- if latestDownloadedBlockHeight >= self.latestDownloadedBlockHeight || force {
253
- self.latestDownloadedBlockHeight = latestDownloadedBlockHeight
254
- }
255
- }
256
-
257
- func latestDownloadedBlockHeight() async -> BlockHeight {
258
- latestDownloadedBlockHeight
259
- }
260
-
261
248
  func setDownloadLimit(_ limit: BlockHeight) async {
262
249
  downloadToHeight = limit
263
250
  }
264
251
 
265
252
  func setSyncRange(_ range: CompactBlockRange, batchSize: Int) async throws {
266
- guard range != syncRange else { return }
267
253
  downloadStream = nil
268
254
  self.batchSize = batchSize
269
255
  syncRange = range
@@ -296,13 +282,13 @@ extension BlockDownloaderImpl: BlockDownloader {
296
282
 
297
283
  func waitUntilRequestedBlocksAreDownloaded(in range: CompactBlockRange) async throws {
298
284
  logger.debug("Waiting until requested blocks are downloaded at \(range)")
299
- var latestDownloadedBlock = latestDownloadedBlockHeight
285
+ var latestDownloadedBlock = await internalSyncProgress.load(.latestDownloadedBlockHeight)
300
286
  while latestDownloadedBlock < range.upperBound {
301
287
  if let error = lastError {
302
288
  throw error
303
289
  }
304
290
  try await Task.sleep(milliseconds: 10)
305
- latestDownloadedBlock = latestDownloadedBlockHeight
291
+ latestDownloadedBlock = await internalSyncProgress.load(.latestDownloadedBlockHeight)
306
292
  }
307
293
  logger.debug("Waiting done. Blocks are downloaded at \(range)")
308
294
  }
@@ -107,8 +107,8 @@ extension BlockDownloaderServiceImpl: BlockDownloaderService {
107
107
  try await self.storage.rewind(to: height)
108
108
  }
109
109
 
110
- func lastDownloadedBlockHeight() async throws -> BlockHeight {
111
- try await self.storage.latestHeight()
110
+ func lastDownloadedBlockHeight() async -> BlockHeight {
111
+ await self.storage.latestHeight()
112
112
  }
113
113
 
114
114
  func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched {
@@ -7,56 +7,13 @@
7
7
 
8
8
  import Foundation
9
9
 
10
- public struct EnhancementProgress: Equatable {
11
- /// total transactions that were detected in the `range`
12
- public let totalTransactions: Int
13
- /// enhanced transactions so far
14
- public let enhancedTransactions: Int
15
- /// last found transaction
16
- public let lastFoundTransaction: ZcashTransaction.Overview?
17
- /// block range that's being enhanced
18
- public let range: CompactBlockRange
19
- /// whether this transaction can be considered `newly mined` and not part of the
20
- /// wallet catching up to stale and uneventful blocks.
21
- public let newlyMined: Bool
22
-
23
- public init(
24
- totalTransactions: Int,
25
- enhancedTransactions: Int,
26
- lastFoundTransaction: ZcashTransaction.Overview?,
27
- range: CompactBlockRange,
28
- newlyMined: Bool
29
- ) {
30
- self.totalTransactions = totalTransactions
31
- self.enhancedTransactions = enhancedTransactions
32
- self.lastFoundTransaction = lastFoundTransaction
33
- self.range = range
34
- self.newlyMined = newlyMined
35
- }
36
-
37
- public var progress: Float {
38
- totalTransactions > 0 ? Float(enhancedTransactions) / Float(totalTransactions) : 0
39
- }
40
-
41
- public static var zero: EnhancementProgress {
42
- EnhancementProgress(totalTransactions: 0, enhancedTransactions: 0, lastFoundTransaction: nil, range: 0...0, newlyMined: false)
43
- }
44
-
45
- public static func == (lhs: EnhancementProgress, rhs: EnhancementProgress) -> Bool {
46
- return
47
- lhs.totalTransactions == rhs.totalTransactions &&
48
- lhs.enhancedTransactions == rhs.enhancedTransactions &&
49
- lhs.lastFoundTransaction?.rawID == rhs.lastFoundTransaction?.rawID &&
50
- lhs.range == rhs.range
51
- }
52
- }
53
-
54
10
  protocol BlockEnhancer {
55
- func enhance(at range: CompactBlockRange, didEnhance: @escaping (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview]?
11
+ func enhance(at range: CompactBlockRange, didEnhance: (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview]?
56
12
  }
57
13
 
58
14
  struct BlockEnhancerImpl {
59
15
  let blockDownloaderService: BlockDownloaderService
16
+ let internalSyncProgress: InternalSyncProgress
60
17
  let rustBackend: ZcashRustBackendWelding
61
18
  let transactionRepository: TransactionRepository
62
19
  let metrics: SDKMetrics
@@ -81,7 +38,7 @@ struct BlockEnhancerImpl {
81
38
  }
82
39
 
83
40
  extension BlockEnhancerImpl: BlockEnhancer {
84
- func enhance(at range: CompactBlockRange, didEnhance: @escaping (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview]? {
41
+ func enhance(at range: CompactBlockRange, didEnhance: (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview]? {
85
42
  try Task.checkCancellation()
86
43
 
87
44
  logger.debug("Started Enhancing range: \(range)")
@@ -95,6 +52,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
95
52
  let transactions = try await transactionRepository.find(in: range, limit: Int.max, kind: .all)
96
53
 
97
54
  guard !transactions.isEmpty else {
55
+ await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
98
56
  logger.debug("no transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
99
57
  return nil
100
58
  }
@@ -124,6 +82,10 @@ extension BlockEnhancerImpl: BlockEnhancer {
124
82
  )
125
83
 
126
84
  await didEnhance(progress)
85
+
86
+ if let minedHeight = confirmedTx.minedHeight {
87
+ await internalSyncProgress.set(minedHeight, .latestEnhancedHeight)
88
+ }
127
89
  } catch {
128
90
  retries += 1
129
91
  logger.error("could not enhance txId \(transaction.rawID.toHexStringTxId()) - Error: \(error)")
@@ -135,6 +97,11 @@ extension BlockEnhancerImpl: BlockEnhancer {
135
97
  }
136
98
 
137
99
  metrics.pushProgressReport(
100
+ progress: BlockProgress(
101
+ startHeight: range.lowerBound,
102
+ targetHeight: range.upperBound,
103
+ progressHeight: range.upperBound
104
+ ),
138
105
  start: startTime,
139
106
  end: Date(),
140
107
  batchSize: range.count,
@@ -144,6 +111,8 @@ extension BlockEnhancerImpl: BlockEnhancer {
144
111
  logger.error("error enhancing transactions! \(error)")
145
112
  throw error
146
113
  }
114
+
115
+ await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
147
116
 
148
117
  if Task.isCancelled {
149
118
  logger.debug("Warning: compactBlockEnhancement on range \(range) cancelled")
@@ -18,7 +18,8 @@ struct UTXOFetcherConfig {
18
18
 
19
19
  protocol UTXOFetcher {
20
20
  func fetch(
21
- didFetch: @escaping (Float) async -> Void
21
+ at range: CompactBlockRange,
22
+ didFetch: (Float) async -> Void
22
23
  ) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])
23
24
  }
24
25
 
@@ -26,6 +27,7 @@ struct UTXOFetcherImpl {
26
27
  let accountRepository: AccountRepository
27
28
  let blockDownloaderService: BlockDownloaderService
28
29
  let config: UTXOFetcherConfig
30
+ let internalSyncProgress: InternalSyncProgress
29
31
  let rustBackend: ZcashRustBackendWelding
30
32
  let metrics: SDKMetrics
31
33
  let logger: Logger
@@ -33,7 +35,8 @@ struct UTXOFetcherImpl {
33
35
 
34
36
  extension UTXOFetcherImpl: UTXOFetcher {
35
37
  func fetch(
36
- didFetch: @escaping (Float) async -> Void
38
+ at range: CompactBlockRange,
39
+ didFetch: (Float) async -> Void
37
40
  ) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) {
38
41
  try Task.checkCancellation()
39
42
 
@@ -79,6 +82,7 @@ extension UTXOFetcherImpl: UTXOFetcher {
79
82
 
80
83
  counter += 1
81
84
  await didFetch(counter / all)
85
+ await internalSyncProgress.set(utxo.height, .latestUTXOFetchedHeight)
82
86
  } catch {
83
87
  logger.error("failed to put utxo - error: \(error)")
84
88
  skipped.append(utxo)
@@ -86,16 +90,23 @@ extension UTXOFetcherImpl: UTXOFetcher {
86
90
  }
87
91
 
88
92
  metrics.pushProgressReport(
93
+ progress: BlockProgress(
94
+ startHeight: range.lowerBound,
95
+ targetHeight: range.upperBound,
96
+ progressHeight: range.upperBound
97
+ ),
89
98
  start: startTime,
90
99
  end: Date(),
91
- batchSize: 1,
100
+ batchSize: range.count,
92
101
  operation: .fetchUTXOs
93
102
  )
94
103
 
95
104
  let result = (inserted: refreshed, skipped: skipped)
96
105
 
106
+ await internalSyncProgress.set(range.upperBound, .latestUTXOFetchedHeight)
107
+
97
108
  if Task.isCancelled {
98
- logger.debug("Warning: fetchUnspentTxOutputs cancelled")
109
+ logger.debug("Warning: fetchUnspentTxOutputs on range \(range) cancelled")
99
110
  }
100
111
 
101
112
  return result
@@ -64,8 +64,8 @@ extension FSCompactBlockRepository: CompactBlockRepository {
64
64
  }
65
65
  }
66
66
 
67
- func latestHeight() async throws -> BlockHeight {
68
- try await metadataStore.latestHeight()
67
+ func latestHeight() async -> BlockHeight {
68
+ await metadataStore.latestHeight()
69
69
  }
70
70
 
71
71
  func write(blocks: [ZcashCompactBlock]) async throws {
@@ -251,7 +251,7 @@ struct FSMetadataStore {
251
251
  let saveBlocksMeta: ([ZcashCompactBlock]) async throws -> Void
252
252
  let rewindToHeight: (BlockHeight) async throws -> Void
253
253
  let initFsBlockDbRoot: () async throws -> Void
254
- let latestHeight: () async throws -> BlockHeight
254
+ let latestHeight: () async -> BlockHeight
255
255
  }
256
256
 
257
257
  extension FSMetadataStore {
@@ -268,7 +268,7 @@ extension FSMetadataStore {
268
268
  } initFsBlockDbRoot: {
269
269
  try await rustBackend.initBlockMetadataDb()
270
270
  } latestHeight: {
271
- try await rustBackend.latestCachedBlockHeight()
271
+ await rustBackend.latestCachedBlockHeight()
272
272
  }
273
273
  }
274
274
  }
@@ -16,7 +16,8 @@ protocol BlockScanner {
16
16
  @discardableResult
17
17
  func scanBlocks(
18
18
  at range: CompactBlockRange,
19
- didScan: @escaping (BlockHeight, UInt32) async throws -> Void
19
+ totalProgressRange: CompactBlockRange,
20
+ didScan: @escaping (BlockHeight) async -> Void
20
21
  ) async throws -> BlockHeight
21
22
  }
22
23
 
@@ -26,18 +27,20 @@ struct BlockScannerImpl {
26
27
  let transactionRepository: TransactionRepository
27
28
  let metrics: SDKMetrics
28
29
  let logger: Logger
30
+ let latestBlocksDataProvider: LatestBlocksDataProvider
29
31
  }
30
32
 
31
33
  extension BlockScannerImpl: BlockScanner {
32
34
  @discardableResult
33
35
  func scanBlocks(
34
36
  at range: CompactBlockRange,
35
- didScan: @escaping (BlockHeight, UInt32) async throws -> Void
37
+ totalProgressRange: CompactBlockRange,
38
+ didScan: @escaping (BlockHeight) async -> Void
36
39
  ) async throws -> BlockHeight {
37
40
  logger.debug("Going to scan blocks in range: \(range)")
38
41
  try Task.checkCancellation()
39
42
 
40
- let scanStartHeight = range.lowerBound
43
+ let scanStartHeight = try await transactionRepository.lastScannedHeight()
41
44
  let targetScanHeight = range.upperBound
42
45
 
43
46
  var scannedNewBlocks = false
@@ -47,13 +50,13 @@ extension BlockScannerImpl: BlockScanner {
47
50
  try Task.checkCancellation()
48
51
 
49
52
  let previousScannedHeight = lastScannedHeight
50
- let startHeight = previousScannedHeight + 1
51
53
 
52
- let batchSize = UInt32(config.scanningBatchSize)
54
+ // TODO: [#576] remove this arbitrary batch size https://github.com/zcash/ZcashLightClientKit/issues/576
55
+ let batchSize = scanBatchSize(startScanHeight: previousScannedHeight + 1, network: config.networkType)
53
56
 
54
57
  let scanStartTime = Date()
55
58
  do {
56
- try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize)
59
+ try await self.rustBackend.scanBlocks(limit: batchSize)
57
60
  } catch {
58
61
  logger.debug("block scanning failed with error: \(String(describing: error))")
59
62
  throw error
@@ -61,15 +64,24 @@ extension BlockScannerImpl: BlockScanner {
61
64
 
62
65
  let scanFinishTime = Date()
63
66
 
64
- // TODO: [#1259] potential bug when rustBackend.scanBlocks scan less blocks than batchSize,
65
- // https://github.com/zcash/ZcashLightClientKit/issues/1259
66
- lastScannedHeight = startHeight + Int(batchSize) - 1
67
+ if let lastScannedBlock = try await transactionRepository.lastScannedBlock() {
68
+ lastScannedHeight = lastScannedBlock.height
69
+ await latestBlocksDataProvider.updateLatestScannedHeight(lastScannedHeight)
70
+ await latestBlocksDataProvider.updateLatestScannedTime(TimeInterval(lastScannedBlock.time))
71
+ }
67
72
 
68
73
  scannedNewBlocks = previousScannedHeight != lastScannedHeight
69
74
  if scannedNewBlocks {
70
- try await didScan(lastScannedHeight, batchSize)
75
+ await didScan(lastScannedHeight)
76
+
77
+ let progress = BlockProgress(
78
+ startHeight: totalProgressRange.lowerBound,
79
+ targetHeight: totalProgressRange.upperBound,
80
+ progressHeight: lastScannedHeight
81
+ )
71
82
 
72
83
  metrics.pushProgressReport(
84
+ progress: progress,
73
85
  start: scanStartTime,
74
86
  end: scanFinishTime,
75
87
  batchSize: Int(batchSize),
@@ -86,4 +98,17 @@ extension BlockScannerImpl: BlockScanner {
86
98
 
87
99
  return lastScannedHeight
88
100
  }
101
+
102
+ private func scanBatchSize(startScanHeight height: BlockHeight, network: NetworkType) -> UInt32 {
103
+ assert(config.scanningBatchSize > 0, "ZcashSDK.DefaultScanningBatch must be larger than 0!")
104
+ guard network == .mainnet else { return UInt32(config.scanningBatchSize) }
105
+
106
+ if height > 1_650_000 {
107
+ // librustzcash thread saturation at a number of blocks
108
+ // that contains 100 * num_cores Sapling outputs.
109
+ return UInt32(max(ProcessInfo().activeProcessorCount, 10))
110
+ }
111
+
112
+ return UInt32(config.scanningBatchSize)
113
+ }
89
114
  }
@@ -0,0 +1,200 @@
1
+ //
2
+ // InternalSyncProgress.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 23.11.2022.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ struct SyncRanges: Equatable {
11
+ let latestBlockHeight: BlockHeight
12
+ /// The sync process can be interrupted in any phase. It may happen that it's interrupted while downloading blocks. In that case in next sync
13
+ /// process already downloaded blocks needs to be scanned before the sync process starts to download new blocks. And the range of blocks that are
14
+ /// already downloaded but not scanned is stored in this variable.
15
+ let downloadedButUnscannedRange: CompactBlockRange?
16
+ /// Range of blocks that are not yet downloaded and not yet scanned.
17
+ let downloadAndScanRange: CompactBlockRange?
18
+ /// Range of blocks that are not enhanced yet.
19
+ let enhanceRange: CompactBlockRange?
20
+ /// Range of blocks for which no UTXOs are fetched yet.
21
+ let fetchUTXORange: CompactBlockRange?
22
+
23
+ let latestScannedHeight: BlockHeight?
24
+
25
+ let latestDownloadedBlockHeight: BlockHeight?
26
+ }
27
+
28
+ protocol InternalSyncProgressStorage {
29
+ func bool(forKey defaultName: String) -> Bool
30
+ func integer(forKey defaultName: String) -> Int
31
+ func set(_ value: Int, forKey defaultName: String)
32
+ func set(_ value: Bool, forKey defaultName: String)
33
+ @discardableResult
34
+ func synchronize() -> Bool
35
+ }
36
+
37
+ extension UserDefaults: InternalSyncProgressStorage { }
38
+
39
+ actor InternalSyncProgress {
40
+ enum Key: String, CaseIterable {
41
+ case latestDownloadedBlockHeight
42
+ case latestEnhancedHeight
43
+ case latestUTXOFetchedHeight
44
+
45
+ func with(_ alias: ZcashSynchronizerAlias) -> String {
46
+ switch alias {
47
+ case .`default`:
48
+ return self.rawValue
49
+ case let .custom(rawAlias):
50
+ return "\(self.rawValue)_\(rawAlias)"
51
+ }
52
+ }
53
+ }
54
+
55
+ private let alias: ZcashSynchronizerAlias
56
+ private let storage: InternalSyncProgressStorage
57
+ let logger: Logger
58
+
59
+ var latestDownloadedBlockHeight: BlockHeight { load(.latestDownloadedBlockHeight) }
60
+ var latestEnhancedHeight: BlockHeight { load(.latestEnhancedHeight) }
61
+ var latestUTXOFetchedHeight: BlockHeight { load(.latestUTXOFetchedHeight) }
62
+
63
+ init(alias: ZcashSynchronizerAlias, storage: InternalSyncProgressStorage, logger: Logger) {
64
+ self.alias = alias
65
+ self.storage = storage
66
+ self.logger = logger
67
+ }
68
+
69
+ func load(_ key: Key) -> BlockHeight {
70
+ storage.integer(forKey: key.with(alias))
71
+ }
72
+
73
+ func set(_ value: BlockHeight, _ key: Key) {
74
+ storage.set(value, forKey: key.with(alias))
75
+ storage.synchronize()
76
+ }
77
+
78
+ func rewind(to: BlockHeight) {
79
+ Key.allCases.forEach { key in
80
+ let finalRewindHeight = min(load(key), to)
81
+ self.set(finalRewindHeight, key)
82
+ }
83
+ }
84
+
85
+ /// `InternalSyncProgress` is from now on used to track which block were already downloaded. Previous versions of the SDK were using cache DB to
86
+ /// track this. Because of this we have to migrate height of latest downloaded block from cache DB to here.
87
+ ///
88
+ /// - Parameter latestDownloadedBlockHeight: Height of latest downloaded block from cache DB.
89
+ func migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB latestDownloadedBlockHeight: BlockHeight) {
90
+ let key = "InternalSyncProgressMigrated"
91
+ if !storage.bool(forKey: key) {
92
+ set(latestDownloadedBlockHeight, .latestDownloadedBlockHeight)
93
+ }
94
+ storage.set(true, forKey: key)
95
+ storage.synchronize()
96
+ }
97
+
98
+ /// Computes the next state for the sync process. Thanks to this it's possible to interrupt the sync process at any phase and then it can be safely
99
+ /// resumed.
100
+ ///
101
+ /// The sync process has 4 phases (download, scan, enhance, fetch UTXO). `InternalSyncProgress` tracks independently which blocks were already
102
+ /// processed in each phase. To compute the next state these 4 numbers are compared with `latestBlockHeight`.
103
+ ///
104
+ /// - If any of these numbers are larger than `latestBlockHeight` then `wait` is used as the next state. We have locally higher block heights than
105
+ /// are currently available at LightWalletd.
106
+ /// - If any of these numbers are lower than `latestBlockHeight` then `processNewBlocks` is used as the next state. The sync process should run.
107
+ /// - Otherwise `finishProcessing` is used as the next state. It means that local data are synced with what is available at LightWalletd.
108
+ ///
109
+ /// - Parameters:
110
+ /// - latestBlockHeight: Latest height fetched from LightWalletd API.
111
+ /// - latestScannedHeight: Latest height of latest block scanned.
112
+ /// - walletBirthday: Wallet birthday.
113
+ /// - Returns: Computed state.
114
+ func computeNextState(
115
+ latestBlockHeight: BlockHeight,
116
+ latestScannedHeight: BlockHeight,
117
+ walletBirthday: BlockHeight
118
+ ) -> CompactBlockProcessor.NextState {
119
+ logger.debug("""
120
+ Init numbers:
121
+ latestBlockHeight: \(latestBlockHeight)
122
+ latestDownloadedHeight: \(latestDownloadedBlockHeight)
123
+ latestScannedHeight: \(latestScannedHeight)
124
+ latestEnhancedHeight: \(latestEnhancedHeight)
125
+ latestUTXOFetchedHeight: \(latestUTXOFetchedHeight)
126
+ """)
127
+
128
+ if latestDownloadedBlockHeight > latestBlockHeight ||
129
+ latestScannedHeight > latestBlockHeight ||
130
+ latestEnhancedHeight > latestBlockHeight ||
131
+ latestUTXOFetchedHeight > latestBlockHeight {
132
+ return .wait(latestHeight: latestBlockHeight, latestDownloadHeight: latestDownloadedBlockHeight)
133
+ } else if latestDownloadedBlockHeight < latestBlockHeight ||
134
+ latestScannedHeight < latestBlockHeight ||
135
+ latestEnhancedHeight < latestEnhancedHeight ||
136
+ latestUTXOFetchedHeight < latestBlockHeight {
137
+ let ranges = computeSyncRanges(
138
+ birthday: walletBirthday,
139
+ latestBlockHeight: latestBlockHeight,
140
+ latestScannedHeight: latestScannedHeight
141
+ )
142
+ return .processNewBlocks(ranges: ranges)
143
+ } else {
144
+ return .finishProcessing(height: latestBlockHeight)
145
+ }
146
+ }
147
+
148
+ func computeSyncRanges(
149
+ birthday: BlockHeight,
150
+ latestBlockHeight: BlockHeight,
151
+ latestScannedHeight: BlockHeight
152
+ ) -> SyncRanges {
153
+ // If there is more downloaded then scanned blocks we have to range for these blocks. The sync process will then start with scanning these
154
+ // blocks instead of downloading new ones.
155
+ let downloadedButUnscannedRange: CompactBlockRange?
156
+ if latestScannedHeight < latestDownloadedBlockHeight {
157
+ downloadedButUnscannedRange = latestScannedHeight + 1...latestDownloadedBlockHeight
158
+ } else {
159
+ downloadedButUnscannedRange = nil
160
+ }
161
+
162
+ if latestScannedHeight > latestDownloadedBlockHeight {
163
+ logger.warn("""
164
+ InternalSyncProgress found inconsistent state.
165
+ latestBlockHeight: \(latestBlockHeight)
166
+ --> latestDownloadedHeight: \(latestDownloadedBlockHeight)
167
+ latestScannedHeight: \(latestScannedHeight)
168
+ latestEnhancedHeight: \(latestEnhancedHeight)
169
+ latestUTXOFetchedHeight: \(latestUTXOFetchedHeight)
170
+
171
+ latest downloaded height
172
+ """)
173
+ }
174
+
175
+ // compute the range that must be downloaded and scanned based on
176
+ // birthday, `latestDownloadedBlockHeight`, `latestScannedHeight` and
177
+ // latest block height fetched from the chain.
178
+ let downloadAndScanRange = computeRange(
179
+ latestHeight: max(latestDownloadedBlockHeight, latestScannedHeight),
180
+ birthday: birthday,
181
+ latestBlockHeight: latestBlockHeight
182
+ )
183
+
184
+ return SyncRanges(
185
+ latestBlockHeight: latestBlockHeight,
186
+ downloadedButUnscannedRange: downloadedButUnscannedRange,
187
+ downloadAndScanRange: downloadAndScanRange,
188
+ enhanceRange: computeRange(latestHeight: latestEnhancedHeight, birthday: birthday, latestBlockHeight: latestBlockHeight),
189
+ fetchUTXORange: computeRange(latestHeight: latestUTXOFetchedHeight, birthday: birthday, latestBlockHeight: latestBlockHeight),
190
+ latestScannedHeight: latestScannedHeight,
191
+ latestDownloadedBlockHeight: latestDownloadedBlockHeight
192
+ )
193
+ }
194
+
195
+ private func computeRange(latestHeight: BlockHeight, birthday: BlockHeight, latestBlockHeight: BlockHeight) -> CompactBlockRange? {
196
+ guard latestHeight < latestBlockHeight else { return nil }
197
+ let lowerBound = latestHeight <= birthday ? birthday : latestHeight + 1
198
+ return lowerBound...latestBlockHeight
199
+ }
200
+ }