[#1141] Implement firstUnenhancedHeight

- Transaction repository extended to provide first unenhanced height or nil

[#1141] Implement firstUnenhancedHeight (#1145)

- tests and mocks extended to be aware of firstUnenhancedBlock
This commit is contained in:
Lukas Korba 2023-06-15 16:54:09 +02:00
parent 3629861b7a
commit f1c4947a2c
12 changed files with 147 additions and 5 deletions

View File

@ -10,6 +10,7 @@ import Foundation
final class ChecksBeforeSyncAction {
let internalSyncProgress: InternalSyncProgress
let storage: CompactBlockRepository
init(container: DIContainer) {
internalSyncProgress = container.resolve(InternalSyncProgress.self)
storage = container.resolve(CompactBlockRepository.self)

View File

@ -10,6 +10,7 @@ import Foundation
final class ClearAlreadyScannedBlocksAction {
let storage: CompactBlockRepository
let transactionRepository: TransactionRepository
init(container: DIContainer) {
storage = container.resolve(CompactBlockRepository.self)
transactionRepository = container.resolve(TransactionRepository.self)

View File

@ -9,6 +9,7 @@ import Foundation
final class ClearCacheAction {
let storage: CompactBlockRepository
init(container: DIContainer) {
storage = container.resolve(CompactBlockRepository.self)
}

View File

@ -13,6 +13,7 @@ final class EnhanceAction {
let internalSyncProgress: InternalSyncProgress
let logger: Logger
let transactionRepository: TransactionRepository
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
blockEnhancer = container.resolve(BlockEnhancer.self)
self.configProvider = configProvider

View File

@ -34,15 +34,74 @@ struct Block: Codable {
let hash: Data
let time: Int
let saplingTree: Data
static let table = Table("blocks")
}
struct VTransaction: Codable {
enum CodingKeys: String, CodingKey {
case accountId = "account_id"
case idTx = "id_tx"
case minedHeight = "mined_height"
case txIndex = "tx_index"
case txId = "txid"
case expiryHeight = "expiry_height"
case raw = "raw"
case accountBalanceDelta = "account_balance_delta"
case feePaid = "fee_paid"
case expiredUnmined = "expired_unmined"
case hasChange = "has_change"
case sentNoteCount = "sent_note_count"
case recievedNoteCount = "received_note_count"
case memoCount = "memo_count"
case blockTime = "block_time"
}
enum TableStructure {
static let accountId = Expression<Int>(VTransaction.CodingKeys.accountId.rawValue)
static let idTx = Expression<Int>(VTransaction.CodingKeys.idTx.rawValue)
static let minedHeight = Expression<Int>(VTransaction.CodingKeys.minedHeight.rawValue)
static let txIndex = Expression<Int>(VTransaction.CodingKeys.txIndex.rawValue)
static let txId = Expression<Data>(VTransaction.CodingKeys.txId.rawValue)
static let expiryHeight = Expression<Int?>(VTransaction.CodingKeys.expiryHeight.rawValue)
static let raw = Expression<Data?>(VTransaction.CodingKeys.raw.rawValue)
static let accountBalanceDelta = Expression<Int>(VTransaction.CodingKeys.accountBalanceDelta.rawValue)
static let feePaid = Expression<Int?>(VTransaction.CodingKeys.feePaid.rawValue)
static let expiredUnmined = Expression<Int>(VTransaction.CodingKeys.expiredUnmined.rawValue)
static let hasChange = Expression<Bool>(VTransaction.CodingKeys.hasChange.rawValue)
static let sentNoteCount = Expression<Int>(VTransaction.CodingKeys.sentNoteCount.rawValue)
static let recievedNoteCount = Expression<Int>(VTransaction.CodingKeys.recievedNoteCount.rawValue)
static let memoCount = Expression<Int>(VTransaction.CodingKeys.memoCount.rawValue)
static let blockTime = Expression<Int>(VTransaction.CodingKeys.blockTime.rawValue)
}
let accountId: Int
let idTx: Int
let minedHeight: Int
let txIndex: Int
let txId: Data
let expiryHeight: Int?
let raw: Data?
let accountBalanceDelta: Int
let feePaid: Int?
let expiredUnmined: Int
let hasChange: Bool
let sentNoteCount: Int
let recievedNoteCount: Int
let memoCount: Int
let blockTime: Int
static let table = Table("v_transactions")
}
class BlockSQLDAO: BlockDao {
let dbProvider: ConnectionProvider
let table: Table
let height = Expression<Int>("height")
let minedHeight = Expression<Int>("mined_height")
let raw = Expression<Data?>("raw")
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
self.table = Table("Blocks")
@ -103,6 +162,30 @@ class BlockSQLDAO: BlockDao {
}
}
}
func firstUnenhancedHeight(in range: CompactBlockRange? = nil) throws -> BlockHeight? {
do {
return try dbProvider
.connection()
.prepare(
VTransaction.table
.order(minedHeight.asc)
.filter(raw == nil)
.limit(1)
)
.map {
do {
let vTransaction: VTransaction = try $0.decode()
return vTransaction.minedHeight
} catch {
throw ZcashError.blockDAOFirstUnenhancedCantDecode(error)
}
}
.first
} catch {
throw ZcashError.blockDAOFirstUnenhancedHeight(error)
}
}
}
extension BlockSQLDAO: BlockRepository {

View File

@ -49,6 +49,10 @@ class TransactionSQLDAO: TransactionRepository {
try blockDao.latestBlock()
}
func firstUnenhancedHeight() throws -> BlockHeight? {
try blockDao.firstUnenhancedHeight()
}
func isInitialized() async throws -> Bool {
true
}

View File

@ -94,10 +94,18 @@ public enum ZcashError: Equatable, Error {
/// - `sqliteError` is error produced by SQLite library.
/// ZBDAO0004
case blockDAOLatestBlock(_ sqliteError: Error)
/// Fetched latesxt block information from DB but can't decode them.
/// Fetched latest block information from DB but can't decode them.
/// - `error` is decoding error.
/// ZBDAO0005
case blockDAOLatestBlockCantDecode(_ error: Error)
/// SQLite query failed when fetching the first unenhanced block from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZBDAO0006
case blockDAOFirstUnenhancedHeight(_ sqliteError: Error)
/// Fetched unenhanced block information from DB but can't decode them.
/// - `error` is decoding error.
/// ZBDAO0007
case blockDAOFirstUnenhancedCantDecode(_ error: Error)
/// Error from rust layer when calling ZcashRustBackend.createAccount
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0001
@ -552,7 +560,9 @@ public enum ZcashError: Equatable, Error {
case .blockDAOCantDecode: return "Fetched block information from DB but can't decode them."
case .blockDAOLatestBlockHeight: return "SQLite query failed when fetching height of the latest block from the database."
case .blockDAOLatestBlock: return "SQLite query failed when fetching the latest block from the database."
case .blockDAOLatestBlockCantDecode: return "Fetched latesxt block information from DB but can't decode them."
case .blockDAOLatestBlockCantDecode: return "Fetched latest block information from DB but can't decode them."
case .blockDAOFirstUnenhancedHeight: return "SQLite query failed when fetching the first unenhanced block from the database."
case .blockDAOFirstUnenhancedCantDecode: return "Fetched unenhanced block information from DB but can't decode them."
case .rustCreateAccount: return "Error from rust layer when calling ZcashRustBackend.createAccount"
case .rustCreateToAddress: return "Error from rust layer when calling ZcashRustBackend.createToAddress"
case .rustDecryptAndStoreTransaction: return "Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction"
@ -710,6 +720,8 @@ public enum ZcashError: Equatable, Error {
case .blockDAOLatestBlockHeight: return .blockDAOLatestBlockHeight
case .blockDAOLatestBlock: return .blockDAOLatestBlock
case .blockDAOLatestBlockCantDecode: return .blockDAOLatestBlockCantDecode
case .blockDAOFirstUnenhancedHeight: return .blockDAOFirstUnenhancedHeight
case .blockDAOFirstUnenhancedCantDecode: return .blockDAOFirstUnenhancedCantDecode
case .rustCreateAccount: return .rustCreateAccount
case .rustCreateToAddress: return .rustCreateToAddress
case .rustDecryptAndStoreTransaction: return .rustDecryptAndStoreTransaction

View File

@ -57,8 +57,12 @@ public enum ZcashErrorCode: String {
case blockDAOLatestBlockHeight = "ZBDAO0003"
/// SQLite query failed when fetching the latest block from the database.
case blockDAOLatestBlock = "ZBDAO0004"
/// Fetched latesxt block information from DB but can't decode them.
/// Fetched latest block information from DB but can't decode them.
case blockDAOLatestBlockCantDecode = "ZBDAO0005"
/// SQLite query failed when fetching the first unenhanced block from the database.
case blockDAOFirstUnenhancedHeight = "ZBDAO0006"
/// Fetched unenhanced block information from DB but can't decode them.
case blockDAOFirstUnenhancedCantDecode = "ZBDAO0007"
/// Error from rust layer when calling ZcashRustBackend.createAccount
case rustCreateAccount = "ZRUST0001"
/// Error from rust layer when calling ZcashRustBackend.createToAddress

View File

@ -122,10 +122,18 @@ enum ZcashErrorDefinition {
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZBDAO0004"
case blockDAOLatestBlock(_ sqliteError: Error)
/// Fetched latesxt block information from DB but can't decode them.
/// Fetched latest block information from DB but can't decode them.
/// - `error` is decoding error.
// sourcery: code="ZBDAO0005"
case blockDAOLatestBlockCantDecode(_ error: Error)
/// SQLite query failed when fetching the first unenhanced block from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZBDAO0006"
case blockDAOFirstUnenhancedHeight(_ sqliteError: Error)
/// Fetched unenhanced block information from DB but can't decode them.
/// - `error` is decoding error.
// sourcery: code="ZBDAO0007"
case blockDAOFirstUnenhancedCantDecode(_ error: Error)
// MARK: - Rust

View File

@ -14,6 +14,7 @@ protocol TransactionRepository {
func blockForHeight(_ height: BlockHeight) async throws -> Block?
func lastScannedHeight() async throws -> BlockHeight
func lastScannedBlock() async throws -> Block?
func firstUnenhancedHeight() throws -> BlockHeight?
func isInitialized() async throws -> Bool
func find(id: Int) async throws -> ZcashTransaction.Overview
func find(rawID: Data) async throws -> ZcashTransaction.Overview

View File

@ -115,6 +115,10 @@ extension MockTransactionRepository: TransactionRepository {
nil
}
func firstUnenhancedHeight() throws -> ZcashLightClientKit.BlockHeight? {
nil
}
func isInitialized() throws -> Bool {
true
}

View File

@ -1709,6 +1709,28 @@ class TransactionRepositoryMock: TransactionRepository {
}
}
// MARK: - firstUnenhancedHeight
var firstUnenhancedHeightThrowableError: Error?
var firstUnenhancedHeightCallsCount = 0
var firstUnenhancedHeightCalled: Bool {
return firstUnenhancedHeightCallsCount > 0
}
var firstUnenhancedHeightReturnValue: BlockHeight?
var firstUnenhancedHeightClosure: (() throws -> BlockHeight?)?
func firstUnenhancedHeight() throws -> BlockHeight? {
if let error = firstUnenhancedHeightThrowableError {
throw error
}
firstUnenhancedHeightCallsCount += 1
if let closure = firstUnenhancedHeightClosure {
return try closure()
} else {
return firstUnenhancedHeightReturnValue
}
}
// MARK: - isInitialized
var isInitializedThrowableError: Error?