Merge pull request #1291 from zcash/release-2.0.0

Post-Release 2.0.0 merge to `main`
This commit is contained in:
Kris Nuttycombe 2023-09-25 16:54:13 -06:00 committed by GitHub
commit 494a060469
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 350 additions and 255 deletions

View File

@ -4,7 +4,7 @@ All notable changes to this library will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# 2.0.0-rc.1 - 2023-09-08
# 2.0.0 - 2023-09-25
## Notable Changes
@ -13,7 +13,8 @@ synchronization algorithm.
## Changed
Updated to `zcash-light-client-ffi 0.4.0-rc.1`
Updated dependencies:
- `zcash-light-client-ffi 0.4.0`
`CompactBlockProcessor` now processes compact blocks from the lightwalletd server with Spend-before-Sync algorithm (i.e. non-linear order). This feature shortens the time after which a wallet's spendable balance can be used.

View File

@ -86,8 +86,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "33a20e650c33f6d72d822d558333f2085effa3dc",
"version" : "2.5.0"
"revision" : "60f13f60c4d093691934dc6cfdf5f508ada1f894",
"version" : "2.6.0"
}
},
{
@ -95,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "32e8d724467f8fe623624570367e3d50c5638e46",
"version" : "1.5.2"
"revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed",
"version" : "1.5.3"
}
},
{
@ -104,8 +104,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "cf281631ff10ec6111f2761052aa81896a83a007",
"version" : "2.58.0"
"revision" : "3db5c4aeee8100d2db6f1eaf3864afdad5dc68fd",
"version" : "2.59.0"
}
},
{
@ -113,8 +113,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "0e0d0aab665ff1a0659ce75ac003081f2b1c8997",
"version" : "1.19.0"
"revision" : "fb70a0f5e984f23be48b11b4f1909f3bee016178",
"version" : "1.19.1"
}
},
{
@ -122,8 +122,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "6d021a48483dbb273a9be43f65234bdc9185b364",
"version" : "1.26.0"
"revision" : "a8ccf13fa62775277a5d56844878c828bbb3be1a",
"version" : "1.27.0"
}
},
{
@ -131,8 +131,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "e866a626e105042a6a72a870c88b4c531ba05f83",
"version" : "2.24.0"
"revision" : "320bd978cceb8e88c125dcbb774943a92f6286e9",
"version" : "2.25.0"
}
},
{
@ -140,8 +140,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "41f4098903878418537020075a4d8a6e20a0b182",
"version" : "1.17.0"
"revision" : "e7403c35ca6bb539a7ca353b91cc2d8ec0362d58",
"version" : "1.19.0"
}
},
{
@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "0af9125c4eae12a4973fb66574c53a54962a9e1e",
"version" : "1.21.0"
"revision" : "cf62cdaea48b77f1a631e5cb3aeda6047c2cba1d",
"version" : "1.23.0"
}
},
{
@ -158,8 +158,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "8607dc26a637697e53e0be1fb09b81cba9d8475a",
"version" : "0.4.0-rc.2"
"revision" : "9bc5877ef6302e877922f79ebead52e50bce94fd",
"version" : "0.4.0"
}
}
],

View File

@ -81,7 +81,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options: NotificationBubble.sucessOptions(
animation: NotificationBubble.Animation.fade(duration: 1)
),
attributedText: NSAttributedString(string: "Transaction \(String(describing: transaction.id))mined!"),
attributedText: NSAttributedString(string: "Transaction \(String(describing: transaction.rawID))mined!"),
handleTap: {}
)
}

View File

@ -37,8 +37,10 @@ class TransactionsDataSource: NSObject {
case .cleared:
let rawTransactions = await synchronizer.transactions
for transaction in rawTransactions {
let memos = try await synchronizer.getMemos(for: transaction)
transactions.append(TransactionDetailModel(transaction: transaction, memos: memos))
if transaction.minedHeight != nil {
let memos = try await synchronizer.getMemos(for: transaction)
transactions.append(TransactionDetailModel(transaction: transaction, memos: memos))
}
}
case .received:
let rawTransactions = await synchronizer.receivedTransactions

View File

@ -316,6 +316,9 @@ extension SDKSynchronizer {
case .unprepared:
return "Unprepared 😅"
case .stopped:
return "Stopped"
case .error(ZcashError.synchronizerDisconnected):
return "disconnected 💔"

View File

@ -64,7 +64,7 @@ class SyncBlocksListViewController: UIViewController {
loggerProxy.debug("Processing synchronizer with alias \(synchronizer.alias.description) \(index)")
switch syncStatus {
case .unprepared, .upToDate, .error(ZcashError.synchronizerDisconnected), .error:
case .unprepared, .upToDate, .error(ZcashError.synchronizerDisconnected), .error, .stopped:
do {
if syncStatus == .unprepared {
_ = try! await synchronizer.prepare(
@ -145,7 +145,7 @@ extension SyncBlocksListViewController: UITableViewDataSource {
let image: UIImage?
switch synchronizerStatus {
case .unprepared, .upToDate, .error(ZcashError.synchronizerDisconnected), .error:
case .unprepared, .upToDate, .error(ZcashError.synchronizerDisconnected), .error, .stopped:
image = UIImage(systemName: "play.circle")
case .syncing:
image = UIImage(systemName: "stop.circle")
@ -176,6 +176,8 @@ extension SyncStatus {
return "Up to Date 😎"
case .unprepared:
return "Unprepared"
case .stopped:
return "Stopped"
case .error(ZcashError.synchronizerDisconnected):
return "Disconnected"
case let .error(error):

View File

@ -93,7 +93,7 @@ class SyncBlocksViewController: UIViewController {
metricLabel.text = currentMetricName + report.debugDescription
}
case .upToDate:
case .upToDate, .stopped:
accumulateMetrics()
summaryLabel.text = "enhancement: \(accumulatedMetrics.debugDescription)"
overallSummary()
@ -205,7 +205,7 @@ class SyncBlocksViewController: UIViewController {
switch state {
case .syncing:
return "Pause"
case .unprepared:
case .unprepared, .stopped:
return "Start"
case .upToDate:
return "Chill!"
@ -222,6 +222,8 @@ class SyncBlocksViewController: UIViewController {
return "Up to Date 😎"
case .unprepared:
return "Unprepared"
case .stopped:
return "Stopped"
case .error(ZcashError.synchronizerDisconnected):
return "Disconnected"
case .error:

View File

@ -10,6 +10,10 @@ import UIKit
import ZcashLightClientKit
final class TransactionDetailModel {
// FIXME: This enumeration does not represent a sensible set of potential transaction states.
// A transaction may be both sent from and received by the same wallet, and in either
// case this designation is orthogonal with respect to whether the transaction is
// in a pending, mined, or expired state.
enum Transaction {
case sent(ZcashTransaction.Overview)
case received(ZcashTransaction.Overview)
@ -62,7 +66,11 @@ final class TransactionDetailModel {
}
init(transaction: ZcashTransaction.Overview, memos: [Memo]) {
self.transaction = .cleared(transaction)
if transaction.minedHeight == nil {
self.transaction = .pending(transaction)
} else {
self.transaction = .cleared(transaction)
}
self.id = transaction.rawID
self.minedHeight = transaction.minedHeight
self.expiryHeight = transaction.expiryHeight

View File

@ -1,21 +1,12 @@
{
"pins" : [
{
"identity" : "csqlite",
"kind" : "remoteSourceControl",
"location" : "https://github.com/stephencelis/CSQLite.git",
"state" : {
"revision" : "9106e983d5e3d5149ee35281ec089484b0def018",
"version" : "0.0.3"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-swift.git",
"state" : {
"revision" : "a20cac0cad4e0da457de687c45cb55aee9a45e19",
"version" : "1.14.1"
"revision" : "84bac657e9930d26e9124bac082f26586dc2d209",
"version" : "1.19.1"
}
},
{
@ -32,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "ff3d2212b6b093db7f177d0855adbc4ef9c5f036",
"version" : "1.0.3"
"revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10",
"version" : "1.1.0"
}
},
{
@ -50,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "32e8d724467f8fe623624570367e3d50c5638e46",
"version" : "1.5.2"
"revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed",
"version" : "1.5.3"
}
},
{
@ -59,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "45167b8006448c79dda4b7bd604e07a034c15c49",
"version" : "2.48.0"
"revision" : "3db5c4aeee8100d2db6f1eaf3864afdad5dc68fd",
"version" : "2.59.0"
}
},
{
@ -68,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "0e0d0aab665ff1a0659ce75ac003081f2b1c8997",
"version" : "1.19.0"
"revision" : "fb70a0f5e984f23be48b11b4f1909f3bee016178",
"version" : "1.19.1"
}
},
{
@ -77,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "38feec96bcd929028939107684073554bf01abeb",
"version" : "1.25.2"
"revision" : "a8ccf13fa62775277a5d56844878c828bbb3be1a",
"version" : "1.27.0"
}
},
{
@ -86,8 +77,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-ssl.git",
"state" : {
"revision" : "4fb7ead803e38949eb1d6fabb849206a72c580f3",
"version" : "2.23.0"
"revision" : "320bd978cceb8e88c125dcbb774943a92f6286e9",
"version" : "2.25.0"
}
},
{
@ -95,8 +86,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-transport-services.git",
"state" : {
"revision" : "c0d9a144cfaec8d3d596aadde3039286a266c15c",
"version" : "1.15.0"
"revision" : "e7403c35ca6bb539a7ca353b91cc2d8ec0362d58",
"version" : "1.19.0"
}
},
{
@ -104,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "0af9125c4eae12a4973fb66574c53a54962a9e1e",
"version" : "1.21.0"
"revision" : "cf62cdaea48b77f1a631e5cb3aeda6047c2cba1d",
"version" : "1.23.0"
}
},
{
@ -113,8 +104,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "8607dc26a637697e53e0be1fb09b81cba9d8475a",
"version" : "0.4.0-rc.2"
"revision" : "9bc5877ef6302e877922f79ebead52e50bce94fd",
"version" : "0.4.0"
}
}
],

View File

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

View File

@ -11,12 +11,14 @@ final class ScanAction {
let configProvider: CompactBlockProcessor.ConfigProvider
let blockScanner: BlockScanner
let rustBackend: ZcashRustBackendWelding
let latestBlocksDataProvider: LatestBlocksDataProvider
let logger: Logger
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
self.configProvider = configProvider
blockScanner = container.resolve(BlockScanner.self)
rustBackend = container.resolve(ZcashRustBackendWelding.self)
latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
logger = container.resolve(Logger.self)
}
@ -54,7 +56,8 @@ extension ScanAction: Action {
let processedHeight = await context.processedHeight
let incrementedprocessedHeight = processedHeight + BlockHeight(increment)
await context.update(processedHeight: incrementedprocessedHeight)
await self?.latestBlocksDataProvider.updateScannedData()
// report scan progress only if it's available
if let scanProgress = try? await self?.rustBackend.getScanProgress() {
let progress = try scanProgress.progress()

View File

@ -11,12 +11,14 @@ final class UpdateChainTipAction {
let rustBackend: ZcashRustBackendWelding
let downloader: BlockDownloader
let service: LightWalletService
let latestBlocksDataProvider: LatestBlocksDataProvider
let logger: Logger
init(container: DIContainer) {
service = container.resolve(LightWalletService.self)
downloader = container.resolve(BlockDownloader.self)
rustBackend = container.resolve(ZcashRustBackendWelding.self)
latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
logger = container.resolve(Logger.self)
}
@ -26,6 +28,7 @@ final class UpdateChainTipAction {
logger.info("Latest block height is \(latestBlockHeight)")
try await rustBackend.updateChainTip(height: Int32(latestBlockHeight))
await context.update(lastChainTipUpdateTime: time)
await latestBlocksDataProvider.update(latestBlockHeight)
}
}

View File

@ -46,7 +46,7 @@ public struct EnhancementProgress: Equatable {
return
lhs.totalTransactions == rhs.totalTransactions &&
lhs.enhancedTransactions == rhs.enhancedTransactions &&
lhs.lastFoundTransaction?.id == rhs.lastFoundTransaction?.id &&
lhs.lastFoundTransaction?.rawID == rhs.lastFoundTransaction?.rawID &&
lhs.range == rhs.range
}
}

View File

@ -49,8 +49,7 @@ extension BlockScannerImpl: BlockScanner {
let previousScannedHeight = lastScannedHeight
let startHeight = previousScannedHeight + 1
// TODO: [#576] remove this arbitrary batch size https://github.com/zcash/ZcashLightClientKit/issues/576
let batchSize = scanBatchSize(startScanHeight: startHeight, network: config.networkType)
let batchSize = UInt32(config.scanningBatchSize)
let scanStartTime = Date()
do {
@ -87,17 +86,4 @@ extension BlockScannerImpl: BlockScanner {
return lastScannedHeight
}
private func scanBatchSize(startScanHeight height: BlockHeight, network: NetworkType) -> UInt32 {
assert(config.scanningBatchSize > 0, "ZcashSDK.DefaultScanningBatch must be larger than 0!")
guard network == .mainnet else { return UInt32(config.scanningBatchSize) }
if height > 1_650_000 {
// librustzcash thread saturation at a number of blocks
// that contains 100 * num_cores Sapling outputs.
return UInt32(max(ProcessInfo().activeProcessorCount, 10))
}
return UInt32(config.scanningBatchSize)
}
}

View File

@ -8,6 +8,22 @@
import Foundation
import SQLite
extension Connection {
public func scalarLocked<V: Value>(_ query: ScalarQuery<V?>) throws -> V.ValueType? {
globalDBLock.lock()
defer { globalDBLock.unlock() }
return try scalar(query)
}
public func scalarLocked<V: Value>(_ query: ScalarQuery<V>) throws -> V {
globalDBLock.lock()
defer { globalDBLock.unlock() }
return try scalar(query)
}
}
class TransactionSQLDAO: TransactionRepository {
enum NotesTableStructure {
static let transactionID = Expression<Int>("tx")
@ -41,7 +57,7 @@ class TransactionSQLDAO: TransactionRepository {
func countAll() async throws -> Int {
do {
return try connection().scalar(transactionsView.count)
return try connection().scalarLocked(transactionsView.count)
} catch {
throw ZcashError.transactionRepositoryCountAll(error)
}
@ -49,20 +65,12 @@ class TransactionSQLDAO: TransactionRepository {
func countUnmined() async throws -> Int {
do {
return try connection().scalar(transactionsView.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
return try connection().scalarLocked(transactionsView.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
} catch {
throw ZcashError.transactionRepositoryCountUnmined(error)
}
}
func find(id: Int) async throws -> ZcashTransaction.Overview {
let query = transactionsView
.filter(ZcashTransaction.Overview.Column.id == id)
.limit(1)
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
}
func find(rawID: Data) async throws -> ZcashTransaction.Overview {
let query = transactionsView
.filter(ZcashTransaction.Overview.Column.rawID == Blob(bytes: rawID.bytes))
@ -73,7 +81,7 @@ class TransactionSQLDAO: TransactionRepository {
func find(offset: Int, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview] {
let query = transactionsView
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.filterQueryFor(kind: kind)
.limit(limit, offset: offset)
@ -82,7 +90,7 @@ class TransactionSQLDAO: TransactionRepository {
func find(in range: CompactBlockRange, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview] {
let query = transactionsView
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.filter(
ZcashTransaction.Overview.Column.minedHeight >= BlockHeight(range.lowerBound) &&
ZcashTransaction.Overview.Column.minedHeight <= BlockHeight(range.upperBound)
@ -95,17 +103,18 @@ class TransactionSQLDAO: TransactionRepository {
func find(from transaction: ZcashTransaction.Overview, limit: Int, kind: TransactionKind) async throws -> [ZcashTransaction.Overview] {
guard
let transactionIndex = transaction.index,
let transactionBlockTime = transaction.blockTime
let transactionBlockHeight = transaction.minedHeight
else { throw ZcashError.transactionRepositoryTransactionMissingRequiredFields }
let transactionIndex = transaction.index ?? Int.max
let query = transactionsView
.order(
(ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc, ZcashTransaction.Overview.Column.id.desc
)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.filter(
Int64(transactionBlockTime) > ZcashTransaction.Overview.Column.blockTime
&& transactionIndex > ZcashTransaction.Overview.Column.index
transactionBlockHeight > ZcashTransaction.Overview.Column.minedHeight
|| (
transactionBlockHeight == ZcashTransaction.Overview.Column.minedHeight &&
transactionIndex > (ZcashTransaction.Overview.Column.index ?? -1)
)
)
.filterQueryFor(kind: kind)
.limit(limit)
@ -116,7 +125,7 @@ class TransactionSQLDAO: TransactionRepository {
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)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.limit(limit, offset: offset)
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
@ -125,7 +134,7 @@ class TransactionSQLDAO: TransactionRepository {
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)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.limit(limit, offset: offset)
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
@ -134,7 +143,7 @@ class TransactionSQLDAO: TransactionRepository {
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)
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.limit(limit, offset: offset)
return try execute(query) { try ZcashTransaction.Overview(row: $0) }
@ -142,22 +151,22 @@ class TransactionSQLDAO: TransactionRepository {
func findMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo] {
do {
return try await getTransactionOutputs(for: transaction.id)
return try await getTransactionOutputs(for: transaction.rawID)
.compactMap { $0.memo }
} catch {
throw ZcashError.transactionRepositoryFindMemos(error)
}
}
func getTransactionOutputs(for id: Int) async throws -> [ZcashTransaction.Output] {
func getTransactionOutputs(for rawID: Data) async throws -> [ZcashTransaction.Output] {
let query = self.txOutputsView
.filter(ZcashTransaction.Output.Column.idTx == id)
.filter(ZcashTransaction.Output.Column.rawID == Blob(bytes: rawID.bytes))
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 }
func getRecipients(for rawID: Data) async throws -> [TransactionRecipient] {
try await getTransactionOutputs(for: rawID).map { $0.recipient }
}
private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> Entity {
@ -167,11 +176,14 @@ class TransactionSQLDAO: TransactionRepository {
}
private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> [Entity] {
globalDBLock.lock()
defer { globalDBLock.unlock() }
do {
let entities = try connection()
.prepare(query)
.map(createEntity)
return entities
} catch {
if let error = error as? ZcashError {

View File

@ -109,6 +109,9 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
)
"""
do {
globalDBLock.lock()
defer { globalDBLock.unlock() }
try dbProvider.connection().run(stringStatement)
} catch {
throw ZcashError.unspentTransactionOutputDAOCreateTable(error)
@ -118,8 +121,11 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
/// - Throws: `unspentTransactionOutputDAOStore` if sqlite query fails.
func store(utxos: [UnspentTransactionOutputEntity]) async throws {
do {
globalDBLock.lock()
defer { globalDBLock.unlock() }
let db = try dbProvider.connection()
try dbProvider.connection().transaction {
try db.transaction {
for utxo in utxos.map({ $0 as? UTXO ?? $0.asUTXO() }) {
try db.run(table.insert(utxo))
}
@ -132,6 +138,9 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
/// - Throws: `unspentTransactionOutputDAOClearAll` if sqlite query fails.
func clearAll(address: String?) async throws {
do {
globalDBLock.lock()
defer { globalDBLock.unlock() }
if let tAddr = address {
try dbProvider.connection().run(table.filter(TableColumns.address == tAddr).delete())
} else {
@ -178,16 +187,16 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
/// - Throws: `unspentTransactionOutputDAOBalance` if sqlite query fails.
func balance(address: String, latestHeight: BlockHeight) async throws -> WalletBalance {
do {
let verified = try dbProvider.connection().scalar(
let verified = try dbProvider.connection().scalarLocked(
table.select(TableColumns.valueZat.sum)
.filter(TableColumns.address == address)
.filter(TableColumns.height <= latestHeight - ZcashSDK.defaultStaleTolerance)
) ?? 0
let total = try dbProvider.connection().scalar(
let total = try dbProvider.connection().scalarLocked(
table.select(TableColumns.valueZat.sum)
.filter(TableColumns.address == address)
) ?? 0
return WalletBalance(
verified: Zatoshi(Int64(verified)),
total: Zatoshi(Int64(total))

View File

@ -66,6 +66,9 @@ class AccountSQDAO: AccountRepository {
/// - `accountDAOGetAll` if sqlite query fetching account data failed.
func getAll() throws -> [AccountEntity] {
do {
globalDBLock.lock()
defer { globalDBLock.unlock() }
return try dbProvider.connection()
.prepare(table)
.map { row -> DbAccount in
@ -90,6 +93,9 @@ class AccountSQDAO: AccountRepository {
func findBy(account: Int) throws -> AccountEntity? {
let query = table.filter(TableColums.account == account).limit(1)
do {
globalDBLock.lock()
defer { globalDBLock.unlock() }
return try dbProvider.connection()
.prepare(query)
.map {
@ -119,6 +125,9 @@ class AccountSQDAO: AccountRepository {
let updatedRows: Int
do {
globalDBLock.lock()
defer { globalDBLock.unlock() }
updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
} catch {
throw ZcashError.accountDAOUpdate(error)

View File

@ -25,9 +25,9 @@ public enum ZcashTransaction {
init(
currentHeight: BlockHeight,
minedHeight: BlockHeight?,
expiredUnmined: Bool
expiredUnmined: Bool?
) {
guard !expiredUnmined else {
guard let expiredUnmined, !expiredUnmined else {
self = .expired
return
}
@ -48,7 +48,6 @@ public enum ZcashTransaction {
public let blockTime: TimeInterval?
public let expiryHeight: BlockHeight?
public let fee: Zatoshi?
public let id: Int
public let index: Int?
public var isSentTransaction: Bool { value < Zatoshi(0) }
public let hasChange: Bool
@ -59,7 +58,7 @@ public enum ZcashTransaction {
public let receivedNoteCount: Int
public let sentNoteCount: Int
public let value: Zatoshi
public let isExpiredUmined: Bool
public let isExpiredUmined: Bool?
}
public struct Output {
@ -79,7 +78,7 @@ public enum ZcashTransaction {
}
}
public let idTx: Int
public let rawID: Data
public let pool: Pool
public let index: Int
public let fromAccount: Int?
@ -99,7 +98,7 @@ public enum ZcashTransaction {
extension ZcashTransaction.Output {
enum Column {
static let idTx = Expression<Int>("id_tx")
static let rawID = Expression<Blob>("txid")
static let pool = Expression<Int>("output_pool")
static let index = Expression<Int>("output_index")
static let toAccount = Expression<Int?>("to_account")
@ -112,7 +111,7 @@ extension ZcashTransaction.Output {
init(row: Row) throws {
do {
idTx = try row.get(Column.idTx)
rawID = Data(blob: try row.get(Column.rawID))
pool = .init(rawValue: try row.get(Column.pool))
index = try row.get(Column.index)
fromAccount = try row.get(Column.fromAccount)
@ -144,7 +143,6 @@ extension ZcashTransaction.Output {
extension ZcashTransaction.Overview {
enum Column {
static let accountId = Expression<Int>("account_id")
static let id = Expression<Int>("id_tx")
static let minedHeight = Expression<BlockHeight?>("mined_height")
static let index = Expression<Int?>("tx_index")
static let rawID = Expression<Blob>("txid")
@ -157,14 +155,13 @@ extension ZcashTransaction.Overview {
static let receivedNoteCount = Expression<Int>("received_note_count")
static let memoCount = Expression<Int>("memo_count")
static let blockTime = Expression<Int64?>("block_time")
static let expiredUnmined = Expression<Bool>("expired_unmined")
static let expiredUnmined = Expression<Bool?>("expired_unmined")
}
init(row: Row) throws {
do {
self.accountId = try row.get(Column.accountId)
self.expiryHeight = try row.get(Column.expiryHeight)
self.id = try row.get(Column.id)
self.index = try row.get(Column.index)
self.hasChange = try row.get(Column.hasChange)
self.memoCount = try row.get(Column.memoCount)

View File

@ -12,8 +12,11 @@ struct ScanProgress: Equatable {
let denominator: UInt64
func progress() throws -> Float {
// division by 0 is not a concern here because `ZcashRustBackend.getScanProgress() -> ScanProgress?`
// handles the 0 and returns nil rather than returning nil progress or 0 value here
guard denominator != 0 else {
// this shouldn't happen but if it does, we need to get notified by clients and work on a fix
throw ZcashError.rustScanProgressOutOfRange("\(numerator)/\(denominator)")
}
let value = Float(numerator) / Float(denominator)
// this shouldn't happen but if it does, we need to get notified by clients and work on a fix

View File

@ -16,6 +16,7 @@ protocol LatestBlocksDataProvider {
func updateScannedData() async
func updateBlockData() async
func updateWalletBirthday(_ walletBirthday: BlockHeight) async
func update(_ latestBlockHeight: BlockHeight) async
}
actor LatestBlocksDataProviderImpl: LatestBlocksDataProvider {
@ -41,13 +42,18 @@ actor LatestBlocksDataProviderImpl: LatestBlocksDataProvider {
}
func updateBlockData() async {
if let newLatestBlockHeight = try? await service.latestBlockHeight(),
latestBlockHeight < newLatestBlockHeight {
latestBlockHeight = newLatestBlockHeight
if let newLatestBlockHeight = try? await service.latestBlockHeight() {
await update(newLatestBlockHeight)
}
}
func updateWalletBirthday(_ walletBirthday: BlockHeight) async {
self.walletBirthday = walletBirthday
}
func update(_ newLatestBlockHeight: BlockHeight) async {
if latestBlockHeight < newLatestBlockHeight {
latestBlockHeight = newLatestBlockHeight
}
}
}

View File

@ -12,7 +12,6 @@ protocol TransactionRepository {
func countAll() async throws -> Int
func countUnmined() async throws -> Int
func isInitialized() async throws -> Bool
func find(id: Int) async throws -> ZcashTransaction.Overview
func find(rawID: Data) async throws -> ZcashTransaction.Overview
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]
@ -21,6 +20,6 @@ protocol TransactionRepository {
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 getRecipients(for id: Int) async throws -> [TransactionRecipient]
func getTransactionOutputs(for id: Int) async throws -> [ZcashTransaction.Output]
func getRecipients(for rawID: Data) async throws -> [TransactionRecipient]
func getTransactionOutputs(for rawID: Data) async throws -> [ZcashTransaction.Output]
}

View File

@ -9,6 +9,8 @@
import Foundation
import libzcashlc
let globalDBLock = NSLock()
actor ZcashRustBackend: ZcashRustBackendWelding {
let minimumConfirmations: UInt32 = 10
let useZIP317Fees = false
@ -56,6 +58,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let treeStateBytes = try treeState.serializedData(partial: false).bytes
globalDBLock.lock()
let ffiBinaryKeyPtr = zcashlc_create_account(
dbData.0,
dbData.1,
@ -66,6 +69,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
rUntil,
networkType.networkId
)
globalDBLock.unlock()
guard let ffiBinaryKeyPtr else {
throw ZcashError.rustCreateAccount(lastErrorMessage(fallback: "`createAccount` failed with unknown error"))
@ -84,6 +88,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
) async throws -> Data {
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
globalDBLock.lock()
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
usk.bytes.withUnsafeBufferPointer { uskPtr in
zcashlc_create_to_address(
@ -105,6 +110,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
}
}
globalDBLock.unlock()
guard success else {
throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
@ -116,6 +122,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws {
globalDBLock.lock()
let result = zcashlc_decrypt_and_store_transaction(
dbData.0,
dbData.1,
@ -124,6 +131,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
UInt32(minedHeight),
networkType.networkId
)
globalDBLock.unlock()
guard result != 0 else {
throw ZcashError.rustDecryptAndStoreTransaction(lastErrorMessage(fallback: "`decryptAndStoreTransaction` failed with unknown error"))
@ -131,7 +139,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func getBalance(account: Int32) async throws -> Int64 {
globalDBLock.lock()
let balance = zcashlc_get_balance(dbData.0, dbData.1, account, networkType.networkId)
globalDBLock.unlock()
guard balance >= 0 else {
throw ZcashError.rustGetBalance(Int(account), lastErrorMessage(fallback: "Error getting total balance from account \(account)"))
@ -141,12 +151,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func getCurrentAddress(account: Int32) async throws -> UnifiedAddress {
globalDBLock.lock()
let addressCStr = zcashlc_get_current_address(
dbData.0,
dbData.1,
account,
networkType.networkId
)
globalDBLock.unlock()
guard let addressCStr else {
throw ZcashError.rustGetCurrentAddress(lastErrorMessage(fallback: "`getCurrentAddress` failed with unknown error"))
@ -162,12 +174,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func getNearestRewindHeight(height: Int32) async throws -> Int32 {
globalDBLock.lock()
let result = zcashlc_get_nearest_rewind_height(
dbData.0,
dbData.1,
height,
networkType.networkId
)
globalDBLock.unlock()
guard result > 0 else {
throw ZcashError.rustGetNearestRewindHeight(lastErrorMessage(fallback: "`getNearestRewindHeight` failed with unknown error"))
@ -177,12 +191,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func getNextAvailableAddress(account: Int32) async throws -> UnifiedAddress {
globalDBLock.lock()
let addressCStr = zcashlc_get_next_available_address(
dbData.0,
dbData.1,
account,
networkType.networkId
)
globalDBLock.unlock()
guard let addressCStr else {
throw ZcashError.rustGetNextAvailableAddress(lastErrorMessage(fallback: "`getNextAvailableAddress` failed with unknown error"))
@ -205,9 +221,11 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
var contiguousMemoBytes = ContiguousArray<UInt8>(MemoBytes.empty().bytes)
var success = false
globalDBLock.lock()
contiguousMemoBytes.withUnsafeMutableBufferPointer { memoBytePtr in
success = zcashlc_get_memo(dbData.0, dbData.1, txId.bytes, outputIndex, memoBytePtr.baseAddress, networkType.networkId)
}
globalDBLock.unlock()
guard success else { return nil }
@ -219,12 +237,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
throw ZcashError.rustGetTransparentBalanceNegativeAccount(Int(account))
}
globalDBLock.lock()
let balance = zcashlc_get_total_transparent_balance_for_account(
dbData.0,
dbData.1,
networkType.networkId,
account
)
globalDBLock.unlock()
guard balance >= 0 else {
throw ZcashError.rustGetTransparentBalance(
@ -237,6 +257,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func getVerifiedBalance(account: Int32) async throws -> Int64 {
globalDBLock.lock()
let balance = zcashlc_get_verified_balance(
dbData.0,
dbData.1,
@ -244,6 +265,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
networkType.networkId,
minimumConfirmations
)
globalDBLock.unlock()
guard balance >= 0 else {
throw ZcashError.rustGetVerifiedBalance(
@ -260,6 +282,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
throw ZcashError.rustGetVerifiedTransparentBalanceNegativeAccount(Int(account))
}
globalDBLock.lock()
let balance = zcashlc_get_verified_transparent_balance_for_account(
dbData.0,
dbData.1,
@ -267,6 +290,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
account,
minimumConfirmations
)
globalDBLock.unlock()
guard balance >= 0 else {
throw ZcashError.rustGetVerifiedTransparentBalance(
@ -279,7 +303,11 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func initDataDb(seed: [UInt8]?) async throws -> DbInitResult {
switch zcashlc_init_data_database(dbData.0, dbData.1, seed, UInt(seed?.count ?? 0), networkType.networkId) {
globalDBLock.lock()
let initResult = zcashlc_init_data_database(dbData.0, dbData.1, seed, UInt(seed?.count ?? 0), networkType.networkId)
globalDBLock.unlock()
switch initResult {
case 0: // ok
return DbInitResult.success
case 1:
@ -290,7 +318,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func initBlockMetadataDb() async throws {
globalDBLock.lock()
let result = zcashlc_init_block_metadata_db(fsBlockDbRoot.0, fsBlockDbRoot.1)
globalDBLock.unlock()
guard result else {
throw ZcashError.rustInitBlockMetadataDb(lastErrorMessage(fallback: "`initBlockMetadataDb` failed with unknown error"))
@ -346,7 +376,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
fsBlocks.initialize(to: meta)
globalDBLock.lock()
let res = zcashlc_write_block_metadata(fsBlockDbRoot.0, fsBlockDbRoot.1, fsBlocks)
globalDBLock.unlock()
guard res else {
throw ZcashError.rustWriteBlocksMetadata(lastErrorMessage(fallback: "`writeBlocksMetadata` failed with unknown error"))
@ -355,7 +387,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func latestCachedBlockHeight() async throws -> BlockHeight {
globalDBLock.lock()
let height = zcashlc_latest_cached_block_height(fsBlockDbRoot.0, fsBlockDbRoot.1)
globalDBLock.unlock()
if height >= 0 {
return BlockHeight(height)
@ -367,12 +401,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func listTransparentReceivers(account: Int32) async throws -> [TransparentAddress] {
globalDBLock.lock()
let encodedKeysPtr = zcashlc_list_transparent_receivers(
dbData.0,
dbData.1,
account,
networkType.networkId
)
globalDBLock.unlock()
guard let encodedKeysPtr else {
throw ZcashError.rustListTransparentReceivers(lastErrorMessage(fallback: "`listTransparentReceivers` failed with unknown error"))
@ -404,6 +440,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
value: Int64,
height: BlockHeight
) async throws {
globalDBLock.lock()
let result = zcashlc_put_utxo(
dbData.0,
dbData.1,
@ -416,6 +453,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
Int32(height),
networkType.networkId
)
globalDBLock.unlock()
guard result else {
throw ZcashError.rustPutUnspentTransparentOutput(lastErrorMessage(fallback: "`putUnspentTransparentOutput` failed with unknown error"))
@ -423,7 +461,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func rewindToHeight(height: Int32) async throws {
globalDBLock.lock()
let result = zcashlc_rewind_to_height(dbData.0, dbData.1, height, networkType.networkId)
globalDBLock.unlock()
guard result else {
throw ZcashError.rustRewindToHeight(height, lastErrorMessage(fallback: "`rewindToHeight` failed with unknown error"))
@ -431,7 +471,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func rewindCacheToHeight(height: Int32) async throws {
globalDBLock.lock()
let result = zcashlc_rewind_fs_block_cache_to_height(fsBlockDbRoot.0, fsBlockDbRoot.1, height)
globalDBLock.unlock()
guard result else {
throw ZcashError.rustRewindCacheToHeight(lastErrorMessage(fallback: "`rewindCacheToHeight` failed with unknown error"))
@ -474,7 +516,10 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let rootsPtr = UnsafeMutablePointer<FfiSubtreeRoots>.allocate(capacity: 1)
defer { ffiSubtreeRootsVec.deallocateElements() }
defer {
ffiSubtreeRootsVec.deallocateElements()
rootsPtr.deallocate()
}
try contiguousFfiRoots.withContiguousMutableStorageIfAvailable { ptr in
var roots = FfiSubtreeRoots()
@ -483,7 +528,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
rootsPtr.initialize(to: roots)
globalDBLock.lock()
let res = zcashlc_put_sapling_subtree_roots(dbData.0, dbData.1, startIndex, rootsPtr, networkType.networkId)
globalDBLock.unlock()
guard res else {
throw ZcashError.rustPutSaplingSubtreeRoots(lastErrorMessage(fallback: "`putSaplingSubtreeRoots` failed with unknown error"))
@ -492,7 +539,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func updateChainTip(height: Int32) async throws {
globalDBLock.lock()
let result = zcashlc_update_chain_tip(dbData.0, dbData.1, height, networkType.networkId)
globalDBLock.unlock()
guard result else {
throw ZcashError.rustUpdateChainTip(lastErrorMessage(fallback: "`updateChainTip` failed with unknown error"))
@ -500,7 +549,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func fullyScannedHeight() async throws -> BlockHeight? {
globalDBLock.lock()
let height = zcashlc_fully_scanned_height(dbData.0, dbData.1, networkType.networkId)
globalDBLock.unlock()
if height >= 0 {
return BlockHeight(height)
@ -512,7 +563,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func maxScannedHeight() async throws -> BlockHeight? {
globalDBLock.lock()
let height = zcashlc_max_scanned_height(dbData.0, dbData.1, networkType.networkId)
globalDBLock.unlock()
if height >= 0 {
return BlockHeight(height)
@ -524,7 +577,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func getScanProgress() async throws -> ScanProgress? {
globalDBLock.lock()
let result = zcashlc_get_scan_progress(dbData.0, dbData.1, networkType.networkId)
globalDBLock.unlock()
if result.denominator == 0 {
switch result.numerator {
@ -539,7 +594,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func suggestScanRanges() async throws -> [ScanRange] {
globalDBLock.lock()
let scanRangesPtr = zcashlc_suggest_scan_ranges(dbData.0, dbData.1, networkType.networkId)
globalDBLock.unlock()
guard let scanRangesPtr else {
throw ZcashError.rustSuggestScanRanges(lastErrorMessage(fallback: "`suggestScanRanges` failed with unknown error"))
@ -567,7 +624,9 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws {
globalDBLock.lock()
let result = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId)
globalDBLock.unlock()
guard result != 0 else {
throw ZcashError.rustScanBlocks(lastErrorMessage(fallback: "`scanBlocks` failed with unknown error"))
@ -581,6 +640,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
) async throws -> Data {
var contiguousTxIdBytes = ContiguousArray<UInt8>([UInt8](repeating: 0x0, count: 32))
globalDBLock.lock()
let success = contiguousTxIdBytes.withUnsafeMutableBufferPointer { txIdBytePtr in
usk.bytes.withUnsafeBufferPointer { uskBuffer in
zcashlc_shield_funds(
@ -601,6 +661,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
}
}
globalDBLock.unlock()
guard success else {
throw ZcashError.rustShieldFunds(lastErrorMessage(fallback: "`shieldFunds` failed with unknown error"))

View File

@ -317,6 +317,9 @@ public enum SyncStatus: Equatable {
/// When set, a UI element may want to turn green.
case upToDate
/// Indicates that this Synchronizer was succesfully stopped via `stop()` method.
case stopped
case error(_ error: Error)
public var isSyncing: Bool {
@ -347,6 +350,7 @@ public enum SyncStatus: Equatable {
switch self {
case .unprepared: return "unprepared"
case .syncing: return "syncing"
case .stopped: return "stopped"
case .upToDate: return "up to date"
case .error: return "error"
}
@ -469,7 +473,7 @@ extension InternalSyncStatus {
case .synced:
return .upToDate
case .stopped:
return .upToDate
return .stopped
case .disconnected:
return .error(ZcashError.synchronizerDisconnected)
case .error(let error):

View File

@ -386,11 +386,11 @@ public class SDKSynchronizer: Synchronizer {
}
public func getRecipients(for transaction: ZcashTransaction.Overview) async -> [TransactionRecipient] {
return (try? await transactionRepository.getRecipients(for: transaction.id)) ?? []
return (try? await transactionRepository.getRecipients(for: transaction.rawID)) ?? []
}
public func getTransactionOutputs(for transaction: ZcashTransaction.Overview) async -> [ZcashTransaction.Output] {
return (try? await transactionRepository.getTransactionOutputs(for: transaction.id)) ?? []
return (try? await transactionRepository.getTransactionOutputs(for: transaction.rawID)) ?? []
}
public func latestHeight() async throws -> BlockHeight {

View File

@ -10,8 +10,8 @@ import Foundation
typealias TransactionEncoderResultBlock = (_ result: Result<EncodedTransaction, Error>) -> Void
public enum TransactionEncoderError: Error {
case notFound(transactionId: Int)
case notEncoded(transactionId: Int)
case notFound(txId: Data)
case notEncoded(txId: Data)
case missingParams
case spendingKeyWrongNetwork
case couldNotExpand(txId: Data)

View File

@ -157,7 +157,7 @@ class WalletTransactionEncoder: TransactionEncoder {
extension ZcashTransaction.Overview {
func encodedTransaction() throws -> EncodedTransaction {
guard let raw else {
throw TransactionEncoderError.notEncoded(transactionId: self.id)
throw TransactionEncoderError.notEncoded(txId: self.rawID)
}
return EncodedTransaction(transactionId: self.rawID, raw: raw)

View File

@ -1148,14 +1148,14 @@ class BalanceTests: ZcashTestCase {
)
)
let expiredPending = try await transactionRepo.find(id: pendingTransaction.id)
let expiredPending = try await transactionRepo.find(rawID: pendingTransaction.rawID)
/*
there no sent transaction displayed
*/
let sentTransactions = try await coordinator.synchronizer.allSentTransactions()
XCTAssertNil(sentTransactions.first(where: { $0.id == pendingTransaction.id }))
XCTAssertNil(sentTransactions.first(where: { $0.rawID == pendingTransaction.rawID }))
/*
Theres a pending transaction that has expired
*/

View File

@ -415,7 +415,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
synchronizer.clearedTransactions() { transactions in
XCTAssertEqual(transactions.count, 1)
XCTAssertEqual(transactions[0].id, self.data.clearedTransaction.id)
XCTAssertEqual(transactions[0].rawID, self.data.clearedTransaction.rawID)
expectation.fulfill()
}
@ -429,7 +429,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
synchronizer.sentTranscations() { transactions in
XCTAssertEqual(transactions.count, 1)
XCTAssertEqual(transactions[0].id, self.data.sentTransaction.id)
XCTAssertEqual(transactions[0].rawID, self.data.sentTransaction.rawID)
expectation.fulfill()
}
@ -443,7 +443,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
synchronizer.receivedTransactions() { transactions in
XCTAssertEqual(transactions.count, 1)
XCTAssertEqual(transactions[0].id, self.data.receivedTransaction.id)
XCTAssertEqual(transactions[0].rawID, self.data.receivedTransaction.rawID)
expectation.fulfill()
}
@ -454,7 +454,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
let memo: Memo = .text(try MemoText("Some message"))
synchronizerMock.getMemosForClearedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(receivedTransaction.rawID, self.data.clearedTransaction.rawID)
return [memo]
}
@ -497,7 +497,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
let expectedRecipient: TransactionRecipient = .address(.transparent(data.transparentAddress))
synchronizerMock.getRecipientsForClearedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(receivedTransaction.rawID, self.data.clearedTransaction.rawID)
return [expectedRecipient]
}
@ -514,7 +514,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
func testAllConfirmedTransactionsSucceed() throws {
synchronizerMock.allTransactionsFromLimitClosure = { receivedTransaction, limit in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(receivedTransaction.rawID, self.data.clearedTransaction.rawID)
XCTAssertEqual(limit, 3)
return [self.data.clearedTransaction]
}
@ -525,7 +525,7 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
switch result {
case let .success(transactions):
XCTAssertEqual(transactions.count, 1)
XCTAssertEqual(transactions[0].id, self.data.clearedTransaction.id)
XCTAssertEqual(transactions[0].rawID, self.data.clearedTransaction.rawID)
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")

View File

@ -390,7 +390,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.id, self.data.pendingTransactionEntity.id)
XCTAssertEqual(value.rawID, self.data.pendingTransactionEntity.rawID)
}
)
.store(in: &cancellables)
@ -444,7 +444,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.map { $0.id }, [self.data.clearedTransaction.id])
XCTAssertEqual(value.map { $0.rawID }, [self.data.clearedTransaction.rawID])
}
)
.store(in: &cancellables)
@ -468,7 +468,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.map { $0.id }, [self.data.sentTransaction.id])
XCTAssertEqual(value.map { $0.rawID }, [self.data.sentTransaction.rawID])
}
)
.store(in: &cancellables)
@ -492,7 +492,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.map { $0.id }, [self.data.receivedTransaction.id])
XCTAssertEqual(value.map { $0.rawID }, [self.data.receivedTransaction.rawID])
}
)
.store(in: &cancellables)
@ -504,7 +504,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
let memo: Memo = .text(try MemoText("Some message"))
synchronizerMock.getMemosForClearedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(receivedTransaction.rawID, self.data.clearedTransaction.rawID)
return [memo]
}
@ -559,7 +559,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
let expectedRecipient = TransactionRecipient.address(.transparent(self.data.transparentAddress))
synchronizerMock.getRecipientsForClearedTransactionClosure = { receivedTransaction in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(receivedTransaction.rawID, self.data.clearedTransaction.rawID)
return [expectedRecipient]
}
@ -586,7 +586,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
func testAllConfirmedTransactionsSucceed() throws {
synchronizerMock.allTransactionsFromLimitClosure = { receivedTransaction, limit in
XCTAssertEqual(receivedTransaction.id, self.data.clearedTransaction.id)
XCTAssertEqual(receivedTransaction.rawID, self.data.clearedTransaction.rawID)
XCTAssertEqual(limit, 3)
return [self.data.clearedTransaction]
}
@ -604,7 +604,7 @@ class CombineSynchronizerOfflineTests: XCTestCase {
}
},
receiveValue: { value in
XCTAssertEqual(value.map { $0.id }, [self.data.clearedTransaction.id])
XCTAssertEqual(value.map { $0.rawID }, [self.data.clearedTransaction.rawID])
}
)
.store(in: &cancellables)

View File

@ -141,18 +141,18 @@ final class EnhanceActionTests: ZcashTestCase {
func testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions() async throws {
let blockEnhancerMock = BlockEnhancerMock()
let rawID = Data(fromHexEncodedString: "90058596ae18adedfd74681aee3812c2a7d3d361934347fb05550c77b677a615")!
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663206,
fee: Zatoshi(0),
id: 2,
index: 1,
hasChange: false,
memoCount: 1,
minedHeight: 663188,
raw: Data(),
rawID: Data(),
rawID: rawID,
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000),
@ -197,19 +197,19 @@ final class EnhanceActionTests: ZcashTestCase {
func testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction() async throws {
let blockEnhancerMock = BlockEnhancerMock()
let rawID = Data(fromHexEncodedString: "90058596ae18adedfd74681aee3812c2a7d3d361934347fb05550c77b677a615")!
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663206,
fee: Zatoshi(0),
id: 2,
index: 1,
hasChange: false,
memoCount: 1,
minedHeight: 663188,
raw: Data(),
rawID: Data(),
rawID: rawID,
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000),
@ -259,18 +259,18 @@ final class EnhanceActionTests: ZcashTestCase {
func testEnhanceAction_EnhancementOfBlocksCalled_usingSmallRange_minedTransaction() async throws {
let blockEnhancerMock = BlockEnhancerMock()
let rawID = Data(fromHexEncodedString: "90058596ae18adedfd74681aee3812c2a7d3d361934347fb05550c77b677a615")!
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1.0,
expiryHeight: 663206,
fee: Zatoshi(0),
id: 2,
index: 1,
hasChange: false,
memoCount: 1,
minedHeight: 663188,
raw: Data(),
rawID: Data(),
rawID: rawID,
receivedNoteCount: 1,
sentNoteCount: 0,
value: Zatoshi(100000),

View File

@ -13,7 +13,7 @@ final class ScanActionTests: ZcashTestCase {
func testScanAction_NextAction() async throws {
let blockScannerMock = BlockScannerMock()
let loggerMock = LoggerMock()
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let scanAction = setupAction(blockScannerMock, loggerMock)
@ -46,7 +46,7 @@ final class ScanActionTests: ZcashTestCase {
func testScanAction_EarlyOutForNoDownloadAndScanRangeSet() async throws {
let blockScannerMock = BlockScannerMock()
let loggerMock = LoggerMock()
let scanAction = setupAction(blockScannerMock, loggerMock)
let syncContext = ActionContextMock.default()
@ -83,7 +83,7 @@ final class ScanActionTests: ZcashTestCase {
func testScanAction_EndRangeProperlySetLowerThanBatchSize() async throws {
let blockScannerMock = BlockScannerMock()
let loggerMock = LoggerMock()
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let scanAction = setupAction(blockScannerMock, loggerMock)
@ -107,7 +107,7 @@ final class ScanActionTests: ZcashTestCase {
func testScanAction_EndRangeProperlySetBatchSize() async throws {
let blockScannerMock = BlockScannerMock()
let loggerMock = LoggerMock()
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let scanAction = setupAction(blockScannerMock, loggerMock)
@ -130,7 +130,8 @@ final class ScanActionTests: ZcashTestCase {
private func setupAction(
_ blockScannerMock: BlockScannerMock,
_ loggerMock: LoggerMock
_ loggerMock: LoggerMock,
_ latestBlocksDataProvider: LatestBlocksDataProvider = LatestBlocksDataProviderMock()
) -> ScanAction {
let rustBackendMock = ZcashRustBackendWeldingMock(
consensusBranchIdForHeightClosure: { height in
@ -142,7 +143,8 @@ final class ScanActionTests: ZcashTestCase {
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in rustBackendMock }
mockContainer.mock(type: BlockScanner.self, isSingleton: true) { _ in blockScannerMock }
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in latestBlocksDataProvider }
let config: CompactBlockProcessor.Configuration = .standard(
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
)

View File

@ -27,11 +27,13 @@ final class UpdateChainTipActionTests: ZcashTestCase {
func testUpdateChainTipAction_UpdateChainTipTimeTriggered() async throws {
let loggerMock = LoggerMock()
let blockDownloaderMock = BlockDownloaderMock()
let latestBlocksDataProvider = LatestBlocksDataProviderMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
blockDownloaderMock.stopDownloadClosure = { }
let updateChainTipAction = await setupAction(loggerMock, blockDownloaderMock)
latestBlocksDataProvider.updateClosure = { _ in }
let updateChainTipAction = await setupAction(loggerMock, blockDownloaderMock, latestBlocksDataProvider)
do {
let context = ActionContextMock.default()
@ -53,11 +55,13 @@ final class UpdateChainTipActionTests: ZcashTestCase {
func testUpdateChainTipAction_UpdateChainTipPrevActionTriggered() async throws {
let loggerMock = LoggerMock()
let blockDownloaderMock = BlockDownloaderMock()
let latestBlocksDataProvider = LatestBlocksDataProviderMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
blockDownloaderMock.stopDownloadClosure = { }
latestBlocksDataProvider.updateClosure = { _ in }
let updateChainTipAction = await setupAction(loggerMock, blockDownloaderMock)
let updateChainTipAction = await setupAction(loggerMock, blockDownloaderMock, latestBlocksDataProvider)
do {
let context = ActionContextMock.default()
@ -104,7 +108,8 @@ final class UpdateChainTipActionTests: ZcashTestCase {
private func setupAction(
_ loggerMock: LoggerMock = LoggerMock(),
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock()
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock(),
_ latestBlocksDataProvider: LatestBlocksDataProvider = LatestBlocksDataProviderMock()
) async -> UpdateChainTipAction {
let config: CompactBlockProcessor.Configuration = .standard(
for: ZcashNetworkBuilder.network(for: underlyingNetworkType), walletBirthday: 0
@ -133,6 +138,7 @@ final class UpdateChainTipActionTests: ZcashTestCase {
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in serviceMock }
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
mockContainer.mock(type: BlockDownloader.self, isSingleton: true) { _ in blockDownloaderMock }
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in latestBlocksDataProvider }
return UpdateChainTipAction(container: mockContainer)
}

View File

@ -14,7 +14,11 @@ class TransactionRepositoryTests: XCTestCase {
override func setUp() async throws {
try await super.setUp()
let rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: Environment.uniqueTestTempDirectory, networkType: .testnet)
let rustBackend = ZcashRustBackend.makeForTests(
dbData: TestDbBuilder.prePopulatedMainnetDataDbURL()!,
fsBlockDbRoot: Environment.uniqueTestTempDirectory,
networkType: .mainnet
)
transactionRepository = try! await TestDbBuilder.transactionRepository(rustBackend: rustBackend)
}
@ -46,17 +50,10 @@ class TransactionRepositoryTests: XCTestCase {
XCTAssertEqual(transactions[2].isSentTransaction, false)
}
func testFindById() async throws {
let transaction = try await self.transactionRepository.find(id: 10)
XCTAssertEqual(transaction.id, 10)
XCTAssertEqual(transaction.minedHeight, 663942)
XCTAssertEqual(transaction.index, 5)
}
func testFindByTxId() async throws {
let id = Data(fromHexEncodedString: "01af48bcc4e9667849a073b8b5c539a0fc19de71aac775377929dc6567a36eff")!
let transaction = try await self.transactionRepository.find(rawID: id)
XCTAssertEqual(transaction.id, 8)
XCTAssertEqual(transaction.rawID, id)
XCTAssertEqual(transaction.minedHeight, 663922)
XCTAssertEqual(transaction.index, 1)
}
@ -94,19 +91,26 @@ class TransactionRepositoryTests: XCTestCase {
XCTAssertEqual(transactions[2].minedHeight, 663956)
}
func testGetTransactionOutputs() async throws {
let rawID = Data(fromHexEncodedString: "08cb5838ffd2c18ce15e7e8c50174940cd9526fff37601986f5480b7ca07e534")!
let outputs = try await self.transactionRepository.getTransactionOutputs(for: rawID)
XCTAssertEqual(outputs.count, 2)
}
func testFindMemoForTransaction() async throws {
let rawID = Data(fromHexEncodedString: "08cb5838ffd2c18ce15e7e8c50174940cd9526fff37601986f5480b7ca07e534")!
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: nil,
expiryHeight: nil,
fee: nil,
id: 9,
index: nil,
hasChange: false,
memoCount: 0,
minedHeight: nil,
raw: nil,
rawID: Data(),
rawID: rawID,
receivedNoteCount: 0,
sentNoteCount: 0,
value: Zatoshi(-1000),
@ -116,7 +120,7 @@ class TransactionRepositoryTests: XCTestCase {
let memos = try await self.transactionRepository.findMemos(for: transaction)
guard memos.count == 1 else {
XCTFail("Expected transaction to have one memo")
XCTFail("Expected transaction to have one memo, found \(memos.count)")
return
}
@ -124,18 +128,18 @@ class TransactionRepositoryTests: XCTestCase {
}
func testFindMemoForReceivedTransaction() async throws {
let rawID = Data(fromHexEncodedString: "1f49cfcfcdebd5cb9085d9ff2efbcda87121dda13f2c791113fcf2e79ba82108")!
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1,
expiryHeight: nil,
fee: nil,
id: 5,
index: 0,
hasChange: false,
memoCount: 1,
minedHeight: 0,
raw: nil,
rawID: Data(),
rawID: rawID,
receivedNoteCount: 1,
sentNoteCount: 0,
value: .zero,
@ -148,18 +152,18 @@ class TransactionRepositoryTests: XCTestCase {
}
func testFindMemoForSentTransaction() async throws {
let rawID = Data(fromHexEncodedString: "08cb5838ffd2c18ce15e7e8c50174940cd9526fff37601986f5480b7ca07e534")!
let transaction = ZcashTransaction.Overview(
accountId: 0,
blockTime: 1,
expiryHeight: nil,
fee: nil,
id: 9,
index: 0,
hasChange: false,
memoCount: 1,
minedHeight: nil,
raw: nil,
rawID: Data(),
rawID: rawID,
receivedNoteCount: 0,
sentNoteCount: 2,
value: .zero,
@ -189,24 +193,25 @@ class TransactionRepositoryTests: XCTestCase {
}
func testFindAllFrom() async throws {
let transaction = try await self.transactionRepository.find(id: 16)
let rawID = Data(fromHexEncodedString: "5d9b91e31a6d3f94844a4c330e727a2d5d0643f6caa6c75573b28aefe859e8d2")!
let transaction = try await self.transactionRepository.find(rawID: rawID)
let transactionsFrom = try await self.transactionRepository.find(from: transaction, limit: Int.max, kind: .all)
XCTAssertEqual(transactionsFrom.count, 8)
XCTAssertEqual(transactionsFrom.count, 15)
transactionsFrom.forEach { preceededTransaction in
guard let preceededTransactionIndex = preceededTransaction.index, let transactionIndex = transaction.index else {
XCTFail("Transactions are missing indexes.")
guard let precedingHeight = preceededTransaction.minedHeight, let transactionHeight = transaction.minedHeight else {
XCTFail("Transactions are missing mined heights.")
return
}
guard let preceededTransactionBlockTime = preceededTransaction.blockTime, let transactionBlockTime = transaction.blockTime else {
guard let precedingBlockTime = preceededTransaction.blockTime, let transactionBlockTime = transaction.blockTime else {
XCTFail("Transactions are missing block time.")
return
}
XCTAssertLessThan(preceededTransactionIndex, transactionIndex)
XCTAssertLessThan(preceededTransactionBlockTime, transactionBlockTime)
XCTAssertLessThanOrEqual(precedingHeight, transactionHeight)
XCTAssertLessThan(precedingBlockTime, transactionBlockTime)
}
}
}

View File

@ -157,4 +157,14 @@ class ZcashRustBackendTests: XCTestCase {
XCTAssertEqual(metadata?.networkType, .mainnet)
XCTAssertEqual(metadata?.addressType, .sapling)
}
func testScanProgressThrowsOnWrongValues() {
// Assert that throws on Zero denominator
XCTAssertThrowsError(try ScanProgress(numerator: 0, denominator: 0).progress())
// Assert that throws on numerator > denominator
XCTAssertThrowsError(try ScanProgress(numerator: 23, denominator: 2).progress())
XCTAssertNoThrow(try ScanProgress(numerator: 3, denominator: 4).progress())
}
}

View File

@ -73,7 +73,7 @@ extension MockTransactionRepository.Kind: Equatable {}
// MARK: - TransactionRepository
extension MockTransactionRepository: TransactionRepository {
func getTransactionOutputs(for id: Int) async throws -> [ZcashLightClientKit.ZcashTransaction.Output] {
func getTransactionOutputs(for rawID: Data) async throws -> [ZcashLightClientKit.ZcashTransaction.Output] {
[]
}
@ -81,7 +81,7 @@ extension MockTransactionRepository: TransactionRepository {
[]
}
func getRecipients(for id: Int) -> [TransactionRecipient] {
func getRecipients(for rawID: Data) -> [TransactionRecipient] {
[]
}
@ -95,10 +95,6 @@ extension MockTransactionRepository: TransactionRepository {
unminedCount
}
func findBy(id: Int) throws -> ZcashTransaction.Overview? {
transactions.first(where: { $0.id == id })
}
func findBy(rawId: Data) throws -> ZcashTransaction.Overview? {
transactions.first(where: { $0.rawID == rawId })
}
@ -139,7 +135,6 @@ extension MockTransactionRepository: TransactionRepository {
blockTime: randomTimeInterval(),
expiryHeight: BlockHeight.max,
fee: Zatoshi(2),
id: index,
index: index,
hasChange: true,
memoCount: 0,
@ -159,7 +154,6 @@ extension MockTransactionRepository: TransactionRepository {
blockTime: randomTimeInterval(),
expiryHeight: BlockHeight.max,
fee: Zatoshi(2),
id: index,
index: index,
hasChange: true,
memoCount: 0,
@ -179,14 +173,6 @@ extension MockTransactionRepository: TransactionRepository {
return Array(txs[offset ..< min(offset + limit, txs.count - offset)])
}
func find(id: Int) throws -> ZcashTransaction.Overview {
guard let transaction = transactions.first(where: { $0.id == id }) else {
throw ZcashError.transactionRepositoryEntityNotFound
}
return transaction
}
func find(rawID: Data) throws -> ZcashTransaction.Overview {
guard let transaction = transactions.first(where: { $0.rawID == rawID }) else {
throw ZcashError.transactionRepositoryEntityNotFound

View File

@ -712,6 +712,21 @@ class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
await updateWalletBirthdayClosure!(walletBirthday)
}
// MARK: - update
var updateCallsCount = 0
var updateCalled: Bool {
return updateCallsCount > 0
}
var updateReceivedLatestBlockHeight: BlockHeight?
var updateClosure: ((BlockHeight) async -> Void)?
func update(_ latestBlockHeight: BlockHeight) async {
updateCallsCount += 1
updateReceivedLatestBlockHeight = latestBlockHeight
await updateClosure!(latestBlockHeight)
}
}
class LightWalletServiceMock: LightWalletService {
@ -1693,30 +1708,6 @@ class TransactionRepositoryMock: TransactionRepository {
// MARK: - find
var findIdThrowableError: Error?
var findIdCallsCount = 0
var findIdCalled: Bool {
return findIdCallsCount > 0
}
var findIdReceivedId: Int?
var findIdReturnValue: ZcashTransaction.Overview!
var findIdClosure: ((Int) async throws -> ZcashTransaction.Overview)?
func find(id: Int) async throws -> ZcashTransaction.Overview {
if let error = findIdThrowableError {
throw error
}
findIdCallsCount += 1
findIdReceivedId = id
if let closure = findIdClosure {
return try await closure(id)
} else {
return findIdReturnValue
}
}
// MARK: - find
var findRawIDThrowableError: Error?
var findRawIDCallsCount = 0
var findRawIDCalled: Bool {
@ -1914,18 +1905,18 @@ class TransactionRepositoryMock: TransactionRepository {
var getRecipientsForCalled: Bool {
return getRecipientsForCallsCount > 0
}
var getRecipientsForReceivedId: Int?
var getRecipientsForReceivedRawID: Data?
var getRecipientsForReturnValue: [TransactionRecipient]!
var getRecipientsForClosure: ((Int) async throws -> [TransactionRecipient])?
var getRecipientsForClosure: ((Data) async throws -> [TransactionRecipient])?
func getRecipients(for id: Int) async throws -> [TransactionRecipient] {
func getRecipients(for rawID: Data) async throws -> [TransactionRecipient] {
if let error = getRecipientsForThrowableError {
throw error
}
getRecipientsForCallsCount += 1
getRecipientsForReceivedId = id
getRecipientsForReceivedRawID = rawID
if let closure = getRecipientsForClosure {
return try await closure(id)
return try await closure(rawID)
} else {
return getRecipientsForReturnValue
}
@ -1938,18 +1929,18 @@ class TransactionRepositoryMock: TransactionRepository {
var getTransactionOutputsForCalled: Bool {
return getTransactionOutputsForCallsCount > 0
}
var getTransactionOutputsForReceivedId: Int?
var getTransactionOutputsForReceivedRawID: Data?
var getTransactionOutputsForReturnValue: [ZcashTransaction.Output]!
var getTransactionOutputsForClosure: ((Int) async throws -> [ZcashTransaction.Output])?
var getTransactionOutputsForClosure: ((Data) async throws -> [ZcashTransaction.Output])?
func getTransactionOutputs(for id: Int) async throws -> [ZcashTransaction.Output] {
func getTransactionOutputs(for rawID: Data) async throws -> [ZcashTransaction.Output] {
if let error = getTransactionOutputsForThrowableError {
throw error
}
getTransactionOutputsForCallsCount += 1
getTransactionOutputsForReceivedId = id
getTransactionOutputsForReceivedRawID = rawID
if let closure = getTransactionOutputsForClosure {
return try await closure(id)
return try await closure(rawID)
} else {
return getTransactionOutputsForReturnValue
}

View File

@ -54,10 +54,8 @@ enum TestDbBuilder {
Bundle.module.url(forResource: "darkside_caches", withExtension: "db")
}
static func prepopulatedDataDbProvider(rustBackend: ZcashRustBackendWelding) async throws -> ConnectionProvider? {
guard let url = prePopulatedMainnetDataDbURL() else { return nil }
let provider = SimpleConnectionProvider(path: url.absoluteString, readonly: true)
static func prepopulatedDataDbProvider(rustBackend: ZcashRustBackend) async throws -> ConnectionProvider? {
let provider = SimpleConnectionProvider(path: (await rustBackend.dbData).0, readonly: true)
let initResult = try await rustBackend.initDataDb(seed: Environment.seedBytes)
@ -68,13 +66,13 @@ enum TestDbBuilder {
}
}
static func transactionRepository(rustBackend: ZcashRustBackendWelding) async throws -> TransactionRepository? {
static func transactionRepository(rustBackend: ZcashRustBackend) async throws -> TransactionRepository? {
guard let provider = try await prepopulatedDataDbProvider(rustBackend: rustBackend) else { return nil }
return TransactionSQLDAO(dbProvider: provider)
}
static func transactionRepository(rustBackend: ZcashRustBackendWelding, withTrace closure: @escaping ((String) -> Void)) async throws -> TransactionRepository? {
static func transactionRepository(rustBackend: ZcashRustBackend, withTrace closure: @escaping ((String) -> Void)) async throws -> TransactionRepository? {
guard let provider = try await prepopulatedDataDbProvider(rustBackend: rustBackend) else { return nil }
return TransactionSQLDAO(dbProvider: provider, traceClosure: closure)

View File

@ -146,7 +146,7 @@ extension ZcashRustBackend {
spendParamsPath: URL = SaplingParamsSourceURL.default.spendParamFileURL,
outputParamsPath: URL = SaplingParamsSourceURL.default.outputParamFileURL,
networkType: NetworkType
) -> ZcashRustBackendWelding {
) -> ZcashRustBackend {
ZcashRustBackend(
dbData: dbData,
fsBlockDbRoot: fsBlockDbRoot,

View File

@ -39,13 +39,12 @@ class TestsData {
blockTime: nil,
expiryHeight: nil,
fee: Zatoshi(1000),
id: 0,
index: nil,
hasChange: true,
memoCount: 0,
minedHeight: nil,
raw: nil,
rawID: Data(),
rawID: Data(repeating: 1, count: 32),
receivedNoteCount: 0,
sentNoteCount: 0,
value: Zatoshi(10),
@ -59,13 +58,12 @@ class TestsData {
blockTime: Date().timeIntervalSince1970,
expiryHeight: 123000,
fee: Zatoshi(10),
id: 333,
index: nil,
hasChange: false,
memoCount: 0,
minedHeight: 120000,
raw: nil,
rawID: Data(),
rawID: Data(repeating: 2, count: 32),
receivedNoteCount: 0,
sentNoteCount: 0,
value: Zatoshi(100),
@ -79,13 +77,12 @@ class TestsData {
blockTime: 1,
expiryHeight: nil,
fee: Zatoshi(10000),
id: 9,
index: 0,
hasChange: true,
memoCount: 0,
minedHeight: 0,
raw: nil,
rawID: Data(),
rawID: Data(repeating: 3, count: 32),
receivedNoteCount: 0,
sentNoteCount: 2,
value: .zero,
@ -99,13 +96,12 @@ class TestsData {
blockTime: 1,
expiryHeight: nil,
fee: nil,
id: 9,
index: 0,
hasChange: true,
memoCount: 0,
minedHeight: 0,
raw: nil,
rawID: Data(),
rawID: Data(repeating: 4, count: 32),
receivedNoteCount: 0,
sentNoteCount: 2,
value: .zero,