Replace `Synchronizer.getAddress()` with `Synchronizer.getCurrentAddress()`
The SDK now exposes UAs primarily, with additional methods for obtaining corresponding Sapling and transparent addresses for legacy compatibility. The `Account` DAO is also fixed to use the migrated `accounts` table format. The demo app now shows the current UA and the legacy Sapling and transparent addresses. Closes zcash/zcash-android-wallet-sdk#677.
This commit is contained in:
parent
f69cacb9e6
commit
597cc43886
|
@ -5,6 +5,9 @@ Change Log
|
|||
|
||||
### Added
|
||||
- `cash.z.ecc.android.sdk`:
|
||||
- `Synchronizer.getCurrentAddress`
|
||||
- `Synchronizer.getLegacySaplingAddress`
|
||||
- `Synchronizer.getLegacyTransparentAddress`
|
||||
- `Synchronizer.isValidUnifiedAddr`
|
||||
- `cash.z.ecc.android.sdk.model`:
|
||||
- `FirstClassByteArray`
|
||||
|
@ -37,6 +40,10 @@ Change Log
|
|||
all transparent secret keys within an account) instead of a transparent secret key.
|
||||
|
||||
### Removed
|
||||
- `cash.z.ecc.android.sdk`:
|
||||
- `Synchronizer.getAddress` (use `Synchronizer.getCurrentAddress` instead).
|
||||
- `Synchronizer.getShieldedAddress` (use `Synchronizer.getLegacySaplingAddress` instead).
|
||||
- `Synchronizer.getTransparentAddress` (use `Synchronizer.getLegacyTransparentAddress` instead).
|
||||
- `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
|
||||
|
|
|
@ -76,7 +76,7 @@ class SampleCodeTest {
|
|||
// ///////////////////////////////////////////////////
|
||||
// Get Address
|
||||
@Test fun getAddress() = runBlocking {
|
||||
val address = synchronizer.getAddress()
|
||||
val address = synchronizer.getCurrentAddress()
|
||||
assertFalse(address.isBlank())
|
||||
log("Address: $address")
|
||||
}
|
||||
|
|
|
@ -5,11 +5,15 @@ import android.view.LayoutInflater
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
|
||||
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetAddressBinding
|
||||
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.LightWalletEndpoint
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.model.defaultForNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.UnifiedFullViewingKey
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -21,6 +25,7 @@ import kotlinx.coroutines.runBlocking
|
|||
*/
|
||||
class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
|
||||
|
||||
private lateinit var synchronizer: Synchronizer
|
||||
private lateinit var viewingKey: UnifiedFullViewingKey
|
||||
private lateinit var seed: ByteArray
|
||||
|
||||
|
@ -36,28 +41,46 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
|
|||
// have the seed stored
|
||||
seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
|
||||
// the derivation tool can be used for generating keys and addresses
|
||||
// converting seed into viewingKey
|
||||
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 uaddress = DerivationTool.deriveUnifiedAddress(
|
||||
seed,
|
||||
ZcashNetwork.fromResources(requireApplicationContext())
|
||||
)
|
||||
binding.textInfo.text = "address:\n$uaddress"
|
||||
// using the ViewingKey to initialize
|
||||
runBlocking {
|
||||
Initializer.new(requireApplicationContext(), null) {
|
||||
val network = ZcashNetwork.fromResources(requireApplicationContext())
|
||||
it.newWallet(
|
||||
viewingKey,
|
||||
network = network,
|
||||
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(network)
|
||||
)
|
||||
}
|
||||
}.let { initializer ->
|
||||
synchronizer = Synchronizer.newBlocking(initializer)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [#677]: Show an example with the synchronizer
|
||||
// TODO [#677]: https://github.com/zcash/zcash-android-wallet-sdk/issues/677
|
||||
private fun displayAddress() {
|
||||
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
|
||||
val uaddress = synchronizer.getCurrentAddress()
|
||||
val sapling = synchronizer.getLegacySaplingAddress()
|
||||
val transparent = synchronizer.getLegacyTransparentAddress()
|
||||
binding.textInfo.text = """
|
||||
Unified Address:
|
||||
$uaddress
|
||||
|
||||
Legacy Sapling:
|
||||
$sapling
|
||||
|
||||
Legacy transparent:
|
||||
$transparent
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Android Lifecycle overrides
|
||||
|
|
|
@ -2028,6 +2028,7 @@ dependencies = [
|
|||
"schemer",
|
||||
"secp256k1",
|
||||
"secrecy",
|
||||
"zcash_address",
|
||||
"zcash_client_backend",
|
||||
"zcash_client_sqlite",
|
||||
"zcash_primitives",
|
||||
|
|
|
@ -21,6 +21,7 @@ log-panics = "2.0.0"
|
|||
schemer = "0.2"
|
||||
secp256k1 = "0.21"
|
||||
secrecy = "0.8"
|
||||
zcash_address = "0.1"
|
||||
zcash_client_backend = { version = "0.5", features = ["transparent-inputs", "unstable"] }
|
||||
zcash_client_sqlite = { version = "0.3", features = ["transparent-inputs", "unstable"] }
|
||||
zcash_primitives = "0.7"
|
||||
|
@ -30,6 +31,7 @@ zcash_proofs = "0.7"
|
|||
[patch.crates-io]
|
||||
group = { git = "https://github.com/zkcrypto/group.git", rev = "a7f3ceb2373e9fe536996f7b4d55c797f3e667f0" }
|
||||
orchard = { git = 'https://github.com/zcash/orchard.git', rev='f206b3f5d4e31bba75d03d9d03d5fa25825a9384' }
|
||||
zcash_address = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
||||
zcash_client_backend = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
||||
zcash_client_sqlite = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
||||
zcash_note_encryption = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffadf5a0120a74d70d281974d079ccd58c600' }
|
||||
|
@ -38,6 +40,7 @@ zcash_proofs = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffad
|
|||
|
||||
## Uncomment this to test librustzcash changes locally
|
||||
#[patch.crates-io]
|
||||
#zcash_address = { path = '../../clones/librustzcash/components/zcash_address' }
|
||||
#zcash_client_backend = { path = '../../clones/librustzcash/zcash_client_backend' }
|
||||
#zcash_client_sqlite = { path = '../../clones/librustzcash/zcash_client_sqlite' }
|
||||
#zcash_primitives = { path = '../../clones/librustzcash/zcash_primitives' }
|
||||
|
@ -45,6 +48,7 @@ zcash_proofs = { git = 'https://github.com/zcash/librustzcash.git', rev='774ffad
|
|||
|
||||
## Uncomment this to test someone else's librustzcash changes in a branch
|
||||
#[patch.crates-io]
|
||||
#zcash_address = { git = "https://github.com/zcash/librustzcash", branch = "branch-name" }
|
||||
#zcash_client_backend = { git = "https://github.com/zcash/librustzcash", branch = "branch-name" }
|
||||
#zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash", branch = "branch-name" }
|
||||
#zcash_primitives = { git = "https://github.com/zcash/librustzcash", branch = "branch-name" }
|
||||
|
|
|
@ -357,7 +357,7 @@ class SdkSynchronizer internal constructor(
|
|||
|
||||
suspend fun refreshUtxos() {
|
||||
twig("refreshing utxos", -1)
|
||||
refreshUtxos(getTransparentAddress())
|
||||
refreshUtxos(getLegacyTransparentAddress())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,7 +379,7 @@ class SdkSynchronizer internal constructor(
|
|||
|
||||
suspend fun refreshTransparentBalance() {
|
||||
twig("refreshing transparent balance")
|
||||
_transparentBalances.value = processor.getUtxoCacheBalance(getTransparentAddress())
|
||||
_transparentBalances.value = processor.getUtxoCacheBalance(getLegacyTransparentAddress())
|
||||
}
|
||||
|
||||
suspend fun isValidAddress(address: String): Boolean {
|
||||
|
@ -637,20 +637,22 @@ class SdkSynchronizer internal constructor(
|
|||
|
||||
override suspend fun cancelSpend(pendingId: Long) = txManager.cancel(pendingId)
|
||||
|
||||
// TODO(str4d): Rename this to getCurrentAddress (and remove/add in changelog).
|
||||
/**
|
||||
* Returns the current Unified Address for this account.
|
||||
*/
|
||||
override suspend fun getAddress(accountId: Int): String = getShieldedAddress(accountId)
|
||||
override suspend fun getCurrentAddress(accountId: Int): String =
|
||||
processor.getCurrentAddress(accountId)
|
||||
|
||||
override suspend fun getShieldedAddress(accountId: Int): String =
|
||||
processor.getShieldedAddress(accountId)
|
||||
/**
|
||||
* Returns the legacy Sapling address corresponding to the current Unified Address for this account.
|
||||
*/
|
||||
override suspend fun getLegacySaplingAddress(accountId: Int): String =
|
||||
processor.getLegacySaplingAddress(accountId)
|
||||
|
||||
// TODO(str4d): Change this to do the right thing.
|
||||
/**
|
||||
* Returns the legacy transparent address corresponding to the current Unified Address for this account.
|
||||
*/
|
||||
override suspend fun getTransparentAddress(accountId: Int): String =
|
||||
override suspend fun getLegacyTransparentAddress(accountId: Int): String =
|
||||
processor.getTransparentAddress(accountId)
|
||||
|
||||
override fun sendToAddress(
|
||||
|
@ -692,7 +694,7 @@ class SdkSynchronizer internal constructor(
|
|||
val tAddr =
|
||||
DerivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, network)
|
||||
val tBalance = processor.getUtxoCacheBalance(tAddr)
|
||||
val zAddr = getAddress(0)
|
||||
val zAddr = getCurrentAddress(0)
|
||||
|
||||
// Emit the placeholder transaction, then switch to monitoring the database
|
||||
txManager.initSpend(tBalance.available, zAddr, memo, 0).let { placeHolderTx ->
|
||||
|
|
|
@ -5,7 +5,6 @@ import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
|
|||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
|
@ -188,35 +187,34 @@ interface Synchronizer {
|
|||
// suspend fun createAccount(seed: ByteArray): UnifiedSpendingKey
|
||||
|
||||
/**
|
||||
* Gets the shielded address for the given account. This is syntactic sugar for
|
||||
* [getShieldedAddress] because we use z-addrs by default.
|
||||
* Gets the current unified address for the given account.
|
||||
*
|
||||
* @param accountId the optional accountId whose address is of interest. By default, the first
|
||||
* account is used.
|
||||
*
|
||||
* @return the shielded address for the given account.
|
||||
* @return the current unified address for the given account.
|
||||
*/
|
||||
suspend fun getAddress(accountId: Int = 0) = getShieldedAddress(accountId)
|
||||
suspend fun getCurrentAddress(accountId: Int = 0): String
|
||||
|
||||
/**
|
||||
* Gets the shielded address for the given account.
|
||||
* Gets the legacy Sapling address corresponding to the current unified address for the given account.
|
||||
*
|
||||
* @param accountId the optional accountId whose address is of interest. By default, the first
|
||||
* account is used.
|
||||
*
|
||||
* @return the shielded address for the given account.
|
||||
* @return a legacy Sapling address for the given account.
|
||||
*/
|
||||
suspend fun getShieldedAddress(accountId: Int = 0): String
|
||||
suspend fun getLegacySaplingAddress(accountId: Int = 0): String
|
||||
|
||||
/**
|
||||
* Gets the transparent address for the given account.
|
||||
* Gets the legacy transparent address corresponding to the current unified address for the given account.
|
||||
*
|
||||
* @param accountId the optional accountId whose address is of interest. By default, the first
|
||||
* account is used.
|
||||
*
|
||||
* @return the address for the given account.
|
||||
* @return a legacy transparent address for the given account.
|
||||
*/
|
||||
suspend fun getTransparentAddress(accountId: Int = 0): String
|
||||
suspend fun getLegacyTransparentAddress(accountId: Int = 0): String
|
||||
|
||||
/**
|
||||
* Sends zatoshi.
|
||||
|
|
|
@ -1023,17 +1023,34 @@ class CompactBlockProcessor internal constructor(
|
|||
rustBackend.createAccount(seed)
|
||||
|
||||
/**
|
||||
* Get address corresponding to the given account for this wallet.
|
||||
* Get the current unified address for the given wallet account.
|
||||
*
|
||||
* @return the address of this wallet.
|
||||
* @return the current unified address of this account.
|
||||
*/
|
||||
suspend fun getShieldedAddress(accountId: Int = 0) =
|
||||
repository.getAccount(accountId)?.rawShieldedAddress
|
||||
?: throw InitializerException.MissingAddressException("shielded")
|
||||
suspend fun getCurrentAddress(accountId: Int = 0) =
|
||||
rustBackend.getCurrentAddress(accountId)
|
||||
|
||||
/**
|
||||
* Get the legacy Sapling address corresponding to the current unified address for the given wallet account.
|
||||
*
|
||||
* @return a Sapling address.
|
||||
*/
|
||||
suspend fun getLegacySaplingAddress(accountId: Int = 0) =
|
||||
rustBackend.getSaplingReceiver(
|
||||
rustBackend.getCurrentAddress(accountId)
|
||||
)
|
||||
?: throw InitializerException.MissingAddressException("legacy Sapling")
|
||||
|
||||
/**
|
||||
* Get the legacy transparent address corresponding to the current unified address for the given wallet account.
|
||||
*
|
||||
* @return a transparent address.
|
||||
*/
|
||||
suspend fun getTransparentAddress(accountId: Int = 0) =
|
||||
repository.getAccount(accountId)?.rawTransparentAddress
|
||||
?: throw InitializerException.MissingAddressException("transparent")
|
||||
rustBackend.getTransparentReceiver(
|
||||
rustBackend.getCurrentAddress(accountId)
|
||||
)
|
||||
?: throw InitializerException.MissingAddressException("legacy transparent")
|
||||
|
||||
/**
|
||||
* Calculates the latest balance info. Defaults to the first account.
|
||||
|
|
|
@ -12,10 +12,5 @@ data class Account(
|
|||
val account: Int? = 0,
|
||||
|
||||
@ColumnInfo(name = "ufvk")
|
||||
val unifiedFullViewingKey: String? = "",
|
||||
|
||||
val address: String? = "",
|
||||
|
||||
@ColumnInfo(name = "transparent_address")
|
||||
val transparentAddress: String? = ""
|
||||
val unifiedFullViewingKey: String? = ""
|
||||
)
|
||||
|
|
|
@ -231,17 +231,6 @@ interface SentDao {
|
|||
interface AccountDao {
|
||||
@Query("SELECT COUNT(account) FROM accounts")
|
||||
suspend fun count(): Int
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT account AS accountId,
|
||||
transparent_address AS rawTransparentAddress,
|
||||
address AS rawShieldedAddress
|
||||
FROM accounts
|
||||
WHERE account = :id
|
||||
"""
|
||||
)
|
||||
suspend fun findAccountById(id: Int): UnifiedAddressAccount?
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -94,8 +94,6 @@ internal class PagedTransactionRepository private constructor(
|
|||
|
||||
override suspend fun count() = transactions.count()
|
||||
|
||||
override suspend fun getAccount(accountId: Int) = accounts.findAccountById(accountId)
|
||||
|
||||
override suspend fun getAccountCount() = accounts.count()
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,8 +85,6 @@ interface TransactionRepository {
|
|||
|
||||
suspend fun count(): Int
|
||||
|
||||
suspend fun getAccount(accountId: Int): UnifiedAddressAccount?
|
||||
|
||||
suspend fun getAccountCount(): Int
|
||||
|
||||
//
|
||||
|
|
|
@ -109,12 +109,9 @@ internal class RustBackend private constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun getTransparentAddress(account: Int, index: Int): String {
|
||||
throw NotImplementedError(
|
||||
"TODO: implement this at the zcash_client_sqlite level. But for now, use " +
|
||||
"DerivationTool, instead to derive addresses from seeds"
|
||||
)
|
||||
}
|
||||
override fun getTransparentReceiver(ua: String) = getTransparentReceiverForUnifiedAddress(ua)
|
||||
|
||||
override fun getSaplingReceiver(ua: String) = getSaplingReceiverForUnifiedAddress(ua)
|
||||
|
||||
override suspend fun getBalance(account: Int): Zatoshi {
|
||||
val longValue = withContext(SdkDispatchers.DATABASE_IO) {
|
||||
|
@ -426,6 +423,12 @@ internal class RustBackend private constructor(
|
|||
networkId: Int
|
||||
): String
|
||||
|
||||
@JvmStatic
|
||||
private external fun getTransparentReceiverForUnifiedAddress(ua: String): String?
|
||||
|
||||
@JvmStatic
|
||||
private external fun getSaplingReceiverForUnifiedAddress(ua: String): String?
|
||||
|
||||
@JvmStatic
|
||||
private external fun isValidShieldedAddress(addr: String, networkId: Int): Boolean
|
||||
|
||||
|
|
|
@ -53,7 +53,9 @@ internal interface RustBackendWelding {
|
|||
|
||||
suspend fun getCurrentAddress(account: Int = 0): String
|
||||
|
||||
suspend fun getTransparentAddress(account: Int = 0, index: Int = 0): String
|
||||
fun getTransparentReceiver(ua: String): String?
|
||||
|
||||
fun getSaplingReceiver(ua: String): String?
|
||||
|
||||
suspend fun getBalance(account: Int = 0): Zatoshi
|
||||
|
||||
|
|
|
@ -21,9 +21,10 @@ use log::Level;
|
|||
use schemer::MigratorError;
|
||||
use secp256k1::PublicKey;
|
||||
use secrecy::SecretVec;
|
||||
use zcash_address::{ToAddress, ZcashAddress};
|
||||
use zcash_client_backend::keys::UnifiedSpendingKey;
|
||||
use zcash_client_backend::{
|
||||
address::RecipientAddress,
|
||||
address::{RecipientAddress, UnifiedAddress},
|
||||
data_api::{
|
||||
chain::{scan_cached_blocks, validate_chain},
|
||||
error::Error,
|
||||
|
@ -490,6 +491,91 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getCurrentA
|
|||
unwrap_exc_or(&env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
struct UnifiedAddressParser(UnifiedAddress);
|
||||
|
||||
impl zcash_address::TryFromRawAddress for UnifiedAddressParser {
|
||||
type Error = failure::Error;
|
||||
|
||||
fn try_from_raw_unified(
|
||||
data: zcash_address::unified::Address,
|
||||
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
|
||||
data.try_into()
|
||||
.map(UnifiedAddressParser)
|
||||
.map_err(|e| format_err!("Invalid Unified Address: {}", e).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the transparent receiver within the given Unified Address, if any.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getTransparentReceiverForUnifiedAddress(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
ua: JString<'_>,
|
||||
) -> jstring {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let ua_str = utils::java_string_to_rust(&env, ua);
|
||||
|
||||
let (network, ua) = match ZcashAddress::try_from_encoded(&ua_str) {
|
||||
Ok(addr) => addr
|
||||
.convert::<(_, UnifiedAddressParser)>()
|
||||
.map_err(|e| format_err!("Not a Unified Address: {}", e)),
|
||||
Err(e) => return Err(format_err!("Invalid Zcash address: {}", e)),
|
||||
}?;
|
||||
|
||||
if let Some(taddr) = ua.0.transparent() {
|
||||
let taddr = match taddr {
|
||||
TransparentAddress::PublicKey(data) => {
|
||||
ZcashAddress::from_transparent_p2pkh(network, *data)
|
||||
}
|
||||
TransparentAddress::Script(data) => {
|
||||
ZcashAddress::from_transparent_p2sh(network, *data)
|
||||
}
|
||||
};
|
||||
|
||||
let output = env
|
||||
.new_string(taddr.encode())
|
||||
.expect("Couldn't create Java string!");
|
||||
Ok(output.into_inner())
|
||||
} else {
|
||||
Err(format_err!(
|
||||
"Unified Address doesn't contain a transparent receiver"
|
||||
))
|
||||
}
|
||||
});
|
||||
unwrap_exc_or(&env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Returns the Sapling receiver within the given Unified Address, if any.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getSaplingReceiverForUnifiedAddress(
|
||||
env: JNIEnv<'_>,
|
||||
_: JClass<'_>,
|
||||
ua: JString<'_>,
|
||||
) -> jstring {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let ua_str = utils::java_string_to_rust(&env, ua);
|
||||
|
||||
let (network, ua) = match ZcashAddress::try_from_encoded(&ua_str) {
|
||||
Ok(addr) => addr
|
||||
.convert::<(_, UnifiedAddressParser)>()
|
||||
.map_err(|e| format_err!("Not a Unified Address: {}", e)),
|
||||
Err(e) => return Err(format_err!("Invalid Zcash address: {}", e)),
|
||||
}?;
|
||||
|
||||
if let Some(addr) = ua.0.sapling() {
|
||||
let output = env
|
||||
.new_string(ZcashAddress::from_sapling(network, addr.to_bytes()).encode())
|
||||
.expect("Couldn't create Java string!");
|
||||
Ok(output.into_inner())
|
||||
} else {
|
||||
Err(format_err!(
|
||||
"Unified Address doesn't contain a Sapling receiver"
|
||||
))
|
||||
}
|
||||
});
|
||||
unwrap_exc_or(&env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidShieldedAddress(
|
||||
env: JNIEnv<'_>,
|
||||
|
|
Loading…
Reference in New Issue