165 lines
6.8 KiB
Kotlin
165 lines
6.8 KiB
Kotlin
package cash.z.ecc.android.sdk.internal.db.derived
|
|
|
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
|
import cash.z.ecc.android.sdk.internal.db.CursorParser
|
|
import cash.z.ecc.android.sdk.internal.db.optBlobOrThrow
|
|
import cash.z.ecc.android.sdk.internal.db.queryAndMap
|
|
import cash.z.ecc.android.sdk.model.BlockHeight
|
|
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
|
import cash.z.ecc.android.sdk.model.Zatoshi
|
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
|
import kotlinx.coroutines.flow.first
|
|
import kotlinx.coroutines.flow.firstOrNull
|
|
import java.util.Locale
|
|
import kotlin.math.absoluteValue
|
|
|
|
internal class AllTransactionView(
|
|
private val zcashNetwork: ZcashNetwork,
|
|
private val sqliteDatabase: SupportSQLiteDatabase
|
|
) {
|
|
companion object {
|
|
|
|
private const val COLUMN_SORT_HEIGHT = "sort_height" // $NON-NLS
|
|
|
|
private val COLUMNS = arrayOf(
|
|
"*", // $NON-NLS
|
|
@Suppress("MaxLineLength")
|
|
"IFNULL(${AllTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT}, ${UInt.MAX_VALUE}) AS $COLUMN_SORT_HEIGHT" // $NON-NLS
|
|
)
|
|
|
|
private val ORDER_BY = String.format(
|
|
Locale.ROOT,
|
|
"%s DESC, %s DESC", // $NON-NLS
|
|
COLUMN_SORT_HEIGHT,
|
|
AllTransactionViewDefinition.COLUMN_INTEGER_ID
|
|
)
|
|
|
|
private val SELECTION_BLOCK_RANGE = String.format(
|
|
Locale.ROOT,
|
|
"%s >= ? AND %s <= ?", // $NON-NLS
|
|
AllTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT,
|
|
AllTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT
|
|
)
|
|
|
|
private val PROJECTION_COUNT = arrayOf("COUNT(*)") // $NON-NLS
|
|
}
|
|
|
|
private val cursorParser: CursorParser<TransactionOverview> = CursorParser { cursor ->
|
|
val idColumnIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_INTEGER_ID)
|
|
val minedHeightColumnIndex =
|
|
cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_INTEGER_MINED_HEIGHT)
|
|
val transactionIndexColumnIndex = cursor.getColumnIndex(
|
|
AllTransactionViewDefinition.COLUMN_INTEGER_TRANSACTION_INDEX
|
|
)
|
|
val rawTransactionIdIndex =
|
|
cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_BLOB_RAW_TRANSACTION_ID)
|
|
val expiryHeightIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_INTEGER_EXPIRY_HEIGHT)
|
|
val rawIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_BLOB_RAW)
|
|
val netValueIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_LONG_VALUE)
|
|
val feePaidIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_LONG_FEE_PAID)
|
|
val isChangeIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_BOOLEAN_IS_CHANGE)
|
|
val isWalletInternalIndex = cursor.getColumnIndex(
|
|
AllTransactionViewDefinition.COLUMN_BOOLEAN_IS_WALLET_INTERNAL
|
|
)
|
|
val receivedNoteCountIndex = cursor.getColumnIndex(
|
|
AllTransactionViewDefinition.COLUMN_INTEGER_RECEIVED_NOTE_COUNT
|
|
)
|
|
val sentNoteCountIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_INTEGER_SENT_NOTE_COUNT)
|
|
val memoCountIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_INTEGER_MEMO_COUNT)
|
|
val blockTimeIndex = cursor.getColumnIndex(AllTransactionViewDefinition.COLUMN_INTEGER_BLOCK_TIME)
|
|
|
|
val netValueLong = cursor.getLong(netValueIndex)
|
|
val isSent = netValueLong < 0
|
|
|
|
val expiryHeightLong = cursor.getLong(expiryHeightIndex)
|
|
|
|
TransactionOverview(
|
|
id = cursor.getLong(idColumnIndex),
|
|
rawId = FirstClassByteArray(cursor.getBlob(rawTransactionIdIndex)),
|
|
minedHeight = BlockHeight.new(zcashNetwork, cursor.getLong(minedHeightColumnIndex)),
|
|
expiryHeight = if (0L == expiryHeightLong) {
|
|
null
|
|
} else {
|
|
BlockHeight.new(zcashNetwork, expiryHeightLong)
|
|
},
|
|
index = cursor.getLong(transactionIndexColumnIndex),
|
|
raw = cursor.optBlobOrThrow(rawIndex)?.let { FirstClassByteArray(it) },
|
|
isSentTransaction = isSent,
|
|
netValue = Zatoshi(netValueLong.absoluteValue),
|
|
feePaid = Zatoshi(cursor.getLong(feePaidIndex)),
|
|
isChange = cursor.getInt(isChangeIndex) != 0,
|
|
isWalletInternal = cursor.getInt(isWalletInternalIndex) != 0,
|
|
receivedNoteCount = cursor.getInt(receivedNoteCountIndex),
|
|
sentNoteCount = cursor.getInt(sentNoteCountIndex),
|
|
memoCount = cursor.getInt(memoCountIndex),
|
|
blockTimeEpochSeconds = cursor.getLong(blockTimeIndex)
|
|
)
|
|
}
|
|
|
|
suspend fun count() = sqliteDatabase.queryAndMap(
|
|
AllTransactionViewDefinition.VIEW_NAME,
|
|
columns = PROJECTION_COUNT,
|
|
cursorParser = { it.getLong(0) }
|
|
).first()
|
|
|
|
fun getAllTransactions() =
|
|
sqliteDatabase.queryAndMap(
|
|
table = AllTransactionViewDefinition.VIEW_NAME,
|
|
columns = COLUMNS,
|
|
orderBy = ORDER_BY,
|
|
cursorParser = cursorParser
|
|
)
|
|
|
|
fun getTransactionRange(blockHeightRange: ClosedRange<BlockHeight>) =
|
|
sqliteDatabase.queryAndMap(
|
|
table = AllTransactionViewDefinition.VIEW_NAME,
|
|
columns = COLUMNS,
|
|
orderBy = ORDER_BY,
|
|
selection = SELECTION_BLOCK_RANGE,
|
|
selectionArgs = arrayOf(blockHeightRange.start.value, blockHeightRange.endInclusive.value),
|
|
cursorParser = cursorParser
|
|
)
|
|
|
|
suspend fun getOldestTransaction() =
|
|
sqliteDatabase.queryAndMap(
|
|
table = AllTransactionViewDefinition.VIEW_NAME,
|
|
columns = COLUMNS,
|
|
orderBy = ORDER_BY,
|
|
limit = "1",
|
|
cursorParser = cursorParser
|
|
).firstOrNull()
|
|
}
|
|
|
|
internal object AllTransactionViewDefinition {
|
|
const val VIEW_NAME = "v_transactions" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_ID = "id_tx" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_MINED_HEIGHT = "mined_height" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_TRANSACTION_INDEX = "tx_index" // $NON-NLS
|
|
|
|
const val COLUMN_BLOB_RAW_TRANSACTION_ID = "txid" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_EXPIRY_HEIGHT = "expiry_height" // $NON-NLS
|
|
|
|
const val COLUMN_BLOB_RAW = "raw" // $NON-NLS
|
|
|
|
const val COLUMN_LONG_VALUE = "net_value" // $NON-NLS
|
|
|
|
const val COLUMN_LONG_FEE_PAID = "fee_paid" // $NON-NLS
|
|
|
|
const val COLUMN_BOOLEAN_IS_WALLET_INTERNAL = "is_wallet_internal" // $NON-NLS
|
|
|
|
const val COLUMN_BOOLEAN_IS_CHANGE = "has_change" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_SENT_NOTE_COUNT = "sent_note_count" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_RECEIVED_NOTE_COUNT = "received_note_count" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_MEMO_COUNT = "memo_count" // $NON-NLS
|
|
|
|
const val COLUMN_INTEGER_BLOCK_TIME = "block_time" // $NON-NLS
|
|
}
|