[#525] Replace UnifiedViewingKey with UnifiedFullViewingKey
UnifiedViewingKey had a bug that made it incompatible with ZIP 316. For compatibility with the current `zcash/librustzcash` revision we use a temporary fake UFVK encoding that stores sufficient information to use the current APIs, and a superset of the actual ZIP 316 FVK information.
This commit is contained in:
parent
e01a906407
commit
f7c9bad367
36
CHANGELOG.md
36
CHANGELOG.md
|
@ -1,7 +1,41 @@
|
|||
Change Log
|
||||
==========
|
||||
|
||||
Upcoming
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- `cash.z.ecc.android.sdk.type.UnifiedFullViewingKey`, representing a Unified Full Viewing
|
||||
Key as specified in [ZIP 316](https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys).
|
||||
- TODO: Actually encode per ZIP 316.
|
||||
- `cash.z.ecc.android.sdk.tool`:
|
||||
- `DerivationTool.deriveUnifiedAddress`
|
||||
- `DerivationTool.deriveUnifiedFullViewingKeys`
|
||||
- `DerivationTool.validateUnifiedFullViewingKey`
|
||||
- Still unimplemented.
|
||||
|
||||
### Changed
|
||||
- The following methods now take or return `UnifiedFullViewingKey` instead of
|
||||
`UnifiedViewingKey`:
|
||||
- `cash.z.ecc.android.sdk`:
|
||||
- `Initializer.Config.addViewingKey`
|
||||
- `Initializer.Config.importWallet`
|
||||
- `Initializer.Config.newWallet`
|
||||
- `Initializer.Config.setViewingKeys`
|
||||
|
||||
### Removed
|
||||
- `cash.z.ecc.android.sdk.type.UnifiedViewingKey`
|
||||
- This type had a bug where the `extpub` field actually was storing a plain transparent
|
||||
public key, and not the extended public key as intended. This made it incompatible
|
||||
with ZIP 316.
|
||||
- `cash.z.ecc.android.sdk.tool`:
|
||||
- `DerivationTool.deriveShieldedAddress`
|
||||
- TODO: Do we still need to be able to derive Sapling shielded addresses for legacy
|
||||
support? Currently removed because `UnifiedFullViewingKey` doesn't expose the
|
||||
Sapling FVK on the Kotlin side (unlike the previous `UnifiedViewingKey`).
|
||||
- `DerivationTool.deriveUnifiedViewingKeys`
|
||||
- `DerivationTool.validateUnifiedViewingKey`
|
||||
|
||||
1.9.0-beta01
|
||||
------------------------------------
|
||||
- Split `ZcashNetwork` into `ZcashNetwork` and `LightWalletEndpoint` to decouple network and server configuration
|
||||
|
||||
|
|
|
@ -74,8 +74,8 @@ class TestWallet(
|
|||
val service = (synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService)
|
||||
|
||||
val available get() = synchronizer.saplingBalances.value?.available
|
||||
val shieldedAddress =
|
||||
runBlocking { DerivationTool.deriveShieldedAddress(seed, network = network) }
|
||||
val unifiedAddress =
|
||||
runBlocking { DerivationTool.deriveUnifiedAddress(seed, network = network) }
|
||||
val transparentAddress =
|
||||
runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) }
|
||||
val birthdayHeight get() = synchronizer.latestBirthdayHeight
|
||||
|
|
|
@ -11,7 +11,7 @@ import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
|
|||
import cash.z.ecc.android.sdk.demoapp.util.fromResources
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
@ -21,7 +21,7 @@ import kotlinx.coroutines.runBlocking
|
|||
*/
|
||||
class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
|
||||
|
||||
private lateinit var viewingKey: UnifiedViewingKey
|
||||
private lateinit var viewingKey: UnifiedFullViewingKey
|
||||
private lateinit var seed: ByteArray
|
||||
|
||||
/**
|
||||
|
@ -37,15 +37,22 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
|
|||
seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
|
||||
// the derivation tool can be used for generating keys and addresses
|
||||
viewingKey = runBlocking { DerivationTool.deriveUnifiedViewingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext())).first() }
|
||||
viewingKey = runBlocking {
|
||||
DerivationTool.deriveUnifiedFullViewingKeys(
|
||||
seed,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
).first()
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayAddress() {
|
||||
// a full fledged app would just get the address from the synchronizer
|
||||
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
|
||||
val zaddress = DerivationTool.deriveShieldedAddress(seed, ZcashNetwork.fromResources(requireApplicationContext()))
|
||||
val taddress = DerivationTool.deriveTransparentAddress(seed, ZcashNetwork.fromResources(requireApplicationContext()))
|
||||
binding.textInfo.text = "z-addr:\n$zaddress\n\n\nt-addr:\n$taddress"
|
||||
val uaddress = DerivationTool.deriveUnifiedAddress(
|
||||
seed,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
)
|
||||
binding.textInfo.text = "address:\n$uaddress"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,11 +79,11 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
|
|||
override fun onActionButtonClicked() {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
copyToClipboard(
|
||||
DerivationTool.deriveShieldedAddress(
|
||||
viewingKey.extfvk,
|
||||
DerivationTool.deriveUnifiedAddress(
|
||||
viewingKey.encoding,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
),
|
||||
"Shielded address copied to clipboard!"
|
||||
"Unified address copied to clipboard!"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,12 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
|
|||
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
|
||||
// converting seed into viewingKey
|
||||
val viewingKey = runBlocking { DerivationTool.deriveUnifiedViewingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext())).first() }
|
||||
val viewingKey = runBlocking {
|
||||
DerivationTool.deriveUnifiedFullViewingKeys(
|
||||
seed,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
).first()
|
||||
}
|
||||
|
||||
// using the ViewingKey to initialize
|
||||
runBlocking {
|
||||
|
|
|
@ -78,11 +78,11 @@ class GetPrivateKeyFragment : BaseDemoFragment<FragmentGetPrivateKeyBinding>() {
|
|||
override fun onActionButtonClicked() {
|
||||
lifecycleScope.launch {
|
||||
copyToClipboard(
|
||||
DerivationTool.deriveUnifiedViewingKeys(
|
||||
DerivationTool.deriveUnifiedFullViewingKeys(
|
||||
seed,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
).first().extpub,
|
||||
"ViewingKey copied to clipboard!"
|
||||
).first().encoding,
|
||||
"UnifiedFullViewingKey copied to clipboard!"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
|
|||
}
|
||||
)
|
||||
address = runBlocking {
|
||||
DerivationTool.deriveShieldedAddress(
|
||||
DerivationTool.deriveUnifiedAddress(
|
||||
seed,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
)
|
||||
|
|
|
@ -1538,6 +1538,7 @@ version = "0.0.4"
|
|||
dependencies = [
|
||||
"android_logger",
|
||||
"failure",
|
||||
"hdwallet",
|
||||
"hex",
|
||||
"jni",
|
||||
"log",
|
||||
|
|
|
@ -12,6 +12,7 @@ edition = "2018"
|
|||
[dependencies]
|
||||
android_logger = "0.9"
|
||||
failure = "0.1"
|
||||
hdwallet = "=0.3.0"
|
||||
hex = "0.4"
|
||||
jni = { version = "0.17", default-features = false }
|
||||
log = "0.4"
|
||||
|
|
|
@ -21,8 +21,7 @@ import org.junit.runners.Parameterized
|
|||
@RunWith(Parameterized::class)
|
||||
class SanityTest(
|
||||
private val wallet: TestWallet,
|
||||
private val extfvk: String,
|
||||
private val extpub: String,
|
||||
private val encoding: String,
|
||||
private val birthday: Int
|
||||
|
||||
) {
|
||||
|
@ -61,14 +60,9 @@ class SanityTest(
|
|||
@Test
|
||||
fun testViewingKeys() {
|
||||
assertEquals(
|
||||
"$name has invalid extfvk",
|
||||
extfvk,
|
||||
wallet.initializer.viewingKeys[0].extfvk
|
||||
)
|
||||
assertEquals(
|
||||
"$name has invalid extpub",
|
||||
extpub,
|
||||
wallet.initializer.viewingKeys[0].extpub
|
||||
"$name has invalid encoding",
|
||||
encoding,
|
||||
wallet.initializer.viewingKeys[0].encoding
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -101,14 +95,12 @@ class SanityTest(
|
|||
arrayOf(
|
||||
TestWallet(TestWallet.Backups.SAMPLE_WALLET),
|
||||
"zxviewtestsapling1qv0ue89kqqqqpqqyt4cl5wvssx4wqq30e5m948p07dnwl9x3u75vvnzvjwwpjkrf8yk2gva0kkxk9p8suj4xawlzw9pajuxgap83wykvsuyzfrm33a2p2m4jz2205kgzx0l2lj2kyegtnuph6crkyvyjqmfxut84nu00wxgrstu5fy3eu49nzl8jzr4chmql4ysgg2t8htn9dtvxy8c7wx9rvcerqsjqm6lqln9syk3g8rr3xpy3l4nj0kawenzpcdtnv9qmy98vdhqzaf063",
|
||||
"0234965f30c8611253d035f44e68d4e2ce82150e8665c95f41ccbaf916b16c69d8",
|
||||
1320000
|
||||
),
|
||||
// Mainnet wallet
|
||||
arrayOf(
|
||||
TestWallet(TestWallet.Backups.SAMPLE_WALLET, ZcashNetwork.Mainnet),
|
||||
"zxviews1q0hxkupsqqqqpqzsffgrk2smjuccedua7zswf5e3rgtv3ga9nhvhjug670egshd6me53r5n083s2m9mf4va4z7t39ltd3wr7hawnjcw09eu85q0ammsg0tsgx24p4ma0uvr4p8ltx5laum2slh2whc23ctwlnxme9w4dw92kalwk5u4wyem8dynknvvqvs68ktvm8qh7nx9zg22xfc77acv8hk3qqll9k3x4v2fa26puu2939ea7hy4hh60ywma69xtqhcy4037ne8g2sg8sq",
|
||||
"031c6355641237643317e2d338f5e8734c57e8aa8ce960ee22283cf2d76bef73be",
|
||||
1000000
|
||||
)
|
||||
)
|
||||
|
|
|
@ -32,8 +32,7 @@ class SmokeTest {
|
|||
|
||||
@Test
|
||||
fun testViewingKeys() {
|
||||
Assert.assertEquals("Invalid extfvk", "zxviewtestsapling1qv0ue89kqqqqpqqyt4cl5wvssx4wqq30e5m948p07dnwl9x3u75vvnzvjwwpjkrf8yk2gva0kkxk9p8suj4xawlzw9pajuxgap83wykvsuyzfrm33a2p2m4jz2205kgzx0l2lj2kyegtnuph6crkyvyjqmfxut84nu00wxgrstu5fy3eu49nzl8jzr4chmql4ysgg2t8htn9dtvxy8c7wx9rvcerqsjqm6lqln9syk3g8rr3xpy3l4nj0kawenzpcdtnv9qmy98vdhqzaf063", wallet.initializer.viewingKeys[0].extfvk)
|
||||
Assert.assertEquals("Invalid extpub", "0234965f30c8611253d035f44e68d4e2ce82150e8665c95f41ccbaf916b16c69d8", wallet.initializer.viewingKeys[0].extpub)
|
||||
Assert.assertEquals("Invalid encoding", "zxviewtestsapling1qv0ue89kqqqqpqqyt4cl5wvssx4wqq30e5m948p07dnwl9x3u75vvnzvjwwpjkrf8yk2gva0kkxk9p8suj4xawlzw9pajuxgap83wykvsuyzfrm33a2p2m4jz2205kgzx0l2lj2kyegtnuph6crkyvyjqmfxut84nu00wxgrstu5fy3eu49nzl8jzr4chmql4ysgg2t8htn9dtvxy8c7wx9rvcerqsjqm6lqln9syk3g8rr3xpy3l4nj0kawenzpcdtnv9qmy98vdhqzaf063", wallet.initializer.viewingKeys[0].encoding)
|
||||
}
|
||||
|
||||
// This test takes an extremely long time
|
||||
|
|
|
@ -38,12 +38,12 @@ class TransparentTest(val expected: Expected, val network: ZcashNetwork) {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun deriveUnifiedViewingKeysFromSeedTest() = runBlocking {
|
||||
val uvks = DerivationTool.deriveUnifiedViewingKeys(SEED, network = network)
|
||||
fun deriveUnifiedFullViewingKeysFromSeedTest() = runBlocking {
|
||||
val uvks = DerivationTool.deriveUnifiedFullViewingKeys(SEED, network = network)
|
||||
assertEquals(1, uvks.size)
|
||||
val uvk = uvks.first()
|
||||
assertEquals(expected.zAddr, DerivationTool.deriveShieldedAddress(uvk.extfvk, network = network))
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPublicKey(uvk.extpub, network = network))
|
||||
assertEquals(expected.zAddr, DerivationTool.deriveUnifiedAddress(uvk.encoding, network = network))
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPublicKey(uvk.encoding, network = network))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -30,7 +30,7 @@ class ShieldFundsSample {
|
|||
|
||||
val wallet = TestWallet(TestWallet.Backups.DEV_WALLET, ZcashNetwork.Mainnet)
|
||||
|
||||
Assert.assertEquals("foo", "${wallet.shieldedAddress} ${wallet.transparentAddress}")
|
||||
Assert.assertEquals("foo", "${wallet.unifiedAddress} ${wallet.transparentAddress}")
|
||||
// wallet.shieldFunds()
|
||||
|
||||
Twig.clip("ShieldFundsSample")
|
||||
|
|
|
@ -36,7 +36,7 @@ class AddressGeneratorUtil {
|
|||
.map { seedPhrase ->
|
||||
mnemonics.toSeed(seedPhrase.toCharArray())
|
||||
}.map { seed ->
|
||||
DerivationTool.deriveShieldedAddress(seed, ZcashNetwork.Mainnet)
|
||||
DerivationTool.deriveUnifiedAddress(seed, ZcashNetwork.Mainnet)
|
||||
}.collect { address ->
|
||||
println("xrxrx2\t$address")
|
||||
assertTrue(address.startsWith("zs1"))
|
||||
|
|
|
@ -74,8 +74,8 @@ class TestWallet(
|
|||
val service = (synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService)
|
||||
|
||||
val available get() = synchronizer.saplingBalances.value?.available
|
||||
val shieldedAddress =
|
||||
runBlocking { DerivationTool.deriveShieldedAddress(seed, network = network) }
|
||||
val unifiedAddress =
|
||||
runBlocking { DerivationTool.deriveUnifiedAddress(seed, network = network) }
|
||||
val transparentAddress =
|
||||
runBlocking { DerivationTool.deriveTransparentAddress(seed, network = network) }
|
||||
val birthdayHeight get() = synchronizer.latestBirthdayHeight
|
||||
|
|
|
@ -14,7 +14,7 @@ import cash.z.ecc.android.sdk.model.LightWalletEndpoint
|
|||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.CheckpointTool
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
@ -29,7 +29,7 @@ class Initializer private constructor(
|
|||
val network: ZcashNetwork,
|
||||
val alias: String,
|
||||
val lightWalletEndpoint: LightWalletEndpoint,
|
||||
val viewingKeys: List<UnifiedViewingKey>,
|
||||
val viewingKeys: List<UnifiedFullViewingKey>,
|
||||
val overwriteVks: Boolean,
|
||||
internal val checkpoint: Checkpoint
|
||||
) {
|
||||
|
@ -37,7 +37,7 @@ class Initializer private constructor(
|
|||
suspend fun erase() = erase(context, network, alias)
|
||||
|
||||
class Config private constructor(
|
||||
val viewingKeys: MutableList<UnifiedViewingKey> = mutableListOf(),
|
||||
val viewingKeys: MutableList<UnifiedFullViewingKey> = mutableListOf(),
|
||||
var alias: String = ZcashSdk.DEFAULT_ALIAS
|
||||
) {
|
||||
var birthdayHeight: BlockHeight? = null
|
||||
|
@ -119,13 +119,13 @@ class Initializer private constructor(
|
|||
* probably has serious bugs.
|
||||
*/
|
||||
fun setViewingKeys(
|
||||
vararg unifiedViewingKeys: UnifiedViewingKey,
|
||||
vararg unifiedFullViewingKeys: UnifiedFullViewingKey,
|
||||
overwrite: Boolean = false
|
||||
): Config = apply {
|
||||
overwriteVks = overwrite
|
||||
viewingKeys.apply {
|
||||
clear()
|
||||
addAll(unifiedViewingKeys)
|
||||
addAll(unifiedFullViewingKeys)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ class Initializer private constructor(
|
|||
* is not currently well supported. Consider it an alpha-preview feature that might work but
|
||||
* probably has serious bugs.
|
||||
*/
|
||||
fun addViewingKey(unifiedFullViewingKey: UnifiedViewingKey): Config = apply {
|
||||
fun addViewingKey(unifiedFullViewingKey: UnifiedFullViewingKey): Config = apply {
|
||||
viewingKeys.add(unifiedFullViewingKey)
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ class Initializer private constructor(
|
|||
alias: String = ZcashSdk.DEFAULT_ALIAS
|
||||
): Config =
|
||||
importWallet(
|
||||
DerivationTool.deriveUnifiedViewingKeys(seed, network = network)[0],
|
||||
DerivationTool.deriveUnifiedFullViewingKeys(seed, network = network)[0],
|
||||
birthday,
|
||||
network,
|
||||
lightWalletEndpoint,
|
||||
|
@ -185,8 +185,9 @@ class Initializer private constructor(
|
|||
/**
|
||||
* Default function for importing a wallet.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
fun importWallet(
|
||||
viewingKey: UnifiedViewingKey,
|
||||
viewingKey: UnifiedFullViewingKey,
|
||||
birthday: BlockHeight?,
|
||||
network: ZcashNetwork,
|
||||
lightWalletEndpoint: LightWalletEndpoint,
|
||||
|
@ -207,7 +208,7 @@ class Initializer private constructor(
|
|||
lightWalletEndpoint: LightWalletEndpoint,
|
||||
alias: String = ZcashSdk.DEFAULT_ALIAS
|
||||
): Config = newWallet(
|
||||
DerivationTool.deriveUnifiedViewingKeys(seed, network)[0],
|
||||
DerivationTool.deriveUnifiedFullViewingKeys(seed, network)[0],
|
||||
network,
|
||||
lightWalletEndpoint,
|
||||
alias
|
||||
|
@ -217,7 +218,7 @@ class Initializer private constructor(
|
|||
* Default function for creating a new wallet.
|
||||
*/
|
||||
fun newWallet(
|
||||
viewingKey: UnifiedViewingKey,
|
||||
viewingKey: UnifiedFullViewingKey,
|
||||
network: ZcashNetwork,
|
||||
lightWalletEndpoint: LightWalletEndpoint,
|
||||
alias: String = ZcashSdk.DEFAULT_ALIAS
|
||||
|
@ -232,6 +233,7 @@ class Initializer private constructor(
|
|||
* Convenience method for setting thew viewingKeys from a given seed. This is the same as
|
||||
* calling `setViewingKeys` with the keys that match this seed.
|
||||
*/
|
||||
@Suppress("SpreadOperator")
|
||||
suspend fun setSeed(
|
||||
seed: ByteArray,
|
||||
network: ZcashNetwork,
|
||||
|
@ -239,7 +241,7 @@ class Initializer private constructor(
|
|||
): Config =
|
||||
apply {
|
||||
setViewingKeys(
|
||||
*DerivationTool.deriveUnifiedViewingKeys(
|
||||
*DerivationTool.deriveUnifiedFullViewingKeys(
|
||||
seed,
|
||||
network,
|
||||
numberOfAccounts
|
||||
|
@ -288,7 +290,7 @@ class Initializer private constructor(
|
|||
" have been set on this Initializer."
|
||||
}
|
||||
viewingKeys.forEach {
|
||||
DerivationTool.validateUnifiedViewingKey(it)
|
||||
DerivationTool.validateUnifiedFullViewingKey(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import cash.z.ecc.android.sdk.internal.twig
|
|||
import cash.z.ecc.android.sdk.jni.RustBackend
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -111,13 +111,14 @@ internal class PagedTransactionRepository private constructor(
|
|||
// TODO: convert this into a wallet repository rather than "transaction repository"
|
||||
|
||||
companion object {
|
||||
internal suspend fun new(
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun new(
|
||||
appContext: Context,
|
||||
zcashNetwork: ZcashNetwork,
|
||||
pageSize: Int = 10,
|
||||
rustBackend: RustBackend,
|
||||
birthday: Checkpoint,
|
||||
viewingKeys: List<UnifiedViewingKey>,
|
||||
viewingKeys: List<UnifiedFullViewingKey>,
|
||||
overwriteVks: Boolean = false
|
||||
): PagedTransactionRepository {
|
||||
initMissingDatabases(rustBackend, birthday, viewingKeys)
|
||||
|
@ -159,7 +160,7 @@ internal class PagedTransactionRepository private constructor(
|
|||
private suspend fun initMissingDatabases(
|
||||
rustBackend: RustBackend,
|
||||
birthday: Checkpoint,
|
||||
viewingKeys: List<UnifiedViewingKey>
|
||||
viewingKeys: List<UnifiedFullViewingKey>
|
||||
) {
|
||||
maybeCreateDataDb(rustBackend)
|
||||
maybeInitBlocksTable(rustBackend, birthday)
|
||||
|
@ -199,7 +200,7 @@ internal class PagedTransactionRepository private constructor(
|
|||
*/
|
||||
private suspend fun maybeInitAccountsTable(
|
||||
rustBackend: RustBackend,
|
||||
viewingKeys: List<UnifiedViewingKey>
|
||||
viewingKeys: List<UnifiedFullViewingKey>
|
||||
) {
|
||||
// TODO: consider converting these to typed exceptions in the welding layer
|
||||
tryWarn(
|
||||
|
@ -214,7 +215,7 @@ internal class PagedTransactionRepository private constructor(
|
|||
private suspend fun applyKeyMigrations(
|
||||
rustBackend: RustBackend,
|
||||
overwriteVks: Boolean,
|
||||
viewingKeys: List<UnifiedViewingKey>
|
||||
viewingKeys: List<UnifiedFullViewingKey>
|
||||
) {
|
||||
if (overwriteVks) {
|
||||
twig("applying key migrations . . .")
|
||||
|
|
|
@ -11,7 +11,7 @@ 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 cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
|
@ -50,18 +50,13 @@ internal class RustBackend private constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun initAccountsTable(vararg keys: UnifiedViewingKey): Boolean {
|
||||
val extfvks = Array(keys.size) { "" }
|
||||
val extpubs = Array(keys.size) { "" }
|
||||
keys.forEachIndexed { i, key ->
|
||||
extfvks[i] = key.extfvk
|
||||
extpubs[i] = key.extpub
|
||||
}
|
||||
override suspend fun initAccountsTable(vararg keys: UnifiedFullViewingKey): Boolean {
|
||||
val ufvks = Array(keys.size) { keys[it].encoding }
|
||||
|
||||
return withContext(SdkDispatchers.DATABASE_IO) {
|
||||
initAccountsTableWithKeys(
|
||||
pathDataDb,
|
||||
extfvks,
|
||||
extpubs,
|
||||
ufvks,
|
||||
networkId = network.id
|
||||
)
|
||||
}
|
||||
|
@ -70,8 +65,8 @@ internal class RustBackend private constructor(
|
|||
override suspend fun initAccountsTable(
|
||||
seed: ByteArray,
|
||||
numberOfAccounts: Int
|
||||
): Array<UnifiedViewingKey> {
|
||||
return DerivationTool.deriveUnifiedViewingKeys(seed, network, numberOfAccounts).apply {
|
||||
): Array<UnifiedFullViewingKey> {
|
||||
return DerivationTool.deriveUnifiedFullViewingKeys(seed, network, numberOfAccounts).apply {
|
||||
initAccountsTable(*this)
|
||||
}
|
||||
}
|
||||
|
@ -387,8 +382,7 @@ internal class RustBackend private constructor(
|
|||
@JvmStatic
|
||||
private external fun initAccountsTableWithKeys(
|
||||
dbDataPath: String,
|
||||
extfvk: Array<out String>,
|
||||
extpub: Array<out String>,
|
||||
ufvks: Array<out String>,
|
||||
networkId: Int
|
||||
): Boolean
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import cash.z.ecc.android.sdk.model.BlockHeight
|
|||
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.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
|
||||
/**
|
||||
* Contract defining the exposed capabilities of the Rust backend.
|
||||
|
@ -34,9 +34,9 @@ internal interface RustBackendWelding {
|
|||
|
||||
suspend fun decryptAndStoreTransaction(tx: ByteArray)
|
||||
|
||||
suspend fun initAccountsTable(seed: ByteArray, numberOfAccounts: Int): Array<UnifiedViewingKey>
|
||||
suspend fun initAccountsTable(seed: ByteArray, numberOfAccounts: Int): Array<UnifiedFullViewingKey>
|
||||
|
||||
suspend fun initAccountsTable(vararg keys: UnifiedViewingKey): Boolean
|
||||
suspend fun initAccountsTable(vararg keys: UnifiedFullViewingKey): Boolean
|
||||
|
||||
suspend fun initBlocksTable(checkpoint: Checkpoint): Boolean
|
||||
|
||||
|
@ -88,12 +88,12 @@ internal interface RustBackendWelding {
|
|||
|
||||
// Implemented by `DerivationTool`
|
||||
interface Derivation {
|
||||
suspend fun deriveShieldedAddress(
|
||||
suspend fun deriveUnifiedAddress(
|
||||
viewingKey: String,
|
||||
network: ZcashNetwork
|
||||
): String
|
||||
|
||||
suspend fun deriveShieldedAddress(
|
||||
suspend fun deriveUnifiedAddress(
|
||||
seed: ByteArray,
|
||||
network: ZcashNetwork,
|
||||
accountIndex: Int = 0
|
||||
|
@ -134,10 +134,10 @@ internal interface RustBackendWelding {
|
|||
network: ZcashNetwork
|
||||
): String
|
||||
|
||||
suspend fun deriveUnifiedViewingKeys(
|
||||
suspend fun deriveUnifiedFullViewingKeys(
|
||||
seed: ByteArray,
|
||||
network: ZcashNetwork,
|
||||
numberOfAccounts: Int = 1
|
||||
): Array<UnifiedViewingKey>
|
||||
): Array<UnifiedFullViewingKey>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,25 +3,25 @@ package cash.z.ecc.android.sdk.tool
|
|||
import cash.z.ecc.android.sdk.jni.RustBackend
|
||||
import cash.z.ecc.android.sdk.jni.RustBackendWelding
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
|
||||
class DerivationTool {
|
||||
|
||||
companion object : RustBackendWelding.Derivation {
|
||||
|
||||
/**
|
||||
* Given a seed and a number of accounts, return the associated viewing keys.
|
||||
* Given a seed and a number of accounts, return the associated Unified Full Viewing Keys.
|
||||
*
|
||||
* @param seed the seed from which to derive viewing keys.
|
||||
* @param numberOfAccounts the number of accounts to use. Multiple accounts are not fully
|
||||
* supported so the default value of 1 is recommended.
|
||||
*
|
||||
* @return the viewing keys that correspond to the seed, formatted as Strings.
|
||||
* @return the UFVKs derived from the seed, encoded as Strings.
|
||||
*/
|
||||
override suspend fun deriveUnifiedViewingKeys(seed: ByteArray, network: ZcashNetwork, numberOfAccounts: Int): Array<UnifiedViewingKey> =
|
||||
override suspend fun deriveUnifiedFullViewingKeys(seed: ByteArray, network: ZcashNetwork, numberOfAccounts: Int): Array<UnifiedFullViewingKey> =
|
||||
withRustBackendLoaded {
|
||||
deriveUnifiedViewingKeysFromSeed(seed, numberOfAccounts, networkId = network.id).map {
|
||||
UnifiedViewingKey(it[0], it[1])
|
||||
deriveUnifiedFullViewingKeysFromSeed(seed, numberOfAccounts, networkId = network.id).map {
|
||||
UnifiedFullViewingKey(it)
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ class DerivationTool {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a seed and account index, return the associated address.
|
||||
* 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. Multiple
|
||||
|
@ -59,21 +59,21 @@ class DerivationTool {
|
|||
*
|
||||
* @return the address that corresponds to the seed and account index.
|
||||
*/
|
||||
override suspend fun deriveShieldedAddress(seed: ByteArray, network: ZcashNetwork, accountIndex: Int): String =
|
||||
override suspend fun deriveUnifiedAddress(seed: ByteArray, network: ZcashNetwork, accountIndex: Int): String =
|
||||
withRustBackendLoaded {
|
||||
deriveShieldedAddressFromSeed(seed, accountIndex, networkId = network.id)
|
||||
deriveUnifiedAddressFromSeed(seed, accountIndex, networkId = network.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a viewing key string, return the associated address.
|
||||
* 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 suspend fun deriveShieldedAddress(viewingKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveShieldedAddressFromViewingKey(viewingKey, networkId = network.id)
|
||||
override suspend fun deriveUnifiedAddress(viewingKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveUnifiedAddressFromViewingKey(viewingKey, networkId = network.id)
|
||||
}
|
||||
|
||||
// WIP probably shouldn't be used just yet. Why?
|
||||
|
@ -95,7 +95,8 @@ class DerivationTool {
|
|||
deriveTransparentSecretKeyFromSeed(seed, account, index, networkId = network.id)
|
||||
}
|
||||
|
||||
fun validateUnifiedViewingKey(viewingKey: UnifiedViewingKey, networkId: Int = ZcashNetwork.Mainnet.id) {
|
||||
@Suppress("UnusedPrivateMember")
|
||||
fun validateUnifiedFullViewingKey(viewingKey: UnifiedFullViewingKey, networkId: Int = ZcashNetwork.Mainnet.id) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
@ -121,24 +122,24 @@ class DerivationTool {
|
|||
): Array<String>
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveUnifiedViewingKeysFromSeed(
|
||||
private external fun deriveUnifiedFullViewingKeysFromSeed(
|
||||
seed: ByteArray,
|
||||
numberOfAccounts: Int,
|
||||
networkId: Int
|
||||
): Array<Array<String>>
|
||||
): Array<String>
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveExtendedFullViewingKey(spendingKey: String, networkId: Int): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveShieldedAddressFromSeed(
|
||||
private external fun deriveUnifiedAddressFromSeed(
|
||||
seed: ByteArray,
|
||||
accountIndex: Int,
|
||||
networkId: Int
|
||||
): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveShieldedAddressFromViewingKey(key: String, networkId: Int): String
|
||||
private external fun deriveUnifiedAddressFromViewingKey(key: String, networkId: Int): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveTransparentAddressFromSeed(seed: ByteArray, account: Int, index: Int, networkId: Int): String
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package cash.z.ecc.android.sdk.type
|
||||
|
||||
/**
|
||||
* A grouping of keys that correspond to a single wallet account but do not have spend authority.
|
||||
* A [ZIP 316] Unified Full Viewing Key, corresponding to a single wallet account.
|
||||
*
|
||||
* @param extfvk the extended full viewing key which provides the ability to see inbound and
|
||||
* outbound shielded transactions. It can also be used to derive a z-addr.
|
||||
* @param extpub the extended public key which provides the ability to see transparent
|
||||
* transactions. It can also be used to derive a t-addr.
|
||||
* A `UnifiedFullViewingKey` has the authority to view transactions for an account, but
|
||||
* does not have spend authority. It can be used to derive a [UnifiedAddress] for the
|
||||
* account.
|
||||
*
|
||||
* @param[encoding] The string encoding of the UFVK.
|
||||
*/
|
||||
data class UnifiedViewingKey(
|
||||
val extfvk: String = "",
|
||||
val extpub: String = ""
|
||||
data class UnifiedFullViewingKey(
|
||||
val encoding: String = ""
|
||||
)
|
||||
|
||||
data class UnifiedAddressAccount(
|
||||
|
|
|
@ -26,14 +26,12 @@ use zcash_client_backend::{
|
|||
WalletRead,
|
||||
},
|
||||
encoding::{
|
||||
decode_extended_full_viewing_key, decode_extended_spending_key,
|
||||
encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address,
|
||||
AddressCodec,
|
||||
decode_extended_spending_key, encode_extended_full_viewing_key,
|
||||
encode_extended_spending_key, encode_payment_address, AddressCodec,
|
||||
},
|
||||
keys::{
|
||||
derive_public_key_from_seed, derive_secret_key_from_seed,
|
||||
derive_transparent_address_from_public_key, derive_transparent_address_from_secret_key,
|
||||
spending_key, Wif,
|
||||
derive_secret_key_from_seed, derive_transparent_address_from_public_key,
|
||||
derive_transparent_address_from_secret_key, spending_key, Wif,
|
||||
},
|
||||
wallet::{AccountId, OvkPolicy, WalletTransparentOutput},
|
||||
};
|
||||
|
@ -127,42 +125,39 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccount
|
|||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
db_data: JString<'_>,
|
||||
extfvks_arr: jobjectArray,
|
||||
extpubs_arr: jobjectArray,
|
||||
ufvks_arr: jobjectArray,
|
||||
network_id: jint,
|
||||
) -> jboolean {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let network = parse_network(network_id as u32)?;
|
||||
let db_data = wallet_db(&env, network, db_data)?;
|
||||
// TODO: avoid all this unwrapping and also surface errors, better
|
||||
let count = env.get_array_length(extfvks_arr).unwrap();
|
||||
let extfvks = (0..count)
|
||||
.map(|i| env.get_object_array_element(extfvks_arr, i))
|
||||
let count = env.get_array_length(ufvks_arr).unwrap();
|
||||
let ufvks = (0..count)
|
||||
.map(|i| env.get_object_array_element(ufvks_arr, i))
|
||||
.map(|jstr| utils::java_string_to_rust(&env, jstr.unwrap().into()))
|
||||
.map(|vkstr| {
|
||||
decode_extended_full_viewing_key(
|
||||
network.hrp_sapling_extended_full_viewing_key(),
|
||||
&vkstr,
|
||||
)
|
||||
.map_err(|err| format_err!("Invalid bech32: {}", err))
|
||||
.and_then(|extfvk|
|
||||
extfvk.ok_or_else(|| {
|
||||
.map(|ufvkstr| {
|
||||
// TODO: replace with `zcash_address::unified::Ufvk`
|
||||
utils::fake_ufvk_decode(&ufvkstr).ok_or_else(|| {
|
||||
let (network_name, other) = if network == TestNetwork {
|
||||
("testnet", "mainnet")
|
||||
} else {
|
||||
("mainnet", "testnet")
|
||||
};
|
||||
format_err!("Error: Wrong network! Unable to decode viewing key for {}. Check whether this is a key for {}.", network_name, other)
|
||||
}))
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let taddrs: Vec<_> = (0..count)
|
||||
.map(|i| env.get_object_array_element(extpubs_arr, i))
|
||||
.map(|jstr| utils::java_string_to_rust(&env, jstr.unwrap().into()))
|
||||
.map(|extpub_str| PublicKey::from_str(&extpub_str).unwrap())
|
||||
.map(|pk| derive_transparent_address_from_public_key(&pk))
|
||||
.collect::<Vec<_>>();
|
||||
let (taddrs, extfvks): (Vec<_>, Vec<_>) = ufvks
|
||||
.into_iter()
|
||||
.map(|(extpub, extfvk)| {
|
||||
(
|
||||
derive_transparent_address_from_public_key(&extpub.public_key),
|
||||
extfvk,
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
match init_accounts_table(&db_data, &extfvks[..], &taddrs[..]) {
|
||||
Ok(()) => Ok(JNI_TRUE),
|
||||
|
@ -210,7 +205,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveE
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedViewingKeysFromSeed(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedFullViewingKeysFromSeed(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
seed: jbyteArray,
|
||||
|
@ -226,32 +221,25 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveU
|
|||
return Err(format_err!("accounts argument must be greater than zero"));
|
||||
};
|
||||
|
||||
let extfvks: Vec<_> = (0..accounts)
|
||||
let ufvks: Vec<_> = (0..accounts)
|
||||
.map(|account| {
|
||||
encode_extended_full_viewing_key(
|
||||
network.hrp_sapling_extended_full_viewing_key(),
|
||||
&ExtendedFullViewingKey::from(&spending_key(
|
||||
&seed,
|
||||
network.coin_type(),
|
||||
AccountId(account),
|
||||
)),
|
||||
)
|
||||
let sapling = ExtendedFullViewingKey::from(&spending_key(
|
||||
&seed,
|
||||
network.coin_type(),
|
||||
AccountId(account),
|
||||
));
|
||||
let p2pkh =
|
||||
utils::p2pkh_full_viewing_key(&network, &seed, AccountId(account)).unwrap();
|
||||
// TODO: Replace with `zcash_address::unified::Ufvk`
|
||||
utils::fake_ufvk_encode(&p2pkh, &sapling)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let extpubs: Vec<_> = (0..accounts)
|
||||
.map(|account| {
|
||||
let pk =
|
||||
derive_public_key_from_seed(&network, &seed, AccountId(account), 0).unwrap();
|
||||
hex::encode(&pk.serialize())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(utils::rust_vec_to_java_2d(
|
||||
Ok(utils::rust_vec_to_java(
|
||||
&env,
|
||||
extfvks,
|
||||
extpubs,
|
||||
|env, extfvkstr| env.new_string(extfvkstr),
|
||||
ufvks,
|
||||
"java/lang/String",
|
||||
|env, ufvk| env.new_string(ufvk),
|
||||
|env| env.new_string(""),
|
||||
))
|
||||
});
|
||||
|
@ -259,7 +247,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_deriveShieldedAddressFromSeed(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedAddressFromSeed(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
seed: jbyteArray,
|
||||
|
@ -275,11 +263,16 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveS
|
|||
return Err(format_err!("accountIndex argument must be positive"));
|
||||
};
|
||||
|
||||
let address = spending_key(&seed, network.coin_type(), AccountId(account_index))
|
||||
let (di, sapling) = spending_key(&seed, network.coin_type(), AccountId(account_index))
|
||||
.default_address()
|
||||
.unwrap()
|
||||
.1;
|
||||
let address_str = encode_payment_address(network.hrp_sapling_payment_address(), &address);
|
||||
.unwrap();
|
||||
let p2pkh = utils::p2pkh_addr(
|
||||
utils::p2pkh_full_viewing_key(&network, &seed, AccountId(account_index)).unwrap(),
|
||||
di,
|
||||
)
|
||||
.unwrap();
|
||||
// TODO: replace this with `zcash_address::unified::Address`.
|
||||
let address_str = utils::fake_ua_encode(&p2pkh, &sapling);
|
||||
let output = env
|
||||
.new_string(address_str)
|
||||
.expect("Couldn't create Java string!");
|
||||
|
@ -289,33 +282,31 @@ 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_deriveShieldedAddressFromViewingKey(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedAddressFromViewingKey(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
extfvk_string: JString<'_>,
|
||||
network_id: jint,
|
||||
ufvk_string: JString<'_>,
|
||||
_network_id: jint,
|
||||
) -> jstring {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let network = parse_network(network_id as u32)?;
|
||||
let extfvk_string = utils::java_string_to_rust(&env, extfvk_string);
|
||||
let extfvk = match decode_extended_full_viewing_key(
|
||||
network.hrp_sapling_extended_full_viewing_key(),
|
||||
&extfvk_string,
|
||||
) {
|
||||
Ok(Some(extfvk)) => extfvk,
|
||||
Ok(None) => {
|
||||
return Err(format_err!("Failed to parse viewing key string in order to derive the address. Deriving a viewing key from the string returned no results. Encoding was valid but type was incorrect."));
|
||||
}
|
||||
Err(e) => {
|
||||
//let network = parse_network(network_id as u32)?;
|
||||
let ufvk_string = utils::java_string_to_rust(&env, ufvk_string);
|
||||
let ufvk = match utils::fake_ufvk_decode(&ufvk_string) {
|
||||
Some(ufvk) => ufvk,
|
||||
None => {
|
||||
return Err(format_err!(
|
||||
"Error while deriving viewing key from string input: {}",
|
||||
e
|
||||
"Error while deriving viewing key from string input"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let address = extfvk.default_address().unwrap().1;
|
||||
let address_str = encode_payment_address(network.hrp_sapling_payment_address(), &address);
|
||||
// Derive the default Sapling payment address (like older SDKs used).
|
||||
let (di, sapling) = ufvk.1.default_address().unwrap();
|
||||
// Derive the transparent address corresponding to the default Sapling diversifier
|
||||
// index (matching ZIP 316).
|
||||
let p2pkh = utils::p2pkh_addr(ufvk.0, di).unwrap();
|
||||
// TODO: replace this with `zcash_address::unified::Address`.
|
||||
let address_str = utils::fake_ua_encode(&p2pkh, &sapling);
|
||||
let output = env
|
||||
.new_string(address_str)
|
||||
.expect("Couldn't create Java string!");
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use hdwallet::{
|
||||
traits::{Deserialize, Serialize},
|
||||
ExtendedPrivKey, ExtendedPubKey, KeyIndex,
|
||||
};
|
||||
use jni::{
|
||||
descriptors::Desc,
|
||||
errors::Result as JNIResult,
|
||||
|
@ -5,7 +9,15 @@ use jni::{
|
|||
sys::{jobjectArray, jsize},
|
||||
JNIEnv,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
use zcash_client_backend::{keys::derive_transparent_address_from_public_key, wallet::AccountId};
|
||||
use zcash_primitives::{
|
||||
consensus,
|
||||
legacy::TransparentAddress,
|
||||
sapling::PaymentAddress,
|
||||
zip32::{DiversifierIndex, ExtendedFullViewingKey},
|
||||
};
|
||||
|
||||
use std::{convert::TryInto, ops::Deref};
|
||||
|
||||
pub(crate) mod exception;
|
||||
|
||||
|
@ -41,39 +53,111 @@ where
|
|||
}
|
||||
|
||||
// 2D array
|
||||
pub(crate) fn rust_vec_to_java_2d<'a, T, V, F, G>(
|
||||
env: &JNIEnv<'a>,
|
||||
data1: Vec<T>,
|
||||
data2: Vec<T>,
|
||||
element_map: F,
|
||||
empty_element: G,
|
||||
) -> jobjectArray
|
||||
where
|
||||
V: Deref<Target = JObject<'a>>,
|
||||
F: Fn(&JNIEnv<'a>, T) -> JNIResult<V>,
|
||||
G: Fn(&JNIEnv<'a>) -> JNIResult<V>,
|
||||
{
|
||||
let jempty = empty_element(env).expect("Couldn't create Java string!");
|
||||
let outer = env
|
||||
.new_object_array(
|
||||
data1.len() as jsize,
|
||||
"[Ljava/lang/String;",
|
||||
*jni::objects::JObject::null(),
|
||||
)
|
||||
.expect("Couldn't create Java array of string arrays!");
|
||||
//pub(crate) fn rust_vec_to_java_2d<'a, T, V, F, G>(
|
||||
// env: &JNIEnv<'a>,
|
||||
// data1: Vec<T>,
|
||||
// data2: Vec<T>,
|
||||
// element_map: F,
|
||||
// empty_element: G,
|
||||
//) -> jobjectArray
|
||||
//where
|
||||
// V: Deref<Target = JObject<'a>>,
|
||||
// F: Fn(&JNIEnv<'a>, T) -> JNIResult<V>,
|
||||
// G: Fn(&JNIEnv<'a>) -> JNIResult<V>,
|
||||
//{
|
||||
// let jempty = empty_element(env).expect("Couldn't create Java string!");
|
||||
// let outer = env
|
||||
// .new_object_array(
|
||||
// data1.len() as jsize,
|
||||
// "[Ljava/lang/String;",
|
||||
// *jni::objects::JObject::null(),
|
||||
// )
|
||||
// .expect("Couldn't create Java array of string arrays!");
|
||||
//
|
||||
// for (i, (elem1, elem2)) in data1.into_iter().zip(data2.into_iter()).enumerate() {
|
||||
// let inner = env
|
||||
// .new_object_array(2 as jsize, "java/lang/String", *jempty)
|
||||
// .expect("Couldn't create Java array!");
|
||||
// let jelem1 = element_map(env, elem1).expect("Couldn't map element to Java!");
|
||||
// let jelem2 = element_map(env, elem2).expect("Couldn't map element to Java!");
|
||||
// env.set_object_array_element(inner, 0 as jsize, *jelem1)
|
||||
// .expect("Couldn't set Java array element!");
|
||||
// env.set_object_array_element(inner, 1 as jsize, *jelem2)
|
||||
// .expect("Couldn't set Java array element!");
|
||||
// env.set_object_array_element(outer, i as jsize, inner)
|
||||
// .expect("Couldn't set Java array element!");
|
||||
// }
|
||||
// outer
|
||||
//}
|
||||
|
||||
for (i, (elem1, elem2)) in data1.into_iter().zip(data2.into_iter()).enumerate() {
|
||||
let inner = env
|
||||
.new_object_array(2 as jsize, "java/lang/String", *jempty)
|
||||
.expect("Couldn't create Java array!");
|
||||
let jelem1 = element_map(env, elem1).expect("Couldn't map element to Java!");
|
||||
let jelem2 = element_map(env, elem2).expect("Couldn't map element to Java!");
|
||||
env.set_object_array_element(inner, 0 as jsize, *jelem1)
|
||||
.expect("Couldn't set Java array element!");
|
||||
env.set_object_array_element(inner, 1 as jsize, *jelem2)
|
||||
.expect("Couldn't set Java array element!");
|
||||
env.set_object_array_element(outer, i as jsize, inner)
|
||||
.expect("Couldn't set Java array element!");
|
||||
}
|
||||
outer
|
||||
pub(crate) fn p2pkh_full_viewing_key<P: consensus::Parameters>(
|
||||
params: &P,
|
||||
seed: &[u8],
|
||||
account: AccountId,
|
||||
) -> Result<ExtendedPubKey, hdwallet::error::Error> {
|
||||
let pk = ExtendedPrivKey::with_seed(&seed)?;
|
||||
let private_key = pk
|
||||
.derive_private_key(KeyIndex::hardened_from_normalize_index(44)?)?
|
||||
.derive_private_key(KeyIndex::hardened_from_normalize_index(params.coin_type())?)?
|
||||
.derive_private_key(KeyIndex::hardened_from_normalize_index(account.0)?)?;
|
||||
Ok(ExtendedPubKey::from_private_key(&private_key))
|
||||
}
|
||||
|
||||
pub(crate) fn p2pkh_addr(
|
||||
fvk: ExtendedPubKey,
|
||||
index: DiversifierIndex,
|
||||
) -> Result<TransparentAddress, hdwallet::error::Error> {
|
||||
let pubkey = fvk
|
||||
.derive_public_key(KeyIndex::Normal(0))?
|
||||
.derive_public_key(KeyIndex::Normal(u32::from_le_bytes(
|
||||
index.0[..4].try_into().unwrap(),
|
||||
)))?
|
||||
.public_key;
|
||||
Ok(derive_transparent_address_from_public_key(&pubkey))
|
||||
}
|
||||
|
||||
/// This is temporary, and will be replaced by `zcash_address::unified::Ufvk`.
|
||||
pub(crate) fn fake_ufvk_encode(p2pkh: &ExtendedPubKey, sapling: &ExtendedFullViewingKey) -> String {
|
||||
let mut ufvk = p2pkh.serialize();
|
||||
sapling.write(&mut ufvk).unwrap();
|
||||
format!("DONOTUSEUFVK{}", hex::encode(&ufvk))
|
||||
}
|
||||
|
||||
/// This is temporary, and will be replaced by `zcash_address::unified::Ufvk`.
|
||||
pub(crate) fn fake_ufvk_decode(encoding: &str) -> Option<(ExtendedPubKey, ExtendedFullViewingKey)> {
|
||||
encoding
|
||||
.strip_prefix("DONOTUSEUFVK")
|
||||
.and_then(|data| hex::decode(data).ok())
|
||||
.and_then(|data| {
|
||||
ExtendedPubKey::deserialize(&data[..65])
|
||||
.ok()
|
||||
.zip(ExtendedFullViewingKey::read(&data[65..]).ok())
|
||||
})
|
||||
}
|
||||
|
||||
/// This is temporary, and will be replaced by `zcash_address::unified::Address`.
|
||||
pub(crate) fn fake_ua_encode(p2pkh: &TransparentAddress, sapling: &PaymentAddress) -> String {
|
||||
format!(
|
||||
"DONOTUSEUA{}{}",
|
||||
hex::encode(match p2pkh {
|
||||
TransparentAddress::PublicKey(data) => data,
|
||||
TransparentAddress::Script(_) => panic!(),
|
||||
}),
|
||||
hex::encode(&sapling.to_bytes())
|
||||
)
|
||||
}
|
||||
|
||||
// This is temporary, and will be replaced by `zcash_address::unified::Address`.
|
||||
//pub(crate) fn fake_ua_decode(encoding: &str) -> Option<(TransparentAddress, PaymentAddress)> {
|
||||
// encoding
|
||||
// .strip_prefix("DONOTUSEUA")
|
||||
// .and_then(|data| hex::decode(data).ok())
|
||||
// .and_then(|data| {
|
||||
// PaymentAddress::from_bytes(&data[20..].try_into().unwrap()).map(|pa| {
|
||||
// (
|
||||
// TransparentAddress::PublicKey(data[..20].try_into().unwrap()),
|
||||
// pa,
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
//}
|
||||
|
|
Loading…
Reference in New Issue