[#525] Add `AddressType.Unified` and `Synchronizer.isValidUnifiedAddr`

This commit is contained in:
Jack Grigg 2022-06-17 12:06:21 +00:00 committed by Jack Grigg
parent cfdd3640a9
commit 46af668449
11 changed files with 110 additions and 21 deletions

View File

@ -4,9 +4,8 @@ Change Log
## 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`:
- `Synchronizer.isValidUnifiedAddr`
- `cash.z.ecc.android.sdk.tool`:
- `DerivationTool.deriveTransparentAccountPrivateKey`
- `DerivationTool.deriveTransparentAddressFromAccountPrivateKey`
@ -14,6 +13,11 @@ Change Log
- `DerivationTool.deriveUnifiedFullViewingKeys`
- `DerivationTool.validateUnifiedFullViewingKey`
- Still unimplemented.
- `cash.z.ecc.android.sdk.type`:
- `AddressType.Unified`
- `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.
### Changed
- The following methods now take or return `UnifiedFullViewingKey` instead of

View File

@ -55,6 +55,7 @@ import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.AddressType
import cash.z.ecc.android.sdk.type.AddressType.Shielded
import cash.z.ecc.android.sdk.type.AddressType.Transparent
import cash.z.ecc.android.sdk.type.AddressType.Unified
import cash.z.ecc.android.sdk.type.ConsensusMatchType
import cash.z.wallet.sdk.rpc.Service
import io.grpc.ManagedChannel
@ -698,21 +699,22 @@ class SdkSynchronizer internal constructor(
override suspend fun isValidTransparentAddr(address: String) =
txManager.isValidTransparentAddress(address)
override suspend fun isValidUnifiedAddr(address: String) =
txManager.isValidUnifiedAddress(address)
override suspend fun validateAddress(address: String): AddressType {
return try {
if (isValidShieldedAddr(address)) Shielded else Transparent
} catch (zError: Throwable) {
var message = zError.message
try {
if (isValidTransparentAddr(address)) Transparent else Shielded
} catch (tError: Throwable) {
AddressType.Invalid(
if (message != tError.message) "$message and ${tError.message}" else (
message
?: "Invalid"
)
)
if (isValidShieldedAddr(address)) {
Shielded
} else if (isValidTransparentAddr(address)) {
Transparent
} else if (isValidUnifiedAddr(address)) {
Unified
} else {
AddressType.Invalid("Not a Zcash address")
}
} catch (@Suppress("TooGenericExceptionCaught") error: Throwable) {
AddressType.Invalid(error.message ?: "Invalid")
}
}

View File

@ -241,6 +241,20 @@ interface Synchronizer {
*/
suspend fun isValidTransparentAddr(address: String): Boolean
/**
* Returns true when the given address is a valid ZIP 316 unified address.
*
* This method is intended for type checking (e.g. form validation). Invalid
* addresses will throw an exception.
*
* @param address the address to validate.
*
* @return true when the given address is a valid unified address.
*
* @throws RuntimeException when the address is invalid.
*/
suspend fun isValidUnifiedAddr(address: String): Boolean
/**
* Validate whether the server and this SDK share the same consensus branch. This is
* particularly important to check around network updates so that any wallet that's connected to
@ -256,10 +270,11 @@ interface Synchronizer {
/**
* Validates the given address, returning information about why it is invalid. This is a
* convenience method that combines the behavior of [isValidShieldedAddr] and
* [isValidTransparentAddr] into one call so that the developer doesn't have to worry about
* handling the exceptions that they throw. Rather, exceptions are converted to
* [AddressType.Invalid] which has a `reason` property describing why it is invalid.
* convenience method that combines the behavior of [isValidShieldedAddr],
* [isValidTransparentAddr], and [isValidUnifiedAddr] into one call so that the developer
* doesn't have to worry about handling the exceptions that they throw. Rather, exceptions
* are converted to [AddressType.Invalid] which has a `reason` property describing why it is
* invalid.
*
* @param address the address to validate.
*

View File

@ -234,6 +234,9 @@ class PersistentTransactionManager(
override suspend fun isValidTransparentAddress(address: String) =
encoder.isValidTransparentAddress(address)
override suspend fun isValidUnifiedAddress(address: String) =
encoder.isValidUnifiedAddress(address)
override suspend fun cancel(pendingId: Long): Boolean {
return pendingTransactionDao {
val tx = findById(pendingId)

View File

@ -51,6 +51,16 @@ interface TransactionEncoder {
*/
suspend fun isValidTransparentAddress(address: String): Boolean
/**
* Utility function to help with validation. This is not called during [createTransaction]
* because this class asserts that all validation is done externally by the UI, for now.
*
* @param address the address to validate
*
* @return true when the given address is a valid ZIP 316 Unified Address
*/
suspend fun isValidUnifiedAddress(address: String): Boolean
/**
* Return the consensus branch that the encoder is using when making transactions.
*/

View File

@ -97,6 +97,15 @@ interface OutboundTransactionManager {
*/
suspend fun isValidTransparentAddress(address: String): Boolean
/**
* Return true when the given address is a valid ZIP 316 Unified Address.
*
* @param address the address to validate.
*
* @return true when the given address is a valid ZIP 316 Unified Address.
*/
suspend fun isValidUnifiedAddress(address: String): Boolean
/**
* Attempt to cancel a transaction.
*

View File

@ -81,6 +81,17 @@ internal class WalletTransactionEncoder(
override suspend fun isValidTransparentAddress(address: String): Boolean =
rustBackend.isValidTransparentAddr(address)
/**
* Utility function to help with validation. This is not called during [createTransaction]
* because this class asserts that all validation is done externally by the UI, for now.
*
* @param address the address to validate
*
* @return true when the given address is a valid ZIP 316 Unified Address
*/
override suspend fun isValidUnifiedAddress(address: String): Boolean =
rustBackend.isValidUnifiedAddr(address)
override suspend fun getConsensusBranchId(): Long {
val height = repository.lastScannedHeight()
if (height < rustBackend.network.saplingActivationHeight) {

View File

@ -310,6 +310,9 @@ internal class RustBackend private constructor(
override fun isValidTransparentAddr(addr: String) =
isValidTransparentAddress(addr, networkId = network.id)
override fun isValidUnifiedAddr(addr: String) =
isValidUnifiedAddress(addr, networkId = network.id)
override fun getBranchIdForHeight(height: BlockHeight): Long =
branchIdForHeight(height.value, networkId = network.id)
@ -409,6 +412,9 @@ internal class RustBackend private constructor(
@JvmStatic
private external fun isValidTransparentAddress(addr: String, networkId: Int): Boolean
@JvmStatic
private external fun isValidUnifiedAddress(addr: String, networkId: Int): Boolean
@JvmStatic
private external fun getBalance(dbDataPath: String, account: Int, networkId: Int): Long

View File

@ -46,6 +46,8 @@ internal interface RustBackendWelding {
fun isValidTransparentAddr(addr: String): Boolean
fun isValidUnifiedAddr(addr: String): Boolean
suspend fun getShieldedAddress(account: Int = 0): String
suspend fun getTransparentAddress(account: Int = 0, index: Int = 0): String

View File

@ -1,8 +1,9 @@
package cash.z.ecc.android.sdk.type
/**
* Validation helper class, representing the types of addresses, either Shielded, Transparent or
* Invalid. Used in conjuction with [cash.z.ecc.android.sdk.Synchronizer.validateAddress].
* Validation helper class, representing the types of addresses, either Shielded,
* Transparent, Unified, or Invalid. Used in conjuction with
* [cash.z.ecc.android.sdk.Synchronizer.validateAddress].
*/
sealed class AddressType {
/**
@ -20,6 +21,11 @@ sealed class AddressType {
*/
object Transparent : Valid, AddressType()
/**
* An instance of [AddressType] corresponding to a valid ZIP 316 unified address.
*/
object Unified : Valid, AddressType()
/**
* An instance of [AddressType] corresponding to an invalid address.
*

View File

@ -466,6 +466,27 @@ pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidTran
unwrap_exc_or(&env, res, JNI_FALSE)
}
#[no_mangle]
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_isValidUnifiedAddress(
env: JNIEnv<'_>,
_: JClass<'_>,
addr: JString<'_>,
network_id: jint,
) -> jboolean {
let res = panic::catch_unwind(|| {
let network = parse_network(network_id as u32)?;
let addr = utils::java_string_to_rust(&env, addr);
match RecipientAddress::decode(&network, &addr) {
Some(addr) => match addr {
RecipientAddress::Shielded(_) | RecipientAddress::Transparent(_) => Ok(JNI_FALSE),
},
None => Err(format_err!("Address is for the wrong network")),
}
});
unwrap_exc_or(&env, res, JNI_FALSE)
}
#[no_mangle]
pub unsafe extern "C" fn Java_cash_z_ecc_android_sdk_jni_RustBackend_getBalance(
env: JNIEnv<'_>,