[#1001] Remove PendingDb in favor of `v_transactions` and `v_tx_output` Views (#1001)

Removes `PendingTransactionEntity` and all of its related components.
Pending items are still tracked and visualized by the existing APIs
but they are retrieved from the `TransactionRepository` instead by
returning `ZcashTransaction.Overview` instead.

`pendingDbURL` is removed from every place it was required. Its
deletion is responsibility of wallet developers.

`ClearedTransactions` are now just `transactions`.

`MigrationManager` is deleted. Now all migrations are in charge of
the rust welding layer.

`PendingTransactionDao.swift` is removed.

Implementation of `AccountEntity` called `Account` is now `DbAccount`

`ZcashTransaction.Overview` can be checked for "pending-ness" by calling
`.isPending(latestHeight:)` latest height must be provided so that minedHeight
can be compared with the lastest and the `defaultStaleTolerance` constant.

`TransactionRecipient` is now a public type.

protocol `PendingTransactionRepository` is removed.

`TransactionManagerError` and `PersistentTransactionManager` are deleted.

`OutboundTransactionManager` is deleted and replaced by `TransactionEncoder`
which now incorporates `submit(encoded:)` functionality

`WalletTransactionEncoder` now uses a `LightWalletService` to submit the
encoded transactions.

Add changelog changes

Delete references to PendingDb from tests and documentation.

Fixes some typos. Adds the ability to trace transaction repository
SQL queries from test

Fix rebase conflicts and generate code

[#837] Memo tests regarding transparent address

Closes #837

Add model for transaction output

Point to FFI branch

Fix issue where sync wouldn't resume after wipe. Becasue GRPC
channel would be closed

Fix Tests

Fix testPendingTransactionMinedHeightUpdated

Fix testLastStates

[#921] Fix  broken SynchronizerDarksideTests

Add ZcashTransaction.Output API to Synchronizer

Changelog + comment fix

Add Assertions for transaction outputs and recipients

Point to FFI 0.3.1

Fix Demo App Compiler errors

Fix Demo App Compiler errors

fix cacheDb warnings

Fix Tests and compiler errors of rebase

build demo app

Remove `ZcashTransaction.Sent` and `.Received`. Add `.State` and tests

Fix SPM warning

PR Suggestions

Removes errors that are not used anymore

fix warnings
This commit is contained in:
Francisco Gindre 2023-05-05 14:30:47 -03:00 committed by GitHub
parent 0324d9ace5
commit f5e7c027af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1008 additions and 3128 deletions

View File

@ -1,5 +1,72 @@
# Unreleased
### Changed
- `WalletTransactionEncoder` now uses a `LightWalletService` to submit the
encoded transactions.
- Functions returning or receiving `ZcashTransaction.Sent` or `ZcashTransaction.Received` now
will be simplified by returning `ZcashTransaction.Overview` or be replaced by their Overview
counterparts
### Added
- `ZcashTransaction.Overview` can be checked for "pending-ness" by calling`.isPending(latestHeight:)` latest height must be provided so that minedHeight
can be compared with the lastest and the `defaultStaleTolerance` constant.
`TransactionRecipient` is now a public type.
- `ZcashTransaction.Output` can be queried to know the inner details of a
`ZcashTransaction.Overview`. It will return an array with all the tracked
outputs for that transaction so that they can be shown to users who request them
- `ZcashTransaction.Overview.State` is introduced to represent `confirmed`,
`pending` or `expired` states. This State is relative to the current height
of the chain that is passed to the function `getState(for currentHeight: BlockHeight)`.
State should be a transient value and it's not adviced to store it unless
transactions have stale values such as `confirmed` or `expired`.
#### Synchronizer
- `public func getTransactionOutputs(transaction) async -> [ZcashTransaction.Output]` is added to
get the outputs related to the given transaction. You can use this to know every detail of the
transaction Overview and show it in a more fine-grained UI.
- `TransactionRecipient` is returned on `getRecipients(for:)`.
### Renamed
- `AccountEntity` called `Account` is now `DbAccount`
### Removed
- `ZcashTransaction.Received` and `ZcashTransaction.Sent` are removed
and replaced by `Overview` since the notion of Sent and received is
not entirely applicable to Zcash transactions where value can be
sent and received at the same time. Transactions with negative value
will be considered as "sent" but that won't be enforced with a type
anymore
- `cancelSpend()`: support for cancel spend was removed since its
completion was not guaranteed
- `PendingTransactionEntity` and all of its related components.
Pending items are still tracked and visualized by the existing APIs
but they are retrieved from the `TransactionRepository` instead by
returning `ZcashTransaction.Overview` instead.
- `pendingDbURL` is removed from every place it was required. Its
deletion is responsibility of wallet developers.
- `ClearedTransactions` are now just `transactions`.`MigrationManager`
is deleted. Now all migrations are in charge of the rust welding layer.
- `PendingTransactionDao.swift` is removed.
- `PendingTransactionRepository` protocol is removed.
- `TransactionManagerError`
- `PersistentTransactionManager`
- `OutboundTransactionManager` is deleted and replaced by `TransactionEncoder`
which now incorporates `submit(encoded:)` functionality
- `DatabaseMigrationManager` is remove since it's no longer needed all Database
migrations shall be hanlded by the rust layer.
- `ZcashSDK.defaultPendingDbName` along with any sibling members
- `TransactionRepository`
- `findMemos(for receivedTransaction: ZcashTransaction.Received)`
- `findMemos(for sentTransaction: ZcashTransaction.Sent)`
### [#1013] Enable more granular control over logging behavior
Now the SDK allows for more fine-tuning of its logging behavior. The `LoggingPolicy` enum

View File

@ -47,7 +47,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
cacheDbURL: nil,
fsBlockDbRoot: try! fsBlockDbRootURLHelper(),
dataDbURL: try! dataDbURLHelper(),
pendingDbURL: try! pendingDbURLHelper(),
endpoint: DemoAppConfig.endpoint,
network: kZcashNetwork,
spendParamsURL: try! spendParamsURLHelper(),
@ -74,7 +73,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
.store(in: &cancellables)
}
func txMined(_ transaction: PendingTransactionEntity) {
func txMined(_ transaction: ZcashTransaction.Overview) {
NotificationBubble.display(
in: window!.rootViewController!.view,
options: NotificationBubble.sucessOptions(
@ -189,11 +188,6 @@ func dataDbURLHelper() throws -> URL {
)
}
func pendingDbURLHelper() throws -> URL {
try documentsDirectoryHelper()
.appendingPathComponent(kZcashNetwork.constants.defaultDbNamePrefix + ZcashSDK.defaultPendingDbName)
}
func spendParamsURLHelper() throws -> URL {
try documentsDirectoryHelper().appendingPathComponent("sapling-spend.params")
}

View File

@ -39,14 +39,12 @@ class TransactionsDataSource: NSObject {
case .pending:
let rawTransactions = await synchronizer.pendingTransactions
for pendingTransaction in rawTransactions {
let defaultFee: Zatoshi = kZcashNetwork.constants.defaultFee(for: pendingTransaction.minedHeight)
let transaction = pendingTransaction.makeTransactionEntity(defaultFee: defaultFee)
let memos = try await synchronizer.getMemos(for: transaction)
let memos = try await synchronizer.getMemos(for: pendingTransaction)
transactions.append(TransactionDetailModel(pendingTransaction: pendingTransaction, memos: memos))
}
case .cleared:
let rawTransactions = await synchronizer.clearedTransactions
let rawTransactions = await synchronizer.transactions
for transaction in rawTransactions {
let memos = try await synchronizer.getMemos(for: transaction)
transactions.append(TransactionDetailModel(transaction: transaction, memos: memos))
@ -66,13 +64,11 @@ class TransactionsDataSource: NSObject {
case .all:
let rawPendingTransactions = await synchronizer.pendingTransactions
for pendingTransaction in rawPendingTransactions {
let defaultFee: Zatoshi = kZcashNetwork.constants.defaultFee(for: pendingTransaction.minedHeight)
let transaction = pendingTransaction.makeTransactionEntity(defaultFee: defaultFee)
let memos = try await synchronizer.getMemos(for: transaction)
let memos = try await synchronizer.getMemos(for: pendingTransaction)
transactions.append(TransactionDetailModel(pendingTransaction: pendingTransaction, memos: memos))
}
let rawClearedTransactions = await synchronizer.clearedTransactions
let rawClearedTransactions = await synchronizer.transactions
for transaction in rawClearedTransactions {
let memos = try await synchronizer.getMemos(for: transaction)
transactions.append(TransactionDetailModel(transaction: transaction, memos: memos))

View File

@ -105,7 +105,6 @@ class SyncBlocksListViewController: UIViewController {
cacheDbURL: nil,
fsBlockDbRoot: try! fsBlockDbRootURLHelper(),
dataDbURL: try! dataDbURLHelper(),
pendingDbURL: try! pendingDbURLHelper(),
endpoint: DemoAppConfig.endpoint,
network: kZcashNetwork,
spendParamsURL: try! spendParamsURLHelper(),

View File

@ -11,9 +11,9 @@ import ZcashLightClientKit
final class TransactionDetailModel {
enum Transaction {
case sent(ZcashTransaction.Sent)
case received(ZcashTransaction.Received)
case pending(PendingTransactionEntity)
case sent(ZcashTransaction.Overview)
case received(ZcashTransaction.Overview)
case pending(ZcashTransaction.Overview)
case cleared(ZcashTransaction.Overview)
}
@ -25,7 +25,7 @@ final class TransactionDetailModel {
var zatoshi: Zatoshi
var memo: Memo?
init(sendTransaction transaction: ZcashTransaction.Sent, memos: [Memo]) {
init(sendTransaction transaction: ZcashTransaction.Overview, memos: [Memo]) {
self.transaction = .sent(transaction)
self.id = transaction.rawID
self.minedHeight = transaction.minedHeight
@ -41,22 +41,22 @@ final class TransactionDetailModel {
}
}
init(receivedTransaction transaction: ZcashTransaction.Received, memos: [Memo]) {
init(receivedTransaction transaction: ZcashTransaction.Overview, memos: [Memo]) {
self.transaction = .received(transaction)
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)
self.created = Date(timeIntervalSince1970: transaction.blockTime ?? Date().timeIntervalSince1970)
}
init(pendingTransaction transaction: PendingTransactionEntity, memos: [Memo]) {
init(pendingTransaction transaction: ZcashTransaction.Overview, memos: [Memo]) {
self.transaction = .pending(transaction)
self.id = transaction.rawTransactionId
self.id = transaction.rawID
self.minedHeight = transaction.minedHeight
self.expiryHeight = transaction.expiryHeight
self.created = Date(timeIntervalSince1970: transaction.createTime)
self.created = Date(timeIntervalSince1970: transaction.blockTime ?? Date().timeIntervalSince1970)
self.zatoshi = transaction.value
self.memo = memos.first
}

View File

@ -1,3 +1,11 @@
# Migrating from previous versions to _Unreleased_
PendingDb is no longer used. Wallet developers should take care about deleting
the database file since the SDK will no longer require it or any of the
information stored.
Failed transactions will be treated as "Expired-Unmined" instead. The SDK won't
track failures on its own. Wallet developers would have to account for those.
# Migrating from previous versions to 0.20.0-beta
The `SDKSynchronizer` no longer uses `NotificationCenter` to send notifications.
Notifications are replaced with `Combine` publishers.

View File

@ -104,8 +104,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "bf5992c2e53749ad11c1e85ad5d9c63e39bdf3cc",
"version" : "0.3.0"
"revision" : "75821e2b859600707318e4a788abbe27e6615833",
"version" : "0.3.1"
}
}
],

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.3.0")
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", from: "0.3.1")
],
targets: [
.target(
@ -42,14 +42,13 @@ let package = Package(
exclude: [
"proto/darkside.proto",
"Sourcery/AutoMockable.stencil",
"Sourcery/generateMocks"
"Sourcery/generateMocks.sh"
],
resources: [
.copy("Resources/test_data.db"),
.copy("Resources/cache.db"),
.copy("Resources/darkside_caches.db"),
.copy("Resources/darkside_data.db"),
.copy("Resources/darkside_pending.db"),
.copy("Resources/sandblasted_mainnet_block.json"),
.copy("Resources/txBase64String.txt"),
.copy("Resources/txFromAndroidSDK.txt"),

View File

@ -676,10 +676,6 @@ actor CompactBlockProcessor {
try fileManager.removeItem(at: config.dataDb)
}
if fileManager.fileExists(atPath: context.pendingDbURL.path) {
try fileManager.removeItem(at: context.pendingDbURL)
}
await context.completion(nil)
} catch {
await context.completion(error)

View File

@ -1,177 +0,0 @@
//
// DatabaseMigrationManager.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 3/31/21.
//
import Foundation
import SQLite
class MigrationManager {
// swiftlint:disable identifier_name
enum PendingDbMigration: Int32, CaseIterable {
case none = 0
case v1 = 1
case v2 = 2
}
static let nextPendingDbMigration = PendingDbMigration.v2
let pendingDb: ConnectionProvider
let network: NetworkType
let logger: Logger
init(
pendingDbConnection: ConnectionProvider,
networkType: NetworkType,
logger: Logger
) {
self.pendingDb = pendingDbConnection
self.network = networkType
self.logger = logger
}
func performMigration() throws {
try migratePendingDb()
}
}
private extension MigrationManager {
/// - Throws:
/// - `dbMigrationGenericFailure` when can't read current version of the pending DB.
/// - `dbMigrationInvalidVersion` when unknown version is read from the current pending DB.
/// - `dbMigrationV1` when migration to version 1 fails.
/// - `dbMigrationV2` when migration to version 2 fails.
func migratePendingDb() throws {
// getUserVersion returns a default value of zero for an unmigrated database.
let currentPendingDbVersion: Int32
do {
currentPendingDbVersion = try pendingDb.connection().getUserVersion()
} catch {
throw ZcashError.dbMigrationGenericFailure(error)
}
logger.debug(
"Attempting to perform migration for pending Db - currentVersion: \(currentPendingDbVersion)." +
"Latest version is: \(Self.nextPendingDbMigration.rawValue - 1)"
)
for version in (currentPendingDbVersion..<Self.nextPendingDbMigration.rawValue) {
switch PendingDbMigration(rawValue: version) {
case .some(.none):
try migratePendingDbV1()
case .some(.v1):
try migratePendingDbV2()
case .some(.v2):
// we have no migrations to run after v2; this case should ordinarily be
// unreachable due to the bound on the loop.
break
case nil:
throw ZcashError.dbMigrationInvalidVersion
}
}
}
func migratePendingDbV1() throws {
let statement = PendingTransactionSQLDAO.table.create(ifNotExists: true) { createdTable in
createdTable.column(PendingTransactionSQLDAO.TableColumns.id, primaryKey: .autoincrement)
createdTable.column(PendingTransactionSQLDAO.TableColumns.toAddress)
createdTable.column(PendingTransactionSQLDAO.TableColumns.accountIndex)
createdTable.column(PendingTransactionSQLDAO.TableColumns.minedHeight)
createdTable.column(PendingTransactionSQLDAO.TableColumns.expiryHeight)
createdTable.column(PendingTransactionSQLDAO.TableColumns.cancelled)
createdTable.column(PendingTransactionSQLDAO.TableColumns.encodeAttempts, defaultValue: 0)
createdTable.column(PendingTransactionSQLDAO.TableColumns.errorMessage)
createdTable.column(PendingTransactionSQLDAO.TableColumns.errorCode)
createdTable.column(PendingTransactionSQLDAO.TableColumns.submitAttempts, defaultValue: 0)
createdTable.column(PendingTransactionSQLDAO.TableColumns.createTime)
createdTable.column(PendingTransactionSQLDAO.TableColumns.rawTransactionId)
createdTable.column(PendingTransactionSQLDAO.TableColumns.value)
createdTable.column(PendingTransactionSQLDAO.TableColumns.raw)
createdTable.column(PendingTransactionSQLDAO.TableColumns.memo)
createdTable.column(PendingTransactionSQLDAO.TableColumns.fee)
}
do {
try pendingDb.connection().transaction(.immediate) {
try pendingDb.connection().execute(statement)
try pendingDb.connection().setUserVersion(PendingDbMigration.v1.rawValue)
}
} catch {
throw ZcashError.dbMigrationV1(error)
}
}
func migratePendingDbV2() throws {
do {
try pendingDb.connection().transaction(.immediate) {
let statement =
"""
ALTER TABLE pending_transactions RENAME TO pending_transactions_old;
CREATE TABLE pending_transactions(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
to_address TEXT,
to_internal INTEGER,
account_index INTEGER NOT NULL,
mined_height INTEGER,
expiry_height INTEGER,
cancelled INTEGER,
encode_attempts INTEGER DEFAULT (0),
error_message TEXT,
error_code INTEGER,
submit_attempts INTEGER DEFAULT (0),
create_time REAL,
txid BLOB,
value INTEGER NOT NULL,
raw BLOB,
memo BLOB,
fee INTEGER
);
INSERT INTO pending_transactions
SELECT
id,
to_address,
NULL,
account_index,
mined_height,
expiry_height,
cancelled,
encode_attempts,
error_message,
error_code,
submit_attempts,
create_time,
txid,
value,
raw,
memo,
NULL
FROM pending_transactions_old;
DROP TABLE pending_transactions_old
"""
try pendingDb.connection().execute(statement)
try pendingDb.connection().setUserVersion(PendingDbMigration.v2.rawValue)
}
} catch {
throw ZcashError.dbMigrationV2(error)
}
}
}
private extension Connection {
func getUserVersion() throws -> Int32 {
guard let version = try scalar("PRAGMA user_version") as? Int64 else {
return 0
}
return Int32(version)
}
func setUserVersion(_ version: Int32) throws {
try execute("PRAGMA user_version = \(version)")
}
}

View File

@ -9,7 +9,6 @@ import Foundation
class AfterSyncHooksManager {
struct WipeContext {
let pendingDbURL: URL
let prewipe: () -> Void
let completion: (Error?) async -> Void
}
@ -52,7 +51,6 @@ class AfterSyncHooksManager {
static var emptyWipe: Hook {
return .wipe(
WipeContext(
pendingDbURL: URL(fileURLWithPath: "/"),
prewipe: { },
completion: { _ in }
)

View File

@ -41,31 +41,23 @@ public protocol ClosureSynchronizer {
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?,
completion: @escaping (Result<PendingTransactionEntity, Error>) -> Void
completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
)
func shieldFunds(
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi,
completion: @escaping (Result<PendingTransactionEntity, Error>) -> Void
completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
)
func cancelSpend(transaction: PendingTransactionEntity, completion: @escaping (Bool) -> Void)
func pendingTransactions(completion: @escaping ([PendingTransactionEntity]) -> Void)
func pendingTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func clearedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func sentTranscations(completion: @escaping ([ZcashTransaction.Sent]) -> Void)
func receivedTransactions(completion: @escaping ([ZcashTransaction.Received]) -> Void)
func sentTranscations(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func receivedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func paginatedTransactions(of kind: TransactionKind) -> PaginatedTransactionRepository
func getMemos(for transaction: ZcashTransaction.Overview, completion: @escaping (Result<[Memo], Error>) -> Void)
func getMemos(for receivedTransaction: ZcashTransaction.Received, completion: @escaping (Result<[Memo], Error>) -> Void)
func getMemos(for sentTransaction: ZcashTransaction.Sent, completion: @escaping (Result<[Memo], Error>) -> Void)
func getRecipients(for transaction: ZcashTransaction.Overview, completion: @escaping ([TransactionRecipient]) -> Void)
func getRecipients(for transaction: ZcashTransaction.Sent, completion: @escaping ([TransactionRecipient]) -> Void)
func allConfirmedTransactions(
from transaction: ZcashTransaction.Overview,

View File

@ -40,31 +40,28 @@ public protocol CombineSynchronizer {
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?
) -> SinglePublisher<PendingTransactionEntity, Error>
) -> SinglePublisher<ZcashTransaction.Overview, Error>
func shieldFunds(
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi
) -> SinglePublisher<PendingTransactionEntity, Error>
) -> SinglePublisher<ZcashTransaction.Overview, Error>
func cancelSpend(transaction: PendingTransactionEntity) -> SinglePublisher<Bool, Never>
var pendingTransactions: SinglePublisher<[PendingTransactionEntity], Never> { get }
var clearedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var sentTransactions: SinglePublisher<[ZcashTransaction.Sent], Never> { get }
var receivedTransactions: SinglePublisher<[ZcashTransaction.Received], Never> { get }
var allTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var pendingTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var sentTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var receivedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
func paginatedTransactions(of kind: TransactionKind) -> PaginatedTransactionRepository
func getMemos(for transaction: ZcashTransaction.Overview) -> SinglePublisher<[Memo], Error>
func getMemos(for receivedTransaction: ZcashTransaction.Received) -> SinglePublisher<[Memo], Error>
func getMemos(for sentTransaction: ZcashTransaction.Sent) -> SinglePublisher<[Memo], Error>
func getRecipients(for transaction: ZcashTransaction.Overview) -> SinglePublisher<[TransactionRecipient], Never>
func getRecipients(for transaction: ZcashTransaction.Sent) -> SinglePublisher<[TransactionRecipient], Never>
func allConfirmedTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error>
func allPendingTransactions() -> SinglePublisher<[ZcashTransaction.Overview], Error>
func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error>
func latestHeight() -> SinglePublisher<BlockHeight, Error>

View File

@ -128,9 +128,6 @@ public enum ZcashSDK {
/// Default Name for Compact Block caches db
public static let defaultCacheDbName = "caches.db"
/// Default name for pending transactions db
public static let defaultPendingDbName = "pending.db"
/// The Url that is used by default in zcashd.
/// We'll want to make this externally configurable, rather than baking it into the SDK but
/// this will do for now, since we're using a cloudfront URL that already redirects.
@ -161,9 +158,6 @@ public protocol NetworkConstants {
@available(*, deprecated, message: "use this name to clean up the sqlite compact block database")
static var defaultCacheDbName: String { get }
/// Default name for pending transactions db
static var defaultPendingDbName: String { get }
/// Default prefix for db filenames
static var defaultDbNamePrefix: String { get }
@ -195,9 +189,6 @@ public enum ZcashSDKMainnetConstants: NetworkConstants {
/// Default Name for Compact Block caches db
public static let defaultCacheDbName = "caches.db"
/// Default name for pending transactions db
public static let defaultPendingDbName = "pending.db"
public static let defaultDbNamePrefix = "ZcashSdk_mainnet_"
@ -216,9 +207,6 @@ public enum ZcashSDKTestnetConstants: NetworkConstants {
public static let defaultCacheDbName = "caches.db"
public static let defaultFsBlockDbRootName = "fs_cache"
/// Default name for pending transactions db
public static let defaultPendingDbName = "pending.db"
public static let defaultDbNamePrefix = "ZcashSdk_testnet_"

View File

@ -1,201 +0,0 @@
//
// 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))
}
}
}
}

View File

@ -1,378 +0,0 @@
//
// PendingTransactionDao.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/19/19.
//
import Foundation
import SQLite
struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
enum CodingKeys: String, CodingKey {
case toAddress = "to_address"
case toInternalAccount = "to_internal"
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"
case fee
}
let recipient: PendingTransactionRecipient
let accountIndex: Int
var minedHeight: BlockHeight
var expiryHeight: BlockHeight
var cancelled: Int
var encodeAttempts: Int
var submitAttempts: Int
var errorMessage: String?
var errorCode: Int?
let createTime: TimeInterval
var raw: Data?
var id: Int?
let value: Zatoshi
let memo: Data?
var rawTransactionId: Data?
let fee: Zatoshi?
static func from(entity: PendingTransactionEntity) -> PendingTransaction {
PendingTransaction(
recipient: entity.recipient,
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,
fee: entity.fee
)
}
init(
recipient: PendingTransactionRecipient,
accountIndex: Int,
minedHeight: BlockHeight,
expiryHeight: BlockHeight,
cancelled: Int,
encodeAttempts: Int,
submitAttempts: Int,
errorMessage: String?,
errorCode: Int?,
createTime: TimeInterval,
raw: Data?,
id: Int?,
value: Zatoshi,
memo: Data?,
rawTransactionId: Data?,
fee: Zatoshi?
) {
self.recipient = recipient
self.accountIndex = accountIndex
self.minedHeight = minedHeight
self.expiryHeight = expiryHeight
self.cancelled = cancelled
self.encodeAttempts = encodeAttempts
self.submitAttempts = submitAttempts
self.errorMessage = errorMessage
self.errorCode = errorCode
self.createTime = createTime
self.raw = raw
self.id = id
self.value = value
self.memo = memo
self.rawTransactionId = rawTransactionId
self.fee = fee
}
/// - Throws:
/// - `pendingTransactionDecodeInvalidData` if some of the fields contain invalid data.
/// - `pendingTransactionCantDecode` if decoding fails.
init(from decoder: Decoder) throws {
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
let toAddress: String? = try container.decodeIfPresent(String.self, forKey: .toAddress)
let toInternalAccount: Int? = try container.decodeIfPresent(Int.self, forKey: .toInternalAccount)
switch (toAddress, toInternalAccount) {
case let (.some(address), nil):
guard let recipient = Recipient.forEncodedAddress(encoded: address) else {
throw ZcashError.pendingTransactionDecodeInvalidData(["toAddress"])
}
self.recipient = .address(recipient.0)
case let (nil, .some(accountId)):
self.recipient = .internalAccount(UInt32(accountId))
default:
throw ZcashError.pendingTransactionDecodeInvalidData(["toAddress", "toInternalAccount"])
}
self.accountIndex = try container.decode(Int.self, forKey: .accountIndex)
self.minedHeight = try container.decode(BlockHeight.self, forKey: .minedHeight)
self.expiryHeight = try container.decode(BlockHeight.self, forKey: .expiryHeight)
self.cancelled = try container.decode(Int.self, forKey: .cancelled)
self.encodeAttempts = try container.decode(Int.self, forKey: .encodeAttempts)
self.submitAttempts = try container.decode(Int.self, forKey: .submitAttempts)
self.errorMessage = try container.decodeIfPresent(String.self, forKey: .errorMessage)
self.errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode)
self.createTime = try container.decode(TimeInterval.self, forKey: .createTime)
self.raw = try container.decodeIfPresent(Data.self, forKey: .raw)
self.id = try container.decodeIfPresent(Int.self, forKey: .id)
let zatoshiValue = try container.decode(Int64.self, forKey: .value)
self.value = Zatoshi(zatoshiValue)
self.memo = try container.decodeIfPresent(Data.self, forKey: .memo)
self.rawTransactionId = try container.decodeIfPresent(Data.self, forKey: .rawTransactionId)
if let feeValue = try container.decodeIfPresent(Int64.self, forKey: .fee) {
self.fee = Zatoshi(feeValue)
} else {
self.fee = nil
}
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.pendingTransactionCantDecode(error)
}
}
}
/// - Throws:
/// - `pendingTransactionCantEncode` if encoding fails.
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var toAddress: String?
var accountId: Int?
switch recipient {
case .address(let recipient):
toAddress = recipient.stringEncoded
case .internalAccount(let acct):
accountId = Int(acct)
}
do {
try container.encodeIfPresent(toAddress, forKey: .toAddress)
try container.encodeIfPresent(accountId, forKey: .toInternalAccount)
try container.encode(self.accountIndex, forKey: .accountIndex)
try container.encode(self.minedHeight, forKey: .minedHeight)
try container.encode(self.expiryHeight, forKey: .expiryHeight)
try container.encode(self.cancelled, forKey: .cancelled)
try container.encode(self.encodeAttempts, forKey: .encodeAttempts)
try container.encode(self.submitAttempts, forKey: .submitAttempts)
try container.encodeIfPresent(self.errorMessage, forKey: .errorMessage)
try container.encodeIfPresent(self.errorCode, forKey: .errorCode)
try container.encode(self.createTime, forKey: .createTime)
try container.encodeIfPresent(self.raw, forKey: .raw)
try container.encodeIfPresent(self.id, forKey: .id)
try container.encode(self.value.amount, forKey: .value)
try container.encodeIfPresent(self.memo, forKey: .memo)
try container.encodeIfPresent(self.rawTransactionId, forKey: .rawTransactionId)
try container.encodeIfPresent(self.fee?.amount, forKey: .fee)
} catch {
throw ZcashError.pendingTransactionCantEncode(error)
}
}
func isSameTransactionId<T>(other: T) -> Bool where T: RawIdentifiable {
self.rawTransactionId == other.rawTransactionId
}
}
extension PendingTransaction {
init(value: Zatoshi, recipient: PendingTransactionRecipient, memo: MemoBytes?, account index: Int) {
var memoData: Data?
if let memo {
memoData = Data(memo.bytes)
}
self = PendingTransaction(
recipient: recipient,
accountIndex: index,
minedHeight: -1,
expiryHeight: -1,
cancelled: 0,
encodeAttempts: 0,
submitAttempts: 0,
errorMessage: nil,
errorCode: nil,
createTime: Date().timeIntervalSince1970,
raw: nil,
id: nil,
value: value,
memo: memoData,
rawTransactionId: nil,
fee: nil
)
}
}
class PendingTransactionSQLDAO: PendingTransactionRepository {
enum TableColumns {
static let toAddress = Expression<String?>("to_address")
static let toInternalAccount = Expression<Int?>("to_internal")
static let accountIndex = Expression<Int>("account_index")
static let minedHeight = Expression<Int?>("mined_height")
static let expiryHeight = Expression<Int?>("expiry_height")
static let cancelled = Expression<Int?>("cancelled")
static let encodeAttempts = Expression<Int?>("encode_attempts")
static let errorMessage = Expression<String?>("error_message")
static let submitAttempts = Expression<Int?>("submit_attempts")
static let errorCode = Expression<Int?>("error_code")
static let createTime = Expression<TimeInterval?>("create_time")
static let raw = Expression<Blob?>("raw")
static let id = Expression<Int>("id")
static let value = Expression<Zatoshi>("value")
static let memo = Expression<Blob?>("memo")
static let rawTransactionId = Expression<Blob?>("txid")
static let fee = Expression<Zatoshi?>("fee")
}
static let table = Table("pending_transactions")
let dbProvider: ConnectionProvider
let logger: Logger
init(dbProvider: ConnectionProvider, logger: Logger) {
self.dbProvider = dbProvider
self.logger = logger
}
func closeDBConnection() {
dbProvider.close()
}
/// - Throws:
/// - `pendingTransactionDAOCreate` if sqlite fails.
func create(_ transaction: PendingTransactionEntity) throws -> Int {
do {
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
return try Int(dbProvider.connection().run(Self.table.insert(pendingTx)))
} catch {
throw ZcashError.pendingTransactionDAOCreate(error)
}
}
/// - Throws:
/// - `pendingTransactionDAOUpdateMissingID` if `transaction` (or entity created from `transaction`) is missing id.
/// - `pendingTransactionDAOUpdate` if sqlite fails
func update(_ transaction: PendingTransactionEntity) throws {
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
guard let id = pendingTx.id else {
throw ZcashError.pendingTransactionDAOUpdateMissingID
}
do {
let updatedRows = try dbProvider.connection().run(Self.table.filter(TableColumns.id == id).update(pendingTx))
if updatedRows == 0 {
logger.error("attempted to update pending transactions but no rows were updated")
}
} catch {
throw ZcashError.pendingTransactionDAOUpdate(error)
}
}
/// - Throws:
/// - `pendingTransactionDAODeleteMissingID` if `transaction` (or entity created from `transaction`) is missing id.
/// - `pendingTransactionDAODelete` if sqlite fails
func delete(_ transaction: PendingTransactionEntity) throws {
guard let id = transaction.id else {
throw ZcashError.pendingTransactionDAODeleteMissingID
}
do {
try dbProvider.connection().run(Self.table.filter(TableColumns.id == id).delete())
} catch {
throw ZcashError.pendingTransactionDAODelete(error)
}
}
/// - Throws:
/// - `pendingTransactionDAOCancelMissingID` if `transaction` (or entity created from `transaction`) is missing id.
/// - `pendingTransactionDAOCancel` if sqlite fails
func cancel(_ transaction: PendingTransactionEntity) throws {
var pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
pendingTx.cancelled = 1
guard let txId = pendingTx.id else {
throw ZcashError.pendingTransactionDAOCancelMissingID
}
do {
try dbProvider.connection().run(Self.table.filter(TableColumns.id == txId).update(pendingTx))
} catch {
throw ZcashError.pendingTransactionDAOCancel(error)
}
}
/// - Throws:
/// - `pendingTransactionDAOFind` if sqlite fails
func find(by id: Int) throws -> PendingTransactionEntity? {
do {
guard let row = try dbProvider.connection().pluck(Self.table.filter(TableColumns.id == id).limit(1)) else {
return nil
}
let pendingTx: PendingTransaction = try row.decode()
return pendingTx
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.pendingTransactionDAOFind(error)
}
}
}
/// - Throws:
/// - `pendingTransactionDAOGetAll` if sqlite fails
func getAll() throws -> [PendingTransactionEntity] {
do {
let allTxs: [PendingTransaction] = try dbProvider.connection().prepare(Self.table).map { row in
try row.decode()
}
return allTxs
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.pendingTransactionDAOGetAll(error)
}
}
}
/// - Throws:
/// - `pendingTransactionDAOApplyMinedHeight` if sqlite fails
func applyMinedHeight(_ height: BlockHeight, id: Int) throws {
do {
let transaction = Self.table.filter(TableColumns.id == id)
let updatedRows = try dbProvider.connection()
.run(transaction.update([TableColumns.minedHeight <- height]))
if updatedRows == 0 {
logger.error("attempted to update a row but none was updated")
}
} catch {
throw ZcashError.pendingTransactionDAOApplyMinedHeight(error)
}
}
}

View File

@ -15,19 +15,15 @@ class TransactionSQLDAO: TransactionRepository {
}
let dbProvider: ConnectionProvider
let transactions = Table("transactions")
private let blockDao: BlockSQLDAO
private let sentNotesRepository: SentNotesRepository
private let transactionsView = View("v_transactions")
private let receivedNotesTable = Table("received_notes")
private let sentNotesTable = Table("sent_notes")
private let txOutputsView = View("v_tx_outputs")
private let traceClosure: ((String) -> Void)?
init(dbProvider: ConnectionProvider, traceClosure: ((String) -> Void)? = nil) {
self.dbProvider = dbProvider
self.blockDao = BlockSQLDAO(dbProvider: dbProvider)
self.sentNotesRepository = SentNotesSQLDAO(dbProvider: dbProvider)
self.traceClosure = traceClosure
}
@ -59,7 +55,7 @@ class TransactionSQLDAO: TransactionRepository {
func countAll() async throws -> Int {
do {
return try connection().scalar(transactions.count)
return try connection().scalar(transactionsView.count)
} catch {
throw ZcashError.transactionRepositoryCountAll(error)
}
@ -67,7 +63,7 @@ class TransactionSQLDAO: TransactionRepository {
func countUnmined() async throws -> Int {
do {
return try connection().scalar(transactions.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
return try connection().scalar(transactionsView.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
} catch {
throw ZcashError.transactionRepositoryCountUnmined(error)
}
@ -131,66 +127,53 @@ class TransactionSQLDAO: TransactionRepository {
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
}
func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Received] {
func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
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.Overview(row: $0) }
.compactMap { ZcashTransaction.Received(overview: $0) }
}
func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Sent] {
func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
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.Overview(row: $0) }
.compactMap { ZcashTransaction.Sent(overview: $0) }
}
func findPendingTransactions(latestHeight: BlockHeight, offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
let query = transactionsView
.filterPendingFrom(latestHeight)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
.limit(limit, offset: offset)
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
}
func findMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo] {
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] {
return try await findMemos(for: receivedTransaction.id, table: receivedNotesTable)
}
func findMemos(for sentTransaction: ZcashTransaction.Sent) async throws -> [Memo] {
return try await findMemos(for: sentTransaction.id, table: sentNotesTable)
}
func getRecipients(for id: Int) async -> [TransactionRecipient] {
return self.sentNotesRepository.getRecipients(for: id)
}
private func findMemos(for transactionID: Int, table: Table) async throws -> [Memo] {
let query = table
.filter(NotesTableStructure.transactionID == transactionID)
do {
let memos = try connection().prepare(query).compactMap { row in
do {
let rawMemo = try row.get(NotesTableStructure.memo)
return try Memo(bytes: rawMemo.bytes)
} catch {
return nil
}
}
return memos
return try await getTransactionOutputs(for: transaction.id)
.compactMap { $0.memo }
} catch {
throw ZcashError.transactionRepositoryFindMemos(error)
}
}
func getTransactionOutputs(for id: Int) async throws -> [ZcashTransaction.Output] {
let query = self.txOutputsView
.filter(ZcashTransaction.Output.Column.idTx == id)
return try execute(query) { try ZcashTransaction.Output(row: $0) }
}
func getRecipients(for id: Int) async throws -> [TransactionRecipient] {
try await getTransactionOutputs(for: id).map { $0.recipient }
}
private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> Entity {
let entities: [Entity] = try execute(query, createEntity: createEntity)
guard let entity = entities.first else { throw ZcashError.transactionRepositoryEntityNotFound }
@ -214,6 +197,24 @@ class TransactionSQLDAO: TransactionRepository {
}
}
private extension View {
func filterPendingFrom(_ latestHeight: BlockHeight) -> View {
filter(
// transaction has not expired
ZcashTransaction.Overview.Column.expiredUnmined == false &&
// transaction is "sent"
ZcashTransaction.Overview.Column.value < 0 &&
// transaction has not been mined yet OR
// it has been within the latest `defaultStaleTolerance` blocks
(
ZcashTransaction.Overview.Column.minedHeight == nil ||
ZcashTransaction.Overview.Column.minedHeight > (latestHeight - ZcashSDK.defaultStaleTolerance)
)
)
}
}
private extension View {
func filterQueryFor(kind: TransactionKind) -> View {
switch kind {

View File

@ -13,7 +13,7 @@ protocol AccountEntity {
var ufvk: String { get }
}
struct Account: AccountEntity, Encodable, Decodable {
struct DbAccount: AccountEntity, Encodable, Decodable {
enum CodingKeys: String, CodingKey {
case account
case ufvk
@ -23,7 +23,7 @@ struct Account: AccountEntity, Encodable, Decodable {
let ufvk: String
}
extension Account: Hashable {
extension DbAccount: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(account)
hasher.combine(ufvk)
@ -68,7 +68,7 @@ class AccountSQDAO: AccountRepository {
do {
return try dbProvider.connection()
.prepare(table)
.map { row -> Account in
.map { row -> DbAccount in
do {
return try row.decode()
} catch {
@ -94,7 +94,7 @@ class AccountSQDAO: AccountRepository {
.prepare(query)
.map {
do {
return try $0.decode() as Account
return try $0.decode() as DbAccount
} catch {
throw ZcashError.accountDAOFindByCantDecode(error)
}
@ -113,7 +113,7 @@ class AccountSQDAO: AccountRepository {
/// - `accountDAOUpdate` if sqlite query updating account failed.
/// - `accountDAOUpdatedZeroRows` if sqlite query updating account pass but it affects 0 rows.
func update(_ account: AccountEntity) throws {
guard let acc = account as? Account else {
guard let acc = account as? DbAccount else {
throw ZcashError.accountDAOUpdateInvalidAccount
}

View File

@ -9,7 +9,7 @@ import Foundation
struct EncodedTransaction {
let transactionId: Data
let raw: Data?
let raw: Data
}
extension EncodedTransaction: Hashable {

View File

@ -1,220 +0,0 @@
//
// PendingTransactionEntity.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/19/19.
//
import Foundation
public typealias TransactionRecipient = PendingTransactionRecipient
public enum PendingTransactionRecipient: Equatable {
case address(Recipient)
case internalAccount(UInt32)
}
/**
Represents a sent transaction that has not been confirmed yet on the blockchain
*/
public protocol PendingTransactionEntity: RawIdentifiable {
/**
internal id for this transaction
*/
var id: Int? { get set }
/**
value in zatoshi
*/
var value: Zatoshi { get }
/**
data containing the memo if any
*/
var memo: Data? { get }
var fee: Zatoshi? { get }
var raw: Data? { get set }
/**
recipient address
*/
var recipient: PendingTransactionRecipient { get }
/**
index of the account from which the funds were sent
*/
var accountIndex: Int { get }
/**
height which the block was mined at.
-1 when block has not been mined yet
*/
var minedHeight: BlockHeight { get set }
/**
height for which the represented transaction would be considered expired
*/
var expiryHeight: BlockHeight { get set }
/**
value is 1 if the transaction was cancelled
*/
var cancelled: Int { get set }
/**
how many times this transaction encoding was attempted
*/
var encodeAttempts: Int { get set }
/**
How many attempts to send this transaction have been done
*/
var submitAttempts: Int { get set }
/**
Error message if available.
*/
var errorMessage: String? { get set }
/**
error code, if available
*/
var errorCode: Int? { get set }
/**
create time of the represented transaction
- Note: represented in timeIntervalySince1970
*/
var createTime: TimeInterval { get }
/**
Checks whether this transaction is the same as the given transaction
*/
func isSameTransactionId<T: RawIdentifiable> (other: T) -> Bool
/**
returns whether the represented transaction is pending based on the provided block height
*/
func isPending(currentHeight: Int) -> Bool
/**
if the represented transaction is being created
*/
var isCreating: Bool { get }
/**
returns whether the represented transaction has failed to be encoded
*/
var isFailedEncoding: Bool { get }
/**
returns whether the represented transaction has failed to be submitted
*/
var isFailedSubmit: Bool { get }
/**
returns whether the represented transaction presents some kind of error
*/
var isFailure: Bool { get }
/**
returns whether the represented transaction has been cancelled by the user
*/
var isCancelled: Bool { get }
/**
returns whether the represented transaction has been successfully mined
*/
var isMined: Bool { get }
/**
returns whether the represented transaction has been submitted
*/
var isSubmitted: Bool { get }
/**
returns whether the represented transaction has been submitted successfully
*/
var isSubmitSuccess: Bool { get }
}
public extension PendingTransactionEntity {
func isSameTransaction<T: RawIdentifiable>(other: T) -> Bool {
guard let selfId = self.rawTransactionId, let otherId = other.rawTransactionId else { return false }
return selfId == otherId
}
var isCreating: Bool {
(raw?.isEmpty ?? true) != false && submitAttempts <= 0 && !isFailedSubmit && !isFailedEncoding
}
var isFailedEncoding: Bool {
(raw?.isEmpty ?? true) != false && encodeAttempts > 0
}
var isFailedSubmit: Bool {
errorMessage != nil || (errorCode != nil && (errorCode ?? 0) < 0)
}
var isFailure: Bool {
isFailedEncoding || isFailedSubmit
}
var isCancelled: Bool {
cancelled > 0
}
var isMined: Bool {
minedHeight > 0
}
var isSubmitted: Bool {
submitAttempts > 0
}
func isPending(currentHeight: Int = -1) -> Bool {
// not mined and not expired and successfully created
isSubmitSuccess && !isConfirmed(currentHeight: currentHeight) && (expiryHeight == -1 || expiryHeight > currentHeight) && raw != nil
}
var isSubmitSuccess: Bool {
submitAttempts > 0 && (errorCode == nil || (errorCode ?? 0) >= 0) && errorMessage == nil
}
func isConfirmed(currentHeight: Int = -1 ) -> Bool {
guard minedHeight > 0 else {
return false
}
guard currentHeight > 0 else {
return false
}
return abs(currentHeight - minedHeight) >= ZcashSDK.defaultStaleTolerance
}
}
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,
hasChange: false,
memoCount: 0,
minedHeight: minedHeight,
raw: raw,
rawID: rawTransactionId ?? Data(),
receivedNoteCount: 0,
sentNoteCount: 0,
value: value,
isExpiredUmined: false
)
}
}

View File

@ -1,22 +0,0 @@
//
// ReceivedNotesEntity.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/14/19.
//
import Foundation
protocol ReceivedNoteEntity {
var id: Int { get }
var transactionId: Int { get }
var outputIndex: Int { get }
var account: Int { get }
var value: Int { get }
var memo: Data? { get }
var spent: Int? { get }
var diversifier: Data { get }
var rcm: Data { get }
var nf: Data { get }
var isChange: Bool { get }
}

View File

@ -10,6 +10,39 @@ import SQLite
public enum ZcashTransaction {
public struct Overview {
/// Represents the transaction state based on current height of the chain,
/// mined height and expiry height of a transaction.
public enum State {
/// transaction has a `minedHeight` that's greater or equal than
/// `ZcashSDK.defaultStaleTolerance` confirmations.
case confirmed
/// transaction has no `minedHeight` but current known height is less than
/// `expiryHeight`.
case pending
/// transaction has no
case expired
init(currentHeight: BlockHeight,
minedHeight: BlockHeight?,
expiredUnmined: Bool
) {
guard !expiredUnmined else {
self = .expired
return
}
if let minedHeight, (currentHeight - minedHeight) >= ZcashSDK.defaultStaleTolerance {
self = .confirmed
} else if let minedHeight, (currentHeight - minedHeight) < ZcashSDK.defaultStaleTolerance {
self = .pending
} else if minedHeight == nil {
self = .pending
} else {
self = .expired
}
}
}
public let accountId: Int
public let blockTime: TimeInterval?
public let expiryHeight: BlockHeight?
@ -28,32 +61,31 @@ public enum ZcashTransaction {
public let isExpiredUmined: Bool
}
public struct Received {
public let blockTime: TimeInterval
public let expiryHeight: BlockHeight?
public let fromAccount: Int
public let id: Int
public let index: Int
public let memoCount: Int
public let minedHeight: BlockHeight
public let noteCount: Int
public let raw: Data?
public let rawID: Data?
public let value: Zatoshi
}
public struct Output {
public enum Pool {
case transaparent
case sapling
case other(Int)
init(rawValue: Int) {
switch rawValue {
case 0:
self = .transaparent
case 2:
self = .sapling
default:
self = .other(rawValue)
}
}
}
public struct Sent {
public let blockTime: TimeInterval?
public let expiryHeight: BlockHeight?
public let fromAccount: Int
public let id: Int
public let index: Int?
public let memoCount: Int
public let minedHeight: BlockHeight?
public let noteCount: Int
public let raw: Data?
public let rawID: Data?
public let idTx: Int
public let pool: Pool
public let index: Int
public let fromAccount: Int?
public let recipient: TransactionRecipient
public let value: Zatoshi
public let isChange: Bool
public let memo: Memo?
}
/// Used when fetching blocks from the lightwalletd
@ -64,6 +96,50 @@ public enum ZcashTransaction {
}
}
extension ZcashTransaction.Output {
enum Column {
static let idTx = Expression<Int>("id_tx")
static let pool = Expression<Int>("output_pool")
static let index = Expression<Int>("output_index")
static let toAccount = Expression<Int?>("to_account")
static let fromAccount = Expression<Int?>("from_account")
static let toAddress = Expression<String?>("to_address")
static let value = Expression<Int64>("value")
static let isChange = Expression<Bool>("is_change")
static let memo = Expression<Blob?>("memo")
}
init(row: Row) throws {
do {
idTx = try row.get(Column.idTx)
pool = .init(rawValue: try row.get(Column.pool))
index = try row.get(Column.index)
fromAccount = try row.get(Column.fromAccount)
value = Zatoshi(try row.get(Column.value))
isChange = try row.get(Column.isChange)
if
let outputRecipient = try row.get(Column.toAddress),
let metadata = DerivationTool.getAddressMetadata(outputRecipient)
{
recipient = TransactionRecipient.address(try Recipient(outputRecipient, network: metadata.networkType))
} else if let toAccount = try row.get(Column.toAccount) {
recipient = .internalAccount(UInt32(toAccount))
} else {
throw ZcashError.zcashTransactionOutputInconsistentRecipient
}
if let memoData = try row.get(Column.memo) {
memo = try Memo(bytes: memoData.bytes)
} else {
memo = nil
}
} catch {
throw ZcashError.zcashTransactionOutputInit(error)
}
}
}
extension ZcashTransaction.Overview {
enum Column {
static let accountId = Expression<Int>("account_id")
@ -97,6 +173,7 @@ extension ZcashTransaction.Overview {
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 {
@ -134,51 +211,21 @@ extension ZcashTransaction.Overview {
}
}
extension ZcashTransaction.Received {
/// 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 }
/// extension to handle pending states
public extension ZcashTransaction.Overview {
func getState(for currentHeight: BlockHeight) -> State {
State(
currentHeight: currentHeight,
minedHeight: minedHeight,
expiredUnmined: self.isExpiredUmined
)
}
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
func isPending(currentHeight: BlockHeight) -> Bool {
getState(for: currentHeight) == .pending
}
}
extension ZcashTransaction.Sent {
init?(overview: ZcashTransaction.Overview) {
guard overview.isSentTransaction else { return 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
}
}
/**
Capabilities of an entity that can be uniquely identified by a raw transaction id

View File

@ -44,18 +44,6 @@ public enum ZcashError: Equatable, Error {
/// LightWalletService.blockStream failed.
/// ZSRVC0000
case serviceBlockStreamFailed(_ error: LightWalletServiceError)
/// Migration of the pending DB failed because of unspecific reason.
/// ZDBMG0001
case dbMigrationGenericFailure(_ error: Error)
/// Migration of the pending DB failed because unknown version of the existing database.
/// ZDBMG00002
case dbMigrationInvalidVersion
/// Migration of the pending DB to version 1 failed.
/// ZDBMG00003
case dbMigrationV1(_ dbError: Error)
/// Migration of the pending DB to version 2 failed.
/// ZDBMG00004
case dbMigrationV2(_ dbError: Error)
/// SimpleConnectionProvider init of Connection failed.
/// ZSCPC0001
case simpleConnectionProvider(_ error: Error)
@ -76,30 +64,6 @@ public enum ZcashError: Equatable, Error {
/// - `destination` is filesystem URL pointing to location where downloaded file should be moved.
/// ZSAPP0004
case saplingParamsCantMoveDownloadedFile(_ error: Error, _ downloadURL: URL, _ destination: URL)
/// SQLite query failed when fetching received notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0001
case notesDAOReceivedCount(_ sqliteError: Error)
/// SQLite query failed when fetching received notes from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0002
case notesDAOReceivedNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
/// ZNDAO0003
case notesDAOReceivedCantDecode(_ error: Error)
/// SQLite query failed when fetching sent notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0004
case notesDAOSentCount(_ sqliteError: Error)
/// SQLite query failed when fetching sent notes from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0005
case notesDAOSentNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
/// ZNDAO0006
case notesDAOSentCantDecode(_ error: Error)
/// SQLite query failed when fetching block information from database.
/// - `sqliteError` is error produced by SQLite library.
/// ZBDAO0001
@ -361,6 +325,12 @@ public enum ZcashError: Equatable, Error {
/// Initialization of `ZcashTransaction.Sent` failed.
/// ZTEZT0003
case zcashTransactionSentInit(_ error: Error)
/// Initialization of `ZcashTransaction.Output` failed.
/// ZTEZT0004
case zcashTransactionOutputInit(_ error: Error)
/// Initialization of `ZcashTransaction.Output` failed because there an inconsistency in the output recipient.
/// ZTEZT0005
case zcashTransactionOutputInconsistentRecipient
/// Entity not found in the database, result of `createEntity` execution.
/// ZTREE0001
case transactionRepositoryEntityNotFound
@ -403,55 +373,6 @@ public enum ZcashError: Equatable, Error {
/// Failed to decode `Checkpoint` object.
/// ZCHKP0002
case checkpointDecode(_ error: Error)
/// Decoding of `PendingTransaction` failed because of specific invalid data.
/// - `field` is list of fields names that contain invalid data.
/// ZPETR0001
case pendingTransactionDecodeInvalidData(_ fields: [String])
/// Can't decode `PendingTransaction`.
/// - `error` is error which described why decoding failed.
/// ZPETR0002
case pendingTransactionCantDecode(_ error: Error)
/// Can't encode `PendingTransaction`.
/// - `error` is error which described why encoding failed.
/// ZPETR0003
case pendingTransactionCantEncode(_ error: Error)
/// SQLite query failed when creating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0004
case pendingTransactionDAOCreate(_ sqliteError: Error)
/// Pending transaction which should be updated is missing ID.
/// ZPETR0005
case pendingTransactionDAOUpdateMissingID
/// SQLite query failed when updating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0006
case pendingTransactionDAOUpdate(_ sqliteError: Error)
/// Pending transaction which should be deleted is missing ID.
/// ZPETR0007
case pendingTransactionDAODeleteMissingID
/// SQLite query failed when deleting pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0008
case pendingTransactionDAODelete(_ sqliteError: Error)
/// Pending transaction which should be canceled is missing ID.
/// ZPETR0009
case pendingTransactionDAOCancelMissingID
/// SQLite query failed when canceling pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0010
case pendingTransactionDAOCancel(_ sqliteError: Error)
/// SQLite query failed when seaching for pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0011
case pendingTransactionDAOFind(_ sqliteError: Error)
/// SQLite query failed when getting pending transactions.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0012
case pendingTransactionDAOGetAll(_ sqliteError: Error)
/// SQLite query failed when applying mined height.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0013
case pendingTransactionDAOApplyMinedHeight(_ sqliteError: Error)
/// Invalid account when trying to derive spending key
/// ZDRVT0001
case derivationToolSpendingKeyInvalidAccount
@ -506,33 +427,6 @@ public enum ZcashError: Equatable, Error {
/// WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk.
/// ZWLTE0002
case walletTransEncoderShieldFundsMissingSaplingParams
/// PersistentTransactionsManager cant create transaction.
/// ZPTRM0001
case persistentTransManagerCantCreateTransaction(_ recipient: PendingTransactionRecipient, _ account: Int, _ zatoshi: Zatoshi)
/// PersistentTransactionsManager cant get to address from pending transaction.
/// ZPTRM0002
case persistentTransManagerEncodeUknownToAddress(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id.
/// ZPTRM0003
case persistentTransManagerSubmitTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored.
/// ZPTRM0004
case persistentTransManagerSubmitTransactionNotFound(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is canceled.
/// ZPTRM0005
case persistentTransManagerSubmitTransactionCanceled(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data.
/// ZPTRM0006
case persistentTransManagerSubmitTransactionRawDataMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but submit API call failed.
/// ZPTRM0007
case persistentTransManagerSubmitFailed(_ entity: PendingTransactionEntity, _ serviceErrorCode: Int)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored.
/// ZPTRM0008
case persistentTransManagerApplyMinedHeightTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored.
/// ZPTRM0009
case persistentTransManagerApplyMinedHeightTransactionNotFound(_ entity: PendingTransactionEntity)
/// Initiatilzation fo `Zatoshi` from a decoder failed.
/// ZTSHO0001
case zatoshiDecode(_ error: Error)
@ -622,21 +516,11 @@ public enum ZcashError: Equatable, Error {
case .serviceFetchTransactionFailed: return "LightWalletService.fetchTransaction failed."
case .serviceFetchUTXOsFailed: return "LightWalletService.fetchUTXOs failed."
case .serviceBlockStreamFailed: return "LightWalletService.blockStream failed."
case .dbMigrationGenericFailure: return "Migration of the pending DB failed because of unspecific reason."
case .dbMigrationInvalidVersion: return "Migration of the pending DB failed because unknown version of the existing database."
case .dbMigrationV1: return "Migration of the pending DB to version 1 failed."
case .dbMigrationV2: return "Migration of the pending DB to version 2 failed."
case .simpleConnectionProvider: return "SimpleConnectionProvider init of Connection failed."
case .saplingParamsInvalidSpendParams: return "Downloaded file with sapling spending parameters isn't valid."
case .saplingParamsInvalidOutputParams: return "Downloaded file with sapling output parameters isn't valid."
case .saplingParamsDownload: return "Failed to download sapling parameters file"
case .saplingParamsCantMoveDownloadedFile: return "Failed to move sapling parameters file to final destination after download."
case .notesDAOReceivedCount: return "SQLite query failed when fetching received notes count from the database."
case .notesDAOReceivedNote: return "SQLite query failed when fetching received notes from the database."
case .notesDAOReceivedCantDecode: return "Fetched note from the SQLite but can't decode that."
case .notesDAOSentCount: return "SQLite query failed when fetching sent notes count from the database."
case .notesDAOSentNote: return "SQLite query failed when fetching sent notes from the database."
case .notesDAOSentCantDecode: return "Fetched note from the SQLite but can't decode that."
case .blockDAOBlock: return "SQLite query failed when fetching block information from database."
case .blockDAOCantDecode: return "Fetched block information from DB but can't decode them."
case .blockDAOLatestBlockHeight: return "SQLite query failed when fetching height of the latest block from the database."
@ -709,6 +593,8 @@ public enum ZcashError: Equatable, Error {
case .zcashTransactionOverviewInit: return "Initialization of `ZcashTransaction.Overview` failed."
case .zcashTransactionReceivedInit: return "Initialization of `ZcashTransaction.Received` failed."
case .zcashTransactionSentInit: return "Initialization of `ZcashTransaction.Sent` failed."
case .zcashTransactionOutputInit: return "Initialization of `ZcashTransaction.Output` failed."
case .zcashTransactionOutputInconsistentRecipient: return "Initialization of `ZcashTransaction.Output` failed because there an inconsistency in the output recipient."
case .transactionRepositoryEntityNotFound: return "Entity not found in the database, result of `createEntity` execution."
case .transactionRepositoryTransactionMissingRequiredFields: return "`Find` call is missing fields, required fields are transaction `index` and `blockTime`."
case .transactionRepositoryCountAll: return "Counting all transactions failed."
@ -723,19 +609,6 @@ public enum ZcashError: Equatable, Error {
case .memoBytesInvalidUTF8: return "Invalid UTF-8 Bytes where detected when attempting to convert MemoBytes to Memo."
case .checkpointCantLoadFromDisk: return "Failed to load JSON with checkpoint from disk."
case .checkpointDecode: return "Failed to decode `Checkpoint` object."
case .pendingTransactionDecodeInvalidData: return "Decoding of `PendingTransaction` failed because of specific invalid data."
case .pendingTransactionCantDecode: return "Can't decode `PendingTransaction`."
case .pendingTransactionCantEncode: return "Can't encode `PendingTransaction`."
case .pendingTransactionDAOCreate: return "SQLite query failed when creating pending transaction."
case .pendingTransactionDAOUpdateMissingID: return "Pending transaction which should be updated is missing ID."
case .pendingTransactionDAOUpdate: return "SQLite query failed when updating pending transaction."
case .pendingTransactionDAODeleteMissingID: return "Pending transaction which should be deleted is missing ID."
case .pendingTransactionDAODelete: return "SQLite query failed when deleting pending transaction."
case .pendingTransactionDAOCancelMissingID: return "Pending transaction which should be canceled is missing ID."
case .pendingTransactionDAOCancel: return "SQLite query failed when canceling pending transaction."
case .pendingTransactionDAOFind: return "SQLite query failed when seaching for pending transaction."
case .pendingTransactionDAOGetAll: return "SQLite query failed when getting pending transactions."
case .pendingTransactionDAOApplyMinedHeight: return "SQLite query failed when applying mined height."
case .derivationToolSpendingKeyInvalidAccount: return "Invalid account when trying to derive spending key"
case .unspentTransactionOutputDAOCreateTable: return "Creation of the table for unspent transaction output failed."
case .unspentTransactionOutputDAOStore: return "SQLite query failed when storing unspent transaction output."
@ -752,15 +625,6 @@ public enum ZcashError: Equatable, Error {
case .recipientInvalidInput: return "Can't create `Recipient` because input is invalid."
case .walletTransEncoderCreateTransactionMissingSaplingParams: return "WalletTransactionEncoder wants to create transaction but files with sapling parameters are not present on disk."
case .walletTransEncoderShieldFundsMissingSaplingParams: return "WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk."
case .persistentTransManagerCantCreateTransaction: return "PersistentTransactionsManager cant create transaction."
case .persistentTransManagerEncodeUknownToAddress: return "PersistentTransactionsManager cant get to address from pending transaction."
case .persistentTransManagerSubmitTransactionIDMissing: return "PersistentTransactionsManager wants to submit pending transaction but transaction is missing id."
case .persistentTransManagerSubmitTransactionNotFound: return "PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored."
case .persistentTransManagerSubmitTransactionCanceled: return "PersistentTransactionsManager wants to submit pending transaction but transaction is canceled."
case .persistentTransManagerSubmitTransactionRawDataMissing: return "PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data."
case .persistentTransManagerSubmitFailed: return "PersistentTransactionsManager wants to submit pending transaction but submit API call failed."
case .persistentTransManagerApplyMinedHeightTransactionIDMissing: return "PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored."
case .persistentTransManagerApplyMinedHeightTransactionNotFound: return "PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored."
case .zatoshiDecode: return "Initiatilzation fo `Zatoshi` from a decoder failed."
case .zatoshiEncode: return "Encode of `Zatoshi` failed."
case .unspentTransactionFetcherStream: return "Awaiting transactions from the stream failed."
@ -802,21 +666,11 @@ public enum ZcashError: Equatable, Error {
case .serviceFetchTransactionFailed: return .serviceFetchTransactionFailed
case .serviceFetchUTXOsFailed: return .serviceFetchUTXOsFailed
case .serviceBlockStreamFailed: return .serviceBlockStreamFailed
case .dbMigrationGenericFailure: return .dbMigrationGenericFailure
case .dbMigrationInvalidVersion: return .dbMigrationInvalidVersion
case .dbMigrationV1: return .dbMigrationV1
case .dbMigrationV2: return .dbMigrationV2
case .simpleConnectionProvider: return .simpleConnectionProvider
case .saplingParamsInvalidSpendParams: return .saplingParamsInvalidSpendParams
case .saplingParamsInvalidOutputParams: return .saplingParamsInvalidOutputParams
case .saplingParamsDownload: return .saplingParamsDownload
case .saplingParamsCantMoveDownloadedFile: return .saplingParamsCantMoveDownloadedFile
case .notesDAOReceivedCount: return .notesDAOReceivedCount
case .notesDAOReceivedNote: return .notesDAOReceivedNote
case .notesDAOReceivedCantDecode: return .notesDAOReceivedCantDecode
case .notesDAOSentCount: return .notesDAOSentCount
case .notesDAOSentNote: return .notesDAOSentNote
case .notesDAOSentCantDecode: return .notesDAOSentCantDecode
case .blockDAOBlock: return .blockDAOBlock
case .blockDAOCantDecode: return .blockDAOCantDecode
case .blockDAOLatestBlockHeight: return .blockDAOLatestBlockHeight
@ -889,6 +743,8 @@ public enum ZcashError: Equatable, Error {
case .zcashTransactionOverviewInit: return .zcashTransactionOverviewInit
case .zcashTransactionReceivedInit: return .zcashTransactionReceivedInit
case .zcashTransactionSentInit: return .zcashTransactionSentInit
case .zcashTransactionOutputInit: return .zcashTransactionOutputInit
case .zcashTransactionOutputInconsistentRecipient: return .zcashTransactionOutputInconsistentRecipient
case .transactionRepositoryEntityNotFound: return .transactionRepositoryEntityNotFound
case .transactionRepositoryTransactionMissingRequiredFields: return .transactionRepositoryTransactionMissingRequiredFields
case .transactionRepositoryCountAll: return .transactionRepositoryCountAll
@ -903,19 +759,6 @@ public enum ZcashError: Equatable, Error {
case .memoBytesInvalidUTF8: return .memoBytesInvalidUTF8
case .checkpointCantLoadFromDisk: return .checkpointCantLoadFromDisk
case .checkpointDecode: return .checkpointDecode
case .pendingTransactionDecodeInvalidData: return .pendingTransactionDecodeInvalidData
case .pendingTransactionCantDecode: return .pendingTransactionCantDecode
case .pendingTransactionCantEncode: return .pendingTransactionCantEncode
case .pendingTransactionDAOCreate: return .pendingTransactionDAOCreate
case .pendingTransactionDAOUpdateMissingID: return .pendingTransactionDAOUpdateMissingID
case .pendingTransactionDAOUpdate: return .pendingTransactionDAOUpdate
case .pendingTransactionDAODeleteMissingID: return .pendingTransactionDAODeleteMissingID
case .pendingTransactionDAODelete: return .pendingTransactionDAODelete
case .pendingTransactionDAOCancelMissingID: return .pendingTransactionDAOCancelMissingID
case .pendingTransactionDAOCancel: return .pendingTransactionDAOCancel
case .pendingTransactionDAOFind: return .pendingTransactionDAOFind
case .pendingTransactionDAOGetAll: return .pendingTransactionDAOGetAll
case .pendingTransactionDAOApplyMinedHeight: return .pendingTransactionDAOApplyMinedHeight
case .derivationToolSpendingKeyInvalidAccount: return .derivationToolSpendingKeyInvalidAccount
case .unspentTransactionOutputDAOCreateTable: return .unspentTransactionOutputDAOCreateTable
case .unspentTransactionOutputDAOStore: return .unspentTransactionOutputDAOStore
@ -932,15 +775,6 @@ public enum ZcashError: Equatable, Error {
case .recipientInvalidInput: return .recipientInvalidInput
case .walletTransEncoderCreateTransactionMissingSaplingParams: return .walletTransEncoderCreateTransactionMissingSaplingParams
case .walletTransEncoderShieldFundsMissingSaplingParams: return .walletTransEncoderShieldFundsMissingSaplingParams
case .persistentTransManagerCantCreateTransaction: return .persistentTransManagerCantCreateTransaction
case .persistentTransManagerEncodeUknownToAddress: return .persistentTransManagerEncodeUknownToAddress
case .persistentTransManagerSubmitTransactionIDMissing: return .persistentTransManagerSubmitTransactionIDMissing
case .persistentTransManagerSubmitTransactionNotFound: return .persistentTransManagerSubmitTransactionNotFound
case .persistentTransManagerSubmitTransactionCanceled: return .persistentTransManagerSubmitTransactionCanceled
case .persistentTransManagerSubmitTransactionRawDataMissing: return .persistentTransManagerSubmitTransactionRawDataMissing
case .persistentTransManagerSubmitFailed: return .persistentTransManagerSubmitFailed
case .persistentTransManagerApplyMinedHeightTransactionIDMissing: return .persistentTransManagerApplyMinedHeightTransactionIDMissing
case .persistentTransManagerApplyMinedHeightTransactionNotFound: return .persistentTransManagerApplyMinedHeightTransactionNotFound
case .zatoshiDecode: return .zatoshiDecode
case .zatoshiEncode: return .zatoshiEncode
case .unspentTransactionFetcherStream: return .unspentTransactionFetcherStream

View File

@ -31,14 +31,6 @@ public enum ZcashErrorCode: String {
case serviceFetchUTXOsFailed = "ZSRVC0008"
/// LightWalletService.blockStream failed.
case serviceBlockStreamFailed = "ZSRVC0000"
/// Migration of the pending DB failed because of unspecific reason.
case dbMigrationGenericFailure = "ZDBMG0001"
/// Migration of the pending DB failed because unknown version of the existing database.
case dbMigrationInvalidVersion = "ZDBMG00002"
/// Migration of the pending DB to version 1 failed.
case dbMigrationV1 = "ZDBMG00003"
/// Migration of the pending DB to version 2 failed.
case dbMigrationV2 = "ZDBMG00004"
/// SimpleConnectionProvider init of Connection failed.
case simpleConnectionProvider = "ZSCPC0001"
/// Downloaded file with sapling spending parameters isn't valid.
@ -49,18 +41,6 @@ public enum ZcashErrorCode: String {
case saplingParamsDownload = "ZSAPP0003"
/// Failed to move sapling parameters file to final destination after download.
case saplingParamsCantMoveDownloadedFile = "ZSAPP0004"
/// SQLite query failed when fetching received notes count from the database.
case notesDAOReceivedCount = "ZNDAO0001"
/// SQLite query failed when fetching received notes from the database.
case notesDAOReceivedNote = "ZNDAO0002"
/// Fetched note from the SQLite but can't decode that.
case notesDAOReceivedCantDecode = "ZNDAO0003"
/// SQLite query failed when fetching sent notes count from the database.
case notesDAOSentCount = "ZNDAO0004"
/// SQLite query failed when fetching sent notes from the database.
case notesDAOSentNote = "ZNDAO0005"
/// Fetched note from the SQLite but can't decode that.
case notesDAOSentCantDecode = "ZNDAO0006"
/// SQLite query failed when fetching block information from database.
case blockDAOBlock = "ZBDAO0001"
/// Fetched block information from DB but can't decode them.
@ -205,6 +185,10 @@ public enum ZcashErrorCode: String {
case zcashTransactionReceivedInit = "ZTEZT0002"
/// Initialization of `ZcashTransaction.Sent` failed.
case zcashTransactionSentInit = "ZTEZT0003"
/// Initialization of `ZcashTransaction.Output` failed.
case zcashTransactionOutputInit = "ZTEZT0004"
/// Initialization of `ZcashTransaction.Output` failed because there an inconsistency in the output recipient.
case zcashTransactionOutputInconsistentRecipient = "ZTEZT0005"
/// Entity not found in the database, result of `createEntity` execution.
case transactionRepositoryEntityNotFound = "ZTREE0001"
/// `Find` call is missing fields, required fields are transaction `index` and `blockTime`.
@ -233,32 +217,6 @@ public enum ZcashErrorCode: String {
case checkpointCantLoadFromDisk = "ZCHKP0001"
/// Failed to decode `Checkpoint` object.
case checkpointDecode = "ZCHKP0002"
/// Decoding of `PendingTransaction` failed because of specific invalid data.
case pendingTransactionDecodeInvalidData = "ZPETR0001"
/// Can't decode `PendingTransaction`.
case pendingTransactionCantDecode = "ZPETR0002"
/// Can't encode `PendingTransaction`.
case pendingTransactionCantEncode = "ZPETR0003"
/// SQLite query failed when creating pending transaction.
case pendingTransactionDAOCreate = "ZPETR0004"
/// Pending transaction which should be updated is missing ID.
case pendingTransactionDAOUpdateMissingID = "ZPETR0005"
/// SQLite query failed when updating pending transaction.
case pendingTransactionDAOUpdate = "ZPETR0006"
/// Pending transaction which should be deleted is missing ID.
case pendingTransactionDAODeleteMissingID = "ZPETR0007"
/// SQLite query failed when deleting pending transaction.
case pendingTransactionDAODelete = "ZPETR0008"
/// Pending transaction which should be canceled is missing ID.
case pendingTransactionDAOCancelMissingID = "ZPETR0009"
/// SQLite query failed when canceling pending transaction.
case pendingTransactionDAOCancel = "ZPETR0010"
/// SQLite query failed when seaching for pending transaction.
case pendingTransactionDAOFind = "ZPETR0011"
/// SQLite query failed when getting pending transactions.
case pendingTransactionDAOGetAll = "ZPETR0012"
/// SQLite query failed when applying mined height.
case pendingTransactionDAOApplyMinedHeight = "ZPETR0013"
/// Invalid account when trying to derive spending key
case derivationToolSpendingKeyInvalidAccount = "ZDRVT0001"
/// Creation of the table for unspent transaction output failed.
@ -291,24 +249,6 @@ public enum ZcashErrorCode: String {
case walletTransEncoderCreateTransactionMissingSaplingParams = "ZWLTE0001"
/// WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk.
case walletTransEncoderShieldFundsMissingSaplingParams = "ZWLTE0002"
/// PersistentTransactionsManager cant create transaction.
case persistentTransManagerCantCreateTransaction = "ZPTRM0001"
/// PersistentTransactionsManager cant get to address from pending transaction.
case persistentTransManagerEncodeUknownToAddress = "ZPTRM0002"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id.
case persistentTransManagerSubmitTransactionIDMissing = "ZPTRM0003"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored.
case persistentTransManagerSubmitTransactionNotFound = "ZPTRM0004"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is canceled.
case persistentTransManagerSubmitTransactionCanceled = "ZPTRM0005"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data.
case persistentTransManagerSubmitTransactionRawDataMissing = "ZPTRM0006"
/// PersistentTransactionsManager wants to submit pending transaction but submit API call failed.
case persistentTransManagerSubmitFailed = "ZPTRM0007"
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored.
case persistentTransManagerApplyMinedHeightTransactionIDMissing = "ZPTRM0008"
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored.
case persistentTransManagerApplyMinedHeightTransactionNotFound = "ZPTRM0009"
/// Initiatilzation fo `Zatoshi` from a decoder failed.
case zatoshiDecode = "ZTSHO0001"
/// Encode of `Zatoshi` failed.

View File

@ -63,21 +63,6 @@ enum ZcashErrorDefinition {
// sourcery: code="ZSRVC0000"
case serviceBlockStreamFailed(_ error: LightWalletServiceError)
// MARK: - DB Migration
/// Migration of the pending DB failed because of unspecific reason.
// sourcery: code="ZDBMG0001"
case dbMigrationGenericFailure(_ error: Error)
/// Migration of the pending DB failed because unknown version of the existing database.
// sourcery: code="ZDBMG00002"
case dbMigrationInvalidVersion
/// Migration of the pending DB to version 1 failed.
// sourcery: code="ZDBMG00003"
case dbMigrationV1(_ dbError: Error)
/// Migration of the pending DB to version 2 failed.
// sourcery: code="ZDBMG00004"
case dbMigrationV2(_ dbError: Error)
// MARK: SQLite connection
/// SimpleConnectionProvider init of Connection failed.
@ -104,33 +89,6 @@ enum ZcashErrorDefinition {
// sourcery: code="ZSAPP0004"
case saplingParamsCantMoveDownloadedFile(_ error: Error, _ downloadURL: URL, _ destination: URL)
// MARK: - NotesDAO
/// SQLite query failed when fetching received notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0001"
case notesDAOReceivedCount(_ sqliteError: Error)
/// SQLite query failed when fetching received notes from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0002"
case notesDAOReceivedNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
// sourcery: code="ZNDAO0003"
case notesDAOReceivedCantDecode(_ error: Error)
/// SQLite query failed when fetching sent notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0004"
case notesDAOSentCount(_ sqliteError: Error)
/// SQLite query failed when fetching sent notes from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0005"
case notesDAOSentNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
// sourcery: code="ZNDAO0006"
case notesDAOSentCantDecode(_ error: Error)
// MARK: - BlockDAO
/// SQLite query failed when fetching block information from database.
@ -412,6 +370,13 @@ enum ZcashErrorDefinition {
/// Initialization of `ZcashTransaction.Sent` failed.
// sourcery: code="ZTEZT0003"
case zcashTransactionSentInit(_ error: Error)
/// Initialization of `ZcashTransaction.Output` failed.
// sourcery: code="ZTEZT0004"
case zcashTransactionOutputInit(_ error: Error)
/// Initialization of `ZcashTransaction.Output` failed because there an inconsistency in the output recipient.
// sourcery: code="ZTEZT0005"
case zcashTransactionOutputInconsistentRecipient
// MARK: - Transaction Repository
@ -467,58 +432,6 @@ enum ZcashErrorDefinition {
// sourcery: code="ZCHKP0002"
case checkpointDecode(_ error: Error)
// MARK: - PendingTransactionDAO
/// Decoding of `PendingTransaction` failed because of specific invalid data.
/// - `field` is list of fields names that contain invalid data.
// sourcery: code="ZPETR0001"
case pendingTransactionDecodeInvalidData(_ fields: [String])
/// Can't decode `PendingTransaction`.
/// - `error` is error which described why decoding failed.
// sourcery: code="ZPETR0002"
case pendingTransactionCantDecode(_ error: Error)
/// Can't encode `PendingTransaction`.
/// - `error` is error which described why encoding failed.
// sourcery: code="ZPETR0003"
case pendingTransactionCantEncode(_ error: Error)
/// SQLite query failed when creating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0004"
case pendingTransactionDAOCreate(_ sqliteError: Error)
/// Pending transaction which should be updated is missing ID.
// sourcery: code="ZPETR0005"
case pendingTransactionDAOUpdateMissingID
/// SQLite query failed when updating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0006"
case pendingTransactionDAOUpdate(_ sqliteError: Error)
/// Pending transaction which should be deleted is missing ID.
// sourcery: code="ZPETR0007"
case pendingTransactionDAODeleteMissingID
/// SQLite query failed when deleting pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0008"
case pendingTransactionDAODelete(_ sqliteError: Error)
/// Pending transaction which should be canceled is missing ID.
// sourcery: code="ZPETR0009"
case pendingTransactionDAOCancelMissingID
/// SQLite query failed when canceling pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0010"
case pendingTransactionDAOCancel(_ sqliteError: Error)
/// SQLite query failed when seaching for pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0011"
case pendingTransactionDAOFind(_ sqliteError: Error)
/// SQLite query failed when getting pending transactions.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0012"
case pendingTransactionDAOGetAll(_ sqliteError: Error)
/// SQLite query failed when applying mined height.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0013"
case pendingTransactionDAOApplyMinedHeight(_ sqliteError: Error)
// MARK: - DerivationTool
/// Invalid account when trying to derive spending key
@ -584,36 +497,6 @@ enum ZcashErrorDefinition {
/// WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk.
// sourcery: code="ZWLTE0002"
case walletTransEncoderShieldFundsMissingSaplingParams
// MARK: - PersistentTransactionManager
/// PersistentTransactionsManager cant create transaction.
// sourcery: code="ZPTRM0001"
case persistentTransManagerCantCreateTransaction(_ recipient: PendingTransactionRecipient, _ account: Int, _ zatoshi: Zatoshi)
/// PersistentTransactionsManager cant get to address from pending transaction.
// sourcery: code="ZPTRM0002"
case persistentTransManagerEncodeUknownToAddress(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id.
// sourcery: code="ZPTRM0003"
case persistentTransManagerSubmitTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored.
// sourcery: code="ZPTRM0004"
case persistentTransManagerSubmitTransactionNotFound(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is canceled.
// sourcery: code="ZPTRM0005"
case persistentTransManagerSubmitTransactionCanceled(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data.
// sourcery: code="ZPTRM0006"
case persistentTransManagerSubmitTransactionRawDataMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but submit API call failed.
// sourcery: code="ZPTRM0007"
case persistentTransManagerSubmitFailed(_ entity: PendingTransactionEntity, _ serviceErrorCode: Int)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored.
// sourcery: code="ZPTRM0008"
case persistentTransManagerApplyMinedHeightTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored.
// sourcery: code="ZPTRM0009"
case persistentTransManagerApplyMinedHeightTransactionNotFound(_ entity: PendingTransactionEntity)
// MARK: - Zatoshi

View File

@ -89,7 +89,6 @@ public class Initializer {
struct URLs {
let fsBlockDbRoot: URL
let dataDbURL: URL
let pendingDbURL: URL
let spendParamsURL: URL
let outputParamsURL: URL
}
@ -112,7 +111,6 @@ public class Initializer {
let endpoint: LightWalletEndpoint
let fsBlockDbRoot: URL
let dataDbURL: URL
let pendingDbURL: URL
let spendParamsURL: URL
let outputParamsURL: URL
let saplingParamsSourceURL: SaplingParamsSourceURL
@ -143,7 +141,6 @@ public class Initializer {
/// just pass `nil` here.
/// - fsBlockDbRoot: location of the compact blocks cache
/// - dataDbURL: Location of the data db
/// - pendingDbURL: location of the pending transactions database
/// - endpoint: the endpoint representing the lightwalletd instance you want to point to
/// - spendParamsURL: location of the spend parameters
/// - outputParamsURL: location of the output parameters
@ -151,7 +148,6 @@ public class Initializer {
cacheDbURL: URL?,
fsBlockDbRoot: URL,
dataDbURL: URL,
pendingDbURL: URL,
endpoint: LightWalletEndpoint,
network: ZcashNetwork,
spendParamsURL: URL,
@ -163,7 +159,6 @@ public class Initializer {
let urls = URLs(
fsBlockDbRoot: fsBlockDbRoot,
dataDbURL: dataDbURL,
pendingDbURL: pendingDbURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL
)
@ -244,7 +239,6 @@ public class Initializer {
self.rustBackend = rustBackend
self.fsBlockDbRoot = urls.fsBlockDbRoot
self.dataDbURL = urls.dataDbURL
self.pendingDbURL = urls.pendingDbURL
self.endpoint = endpoint
self.spendParamsURL = urls.spendParamsURL
self.outputParamsURL = urls.outputParamsURL
@ -261,7 +255,7 @@ public class Initializer {
self.logger = logger
}
private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {
static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {
return LightWalletServiceFactory(endpoint: endpoint)
}
@ -273,7 +267,7 @@ public class Initializer {
/// - /some/path/to/directory -> /some/path/to/c_anotherInstance_directory
///
/// If any of the URLs can't be parsed then returned error isn't nil.
private static func tryToUpdateURLs(
static func tryToUpdateURLs(
with alias: ZcashSynchronizerAlias,
urls: URLs
) -> (URLs, ZcashError?) {
@ -307,10 +301,6 @@ public class Initializer {
return .failure(.initializerCantUpdateURLWithAlias(urls.dataDbURL))
}
guard let updatedPendingDbURL = urls.pendingDbURL.updateLastPathComponent(with: alias) else {
return .failure(.initializerCantUpdateURLWithAlias(urls.pendingDbURL))
}
guard let updatedSpendParamsURL = urls.spendParamsURL.updateLastPathComponent(with: alias) else {
return .failure(.initializerCantUpdateURLWithAlias(urls.spendParamsURL))
}
@ -323,7 +313,6 @@ public class Initializer {
URLs(
fsBlockDbRoot: updatedFsBlockDbRoot,
dataDbURL: updatedDataDbURL,
pendingDbURL: updatedPendingDbURL,
spendParamsURL: updatedSpendParamsURL,
outputParamsURL: updateOutputParamsURL
)
@ -374,14 +363,6 @@ public class Initializer {
throw error
}
let migrationManager = MigrationManager(
pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path),
networkType: self.network.networkType,
logger: logger
)
try migrationManager.performMigration()
return .success
}

View File

@ -165,7 +165,8 @@ public extension MemoBytes {
func intoMemo() throws -> Memo {
switch self.bytes[0] {
case 0x00 ... 0xF4:
guard let validatedUTF8String = String(validatingUTF8: self.unpaddedRawBytes()) else {
let unpadded = self.unpaddedRawBytes()
guard let validatedUTF8String = String(validatingUTF8: unpadded) else {
throw ZcashError.memoBytesInvalidUTF8
}

View File

@ -213,6 +213,11 @@ public struct UnifiedAddress: Equatable, StringEncoded {
}
}
public enum TransactionRecipient: Equatable {
case address(Recipient)
case internalAccount(UInt32)
}
/// Represents a valid recipient of Zcash
public enum Recipient: Equatable, StringEncoded {
case transparent(TransparentAddress)

View File

@ -14,9 +14,6 @@ public enum ResourceProviderError: Error {
public protocol ResourceProvider {
var dataDbURL: URL { get }
var fsCacheURL: URL { get }
@available(*, deprecated, message: "you should use this to delete the existing cache.db sqlite.")
var cacheDbURL: URL { get }
}
/**
Convenience provider for a data db and cache db resources.
@ -43,16 +40,6 @@ public struct DefaultResourceProvider: ResourceProvider {
return URL(fileURLWithPath: "file://\(constants.defaultFsBlockDbRootName)")
}
}
public var cacheDbURL: URL {
let constants = network.constants
do {
let path = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
return path.appendingPathComponent(constants.defaultCacheDbName)
} catch {
return URL(fileURLWithPath: "file://\(constants.defaultCacheDbName)")
}
}
public var spendParamsURL: URL {
do {

View File

@ -1,19 +0,0 @@
//
// PendingTransactionRepository.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/19/19.
//
import Foundation
protocol PendingTransactionRepository {
func closeDBConnection()
func create(_ transaction: PendingTransactionEntity) throws -> Int
func update(_ transaction: PendingTransactionEntity) throws
func delete(_ transaction: PendingTransactionEntity) throws
func cancel(_ transaction: PendingTransactionEntity) throws
func find(by id: Int) throws -> PendingTransactionEntity?
func getAll() throws -> [PendingTransactionEntity]
func applyMinedHeight(_ height: BlockHeight, id: Int) throws
}

View File

@ -20,10 +20,10 @@ protocol TransactionRepository {
func find(offset: Int, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview]
func find(in range: CompactBlockRange, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview]
func find(from: ZcashTransaction.Overview, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview]
func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Received]
func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Sent]
func findPendingTransactions(latestHeight: BlockHeight, offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview]
func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview]
func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview]
func findMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo]
func findMemos(for receivedTransaction: ZcashTransaction.Received) async throws -> [Memo]
func findMemos(for sentTransaction: ZcashTransaction.Sent) async throws -> [Memo]
func getRecipients(for id: Int) async -> [TransactionRecipient]
func getRecipients(for id: Int) async throws -> [TransactionRecipient]
func getTransactionOutputs(for id: Int) async throws -> [ZcashTransaction.Output]
}

View File

@ -64,7 +64,7 @@ public struct SynchronizerState: Equatable {
public enum SynchronizerEvent {
// Sent when the synchronizer finds a pendingTransaction that hast been newly mined.
case minedTransaction(PendingTransactionEntity)
case minedTransaction(ZcashTransaction.Overview)
// Sent when the synchronizer finds a mined transaction
case foundTransactions(_ transactions: [ZcashTransaction.Overview], _ inRange: CompactBlockRange)
// Sent when the synchronizer fetched utxos from lightwalletd attempted to store them.
@ -163,7 +163,7 @@ public protocol Synchronizer: AnyObject {
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?
) async throws -> PendingTransactionEntity
) async throws -> ZcashTransaction.Overview
/// Shields transparent funds from the given private key into the best shielded pool of the account associated to the given `UnifiedSpendingKey`.
/// - Parameter spendingKey: the `UnifiedSpendingKey` that allows to spend transparent funds
@ -175,25 +175,19 @@ public protocol Synchronizer: AnyObject {
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi
) async throws -> PendingTransactionEntity
/// Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only
/// an option if the transaction has not yet been submitted to the server.
/// - Parameter transaction: the transaction to cancel.
/// - Returns: true when the cancellation request was successful. False when it is too late.
func cancelSpend(transaction: PendingTransactionEntity) async -> Bool
) async throws -> ZcashTransaction.Overview
/// all outbound pending transactions that have been sent but are awaiting confirmations
var pendingTransactions: [PendingTransactionEntity] { get async }
var pendingTransactions: [ZcashTransaction.Overview] { get async }
/// all the transactions that are on the blockchain
var clearedTransactions: [ZcashTransaction.Overview] { get async }
var transactions: [ZcashTransaction.Overview] { get async }
/// All transactions that are related to sending funds
var sentTransactions: [ZcashTransaction.Sent] { get async }
var sentTransactions: [ZcashTransaction.Overview] { get async }
/// all transactions related to receiving funds
var receivedTransactions: [ZcashTransaction.Received] { get async }
var receivedTransactions: [ZcashTransaction.Overview] { get async }
/// A repository serving transactions in a paginated manner
/// - Parameter kind: Transaction Kind expected from this PaginatedTransactionRepository
@ -204,16 +198,6 @@ public protocol Synchronizer: AnyObject {
// sourcery: mockedName="getMemosForClearedTransaction"
func getMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo]
/// Get all memos for `receivedTransaction`.
///
// sourcery: mockedName="getMemosForReceivedTransaction"
func getMemos(for receivedTransaction: ZcashTransaction.Received) async throws -> [Memo]
/// Get all memos for `sentTransaction`.
///
// sourcery: mockedName="getMemosForSentTransaction"
func getMemos(for sentTransaction: ZcashTransaction.Sent) async throws -> [Memo]
/// Attempt to get recipients from a Transaction Overview.
/// - parameter transaction: A transaction overview
/// - returns the recipients or an empty array if no recipients are found on this transaction because it's not an outgoing
@ -221,21 +205,24 @@ public protocol Synchronizer: AnyObject {
///
// sourcery: mockedName="getRecipientsForClearedTransaction"
func getRecipients(for transaction: ZcashTransaction.Overview) async -> [TransactionRecipient]
/// Get the recipients for the given a sent transaction
/// Attempt to get outputs involved in a given Transaction.
/// - parameter transaction: A transaction overview
/// - returns the recipients or an empty array if no recipients are found on this transaction because it's not an outgoing
/// transaction
/// - returns the array of outputs involved in this transaction. Transparent outputs might not be tracked
///
// sourcery: mockedName="getRecipientsForSentTransaction"
func getRecipients(for transaction: ZcashTransaction.Sent) async -> [TransactionRecipient]
// sourcery: mockedName="getTransactionOutputsForTransaction"
func getTransactionOutputs(for transaction: ZcashTransaction.Overview) async -> [ZcashTransaction.Output]
/// 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: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview]
/// - Returns: an array with the given Transactions or an empty array
func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview]
/// Fetch all pending transactions
/// - Returns: an array of transactions which are considered pending confirmation. can be empty
func allPendingTransactions() async throws -> [ZcashTransaction.Overview]
/// Returns the latest block height from the provided Lightwallet endpoint
func latestHeight() async throws -> BlockHeight

View File

@ -75,7 +75,7 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?,
completion: @escaping (Result<PendingTransactionEntity, Error>) -> Void
completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
) {
AsyncToClosureGateway.executeThrowingAction(completion) {
try await self.synchronizer.sendToAddress(spendingKey: spendingKey, zatoshi: zatoshi, toAddress: toAddress, memo: memo)
@ -86,20 +86,14 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi,
completion: @escaping (Result<PendingTransactionEntity, Error>) -> Void
completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
) {
AsyncToClosureGateway.executeThrowingAction(completion) {
try await self.synchronizer.shieldFunds(spendingKey: spendingKey, memo: memo, shieldingThreshold: shieldingThreshold)
}
}
public func cancelSpend(transaction: PendingTransactionEntity, completion: @escaping (Bool) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.cancelSpend(transaction: transaction)
}
}
public func pendingTransactions(completion: @escaping ([PendingTransactionEntity]) -> Void) {
public func pendingTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.pendingTransactions
}
@ -107,17 +101,17 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
public func clearedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.clearedTransactions
await self.synchronizer.transactions
}
}
public func sentTranscations(completion: @escaping ([ZcashTransaction.Sent]) -> Void) {
public func sentTranscations(completion: @escaping ([ZcashTransaction.Overview]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.sentTransactions
}
}
public func receivedTransactions(completion: @escaping ([ZcashTransaction.Received]) -> Void) {
public func receivedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.receivedTransactions
}
@ -131,37 +125,19 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
}
}
public func getMemos(for receivedTransaction: ZcashTransaction.Received, completion: @escaping (Result<[Memo], Error>) -> Void) {
AsyncToClosureGateway.executeThrowingAction(completion) {
try await self.synchronizer.getMemos(for: receivedTransaction)
}
}
public func getMemos(for sentTransaction: ZcashTransaction.Sent, completion: @escaping (Result<[Memo], Error>) -> Void) {
AsyncToClosureGateway.executeThrowingAction(completion) {
try await self.synchronizer.getMemos(for: sentTransaction)
}
}
public func getRecipients(for transaction: ZcashTransaction.Overview, completion: @escaping ([TransactionRecipient]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.getRecipients(for: transaction)
}
}
public func getRecipients(for transaction: ZcashTransaction.Sent, completion: @escaping ([TransactionRecipient]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.getRecipients(for: transaction)
}
}
public func allConfirmedTransactions(
from transaction: ZcashTransaction.Overview,
limit: Int,
completion: @escaping (Result<[ZcashTransaction.Overview], Error>) -> Void
) {
AsyncToClosureGateway.executeThrowingAction(completion) {
try await self.synchronizer.allConfirmedTransactions(from: transaction, limit: limit)
try await self.synchronizer.allTransactions(from: transaction, limit: limit)
}
}

View File

@ -74,7 +74,7 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?
) -> SinglePublisher<PendingTransactionEntity, Error> {
) -> SinglePublisher<ZcashTransaction.Overview, Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.sendToAddress(spendingKey: spendingKey, zatoshi: zatoshi, toAddress: toAddress, memo: memo)
}
@ -84,37 +84,31 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi
) -> SinglePublisher<PendingTransactionEntity, Error> {
) -> SinglePublisher<ZcashTransaction.Overview, Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.shieldFunds(spendingKey: spendingKey, memo: memo, shieldingThreshold: shieldingThreshold)
}
}
public func cancelSpend(transaction: PendingTransactionEntity) -> SinglePublisher<Bool, Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.cancelSpend(transaction: transaction)
}
}
public var pendingTransactions: SinglePublisher<[PendingTransactionEntity], Never> {
public var pendingTransactions: AnyPublisher<[ZcashTransaction.Overview], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.pendingTransactions
}
}
public var clearedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> {
public var allTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.clearedTransactions
await self.synchronizer.transactions
}
}
public var sentTransactions: SinglePublisher<[ZcashTransaction.Sent], Never> {
public var sentTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.sentTransactions
}
}
public var receivedTransactions: SinglePublisher<[ZcashTransaction.Received], Never> {
public var receivedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.receivedTransactions
}
@ -128,33 +122,21 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
}
}
public func getMemos(for receivedTransaction: ZcashTransaction.Received) -> SinglePublisher<[Memo], Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.getMemos(for: receivedTransaction)
}
}
public func getMemos(for sentTransaction: ZcashTransaction.Sent) -> SinglePublisher<[Memo], Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.getMemos(for: sentTransaction)
}
}
public func getRecipients(for transaction: ZcashTransaction.Overview) -> SinglePublisher<[TransactionRecipient], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.getRecipients(for: transaction)
}
}
public func getRecipients(for transaction: ZcashTransaction.Sent) -> SinglePublisher<[TransactionRecipient], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.getRecipients(for: transaction)
public func allPendingTransactions() -> AnyPublisher<[ZcashTransaction.Overview], Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.allPendingTransactions()
}
}
public func allConfirmedTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error> {
public func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.allConfirmedTransactions(from: transaction, limit: limit)
try await self.synchronizer.allTransactions(from: transaction, limit: limit)
}
}

View File

@ -37,7 +37,7 @@ public class SDKSynchronizer: Synchronizer {
public let initializer: Initializer
public var connectionState: ConnectionState
public let network: ZcashNetwork
private let transactionManager: OutboundTransactionManager
private let transactionEncoder: TransactionEncoder
private let transactionRepository: TransactionRepository
private let utxoRepository: UnspentTransactionOutputRepository
@ -58,7 +58,7 @@ public class SDKSynchronizer: Synchronizer {
self.init(
status: .unprepared,
initializer: initializer,
transactionManager: OutboundTransactionManagerBuilder.build(initializer: initializer),
transactionEncoder: WalletTransactionEncoder(initializer: initializer),
transactionRepository: initializer.transactionRepository,
utxoRepository: UTXORepositoryBuilder.build(initializer: initializer),
blockProcessor: CompactBlockProcessor(
@ -78,7 +78,7 @@ public class SDKSynchronizer: Synchronizer {
init(
status: SyncStatus,
initializer: Initializer,
transactionManager: OutboundTransactionManager,
transactionEncoder: TransactionEncoder,
transactionRepository: TransactionRepository,
utxoRepository: UnspentTransactionOutputRepository,
blockProcessor: CompactBlockProcessor,
@ -90,7 +90,7 @@ public class SDKSynchronizer: Synchronizer {
self.connectionState = .idle
self.underlyingStatus = GenericActor(status)
self.initializer = initializer
self.transactionManager = transactionManager
self.transactionEncoder = transactionEncoder
self.transactionRepository = transactionRepository
self.utxoRepository = utxoRepository
self.blockProcessor = blockProcessor
@ -224,7 +224,8 @@ public class SDKSynchronizer: Synchronizer {
self?.foundTransactions(transactions: transactions, in: range)
case let .handledReorg(reorgHeight, rewindHeight):
await self?.handledReorg(reorgHeight: reorgHeight, rewindHeight: rewindHeight)
// log reorg information
self?.logger.info("handling reorg at: \(reorgHeight) with rewind height: \(rewindHeight)")
case let .progressUpdated(progress):
await self?.progressUpdated(progress: progress)
@ -255,8 +256,7 @@ public class SDKSynchronizer: Synchronizer {
private func finished(lastScannedHeight: BlockHeight, foundBlocks: Bool) async {
await latestBlocksDataProvider.updateScannedData()
// FIX: Pending transaction updates fail if done from another thread. Improvement needed: explicitly define queues for sql repositories see: https://github.com/zcash/ZcashLightClientKit/issues/450
await refreshPendingTransactions()
await updateStatus(.synced)
if let syncStartDate {
@ -273,16 +273,6 @@ public class SDKSynchronizer: Synchronizer {
}
}
private func handledReorg(reorgHeight: BlockHeight, rewindHeight: BlockHeight) async {
logger.debug("handling reorg at: \(reorgHeight) with rewind height: \(rewindHeight)")
do {
try await transactionManager.handleReorg(at: rewindHeight)
} catch {
logger.debug("error handling reorg: \(error)")
}
}
private func progressUpdated(progress: CompactBlockProgress) async {
let newStatus = SyncStatus(progress)
await updateStatus(newStatus)
@ -301,9 +291,13 @@ public class SDKSynchronizer: Synchronizer {
zatoshi: Zatoshi,
toAddress: Recipient,
memo: Memo?
) async throws -> PendingTransactionEntity {
) async throws -> ZcashTransaction.Overview {
try throwIfUnprepared()
if case Recipient.transparent = toAddress, memo != nil {
throw ZcashError.synchronizerSendMemoToTransparentAddress
}
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
spendURL: initializer.spendParamsURL,
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
@ -312,10 +306,6 @@ public class SDKSynchronizer: Synchronizer {
logger: logger
)
if case Recipient.transparent = toAddress, memo != nil {
throw ZcashError.synchronizerSendMemoToTransparentAddress
}
return try await createToAddress(
spendingKey: spendingKey,
zatoshi: zatoshi,
@ -328,33 +318,30 @@ public class SDKSynchronizer: Synchronizer {
spendingKey: UnifiedSpendingKey,
memo: Memo,
shieldingThreshold: Zatoshi
) async throws -> PendingTransactionEntity {
) async throws -> ZcashTransaction.Overview {
try throwIfUnprepared()
// let's see if there are funds to shield
let accountIndex = Int(spendingKey.account)
let tBalance = try await self.getTransparentBalance(accountIndex: accountIndex)
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
guard tBalance.verified >= self.network.constants.defaultFee(for: await self.latestBlocksDataProvider.latestScannedHeight) else {
throw ZcashError.synchronizerShieldFundsInsuficientTransparentFunds
}
let shieldingSpend = try await transactionManager.initSpend(
zatoshi: tBalance.verified,
recipient: .internalAccount(spendingKey.account),
memo: try memo.asMemoBytes(),
from: accountIndex
)
// TODO: [#487] Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
let transaction = try await transactionManager.encodeShieldingTransaction(
let transaction = try await transactionEncoder.createShieldingTransaction(
spendingKey: spendingKey,
shieldingThreshold: shieldingThreshold,
pendingTransaction: shieldingSpend
memoBytes: memo.asMemoBytes(),
from: Int(spendingKey.account)
)
return try await transactionManager.submit(pendingTransaction: transaction)
let encodedTx = try transaction.encodedTransaction()
try await transactionEncoder.submit(transaction: encodedTx)
return transaction
}
func createToAddress(
@ -362,43 +349,51 @@ public class SDKSynchronizer: Synchronizer {
zatoshi: Zatoshi,
recipient: Recipient,
memo: Memo?
) async throws -> PendingTransactionEntity {
let spend = try await transactionManager.initSpend(
zatoshi: zatoshi,
recipient: .address(recipient),
memo: memo?.asMemoBytes(),
from: Int(spendingKey.account)
)
let transaction = try await transactionManager.encode(
spendingKey: spendingKey,
pendingTransaction: spend
)
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
return submittedTx
) async throws -> ZcashTransaction.Overview {
do {
if
case .transparent = recipient,
memo != nil {
throw ZcashError.synchronizerSendMemoToTransparentAddress
}
let transaction = try await transactionEncoder.createTransaction(
spendingKey: spendingKey,
zatoshi: zatoshi,
to: recipient.stringEncoded,
memoBytes: memo?.asMemoBytes(),
from: Int(spendingKey.account)
)
let encodedTransaction = try transaction.encodedTransaction()
try await transactionEncoder.submit(transaction: encodedTransaction)
return transaction
} catch {
throw error
}
}
public func cancelSpend(transaction: PendingTransactionEntity) async -> Bool {
await transactionManager.cancel(pendingTransaction: transaction)
}
public func allReceivedTransactions() async throws -> [ZcashTransaction.Received] {
public func allReceivedTransactions() async throws -> [ZcashTransaction.Overview] {
try await transactionRepository.findReceived(offset: 0, limit: Int.max)
}
public func allPendingTransactions() async throws -> [PendingTransactionEntity] {
try await transactionManager.allPendingTransactions()
public func allPendingTransactions() async throws -> [ZcashTransaction.Overview] {
let latestScannedHeight = self.latestState.latestScannedHeight
return try await transactionRepository.findPendingTransactions(latestHeight: latestScannedHeight, offset: 0, limit: .max)
}
public func allClearedTransactions() async throws -> [ZcashTransaction.Overview] {
public func allTransactions() async throws -> [ZcashTransaction.Overview] {
return try await transactionRepository.find(offset: 0, limit: Int.max, kind: .all)
}
public func allSentTransactions() async throws -> [ZcashTransaction.Sent] {
public func allSentTransactions() async throws -> [ZcashTransaction.Overview] {
return try await transactionRepository.findSent(offset: 0, limit: Int.max)
}
public func allConfirmedTransactions(from transaction: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview] {
public func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview] {
return try await transactionRepository.find(from: transaction, limit: limit, kind: .all)
}
@ -410,20 +405,12 @@ public class SDKSynchronizer: Synchronizer {
return try await transactionRepository.findMemos(for: transaction)
}
public func getMemos(for receivedTransaction: ZcashTransaction.Received) async throws -> [Memo] {
return try await transactionRepository.findMemos(for: receivedTransaction)
}
public func getMemos(for sentTransaction: ZcashTransaction.Sent) async throws -> [Memo] {
return try await transactionRepository.findMemos(for: sentTransaction)
}
public func getRecipients(for transaction: ZcashTransaction.Overview) async -> [TransactionRecipient] {
return await transactionRepository.getRecipients(for: transaction.id)
return (try? await transactionRepository.getRecipients(for: transaction.id)) ?? []
}
public func getRecipients(for transaction: ZcashTransaction.Sent) async -> [TransactionRecipient] {
return await transactionRepository.getRecipients(for: transaction.id)
public func getTransactionOutputs(for transaction: ZcashTransaction.Overview) async -> [ZcashTransaction.Output] {
return (try? await transactionRepository.getTransactionOutputs(for: transaction.id)) ?? []
}
public func latestHeight() async throws -> BlockHeight {
@ -515,15 +502,10 @@ public class SDKSynchronizer: Synchronizer {
let context = AfterSyncHooksManager.RewindContext(
height: height,
completion: { [weak self] result in
completion: { result in
switch result {
case let .success(rewindHeight):
do {
try await self?.transactionManager.handleReorg(at: rewindHeight)
subject.send(completion: .finished)
} catch {
subject.send(completion: .failure(error))
}
case .success:
subject.send(completion: .finished)
case let .failure(error):
subject.send(completion: .failure(error))
@ -547,9 +529,8 @@ public class SDKSynchronizer: Synchronizer {
}
let context = AfterSyncHooksManager.WipeContext(
pendingDbURL: initializer.pendingDbURL,
prewipe: { [weak self] in
self?.transactionManager.closeDBConnection()
self?.transactionEncoder.closeDBConnection()
self?.transactionRepository.closeDBConnection()
},
completion: { [weak self] possibleError in
@ -617,44 +598,7 @@ public class SDKSynchronizer: Synchronizer {
}
}
// MARK: book keeping
private func updateMinedTransactions() async throws {
let transactions = try await transactionManager.allPendingTransactions()
.filter { $0.isSubmitSuccess && !$0.isMined }
for pendingTx in transactions {
guard let rawID = pendingTx.rawTransactionId else { return }
let transaction = try await transactionRepository.find(rawID: rawID)
guard let minedHeight = transaction.minedHeight else { return }
let minedTx = try await transactionManager.applyMinedHeight(pendingTransaction: pendingTx, minedHeight: minedHeight)
notifyMinedTransaction(minedTx)
}
}
private func removeConfirmedTransactions() async throws {
let latestHeight = try await transactionRepository.lastScannedHeight()
let transactions = try await transactionManager.allPendingTransactions()
.filter { $0.minedHeight > 0 && abs($0.minedHeight - latestHeight) >= ZcashSDK.defaultStaleTolerance }
for transaction in transactions {
try await transactionManager.delete(pendingTransaction: transaction)
}
}
private func refreshPendingTransactions() async {
do {
try await updateMinedTransactions()
try await removeConfirmedTransactions()
} catch {
logger.debug("error refreshing pending transactions: \(error)")
}
}
private func notifyMinedTransaction(_ transaction: PendingTransactionEntity) {
private func notifyMinedTransaction(_ transaction: ZcashTransaction.Overview) {
streamsUpdateQueue.async { [weak self] in
self?.eventSubject.send(.minedTransaction(transaction))
}
@ -662,29 +606,29 @@ public class SDKSynchronizer: Synchronizer {
}
extension SDKSynchronizer {
public var pendingTransactions: [PendingTransactionEntity] {
public var transactions: [ZcashTransaction.Overview] {
get async {
(try? await self.allPendingTransactions()) ?? [PendingTransactionEntity]()
(try? await self.allTransactions()) ?? []
}
}
public var clearedTransactions: [ZcashTransaction.Overview] {
get async {
(try? await allClearedTransactions()) ?? []
}
}
public var sentTransactions: [ZcashTransaction.Sent] {
public var sentTransactions: [ZcashTransaction.Overview] {
get async {
(try? await allSentTransactions()) ?? []
}
}
public var receivedTransactions: [ZcashTransaction.Received] {
public var receivedTransactions: [ZcashTransaction.Overview] {
get async {
(try? await allReceivedTransactions()) ?? []
}
}
public var pendingTransactions: [ZcashTransaction.Overview] {
get async {
(try? await allPendingTransactions()) ?? []
}
}
}
extension SyncStatus {

View File

@ -1,255 +0,0 @@
//
// PendingTransactionsManager.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/26/19.
//
import Foundation
class PersistentTransactionManager: OutboundTransactionManager {
let repository: PendingTransactionRepository
let encoder: TransactionEncoder
let service: LightWalletService
let queue: DispatchQueue
let network: NetworkType
let logger: Logger
init(
encoder: TransactionEncoder,
service: LightWalletService,
repository: PendingTransactionRepository,
networkType: NetworkType,
logger: Logger
) {
self.repository = repository
self.encoder = encoder
self.service = service
self.network = networkType
self.queue = DispatchQueue.init(label: "PersistentTransactionManager.serial.queue", qos: .userInitiated)
self.logger = logger
}
func initSpend(
zatoshi: Zatoshi,
recipient: PendingTransactionRecipient,
memo: MemoBytes?,
from accountIndex: Int
) throws -> PendingTransactionEntity {
guard let insertedTx = try repository.find(
by: try repository.create(
PendingTransaction(
value: zatoshi,
recipient: recipient,
memo: memo,
account: accountIndex
)
)
) else {
throw ZcashError.persistentTransManagerCantCreateTransaction(recipient, accountIndex, zatoshi)
}
logger.debug("pending transaction \(String(describing: insertedTx.id)) created")
return insertedTx
}
func encodeShieldingTransaction(
spendingKey: UnifiedSpendingKey,
shieldingThreshold: Zatoshi,
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity {
let transaction = try await self.encoder.createShieldingTransaction(
spendingKey: spendingKey,
shieldingThreshold: shieldingThreshold,
memoBytes: try pendingTransaction.memo?.intoMemoBytes(),
from: pendingTransaction.accountIndex
)
var pending = pendingTransaction
pending.encodeAttempts += 1
pending.raw = transaction.raw
pending.rawTransactionId = transaction.rawID
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
pending.minedHeight = transaction.minedHeight ?? BlockHeight.empty()
try self.repository.update(pending)
return pending
}
func encode(
spendingKey: UnifiedSpendingKey,
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity {
do {
var toAddress: String?
switch pendingTransaction.recipient {
case .address(let addr):
toAddress = addr.stringEncoded
case .internalAccount:
break
}
guard let toAddress else {
throw ZcashError.persistentTransManagerEncodeUknownToAddress(pendingTransaction)
}
let transaction = try await self.encoder.createTransaction(
spendingKey: spendingKey,
zatoshi: pendingTransaction.value,
to: toAddress,
memoBytes: try pendingTransaction.memo?.intoMemoBytes(),
from: pendingTransaction.accountIndex
)
var pending = pendingTransaction
pending.encodeAttempts += 1
pending.raw = transaction.raw
pending.rawTransactionId = transaction.rawID
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
pending.minedHeight = transaction.minedHeight ?? BlockHeight.empty()
try self.repository.update(pending)
return pending
} catch {
try await self.updateOnFailure(transaction: pendingTransaction, error: error)
throw error
}
}
func submit(
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity {
guard let txId = pendingTransaction.id else {
// this transaction is not stored
throw ZcashError.persistentTransManagerSubmitTransactionIDMissing(pendingTransaction)
}
do {
guard let storedTx = try self.repository.find(by: txId) else {
throw ZcashError.persistentTransManagerSubmitTransactionNotFound(pendingTransaction)
}
guard !storedTx.isCancelled else {
logger.debug("ignoring cancelled transaction \(storedTx)")
throw ZcashError.persistentTransManagerSubmitTransactionCanceled(storedTx)
}
guard let raw = storedTx.raw else {
logger.debug("INCONSISTENCY: attempt to send pending transaction \(txId) that has not raw data")
throw ZcashError.persistentTransManagerSubmitTransactionRawDataMissing(storedTx)
}
let response = try await self.service.submit(spendTransaction: raw)
let transaction = try await self.update(transaction: storedTx, on: response)
guard response.errorCode >= 0 else {
throw ZcashError.persistentTransManagerSubmitFailed(transaction, Int(response.errorCode))
}
return transaction
} catch {
try await self.updateOnFailure(transaction: pendingTransaction, error: error)
throw error
}
}
func applyMinedHeight(pendingTransaction: PendingTransactionEntity, minedHeight: BlockHeight) async throws -> PendingTransactionEntity {
guard let id = pendingTransaction.id else {
throw ZcashError.persistentTransManagerApplyMinedHeightTransactionIDMissing(pendingTransaction)
}
guard var transaction = try repository.find(by: id) else {
throw ZcashError.persistentTransManagerApplyMinedHeightTransactionNotFound(pendingTransaction)
}
transaction.minedHeight = minedHeight
guard let pendingTxId = pendingTransaction.id else {
throw ZcashError.persistentTransManagerApplyMinedHeightTransactionIDMissing(pendingTransaction)
}
try repository.applyMinedHeight(minedHeight, id: pendingTxId)
return transaction
}
func handleReorg(at height: BlockHeight) async throws {
let affectedTxs = try await allPendingTransactions()
.filter({ $0.minedHeight >= height })
try affectedTxs
.map { transaction -> PendingTransactionEntity in
var updatedTx = transaction
updatedTx.minedHeight = -1
return updatedTx
}
.forEach { try self.repository.update($0) }
}
func cancel(pendingTransaction: PendingTransactionEntity) async -> Bool {
guard let id = pendingTransaction.id else { return false }
guard let transaction = try? repository.find(by: id) else { return false }
guard !transaction.isSubmitted else { return false }
guard (try? repository.cancel(transaction)) != nil else { return false }
return true
}
func allPendingTransactions() async throws -> [PendingTransactionEntity] {
try repository.getAll()
}
// MARK: other functions
private func updateOnFailure(transaction: PendingTransactionEntity, error: Error) async throws {
var pending = transaction
pending.errorMessage = "\(error)"
pending.encodeAttempts = transaction.encodeAttempts + 1
try self.repository.update(pending)
}
private func update(transaction: PendingTransactionEntity, on sendResponse: LightWalletServiceResponse) async throws -> PendingTransactionEntity {
var pendingTx = transaction
pendingTx.submitAttempts += 1
let error = sendResponse.errorCode < 0
pendingTx.errorCode = error ? Int(sendResponse.errorCode) : nil
pendingTx.errorMessage = error ? sendResponse.errorMessage : nil
try repository.update(pendingTx)
return pendingTx
}
func delete(pendingTransaction: PendingTransactionEntity) async throws {
try repository.delete(pendingTransaction)
}
func closeDBConnection() {
repository.closeDBConnection()
}
}
enum OutboundTransactionManagerBuilder {
static func build(initializer: Initializer) -> OutboundTransactionManager {
PersistentTransactionManager(
encoder: TransactionEncoderbuilder.build(initializer: initializer),
service: initializer.lightWalletService,
repository: PendingTransactionRepositoryBuilder.build(initializer: initializer),
networkType: initializer.network.networkType,
logger: initializer.logger
)
}
}
enum PendingTransactionRepositoryBuilder {
static func build(initializer: Initializer) -> PendingTransactionRepository {
PendingTransactionSQLDAO(
dbProvider: SimpleConnectionProvider(path: initializer.pendingDbURL.path, readonly: false),
logger: initializer.logger
)
}
}
enum TransactionEncoderbuilder {
static func build(initializer: Initializer) -> TransactionEncoder {
WalletTransactionEncoder(initializer: initializer)
}
}

View File

@ -9,6 +9,15 @@ import Foundation
typealias TransactionEncoderResultBlock = (_ result: Result<EncodedTransaction, Error>) -> Void
public enum TransactionEncoderError: Error {
case notFound(transactionId: Int)
case notEncoded(transactionId: Int)
case missingParams
case spendingKeyWrongNetwork
case couldNotExpand(txId: Data)
case submitError(code: Int, message: String)
}
protocol TransactionEncoder {
/// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
/// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
@ -48,4 +57,10 @@ protocol TransactionEncoder {
memoBytes: MemoBytes?,
from accountIndex: Int
) async throws -> ZcashTransaction.Overview
/// submits a transaction to the Zcash peer-to-peer network.
/// - Parameter transaction: a transaction overview
func submit(transaction: EncodedTransaction) async throws
func closeDBConnection()
}

View File

@ -1,59 +0,0 @@
//
// TransactionManager.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/26/19.
//
import Foundation
/**
Manage outbound transactions with the main purpose of reporting which ones are still pending,
particularly after failed attempts or dropped connectivity. The intent is to help see outbound
transactions through to completion.
*/
protocol OutboundTransactionManager {
func initSpend(
zatoshi: Zatoshi,
recipient: PendingTransactionRecipient,
memo: MemoBytes?,
from accountIndex: Int
) async throws -> PendingTransactionEntity
func encodeShieldingTransaction(
spendingKey: UnifiedSpendingKey,
shieldingThreshold: Zatoshi,
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity
func encode(
spendingKey: UnifiedSpendingKey,
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity
func submit(
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity
func applyMinedHeight(
pendingTransaction: PendingTransactionEntity,
minedHeight: BlockHeight
) async throws -> PendingTransactionEntity
/**
Attempts to Cancel a transaction. Returns true if successful
*/
func cancel(pendingTransaction: PendingTransactionEntity) async -> Bool
func allPendingTransactions() async throws -> [PendingTransactionEntity]
func handleReorg(at blockHeight: BlockHeight) async throws
/**
Deletes a pending transaction from the database
*/
func delete(pendingTransaction: PendingTransactionEntity) async throws
func closeDBConnection()
}

View File

@ -8,6 +8,7 @@
import Foundation
class WalletTransactionEncoder: TransactionEncoder {
let lightWalletService: LightWalletService
let rustBackend: ZcashRustBackendWelding
let repository: TransactionRepository
let logger: Logger
@ -22,6 +23,7 @@ class WalletTransactionEncoder: TransactionEncoder {
rustBackend: ZcashRustBackendWelding,
dataDb: URL,
fsBlockDbRoot: URL,
service: LightWalletService,
repository: TransactionRepository,
outputParams: URL,
spendParams: URL,
@ -31,6 +33,7 @@ class WalletTransactionEncoder: TransactionEncoder {
self.rustBackend = rustBackend
self.dataDbURL = dataDb
self.fsBlockDbRoot = fsBlockDbRoot
self.lightWalletService = service
self.repository = repository
self.outputParamsURL = outputParams
self.spendParamsURL = spendParams
@ -43,6 +46,7 @@ class WalletTransactionEncoder: TransactionEncoder {
rustBackend: initializer.rustBackend,
dataDb: initializer.dataDbURL,
fsBlockDbRoot: initializer.fsBlockDbRoot,
service: initializer.lightWalletService,
repository: initializer.transactionRepository,
outputParams: initializer.outputParamsURL,
spendParams: initializer.spendParamsURL,
@ -126,6 +130,17 @@ class WalletTransactionEncoder: TransactionEncoder {
return Int(txId)
}
func submit(
transaction: EncodedTransaction
) async throws {
let response = try await self.lightWalletService.submit(spendTransaction: transaction.raw)
guard response.errorCode >= 0 else {
throw TransactionEncoderError.submitError(code: Int(response.errorCode) , message: response.errorMessage)
}
}
func ensureParams(spend: URL, output: URL) -> Bool {
let readableSpend = FileManager.default.isReadableFile(atPath: spend.path)
@ -134,4 +149,18 @@ class WalletTransactionEncoder: TransactionEncoder {
// TODO: [#713] change this to something that makes sense, https://github.com/zcash/ZcashLightClientKit/issues/713
return readableSpend && readableOutput
}
func closeDBConnection() {
self.repository.closeDBConnection()
}
}
extension ZcashTransaction.Overview {
func encodedTransaction() throws -> EncodedTransaction {
guard let raw else {
throw TransactionEncoderError.notEncoded(transactionId: self.id)
}
return EncodedTransaction(transactionId: self.rawID, raw: raw)
}
}

View File

@ -70,7 +70,6 @@ class SDKSynchronizerAliasDarksideTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
coordinators = []
}

View File

@ -28,7 +28,12 @@ class AdvancedReOrgTests: XCTestCase {
override func setUp() async throws {
try await super.setUp()
// don't use an exact birthday, users never do.
self.coordinator = try await TestCoordinator(walletBirthday: birthday + 50, network: network)
self.coordinator = try await TestCoordinator(
walletBirthday: birthday + 50,
network: network,
dbTracingClosure: { logger.debug($0) }
)
try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName)
}
@ -41,7 +46,6 @@ class AdvancedReOrgTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func handleReorg(event: CompactBlockProcessor.Event) {
@ -305,7 +309,7 @@ class AdvancedReOrgTests: XCTestCase {
await fulfillment(of: [preTxExpectation], timeout: 5)
let sendExpectation = XCTestExpectation(description: "sendToAddress")
var pendingEntity: PendingTransactionEntity?
var pendingEntity: ZcashTransaction.Overview?
var testError: Error?
let sendAmount = Zatoshi(10000)
@ -436,13 +440,14 @@ class AdvancedReOrgTests: XCTestCase {
await fulfillment(of: [lastSyncExpectation], timeout: 5)
let expectedVerifiedBalance = try await coordinator.synchronizer.getShieldedVerifiedBalance()
let expectedVerifiedBalance = initialTotalBalance + pendingTx.value
let currentVerifiedBalance = try await coordinator.synchronizer.getShieldedVerifiedBalance()
let expectedPendingTransactionsCount = await coordinator.synchronizer.pendingTransactions.count
XCTAssertEqual(expectedPendingTransactionsCount, 0)
XCTAssertEqual(initialTotalBalance - pendingTx.value - Zatoshi(1000), expectedVerifiedBalance)
XCTAssertEqual(expectedVerifiedBalance, currentVerifiedBalance)
let resultingBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
XCTAssertEqual(resultingBalance, expectedVerifiedBalance)
XCTAssertEqual(resultingBalance, currentVerifiedBalance)
}
func testIncomingTransactionIndexChange() async throws {
@ -571,6 +576,27 @@ class AdvancedReOrgTests: XCTestCase {
XCTAssertEqual(afterReOrgBalance, initialBalance)
XCTAssertEqual(afterReOrgVerifiedBalance, initialVerifiedBalance)
guard
let receivedTransaction = try await coordinator.synchronizer.allTransactions().first
else {
XCTFail("expected to have a received transaction, but found none")
return
}
let transactionOutputs = await coordinator.synchronizer.getTransactionOutputs(
for: receivedTransaction
)
guard transactionOutputs.count == 1 else {
XCTFail("expected output count to be 1")
return
}
let output = transactionOutputs[0]
XCTAssertEqual(output.recipient, .internalAccount(0))
XCTAssertEqual(output.value, Zatoshi(100000))
}
/// Steps:
@ -598,7 +624,7 @@ class AdvancedReOrgTests: XCTestCase {
var initialBalance = Zatoshi(-1)
var initialVerifiedBalance = Zatoshi(-1)
var incomingTx: ZcashTransaction.Received!
var incomingTx: ZcashTransaction.Overview!
try await coordinator.sync(
completion: { _ in
@ -765,16 +791,18 @@ class AdvancedReOrgTests: XCTestCase {
let initialTotalBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
let sendExpectation = XCTestExpectation(description: "send expectation")
var pendingEntity: PendingTransactionEntity?
var pendingEntity: ZcashTransaction.Overview?
/*
2. send transaction to recipient address
*/
let recipient = try Recipient(Environment.testRecipientAddress, network: self.network.networkType)
do {
let pendingTx = try await coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKey,
zatoshi: Zatoshi(20000),
toAddress: try Recipient(Environment.testRecipientAddress, network: self.network.networkType),
toAddress: recipient,
memo: try Memo(string: "this is a test")
)
pendingEntity = pendingTx
@ -889,7 +917,7 @@ class AdvancedReOrgTests: XCTestCase {
return
}
XCTAssertEqual(newPendingTx.minedHeight, BlockHeight.empty())
XCTAssertNil(newPendingTx.minedHeight)
/*
11. applyHeight(sentTxHeight + 2)
@ -920,7 +948,7 @@ class AdvancedReOrgTests: XCTestCase {
*/
pendingTransactionsCount = await coordinator.synchronizer.pendingTransactions.count
XCTAssertEqual(pendingTransactionsCount, 1)
guard let newlyPendingTx = try await coordinator.synchronizer.allPendingTransactions().first else {
guard let newlyPendingTx = try await coordinator.synchronizer.allPendingTransactions().first(where: { $0.isSentTransaction }) else {
XCTFail("no pending transaction")
try await coordinator.stop()
return
@ -962,7 +990,7 @@ class AdvancedReOrgTests: XCTestCase {
let sentTransactions = await coordinator.synchronizer.sentTransactions
.first(
where: { transaction in
return transaction.rawID == newlyPendingTx.rawTransactionId
return transaction.rawID == newlyPendingTx.rawID
}
)
@ -975,14 +1003,19 @@ class AdvancedReOrgTests: XCTestCase {
let expectedBalance = try await coordinator.synchronizer.getShieldedBalance()
XCTAssertEqual(
initialTotalBalance - newlyPendingTx.value - Zatoshi(1000),
initialTotalBalance + newlyPendingTx.value, // Note: sent transactions have negative values
expectedBalance
)
XCTAssertEqual(
initialTotalBalance - newlyPendingTx.value - Zatoshi(1000),
initialTotalBalance + newlyPendingTx.value, // Note: sent transactions have negative values
expectedVerifiedBalance
)
let txRecipients = await coordinator.synchronizer.getRecipients(for: newPendingTx)
XCTAssertEqual(txRecipients.count, 2)
XCTAssertNotNil(txRecipients.first(where: { $0 == .internalAccount(0) }))
XCTAssertNotNil(txRecipients.first(where: { $0 == .address(recipient) }))
}
/// Uses the zcash-hackworks data set.
@ -1180,7 +1213,7 @@ class AdvancedReOrgTests: XCTestCase {
let initialTotalBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
let sendExpectation = XCTestExpectation(description: "send expectation")
var pendingEntity: PendingTransactionEntity?
var pendingEntity: ZcashTransaction.Overview?
/*
2. send transaction to recipient address
@ -1274,7 +1307,7 @@ class AdvancedReOrgTests: XCTestCase {
return
}
XCTAssertFalse(pendingTx.isMined)
XCTAssertNil(pendingTx.minedHeight)
LoggerProxy.info("applyStaged(blockheight: \(sentTxHeight + extraBlocks - 1))")
try coordinator.applyStaged(blockheight: sentTxHeight + extraBlocks - 1)

View File

@ -38,7 +38,6 @@ class BalanceTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
/**
@ -85,7 +84,7 @@ class BalanceTests: XCTestCase {
// 4 send the transaction
let spendingKey = coordinator.spendingKey
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let transaction = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -106,9 +105,9 @@ class BalanceTests: XCTestCase {
}
notificationHandler.synchronizerMinedTransaction = { transaction in
XCTAssertNotNil(transaction.rawTransactionId)
XCTAssertNotNil(pendingTx.rawTransactionId)
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
XCTAssertNotNil(transaction.rawID)
XCTAssertNotNil(pendingTx.rawID)
XCTAssertEqual(transaction.rawID, pendingTx.rawID)
transactionMinedExpectation.fulfill()
}
@ -123,7 +122,7 @@ class BalanceTests: XCTestCase {
let sentTxHeight = latestHeight + 1
notificationHandler.transactionsFound = { txs in
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawTransactionId })
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(foundTx)
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
@ -139,9 +138,9 @@ class BalanceTests: XCTestCase {
do {
try await coordinator.sync(
completion: { synchronizer in
let pendingEntity = await synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
let pendingEntity = try await synchronizer.allPendingTransactions().first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
XCTAssertTrue(pendingEntity?.isMined ?? false)
XCTAssertNotNil(pendingEntity?.minedHeight)
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
mineExpectation.fulfill()
},
@ -181,7 +180,7 @@ class BalanceTests: XCTestCase {
await fulfillment(of: [confirmExpectation], timeout: 5)
let confirmedPending = try await coordinator.synchronizer.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
@ -234,7 +233,7 @@ class BalanceTests: XCTestCase {
// 3 create a transaction for the max amount possible
// 4 send the transaction
let spendingKey = coordinator.spendingKey
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let transaction = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -255,9 +254,9 @@ class BalanceTests: XCTestCase {
}
notificationHandler.synchronizerMinedTransaction = { transaction in
XCTAssertNotNil(transaction.rawTransactionId)
XCTAssertNotNil(pendingTx.rawTransactionId)
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
XCTAssertNotNil(transaction.rawID)
XCTAssertNotNil(pendingTx.rawID)
XCTAssertEqual(transaction.rawID, pendingTx.rawID)
transactionMinedExpectation.fulfill()
}
@ -272,7 +271,7 @@ class BalanceTests: XCTestCase {
let sentTxHeight = latestHeight + 1
notificationHandler.transactionsFound = { txs in
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawTransactionId })
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(foundTx)
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
@ -288,9 +287,9 @@ class BalanceTests: XCTestCase {
do {
try await coordinator.sync(
completion: { synchronizer in
let pendingEntity = await synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
let pendingEntity = try await synchronizer.allPendingTransactions().first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
XCTAssertTrue(pendingEntity?.isMined ?? false)
XCTAssertNotNil(pendingEntity?.minedHeight)
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
mineExpectation.fulfill()
},
@ -303,8 +302,9 @@ class BalanceTests: XCTestCase {
await fulfillment(of: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
// 7 advance to confirmation
try coordinator.applyStaged(blockheight: sentTxHeight + 10)
let advanceToConfirmationHeight = sentTxHeight + 10
try coordinator.applyStaged(blockheight: advanceToConfirmationHeight)
sleep(2)
@ -331,7 +331,7 @@ class BalanceTests: XCTestCase {
let confirmedPending = try await coordinator.synchronizer
.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
@ -384,7 +384,7 @@ class BalanceTests: XCTestCase {
// 3 create a transaction for the max amount possible
// 4 send the transaction
let spendingKey = coordinator.spendingKey
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let transaction = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -405,9 +405,9 @@ class BalanceTests: XCTestCase {
}
notificationHandler.synchronizerMinedTransaction = { transaction in
XCTAssertNotNil(transaction.rawTransactionId)
XCTAssertNotNil(pendingTx.rawTransactionId)
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
XCTAssertNotNil(transaction.rawID)
XCTAssertNotNil(pendingTx.rawID)
XCTAssertEqual(transaction.rawID, pendingTx.rawID)
transactionMinedExpectation.fulfill()
}
@ -422,7 +422,7 @@ class BalanceTests: XCTestCase {
let sentTxHeight = latestHeight + 1
notificationHandler.transactionsFound = { txs in
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawTransactionId })
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(foundTx)
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
@ -438,9 +438,9 @@ class BalanceTests: XCTestCase {
do {
try await coordinator.sync(
completion: { synchronizer in
let pendingEntity = await synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
let pendingEntity = try await synchronizer.allPendingTransactions().first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
XCTAssertTrue(pendingEntity?.isMined ?? false)
XCTAssertTrue(pendingEntity?.minedHeight != nil)
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
mineExpectation.fulfill()
},
@ -453,8 +453,9 @@ class BalanceTests: XCTestCase {
await fulfillment(of: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
// 7 advance to confirmation
try coordinator.applyStaged(blockheight: sentTxHeight + 10)
let advanceToConfirmation = sentTxHeight + 10
try coordinator.applyStaged(blockheight: advanceToConfirmation)
sleep(2)
@ -481,7 +482,7 @@ class BalanceTests: XCTestCase {
let confirmedPending = try await coordinator.synchronizer
.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
@ -537,7 +538,8 @@ class BalanceTests: XCTestCase {
*/
XCTAssertTrue(presendVerifiedBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let transaction = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -609,59 +611,29 @@ class BalanceTests: XCTestCase {
basic health check
*/
XCTAssertEqual(transaction.value, self.sendAmount)
/*
build up repos to get data
*/
guard let txid = transaction.rawTransactionId else {
XCTFail("sent transaction has no internal id")
let outputs = await coordinator.synchronizer.getTransactionOutputs(for: transaction)
guard outputs.count == 2 else {
XCTFail("Expected sent transaction to have 2 outputs")
return
}
let sentNoteDAO = SentNotesSQLDAO(
dbProvider: SimpleConnectionProvider(
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
readonly: true
)
)
let receivedNoteDAO = ReceivedNotesSQLDAO(
dbProvider: SimpleConnectionProvider(
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
readonly: true
)
)
var sentEntity: SentNoteEntity?
do {
sentEntity = try sentNoteDAO.sentNote(byRawTransactionId: txid)
} catch {
XCTFail("error retrieving sent note: \(error)")
}
guard let sentNote = sentEntity else {
XCTFail("could not find sent note for this transaction")
guard let changeOutput = outputs.first(where: { $0.isChange }) else {
XCTFail("Sent transaction has no change")
return
}
var receivedEntity: ReceivedNoteEntity?
do {
receivedEntity = try receivedNoteDAO.receivedNote(byRawTransactionId: txid)
} catch {
XCTFail("error retrieving received note: \(error)")
}
guard let receivedNote = receivedEntity else {
XCTFail("could not find sent note for this transaction")
guard let sentOutput = outputs.first(where: { !$0.isChange }) else {
XCTFail("sent transaction does not have a 'sent' output")
return
}
// (previous available funds - spent note + change) equals to (previous available funds - sent amount)
self.verifiedBalanceValidation(
previousBalance: presendVerifiedBalance,
spentNoteValue: Zatoshi(Int64(sentNote.value)),
changeValue: Zatoshi(Int64(receivedNote.value)),
spentNoteValue: sentOutput.value,
changeValue: changeOutput.value,
sentAmount: self.sendAmount,
currentVerifiedBalance: try await coordinator.synchronizer.getShieldedVerifiedBalance()
)
@ -709,7 +681,7 @@ class BalanceTests: XCTestCase {
// there's more zatoshi to send than network fee
XCTAssertTrue(presendBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
var testError: Error?
do {
@ -750,8 +722,6 @@ class BalanceTests: XCTestCase {
presendBalance - self.sendAmount - network.constants.defaultFee(for: defaultLatestHeight)
)
XCTAssertNil(transaction.errorCode)
let latestHeight = try await coordinator.latestHeight()
let sentTxHeight = latestHeight + 1
try coordinator.stageBlockCreate(height: sentTxHeight)
@ -814,7 +784,7 @@ class BalanceTests: XCTestCase {
await fulfillment(of: [syncedExpectation], timeout: 5)
let clearedTransactions = await coordinator.synchronizer.clearedTransactions
let clearedTransactions = await coordinator.synchronizer.transactions
let expectedBalance = try await coordinator.synchronizer.getShieldedBalance()
XCTAssertEqual(clearedTransactions.count, 2)
XCTAssertEqual(expectedBalance, Zatoshi(200000))
@ -878,7 +848,8 @@ class BalanceTests: XCTestCase {
Send
*/
let memo = try Memo(string: "shielding is fun!")
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let transaction = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -918,8 +889,8 @@ class BalanceTests: XCTestCase {
completion: { synchronizer in
let confirmedTx: ZcashTransaction.Overview!
do {
confirmedTx = try await synchronizer.allClearedTransactions().first(where: { confirmed -> Bool in
confirmed.rawID == pendingTx?.rawTransactionId
confirmedTx = try await synchronizer.allTransactions().first(where: { confirmed -> Bool in
confirmed.rawID == pendingTx?.rawID
})
} catch {
XCTFail("Error retrieving cleared transactions")
@ -937,30 +908,21 @@ class BalanceTests: XCTestCase {
/*
Find out what note was used
*/
let sentNotesRepo = SentNotesSQLDAO(
dbProvider: SimpleConnectionProvider(
path: synchronizer.initializer.dataDbURL.absoluteString,
readonly: true
)
)
guard let sentNote = try? sentNotesRepo.sentNote(byRawTransactionId: confirmedTx.rawID) else {
XCTFail("Could not finde sent note with transaction Id \(confirmedTx.rawID)")
let outputs = await self.coordinator.synchronizer.getTransactionOutputs(for: confirmedTx)
guard outputs.count == 2 else {
XCTFail("Expected sent transaction to have 2 outputs")
return
}
let receivedNotesRepo = ReceivedNotesSQLDAO(
dbProvider: SimpleConnectionProvider(
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
readonly: true
)
)
guard let changeOutput = outputs.first(where: { $0.isChange }) else {
XCTFail("Sent transaction has no change")
return
}
/*
get change note
*/
guard let receivedNote = try? receivedNotesRepo.receivedNote(byRawTransactionId: confirmedTx.rawID) else {
XCTFail("Could not find received not with change for transaction Id \(confirmedTx.rawID)")
guard let sentOutput = outputs.first(where: { !$0.isChange }) else {
XCTFail("sent transaction does not have a 'sent' output")
return
}
@ -969,7 +931,7 @@ class BalanceTests: XCTestCase {
*/
XCTAssertEqual(
previousVerifiedBalance - self.sendAmount - self.network.constants.defaultFee(for: self.defaultLatestHeight),
Zatoshi(Int64(receivedNote.value))
changeOutput.value
)
/*
@ -977,8 +939,8 @@ class BalanceTests: XCTestCase {
*/
self.verifiedBalanceValidation(
previousBalance: previousVerifiedBalance,
spentNoteValue: Zatoshi(Int64(sentNote.value)),
changeValue: Zatoshi(Int64(receivedNote.value)),
spentNoteValue: sentOutput.value,
changeValue: changeOutput.value,
sentAmount: self.sendAmount,
currentVerifiedBalance: try await synchronizer.getShieldedVerifiedBalance()
)
@ -1046,7 +1008,7 @@ class BalanceTests: XCTestCase {
let previousVerifiedBalance: Zatoshi = try await coordinator.synchronizer.getShieldedVerifiedBalance()
let previousTotalBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
let sendExpectation = XCTestExpectation(description: "send expectation")
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let pending = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -1067,13 +1029,13 @@ class BalanceTests: XCTestCase {
await fulfillment(of: [sendExpectation], timeout: 12)
guard let pendingTransaction = pendingTx, pendingTransaction.expiryHeight > defaultLatestHeight else {
guard let pendingTransaction = pendingTx, let expiryHeight = pendingTransaction.expiryHeight, expiryHeight > defaultLatestHeight else {
XCTFail("No pending transaction")
return
}
let expirationSyncExpectation = XCTestExpectation(description: "expiration sync expectation")
let expiryHeight = pendingTransaction.expiryHeight
try coordinator.applyStaged(blockheight: expiryHeight + 1)
sleep(2)
@ -1102,32 +1064,24 @@ class BalanceTests: XCTestCase {
Total Balance is equal to total balance previously shown before sending the expired transaction
*/
XCTAssertEqual(expectedBalance, previousTotalBalance)
let pendingRepo = PendingTransactionSQLDAO(
dbProvider: SimpleConnectionProvider(
path: coordinator.synchronizer.initializer.pendingDbURL.absoluteString
),
logger: logger
let transactionRepo = TransactionSQLDAO(dbProvider: SimpleConnectionProvider(
path: coordinator.synchronizer.initializer.dataDbURL.absoluteString
)
)
guard
let expiredPending = try? pendingRepo.find(by: pendingTransaction.id!),
let id = expiredPending.id
else {
XCTFail("pending transaction not found")
return
}
let expiredPending = try await transactionRepo.find(id: pendingTransaction.id)
/*
there no sent transaction displayed
*/
let sentTransactions = try await coordinator.synchronizer.allSentTransactions()
XCTAssertNil(sentTransactions.first(where: { $0.id == id }))
XCTAssertNil(sentTransactions.first(where: { $0.id == pendingTransaction.id }))
/*
Theres a pending transaction that has expired
*/
XCTAssertEqual(expiredPending.minedHeight, -1)
XCTAssertNil(expiredPending.minedHeight)
}
func handleError(_ error: Error?) {
@ -1162,7 +1116,7 @@ class BalanceTests: XCTestCase {
class SDKSynchonizerListener {
var transactionsFound: (([ZcashTransaction.Overview]) -> Void)?
var synchronizerMinedTransaction: ((PendingTransactionEntity) -> Void)?
var synchronizerMinedTransaction: ((ZcashTransaction.Overview) -> Void)?
var cancellables: [AnyCancellable] = []
func subscribeToSynchronizer(_ synchronizer: SDKSynchronizer) {
@ -1194,7 +1148,7 @@ class SDKSynchonizerListener {
}
}
func txMined(_ transaction: PendingTransactionEntity) {
func txMined(_ transaction: ZcashTransaction.Overview) {
DispatchQueue.main.async { [weak self] in
self?.synchronizerMinedTransaction?(transaction)
}

View File

@ -41,7 +41,6 @@ class DarksideSanityCheckTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func testDarkside() async throws {

View File

@ -41,7 +41,6 @@ final class InternalStateConsistencyTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func testInternalStateIsConsistentWhenMigrating() async throws {
@ -70,7 +69,7 @@ final class InternalStateConsistencyTests: XCTestCase {
let coordinator = self.coordinator!
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
Task(priority: .userInitiated) {
await coordinator.synchronizer.stop()
coordinator.synchronizer.stop()
}
}

View File

@ -37,7 +37,6 @@ class PendingTransactionUpdatesTest: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func testPendingTransactionMinedHeightUpdated() async throws {
@ -72,7 +71,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
sleep(1)
let sendExpectation = XCTestExpectation(description: "send expectation")
var pendingEntity: PendingTransactionEntity?
var pendingEntity: ZcashTransaction.Overview?
/*
2. send transaction to recipient address
@ -99,12 +98,12 @@ class PendingTransactionUpdatesTest: XCTestCase {
return
}
XCTAssertFalse(
pendingUnconfirmedTx.isConfirmed(currentHeight: 663188),
XCTAssertTrue(
pendingUnconfirmedTx.isPending(currentHeight: 633188),
"pending transaction evaluated as confirmed when it shouldn't"
)
XCTAssertFalse(
pendingUnconfirmedTx.isMined,
XCTAssertNil(
pendingUnconfirmedTx.minedHeight,
"pending transaction evaluated as mined when it shouldn't"
)
@ -171,7 +170,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
*/
LoggerProxy.info("6a. verify that there's a pending transaction with a mined height of \(sentTxHeight)")
XCTAssertEqual(afterStagePendingTx.minedHeight, sentTxHeight)
XCTAssertTrue(afterStagePendingTx.isMined, "pending transaction shown as unmined when it has been mined")
XCTAssertNotNil(afterStagePendingTx.minedHeight, "pending transaction shown as unmined when it has been mined")
XCTAssertTrue(afterStagePendingTx.isPending(currentHeight: sentTxHeight))
/*
@ -207,11 +206,11 @@ class PendingTransactionUpdatesTest: XCTestCase {
let supposedlyPendingUnexistingTransaction = try await coordinator.synchronizer.allPendingTransactions().first
let clearedTransactions = await coordinator.synchronizer
.clearedTransactions
.transactions
let clearedTransaction = clearedTransactions.first(where: { $0.rawID == afterStagePendingTx.rawTransactionId })
let clearedTransaction = clearedTransactions.first(where: { $0.rawID == afterStagePendingTx.rawID } )
XCTAssertEqual(clearedTransaction!.value.amount + clearedTransaction!.fee!.amount, -afterStagePendingTx.value.amount)
XCTAssertEqual(clearedTransaction!.value.amount, afterStagePendingTx.value.amount)
XCTAssertNil(supposedlyPendingUnexistingTransaction)
}

View File

@ -69,7 +69,6 @@ class ReOrgTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func handleReOrgNotification(event: CompactBlockProcessor.Event) {

View File

@ -43,7 +43,6 @@ class RewindRescanTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func handleError(_ error: Error?) {
@ -271,7 +270,7 @@ class RewindRescanTests: XCTestCase {
XCTAssertEqual(verifiedBalance, totalBalance)
// rewind to transaction
guard let transaction = try await coordinator.synchronizer.allClearedTransactions().first else {
guard let transaction = try await coordinator.synchronizer.allTransactions().first else {
XCTFail("failed to get a transaction to rewind to")
return
}
@ -362,7 +361,7 @@ class RewindRescanTests: XCTestCase {
// 3 create a transaction for the max amount possible
// 4 send the transaction
let spendingKey = coordinator.spendingKey
var pendingTx: PendingTransactionEntity?
var pendingTx: ZcashTransaction.Overview?
do {
let transaction = try await coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey,
@ -382,9 +381,9 @@ class RewindRescanTests: XCTestCase {
}
notificationHandler.synchronizerMinedTransaction = { transaction in
XCTAssertNotNil(transaction.rawTransactionId)
XCTAssertNotNil(pendingTx.rawTransactionId)
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
XCTAssertNotNil(transaction.rawID)
XCTAssertNotNil(pendingTx.rawID)
XCTAssertEqual(transaction.rawID, pendingTx.rawID)
transactionMinedExpectation.fulfill()
}
@ -399,7 +398,7 @@ class RewindRescanTests: XCTestCase {
let sentTxHeight = latestHeight + 1
notificationHandler.transactionsFound = { txs in
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawTransactionId })
let foundTx = txs.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(foundTx)
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
@ -416,10 +415,10 @@ class RewindRescanTests: XCTestCase {
do {
try await coordinator.sync(
completion: { synchronizer in
let pendingTransaction = await synchronizer.pendingTransactions
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
let pendingTransaction = try await synchronizer.allPendingTransactions()
.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNotNil(pendingTransaction, "pending transaction should have been mined by now")
XCTAssertTrue(pendingTransaction?.isMined ?? false)
XCTAssertNotNil(pendingTransaction?.minedHeight)
XCTAssertEqual(pendingTransaction?.minedHeight, sentTxHeight)
mineExpectation.fulfill()
}, error: self.handleError
@ -431,16 +430,18 @@ class RewindRescanTests: XCTestCase {
await fulfillment(of: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
// 7 advance to confirmation
try coordinator.applyStaged(blockheight: sentTxHeight + 10)
let advanceToConfirmationHeight = sentTxHeight + 10
try coordinator.applyStaged(blockheight: advanceToConfirmationHeight)
sleep(2)
let rewindExpectation = XCTestExpectation(description: "RewindExpectation")
let rewindHeight = sentTxHeight - 5
try await withCheckedThrowingContinuation { continuation in
// rewind 5 blocks prior to sending
coordinator.synchronizer.rewind(.height(blockheight: sentTxHeight - 5))
coordinator.synchronizer.rewind(.height(blockheight: rewindHeight))
.sink(
receiveCompletion: { result in
rewindExpectation.fulfill()
@ -462,13 +463,13 @@ class RewindRescanTests: XCTestCase {
guard
let pendingEntity = try await coordinator.synchronizer.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
.first(where: { $0.rawID == pendingTx.rawID })
else {
XCTFail("sent pending transaction not found after rewind")
return
}
XCTAssertFalse(pendingEntity.isMined)
XCTAssertNil(pendingEntity.minedHeight)
let confirmExpectation = XCTestExpectation(description: "confirm expectation")
notificationHandler.transactionsFound = { txs in
@ -477,7 +478,7 @@ class RewindRescanTests: XCTestCase {
XCTFail("should have found sent transaction but didn't")
return
}
XCTAssertEqual(transaction.rawID, pendingTx.rawTransactionId, "should have mined sent transaction but didn't")
XCTAssertEqual(transaction.rawID, pendingTx.rawID, "should have mined sent transaction but didn't")
}
notificationHandler.synchronizerMinedTransaction = { transaction in
@ -498,7 +499,7 @@ class RewindRescanTests: XCTestCase {
await fulfillment(of: [confirmExpectation], timeout: 10)
let confirmedPending = try await coordinator.synchronizer.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
.first(where: { $0.rawID == pendingTx.rawID })
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")

View File

@ -38,7 +38,6 @@ class ShieldFundsTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
/// Tests shielding funds from a UTXO
@ -206,7 +205,7 @@ class ShieldFundsTests: XCTestCase {
shouldContinue = false
var shieldingPendingTx: PendingTransactionEntity?
var shieldingPendingTx: ZcashTransaction.Overview?
// shield the funds
do {
@ -216,7 +215,7 @@ class ShieldFundsTests: XCTestCase {
shieldingThreshold: Zatoshi(10000)
)
shouldContinue = true
XCTAssertEqual(pendingTx.value, Zatoshi(10000))
XCTAssertEqual(pendingTx.value, Zatoshi(10000) - pendingTx.fee!)
shieldingPendingTx = pendingTx
shieldFundsExpectation.fulfill()
} catch {
@ -320,8 +319,8 @@ class ShieldFundsTests: XCTestCase {
guard shouldContinue else { return }
// verify that there's a confirmed transaction that's the shielding transaction
let clearedTransaction = await coordinator.synchronizer.clearedTransactions.first(
where: { $0.rawID == shieldingPendingTx?.rawTransactionId }
let clearedTransaction = await coordinator.synchronizer.transactions.first(
where: { $0.rawID == shieldingPendingTx?.rawID }
)
XCTAssertNotNil(clearedTransaction)

View File

@ -45,7 +45,6 @@ class SynchronizerDarksideTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func testFoundTransactions() async throws {
@ -181,7 +180,7 @@ class SynchronizerDarksideTests: XCTestCase {
transparentBalance: .zero,
syncStatus: .disconnected,
latestScannedHeight: 663150,
latestBlockHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 1576821833
),
SynchronizerState(
@ -190,13 +189,12 @@ class SynchronizerDarksideTests: XCTestCase {
transparentBalance: .zero,
syncStatus: .syncing(BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0)),
latestScannedHeight: 663150,
latestBlockHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 1576821833
),
SynchronizerState(
syncSessionID: uuids[0],
shieldedBalance: .zero,
// shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
transparentBalance: .zero,
syncStatus: .syncing(BlockProgress(startHeight: 663150, targetHeight: 663189, progressHeight: 663189)),
latestScannedHeight: 663189,
@ -296,7 +294,15 @@ class SynchronizerDarksideTests: XCTestCase {
)
]
XCTAssertEqual(states, expectedStates)
XCTAssertEqual(states.count, expectedStates.count)
XCTAssertEqual(states[0], expectedStates[0])
XCTAssertEqual(states[1], expectedStates[1])
XCTAssertEqual(states[2], expectedStates[2])
XCTAssertEqual(states[3], expectedStates[3])
XCTAssertEqual(states[4], expectedStates[4])
XCTAssertEqual(states[5], expectedStates[5])
XCTAssertEqual(states[6], expectedStates[6])
XCTAssertEqual(states[7], expectedStates[7])
}
func testSyncSessionUpdates() async throws {
@ -338,7 +344,7 @@ class SynchronizerDarksideTests: XCTestCase {
syncStatus: .disconnected,
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedTime: 1576821833.0
),
SynchronizerState(
syncSessionID: uuids[0],
@ -347,25 +353,25 @@ class SynchronizerDarksideTests: XCTestCase {
syncStatus: .syncing(BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0)),
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedTime: 1576821833.0
),
SynchronizerState(
syncSessionID: uuids[0],
shieldedBalance: .zero,
shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
transparentBalance: .zero,
syncStatus: .syncing(BlockProgress(startHeight: 663150, targetHeight: 663189, progressHeight: 663189)),
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663189,
latestBlockHeight: 663189,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[0],
shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .enhancing(EnhancementProgress(totalTransactions: 0, enhancedTransactions: 0, lastFoundTransaction: nil, range: 0...0)),
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663189,
latestBlockHeight: 663189,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[0],
@ -379,7 +385,7 @@ class SynchronizerDarksideTests: XCTestCase {
accountId: 0,
blockTime: 1.0,
expiryHeight: 663206,
fee: Zatoshi(0),
fee: nil,
id: 2,
index: 1,
hasChange: false,
@ -395,9 +401,9 @@ class SynchronizerDarksideTests: XCTestCase {
range: 663150...663189
)
),
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663189,
latestBlockHeight: 663189,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[0],
@ -411,7 +417,7 @@ class SynchronizerDarksideTests: XCTestCase {
accountId: 0,
blockTime: 1.0,
expiryHeight: 663192,
fee: Zatoshi(0),
fee: nil,
id: 1,
index: 1,
hasChange: false,
@ -427,18 +433,18 @@ class SynchronizerDarksideTests: XCTestCase {
range: 663150...663189
)
),
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663189,
latestBlockHeight: 663189,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[0],
shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .fetching,
latestScannedHeight: 663150,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663189,
latestBlockHeight: 663189,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[0],
@ -446,12 +452,20 @@ class SynchronizerDarksideTests: XCTestCase {
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .synced,
latestScannedHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 0
latestBlockHeight: 663189,
latestScannedTime: 1
)
]
XCTAssertEqual(states, expectedStates)
XCTAssertEqual(states.count, expectedStates.count)
XCTAssertEqual(states[0], expectedStates[0])
XCTAssertEqual(states[1], expectedStates[1])
XCTAssertEqual(states[2], expectedStates[2])
XCTAssertEqual(states[3], expectedStates[3])
XCTAssertEqual(states[4], expectedStates[4])
XCTAssertEqual(states[5], expectedStates[5])
XCTAssertEqual(states[7], expectedStates[7])
try coordinator.service.applyStaged(nextLatestHeight: 663_200)
@ -477,35 +491,35 @@ class SynchronizerDarksideTests: XCTestCase {
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .syncing(BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0)),
latestScannedHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 0
latestBlockHeight: 663189,
latestScannedTime: 1.0
),
SynchronizerState(
syncSessionID: uuids[1],
shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
shieldedBalance: WalletBalance(verified: Zatoshi(200000), total: Zatoshi(200000)),
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .syncing(BlockProgress(startHeight: 663190, targetHeight: 663200, progressHeight: 663200)),
latestScannedHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663200,
latestBlockHeight: 663200,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[1],
shieldedBalance: WalletBalance(verified: Zatoshi(200000), total: Zatoshi(200000)),
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .enhancing(EnhancementProgress(totalTransactions: 0, enhancedTransactions: 0, lastFoundTransaction: nil, range: 0...0)),
latestScannedHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663200,
latestBlockHeight: 663200,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[1],
shieldedBalance: WalletBalance(verified: Zatoshi(200000), total: Zatoshi(200000)),
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .fetching,
latestScannedHeight: 663189,
latestBlockHeight: 0,
latestScannedTime: 0
latestScannedHeight: 663200,
latestBlockHeight: 663200,
latestScannedTime: 1
),
SynchronizerState(
syncSessionID: uuids[1],
@ -513,12 +527,18 @@ class SynchronizerDarksideTests: XCTestCase {
transparentBalance: WalletBalance(verified: Zatoshi(0), total: Zatoshi(0)),
syncStatus: .synced,
latestScannedHeight: 663200,
latestBlockHeight: 0,
latestScannedTime: 0
latestBlockHeight: 663200,
latestScannedTime: 1
)
]
XCTAssertEqual(states, secondBatchOfExpectedStates)
XCTAssertEqual(states.count, secondBatchOfExpectedStates.count)
XCTAssertEqual(states[0], secondBatchOfExpectedStates[0])
XCTAssertEqual(states[1], secondBatchOfExpectedStates[1])
XCTAssertEqual(states[2], secondBatchOfExpectedStates[2])
XCTAssertEqual(states[3], secondBatchOfExpectedStates[3])
XCTAssertEqual(states[4], secondBatchOfExpectedStates[4])
}
func testSyncAfterWipeWorks() async throws {

View File

@ -51,7 +51,6 @@ final class SynchronizerTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func handleReorg(event: CompactBlockProcessor.Event) {
@ -92,7 +91,7 @@ final class SynchronizerTests: XCTestCase {
)
try await Task.sleep(nanoseconds: 5_000_000_000)
await self.coordinator.synchronizer.stop()
self.coordinator.synchronizer.stop()
await fulfillment(of: [syncStoppedExpectation], timeout: 6)
@ -221,7 +220,7 @@ final class SynchronizerTests: XCTestCase {
let storage = await self.coordinator.synchronizer.blockProcessor.storage as! FSCompactBlockRepository
let fm = FileManager.default
print(coordinator.synchronizer.initializer.dataDbURL.path)
XCTAssertFalse(fm.fileExists(atPath: coordinator.synchronizer.initializer.pendingDbURL.path), "Pending DB should be deleted")
XCTAssertFalse(fm.fileExists(atPath: coordinator.synchronizer.initializer.dataDbURL.path), "Data DB should be deleted.")
XCTAssertTrue(fm.fileExists(atPath: storage.blocksDirectory.path), "FS Cache directory should exist")
XCTAssertEqual(try fm.contentsOfDirectory(atPath: storage.blocksDirectory.path), [], "FS Cache directory should be empty")
@ -262,7 +261,7 @@ final class SynchronizerTests: XCTestCase {
// 1 sync and get spendable funds
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
try coordinator.applyStaged(blockheight: defaultLatestHeight)
try coordinator.applyStaged(blockheight: 663200)
let initialVerifiedBalance: Zatoshi = try await coordinator.synchronizer.getShieldedVerifiedBalance()
let initialTotalBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
sleep(1)
@ -281,6 +280,12 @@ final class SynchronizerTests: XCTestCase {
await fulfillment(of: [firstSyncExpectation], timeout: 12)
let verifiedBalance: Zatoshi = try await coordinator.synchronizer.getShieldedVerifiedBalance()
let totalBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
// 2 check that there are no unconfirmed funds
XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: defaultLatestHeight))
XCTAssertEqual(verifiedBalance, totalBalance)
// Add more blocks to the chain so the long sync can start.
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName, length: 10000)
try coordinator.applyStaged(blockheight: birthday + 10000)
@ -305,19 +310,12 @@ final class SynchronizerTests: XCTestCase {
await fulfillment(of: [waitExpectation], timeout: 1)
let verifiedBalance: Zatoshi = try await coordinator.synchronizer.getShieldedVerifiedBalance()
let totalBalance: Zatoshi = try await coordinator.synchronizer.getShieldedBalance()
// 2 check that there are no unconfirmed funds
XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: defaultLatestHeight))
XCTAssertEqual(verifiedBalance, totalBalance)
let rewindExpectation = XCTestExpectation(description: "RewindExpectation")
// rewind to birthday
coordinator.synchronizer.rewind(.birthday)
.sink(
receiveCompletion: { result in
rewindExpectation.fulfill()
switch result {
case .finished:
break
@ -326,7 +324,7 @@ final class SynchronizerTests: XCTestCase {
}
rewindExpectation.fulfill()
},
receiveValue: { _ in }
receiveValue: { _ in rewindExpectation.fulfill() }
)
.store(in: &cancellables)

View File

@ -41,7 +41,6 @@ class Z2TReceiveTests: XCTestCase {
try await coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
}
func subscribeToFoundTransactions() {
@ -134,7 +133,7 @@ class Z2TReceiveTests: XCTestCase {
await fulfillment(of: [preTxExpectation, foundTransactionsExpectation], timeout: 5)
let sendExpectation = XCTestExpectation(description: "sendToAddress")
var pendingEntity: PendingTransactionEntity?
var pendingEntity: ZcashTransaction.Overview?
var testError: Error?
let sendAmount = Zatoshi(10000)
/*

View File

@ -307,7 +307,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
}
func testSendToAddressSucceed() throws {
let amount = Zatoshi(100)
let amount = Zatoshi(10)
let recipient: Recipient = .transparent(data.transparentAddress)
let memo: Memo = .text(try MemoText("Some message"))
let mockedSpendingKey = data.spendingKey
@ -326,7 +326,8 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
synchronizer.sendToAddress(spendingKey: mockedSpendingKey, zatoshi: amount, toAddress: recipient, memo: memo) { result in
switch result {
case let .success(receivedEntity):
XCTAssertEqual(receivedEntity.recipient, self.data.pendingTransactionEntity.recipient)
XCTAssertEqual(receivedEntity.value, amount)
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
@ -377,7 +378,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
synchronizer.shieldFunds(spendingKey: mockedSpendingKey, memo: memo, shieldingThreshold: shieldingThreshold) { result in
switch result {
case let .success(receivedEntity):
XCTAssertEqual(receivedEntity.recipient, self.data.pendingTransactionEntity.recipient)
XCTAssertEqual(receivedEntity.value, self.data.pendingTransactionEntity.value)
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
@ -410,22 +411,6 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testCancelSpendSucceed() {
synchronizerMock.cancelSpendTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.recipient, self.data.pendingTransactionEntity.recipient)
return true
}
let expectation = XCTestExpectation()
synchronizer.cancelSpend(transaction: data.pendingTransactionEntity) { result in
XCTAssertTrue(result)
expectation.fulfill()
}
wait(for: [expectation], timeout: 0.5)
}
func testPendingTransactionsSucceed() {
synchronizerMock.underlyingPendingTransactions = [data.pendingTransactionEntity]
@ -433,7 +418,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
synchronizer.pendingTransactions() { transactions in
XCTAssertEqual(transactions.count, 1)
XCTAssertEqual(transactions[0].recipient, self.data.pendingTransactionEntity.recipient)
XCTAssertEqual(transactions[0].id, self.data.pendingTransactionEntity.id)
expectation.fulfill()
}
@ -441,7 +426,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
}
func testClearedTransactionsSucceed() {
synchronizerMock.underlyingClearedTransactions = [data.clearedTransaction]
synchronizerMock.underlyingTransactions = [data.clearedTransaction]
let expectation = XCTestExpectation()
@ -525,92 +510,6 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForReceivedTransactionSucceed() throws {
let memo: Memo = .text(try MemoText("Some message"))
synchronizerMock.getMemosForReceivedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.receivedTransaction.id)
return [memo]
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.receivedTransaction) { result in
switch result {
case let .success(memos):
XCTAssertEqual(memos.count, 1)
XCTAssertEqual(memos[0], memo)
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
}
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForReceivedTransactionThrowsError() {
synchronizerMock.getMemosForReceivedTransactionClosure = { _ in
throw "Some error"
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.receivedTransaction) { result in
switch result {
case .success:
XCTFail("Error should be thrown.")
case .failure:
expectation.fulfill()
}
}
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForSentTransactionSucceed() throws {
let memo: Memo = .text(try MemoText("Some message"))
synchronizerMock.getMemosForSentTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.sentTransaction.id)
return [memo]
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.sentTransaction) { result in
switch result {
case let .success(memos):
XCTAssertEqual(memos.count, 1)
XCTAssertEqual(memos[0], memo)
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
}
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForSentTransactionThrowsError() {
synchronizerMock.getMemosForSentTransactionClosure = { _ in
throw "Some error"
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.sentTransaction) { result in
switch result {
case .success:
XCTFail("Error should be thrown.")
case .failure:
expectation.fulfill()
}
}
wait(for: [expectation], timeout: 0.5)
}
func testGetRecipientsForClearedTransaction() {
let expectedRecipient: TransactionRecipient = .address(.transparent(data.transparentAddress))
@ -630,27 +529,8 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testGetRecipientsForSentTransaction() {
let expectedRecipient: TransactionRecipient = .address(.transparent(data.transparentAddress))
synchronizerMock.getRecipientsForSentTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.sentTransaction.id)
return [expectedRecipient]
}
let expectation = XCTestExpectation()
synchronizer.getRecipients(for: data.sentTransaction) { recipients in
XCTAssertEqual(recipients.count, 1)
XCTAssertEqual(recipients[0], expectedRecipient)
expectation.fulfill()
}
wait(for: [expectation], timeout: 0.5)
}
func testAllConfirmedTransactionsSucceed() throws {
synchronizerMock.allConfirmedTransactionsFromLimitClosure = { receivedTransaction, limit in
synchronizerMock.allTransactionsFromLimitClosure = { receivedTransaction, limit in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(limit, 3)
return [self.data.clearedTransaction]
@ -673,7 +553,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
}
func testAllConfirmedTransactionsThrowsError() throws {
synchronizerMock.allConfirmedTransactionsFromLimitClosure = { _, _ in
synchronizerMock.allTransactionsFromLimitClosure = { _, _ in
throw "Some error"
}

View File

@ -329,7 +329,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.recipient, self.data.pendingTransactionEntity.recipient)
XCTAssertEqual(value.value, self.data.pendingTransactionEntity.value)
}
)
.store(in: &cancellables)
@ -393,7 +393,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.recipient, self.data.pendingTransactionEntity.recipient)
XCTAssertEqual(value.id, self.data.pendingTransactionEntity.id)
}
)
.store(in: &cancellables)
@ -431,33 +431,6 @@ class CombineSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testCancelSpendSucceed() {
synchronizerMock.cancelSpendTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.recipient, self.data.pendingTransactionEntity.recipient)
return true
}
let expectation = XCTestExpectation()
synchronizer.cancelSpend(transaction: data.pendingTransactionEntity)
.sink(
receiveCompletion: { result in
switch result {
case .finished:
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
},
receiveValue: { value in
XCTAssertTrue(value)
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testPendingTransactionsSucceed() {
synchronizerMock.underlyingPendingTransactions = [data.pendingTransactionEntity]
@ -483,11 +456,11 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
func testClearedTransactionsSucceed() {
synchronizerMock.underlyingClearedTransactions = [data.clearedTransaction]
synchronizerMock.underlyingTransactions = [data.clearedTransaction]
let expectation = XCTestExpectation()
synchronizer.clearedTransactions
synchronizer.allTransactions
.sink(
receiveCompletion: { result in
switch result {
@ -609,118 +582,8 @@ class CombineSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForReceivedTransactionSucceed() throws {
let memo: Memo = .text(try MemoText("Some message"))
synchronizerMock.getMemosForReceivedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.receivedTransaction.id)
return [memo]
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.receivedTransaction)
.sink(
receiveCompletion: { result in
switch result {
case .finished:
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
},
receiveValue: { value in
XCTAssertEqual(value, [memo])
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForReceivedTransactionThrowsError() {
synchronizerMock.getMemosForReceivedTransactionClosure = { _ in
throw "Some error"
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.receivedTransaction)
.sink(
receiveCompletion: { result in
switch result {
case .finished:
XCTFail("Error should be thrown.")
case .failure:
expectation.fulfill()
}
},
receiveValue: { _ in
XCTFail("No value is expected")
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForSentTransactionSucceed() throws {
let memo: Memo = .text(try MemoText("Some message"))
synchronizerMock.getMemosForSentTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.sentTransaction.id)
return [memo]
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.sentTransaction)
.sink(
receiveCompletion: { result in
switch result {
case .finished:
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
},
receiveValue: { value in
XCTAssertEqual(value, [memo])
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testGetMemosForSentTransactionThrowsError() {
synchronizerMock.getMemosForSentTransactionClosure = { _ in
throw "Some error"
}
let expectation = XCTestExpectation()
synchronizer.getMemos(for: data.sentTransaction)
.sink(
receiveCompletion: { result in
switch result {
case .finished:
XCTFail("Error should be thrown.")
case .failure:
expectation.fulfill()
}
},
receiveValue: { _ in
XCTFail("No value is expected")
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testGetRecipientsForClearedTransaction() {
let expectedRecipient: TransactionRecipient = .address(.transparent(data.transparentAddress))
let expectedRecipient: TransactionRecipient = TransactionRecipient.address(.transparent(self.data.transparentAddress))
synchronizerMock.getRecipientsForClearedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
@ -748,37 +611,8 @@ class CombineSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testGetRecipientsForSentTransaction() {
let expectedRecipient: TransactionRecipient = .address(.transparent(data.transparentAddress))
synchronizerMock.getRecipientsForSentTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.sentTransaction.id)
return [expectedRecipient]
}
let expectation = XCTestExpectation()
synchronizer.getRecipients(for: data.sentTransaction)
.sink(
receiveCompletion: { result in
switch result {
case .finished:
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
},
receiveValue: { value in
XCTAssertEqual(value, [expectedRecipient])
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testAllConfirmedTransactionsSucceed() throws {
synchronizerMock.allConfirmedTransactionsFromLimitClosure = { receivedTransaction, limit in
synchronizerMock.allTransactionsFromLimitClosure = { receivedTransaction, limit in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(limit, 3)
return [self.data.clearedTransaction]
@ -786,7 +620,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
let expectation = XCTestExpectation()
synchronizer.allConfirmedTransactions(from: data.clearedTransaction, limit: 3)
synchronizer.allTransactions(from: data.clearedTransaction, limit: 3)
.sink(
receiveCompletion: { result in
switch result {
@ -806,13 +640,13 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
func testAllConfirmedTransactionsThrowsError() throws {
synchronizerMock.allConfirmedTransactionsFromLimitClosure = { _, _ in
synchronizerMock.allTransactionsFromLimitClosure = { _, _ in
throw "Some error"
}
let expectation = XCTestExpectation()
synchronizer.allConfirmedTransactions(from: data.clearedTransaction, limit: 3)
synchronizer.allTransactions(from: data.clearedTransaction, limit: 3)
.sink(
receiveCompletion: { result in
switch result {

View File

@ -20,7 +20,6 @@ class InitializerOfflineTests: XCTestCase {
private func makeInitializer(
fsBlockDbRoot: URL,
dataDbURL: URL,
pendingDbURL: URL,
spendParamsURL: URL,
outputParamsURL: URL,
alias: ZcashSynchronizerAlias
@ -29,7 +28,6 @@ class InitializerOfflineTests: XCTestCase {
cacheDbURL: nil,
fsBlockDbRoot: fsBlockDbRoot,
dataDbURL: dataDbURL,
pendingDbURL: pendingDbURL,
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: .testnet),
spendParamsURL: spendParamsURL,
@ -54,7 +52,6 @@ class InitializerOfflineTests: XCTestCase {
private func genericTestForURLsParsingFailures(
fsBlockDbRoot: URL,
dataDbURL: URL,
pendingDbURL: URL,
spendParamsURL: URL,
outputParamsURL: URL,
alias: ZcashSynchronizerAlias,
@ -63,7 +60,6 @@ class InitializerOfflineTests: XCTestCase {
let initializer = makeInitializer(
fsBlockDbRoot: fsBlockDbRoot,
dataDbURL: dataDbURL,
pendingDbURL: pendingDbURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
alias: alias
@ -77,7 +73,6 @@ class InitializerOfflineTests: XCTestCase {
XCTAssertEqual(initializer.fsBlockDbRoot, fsBlockDbRoot, "Failing \(function)")
XCTAssertEqual(initializer.dataDbURL, dataDbURL, "Failing \(function)")
XCTAssertEqual(initializer.pendingDbURL, pendingDbURL, "Failing \(function)")
XCTAssertEqual(initializer.spendParamsURL, spendParamsURL, "Failing \(function)")
XCTAssertEqual(initializer.outputParamsURL, outputParamsURL, "Failing \(function)")
}
@ -86,7 +81,6 @@ class InitializerOfflineTests: XCTestCase {
let initializer = makeInitializer(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
@ -95,7 +89,6 @@ class InitializerOfflineTests: XCTestCase {
XCTAssertNil(initializer.urlsParsingError)
XCTAssertEqual(initializer.fsBlockDbRoot, validDirectoryURL)
XCTAssertEqual(initializer.dataDbURL, validFileURL)
XCTAssertEqual(initializer.pendingDbURL, validFileURL)
XCTAssertEqual(initializer.spendParamsURL, validFileURL)
XCTAssertEqual(initializer.outputParamsURL, validFileURL)
}
@ -104,7 +97,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: invalidPathURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
@ -115,18 +107,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: invalidPathURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
)
}
func test__defaultAlias__invalidPendingDbURL__errorIsGenerated() {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: invalidPathURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
@ -137,7 +117,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: invalidPathURL,
outputParamsURL: validFileURL,
alias: .default
@ -148,7 +127,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: invalidPathURL,
alias: .default
@ -160,7 +138,6 @@ class InitializerOfflineTests: XCTestCase {
let initializer = makeInitializer(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: alias
@ -169,7 +146,6 @@ class InitializerOfflineTests: XCTestCase {
XCTAssertNil(initializer.urlsParsingError)
XCTAssertEqual(initializer.fsBlockDbRoot, update(url: validDirectoryURL, with: alias))
XCTAssertEqual(initializer.dataDbURL, update(url: validFileURL, with: alias))
XCTAssertEqual(initializer.pendingDbURL, update(url: validFileURL, with: alias))
XCTAssertEqual(initializer.spendParamsURL, update(url: validFileURL, with: alias))
XCTAssertEqual(initializer.outputParamsURL, update(url: validFileURL, with: alias))
}
@ -178,7 +154,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: invalidPathURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
@ -189,18 +164,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: invalidPathURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
)
}
func test__customAlias__invalidPendingDbURL__errorIsGenerated() {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: invalidPathURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
@ -211,7 +174,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: invalidPathURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
@ -222,7 +184,6 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: validFileURL,
spendParamsURL: validFileURL,
outputParamsURL: invalidPathURL,
alias: .custom("alias")

View File

@ -1,40 +0,0 @@
//
// NotesRepositoryTests.swift
// ZcashLightClientKit-Unit-Tests
//
// Created by Francisco Gindre on 11/18/19.
//
import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
class NotesRepositoryTests: XCTestCase {
var sentNotesRepository: SentNotesRepository!
var receivedNotesRepository: ReceivedNoteRepository!
override func setUp() async throws {
try await super.setUp()
let rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: Environment.uniqueTestTempDirectory, networkType: .testnet)
sentNotesRepository = try! await TestDbBuilder.sentNotesRepository(rustBackend: rustBackend)
receivedNotesRepository = try! await TestDbBuilder.receivedNotesRepository(rustBackend: rustBackend)
}
override func tearDown() {
super.tearDown()
sentNotesRepository = nil
receivedNotesRepository = nil
}
func testSentCount() {
var count: Int?
XCTAssertNoThrow(try { count = try sentNotesRepository.count() }())
XCTAssertEqual(count, 13)
}
func testReceivedCount() {
var count: Int?
XCTAssertNoThrow(try { count = try receivedNotesRepository.count() }())
XCTAssertEqual(count, 22)
}
}

View File

@ -1,181 +0,0 @@
//
// PendingTransactionRepositoryTests.swift
// ZcashLightClientKit-Unit-Tests
//
// Created by Francisco Gindre on 11/27/19.
//
import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
class PendingTransactionRepositoryTests: XCTestCase {
let dbUrl = try! TestDbBuilder.pendingTransactionsDbURL()
let recipient = SaplingAddress(validatedEncoding: "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6")
var pendingRepository: PendingTransactionRepository!
override func setUp() {
super.setUp()
cleanUpDb()
let pendingDbProvider = SimpleConnectionProvider(path: try! TestDbBuilder.pendingTransactionsDbURL().absoluteString)
let migrations = MigrationManager(pendingDbConnection: pendingDbProvider, networkType: .testnet, logger: logger)
try! migrations.performMigration()
pendingRepository = PendingTransactionSQLDAO(dbProvider: pendingDbProvider, logger: logger)
}
override func tearDown() {
super.tearDown()
cleanUpDb()
pendingRepository = nil
}
func cleanUpDb() {
try? FileManager.default.removeItem(at: TestDbBuilder.pendingTransactionsDbURL())
}
func testCreate() {
let transaction = createAndStoreMockedTransaction()
guard let id = transaction.id, id >= 0 else {
XCTFail("failed to create mocked transaction that was just inserted")
return
}
var expectedTx: PendingTransactionEntity?
XCTAssertNoThrow(try { expectedTx = try pendingRepository.find(by: id) }())
guard let expected = expectedTx else {
XCTFail("failed to retrieve mocked transaction by id \(id) that was just inserted")
return
}
XCTAssertEqual(transaction.accountIndex, expected.accountIndex)
XCTAssertEqual(transaction.value, expected.value)
XCTAssertEqual(transaction.recipient, expected.recipient)
}
func testFindById() {
let transaction = createAndStoreMockedTransaction()
var expected: PendingTransactionEntity?
guard let id = transaction.id else {
XCTFail("transaction with no id")
return
}
XCTAssertNoThrow(try { expected = try pendingRepository.find(by: id) }())
XCTAssertNotNil(expected)
}
func testCancel() {
let transaction = createAndStoreMockedTransaction()
guard let id = transaction.id else {
XCTFail("transaction with no id")
return
}
guard id >= 0 else {
XCTFail("failed to create mocked transaction that was just inserted")
return
}
XCTAssertNoThrow(try pendingRepository.cancel(transaction))
}
func testDelete() {
let transaction = createAndStoreMockedTransaction()
guard let id = transaction.id else {
XCTFail("transaction with no id")
return
}
guard id >= 0 else {
XCTFail("failed to create mocked transaction that was just inserted")
return
}
XCTAssertNoThrow(try pendingRepository.delete(transaction))
var unexpectedTx: PendingTransactionEntity?
XCTAssertNoThrow(try { unexpectedTx = try pendingRepository.find(by: id) }())
XCTAssertNil(unexpectedTx)
}
func testGetAll() {
var mockTransactions: [PendingTransactionEntity] = []
for _ in 1...100 {
mockTransactions.append(createAndStoreMockedTransaction())
}
var all: [PendingTransactionEntity]?
XCTAssertNoThrow(try { all = try pendingRepository.getAll() }())
guard let allTxs = all else {
XCTFail("failed to get all transactions")
return
}
XCTAssertEqual(mockTransactions.count, allTxs.count)
}
func testUpdate() {
let transaction = createAndStoreMockedTransaction()
guard let id = transaction.id else {
XCTFail("transaction with no id")
return
}
var stored: PendingTransactionEntity?
XCTAssertNoThrow(try { stored = try pendingRepository.find(by: id) }())
guard stored != nil else {
XCTFail("failed to store tx")
return
}
let oldEncodeAttempts = stored!.encodeAttempts
let oldSubmitAttempts = stored!.submitAttempts
stored!.encodeAttempts += 1
stored!.submitAttempts += 5
XCTAssertNoThrow(try pendingRepository.update(stored!))
guard let updatedTransaction = try? pendingRepository.find(by: stored!.id!) else {
XCTFail("failed to retrieve updated transaction with id: \(stored!.id!)")
return
}
XCTAssertEqual(updatedTransaction.encodeAttempts, oldEncodeAttempts + 1)
XCTAssertEqual(updatedTransaction.submitAttempts, oldSubmitAttempts + 5)
XCTAssertEqual(updatedTransaction.recipient, stored!.recipient)
}
func createAndStoreMockedTransaction(with value: Zatoshi = Zatoshi(1000)) -> PendingTransactionEntity {
var transaction = mockTransaction(with: value)
var id: Int?
XCTAssertNoThrow(try { id = try pendingRepository.create(transaction) }())
transaction.id = Int(id ?? -1)
return transaction
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
_ = try! pendingRepository.getAll()
}
}
private func mockTransaction(with value: Zatoshi = Zatoshi(1000)) -> PendingTransactionEntity {
PendingTransaction(value: value, recipient: .address(.sapling(recipient)), memo: .empty(), account: 0)
}
}

View File

@ -175,6 +175,25 @@ class SynchronizerOfflineTests: XCTestCase {
}
}
func testSendToWithTransparentAddressThrowsError() async throws {
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: true)
do {
_ = try await testCoordinator.synchronizer.sendToAddress(
spendingKey: testCoordinator.spendingKey,
zatoshi: Zatoshi(1),
toAddress: .transparent(data.transparentAddress),
memo: try .init(string: "hello")
)
XCTFail("Send to address should fail.")
} catch {
if let error = error as? ZcashError, case .synchronizerSendMemoToTransparentAddress = error {
} else {
XCTFail("Send to address failed with unexpected error: \(error)")
}
}
}
func testShieldFundsCalledWithoutPrepareThrowsError() async throws {
let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false)
@ -233,7 +252,7 @@ class SynchronizerOfflineTests: XCTestCase {
await fulfillment(of: [expectation], timeout: 1)
}
func testURLsParsingFailsInInitialierPrepareThenThrowsError() async throws {
func testURLsParsingFailsInInitializerPrepareThenThrowsError() async throws {
let validFileURL = URL(fileURLWithPath: "/some/valid/path/to.file")
let validDirectoryURL = URL(fileURLWithPath: "/some/valid/path/to/directory")
let invalidPathURL = URL(string: "https://whatever")!
@ -241,8 +260,7 @@ class SynchronizerOfflineTests: XCTestCase {
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: invalidPathURL,
dataDbURL: invalidPathURL,
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: .testnet),
spendParamsURL: validFileURL,
@ -274,7 +292,7 @@ class SynchronizerOfflineTests: XCTestCase {
}
}
func testURLsParsingFailsInInitialierWipeThenThrowsError() async throws {
func testURLsParsingFailsInInitializerWipeThenThrowsError() async throws {
let validFileURL = URL(fileURLWithPath: "/some/valid/path/to.file")
let validDirectoryURL = URL(fileURLWithPath: "/some/valid/path/to/directory")
let invalidPathURL = URL(string: "https://whatever")!
@ -282,8 +300,7 @@ class SynchronizerOfflineTests: XCTestCase {
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
pendingDbURL: invalidPathURL,
dataDbURL: invalidPathURL,
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: .testnet),
spendParamsURL: validFileURL,

View File

@ -134,18 +134,22 @@ class TransactionRepositoryTests: XCTestCase {
}
func testFindMemoForReceivedTransaction() async throws {
let transaction = ZcashTransaction.Received(
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1,
expiryHeight: nil,
fromAccount: 0,
fee: nil,
id: 5,
index: 0,
memoCount: 0,
hasChange: false,
memoCount: 1,
minedHeight: 0,
noteCount: 0,
raw: nil,
rawID: nil,
value: Zatoshi.zero
rawID: Data(),
receivedNoteCount: 1,
sentNoteCount: 0,
value: .zero,
isExpiredUmined: false
)
let memos = try await self.transactionRepository.findMemos(for: transaction)
@ -154,18 +158,22 @@ class TransactionRepositoryTests: XCTestCase {
}
func testFindMemoForSentTransaction() async throws {
let transaction = ZcashTransaction.Sent(
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1,
expiryHeight: nil,
fromAccount: 0,
fee: nil,
id: 9,
index: 0,
memoCount: 0,
minedHeight: 0,
noteCount: 0,
hasChange: false,
memoCount: 1,
minedHeight: nil,
raw: nil,
rawID: nil,
value: Zatoshi.zero
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 2,
value: .zero,
isExpiredUmined: false
)
let memos = try await self.transactionRepository.findMemos(for: transaction)

View File

@ -44,7 +44,6 @@ class WalletTests: XCTestCase {
cacheDbURL: nil,
fsBlockDbRoot: testTempDirectory,
dataDbURL: try __dataDbURL(),
pendingDbURL: try TestDbBuilder.pendingTransactionsDbURL(),
endpoint: LightWalletEndpointBuilder.default,
network: network,
spendParamsURL: try __spendParamsURL(),

View File

@ -0,0 +1,87 @@
//
// ZcashTransactionStateTests.swift
//
//
// Created by Francisco Gindre on 5/3/23.
//
import XCTest
import TestUtils
@testable import ZcashLightClientKit
final class ZcashTransactionStateTests: XCTestCase {
func testExpiredUnminedState() throws {
let currentHeight = 1010
XCTAssertEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: nil,
expiredUnmined: true
),
.expired
)
}
func testConfirmationsBelowStaleConstantIsPending() {
let currentHeight = 1010
XCTAssertEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: currentHeight,
expiredUnmined: false
),
.pending
)
XCTAssertEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: currentHeight - ZcashSDK.defaultStaleTolerance + 1,
expiredUnmined: false
),
.pending
)
XCTAssertNotEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: currentHeight - ZcashSDK.defaultStaleTolerance,
expiredUnmined: false
),
.pending
)
}
func testMinedHeightAboveOrEqualToStaleConstantIsConfirmed() {
let currentHeight = 1010
XCTAssertEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: currentHeight - ZcashSDK.defaultStaleTolerance,
expiredUnmined: false
),
.confirmed
)
XCTAssertEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: currentHeight - ZcashSDK.defaultStaleTolerance - 1,
expiredUnmined: false
),
.confirmed
)
XCTAssertNotEqual(
ZcashTransaction.Overview.State(
currentHeight: currentHeight,
minedHeight: currentHeight - ZcashSDK.defaultStaleTolerance + 1,
expiredUnmined: false
),
.confirmed
)
}
}

View File

@ -72,7 +72,6 @@ class SynchronizerTests: XCTestCase {
cacheDbURL: nil,
fsBlockDbRoot: databases.fsCacheDbRoot,
dataDbURL: databases.dataDB,
pendingDbURL: databases.pendingDB,
endpoint: endpoint,
network: network,
spendParamsURL: try __spendParamsURL(),
@ -84,7 +83,6 @@ class SynchronizerTests: XCTestCase {
try? FileManager.default.removeItem(at: databases.fsCacheDbRoot)
try? FileManager.default.removeItem(at: databases.dataDB)
try? FileManager.default.removeItem(at: databases.pendingDB)
synchronizer = SDKSynchronizer(initializer: initializer)

View File

@ -195,10 +195,6 @@ enum DarksideWalletDConstants: NetworkConstants {
ZcashSDKMainnetConstants.defaultCacheDbName
}
static var defaultPendingDbName: String {
ZcashSDKMainnetConstants.defaultPendingDbName
}
static var defaultDbNamePrefix: String {
ZcashSDKMainnetConstants.defaultDbNamePrefix
}

View File

@ -26,8 +26,8 @@ class MockTransactionRepository {
var network: ZcashNetwork
var transactions: [ZcashTransaction.Overview] = []
var receivedTransactions: [ZcashTransaction.Received] = []
var sentTransactions: [ZcashTransaction.Sent] = []
var receivedTransactions: [ZcashTransaction.Overview] = []
var sentTransactions: [ZcashTransaction.Overview] = []
var allCount: Int {
receivedCount + sentCount
@ -73,6 +73,14 @@ extension MockTransactionRepository.Kind: Equatable {}
// MARK: - TransactionRepository
extension MockTransactionRepository: TransactionRepository {
func getTransactionOutputs(for id: Int) async throws -> [ZcashLightClientKit.ZcashTransaction.Output] {
[]
}
func findPendingTransactions(latestHeight: ZcashLightClientKit.BlockHeight, offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
[]
}
func getRecipients(for id: Int) -> [TransactionRecipient] {
[]
}
@ -203,25 +211,17 @@ extension MockTransactionRepository: TransactionRepository {
throw MockTransactionRepositoryError.notImplemented
}
func findReceived(offset: Int, limit: Int) throws -> [ZcashTransaction.Received] {
func findReceived(offset: Int, limit: Int) throws -> [ZcashTransaction.Overview] {
throw MockTransactionRepositoryError.notImplemented
}
func findSent(offset: Int, limit: Int) throws -> [ZcashTransaction.Sent] {
func findSent(offset: Int, limit: Int) throws -> [ZcashTransaction.Overview] {
throw MockTransactionRepositoryError.notImplemented
}
func findMemos(for transaction: ZcashLightClientKit.ZcashTransaction.Overview) throws -> [ZcashLightClientKit.Memo] {
throw MockTransactionRepositoryError.notImplemented
}
func findMemos(for receivedTransaction: ZcashLightClientKit.ZcashTransaction.Received) throws -> [ZcashLightClientKit.Memo] {
throw MockTransactionRepositoryError.notImplemented
}
func findMemos(for sentTransaction: ZcashLightClientKit.ZcashTransaction.Sent) throws -> [ZcashLightClientKit.Memo] {
throw MockTransactionRepositoryError.notImplemented
}
}
extension Array {

View File

@ -18,7 +18,7 @@ extension SDKSynchronizer {
self.init(
status: .unprepared,
initializer: initializer,
transactionManager: OutboundTransactionManagerBuilder.build(initializer: initializer),
transactionEncoder: WalletTransactionEncoder(initializer: initializer),
transactionRepository: initializer.transactionRepository,
utxoRepository: UTXORepositoryBuilder.build(initializer: initializer),
blockProcessor: CompactBlockProcessor(

View File

@ -36,22 +36,22 @@ class SynchronizerMock: Synchronizer {
get { return underlyingMetrics }
}
var underlyingMetrics: SDKMetrics!
var pendingTransactions: [PendingTransactionEntity] {
var pendingTransactions: [ZcashTransaction.Overview] {
get async { return underlyingPendingTransactions }
}
var underlyingPendingTransactions: [PendingTransactionEntity] = []
var clearedTransactions: [ZcashTransaction.Overview] {
get async { return underlyingClearedTransactions }
var underlyingPendingTransactions: [ZcashTransaction.Overview] = []
var transactions: [ZcashTransaction.Overview] {
get async { return underlyingTransactions }
}
var underlyingClearedTransactions: [ZcashTransaction.Overview] = []
var sentTransactions: [ZcashTransaction.Sent] {
var underlyingTransactions: [ZcashTransaction.Overview] = []
var sentTransactions: [ZcashTransaction.Overview] {
get async { return underlyingSentTransactions }
}
var underlyingSentTransactions: [ZcashTransaction.Sent] = []
var receivedTransactions: [ZcashTransaction.Received] {
var underlyingSentTransactions: [ZcashTransaction.Overview] = []
var receivedTransactions: [ZcashTransaction.Overview] {
get async { return underlyingReceivedTransactions }
}
var underlyingReceivedTransactions: [ZcashTransaction.Received] = []
var underlyingReceivedTransactions: [ZcashTransaction.Overview] = []
// MARK: - prepare
@ -189,10 +189,10 @@ class SynchronizerMock: Synchronizer {
return sendToAddressSpendingKeyZatoshiToAddressMemoCallsCount > 0
}
var sendToAddressSpendingKeyZatoshiToAddressMemoReceivedArguments: (spendingKey: UnifiedSpendingKey, zatoshi: Zatoshi, toAddress: Recipient, memo: Memo?)?
var sendToAddressSpendingKeyZatoshiToAddressMemoReturnValue: PendingTransactionEntity!
var sendToAddressSpendingKeyZatoshiToAddressMemoClosure: ((UnifiedSpendingKey, Zatoshi, Recipient, Memo?) async throws -> PendingTransactionEntity)?
var sendToAddressSpendingKeyZatoshiToAddressMemoReturnValue: ZcashTransaction.Overview!
var sendToAddressSpendingKeyZatoshiToAddressMemoClosure: ((UnifiedSpendingKey, Zatoshi, Recipient, Memo?) async throws -> ZcashTransaction.Overview)?
func sendToAddress(spendingKey: UnifiedSpendingKey, zatoshi: Zatoshi, toAddress: Recipient, memo: Memo?) async throws -> PendingTransactionEntity {
func sendToAddress(spendingKey: UnifiedSpendingKey, zatoshi: Zatoshi, toAddress: Recipient, memo: Memo?) async throws -> ZcashTransaction.Overview {
if let error = sendToAddressSpendingKeyZatoshiToAddressMemoThrowableError {
throw error
}
@ -213,10 +213,10 @@ class SynchronizerMock: Synchronizer {
return shieldFundsSpendingKeyMemoShieldingThresholdCallsCount > 0
}
var shieldFundsSpendingKeyMemoShieldingThresholdReceivedArguments: (spendingKey: UnifiedSpendingKey, memo: Memo, shieldingThreshold: Zatoshi)?
var shieldFundsSpendingKeyMemoShieldingThresholdReturnValue: PendingTransactionEntity!
var shieldFundsSpendingKeyMemoShieldingThresholdClosure: ((UnifiedSpendingKey, Memo, Zatoshi) async throws -> PendingTransactionEntity)?
var shieldFundsSpendingKeyMemoShieldingThresholdReturnValue: ZcashTransaction.Overview!
var shieldFundsSpendingKeyMemoShieldingThresholdClosure: ((UnifiedSpendingKey, Memo, Zatoshi) async throws -> ZcashTransaction.Overview)?
func shieldFunds(spendingKey: UnifiedSpendingKey, memo: Memo, shieldingThreshold: Zatoshi) async throws -> PendingTransactionEntity {
func shieldFunds(spendingKey: UnifiedSpendingKey, memo: Memo, shieldingThreshold: Zatoshi) async throws -> ZcashTransaction.Overview {
if let error = shieldFundsSpendingKeyMemoShieldingThresholdThrowableError {
throw error
}
@ -229,26 +229,6 @@ class SynchronizerMock: Synchronizer {
}
}
// MARK: - cancelSpend
var cancelSpendTransactionCallsCount = 0
var cancelSpendTransactionCalled: Bool {
return cancelSpendTransactionCallsCount > 0
}
var cancelSpendTransactionReceivedTransaction: PendingTransactionEntity?
var cancelSpendTransactionReturnValue: Bool!
var cancelSpendTransactionClosure: ((PendingTransactionEntity) async -> Bool)?
func cancelSpend(transaction: PendingTransactionEntity) async -> Bool {
cancelSpendTransactionCallsCount += 1
cancelSpendTransactionReceivedTransaction = transaction
if let closure = cancelSpendTransactionClosure {
return await closure(transaction)
} else {
return cancelSpendTransactionReturnValue
}
}
// MARK: - paginatedTransactions
var paginatedTransactionsOfCallsCount = 0
@ -293,54 +273,6 @@ class SynchronizerMock: Synchronizer {
}
}
// MARK: - getMemos
var getMemosForReceivedTransactionThrowableError: Error?
var getMemosForReceivedTransactionCallsCount = 0
var getMemosForReceivedTransactionCalled: Bool {
return getMemosForReceivedTransactionCallsCount > 0
}
var getMemosForReceivedTransactionReceivedReceivedTransaction: ZcashTransaction.Received?
var getMemosForReceivedTransactionReturnValue: [Memo]!
var getMemosForReceivedTransactionClosure: ((ZcashTransaction.Received) async throws -> [Memo])?
func getMemos(for receivedTransaction: ZcashTransaction.Received) async throws -> [Memo] {
if let error = getMemosForReceivedTransactionThrowableError {
throw error
}
getMemosForReceivedTransactionCallsCount += 1
getMemosForReceivedTransactionReceivedReceivedTransaction = receivedTransaction
if let closure = getMemosForReceivedTransactionClosure {
return try await closure(receivedTransaction)
} else {
return getMemosForReceivedTransactionReturnValue
}
}
// MARK: - getMemos
var getMemosForSentTransactionThrowableError: Error?
var getMemosForSentTransactionCallsCount = 0
var getMemosForSentTransactionCalled: Bool {
return getMemosForSentTransactionCallsCount > 0
}
var getMemosForSentTransactionReceivedSentTransaction: ZcashTransaction.Sent?
var getMemosForSentTransactionReturnValue: [Memo]!
var getMemosForSentTransactionClosure: ((ZcashTransaction.Sent) async throws -> [Memo])?
func getMemos(for sentTransaction: ZcashTransaction.Sent) async throws -> [Memo] {
if let error = getMemosForSentTransactionThrowableError {
throw error
}
getMemosForSentTransactionCallsCount += 1
getMemosForSentTransactionReceivedSentTransaction = sentTransaction
if let closure = getMemosForSentTransactionClosure {
return try await closure(sentTransaction)
} else {
return getMemosForSentTransactionReturnValue
}
}
// MARK: - getRecipients
var getRecipientsForClearedTransactionCallsCount = 0
@ -361,47 +293,69 @@ class SynchronizerMock: Synchronizer {
}
}
// MARK: - getRecipients
// MARK: - getTransactionOutputs
var getRecipientsForSentTransactionCallsCount = 0
var getRecipientsForSentTransactionCalled: Bool {
return getRecipientsForSentTransactionCallsCount > 0
var getTransactionOutputsForTransactionCallsCount = 0
var getTransactionOutputsForTransactionCalled: Bool {
return getTransactionOutputsForTransactionCallsCount > 0
}
var getRecipientsForSentTransactionReceivedTransaction: ZcashTransaction.Sent?
var getRecipientsForSentTransactionReturnValue: [TransactionRecipient]!
var getRecipientsForSentTransactionClosure: ((ZcashTransaction.Sent) async -> [TransactionRecipient])?
var getTransactionOutputsForTransactionReceivedTransaction: ZcashTransaction.Overview?
var getTransactionOutputsForTransactionReturnValue: [ZcashTransaction.Output]!
var getTransactionOutputsForTransactionClosure: ((ZcashTransaction.Overview) async -> [ZcashTransaction.Output])?
func getRecipients(for transaction: ZcashTransaction.Sent) async -> [TransactionRecipient] {
getRecipientsForSentTransactionCallsCount += 1
getRecipientsForSentTransactionReceivedTransaction = transaction
if let closure = getRecipientsForSentTransactionClosure {
func getTransactionOutputs(for transaction: ZcashTransaction.Overview) async -> [ZcashTransaction.Output] {
getTransactionOutputsForTransactionCallsCount += 1
getTransactionOutputsForTransactionReceivedTransaction = transaction
if let closure = getTransactionOutputsForTransactionClosure {
return await closure(transaction)
} else {
return getRecipientsForSentTransactionReturnValue
return getTransactionOutputsForTransactionReturnValue
}
}
// MARK: - allConfirmedTransactions
// MARK: - allTransactions
var allConfirmedTransactionsFromLimitThrowableError: Error?
var allConfirmedTransactionsFromLimitCallsCount = 0
var allConfirmedTransactionsFromLimitCalled: Bool {
return allConfirmedTransactionsFromLimitCallsCount > 0
var allTransactionsFromLimitThrowableError: Error?
var allTransactionsFromLimitCallsCount = 0
var allTransactionsFromLimitCalled: Bool {
return allTransactionsFromLimitCallsCount > 0
}
var allConfirmedTransactionsFromLimitReceivedArguments: (transaction: ZcashTransaction.Overview, limit: Int)?
var allConfirmedTransactionsFromLimitReturnValue: [ZcashTransaction.Overview]!
var allConfirmedTransactionsFromLimitClosure: ((ZcashTransaction.Overview, Int) async throws -> [ZcashTransaction.Overview])?
var allTransactionsFromLimitReceivedArguments: (transaction: ZcashTransaction.Overview, limit: Int)?
var allTransactionsFromLimitReturnValue: [ZcashTransaction.Overview]!
var allTransactionsFromLimitClosure: ((ZcashTransaction.Overview, Int) async throws -> [ZcashTransaction.Overview])?
func allConfirmedTransactions(from transaction: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview] {
if let error = allConfirmedTransactionsFromLimitThrowableError {
func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview] {
if let error = allTransactionsFromLimitThrowableError {
throw error
}
allConfirmedTransactionsFromLimitCallsCount += 1
allConfirmedTransactionsFromLimitReceivedArguments = (transaction: transaction, limit: limit)
if let closure = allConfirmedTransactionsFromLimitClosure {
allTransactionsFromLimitCallsCount += 1
allTransactionsFromLimitReceivedArguments = (transaction: transaction, limit: limit)
if let closure = allTransactionsFromLimitClosure {
return try await closure(transaction, limit)
} else {
return allConfirmedTransactionsFromLimitReturnValue
return allTransactionsFromLimitReturnValue
}
}
// MARK: - allPendingTransactions
var allPendingTransactionsThrowableError: Error?
var allPendingTransactionsCallsCount = 0
var allPendingTransactionsCalled: Bool {
return allPendingTransactionsCallsCount > 0
}
var allPendingTransactionsReturnValue: [ZcashTransaction.Overview]!
var allPendingTransactionsClosure: (() async throws -> [ZcashTransaction.Overview])?
func allPendingTransactions() async throws -> [ZcashTransaction.Overview] {
if let error = allPendingTransactionsThrowableError {
throw error
}
allPendingTransactionsCallsCount += 1
if let closure = allPendingTransactionsClosure {
return try await closure()
} else {
return allPendingTransactionsReturnValue
}
}

View File

@ -59,25 +59,71 @@ class TestCoordinator {
network: ZcashNetwork,
callPrepareInConstructor: Bool = true,
endpoint: LightWalletEndpoint = TestCoordinator.defaultEndpoint,
syncSessionIDGenerator: SyncSessionIDGenerator = UniqueSyncSessionIDGenerator()
syncSessionIDGenerator: SyncSessionIDGenerator = UniqueSyncSessionIDGenerator(),
dbTracingClosure: ((String) -> Void)? = nil
) async throws {
await InternalSyncProgress(alias: alias, storage: UserDefaults.standard, logger: logger).rewind(to: 0)
let databases = TemporaryDbBuilder.build()
self.databases = databases
let initializer = Initializer(
cacheDbURL: nil,
let urls = Initializer.URLs(
fsBlockDbRoot: databases.fsCacheDbRoot,
dataDbURL: databases.dataDB,
pendingDbURL: databases.pendingDB,
endpoint: endpoint,
network: network,
spendParamsURL: try __spendParamsURL(),
outputParamsURL: try __outputParamsURL(),
outputParamsURL: try __outputParamsURL()
)
let (updatedURLs, parsingError) = Initializer.tryToUpdateURLs(with: alias, urls: urls)
let backend = ZcashRustBackend(
dbData: updatedURLs.dataDbURL,
fsBlockDbRoot: updatedURLs.fsBlockDbRoot,
spendParamsPath: updatedURLs.spendParamsURL,
outputParamsPath: updatedURLs.outputParamsURL,
networkType: network.networkType
)
let transactionRepository = TransactionSQLDAO(
dbProvider: SimpleConnectionProvider(path: updatedURLs.dataDbURL.absoluteString),
traceClosure: dbTracingClosure
)
let accountRepository = AccountRepositoryBuilder.build(
dataDbURL: updatedURLs.dataDbURL,
readOnly: true,
caching: true,
logger: logger
)
let fsBlockRepository = FSCompactBlockRepository(
fsBlockDbRoot: updatedURLs.fsBlockDbRoot,
metadataStore: .live(
fsBlockDbRoot: updatedURLs.fsBlockDbRoot,
rustBackend: backend,
logger: logger
),
blockDescriptor: .live,
contentProvider: DirectoryListingProviders.defaultSorted,
logger: logger
)
let service = Initializer.makeLightWalletServiceFactory(endpoint: endpoint).make()
let initializer = Initializer(
rustBackend: backend,
network: network,
cacheDbURL: nil,
urls: updatedURLs,
endpoint: endpoint,
service: service,
repository: transactionRepository,
accountRepository: accountRepository,
storage: fsBlockRepository,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
alias: alias,
loggingPolicy: .default(.debug)
urlsParsingError: parsingError,
logger: OSLogger(logLevel: .debug)
)
let derivationTool = DerivationTool(networkType: network.networkType)
@ -243,7 +289,6 @@ extension TestCoordinator {
struct TemporaryTestDatabases {
var fsCacheDbRoot: URL
var dataDB: URL
var pendingDB: URL
}
enum TemporaryDbBuilder {
@ -253,8 +298,7 @@ enum TemporaryDbBuilder {
return TemporaryTestDatabases(
fsCacheDbRoot: tempUrl.appendingPathComponent("fs_cache_\(timestamp)"),
dataDB: tempUrl.appendingPathComponent("data_db_\(timestamp).db"),
pendingDB: tempUrl.appendingPathComponent("pending_db_\(timestamp).db")
dataDB: tempUrl.appendingPathComponent("data_db_\(timestamp).db")
)
}
}

View File

@ -42,10 +42,6 @@ enum TestDbBuilder {
case generalError
}
static func pendingTransactionsDbURL() throws -> URL {
try __documentsDirectory().appendingPathComponent("pending.db")
}
static func prePopulatedDataDbURL() -> URL? {
Bundle.module.url(forResource: "test_data", withExtension: "db")
}
@ -68,7 +64,7 @@ enum TestDbBuilder {
switch initResult {
case .success: return provider
case .seedRequired:
throw ZcashError.dbMigrationGenericFailure("Seed value required to initialize the wallet database")
throw ZcashError.compactBlockProcessorDataDbInitFailed("Seed value required to initialize the wallet database")
}
}
@ -83,16 +79,6 @@ enum TestDbBuilder {
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 }
return SentNotesSQLDAO(dbProvider: provider)
}
static func receivedNotesRepository(rustBackend: ZcashRustBackendWelding) async throws -> ReceivedNoteRepository? {
guard let provider = try await prepopulatedDataDbProvider(rustBackend: rustBackend) else { return nil }
return ReceivedNotesSQLDAO(dbProvider: provider)
}
static func seed(db: CompactBlockRepository, with blockRange: CompactBlockRange) async throws {
guard let blocks = StubBlockCreator.createBlockRange(blockRange) else {

View File

@ -16,7 +16,6 @@ class TestsData {
cacheDbURL: nil,
fsBlockDbRoot: URL(fileURLWithPath: "/"),
dataDbURL: URL(fileURLWithPath: "/"),
pendingDbURL: URL(fileURLWithPath: "/"),
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: networkType),
spendParamsURL: URL(fileURLWithPath: "/"),
@ -34,7 +33,23 @@ class TestsData {
)
let transparentAddress = TransparentAddress(validatedEncoding: "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz")
lazy var pendingTransactionEntity = {
PendingTransaction(value: Zatoshi(10), recipient: .address(.transparent(transparentAddress)), memo: .empty(), account: 0)
ZcashTransaction.Overview(
accountId: 0,
blockTime: nil,
expiryHeight: nil,
fee: Zatoshi(1000),
id: 0,
index: nil,
hasChange: true,
memoCount: 0,
minedHeight: nil,
raw: nil,
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 0,
value: Zatoshi(10),
isExpiredUmined: false
)
}()
let clearedTransaction = {
@ -58,34 +73,42 @@ class TestsData {
}()
let sentTransaction = {
ZcashTransaction.Sent(
ZcashTransaction.Overview(
accountId: 0,
blockTime: 1,
expiryHeight: nil,
fromAccount: 0,
fee: Zatoshi(10000),
id: 9,
index: 0,
hasChange: true,
memoCount: 0,
minedHeight: 0,
noteCount: 0,
raw: nil,
rawID: nil,
value: Zatoshi.zero
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 2,
value: .zero,
isExpiredUmined: false
)
}()
let receivedTransaction = {
ZcashTransaction.Received(
ZcashTransaction.Overview(
accountId: 0,
blockTime: 1,
expiryHeight: nil,
fromAccount: 0,
fee: nil,
id: 9,
index: 0,
hasChange: true,
memoCount: 0,
minedHeight: 0,
noteCount: 0,
raw: nil,
rawID: nil,
value: Zatoshi.zero
rawID: Data(),
receivedNoteCount: 0,
sentNoteCount: 2,
value: .zero,
isExpiredUmined: false
)
}()

View File

@ -1,6 +1,6 @@
# Development Process
the `master` branch is considered a Development-Stable branch. This means that every PR merged to it compiles, tests pass and is considered to be _deployable_. However, SDK clients **MUST NOT** import the SDK as a dependency by pointing to `master` unless they are purposely doing so for development or feature previewing.
the `main` branch is considered a Development-Stable branch. This means that every PR merged to it compiles, tests pass and is considered to be _deployable_. However, SDK clients **MUST NOT** import the SDK as a dependency by pointing to `main` unless they are purposely doing so for development or feature previewing.
Even though the SDK is currently in **beta** and developers should always use the dependencies released through published tags.