Skip to content

Commit

Permalink
[#1485] Handle several Rust API calls errors
Browse files Browse the repository at this point in the history
Closes #1485
Filed a follow-up issue #1484
  • Loading branch information
HonzaR committed May 31, 2024
1 parent 90bff31 commit dd17e39
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ interface Backend {

fun isValidUnifiedAddr(addr: String): Boolean

@Throws(RuntimeException::class)
suspend fun getCurrentAddress(account: Int): String

fun getTransparentReceiver(ua: String): String?
Expand Down Expand Up @@ -132,6 +133,7 @@ interface Backend {
* @return The height to which the wallet has been fully scanned, or Null if no blocks have been scanned.
* @throws RuntimeException as a common indicator of the operation failure
*/
@Throws(RuntimeException::class)
suspend fun getFullyScannedHeight(): Long?

/**
Expand All @@ -144,6 +146,7 @@ interface Backend {
* @return The maximum height that the wallet has scanned, or Null if no blocks have been scanned.
* @throws RuntimeException as a common indicator of the operation failure
*/
@Throws(RuntimeException::class)
suspend fun getMaxScannedHeight(): Long?

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,13 @@ class SdkSynchronizer private constructor(
override val transactions
get() =
combine(processor.networkHeight, storage.allTransactions) { networkHeight, allTransactions ->
val latestBlockHeight = networkHeight ?: backend.getMaxScannedHeight()
val latestBlockHeight =
networkHeight ?: runCatching {
backend.getMaxScannedHeight()
}.onFailure {
Twig.error(it) { "Failed to get max scanned height" }
}.getOrNull()

allTransactions.map { TransactionOverview.new(it, latestBlockHeight) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,35 @@ import cash.z.ecc.android.sdk.model.FirstClassByteArray
import cash.z.ecc.android.sdk.model.ZcashNetwork
import co.electriccoin.lightwallet.client.model.BlockHeightUnsafe

// TODO [#1484]: Validate and standardize SDK exceptions
// TODO [#1484]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/1484

/**
* Marker for all custom exceptions from the SDK. Making it an interface would result in more typing
* so it's a supertype, instead.
*/
open class SdkException(message: String, cause: Throwable?) : RuntimeException(message, cause)

/**
* Exceptions thrown in the Rust layer of the SDK. We may not always be able to surface details about this
* exception so it's important for the SDK to provide helpful messages whenever these errors are encountered.
* Exceptions thrown in the Rust layer as [RuntimeException] that the Kotlin layer translates to a more detailed ones.
* It's important for the SDK to provide helpful messages whenever these errors are encountered.
*/
sealed class RustLayerException(message: String, cause: Throwable? = null) : SdkException(message, cause) {
class BalanceException(cause: Throwable) : RustLayerException(
"Error while requesting the current balance over " +
"JNI. This might mean that the database has been corrupted and needs to be rebuilt. Verify that " +
"blocks are not missing or have not been scanned out of order.",
class GetCurrentAddressException(cause: Throwable) : RustLayerException(
"Error while requesting the current address from the Rust layer over JNI. This might mean that the SDK is " +
"not yet correctly set up.",
cause
)

class GetFullyScannedHeight(cause: Throwable) : RustLayerException(
"Error while requesting the fully scanned height from the Rust layer over JNI. This might mean that the SDK " +
"is not yet correctly set up.",
cause
)

class GetMaxScannedHeight(cause: Throwable) : RustLayerException(
"Error while requesting the max scanned height from the Rust layer over JNI. This might mean that the SDK " +
"is not yet correctly set up.",
cause
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cash.z.ecc.android.sdk.internal

import cash.z.ecc.android.sdk.exception.InitializeException
import cash.z.ecc.android.sdk.exception.RustLayerException
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.model.ScanRange
import cash.z.ecc.android.sdk.internal.model.ScanSummary
Expand Down Expand Up @@ -46,6 +47,7 @@ internal interface TypesafeBackend {
usk: UnifiedSpendingKey
): List<FirstClassByteArray>

@Throws(RustLayerException.GetCurrentAddressException::class)
suspend fun getCurrentAddress(account: Account): String

suspend fun listTransparentReceivers(account: Account): List<String>
Expand Down Expand Up @@ -107,8 +109,9 @@ internal interface TypesafeBackend {
* preceding blocks above the wallet's birthday height.
*
* @return The height to which the wallet has been fully scanned, or Null if no blocks have been scanned.
* @throws RuntimeException as a common indicator of the operation failure
* @throws RustLayerException.GetFullyScannedHeight as a common indicator of the operation failure
*/
@Throws(RustLayerException.GetFullyScannedHeight::class)
suspend fun getFullyScannedHeight(): BlockHeight?

/**
Expand All @@ -119,8 +122,9 @@ internal interface TypesafeBackend {
* height due to the fact that out-of-order scanning can leave gaps.
*
* @return The maximum height that the wallet has scanned, or Null if no blocks have been scanned.
* @throws RuntimeException as a common indicator of the operation failure
* @throws RustLayerException.GetMaxScannedHeight as a common indicator of the operation failure
*/
@Throws(RustLayerException.GetMaxScannedHeight::class)
suspend fun getMaxScannedHeight(): BlockHeight?

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cash.z.ecc.android.sdk.internal

import cash.z.ecc.android.sdk.exception.InitializeException
import cash.z.ecc.android.sdk.exception.RustLayerException
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.model.JniSubtreeRoot
import cash.z.ecc.android.sdk.internal.model.ScanRange
Expand Down Expand Up @@ -80,7 +81,11 @@ internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBacke
).map { FirstClassByteArray(it) }

override suspend fun getCurrentAddress(account: Account): String {
return backend.getCurrentAddress(account.value)
return runCatching {
backend.getCurrentAddress(account.value)
}.onFailure {
Twig.error(it) { "Failed to get current address" }
}.getOrElse { throw RustLayerException.GetCurrentAddressException(it) }
}

override suspend fun listTransparentReceivers(account: Account): List<String> {
Expand Down Expand Up @@ -195,21 +200,29 @@ internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBacke
override suspend fun updateChainTip(height: BlockHeight) = backend.updateChainTip(height.value)

override suspend fun getFullyScannedHeight(): BlockHeight? {
return backend.getFullyScannedHeight()?.let {
BlockHeight.new(
ZcashNetwork.from(backend.networkId),
it
)
}
return runCatching {
backend.getFullyScannedHeight()?.let {
BlockHeight.new(
ZcashNetwork.from(backend.networkId),
it
)
}
}.onFailure {
Twig.error(it) { "Failed to get fully scanned height" }
}.getOrElse { throw RustLayerException.GetFullyScannedHeight(it) }
}

override suspend fun getMaxScannedHeight(): BlockHeight? {
return backend.getMaxScannedHeight()?.let {
BlockHeight.new(
ZcashNetwork.from(backend.networkId),
it
)
}
return runCatching {
backend.getMaxScannedHeight()?.let {
BlockHeight.new(
ZcashNetwork.from(backend.networkId),
it
)
}
}.onFailure {
Twig.error(it) { "Failed to get max scanned height" }
}.getOrElse { throw RustLayerException.GetMaxScannedHeight(it) }
}

override suspend fun scanBlocks(
Expand Down

0 comments on commit dd17e39

Please sign in to comment.