diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index f8b10e9c..f24eecc4 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -357,6 +357,9 @@ jobs:
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS: androiddebugkey
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS_PASSWORD: android
run: |
+ # Due to issues with the Rust integration, building the release APK requires running the task twice to
+ # ensure the native libraries are bundled in the APK
+ ./gradlew assembleRelease
./gradlew assembleRelease
- name: Collect Artifacts
timeout-minutes: 1
diff --git a/.idea/runConfigurations/_backend_lib_connectedAndroidTest.xml b/.idea/runConfigurations/_backend_lib_connectedAndroidTest.xml
new file mode 100644
index 00000000..c301e498
--- /dev/null
+++ b/.idea/runConfigurations/_backend_lib_connectedAndroidTest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/_lightwallet_client_lib_connectedAndroidTest.xml b/.idea/runConfigurations/_lightwallet_client_lib_connectedAndroidTest.xml
new file mode 100644
index 00000000..d94f6163
--- /dev/null
+++ b/.idea/runConfigurations/_lightwallet_client_lib_connectedAndroidTest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/_sdk_incubator_lib_connectedAndroidTest.xml b/.idea/runConfigurations/_sdk_incubator_lib_connectedAndroidTest.xml
new file mode 100644
index 00000000..464baf38
--- /dev/null
+++ b/.idea/runConfigurations/_sdk_incubator_lib_connectedAndroidTest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/demo_app_benchmark_test_connectedBenchmarkAndroidTest.xml b/.idea/runConfigurations/demo_app_benchmark_test_connectedBenchmarkAndroidTest.xml
index d381e039..71edf0a5 100644
--- a/.idea/runConfigurations/demo_app_benchmark_test_connectedBenchmarkAndroidTest.xml
+++ b/.idea/runConfigurations/demo_app_benchmark_test_connectedBenchmarkAndroidTest.xml
@@ -1,7 +1,6 @@
-
-
+
+
@@ -16,8 +15,8 @@
-
-
+
+
@@ -25,6 +24,8 @@
+
+
@@ -32,14 +33,21 @@
+
+
-
+
+
+
+
+
+
diff --git a/.run/_darkside-test-lib_connectedAndroidTest.run.xml b/.run/_darkside-test-lib_connectedAndroidTest.run.xml
index 01075dd4..76905b54 100644
--- a/.run/_darkside-test-lib_connectedAndroidTest.run.xml
+++ b/.run/_darkside-test-lib_connectedAndroidTest.run.xml
@@ -1,20 +1,22 @@
-
+
+
-
+
+
+
-
-
+
-
+
@@ -22,6 +24,8 @@
+
+
@@ -29,20 +33,27 @@
+
+
-
+
+
+
+
+
+
-
+
diff --git a/.run/_sdkLib_connectedCheck.run.xml b/.run/_sdk-lib_connectedAndroidTest.run.xml
similarity index 80%
rename from .run/_sdkLib_connectedCheck.run.xml
rename to .run/_sdk-lib_connectedAndroidTest.run.xml
index 44b8b56d..8442b277 100644
--- a/.run/_sdkLib_connectedCheck.run.xml
+++ b/.run/_sdk-lib_connectedAndroidTest.run.xml
@@ -5,9 +5,9 @@
+
-
@@ -16,6 +16,7 @@
+
@@ -23,6 +24,8 @@
+
+
@@ -30,14 +33,21 @@
+
+
-
+
+
+
+
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 948b65ce..a32141b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,25 +1,20 @@
-Change Log
-==========
-
+# Change Log
+
## Unreleased
-- The SDK's `CompactBlockProcessor` switched from processing **all blocks in one run** mechanism to **batched blocks**
-processing. This was necessary for the sync state's parallelization. Example of syncing of the latest
- 100 blocks:
- - Previously: _Download 100 blocks -> Validate 100 blocks -> Scan 100 blocks -> SYNCED_
- - Now: _10x (Download 10 blocks -> Validate 10 blocks -> Scan 10 blocks) -> SYNCED_
-- `Synchronizer.progress` now returns `Flow` instead of `Flow`. PercentDecimal is a type-safe
- model.
- Use `PercentDecimal.toPercentage()` to get a number within 0-100% scale.
-- `Synchronizer.status` now provides a new `SYNCING` state, which covers all three previous `DOWNLOADING`,
- `VALIDATING`, and `SCANNING` states, which were eliminated in favor of `SYNCING` state.
-
-## 1.17.0-beta01
-- Synchronizer APIs for listing sent and received transactions have been removed.
-- Synchronizer APIs for listing pending transactions have been removed, along with the `PendingTransaction` object.
-- `Synchronizer.clearedTransactions` has been renamed to `Synchronizer.transactions` and includes sent, received, and pending transactions.
+- Transparent fund balances are now displayed almost immediately
+- Synchronization of shielded balances and transaction history is about 30% faster
+- Disk space usage is reduced by about 90%
+- `Synchronizer.status` has been simplified by combining `DOWNLOADING`, `VALIDATING`, and `SCANNING` states into a single `SYNCING` state.
+- `Synchronizer.progress` now returns `Flow` instead of `Flow`. PercentDecimal is a type-safe model. Use `PercentDecimal.toPercentage()` to get a number within 0-100% scale.
+- `Synchronizer.clearedTransactions` has been renamed to `Synchronizer.transactions` and includes sent, received, and pending transactions. Synchronizer APIs for listing sent, received, and pending transactions have been removed. Clients can determine whether a transaction is sent, received, or pending by filtering the `TransactionOverview` objects returned by `Synchronizer.transactions`
- `Synchronizer.send()` and `shieldFunds()` are now `suspend` functions with `Long` return values representing the ID of the newly created transaction. Errors are reported by thrown exceptions.
+ - `DerivationTool` is now an interface, rather than an `object`, which makes it easier to inject alternative implementations into tests. To adapt to the new API, replace calls to `DerivationTool.methodName()` with `DerivationTool.getInstance().methodName()`.
+ - `DerivationTool` methods are no longer suspending, which should make it easier to call them in various situations. Obtaining a `DerivationTool` instance via `DerivationTool.getInstance()` frontloads the need for a suspending call.
+ - `DerivationTool.deriveUnifiedFullViewingKeys()` no longer has a default argument for `numberOfAccounts`. Clients should now pass `DerivationTool.DEFAULT_NUMBER_OF_ACCOUNTS` as the value. Note that the SDK does not currently have proper support for multiple accounts.
+ - The SDK's internals for connecting with librustzcash have been refactored to a separate Gradle module `backend-lib` (and therefore a separate artifact) which is a transitive dependency of the Zcash Android SDK. SDK consumers that use Gradle dependency locks may notice this difference, but otherwise it should be mostly an invisible change.
## 1.16.0-beta01
+(This version was only deployed as a snapshot and not released on Maven Central)
### Changed
- The minimum supported version of Android is now API level 27.
@@ -27,7 +22,7 @@ processing. This was necessary for the sync state's parallelization. Example of
### Changed
- A new package `sdk-incubator-lib` is now available as a public API. This package contains experimental APIs that may be promoted to the SDK in the future. The APIs in this package are not guaranteed to be stable, and may change at any time.
- `Synchronizer.refreshUtxos` now takes `Account` type as first parameter instead of transparent address of type
- `String`, and thus it downloads all UTXOs for the given account addresses.
+ `String`, and thus it downloads all UTXOs for the given account addresses. The Account object provides a default `0` index Account with `Account.DEFAULT`.
## 1.14.0-beta01
### Changed
@@ -41,6 +36,7 @@ processing. This was necessary for the sync state's parallelization. Example of
- The new networking module now provides a `LightWalletClient` for asynchronous calls.
- Most unary calls respond with the new `Response` class and its subclasses. Streaming calls will be updated
with the Response class later.
+ - SDK clients should avoid using generated GRPC objects, as these are an internal implementation detail and are in process of being removed from the public API. Any clients using GRPC objects will find these have been repackaged from `cash.z.wallet.sdk.rpc` to `cash.z.wallet.sdk.internal.rpc` to signal they are not a public API.
## 1.12.0-beta01
### Changed
@@ -122,58 +118,48 @@ processing. This was necessary for the sync state's parallelization. Example of
- `DerivationTool.deriveUnifiedViewingKeys` (use `DerivationTool.deriveUnifiedFullViewingKey` instead)
- `DerivationTool.validateUnifiedViewingKey`
-Version 1.9.0-beta05
-------------------------------------
+## Version 1.9.0-beta05
- The minimum version of Android supported is now API 21
- Fixed R8/ProGuard consumer rule, which eliminates a runtime crash for minified apps
-Version 1.9.0-beta04
-------------------------------------
+## Version 1.9.0-beta04
- The SDK now stores sapling param files in `no_backup/co.electricoin.zcash` folder instead of the `cache/params`
folder. Besides that, `SaplingParamTool` also does validation of downloaded sapling param file hash and size.
**No action required from client app**.
-Version 1.9.0-beta03
-------------------------------------
+## Version 1.9.0-beta03
- No changes; this release is a test of a new deployment process
-Version 1.9.0-beta02
-------------------------------------
+## Version 1.9.0-beta02
- The SDK now stores database files in `no_backup/co.electricoin.zcash` folder instead of the `database` folder. **No action required from client app**.
-Version 1.9.0-beta01
-------------------------------------
+## Version 1.9.0-beta01
- Split `ZcashNetwork` into `ZcashNetwork` and `LightWalletEndpoint` to decouple network and server configuration
- Gradle 7.5.1
- Updated checkpoints
-Version 1.8.0-beta01
-------------------------------------
+## Version 1.8.0-beta01
- Enabled automated unit tests run on the CI server
- Added `BlockHeight` typesafe object to represent block heights
- Significantly reduced memory usage, fixing potential OutOfMemoryError during block download
- Kotlin 1.7.10
- Updated checkpoints
-Version 1.7.0-beta01
-------------------------------------
+## Version 1.7.0-beta01
- Added `Zatoshi` typesafe object to represent amounts.
- Kotlin 1.7.0
-Version 1.6.0-beta01
-------------------------------------
+## Version 1.6.0-beta01
- Updated checkpoints for Mainnet and Testnet
- Fix: SDK can now be used on Intel x86_64 emulators
- Prevent R8 warnings for apps consuming the SDK
-Version 1.5.0-beta01
-------------------------------------
+## Version 1.5.0-beta01
- New: Transactions can be created after NU5 activation.
- New: Support for receiving v5 transactions.
- Known issues: The SDK will not run on Intel 64-bit API 31+ emulators. Workarounds include: testing on a physical device, using an older 32-bit API version Intel emulator, or using an ARM emulator.
-Version 1.4.0-beta01
-------------------------------------
+## Version 1.4.0-beta01
- Main entrypoint to the SDK has changed. See [MIGRATIONS.md](MIGRATIONS.md)
- The minimum version of Android supported is now API 19
- Updated checkpoints for Mainnet and Testnet
@@ -182,83 +168,68 @@ Version 1.4.0-beta01
- Updated dependencies, including Kotlin 1.6.21, Coroutines 1.6.1, GRPC 1.46.0, Okio 3.1.0, NDK 23
- Known issues: The SDK will not run on Intel 64-bit API 31+ emulators. Workarounds include: testing on a physical device, using an older 32-bit API version Intel emulator, or using an ARM emulator.
-Version 1.3.0-beta20
-------------------------------------
+## Version 1.3.0-beta20
- New: Updated checkpoints for Mainnet and Testnet
-Version 1.3.0-beta19
-------------------------------------
+## Version 1.3.0-beta19
- New: Updated checkpoints for Mainnet and Testnet
- Fix: Repackaged internal classes to a new `internal` package name
- Fix: Testnet checkpoints have been corrected
- Updated dependencies
-Version 1.3.0-beta18
-------------------------------------
+## Version 1.3.0-beta18
- Fix: Corrected logic when calculating birthdates for wallets with zero received notes.
-Version 1.3.0-beta17
-------------------------------------
+## Version 1.3.0-beta17
- Fix: Autoshielding confirmation count error so funds are available after 10 confirmations.
- New: Allow developers to enable Rust logs.
- New: Accept GZIP compression from lightwalletd.
- New: Reduce the UTXO retry time.
-Version 1.3.0-beta16
-------------------------------------
+## Version 1.3.0-beta16
- Fix: Gracefully handle failures while fetching UTXOs.
- New: Expose StateFlows for balances.
- New: Make it easier to subscribe to transactions.
- New: Cleanup default logs.
- New: Convenience functions for WalletBalance objects.
-Version 1.3.0-beta15
-------------------------------------
+## Version 1.3.0-beta15
- Fix: Increase reconnection attempts on failed app restart.
- New: Updated checkpoints for testnet and mainnet.
-Version 1.3.0-beta14
-------------------------------------
+## Version 1.3.0-beta14
- New: Add separate flows for sapling, orchard and tranparent balances.
- Fix: Continue troubleshooting and fixing server disconnects.
- Updated dependencies.
-Version 1.3.0-beta12
-------------------------------------
+## Version 1.3.0-beta12
- New: Expose network height as StateFlow.
- Fix: Reconnect to lightwalletd when a service exception occurs.
-Version 1.3.0-beta11
-------------------------------------
+## Version 1.3.0-beta11
- Fix: Remove unused flag that was breaking new wallet creation for some wallets.
-Version 1.3.0-beta10
-------------------------------------
+## Version 1.3.0-beta10
- Fix: Make it safe to call the new prepare function more than once.
-Version 1.3.0-beta09
-------------------------------------
+## Version 1.3.0-beta09
- New: Add quick rewind feature, which makes it easy to rescan blocks after an upgrade.
- Fix: Repair complex data migration bug that caused crashes on upgrades.
-Version 1.3.0-beta08
-------------------------------------
+## Version 1.3.0-beta08
- Fix: Disable librustzcash logs by default.
-Version 1.3.0-beta07
-------------------------------------
+## Version 1.3.0-beta07
- Fix: Address issues with key migration, allowing wallets to reset viewing keys, when needed.
-Version 1.3.0-beta06
-------------------------------------
+## Version 1.3.0-beta06
- Fix: Repair publishing so that AARs work on Windows machines [issue #222].
- Fix: Incorrect BranchId on 32-bit devics [issue #224].
- Fix: Rescan should not go beyond the wallet checkpoint.
- New: Drop Android Jetifier since it is no longer used.
- Updated checkpoints, improved tests (added Test Suites) and better error messages.
-Version 1.3.0-beta05
-------------------------------------
+## Version 1.3.0-beta05
- Major: Consolidate product flavors into one library for the SDK instead of two.
- Major: Integrates with latest Librustzcash including full Data Access API support.
- Major: Move off of JCenter and onto Maven Central.
@@ -283,44 +254,36 @@ Version 1.3.0-beta05
- New: Derive sapling activation height from the active network.
- New: Latest checkpoints for mainnet and testnet.
-Version 1.2.1-beta04
-------------------------------------
+## Version 1.2.1-beta04
- New: Updated to latest versions of grpc, grpc-okhttp and protoc
- Fix: Addresses root issue of Android 11 crash on SSL sockets
-Version 1.2.1-beta03
-------------------------------------
+## Version 1.2.1-beta03
- New: Implements ZIP-313, reducing the default fee from 10,000 to 1,000 zats.
- Fix: 80% reduction in build warnings from 90 -> 18 and improved docs [Credit: @herou].
-Version 1.2.1-beta02
-------------------------------------
+## Version 1.2.1-beta02
- New: Improve birthday configuration and config functions.
- Fix: Broken layout in demo app transaction list.
-Version 1.2.1-beta01
-------------------------------------
+## Version 1.2.1-beta01
- New: Added latest checkpoints for testnet and mainnet.
- New: Added display name for Canopy.
- New: Update to the latest lightwalletd service definition.
- Fix: Convert Initializer.Builder to Initializer.Config to simplify the constructors.
-Version 1.2.0-beta01
-------------------------------------
+## Version 1.2.0-beta01
- New: Added ability to erase initializer data.
- Fix: Updated to latest librustzcash, fixing send functionality on Canopy.
-Version 1.1.0-beta10
-------------------------------------
+## Version 1.1.0-beta10
- New: Modified visibility on a few things to facilitate partner integrations.
-Version 1.1.0-beta08
-------------------------------------
+## Version 1.1.0-beta08
- Fix: Publishing has been corrected by jcenter's support team.
- New: Minor improvement to initializer
-Version 1.1.0-beta05
-------------------------------------
+## Version 1.1.0-beta05
- New: Synchronizer can now be started with just a viewing key.
- New: Initializer improvements.
- New: Added tool for loading checkpoints.
@@ -330,8 +293,7 @@ Version 1.1.0-beta05
- Fix: Broken testnet demo app.
- Fix: Publishing configuration.
-Version 1.1.0-beta04
-------------------------------------
+## Version 1.1.0-beta04
- New: Add support for canopy on testnet.
- New: Change the default lightwalletd server.
- New: Add lightwalletd service for fetching t-addr transactions.
@@ -340,8 +302,7 @@ Version 1.1.0-beta04
- New: Added new checkpoints.
- Fix: Minor enhancements.
-Version 1.1.0-beta03
-------------------------------------
+## Version 1.1.0-beta03
- New: Add robust support for transaction cancellation.
- New: Update to latest version of librustzcash.
- New: Expand test support.
diff --git a/MIGRATIONS.md b/MIGRATIONS.md
index cd5372c3..0ace03d5 100644
--- a/MIGRATIONS.md
+++ b/MIGRATIONS.md
@@ -1,27 +1,7 @@
Troubleshooting Migrations
==========
-Migration to Version 1.17
----------------------------------
-Synchronizer APIs for listing sent and received transactions have been removed. Clients should use `Synchronizer.transactions`, filtering on the field `TransactionOverview.isSentTransaction`.
-
-Synchronizer APIs for pending transactions have been removed. Clients should use `Synchronizer.transactions`, filtering on the field `TransactionOverview.transactionState`
-
-The Synchronizer APIs for sending transactions (`send()` and `shieldFunds()`) are now suspend functions that return `Long` which contains the ID of the newly created transaction. To monitor for changes to pending transactions, observe `Synchronizer.transactions`. If a failure occurs, `send()` and `shieldFunds()` throw exceptions.
-
-Migration to Version 1.15
----------------------------------
-The updated `Synchronizer.refreshUtxos` is now supposed to be called with `Account` parameter instead of `String` address parameter. The Account object provides a default `0` index Account with `Account.DEFAULT`.
-
-Migration to Version 1.13
----------------------------------
-Update usages of `z.cash.ecc.android.sdk.model.LightWalletEndpoint` to `co.electriccoin.lightwallet.client.model.LightWalletEndpoint`.
-
-SDK clients should avoid using generated GRPC objects, as these are an internal implementation detail and are in process of being removed from the public API. Any clients using GRPC objects will find these have been repackaged from `cash.z.wallet.sdk.rpc` to `cash.z.wallet.sdk.internal.rpc` to signal they are not a public API.
-
-Migration to Version 1.12
----------------------------------
-`TransactionOverview`, `Transaction.Sent`, and `Transaction.Received` have been updated to reflect that `minedHeight` is nullable.
+Note: Going forward, migrations will be incorporated into the CHANGELOG.md.
Migration to Version 1.11
---------------------------------
diff --git a/sdk-lib/Cargo.lock b/backend-lib/Cargo.lock
similarity index 100%
rename from sdk-lib/Cargo.lock
rename to backend-lib/Cargo.lock
diff --git a/sdk-lib/Cargo.toml b/backend-lib/Cargo.toml
similarity index 100%
rename from sdk-lib/Cargo.toml
rename to backend-lib/Cargo.toml
diff --git a/backend-lib/build.gradle.kts b/backend-lib/build.gradle.kts
new file mode 100644
index 00000000..e2ce13b4
--- /dev/null
+++ b/backend-lib/build.gradle.kts
@@ -0,0 +1,124 @@
+plugins {
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+ id("zcash-sdk.android-conventions")
+
+ id("org.jetbrains.dokka")
+ id("org.mozilla.rust-android-gradle.rust-android")
+
+ id("wtf.emulator.gradle")
+ id("zcash-sdk.emulator-wtf-conventions")
+
+ id("maven-publish")
+ id("signing")
+ id("zcash-sdk.publishing-conventions")
+}
+
+// Publishing information
+
+val myVersion = project.property("LIBRARY_VERSION").toString()
+val myArtifactId = "zcash-android-backend"
+publishing {
+ publications {
+ publications.withType().all {
+ artifactId = myArtifactId
+ }
+ }
+}
+
+android {
+ namespace = "cash.z.ecc.android.backend"
+
+ useLibrary("android.test.runner")
+
+ defaultConfig {
+ consumerProguardFiles("proguard-consumer.txt")
+ }
+
+ buildTypes {
+ getByName("debug").apply {
+ // test builds exceed the dex limit because they pull in large test libraries
+ isMinifyEnabled = false
+ }
+ getByName("release").apply {
+ isMinifyEnabled = project.property("IS_MINIFY_SDK_ENABLED").toString().toBoolean()
+ proguardFiles.addAll(
+ listOf(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ File("proguard-project.txt")
+ )
+ )
+ }
+ create("benchmark") {
+ // We provide the extra benchmark build type just for benchmarking purposes
+ initWith(buildTypes.getByName("release"))
+ matchingFallbacks += listOf("release")
+ }
+ }
+
+ lint {
+ baseline = File("lint-baseline.xml")
+ }
+}
+
+cargo {
+ module = "."
+ libname = "zcashwalletsdk"
+ targets = listOf(
+ "arm",
+ "arm64",
+ "x86",
+ "x86_64"
+ )
+ val minSdkVersion = project.property("ANDROID_MIN_SDK_VERSION").toString().toInt()
+ apiLevels = mapOf(
+ "arm" to minSdkVersion,
+ "arm64" to minSdkVersion,
+ "x86" to minSdkVersion,
+ "x86_64" to minSdkVersion,
+ )
+
+ profile = "release"
+ prebuiltToolchains = true
+}
+
+dependencies {
+ api(projects.lightwalletClientLib)
+
+ implementation(libs.androidx.annotation)
+
+ // Kotlin
+ implementation(libs.kotlin.stdlib)
+ implementation(libs.kotlinx.coroutines.core)
+ implementation(libs.kotlinx.coroutines.android)
+
+ androidTestImplementation(libs.androidx.multidex)
+ androidTestImplementation(libs.androidx.test.runner)
+ androidTestImplementation(libs.androidx.test.junit)
+ androidTestImplementation(libs.androidx.test.core)
+ androidTestImplementation(libs.kotlin.test)
+ androidTestImplementation(libs.kotlinx.coroutines.test)
+
+ androidTestImplementation(libs.zcashwalletplgn)
+ androidTestImplementation(libs.bip39)
+}
+
+tasks {
+ /*
+ * The Mozilla Rust Gradle plugin caches the native build data under the "target" directory,
+ * which does not normally get deleted during a clean. The following task and dependency solves
+ * that.
+ */
+ getByName("clean").dependsOn(create("cleanRustBuildOutput") {
+ delete("target")
+ })
+}
+
+project.afterEvaluate {
+ val cargoTask = tasks.getByName("cargoBuild")
+ tasks.getByName("javaPreCompileDebug").dependsOn(cargoTask)
+ tasks.getByName("javaPreCompileRelease").dependsOn(cargoTask)
+}
+
+fun MinimalExternalModuleDependency.asCoordinateString() =
+ "${module.group}:${module.name}:${versionConstraint.displayName}"
\ No newline at end of file
diff --git a/backend-lib/lint-baseline.xml b/backend-lib/lint-baseline.xml
new file mode 100644
index 00000000..1d527bce
--- /dev/null
+++ b/backend-lib/lint-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/backend-lib/lint.xml b/backend-lib/lint.xml
new file mode 100644
index 00000000..791d4c8c
--- /dev/null
+++ b/backend-lib/lint.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/backend-lib/proguard-consumer.txt b/backend-lib/proguard-consumer.txt
new file mode 100644
index 00000000..0cd8f465
--- /dev/null
+++ b/backend-lib/proguard-consumer.txt
@@ -0,0 +1,3 @@
+-keepclasseswithmembernames,includedescriptorclasses class * {
+ native ;
+}
diff --git a/backend-lib/proguard-project.txt b/backend-lib/proguard-project.txt
new file mode 100644
index 00000000..a0cff949
--- /dev/null
+++ b/backend-lib/proguard-project.txt
@@ -0,0 +1,18 @@
+# This improves obfuscation and moves non-public classes to their own namespace.
+-repackageclasses 'cash.z.ecc.android.sdk.internal'
+
+# This makes it easier to autocomplete methods in an IDE using this obfuscated library.
+-keepparameternames
+
+# The ProGuard manual recommends keeping these attributes for libraries.
+-keepattributes EnclosingMethod,InnerClasses,Signature,Exceptions,*Annotation*
+
+# Ensure that stacktraces are reversible.
+-renamesourcefileattribute SourceFile
+-keepattributes SourceFile,LineNumberTable
+
+# Keep the public interface of the library.
+-keep public class cash.z.ecc.android.sdk.internal.Backend { public protected *; }
+-keep public class cash.z.ecc.android.sdk.internal.Derivation { public protected *; }
+-keep public class cash.z.ecc.android.sdk.internal.jni.RustBackend { public protected *; }
+-keep public class cash.z.ecc.android.sdk.internal.model.* { public protected *; }
diff --git a/backend-lib/src/androidTest/AndroidManifest.xml b/backend-lib/src/androidTest/AndroidManifest.xml
new file mode 100644
index 00000000..a2a699c4
--- /dev/null
+++ b/backend-lib/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
diff --git a/backend-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/jni/RustDerivationToolTest.kt b/backend-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/jni/RustDerivationToolTest.kt
new file mode 100644
index 00000000..00e02b79
--- /dev/null
+++ b/backend-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/jni/RustDerivationToolTest.kt
@@ -0,0 +1,26 @@
+package cash.z.ecc.android.sdk.internal.jni
+
+import cash.z.ecc.android.bip39.Mnemonics
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import kotlin.test.assertContentEquals
+
+class RustDerivationToolTest {
+
+ companion object {
+ private const val SEED_PHRASE =
+ "kitchen renew wide common vague fold vacuum tilt amazing pear square gossip jewel month tree shock scan alpha just spot fluid toilet view dinner"
+ }
+
+ @Test
+ @OptIn(ExperimentalCoroutinesApi::class)
+ fun create_spending_key_does_not_mutate_passed_bytes() = runTest {
+ val bytesOne = Mnemonics.MnemonicCode(SEED_PHRASE).toEntropy()
+ val bytesTwo = Mnemonics.MnemonicCode(SEED_PHRASE).toEntropy()
+
+ RustDerivationTool.new().deriveUnifiedSpendingKey(bytesOne, networkId = 1, accountIndex = 0)
+
+ assertContentEquals(bytesTwo, bytesOne)
+ }
+}
diff --git a/backend-lib/src/main/AndroidManifest.xml b/backend-lib/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a2f47b60
--- /dev/null
+++ b/backend-lib/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/Backend.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt
similarity index 90%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/Backend.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt
index b6bacab6..65e67454 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/Backend.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt
@@ -1,9 +1,7 @@
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
-import cash.z.ecc.android.sdk.model.BlockHeight
-import cash.z.ecc.android.sdk.model.ZcashNetwork
-import java.io.File
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
/**
* Contract defining the exposed capabilities of the Rust backend.
@@ -14,11 +12,9 @@ import java.io.File
// TODO [#920]: Tweak RustBackend public APIs to have void return values
// TODO [#920]: https://github.com/zcash/zcash-android-wallet-sdk/issues/920
@Suppress("TooManyFunctions")
-internal interface Backend {
+interface Backend {
- val network: ZcashNetwork
-
- val saplingParamDir: File
+ val networkId: Int
suspend fun initBlockMetaDb(): Int
@@ -53,7 +49,7 @@ internal interface Backend {
suspend fun initDataDb(seed: ByteArray?): Int
- suspend fun createAccount(seed: ByteArray): UnifiedSpendingKeyJni
+ suspend fun createAccount(seed: ByteArray): JniUnifiedSpendingKey
fun isValidShieldedAddr(addr: String): Boolean
@@ -112,6 +108,6 @@ internal interface Backend {
index: Int,
script: ByteArray,
value: Long,
- height: BlockHeight
+ height: Long
): Boolean
}
diff --git a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Derivation.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Derivation.kt
new file mode 100644
index 00000000..3f61d159
--- /dev/null
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Derivation.kt
@@ -0,0 +1,44 @@
+package cash.z.ecc.android.sdk.internal
+
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
+
+interface Derivation {
+ fun deriveUnifiedAddress(
+ viewingKey: String,
+ networkId: Int
+ ): String
+
+ fun deriveUnifiedAddress(
+ seed: ByteArray,
+ networkId: Int,
+ accountIndex: Int
+ ): String
+
+ fun deriveUnifiedSpendingKey(
+ seed: ByteArray,
+ networkId: Int,
+ accountIndex: Int
+ ): JniUnifiedSpendingKey
+
+ /**
+ * @return a unified full viewing key.
+ */
+ fun deriveUnifiedFullViewingKey(
+ usk: JniUnifiedSpendingKey,
+ networkId: Int
+ ): String
+
+ /**
+ * @param numberOfAccounts Use [DEFAULT_NUMBER_OF_ACCOUNTS] to derive a single key.
+ * @return an array of unified full viewing keys, one for each account.
+ */
+ fun deriveUnifiedFullViewingKeys(
+ seed: ByteArray,
+ networkId: Int,
+ numberOfAccounts: Int
+ ): Array
+
+ companion object {
+ const val DEFAULT_NUMBER_OF_ACCOUNTS = 1
+ }
+}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SdkDispatchers.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/SdkDispatchers.kt
similarity index 97%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SdkDispatchers.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/SdkDispatchers.kt
index afc33f69..7ef4513a 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SdkDispatchers.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/SdkDispatchers.kt
@@ -16,7 +16,7 @@ internal object SdkExecutors {
val DATABASE_IO = Executors.newSingleThreadExecutor()
}
-internal object SdkDispatchers {
+object SdkDispatchers {
/**
* Dispatcher used for database IO that's shared with the Rust native library.
*/
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/FileExt.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/FileExt.kt
similarity index 81%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/FileExt.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/FileExt.kt
index 36a52807..ca59b8af 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/FileExt.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/FileExt.kt
@@ -9,15 +9,15 @@ import java.io.FileInputStream
import java.security.DigestInputStream
import java.security.MessageDigest
-internal suspend fun File.canWriteSuspend() = withContext(Dispatchers.IO) { canWrite() }
+suspend fun File.canWriteSuspend() = withContext(Dispatchers.IO) { canWrite() }
suspend fun File.createNewFileSuspend() = withContext(Dispatchers.IO) { createNewFile() }
-internal suspend fun File.deleteSuspend() = withContext(Dispatchers.IO) { delete() }
+suspend fun File.deleteSuspend() = withContext(Dispatchers.IO) { delete() }
suspend fun File.deleteRecursivelySuspend() = withContext(Dispatchers.IO) { deleteRecursively() }
-internal suspend fun File.existsSuspend() = withContext(Dispatchers.IO) { exists() }
+suspend fun File.existsSuspend() = withContext(Dispatchers.IO) { exists() }
suspend fun File.inputStreamSuspend(): FileInputStream = withContext(Dispatchers.IO) { inputStream() }
@@ -27,11 +27,11 @@ suspend fun File.listFilesSuspend(): Array? = withContext(Dispatchers.IO)
suspend fun File.listSuspend(): Array? = withContext(Dispatchers.IO) { list() }
-internal suspend fun File.mkdirsSuspend() = withContext(Dispatchers.IO) { mkdirs() }
+suspend fun File.mkdirsSuspend() = withContext(Dispatchers.IO) { mkdirs() }
suspend fun File.readBytesSuspend() = withContext(Dispatchers.IO) { readBytes() }
-internal suspend fun File.renameToSuspend(dest: File) = withContext(Dispatchers.IO) { renameTo(dest) }
+suspend fun File.renameToSuspend(dest: File) = withContext(Dispatchers.IO) { renameTo(dest) }
suspend fun File.writeBytesSuspend(byteArray: ByteArray) = withContext(Dispatchers.IO) { writeBytes(byteArray) }
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/NativeLibraryLoader.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/NativeLibraryLoader.kt
similarity index 81%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/NativeLibraryLoader.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/NativeLibraryLoader.kt
index 87e8e55c..2e8906b7 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/NativeLibraryLoader.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/NativeLibraryLoader.kt
@@ -1,12 +1,10 @@
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal.jni
-import cash.z.ecc.android.sdk.internal.Twig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.util.concurrent.atomic.AtomicBoolean
-import kotlin.system.measureTimeMillis
/**
* Loads a native library once. This class is thread-safe.
@@ -39,14 +37,7 @@ internal class NativeLibraryLoader(private val libraryName: String) {
private suspend fun loadNativeLibrary() {
runCatching {
- Twig.debug { "Loading native library $libraryName" }
-
- val loadTimeMillis = measureTimeMillis {
- loadLibrarySuspend(libraryName)
- }
-
- Twig.debug { "Loading native library took $loadTimeMillis milliseconds" }
-
+ loadLibrarySuspend(libraryName)
isLoaded.set(true)
}.onFailure {
// Fail fast, because this is not a recoverable error
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/RustBackend.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt
similarity index 86%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/RustBackend.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt
index 2e944fd7..c1d19c04 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/RustBackend.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt
@@ -1,13 +1,11 @@
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal.jni
-import cash.z.ecc.android.sdk.internal.SaplingParamTool
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.SdkDispatchers
-import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.ext.deleteRecursivelySuspend
import cash.z.ecc.android.sdk.internal.ext.deleteSuspend
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
-import cash.z.ecc.android.sdk.model.BlockHeight
-import cash.z.ecc.android.sdk.model.ZcashNetwork
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
import kotlinx.coroutines.withContext
import java.io.File
@@ -17,12 +15,12 @@ import java.io.File
* should be used such as Wallet.kt or CompactBlockProcessor.kt.
*/
@Suppress("TooManyFunctions")
-internal class RustBackend private constructor(
- override val network: ZcashNetwork,
- val birthdayHeight: BlockHeight,
- val dataDbFile: File,
- val fsBlockDbRoot: File,
- override val saplingParamDir: File
+class RustBackend private constructor(
+ override val networkId: Int,
+ private val dataDbFile: File,
+ private val fsBlockDbRoot: File,
+ private val saplingSpendFile: File,
+ private val saplingOutputFile: File,
) : Backend {
/**
@@ -38,16 +36,14 @@ internal class RustBackend private constructor(
var cacheClearResult = true
var dataClearResult = true
if (clearCache) {
- Twig.debug { "Deleting the cache files..." }
fsBlockDbRoot.deleteRecursivelySuspend().also { result ->
- Twig.debug { "Deletion of the cache files ${if (result) "succeeded" else "failed"}!" }
+ // Twig.debug { "Deletion of the cache files ${if (result) "succeeded" else "failed"}!" }
cacheClearResult = result
}
}
if (clearDataDb) {
- Twig.debug { "Deleting the data database..." }
dataDbFile.deleteSuspend().also { result ->
- Twig.debug { "Deletion of the data database ${if (result) "succeeded" else "failed"}!" }
+ // Twig.debug { "Deletion of the data database ${if (result) "succeeded" else "failed"}!" }
dataClearResult = result
}
}
@@ -68,16 +64,16 @@ internal class RustBackend private constructor(
initDataDb(
dataDbFile.absolutePath,
seed,
- networkId = network.id
+ networkId = networkId
)
}
- override suspend fun createAccount(seed: ByteArray): UnifiedSpendingKeyJni {
+ override suspend fun createAccount(seed: ByteArray): JniUnifiedSpendingKey {
return withContext(SdkDispatchers.DATABASE_IO) {
createAccount(
dataDbFile.absolutePath,
seed,
- networkId = network.id
+ networkId = networkId
)
}
}
@@ -90,7 +86,7 @@ internal class RustBackend private constructor(
initAccountsTableWithKeys(
dataDbFile.absolutePath,
keys,
- networkId = network.id
+ networkId = networkId
)
}
}
@@ -108,7 +104,7 @@ internal class RustBackend private constructor(
checkpointHash,
checkpointTime,
checkpointSaplingTree,
- networkId = network.id
+ networkId = networkId
)
}
}
@@ -118,7 +114,7 @@ internal class RustBackend private constructor(
getCurrentAddress(
dataDbFile.absolutePath,
account,
- networkId = network.id
+ networkId = networkId
)
}
@@ -131,7 +127,7 @@ internal class RustBackend private constructor(
listTransparentReceivers(
dbDataPath = dataDbFile.absolutePath,
account = account,
- networkId = network.id
+ networkId = networkId
).asList()
}
}
@@ -141,7 +137,7 @@ internal class RustBackend private constructor(
getBalance(
dataDbFile.absolutePath,
account,
- networkId = network.id
+ networkId = networkId
)
}
@@ -153,7 +149,7 @@ internal class RustBackend private constructor(
getVerifiedBalance(
dbDataPath = dataDbFile.absolutePath,
account = account,
- networkId = network.id
+ networkId = networkId
)
}
@@ -165,7 +161,7 @@ internal class RustBackend private constructor(
getReceivedMemoAsUtf8(
dataDbFile.absolutePath,
idNote,
- networkId = network.id
+ networkId = networkId
)
}
@@ -174,7 +170,7 @@ internal class RustBackend private constructor(
getSentMemoAsUtf8(
dataDbFile.absolutePath,
idNote,
- networkId = network.id
+ networkId = networkId
)
}
@@ -219,7 +215,7 @@ internal class RustBackend private constructor(
dbCachePath = fsBlockDbRoot.absolutePath,
dbDataPath = dataDbFile.absolutePath,
limit = limit ?: -1,
- networkId = network.id
+ networkId = networkId
)
if (-1L == validationResult) {
@@ -234,7 +230,7 @@ internal class RustBackend private constructor(
getVerifiedTransparentBalance(
dataDbFile.absolutePath,
address,
- networkId = network.id
+ networkId = networkId
)
}
@@ -243,7 +239,7 @@ internal class RustBackend private constructor(
getTotalTransparentBalance(
dataDbFile.absolutePath,
address,
- networkId = network.id
+ networkId = networkId
)
}
@@ -252,7 +248,7 @@ internal class RustBackend private constructor(
getNearestRewindHeight(
dataDbFile.absolutePath,
height,
- networkId = network.id
+ networkId = networkId
)
}
@@ -266,7 +262,7 @@ internal class RustBackend private constructor(
rewindToHeight(
dataDbFile.absolutePath,
height,
- networkId = network.id
+ networkId = networkId
)
}
@@ -276,7 +272,7 @@ internal class RustBackend private constructor(
fsBlockDbRoot.absolutePath,
dataDbFile.absolutePath,
limit ?: -1,
- networkId = network.id
+ networkId = networkId
)
}
}
@@ -286,7 +282,7 @@ internal class RustBackend private constructor(
decryptAndStoreTransaction(
dataDbFile.absolutePath,
tx,
- networkId = network.id
+ networkId = networkId
)
}
@@ -303,9 +299,9 @@ internal class RustBackend private constructor(
to,
value,
memo ?: ByteArray(0),
- File(saplingParamDir, SaplingParamTool.SPEND_PARAM_FILE_NAME).absolutePath,
- File(saplingParamDir, SaplingParamTool.OUTPUT_PARAM_FILE_NAME).absolutePath,
- networkId = network.id,
+ spendParamsPath = saplingSpendFile.absolutePath,
+ outputParamsPath = saplingOutputFile.absolutePath,
+ networkId = networkId,
useZip317Fees = IS_USE_ZIP_317_FEES
)
}
@@ -315,15 +311,14 @@ internal class RustBackend private constructor(
unifiedSpendingKey: ByteArray,
memo: ByteArray?
): Long {
- Twig.debug { "TMP: shieldToAddress with db path: $dataDbFile, ${memo?.size}" }
return withContext(SdkDispatchers.DATABASE_IO) {
shieldToAddress(
dataDbFile.absolutePath,
unifiedSpendingKey,
memo ?: ByteArray(0),
- File(saplingParamDir, SaplingParamTool.SPEND_PARAM_FILE_NAME).absolutePath,
- File(saplingParamDir, SaplingParamTool.OUTPUT_PARAM_FILE_NAME).absolutePath,
- networkId = network.id,
+ spendParamsPath = saplingSpendFile.absolutePath,
+ outputParamsPath = saplingOutputFile.absolutePath,
+ networkId = networkId,
useZip317Fees = IS_USE_ZIP_317_FEES
)
}
@@ -335,7 +330,7 @@ internal class RustBackend private constructor(
index: Int,
script: ByteArray,
value: Long,
- height: BlockHeight
+ height: Long
): Boolean = withContext(SdkDispatchers.DATABASE_IO) {
putUtxo(
dataDbFile.absolutePath,
@@ -344,22 +339,22 @@ internal class RustBackend private constructor(
index,
script,
value,
- height.value,
- networkId = network.id
+ height,
+ networkId = networkId
)
}
override fun isValidShieldedAddr(addr: String) =
- isValidShieldedAddress(addr, networkId = network.id)
+ isValidShieldedAddress(addr, networkId = networkId)
override fun isValidTransparentAddr(addr: String) =
- isValidTransparentAddress(addr, networkId = network.id)
+ isValidTransparentAddress(addr, networkId = networkId)
override fun isValidUnifiedAddr(addr: String) =
- isValidUnifiedAddress(addr, networkId = network.id)
+ isValidUnifiedAddress(addr, networkId = networkId)
override fun getBranchIdForHeight(height: Long): Long =
- branchIdForHeight(height, networkId = network.id)
+ branchIdForHeight(height, networkId = networkId)
// /**
// * This is a proof-of-concept for doing Local RPC, where we are effectively using the JNI
@@ -400,21 +395,21 @@ internal class RustBackend private constructor(
* Loads the library and initializes path variables. Although it is best to only call this
* function once, it is idempotent.
*/
- suspend fun init(
+ suspend fun new(
fsBlockDbRoot: File,
dataDbFile: File,
- saplingParamsDir: File,
- zcashNetwork: ZcashNetwork,
- birthdayHeight: BlockHeight
+ saplingSpendFile: File,
+ saplingOutputFile: File,
+ zcashNetworkId: Int,
): RustBackend {
loadLibrary()
return RustBackend(
- zcashNetwork,
- birthdayHeight,
+ zcashNetworkId,
dataDbFile = dataDbFile,
fsBlockDbRoot = fsBlockDbRoot,
- saplingParamDir = saplingParamsDir
+ saplingSpendFile = saplingSpendFile,
+ saplingOutputFile = saplingOutputFile
)
}
@@ -450,7 +445,7 @@ internal class RustBackend private constructor(
): Boolean
@JvmStatic
- private external fun createAccount(dbDataPath: String, seed: ByteArray, networkId: Int): UnifiedSpendingKeyJni
+ private external fun createAccount(dbDataPath: String, seed: ByteArray, networkId: Int): JniUnifiedSpendingKey
@JvmStatic
private external fun getCurrentAddress(
@@ -468,7 +463,7 @@ internal class RustBackend private constructor(
@JvmStatic
private external fun listTransparentReceivers(dbDataPath: String, account: Int, networkId: Int): Array
- internal fun validateUnifiedSpendingKey(bytes: ByteArray) =
+ fun validateUnifiedSpendingKey(bytes: ByteArray) =
isValidSpendingKey(bytes)
@JvmStatic
diff --git a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustDerivationTool.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustDerivationTool.kt
new file mode 100644
index 00000000..f4210c31
--- /dev/null
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustDerivationTool.kt
@@ -0,0 +1,78 @@
+package cash.z.ecc.android.sdk.internal.jni
+
+import cash.z.ecc.android.sdk.internal.Derivation
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
+
+class RustDerivationTool private constructor() : Derivation {
+
+ override fun deriveUnifiedFullViewingKeys(
+ seed: ByteArray,
+ networkId: Int,
+ numberOfAccounts: Int
+ ): Array =
+ deriveUnifiedFullViewingKeysFromSeed(seed, numberOfAccounts, networkId = networkId)
+
+ override fun deriveUnifiedFullViewingKey(
+ usk: JniUnifiedSpendingKey,
+ networkId: Int
+ ): String =
+ deriveUnifiedFullViewingKey(usk.bytes, networkId = networkId)
+
+ override fun deriveUnifiedSpendingKey(
+ seed: ByteArray,
+ networkId: Int,
+ accountIndex: Int
+ ): JniUnifiedSpendingKey = deriveSpendingKey(seed, accountIndex, networkId = networkId)
+
+ override fun deriveUnifiedAddress(seed: ByteArray, networkId: Int, accountIndex: Int): String =
+ deriveUnifiedAddressFromSeed(seed, accountIndex = accountIndex, networkId = networkId)
+
+ /**
+ * Given a Unified Full Viewing Key string, return the associated Unified Address.
+ *
+ * @param viewingKey the viewing key to use for deriving the address. The viewing key is tied to
+ * a specific account so no account index is required.
+ *
+ * @return the address that corresponds to the viewing key.
+ */
+ override fun deriveUnifiedAddress(
+ viewingKey: String,
+ networkId: Int
+ ): String =
+ deriveUnifiedAddressFromViewingKey(viewingKey, networkId = networkId)
+
+ companion object {
+ suspend fun new(): Derivation {
+ RustBackend.loadLibrary()
+
+ return RustDerivationTool()
+ }
+
+ @JvmStatic
+ private external fun deriveSpendingKey(
+ seed: ByteArray,
+ account: Int,
+ networkId: Int
+ ): JniUnifiedSpendingKey
+
+ @JvmStatic
+ private external fun deriveUnifiedFullViewingKeysFromSeed(
+ seed: ByteArray,
+ numberOfAccounts: Int,
+ networkId: Int
+ ): Array
+
+ @JvmStatic
+ private external fun deriveUnifiedFullViewingKey(usk: ByteArray, networkId: Int): String
+
+ @JvmStatic
+ private external fun deriveUnifiedAddressFromSeed(
+ seed: ByteArray,
+ accountIndex: Int,
+ networkId: Int
+ ): String
+
+ @JvmStatic
+ private external fun deriveUnifiedAddressFromViewingKey(key: String, networkId: Int): String
+ }
+}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMeta.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMeta.kt
similarity index 96%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMeta.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMeta.kt
index 83f5306e..faf7f324 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMeta.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMeta.kt
@@ -37,7 +37,7 @@ class JniBlockMeta(
companion object {
private val UINT_RANGE = 0.toLong()..UInt.MAX_VALUE.toLong()
- internal fun new(block: CompactBlockUnsafe): JniBlockMeta {
+ fun new(block: CompactBlockUnsafe): JniBlockMeta {
return JniBlockMeta(
height = block.height,
hash = block.hash,
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/UnifiedSpendingKeyJni.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniUnifiedSpendingKey.kt
similarity index 67%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/UnifiedSpendingKeyJni.kt
rename to backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniUnifiedSpendingKey.kt
index e1b9ffc2..87d9eedb 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/UnifiedSpendingKeyJni.kt
+++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniUnifiedSpendingKey.kt
@@ -1,4 +1,4 @@
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal.model
import androidx.annotation.Keep
@@ -12,7 +12,7 @@ import androidx.annotation.Keep
* export/import, or backup purposes.
*/
@Keep
-class UnifiedSpendingKeyJni(
+class JniUnifiedSpendingKey(
val account: Int,
/**
* The binary encoding of the [ZIP 316](https://zips.z.cash/zip-0316) Unified Spending
@@ -22,27 +22,17 @@ class UnifiedSpendingKeyJni(
* inherently unstable, and only intended to be passed between the SDK and the storage
* backend. Wallets **MUST NOT** allow this encoding to be exported or imported.
*/
- private val bytes: ByteArray
+ val bytes: ByteArray
) {
- /**
- * The binary encoding of the [ZIP 316](https://zips.z.cash/zip-0316) Unified Spending
- * Key for [account].
- *
- * This encoding **MUST NOT** be exposed to users. It is an internal encoding that is
- * inherently unstable, and only intended to be passed between the SDK and the storage
- * backend. Wallets **MUST NOT** allow this encoding to be exported or imported.
- */
- fun copyBytes() = bytes.copyOf()
-
// Override to prevent leaking key to logs
- override fun toString() = "UnifiedSpendingKeyJni(account=$account)"
+ override fun toString() = "JniUnifiedSpendingKey(account=$account, bytes=***)"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
- other as UnifiedSpendingKeyJni
+ other as JniUnifiedSpendingKey
if (account != other.account) return false
if (!bytes.contentEquals(other.bytes)) return false
diff --git a/sdk-lib/src/main/rust/lib.rs b/backend-lib/src/main/rust/lib.rs
similarity index 92%
rename from sdk-lib/src/main/rust/lib.rs
rename to backend-lib/src/main/rust/lib.rs
index 121e3457..f9696b6b 100644
--- a/sdk-lib/src/main/rust/lib.rs
+++ b/backend-lib/src/main/rust/lib.rs
@@ -95,7 +95,7 @@ fn block_db(env: &JNIEnv<'_>, fsblockdb_root: JString<'_>) -> Result,
_: JClass<'_>,
) {
@@ -134,7 +134,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initOnLoad(
///
/// Returns 0 if successful, or -1 otherwise.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initBlockMetaDb(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_initBlockMetaDb(
env: JNIEnv<'_>,
_: JClass<'_>,
fsblockdb_root: JString<'_>,
@@ -160,7 +160,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initBlockMe
/// Returns 0 if successful, 1 if the seed must be provided in order to execute the requested
/// migrations, or -1 otherwise.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initDataDb(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_initDataDb(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -197,7 +197,7 @@ fn encode_usk(
let encoded = SecretVec::new(usk.to_bytes(Era::Orchard));
let bytes = env.byte_array_from_slice(encoded.expose_secret())?;
let output = env.new_object(
- "cash/z/ecc/android/sdk/jni/UnifiedSpendingKeyJni",
+ "cash/z/ecc/android/sdk/internal/model/JniUnifiedSpendingKey",
"(I[B)V",
&[
JValue::Int(u32::from(account) as i32),
@@ -241,7 +241,7 @@ fn decode_usk(env: &JNIEnv<'_>, usk: jbyteArray) -> Result,
_: JClass<'_>,
db_data: JString<'_>,
@@ -268,7 +268,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createAccou
/// This should only be used in special cases for implementing wallet recovery; prefer
/// `RustBackend.createAccount` for normal account creation purposes.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccountsTableWithKeys(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_initAccountsTableWithKeys(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -315,7 +315,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccount
/// of the [`UnifiedSpendingKey`] for the newly created account. The caller should store
/// the returned spending key in a secure fashion.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveSpendingKey(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustDerivationTool_deriveSpendingKey(
env: JNIEnv<'_>,
_: JClass<'_>,
seed: jbyteArray,
@@ -340,7 +340,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveS
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedFullViewingKeysFromSeed(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustDerivationTool_deriveUnifiedFullViewingKeysFromSeed(
env: JNIEnv<'_>,
_: JClass<'_>,
seed: jbyteArray,
@@ -379,7 +379,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveU
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedAddressFromSeed(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustDerivationTool_deriveUnifiedAddressFromSeed(
env: JNIEnv<'_>,
_: JClass<'_>,
seed: jbyteArray,
@@ -413,7 +413,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveU
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedAddressFromViewingKey(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustDerivationTool_deriveUnifiedAddressFromViewingKey(
env: JNIEnv<'_>,
_: JClass<'_>,
ufvk_string: JString<'_>,
@@ -445,7 +445,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveU
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedFullViewingKey(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustDerivationTool_deriveUnifiedFullViewingKey(
env: JNIEnv<'_>,
_: JClass<'_>,
usk: jbyteArray,
@@ -467,7 +467,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveU
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initBlocksTable(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_initBlocksTable(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -509,7 +509,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initBlocksT
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getCurrentAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getCurrentAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -553,7 +553,7 @@ impl zcash_address::TryFromRawAddress for UnifiedAddressParser {
/// Returns the transparent receiver within the given Unified Address, if any.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getTransparentReceiverForUnifiedAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getTransparentReceiverForUnifiedAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
ua: JString<'_>,
@@ -593,7 +593,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getTranspar
/// Returns the Sapling receiver within the given Unified Address, if any.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getSaplingReceiverForUnifiedAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getSaplingReceiverForUnifiedAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
ua: JString<'_>,
@@ -623,7 +623,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getSaplingR
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidSpendingKey(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_isValidSpendingKey(
env: JNIEnv<'_>,
_: JClass<'_>,
usk: jbyteArray,
@@ -636,7 +636,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidSpen
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidShieldedAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_isValidShieldedAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
addr: JString<'_>,
@@ -658,7 +658,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidShie
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidTransparentAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_isValidTransparentAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
addr: JString<'_>,
@@ -680,7 +680,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidTran
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidUnifiedAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_isValidUnifiedAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
addr: JString<'_>,
@@ -702,7 +702,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidUnif
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getBalance(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getBalance(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -740,7 +740,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getBalance(
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getVerifiedTransparentBalance(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getVerifiedTransparentBalance(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -778,7 +778,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getVerified
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getTotalTransparentBalance(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getTotalTransparentBalance(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -820,7 +820,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getTotalTra
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getVerifiedBalance(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getVerifiedBalance(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -852,7 +852,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getVerified
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getReceivedMemoAsUtf8(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getReceivedMemoAsUtf8(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -879,7 +879,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getReceived
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getSentMemoAsUtf8(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getSentMemoAsUtf8(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -946,7 +946,7 @@ fn decode_blockmeta(env: &JNIEnv<'_>, obj: JObject<'_>) -> Result,
_: JClass<'_>,
db_cache: JString<'_>,
@@ -978,7 +978,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_writeBlockM
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getLatestHeight(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getLatestHeight(
env: JNIEnv<'_>,
_: JClass<'_>,
fsblockdb_root: JString<'_>,
@@ -1000,7 +1000,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getLatestHe
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_findBlockMetadata(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_findBlockMetadata(
env: JNIEnv<'_>,
_: JClass<'_>,
fsblockdb_root: JString<'_>,
@@ -1023,7 +1023,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_findBlockMe
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_rewindBlockMetadataToHeight(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_rewindBlockMetadataToHeight(
env: JNIEnv<'_>,
_: JClass<'_>,
fsblockdb_root: JString<'_>,
@@ -1046,7 +1046,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_rewindBlock
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_validateCombinedChain(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_validateCombinedChain(
env: JNIEnv<'_>,
_: JClass<'_>,
db_cache: JString<'_>,
@@ -1084,7 +1084,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_validateCom
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getNearestRewindHeight(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getNearestRewindHeight(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -1120,7 +1120,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getNearestR
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_rewindToHeight(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_rewindToHeight(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -1143,7 +1143,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_rewindToHei
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlocks(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_scanBlocks(
env: JNIEnv<'_>,
_: JClass<'_>,
db_cache: JString<'_>,
@@ -1171,7 +1171,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlocks(
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_putUtxo(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_putUtxo(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -1218,7 +1218,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_putUtxo(
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_decryptAndStoreTransaction(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_decryptAndStoreTransaction(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -1271,7 +1271,7 @@ fn zip317_helper(
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_createToAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -1363,7 +1363,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAdd
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAddress(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_shieldToAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
@@ -1454,7 +1454,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
}
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_branchIdForHeight(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_branchIdForHeight(
env: JNIEnv<'_>,
_: JClass<'_>,
height: jlong,
@@ -1499,7 +1499,7 @@ fn parse_network(value: u32) -> Result {
/// - Call [`zcashlc_free_keys`] to free the memory associated with the returned pointer
/// when done using it.
#[no_mangle]
-pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_listTransparentReceivers(
+pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_listTransparentReceivers(
env: JNIEnv<'_>,
_: JClass<'_>,
db_data: JString<'_>,
diff --git a/sdk-lib/src/main/rust/local_rpc_types.rs b/backend-lib/src/main/rust/local_rpc_types.rs
similarity index 100%
rename from sdk-lib/src/main/rust/local_rpc_types.rs
rename to backend-lib/src/main/rust/local_rpc_types.rs
diff --git a/sdk-lib/src/main/rust/utils.rs b/backend-lib/src/main/rust/utils.rs
similarity index 100%
rename from sdk-lib/src/main/rust/utils.rs
rename to backend-lib/src/main/rust/utils.rs
diff --git a/sdk-lib/src/main/rust/utils/exception.rs b/backend-lib/src/main/rust/utils/exception.rs
similarity index 100%
rename from sdk-lib/src/main/rust/utils/exception.rs
rename to backend-lib/src/main/rust/utils/exception.rs
diff --git a/sdk-lib/src/main/rust/utils/target_ndk.rs b/backend-lib/src/main/rust/utils/target_ndk.rs
similarity index 100%
rename from sdk-lib/src/main/rust/utils/target_ndk.rs
rename to backend-lib/src/main/rust/utils/target_ndk.rs
diff --git a/sdk-lib/src/main/rust/utils/trace.rs b/backend-lib/src/main/rust/utils/trace.rs
similarity index 100%
rename from sdk-lib/src/main/rust/utils/trace.rs
rename to backend-lib/src/main/rust/utils/trace.rs
diff --git a/darkside-test-lib/src/androidTest/java/cash/z/ecc/android/sdk/darkside/test/TestWallet.kt b/darkside-test-lib/src/androidTest/java/cash/z/ecc/android/sdk/darkside/test/TestWallet.kt
index 4a521574..d613d2f4 100644
--- a/darkside-test-lib/src/androidTest/java/cash/z/ecc/android/sdk/darkside/test/TestWallet.kt
+++ b/darkside-test-lib/src/androidTest/java/cash/z/ecc/android/sdk/darkside/test/TestWallet.kt
@@ -58,7 +58,7 @@ class TestWallet(
private val context = InstrumentationRegistry.getInstrumentation().context
private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
private val shieldedSpendingKey =
- runBlocking { DerivationTool.deriveUnifiedSpendingKey(seed, network = network, account) }
+ runBlocking { DerivationTool.getInstance().deriveUnifiedSpendingKey(seed, network = network, account) }
val synchronizer: SdkSynchronizer = Synchronizer.newBlocking(
context,
network,
diff --git a/demo-app/src/androidTest/java/cash/z/wallet/sdk/sample/demoapp/SampleCodeTest.kt b/demo-app/src/androidTest/java/cash/z/wallet/sdk/sample/demoapp/SampleCodeTest.kt
index f04b26d4..51bdd8c1 100644
--- a/demo-app/src/androidTest/java/cash/z/wallet/sdk/sample/demoapp/SampleCodeTest.kt
+++ b/demo-app/src/androidTest/java/cash/z/wallet/sdk/sample/demoapp/SampleCodeTest.kt
@@ -178,7 +178,7 @@ class SampleCodeTest {
val amount = 0.123.convertZecToZatoshi()
val address = "ztestsapling1tklsjr0wyw0d58f3p7wufvrj2cyfv6q6caumyueadq8qvqt8lda6v6tpx474rfru9y6u75u7qnw"
val memo = "Test Transaction"
- val spendingKey = DerivationTool.deriveUnifiedSpendingKey(seed, ZcashNetwork.Mainnet, Account.DEFAULT)
+ val spendingKey = DerivationTool.getInstance().deriveUnifiedSpendingKey(seed, ZcashNetwork.Mainnet, Account.DEFAULT)
synchronizer.sendToAddress(spendingKey, amount, address, memo)
}
diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt
index 205a7e09..d675f256 100644
--- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt
+++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/Navigation.kt
@@ -134,7 +134,7 @@ internal fun ComposeActivity.Navigation() {
} else {
Transactions(
synchronizer = synchronizer,
- onBack = { navController.popBackStackJustOnce(SEND) }
+ onBack = { navController.popBackStackJustOnce(TRANSACTIONS) }
)
}
}
diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getaddress/GetAddressFragment.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getaddress/GetAddressFragment.kt
index 550a3921..cd3c9938 100644
--- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getaddress/GetAddressFragment.kt
+++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getaddress/GetAddressFragment.kt
@@ -84,7 +84,7 @@ class GetAddressFragment : BaseDemoFragment() {
override fun onActionButtonClicked() {
viewLifecycleOwner.lifecycleScope.launch {
copyToClipboard(
- DerivationTool.deriveUnifiedAddress(
+ DerivationTool.getInstance().deriveUnifiedAddress(
viewingKey.encoding,
ZcashNetwork.fromResources(requireApplicationContext())
),
diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getbalance/GetBalanceFragment.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getbalance/GetBalanceFragment.kt
index 6a27887e..66fedc13 100644
--- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getbalance/GetBalanceFragment.kt
+++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getbalance/GetBalanceFragment.kt
@@ -69,7 +69,7 @@ class GetBalanceFragment : BaseDemoFragment() {
setOnClickListener {
lifecycleScope.launch {
sharedViewModel.synchronizerFlow.value?.shieldFunds(
- DerivationTool.deriveUnifiedSpendingKey(
+ DerivationTool.getInstance().deriveUnifiedSpendingKey(
seed,
network,
Account.DEFAULT
diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt
index daee4fa0..d6716bb5 100644
--- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt
+++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/demos/getprivatekey/GetPrivateKeyFragment.kt
@@ -47,14 +47,14 @@ class GetPrivateKeyFragment : BaseDemoFragment() {
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
@Suppress("MagicNumber")
- val spendingKey = DerivationTool.deriveUnifiedSpendingKey(
+ val spendingKey = DerivationTool.getInstance().deriveUnifiedSpendingKey(
seed,
ZcashNetwork.fromResources(requireApplicationContext()),
Account(5)
)
// derive the key that allows you to view but not spend transactions
- val viewingKey = DerivationTool.deriveUnifiedFullViewingKey(
+ val viewingKey = DerivationTool.getInstance().deriveUnifiedFullViewingKey(
spendingKey,
ZcashNetwork.fromResources(requireApplicationContext())
)
@@ -91,9 +91,10 @@ class GetPrivateKeyFragment : BaseDemoFragment() {
override fun onActionButtonClicked() {
lifecycleScope.launch {
copyToClipboard(
- DerivationTool.deriveUnifiedFullViewingKeys(
+ DerivationTool.getInstance().deriveUnifiedFullViewingKeys(
seed,
- ZcashNetwork.fromResources(requireApplicationContext())
+ ZcashNetwork.fromResources(requireApplicationContext()),
+ DerivationTool.DEFAULT_NUMBER_OF_ACCOUNTS
).first().encoding,
"UnifiedFullViewingKey copied to clipboard!"
)
diff --git a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ui/screen/home/viewmodel/WalletViewModel.kt b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ui/screen/home/viewmodel/WalletViewModel.kt
index 40a3d7aa..b2715f27 100644
--- a/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ui/screen/home/viewmodel/WalletViewModel.kt
+++ b/demo-app/src/main/java/cash/z/ecc/android/sdk/demoapp/ui/screen/home/viewmodel/WalletViewModel.kt
@@ -90,7 +90,7 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
val bip39Seed = withContext(Dispatchers.IO) {
Mnemonics.MnemonicCode(it.seedPhrase.joinToString()).toSeed()
}
- DerivationTool.deriveUnifiedSpendingKey(
+ DerivationTool.getInstance().deriveUnifiedSpendingKey(
seed = bip39Seed,
network = it.network,
account = Account.DEFAULT
diff --git a/docs/Architecture.md b/docs/Architecture.md
index 4a9940b4..7b60ed50 100644
--- a/docs/Architecture.md
+++ b/docs/Architecture.md
@@ -10,6 +10,7 @@ Thankfully, the only thing an app developer has to be concerned with is the foll
# Modules
The SDK is broken down into several logical components, implemented as Gradle modules. At a high level, the modularization is:
+ * backend-lib — Native library interfaces. This is internal to the SDK.
* sdk-lib — Compiles all of the modules together for the SDK.
* sdk-incubator-lib — Incubator for new APIs that may eventually be promoted to the SDK. Classes are packaged to match the SDK, so that moving them will not necessarily change the API. While SDK clients can use classes in sdk-incubator-lib, they should anticipate a greater amount of public API churn.
* lightwallet-client-lib — Provides a set of Kotlin APIs for interacting with lightwalletd over the network.
@@ -18,6 +19,7 @@ The SDK is broken down into several logical components, implemented as Gradle mo
```mermaid
flowchart TB;
+ backendLib[[backend-lib]] --> sdkLib[[sdk-lib]];
lightwalletClientLib[[lightwallet-client-lib]] --> sdkLib[[sdk-lib]];
sdkLib[[sdk-lib]] --> demoApp[[demo-app]];
sdkLib[[sdk-lib]] --> sdkIncubatorLib[[sdk-incubator-lib]];
@@ -28,9 +30,11 @@ The SDK is broken down into several logical components, implemented as Gradle mo
# Data model
Before diving into some of the module specifics, it is helpful to provide some context on the data model representations as data flows between the different modules of this repository. There are multiple data representations, including:
-1. Network — The wire representation for calls to and from the Lightwalletd server. These are generated by `protoc` at compile time. These are not generally a public API.
-2. Unsafe — The representation provided as the output from `lightwallet-client-lib`. These values are not necessarily validated, hence the smurf naming with the suffix `Unsafe`. These are not generally a public API for clients of the SDK.
-3. SDK — Objects exposed as a public API for clients of the SDK.
+1. Primitive — The JNI generally requires primitive values (e.g. `Int`, `Long`, `String`, `ByteArray`). These are not generally part of the public API.
+2. JNI — Objects that may cross the JNI boundary. These are not generally part of the public API.
+3. Network — The wire representation for calls to and from the Lightwalletd server. These are generated by `protoc` at compile time. These are not generally a public API.
+4. Unsafe — The representation provided as the output from `lightwallet-client-lib`. These values are not necessarily validated, hence the smurf naming with the suffix `Unsafe`. These are not generally a public API for clients of the SDK.
+5. SDK — Objects exposed as a public API for clients of the SDK.
# lightwallet-client-lib
This library is a work-in-progress.
diff --git a/lightwallet-client-lib/build.gradle.kts b/lightwallet-client-lib/build.gradle.kts
index 4674af40..4787f521 100644
--- a/lightwallet-client-lib/build.gradle.kts
+++ b/lightwallet-client-lib/build.gradle.kts
@@ -115,7 +115,8 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)
- // TODO [#673]: Make `implementation` https://github.com/zcash/zcash-android-wallet-sdk/issues/673
+ // TODO [#1030]: Make sdk-lib gRPC objects free (and then make this `implementation`)
+ // TODO [#1030]: https://github.com/zcash/zcash-android-wallet-sdk/issues/1030
api(libs.bundles.grpc)
// Tests
diff --git a/lightwallet-client-lib/lint-baseline.xml b/lightwallet-client-lib/lint-baseline.xml
index 82613e7f..1d527bce 100644
--- a/lightwallet-client-lib/lint-baseline.xml
+++ b/lightwallet-client-lib/lint-baseline.xml
@@ -1,48 +1,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/lightwallet-client-lib/proguard-consumer.txt b/lightwallet-client-lib/proguard-consumer.txt
index 65a66697..903b6131 100644
--- a/lightwallet-client-lib/proguard-consumer.txt
+++ b/lightwallet-client-lib/proguard-consumer.txt
@@ -1,7 +1,3 @@
--keepclasseswithmembernames,includedescriptorclasses class * {
- native ;
-}
-
# https://github.com/grpc/grpc-java/blob/master/android/proguard-rules.txt
-keepclassmembers class io.grpc.okhttp.OkHttpChannelBuilder {
io.grpc.okhttp.OkHttpChannelBuilder forTarget(java.lang.String);
@@ -10,6 +6,9 @@
io.grpc.okhttp.OkHttpChannelBuilder transportExecutor(java.util.concurrent.Executor);
}
+# gRPC related - https://github.com/grpc/grpc-java/issues/6612
+-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
+
# Prevent OKHttp from causing warnings for consumers of the SDK
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
diff --git a/lightwallet-client-lib/src/main/java/co/electriccoin/lightwallet/client/model/CompactBlockUnsafe.kt b/lightwallet-client-lib/src/main/java/co/electriccoin/lightwallet/client/model/CompactBlockUnsafe.kt
index a5ad8f39..3368b453 100644
--- a/lightwallet-client-lib/src/main/java/co/electriccoin/lightwallet/client/model/CompactBlockUnsafe.kt
+++ b/lightwallet-client-lib/src/main/java/co/electriccoin/lightwallet/client/model/CompactBlockUnsafe.kt
@@ -46,7 +46,7 @@ class CompactBlockUnsafe(
}
}
- private data class CompactBlockOutputsCounts(
+ data class CompactBlockOutputsCounts(
val saplingOutputsCount: UInt,
val orchardActionsCount: UInt
)
diff --git a/sdk-incubator-lib/src/main/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt b/sdk-incubator-lib/src/main/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt
index fa5a502b..f01b06d7 100644
--- a/sdk-incubator-lib/src/main/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt
+++ b/sdk-incubator-lib/src/main/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt
@@ -20,7 +20,11 @@ sealed class WalletFixture {
seed: String = seedPhrase,
network: ZcashNetwork,
account: Account = Account.DEFAULT
- ) = DerivationTool.deriveUnifiedSpendingKey(Mnemonics.MnemonicCode(seed).toEntropy(), network, account)
+ ) = DerivationTool.getInstance().deriveUnifiedSpendingKey(
+ Mnemonics.MnemonicCode(seed).toEntropy(),
+ network,
+ account
+ )
@Suppress("MaxLineLength")
object Ben : WalletFixture() {
@@ -34,9 +38,11 @@ sealed class WalletFixture {
ZcashNetwork.ID_TESTNET -> {
BlockHeight.new(zcashNetwork, 2170000L)
}
+
ZcashNetwork.ID_MAINNET -> {
BlockHeight.new(zcashNetwork, 1935000L)
}
+
else -> error("Unknown network $zcashNetwork")
}
@@ -50,6 +56,7 @@ sealed class WalletFixture {
transparent = "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE"
)
}
+
ZcashNetwork.ID_MAINNET -> {
Addresses(
unified =
@@ -59,6 +66,7 @@ sealed class WalletFixture {
transparent = "t1JP7PHu72xHztsZiwH6cye4yvC9Prb3EvQ"
)
}
+
else -> error("Unknown network $zcashNetwork")
}
}
@@ -76,9 +84,11 @@ sealed class WalletFixture {
ZcashNetwork.ID_TESTNET -> {
BlockHeight.new(zcashNetwork, 2170000L)
}
+
ZcashNetwork.ID_MAINNET -> {
BlockHeight.new(zcashNetwork, 1935000L)
}
+
else -> error("Unknown network $zcashNetwork")
}
@@ -92,6 +102,7 @@ sealed class WalletFixture {
transparent = "tmCxJG72RWN66xwPtNgu4iKHpyysGrc7rEg"
)
}
+
ZcashNetwork.ID_MAINNET -> {
Addresses(
unified =
@@ -101,6 +112,7 @@ sealed class WalletFixture {
transparent = "t1duiEGg7b39nfQee3XaTY4f5McqfyJKhBi"
)
}
+
else -> error("Unknown network $zcashNetwork")
}
}
diff --git a/sdk-lib/build.gradle.kts b/sdk-lib/build.gradle.kts
index 4f791379..45b7af76 100644
--- a/sdk-lib/build.gradle.kts
+++ b/sdk-lib/build.gradle.kts
@@ -5,7 +5,6 @@ plugins {
id("org.jetbrains.kotlin.plugin.allopen")
id("org.jetbrains.dokka")
- id("org.mozilla.rust-android-gradle.rust-android")
id("wtf.emulator.gradle")
id("zcash-sdk.emulator-wtf-conventions")
@@ -84,28 +83,9 @@ tasks.dokkaHtml.configure {
}
}
-cargo {
- module = "."
- libname = "zcashwalletsdk"
- targets = listOf(
- "arm",
- "arm64",
- "x86",
- "x86_64"
- )
- apiLevels = mapOf(
- "arm" to 16,
- "arm64" to 21,
- "x86" to 16,
- "x86_64" to 21,
- )
-
- profile = "release"
- prebuiltToolchains = true
-}
-
dependencies {
api(projects.lightwalletClientLib)
+ implementation(projects.backendLib)
implementation(libs.androidx.annotation)
implementation(libs.androidx.appcompat)
@@ -149,22 +129,5 @@ dependencies {
androidTestImplementation(libs.bip39)
}
-tasks {
- /*
- * The Mozilla Rust Gradle plugin caches the native build data under the "target" directory,
- * which does not normally get deleted during a clean. The following task and dependency solves
- * that.
- */
- getByName("clean").dependsOn(create("cleanRustBuildOutput") {
- delete("target")
- })
-}
-
-project.afterEvaluate {
- val cargoTask = tasks.getByName("cargoBuild")
- tasks.getByName("javaPreCompileDebug").dependsOn(cargoTask)
- tasks.getByName("javaPreCompileRelease").dependsOn(cargoTask)
-}
-
fun MinimalExternalModuleDependency.asCoordinateString() =
"${module.group}:${module.name}:${versionConstraint.displayName}"
\ No newline at end of file
diff --git a/sdk-lib/lint-baseline.xml b/sdk-lib/lint-baseline.xml
index 82613e7f..1d527bce 100644
--- a/sdk-lib/lint-baseline.xml
+++ b/sdk-lib/lint-baseline.xml
@@ -1,48 +1,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/sdk-lib/proguard-consumer.txt b/sdk-lib/proguard-consumer.txt
index 1d186adb..e69de29b 100644
--- a/sdk-lib/proguard-consumer.txt
+++ b/sdk-lib/proguard-consumer.txt
@@ -1,24 +0,0 @@
--keepclasseswithmembernames,includedescriptorclasses class * {
- native ;
-}
-
-# https://github.com/grpc/grpc-java/blob/master/android/proguard-rules.txt
--keepclassmembers class io.grpc.okhttp.OkHttpChannelBuilder {
- io.grpc.okhttp.OkHttpChannelBuilder forTarget(java.lang.String);
- io.grpc.okhttp.OkHttpChannelBuilder scheduledExecutorService(java.util.concurrent.ScheduledExecutorService);
- io.grpc.okhttp.OkHttpChannelBuilder sslSocketFactory(javax.net.ssl.SSLSocketFactory);
- io.grpc.okhttp.OkHttpChannelBuilder transportExecutor(java.util.concurrent.Executor);
-}
-# gRPC related - https://github.com/grpc/grpc-java/issues/6612
--keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
-
-# Prevent OKHttp from causing warnings for consumers of the SDK
--dontwarn org.bouncycastle.jsse.BCSSLParameters
--dontwarn org.bouncycastle.jsse.BCSSLSocket
--dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
--dontwarn org.conscrypt.Conscrypt
--dontwarn org.conscrypt.Conscrypt$Version
--dontwarn org.conscrypt.ConscryptHostnameVerifier
--dontwarn org.openjsse.javax.net.ssl.SSLParameters
--dontwarn org.openjsse.javax.net.ssl.SSLSocket
--dontwarn org.openjsse.net.ssl.OpenJSSE
\ No newline at end of file
diff --git a/sdk-lib/proguard-project.txt b/sdk-lib/proguard-project.txt
index 771dd1fc..e2790328 100644
--- a/sdk-lib/proguard-project.txt
+++ b/sdk-lib/proguard-project.txt
@@ -19,6 +19,5 @@
-keep public class cash.z.ecc.android.sdk.db.entity.* { public protected *; }
-keep public class cash.z.ecc.android.sdk.exception.* { public protected *; }
-keep public class cash.z.ecc.android.sdk.ext.* { public protected *; }
--keep public class cash.z.ecc.android.sdk.jni.* { public protected *; }
-keep public class cash.z.ecc.android.sdk.tool.* { public protected *; }
-keep public class cash.z.ecc.android.sdk.type.* { public protected *; }
\ No newline at end of file
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt
index 15bdf174..4ec1ae86 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/db/DatabaseCoordinatorTest.kt
@@ -1,8 +1,9 @@
package cash.z.ecc.android.sdk.db
+import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.internal.db.DatabaseCoordinator
-import cash.z.ecc.android.sdk.internal.ext.existsSuspend
+import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.test.getAppContext
import cash.z.ecc.fixture.DatabaseCacheFilesRootFixture
import cash.z.ecc.fixture.DatabaseNameFixture
@@ -124,28 +125,28 @@ class DatabaseCoordinatorTest {
DatabaseNameFixture.newDbWal(name = DatabaseCoordinator.DB_DATA_NAME)
)
- assertTrue(originalDbFile.existsSuspend())
- assertTrue(originalDbJournalFile.existsSuspend())
- assertTrue(originalDbWalFile.existsSuspend())
+ assertTrue(originalDbFile.exists())
+ assertTrue(originalDbJournalFile.exists())
+ assertTrue(originalDbWalFile.exists())
- assertFalse(expectedDbFile.existsSuspend())
- assertFalse(expectedDbJournalFile.existsSuspend())
- assertFalse(expectedDbWalFile.existsSuspend())
+ assertFalse(expectedDbFile.exists())
+ assertFalse(expectedDbJournalFile.exists())
+ assertFalse(expectedDbWalFile.exists())
dbCoordinator.dataDbFile(
DatabaseNameFixture.TEST_DB_NETWORK,
DatabaseNameFixture.TEST_DB_ALIAS
).also { resultFile ->
- assertTrue(resultFile.existsSuspend())
+ assertTrue(resultFile.exists())
assertEquals(expectedDbFile.absolutePath, resultFile.absolutePath)
- assertTrue(expectedDbFile.existsSuspend())
- assertTrue(expectedDbJournalFile.existsSuspend())
- assertTrue(expectedDbWalFile.existsSuspend())
+ assertTrue(expectedDbFile.exists())
+ assertTrue(expectedDbJournalFile.exists())
+ assertTrue(expectedDbWalFile.exists())
- assertFalse(originalDbFile.existsSuspend())
- assertFalse(originalDbJournalFile.existsSuspend())
- assertFalse(originalDbWalFile.existsSuspend())
+ assertFalse(originalDbFile.exists())
+ assertFalse(originalDbJournalFile.exists())
+ assertFalse(originalDbWalFile.exists())
}
}
@@ -186,14 +187,14 @@ class DatabaseCoordinatorTest {
fileName = DatabaseNameFixture.newDbWal(name = DatabaseCoordinator.DB_DATA_NAME)
)
- assertTrue(dbFile.existsSuspend())
- assertTrue(dbJournalFile.existsSuspend())
- assertTrue(dbWalFile.existsSuspend())
+ assertTrue(dbFile.exists())
+ assertTrue(dbJournalFile.exists())
+ assertTrue(dbWalFile.exists())
dbCoordinator.deleteDatabases(DatabaseNameFixture.TEST_DB_NETWORK, DatabaseNameFixture.TEST_DB_ALIAS).also {
- assertFalse(dbFile.existsSuspend())
- assertFalse(dbJournalFile.existsSuspend())
- assertFalse(dbWalFile.existsSuspend())
+ assertFalse(dbFile.exists())
+ assertFalse(dbJournalFile.exists())
+ assertFalse(dbWalFile.exists())
}
}
@@ -276,13 +277,13 @@ class DatabaseCoordinatorTest {
)
// check all files in place
- assertTrue(olderLegacyDbFile.existsSuspend())
- assertTrue(olderLegacyDbJournalFile.existsSuspend())
- assertTrue(olderLegacyDbWalFile.existsSuspend())
+ assertTrue(olderLegacyDbFile.exists())
+ assertTrue(olderLegacyDbJournalFile.exists())
+ assertTrue(olderLegacyDbWalFile.exists())
- assertTrue(newerLegacyDbFile.existsSuspend())
- assertTrue(newerLegacyDbJournalFile.existsSuspend())
- assertTrue(newerLegacyDbWalFile.existsSuspend())
+ assertTrue(newerLegacyDbFile.exists())
+ assertTrue(newerLegacyDbJournalFile.exists())
+ assertTrue(newerLegacyDbWalFile.exists())
// once we access the latest file system blocks storage root directory, all the legacy database files should
// be removed
@@ -290,13 +291,41 @@ class DatabaseCoordinatorTest {
network = DatabaseNameFixture.TEST_DB_NETWORK,
alias = DatabaseNameFixture.TEST_DB_ALIAS
).also {
- assertFalse(olderLegacyDbFile.existsSuspend())
- assertFalse(olderLegacyDbJournalFile.existsSuspend())
- assertFalse(olderLegacyDbWalFile.existsSuspend())
+ assertFalse(olderLegacyDbFile.exists())
+ assertFalse(olderLegacyDbJournalFile.exists())
+ assertFalse(olderLegacyDbWalFile.exists())
- assertFalse(newerLegacyDbFile.existsSuspend())
- assertFalse(newerLegacyDbJournalFile.existsSuspend())
- assertFalse(newerLegacyDbWalFile.existsSuspend())
+ assertFalse(newerLegacyDbFile.exists())
+ assertFalse(newerLegacyDbJournalFile.exists())
+ assertFalse(newerLegacyDbWalFile.exists())
}
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ @SmallTest
+ fun data_db_path() = runTest {
+ val coordinator = DatabaseCoordinator.getInstance(ApplicationProvider.getApplicationContext())
+ val dataDbFile = coordinator.dataDbFile(ZcashNetwork.Testnet, "TestWallet")
+ assertTrue(
+ "Invalid DataDB file",
+ dataDbFile.absolutePath.endsWith(
+ "no_backup/co.electricoin.zcash/TestWallet_testnet_${DatabaseCoordinator.DB_DATA_NAME}"
+ )
+ )
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ @SmallTest
+ fun cache_path() = runTest {
+ val coordinator = DatabaseCoordinator.getInstance(ApplicationProvider.getApplicationContext())
+ val cache = coordinator.fsBlockDbRoot(ZcashNetwork.Testnet, "TestWallet")
+ assertTrue(
+ "Invalid CacheDB file",
+ cache.absolutePath.endsWith(
+ "no_backup/co.electricoin.zcash/TestWallet_testnet_${DatabaseCoordinator.DB_FS_BLOCK_DB_ROOT_NAME}"
+ )
+ )
+ }
}
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt
index fc404f7e..6cd67ee1 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/fixture/WalletFixture.kt
@@ -1,9 +1,10 @@
package cash.z.ecc.android.sdk.fixture
import cash.z.ecc.android.bip39.Mnemonics
+import cash.z.ecc.android.sdk.internal.deriveUnifiedSpendingKey
+import cash.z.ecc.android.sdk.internal.jni.RustDerivationTool
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.ZcashNetwork
-import cash.z.ecc.android.sdk.tool.DerivationTool
object WalletFixture {
val NETWORK = ZcashNetwork.Mainnet
@@ -14,5 +15,5 @@ object WalletFixture {
seed: String = SEED_PHRASE,
network: ZcashNetwork = NETWORK,
account: Account = Account.DEFAULT
- ) = DerivationTool.deriveUnifiedSpendingKey(Mnemonics.MnemonicCode(seed).toEntropy(), network, account)
+ ) = RustDerivationTool.new().deriveUnifiedSpendingKey(Mnemonics.MnemonicCode(seed).toEntropy(), network, account)
}
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/SynchronizerFactoryTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/SynchronizerFactoryTest.kt
deleted file mode 100644
index 208c44e6..00000000
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/SynchronizerFactoryTest.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package cash.z.ecc.android.sdk.integration
-
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.filters.SmallTest
-import cash.z.ecc.android.sdk.DefaultSynchronizerFactory
-import cash.z.ecc.android.sdk.internal.SaplingParamTool
-import cash.z.ecc.android.sdk.internal.db.DatabaseCoordinator
-import cash.z.ecc.android.sdk.model.ZcashNetwork
-import cash.z.ecc.android.sdk.util.TestWallet
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-class SynchronizerFactoryTest {
-
- @Test
- @SmallTest
- fun testFilePaths() {
- val rustBackend = runBlocking {
- val coordinator = DatabaseCoordinator.getInstance(ApplicationProvider.getApplicationContext())
- DefaultSynchronizerFactory.defaultRustBackend(
- ZcashNetwork.Testnet,
- "TestWallet",
- TestWallet.Backups.SAMPLE_WALLET.testnetBirthday,
- SaplingParamTool.new(ApplicationProvider.getApplicationContext()),
- coordinator
- )
- }
- assertTrue(
- "Invalid DataDB file",
- rustBackend.dataDbFile.absolutePath.endsWith(
- "no_backup/co.electricoin.zcash/TestWallet_testnet_${DatabaseCoordinator.DB_DATA_NAME}"
- )
- )
- assertTrue(
- "Invalid CacheDB file",
- rustBackend.fsBlockDbRoot.absolutePath.endsWith(
- "no_backup/co.electricoin.zcash/TestWallet_testnet_${DatabaseCoordinator.DB_FS_BLOCK_DB_ROOT_NAME}"
- )
- )
- assertTrue(
- "Invalid CacheDB params dir",
- rustBackend.saplingParamDir.endsWith(
- "no_backup/co.electricoin.zcash"
- )
- )
- }
-}
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/TestnetIntegrationTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/TestnetIntegrationTest.kt
index 3ce9249b..be8142ef 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/TestnetIntegrationTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/integration/TestnetIntegrationTest.kt
@@ -101,7 +101,7 @@ class TestnetIntegrationTest : ScopedTest() {
}
private suspend fun sendFunds(): Boolean {
- val spendingKey = DerivationTool.deriveUnifiedSpendingKey(seed, synchronizer.network, Account.DEFAULT)
+ val spendingKey = DerivationTool.getInstance().deriveUnifiedSpendingKey(seed, synchronizer.network, Account.DEFAULT)
log("sending to address")
synchronizer.sendToAddress(
spendingKey,
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/CheckpointTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/CheckpointTest.kt
index 6382a025..a5fb00c1 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/CheckpointTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/CheckpointTest.kt
@@ -2,6 +2,13 @@ package cash.z.ecc.android.sdk.internal
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.internal.model.Checkpoint
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_EPOCH_SECONDS
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_HASH
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_HEIGHT
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_TREE
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_VERSION
+import cash.z.ecc.android.sdk.internal.model.ext.VERSION_1
+import cash.z.ecc.android.sdk.internal.model.ext.from
import cash.z.ecc.fixture.CheckpointFixture
import cash.z.ecc.fixture.toJson
import org.json.JSONObject
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/SaplingParamToolBasicTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/SaplingParamToolBasicTest.kt
index 8aa2d836..61e21c76 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/SaplingParamToolBasicTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/SaplingParamToolBasicTest.kt
@@ -1,5 +1,6 @@
package cash.z.ecc.android.sdk.internal
+import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.MediumTest
import androidx.test.filters.SmallTest
import cash.z.ecc.android.sdk.exception.TransactionEncoderException
@@ -11,6 +12,7 @@ import cash.z.ecc.fixture.SaplingParamsFixture
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
+import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.io.File
@@ -168,4 +170,17 @@ class SaplingParamToolBasicTest {
saplingParamTool.ensureParams(SaplingParamToolFixture.PARAMS_DIRECTORY)
}
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ @SmallTest
+ fun sapling_params_path() = runTest {
+ val paramsDir = SaplingParamTool.new(ApplicationProvider.getApplicationContext()).properties.paramsDirectory
+ Assert.assertTrue(
+ "Invalid CacheDB params dir",
+ paramsDir.endsWith(
+ "no_backup/co.electricoin.zcash"
+ )
+ )
+ }
}
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt
index adb30456..943a7731 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt
@@ -1,10 +1,10 @@
package cash.z.ecc.android.sdk.internal.storage.block
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.ext.deleteRecursivelySuspend
import cash.z.ecc.android.sdk.internal.ext.existsSuspend
import cash.z.ecc.android.sdk.internal.ext.listSuspend
import cash.z.ecc.android.sdk.internal.ext.mkdirsSuspend
-import cash.z.ecc.android.sdk.jni.Backend
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.fixture.FakeRustBackendFixture
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt
index 9fc70f46..8dae2521 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt
@@ -2,6 +2,10 @@ package cash.z.ecc.android.sdk.jni
import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose
+import cash.z.ecc.android.sdk.internal.Backend
+import cash.z.ecc.android.sdk.internal.getBranchIdForHeight
+import cash.z.ecc.android.sdk.internal.jni.RustBackend
+import cash.z.ecc.android.sdk.internal.network
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.runBlocking
@@ -47,21 +51,21 @@ class BranchIdTest internal constructor(
// However, due to quirks on certain devices, we created this test at the Android level,
// as a sanity check
val testnetBackend = runBlocking {
- RustBackend.init(
+ RustBackend.new(
File(""),
File(""),
File(""),
- ZcashNetwork.Testnet,
- ZcashNetwork.Testnet.saplingActivationHeight
+ File(""),
+ ZcashNetwork.Testnet.id,
)
}
val mainnetBackend = runBlocking {
- RustBackend.init(
+ RustBackend.new(
File(""),
File(""),
File(""),
- ZcashNetwork.Mainnet,
- ZcashNetwork.Mainnet.saplingActivationHeight
+ File(""),
+ ZcashNetwork.Mainnet.id,
)
}
return listOf(
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/TransparentTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/TransparentTest.kt
index e62c8502..6d0928a3 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/TransparentTest.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/TransparentTest.kt
@@ -5,8 +5,11 @@ import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode
import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.annotation.MaintainedTest
import cash.z.ecc.android.sdk.annotation.TestPurpose
+import cash.z.ecc.android.sdk.internal.Derivation
+import cash.z.ecc.android.sdk.internal.deriveUnifiedAddress
+import cash.z.ecc.android.sdk.internal.deriveUnifiedFullViewingKeysTypesafe
+import cash.z.ecc.android.sdk.internal.jni.RustDerivationTool
import cash.z.ecc.android.sdk.model.ZcashNetwork
-import cash.z.ecc.android.sdk.tool.DerivationTool
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.BeforeClass
@@ -21,10 +24,15 @@ class TransparentTest(val expected: Expected, val network: ZcashNetwork) {
@Test
fun deriveUnifiedFullViewingKeysFromSeedTest() = runBlocking {
- val ufvks = DerivationTool.deriveUnifiedFullViewingKeys(SEED, network = network)
+ val ufvks = RustDerivationTool.new().deriveUnifiedFullViewingKeysTypesafe(
+ SEED,
+ network = network,
+ numberOfAccounts =
+ Derivation.DEFAULT_NUMBER_OF_ACCOUNTS
+ )
assertEquals(1, ufvks.size)
val ufvk = ufvks.first()
- assertEquals(expected.uAddr, DerivationTool.deriveUnifiedAddress(ufvk.encoding, network = network))
+ assertEquals(expected.uAddr, RustDerivationTool.new().deriveUnifiedAddress(ufvk.encoding, network = network))
// TODO: If we need this, change DerivationTool to derive from the UFVK instead of the public key.
// assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPublicKey(ufvk.encoding,
// network = network))
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/tool/DerivationToolTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/tool/DerivationToolTest.kt
deleted file mode 100644
index bf82256e..00000000
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/tool/DerivationToolTest.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package cash.z.ecc.android.sdk.tool
-
-import cash.z.ecc.android.bip39.Mnemonics
-import cash.z.ecc.android.sdk.fixture.WalletFixture
-import cash.z.ecc.android.sdk.model.Account
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import kotlin.test.assertContentEquals
-
-class DerivationToolTest {
- @Test
- @OptIn(ExperimentalCoroutinesApi::class)
- fun create_spending_key_does_not_mutate_passed_bytes() = runTest {
- val bytesOne = Mnemonics.MnemonicCode(WalletFixture.SEED_PHRASE).toEntropy()
- val bytesTwo = Mnemonics.MnemonicCode(WalletFixture.SEED_PHRASE).toEntropy()
-
- DerivationTool.deriveUnifiedSpendingKey(bytesOne, WalletFixture.NETWORK, Account.DEFAULT)
-
- assertContentEquals(bytesTwo, bytesOne)
- }
-}
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/AddressGeneratorUtil.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/AddressGeneratorUtil.kt
index 58b8c666..73759313 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/AddressGeneratorUtil.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/AddressGeneratorUtil.kt
@@ -1,9 +1,10 @@
package cash.z.ecc.android.sdk.util
+import cash.z.ecc.android.sdk.internal.deriveUnifiedAddress
+import cash.z.ecc.android.sdk.internal.jni.RustDerivationTool
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.test.readFileLinesInFlow
-import cash.z.ecc.android.sdk.tool.DerivationTool
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
@@ -31,7 +32,7 @@ class AddressGeneratorUtil {
.map { seedPhrase ->
mnemonics.toSeed(seedPhrase.toCharArray())
}.map { seed ->
- DerivationTool.deriveUnifiedAddress(seed, ZcashNetwork.Mainnet, Account.DEFAULT)
+ RustDerivationTool.new().deriveUnifiedAddress(seed, ZcashNetwork.Mainnet, Account.DEFAULT)
}.collect { address ->
println("xrxrx2\t$address")
assertTrue(address.startsWith("u1"))
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/TestWallet.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/TestWallet.kt
index bb9494e7..a15ce35a 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/TestWallet.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/util/TestWallet.kt
@@ -6,13 +6,14 @@ import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.internal.Twig
+import cash.z.ecc.android.sdk.internal.deriveUnifiedSpendingKey
+import cash.z.ecc.android.sdk.internal.jni.RustDerivationTool
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.Testnet
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
-import cash.z.ecc.android.sdk.tool.DerivationTool
import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
@@ -58,7 +59,7 @@ class TestWallet(
private val context = InstrumentationRegistry.getInstrumentation().context
private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
private val spendingKey =
- runBlocking { DerivationTool.deriveUnifiedSpendingKey(seed, network = network, account) }
+ runBlocking { RustDerivationTool.new().deriveUnifiedSpendingKey(seed, network = network, account) }
val synchronizer: SdkSynchronizer = Synchronizer.newBlocking(
context,
network,
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/CheckpointFixture.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/CheckpointFixture.kt
index a7781fc5..ba3a83ce 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/CheckpointFixture.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/CheckpointFixture.kt
@@ -1,12 +1,12 @@
package cash.z.ecc.fixture
-import cash.z.ecc.android.sdk.internal.KEY_EPOCH_SECONDS
-import cash.z.ecc.android.sdk.internal.KEY_HASH
-import cash.z.ecc.android.sdk.internal.KEY_HEIGHT
-import cash.z.ecc.android.sdk.internal.KEY_TREE
-import cash.z.ecc.android.sdk.internal.KEY_VERSION
-import cash.z.ecc.android.sdk.internal.VERSION_1
import cash.z.ecc.android.sdk.internal.model.Checkpoint
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_EPOCH_SECONDS
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_HASH
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_HEIGHT
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_TREE
+import cash.z.ecc.android.sdk.internal.model.ext.KEY_VERSION
+import cash.z.ecc.android.sdk.internal.model.ext.VERSION_1
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import org.json.JSONObject
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackend.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackend.kt
index c4a66243..13f6e786 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackend.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackend.kt
@@ -1,15 +1,11 @@
package cash.z.ecc.fixture
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
-import cash.z.ecc.android.sdk.jni.Backend
-import cash.z.ecc.android.sdk.jni.UnifiedSpendingKeyJni
-import cash.z.ecc.android.sdk.model.BlockHeight
-import cash.z.ecc.android.sdk.model.ZcashNetwork
-import java.io.File
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
internal class FakeRustBackend(
- override val network: ZcashNetwork,
- override val saplingParamDir: File,
+ override val networkId: Int,
val metadata: MutableList
) : Backend {
@@ -34,6 +30,17 @@ internal class FakeRustBackend(
TODO("Not yet implemented")
}
+ override suspend fun putUtxo(
+ tAddress: String,
+ txId: ByteArray,
+ index: Int,
+ script: ByteArray,
+ value: Long,
+ height: Long
+ ): Boolean {
+ TODO("Not yet implemented")
+ }
+
override suspend fun findBlockMetadata(height: Long): JniBlockMeta? {
return metadata.findLast { it.height == height }
}
@@ -78,7 +85,7 @@ internal class FakeRustBackend(
override suspend fun initDataDb(seed: ByteArray?): Int =
error("Intentionally not implemented in mocked FakeRustBackend implementation.")
- override suspend fun createAccount(seed: ByteArray): UnifiedSpendingKeyJni =
+ override suspend fun createAccount(seed: ByteArray): JniUnifiedSpendingKey =
error("Intentionally not implemented in mocked FakeRustBackend implementation.")
override fun isValidShieldedAddr(addr: String): Boolean =
@@ -128,13 +135,4 @@ internal class FakeRustBackend(
override suspend fun scanBlocks(limit: Long?): Boolean =
error("Intentionally not implemented in mocked FakeRustBackend implementation.")
-
- override suspend fun putUtxo(
- tAddress: String,
- txId: ByteArray,
- index: Int,
- script: ByteArray,
- value: Long,
- height: BlockHeight
- ): Boolean = error("Intentionally not implemented in mocked FakeRustBackend implementation.")
}
diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackendFixture.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackendFixture.kt
index 89c36988..8fcce255 100644
--- a/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackendFixture.kt
+++ b/sdk-lib/src/androidTest/java/cash/z/ecc/fixture/FakeRustBackendFixture.kt
@@ -2,20 +2,16 @@ package cash.z.ecc.fixture
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.model.ZcashNetwork
-import java.io.File
internal class FakeRustBackendFixture {
- private val DEFAULT_SAPLING_PARAM_DIR = File(DatabasePathFixture.new())
private val DEFAULT_NETWORK = ZcashNetwork.Testnet
fun new(
- saplingParamDir: File = DEFAULT_SAPLING_PARAM_DIR,
network: ZcashNetwork = DEFAULT_NETWORK,
metadata: MutableList = mutableListOf()
) = FakeRustBackend(
- saplingParamDir = saplingParamDir,
- network = network,
+ networkId = network.id,
metadata = metadata
)
}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt
index 5ebdfae4..1aa4d01a 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt
@@ -17,15 +17,17 @@ import cash.z.ecc.android.sdk.exception.TransactionEncoderException
import cash.z.ecc.android.sdk.exception.TransactionSubmitException
import cash.z.ecc.android.sdk.ext.ConsensusBranchId
import cash.z.ecc.android.sdk.ext.ZcashSdk
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.SaplingParamTool
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.block.CompactBlockDownloader
import cash.z.ecc.android.sdk.internal.db.DatabaseCoordinator
import cash.z.ecc.android.sdk.internal.db.derived.DbDerivedDataRepository
import cash.z.ecc.android.sdk.internal.db.derived.DerivedDataDb
+import cash.z.ecc.android.sdk.internal.ext.isNullOrEmpty
import cash.z.ecc.android.sdk.internal.ext.toHexReversed
import cash.z.ecc.android.sdk.internal.ext.tryNull
-import cash.z.ecc.android.sdk.internal.isNullOrEmpty
+import cash.z.ecc.android.sdk.internal.jni.RustBackend
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.repository.CompactBlockRepository
import cash.z.ecc.android.sdk.internal.repository.DerivedDataRepository
@@ -34,7 +36,6 @@ import cash.z.ecc.android.sdk.internal.transaction.OutboundTransactionManager
import cash.z.ecc.android.sdk.internal.transaction.OutboundTransactionManagerImpl
import cash.z.ecc.android.sdk.internal.transaction.TransactionEncoder
import cash.z.ecc.android.sdk.internal.transaction.TransactionEncoderImpl
-import cash.z.ecc.android.sdk.jni.RustBackend
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.PercentDecimal
@@ -69,6 +70,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
+import java.io.File
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.CoroutineContext
@@ -92,7 +94,7 @@ class SdkSynchronizer private constructor(
private val storage: DerivedDataRepository,
private val txManager: OutboundTransactionManager,
val processor: CompactBlockProcessor,
- private val rustBackend: RustBackend
+ private val backend: Backend
) : CloseableSynchronizer {
companion object {
@@ -116,7 +118,7 @@ class SdkSynchronizer private constructor(
repository: DerivedDataRepository,
txManager: OutboundTransactionManager,
processor: CompactBlockProcessor,
- rustBackend: RustBackend
+ backend: Backend
): CloseableSynchronizer {
val synchronizerKey = SynchronizerKey(zcashNetwork, alias)
@@ -128,7 +130,7 @@ class SdkSynchronizer private constructor(
repository,
txManager,
processor,
- rustBackend
+ backend
).apply {
instances[synchronizerKey] = InstanceState.Active
@@ -311,10 +313,10 @@ class SdkSynchronizer private constructor(
return storage.getNoteIds(transactionOverview.id).map {
when (transactionOverview.isSentTransaction) {
true -> {
- rustBackend.getSentMemoAsUtf8(it)
+ backend.getSentMemoAsUtf8(it)
}
false -> {
- rustBackend.getReceivedMemoAsUtf8(it)
+ backend.getReceivedMemoAsUtf8(it)
}
}
}.filterNotNull()
@@ -405,7 +407,6 @@ class SdkSynchronizer private constructor(
lastScanTime = now
SYNCED
}
-
is Stopped -> STOPPED
is Disconnected -> DISCONNECTED
is Syncing, Initialized -> SYNCING
@@ -509,25 +510,25 @@ class SdkSynchronizer private constructor(
// Not ready to be a public API; internal for testing only
internal suspend fun createAccount(seed: ByteArray): UnifiedSpendingKey =
- CompactBlockProcessor.createAccount(rustBackend, seed)
+ CompactBlockProcessor.createAccount(backend, seed)
/**
* Returns the current Unified Address for this account.
*/
override suspend fun getUnifiedAddress(account: Account): String =
- CompactBlockProcessor.getCurrentAddress(rustBackend, account)
+ CompactBlockProcessor.getCurrentAddress(backend, account)
/**
* Returns the legacy Sapling address corresponding to the current Unified Address for this account.
*/
override suspend fun getSaplingAddress(account: Account): String =
- CompactBlockProcessor.getLegacySaplingAddress(rustBackend, account)
+ CompactBlockProcessor.getLegacySaplingAddress(backend, account)
/**
* Returns the legacy transparent address corresponding to the current Unified Address for this account.
*/
override suspend fun getTransparentAddress(account: Account): String =
- CompactBlockProcessor.getTransparentAddress(rustBackend, account)
+ CompactBlockProcessor.getTransparentAddress(backend, account)
@Throws(TransactionEncoderException::class, TransactionSubmitException::class)
override suspend fun sendToAddress(
@@ -557,7 +558,7 @@ class SdkSynchronizer private constructor(
memo: String
): Long {
Twig.debug { "Initializing shielding transaction" }
- val tAddr = CompactBlockProcessor.getTransparentAddress(rustBackend, usk.account)
+ val tAddr = CompactBlockProcessor.getTransparentAddress(backend, usk.account)
val tBalance = processor.getUtxoCacheBalance(tAddr)
val encodedTx = txManager.encode(
@@ -628,26 +629,26 @@ class SdkSynchronizer private constructor(
*/
internal object DefaultSynchronizerFactory {
- internal suspend fun defaultRustBackend(
+ internal suspend fun defaultBackend(
network: ZcashNetwork,
alias: String,
- blockHeight: BlockHeight,
saplingParamTool: SaplingParamTool,
coordinator: DatabaseCoordinator
- ): RustBackend {
- return RustBackend.init(
+ ): Backend {
+ return RustBackend.new(
coordinator.fsBlockDbRoot(network, alias),
coordinator.dataDbFile(network, alias),
- saplingParamTool.properties.paramsDirectory,
- network,
- blockHeight
+ saplingOutputFile = saplingParamTool.outputParamsFile,
+ saplingSpendFile = saplingParamTool.spendParamsFile,
+ zcashNetworkId = network.id
)
}
@Suppress("LongParameterList")
internal suspend fun defaultDerivedDataRepository(
context: Context,
- rustBackend: RustBackend,
+ rustBackend: Backend,
+ databaseFile: File,
zcashNetwork: ZcashNetwork,
checkpoint: Checkpoint,
seed: ByteArray?,
@@ -657,6 +658,7 @@ internal object DefaultSynchronizerFactory {
DerivedDataDb.new(
context,
rustBackend,
+ databaseFile,
zcashNetwork,
checkpoint,
seed,
@@ -664,19 +666,20 @@ internal object DefaultSynchronizerFactory {
)
)
- internal suspend fun defaultFileCompactBlockRepository(rustBackend: RustBackend): CompactBlockRepository =
+ internal suspend fun defaultCompactBlockRepository(blockCacheRoot: File, backend: Backend): CompactBlockRepository =
FileCompactBlockRepository.new(
- rustBackend
+ blockCacheRoot,
+ backend
)
fun defaultService(context: Context, lightWalletEndpoint: LightWalletEndpoint): LightWalletClient =
LightWalletClient.new(context, lightWalletEndpoint)
internal fun defaultEncoder(
- rustBackend: RustBackend,
+ backend: Backend,
saplingParamTool: SaplingParamTool,
repository: DerivedDataRepository
- ): TransactionEncoder = TransactionEncoderImpl(rustBackend, saplingParamTool, repository)
+ ): TransactionEncoder = TransactionEncoderImpl(backend, saplingParamTool, repository)
fun defaultDownloader(
service: LightWalletClient,
@@ -689,19 +692,20 @@ internal object DefaultSynchronizerFactory {
): OutboundTransactionManager {
return OutboundTransactionManagerImpl.new(
encoder,
- service,
+ service
)
}
internal fun defaultProcessor(
- rustBackend: RustBackend,
+ backend: Backend,
downloader: CompactBlockDownloader,
- repository: DerivedDataRepository
+ repository: DerivedDataRepository,
+ birthdayHeight: BlockHeight
): CompactBlockProcessor = CompactBlockProcessor(
downloader,
repository,
- rustBackend,
- rustBackend.birthdayHeight
+ backend,
+ birthdayHeight
)
}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt
index b366edf6..97244e24 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt
@@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk
import android.content.Context
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.ext.ZcashSdk
+import cash.z.ecc.android.sdk.internal.Derivation
import cash.z.ecc.android.sdk.internal.SaplingParamTool
import cash.z.ecc.android.sdk.internal.db.DatabaseCoordinator
import cash.z.ecc.android.sdk.model.Account
@@ -412,7 +413,7 @@ interface Synchronizer {
* If customized initialization is required (e.g. for dependency injection or testing), see
* [DefaultSynchronizerFactory].
*/
- @Suppress("LongParameterList")
+ @Suppress("LongParameterList", "LongMethod")
suspend fun new(
context: Context,
zcashNetwork: ZcashNetwork,
@@ -437,29 +438,29 @@ interface Synchronizer {
// The pending transaction database no longer exists, so we can delete the file
coordinator.deletePendingTransactionDatabase(zcashNetwork, alias)
- val rustBackend = DefaultSynchronizerFactory.defaultRustBackend(
+ val backend = DefaultSynchronizerFactory.defaultBackend(
zcashNetwork,
alias,
- loadedCheckpoint.height,
saplingParamTool,
coordinator
)
val blockStore =
DefaultSynchronizerFactory
- .defaultFileCompactBlockRepository(rustBackend)
+ .defaultCompactBlockRepository(coordinator.fsBlockDbRoot(zcashNetwork, alias), backend)
val viewingKeys = seed?.let {
- DerivationTool.deriveUnifiedFullViewingKeys(
+ DerivationTool.getInstance().deriveUnifiedFullViewingKeys(
seed,
zcashNetwork,
- 1
+ Derivation.DEFAULT_NUMBER_OF_ACCOUNTS
).toList()
} ?: emptyList()
val repository = DefaultSynchronizerFactory.defaultDerivedDataRepository(
applicationContext,
- rustBackend,
+ backend,
+ coordinator.dataDbFile(zcashNetwork, alias),
zcashNetwork,
loadedCheckpoint,
seed,
@@ -467,10 +468,19 @@ interface Synchronizer {
)
val service = DefaultSynchronizerFactory.defaultService(applicationContext, lightWalletEndpoint)
- val encoder = DefaultSynchronizerFactory.defaultEncoder(rustBackend, saplingParamTool, repository)
+ val encoder = DefaultSynchronizerFactory.defaultEncoder(backend, saplingParamTool, repository)
val downloader = DefaultSynchronizerFactory.defaultDownloader(service, blockStore)
- val txManager = DefaultSynchronizerFactory.defaultTxManager(encoder, service)
- val processor = DefaultSynchronizerFactory.defaultProcessor(rustBackend, downloader, repository)
+ val txManager = DefaultSynchronizerFactory.defaultTxManager(
+ encoder,
+ service
+ )
+ val processor = DefaultSynchronizerFactory.defaultProcessor(
+ backend,
+ downloader,
+ repository,
+ birthday
+ ?: zcashNetwork.saplingActivationHeight
+ )
return SdkSynchronizer.new(
zcashNetwork,
@@ -478,7 +488,7 @@ interface Synchronizer {
repository,
txManager,
processor,
- rustBackend
+ backend
)
}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt
index dcb8d076..a5872fba 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt
@@ -14,30 +14,31 @@ import cash.z.ecc.android.sdk.ext.BatchMetrics
import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.ZcashSdk.MAX_BACKOFF_INTERVAL
import cash.z.ecc.android.sdk.ext.ZcashSdk.POLL_INTERVAL
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.block.CompactBlockDownloader
+import cash.z.ecc.android.sdk.internal.createAccountAndGetSpendingKey
+import cash.z.ecc.android.sdk.internal.ext.isNullOrEmpty
+import cash.z.ecc.android.sdk.internal.ext.length
import cash.z.ecc.android.sdk.internal.ext.retryUpTo
import cash.z.ecc.android.sdk.internal.ext.retryWithBackoff
import cash.z.ecc.android.sdk.internal.ext.toHexReversed
-import cash.z.ecc.android.sdk.internal.isNullOrEmpty
-import cash.z.ecc.android.sdk.internal.length
+import cash.z.ecc.android.sdk.internal.getBalance
+import cash.z.ecc.android.sdk.internal.getBranchIdForHeight
+import cash.z.ecc.android.sdk.internal.getCurrentAddress
+import cash.z.ecc.android.sdk.internal.getDownloadedUtxoBalance
+import cash.z.ecc.android.sdk.internal.getNearestRewindHeight
+import cash.z.ecc.android.sdk.internal.getVerifiedBalance
+import cash.z.ecc.android.sdk.internal.listTransparentReceivers
import cash.z.ecc.android.sdk.internal.model.BlockBatch
import cash.z.ecc.android.sdk.internal.model.DbTransactionOverview
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.model.ext.from
import cash.z.ecc.android.sdk.internal.model.ext.toBlockHeight
+import cash.z.ecc.android.sdk.internal.network
import cash.z.ecc.android.sdk.internal.repository.DerivedDataRepository
-import cash.z.ecc.android.sdk.jni.Backend
-import cash.z.ecc.android.sdk.jni.createAccountAndGetSpendingKey
-import cash.z.ecc.android.sdk.jni.getBalance
-import cash.z.ecc.android.sdk.jni.getBranchIdForHeight
-import cash.z.ecc.android.sdk.jni.getCurrentAddress
-import cash.z.ecc.android.sdk.jni.getDownloadedUtxoBalance
-import cash.z.ecc.android.sdk.jni.getNearestRewindHeight
-import cash.z.ecc.android.sdk.jni.getVerifiedBalance
-import cash.z.ecc.android.sdk.jni.listTransparentReceivers
-import cash.z.ecc.android.sdk.jni.rewindToHeight
-import cash.z.ecc.android.sdk.jni.validateCombinedChainOrErrorBlockHeight
+import cash.z.ecc.android.sdk.internal.rewindToHeight
+import cash.z.ecc.android.sdk.internal.validateCombinedChainOrErrorBlockHeight
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.PercentDecimal
@@ -80,7 +81,7 @@ import kotlin.time.Duration.Companion.days
* @property downloader the component responsible for downloading compact blocks and persisting them
* locally for processing.
* @property repository the repository holding transaction information.
- * @property rustBackend the librustzcash functionality available and exposed to the SDK.
+ * @property backend the librustzcash functionality available and exposed to the SDK.
* @param minimumHeight the lowest height that we could care about. This is mostly used during
* reorgs as a backstop to make sure we do not rewind beyond sapling activation. It also is factored
* in when considering initial range to download. In most cases, this should be the birthday height
@@ -91,8 +92,8 @@ import kotlin.time.Duration.Companion.days
class CompactBlockProcessor internal constructor(
val downloader: CompactBlockDownloader,
private val repository: DerivedDataRepository,
- private val rustBackend: Backend,
- minimumHeight: BlockHeight = rustBackend.network.saplingActivationHeight
+ private val backend: Backend,
+ minimumHeight: BlockHeight
) {
/**
* Callback for any non-trivial errors that occur while processing compact blocks.
@@ -130,9 +131,15 @@ class CompactBlockProcessor internal constructor(
var onScanMetricCompleteListener: ((BatchMetrics, Boolean) -> Unit)? = null
private val consecutiveChainErrors = AtomicInteger(0)
+
+ /**
+ * The zcash network that is being processed. Either Testnet or Mainnet.
+ */
+ val network = backend.network
+
private val lowerBoundHeight: BlockHeight = BlockHeight(
max(
- rustBackend.network.saplingActivationHeight.value,
+ network.saplingActivationHeight.value,
minimumHeight.value - MAX_REORG_SIZE
)
)
@@ -152,11 +159,6 @@ class CompactBlockProcessor internal constructor(
*/
private val _birthdayHeight = MutableStateFlow(lowerBoundHeight)
- /**
- * The zcash network that is being processed. Either Testnet or Mainnet.
- */
- val network = rustBackend.network
-
/**
* The flow of state values so that a wallet can monitor the state of this class without needing
* to poll.
@@ -357,7 +359,7 @@ class CompactBlockProcessor internal constructor(
// Sync
var syncResult: BlockProcessingResult = BlockProcessingResult.Success
syncNewBlocks(
- backend = rustBackend,
+ backend = backend,
downloader = downloader,
repository = repository,
network = network,
@@ -508,7 +510,7 @@ class CompactBlockProcessor internal constructor(
is Response.Success -> {
runCatching {
Twig.debug { "decrypting and storing transaction (id:$id block:$minedHeight)" }
- rustBackend.decryptAndStoreTransaction(response.result.data)
+ backend.decryptAndStoreTransaction(response.result.data)
}.onSuccess {
Twig.debug { "DONE: enhancing transaction (id:$id block:$minedHeight)" }
}.onFailure { error ->
@@ -548,9 +550,9 @@ class CompactBlockProcessor internal constructor(
} else {
val clientBranch = "%x".format(
Locale.ROOT,
- rustBackend.getBranchIdForHeight(serverBlockHeight)
+ backend.getBranchIdForHeight(serverBlockHeight)
)
- val network = rustBackend.network.networkName
+ val network = backend.network.networkName
if (!clientBranch.equals(info.consensusBranchId, true)) {
MismatchedNetwork(
@@ -610,7 +612,7 @@ class CompactBlockProcessor internal constructor(
@Suppress("TooGenericExceptionCaught")
try {
retryUpTo(3) {
- val tAddresses = rustBackend.listTransparentReceivers(account)
+ val tAddresses = backend.listTransparentReceivers(account)
downloader.lightWalletClient.fetchUtxos(
tAddresses,
@@ -689,13 +691,13 @@ class CompactBlockProcessor internal constructor(
// TODO [#920]: Tweak RustBackend public APIs to have void return values.
// TODO [#920]: Thus, we don't need to check the boolean result of this call until fixed.
// TODO [#920]: https://github.com/zcash/zcash-android-wallet-sdk/issues/920
- rustBackend.putUtxo(
+ backend.putUtxo(
utxo.address,
utxo.txid,
utxo.index,
utxo.script,
utxo.valueZat,
- BlockHeight(utxo.height)
+ utxo.height
)
true
} catch (t: Throwable) {
@@ -1071,7 +1073,7 @@ class CompactBlockProcessor internal constructor(
// tricky: subtract one because we delete ABOVE this block
// This could create an invalid height if if height was saplingActivationHeight
val rewindHeight = BlockHeight(height.value - 1)
- rustBackend.getNearestRewindHeight(rewindHeight)
+ backend.getNearestRewindHeight(rewindHeight)
}
}
@@ -1109,10 +1111,10 @@ class CompactBlockProcessor internal constructor(
if (null == lastSyncedHeight && targetHeight < lastLocalBlock) {
Twig.debug { "Rewinding because targetHeight is less than lastLocalBlock." }
- rustBackend.rewindToHeight(targetHeight)
+ backend.rewindToHeight(targetHeight)
} else if (null != lastSyncedHeight && targetHeight < lastSyncedHeight) {
Twig.debug { "Rewinding because targetHeight is less than lastSyncedHeight." }
- rustBackend.rewindToHeight(targetHeight)
+ backend.rewindToHeight(targetHeight)
} else {
Twig.debug {
"Not rewinding dataDb because the last synced height is $lastSyncedHeight and the" +
@@ -1265,7 +1267,7 @@ class CompactBlockProcessor internal constructor(
}
return buildList {
add(lowerBoundHeight)
- add(rustBackend.network.saplingActivationHeight)
+ add(backend.network.saplingActivationHeight)
oldestTransactionHeight?.let { add(it) }
}.maxOf { it }
}
@@ -1280,9 +1282,9 @@ class CompactBlockProcessor internal constructor(
suspend fun getBalanceInfo(account: Account): WalletBalance {
@Suppress("TooGenericExceptionCaught")
return try {
- val balanceTotal = rustBackend.getBalance(account)
+ val balanceTotal = backend.getBalance(account)
Twig.debug { "found total balance: $balanceTotal" }
- val balanceAvailable = rustBackend.getVerifiedBalance(account)
+ val balanceAvailable = backend.getVerifiedBalance(account)
Twig.debug { "found available balance: $balanceAvailable" }
WalletBalance(balanceTotal, balanceAvailable)
} catch (t: Throwable) {
@@ -1292,7 +1294,7 @@ class CompactBlockProcessor internal constructor(
}
suspend fun getUtxoCacheBalance(address: String): WalletBalance =
- rustBackend.getDownloadedUtxoBalance(address)
+ backend.getDownloadedUtxoBalance(address)
/**
* Transmits the given state for this processor.
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/BackendExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/BackendExt.kt
similarity index 83%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/BackendExt.kt
rename to sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/BackendExt.kt
index fc794d61..c84cbf11 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/BackendExt.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/BackendExt.kt
@@ -1,8 +1,7 @@
@file:Suppress("TooManyFunctions")
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal
-import cash.z.ecc.android.sdk.internal.SdkDispatchers
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.model.Account
@@ -11,9 +10,13 @@ import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
+import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.tool.DerivationTool
import kotlinx.coroutines.withContext
+internal val Backend.network: ZcashNetwork
+ get() = ZcashNetwork.from(networkId)
+
internal suspend fun Backend.initAccountsTable(vararg keys: UnifiedFullViewingKey): Boolean {
val ufvks = Array(keys.size) { keys[it].encoding }
@@ -21,14 +24,11 @@ internal suspend fun Backend.initAccountsTable(vararg keys: UnifiedFullViewingKe
return initAccountsTable(*ufvks)
}
-internal suspend fun Backend.initAccountsTable(
+internal suspend fun Backend.initAccountsTableTypesafe(
seed: ByteArray,
numberOfAccounts: Int
-): Array {
- return DerivationTool.deriveUnifiedFullViewingKeys(seed, network, numberOfAccounts).apply {
- @Suppress("SpreadOperator")
- initAccountsTable(*this)
- }
+): List {
+ return DerivationTool.getInstance().deriveUnifiedFullViewingKeys(seed, network, numberOfAccounts)
}
internal suspend fun Backend.initBlocksTable(checkpoint: Checkpoint): Boolean = initBlocksTable(
@@ -80,7 +80,7 @@ internal suspend fun Backend.getVerifiedBalance(account: Account): Zatoshi = Zat
)
internal suspend fun Backend.getNearestRewindHeight(height: BlockHeight): BlockHeight = BlockHeight.new(
- network,
+ ZcashNetwork.from(networkId),
getNearestRewindHeight(height.value)
)
@@ -88,7 +88,7 @@ internal suspend fun Backend.rewindToHeight(height: BlockHeight): Boolean = rewi
internal suspend fun Backend.getLatestBlockHeight(): BlockHeight? = getLatestHeight()?.let {
BlockHeight.new(
- network,
+ ZcashNetwork.from(networkId),
it
)
}
@@ -106,7 +106,7 @@ internal suspend fun Backend.rewindBlockMetadataToHeight(height: BlockHeight) =
internal suspend fun Backend.validateCombinedChainOrErrorBlockHeight(limit: Long?): BlockHeight? =
validateCombinedChainOrErrorHeight(limit)?.let {
BlockHeight.new(
- network,
+ ZcashNetwork.from(networkId),
it
)
}
@@ -124,3 +124,20 @@ internal suspend fun Backend.getDownloadedUtxoBalance(address: String): WalletBa
}
return WalletBalance(Zatoshi(total), Zatoshi(verified))
}
+
+@Suppress("LongParameterList")
+internal suspend fun Backend.putUtxo(
+ tAddress: String,
+ txId: ByteArray,
+ index: Int,
+ script: ByteArray,
+ value: Long,
+ height: BlockHeight
+): Boolean = putUtxo(
+ tAddress,
+ txId,
+ index,
+ script,
+ value,
+ height.value
+)
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/DerivationToolExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/DerivationToolExt.kt
new file mode 100644
index 00000000..4b66ed61
--- /dev/null
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/DerivationToolExt.kt
@@ -0,0 +1,44 @@
+package cash.z.ecc.android.sdk.internal
+
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
+import cash.z.ecc.android.sdk.model.Account
+import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
+import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
+import cash.z.ecc.android.sdk.model.ZcashNetwork
+
+fun Derivation.deriveUnifiedAddress(
+ seed: ByteArray,
+ network: ZcashNetwork,
+ account: Account
+): String = deriveUnifiedAddress(seed, network.id, account.value)
+
+fun Derivation.deriveUnifiedAddress(
+ viewingKey: String,
+ network: ZcashNetwork,
+): String = deriveUnifiedAddress(viewingKey, network.id)
+
+fun Derivation.deriveUnifiedSpendingKey(
+ seed: ByteArray,
+ network: ZcashNetwork,
+ account: Account
+): UnifiedSpendingKey = UnifiedSpendingKey(deriveUnifiedSpendingKey(seed, network.id, account.value))
+
+fun Derivation.deriveUnifiedFullViewingKey(
+ usk: UnifiedSpendingKey,
+ network: ZcashNetwork
+): UnifiedFullViewingKey = UnifiedFullViewingKey(
+ deriveUnifiedFullViewingKey(
+ JniUnifiedSpendingKey(
+ usk.account.value,
+ usk.copyBytes()
+ ),
+ network.id
+ )
+)
+
+fun Derivation.deriveUnifiedFullViewingKeysTypesafe(
+ seed: ByteArray,
+ network: ZcashNetwork,
+ numberOfAccounts: Int
+): List =
+ deriveUnifiedFullViewingKeys(seed, network.id, numberOfAccounts).map { UnifiedFullViewingKey(it) }
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt
index 31cdba9f..3256e5fc 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt
@@ -21,6 +21,13 @@ import java.nio.channels.Channels
import kotlin.time.Duration.Companion.milliseconds
internal class SaplingParamTool(val properties: SaplingParamToolProperties) {
+
+ val spendParamsFile: File
+ get() = File(properties.paramsDirectory, SPEND_PARAM_FILE_NAME)
+
+ val outputParamsFile: File
+ get() = File(properties.paramsDirectory, OUTPUT_PARAM_FILE_NAME)
+
companion object {
/**
* Maximum file size for the sapling spend params - 50MB
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/Sma.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/Sma.kt
deleted file mode 100644
index bacdafc5..00000000
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/Sma.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package cash.z.ecc.android.sdk.internal
-
-/**
- * Simple implementation of Simple moving average.
- */
-class Sma(val window: Int = 3) {
- private val values = Array(window) { 0.0 }
- var average = 0.0
- private set
-
- var count: Int = 0
- var index: Int = 0
-
- fun add(value: Number) = add(value.toDouble())
-
- fun add(value: Double): Double {
- when {
- // full window
- count == window -> {
- index = (index + 1) % window
- average += ((value - values[index]) / count.toFloat())
- values[index] = value
- }
- // partially-filled window
- count != 0 -> {
- index = (index + 1) % window
- average = ((value + count.toFloat() * average) / (count + 1).toFloat())
- values[index] = value
- count++
- }
- // empty window
- else -> {
- // simply assign given value as current average:
- average = value
- values[0] = value
- count = 1
- }
- }
- return average
- }
-
- fun format(places: Int = 0) = "%.${places}f".format(average)
-}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SuspendingLazy.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SuspendingLazy.kt
new file mode 100644
index 00000000..8553dee9
--- /dev/null
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SuspendingLazy.kt
@@ -0,0 +1,28 @@
+package cash.z.ecc.android.sdk.internal
+
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+/**
+ * Implements a coroutines-friendly lazy singleton pattern with an input argument.
+ *
+ * This class is thread-safe.
+ */
+internal class SuspendingLazy(private val deferredCreator: suspend ((Input) -> Output)) {
+ private var singletonInstance: Output? = null
+
+ private val mutex = Mutex()
+
+ suspend fun getInstance(input: Input): Output {
+ mutex.withLock {
+ singletonInstance?.let {
+ return it
+ }
+
+ val newInstance = deferredCreator(input)
+ singletonInstance = newInstance
+
+ return newInstance
+ }
+ }
+}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/TypesafeBackend.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt
similarity index 95%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/TypesafeBackend.kt
rename to sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt
index 72439c29..5018372f 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/TypesafeBackend.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt
@@ -1,4 +1,4 @@
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
@@ -16,7 +16,7 @@ internal interface TypesafeBackend {
suspend fun initAccountsTable(
seed: ByteArray,
numberOfAccounts: Int
- ): Array
+ ): List
suspend fun initBlocksTable(checkpoint: Checkpoint): Boolean
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/TypesafeBackendImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt
similarity index 90%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/TypesafeBackendImpl.kt
rename to sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt
index 9c958c39..08b7e1cc 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/TypesafeBackendImpl.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt
@@ -1,4 +1,4 @@
-package cash.z.ecc.android.sdk.jni
+package cash.z.ecc.android.sdk.internal
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
@@ -8,6 +8,7 @@ import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi
+// This class is currently unused, although the goal is to swap out usages of BackendExt for this throughout the SDK.
@Suppress("TooManyFunctions")
internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBackend {
override suspend fun initAccountsTable(vararg keys: UnifiedFullViewingKey): Boolean =
@@ -16,7 +17,7 @@ internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBacke
override suspend fun initAccountsTable(
seed: ByteArray,
numberOfAccounts: Int
- ): Array = backend.initAccountsTable(seed, numberOfAccounts)
+ ): List = backend.initAccountsTableTypesafe(seed, numberOfAccounts)
override suspend fun initBlocksTable(checkpoint: Checkpoint): Boolean = backend.initBlocksTable(checkpoint)
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeDerivationToolImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeDerivationToolImpl.kt
new file mode 100644
index 00000000..c3f7755e
--- /dev/null
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeDerivationToolImpl.kt
@@ -0,0 +1,38 @@
+package cash.z.ecc.android.sdk.internal
+
+import cash.z.ecc.android.sdk.model.Account
+import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
+import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
+import cash.z.ecc.android.sdk.model.ZcashNetwork
+import cash.z.ecc.android.sdk.tool.DerivationTool
+
+internal class TypesafeDerivationToolImpl(private val derivation: Derivation) : DerivationTool {
+
+ override suspend fun deriveUnifiedFullViewingKeys(
+ seed: ByteArray,
+ network: ZcashNetwork,
+ numberOfAccounts: Int
+ ): List = derivation.deriveUnifiedFullViewingKeysTypesafe(seed, network, numberOfAccounts)
+
+ override suspend fun deriveUnifiedFullViewingKey(
+ usk: UnifiedSpendingKey,
+ network: ZcashNetwork
+ ): UnifiedFullViewingKey = derivation.deriveUnifiedFullViewingKey(usk, network)
+
+ override suspend fun deriveUnifiedSpendingKey(
+ seed: ByteArray,
+ network: ZcashNetwork,
+ account: Account
+ ): UnifiedSpendingKey = derivation.deriveUnifiedSpendingKey(seed, network, account)
+
+ override suspend fun deriveUnifiedAddress(
+ seed: ByteArray,
+ network: ZcashNetwork,
+ account: Account
+ ): String = derivation.deriveUnifiedAddress(seed, network, account)
+
+ override suspend fun deriveUnifiedAddress(
+ viewingKey: String,
+ network: ZcashNetwork,
+ ): String = derivation.deriveUnifiedAddress(viewingKey, network)
+}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt
index c3e806f4..4dd98a18 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt
@@ -2,17 +2,18 @@ package cash.z.ecc.android.sdk.internal.db.derived
import android.content.Context
import androidx.sqlite.db.SupportSQLiteDatabase
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.NoBackupContextWrapper
import cash.z.ecc.android.sdk.internal.db.ReadOnlySupportSqliteOpenHelper
import cash.z.ecc.android.sdk.internal.ext.tryWarn
+import cash.z.ecc.android.sdk.internal.initAccountsTable
+import cash.z.ecc.android.sdk.internal.initBlocksTable
import cash.z.ecc.android.sdk.internal.model.Checkpoint
-import cash.z.ecc.android.sdk.jni.RustBackend
-import cash.z.ecc.android.sdk.jni.initAccountsTable
-import cash.z.ecc.android.sdk.jni.initBlocksTable
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import java.io.File
internal class DerivedDataDb private constructor(
zcashNetwork: ZcashNetwork,
@@ -42,13 +43,14 @@ internal class DerivedDataDb private constructor(
@Suppress("LongParameterList", "SpreadOperator")
suspend fun new(
context: Context,
- rustBackend: RustBackend,
+ backend: Backend,
+ databaseFile: File,
zcashNetwork: ZcashNetwork,
checkpoint: Checkpoint,
seed: ByteArray?,
viewingKeys: List
): DerivedDataDb {
- rustBackend.initDataDb(seed)
+ backend.initDataDb(seed)
// TODO [#681]: consider converting these to typed exceptions in the welding layer
// TODO [#681]: https://github.com/zcash/zcash-android-wallet-sdk/issues/681
@@ -56,22 +58,22 @@ internal class DerivedDataDb private constructor(
"Did not initialize the blocks table. It probably was already initialized.",
ifContains = "table is not empty"
) {
- rustBackend.initBlocksTable(checkpoint)
+ backend.initBlocksTable(checkpoint)
}
tryWarn(
"Did not initialize the accounts table. It probably was already initialized.",
ifContains = "table is not empty"
) {
- rustBackend.initAccountsTable(*viewingKeys.toTypedArray())
+ backend.initAccountsTable(*viewingKeys.toTypedArray())
}
val database = ReadOnlySupportSqliteOpenHelper.openExistingDatabaseAsReadOnly(
NoBackupContextWrapper(
context,
- rustBackend.dataDbFile.parentFile!!
+ databaseFile.parentFile!!
),
- rustBackend.dataDbFile,
+ databaseFile,
DATABASE_VERSION
)
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ClosedRangeExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/ClosedRangeExt.kt
similarity index 86%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ClosedRangeExt.kt
rename to sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/ClosedRangeExt.kt
index ccffce85..c851005e 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ClosedRangeExt.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/ext/ClosedRangeExt.kt
@@ -1,4 +1,4 @@
-package cash.z.ecc.android.sdk.internal
+package cash.z.ecc.android.sdk.internal.ext
import cash.z.ecc.android.sdk.model.BlockHeight
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMetaExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMetaExt.kt
new file mode 100644
index 00000000..5a0950da
--- /dev/null
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/JniBlockMetaExt.kt
@@ -0,0 +1,17 @@
+package cash.z.ecc.android.sdk.internal.model
+
+import cash.z.wallet.sdk.internal.rpc.CompactFormats
+import co.electriccoin.lightwallet.client.model.CompactBlockUnsafe
+
+internal fun JniBlockMeta.Companion.new(
+ block: CompactFormats.CompactBlock,
+ outputs: CompactBlockUnsafe.CompactBlockOutputsCounts
+): JniBlockMeta {
+ return JniBlockMeta(
+ height = block.height,
+ hash = block.hash.toByteArray(),
+ time = block.time.toLong(),
+ saplingOutputsCount = outputs.saplingOutputsCount.toLong(),
+ orchardOutputsCount = outputs.orchardActionsCount.toLong()
+ )
+}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/CheckpointExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/ext/CheckpointExt.kt
similarity index 97%
rename from sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/CheckpointExt.kt
rename to sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/ext/CheckpointExt.kt
index c526e0e3..7cc1f493 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/CheckpointExt.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/model/ext/CheckpointExt.kt
@@ -1,4 +1,4 @@
-package cash.z.ecc.android.sdk.internal
+package cash.z.ecc.android.sdk.internal.model.ext
import cash.z.ecc.android.sdk.internal.model.Checkpoint
import cash.z.ecc.android.sdk.model.BlockHeight
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt
index c3a62cdb..eb41f084 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt
@@ -1,6 +1,7 @@
package cash.z.ecc.android.sdk.internal.storage.block
import androidx.annotation.VisibleForTesting
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.internal.ext.createNewFileSuspend
import cash.z.ecc.android.sdk.internal.ext.deleteRecursivelySuspend
@@ -12,13 +13,11 @@ import cash.z.ecc.android.sdk.internal.ext.mkdirsSuspend
import cash.z.ecc.android.sdk.internal.ext.renameToSuspend
import cash.z.ecc.android.sdk.internal.ext.toHexReversed
import cash.z.ecc.android.sdk.internal.ext.writeBytesSuspend
+import cash.z.ecc.android.sdk.internal.findBlockMetadata
+import cash.z.ecc.android.sdk.internal.getLatestBlockHeight
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.repository.CompactBlockRepository
-import cash.z.ecc.android.sdk.jni.Backend
-import cash.z.ecc.android.sdk.jni.RustBackend
-import cash.z.ecc.android.sdk.jni.findBlockMetadata
-import cash.z.ecc.android.sdk.jni.getLatestBlockHeight
-import cash.z.ecc.android.sdk.jni.rewindBlockMetadataToHeight
+import cash.z.ecc.android.sdk.internal.rewindBlockMetadataToHeight
import cash.z.ecc.android.sdk.model.BlockHeight
import co.electriccoin.lightwallet.client.model.CompactBlockUnsafe
import kotlinx.coroutines.flow.Flow
@@ -26,12 +25,12 @@ import java.io.File
internal class FileCompactBlockRepository(
private val blocksDirectory: File,
- private val rustBackend: Backend
+ private val backend: Backend
) : CompactBlockRepository {
- override suspend fun getLatestHeight() = rustBackend.getLatestBlockHeight()
+ override suspend fun getLatestHeight() = backend.getLatestBlockHeight()
- override suspend fun findCompactBlock(height: BlockHeight) = rustBackend.findBlockMetadata(height)
+ override suspend fun findCompactBlock(height: BlockHeight) = backend.findBlockMetadata(height)
override suspend fun write(blocks: Flow): List {
val processingBlocks = mutableListOf()
@@ -66,11 +65,11 @@ internal class FileCompactBlockRepository(
* Write block metadata to storage when the buffer is full or when we reached the current range end.
*/
private suspend fun writeAndClearBuffer(metaDataBuffer: MutableList) {
- rustBackend.writeBlockMetadata(metaDataBuffer)
+ backend.writeBlockMetadata(metaDataBuffer)
metaDataBuffer.clear()
}
- override suspend fun rewindTo(height: BlockHeight) = rustBackend.rewindBlockMetadataToHeight(height)
+ override suspend fun rewindTo(height: BlockHeight) = backend.rewindBlockMetadataToHeight(height)
override suspend fun deleteAllCompactBlockFiles(): Boolean {
Twig.verbose { "Deleting all blocks from directory ${blocksDirectory.path}" }
@@ -129,13 +128,16 @@ internal class FileCompactBlockRepository(
*/
const val BLOCKS_METADATA_BUFFER_SIZE = 10
+ /**
+ * @param blockCacheRoot The root directory for the compact block cache (contains the database and a
+ * subdirectory for the blocks).
+ */
suspend fun new(
- rustBackend: RustBackend
+ blockCacheRoot: File,
+ rustBackend: Backend
): FileCompactBlockRepository {
- Twig.debug { "${rustBackend.fsBlockDbRoot.absolutePath} \n ${rustBackend.dataDbFile.absolutePath}" }
-
// create and check cache directories
- val blocksDirectory = File(rustBackend.fsBlockDbRoot, BLOCKS_DOWNLOAD_DIRECTORY).also {
+ val blocksDirectory = File(blockCacheRoot, BLOCKS_DOWNLOAD_DIRECTORY).also {
it.mkdirsSuspend()
}
if (!blocksDirectory.existsSuspend()) {
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt
index b03af114..0bd0c482 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt
@@ -2,14 +2,15 @@ package cash.z.ecc.android.sdk.internal.transaction
import cash.z.ecc.android.sdk.exception.TransactionEncoderException
import cash.z.ecc.android.sdk.ext.masked
+import cash.z.ecc.android.sdk.internal.Backend
import cash.z.ecc.android.sdk.internal.SaplingParamTool
import cash.z.ecc.android.sdk.internal.Twig
+import cash.z.ecc.android.sdk.internal.createToAddress
+import cash.z.ecc.android.sdk.internal.getBranchIdForHeight
import cash.z.ecc.android.sdk.internal.model.EncodedTransaction
+import cash.z.ecc.android.sdk.internal.network
import cash.z.ecc.android.sdk.internal.repository.DerivedDataRepository
-import cash.z.ecc.android.sdk.jni.Backend
-import cash.z.ecc.android.sdk.jni.createToAddress
-import cash.z.ecc.android.sdk.jni.getBranchIdForHeight
-import cash.z.ecc.android.sdk.jni.shieldToAddress
+import cash.z.ecc.android.sdk.internal.shieldToAddress
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.Zatoshi
@@ -19,7 +20,7 @@ import cash.z.ecc.android.sdk.model.Zatoshi
* behaving like a stateless API so that callers can request [createTransaction] and receive a
* result, even though there are intermediate database interactions.
*
- * @property backend the instance of RustBackendWelding to use for creating and validating.
+ * @property rustBackend the instance of RustBackendWelding to use for creating and validating.
* @property repository the repository that stores information about the transactions being created
* such as the raw bytes and raw txId.
*/
@@ -132,7 +133,7 @@ internal class TransactionEncoderImpl(
@Suppress("TooGenericExceptionCaught")
return try {
- saplingParamTool.ensureParams(backend.saplingParamDir)
+ saplingParamTool.ensureParams(saplingParamTool.properties.paramsDirectory)
Twig.debug { "params exist! attempting to send..." }
backend.createToAddress(
usk,
@@ -154,7 +155,7 @@ internal class TransactionEncoderImpl(
): Long {
@Suppress("TooGenericExceptionCaught")
return try {
- saplingParamTool.ensureParams(backend.saplingParamDir)
+ saplingParamTool.ensureParams(saplingParamTool.properties.paramsDirectory)
Twig.debug { "params exist! attempting to shield..." }
backend.shieldToAddress(
usk,
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/Derivation.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/Derivation.kt
deleted file mode 100644
index a94b9a90..00000000
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/jni/Derivation.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package cash.z.ecc.android.sdk.jni
-
-import cash.z.ecc.android.sdk.model.Account
-import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
-import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
-import cash.z.ecc.android.sdk.model.ZcashNetwork
-
-// Implemented by `DerivationTool`
-interface Derivation {
- suspend fun deriveUnifiedAddress(
- viewingKey: String,
- network: ZcashNetwork
- ): String
-
- suspend fun deriveUnifiedAddress(
- seed: ByteArray,
- network: ZcashNetwork,
- account: Account
- ): String
-
- suspend fun deriveUnifiedSpendingKey(
- seed: ByteArray,
- network: ZcashNetwork,
- account: Account
- ): UnifiedSpendingKey
-
- suspend fun deriveUnifiedFullViewingKey(
- usk: UnifiedSpendingKey,
- network: ZcashNetwork
- ): UnifiedFullViewingKey
-
- suspend fun deriveUnifiedFullViewingKeys(
- seed: ByteArray,
- network: ZcashNetwork,
- numberOfAccounts: Int = 1
- ): Array
-}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/UnifiedSpendingKey.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/UnifiedSpendingKey.kt
index e840ad10..c038d6ce 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/UnifiedSpendingKey.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/model/UnifiedSpendingKey.kt
@@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk.model
-import cash.z.ecc.android.sdk.jni.RustBackend
-import cash.z.ecc.android.sdk.jni.UnifiedSpendingKeyJni
+import cash.z.ecc.android.sdk.internal.jni.RustBackend
+import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
/**
* A [ZIP 316](https://zips.z.cash/zip-0316) Unified Spending Key.
@@ -26,9 +26,9 @@ class UnifiedSpendingKey private constructor(
private val bytes: FirstClassByteArray
) {
- internal constructor(uskJni: UnifiedSpendingKeyJni) : this(
+ internal constructor(uskJni: JniUnifiedSpendingKey) : this(
Account(uskJni.account),
- FirstClassByteArray(uskJni.copyBytes())
+ FirstClassByteArray(uskJni.bytes.copyOf())
)
/**
@@ -75,9 +75,7 @@ class UnifiedSpendingKey private constructor(
val bytesCopy = bytes.copyOf()
RustBackend.loadLibrary()
return runCatching {
- // We can ignore the Boolean returned from this, because if an error
- // occurs the Rust side will throw.
- RustBackend.validateUnifiedSpendingKey(bytesCopy)
+ require(RustBackend.validateUnifiedSpendingKey(bytesCopy))
UnifiedSpendingKey(account, FirstClassByteArray(bytesCopy))
}
}
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/CheckpointTool.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/CheckpointTool.kt
index 5207388c..a6e4a8eb 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/CheckpointTool.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/CheckpointTool.kt
@@ -4,8 +4,8 @@ import android.content.Context
import androidx.annotation.VisibleForTesting
import cash.z.ecc.android.sdk.exception.BirthdayException
import cash.z.ecc.android.sdk.internal.Twig
-import cash.z.ecc.android.sdk.internal.from
import cash.z.ecc.android.sdk.internal.model.Checkpoint
+import cash.z.ecc.android.sdk.internal.model.ext.from
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlinx.coroutines.Dispatchers
diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/DerivationTool.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/DerivationTool.kt
index c11b5236..e699e2ea 100644
--- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/DerivationTool.kt
+++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/tool/DerivationTool.kt
@@ -1,15 +1,15 @@
package cash.z.ecc.android.sdk.tool
-import cash.z.ecc.android.sdk.jni.Derivation
-import cash.z.ecc.android.sdk.jni.RustBackend
-import cash.z.ecc.android.sdk.jni.UnifiedSpendingKeyJni
+import cash.z.ecc.android.sdk.internal.Derivation
+import cash.z.ecc.android.sdk.internal.SuspendingLazy
+import cash.z.ecc.android.sdk.internal.TypesafeDerivationToolImpl
+import cash.z.ecc.android.sdk.internal.jni.RustDerivationTool
import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.ZcashNetwork
-@Suppress("TooManyFunctions")
-object DerivationTool : Derivation {
+interface DerivationTool {
/**
* Given a seed and a number of accounts, return the associated Unified Full Viewing Keys.
@@ -20,16 +20,11 @@ object DerivationTool : Derivation {
*
* @return the UFVKs derived from the seed, encoded as Strings.
*/
- override suspend fun deriveUnifiedFullViewingKeys(
+ suspend fun deriveUnifiedFullViewingKeys(
seed: ByteArray,
network: ZcashNetwork,
numberOfAccounts: Int
- ): Array =
- withRustBackendLoaded {
- deriveUnifiedFullViewingKeysFromSeed(seed, numberOfAccounts, networkId = network.id).map {
- UnifiedFullViewingKey(it)
- }.toTypedArray()
- }
+ ): List
/**
* Given a unified spending key, return the associated unified full viewing key.
@@ -38,14 +33,10 @@ object DerivationTool : Derivation {
*
* @return a unified full viewing key.
*/
- override suspend fun deriveUnifiedFullViewingKey(
+ suspend fun deriveUnifiedFullViewingKey(
usk: UnifiedSpendingKey,
network: ZcashNetwork
- ): UnifiedFullViewingKey = withRustBackendLoaded {
- UnifiedFullViewingKey(
- deriveUnifiedFullViewingKey(usk.copyBytes(), networkId = network.id)
- )
- }
+ ): UnifiedFullViewingKey
/**
* Derives and returns a unified spending key from the given seed for the given account ID.
@@ -59,26 +50,21 @@ object DerivationTool : Derivation {
*
* @return the unified spending key for the account.
*/
- override suspend fun deriveUnifiedSpendingKey(
+ suspend fun deriveUnifiedSpendingKey(
seed: ByteArray,
network: ZcashNetwork,
account: Account
- ): UnifiedSpendingKey = withRustBackendLoaded {
- UnifiedSpendingKey(deriveSpendingKey(seed, account.value, networkId = network.id))
- }
+ ): UnifiedSpendingKey
/**
* Given a seed and account index, return the associated Unified Address.
*
* @param seed the seed from which to derive the address.
- * @param accountIndex the index of the account to use for deriving the address.
+ * @param account the index of the account to use for deriving the address.
*
* @return the address that corresponds to the seed and account index.
*/
- override suspend fun deriveUnifiedAddress(seed: ByteArray, network: ZcashNetwork, account: Account): String =
- withRustBackendLoaded {
- deriveUnifiedAddressFromSeed(seed, account.value, networkId = network.id)
- }
+ suspend fun deriveUnifiedAddress(seed: ByteArray, network: ZcashNetwork, account: Account): String
/**
* Given a Unified Full Viewing Key string, return the associated Unified Address.
@@ -88,56 +74,17 @@ object DerivationTool : Derivation {
*
* @return the address that corresponds to the viewing key.
*/
- override suspend fun deriveUnifiedAddress(
+ suspend fun deriveUnifiedAddress(
viewingKey: String,
network: ZcashNetwork
- ): String = withRustBackendLoaded {
- deriveUnifiedAddressFromViewingKey(viewingKey, networkId = network.id)
- }
-
- @Suppress("UNUSED_PARAMETER")
- fun validateUnifiedFullViewingKey(viewingKey: UnifiedFullViewingKey, networkId: Int = ZcashNetwork.Mainnet.id) {
- // TODO [#654] https://github.com/zcash/zcash-android-wallet-sdk/issues/654
- }
-
- /**
- * A helper function to ensure that the Rust libraries are loaded before any code in this
- * class attempts to interact with it, indirectly, by invoking JNI functions. It would be
- * nice to have an annotation like @UsesSystemLibrary for this
- */
- private suspend fun withRustBackendLoaded(block: () -> T): T {
- RustBackend.loadLibrary()
- return block()
- }
-
- //
- // JNI functions
- //
-
- @JvmStatic
- private external fun deriveSpendingKey(
- seed: ByteArray,
- account: Int,
- networkId: Int
- ): UnifiedSpendingKeyJni
-
- @JvmStatic
- private external fun deriveUnifiedFullViewingKeysFromSeed(
- seed: ByteArray,
- numberOfAccounts: Int,
- networkId: Int
- ): Array
-
- @JvmStatic
- private external fun deriveUnifiedFullViewingKey(usk: ByteArray, networkId: Int): String
-
- @JvmStatic
- private external fun deriveUnifiedAddressFromSeed(
- seed: ByteArray,
- accountIndex: Int,
- networkId: Int
): String
- @JvmStatic
- private external fun deriveUnifiedAddressFromViewingKey(key: String, networkId: Int): String
+ companion object {
+ const val DEFAULT_NUMBER_OF_ACCOUNTS = Derivation.DEFAULT_NUMBER_OF_ACCOUNTS
+
+ private val instance = SuspendingLazy {
+ TypesafeDerivationToolImpl(RustDerivationTool.new())
+ }
+ suspend fun getInstance() = instance.getInstance(Unit)
+ }
}
diff --git a/sdk-lib/testing.gradle b/sdk-lib/testing.gradle
deleted file mode 100755
index eae47b4c..00000000
--- a/sdk-lib/testing.gradle
+++ /dev/null
@@ -1,46 +0,0 @@
-task unregistered {
- println "configuring unregistered"
- doLast {
- println 'unregistered'
- }
-}
-
-tasks.register("pb") {
- println "configuring pb"
- doLast {
- println 'preBuild'
- }
-}
-tasks.register("generateJni") {
- println "configuring generateJni"
- doLast {
- println 'jni'
- }
-}
-tasks.register("copyA") {
- dependsOn generateJni
- println "configuring copyA"
- doLast {
- println 'copyA'
- }
-}
-tasks.register("copyB") {
- dependsOn generateJni
- println "configuring copyB"
- doLast {
- println 'copyB'
- }
-}
-tasks.register("copyC") {
- dependsOn generateJni
- println "configuring copyC"
- doLast {
- println 'copyC'
- }
-}
-
-task copyAll {
- dependsOn copyA, copyB, copyC
-}
-
-pb.dependsOn copyAll
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 1ecc53ff..c52e8f47 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -269,6 +269,7 @@ rootProject.name = "zcash-android-sdk"
includeBuild("build-conventions")
+include("backend-lib")
include("darkside-test-lib")
include("demo-app")
include("demo-app-benchmark-test")