New: Add UnifiedViewingKey concept.
A unified viewing keys serves as a grouping of keys that are all related to the same account but do not have spend authority. This is most important when initializing the database for scanning.
This commit is contained in:
parent
ac9f803d5b
commit
04293f4f75
|
@ -5,6 +5,7 @@ import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode
|
|||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.test.BuildConfig
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
|
@ -15,23 +16,35 @@ import org.junit.runner.RunWith
|
|||
@RunWith(AndroidJUnit4::class)
|
||||
class TransparentTest {
|
||||
|
||||
lateinit var expected: Expected
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
expected = if (BuildConfig.FLAVOR == "zcashtestnet") ExpectedTestnet else ExpectedMainnet
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deriveTransparentSecretKeyTest() {
|
||||
assertEquals(Expected.tskCompressed, DerivationTool.deriveTransparentSecretKey(SEED))
|
||||
assertEquals(expected.tskCompressed, DerivationTool.deriveTransparentSecretKey(SEED))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deriveTransparentAddressTest() {
|
||||
assertEquals(Expected.tAddr, DerivationTool.deriveTransparentAddress(SEED))
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddress(SEED))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deriveTransparentAddressFromSecretKeyTest() {
|
||||
assertEquals(Expected.tAddr, DerivationTool.deriveTransparentAddress(Expected.tskCompressed))
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPrivateKey(expected.tskCompressed))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deriveUnifiedViewingKeysFromSeedTest() {
|
||||
val uvks = DerivationTool.deriveUnifiedViewingKeys(SEED)
|
||||
assertEquals(1, uvks.size)
|
||||
val uvk = uvks.first()
|
||||
assertEquals(expected.zAddr, DerivationTool.deriveShieldedAddress(uvk.extfvk))
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPublicKey(uvk.extpub))
|
||||
}
|
||||
|
||||
// @Test
|
||||
|
@ -49,11 +62,18 @@ class TransparentTest {
|
|||
val MNEMONIC = MnemonicCode(PHRASE)
|
||||
val SEED = MNEMONIC.toSeed()
|
||||
|
||||
object Expected {
|
||||
val tAddr = "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4"
|
||||
object ExpectedMainnet : Expected {
|
||||
override val tAddr = "t1PKtYdJJHhc3Pxowmznkg7vdTwnhEsCvR4"
|
||||
override val zAddr = "zs1yc4sgtfwwzz6xfsy2xsradzr6m4aypgxhfw2vcn3hatrh5ryqsr08sgpemlg39vdh9kfupx20py"
|
||||
override val tskCompressed = "L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2"
|
||||
override val tpk = "03b1d7fb28d17c125b504d06b1530097e0a3c76ada184237e3bc0925041230a5af"
|
||||
}
|
||||
|
||||
// private key in compressed Wallet Import Format (WIF)
|
||||
val tskCompressed = "L4BvDC33yLjMRxipZvdiUmdYeRfZmR8viziwsVwe72zJdGbiJPv2"
|
||||
object ExpectedTestnet : Expected {
|
||||
override val tAddr = "tm9v3KTsjXK8XWSqiwFjic6Vda6eHY9Mjjq"
|
||||
override val zAddr = "ztestsapling1wn3tw9w5rs55x5yl586gtk72e8hcfdq8zsnjzcu8p7ghm8lrx54axc74mvm335q7lmy3g0sqje6"
|
||||
override val tskCompressed = "KzVugoXxR7AtTMdR5sdJtHxCNvMzQ4H196k7ATv4nnjoummsRC9G"
|
||||
override val tpk = "03b1d7fb28d17c125b504d06b1530097e0a3c76ada184237e3bc0925041230a5af"
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -62,4 +82,11 @@ class TransparentTest {
|
|||
Twig.plant(TroubleshootingTwig(formatter = { "@TWIG $it" }))
|
||||
}
|
||||
}
|
||||
|
||||
interface Expected {
|
||||
val tAddr: String
|
||||
val zAddr: String
|
||||
val tskCompressed: String
|
||||
val tpk: String
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import cash.z.ecc.android.sdk.ext.twig
|
|||
import cash.z.ecc.android.sdk.jni.RustBackend
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import java.io.File
|
||||
|
||||
|
@ -21,7 +22,7 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
override val alias: String
|
||||
override val host: String
|
||||
override val port: Int
|
||||
val viewingKeys: List<String>
|
||||
val viewingKeys: List<UnifiedViewingKey>
|
||||
val birthday: WalletBirthday
|
||||
|
||||
/**
|
||||
|
@ -62,7 +63,7 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
|
||||
private fun initMissingDatabases(
|
||||
birthday: WalletBirthday,
|
||||
vararg viewingKeys: String
|
||||
vararg viewingKeys: UnifiedViewingKey
|
||||
) {
|
||||
maybeCreateDataDb()
|
||||
maybeInitBlocksTable(birthday)
|
||||
|
@ -101,9 +102,10 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
/**
|
||||
* Initialize the accounts table with the given viewing keys, if needed.
|
||||
*/
|
||||
private fun maybeInitAccountsTable(vararg viewingKeys: String) {
|
||||
private fun maybeInitAccountsTable(vararg viewingKeys: UnifiedViewingKey) {
|
||||
tryWarn(
|
||||
"Warning: did not initialize the accounts table. It probably was already initialized."
|
||||
"Warning: did not initialize the accounts table. It probably was already initialized.",
|
||||
unlessContains = "constraint failed"
|
||||
) {
|
||||
rustBackend.initAccountsTable(*viewingKeys)
|
||||
accountsCreated = true
|
||||
|
@ -133,7 +135,7 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
}
|
||||
|
||||
class Config private constructor (
|
||||
val viewingKeys: MutableList<String> = mutableListOf(),
|
||||
val viewingKeys: MutableList<UnifiedViewingKey> = mutableListOf(),
|
||||
var alias: String = ZcashSdk.DEFAULT_ALIAS,
|
||||
var host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
var port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT,
|
||||
|
@ -207,10 +209,10 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
* is not currently well supported. Consider it an alpha-preview feature that might work but
|
||||
* probably has serious bugs.
|
||||
*/
|
||||
fun setViewingKeys(vararg extendedFullViewingKeys: String): Config = apply {
|
||||
fun setViewingKeys(vararg unifiedViewingKeys: UnifiedViewingKey): Config = apply {
|
||||
viewingKeys.apply {
|
||||
clear()
|
||||
addAll(extendedFullViewingKeys)
|
||||
addAll(unifiedViewingKeys)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,8 +221,8 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
* is not currently well supported. Consider it an alpha-preview feature that might work but
|
||||
* probably has serious bugs.
|
||||
*/
|
||||
fun addViewingKey(extendedFullViewingKey: String): Config = apply {
|
||||
viewingKeys.add(extendedFullViewingKey)
|
||||
fun addViewingKey(unifiedFullViewingKey: UnifiedViewingKey): Config = apply {
|
||||
viewingKeys.add(unifiedFullViewingKey)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -238,7 +240,7 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
}
|
||||
|
||||
fun importWallet(
|
||||
viewingKey: String,
|
||||
viewingKey: UnifiedViewingKey,
|
||||
birthdayHeight: Int? = null,
|
||||
host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT
|
||||
|
@ -259,7 +261,7 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
}
|
||||
|
||||
fun newWallet(
|
||||
viewingKey: String,
|
||||
viewingKey: UnifiedViewingKey,
|
||||
host: String = ZcashSdk.DEFAULT_LIGHTWALLETD_HOST,
|
||||
port: Int = ZcashSdk.DEFAULT_LIGHTWALLETD_PORT
|
||||
): Config = apply {
|
||||
|
@ -273,7 +275,7 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
* calling `setViewingKeys` with the keys that match this seed.
|
||||
*/
|
||||
fun setSeed(seed: ByteArray, numberOfAccounts: Int = 1): Config = apply {
|
||||
setViewingKeys(*DerivationTool.deriveViewingKeys(seed, numberOfAccounts))
|
||||
setViewingKeys(*DerivationTool.deriveUnifiedViewingKeys(seed, numberOfAccounts))
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -303,11 +305,11 @@ class Initializer constructor(appContext: Context, config: Config) : SdkSynchron
|
|||
|
||||
private fun validateViewingKeys() {
|
||||
require(viewingKeys.isNotEmpty()) {
|
||||
"Viewing keys are required. Ensure that the viewing keys or seed have been set" +
|
||||
" on this Initializer."
|
||||
"Unified Viewing keys are required. Ensure that the unified viewing keys or seed" +
|
||||
" have been set on this Initializer."
|
||||
}
|
||||
viewingKeys.forEach {
|
||||
DerivationTool.validateViewingKey(it)
|
||||
DerivationTool.validateUnifiedViewingKey(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,13 +399,15 @@ internal fun validateAlias(alias: String) {
|
|||
) {
|
||||
"ERROR: Invalid alias ($alias). For security, the alias must be shorter than 100 " +
|
||||
"characters and only contain letters, digits or underscores and start with a letter; " +
|
||||
"ideally, it would also differentiate across mainnet and testnet but that is not " +
|
||||
"enforced."
|
||||
"ideally, it would also differentiate across mainnet and testnet but that is not " +
|
||||
"enforced."
|
||||
}
|
||||
|
||||
// TODO: consider exposing this as a proper warning that can be received by apps, since most apps won't use logging
|
||||
if (alias.toLowerCase().contains(BuildConfig.FLAVOR.toLowerCase())) {
|
||||
twig("WARNING: alias does not contain the build flavor but it probably should to help" +
|
||||
" prevent testnet data from contaminating mainnet data.")
|
||||
twig(
|
||||
"WARNING: alias does not contain the build flavor but it probably should to help" +
|
||||
" prevent testnet data from contaminating mainnet data."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -603,7 +603,7 @@ class SdkSynchronizer internal constructor(
|
|||
memo: String
|
||||
): Flow<PendingTransaction> = flow {
|
||||
twig("Initializing shielding transaction")
|
||||
val tAddr = DerivationTool.deriveTransparentAddress(transparentSecretKey)
|
||||
val tAddr = DerivationTool.deriveTransparentAddressFromPrivateKey(transparentSecretKey)
|
||||
val tBalance = processor.getUtxoCacheBalance(tAddr)
|
||||
val zAddr = getAddress(0)
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import cash.z.ecc.android.sdk.exception.BirthdayException
|
|||
import cash.z.ecc.android.sdk.ext.ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk.SPEND_PARAM_FILE_NAME
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.WalletBalance
|
||||
import java.io.File
|
||||
|
||||
|
@ -47,13 +49,24 @@ class RustBackend private constructor() : RustBackendWelding {
|
|||
|
||||
override fun initDataDb() = initDataDb(pathDataDb)
|
||||
|
||||
override fun initAccountsTable(vararg extfvks: String) =
|
||||
initAccountsTableWithKeys(pathDataDb, extfvks)
|
||||
override 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
|
||||
}
|
||||
return initAccountsTableWithKeys(pathDataDb, extfvks, extpubs)
|
||||
}
|
||||
|
||||
override fun initAccountsTable(
|
||||
seed: ByteArray,
|
||||
numberOfAccounts: Int
|
||||
) = initAccountsTable(pathDataDb, seed, numberOfAccounts)
|
||||
): Array<UnifiedViewingKey> {
|
||||
return DerivationTool.deriveUnifiedViewingKeys(seed, numberOfAccounts).apply {
|
||||
initAccountsTable(*this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initBlocksTable(
|
||||
height: Int,
|
||||
|
@ -230,15 +243,10 @@ class RustBackend private constructor() : RustBackendWelding {
|
|||
|
||||
@JvmStatic private external fun initDataDb(dbDataPath: String): Boolean
|
||||
|
||||
@JvmStatic private external fun initAccountsTable(
|
||||
dbDataPath: String,
|
||||
seed: ByteArray,
|
||||
accounts: Int
|
||||
): Array<String>
|
||||
|
||||
@JvmStatic private external fun initAccountsTableWithKeys(
|
||||
dbDataPath: String,
|
||||
extfvk: Array<out String>
|
||||
extfvk: Array<out String>,
|
||||
extpub: Array<out String>,
|
||||
): Boolean
|
||||
|
||||
@JvmStatic private external fun initBlocksTable(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cash.z.ecc.android.sdk.jni
|
||||
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
|
||||
import cash.z.ecc.android.sdk.type.WalletBalance
|
||||
|
||||
/**
|
||||
|
@ -28,9 +29,9 @@ interface RustBackendWelding {
|
|||
|
||||
fun decryptAndStoreTransaction(tx: ByteArray)
|
||||
|
||||
fun initAccountsTable(seed: ByteArray, numberOfAccounts: Int): Array<String>
|
||||
fun initAccountsTable(seed: ByteArray, numberOfAccounts: Int): Array<UnifiedViewingKey>
|
||||
|
||||
fun initAccountsTable(vararg extfvks: String): Boolean
|
||||
fun initAccountsTable(vararg keys: UnifiedViewingKey): Boolean
|
||||
|
||||
fun initBlocksTable(height: Int, hash: String, time: Long, saplingTree: String): Boolean
|
||||
|
||||
|
@ -85,12 +86,14 @@ interface RustBackendWelding {
|
|||
|
||||
fun deriveTransparentAddress(seed: ByteArray, account: Int = 0, index: Int = 0): String
|
||||
|
||||
fun deriveTransparentAddress(transparentSecretKey: String): String
|
||||
fun deriveTransparentAddressFromPublicKey(publicKey: String): String
|
||||
|
||||
fun deriveTransparentAddressFromPrivateKey(privateKey: String): String
|
||||
|
||||
fun deriveTransparentSecretKey(seed: ByteArray, account: Int = 0, index: Int = 0): String
|
||||
|
||||
fun deriveViewingKey(spendingKey: String): String
|
||||
|
||||
fun deriveViewingKeys(seed: ByteArray, numberOfAccounts: Int = 1): Array<String>
|
||||
fun deriveUnifiedViewingKeys(seed: ByteArray, numberOfAccounts: Int = 1): Array<UnifiedViewingKey>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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.type.UnifiedViewingKey
|
||||
|
||||
class DerivationTool {
|
||||
|
||||
|
@ -16,9 +17,11 @@ class DerivationTool {
|
|||
*
|
||||
* @return the viewing keys that correspond to the seed, formatted as Strings.
|
||||
*/
|
||||
override fun deriveViewingKeys(seed: ByteArray, numberOfAccounts: Int): Array<String> =
|
||||
override fun deriveUnifiedViewingKeys(seed: ByteArray, numberOfAccounts: Int): Array<UnifiedViewingKey> =
|
||||
withRustBackendLoaded {
|
||||
deriveExtendedFullViewingKeys(seed, numberOfAccounts)
|
||||
deriveUnifiedViewingKeysFromSeed(seed, numberOfAccounts).map {
|
||||
UnifiedViewingKey(it[0], it[1])
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,15 +82,19 @@ class DerivationTool {
|
|||
deriveTransparentAddressFromSeed(seed, account, index)
|
||||
}
|
||||
|
||||
override fun deriveTransparentAddress(transparentSecretKey: String): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromSecretKey(transparentSecretKey)
|
||||
override fun deriveTransparentAddressFromPublicKey(transparentPublicKey: String): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPubKey(transparentPublicKey)
|
||||
}
|
||||
|
||||
override fun deriveTransparentAddressFromPrivateKey(transparentPrivateKey: String): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPrivKey(transparentPrivateKey)
|
||||
}
|
||||
|
||||
override fun deriveTransparentSecretKey(seed: ByteArray, account: Int, index: Int): String = withRustBackendLoaded {
|
||||
deriveTransparentSecretKeyFromSeed(seed, account, index)
|
||||
}
|
||||
|
||||
fun validateViewingKey(viewingKey: String) {
|
||||
fun validateUnifiedViewingKey(viewingKey: UnifiedViewingKey) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
@ -112,10 +119,10 @@ class DerivationTool {
|
|||
): Array<String>
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveExtendedFullViewingKeys(
|
||||
private external fun deriveUnifiedViewingKeysFromSeed(
|
||||
seed: ByteArray,
|
||||
numberOfAccounts: Int
|
||||
): Array<String>
|
||||
): Array<Array<String>>
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveExtendedFullViewingKey(spendingKey: String): String
|
||||
|
@ -133,7 +140,10 @@ class DerivationTool {
|
|||
private external fun deriveTransparentAddressFromSeed(seed: ByteArray, account: Int, index: Int): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveTransparentAddressFromSecretKey(tsk: String): String
|
||||
private external fun deriveTransparentAddressFromPubKey(pk: String): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveTransparentAddressFromPrivKey(sk: String): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveTransparentSecretKeyFromSeed(seed: ByteArray, account: Int, index: Int): String
|
||||
|
|
|
@ -30,3 +30,15 @@ data class WalletBirthday(
|
|||
val tree: String = ""
|
||||
)
|
||||
|
||||
/**
|
||||
* A grouping of keys that correspond to a single wallet account but do not have spend authority.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
data class UnifiedViewingKey(
|
||||
val extfvk: String = "",
|
||||
val extpub: String = ""
|
||||
)
|
|
@ -5,6 +5,7 @@ use std::convert::{TryFrom, TryInto};
|
|||
use std::panic;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use android_logger::Config;
|
||||
use failure::format_err;
|
||||
|
@ -20,7 +21,7 @@ use zcash_client_backend::{
|
|||
chain::{scan_cached_blocks, validate_chain},
|
||||
error::Error,
|
||||
wallet::{create_spend_to_address, decrypt_and_store_transaction},
|
||||
WalletRead, WalletWrite,
|
||||
WalletRead,
|
||||
},
|
||||
encoding::{
|
||||
AddressCodec, decode_extended_full_viewing_key,
|
||||
|
@ -28,7 +29,8 @@ use zcash_client_backend::{
|
|||
encode_payment_address,
|
||||
},
|
||||
keys::{
|
||||
derive_secret_key_from_seed, derive_transparent_address_from_secret_key,
|
||||
derive_secret_key_from_seed, derive_public_key_from_seed,
|
||||
derive_transparent_address_from_public_key, derive_transparent_address_from_secret_key,
|
||||
spending_key, Wif,
|
||||
},
|
||||
wallet::{AccountId, OvkPolicy, WalletTransparentOutput},
|
||||
|
@ -61,10 +63,9 @@ use zcash_primitives::consensus::{MAIN_NETWORK, MainNetwork};
|
|||
#[cfg(not(feature = "mainnet"))]
|
||||
use zcash_primitives::consensus::{TEST_NETWORK, TestNetwork};
|
||||
use zcash_proofs::prover::LocalTxProver;
|
||||
use secp256k1::key::SecretKey;
|
||||
use secp256k1::key::{SecretKey, PublicKey};
|
||||
|
||||
use crate::utils::exception::unwrap_exc_or;
|
||||
use zcash_client_sqlite::wallet::get_unspent_transparent_utxos;
|
||||
|
||||
mod utils;
|
||||
|
||||
|
@ -127,56 +128,13 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initDataDb(
|
|||
unwrap_exc_or(&env, res, JNI_FALSE)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccountsTable(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
db_data: JString<'_>,
|
||||
seed: jbyteArray,
|
||||
accounts: jint,
|
||||
) -> jobjectArray {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let db_data = wallet_db(&env, NETWORK, db_data)?;
|
||||
let seed = env.convert_byte_array(seed).unwrap();
|
||||
let accounts = if accounts >= 0 {
|
||||
accounts as u32
|
||||
} else {
|
||||
return Err(format_err!("accounts argument must be positive"));
|
||||
};
|
||||
|
||||
let extsks: Vec<_> = (0..accounts)
|
||||
.map(|account| spending_key(&seed, NETWORK.coin_type(), account))
|
||||
.collect();
|
||||
let extfvks: Vec<_> = extsks.iter().map(ExtendedFullViewingKey::from).collect();
|
||||
|
||||
init_accounts_table(&db_data, &extfvks)
|
||||
.map(|_| {
|
||||
// Return the ExtendedSpendingKeys for the created accounts
|
||||
utils::rust_vec_to_java(
|
||||
&env,
|
||||
extsks,
|
||||
"java/lang/String",
|
||||
|env, extsk| {
|
||||
env.new_string(encode_extended_spending_key(
|
||||
NETWORK.hrp_sapling_extended_spending_key(),
|
||||
&extsk,
|
||||
))
|
||||
},
|
||||
|env| env.new_string(""),
|
||||
)
|
||||
})
|
||||
.map_err(|e| format_err!("Error while initializing accounts: {}", e))
|
||||
});
|
||||
|
||||
unwrap_exc_or(&env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccountsTableWithKeys(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
db_data: JString<'_>,
|
||||
extfvks_arr: jobjectArray,
|
||||
extpubs_arr: jobjectArray,
|
||||
) -> jboolean {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let db_data = wallet_db(&env, NETWORK, db_data)?;
|
||||
|
@ -195,7 +153,14 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_initAccount
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match init_accounts_table(&db_data, &extfvks) {
|
||||
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();
|
||||
|
||||
match init_accounts_table(&db_data, &extfvks, &taddrs) {
|
||||
Ok(()) => Ok(JNI_TRUE),
|
||||
Err(e) => Err(format_err!("Error while initializing accounts: {}", e)),
|
||||
}
|
||||
|
@ -239,7 +204,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_deriveExtendedFullViewingKeys(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveUnifiedViewingKeysFromSeed(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
seed: jbyteArray,
|
||||
|
@ -255,19 +220,26 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveE
|
|||
|
||||
let extfvks: Vec<_> = (0..accounts)
|
||||
.map(|account| {
|
||||
ExtendedFullViewingKey::from(&spending_key(&seed, NETWORK.coin_type(), account))
|
||||
encode_extended_full_viewing_key(
|
||||
NETWORK.hrp_sapling_extended_full_viewing_key(),
|
||||
&ExtendedFullViewingKey::from(&spending_key(&seed, NETWORK.coin_type(), account))
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(utils::rust_vec_to_java(
|
||||
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(
|
||||
&env,
|
||||
extfvks,
|
||||
"java/lang/String",
|
||||
|env, extfvk| {
|
||||
env.new_string(encode_extended_full_viewing_key(
|
||||
NETWORK.hrp_sapling_extended_full_viewing_key(),
|
||||
&extfvk,
|
||||
))
|
||||
extpubs,
|
||||
|env, extfvkstr| {
|
||||
env.new_string(extfvkstr)
|
||||
},
|
||||
|env| env.new_string(""),
|
||||
))
|
||||
|
@ -704,9 +676,6 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_rewindToHei
|
|||
) -> jboolean {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let db_data = wallet_db(&env, NETWORK, db_data)?;
|
||||
let mut update_ops = (&db_data)
|
||||
.get_update_ops()
|
||||
.map_err(|e| format_err!("Could not obtain a writable database connection: {}", e))?;
|
||||
|
||||
let height = BlockHeight::try_from(height)?;
|
||||
rewind_to_height(&db_data, height)
|
||||
|
@ -889,7 +858,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveT
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentAddressFromSecretKey(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentAddressFromPrivKey(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
secret_key: JString<'_>,
|
||||
|
@ -910,6 +879,29 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveT
|
|||
unwrap_exc_or(&env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentAddressFromPubKey(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
public_key: JString<'_>,
|
||||
) -> jstring {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let public_key_str = utils::java_string_to_rust(&env, public_key);
|
||||
let pk = PublicKey::from_str(&public_key_str)?;
|
||||
let taddr =
|
||||
derive_transparent_address_from_public_key(pk)
|
||||
.encode(&NETWORK);
|
||||
|
||||
let output = env
|
||||
.new_string(taddr)
|
||||
.expect("Couldn't create Java string!");
|
||||
|
||||
Ok(output.into_inner())
|
||||
});
|
||||
unwrap_exc_or(&env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_decryptAndStoreTransaction(
|
||||
env: JNIEnv<'_>,
|
||||
|
@ -984,7 +976,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_createToAdd
|
|||
}
|
||||
};
|
||||
|
||||
let memo = Memo::from_bytes(&memo_bytes).map_err(|_| format_err!("Invalid memo"))?;;
|
||||
let memo = Memo::from_bytes(&memo_bytes).map_err(|_| format_err!("Invalid memo"))?;
|
||||
|
||||
let prover = LocalTxProver::new(Path::new(&spend_params), Path::new(&output_params));
|
||||
|
||||
|
|
|
@ -39,3 +39,32 @@ where
|
|||
}
|
||||
jret
|
||||
}
|
||||
|
||||
// 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!");
|
||||
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue