[#173] Adopt SDK API changes
This allows the app to consume the snapshot version of the SDK, which contains a number of API changes. A followup issue of #279 was filed so that the app can be reverted to the release version of the SDK once the SDK API stabilizes again.
This commit is contained in:
parent
de44984ea2
commit
45e14e6a4b
|
@ -94,7 +94,8 @@ KOTLIN_VERSION=1.6.10
|
|||
KOTLINX_COROUTINES_VERSION=1.6.0
|
||||
ZCASH_ANDROID_WALLET_PLUGINS_VERSION=1.0.0
|
||||
ZCASH_BIP39_VERSION=1.0.2
|
||||
ZCASH_SDK_VERSION=1.3.0-beta19
|
||||
# TODO [#279]: Revert to stable SDK before app release
|
||||
ZCASH_SDK_VERSION=1.4.0-beta01-SNAPSHOT
|
||||
ZXING_VERSION=3.4.1
|
||||
|
||||
# Toolchain is the Java version used to build the application, which is separate from the
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import cash.z.ecc.sdk.fixture.WalletBirthdayFixture
|
||||
import cash.z.ecc.sdk.test.count
|
||||
import org.junit.Assert.assertEquals
|
||||
|
@ -16,17 +17,17 @@ class WalletBirthdayTest {
|
|||
|
||||
val jsonObject = walletBirthday.toJson()
|
||||
assertEquals(5, jsonObject.keys().count())
|
||||
assertTrue(jsonObject.has(WalletBirthdayCompanion.KEY_VERSION))
|
||||
assertTrue(jsonObject.has(WalletBirthdayCompanion.KEY_HEIGHT))
|
||||
assertTrue(jsonObject.has(WalletBirthdayCompanion.KEY_HASH))
|
||||
assertTrue(jsonObject.has(WalletBirthdayCompanion.KEY_EPOCH_SECONDS))
|
||||
assertTrue(jsonObject.has(WalletBirthdayCompanion.KEY_TREE))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_VERSION))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_HEIGHT))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_HASH))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_EPOCH_SECONDS))
|
||||
assertTrue(jsonObject.has(WalletBirthday.KEY_TREE))
|
||||
|
||||
assertEquals(1, jsonObject.getInt(WalletBirthdayCompanion.KEY_VERSION))
|
||||
assertEquals(WalletBirthdayFixture.HEIGHT, jsonObject.getInt(WalletBirthdayCompanion.KEY_HEIGHT))
|
||||
assertEquals(WalletBirthdayFixture.HASH, jsonObject.getString(WalletBirthdayCompanion.KEY_HASH))
|
||||
assertEquals(WalletBirthdayFixture.EPOCH_SECONDS, jsonObject.getLong(WalletBirthdayCompanion.KEY_EPOCH_SECONDS))
|
||||
assertEquals(WalletBirthdayFixture.TREE, jsonObject.getString(WalletBirthdayCompanion.KEY_TREE))
|
||||
assertEquals(1, jsonObject.getInt(WalletBirthday.KEY_VERSION))
|
||||
assertEquals(WalletBirthdayFixture.HEIGHT, jsonObject.getInt(WalletBirthday.KEY_HEIGHT))
|
||||
assertEquals(WalletBirthdayFixture.HASH, jsonObject.getString(WalletBirthday.KEY_HASH))
|
||||
assertEquals(WalletBirthdayFixture.EPOCH_SECONDS, jsonObject.getLong(WalletBirthday.KEY_EPOCH_SECONDS))
|
||||
assertEquals(WalletBirthdayFixture.TREE, jsonObject.getString(WalletBirthday.KEY_TREE))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -36,9 +37,9 @@ class WalletBirthdayTest {
|
|||
|
||||
val jsonObject = walletBirthday.toJson()
|
||||
|
||||
assertEquals(Long.MAX_VALUE, jsonObject.getLong(WalletBirthdayCompanion.KEY_EPOCH_SECONDS))
|
||||
assertEquals(Long.MAX_VALUE, jsonObject.getLong(WalletBirthday.KEY_EPOCH_SECONDS))
|
||||
|
||||
WalletBirthdayCompanion.from(jsonObject).also {
|
||||
WalletBirthday.from(jsonObject).also {
|
||||
assertEquals(Long.MAX_VALUE, it.time)
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +49,7 @@ class WalletBirthdayTest {
|
|||
fun round_trip() {
|
||||
val walletBirthday = WalletBirthdayFixture.new()
|
||||
|
||||
val deserialized = WalletBirthdayCompanion.from(walletBirthday.toJson())
|
||||
val deserialized = WalletBirthday.from(walletBirthday.toJson())
|
||||
|
||||
assertEquals(walletBirthday, deserialized)
|
||||
assertFalse(walletBirthday === deserialized)
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package cash.z.ecc.sdk
|
||||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.sdk.model.PersistableWallet
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// Synchronizer needs a Companion object
|
||||
// https://github.com/zcash/zcash-android-wallet-sdk/issues/310
|
||||
object SynchronizerCompanion {
|
||||
suspend fun load(context: Context, persistableWallet: PersistableWallet): Synchronizer {
|
||||
val config = persistableWallet.toConfig()
|
||||
val initializer = withContext(Dispatchers.IO) { Initializer(context, config) }
|
||||
return withContext(Dispatchers.IO) { Synchronizer(initializer) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun PersistableWallet.deriveViewingKey(): UnifiedViewingKey {
|
||||
// Dispatcher needed because SecureRandom is loaded, which is slow and performs IO
|
||||
// https://github.com/zcash/kotlin-bip39/issues/13
|
||||
val bip39Seed = withContext(Dispatchers.IO) {
|
||||
Mnemonics.MnemonicCode(seedPhrase.joinToString()).toSeed()
|
||||
}
|
||||
|
||||
// Dispatchers needed until an SDK is published with the implementation of
|
||||
// https://github.com/zcash/zcash-android-wallet-sdk/issues/269
|
||||
val viewingKey = withContext(Dispatchers.IO) {
|
||||
DerivationTool.deriveUnifiedViewingKeys(bip39Seed, network)[0]
|
||||
}
|
||||
|
||||
return viewingKey
|
||||
}
|
||||
|
||||
private suspend fun PersistableWallet.toConfig(): Initializer.Config {
|
||||
val network = network
|
||||
val vk = deriveViewingKey()
|
||||
|
||||
return Initializer.Config {
|
||||
it.importWallet(vk, birthday?.height, network, network.defaultHost, network.defaultPort)
|
||||
}
|
||||
}
|
|
@ -34,10 +34,8 @@ data class PersistableWallet(
|
|||
put(KEY_SEED_PHRASE, seedPhrase.joinToString())
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
// For security, intentionally override the toString method to reduce risk of accidentally logging secrets
|
||||
return "PersistableWallet"
|
||||
}
|
||||
// For security, intentionally override the toString method to reduce risk of accidentally logging secrets
|
||||
override fun toString() = "PersistableWallet"
|
||||
|
||||
companion object {
|
||||
private const val VERSION_1 = 1
|
||||
|
@ -52,7 +50,7 @@ data class PersistableWallet(
|
|||
VERSION_1 -> {
|
||||
val networkId = jsonObject.getInt(KEY_NETWORK_ID)
|
||||
val birthday = if (jsonObject.has(KEY_BIRTHDAY)) {
|
||||
WalletBirthdayCompanion.from(jsonObject.getJSONObject(KEY_BIRTHDAY))
|
||||
WalletBirthday.from(jsonObject.getJSONObject(KEY_BIRTHDAY))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -71,11 +69,8 @@ data class PersistableWallet(
|
|||
*/
|
||||
suspend fun new(application: Application): PersistableWallet {
|
||||
val zcashNetwork = ZcashNetwork.fromResources(application)
|
||||
// Dispatchers can be removed once a new SDK is released implementing
|
||||
// https://github.com/zcash/zcash-android-wallet-sdk/issues/269
|
||||
val walletBirthday = withContext(Dispatchers.IO) {
|
||||
WalletBirthdayTool.loadNearest(application, zcashNetwork)
|
||||
}
|
||||
val walletBirthday = WalletBirthdayTool.loadNearest(application, zcashNetwork)
|
||||
|
||||
val seedPhrase = newSeedPhrase()
|
||||
|
||||
return PersistableWallet(zcashNetwork, walletBirthday, seedPhrase)
|
||||
|
|
|
@ -23,21 +23,19 @@ data class WalletAddresses(
|
|||
Mnemonics.MnemonicCode(persistableWallet.seedPhrase.joinToString()).toSeed()
|
||||
}
|
||||
|
||||
// Dispatchers needed until an SDK is published with the implementation of
|
||||
// https://github.com/zcash/zcash-android-wallet-sdk/issues/269
|
||||
val viewingKey = withContext(Dispatchers.IO) {
|
||||
DerivationTool.deriveUnifiedViewingKeys(bip39Seed, persistableWallet.network)[0]
|
||||
}.extpub
|
||||
val viewingKey = DerivationTool.deriveUnifiedViewingKeys(bip39Seed, persistableWallet.network)[0].extpub
|
||||
|
||||
val shieldedSaplingAddress = withContext(Dispatchers.IO) {
|
||||
DerivationTool.deriveShieldedAddress(bip39Seed, persistableWallet.network)
|
||||
}.let {
|
||||
val shieldedSaplingAddress = DerivationTool.deriveShieldedAddress(
|
||||
bip39Seed,
|
||||
persistableWallet.network
|
||||
).let {
|
||||
WalletAddress.ShieldedSapling.new(it)
|
||||
}
|
||||
|
||||
val transparentAddress = withContext(Dispatchers.IO) {
|
||||
DerivationTool.deriveTransparentAddress(bip39Seed, persistableWallet.network)
|
||||
}.let {
|
||||
val transparentAddress = DerivationTool.deriveTransparentAddress(
|
||||
bip39Seed,
|
||||
persistableWallet.network
|
||||
).let {
|
||||
WalletAddress.Transparent.new(it)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import org.json.JSONObject
|
||||
|
||||
// WalletBirthday needs a companion
|
||||
// https://github.com/zcash/zcash-android-wallet-sdk/issues/310
|
||||
object WalletBirthdayCompanion {
|
||||
internal const val VERSION_1 = 1
|
||||
internal const val KEY_VERSION = "version"
|
||||
internal const val KEY_HEIGHT = "height"
|
||||
internal const val KEY_HASH = "hash"
|
||||
internal const val KEY_EPOCH_SECONDS = "epoch_seconds"
|
||||
internal const val KEY_TREE = "tree"
|
||||
|
||||
fun from(jsonString: String) = from(JSONObject(jsonString))
|
||||
|
||||
fun from(jsonObject: JSONObject): WalletBirthday {
|
||||
when (val version = jsonObject.getInt(KEY_VERSION)) {
|
||||
VERSION_1 -> {
|
||||
val height = jsonObject.getInt(KEY_HEIGHT)
|
||||
val hash = jsonObject.getString(KEY_HASH)
|
||||
val epochSeconds = jsonObject.getLong(KEY_EPOCH_SECONDS)
|
||||
val tree = jsonObject.getString(KEY_TREE)
|
||||
|
||||
return WalletBirthday(height, hash, epochSeconds, tree)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Unsupported version $version")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletBirthday.toJson() = JSONObject().apply {
|
||||
put(WalletBirthdayCompanion.KEY_VERSION, WalletBirthdayCompanion.VERSION_1)
|
||||
put(WalletBirthdayCompanion.KEY_HEIGHT, height)
|
||||
put(WalletBirthdayCompanion.KEY_HASH, hash)
|
||||
put(WalletBirthdayCompanion.KEY_EPOCH_SECONDS, time)
|
||||
put(WalletBirthdayCompanion.KEY_TREE, tree)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import org.json.JSONObject
|
||||
|
||||
internal val WalletBirthday.Companion.VERSION_1
|
||||
get() = 1
|
||||
internal val WalletBirthday.Companion.KEY_VERSION
|
||||
get() = "version"
|
||||
internal val WalletBirthday.Companion.KEY_HEIGHT
|
||||
get() = "height"
|
||||
internal val WalletBirthday.Companion.KEY_HASH
|
||||
get() = "hash"
|
||||
internal val WalletBirthday.Companion.KEY_EPOCH_SECONDS
|
||||
get() = "epoch_seconds"
|
||||
internal val WalletBirthday.Companion.KEY_TREE
|
||||
get() = "tree"
|
||||
|
||||
fun WalletBirthday.Companion.from(jsonString: String) = from(JSONObject(jsonString))
|
||||
|
||||
fun WalletBirthday.Companion.from(jsonObject: JSONObject): WalletBirthday {
|
||||
when (val version = jsonObject.getInt(WalletBirthday.KEY_VERSION)) {
|
||||
WalletBirthday.VERSION_1 -> {
|
||||
val height = jsonObject.getInt(WalletBirthday.KEY_HEIGHT)
|
||||
val hash = jsonObject.getString(WalletBirthday.KEY_HASH)
|
||||
val epochSeconds = jsonObject.getLong(WalletBirthday.KEY_EPOCH_SECONDS)
|
||||
val tree = jsonObject.getString(WalletBirthday.KEY_TREE)
|
||||
|
||||
return WalletBirthday(height, hash, epochSeconds, tree)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Unsupported version $version")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletBirthday.toJson() = JSONObject().apply {
|
||||
put(WalletBirthday.KEY_VERSION, WalletBirthday.VERSION_1)
|
||||
put(WalletBirthday.KEY_HEIGHT, height)
|
||||
put(WalletBirthday.KEY_HASH, hash)
|
||||
put(WalletBirthday.KEY_EPOCH_SECONDS, time)
|
||||
put(WalletBirthday.KEY_TREE, tree)
|
||||
}
|
|
@ -79,6 +79,13 @@ dependencyResolutionManagement {
|
|||
}
|
||||
}
|
||||
}
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots") {
|
||||
if (isRepoRestrictionEnabled) {
|
||||
content {
|
||||
includeGroup("cash.z.ecc.android")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage", "MaxLineLength")
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package co.electriccoin.zcash.global
|
||||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import cash.z.ecc.sdk.SynchronizerCompanion
|
||||
import cash.z.ecc.sdk.model.PersistableWallet
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.spackle.LazyWithArgument
|
||||
import co.electriccoin.zcash.ui.preference.EncryptedPreferenceKeys
|
||||
|
@ -26,6 +30,7 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class WalletCoordinator(context: Context) {
|
||||
companion object {
|
||||
|
@ -67,9 +72,9 @@ class WalletCoordinator(context: Context) {
|
|||
.filterNotNull()
|
||||
.flatMapConcat {
|
||||
callbackFlow {
|
||||
val initializer = Initializer.new(context, it.toConfig())
|
||||
val synchronizer = synchronizerMutex.withLock {
|
||||
val synchronizer = SynchronizerCompanion.load(applicationContext, it)
|
||||
|
||||
val synchronizer = Synchronizer.new(initializer)
|
||||
synchronizer.start(walletScope)
|
||||
}
|
||||
|
||||
|
@ -153,3 +158,22 @@ class WalletCoordinator(context: Context) {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun PersistableWallet.deriveViewingKey(): UnifiedViewingKey {
|
||||
// Dispatcher needed because SecureRandom is loaded, which is slow and performs IO
|
||||
// https://github.com/zcash/kotlin-bip39/issues/13
|
||||
val bip39Seed = withContext(Dispatchers.IO) {
|
||||
Mnemonics.MnemonicCode(seedPhrase.joinToString()).toSeed()
|
||||
}
|
||||
|
||||
return DerivationTool.deriveUnifiedViewingKeys(bip39Seed, network)[0]
|
||||
}
|
||||
|
||||
private suspend fun PersistableWallet.toConfig(): Initializer.Config {
|
||||
val network = network
|
||||
val vk = deriveViewingKey()
|
||||
|
||||
return Initializer.Config {
|
||||
it.importWallet(vk, birthday?.height, network, network.defaultHost, network.defaultPort)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue