react-native-zcash 0.6.13 → 0.7.0

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 (162) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/android/build.gradle +4 -4
  3. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2470000.json +8 -0
  4. package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2480000.json +8 -0
  5. package/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt +51 -9
  6. package/ios/RNZcash.m +8 -0
  7. package/ios/RNZcash.swift +66 -16
  8. package/ios/ZCashLightClientKit/Block/Actions/DownloadAction.swift +1 -1
  9. package/ios/ZCashLightClientKit/Block/Actions/EnhanceAction.swift +3 -1
  10. package/ios/ZCashLightClientKit/Block/Actions/FetchUTXOsAction.swift +1 -1
  11. package/ios/ZCashLightClientKit/Block/Actions/ProcessSuggestedScanRangesAction.swift +11 -2
  12. package/ios/ZCashLightClientKit/Block/Actions/RewindAction.swift +2 -2
  13. package/ios/ZCashLightClientKit/Block/Actions/ScanAction.swift +28 -11
  14. package/ios/ZCashLightClientKit/Block/Actions/UpdateChainTipAction.swift +4 -4
  15. package/ios/ZCashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift +36 -7
  16. package/ios/ZCashLightClientKit/Block/Actions/ValidateServerAction.swift +1 -1
  17. package/ios/ZCashLightClientKit/Block/CompactBlockProcessor.swift +93 -51
  18. package/ios/ZCashLightClientKit/Block/Download/BlockDownloader.swift +0 -26
  19. package/ios/ZCashLightClientKit/Block/Enhance/BlockEnhancer.swift +5 -6
  20. package/ios/ZCashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +1 -11
  21. package/ios/ZCashLightClientKit/Block/SaplingParameters/SaplingParametersHandler.swift +6 -4
  22. package/ios/ZCashLightClientKit/Block/Scan/BlockScanner.swift +10 -12
  23. package/ios/ZCashLightClientKit/Checkpoint/BundleCheckpointSource.swift +38 -0
  24. package/ios/ZCashLightClientKit/Checkpoint/BundleCheckpointURLProvider.swift +40 -0
  25. package/ios/ZCashLightClientKit/{Constants/Checkpoint+Constants.swift → Checkpoint/Checkpoint+helpers.swift} +1 -33
  26. package/ios/ZCashLightClientKit/Checkpoint/CheckpointSource.swift +34 -0
  27. package/ios/ZCashLightClientKit/Checkpoint/CheckpointSourceFactory.swift +14 -0
  28. package/ios/ZCashLightClientKit/ClosureSynchronizer.swift +61 -6
  29. package/ios/ZCashLightClientKit/CombineSynchronizer.swift +63 -4
  30. package/ios/ZCashLightClientKit/Constants/ZcashSDK.swift +4 -0
  31. package/ios/ZCashLightClientKit/DAO/TransactionDao.swift +21 -33
  32. package/ios/ZCashLightClientKit/DAO/UnspentTransactionOutputDao.swift +0 -182
  33. package/ios/ZCashLightClientKit/Entity/AccountEntity.swift +0 -173
  34. package/ios/ZCashLightClientKit/Entity/TransactionEntity.swift +5 -2
  35. package/ios/ZCashLightClientKit/Error/Sourcery/generateErrorCode.sh +1 -1
  36. package/ios/ZCashLightClientKit/Error/ZcashError.swift +53 -1
  37. package/ios/ZCashLightClientKit/Error/ZcashErrorCode.swift +19 -1
  38. package/ios/ZCashLightClientKit/Error/ZcashErrorCodeDefinition.swift +32 -0
  39. package/ios/ZCashLightClientKit/Initializer.swift +8 -17
  40. package/ios/ZCashLightClientKit/Metrics/SDKMetrics.swift +103 -217
  41. package/ios/ZCashLightClientKit/Model/Proposal.swift +45 -0
  42. package/ios/ZCashLightClientKit/Model/ScanSummary.swift +14 -0
  43. package/ios/ZCashLightClientKit/Model/WalletSummary.swift +58 -0
  44. package/ios/ZCashLightClientKit/Model/WalletTypes.swift +0 -16
  45. package/ios/ZCashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +5 -3
  46. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proposal.pb.swift +934 -0
  47. package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/proposal.proto +138 -0
  48. package/ios/ZCashLightClientKit/Modules/Service/LightWalletService.swift +2 -4
  49. package/ios/ZCashLightClientKit/Providers/LatestBlocksDataProvider.swift +9 -1
  50. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2272500.json +8 -0
  51. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2275000.json +8 -0
  52. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2277500.json +8 -0
  53. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2282500.json +8 -0
  54. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2285000.json +8 -0
  55. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2287500.json +8 -0
  56. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2292500.json +8 -0
  57. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2295000.json +8 -0
  58. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2297500.json +8 -0
  59. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2302500.json +8 -0
  60. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2305000.json +8 -0
  61. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2307500.json +8 -0
  62. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2312500.json +8 -0
  63. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2315000.json +8 -0
  64. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2317500.json +8 -0
  65. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2322500.json +8 -0
  66. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2325000.json +8 -0
  67. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2327500.json +8 -0
  68. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2332500.json +8 -0
  69. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2335000.json +8 -0
  70. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2337500.json +8 -0
  71. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2342500.json +8 -0
  72. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2345000.json +8 -0
  73. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2347500.json +8 -0
  74. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2352500.json +8 -0
  75. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2355000.json +8 -0
  76. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2357500.json +8 -0
  77. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2362500.json +8 -0
  78. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2365000.json +8 -0
  79. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2367500.json +8 -0
  80. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2372500.json +8 -0
  81. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2375000.json +8 -0
  82. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2377500.json +8 -0
  83. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2382500.json +8 -0
  84. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2385000.json +8 -0
  85. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2387500.json +8 -0
  86. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2392500.json +8 -0
  87. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2395000.json +8 -0
  88. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2397500.json +8 -0
  89. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2402500.json +8 -0
  90. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2405000.json +8 -0
  91. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2407500.json +8 -0
  92. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2412500.json +8 -0
  93. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2415000.json +8 -0
  94. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2417500.json +8 -0
  95. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2422500.json +8 -0
  96. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2425000.json +8 -0
  97. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2427500.json +8 -0
  98. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2432500.json +8 -0
  99. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2435000.json +8 -0
  100. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2437500.json +8 -0
  101. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2442500.json +8 -0
  102. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2445000.json +8 -0
  103. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2447500.json +8 -0
  104. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2452500.json +8 -0
  105. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2455000.json +8 -0
  106. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2457500.json +8 -0
  107. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2462500.json +8 -0
  108. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2465000.json +8 -0
  109. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2467500.json +8 -0
  110. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2470000.json +8 -0
  111. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2472500.json +8 -0
  112. package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2480000.json +8 -0
  113. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2560000.json +8 -0
  114. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2570000.json +8 -0
  115. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2580000.json +8 -0
  116. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2590000.json +8 -0
  117. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2600000.json +8 -0
  118. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2610000.json +8 -0
  119. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2620000.json +8 -0
  120. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2630000.json +8 -0
  121. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2640000.json +8 -0
  122. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2650000.json +8 -0
  123. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2660000.json +8 -0
  124. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2670000.json +8 -0
  125. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2680000.json +8 -0
  126. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2690000.json +8 -0
  127. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2700000.json +8 -0
  128. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2710000.json +8 -0
  129. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2720000.json +8 -0
  130. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2730000.json +8 -0
  131. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2740000.json +8 -0
  132. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2750000.json +8 -0
  133. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2760000.json +8 -0
  134. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2770000.json +8 -0
  135. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2780000.json +8 -0
  136. package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2790000.json +8 -0
  137. package/ios/ZCashLightClientKit/Rust/ZcashKeyDerivationBackend.swift +1 -5
  138. package/ios/ZCashLightClientKit/Rust/ZcashRustBackend.swift +327 -153
  139. package/ios/ZCashLightClientKit/Rust/ZcashRustBackendWelding.swift +78 -36
  140. package/ios/ZCashLightClientKit/Rust/zcashlc.h +1441 -0
  141. package/ios/ZCashLightClientKit/Synchronizer/ClosureSDKSynchronizer.swift +43 -14
  142. package/ios/ZCashLightClientKit/Synchronizer/CombineSDKSynchronizer.swift +52 -14
  143. package/ios/ZCashLightClientKit/Synchronizer/Dependencies.swift +10 -6
  144. package/ios/ZCashLightClientKit/Synchronizer/SDKSynchronizer.swift +220 -70
  145. package/ios/ZCashLightClientKit/Synchronizer.swift +105 -29
  146. package/ios/ZCashLightClientKit/Transaction/TransactionEncoder.swift +61 -32
  147. package/ios/ZCashLightClientKit/Transaction/WalletTransactionEncoder.swift +52 -61
  148. package/ios/ZCashLightClientKit/Utils/DBActor.swift +21 -0
  149. package/ios/ZCashLightClientKit/Utils/LoggingProxy.swift +5 -0
  150. package/ios/ZCashLightClientKit/Utils/OSLogger.swift +71 -14
  151. package/ios/libzcashlc.xcframework/ios-arm64/libzcashlc.a +0 -0
  152. package/ios/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.a +0 -0
  153. package/lib/rnzcash.rn.js +21 -6
  154. package/lib/rnzcash.rn.js.map +1 -1
  155. package/lib/src/react-native.d.ts +2 -1
  156. package/lib/src/types.d.ts +9 -1
  157. package/package.json +1 -1
  158. package/src/react-native.ts +23 -4
  159. package/src/types.ts +10 -1
  160. package/ios/ZCashLightClientKit/Model/ScanProgress.swift +0 -29
  161. package/ios/ZCashLightClientKit/Repository/UnspentTransactionOutputRepository.swift +0 -16
  162. /package/ios/ZCashLightClientKit/{Model → Checkpoint}/Checkpoint.swift +0 -0
@@ -9,11 +9,9 @@
9
9
  import Foundation
10
10
 
11
11
 
12
- let globalDBLock = NSLock()
13
-
14
- actor ZcashRustBackend: ZcashRustBackendWelding {
12
+ struct ZcashRustBackend: ZcashRustBackendWelding {
15
13
  let minimumConfirmations: UInt32 = 10
16
- let useZIP317Fees = false
14
+ let useZIP317Fees = true
17
15
 
18
16
  let dbData: (String, UInt)
19
17
  let fsBlockDbRoot: (String, UInt)
@@ -21,7 +19,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
21
19
  let outputParamsPath: (String, UInt)
22
20
  let keyDeriving: ZcashKeyDerivationBackendWelding
23
21
 
24
- nonisolated let networkType: NetworkType
22
+ let networkType: NetworkType
25
23
 
26
24
  static var tracingEnabled = false
27
25
  /// Creates instance of `ZcashRustBackend`.
@@ -49,6 +47,31 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
49
47
  }
50
48
  }
51
49
 
50
+ @DBActor
51
+ func listAccounts() async throws -> [Int32] {
52
+ let accountsPtr = zcashlc_list_accounts(
53
+ dbData.0,
54
+ dbData.1,
55
+ networkType.networkId
56
+ )
57
+
58
+ guard let accountsPtr else {
59
+ throw ZcashError.rustListAccounts(lastErrorMessage(fallback: "`listAccounts` failed with unknown error"))
60
+ }
61
+
62
+ defer { zcashlc_free_accounts(accountsPtr) }
63
+
64
+ var accounts: [Int32] = []
65
+
66
+ for i in (0 ..< Int(accountsPtr.pointee.len)) {
67
+ let account = accountsPtr.pointee.ptr.advanced(by: i).pointee
68
+ accounts.append(Int32(account.account_index))
69
+ }
70
+
71
+ return accounts
72
+ }
73
+
74
+ @DBActor
52
75
  func createAccount(seed: [UInt8], treeState: TreeState, recoverUntil: UInt32?) async throws -> UnifiedSpendingKey {
53
76
  var rUntil: Int64 = -1
54
77
 
@@ -58,7 +81,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
58
81
 
59
82
  let treeStateBytes = try treeState.serializedData(partial: false).bytes
60
83
 
61
- globalDBLock.lock()
62
84
  let ffiBinaryKeyPtr = zcashlc_create_account(
63
85
  dbData.0,
64
86
  dbData.1,
@@ -69,7 +91,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
69
91
  rUntil,
70
92
  networkType.networkId
71
93
  )
72
- globalDBLock.unlock()
73
94
 
74
95
  guard let ffiBinaryKeyPtr else {
75
96
  throw ZcashError.rustCreateAccount(lastErrorMessage(fallback: "`createAccount` failed with unknown error"))
@@ -80,49 +101,85 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
80
101
  return ffiBinaryKeyPtr.pointee.unsafeToUnifiedSpendingKey(network: networkType)
81
102
  }
82
103
 
83
- func createToAddress(
84
- usk: UnifiedSpendingKey,
104
+ @DBActor
105
+ func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool {
106
+ let result = zcashlc_is_seed_relevant_to_any_derived_account(
107
+ dbData.0,
108
+ dbData.1,
109
+ seed,
110
+ UInt(seed.count),
111
+ networkType.networkId
112
+ )
113
+
114
+ // -1 is the error sentinel.
115
+ guard result >= 0 else {
116
+ throw ZcashError.rustIsSeedRelevantToAnyDerivedAccount(lastErrorMessage(fallback: "`isSeedRelevantToAnyDerivedAccount` failed with unknown error"))
117
+ }
118
+
119
+ // 0 is false, 1 is true.
120
+ return result != 0
121
+ }
122
+
123
+ @DBActor
124
+ func proposeTransfer(
125
+ account: Int32,
85
126
  to address: String,
86
127
  value: Int64,
87
128
  memo: MemoBytes?
88
- ) async throws -> Data {
89
- var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
129
+ ) async throws -> FfiProposal {
130
+ let proposal = zcashlc_propose_transfer(
131
+ dbData.0,
132
+ dbData.1,
133
+ account,
134
+ [CChar](address.utf8CString),
135
+ value,
136
+ memo?.bytes,
137
+ networkType.networkId,
138
+ minimumConfirmations,
139
+ useZIP317Fees
140
+ )
90
141
 
91
- globalDBLock.lock()
92
- let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
93
- usk.bytes.withUnsafeBufferPointer { uskPtr in
94
- zcashlc_create_to_address(
95
- dbData.0,
96
- dbData.1,
97
- uskPtr.baseAddress,
98
- UInt(usk.bytes.count),
99
- [CChar](address.utf8CString),
100
- value,
101
- memo?.bytes,
102
- spendParamsPath.0,
103
- spendParamsPath.1,
104
- outputParamsPath.0,
105
- outputParamsPath.1,
106
- networkType.networkId,
107
- minimumConfirmations,
108
- useZIP317Fees,
109
- txIdBytePtr.baseAddress
110
- )
111
- }
142
+ guard let proposal else {
143
+ throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`proposeTransfer` failed with unknown error"))
112
144
  }
113
- globalDBLock.unlock()
114
145
 
115
- guard success else {
116
- throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
117
- }
146
+ defer { zcashlc_free_boxed_slice(proposal) }
118
147
 
119
- return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in
120
- Data(txIdBytePtr)
148
+ return try FfiProposal(contiguousBytes: Data(
149
+ bytes: proposal.pointee.ptr,
150
+ count: Int(proposal.pointee.len)
151
+ ))
152
+ }
153
+
154
+ @DBActor
155
+ func proposeTransferFromURI(
156
+ _ uri: String,
157
+ account: Int32
158
+ ) async throws -> FfiProposal {
159
+ let proposal = zcashlc_propose_transfer_from_uri(
160
+ dbData.0,
161
+ dbData.1,
162
+ account,
163
+ [CChar](uri.utf8CString),
164
+ networkType.networkId,
165
+ minimumConfirmations,
166
+ useZIP317Fees
167
+ )
168
+
169
+ guard let proposal else {
170
+ throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`proposeTransfer` failed with unknown error"))
121
171
  }
172
+
173
+ defer { zcashlc_free_boxed_slice(proposal) }
174
+
175
+ return try FfiProposal(contiguousBytes: Data(
176
+ bytes: proposal.pointee.ptr,
177
+ count: Int(proposal.pointee.len)
178
+ ))
122
179
  }
123
180
 
181
+ @DBActor
124
182
  func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws {
125
- globalDBLock.lock()
126
183
  let result = zcashlc_decrypt_and_store_transaction(
127
184
  dbData.0,
128
185
  dbData.1,
@@ -131,34 +188,20 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
131
188
  UInt32(minedHeight),
132
189
  networkType.networkId
133
190
  )
134
- globalDBLock.unlock()
135
191
 
136
192
  guard result != 0 else {
137
193
  throw ZcashError.rustDecryptAndStoreTransaction(lastErrorMessage(fallback: "`decryptAndStoreTransaction` failed with unknown error"))
138
194
  }
139
195
  }
140
196
 
141
- func getBalance(account: Int32) async throws -> Int64 {
142
- globalDBLock.lock()
143
- let balance = zcashlc_get_balance(dbData.0, dbData.1, account, networkType.networkId)
144
- globalDBLock.unlock()
145
-
146
- guard balance >= 0 else {
147
- throw ZcashError.rustGetBalance(Int(account), lastErrorMessage(fallback: "Error getting total balance from account \(account)"))
148
- }
149
-
150
- return balance
151
- }
152
-
197
+ @DBActor
153
198
  func getCurrentAddress(account: Int32) async throws -> UnifiedAddress {
154
- globalDBLock.lock()
155
199
  let addressCStr = zcashlc_get_current_address(
156
200
  dbData.0,
157
201
  dbData.1,
158
202
  account,
159
203
  networkType.networkId
160
204
  )
161
- globalDBLock.unlock()
162
205
 
163
206
  guard let addressCStr else {
164
207
  throw ZcashError.rustGetCurrentAddress(lastErrorMessage(fallback: "`getCurrentAddress` failed with unknown error"))
@@ -173,15 +216,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
173
216
  return UnifiedAddress(validatedEncoding: address, networkType: networkType)
174
217
  }
175
218
 
219
+ @DBActor
176
220
  func getNearestRewindHeight(height: Int32) async throws -> Int32 {
177
- globalDBLock.lock()
178
221
  let result = zcashlc_get_nearest_rewind_height(
179
222
  dbData.0,
180
223
  dbData.1,
181
224
  height,
182
225
  networkType.networkId
183
226
  )
184
- globalDBLock.unlock()
185
227
 
186
228
  guard result > 0 else {
187
229
  throw ZcashError.rustGetNearestRewindHeight(lastErrorMessage(fallback: "`getNearestRewindHeight` failed with unknown error"))
@@ -190,15 +232,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
190
232
  return result
191
233
  }
192
234
 
235
+ @DBActor
193
236
  func getNextAvailableAddress(account: Int32) async throws -> UnifiedAddress {
194
- globalDBLock.lock()
195
237
  let addressCStr = zcashlc_get_next_available_address(
196
238
  dbData.0,
197
239
  dbData.1,
198
240
  account,
199
241
  networkType.networkId
200
242
  )
201
- globalDBLock.unlock()
202
243
 
203
244
  guard let addressCStr else {
204
245
  throw ZcashError.rustGetNextAvailableAddress(lastErrorMessage(fallback: "`getNextAvailableAddress` failed with unknown error"))
@@ -213,7 +254,8 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
213
254
  return UnifiedAddress(validatedEncoding: address, networkType: networkType)
214
255
  }
215
256
 
216
- func getMemo(txId: Data, outputIndex: UInt16) async throws -> Memo? {
257
+ @DBActor
258
+ func getMemo(txId: Data, outputPool: UInt32, outputIndex: UInt16) async throws -> Memo? {
217
259
  guard txId.count == 32 else {
218
260
  throw ZcashError.rustGetMemoInvalidTxIdLength
219
261
  }
@@ -221,30 +263,27 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
221
263
  var contiguousMemoBytes = ContiguousArray<UInt8>(MemoBytes.empty().bytes)
222
264
  var success = false
223
265
 
224
- globalDBLock.lock()
225
266
  contiguousMemoBytes.withUnsafeMutableBufferPointer { memoBytePtr in
226
- success = zcashlc_get_memo(dbData.0, dbData.1, txId.bytes, outputIndex, memoBytePtr.baseAddress, networkType.networkId)
267
+ success = zcashlc_get_memo(dbData.0, dbData.1, txId.bytes, outputPool, outputIndex, memoBytePtr.baseAddress, networkType.networkId)
227
268
  }
228
- globalDBLock.unlock()
229
269
 
230
270
  guard success else { return nil }
231
271
 
232
272
  return (try? MemoBytes(contiguousBytes: contiguousMemoBytes)).flatMap { try? $0.intoMemo() }
233
273
  }
234
274
 
275
+ @DBActor
235
276
  func getTransparentBalance(account: Int32) async throws -> Int64 {
236
277
  guard account >= 0 else {
237
278
  throw ZcashError.rustGetTransparentBalanceNegativeAccount(Int(account))
238
279
  }
239
280
 
240
- globalDBLock.lock()
241
281
  let balance = zcashlc_get_total_transparent_balance_for_account(
242
282
  dbData.0,
243
283
  dbData.1,
244
284
  networkType.networkId,
245
285
  account
246
286
  )
247
- globalDBLock.unlock()
248
287
 
249
288
  guard balance >= 0 else {
250
289
  throw ZcashError.rustGetTransparentBalance(
@@ -256,33 +295,12 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
256
295
  return balance
257
296
  }
258
297
 
259
- func getVerifiedBalance(account: Int32) async throws -> Int64 {
260
- globalDBLock.lock()
261
- let balance = zcashlc_get_verified_balance(
262
- dbData.0,
263
- dbData.1,
264
- account,
265
- networkType.networkId,
266
- minimumConfirmations
267
- )
268
- globalDBLock.unlock()
269
-
270
- guard balance >= 0 else {
271
- throw ZcashError.rustGetVerifiedBalance(
272
- Int(account),
273
- lastErrorMessage(fallback: "Error getting verified balance from account \(account)")
274
- )
275
- }
276
-
277
- return balance
278
- }
279
-
298
+ @DBActor
280
299
  func getVerifiedTransparentBalance(account: Int32) async throws -> Int64 {
281
300
  guard account >= 0 else {
282
301
  throw ZcashError.rustGetVerifiedTransparentBalanceNegativeAccount(Int(account))
283
302
  }
284
303
 
285
- globalDBLock.lock()
286
304
  let balance = zcashlc_get_verified_transparent_balance_for_account(
287
305
  dbData.0,
288
306
  dbData.1,
@@ -290,7 +308,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
290
308
  account,
291
309
  minimumConfirmations
292
310
  )
293
- globalDBLock.unlock()
294
311
 
295
312
  guard balance >= 0 else {
296
313
  throw ZcashError.rustGetVerifiedTransparentBalance(
@@ -302,31 +319,32 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
302
319
  return balance
303
320
  }
304
321
 
322
+ @DBActor
305
323
  func initDataDb(seed: [UInt8]?) async throws -> DbInitResult {
306
- globalDBLock.lock()
307
324
  let initResult = zcashlc_init_data_database(dbData.0, dbData.1, seed, UInt(seed?.count ?? 0), networkType.networkId)
308
- globalDBLock.unlock()
309
325
 
310
326
  switch initResult {
311
327
  case 0: // ok
312
328
  return DbInitResult.success
313
329
  case 1:
314
330
  return DbInitResult.seedRequired
331
+ case 2:
332
+ return DbInitResult.seedNotRelevant
315
333
  default:
316
334
  throw ZcashError.rustInitDataDb(lastErrorMessage(fallback: "`initDataDb` failed with unknown error"))
317
335
  }
318
336
  }
319
337
 
338
+ @DBActor
320
339
  func initBlockMetadataDb() async throws {
321
- globalDBLock.lock()
322
340
  let result = zcashlc_init_block_metadata_db(fsBlockDbRoot.0, fsBlockDbRoot.1)
323
- globalDBLock.unlock()
324
341
 
325
342
  guard result else {
326
343
  throw ZcashError.rustInitBlockMetadataDb(lastErrorMessage(fallback: "`initBlockMetadataDb` failed with unknown error"))
327
344
  }
328
345
  }
329
346
 
347
+ @DBActor
330
348
  func writeBlocksMetadata(blocks: [ZcashCompactBlock]) async throws {
331
349
  var ffiBlockMetaVec: [FFIBlockMeta] = []
332
350
 
@@ -376,9 +394,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
376
394
 
377
395
  fsBlocks.initialize(to: meta)
378
396
 
379
- globalDBLock.lock()
380
397
  let res = zcashlc_write_block_metadata(fsBlockDbRoot.0, fsBlockDbRoot.1, fsBlocks)
381
- globalDBLock.unlock()
382
398
 
383
399
  guard res else {
384
400
  throw ZcashError.rustWriteBlocksMetadata(lastErrorMessage(fallback: "`writeBlocksMetadata` failed with unknown error"))
@@ -386,10 +402,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
386
402
  }
387
403
  }
388
404
 
405
+ @DBActor
389
406
  func latestCachedBlockHeight() async throws -> BlockHeight {
390
- globalDBLock.lock()
391
407
  let height = zcashlc_latest_cached_block_height(fsBlockDbRoot.0, fsBlockDbRoot.1)
392
- globalDBLock.unlock()
393
408
 
394
409
  if height >= 0 {
395
410
  return BlockHeight(height)
@@ -400,15 +415,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
400
415
  }
401
416
  }
402
417
 
418
+ @DBActor
403
419
  func listTransparentReceivers(account: Int32) async throws -> [TransparentAddress] {
404
- globalDBLock.lock()
405
420
  let encodedKeysPtr = zcashlc_list_transparent_receivers(
406
421
  dbData.0,
407
422
  dbData.1,
408
423
  account,
409
424
  networkType.networkId
410
425
  )
411
- globalDBLock.unlock()
412
426
 
413
427
  guard let encodedKeysPtr else {
414
428
  throw ZcashError.rustListTransparentReceivers(lastErrorMessage(fallback: "`listTransparentReceivers` failed with unknown error"))
@@ -433,6 +447,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
433
447
  return addresses
434
448
  }
435
449
 
450
+ @DBActor
436
451
  func putUnspentTransparentOutput(
437
452
  txid: [UInt8],
438
453
  index: Int,
@@ -440,7 +455,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
440
455
  value: Int64,
441
456
  height: BlockHeight
442
457
  ) async throws {
443
- globalDBLock.lock()
444
458
  let result = zcashlc_put_utxo(
445
459
  dbData.0,
446
460
  dbData.1,
@@ -453,33 +467,31 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
453
467
  Int32(height),
454
468
  networkType.networkId
455
469
  )
456
- globalDBLock.unlock()
457
470
 
458
471
  guard result else {
459
472
  throw ZcashError.rustPutUnspentTransparentOutput(lastErrorMessage(fallback: "`putUnspentTransparentOutput` failed with unknown error"))
460
473
  }
461
474
  }
462
475
 
476
+ @DBActor
463
477
  func rewindToHeight(height: Int32) async throws {
464
- globalDBLock.lock()
465
478
  let result = zcashlc_rewind_to_height(dbData.0, dbData.1, height, networkType.networkId)
466
- globalDBLock.unlock()
467
479
 
468
480
  guard result else {
469
481
  throw ZcashError.rustRewindToHeight(height, lastErrorMessage(fallback: "`rewindToHeight` failed with unknown error"))
470
482
  }
471
483
  }
472
484
 
485
+ @DBActor
473
486
  func rewindCacheToHeight(height: Int32) async throws {
474
- globalDBLock.lock()
475
487
  let result = zcashlc_rewind_fs_block_cache_to_height(fsBlockDbRoot.0, fsBlockDbRoot.1, height)
476
- globalDBLock.unlock()
477
488
 
478
489
  guard result else {
479
490
  throw ZcashError.rustRewindCacheToHeight(lastErrorMessage(fallback: "`rewindCacheToHeight` failed with unknown error"))
480
491
  }
481
492
  }
482
493
 
494
+ @DBActor
483
495
  func putSaplingSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws {
484
496
  var ffiSubtreeRootsVec: [FfiSubtreeRoot] = []
485
497
 
@@ -528,9 +540,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
528
540
 
529
541
  rootsPtr.initialize(to: roots)
530
542
 
531
- globalDBLock.lock()
532
543
  let res = zcashlc_put_sapling_subtree_roots(dbData.0, dbData.1, startIndex, rootsPtr, networkType.networkId)
533
- globalDBLock.unlock()
534
544
 
535
545
  guard res else {
536
546
  throw ZcashError.rustPutSaplingSubtreeRoots(lastErrorMessage(fallback: "`putSaplingSubtreeRoots` failed with unknown error"))
@@ -538,20 +548,75 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
538
548
  }
539
549
  }
540
550
 
551
+ @DBActor
552
+ func putOrchardSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws {
553
+ var ffiSubtreeRootsVec: [FfiSubtreeRoot] = []
554
+
555
+ for root in roots {
556
+ let hashPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: root.rootHash.count)
557
+
558
+ let contiguousHashBytes = ContiguousArray(root.rootHash.bytes)
559
+
560
+ let result: Void? = contiguousHashBytes.withContiguousStorageIfAvailable { hashBytesPtr in
561
+ // swiftlint:disable:next force_unwrapping
562
+ hashPtr.initialize(from: hashBytesPtr.baseAddress!, count: hashBytesPtr.count)
563
+ }
564
+
565
+ guard result != nil else {
566
+ defer {
567
+ hashPtr.deallocate()
568
+ ffiSubtreeRootsVec.deallocateElements()
569
+ }
570
+ throw ZcashError.rustPutOrchardSubtreeRootsAllocationProblem
571
+ }
572
+
573
+ ffiSubtreeRootsVec.append(
574
+ FfiSubtreeRoot(
575
+ root_hash_ptr: hashPtr,
576
+ root_hash_ptr_len: UInt(contiguousHashBytes.count),
577
+ completing_block_height: UInt32(root.completingBlockHeight)
578
+ )
579
+ )
580
+ }
581
+
582
+ var contiguousFfiRoots = ContiguousArray(ffiSubtreeRootsVec)
583
+
584
+ let len = UInt(contiguousFfiRoots.count)
585
+
586
+ let rootsPtr = UnsafeMutablePointer<FfiSubtreeRoots>.allocate(capacity: 1)
587
+
588
+ defer {
589
+ ffiSubtreeRootsVec.deallocateElements()
590
+ rootsPtr.deallocate()
591
+ }
592
+
593
+ try contiguousFfiRoots.withContiguousMutableStorageIfAvailable { ptr in
594
+ var roots = FfiSubtreeRoots()
595
+ roots.ptr = ptr.baseAddress
596
+ roots.len = len
597
+
598
+ rootsPtr.initialize(to: roots)
599
+
600
+ let res = zcashlc_put_orchard_subtree_roots(dbData.0, dbData.1, startIndex, rootsPtr, networkType.networkId)
601
+
602
+ guard res else {
603
+ throw ZcashError.rustPutOrchardSubtreeRoots(lastErrorMessage(fallback: "`putOrchardSubtreeRoots` failed with unknown error"))
604
+ }
605
+ }
606
+ }
607
+
608
+ @DBActor
541
609
  func updateChainTip(height: Int32) async throws {
542
- globalDBLock.lock()
543
610
  let result = zcashlc_update_chain_tip(dbData.0, dbData.1, height, networkType.networkId)
544
- globalDBLock.unlock()
545
611
 
546
612
  guard result else {
547
613
  throw ZcashError.rustUpdateChainTip(lastErrorMessage(fallback: "`updateChainTip` failed with unknown error"))
548
614
  }
549
615
  }
550
616
 
617
+ @DBActor
551
618
  func fullyScannedHeight() async throws -> BlockHeight? {
552
- globalDBLock.lock()
553
619
  let height = zcashlc_fully_scanned_height(dbData.0, dbData.1, networkType.networkId)
554
- globalDBLock.unlock()
555
620
 
556
621
  if height >= 0 {
557
622
  return BlockHeight(height)
@@ -562,10 +627,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
562
627
  }
563
628
  }
564
629
 
630
+ @DBActor
565
631
  func maxScannedHeight() async throws -> BlockHeight? {
566
- globalDBLock.lock()
567
632
  let height = zcashlc_max_scanned_height(dbData.0, dbData.1, networkType.networkId)
568
- globalDBLock.unlock()
569
633
 
570
634
  if height >= 0 {
571
635
  return BlockHeight(height)
@@ -576,27 +640,40 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
576
640
  }
577
641
  }
578
642
 
579
- func getScanProgress() async throws -> ScanProgress? {
580
- globalDBLock.lock()
581
- let result = zcashlc_get_scan_progress(dbData.0, dbData.1, networkType.networkId)
582
- globalDBLock.unlock()
643
+ @DBActor
644
+ func getWalletSummary() async throws -> WalletSummary? {
645
+ let summaryPtr = zcashlc_get_wallet_summary(dbData.0, dbData.1, networkType.networkId, minimumConfirmations)
583
646
 
584
- if result.denominator == 0 {
585
- switch result.numerator {
586
- case 0:
587
- return nil
588
- default:
589
- throw ZcashError.rustGetScanProgress(lastErrorMessage(fallback: "`getScanProgress` failed with unknown error"))
590
- }
591
- } else {
592
- return ScanProgress(numerator: result.numerator, denominator: result.denominator)
647
+ guard let summaryPtr else {
648
+ throw ZcashError.rustGetWalletSummary(lastErrorMessage(fallback: "`getWalletSummary` failed with unknown error"))
649
+ }
650
+
651
+ defer { zcashlc_free_wallet_summary(summaryPtr) }
652
+
653
+ if summaryPtr.pointee.fully_scanned_height < 0 {
654
+ return nil
593
655
  }
656
+
657
+ var accountBalances: [UInt32: AccountBalance] = [:]
658
+
659
+ for i in (0 ..< Int(summaryPtr.pointee.account_balances_len)) {
660
+ let accountBalance = summaryPtr.pointee.account_balances.advanced(by: i).pointee
661
+ accountBalances[accountBalance.account_id] = accountBalance.toAccountBalance()
662
+ }
663
+
664
+ return WalletSummary(
665
+ accountBalances: accountBalances,
666
+ chainTipHeight: BlockHeight(summaryPtr.pointee.chain_tip_height),
667
+ fullyScannedHeight: BlockHeight(summaryPtr.pointee.fully_scanned_height),
668
+ scanProgress: summaryPtr.pointee.scan_progress?.pointee.toScanProgress(),
669
+ nextSaplingSubtreeIndex: UInt32(summaryPtr.pointee.next_sapling_subtree_index),
670
+ nextOrchardSubtreeIndex: UInt32(summaryPtr.pointee.next_orchard_subtree_index)
671
+ )
594
672
  }
595
673
 
674
+ @DBActor
596
675
  func suggestScanRanges() async throws -> [ScanRange] {
597
- globalDBLock.lock()
598
676
  let scanRangesPtr = zcashlc_suggest_scan_ranges(dbData.0, dbData.1, networkType.networkId)
599
- globalDBLock.unlock()
600
677
 
601
678
  guard let scanRangesPtr else {
602
679
  throw ZcashError.rustSuggestScanRanges(lastErrorMessage(fallback: "`suggestScanRanges` failed with unknown error"))
@@ -623,53 +700,108 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
623
700
  return scanRanges
624
701
  }
625
702
 
626
- func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws {
627
- globalDBLock.lock()
628
- let result = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId)
629
- globalDBLock.unlock()
703
+ @DBActor
704
+ func scanBlocks(fromHeight: Int32, fromState: TreeState, limit: UInt32 = 0) async throws -> ScanSummary {
705
+ let fromStateBytes = try fromState.serializedData(partial: false).bytes
630
706
 
631
- guard result != 0 else {
707
+ let summaryPtr = zcashlc_scan_blocks(
708
+ fsBlockDbRoot.0,
709
+ fsBlockDbRoot.1,
710
+ dbData.0,
711
+ dbData.1,
712
+ fromHeight,
713
+ fromStateBytes,
714
+ UInt(fromStateBytes.count),
715
+ limit,
716
+ networkType.networkId
717
+ )
718
+
719
+ guard let summaryPtr else {
632
720
  throw ZcashError.rustScanBlocks(lastErrorMessage(fallback: "`scanBlocks` failed with unknown error"))
633
721
  }
722
+
723
+ defer { zcashlc_free_scan_summary(summaryPtr) }
724
+
725
+ return ScanSummary(
726
+ scannedRange: Range(uncheckedBounds: (
727
+ BlockHeight(summaryPtr.pointee.scanned_start),
728
+ BlockHeight(summaryPtr.pointee.scanned_end)
729
+ )),
730
+ spentSaplingNoteCount: summaryPtr.pointee.spent_sapling_note_count,
731
+ receivedSaplingNoteCount: summaryPtr.pointee.received_sapling_note_count
732
+ )
634
733
  }
635
734
 
636
- func shieldFunds(
637
- usk: UnifiedSpendingKey,
735
+ @DBActor
736
+ func proposeShielding(
737
+ account: Int32,
638
738
  memo: MemoBytes?,
639
- shieldingThreshold: Zatoshi
640
- ) async throws -> Data {
641
- var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
642
-
643
- globalDBLock.lock()
644
- let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
645
- usk.bytes.withUnsafeBufferPointer { uskBuffer in
646
- zcashlc_shield_funds(
739
+ shieldingThreshold: Zatoshi,
740
+ transparentReceiver: String?
741
+ ) async throws -> FfiProposal? {
742
+ let proposal = zcashlc_propose_shielding(
743
+ dbData.0,
744
+ dbData.1,
745
+ account,
746
+ memo?.bytes,
747
+ UInt64(shieldingThreshold.amount),
748
+ transparentReceiver.map { [CChar]($0.utf8CString) },
749
+ networkType.networkId,
750
+ minimumConfirmations,
751
+ useZIP317Fees
752
+ )
753
+
754
+ guard let proposal else {
755
+ throw ZcashError.rustShieldFunds(lastErrorMessage(fallback: "`proposeShielding` failed with unknown error"))
756
+ }
757
+
758
+ defer { zcashlc_free_boxed_slice(proposal) }
759
+
760
+ return try FfiProposal(contiguousBytes: Data(
761
+ bytes: proposal.pointee.ptr,
762
+ count: Int(proposal.pointee.len)
763
+ ))
764
+ }
765
+
766
+ @DBActor
767
+ func createProposedTransactions(
768
+ proposal: FfiProposal,
769
+ usk: UnifiedSpendingKey
770
+ ) async throws -> [Data] {
771
+ let proposalBytes = try proposal.serializedData(partial: false).bytes
772
+
773
+ let txIdsPtr = proposalBytes.withUnsafeBufferPointer { proposalPtr in
774
+ usk.bytes.withUnsafeBufferPointer { uskPtr in
775
+ zcashlc_create_proposed_transactions(
647
776
  dbData.0,
648
777
  dbData.1,
649
- uskBuffer.baseAddress,
778
+ proposalPtr.baseAddress,
779
+ UInt(proposalBytes.count),
780
+ uskPtr.baseAddress,
650
781
  UInt(usk.bytes.count),
651
- memo?.bytes,
652
- UInt64(shieldingThreshold.amount),
653
782
  spendParamsPath.0,
654
783
  spendParamsPath.1,
655
784
  outputParamsPath.0,
656
785
  outputParamsPath.1,
657
- networkType.networkId,
658
- minimumConfirmations,
659
- useZIP317Fees,
660
- txIdBytePtr.baseAddress
786
+ networkType.networkId
661
787
  )
662
788
  }
663
789
  }
664
- globalDBLock.unlock()
665
790
 
666
- guard success else {
667
- throw ZcashError.rustShieldFunds(lastErrorMessage(fallback: "`shieldFunds` failed with unknown error"))
791
+ guard let txIdsPtr else {
792
+ throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
668
793
  }
669
794
 
670
- return contiguousTxIdBytes.withUnsafeBufferPointer { txIdBytePtr in
671
- Data(txIdBytePtr)
795
+ defer { zcashlc_free_txids(txIdsPtr) }
796
+
797
+ var txIds: [Data] = []
798
+
799
+ for i in (0 ..< Int(txIdsPtr.pointee.len)) {
800
+ let txId = FfiTxId(tuple: txIdsPtr.pointee.ptr.advanced(by: i).pointee)
801
+ txIds.append(Data(txId.array))
672
802
  }
803
+
804
+ return txIds
673
805
  }
674
806
 
675
807
  nonisolated func consensusBranchIdFor(height: Int32) throws -> Int32 {
@@ -778,3 +910,45 @@ extension Array where Element == FfiSubtreeRoot {
778
910
  }
779
911
  }
780
912
  }
913
+
914
+ extension FfiBalance {
915
+ /// Converts an [`FfiBalance`] into a [`PoolBalance`].
916
+ func toPoolBalance() -> PoolBalance {
917
+ .init(
918
+ spendableValue: Zatoshi(self.spendable_value),
919
+ changePendingConfirmation: Zatoshi(self.change_pending_confirmation),
920
+ valuePendingSpendability: Zatoshi(self.value_pending_spendability)
921
+ )
922
+ }
923
+ }
924
+
925
+ extension FfiAccountBalance {
926
+ /// Converts an [`FfiAccountBalance`] into a [`AccountBalance`].
927
+ func toAccountBalance() -> AccountBalance {
928
+ .init(
929
+ saplingBalance: self.sapling_balance.toPoolBalance(),
930
+ orchardBalance: self.orchard_balance.toPoolBalance(),
931
+ unshielded: Zatoshi(self.unshielded)
932
+ )
933
+ }
934
+ }
935
+
936
+ extension FfiScanProgress {
937
+ /// Converts an [`FfiScanProgress`] into a [`ScanProgress`].
938
+ func toScanProgress() -> ScanProgress {
939
+ .init(
940
+ numerator: self.numerator,
941
+ denominator: self.denominator
942
+ )
943
+ }
944
+ }
945
+
946
+ // swiftlint:disable large_tuple line_length
947
+ struct FfiTxId {
948
+ var tuple: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
949
+ var array: [UInt8] {
950
+ withUnsafeBytes(of: self.tuple) { buf in
951
+ [UInt8](buf)
952
+ }
953
+ }
954
+ }