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:
parent
0324d9ace5
commit
f5e7c027af
67
CHANGELOG.md
67
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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_"
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
|
||||
struct EncodedTransaction {
|
||||
let transactionId: Data
|
||||
let raw: Data?
|
||||
let raw: Data
|
||||
}
|
||||
|
||||
extension EncodedTransaction: Hashable {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = []
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }))
|
||||
/*
|
||||
There’s 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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
/*
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -195,10 +195,6 @@ enum DarksideWalletDConstants: NetworkConstants {
|
|||
ZcashSDKMainnetConstants.defaultCacheDbName
|
||||
}
|
||||
|
||||
static var defaultPendingDbName: String {
|
||||
ZcashSDKMainnetConstants.defaultPendingDbName
|
||||
}
|
||||
|
||||
static var defaultDbNamePrefix: String {
|
||||
ZcashSDKMainnetConstants.defaultDbNamePrefix
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Binary file not shown.
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}()
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue