[#959] Fix `v_transactions` view issues with value (#963)

This change switches to a new (future) version of the rust crates
that will get rid of the sent and received transactions Views in
favor of a `v_transaction` view that will do better accounting of
outgoing and incoming funds. Additionally it will support an
outputs view for seeing the inner details of transactions enabling
the SDKs tell the users the precise movement of value that a tx
causes in its multiple possible ways according to the protocol.

the `v_tx_outputs` view is not yet implemented.

Sent and Received transaction sub-types are kept for compatibility
purposes but they are generated from Overviews instead of queried
from a specific view.

In the transaction Overview the value represents the whole value
transfer for the transaction from the point of view of a given
account including fees. This means that the value for a single
transaction Overview struct represents the addition or subtraction
of ZEC value to the account's balance.

Future updates will give clients the possibility to drill into the
inner workings of those value changes in a per-output basis for
each transaction.

Also, the field `pending_unmined` field was added to `v_transactions`
so that wallets can query `DataDb` for pending but yet unmined txs

This will prepare the field for removing the notion of a "PendingDb"
and its nuances.

Also updated test database `darkside_data.db`

Closes #959

Closes #971 ZcashLightClientKitSample main target broken swiftlint script

Demo App improvements: Show Short date and value on transaction list
This commit is contained in:
Francisco Gindre 2023-04-18 09:10:56 -03:00 committed by GitHub
parent 99b24c2081
commit 802aaa437d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 196 additions and 173 deletions

View File

@ -1,4 +1,21 @@
# unreleased
### [#959] and [#914] Value of outbound transactions does not match user intended tx input
This change switches to a new (future) version of the rust crates that will get rid of the sent and received transactions Views in favor of a v_transaction view that will do better accounting of outgoing and incoming funds. Additionally it will support an outputs view for seeing the inner details of transactions enabling the SDKs tell the users the precise movement of value that a tx causes in its multiple possible ways according to the protocol.
the v_tx_outputs view is not yet implemented.
Sent and Received transaction sub-types are kept for compatibility purposes but they are generated from Overviews instead of queried from a specific view.
In the transaction Overview the value represents the whole value transfer for the transaction from the point of view of a given account including fees. This means that the value for a single transaction Overview struct represents the addition or subtraction of ZEC value to the account's balance.
Future updates will give clients the possibility to drill into the inner workings of those value changes in a per-output basis for each transaction.
Also, the field pending_unmined field was added to v_transactions so that wallets can query DataDb for pending but yet unmined txs
This will prepare the field for removing the notion of a "PendingDb" and its nuances.
### [#888] Updates to layer between Swift and Rust
This is mostly internal change. But it also touches the public API.

View File

@ -483,7 +483,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "cd ../../\necho \"Linting the SDK code\"\n./Tools/swiftlint lint --config .swiftlint.yml\necho \"Linting tests\"\n./Tools/swiftlint lint --config .swiftlint_tests.yml\n";
shellScript = "swiftlint_version=0.50.3\n\nif which swiftlint >/dev/null; then\n if [[ $(swiftlint version) != $swiftlint_version ]]; then\n echo \"warning: Compatible SwiftLint version not installed, download version $swiftlint_version from https://github.com/realm/SwiftLint. Currently installed version is $(swiftlint version)\"\n fi\n\n cd ../../\n echo \"Linting the Secant code\"\n swiftlint lint --config .swiftlint.yml\n echo \"Linting tests\"\n swiftlint lint --config .swiftlint_tests.yml\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n\n";
};
0DCB58A4237B5B580040096C /* Swiftlint */ = {
isa = PBXShellScriptBuildPhase;

View File

@ -158,8 +158,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "c2dd0eff1defbe6dfb6197c3a42ab21ea2db2fc2",
"version" : "0.2.0"
"revision" : "4bf423956fd5d7e6dbadc27afeacdbfb4abad853"
}
}
],

View File

@ -92,9 +92,10 @@ extension TransactionsDataSource: UITableViewDataSource {
let cell = tableView.dequeueReusableCell(withIdentifier: Self.cellIdentifier, for: indexPath)
let transaction = transactions[indexPath.row]
cell.detailTextLabel?.text = transaction.id ?? "no id"
cell.textLabel?.text = transaction.created ?? "No date"
cell.detailTextLabel?.text = transaction.id?.toHexStringTxId() ?? "no id"
cell.textLabel?.text = "\(transaction.dateDescription) \t\(transaction.amountDescription)"
cell.accessoryType = .disclosureIndicator
return cell
}
}

View File

@ -18,24 +18,24 @@ final class TransactionDetailModel {
}
let transaction: Transaction
var id: String?
var minedHeight: String?
var expiryHeight: String?
var created: String?
var zatoshi: String?
var memo: String?
var id: Data?
var minedHeight: BlockHeight?
var expiryHeight: BlockHeight?
var created: Date?
var zatoshi: Zatoshi
var memo: Memo?
init(sendTransaction transaction: ZcashTransaction.Sent, memos: [Memo]) {
self.transaction = .sent(transaction)
self.id = transaction.rawID?.toHexStringTxId()
self.minedHeight = transaction.minedHeight?.description
self.expiryHeight = transaction.expiryHeight?.description
self.id = transaction.rawID
self.minedHeight = transaction.minedHeight
self.expiryHeight = transaction.expiryHeight
self.zatoshi = transaction.value.decimalString()
self.memo = memos.first?.toString()
self.zatoshi = transaction.value
self.memo = memos.first
if let blockTime = transaction.blockTime {
created = Date(timeIntervalSince1970: blockTime).description
created = Date(timeIntervalSince1970: blockTime)
} else {
created = nil
}
@ -43,32 +43,37 @@ final class TransactionDetailModel {
init(receivedTransaction transaction: ZcashTransaction.Received, memos: [Memo]) {
self.transaction = .received(transaction)
self.id = transaction.rawID?.toHexStringTxId()
self.minedHeight = transaction.minedHeight.description
self.expiryHeight = transaction.expiryHeight?.description
self.created = Date(timeIntervalSince1970: transaction.blockTime).description
self.zatoshi = transaction.value.decimalString()
self.memo = memos.first?.toString()
self.id = transaction.rawID
self.minedHeight = transaction.minedHeight
self.expiryHeight = transaction.expiryHeight
self.zatoshi = transaction.value
self.memo = memos.first
self.created = Date(timeIntervalSince1970: transaction.blockTime)
}
init(pendingTransaction transaction: PendingTransactionEntity, memos: [Memo]) {
self.transaction = .pending(transaction)
self.id = transaction.rawTransactionId?.toHexStringTxId()
self.minedHeight = transaction.minedHeight.description
self.expiryHeight = transaction.expiryHeight.description
self.created = Date(timeIntervalSince1970: transaction.createTime).description
self.zatoshi = transaction.value.decimalString()
self.memo = memos.first?.toString()
self.id = transaction.rawTransactionId
self.minedHeight = transaction.minedHeight
self.expiryHeight = transaction.expiryHeight
self.created = Date(timeIntervalSince1970: transaction.createTime)
self.zatoshi = transaction.value
self.memo = memos.first
}
init(transaction: ZcashTransaction.Overview, memos: [Memo]) {
self.transaction = .cleared(transaction)
self.id = transaction.rawID.toHexStringTxId()
self.minedHeight = transaction.minedHeight?.description
self.expiryHeight = transaction.expiryHeight?.description
self.created = transaction.blockTime?.description
self.zatoshi = transaction.value.decimalString()
self.memo = memos.first?.toString()
self.id = transaction.rawID
self.minedHeight = transaction.minedHeight
self.expiryHeight = transaction.expiryHeight
self.zatoshi = transaction.value
self.memo = memos.first
if let blockTime = transaction.blockTime {
created = Date(timeIntervalSince1970: blockTime)
} else {
created = nil
}
}
func loadMemos(from synchronizer: Synchronizer) async throws -> [Memo] {
@ -99,6 +104,15 @@ final class TransactionDetailModel {
}
}
}
extension TransactionDetailModel {
var dateDescription: String {
self.created?.formatted(date: .abbreviated, time: .shortened) ?? "No date"
}
var amountDescription: String {
self.zatoshi.amount.description
}
}
// swiftlint:disable implicitly_unwrapped_optional
class TransactionDetailViewController: UITableViewController {
@ -119,13 +133,13 @@ class TransactionDetailViewController: UITableViewController {
func setup() {
guard model != nil else { return }
idLabel.text = model.id
minedHeightLabel.text = model.minedHeight ?? "no height"
expiryHeightLabel.text = model.expiryHeight ?? "no height"
createdLabel.text = model.created
zatoshiLabel.text = model.zatoshi
memoLabel.text = model.memo ?? "No memo"
loggerProxy.debug("tx id: \(model.id ?? "no id!!"))")
idLabel.text = model.id?.toHexStringTxId()
minedHeightLabel.text = model.minedHeight?.description ?? "no height"
expiryHeightLabel.text = model.expiryHeight?.description ?? "no height"
createdLabel.text = model.created?.ISO8601Format()
zatoshiLabel.text = model.zatoshi.amount.description
memoLabel.text = model.memo?.toString() ?? "No memo"
loggerProxy.debug("tx id: \(model.id?.toHexStringTxId() ?? "no id!!"))")
Task {
do {

View File

@ -104,8 +104,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "c2dd0eff1defbe6dfb6197c3a42ab21ea2db2fc2",
"version" : "0.2.0"
"revision" : "17804089d537f85e312132aa0bcf3654f4736fe9"
}
}
],

View File

@ -16,7 +16,7 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.14.0"),
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", from: "0.2.0")
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "17804089d537f85e312132aa0bcf3654f4736fe9")
],
targets: [
.target(

View File

@ -20,15 +20,21 @@ class TransactionSQLDAO: TransactionRepository {
private var blockDao: BlockSQLDAO
private var sentNotesRepository: SentNotesRepository
private let transactionsView = View("v_transactions")
private let receivedTransactionsView = View("v_tx_received")
private let sentTransactionsView = View("v_tx_sent")
private let receivedNotesTable = Table("received_notes")
private let sentNotesTable = Table("sent_notes")
private let traceClosure: ((String) -> Void)?
init(dbProvider: ConnectionProvider) {
init(dbProvider: ConnectionProvider, traceClosure: ((String) -> Void)? = nil) {
self.dbProvider = dbProvider
self.blockDao = BlockSQLDAO(dbProvider: dbProvider)
self.sentNotesRepository = SentNotesSQLDAO(dbProvider: dbProvider)
self.traceClosure = traceClosure
}
private func connection() throws -> Connection {
let conn = try dbProvider.connection()
conn.trace(traceClosure)
return conn
}
func closeDBConnection() {
@ -52,11 +58,11 @@ class TransactionSQLDAO: TransactionRepository {
}
func countAll() async throws -> Int {
try dbProvider.connection().scalar(transactions.count)
try connection().scalar(transactions.count)
}
func countUnmined() async throws -> Int {
try dbProvider.connection().scalar(transactions.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
try connection().scalar(transactions.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
}
func find(id: Int) async throws -> ZcashTransaction.Overview {
@ -118,23 +124,31 @@ class TransactionSQLDAO: TransactionRepository {
}
func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Received] {
let query = receivedTransactionsView
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
let query = transactionsView
.filterQueryFor(kind: .received)
.order(ZcashTransaction.Overview.Column.id.desc,(ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc )
.limit(limit, offset: offset)
return try execute(query) { try ZcashTransaction.Received(row: $0) }
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
.compactMap { ZcashTransaction.Received(overview: $0) }
}
func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Sent] {
let query = sentTransactionsView
let query = transactionsView
.filterQueryFor(kind: .sent)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
.limit(limit, offset: offset)
return try execute(query) { try ZcashTransaction.Sent(row: $0) }
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
.compactMap { ZcashTransaction.Sent(overview: $0) }
}
func findMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo] {
return try await findMemos(for: transaction.id, table: receivedNotesTable)
if transaction.isSentTransaction {
return try await findMemos(for: transaction.id, table: sentNotesTable)
} else {
return try await findMemos(for: transaction.id, table: receivedNotesTable)
}
}
func findMemos(for receivedTransaction: ZcashTransaction.Received) async throws -> [Memo] {
@ -153,7 +167,7 @@ class TransactionSQLDAO: TransactionRepository {
let query = table
.filter(NotesTableStructure.transactionID == transactionID)
let memos = try dbProvider.connection().prepare(query).compactMap { row in
let memos = try connection().prepare(query).compactMap { row in
do {
let rawMemo = try row.get(NotesTableStructure.memo)
return try Memo(bytes: rawMemo.bytes)
@ -172,8 +186,7 @@ class TransactionSQLDAO: TransactionRepository {
}
private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> [Entity] {
let entities = try dbProvider
.connection()
let entities = try connection()
.prepare(query)
.map(createEntity)

View File

@ -200,12 +200,12 @@ public extension PendingTransactionEntity {
public extension PendingTransactionEntity {
func makeTransactionEntity(defaultFee: Zatoshi) -> ZcashTransaction.Overview {
return ZcashTransaction.Overview(
accountId: 0,
blockTime: createTime,
expiryHeight: expiryHeight,
fee: fee,
id: id ?? -1,
index: nil,
isWalletInternal: false,
hasChange: false,
memoCount: 0,
minedHeight: minedHeight,
@ -213,7 +213,8 @@ public extension PendingTransactionEntity {
rawID: rawTransactionId ?? Data(),
receivedNoteCount: 0,
sentNoteCount: 0,
value: value
value: value,
isExpiredUmined: false
)
}
}

View File

@ -10,12 +10,12 @@ import SQLite
public enum ZcashTransaction {
public struct Overview {
public let accountId: Int
public let blockTime: TimeInterval?
public let expiryHeight: BlockHeight?
public let fee: Zatoshi?
public let id: Int
public let index: Int?
public let isWalletInternal: Bool
public var isSentTransaction: Bool { value < Zatoshi(0) }
public let hasChange: Bool
public let memoCount: Int
@ -25,6 +25,7 @@ public enum ZcashTransaction {
public let receivedNoteCount: Int
public let sentNoteCount: Int
public let value: Zatoshi
public let isExpiredUmined: Bool
}
public struct Received {
@ -65,27 +66,28 @@ public enum ZcashTransaction {
extension ZcashTransaction.Overview {
enum Column {
static let accountId = Expression<Int>("account_id")
static let id = Expression<Int>("id_tx")
static let minedHeight = Expression<BlockHeight?>("mined_height")
static let index = Expression<Int?>("tx_index")
static let rawID = Expression<Blob>("txid")
static let expiryHeight = Expression<BlockHeight?>("expiry_height")
static let raw = Expression<Blob?>("raw")
static let value = Expression<Int64>("net_value")
static let value = Expression<Int64>("account_balance_delta")
static let fee = Expression<Int64?>("fee_paid")
static let isWalletInternal = Expression<Bool>("is_wallet_internal")
static let hasChange = Expression<Bool>("has_change")
static let sentNoteCount = Expression<Int>("sent_note_count")
static let receivedNoteCount = Expression<Int>("received_note_count")
static let memoCount = Expression<Int>("memo_count")
static let blockTime = Expression<Int64?>("block_time")
static let expiredUnmined = Expression<Bool>("expired_unmined")
}
init(row: Row) throws {
self.accountId = try row.get(Column.accountId)
self.expiryHeight = try row.get(Column.expiryHeight)
self.id = try row.get(Column.id)
self.index = try row.get(Column.index)
self.isWalletInternal = try row.get(Column.isWalletInternal)
self.hasChange = try row.get(Column.hasChange)
self.memoCount = try row.get(Column.memoCount)
self.minedHeight = try row.get(Column.minedHeight)
@ -93,7 +95,7 @@ extension ZcashTransaction.Overview {
self.receivedNoteCount = try row.get(Column.receivedNoteCount)
self.sentNoteCount = try row.get(Column.sentNoteCount)
self.value = Zatoshi(try row.get(Column.value))
self.isExpiredUmined = try row.get(Column.expiredUnmined)
if let blockTime = try row.get(Column.blockTime) {
self.blockTime = TimeInterval(blockTime)
} else {
@ -129,84 +131,48 @@ extension ZcashTransaction.Overview {
}
extension ZcashTransaction.Received {
enum Column {
static let id = Expression<Int>("id_tx")
static let minedHeight = Expression<BlockHeight>("mined_height")
static let index = Expression<Int>("tx_index")
static let rawID = Expression<Blob?>("txid")
static let expiryHeight = Expression<BlockHeight?>("expiry_height")
static let raw = Expression<Blob?>("raw")
static let fromAccount = Expression<Int>("received_by_account")
static let value = Expression<Int64>("received_total")
static let fee = Expression<Int64>("fee_paid")
static let noteCount = Expression<Int>("received_note_count")
static let memoCount = Expression<Int>("memo_count")
static let blockTime = Expression<Int64>("block_time")
}
/// Attempts to create a `ZcashTransaction.Received` from an `Overview`
/// given that the transaction might not be a "sent" transaction, so it won't have the necessary
/// data to actually create it as it is currently defined, this initializer is optional
/// - returns: Optional<Received>. `Some` if the values present suffice to create a received
/// transaction otherwise `.none`
init?(overview: ZcashTransaction.Overview) {
guard
!overview.isSentTransaction,
let txBlocktime = overview.blockTime,
let txIndex = overview.index,
let txMinedHeight = overview.minedHeight
else { return nil }
init(row: Row) throws {
self.blockTime = TimeInterval(try row.get(Column.blockTime))
self.expiryHeight = try row.get(Column.expiryHeight)
self.fromAccount = try row.get(Column.fromAccount)
self.id = try row.get(Column.id)
self.index = try row.get(Column.index)
self.memoCount = try row.get(Column.memoCount)
self.minedHeight = try row.get(Column.minedHeight)
self.noteCount = try row.get(Column.noteCount)
self.value = Zatoshi(try row.get(Column.value))
if let raw = try row.get(Column.raw) {
self.raw = Data(blob: raw)
} else {
self.raw = nil
}
if let rawID = try row.get(Column.rawID) {
self.rawID = Data(blob: rawID)
} else {
self.rawID = nil
}
self.blockTime = txBlocktime
self.expiryHeight = overview.expiryHeight
self.fromAccount = overview.accountId
self.id = overview.id
self.index = txIndex
self.memoCount = overview.memoCount
self.minedHeight = txMinedHeight
self.noteCount = overview.receivedNoteCount
self.value = overview.value
self.raw = overview.raw
self.rawID = overview.rawID
}
}
extension ZcashTransaction.Sent {
enum Column {
static let id = Expression<Int>("id_tx")
static let minedHeight = Expression<BlockHeight?>("mined_height")
static let index = Expression<Int?>("tx_index")
static let rawID = Expression<Blob?>("txid")
static let expiryHeight = Expression<BlockHeight?>("expiry_height")
static let raw = Expression<Blob?>("raw")
static let fromAccount = Expression<Int>("sent_from_account")
static let value = Expression<Int64>("sent_total")
static let fee = Expression<Int64>("fee_paid")
static let noteCount = Expression<Int>("sent_note_count")
static let memoCount = Expression<Int>("memo_count")
static let blockTime = Expression<Int64?>("block_time")
}
init?(overview: ZcashTransaction.Overview) {
guard overview.isSentTransaction else { return nil }
init(row: Row) throws {
self.blockTime = try row.get(Column.blockTime).map { TimeInterval($0) }
self.expiryHeight = try row.get(Column.expiryHeight)
self.fromAccount = try row.get(Column.fromAccount)
self.id = try row.get(Column.id)
self.index = try row.get(Column.index)
self.memoCount = try row.get(Column.memoCount)
self.minedHeight = try row.get(Column.minedHeight)
self.noteCount = try row.get(Column.noteCount)
self.value = Zatoshi(try row.get(Column.value))
if let raw = try row.get(Column.raw) {
self.raw = Data(blob: raw)
} else {
self.raw = nil
}
if let rawID = try row.get(Column.rawID) {
self.rawID = Data(blob: rawID)
} else {
self.rawID = nil
}
self.blockTime = overview.blockTime
self.expiryHeight = overview.expiryHeight
self.fromAccount = overview.accountId
self.id = overview.id
self.index = overview.index
self.memoCount = overview.memoCount
self.minedHeight = overview.minedHeight
self.noteCount = overview.sentNoteCount
self.value = overview.value
self.raw = overview.raw
self.rawID = overview.rawID
}
}

View File

@ -666,7 +666,7 @@ class AdvancedReOrgTests: XCTestCase {
XCTAssertEqual(expectedVerifiedBalance, initialVerifiedBalance)
XCTAssertEqual(expectedBalance, initialBalance)
wait(for: [lastSyncExpectation], timeout: 5)
wait(for: [lastSyncExpectation], timeout: 30)
}
func testTxIndexReorg() async throws {

View File

@ -205,7 +205,13 @@ class PendingTransactionUpdatesTest: XCTestCase {
wait(for: [syncToConfirmExpectation], timeout: 6)
let supposedlyPendingUnexistingTransaction = try await coordinator.synchronizer.allPendingTransactions().first
let clearedTransactions = await coordinator.synchronizer
.clearedTransactions
let clearedTransaction = clearedTransactions.first(where: { $0.rawID == afterStagePendingTx.rawTransactionId } )
XCTAssertEqual(clearedTransaction!.value.amount + clearedTransaction!.fee!.amount, -afterStagePendingTx.value.amount)
XCTAssertNil(supposedlyPendingUnexistingTransaction)
}

View File

@ -221,12 +221,12 @@ class SynchronizerDarksideTests: XCTestCase {
totalTransactions: 2,
enhancedTransactions: 1,
lastFoundTransaction: ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663206,
fee: Zatoshi(0),
id: 2,
index: 1,
isWalletInternal: true,
hasChange: false,
memoCount: 1,
minedHeight: 663188,
@ -234,7 +234,8 @@ class SynchronizerDarksideTests: XCTestCase {
rawID: Data(),
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000)
value: Zatoshi(100000),
isExpiredUmined: false
),
range: 663150...663189
)
@ -252,12 +253,12 @@ class SynchronizerDarksideTests: XCTestCase {
totalTransactions: 2,
enhancedTransactions: 2,
lastFoundTransaction: ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663192,
fee: Zatoshi(0),
id: 1,
index: 1,
isWalletInternal: true,
hasChange: false,
memoCount: 1,
minedHeight: 663174,
@ -265,7 +266,8 @@ class SynchronizerDarksideTests: XCTestCase {
rawID: Data(),
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000)
value: Zatoshi(100000),
isExpiredUmined: false
),
range: 663150...663189
)
@ -374,12 +376,12 @@ class SynchronizerDarksideTests: XCTestCase {
totalTransactions: 2,
enhancedTransactions: 1,
lastFoundTransaction: ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663206,
fee: Zatoshi(0),
id: 2,
index: 1,
isWalletInternal: true,
hasChange: false,
memoCount: 1,
minedHeight: 663188,
@ -387,7 +389,8 @@ class SynchronizerDarksideTests: XCTestCase {
rawID: Data(),
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000)
value: Zatoshi(100000),
isExpiredUmined: false
),
range: 663150...663189
)
@ -405,12 +408,12 @@ class SynchronizerDarksideTests: XCTestCase {
totalTransactions: 2,
enhancedTransactions: 2,
lastFoundTransaction: ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663192,
fee: Zatoshi(0),
id: 1,
index: 1,
isWalletInternal: true,
hasChange: false,
memoCount: 1,
minedHeight: 663174,
@ -418,7 +421,8 @@ class SynchronizerDarksideTests: XCTestCase {
rawID: Data(),
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000)
value: Zatoshi(100000),
isExpiredUmined: false
),
range: 663150...663189
)

View File

@ -91,9 +91,9 @@ class TransactionRepositoryTests: XCTestCase {
func testFindReceivedOffsetLimit() async throws {
let transactions = try await self.transactionRepository.findReceived(offset: 3, limit: 3)
XCTAssertEqual(transactions.count, 3)
XCTAssertEqual(transactions[0].minedHeight, 664022)
XCTAssertEqual(transactions[1].minedHeight, 664012)
XCTAssertEqual(transactions[2].minedHeight, 664003)
XCTAssertEqual(transactions[0].minedHeight, 663229)
XCTAssertEqual(transactions[1].minedHeight, 663218)
XCTAssertEqual(transactions[2].minedHeight, 663202)
}
func testFindSentOffsetLimit() async throws {
@ -106,12 +106,12 @@ class TransactionRepositoryTests: XCTestCase {
func testFindMemoForTransaction() async throws {
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: nil,
expiryHeight: nil,
fee: nil,
id: 9,
index: nil,
isWalletInternal: false,
hasChange: false,
memoCount: 0,
minedHeight: nil,
@ -119,11 +119,17 @@ class TransactionRepositoryTests: XCTestCase {
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 0,
value: Zatoshi.zero
value: Zatoshi(-1000),
isExpiredUmined: false
)
let memos = try await self.transactionRepository.findMemos(for: transaction)
XCTAssertEqual(memos.count, 1)
guard memos.count == 1 else {
XCTFail("Expected transaction to have one memo")
return
}
XCTAssertEqual(memos[0].toString(), "Some funds")
}
@ -132,7 +138,7 @@ class TransactionRepositoryTests: XCTestCase {
blockTime: 1,
expiryHeight: nil,
fromAccount: 0,
id: 9,
id: 5,
index: 0,
memoCount: 0,
minedHeight: 0,
@ -144,7 +150,7 @@ class TransactionRepositoryTests: XCTestCase {
let memos = try await self.transactionRepository.findMemos(for: transaction)
XCTAssertEqual(memos.count, 1)
XCTAssertEqual(memos[0].toString(), "Some funds")
XCTAssertEqual(memos[0].toString(), "first mainnet tx from the SDK")
}
func testFindMemoForSentTransaction() async throws {
@ -166,18 +172,6 @@ class TransactionRepositoryTests: XCTestCase {
XCTAssertEqual(memos.count, 1)
XCTAssertEqual(memos[0].toString(), "Some funds")
}
func testFindTransactionWithNULLMinedHeight() async throws {
let transactions = try await self.transactionRepository.find(offset: 0, limit: 3, kind: .all)
XCTAssertEqual(transactions.count, 3)
XCTAssertEqual(transactions[0].id, 21)
XCTAssertEqual(transactions[0].minedHeight, nil)
XCTAssertEqual(transactions[1].id, 20)
XCTAssertEqual(transactions[1].minedHeight, 664037)
XCTAssertEqual(transactions[2].id, 19)
XCTAssertEqual(transactions[2].minedHeight, 664022)
}
func testFindAllPerformance() {
// This is an example of a performance test case.

View File

@ -131,12 +131,12 @@ extension MockTransactionRepository: TransactionRepository {
func mockSent(_ index: Int) -> ZcashTransaction.Overview {
return ZcashTransaction.Overview(
accountId: 0,
blockTime: randomTimeInterval(),
expiryHeight: BlockHeight.max,
fee: Zatoshi(2),
id: index,
index: index,
isWalletInternal: true,
hasChange: true,
memoCount: 0,
minedHeight: randomBlockHeight(),
@ -144,18 +144,19 @@ extension MockTransactionRepository: TransactionRepository {
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 1,
value: Zatoshi(-Int64.random(in: 1 ... Zatoshi.Constants.oneZecInZatoshi))
value: Zatoshi(-Int64.random(in: 1 ... Zatoshi.Constants.oneZecInZatoshi)),
isExpiredUmined: false
)
}
func mockReceived(_ index: Int) -> ZcashTransaction.Overview {
return ZcashTransaction.Overview(
accountId: 0,
blockTime: randomTimeInterval(),
expiryHeight: BlockHeight.max,
fee: Zatoshi(2),
id: index,
index: index,
isWalletInternal: true,
hasChange: true,
memoCount: 0,
minedHeight: randomBlockHeight(),
@ -163,7 +164,8 @@ extension MockTransactionRepository: TransactionRepository {
rawID: Data(),
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(Int64.random(in: 1 ... Zatoshi.Constants.oneZecInZatoshi))
value: Zatoshi(Int64.random(in: 1 ... Zatoshi.Constants.oneZecInZatoshi)),
isExpiredUmined: false
)
}

View File

@ -77,6 +77,12 @@ enum TestDbBuilder {
return TransactionSQLDAO(dbProvider: provider)
}
static func transactionRepository(rustBackend: ZcashRustBackendWelding, withTrace closure: @escaping ((String) -> Void)) async throws -> TransactionRepository? {
guard let provider = try await prepopulatedDataDbProvider(rustBackend: rustBackend) else { return nil }
return TransactionSQLDAO(dbProvider: provider, traceClosure: closure)
}
static func sentNotesRepository(rustBackend: ZcashRustBackendWelding) async throws -> SentNotesRepository? {
guard let provider = try await prepopulatedDataDbProvider(rustBackend: rustBackend) else { return nil }

View File

@ -39,12 +39,12 @@ class TestsData {
let clearedTransaction = {
ZcashTransaction.Overview(
accountId: 0,
blockTime: Date().timeIntervalSince1970,
expiryHeight: 123000,
fee: Zatoshi(10),
id: 333,
index: nil,
isWalletInternal: false,
hasChange: false,
memoCount: 0,
minedHeight: 120000,
@ -52,7 +52,8 @@ class TestsData {
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 0,
value: Zatoshi(100)
value: Zatoshi(100),
isExpiredUmined: false
)
}()