react-native-zcash 0.6.11 → 0.6.12

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 (144) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/ios/ZCashLightClientKit/Block/Actions/Action.swift +98 -0
  3. package/ios/ZCashLightClientKit/Block/Actions/ClearAlreadyScannedBlocksAction.swift +35 -0
  4. package/ios/ZCashLightClientKit/Block/Actions/ClearCacheAction.swift +30 -0
  5. package/ios/ZCashLightClientKit/Block/Actions/DownloadAction.swift +67 -0
  6. package/ios/ZCashLightClientKit/Block/Actions/EnhanceAction.swift +97 -0
  7. package/ios/ZCashLightClientKit/Block/Actions/FetchUTXOsAction.swift +33 -0
  8. package/ios/ZCashLightClientKit/Block/Actions/MigrateLegacyCacheDBAction.swift +70 -0
  9. package/ios/ZCashLightClientKit/Block/Actions/ProcessSuggestedScanRangesAction.swift +60 -0
  10. package/ios/ZCashLightClientKit/Block/Actions/RewindAction.swift +48 -0
  11. package/ios/ZCashLightClientKit/Block/Actions/SaplingParamsAction.swift +33 -0
  12. package/ios/ZCashLightClientKit/Block/Actions/ScanAction.swift +95 -0
  13. package/ios/ZCashLightClientKit/Block/Actions/UpdateChainTipAction.swift +55 -0
  14. package/ios/ZCashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift +58 -0
  15. package/ios/ZCashLightClientKit/Block/Actions/ValidateServerAction.swift +60 -0
  16. package/ios/ZCashLightClientKit/Block/CompactBlockProcessor.swift +425 -937
  17. package/ios/ZCashLightClientKit/Block/Download/BlockDownloader.swift +31 -17
  18. package/ios/ZCashLightClientKit/Block/Download/BlockDownloaderService.swift +2 -2
  19. package/ios/ZCashLightClientKit/Block/Enhance/BlockEnhancer.swift +46 -15
  20. package/ios/ZCashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +4 -15
  21. package/ios/ZCashLightClientKit/Block/FilesystemStorage/FSCompactBlockRepository.swift +4 -4
  22. package/ios/ZCashLightClientKit/Block/Scan/BlockScanner.swift +10 -35
  23. package/ios/ZCashLightClientKit/Block/Utils/CompactBlockProgress.swift +24 -0
  24. package/ios/ZCashLightClientKit/Block/Utils/SyncControlData.swift +25 -0
  25. package/ios/ZCashLightClientKit/ClosureSynchronizer.swift +1 -2
  26. package/ios/ZCashLightClientKit/CombineSynchronizer.swift +2 -5
  27. package/ios/ZCashLightClientKit/Constants/ZcashSDK.swift +13 -26
  28. package/ios/ZCashLightClientKit/DAO/TransactionDao.swift +40 -42
  29. package/ios/ZCashLightClientKit/DAO/UnspentTransactionOutputDao.swift +13 -4
  30. package/ios/ZCashLightClientKit/Entity/AccountEntity.swift +9 -0
  31. package/ios/ZCashLightClientKit/Entity/TransactionEntity.swift +7 -10
  32. package/ios/ZCashLightClientKit/Error/Sourcery/generateErrorCode.sh +1 -1
  33. package/ios/ZCashLightClientKit/Error/ZcashError.swift +121 -12
  34. package/ios/ZCashLightClientKit/Error/ZcashErrorCode.swift +43 -5
  35. package/ios/ZCashLightClientKit/Error/ZcashErrorCodeDefinition.swift +72 -6
  36. package/ios/ZCashLightClientKit/Extensions/Bool+ToData.swift +15 -0
  37. package/ios/ZCashLightClientKit/Extensions/Data+ToOtherTypes.swift +18 -0
  38. package/ios/ZCashLightClientKit/Extensions/Int+ToData.swift +15 -0
  39. package/ios/ZCashLightClientKit/Initializer.swift +47 -26
  40. package/ios/ZCashLightClientKit/Metrics/SDKMetrics.swift +0 -12
  41. package/ios/ZCashLightClientKit/Model/Checkpoint.swift +12 -0
  42. package/ios/ZCashLightClientKit/Model/ScanProgress.swift +29 -0
  43. package/ios/ZCashLightClientKit/Model/ScanRange.swift +31 -0
  44. package/ios/ZCashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +15 -0
  45. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/compact_formats.pb.swift +150 -46
  46. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto +30 -16
  47. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/service.proto +32 -6
  48. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.grpc.swift +259 -22
  49. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.pb.swift +193 -7
  50. package/ios/ZCashLightClientKit/Modules/Service/LightWalletService.swift +8 -0
  51. package/ios/ZCashLightClientKit/Providers/LatestBlocksDataProvider.swift +18 -28
  52. package/ios/ZCashLightClientKit/Repository/CompactBlockRepository.swift +1 -1
  53. package/ios/ZCashLightClientKit/Repository/TransactionRepository.swift +2 -6
  54. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2092500.json +8 -0
  55. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2095000.json +8 -0
  56. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2097500.json +8 -0
  57. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2102500.json +8 -0
  58. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2105000.json +8 -0
  59. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2107500.json +8 -0
  60. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2112500.json +8 -0
  61. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2115000.json +8 -0
  62. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2117500.json +8 -0
  63. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2122500.json +8 -0
  64. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2125000.json +8 -0
  65. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2127500.json +8 -0
  66. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2132500.json +8 -0
  67. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2135000.json +8 -0
  68. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2137500.json +8 -0
  69. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2142500.json +8 -0
  70. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2145000.json +8 -0
  71. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2147500.json +8 -0
  72. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2152500.json +8 -0
  73. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2155000.json +8 -0
  74. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2157500.json +8 -0
  75. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2162500.json +8 -0
  76. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2165000.json +8 -0
  77. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2167500.json +8 -0
  78. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2172500.json +8 -0
  79. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2175000.json +8 -0
  80. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2177500.json +8 -0
  81. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2182500.json +8 -0
  82. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2185000.json +8 -0
  83. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2187500.json +8 -0
  84. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2192500.json +8 -0
  85. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2195000.json +8 -0
  86. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2197500.json +8 -0
  87. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2202500.json +8 -0
  88. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2205000.json +8 -0
  89. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2207500.json +8 -0
  90. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2212500.json +8 -0
  91. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2215000.json +8 -0
  92. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2217500.json +8 -0
  93. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2222500.json +8 -0
  94. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2225000.json +8 -0
  95. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2227500.json +8 -0
  96. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2232500.json +8 -0
  97. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2235000.json +8 -0
  98. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2237500.json +8 -0
  99. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2242500.json +8 -0
  100. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2245000.json +8 -0
  101. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2247500.json +8 -0
  102. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2252500.json +8 -0
  103. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2255000.json +8 -0
  104. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2257500.json +8 -0
  105. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2262500.json +8 -0
  106. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2265000.json +8 -0
  107. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2267500.json +8 -0
  108. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2350000.json +8 -0
  109. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2360000.json +8 -0
  110. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2370000.json +8 -0
  111. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2380000.json +8 -0
  112. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2390000.json +8 -0
  113. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2400000.json +8 -0
  114. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2410000.json +8 -0
  115. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2420000.json +8 -0
  116. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2430000.json +8 -0
  117. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2440000.json +8 -0
  118. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2450000.json +8 -0
  119. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2460000.json +8 -0
  120. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2470000.json +8 -0
  121. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2480000.json +8 -0
  122. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2490000.json +8 -0
  123. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2500000.json +8 -0
  124. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2510000.json +8 -0
  125. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2520000.json +8 -0
  126. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2530000.json +8 -0
  127. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2540000.json +8 -0
  128. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2550000.json +8 -0
  129. package/ios/ZCashLightClientKit/Rust/ZcashRustBackend.swift +293 -158
  130. package/ios/ZCashLightClientKit/Rust/ZcashRustBackendWelding.swift +58 -64
  131. package/ios/ZCashLightClientKit/Rust/zcashlc.h +618 -512
  132. package/ios/ZCashLightClientKit/Synchronizer/ClosureSDKSynchronizer.swift +2 -8
  133. package/ios/ZCashLightClientKit/Synchronizer/CombineSDKSynchronizer.swift +3 -15
  134. package/ios/ZCashLightClientKit/Synchronizer/Dependencies.swift +11 -30
  135. package/ios/ZCashLightClientKit/Synchronizer/SDKSynchronizer.swift +41 -50
  136. package/ios/ZCashLightClientKit/Synchronizer.swift +51 -65
  137. package/ios/ZCashLightClientKit/Transaction/TransactionEncoder.swift +2 -2
  138. package/ios/ZCashLightClientKit/Transaction/WalletTransactionEncoder.swift +7 -7
  139. package/ios/ZCashLightClientKit/Utils/OSLogger.swift +3 -3
  140. package/ios/ZCashLightClientKit/Utils/ZcashFileManager.swift +16 -0
  141. package/ios/libzcashlc.xcframework/Info.plist +9 -5
  142. package/ios/libzcashlc.xcframework/ios-arm64/libzcashlc.a +0 -0
  143. package/ios/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.a +0 -0
  144. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.6.12 (2024-04-10)
6
+
7
+ - fixed: Correct packaging mistake the previous release
8
+
5
9
  ## 0.6.11 (2024-04-10)
6
10
 
7
11
  - changed: Updated checkpoints
@@ -0,0 +1,98 @@
1
+ //
2
+ // Action.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ protocol ActionContext {
11
+ var state: CBPState { get async }
12
+ var prevState: CBPState? { get async }
13
+ var syncControlData: SyncControlData { get async }
14
+ var requestedRewindHeight: BlockHeight? { get async }
15
+ var processedHeight: BlockHeight { get async }
16
+ var lastChainTipUpdateTime: TimeInterval { get async }
17
+ var lastScannedHeight: BlockHeight? { get async }
18
+ var lastEnhancedHeight: BlockHeight? { get async }
19
+
20
+ func update(state: CBPState) async
21
+ func update(syncControlData: SyncControlData) async
22
+ func update(processedHeight: BlockHeight) async
23
+ func update(lastChainTipUpdateTime: TimeInterval) async
24
+ func update(lastScannedHeight: BlockHeight) async
25
+ func update(lastDownloadedHeight: BlockHeight) async
26
+ func update(lastEnhancedHeight: BlockHeight?) async
27
+ func update(requestedRewindHeight: BlockHeight) async
28
+ }
29
+
30
+ actor ActionContextImpl: ActionContext {
31
+ var state: CBPState
32
+ var prevState: CBPState?
33
+ var syncControlData: SyncControlData
34
+ var requestedRewindHeight: BlockHeight?
35
+ /// Amount of blocks that have been processed so far
36
+ var processedHeight: BlockHeight = 0
37
+ /// Update chain tip must be called repeatadly, this value stores the previous update and help to decide when to call it again
38
+ var lastChainTipUpdateTime: TimeInterval = 0.0
39
+ var lastScannedHeight: BlockHeight?
40
+ var lastDownloadedHeight: BlockHeight?
41
+ var lastEnhancedHeight: BlockHeight?
42
+
43
+ init(state: CBPState) {
44
+ self.state = state
45
+ syncControlData = SyncControlData.empty
46
+ }
47
+
48
+ func update(state: CBPState) async {
49
+ prevState = self.state
50
+ self.state = state
51
+ }
52
+ func update(syncControlData: SyncControlData) async { self.syncControlData = syncControlData }
53
+ func update(processedHeight: BlockHeight) async { self.processedHeight = processedHeight }
54
+ func update(lastChainTipUpdateTime: TimeInterval) async { self.lastChainTipUpdateTime = lastChainTipUpdateTime }
55
+ func update(lastScannedHeight: BlockHeight) async { self.lastScannedHeight = lastScannedHeight }
56
+ func update(lastDownloadedHeight: BlockHeight) async { self.lastDownloadedHeight = lastDownloadedHeight }
57
+ func update(lastEnhancedHeight: BlockHeight?) async { self.lastEnhancedHeight = lastEnhancedHeight }
58
+ func update(requestedRewindHeight: BlockHeight) async { self.requestedRewindHeight = requestedRewindHeight }
59
+ }
60
+
61
+ enum CBPState: CaseIterable {
62
+ case idle
63
+ case migrateLegacyCacheDB
64
+ case validateServer
65
+ case updateSubtreeRoots
66
+ case updateChainTip
67
+ case processSuggestedScanRanges
68
+ case rewind
69
+ case download
70
+ case scan
71
+ case clearAlreadyScannedBlocks
72
+ case enhance
73
+ case fetchUTXO
74
+ case handleSaplingParams
75
+ case clearCache
76
+ case finished
77
+ case failed
78
+ case stopped
79
+ }
80
+
81
+ protocol Action {
82
+ /// If this is true and action fails with error then blocks cache is cleared.
83
+ var removeBlocksCacheWhenFailed: Bool { get }
84
+
85
+ // When any action is created it can get `DIContainer` and resolve any depedencies it requires.
86
+ // Every action uses `context` to get some informartion like download range.
87
+ //
88
+ // `didUpdate` is closure that action use to tell CBP that some part of the work is done. For example if download action would like to
89
+ // update progress on every block downloaded it can use this closure. Also if action doesn't need to update progress on partial work it doesn't
90
+ // need to use this closure at all.
91
+ //
92
+ // Each action updates context accordingly. It should at least set new state. Reason for this is that action can return different states for
93
+ // different conditions. And action is the thing that knows these conditions.
94
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext
95
+
96
+ // Should be called on each existing action when processor wants to stop. Some actions may do it's own background work.
97
+ func stop() async
98
+ }
@@ -0,0 +1,35 @@
1
+ //
2
+ // ClearCacheForLastScannedBatch.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 08.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class ClearAlreadyScannedBlocksAction {
11
+ let storage: CompactBlockRepository
12
+ let transactionRepository: TransactionRepository
13
+
14
+ init(container: DIContainer) {
15
+ storage = container.resolve(CompactBlockRepository.self)
16
+ transactionRepository = container.resolve(TransactionRepository.self)
17
+ }
18
+ }
19
+
20
+ extension ClearAlreadyScannedBlocksAction: Action {
21
+ var removeBlocksCacheWhenFailed: Bool { false }
22
+
23
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
24
+ guard let lastScannedHeight = await context.lastScannedHeight else {
25
+ throw ZcashError.compactBlockProcessorLastScannedHeight
26
+ }
27
+
28
+ try await storage.clear(upTo: lastScannedHeight)
29
+
30
+ await context.update(state: .enhance)
31
+ return context
32
+ }
33
+
34
+ func stop() async { }
35
+ }
@@ -0,0 +1,30 @@
1
+ //
2
+ // ClearCacheAction.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class ClearCacheAction {
11
+ let storage: CompactBlockRepository
12
+
13
+ init(container: DIContainer) {
14
+ storage = container.resolve(CompactBlockRepository.self)
15
+ }
16
+ }
17
+
18
+ extension ClearCacheAction: Action {
19
+ var removeBlocksCacheWhenFailed: Bool { false }
20
+
21
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
22
+ try await storage.clear()
23
+
24
+ await context.update(state: .processSuggestedScanRanges)
25
+
26
+ return context
27
+ }
28
+
29
+ func stop() async { }
30
+ }
@@ -0,0 +1,67 @@
1
+ //
2
+ // DownloadAction.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class DownloadAction {
11
+ let configProvider: CompactBlockProcessor.ConfigProvider
12
+ let downloader: BlockDownloader
13
+ let transactionRepository: TransactionRepository
14
+ let logger: Logger
15
+
16
+ init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
17
+ self.configProvider = configProvider
18
+ downloader = container.resolve(BlockDownloader.self)
19
+ transactionRepository = container.resolve(TransactionRepository.self)
20
+ logger = container.resolve(Logger.self)
21
+ }
22
+
23
+ private func update(context: ActionContext) async -> ActionContext {
24
+ await context.update(state: .scan)
25
+ return context
26
+ }
27
+ }
28
+
29
+ extension DownloadAction: Action {
30
+ var removeBlocksCacheWhenFailed: Bool { true }
31
+
32
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
33
+ guard let lastScannedHeight = await context.lastScannedHeight else {
34
+ return await update(context: context)
35
+ }
36
+
37
+ let config = await configProvider.config
38
+ let latestBlockHeight = await context.syncControlData.latestBlockHeight
39
+ // This action is executed for each batch (batch size is 100 blocks by default) until all the blocks in whole `downloadRange` are downloaded.
40
+ // So the right range for this batch must be computed.
41
+ let batchRangeStart = lastScannedHeight
42
+ let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
43
+
44
+ guard batchRangeStart <= batchRangeEnd else {
45
+ return await update(context: context)
46
+ }
47
+
48
+ let batchRange = batchRangeStart...batchRangeEnd
49
+ let potentialDownloadLimit = batchRange.upperBound + (2 * config.batchSize)
50
+ let downloadLimit = await context.syncControlData.latestBlockHeight >= potentialDownloadLimit ? potentialDownloadLimit : batchRangeEnd
51
+
52
+ logger.debug("Starting download with range: \(batchRangeStart)...\(batchRangeEnd)")
53
+
54
+ await downloader.update(latestDownloadedBlockHeight: batchRangeStart, force: true)
55
+ try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
56
+ await downloader.setDownloadLimit(downloadLimit)
57
+ await downloader.startDownload(maxBlockBufferSize: config.downloadBufferSize)
58
+
59
+ try await downloader.waitUntilRequestedBlocksAreDownloaded(in: batchRange)
60
+
61
+ return await update(context: context)
62
+ }
63
+
64
+ func stop() async {
65
+ await downloader.stopDownload()
66
+ }
67
+ }
@@ -0,0 +1,97 @@
1
+ //
2
+ // EnhanceAction.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class EnhanceAction {
11
+ let blockEnhancer: BlockEnhancer
12
+ let configProvider: CompactBlockProcessor.ConfigProvider
13
+ let logger: Logger
14
+
15
+ init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
16
+ blockEnhancer = container.resolve(BlockEnhancer.self)
17
+ self.configProvider = configProvider
18
+ logger = container.resolve(Logger.self)
19
+ }
20
+
21
+ func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
22
+ guard await context.syncControlData.latestScannedHeight != nil else {
23
+ await context.update(state: .clearCache)
24
+ return context
25
+ }
26
+
27
+ let latestBlockHeight = await context.syncControlData.latestBlockHeight
28
+ if lastScannedHeight >= latestBlockHeight {
29
+ await context.update(state: .clearCache)
30
+ } else {
31
+ await context.update(state: .updateChainTip)
32
+ }
33
+
34
+ return context
35
+ }
36
+ }
37
+
38
+ extension EnhanceAction: Action {
39
+ var removeBlocksCacheWhenFailed: Bool { false }
40
+
41
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
42
+ // Use `BlockEnhancer` to enhance blocks.
43
+ // This action is executed on each downloaded and scanned batch (typically each 100 blocks). But we want to run enhancement each 1000 blocks.
44
+
45
+ // if latestScannedHeight >= context.scanRanges.scanRange.upperBound then everything is processed and sync process should continue to end.
46
+ // If latestScannedHeight < context.scanRanges.scanRange.upperBound then set state to `download` because there are blocks to
47
+ // download and scan.
48
+
49
+ let config = await configProvider.config
50
+ guard let lastScannedHeight = await context.lastScannedHeight else {
51
+ throw ZcashError.compactBlockProcessorLastScannedHeight
52
+ }
53
+
54
+ guard let firstUnenhancedHeight = await context.syncControlData.firstUnenhancedHeight else {
55
+ return await decideWhatToDoNext(context: context, lastScannedHeight: lastScannedHeight)
56
+ }
57
+
58
+ let latestBlockHeight = await context.syncControlData.latestBlockHeight
59
+ let lastEnhancedHeight: BlockHeight
60
+ if let lastEnhancedHeightInContext = await context.lastEnhancedHeight {
61
+ lastEnhancedHeight = lastEnhancedHeightInContext
62
+ } else {
63
+ lastEnhancedHeight = -1
64
+ }
65
+ let enhanceRangeStart = max(firstUnenhancedHeight, lastEnhancedHeight + 1)
66
+ let enhanceRangeEnd = min(latestBlockHeight, lastScannedHeight)
67
+
68
+ // This may happen:
69
+ // For example whole enhance range is 0...2100 Without this force enhance is done for ranges: 0...1000, 1001...2000. And that's it.
70
+ // Last 100 blocks isn't enhanced.
71
+ //
72
+ // This force makes sure that all the blocks are enhanced even when last enhance happened < 1000 blocks ago.
73
+ let forceEnhance = enhanceRangeEnd == latestBlockHeight && enhanceRangeEnd - enhanceRangeStart <= config.enhanceBatchSize
74
+
75
+ if enhanceRangeStart <= enhanceRangeEnd && (forceEnhance || (lastScannedHeight - lastEnhancedHeight >= config.enhanceBatchSize)) {
76
+ let enhanceRange = enhanceRangeStart...enhanceRangeEnd
77
+ let transactions = try await blockEnhancer.enhance(
78
+ at: enhanceRange,
79
+ didEnhance: { progress in
80
+ if let foundTx = progress.lastFoundTransaction, progress.newlyMined {
81
+ await didUpdate(.minedTransaction(foundTx))
82
+ }
83
+ }
84
+ )
85
+
86
+ await context.update(lastEnhancedHeight: enhanceRange.upperBound)
87
+
88
+ if let transactions {
89
+ await didUpdate(.foundTransactions(transactions, enhanceRange))
90
+ }
91
+ }
92
+
93
+ return await decideWhatToDoNext(context: context, lastScannedHeight: lastScannedHeight)
94
+ }
95
+
96
+ func stop() async { }
97
+ }
@@ -0,0 +1,33 @@
1
+ //
2
+ // FetchUTXOsAction.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class FetchUTXOsAction {
11
+ let utxoFetcher: UTXOFetcher
12
+ let logger: Logger
13
+
14
+ init(container: DIContainer) {
15
+ utxoFetcher = container.resolve(UTXOFetcher.self)
16
+ logger = container.resolve(Logger.self)
17
+ }
18
+ }
19
+
20
+ extension FetchUTXOsAction: Action {
21
+ var removeBlocksCacheWhenFailed: Bool { false }
22
+
23
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
24
+ logger.debug("Fetching UTXOs")
25
+ let result = try await utxoFetcher.fetch() { _ in }
26
+ await didUpdate(.storedUTXOs(result))
27
+
28
+ await context.update(state: .handleSaplingParams)
29
+ return context
30
+ }
31
+
32
+ func stop() async { }
33
+ }
@@ -0,0 +1,70 @@
1
+ //
2
+ // MigrateLegacyCacheDB.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 10.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class MigrateLegacyCacheDBAction {
11
+ private let configProvider: CompactBlockProcessor.ConfigProvider
12
+ private let storage: CompactBlockRepository
13
+ private let transactionRepository: TransactionRepository
14
+ private let fileManager: ZcashFileManager
15
+
16
+ init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
17
+ self.configProvider = configProvider
18
+ storage = container.resolve(CompactBlockRepository.self)
19
+ transactionRepository = container.resolve(TransactionRepository.self)
20
+ fileManager = container.resolve(ZcashFileManager.self)
21
+ }
22
+
23
+ private func updateState(_ context: ActionContext) async -> ActionContext {
24
+ await context.update(state: .validateServer)
25
+ return context
26
+ }
27
+ }
28
+
29
+ extension MigrateLegacyCacheDBAction: Action {
30
+ var removeBlocksCacheWhenFailed: Bool { false }
31
+
32
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
33
+ let config = await configProvider.config
34
+ guard let legacyCacheDbURL = config.cacheDbURL else {
35
+ return await updateState(context)
36
+ }
37
+
38
+ guard legacyCacheDbURL != config.fsBlockCacheRoot else {
39
+ throw ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
40
+ }
41
+
42
+ // Instance with alias `default` is same as instance before the Alias was introduced. So it makes sense that only this instance handles
43
+ // legacy cache DB. Any instance with different than `default` alias was created after the Alias was introduced and at this point legacy
44
+ // cache DB doesn't exist anymore. So there is nothing to migrate for instances with not default Alias.
45
+ guard config.alias == .default else {
46
+ return await updateState(context)
47
+ }
48
+
49
+ // if the URL provided is not readable, it means that the client has a reference
50
+ // to the cacheDb file but it has been deleted in a prior sync cycle. there's
51
+ // nothing to do here.
52
+ guard fileManager.isReadableFile(atPath: legacyCacheDbURL.path) else {
53
+ return await updateState(context)
54
+ }
55
+
56
+ do {
57
+ // if there's a readable file at the provided URL, delete it.
58
+ try fileManager.removeItem(at: legacyCacheDbURL)
59
+ } catch {
60
+ throw ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(error)
61
+ }
62
+
63
+ // create the storage
64
+ try await self.storage.create()
65
+
66
+ return await updateState(context)
67
+ }
68
+
69
+ func stop() { }
70
+ }
@@ -0,0 +1,60 @@
1
+ //
2
+ // ProcessSuggestedScanRangesAction.swift
3
+ //
4
+ //
5
+ // Created by Lukáš Korba on 02.08.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class ProcessSuggestedScanRangesAction {
11
+ let rustBackend: ZcashRustBackendWelding
12
+ let service: LightWalletService
13
+ let logger: Logger
14
+
15
+ init(container: DIContainer) {
16
+ service = container.resolve(LightWalletService.self)
17
+ rustBackend = container.resolve(ZcashRustBackendWelding.self)
18
+ logger = container.resolve(Logger.self)
19
+ }
20
+ }
21
+
22
+ extension ProcessSuggestedScanRangesAction: Action {
23
+ var removeBlocksCacheWhenFailed: Bool { false }
24
+
25
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
26
+ logger.info("Getting the suggested scan ranges from the wallet database.")
27
+ let scanRanges = try await rustBackend.suggestScanRanges()
28
+
29
+ if let firstRange = scanRanges.first {
30
+ let rangeStartExclusive = firstRange.range.lowerBound - 1
31
+ let rangeEndInclusive = firstRange.range.upperBound - 1
32
+
33
+ let syncControlData = SyncControlData(
34
+ latestBlockHeight: rangeEndInclusive,
35
+ latestScannedHeight: rangeStartExclusive,
36
+ firstUnenhancedHeight: rangeStartExclusive + 1
37
+ )
38
+
39
+ logger.debug("""
40
+ Init numbers:
41
+ latestBlockHeight [BC]: \(rangeEndInclusive)
42
+ latestScannedHeight [DB]: \(rangeStartExclusive)
43
+ firstUnenhancedHeight [DB]: \(rangeStartExclusive + 1)
44
+ """)
45
+
46
+ await context.update(lastEnhancedHeight: nil)
47
+ await context.update(lastScannedHeight: rangeStartExclusive)
48
+ await context.update(lastDownloadedHeight: rangeStartExclusive)
49
+ await context.update(syncControlData: syncControlData)
50
+
51
+ await context.update(state: .download)
52
+ } else {
53
+ await context.update(state: .finished)
54
+ }
55
+
56
+ return context
57
+ }
58
+
59
+ func stop() async { }
60
+ }
@@ -0,0 +1,48 @@
1
+ //
2
+ // RewindAction.swift
3
+ //
4
+ //
5
+ // Created by Lukáš Korba on 09.08.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class RewindAction {
11
+ let downloader: BlockDownloader
12
+ let rustBackend: ZcashRustBackendWelding
13
+ let downloaderService: BlockDownloaderService
14
+ let logger: Logger
15
+
16
+ init(container: DIContainer) {
17
+ downloader = container.resolve(BlockDownloader.self)
18
+ rustBackend = container.resolve(ZcashRustBackendWelding.self)
19
+ downloaderService = container.resolve(BlockDownloaderService.self)
20
+ logger = container.resolve(Logger.self)
21
+ }
22
+
23
+ private func update(context: ActionContext) async -> ActionContext {
24
+ await context.update(state: .processSuggestedScanRanges)
25
+ return context
26
+ }
27
+ }
28
+
29
+ extension RewindAction: Action {
30
+ var removeBlocksCacheWhenFailed: Bool { false }
31
+
32
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
33
+ guard let rewindHeight = await context.requestedRewindHeight else {
34
+ return await update(context: context)
35
+ }
36
+
37
+ logger.debug("Executing rewind.")
38
+ await downloader.rewind(latestDownloadedBlockHeight: rewindHeight)
39
+ try await rustBackend.rewindToHeight(height: Int32(rewindHeight))
40
+
41
+ // clear cache
42
+ try await downloaderService.rewind(to: rewindHeight)
43
+
44
+ return await update(context: context)
45
+ }
46
+
47
+ func stop() async { }
48
+ }
@@ -0,0 +1,33 @@
1
+ //
2
+ // SaplingParamsAction.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class SaplingParamsAction {
11
+ let saplingParametersHandler: SaplingParametersHandler
12
+ let logger: Logger
13
+
14
+ init(container: DIContainer) {
15
+ saplingParametersHandler = container.resolve(SaplingParametersHandler.self)
16
+ logger = container.resolve(Logger.self)
17
+ }
18
+ }
19
+
20
+ extension SaplingParamsAction: Action {
21
+ var removeBlocksCacheWhenFailed: Bool { false }
22
+
23
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
24
+ logger.debug("Fetching sapling parameters")
25
+ try await saplingParametersHandler.handleIfNeeded()
26
+
27
+ await context.update(state: .updateSubtreeRoots)
28
+
29
+ return context
30
+ }
31
+
32
+ func stop() async { }
33
+ }
@@ -0,0 +1,95 @@
1
+ //
2
+ // ScanAction.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 05.05.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ final class ScanAction {
11
+ let configProvider: CompactBlockProcessor.ConfigProvider
12
+ let blockScanner: BlockScanner
13
+ let rustBackend: ZcashRustBackendWelding
14
+ let latestBlocksDataProvider: LatestBlocksDataProvider
15
+ let logger: Logger
16
+
17
+ init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
18
+ self.configProvider = configProvider
19
+ blockScanner = container.resolve(BlockScanner.self)
20
+ rustBackend = container.resolve(ZcashRustBackendWelding.self)
21
+ latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
22
+ logger = container.resolve(Logger.self)
23
+ }
24
+
25
+ private func update(context: ActionContext) async -> ActionContext {
26
+ await context.update(state: .clearAlreadyScannedBlocks)
27
+ return context
28
+ }
29
+ }
30
+
31
+ extension ScanAction: Action {
32
+ var removeBlocksCacheWhenFailed: Bool { true }
33
+
34
+ func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
35
+ guard let lastScannedHeight = await context.lastScannedHeight else {
36
+ return await update(context: context)
37
+ }
38
+
39
+ let config = await configProvider.config
40
+ let latestBlockHeight = await context.syncControlData.latestBlockHeight
41
+ // This action is executed for each batch (batch size is 100 blocks by default) until all the blocks in whole `scanRange` are scanned.
42
+ // So the right range for this batch must be computed.
43
+ let batchRangeStart = lastScannedHeight
44
+ let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
45
+
46
+ guard batchRangeStart <= batchRangeEnd else {
47
+ return await update(context: context)
48
+ }
49
+
50
+ let batchRange = batchRangeStart...batchRangeEnd
51
+
52
+ logger.debug("Starting scan blocks with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
53
+
54
+ do {
55
+ try await blockScanner.scanBlocks(at: batchRange) { [weak self] lastScannedHeight, increment in
56
+ let processedHeight = await context.processedHeight
57
+ let incrementedprocessedHeight = processedHeight + BlockHeight(increment)
58
+ await context.update(processedHeight: incrementedprocessedHeight)
59
+ await self?.latestBlocksDataProvider.updateScannedData()
60
+
61
+ // report scan progress only if it's available
62
+ if let scanProgress = try? await self?.rustBackend.getScanProgress() {
63
+ let progress = try scanProgress.progress()
64
+ self?.logger.debug("progress: \(progress)")
65
+ await didUpdate(.syncProgress(progress))
66
+ }
67
+
68
+ // ScanAction is controlled locally so it must report back the updated scanned height
69
+ await context.update(lastScannedHeight: lastScannedHeight)
70
+ }
71
+ } catch ZcashError.rustScanBlocks(let errorMsg) {
72
+ if isContinuityError(errorMsg) {
73
+ await context.update(requestedRewindHeight: batchRange.lowerBound - 10)
74
+ await context.update(state: .rewind)
75
+ return context
76
+ } else {
77
+ throw ZcashError.rustScanBlocks(errorMsg)
78
+ }
79
+ } catch {
80
+ throw error
81
+ }
82
+
83
+ return await update(context: context)
84
+ }
85
+
86
+ func stop() async { }
87
+ }
88
+
89
+ private extension ScanAction {
90
+ func isContinuityError(_ errorMsg: String) -> Bool {
91
+ errorMsg.contains("The parent hash of proposed block does not correspond to the block hash at height")
92
+ || errorMsg.contains("Block height discontinuity at height")
93
+ || errorMsg.contains("note commitment tree size provided by a compact block did not match the expected size at height")
94
+ }
95
+ }