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:
parent
99b24c2081
commit
802aaa437d
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}()
|
||||
|
||||
|
|
Loading…
Reference in New Issue