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
@@ -0,0 +1,51 @@
1
+ //
2
+ // CompactBlockValidationInformation.swift
3
+ // ZcashLightClientKit
4
+ //
5
+ // Created by Francisco Gindre on 10/30/19.
6
+ // Copyright © 2019 Electric Coin Company. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ protocol BlockValidator {
12
+ /// Validate all the downloaded blocks that haven't been yet validated.
13
+ func validate() async throws
14
+ }
15
+
16
+ struct BlockValidatorImpl {
17
+ let rustBackend: ZcashRustBackendWelding
18
+ let metrics: SDKMetrics
19
+ let logger: Logger
20
+ }
21
+
22
+ extension BlockValidatorImpl: BlockValidator {
23
+ /// - Throws:
24
+ /// - `rustValidateCombinedChainValidationFailed` if there was an error during validation unrelated to chain validity.
25
+ /// - `rustValidateCombinedChainInvalidChain(upperBound)` if the combined chain is invalid. `upperBound` is the height of the highest invalid
26
+ /// block(on the assumption that the highest block in the cache database is correct).
27
+ func validate() async throws {
28
+ try Task.checkCancellation()
29
+
30
+ let startTime = Date()
31
+ do {
32
+ try await rustBackend.validateCombinedChain(limit: 0)
33
+ pushProgressReport(startTime: startTime, finishTime: Date())
34
+ logger.debug("validateChainFinished")
35
+ } catch {
36
+ logger.debug("Validate chain failed with \(error)")
37
+ pushProgressReport(startTime: startTime, finishTime: Date())
38
+ throw error
39
+ }
40
+ }
41
+
42
+ private func pushProgressReport(startTime: Date, finishTime: Date) {
43
+ metrics.pushProgressReport(
44
+ progress: BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0),
45
+ start: startTime,
46
+ end: finishTime,
47
+ batchSize: 0,
48
+ operation: .validateBlocks
49
+ )
50
+ }
51
+ }
@@ -24,8 +24,8 @@ public protocol ClosureSynchronizer {
24
24
 
25
25
  func prepare(
26
26
  with seed: [UInt8]?,
27
+ viewingKeys: [UnifiedFullViewingKey],
27
28
  walletBirthday: BlockHeight,
28
- for walletMode: WalletInitMode,
29
29
  completion: @escaping (Result<Initializer.InitializationResult, Error>) -> Void
30
30
  )
31
31
 
@@ -51,6 +51,7 @@ public protocol ClosureSynchronizer {
51
51
  completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
52
52
  )
53
53
 
54
+ func pendingTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
54
55
  func clearedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
55
56
  func sentTranscations(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
56
57
  func receivedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
@@ -24,8 +24,8 @@ public protocol CombineSynchronizer {
24
24
 
25
25
  func prepare(
26
26
  with seed: [UInt8]?,
27
- walletBirthday: BlockHeight,
28
- for walletMode: WalletInitMode
27
+ viewingKeys: [UnifiedFullViewingKey],
28
+ walletBirthday: BlockHeight
29
29
  ) -> SinglePublisher<Initializer.InitializationResult, Error>
30
30
 
31
31
  func start(retry: Bool) -> CompletablePublisher<Error>
@@ -49,6 +49,7 @@ public protocol CombineSynchronizer {
49
49
  ) -> SinglePublisher<ZcashTransaction.Overview, Error>
50
50
 
51
51
  var allTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
52
+ var pendingTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
52
53
  var sentTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
53
54
  var receivedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
54
55
 
@@ -58,6 +59,8 @@ public protocol CombineSynchronizer {
58
59
 
59
60
  func getRecipients(for transaction: ZcashTransaction.Overview) -> SinglePublisher<[TransactionRecipient], Never>
60
61
 
62
+ func allPendingTransactions() -> SinglePublisher<[ZcashTransaction.Overview], Error>
63
+
61
64
  func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error>
62
65
 
63
66
  func latestHeight() -> SinglePublisher<BlockHeight, Error>
@@ -88,22 +88,24 @@ public enum ZcashSDK {
88
88
  // MARK: Defaults
89
89
 
90
90
  /// Default size of batches of blocks to request from the compact block service. Which was used both for scanning and downloading.
91
+ /// consider basing your code assumptions on `DefaultDownloadBatch` and `DefaultScanningBatch` instead.
92
+ @available(*, deprecated, message: "this value is being deprecated in favor of `DefaultDownloadBatch` and `DefaultScanningBatch`")
91
93
  public static let DefaultBatchSize = 100
92
-
93
- /// Default batch size for enhancing transactions for the compact block processor
94
- public static let DefaultEnhanceBatch = 1000
94
+
95
+ /// Default batch size for downloading blocks for the compact block processor. Be careful with this number. This amount of blocks is held in
96
+ /// memory at some point of the sync process.
97
+ /// This values can't be smaller than `DefaultScanningBatch`. Otherwise bad things will happen.
98
+ public static let DefaultDownloadBatch = 100
99
+
100
+ /// Default batch size for scanning blocks for the compact block processor
101
+ public static let DefaultScanningBatch = 100
95
102
 
96
103
  /// Default amount of time, in in seconds, to poll for new blocks. Typically, this should be about half the average
97
104
  /// block time.
98
105
  public static let defaultPollInterval: TimeInterval = 20
99
106
 
100
107
  /// Default attempts at retrying.
101
- // This has been tweaked in https://github.com/zcash/ZcashLightClientKit/issues/1303
102
- // There are many places that rely on hasRetryAttempt() that reads and compares this value.
103
- // Better solution is to think about retry logic and potentially either remove completely
104
- // or implement more sophisticated solutuion. Until that time, Int.max solves our UX issues
105
- // TODO: [#1304] smart retry logic, https://github.com/zcash/ZcashLightClientKit/issues/1304
106
- public static let defaultRetries = Int.max
108
+ public static let defaultRetries: Int = 5
107
109
 
108
110
  /// The default maximum amount of time to wait during retry backoff intervals. Failed loops will never wait longer than
109
111
  /// this before retrying.
@@ -159,13 +161,19 @@ public protocol NetworkConstants {
159
161
  /// Default prefix for db filenames
160
162
  static var defaultDbNamePrefix: String { get }
161
163
 
162
- /// Returns the default fee, hardcoded 10k Zatoshi is the minimum ZIP 317 fee
163
- static func defaultFee() -> Zatoshi
164
+ /// fixed height where the SDK considers that the ZIP-321 was deployed. This is a workaround
165
+ /// for librustzcash not figuring out the tx fee from the tx itself.
166
+ static var feeChangeHeight: BlockHeight { get }
167
+
168
+ /// Returns the default fee according to the blockheight. see [ZIP-313](https://zips.z.cash/zip-0313)
169
+ static func defaultFee(for height: BlockHeight) -> Zatoshi
164
170
  }
165
171
 
166
172
  public extension NetworkConstants {
167
- static func defaultFee() -> Zatoshi {
168
- Zatoshi(10_000)
173
+ static func defaultFee(for height: BlockHeight = BlockHeight.max) -> Zatoshi {
174
+ guard height >= feeChangeHeight else { return Zatoshi(10_000) }
175
+
176
+ return Zatoshi(1_000)
169
177
  }
170
178
  }
171
179
 
@@ -183,6 +191,8 @@ public enum ZcashSDKMainnetConstants: NetworkConstants {
183
191
  public static let defaultCacheDbName = "caches.db"
184
192
 
185
193
  public static let defaultDbNamePrefix = "ZcashSdk_mainnet_"
194
+
195
+ public static let feeChangeHeight: BlockHeight = 1_077_550
186
196
  }
187
197
 
188
198
  public enum ZcashSDKTestnetConstants: NetworkConstants {
@@ -199,4 +209,7 @@ public enum ZcashSDKTestnetConstants: NetworkConstants {
199
209
  public static let defaultFsBlockDbRootName = "fs_cache"
200
210
 
201
211
  public static let defaultDbNamePrefix = "ZcashSdk_testnet_"
212
+
213
+ /// Estimated height where wallets are supposed to change the fee
214
+ public static let feeChangeHeight: BlockHeight = 1_028_500
202
215
  }
@@ -0,0 +1,112 @@
1
+ //
2
+ // BlockDao.swift
3
+ // ZcashLightClientKit
4
+ //
5
+ // Created by Francisco Gindre on 10/16/19.
6
+ // Copyright © 2019 Electric Coin Company. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+ import SQLite
11
+
12
+ protocol BlockDao {
13
+ func latestBlockHeight() throws -> BlockHeight
14
+ func latestBlock() throws -> Block?
15
+ func block(at height: BlockHeight) throws -> Block?
16
+ }
17
+
18
+ struct Block: Codable {
19
+ enum CodingKeys: String, CodingKey {
20
+ case height
21
+ case hash
22
+ case time
23
+ case saplingTree = "sapling_tree"
24
+ }
25
+
26
+ enum TableStructure {
27
+ static let height = Expression<Int>(Block.CodingKeys.height.rawValue)
28
+ static let hash = Expression<Blob>(Block.CodingKeys.hash.rawValue)
29
+ static let time = Expression<Int>(Block.CodingKeys.time.rawValue)
30
+ static let saplingTree = Expression<Blob>(Block.CodingKeys.saplingTree.rawValue)
31
+ }
32
+
33
+ let height: BlockHeight
34
+ let hash: Data
35
+ let time: Int
36
+ let saplingTree: Data
37
+
38
+ static let table = Table("blocks")
39
+ }
40
+
41
+ class BlockSQLDAO: BlockDao {
42
+ let dbProvider: ConnectionProvider
43
+ let table: Table
44
+ let height = Expression<Int>("height")
45
+
46
+ init(dbProvider: ConnectionProvider) {
47
+ self.dbProvider = dbProvider
48
+ self.table = Table("Blocks")
49
+ }
50
+
51
+ /// - Throws:
52
+ /// - `blockDAOCantDecode` if block data loaded from DB can't be decoded to `Block` object.
53
+ /// - `blockDAOBlock` if sqlite query to load block metadata failed.
54
+ func block(at height: BlockHeight) throws -> Block? {
55
+ do {
56
+ return try dbProvider
57
+ .connection()
58
+ .prepare(Block.table.filter(Block.TableStructure.height == height).limit(1))
59
+ .map {
60
+ do {
61
+ return try $0.decode()
62
+ } catch {
63
+ throw ZcashError.blockDAOCantDecode(error)
64
+ }
65
+ }
66
+ .first
67
+ } catch {
68
+ if let error = error as? ZcashError {
69
+ throw error
70
+ } else {
71
+ throw ZcashError.blockDAOBlock(error)
72
+ }
73
+ }
74
+ }
75
+
76
+ /// - Throws: `blockDAOLatestBlockHeight` if sqlite to fetch height fails.
77
+ func latestBlockHeight() throws -> BlockHeight {
78
+ do {
79
+ return try dbProvider.connection().scalar(table.select(height.max)) ?? BlockHeight.empty()
80
+ } catch {
81
+ throw ZcashError.blockDAOLatestBlockHeight(error)
82
+ }
83
+ }
84
+
85
+ func latestBlock() throws -> Block? {
86
+ do {
87
+ return try dbProvider
88
+ .connection()
89
+ .prepare(Block.table.order(height.desc).limit(1))
90
+ .map {
91
+ do {
92
+ return try $0.decode()
93
+ } catch {
94
+ throw ZcashError.blockDAOLatestBlockCantDecode(error)
95
+ }
96
+ }
97
+ .first
98
+ } catch {
99
+ if let error = error as? ZcashError {
100
+ throw error
101
+ } else {
102
+ throw ZcashError.blockDAOLatestBlock(error)
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ extension BlockSQLDAO: BlockRepository {
109
+ func lastScannedBlockHeight() -> BlockHeight {
110
+ (try? self.latestBlockHeight()) ?? BlockHeight.empty()
111
+ }
112
+ }
@@ -8,22 +8,6 @@
8
8
  import Foundation
9
9
  import SQLite
10
10
 
11
- extension Connection {
12
- public func scalarLocked<V: Value>(_ query: ScalarQuery<V?>) throws -> V.ValueType? {
13
- globalDBLock.lock()
14
- defer { globalDBLock.unlock() }
15
-
16
- return try scalar(query)
17
- }
18
-
19
- public func scalarLocked<V: Value>(_ query: ScalarQuery<V>) throws -> V {
20
- globalDBLock.lock()
21
- defer { globalDBLock.unlock() }
22
-
23
- return try scalar(query)
24
- }
25
- }
26
-
27
11
  class TransactionSQLDAO: TransactionRepository {
28
12
  enum NotesTableStructure {
29
13
  static let transactionID = Expression<Int>("tx")
@@ -32,12 +16,14 @@ class TransactionSQLDAO: TransactionRepository {
32
16
 
33
17
  let dbProvider: ConnectionProvider
34
18
 
19
+ private let blockDao: BlockSQLDAO
35
20
  private let transactionsView = View("v_transactions")
36
21
  private let txOutputsView = View("v_tx_outputs")
37
22
  private let traceClosure: ((String) -> Void)?
38
23
 
39
24
  init(dbProvider: ConnectionProvider, traceClosure: ((String) -> Void)? = nil) {
40
25
  self.dbProvider = dbProvider
26
+ self.blockDao = BlockSQLDAO(dbProvider: dbProvider)
41
27
  self.traceClosure = traceClosure
42
28
  }
43
29
 
@@ -51,13 +37,25 @@ class TransactionSQLDAO: TransactionRepository {
51
37
  dbProvider.close()
52
38
  }
53
39
 
40
+ func blockForHeight(_ height: BlockHeight) async throws -> Block? {
41
+ try blockDao.block(at: height)
42
+ }
43
+
44
+ func lastScannedHeight() async throws -> BlockHeight {
45
+ try blockDao.latestBlockHeight()
46
+ }
47
+
48
+ func lastScannedBlock() async throws -> Block? {
49
+ try blockDao.latestBlock()
50
+ }
51
+
54
52
  func isInitialized() async throws -> Bool {
55
53
  true
56
54
  }
57
55
 
58
56
  func countAll() async throws -> Int {
59
57
  do {
60
- return try connection().scalarLocked(transactionsView.count)
58
+ return try connection().scalar(transactionsView.count)
61
59
  } catch {
62
60
  throw ZcashError.transactionRepositoryCountAll(error)
63
61
  }
@@ -65,12 +63,20 @@ class TransactionSQLDAO: TransactionRepository {
65
63
 
66
64
  func countUnmined() async throws -> Int {
67
65
  do {
68
- return try connection().scalarLocked(transactionsView.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
66
+ return try connection().scalar(transactionsView.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
69
67
  } catch {
70
68
  throw ZcashError.transactionRepositoryCountUnmined(error)
71
69
  }
72
70
  }
73
71
 
72
+ func find(id: Int) async throws -> ZcashTransaction.Overview {
73
+ let query = transactionsView
74
+ .filter(ZcashTransaction.Overview.Column.id == id)
75
+ .limit(1)
76
+
77
+ return try execute(query) { try ZcashTransaction.Overview(row: $0) }
78
+ }
79
+
74
80
  func find(rawID: Data) async throws -> ZcashTransaction.Overview {
75
81
  let query = transactionsView
76
82
  .filter(ZcashTransaction.Overview.Column.rawID == Blob(bytes: rawID.bytes))
@@ -81,7 +87,7 @@ class TransactionSQLDAO: TransactionRepository {
81
87
 
82
88
  func find(offset: Int, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview] {
83
89
  let query = transactionsView
84
- .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
90
+ .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
85
91
  .filterQueryFor(kind: kind)
86
92
  .limit(limit, offset: offset)
87
93
 
@@ -90,7 +96,7 @@ class TransactionSQLDAO: TransactionRepository {
90
96
 
91
97
  func find(in range: CompactBlockRange, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview] {
92
98
  let query = transactionsView
93
- .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
99
+ .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
94
100
  .filter(
95
101
  ZcashTransaction.Overview.Column.minedHeight >= BlockHeight(range.lowerBound) &&
96
102
  ZcashTransaction.Overview.Column.minedHeight <= BlockHeight(range.upperBound)
@@ -103,18 +109,17 @@ class TransactionSQLDAO: TransactionRepository {
103
109
 
104
110
  func find(from transaction: ZcashTransaction.Overview, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview] {
105
111
  guard
106
- let transactionBlockHeight = transaction.minedHeight
112
+ let transactionIndex = transaction.index,
113
+ let transactionBlockTime = transaction.blockTime
107
114
  else { throw ZcashError.transactionRepositoryTransactionMissingRequiredFields }
108
115
 
109
- let transactionIndex = transaction.index ?? Int.max
110
116
  let query = transactionsView
111
- .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
117
+ .order(
118
+ (ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc
119
+ )
112
120
  .filter(
113
- transactionBlockHeight > ZcashTransaction.Overview.Column.minedHeight
114
- || (
115
- transactionBlockHeight == ZcashTransaction.Overview.Column.minedHeight &&
116
- transactionIndex > (ZcashTransaction.Overview.Column.index ?? -1)
117
- )
121
+ Int64(transactionBlockTime) > ZcashTransaction.Overview.Column.blockTime
122
+ && transactionIndex > ZcashTransaction.Overview.Column.index
118
123
  )
119
124
  .filterQueryFor(kind: kind)
120
125
  .limit(limit)
@@ -125,7 +130,7 @@ class TransactionSQLDAO: TransactionRepository {
125
130
  func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
126
131
  let query = transactionsView
127
132
  .filterQueryFor(kind: .received)
128
- .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
133
+ .order(ZcashTransaction.Overview.Column.id.desc, (ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
129
134
  .limit(limit, offset: offset)
130
135
 
131
136
  return try execute(query) { try ZcashTransaction.Overview(row: $0) }
@@ -134,7 +139,7 @@ class TransactionSQLDAO: TransactionRepository {
134
139
  func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
135
140
  let query = transactionsView
136
141
  .filterQueryFor(kind: .sent)
137
- .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
142
+ .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
138
143
  .limit(limit, offset: offset)
139
144
 
140
145
  return try execute(query) { try ZcashTransaction.Overview(row: $0) }
@@ -143,7 +148,7 @@ class TransactionSQLDAO: TransactionRepository {
143
148
  func findPendingTransactions(latestHeight: BlockHeight, offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
144
149
  let query = transactionsView
145
150
  .filterPendingFrom(latestHeight)
146
- .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
151
+ .order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
147
152
  .limit(limit, offset: offset)
148
153
 
149
154
  return try execute(query) { try ZcashTransaction.Overview(row: $0) }
@@ -151,22 +156,22 @@ class TransactionSQLDAO: TransactionRepository {
151
156
 
152
157
  func findMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo] {
153
158
  do {
154
- return try await getTransactionOutputs(for: transaction.rawID)
159
+ return try await getTransactionOutputs(for: transaction.id)
155
160
  .compactMap { $0.memo }
156
161
  } catch {
157
162
  throw ZcashError.transactionRepositoryFindMemos(error)
158
163
  }
159
164
  }
160
165
 
161
- func getTransactionOutputs(for rawID: Data) async throws -> [ZcashTransaction.Output] {
166
+ func getTransactionOutputs(for id: Int) async throws -> [ZcashTransaction.Output] {
162
167
  let query = self.txOutputsView
163
- .filter(ZcashTransaction.Output.Column.rawID == Blob(bytes: rawID.bytes))
168
+ .filter(ZcashTransaction.Output.Column.idTx == id)
164
169
 
165
170
  return try execute(query) { try ZcashTransaction.Output(row: $0) }
166
171
  }
167
172
 
168
- func getRecipients(for rawID: Data) async throws -> [TransactionRecipient] {
169
- try await getTransactionOutputs(for: rawID).map { $0.recipient }
173
+ func getRecipients(for id: Int) async throws -> [TransactionRecipient] {
174
+ try await getTransactionOutputs(for: id).map { $0.recipient }
170
175
  }
171
176
 
172
177
  private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> Entity {
@@ -176,14 +181,11 @@ class TransactionSQLDAO: TransactionRepository {
176
181
  }
177
182
 
178
183
  private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> [Entity] {
179
- globalDBLock.lock()
180
- defer { globalDBLock.unlock() }
181
-
182
184
  do {
183
185
  let entities = try connection()
184
186
  .prepare(query)
185
187
  .map(createEntity)
186
-
188
+
187
189
  return entities
188
190
  } catch {
189
191
  if let error = error as? ZcashError {
@@ -109,9 +109,6 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
109
109
  )
110
110
  """
111
111
  do {
112
- globalDBLock.lock()
113
- defer { globalDBLock.unlock() }
114
-
115
112
  try dbProvider.connection().run(stringStatement)
116
113
  } catch {
117
114
  throw ZcashError.unspentTransactionOutputDAOCreateTable(error)
@@ -121,11 +118,8 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
121
118
  /// - Throws: `unspentTransactionOutputDAOStore` if sqlite query fails.
122
119
  func store(utxos: [UnspentTransactionOutputEntity]) async throws {
123
120
  do {
124
- globalDBLock.lock()
125
- defer { globalDBLock.unlock() }
126
-
127
121
  let db = try dbProvider.connection()
128
- try db.transaction {
122
+ try dbProvider.connection().transaction {
129
123
  for utxo in utxos.map({ $0 as? UTXO ?? $0.asUTXO() }) {
130
124
  try db.run(table.insert(utxo))
131
125
  }
@@ -138,9 +132,6 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
138
132
  /// - Throws: `unspentTransactionOutputDAOClearAll` if sqlite query fails.
139
133
  func clearAll(address: String?) async throws {
140
134
  do {
141
- globalDBLock.lock()
142
- defer { globalDBLock.unlock() }
143
-
144
135
  if let tAddr = address {
145
136
  try dbProvider.connection().run(table.filter(TableColumns.address == tAddr).delete())
146
137
  } else {
@@ -187,16 +178,16 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
187
178
  /// - Throws: `unspentTransactionOutputDAOBalance` if sqlite query fails.
188
179
  func balance(address: String, latestHeight: BlockHeight) async throws -> WalletBalance {
189
180
  do {
190
- let verified = try dbProvider.connection().scalarLocked(
181
+ let verified = try dbProvider.connection().scalar(
191
182
  table.select(TableColumns.valueZat.sum)
192
183
  .filter(TableColumns.address == address)
193
184
  .filter(TableColumns.height <= latestHeight - ZcashSDK.defaultStaleTolerance)
194
185
  ) ?? 0
195
- let total = try dbProvider.connection().scalarLocked(
186
+ let total = try dbProvider.connection().scalar(
196
187
  table.select(TableColumns.valueZat.sum)
197
188
  .filter(TableColumns.address == address)
198
189
  ) ?? 0
199
-
190
+
200
191
  return WalletBalance(
201
192
  verified: Zatoshi(Int64(verified)),
202
193
  total: Zatoshi(Int64(total))
@@ -66,9 +66,6 @@ class AccountSQDAO: AccountRepository {
66
66
  /// - `accountDAOGetAll` if sqlite query fetching account data failed.
67
67
  func getAll() throws -> [AccountEntity] {
68
68
  do {
69
- globalDBLock.lock()
70
- defer { globalDBLock.unlock() }
71
-
72
69
  return try dbProvider.connection()
73
70
  .prepare(table)
74
71
  .map { row -> DbAccount in
@@ -93,9 +90,6 @@ class AccountSQDAO: AccountRepository {
93
90
  func findBy(account: Int) throws -> AccountEntity? {
94
91
  let query = table.filter(TableColums.account == account).limit(1)
95
92
  do {
96
- globalDBLock.lock()
97
- defer { globalDBLock.unlock() }
98
-
99
93
  return try dbProvider.connection()
100
94
  .prepare(query)
101
95
  .map {
@@ -125,9 +119,6 @@ class AccountSQDAO: AccountRepository {
125
119
 
126
120
  let updatedRows: Int
127
121
  do {
128
- globalDBLock.lock()
129
- defer { globalDBLock.unlock() }
130
-
131
122
  updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
132
123
  } catch {
133
124
  throw ZcashError.accountDAOUpdate(error)
@@ -0,0 +1,24 @@
1
+ //
2
+ // BlockProgress.swift
3
+ //
4
+ //
5
+ // Created by Michal Fousek on 03.02.2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ public struct BlockProgress: Equatable {
11
+ public let startHeight: BlockHeight
12
+ public let targetHeight: BlockHeight
13
+ public let progressHeight: BlockHeight
14
+
15
+ public var progress: Float {
16
+ let overall = self.targetHeight - self.startHeight
17
+
18
+ return overall > 0 ? Float((self.progressHeight - self.startHeight)) / Float(overall) : 0
19
+ }
20
+ }
21
+
22
+ public extension BlockProgress {
23
+ static let nullProgress = BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0)
24
+ }
@@ -25,9 +25,9 @@ public enum ZcashTransaction {
25
25
  init(
26
26
  currentHeight: BlockHeight,
27
27
  minedHeight: BlockHeight?,
28
- expiredUnmined: Bool?
28
+ expiredUnmined: Bool
29
29
  ) {
30
- guard let expiredUnmined, !expiredUnmined else {
30
+ guard !expiredUnmined else {
31
31
  self = .expired
32
32
  return
33
33
  }
@@ -48,6 +48,7 @@ public enum ZcashTransaction {
48
48
  public let blockTime: TimeInterval?
49
49
  public let expiryHeight: BlockHeight?
50
50
  public let fee: Zatoshi?
51
+ public let id: Int
51
52
  public let index: Int?
52
53
  public var isSentTransaction: Bool { value < Zatoshi(0) }
53
54
  public let hasChange: Bool
@@ -58,7 +59,7 @@ public enum ZcashTransaction {
58
59
  public let receivedNoteCount: Int
59
60
  public let sentNoteCount: Int
60
61
  public let value: Zatoshi
61
- public let isExpiredUmined: Bool?
62
+ public let isExpiredUmined: Bool
62
63
  }
63
64
 
64
65
  public struct Output {
@@ -78,7 +79,7 @@ public enum ZcashTransaction {
78
79
  }
79
80
  }
80
81
 
81
- public let rawID: Data
82
+ public let idTx: Int
82
83
  public let pool: Pool
83
84
  public let index: Int
84
85
  public let fromAccount: Int?
@@ -98,7 +99,7 @@ public enum ZcashTransaction {
98
99
 
99
100
  extension ZcashTransaction.Output {
100
101
  enum Column {
101
- static let rawID = Expression<Blob>("txid")
102
+ static let idTx = Expression<Int>("id_tx")
102
103
  static let pool = Expression<Int>("output_pool")
103
104
  static let index = Expression<Int>("output_index")
104
105
  static let toAccount = Expression<Int?>("to_account")
@@ -111,7 +112,7 @@ extension ZcashTransaction.Output {
111
112
 
112
113
  init(row: Row) throws {
113
114
  do {
114
- rawID = Data(blob: try row.get(Column.rawID))
115
+ idTx = try row.get(Column.idTx)
115
116
  pool = .init(rawValue: try row.get(Column.pool))
116
117
  index = try row.get(Column.index)
117
118
  fromAccount = try row.get(Column.fromAccount)
@@ -143,6 +144,7 @@ extension ZcashTransaction.Output {
143
144
  extension ZcashTransaction.Overview {
144
145
  enum Column {
145
146
  static let accountId = Expression<Int>("account_id")
147
+ static let id = Expression<Int>("id_tx")
146
148
  static let minedHeight = Expression<BlockHeight?>("mined_height")
147
149
  static let index = Expression<Int?>("tx_index")
148
150
  static let rawID = Expression<Blob>("txid")
@@ -155,13 +157,14 @@ extension ZcashTransaction.Overview {
155
157
  static let receivedNoteCount = Expression<Int>("received_note_count")
156
158
  static let memoCount = Expression<Int>("memo_count")
157
159
  static let blockTime = Expression<Int64?>("block_time")
158
- static let expiredUnmined = Expression<Bool?>("expired_unmined")
160
+ static let expiredUnmined = Expression<Bool>("expired_unmined")
159
161
  }
160
162
 
161
163
  init(row: Row) throws {
162
164
  do {
163
165
  self.accountId = try row.get(Column.accountId)
164
166
  self.expiryHeight = try row.get(Column.expiryHeight)
167
+ self.id = try row.get(Column.id)
165
168
  self.index = try row.get(Column.index)
166
169
  self.hasChange = try row.get(Column.hasChange)
167
170
  self.memoCount = try row.get(Column.memoCount)