Skip to content

Commit

Permalink
Expose ScanSummary across the FFI
Browse files Browse the repository at this point in the history
Closes #1368.
  • Loading branch information
str4d committed Jan 30, 2024
1 parent bb3733e commit 7149def
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cash.z.ecc.android.sdk.internal

import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.model.JniScanRange
import cash.z.ecc.android.sdk.internal.model.JniScanSummary
import cash.z.ecc.android.sdk.internal.model.JniSubtreeRoot
import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
import cash.z.ecc.android.sdk.internal.model.JniWalletSummary
Expand Down Expand Up @@ -152,7 +153,7 @@ interface Backend {
suspend fun scanBlocks(
fromHeight: Long,
limit: Long
)
): JniScanSummary

/**
* @throws RuntimeException as a common indicator of the operation failure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import cash.z.ecc.android.sdk.internal.ext.deleteRecursivelySuspend
import cash.z.ecc.android.sdk.internal.ext.deleteSuspend
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.model.JniScanRange
import cash.z.ecc.android.sdk.internal.model.JniScanSummary
import cash.z.ecc.android.sdk.internal.model.JniSubtreeRoot
import cash.z.ecc.android.sdk.internal.model.JniUnifiedSpendingKey
import cash.z.ecc.android.sdk.internal.model.JniWalletSummary
Expand Down Expand Up @@ -272,7 +273,7 @@ class RustBackend private constructor(
override suspend fun scanBlocks(
fromHeight: Long,
limit: Long
) {
): JniScanSummary {
return withContext(SdkDispatchers.DATABASE_IO) {
scanBlocks(
fsBlockDbRoot.absolutePath,
Expand Down Expand Up @@ -566,7 +567,7 @@ class RustBackend private constructor(
fromHeight: Long,
limit: Long,
networkId: Int
)
): JniScanSummary

@JvmStatic
private external fun decryptAndStoreTransaction(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cash.z.ecc.android.sdk.internal.model

import androidx.annotation.Keep
import cash.z.ecc.android.sdk.internal.ext.isInUIntRange

/**
* Serves as cross layer (Kotlin, Rust) communication class.
*
* @param startHeight the minimum height in the scanned range (inclusive).
* Although it's type Long, it needs to be a UInt.
* @param endHeight the maximum height in the scanned range (exclusive).
* Although it's type Long, it needs to be a UInt.
* @param spentSaplingNoteCount the number of Sapling notes detected as spent in
* the scanned range.
* @param receivedSaplingNoteCount the number of Sapling notes detected as
* received in the scanned range.
* @throws IllegalArgumentException unless (startHeight and endHeight are UInts,
* and startHeight is not less than endHeight).
*/
@Keep
class JniScanSummary(
val startHeight: Long,
val endHeight: Long,
val spentSaplingNoteCount: Long,
val receivedSaplingNoteCount: Long
) {
init {
// We require some of the parameters below to be in the range of
// unsigned integer, because they are block heights.
require(startHeight.isInUIntRange()) {
"Height $startHeight is outside of allowed UInt range"
}
require(endHeight.isInUIntRange()) {
"Height $endHeight is outside of allowed UInt range"
}
require(endHeight >= startHeight) {
"End height $endHeight must be greater than start height $startHeight."
}
}
}
26 changes: 21 additions & 5 deletions backend-lib/src/main/rust/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use zcash_address::{ToAddress, ZcashAddress};
use zcash_client_backend::{
address::{Address, UnifiedAddress},
data_api::{
chain::{scan_cached_blocks, CommitmentTreeRoot},
chain::{scan_cached_blocks, CommitmentTreeRoot, ScanSummary},
scanning::{ScanPriority, ScanRange},
wallet::{
create_proposed_transaction, decrypt_and_store_transaction,
Expand Down Expand Up @@ -1245,6 +1245,23 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_suggestSc
unwrap_exc_or(&mut env, res, ptr::null_mut())
}

fn encode_scan_summary<'a>(
env: &mut JNIEnv<'a>,
scan_summary: ScanSummary,
) -> Result<JObject<'a>, failure::Error> {
let scanned_range = scan_summary.scanned_range();
Ok(env.new_object(
"cash/z/ecc/android/sdk/internal/model/JniScanSummary",
"(JJJJ)V",
&[
i64::from(u32::from(scanned_range.start)).into(),
i64::from(u32::from(scanned_range.end)).into(),
i64::try_from(scan_summary.spent_sapling_note_count())?.into(),
i64::try_from(scan_summary.received_sapling_note_count())?.into(),
],
)?)
}

#[no_mangle]
pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_scanBlocks<'local>(
mut env: JNIEnv<'local>,
Expand All @@ -1254,7 +1271,7 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_scanBlock
from_height: jlong,
limit: jlong,
network_id: jint,
) -> jboolean {
) -> jobject {
let res = catch_unwind(&mut env, |env| {
let network = parse_network(network_id as u32)?;
let db_cache = block_db(env, db_cache)?;
Expand All @@ -1263,16 +1280,15 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_scanBlock
let limit = usize::try_from(limit)?;

match scan_cached_blocks(&network, &db_cache, &mut db_data, from_height, limit) {
// TODO: Return ScanSummary.
Ok(_) => Ok(JNI_TRUE),
Ok(scan_summary) => Ok(encode_scan_summary(env, scan_summary)?.into_raw()),
Err(e) => Err(format_err!(
"Rust error while scanning blocks (limit {:?}): {}",
limit,
e
)),
}
});
unwrap_exc_or(&mut env, res, JNI_FALSE)
unwrap_exc_or(&mut env, res, ptr::null_mut())
}

#[no_mangle]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1346,7 +1346,7 @@ class CompactBlockProcessor internal constructor(
* @return Flow of [BatchSyncProgress] sync and enhancement results
*/
@VisibleForTesting
@Suppress("LongParameterList", "LongMethod")
@Suppress("CyclomaticComplexMethod", "LongParameterList", "LongMethod")
internal suspend fun runSyncingAndEnhancingOnRange(
backend: TypesafeBackend,
downloader: CompactBlockDownloader,
Expand Down Expand Up @@ -1416,17 +1416,25 @@ class CompactBlockProcessor internal constructor(
}.map { scanResult ->
Twig.debug { "Scan stage done with result: $scanResult" }

if (scanResult.stageResult != SyncingResult.ScanSuccess) {
scanResult
} else {
// Run deletion stage
SyncStageResult(
scanResult.batch,
deleteFilesOfBatchOfBlocks(
downloader = downloader,
batch = scanResult.batch
when (scanResult.stageResult) {
is SyncingResult.ScanSuccess -> {
// TODO [#1369]: Use the scan summary to trigger balance updates.
// TODO [#1369]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/1369
val scannedRange = scanResult.stageResult.summary.scannedRange
assert(scanResult.batch.range.start <= scannedRange.start)
assert(scannedRange.endInclusive <= scanResult.batch.range.endInclusive)

// Run deletion stage
SyncStageResult(
scanResult.batch,
deleteFilesOfBatchOfBlocks(
downloader = downloader,
batch = scanResult.batch
)
)
)
} else -> {
scanResult
}
}
}.onEach { continuousResult ->
Twig.debug { "Deletion stage done with result: $continuousResult" }
Expand Down Expand Up @@ -1614,7 +1622,7 @@ class CompactBlockProcessor internal constructor(
}.onFailure {
Twig.error { "Failed while scanning batch $batch with $it" }
}.fold(
onSuccess = { SyncingResult.ScanSuccess },
onSuccess = { SyncingResult.ScanSuccess(it) },
onFailure = {
// Check if the error is continuity type
if (it.isScanContinuityError()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.internal.model.ScanSummary
import cash.z.ecc.android.sdk.model.BlockHeight

/**
Expand Down Expand Up @@ -35,7 +36,9 @@ internal sealed class SyncingResult {
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()

object ScanSuccess : SyncingResult()
data class ScanSuccess(
val summary: ScanSummary
) : SyncingResult()

data class ScanFailed(
override val failedAtHeight: BlockHeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cash.z.ecc.android.sdk.internal

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
import cash.z.ecc.android.sdk.internal.model.SubtreeRoot
import cash.z.ecc.android.sdk.internal.model.TreeState
import cash.z.ecc.android.sdk.internal.model.WalletSummary
Expand Down Expand Up @@ -121,7 +122,7 @@ internal interface TypesafeBackend {
suspend fun scanBlocks(
fromHeight: BlockHeight,
limit: Long
)
): ScanSummary

/**
* @throws RuntimeException as a common indicator of the operation failure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk.internal
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
import cash.z.ecc.android.sdk.internal.model.ScanSummary
import cash.z.ecc.android.sdk.internal.model.SubtreeRoot
import cash.z.ecc.android.sdk.internal.model.TreeState
import cash.z.ecc.android.sdk.internal.model.WalletSummary
Expand Down Expand Up @@ -193,7 +194,7 @@ internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBacke
override suspend fun scanBlocks(
fromHeight: BlockHeight,
limit: Long
) = backend.scanBlocks(fromHeight.value, limit)
): ScanSummary = ScanSummary.new(backend.scanBlocks(fromHeight.value, limit), network)

override suspend fun getWalletSummary(): WalletSummary? =
backend.getWalletSummary()?.let { jniWalletSummary ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package cash.z.ecc.android.sdk.internal.model

import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork

internal data class ScanSummary(
val scannedRange: ClosedRange<BlockHeight>,
val spentSaplingNoteCount: Long,
val receivedSaplingNoteCount: Long
) {
companion object {
/**
* Note that this function subtracts 1 from [JniScanSummary.endHeight]
* as the rest of the logic works with [ClosedRange] and the endHeight
* is exclusive.
*/
fun new(
jni: JniScanSummary,
zcashNetwork: ZcashNetwork
): ScanSummary {
return ScanSummary(
scannedRange =
BlockHeight.new(
zcashNetwork,
jni.startHeight
)..(
BlockHeight.new(
zcashNetwork,
jni.endHeight
) - 1
),
spentSaplingNoteCount = jni.spentSaplingNoteCount,
receivedSaplingNoteCount = jni.receivedSaplingNoteCount
)
}
}
}

0 comments on commit 7149def

Please sign in to comment.