Provide the pool type when retrieving a memo. (#1436)
* WIP: Provide the pool type when retrieving a memo. * Gitignore new idea file * Let TypesafeBackend work with typesafe class * Query and use protocol for memo obtaining --------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
315e8bf4fe
commit
7dadd57d6b
|
@ -52,6 +52,7 @@ captures/
|
|||
.idea/protoeditor.xml
|
||||
.idea/appInsightsSettings.xml
|
||||
.idea/migrations.xml
|
||||
.idea/deploymentTargetSelector.xml
|
||||
*.iml
|
||||
|
||||
# Keystore files
|
||||
|
|
|
@ -94,6 +94,7 @@ interface Backend {
|
|||
@Throws(RuntimeException::class)
|
||||
suspend fun getMemoAsUtf8(
|
||||
txId: ByteArray,
|
||||
protocol: Int,
|
||||
outputIndex: Int
|
||||
): String?
|
||||
|
||||
|
|
|
@ -125,11 +125,13 @@ class RustBackend private constructor(
|
|||
|
||||
override suspend fun getMemoAsUtf8(
|
||||
txId: ByteArray,
|
||||
protocol: Int,
|
||||
outputIndex: Int
|
||||
) = withContext(SdkDispatchers.DATABASE_IO) {
|
||||
getMemoAsUtf8(
|
||||
dataDbFile.absolutePath,
|
||||
txId,
|
||||
protocol,
|
||||
outputIndex,
|
||||
networkId = networkId
|
||||
)
|
||||
|
@ -501,6 +503,7 @@ class RustBackend private constructor(
|
|||
private external fun getMemoAsUtf8(
|
||||
dbDataPath: String,
|
||||
txId: ByteArray,
|
||||
poolType: Int,
|
||||
outputIndex: Int,
|
||||
networkId: Int
|
||||
): String?
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package cash.z.ecc.android.sdk.internal.model
|
||||
|
||||
/**
|
||||
* An enumeration of supported Zcash protocols.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
enum class ZcashProtocol {
|
||||
TRANSPARENT {
|
||||
override val poolCode = 0
|
||||
},
|
||||
SAPLING {
|
||||
override val poolCode = 2
|
||||
},
|
||||
ORCHARD {
|
||||
override val poolCode = 3
|
||||
};
|
||||
|
||||
abstract val poolCode: Int
|
||||
|
||||
fun isShielded() = this == SAPLING || this == ORCHARD
|
||||
|
||||
companion object {
|
||||
fun validate(poolTypeCode: Int): Boolean {
|
||||
return when (poolTypeCode) {
|
||||
TRANSPARENT.poolCode,
|
||||
SAPLING.poolCode,
|
||||
ORCHARD.poolCode -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun fromPoolType(poolCode: Int): ZcashProtocol {
|
||||
return when (poolCode) {
|
||||
TRANSPARENT.poolCode -> TRANSPARENT
|
||||
SAPLING.poolCode -> SAPLING
|
||||
ORCHARD.poolCode -> ORCHARD
|
||||
else -> error("Unsupported pool type: $poolCode")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -799,15 +799,25 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getTotalT
|
|||
unwrap_exc_or(&mut env, res, -1)
|
||||
}
|
||||
|
||||
fn parse_protocol(code: i32) -> Option<ShieldedProtocol> {
|
||||
match code {
|
||||
2 => Some(ShieldedProtocol::Sapling),
|
||||
3 => Some(ShieldedProtocol::Orchard),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getMemoAsUtf8<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_: JClass<'local>,
|
||||
db_data: JString<'local>,
|
||||
txid_bytes: JByteArray<'local>,
|
||||
pool_type: jint,
|
||||
output_index: jint,
|
||||
network_id: jint,
|
||||
) -> jstring {
|
||||
|
||||
let res = catch_unwind(&mut env, |env| {
|
||||
let _span = tracing::info_span!("RustBackend.getMemoAsUtf8").entered();
|
||||
let network = parse_network(network_id as u32)?;
|
||||
|
@ -815,10 +825,11 @@ pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_getMemoAs
|
|||
|
||||
let txid_bytes = env.convert_byte_array(txid_bytes)?;
|
||||
let txid = TxId::read(&txid_bytes[..])?;
|
||||
let protocol = parse_protocol(pool_type).ok_or(format_err!("Shielded protocol not recognized: {}", pool_type))?;
|
||||
let output_index = u16::try_from(output_index)?;
|
||||
|
||||
let memo = (&db_data)
|
||||
.get_memo(NoteId::new(txid, ShieldedProtocol::Sapling, output_index))
|
||||
.get_memo(NoteId::new(txid, protocol, output_index))
|
||||
.map_err(|e| format_err!("An error occurred retrieving the memo, {}", e))
|
||||
.and_then(|memo| match memo {
|
||||
Some(Memo::Empty) => Ok("".to_string()),
|
||||
|
|
|
@ -150,6 +150,7 @@ internal class FakeRustBackend(
|
|||
|
||||
override suspend fun getMemoAsUtf8(
|
||||
txId: ByteArray,
|
||||
protocol: Int,
|
||||
outputIndex: Int
|
||||
): String? = error("Intentionally not implemented in mocked FakeRustBackend implementation.")
|
||||
|
||||
|
|
|
@ -327,17 +327,25 @@ class SdkSynchronizer private constructor(
|
|||
}
|
||||
|
||||
override fun getMemos(transactionOverview: TransactionOverview): Flow<String> {
|
||||
return storage.getSaplingOutputIndices(transactionOverview.rawId).map {
|
||||
runCatching {
|
||||
backend.getMemoAsUtf8(transactionOverview.rawId.byteArray, it)
|
||||
}.onFailure {
|
||||
Twig.error { "Failed to get memo with: $it" }
|
||||
}.onSuccess {
|
||||
Twig.debug { "Transaction memo queried: $it" }
|
||||
}.fold(
|
||||
onSuccess = { it ?: "" },
|
||||
onFailure = { "" }
|
||||
)
|
||||
return storage.getOutputProperties(transactionOverview.rawId).map { properties ->
|
||||
if (!properties.protocol.isShielded()) {
|
||||
""
|
||||
} else {
|
||||
runCatching {
|
||||
backend.getMemoAsUtf8(
|
||||
txId = transactionOverview.rawId.byteArray,
|
||||
protocol = properties.protocol,
|
||||
outputIndex = properties.index
|
||||
)
|
||||
}.onFailure {
|
||||
Twig.error { "Failed to get memo with: $it" }
|
||||
}.onSuccess {
|
||||
Twig.debug { "Transaction memo queried: $it" }
|
||||
}.fold(
|
||||
onSuccess = { it ?: "" },
|
||||
onFailure = { "" }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ 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
|
||||
import cash.z.ecc.android.sdk.internal.model.ZcashProtocol
|
||||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||
|
@ -75,6 +76,7 @@ internal interface TypesafeBackend {
|
|||
|
||||
suspend fun getMemoAsUtf8(
|
||||
txId: ByteArray,
|
||||
protocol: ZcashProtocol,
|
||||
outputIndex: Int
|
||||
): String?
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ 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
|
||||
import cash.z.ecc.android.sdk.internal.model.ZcashProtocol
|
||||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||
|
@ -147,8 +148,14 @@ internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBacke
|
|||
|
||||
override suspend fun getMemoAsUtf8(
|
||||
txId: ByteArray,
|
||||
protocol: ZcashProtocol,
|
||||
outputIndex: Int
|
||||
): String? = backend.getMemoAsUtf8(txId, outputIndex)
|
||||
): String? =
|
||||
backend.getMemoAsUtf8(
|
||||
txId = txId,
|
||||
protocol = protocol.poolCode,
|
||||
outputIndex = outputIndex
|
||||
)
|
||||
|
||||
override suspend fun initDataDb(seed: ByteArray?) {
|
||||
val ret = backend.initDataDb(seed)
|
||||
|
|
|
@ -53,8 +53,9 @@ internal class DbDerivedDataRepository(
|
|||
override val allTransactions: Flow<List<DbTransactionOverview>>
|
||||
get() = invalidatingFlow.map { derivedDataDb.allTransactionView.getAllTransactions().toList() }
|
||||
|
||||
override fun getSaplingOutputIndices(transactionId: FirstClassByteArray) =
|
||||
derivedDataDb.txOutputsView.getSaplingOutputIndices(transactionId)
|
||||
override fun getOutputProperties(transactionId: FirstClassByteArray) =
|
||||
derivedDataDb.txOutputsView
|
||||
.getOutputProperties(transactionId)
|
||||
|
||||
override fun getRecipients(transactionId: FirstClassByteArray): Flow<TransactionRecipient> {
|
||||
return derivedDataDb.txOutputsView.getRecipients(transactionId)
|
||||
|
|
|
@ -2,6 +2,7 @@ package cash.z.ecc.android.sdk.internal.db.derived
|
|||
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import cash.z.ecc.android.sdk.internal.db.queryAndMap
|
||||
import cash.z.ecc.android.sdk.internal.model.OutputProperties
|
||||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||
|
@ -22,7 +23,11 @@ internal class TxOutputsView(
|
|||
TxOutputsViewDefinition.COLUMN_BLOB_TRANSACTION_ID
|
||||
)
|
||||
|
||||
private val PROJECTION_OUTPUT_INDEX = arrayOf(TxOutputsViewDefinition.COLUMN_INTEGER_OUTPUT_INDEX)
|
||||
private val PROJECTION_OUTPUT_PROPERTIES =
|
||||
arrayOf(
|
||||
TxOutputsViewDefinition.COLUMN_INTEGER_OUTPUT_INDEX,
|
||||
TxOutputsViewDefinition.COLUMN_INTEGER_OUTPUT_POOL,
|
||||
)
|
||||
|
||||
private val PROJECTION_RECIPIENT =
|
||||
arrayOf(
|
||||
|
@ -40,17 +45,22 @@ internal class TxOutputsView(
|
|||
)
|
||||
}
|
||||
|
||||
fun getSaplingOutputIndices(transactionId: FirstClassByteArray) =
|
||||
fun getOutputProperties(transactionId: FirstClassByteArray) =
|
||||
sqliteDatabase.queryAndMap(
|
||||
table = TxOutputsViewDefinition.VIEW_NAME,
|
||||
columns = PROJECTION_OUTPUT_INDEX,
|
||||
columns = PROJECTION_OUTPUT_PROPERTIES,
|
||||
selection = SELECT_BY_TRANSACTION_ID_AND_NOT_CHANGE,
|
||||
selectionArgs = arrayOf(transactionId.byteArray),
|
||||
orderBy = ORDER_BY,
|
||||
cursorParser = {
|
||||
val idColumnOutputIndex = it.getColumnIndex(TxOutputsViewDefinition.COLUMN_INTEGER_OUTPUT_INDEX)
|
||||
val idColumnOutputPoolIndex = it.getColumnIndex(TxOutputsViewDefinition.COLUMN_INTEGER_OUTPUT_POOL)
|
||||
|
||||
it.getInt(idColumnOutputIndex)
|
||||
OutputProperties.new(
|
||||
index = it.getInt(idColumnOutputIndex),
|
||||
// Converting blob to Int
|
||||
poolType = it.getInt(idColumnOutputPoolIndex)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cash.z.ecc.android.sdk.internal.model
|
||||
|
||||
/**
|
||||
* Typesafe model class representing different properties from [TxOutputsView]
|
||||
*/
|
||||
internal class OutputProperties(
|
||||
val index: Int,
|
||||
val protocol: ZcashProtocol
|
||||
) {
|
||||
companion object {
|
||||
fun new(
|
||||
index: Int,
|
||||
poolType: Int
|
||||
): OutputProperties {
|
||||
require(index >= 0) { "Output index: $index must be greater or equal to zero" }
|
||||
|
||||
require(ZcashProtocol.validate(poolType)) { "Output poolType: $poolType unknown" }
|
||||
|
||||
return OutputProperties(
|
||||
index = index,
|
||||
protocol = ZcashProtocol.fromPoolType(poolType)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package cash.z.ecc.android.sdk.internal.repository
|
|||
|
||||
import cash.z.ecc.android.sdk.internal.model.DbTransactionOverview
|
||||
import cash.z.ecc.android.sdk.internal.model.EncodedTransaction
|
||||
import cash.z.ecc.android.sdk.internal.model.OutputProperties
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||
|
@ -81,7 +82,7 @@ internal interface DerivedDataRepository {
|
|||
|
||||
val allTransactions: Flow<List<DbTransactionOverview>>
|
||||
|
||||
fun getSaplingOutputIndices(transactionId: FirstClassByteArray): Flow<Int>
|
||||
fun getOutputProperties(transactionId: FirstClassByteArray): Flow<OutputProperties>
|
||||
|
||||
fun getRecipients(transactionId: FirstClassByteArray): Flow<TransactionRecipient>
|
||||
|
||||
|
|
Loading…
Reference in New Issue