[#525] Derive transparent account privkey instead of transparent secret key
We need the transparent account privkey in order to be able to derive the secret key for any leaf transparent address under the ZIP 316 diversified address tree.
This commit is contained in:
parent
f7c9bad367
commit
cfdd3640a9
|
@ -8,6 +8,8 @@ Change Log
|
|||
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.deriveTransparentAccountPrivateKey`
|
||||
- `DerivationTool.deriveTransparentAddressFromAccountPrivateKey`
|
||||
- `DerivationTool.deriveUnifiedAddress`
|
||||
- `DerivationTool.deriveUnifiedFullViewingKeys`
|
||||
- `DerivationTool.validateUnifiedFullViewingKey`
|
||||
|
@ -21,6 +23,9 @@ Change Log
|
|||
- `Initializer.Config.importWallet`
|
||||
- `Initializer.Config.newWallet`
|
||||
- `Initializer.Config.setViewingKeys`
|
||||
- `cash.z.ecc.android.sdk`:
|
||||
- `Synchronizer.shieldFunds` now takes a transparent account private key (representing
|
||||
all transparent secret keys within an account) instead of a transparent secret key.
|
||||
|
||||
### Removed
|
||||
- `cash.z.ecc.android.sdk.type.UnifiedViewingKey`
|
||||
|
@ -28,6 +33,10 @@ Change Log
|
|||
public key, and not the extended public key as intended. This made it incompatible
|
||||
with ZIP 316.
|
||||
- `cash.z.ecc.android.sdk.tool`:
|
||||
- `DerivationTool.deriveTransparentAddressFromPrivateKey` (use
|
||||
`DerivationTool.deriveTransparentAddressFromAccountPrivateKey` instead).
|
||||
- `DerivationTool.deriveTransparentSecretKey` (use
|
||||
`DerivationTool.deriveTransparentAccountPrivateKey` instead).
|
||||
- `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
|
||||
|
|
|
@ -63,8 +63,8 @@ class TestWallet(
|
|||
private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
private val shieldedSpendingKey =
|
||||
runBlocking { DerivationTool.deriveSpendingKeys(seed, network = network)[0] }
|
||||
private val transparentSecretKey =
|
||||
runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) }
|
||||
private val transparentAccountPrivateKey =
|
||||
runBlocking { DerivationTool.deriveTransparentAccountPrivateKey(seed, network = network) }
|
||||
val initializer = runBlocking {
|
||||
Initializer.new(context) { config ->
|
||||
runBlocking { config.importWallet(seed, startHeight, network, endpoint, alias = alias) }
|
||||
|
@ -133,7 +133,7 @@ class TestWallet(
|
|||
twig("FOUND utxo balance of total: ${walletBalance.total} available: ${walletBalance.available}")
|
||||
|
||||
if (walletBalance.available.value > 0L) {
|
||||
synchronizer.shieldFunds(shieldedSpendingKey, transparentSecretKey)
|
||||
synchronizer.shieldFunds(shieldedSpendingKey, transparentAccountPrivateKey)
|
||||
.onCompletion { twig("done shielding funds") }
|
||||
.catch { twig("Failed with $it") }
|
||||
.collect()
|
||||
|
|
|
@ -35,7 +35,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -45,7 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -120,6 +120,12 @@ version = "0.2.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74"
|
||||
|
||||
[[package]]
|
||||
name = "base58"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
|
@ -203,13 +209,25 @@ dependencies = [
|
|||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
dependencies = [
|
||||
"block-padding 0.1.5",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -218,10 +236,19 @@ version = "0.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"block-padding 0.2.1",
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.2.1"
|
||||
|
@ -257,6 +284,12 @@ version = "3.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -299,7 +332,7 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -420,13 +453,22 @@ dependencies = [
|
|||
"crypto_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -563,6 +605,15 @@ dependencies = [
|
|||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
|
@ -643,6 +694,18 @@ dependencies = [
|
|||
"secp256k1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hdwallet-bitcoin"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "969c513e03167e65d4bb59f5c51ec3820210975044ad7f218ab801fc169760fa"
|
||||
dependencies = [
|
||||
"base58",
|
||||
"hdwallet",
|
||||
"hex",
|
||||
"ripemd160 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
@ -857,6 +920,12 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
|
@ -1051,15 +1120,26 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
"block-buffer 0.9.0",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1196,11 +1276,11 @@ version = "0.9.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1539,6 +1619,7 @@ dependencies = [
|
|||
"android_logger",
|
||||
"failure",
|
||||
"hdwallet",
|
||||
"hdwallet-bitcoin",
|
||||
"hex",
|
||||
"jni",
|
||||
"log",
|
||||
|
@ -1570,7 +1651,7 @@ dependencies = [
|
|||
"protobuf",
|
||||
"protobuf-codegen-pure",
|
||||
"rand_core 0.5.1",
|
||||
"ripemd160",
|
||||
"ripemd160 0.9.1",
|
||||
"secp256k1",
|
||||
"sha2",
|
||||
"subtle",
|
||||
|
@ -1635,7 +1716,7 @@ dependencies = [
|
|||
"log",
|
||||
"rand",
|
||||
"rand_core 0.5.1",
|
||||
"ripemd160",
|
||||
"ripemd160 0.9.1",
|
||||
"secp256k1",
|
||||
"sha2",
|
||||
"subtle",
|
||||
|
|
|
@ -13,6 +13,7 @@ edition = "2018"
|
|||
android_logger = "0.9"
|
||||
failure = "0.1"
|
||||
hdwallet = "=0.3.0"
|
||||
hdwallet-bitcoin = "0.3"
|
||||
hex = "0.4"
|
||||
jni = { version = "0.17", default-features = false }
|
||||
log = "0.4"
|
||||
|
|
|
@ -22,8 +22,8 @@ import org.junit.runners.Parameterized
|
|||
class TransparentTest(val expected: Expected, val network: ZcashNetwork) {
|
||||
|
||||
@Test
|
||||
fun deriveTransparentSecretKeyTest() = runBlocking {
|
||||
assertEquals(expected.tskCompressed, DerivationTool.deriveTransparentSecretKey(SEED, network = network))
|
||||
fun deriveTransparentAccountPrivateKeyTest() = runBlocking {
|
||||
assertEquals(expected.tskCompressed, DerivationTool.deriveTransparentAccountPrivateKey(SEED, network = network))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -32,9 +32,9 @@ class TransparentTest(val expected: Expected, val network: ZcashNetwork) {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun deriveTransparentAddressFromSecretKeyTest() = runBlocking {
|
||||
val pk = DerivationTool.deriveTransparentSecretKey(SEED, network = network)
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPrivateKey(pk, network = network))
|
||||
fun deriveTransparentAddressFromAccountPrivateKeyTest() = runBlocking {
|
||||
val pk = DerivationTool.deriveTransparentAccountPrivateKey(SEED, network = network)
|
||||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromAccountPrivateKey(pk, network = network))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -162,7 +162,7 @@ class TransparentRestoreSample {
|
|||
// private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
// private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
// private val shieldedSpendingKey = DerivationTool.deriveSpendingKeys(seed, Testnet)[0]
|
||||
// private val transparentSecretKey = DerivationTool.deriveTransparentSecretKey(seed, Testnet)
|
||||
// private val transparentAccountPrivateKey = DerivationTool.deriveTransparentAccountPrivateKey(seed, Testnet)
|
||||
// private val host = "lightwalletd.testnet.electriccoin.co"
|
||||
// private val initializer = Initializer(context) { config ->
|
||||
// config.importWallet(seed, startHeight)
|
||||
|
@ -219,7 +219,7 @@ class TransparentRestoreSample {
|
|||
// twig("FOUND utxo balance of total: ${walletBalance.totalZatoshi} available: ${walletBalance.availableZatoshi}")
|
||||
//
|
||||
// if (walletBalance.availableZatoshi > 0L) {
|
||||
// synchronizer.shieldFunds(shieldedSpendingKey, transparentSecretKey)
|
||||
// synchronizer.shieldFunds(shieldedSpendingKey, transparentAccountPrivateKey)
|
||||
// .onCompletion { twig("done shielding funds") }
|
||||
// .catch { twig("Failed with $it") }
|
||||
// .collect()
|
||||
|
|
|
@ -63,8 +63,8 @@ class TestWallet(
|
|||
private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
private val shieldedSpendingKey =
|
||||
runBlocking { DerivationTool.deriveSpendingKeys(seed, network = network)[0] }
|
||||
private val transparentSecretKey =
|
||||
runBlocking { DerivationTool.deriveTransparentSecretKey(seed, network = network) }
|
||||
private val transparentAccountPrivateKey =
|
||||
runBlocking { DerivationTool.deriveTransparentAccountPrivateKey(seed, network = network) }
|
||||
val initializer = runBlocking {
|
||||
Initializer.new(context) { config ->
|
||||
runBlocking { config.importWallet(seed, startHeight, network, endpoint, alias = alias) }
|
||||
|
@ -133,7 +133,7 @@ class TestWallet(
|
|||
twig("FOUND utxo balance of total: ${walletBalance.total} available: ${walletBalance.available}")
|
||||
|
||||
if (walletBalance.available.value > 0L) {
|
||||
synchronizer.shieldFunds(shieldedSpendingKey, transparentSecretKey)
|
||||
synchronizer.shieldFunds(shieldedSpendingKey, transparentAccountPrivateKey)
|
||||
.onCompletion { twig("done shielding funds") }
|
||||
.catch { twig("Failed with $it") }
|
||||
.collect()
|
||||
|
|
|
@ -656,19 +656,19 @@ class SdkSynchronizer internal constructor(
|
|||
|
||||
override fun shieldFunds(
|
||||
spendingKey: String,
|
||||
transparentSecretKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
memo: String
|
||||
): Flow<PendingTransaction> = flow {
|
||||
twig("Initializing shielding transaction")
|
||||
val tAddr =
|
||||
DerivationTool.deriveTransparentAddressFromPrivateKey(transparentSecretKey, network)
|
||||
DerivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, network)
|
||||
val tBalance = processor.getUtxoCacheBalance(tAddr)
|
||||
val zAddr = getAddress(0)
|
||||
|
||||
// Emit the placeholder transaction, then switch to monitoring the database
|
||||
txManager.initSpend(tBalance.available, zAddr, memo, 0).let { placeHolderTx ->
|
||||
emit(placeHolderTx)
|
||||
txManager.encode(spendingKey, transparentSecretKey, placeHolderTx).let { encodedTx ->
|
||||
txManager.encode(spendingKey, transparentAccountPrivateKey, placeHolderTx).let { encodedTx ->
|
||||
// only submit if it wasn't cancelled. Otherwise cleanup, immediately for best UX.
|
||||
if (encodedTx.isCancelled()) {
|
||||
twig("[cleanup] this shielding tx has been cancelled so we will cleanup instead of submitting")
|
||||
|
|
|
@ -213,7 +213,7 @@ interface Synchronizer {
|
|||
|
||||
fun shieldFunds(
|
||||
spendingKey: String,
|
||||
transparentSecretKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
memo: String = ZcashSdk.DEFAULT_SHIELD_FUNDS_MEMO_PREFIX
|
||||
): Flow<PendingTransaction>
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ class PersistentTransactionManager(
|
|||
|
||||
override suspend fun encode(
|
||||
spendingKey: String,
|
||||
transparentSecretKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
pendingTx: PendingTransaction
|
||||
): PendingTransaction {
|
||||
twig("managing the creation of a shielding transaction")
|
||||
|
@ -153,7 +153,7 @@ class PersistentTransactionManager(
|
|||
twig("beginning to encode shielding transaction with : $encoder")
|
||||
val encodedTx = encoder.createShieldingTransaction(
|
||||
spendingKey,
|
||||
transparentSecretKey,
|
||||
transparentAccountPrivateKey,
|
||||
tx.memo
|
||||
)
|
||||
twig("successfully encoded shielding transaction!")
|
||||
|
|
|
@ -43,7 +43,7 @@ interface OutboundTransactionManager {
|
|||
|
||||
suspend fun encode(
|
||||
spendingKey: String,
|
||||
transparentSecretKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
pendingTx: PendingTransaction
|
||||
): PendingTransaction
|
||||
|
||||
|
|
|
@ -51,10 +51,10 @@ internal class WalletTransactionEncoder(
|
|||
|
||||
override suspend fun createShieldingTransaction(
|
||||
spendingKey: String,
|
||||
transparentSecretKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
memo: ByteArray?
|
||||
): EncodedTransaction {
|
||||
val transactionId = createShieldingSpend(spendingKey, transparentSecretKey, memo)
|
||||
val transactionId = createShieldingSpend(spendingKey, transparentAccountPrivateKey, memo)
|
||||
return repository.findEncodedTransactionById(transactionId)
|
||||
?: throw TransactionEncoderException.TransactionNotFoundException(transactionId)
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ internal class WalletTransactionEncoder(
|
|||
|
||||
private suspend fun createShieldingSpend(
|
||||
spendingKey: String,
|
||||
transparentSecretKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
memo: ByteArray? = byteArrayOf()
|
||||
): Long {
|
||||
return twigTask("creating transaction to shield all UTXOs") {
|
||||
|
@ -145,7 +145,7 @@ internal class WalletTransactionEncoder(
|
|||
twig("params exist! attempting to shield...")
|
||||
rustBackend.shieldToAddress(
|
||||
spendingKey,
|
||||
transparentSecretKey,
|
||||
transparentAccountPrivateKey,
|
||||
memo
|
||||
)
|
||||
} catch (t: Throwable) {
|
||||
|
|
|
@ -234,7 +234,7 @@ internal class RustBackend private constructor(
|
|||
|
||||
override suspend fun shieldToAddress(
|
||||
extsk: String,
|
||||
tsk: String,
|
||||
xprv: String,
|
||||
memo: ByteArray?
|
||||
): Long {
|
||||
twig("TMP: shieldToAddress with db path: $pathDataDb, ${memo?.size}")
|
||||
|
@ -243,7 +243,7 @@ internal class RustBackend private constructor(
|
|||
pathDataDb,
|
||||
0,
|
||||
extsk,
|
||||
tsk,
|
||||
xprv,
|
||||
memo ?: ByteArray(0),
|
||||
"$pathParamsDir/$SPEND_PARAM_FILE_NAME",
|
||||
"$pathParamsDir/$OUTPUT_PARAM_FILE_NAME",
|
||||
|
@ -491,11 +491,12 @@ internal class RustBackend private constructor(
|
|||
): Long
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("LongParameterList")
|
||||
private external fun shieldToAddress(
|
||||
dbDataPath: String,
|
||||
account: Int,
|
||||
extsk: String,
|
||||
tsk: String,
|
||||
xprv: String,
|
||||
memo: ByteArray,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
|
|
|
@ -117,16 +117,16 @@ internal interface RustBackendWelding {
|
|||
network: ZcashNetwork
|
||||
): String
|
||||
|
||||
suspend fun deriveTransparentAddressFromPrivateKey(
|
||||
suspend fun deriveTransparentAddressFromAccountPrivateKey(
|
||||
privateKey: String,
|
||||
network: ZcashNetwork
|
||||
network: ZcashNetwork,
|
||||
index: Int = 0
|
||||
): String
|
||||
|
||||
suspend fun deriveTransparentSecretKey(
|
||||
suspend fun deriveTransparentAccountPrivateKey(
|
||||
seed: ByteArray,
|
||||
network: ZcashNetwork,
|
||||
account: Int = 0,
|
||||
index: Int = 0
|
||||
account: Int = 0
|
||||
): String
|
||||
|
||||
suspend fun deriveViewingKey(
|
||||
|
|
|
@ -87,12 +87,12 @@ class DerivationTool {
|
|||
deriveTransparentAddressFromPubKey(transparentPublicKey, networkId = network.id)
|
||||
}
|
||||
|
||||
override suspend fun deriveTransparentAddressFromPrivateKey(transparentPrivateKey: String, network: ZcashNetwork): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromPrivKey(transparentPrivateKey, networkId = network.id)
|
||||
override suspend fun deriveTransparentAddressFromAccountPrivateKey(transparentPrivateKey: String, network: ZcashNetwork, index: Int): String = withRustBackendLoaded {
|
||||
deriveTransparentAddressFromAccountPrivKey(transparentPrivateKey, index, networkId = network.id)
|
||||
}
|
||||
|
||||
override suspend fun deriveTransparentSecretKey(seed: ByteArray, network: ZcashNetwork, account: Int, index: Int): String = withRustBackendLoaded {
|
||||
deriveTransparentSecretKeyFromSeed(seed, account, index, networkId = network.id)
|
||||
override suspend fun deriveTransparentAccountPrivateKey(seed: ByteArray, network: ZcashNetwork, account: Int): String = withRustBackendLoaded {
|
||||
deriveTransparentAccountPrivKeyFromSeed(seed, account, networkId = network.id)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
|
@ -148,9 +148,9 @@ class DerivationTool {
|
|||
private external fun deriveTransparentAddressFromPubKey(pk: String, networkId: Int): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveTransparentAddressFromPrivKey(sk: String, networkId: Int): String
|
||||
private external fun deriveTransparentAddressFromAccountPrivKey(sk: String, index: Int, networkId: Int): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun deriveTransparentSecretKeyFromSeed(seed: ByteArray, account: Int, index: Int, networkId: Int): String
|
||||
private external fun deriveTransparentAccountPrivKeyFromSeed(seed: ByteArray, account: Int, networkId: Int): String
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,14 @@ use std::str::FromStr;
|
|||
|
||||
use android_logger::Config;
|
||||
use failure::format_err;
|
||||
use hdwallet::traits::{Deserialize, Serialize};
|
||||
use jni::{
|
||||
objects::{JClass, JString},
|
||||
sys::{jboolean, jbyteArray, jint, jlong, jobjectArray, jstring, JNI_FALSE, JNI_TRUE},
|
||||
JNIEnv,
|
||||
};
|
||||
use log::Level;
|
||||
use secp256k1::key::{PublicKey, SecretKey};
|
||||
use secp256k1::key::PublicKey;
|
||||
use zcash_client_backend::data_api::wallet::{shield_funds, ANCHOR_OFFSET};
|
||||
use zcash_client_backend::{
|
||||
address::RecipientAddress,
|
||||
|
@ -31,7 +32,7 @@ use zcash_client_backend::{
|
|||
},
|
||||
keys::{
|
||||
derive_secret_key_from_seed, derive_transparent_address_from_public_key,
|
||||
derive_transparent_address_from_secret_key, spending_key, Wif,
|
||||
derive_transparent_address_from_secret_key, spending_key,
|
||||
},
|
||||
wallet::{AccountId, OvkPolicy, WalletTransparentOutput},
|
||||
};
|
||||
|
@ -869,31 +870,30 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_scanBlockBa
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentSecretKeyFromSeed(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentAccountPrivKeyFromSeed(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
seed: jbyteArray,
|
||||
account: jint,
|
||||
index: jint,
|
||||
network_id: jint,
|
||||
) -> jstring {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let network = parse_network(network_id as u32)?;
|
||||
let seed = env.convert_byte_array(seed).unwrap();
|
||||
let account = if account >= 0 {
|
||||
account as u32
|
||||
AccountId(account as u32)
|
||||
} else {
|
||||
return Err(format_err!("account argument must be positive"));
|
||||
};
|
||||
let index = if index >= 0 {
|
||||
index as u32
|
||||
} else {
|
||||
return Err(format_err!("index argument must be positive"));
|
||||
// Derive the BIP 32 extended privkey.
|
||||
let xprv = match utils::p2pkh_xprv(&network, &seed, account) {
|
||||
Ok(xprv) => xprv,
|
||||
Err(e) => return Err(format_err!("Invalid transparent account privkey: {:?}", e)),
|
||||
};
|
||||
let sk = derive_secret_key_from_seed(&network, &seed, AccountId(account), index).unwrap();
|
||||
let sk_wif = Wif::from_secret_key(&sk, true);
|
||||
// Encode using the BIP 32 xprv serialization format.
|
||||
let xprv_str: String = xprv.serialize();
|
||||
let output = env
|
||||
.new_string(sk_wif.0)
|
||||
.new_string(xprv_str)
|
||||
.expect("Couldn't create Java string for private key!");
|
||||
Ok(output.into_inner())
|
||||
});
|
||||
|
@ -934,17 +934,31 @@ 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_deriveTransparentAddressFromPrivKey(
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_tool_DerivationTool_deriveTransparentAddressFromAccountPrivKey(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
secret_key: JString<'_>,
|
||||
index: jint,
|
||||
network_id: jint,
|
||||
) -> jstring {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let network = parse_network(network_id as u32)?;
|
||||
let tsk_wif = utils::java_string_to_rust(&env, secret_key);
|
||||
let sk: SecretKey = (&Wif(tsk_wif)).try_into().expect("invalid private key WIF");
|
||||
let taddr = derive_transparent_address_from_secret_key(&sk).encode(&network);
|
||||
let index = if index >= 0 {
|
||||
index as u32
|
||||
} else {
|
||||
return Err(format_err!("index argument must be positive"));
|
||||
};
|
||||
let xprv_str = utils::java_string_to_rust(&env, secret_key);
|
||||
let xprv = match hdwallet_bitcoin::PrivKey::deserialize(xprv_str) {
|
||||
Ok(xprv) => xprv,
|
||||
Err(e) => return Err(format_err!("Invalid transparent extended privkey: {:?}", e)),
|
||||
};
|
||||
let tfvk = hdwallet::ExtendedPubKey::from_private_key(&xprv.extended_key);
|
||||
let taddr = match utils::p2pkh_addr_with_u32_index(tfvk, index) {
|
||||
Ok(taddr) => taddr,
|
||||
Err(e) => return Err(format_err!("Couldn't derive transparent address: {:?}", e)),
|
||||
};
|
||||
let taddr = taddr.encode(&network);
|
||||
|
||||
let output = env.new_string(taddr).expect("Couldn't create Java string!");
|
||||
|
||||
|
@ -1087,7 +1101,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
|
|||
db_data: JString<'_>,
|
||||
account: jint,
|
||||
extsk: JString<'_>,
|
||||
tsk: JString<'_>,
|
||||
xprv: JString<'_>,
|
||||
memo: jbyteArray,
|
||||
spend_params: JString<'_>,
|
||||
output_params: JString<'_>,
|
||||
|
@ -1103,7 +1117,7 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
|
|||
return Err(format_err!("account argument {} must be positive", account));
|
||||
};
|
||||
let extsk = utils::java_string_to_rust(&env, extsk);
|
||||
let tsk_wif = utils::java_string_to_rust(&env, tsk);
|
||||
let xprv_str = utils::java_string_to_rust(&env, xprv);
|
||||
let memo_bytes = env.convert_byte_array(memo).unwrap();
|
||||
let spend_params = utils::java_string_to_rust(&env, spend_params);
|
||||
let output_params = utils::java_string_to_rust(&env, output_params);
|
||||
|
@ -1119,7 +1133,19 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_shieldToAdd
|
|||
}
|
||||
};
|
||||
|
||||
let sk: SecretKey = (&Wif(tsk_wif)).try_into().expect("invalid private key WIF");
|
||||
let xprv = match hdwallet_bitcoin::PrivKey::deserialize(xprv_str) {
|
||||
Ok(xprv) => xprv,
|
||||
Err(e) => return Err(format_err!("Invalid transparent extended privkey: {:?}", e)),
|
||||
};
|
||||
let sk = match utils::p2pkh_secret_key(xprv.extended_key, 0) {
|
||||
Ok(sk) => sk,
|
||||
Err(e) => {
|
||||
return Err(format_err!(
|
||||
"Transparent extended privkey can't derive spending key 0: {:?}",
|
||||
e
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let memo = Memo::from_bytes(&memo_bytes).unwrap();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use hdwallet::{
|
||||
traits::{Deserialize, Serialize},
|
||||
ExtendedPrivKey, ExtendedPubKey, KeyIndex,
|
||||
ExtendedPrivKey, ExtendedPubKey, KeyChain, KeyIndex,
|
||||
};
|
||||
use jni::{
|
||||
descriptors::Desc,
|
||||
|
@ -9,6 +9,7 @@ use jni::{
|
|||
sys::{jobjectArray, jsize},
|
||||
JNIEnv,
|
||||
};
|
||||
use secp256k1::SecretKey;
|
||||
use zcash_client_backend::{keys::derive_transparent_address_from_public_key, wallet::AccountId};
|
||||
use zcash_primitives::{
|
||||
consensus,
|
||||
|
@ -90,6 +91,22 @@ where
|
|||
// outer
|
||||
//}
|
||||
|
||||
pub(crate) fn p2pkh_xprv<P: consensus::Parameters>(
|
||||
params: &P,
|
||||
seed: &[u8],
|
||||
account: AccountId,
|
||||
) -> Result<hdwallet_bitcoin::PrivKey, hdwallet::error::Error> {
|
||||
let master_key = ExtendedPrivKey::with_seed(&seed)?;
|
||||
let key_chain = hdwallet::DefaultKeyChain::new(master_key);
|
||||
let chain_path = format!("m/44H/{}H/{}H", params.coin_type(), account.0).into();
|
||||
let (extended_key, derivation) = key_chain.derive_private_key(chain_path)?;
|
||||
Ok(hdwallet_bitcoin::PrivKey {
|
||||
network: hdwallet_bitcoin::Network::MainNet,
|
||||
derivation,
|
||||
extended_key,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn p2pkh_full_viewing_key<P: consensus::Parameters>(
|
||||
params: &P,
|
||||
seed: &[u8],
|
||||
|
@ -106,16 +123,30 @@ pub(crate) fn p2pkh_full_viewing_key<P: consensus::Parameters>(
|
|||
pub(crate) fn p2pkh_addr(
|
||||
fvk: ExtendedPubKey,
|
||||
index: DiversifierIndex,
|
||||
) -> Result<TransparentAddress, hdwallet::error::Error> {
|
||||
p2pkh_addr_with_u32_index(fvk, u32::from_le_bytes(index.0[..4].try_into().unwrap()))
|
||||
}
|
||||
|
||||
pub(crate) fn p2pkh_addr_with_u32_index(
|
||||
fvk: ExtendedPubKey,
|
||||
index: u32,
|
||||
) -> 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(),
|
||||
)))?
|
||||
.derive_public_key(KeyIndex::Normal(index))?
|
||||
.public_key;
|
||||
Ok(derive_transparent_address_from_public_key(&pubkey))
|
||||
}
|
||||
|
||||
pub(crate) fn p2pkh_secret_key(
|
||||
tsk: ExtendedPrivKey,
|
||||
index: u32,
|
||||
) -> Result<SecretKey, hdwallet::error::Error> {
|
||||
tsk.derive_private_key(KeyIndex::Normal(0))?
|
||||
.derive_private_key(KeyIndex::Normal(index))
|
||||
.map(|k| k.private_key)
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
@ -148,16 +179,39 @@ pub(crate) fn fake_ua_encode(p2pkh: &TransparentAddress, sapling: &PaymentAddres
|
|||
}
|
||||
|
||||
// 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,
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
//}
|
||||
// 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,
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hdwallet::ExtendedPubKey;
|
||||
use zcash_client_backend::wallet::AccountId;
|
||||
use zcash_primitives::consensus::MAIN_NETWORK;
|
||||
|
||||
use super::{p2pkh_full_viewing_key, p2pkh_xprv};
|
||||
|
||||
#[test]
|
||||
fn test_transparent_xprv() {
|
||||
let params = MAIN_NETWORK;
|
||||
let seed = [0; 32];
|
||||
let account = AccountId(0);
|
||||
|
||||
assert_eq!(
|
||||
ExtendedPubKey::from_private_key(
|
||||
&p2pkh_xprv(¶ms, &seed, account).unwrap().extended_key
|
||||
),
|
||||
p2pkh_full_viewing_key(¶ms, &seed, account).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue