Issue 208 - Improve API method to request transaction history

This commit is contained in:
Francisco Gindre 2020-10-19 21:01:46 -03:00
parent 0df4b9e4f2
commit 972fb08926
11 changed files with 134 additions and 8 deletions

View File

@ -65,9 +65,7 @@ class TransactionsDataSource: NSObject, UITableViewDataSource {
}
case .all:
transactions = (synchronizer.pendingTransactions.map { $0.transactionEntity } +
synchronizer.clearedTransactions.map { $0.transactionEntity } +
synchronizer.receivedTransactions.map { $0.transactionEntity } +
synchronizer.sentTransactions.map { $0.transactionEntity }).map { TransactionDetailModel(transaction: $0)}
synchronizer.clearedTransactions.map { $0.transactionEntity }).map { TransactionDetailModel(transaction: $0)}
}
}

View File

@ -202,6 +202,55 @@ class TransactionSQLDAO: TransactionRepository {
})
}
func findAll(from transaction: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]? {
guard let fromTransaction = transaction else {
return try findAll(offset: 0, limit: limit)
}
return try dbProvider.connection().run("""
SELECT transactions.id_tx AS id,
transactions.block AS minedHeight,
transactions.tx_index AS transactionIndex,
transactions.txid AS rawTransactionId,
transactions.expiry_height AS expiryHeight,
transactions.raw AS raw,
sent_notes.address AS toAddress,
CASE
WHEN sent_notes.value IS NOT NULL THEN sent_notes.value
ELSE received_notes.value
end AS value,
CASE
WHEN sent_notes.memo IS NOT NULL THEN sent_notes.memo
ELSE received_notes.memo
end AS memo,
CASE
WHEN sent_notes.id_note IS NOT NULL THEN sent_notes.id_note
ELSE received_notes.id_note
end AS noteId,
blocks.time AS blockTimeInSeconds
FROM transactions
LEFT JOIN received_notes
ON transactions.id_tx = received_notes.tx
LEFT JOIN sent_notes
ON transactions.id_tx = sent_notes.tx
LEFT JOIN blocks
ON transactions.block = blocks.height
WHERE (\(fromTransaction.blockTimeInSeconds), \(fromTransaction.transactionIndex)) > (blocktimeinseconds, transactionIndex) AND
(sent_notes.address IS NULL AND received_notes.is_change != 1)
OR sent_notes.address IS NOT NULL
ORDER BY ( minedheight IS NOT NULL ),
minedheight DESC,
blocktimeinseconds DESC,
id DESC
LIMIT \(limit)
""").compactMap({ (bindings) -> ConfirmedTransactionEntity? in
guard let tx = TransactionBuilder.createConfirmedTransaction(from: bindings) else {
return nil
}
return tx
})
}
func findTransactions(in range: BlockRange, limit: Int = Int.max) throws -> [TransactionEntity]? {
try dbProvider.connection().run("""
SELECT transactions.id_tx AS id,

View File

@ -135,4 +135,6 @@ public extension ConfirmedTransactionEntity {
var blockTimeInMilliseconds: Double {
self.blockTimeInSeconds * 1000
}
}

View File

@ -194,7 +194,6 @@ public class Initializer {
transactionRepository: transactionRepository,
backend: rustBackend)
guard try rustBackend.initAccountsTable(dbData: dataDbURL, exfvks: viewingKeys) else {
throw rustBackend.lastError() ?? InitializerError.accountInitFailed
}

View File

@ -19,6 +19,7 @@ protocol TransactionRepository {
func findAllSentTransactions(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]?
func findAllReceivedTransactions(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]?
func findAll(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]?
func findAll(from: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]?
func lastScannedHeight() throws -> BlockHeight
func isInitialized() throws -> Bool
func findEncodedTransactionBy(txId: Int) -> EncodedTransaction?

View File

@ -92,7 +92,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
static func initAccountsTable(dbData: URL, exfvks: [String]) throws -> Bool {
let dbData = dbData.osStr()
let viewingKeys = exfvks.map { UnsafePointer(strdup($0)) }
let viewingKeys = exfvks.map { UnsafePointer(strdup($0)) }
guard exfvks.count > 0 else {
throw RustWeldingError.malformedStringInput
@ -100,7 +100,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
let res = zcashlc_init_accounts_table_with_keys(dbData.0, dbData.1, viewingKeys, UInt(viewingKeys.count));
viewingKeys.compactMap({UnsafeMutablePointer(mutating: $0)}).forEach({ free($0) })
viewingKeys.compactMap({ UnsafeMutablePointer(mutating: $0) }).forEach({ free($0) })
guard res else {
if let error = lastError() {

View File

@ -57,7 +57,6 @@ public protocol ZcashRustBackendWelding {
*/
static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32) -> [String]?
/**
initialize the accounts table from a given seed and a number of accounts
- Parameters:

View File

@ -105,6 +105,16 @@ public protocol Synchronizer {
*/
func paginatedTransactions(of kind: TransactionKind) -> PaginatedTransactionRepository
/**
Returns a list of confirmed transactions that preceed the given transaction with a limit count.
- Parameters:
- from: the confirmed transaction from which the query should start from or nil to retrieve from the most recent transaction
- limit: the maximum amount of items this should return if available
- Returns: an array with the given Transactions or nil
*/
func allConfirmedTransactions(from transaction: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]?
/**
gets the latest downloaded height from the compact block cache
*/

View File

@ -63,7 +63,7 @@ public extension Notification.Name {
Synchronizer implementation for UIKit and iOS 12+
*/
public class SDKSynchronizer: Synchronizer {
public struct NotificationKeys {
public static let progress = "SDKSynchronizer.progress"
public static let blockHeight = "SDKSynchronizer.blockHeight"
@ -467,6 +467,10 @@ public class SDKSynchronizer: Synchronizer {
try transactionRepository.findAllSentTransactions(offset: 0, limit: Int.max) ?? [ConfirmedTransactionEntity]()
}
public func allConfirmedTransactions(from transaction: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]? {
try transactionRepository.findAll(from: transaction, limit: limit)
}
public func paginatedTransactions(of kind: TransactionKind = .all) -> PaginatedTransactionRepository {
PagedTransactionRepositoryBuilder.build(initializer: initializer, kind: .all)
}

View File

@ -109,6 +109,66 @@ class TransactionRepositoryTests: XCTestCase {
}
}
}
func testFindAllFrom() throws {
guard let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max),
let allFromNil = try self.transactionRepository.findAll(from: nil, limit: Int.max)
else {
return XCTFail("find all failed")
}
XCTAssertEqual(transactions.count, allFromNil.count)
for t in transactions {
guard allFromNil.first(where: { $0.rawTransactionId == t.rawTransactionId}) != nil else {
XCTFail("not equal")
return
}
}
}
func testFindAllFromSlice() throws {
let limit = 4
let start = 7
guard let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max),
let allFromNil = try self.transactionRepository.findAll(from: transactions[start], limit: limit)
else {
return XCTFail("find all failed")
}
XCTAssertEqual(limit, allFromNil.count)
let slice = transactions[start + 1 ... start + limit]
XCTAssertEqual(slice.count, allFromNil.count)
for t in slice {
guard allFromNil.first(where: { $0.rawTransactionId == t.rawTransactionId}) != nil else {
XCTFail("not equal")
return
}
}
}
func testFindAllFromLastSlice() throws {
let limit = 10
let start = 20
guard let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max),
let allFromNil = try self.transactionRepository.findAll(from: transactions[start], limit: limit)
else {
return XCTFail("find all failed")
}
let slice = transactions[start + 1 ..< transactions.count]
XCTAssertEqual(slice.count, allFromNil.count)
for t in slice {
guard allFromNil.first(where: { $0.rawTransactionId == t.rawTransactionId}) != nil else {
XCTFail("not equal")
return
}
}
}
}
extension Data {

View File

@ -10,6 +10,10 @@ import Foundation
@testable import ZcashLightClientKit
class MockTransactionRepository: TransactionRepository {
func findAll(from: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]? {
nil
}
func findTransactions(in range: BlockRange, limit: Int) throws -> [TransactionEntity]? {
nil
}