react-native-zcash 0.2.1 → 0.2.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # React Native Zcash
2
2
 
3
+ ## 0.2.2 (2022-06-10)
4
+
5
+ - Upgrade SDKs to NU5 compatible versions
6
+ - Android: Upgrade zcash-android-sdk to v1.5.0-beta01
7
+ - iOS: Upgrade ZcashLightClientKit to v0.14.0-beta
8
+ - iOS: Fix memory leak after stopping synchronizer
9
+ - ANdroid: White space and import cleanups
10
+
3
11
  ## 0.2.1 (2022-03-16)
4
12
 
5
13
  - Update the ZcashLightClientKit dependency
@@ -1,7 +1,7 @@
1
1
  buildscript {
2
2
  ext.versions = [
3
3
  'kotlin': '1.5.0',
4
- 'zcash': '1.3.0-beta18',
4
+ 'zcash': '1.5.0-beta01',
5
5
  'room': '2.3.0'
6
6
  ]
7
7
  repositories {
@@ -33,7 +33,7 @@ def safeExtGet(prop, fallback) {
33
33
 
34
34
  def DEFAULT_COMPILE_SDK_VERSION = 28
35
35
  def DEFAULT_BUILD_TOOLS_VERSION = "28.0.2"
36
- def DEFAULT_MIN_SDK_VERSION = 16
36
+ def DEFAULT_MIN_SDK_VERSION = 19
37
37
  def DEFAULT_TARGET_SDK_VERSION = 27
38
38
 
39
39
  android {
@@ -80,4 +80,10 @@ dependencies {
80
80
  implementation "androidx.paging:paging-runtime-ktx:2.1.2"
81
81
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
82
82
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
83
+
84
+ implementation ("androidx.appcompat:appcompat:1.3.1") {
85
+ version {
86
+ strictly '1.3.1'
87
+ }
88
+ }
83
89
  }
@@ -1,32 +1,29 @@
1
1
  package app.edge.rnzcash;
2
2
 
3
- import androidx.paging.PagedList
4
3
  import cash.z.ecc.android.sdk.Initializer
5
4
  import cash.z.ecc.android.sdk.SdkSynchronizer
6
5
  import cash.z.ecc.android.sdk.Synchronizer
7
- import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
8
- import cash.z.ecc.android.sdk.block.CompactBlockProcessor
9
6
  import cash.z.ecc.android.sdk.db.entity.*
10
7
  import cash.z.ecc.android.sdk.ext.*
11
- import cash.z.ecc.android.sdk.transaction.*
8
+ import cash.z.ecc.android.sdk.internal.transaction.PagedTransactionRepository
9
+ import cash.z.ecc.android.sdk.internal.*
12
10
  import cash.z.ecc.android.sdk.type.*
13
11
  import cash.z.ecc.android.sdk.tool.DerivationTool
14
12
  import com.facebook.react.bridge.*
15
13
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
16
14
  import kotlinx.coroutines.CoroutineScope
17
- import kotlinx.coroutines.cancel
18
15
  import kotlinx.coroutines.flow.distinctUntilChanged
19
- import kotlinx.coroutines.flow.filter
20
16
  import kotlinx.coroutines.launch
17
+ import kotlinx.coroutines.runBlocking
21
18
  import java.nio.charset.StandardCharsets
22
19
  import kotlin.coroutines.EmptyCoroutineContext
23
20
 
24
21
  class WalletSynchronizer constructor(val initializer: Initializer) {
25
-
26
- val synchronizer: SdkSynchronizer = Synchronizer(
22
+
23
+ val synchronizer: SdkSynchronizer = Synchronizer.newBlocking(
27
24
  initializer
28
25
  ) as SdkSynchronizer
29
- val repository = PagedTransactionRepository(initializer.context, 10, initializer.rustBackend, initializer.birthday, initializer.viewingKeys)
26
+ val repository = runBlocking { PagedTransactionRepository.new(initializer.context, 10, initializer.rustBackend, initializer.birthday, initializer.viewingKeys) }
30
27
  var isStarted = false
31
28
  }
32
29
 
@@ -50,28 +47,32 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
50
47
  @ReactMethod
51
48
  fun initialize(extfvk: String, extpub: String, birthdayHeight: Int, alias: String, networkName: String = "mainnet", defaultHost: String = "mainnet.lightwalletd.com", defaultPort: Int = 9067, promise: Promise) =
52
49
  promise.wrap {
53
- Twig.plant(TroubleshootingTwig())
54
- var vk = UnifiedViewingKey(extfvk, extpub)
55
- if (synchronizerMap[alias] == null) {
56
- val initializer = Initializer(reactApplicationContext) { config ->
57
- config.importedWalletBirthday(birthdayHeight)
58
- config.setViewingKeys(vk)
59
- config.setNetwork(networks[networkName] ?: ZcashNetwork.Mainnet, defaultHost, defaultPort)
60
- config.alias = alias
61
- }
62
- synchronizerMap[alias] = WalletSynchronizer(initializer)
63
- val wallet = getWallet(alias)
50
+ Twig.plant(TroubleshootingTwig())
51
+ var vk = UnifiedViewingKey(extfvk, extpub)
52
+ if (synchronizerMap[alias] == null) {
53
+ runBlocking {
54
+ Initializer.new(reactApplicationContext) {
55
+ it.importedWalletBirthday(birthdayHeight)
56
+ it.setViewingKeys(vk)
57
+ it.setNetwork(networks[networkName]
58
+ ?: ZcashNetwork.Mainnet, defaultHost, defaultPort)
59
+ it.alias = alias
60
+ }
61
+ }.let { initializer ->
62
+ synchronizerMap[alias] = WalletSynchronizer(initializer)
64
63
  }
65
- val wallet = getWallet(alias)
66
- wallet.synchronizer.hashCode().toString()
67
-
64
+ }
65
+ val wallet = getWallet(alias)
66
+ wallet.synchronizer.hashCode().toString()
68
67
  }
69
68
 
70
69
  @ReactMethod
71
70
  fun start(alias: String, promise: Promise) = promise.wrap {
72
71
  val wallet = getWallet(alias)
73
72
  if (!wallet.isStarted) {
74
- wallet.synchronizer.prepare()
73
+ runBlocking {
74
+ wallet.synchronizer.prepare()
75
+ }
75
76
  wallet.synchronizer.start(moduleScope)
76
77
  val scope = wallet.synchronizer.coroutineScope
77
78
  wallet.synchronizer.processorInfo.collectWith(scope, { update ->
@@ -83,7 +84,7 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
83
84
  args.putInt("networkBlockHeight", update.networkBlockHeight)
84
85
  }
85
86
  })
86
- wallet.synchronizer.status.collectWith(scope, { status ->
87
+ wallet.synchronizer.status.collectWith(scope, { status ->
87
88
  sendEvent("StatusEvent") { args ->
88
89
  args.putString("alias", alias)
89
90
  args.putString("name", status.toString())
@@ -104,7 +105,6 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
104
105
  args.putInt("transactionCount", txList.count())
105
106
  }
106
107
  })
107
- wallet.repository.prepare()
108
108
  wallet.isStarted = true
109
109
  }
110
110
  "success"
@@ -152,7 +152,7 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
152
152
 
153
153
  @ReactMethod
154
154
  fun deriveViewingKey(seedBytesHex: String, network: String = "mainnet", promise: Promise) {
155
- var keys = DerivationTool.deriveUnifiedViewingKeys(seedBytesHex.fromHex(), networks.getOrDefault(network, ZcashNetwork.Mainnet))[0]
155
+ var keys = runBlocking { DerivationTool.deriveUnifiedViewingKeys(seedBytesHex.fromHex(), networks.getOrDefault(network, ZcashNetwork.Mainnet))[0] }
156
156
  val map = Arguments.createMap()
157
157
  map.putString("extfvk", keys.extfvk)
158
158
  map.putString("extpub", keys.extpub)
@@ -161,7 +161,7 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
161
161
 
162
162
  @ReactMethod
163
163
  fun deriveSpendingKey(seedBytesHex: String, network: String = "mainnet", promise: Promise) = promise.wrap {
164
- DerivationTool.deriveSpendingKeys(seedBytesHex.fromHex(), networks.getOrDefault(network, ZcashNetwork.Mainnet))[0]
164
+ runBlocking { DerivationTool.deriveSpendingKeys(seedBytesHex.fromHex(), networks.getOrDefault(network, ZcashNetwork.Mainnet))[0] }
165
165
  }
166
166
 
167
167
  //
@@ -226,7 +226,7 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
226
226
  promise.reject("Err", t)
227
227
  }
228
228
  }
229
-
229
+
230
230
  }
231
231
 
232
232
  //
@@ -235,7 +235,7 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
235
235
 
236
236
  @ReactMethod
237
237
  fun deriveShieldedAddress(viewingKey: String, network: String = "mainnet", promise: Promise) = promise.wrap {
238
- DerivationTool.deriveShieldedAddress(viewingKey, networks.getOrDefault(network, ZcashNetwork.Mainnet))
238
+ runBlocking { DerivationTool.deriveShieldedAddress(viewingKey, networks.getOrDefault(network, ZcashNetwork.Mainnet)) }
239
239
  }
240
240
 
241
241
  @ReactMethod
@@ -256,7 +256,7 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
256
256
  }
257
257
 
258
258
  @ReactMethod
259
- fun isValidTransparentAddress(address: String, network: String, promise: Promise) {
259
+ fun isValidTransparentAddress(address: String, network: String, promise: Promise) {
260
260
  moduleScope.launch {
261
261
  promise.wrap {
262
262
  var isValid = false
@@ -278,13 +278,13 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
278
278
 
279
279
  /**
280
280
  * Retrieve wallet object from synchronizer map
281
- */
281
+ */
282
282
  private fun getWallet(alias: String): WalletSynchronizer {
283
283
  val wallet = synchronizerMap.get(alias)
284
284
  if (wallet == null) throw Exception("Wallet not found")
285
285
  return wallet
286
286
  }
287
-
287
+
288
288
 
289
289
  /**
290
290
  * Wrap the given block of logic in a promise, rejecting for any error.
@@ -314,4 +314,12 @@ class RNZcashModule(private val reactContext: ReactApplicationContext) :
314
314
  "Unable to parse memo."
315
315
  }
316
316
  }
317
+
318
+ inline fun ByteArray.toHexReversed(): String {
319
+ val sb = StringBuilder(size * 2)
320
+ var i = size - 1
321
+ while (i >= 0)
322
+ sb.append(String.format("%02x", this[i--]))
323
+ return sb.toString()
324
+ }
317
325
  }
package/ios/RNZcash.swift CHANGED
@@ -135,6 +135,7 @@ class RNZcash : RCTEventEmitter {
135
135
  @objc func stop(_ alias: String, resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
136
136
  if let wallet = SynchronizerMap[alias] {
137
137
  wallet.synchronizer.stop()
138
+ SynchronizerMap[alias] = nil
138
139
  resolve(nil)
139
140
  } else {
140
141
  reject("StopError", "Wallet does not exist", genericError)
@@ -327,7 +328,7 @@ class WalletSynchronizer : NSObject {
327
328
 
328
329
  public func subscribe() {
329
330
  // Processor status
330
- NotificationCenter.default.addObserver(self, selector: #selector(updateProcessorState(notification:)), name: .blockProcessorUpdated, object: self.synchronizer.blockProcessor)
331
+ NotificationCenter.default.addObserver(self, selector: #selector(updateProcessorState(notification:)), name: nil, object: self.synchronizer.blockProcessor)
331
332
  // Synchronizer Status
332
333
  NotificationCenter.default.addObserver(self, selector: #selector(updateSyncStatus(notification:)), name: .synchronizerDisconnected, object: self.synchronizer)
333
334
  NotificationCenter.default.addObserver(self, selector: #selector(updateSyncStatus(notification:)), name: .synchronizerStopped, object: self.synchronizer)
@@ -371,17 +372,40 @@ class WalletSynchronizer : NSObject {
371
372
  }
372
373
 
373
374
  @objc public func updateProcessorState(notification: NSNotification) {
374
- let status: CompactBlockProgress = notification.userInfo![CompactBlockProcessorNotificationKey.progress] as! CompactBlockProgress
375
- if case .download = status {
376
- self.processorState.lastDownloadedHeight = status.progressHeight!
377
- } else if case .scan = status {
378
- self.processorState.lastScannedHeight = status.progressHeight!
379
- self.processorState.scanProgress = Int(status.progress * 100)
375
+ let prevLastDownloadedHeight = self.processorState.lastDownloadedHeight
376
+ let prevScanProgress = self.processorState.scanProgress
377
+ let prevLastScannedHeight = self.synchronizer.latestScannedHeight
378
+ let prevNetworkBlockHeight = self.processorState.lastScannedHeight
379
+
380
+ if !self.fullySynced {
381
+ switch self.synchronizer.status {
382
+ case .downloading(let status):
383
+ // The SDK emits all zero values just before emitting a SYNCED status so we need to ignore these
384
+ if status.targetHeight == 0 {
385
+ return
386
+ }
387
+ self.processorState.lastDownloadedHeight = status.progressHeight
388
+ self.processorState.networkBlockHeight = status.targetHeight
389
+ break
390
+ case .scanning(let status):
391
+ self.processorState.scanProgress = Int(floor(status.progress * 100))
392
+ self.processorState.lastScannedHeight = status.progressHeight
393
+ self.processorState.networkBlockHeight = status.targetHeight
394
+ default:
395
+ return
396
+ }
380
397
  } else {
381
- return
398
+ self.processorState.lastDownloadedHeight = self.synchronizer.latestScannedHeight
399
+ self.processorState.scanProgress = 100
400
+ self.processorState.lastScannedHeight = self.synchronizer.latestScannedHeight
401
+ self.processorState.networkBlockHeight = try! self.synchronizer.latestHeight()
402
+ }
403
+
404
+ if self.processorState.lastDownloadedHeight != prevLastDownloadedHeight || self.processorState.scanProgress != prevScanProgress ||
405
+ self.processorState.lastScannedHeight != prevLastScannedHeight ||
406
+ self.processorState.networkBlockHeight != prevNetworkBlockHeight {
407
+ emit("UpdateEvent", self.processorState.nsDictionary)
382
408
  }
383
- self.processorState.networkBlockHeight = status.targetHeight!
384
- emit("UpdateEvent", self.processorState.nsDictionary)
385
409
  }
386
410
 
387
411
  func initializeProcessorState() {
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>SchemeUserState</key>
6
+ <dict>
7
+ <key>RNZcash.xcscheme_^#shared#^_</key>
8
+ <dict>
9
+ <key>orderHint</key>
10
+ <integer>28</integer>
11
+ </dict>
12
+ </dict>
13
+ <key>SuppressBuildableAutocreation</key>
14
+ <dict>
15
+ <key>58B511DA1A9E6C8500147676</key>
16
+ <dict>
17
+ <key>primary</key>
18
+ <true/>
19
+ </dict>
20
+ </dict>
21
+ </dict>
22
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-zcash",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Zcash library for React Native",
5
5
  "homepage": "https://github.com/EdgeApp/react-native-zcash",
6
6
  "repository": {
@@ -28,7 +28,6 @@
28
28
  "lib",
29
29
  "package.json",
30
30
  "README.md",
31
- "Scripts",
32
31
  "src"
33
32
  ],
34
33
  "main": "lib/rnzcash.rn.js",
@@ -17,6 +17,6 @@ Pod::Spec.new do |s|
17
17
  s.source_files = "ios/**/*.{h,m,swift}"
18
18
 
19
19
  s.dependency "React"
20
- s.dependency 'ZcashLightClientKit', '0.13.0-beta.2'
20
+ s.dependency 'ZcashLightClientKit', '0.14.0-beta'
21
21
 
22
22
  end