ZcashLightClientKit/ZcashLightClientKit/DAO/PendingTransactionDao.swift

199 lines
7.3 KiB
Swift
Raw Normal View History

//
// PendingTransactionDao.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/19/19.
//
import Foundation
import SQLite
struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
enum CodingKeys: String, CodingKey {
case toAddress = "to_address"
case accountIndex = "account_index"
case minedHeight = "mined_height"
case expiryHeight = "expiry_height"
case cancelled
case encodeAttempts = "encode_attempts"
case submitAttempts = "submit_attempts"
case errorMessage = "error_message"
case errorCode = "error_code"
case createTime = "create_time"
case raw
case id
case value
case memo
case rawTransactionId = "txid"
}
var toAddress: String
var accountIndex: Int
var minedHeight: BlockHeight
var expiryHeight: BlockHeight
var cancelled: Int
var encodeAttempts: Int
var submitAttempts: Int
var errorMessage: String?
var errorCode: Int?
var createTime: TimeInterval
var raw: Data?
var id: Int?
var value: Int
var memo: Data?
var rawTransactionId: Data?
func isSameTransactionId<T>(other: T) -> Bool where T: RawIdentifiable {
self.rawTransactionId == other.rawTransactionId
}
static func from(entity: PendingTransactionEntity) -> PendingTransaction {
PendingTransaction(toAddress: entity.toAddress,
accountIndex: entity.accountIndex,
minedHeight: entity.minedHeight,
expiryHeight: entity.expiryHeight,
cancelled: entity.cancelled,
encodeAttempts: entity.encodeAttempts,
submitAttempts: entity.submitAttempts,
errorMessage: entity.errorMessage,
errorCode: entity.errorCode,
createTime: entity.createTime,
raw: entity.raw,
id: entity.id,
value: entity.value,
memo: entity.memo,
rawTransactionId: entity.raw)
}
}
extension PendingTransaction {
// TODO: Handle Memo
init(value: Int, toAddress: String, memo: String?, account index: Int) {
self = PendingTransaction(toAddress: toAddress,
accountIndex: index,
minedHeight: -1,
expiryHeight: -1,
cancelled: 0,
encodeAttempts: 0,
submitAttempts: 0,
errorMessage: nil,
errorCode: nil,
createTime: Date().timeIntervalSince1970,
raw: nil,
id: nil,
value: Int(value),
memo: memo?.encodeAsZcashTransactionMemo(),
rawTransactionId: nil)
}
}
class PendingTransactionSQLDAO: PendingTransactionRepository {
let table = Table("pending_transactions")
struct TableColumns {
static var toAddress = Expression<String>("to_address")
static var accountIndex = Expression<Int>("account_index")
static var minedHeight = Expression<Int?>("mined_height")
static var expiryHeight = Expression<Int?>("expiry_height")
static var cancelled = Expression<Int?>("cancelled")
static var encodeAttempts = Expression<Int?>("encode_attempts")
static var errorMessage = Expression<String?>("error_message")
static var submitAttempts = Expression<Int?>("submit_attempts")
static var errorCode = Expression<Int?>("error_code")
static var createTime = Expression<TimeInterval?>("create_time")
static var raw = Expression<Blob?>("raw")
static var id = Expression<Int>("id")
static var value = Expression<Int>("value")
static var memo = Expression<Blob?>("memo")
static var rawTransactionId = Expression<Blob?>("txid")
}
var dbProvider: ConnectionProvider
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
}
func createrTableIfNeeded() throws {
let statement = table.create(ifNotExists: true) { t in
t.column(TableColumns.id, primaryKey: .autoincrement)
t.column(TableColumns.toAddress)
t.column(TableColumns.accountIndex)
t.column(TableColumns.minedHeight)
t.column(TableColumns.expiryHeight)
t.column(TableColumns.cancelled)
t.column(TableColumns.encodeAttempts, defaultValue: 0)
t.column(TableColumns.errorMessage)
t.column(TableColumns.errorCode)
t.column(TableColumns.submitAttempts, defaultValue: 0)
t.column(TableColumns.createTime)
t.column(TableColumns.rawTransactionId)
t.column(TableColumns.value)
t.column(TableColumns.raw)
t.column(TableColumns.memo)
}
try dbProvider.connection().run(statement)
}
func create(_ transaction: PendingTransactionEntity) throws -> Int {
let tx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
return try Int(dbProvider.connection().run(table.insert(tx)))
}
func update(_ transaction: PendingTransactionEntity) throws {
let tx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
guard let id = tx.id else {
throw StorageError.malformedEntity(fields: ["id"])
}
try dbProvider.connection().run(table.filter(TableColumns.id == id).update(tx))
}
func delete(_ transaction: PendingTransactionEntity) throws {
guard let id = transaction.id else {
throw StorageError.malformedEntity(fields: ["id"])
}
do {
try dbProvider.connection().run(table.filter(TableColumns.id == id).delete())
} catch {
throw StorageError.updateFailed
}
}
func cancel(_ transaction: PendingTransactionEntity) throws {
var tx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
tx.cancelled = 1
guard let id = tx.id else {
throw StorageError.malformedEntity(fields: ["id"])
}
try dbProvider.connection().run(table.filter(TableColumns.id == id).update(tx))
}
func find(by id: Int) throws -> PendingTransactionEntity? {
guard let row = try dbProvider.connection().pluck(table.filter(TableColumns.id == id).limit(1)) else {
return nil
}
do {
let tx: PendingTransaction = try row.decode()
return tx
} catch {
throw StorageError.operationFailed
}
}
func getAll() throws -> [PendingTransactionEntity] {
let allTxs: [PendingTransaction] = try dbProvider.connection().prepare(table).map({ row in
try row.decode()
})
return allTxs
}
}