react-native-zcash 0.5.0 → 0.6.1

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 (157) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +7 -3
  3. package/android/build.gradle +5 -4
  4. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2240000.json +8 -0
  5. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2250000.json +8 -0
  6. package/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt +221 -169
  7. package/android/src/main/java/app/edge/rnzcash/RNZcashPackage.kt +1 -2
  8. package/ios/RNZcash.m +5 -8
  9. package/ios/RNZcash.swift +177 -137
  10. package/ios/ZCashLightClientKit/Block/Actions/Action.swift +98 -0
  11. package/ios/ZCashLightClientKit/Block/Actions/ClearAlreadyScannedBlocksAction.swift +35 -0
  12. package/ios/ZCashLightClientKit/Block/Actions/ClearCacheAction.swift +30 -0
  13. package/ios/ZCashLightClientKit/Block/Actions/DownloadAction.swift +67 -0
  14. package/ios/ZCashLightClientKit/Block/Actions/EnhanceAction.swift +97 -0
  15. package/ios/ZCashLightClientKit/Block/Actions/FetchUTXOsAction.swift +33 -0
  16. package/ios/ZCashLightClientKit/Block/Actions/MigrateLegacyCacheDBAction.swift +70 -0
  17. package/ios/ZCashLightClientKit/Block/Actions/ProcessSuggestedScanRangesAction.swift +59 -0
  18. package/ios/ZCashLightClientKit/Block/Actions/RewindAction.swift +48 -0
  19. package/ios/ZCashLightClientKit/Block/Actions/SaplingParamsAction.swift +33 -0
  20. package/ios/ZCashLightClientKit/Block/Actions/ScanAction.swift +95 -0
  21. package/ios/ZCashLightClientKit/Block/Actions/UpdateChainTipAction.swift +55 -0
  22. package/ios/ZCashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift +58 -0
  23. package/ios/ZCashLightClientKit/Block/Actions/ValidateServerAction.swift +60 -0
  24. package/ios/ZCashLightClientKit/Block/CompactBlockProcessor.swift +421 -937
  25. package/ios/ZCashLightClientKit/Block/Download/BlockDownloader.swift +31 -17
  26. package/ios/ZCashLightClientKit/Block/Download/BlockDownloaderService.swift +2 -2
  27. package/ios/ZCashLightClientKit/Block/Enhance/BlockEnhancer.swift +46 -15
  28. package/ios/ZCashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +4 -15
  29. package/ios/ZCashLightClientKit/Block/FilesystemStorage/FSCompactBlockRepository.swift +4 -4
  30. package/ios/ZCashLightClientKit/Block/Scan/BlockScanner.swift +10 -35
  31. package/ios/ZCashLightClientKit/Block/Utils/CompactBlockProgress.swift +24 -0
  32. package/ios/ZCashLightClientKit/Block/Utils/SyncControlData.swift +25 -0
  33. package/ios/ZCashLightClientKit/ClosureSynchronizer.swift +1 -2
  34. package/ios/ZCashLightClientKit/CombineSynchronizer.swift +2 -5
  35. package/ios/ZCashLightClientKit/Constants/ZcashSDK.swift +7 -25
  36. package/ios/ZCashLightClientKit/DAO/TransactionDao.swift +40 -42
  37. package/ios/ZCashLightClientKit/DAO/UnspentTransactionOutputDao.swift +13 -4
  38. package/ios/ZCashLightClientKit/Entity/AccountEntity.swift +9 -0
  39. package/ios/ZCashLightClientKit/Entity/TransactionEntity.swift +7 -10
  40. package/ios/ZCashLightClientKit/Error/Sourcery/generateErrorCode.sh +1 -1
  41. package/ios/ZCashLightClientKit/Error/ZcashError.swift +121 -12
  42. package/ios/ZCashLightClientKit/Error/ZcashErrorCode.swift +43 -5
  43. package/ios/ZCashLightClientKit/Error/ZcashErrorCodeDefinition.swift +72 -6
  44. package/ios/ZCashLightClientKit/Extensions/Bool+ToData.swift +15 -0
  45. package/ios/ZCashLightClientKit/Extensions/Data+ToOtherTypes.swift +18 -0
  46. package/ios/ZCashLightClientKit/Extensions/Int+ToData.swift +15 -0
  47. package/ios/ZCashLightClientKit/Initializer.swift +47 -26
  48. package/ios/ZCashLightClientKit/Metrics/SDKMetrics.swift +0 -12
  49. package/ios/ZCashLightClientKit/Model/Checkpoint.swift +12 -0
  50. package/ios/ZCashLightClientKit/Model/ScanProgress.swift +29 -0
  51. package/ios/ZCashLightClientKit/Model/ScanRange.swift +31 -0
  52. package/ios/ZCashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +15 -0
  53. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/compact_formats.pb.swift +150 -46
  54. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto +30 -16
  55. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/service.proto +32 -6
  56. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.grpc.swift +259 -22
  57. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.pb.swift +193 -7
  58. package/ios/ZCashLightClientKit/Modules/Service/LightWalletService.swift +8 -0
  59. package/ios/ZCashLightClientKit/Providers/LatestBlocksDataProvider.swift +18 -28
  60. package/ios/ZCashLightClientKit/Repository/CompactBlockRepository.swift +1 -1
  61. package/ios/ZCashLightClientKit/Repository/TransactionRepository.swift +2 -6
  62. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2092500.json +8 -0
  63. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2095000.json +8 -0
  64. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2097500.json +8 -0
  65. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2102500.json +8 -0
  66. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2105000.json +8 -0
  67. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2107500.json +8 -0
  68. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2112500.json +8 -0
  69. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2115000.json +8 -0
  70. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2117500.json +8 -0
  71. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2122500.json +8 -0
  72. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2125000.json +8 -0
  73. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2127500.json +8 -0
  74. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2132500.json +8 -0
  75. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2135000.json +8 -0
  76. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2137500.json +8 -0
  77. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2142500.json +8 -0
  78. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2145000.json +8 -0
  79. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2147500.json +8 -0
  80. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2152500.json +8 -0
  81. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2155000.json +8 -0
  82. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2157500.json +8 -0
  83. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2162500.json +8 -0
  84. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2165000.json +8 -0
  85. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2167500.json +8 -0
  86. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2172500.json +8 -0
  87. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2175000.json +8 -0
  88. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2177500.json +8 -0
  89. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2182500.json +8 -0
  90. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2185000.json +8 -0
  91. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2187500.json +8 -0
  92. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2192500.json +8 -0
  93. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2195000.json +8 -0
  94. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2197500.json +8 -0
  95. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2202500.json +8 -0
  96. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2205000.json +8 -0
  97. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2207500.json +8 -0
  98. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2212500.json +8 -0
  99. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2215000.json +8 -0
  100. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2217500.json +8 -0
  101. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2222500.json +8 -0
  102. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2225000.json +8 -0
  103. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2227500.json +8 -0
  104. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2230000.json +8 -0
  105. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2232500.json +8 -0
  106. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2235000.json +8 -0
  107. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2237500.json +8 -0
  108. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2240000.json +8 -0
  109. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2242500.json +8 -0
  110. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2245000.json +8 -0
  111. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2247500.json +8 -0
  112. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2250000.json +8 -0
  113. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2350000.json +8 -0
  114. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2360000.json +8 -0
  115. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2370000.json +8 -0
  116. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2380000.json +8 -0
  117. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2390000.json +8 -0
  118. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2400000.json +8 -0
  119. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2410000.json +8 -0
  120. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2420000.json +8 -0
  121. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2430000.json +8 -0
  122. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2440000.json +8 -0
  123. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2450000.json +8 -0
  124. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2460000.json +8 -0
  125. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2470000.json +8 -0
  126. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2480000.json +8 -0
  127. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2490000.json +8 -0
  128. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2500000.json +8 -0
  129. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2510000.json +8 -0
  130. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2520000.json +8 -0
  131. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2530000.json +8 -0
  132. package/ios/ZCashLightClientKit/Rust/ZcashRustBackend.swift +293 -158
  133. package/ios/ZCashLightClientKit/Rust/ZcashRustBackendWelding.swift +58 -64
  134. package/ios/ZCashLightClientKit/Rust/zcashlc.h +618 -512
  135. package/ios/ZCashLightClientKit/Synchronizer/ClosureSDKSynchronizer.swift +2 -8
  136. package/ios/ZCashLightClientKit/Synchronizer/CombineSDKSynchronizer.swift +3 -15
  137. package/ios/ZCashLightClientKit/Synchronizer/Dependencies.swift +11 -30
  138. package/ios/ZCashLightClientKit/Synchronizer/SDKSynchronizer.swift +41 -50
  139. package/ios/ZCashLightClientKit/Synchronizer.swift +51 -65
  140. package/ios/ZCashLightClientKit/Transaction/TransactionEncoder.swift +2 -2
  141. package/ios/ZCashLightClientKit/Transaction/WalletTransactionEncoder.swift +7 -7
  142. package/ios/ZCashLightClientKit/Utils/OSLogger.swift +3 -3
  143. package/ios/ZCashLightClientKit/Utils/ZcashFileManager.swift +16 -0
  144. package/ios/libzcashlc.xcframework/Info.plist +4 -0
  145. package/ios/libzcashlc.xcframework/ios-arm64/libzcashlc.a +0 -0
  146. package/ios/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.a +0 -0
  147. package/lib/rnzcash.rn.js +29 -29
  148. package/lib/rnzcash.rn.js.map +1 -1
  149. package/lib/src/react-native.d.ts +5 -5
  150. package/lib/src/types.d.ts +27 -15
  151. package/package.json +2 -1
  152. package/src/react-native.ts +40 -21
  153. package/src/types.ts +36 -24
  154. package/ios/ZCashLightClientKit/Block/Utils/InternalSyncProgress.swift +0 -200
  155. package/ios/ZCashLightClientKit/Block/Validate/BlockValidator.swift +0 -51
  156. package/ios/ZCashLightClientKit/DAO/BlockDao.swift +0 -112
  157. package/ios/ZCashLightClientKit/Entity/BlockProgress.swift +0 -24
package/ios/RNZcash.swift CHANGED
@@ -8,17 +8,21 @@ var SynchronizerMap = [String: WalletSynchronizer]()
8
8
  struct ConfirmedTx {
9
9
  var minedHeight: Int
10
10
  var toAddress: String?
11
+ var raw: String?
11
12
  var rawTransactionId: String
12
13
  var blockTimeInSeconds: Int
13
14
  var value: String
14
- var memos: Array<String>?
15
+ var fee: String?
16
+ var memos: [String]?
15
17
  var dictionary: [String: Any?] {
16
18
  return [
17
19
  "minedHeight": minedHeight,
18
20
  "toAddress": toAddress,
21
+ "raw": raw,
19
22
  "rawTransactionId": rawTransactionId,
20
23
  "blockTimeInSeconds": blockTimeInSeconds,
21
24
  "value": value,
25
+ "fee": fee,
22
26
  "memos": memos ?? [],
23
27
  ]
24
28
  }
@@ -28,12 +32,16 @@ struct ConfirmedTx {
28
32
  }
29
33
 
30
34
  struct TotalBalances {
31
- var availableZatoshi: String
32
- var totalZatoshi: String
35
+ var transparentAvailableZatoshi: Zatoshi
36
+ var transparentTotalZatoshi: Zatoshi
37
+ var saplingAvailableZatoshi: Zatoshi
38
+ var saplingTotalZatoshi: Zatoshi
33
39
  var dictionary: [String: Any] {
34
40
  return [
35
- "availableZatoshi": availableZatoshi,
36
- "totalZatoshi": totalZatoshi,
41
+ "transparentAvailableZatoshi": String(transparentAvailableZatoshi.amount),
42
+ "transparentTotalZatoshi": String(transparentTotalZatoshi.amount),
43
+ "saplingAvailableZatoshi": String(saplingAvailableZatoshi.amount),
44
+ "saplingTotalZatoshi": String(saplingTotalZatoshi.amount),
37
45
  ]
38
46
  }
39
47
  var nsDictionary: NSDictionary {
@@ -42,16 +50,10 @@ struct TotalBalances {
42
50
  }
43
51
 
44
52
  struct ProcessorState {
45
- var alias: String
46
- var lastDownloadedHeight: Int
47
- var lastScannedHeight: Int
48
53
  var scanProgress: Int
49
54
  var networkBlockHeight: Int
50
55
  var dictionary: [String: Any] {
51
56
  return [
52
- "alias": alias,
53
- "lastDownloadedHeight": lastDownloadedHeight,
54
- "lastScannedHeight": lastScannedHeight,
55
57
  "scanProgress": scanProgress,
56
58
  "networkBlockHeight": networkBlockHeight,
57
59
  ]
@@ -83,7 +85,8 @@ class RNZcash: RCTEventEmitter {
83
85
  // Synchronizer
84
86
  @objc func initialize(
85
87
  _ seed: String, _ birthdayHeight: Int, _ alias: String, _ networkName: String,
86
- _ defaultHost: String, _ defaultPort: Int, resolver resolve: @escaping RCTPromiseResolveBlock,
88
+ _ defaultHost: String, _ defaultPort: Int, _ newWallet: Bool,
89
+ resolver resolve: @escaping RCTPromiseResolveBlock,
87
90
  rejecter reject: @escaping RCTPromiseRejectBlock
88
91
  ) {
89
92
  Task {
@@ -92,6 +95,7 @@ class RNZcash: RCTEventEmitter {
92
95
  let initializer = Initializer(
93
96
  cacheDbURL: try! cacheDbURLHelper(alias, network),
94
97
  fsBlockDbRoot: try! fsBlockDbRootURLHelper(alias, network),
98
+ generalStorageURL: try! generalStorageURLHelper(alias, network),
95
99
  dataDbURL: try! dataDbURLHelper(alias, network),
96
100
  endpoint: endpoint,
97
101
  network: network,
@@ -105,12 +109,12 @@ class RNZcash: RCTEventEmitter {
105
109
  let wallet = try WalletSynchronizer(
106
110
  alias: alias, initializer: initializer, emitter: sendToJs)
107
111
  let seedBytes = try Mnemonic.deterministicSeedBytes(from: seed)
108
- let viewingKeys = try deriveUnifiedViewingKey(seed, network)
112
+ let initMode = newWallet ? WalletInitMode.newWallet : WalletInitMode.existingWallet
109
113
 
110
114
  _ = try await wallet.synchronizer.prepare(
111
115
  with: seedBytes,
112
- viewingKeys: [viewingKeys],
113
- walletBirthday: birthdayHeight
116
+ walletBirthday: birthdayHeight,
117
+ for: initMode
114
118
  )
115
119
  try await wallet.synchronizer.start()
116
120
  wallet.subscribe()
@@ -221,7 +225,7 @@ class RNZcash: RCTEventEmitter {
221
225
  memo: sdkMemo
222
226
  )
223
227
 
224
- let tx: NSMutableDictionary = ["txId": broadcastTx.rawID.hexEncodedString()]
228
+ let tx: NSMutableDictionary = ["txId": broadcastTx.rawID.toHexStringTxId()]
225
229
  if broadcastTx.raw != nil {
226
230
  tx["raw"] = broadcastTx.raw?.hexEncodedString()
227
231
  }
@@ -235,93 +239,39 @@ class RNZcash: RCTEventEmitter {
235
239
  }
236
240
  }
237
241
 
238
- @objc func getTransactions(
239
- _ alias: String, _ first: Int, _ last: Int, resolver resolve: @escaping RCTPromiseResolveBlock,
242
+ @objc func shieldFunds(
243
+ _ alias: String, _ seed: String, _ memo: String, _ threshold: String,
244
+ resolver resolve: @escaping RCTPromiseResolveBlock,
240
245
  rejecter reject: @escaping RCTPromiseRejectBlock
241
246
  ) {
242
247
  Task {
243
248
  if let wallet = SynchronizerMap[alias] {
244
249
  if !wallet.fullySynced {
245
- reject("GetTransactionsError", "Wallet is not synced", genericError)
250
+ reject("shieldFunds", "Wallet is not synced", genericError)
246
251
  return
247
252
  }
248
253
 
249
- var out: [NSDictionary] = []
250
- do {
251
- let txs = try await wallet.synchronizer.allTransactions()
252
- let filterTxs = txs.filter {
253
- $0.minedHeight != nil && $0.minedHeight! >= first && $0.minedHeight! <= last
254
- && $0.blockTime != nil
255
- }
256
- for tx in filterTxs {
257
- var confTx = ConfirmedTx(
258
- minedHeight: tx.minedHeight!,
259
- rawTransactionId: (tx.rawID.toHexStringTxId()),
260
- blockTimeInSeconds: Int(tx.blockTime!),
261
- value: String(describing: abs(tx.value.amount))
262
- )
263
- if tx.isSentTransaction {
264
- let recipients = await wallet.synchronizer.getRecipients(for: tx)
265
- if recipients.count > 0 {
266
- let addresses = recipients.compactMap {
267
- if case let .address(address) = $0 {
268
- return address
269
- } else {
270
- return nil
271
- }
272
- }
273
- if addresses.count > 0 {
274
- confTx.toAddress = addresses.first!.stringEncoded
275
- }
276
- }
277
- }
278
- if tx.memoCount > 0 {
279
- let memos = (try? await wallet.synchronizer.getMemos(for: tx)) ?? []
280
- let textMemos = memos.compactMap {
281
- return $0.toString()
282
- }
283
- confTx.memos = textMemos
284
- }
285
- out.append(confTx.nsDictionary)
286
- }
287
- resolve(out)
288
- } catch {
289
- reject("GetTransactionsError", "Failed to get transactions", genericError)
290
- }
291
- } else {
292
- reject("GetTransactionsError", "Wallet does not exist", genericError)
293
- }
294
- }
295
- }
296
-
297
- @objc func getBalance(
298
- _ alias: String, resolver resolve: @escaping RCTPromiseResolveBlock,
299
- rejecter reject: @escaping RCTPromiseRejectBlock
300
- ) {
301
- Task {
302
- if let wallet = SynchronizerMap[alias] {
303
254
  do {
304
- let totalShieldedBalance = try await wallet.synchronizer.getShieldedBalance()
305
- let availableShieldedBalance = try await wallet.synchronizer.getShieldedVerifiedBalance()
255
+ let spendingKey = try deriveUnifiedSpendingKey(seed, wallet.synchronizer.network)
256
+ let sdkMemo = try Memo(string: memo)
257
+ let shieldingThreshold = Int64(threshold) ?? 10000
306
258
 
307
- let transparentWalletBalance = try await wallet.synchronizer.getTransparentBalance(
308
- accountIndex: 0)
309
- let totalTransparentBalance = transparentWalletBalance.total
310
- let availableTransparentBalance = transparentWalletBalance.verified
259
+ let tx = try await wallet.synchronizer.shieldFunds(
260
+ spendingKey: spendingKey,
261
+ memo: sdkMemo,
262
+ shieldingThreshold: Zatoshi(shieldingThreshold)
263
+ )
311
264
 
312
- let totalAvailable = availableShieldedBalance + availableTransparentBalance
313
- let totalTotal = totalShieldedBalance + totalTransparentBalance
265
+ var confTx = await wallet.parseTx(tx: tx)
314
266
 
315
- let balance = TotalBalances(
316
- availableZatoshi: String(totalAvailable.amount), totalZatoshi: String(totalTotal.amount)
317
- )
318
- resolve(balance.nsDictionary)
319
- return
267
+ // Hack: Memos aren't ready to be queried right after broadcast
268
+ confTx.memos = [memo]
269
+ resolve(confTx.nsDictionary)
320
270
  } catch {
321
- reject("GetShieldedBalanceError", "Error", error)
271
+ reject("shieldFunds", "Failed to shield funds", genericError)
322
272
  }
323
273
  } else {
324
- reject("GetShieldedBalanceError", "Wallet does not exist", genericError)
274
+ reject("shieldFunds", "Wallet does not exist", genericError)
325
275
  }
326
276
  }
327
277
  }
@@ -343,6 +293,7 @@ class RNZcash: RCTEventEmitter {
343
293
  wallet.initializeProcessorState()
344
294
  wallet.cancellables.forEach { $0.cancel() }
345
295
  wallet.subscribe()
296
+ try await wallet.synchronizer.start()
346
297
  resolve(nil)
347
298
  case .failure:
348
299
  reject("RescanError", "Failed to rescan wallet", genericError)
@@ -406,11 +357,12 @@ class RNZcash: RCTEventEmitter {
406
357
  do {
407
358
  let unifiedAddress = try await wallet.synchronizer.getUnifiedAddress(accountIndex: 0)
408
359
  let saplingAddress = try await wallet.synchronizer.getSaplingAddress(accountIndex: 0)
409
- let transparentAddress = try await wallet.synchronizer.getTransparentAddress(accountIndex: 0)
360
+ let transparentAddress = try await wallet.synchronizer.getTransparentAddress(
361
+ accountIndex: 0)
410
362
  let addresses: NSDictionary = [
411
363
  "unifiedAddress": unifiedAddress.stringEncoded,
412
364
  "saplingAddress": saplingAddress.stringEncoded,
413
- "transparentAddress": transparentAddress.stringEncoded
365
+ "transparentAddress": transparentAddress.stringEncoded,
414
366
  ]
415
367
  resolve(addresses)
416
368
  return
@@ -444,7 +396,7 @@ class RNZcash: RCTEventEmitter {
444
396
  }
445
397
 
446
398
  override func supportedEvents() -> [String] {
447
- return ["StatusEvent", "UpdateEvent"]
399
+ return ["BalanceEvent", "StatusEvent", "TransactionEvent", "UpdateEvent"]
448
400
  }
449
401
  }
450
402
 
@@ -457,6 +409,7 @@ class WalletSynchronizer: NSObject {
457
409
  var restart: Bool
458
410
  var processorState: ProcessorState
459
411
  var cancellables: [AnyCancellable] = []
412
+ var balances: TotalBalances
460
413
 
461
414
  init(alias: String, initializer: Initializer, emitter: @escaping (String, Any) -> Void) throws {
462
415
  self.alias = alias
@@ -466,12 +419,14 @@ class WalletSynchronizer: NSObject {
466
419
  self.fullySynced = false
467
420
  self.restart = false
468
421
  self.processorState = ProcessorState(
469
- alias: self.alias,
470
- lastDownloadedHeight: 0,
471
- lastScannedHeight: 0,
472
422
  scanProgress: 0,
473
423
  networkBlockHeight: 0
474
424
  )
425
+ self.balances = TotalBalances(
426
+ transparentAvailableZatoshi: Zatoshi(0),
427
+ transparentTotalZatoshi: Zatoshi(0),
428
+ saplingAvailableZatoshi: Zatoshi(0),
429
+ saplingTotalZatoshi: Zatoshi(0))
475
430
  }
476
431
 
477
432
  public func subscribe() {
@@ -479,6 +434,21 @@ class WalletSynchronizer: NSObject {
479
434
  .throttle(for: .seconds(0.3), scheduler: DispatchQueue.main, latest: true)
480
435
  .sink(receiveValue: { [weak self] state in self?.updateSyncStatus(event: state) })
481
436
  .store(in: &cancellables)
437
+ self.synchronizer.stateStream
438
+ .sink(receiveValue: { [weak self] state in self?.updateProcessorState(event: state) })
439
+ .store(in: &cancellables)
440
+ self.synchronizer.eventStream
441
+ .sink { SynchronizerEvent in
442
+ switch SynchronizerEvent {
443
+ case .minedTransaction(let transaction):
444
+ self.emitTxs(transactions: [transaction])
445
+ case .foundTransactions(let transactions, _):
446
+ self.emitTxs(transactions: transactions)
447
+ default:
448
+ return
449
+ }
450
+ }
451
+ .store(in: &cancellables)
482
452
  }
483
453
 
484
454
  func updateSyncStatus(event: SynchronizerState) {
@@ -502,68 +472,130 @@ class WalletSynchronizer: NSObject {
502
472
  let data: NSDictionary = ["alias": self.alias, "name": self.status]
503
473
  emit("StatusEvent", data)
504
474
  }
505
-
506
- updateProcessorState(event: event)
507
475
  }
508
476
 
509
477
  func updateProcessorState(event: SynchronizerState) {
510
- let prevLastDownloadedHeight = self.processorState.lastDownloadedHeight
511
- let prevScanProgress = self.processorState.scanProgress
512
- let prevLastScannedHeight = self.synchronizer.latestState.latestScannedHeight
513
- let prevNetworkBlockHeight = self.processorState.lastScannedHeight
514
-
515
- if event.internalSyncStatus != .synced {
516
- switch event.internalSyncStatus {
517
- case .syncing(let status):
518
- // The SDK emits all zero values just before emitting a SYNCED status so we need to ignore these
519
- if status.targetHeight == 0 {
520
- return
521
- }
522
- self.processorState.lastDownloadedHeight = status.progressHeight
523
- self.processorState.scanProgress = Int(floor(status.progress * 100))
524
- self.processorState.lastScannedHeight = status.progressHeight
525
- self.processorState.networkBlockHeight = status.targetHeight
526
- default:
527
- return
528
- }
529
- } else {
530
- self.processorState.lastDownloadedHeight = self.synchronizer.latestState.latestScannedHeight
531
- self.processorState.scanProgress = 100
532
- self.processorState.lastScannedHeight = self.synchronizer.latestState.latestScannedHeight
533
- self.processorState.networkBlockHeight = event.latestBlockHeight
478
+ var scanProgress = 0
479
+
480
+ switch event.internalSyncStatus {
481
+ case .syncing(let progress):
482
+ scanProgress = Int(floor(progress * 100))
483
+ case .synced:
484
+ scanProgress = 100
485
+ case .unprepared, .disconnected, .stopped:
486
+ scanProgress = 0
487
+ default:
488
+ return
534
489
  }
535
490
 
536
- if self.processorState.lastDownloadedHeight != prevLastDownloadedHeight
537
- || self.processorState.scanProgress != prevScanProgress
538
- || self.processorState.lastScannedHeight != prevLastScannedHeight
539
- || self.processorState.networkBlockHeight != prevNetworkBlockHeight
491
+ if scanProgress == self.processorState.scanProgress
492
+ && event.latestBlockHeight == self.processorState.networkBlockHeight
540
493
  {
541
- emit("UpdateEvent", self.processorState.nsDictionary)
494
+ return
542
495
  }
496
+
497
+ self.processorState = ProcessorState(
498
+ scanProgress: scanProgress, networkBlockHeight: event.latestBlockHeight)
499
+ let data: NSDictionary = [
500
+ "alias": self.alias, "scanProgress": self.processorState.scanProgress,
501
+ "networkBlockHeight": self.processorState.networkBlockHeight,
502
+ ]
503
+ emit("UpdateEvent", data)
504
+ updateBalanceState(event: event)
543
505
  }
544
506
 
545
507
  func initializeProcessorState() {
546
508
  self.processorState = ProcessorState(
547
- alias: self.alias,
548
- lastDownloadedHeight: 0,
549
- lastScannedHeight: 0,
550
509
  scanProgress: 0,
551
510
  networkBlockHeight: 0
552
511
  )
512
+ self.balances = TotalBalances(
513
+ transparentAvailableZatoshi: Zatoshi(0),
514
+ transparentTotalZatoshi: Zatoshi(0),
515
+ saplingAvailableZatoshi: Zatoshi(0),
516
+ saplingTotalZatoshi: Zatoshi(0))
553
517
  }
554
- }
555
518
 
556
- func z_hexEncodedString(data: Data) -> String {
557
- let hexDigits = Array("0123456789abcdef".utf16)
558
- var chars: [unichar] = []
519
+ func updateBalanceState(event: SynchronizerState) {
520
+ let transparentBalance = event.transparentBalance
521
+ let shieldedBalance = event.shieldedBalance
522
+
523
+ let transparentAvailableZatoshi = transparentBalance.verified
524
+ let transparentTotalZatoshi = transparentBalance.total
559
525
 
560
- chars.reserveCapacity(2 * data.count)
561
- for byte in data {
562
- chars.append(hexDigits[Int(byte / 16)])
563
- chars.append(hexDigits[Int(byte % 16)])
526
+ let saplingAvailableZatoshi = shieldedBalance.verified
527
+ let saplingTotalZatoshi = shieldedBalance.total
528
+
529
+ if transparentAvailableZatoshi == self.balances.transparentAvailableZatoshi
530
+ && transparentTotalZatoshi == self.balances.transparentTotalZatoshi
531
+ && saplingAvailableZatoshi == self.balances.saplingAvailableZatoshi
532
+ && saplingTotalZatoshi == self.balances.saplingTotalZatoshi
533
+ {
534
+ return
535
+ }
536
+
537
+ self.balances = TotalBalances(
538
+ transparentAvailableZatoshi: transparentAvailableZatoshi,
539
+ transparentTotalZatoshi: transparentTotalZatoshi,
540
+ saplingAvailableZatoshi: saplingAvailableZatoshi,
541
+ saplingTotalZatoshi: saplingTotalZatoshi
542
+ )
543
+ let data = NSMutableDictionary(dictionary: self.balances.nsDictionary)
544
+ data["alias"] = self.alias
545
+ emit("BalanceEvent", data)
546
+ }
547
+
548
+ func parseTx(tx: ZcashTransaction.Overview) async -> ConfirmedTx {
549
+ var confTx = ConfirmedTx(
550
+ minedHeight: tx.minedHeight ?? 0,
551
+ rawTransactionId: (tx.rawID.toHexStringTxId()),
552
+ blockTimeInSeconds: Int(tx.blockTime ?? 0),
553
+ value: String(describing: abs(tx.value.amount))
554
+ )
555
+ if tx.raw != nil {
556
+ confTx.raw = tx.raw!.hexEncodedString()
557
+ }
558
+ if tx.fee != nil {
559
+ confTx.fee = String(describing: abs(tx.value.amount))
560
+ }
561
+ if tx.isSentTransaction {
562
+ let recipients = await self.synchronizer.getRecipients(for: tx)
563
+ if recipients.count > 0 {
564
+ let addresses = recipients.compactMap {
565
+ if case let .address(address) = $0 {
566
+ return address
567
+ } else {
568
+ return nil
569
+ }
570
+ }
571
+ if addresses.count > 0 {
572
+ confTx.toAddress = addresses.first!.stringEncoded
573
+ }
574
+ }
575
+ }
576
+ if tx.memoCount > 0 {
577
+ let memos = (try? await self.synchronizer.getMemos(for: tx)) ?? []
578
+ let textMemos = memos.compactMap {
579
+ return $0.toString()
580
+ }
581
+ confTx.memos = textMemos
582
+ }
583
+ return confTx
564
584
  }
565
585
 
566
- return String(utf16CodeUnits: chars, count: chars.count)
586
+ func emitTxs(transactions: [ZcashTransaction.Overview]) {
587
+ Task {
588
+ var out: [NSDictionary] = []
589
+ for tx in transactions {
590
+ if tx.isExpiredUmined ?? false { continue }
591
+ let confTx = await parseTx(tx: tx)
592
+ out.append(confTx.nsDictionary)
593
+ }
594
+
595
+ let data: NSDictionary = ["alias": self.alias, "transactions": NSArray(array: out)]
596
+ emit("TransactionEvent", data)
597
+ }
598
+ }
567
599
  }
568
600
 
569
601
  // Local file helper funcs
@@ -603,3 +635,11 @@ func fsBlockDbRootURLHelper(_ alias: String, _ network: ZcashNetwork) throws ->
603
635
  isDirectory: true
604
636
  )
605
637
  }
638
+
639
+ func generalStorageURLHelper(_ alias: String, _ network: ZcashNetwork) throws -> URL {
640
+ try documentsDirectoryHelper()
641
+ .appendingPathComponent(
642
+ network.constants.defaultDbNamePrefix + alias + "general_storage",
643
+ isDirectory: true
644
+ )
645
+ }
@@ -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
+ }