Expose `ScanSummary` across the FFI
Closes Electric-Coin-Company/zcash-android-wallet-sdk#1368.
This commit is contained in:
parent
bb3733ec94
commit
7149def6f1
|
@ -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
|
||||
|
@ -152,7 +153,7 @@ interface Backend {
|
|||
suspend fun scanBlocks(
|
||||
fromHeight: Long,
|
||||
limit: Long
|
||||
)
|
||||
): JniScanSummary
|
||||
|
||||
/**
|
||||
* @throws RuntimeException as a common indicator of the operation failure
|
||||
|
|
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -566,7 +567,7 @@ class RustBackend private constructor(
|
|||
fromHeight: Long,
|
||||
limit: Long,
|
||||
networkId: Int
|
||||
)
|
||||
): JniScanSummary
|
||||
|
||||
@JvmStatic
|
||||
private external fun decryptAndStoreTransaction(
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
@ -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>,
|
||||
|
@ -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)?;
|
||||
|
@ -1263,8 +1280,7 @@ 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,
|
||||
|
@ -1272,7 +1288,7 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_scanBlock
|
|||
)),
|
||||
}
|
||||
});
|
||||
unwrap_exc_or(&mut env, res, JNI_FALSE)
|
||||
unwrap_exc_or(&mut env, res, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
|
@ -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,
|
||||
|
@ -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" }
|
||||
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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 ->
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue