202 lines
5.9 KiB
Swift
202 lines
5.9 KiB
Swift
//
|
|
// NotesDao.swift
|
|
// ZcashLightClientKit
|
|
//
|
|
// Created by Francisco Gindre on 11/18/19.
|
|
//
|
|
|
|
import Foundation
|
|
import SQLite
|
|
|
|
struct ReceivedNote: ReceivedNoteEntity, Codable {
|
|
enum CodingKeys: String, CodingKey {
|
|
case id = "id_note"
|
|
case diversifier
|
|
case rcm
|
|
case nf
|
|
case isChange = "is_change"
|
|
case transactionId = "id_tx"
|
|
case outputIndex = "output_index"
|
|
case account
|
|
case value
|
|
case memo
|
|
case spent
|
|
case tx
|
|
}
|
|
let id: Int
|
|
let diversifier: Data
|
|
let rcm: Data
|
|
let nf: Data
|
|
let isChange: Bool
|
|
let transactionId: Int
|
|
let outputIndex: Int
|
|
let account: Int
|
|
let value: Int
|
|
let memo: Data?
|
|
let spent: Int?
|
|
let tx: Int
|
|
}
|
|
|
|
class ReceivedNotesSQLDAO: ReceivedNoteRepository {
|
|
let table = Table("received_notes")
|
|
|
|
let dbProvider: ConnectionProvider
|
|
|
|
init(dbProvider: ConnectionProvider) {
|
|
self.dbProvider = dbProvider
|
|
}
|
|
|
|
/// Throws `notesDAOReceivedCount` if sqlite query fetching count fails.
|
|
func count() throws -> Int {
|
|
do {
|
|
return try dbProvider.connection().scalar(table.count)
|
|
} catch {
|
|
throw ZcashError.notesDAOReceivedCount(error)
|
|
}
|
|
}
|
|
|
|
/// - Throws:
|
|
/// - `notesDAOReceivedCantDecode` if fetched note data from the db can't be decoded to the `ReceivedNote` object.
|
|
/// - `notesDAOReceivedNote` if sqlite query fetching note data fails.
|
|
func receivedNote(byRawTransactionId: Data) throws -> ReceivedNoteEntity? {
|
|
let transactions = Table("transactions")
|
|
let idTx = Expression<Int>("id_tx")
|
|
let transaction = Expression<Int>("tx")
|
|
let txid = Expression<Blob>("txid")
|
|
let joinStatement = table
|
|
.join(
|
|
.inner,
|
|
transactions,
|
|
on: transactions[idTx] == table[transaction]
|
|
)
|
|
.where(transactions[txid] == Blob(bytes: byRawTransactionId.bytes))
|
|
.limit(1)
|
|
|
|
do {
|
|
return try dbProvider.connection()
|
|
.prepare(joinStatement)
|
|
.map { row -> ReceivedNote in
|
|
do {
|
|
return try row.decode()
|
|
} catch {
|
|
throw ZcashError.notesDAOReceivedCantDecode(error)
|
|
}
|
|
}
|
|
.first
|
|
} catch {
|
|
if let error = error as? ZcashError {
|
|
throw error
|
|
} else {
|
|
throw ZcashError.notesDAOReceivedNote(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SentNote: SentNoteEntity, Codable {
|
|
enum CodingKeys: String, CodingKey {
|
|
case id = "id_note"
|
|
case transactionId = "tx"
|
|
case outputPool = "output_pool"
|
|
case outputIndex = "output_index"
|
|
case fromAccount = "from_account"
|
|
case toAddress = "to_address"
|
|
case toAccount = "to_account"
|
|
case value
|
|
case memo
|
|
}
|
|
|
|
let id: Int
|
|
let transactionId: Int
|
|
let outputPool: Int
|
|
let outputIndex: Int
|
|
let fromAccount: Int
|
|
let toAddress: String?
|
|
let toAccount: Int?
|
|
let value: Int
|
|
let memo: Data?
|
|
}
|
|
|
|
class SentNotesSQLDAO: SentNotesRepository {
|
|
let table = Table("sent_notes")
|
|
|
|
let dbProvider: ConnectionProvider
|
|
|
|
init(dbProvider: ConnectionProvider) {
|
|
self.dbProvider = dbProvider
|
|
}
|
|
|
|
/// - Throws: `notesDAOSentCount` if sqlite query fetching count fails.
|
|
func count() throws -> Int {
|
|
do {
|
|
return try dbProvider.connection().scalar(table.count)
|
|
} catch {
|
|
throw ZcashError.notesDAOSentCount(error)
|
|
}
|
|
}
|
|
|
|
/// - Throws:
|
|
/// - `notesDAOSentCantDecode` if fetched note data from the db can't be decoded to the `SentNote` object.
|
|
/// - `notesDAOSentNote` if sqlite query fetching note data fails.
|
|
func sentNote(byRawTransactionId: Data) throws -> SentNoteEntity? {
|
|
let transactions = Table("transactions")
|
|
let idTx = Expression<Int>("id_tx")
|
|
let transaction = Expression<Int>("tx")
|
|
let txid = Expression<Blob>("txid")
|
|
let joinStatement = table
|
|
.join(
|
|
.inner,
|
|
transactions,
|
|
on: transactions[idTx] == table[transaction]
|
|
)
|
|
.where(transactions[txid] == Blob(bytes: byRawTransactionId.bytes))
|
|
.limit(1)
|
|
|
|
do {
|
|
return try dbProvider.connection()
|
|
.prepare(joinStatement)
|
|
.map { row -> SentNote in
|
|
do {
|
|
return try row.decode()
|
|
} catch {
|
|
throw ZcashError.notesDAOSentCantDecode(error)
|
|
}
|
|
}
|
|
.first
|
|
} catch {
|
|
if let error = error as? ZcashError {
|
|
throw error
|
|
} else {
|
|
throw ZcashError.notesDAOSentNote(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
func getRecipients(for id: Int) -> [TransactionRecipient] {
|
|
guard let result = try? dbProvider.connection().prepare(
|
|
table.where(id == table[Expression<Int>("tx")])
|
|
) else { return [] }
|
|
|
|
guard let rows = try? result.map({ row -> SentNote in
|
|
try row.decode()
|
|
}) else { return [] }
|
|
|
|
return rows.compactMap { sentNote -> TransactionRecipient? in
|
|
if sentNote.toAccount == nil {
|
|
guard
|
|
let toAddress = sentNote.toAddress,
|
|
let recipient = Recipient.forEncodedAddress(encoded: toAddress)
|
|
else { return nil }
|
|
|
|
return TransactionRecipient.address(recipient.0)
|
|
} else {
|
|
guard let toAccount = sentNote.toAccount else {
|
|
return nil
|
|
}
|
|
|
|
return TransactionRecipient.internalAccount(UInt32(toAccount))
|
|
}
|
|
}
|
|
}
|
|
}
|