2019-11-26 14:32:20 -08:00
|
|
|
//
|
|
|
|
// PendingTransactionDao.swift
|
|
|
|
// ZcashLightClientKit
|
|
|
|
//
|
|
|
|
// Created by Francisco Gindre on 11/19/19.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2019-12-03 07:19:44 -08:00
|
|
|
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?
|
|
|
|
|
2019-12-06 04:38:47 -08:00
|
|
|
func isSameTransactionId<T>(other: T) -> Bool where T: RawIdentifiable {
|
2019-12-03 07:19:44 -08:00
|
|
|
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) {
|
2020-02-26 08:54:48 -08:00
|
|
|
|
|
|
|
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)
|
2019-12-03 07:19:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-26 14:32:20 -08:00
|
|
|
class PendingTransactionSQLDAO: PendingTransactionRepository {
|
2019-12-03 07:19:44 -08:00
|
|
|
|
|
|
|
let table = Table("pending_transactions")
|
2019-11-26 14:32:20 -08:00
|
|
|
|
2019-12-03 07:19:44 -08:00
|
|
|
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")
|
|
|
|
}
|
2019-11-26 14:32:20 -08:00
|
|
|
|
2019-12-03 07:19:44 -08:00
|
|
|
var dbProvider: ConnectionProvider
|
|
|
|
|
2019-11-26 14:32:20 -08:00
|
|
|
init(dbProvider: ConnectionProvider) {
|
|
|
|
self.dbProvider = dbProvider
|
|
|
|
}
|
|
|
|
|
2019-12-03 07:19:44 -08:00
|
|
|
func createrTableIfNeeded() throws {
|
2021-09-15 05:21:29 -07:00
|
|
|
let statement = table.create(ifNotExists: true) { createdTable in
|
|
|
|
createdTable.column(TableColumns.id, primaryKey: .autoincrement)
|
|
|
|
createdTable.column(TableColumns.toAddress)
|
|
|
|
createdTable.column(TableColumns.accountIndex)
|
|
|
|
createdTable.column(TableColumns.minedHeight)
|
|
|
|
createdTable.column(TableColumns.expiryHeight)
|
|
|
|
createdTable.column(TableColumns.cancelled)
|
|
|
|
createdTable.column(TableColumns.encodeAttempts, defaultValue: 0)
|
|
|
|
createdTable.column(TableColumns.errorMessage)
|
|
|
|
createdTable.column(TableColumns.errorCode)
|
|
|
|
createdTable.column(TableColumns.submitAttempts, defaultValue: 0)
|
|
|
|
createdTable.column(TableColumns.createTime)
|
|
|
|
createdTable.column(TableColumns.rawTransactionId)
|
|
|
|
createdTable.column(TableColumns.value)
|
|
|
|
createdTable.column(TableColumns.raw)
|
|
|
|
createdTable.column(TableColumns.memo)
|
2019-12-03 07:19:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
try dbProvider.connection().run(statement)
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|
|
|
|
|
2019-12-03 07:19:44 -08:00
|
|
|
func create(_ transaction: PendingTransactionEntity) throws -> Int {
|
2021-09-15 05:21:29 -07:00
|
|
|
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
|
2019-11-26 14:32:20 -08:00
|
|
|
|
2021-09-15 05:21:29 -07:00
|
|
|
return try Int(dbProvider.connection().run(table.insert(pendingTx)))
|
2019-12-03 07:19:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func update(_ transaction: PendingTransactionEntity) throws {
|
2021-09-15 05:21:29 -07:00
|
|
|
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
|
|
|
|
guard let id = pendingTx.id else {
|
2019-12-03 07:19:44 -08:00
|
|
|
throw StorageError.malformedEntity(fields: ["id"])
|
|
|
|
}
|
2021-09-15 05:21:29 -07:00
|
|
|
let updatedRows = try dbProvider.connection().run(table.filter(TableColumns.id == id).update(pendingTx))
|
2020-07-22 12:32:07 -07:00
|
|
|
if updatedRows == 0 {
|
|
|
|
LoggerProxy.error("attempted to update pending transactions but no rows were updated")
|
|
|
|
}
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func delete(_ transaction: PendingTransactionEntity) throws {
|
2019-12-03 07:19:44 -08:00
|
|
|
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
|
|
|
|
}
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func cancel(_ transaction: PendingTransactionEntity) throws {
|
|
|
|
|
2021-09-15 05:21:29 -07:00
|
|
|
var pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
|
|
|
|
pendingTx.cancelled = 1
|
|
|
|
guard let txId = pendingTx.id else {
|
2019-12-03 07:19:44 -08:00
|
|
|
throw StorageError.malformedEntity(fields: ["id"])
|
|
|
|
}
|
2021-09-15 05:21:29 -07:00
|
|
|
try dbProvider.connection().run(table.filter(TableColumns.id == txId).update(pendingTx))
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|
|
|
|
|
2019-12-03 07:19:44 -08:00
|
|
|
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 {
|
2021-09-15 05:21:29 -07:00
|
|
|
let pendingTx: PendingTransaction = try row.decode()
|
|
|
|
return pendingTx
|
2019-12-03 07:19:44 -08:00
|
|
|
} catch {
|
|
|
|
throw StorageError.operationFailed
|
|
|
|
}
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func getAll() throws -> [PendingTransactionEntity] {
|
2019-12-03 07:19:44 -08:00
|
|
|
let allTxs: [PendingTransaction] = try dbProvider.connection().prepare(table).map({ row in
|
|
|
|
try row.decode()
|
|
|
|
})
|
|
|
|
return allTxs
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|
|
|
|
|
2020-07-22 12:32:07 -07:00
|
|
|
func applyMinedHeight(_ height: BlockHeight, id: Int) throws {
|
|
|
|
|
|
|
|
let tx = table.filter(TableColumns.id == id)
|
|
|
|
|
|
|
|
let updatedRows = try dbProvider.connection().run(tx.update(
|
|
|
|
[TableColumns.minedHeight <- height]
|
|
|
|
))
|
|
|
|
if updatedRows == 0 {
|
|
|
|
LoggerProxy.error("attempted to update a row but none was updated")
|
|
|
|
}
|
|
|
|
}
|
2019-11-26 14:32:20 -08:00
|
|
|
}
|