Merge pull request #1114 from Chlup/1111_isp_disk_storage

[#1111] Change storage for InternalSyncProgress
This commit is contained in:
Michal Fousek 2023-05-23 18:43:24 +02:00 committed by GitHub
commit ee3d082155
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 644 additions and 257 deletions

View File

@ -1,3 +1,13 @@
# Unreleased
### [#1111] Change how the sync progress is stored inside the SDK
`Initializer` has now a new parameter called `generalStorageURL`. This URL is the location of the directory
where the SDK can store any information it needs. A directory doesn't have to exist. But the SDK must
be able to write to this location after it creates this directory. It is suggested that this directory is
a subdirectory of the `Documents` directory. If this information is stored in `Documents` then the
system itself won't remove these data.
# 0.22.0-beta
## Checkpoints
@ -38,6 +48,16 @@ Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2340000.json
This fixes a memory consumption issue coming from GRPC-Swift.
- [#1019] Memo has trailing garbled text
### [#1111] Change how the sync progress is stored inside the SDK
`Initializer` has now a new parameter called `generalStorageURL`. This URL is the location of the directory
where the SDK can store any information it needs. A directory doesn't have to exist. But the SDK must
be able to write to this location after it creates this directory. It is suggested that this directory is
a subdirectory of the `Documents` directory. If this information is stored in `Documents` then the
system itself won't remove these data.
### [#1019] Memo has trailing garbled text
Changes the way unpadded bytes are turned into a UTF-8 Swift String
without using cString assuming APIs that would overflow memory and
add garbled trailing bytes.

View File

@ -46,6 +46,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
let wallet = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: try! fsBlockDbRootURLHelper(),
generalStorageURL: try! generalStorageURLHelper(),
dataDbURL: try! dataDbURLHelper(),
endpoint: DemoAppConfig.endpoint,
network: kZcashNetwork,
@ -173,6 +174,15 @@ func fsBlockDbRootURLHelper() throws -> URL {
)
}
func generalStorageURLHelper() throws -> URL {
try documentsDirectoryHelper()
.appendingPathComponent(kZcashNetwork.networkType.chainName)
.appendingPathComponent(
"general_storage",
isDirectory: true
)
}
func cacheDbURLHelper() throws -> URL {
try documentsDirectoryHelper()
.appendingPathComponent(

View File

@ -104,6 +104,7 @@ class SyncBlocksListViewController: UIViewController {
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: try! fsBlockDbRootURLHelper(),
generalStorageURL: try! generalStorageURLHelper(),
dataDbURL: try! dataDbURLHelper(),
endpoint: DemoAppConfig.endpoint,
network: kZcashNetwork,

View File

@ -530,7 +530,7 @@ actor CompactBlockProcessor {
/// - Parameter height: height to rewind to. If nil is provided, it will rescan to nearest height (quick rescan)
///
/// - Note: If this is called while sync is in progress then the sync process is stopped first and then rewind is executed.
func rewind(context: AfterSyncHooksManager.RewindContext) async {
func rewind(context: AfterSyncHooksManager.RewindContext) async throws {
logger.debug("Starting rewind")
switch self.state {
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
@ -540,13 +540,13 @@ actor CompactBlockProcessor {
case .stopped, .error, .synced:
logger.debug("Sync doesn't run. Executing rewind.")
await doRewind(context: context)
try await doRewind(context: context)
}
}
private func doRewind(context: AfterSyncHooksManager.RewindContext) async {
private func doRewind(context: AfterSyncHooksManager.RewindContext) async throws {
logger.debug("Executing rewind.")
let lastDownloaded = await internalSyncProgress.latestDownloadedBlockHeight
let lastDownloaded = try await internalSyncProgress.latestDownloadedBlockHeight
let height = Int32(context.height ?? lastDownloaded)
let nearestHeight: Int32
@ -575,7 +575,7 @@ actor CompactBlockProcessor {
return await context.completion(.failure(error))
}
await internalSyncProgress.rewind(to: rewindBlockHeight)
try await internalSyncProgress.rewind(to: rewindBlockHeight)
self.lastChainValidationFailure = nil
await context.completion(.success(rewindBlockHeight))
@ -583,7 +583,7 @@ actor CompactBlockProcessor {
// MARK: Wipe
func wipe(context: AfterSyncHooksManager.WipeContext) async {
func wipe(context: AfterSyncHooksManager.WipeContext) async throws {
logger.debug("Starting wipe")
switch self.state {
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
@ -593,11 +593,11 @@ actor CompactBlockProcessor {
case .stopped, .error, .synced:
logger.debug("Sync doesn't run. Executing wipe.")
await doWipe(context: context)
try await doWipe(context: context)
}
}
private func doWipe(context: AfterSyncHooksManager.WipeContext) async {
private func doWipe(context: AfterSyncHooksManager.WipeContext) async throws {
logger.debug("Executing wipe.")
context.prewipe()
@ -605,7 +605,7 @@ actor CompactBlockProcessor {
do {
try await self.storage.clear()
await internalSyncProgress.rewind(to: 0)
try await internalSyncProgress.rewind(to: 0)
wipeLegacyCacheDbIfNeeded()
@ -637,6 +637,7 @@ actor CompactBlockProcessor {
}
/// Processes new blocks on the given range based on the configuration set for this instance
// swiftlint:disable:next cyclomatic_complexity
func processNewBlocks(ranges: SyncRanges) async {
self.foundBlocks = true
@ -662,7 +663,7 @@ actor CompactBlockProcessor {
// properly and internal state set to the appropriate value
if let newLatestDownloadedHeight = ranges.shouldClearBlockCacheAndUpdateInternalState() {
try await storage.clear()
await internalSyncProgress.set(newLatestDownloadedHeight, .latestDownloadedBlockHeight)
try await internalSyncProgress.set(newLatestDownloadedHeight, .latestDownloadedBlockHeight)
} else {
try await storage.create()
}
@ -735,7 +736,11 @@ actor CompactBlockProcessor {
if Task.isCancelled {
logger.info("Processing cancelled.")
await updateState(.stopped)
await handleAfterSyncHooks()
do {
try await handleAfterSyncHooks()
} catch {
await fail(error)
}
} else {
if case let ZcashError.rustValidateCombinedChainInvalidChain(height) = error {
await validationFailed(at: BlockHeight(height))
@ -748,14 +753,14 @@ actor CompactBlockProcessor {
}
}
private func handleAfterSyncHooks() async {
private func handleAfterSyncHooks() async throws {
let afterSyncHooksManager = self.afterSyncHooksManager
self.afterSyncHooksManager = AfterSyncHooksManager()
if let wipeContext = afterSyncHooksManager.shouldExecuteWipeHook() {
await doWipe(context: wipeContext)
try await doWipe(context: wipeContext)
} else if let rewindContext = afterSyncHooksManager.shouldExecuteRewindHook() {
await doRewind(context: rewindContext)
try await doRewind(context: rewindContext)
} else if afterSyncHooksManager.shouldExecuteAnotherSyncHook() {
logger.debug("Starting new sync.")
await nextBatch()
@ -937,7 +942,8 @@ actor CompactBlockProcessor {
latestBlocksDataProvider: latestBlocksDataProvider,
config: self.config,
rustBackend: self.rustBackend,
internalSyncProgress: internalSyncProgress
internalSyncProgress: internalSyncProgress,
alias: config.alias
)
switch nextState {
case .finishProcessing(let height):
@ -983,7 +989,7 @@ actor CompactBlockProcessor {
do {
try await blockDownloaderService.rewind(to: rewindHeight)
await internalSyncProgress.rewind(to: rewindHeight)
try await internalSyncProgress.rewind(to: rewindHeight)
await send(event: .handledReorg(height, rewindHeight))
@ -1025,7 +1031,7 @@ actor CompactBlockProcessor {
//
// Scanning is done until 10300 so the SDK can be sure that blocks with height below 10300 are not required. So it makes sense to set
// `.latestDownloadedBlockHeight` to `lastScannedHeight`. And sync will work fine in next run.
await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
try await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
try await clearCompactBlockCache()
} catch {
logger.error("`clearCompactBlockCache` failed after error: \(error)")
@ -1218,7 +1224,8 @@ extension CompactBlockProcessor {
latestBlocksDataProvider: latestBlocksDataProvider,
config: config,
rustBackend: rustBackend,
internalSyncProgress: internalSyncProgress
internalSyncProgress: internalSyncProgress,
alias: config.alias
)
} catch {
throw error
@ -1235,7 +1242,8 @@ extension CompactBlockProcessor {
latestBlocksDataProvider: LatestBlocksDataProvider,
config: Configuration,
rustBackend: ZcashRustBackendWelding,
internalSyncProgress: InternalSyncProgress
internalSyncProgress: InternalSyncProgress,
alias: ZcashSynchronizerAlias
) async throws -> CompactBlockProcessor.NextState {
// It should be ok to not create new Task here because this method is already async. But for some reason something not good happens
// when Task is not created here. For example tests start failing. Reason is unknown at this time.
@ -1251,12 +1259,12 @@ extension CompactBlockProcessor {
let latestDownloadHeight = try await downloaderService.lastDownloadedBlockHeight()
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadHeight, alias: alias)
await latestBlocksDataProvider.updateScannedData()
await latestBlocksDataProvider.updateBlockData()
return await internalSyncProgress.computeNextState(
return try await internalSyncProgress.computeNextState(
latestBlockHeight: latestBlocksDataProvider.latestBlockHeight,
latestScannedHeight: latestBlocksDataProvider.latestScannedHeight,
walletBirthday: config.walletBirthday
@ -1321,7 +1329,7 @@ extension CompactBlockProcessor {
// by a previous processing cycle.
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
try await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
}
func wipeLegacyCacheDbIfNeeded() {

View File

@ -100,7 +100,7 @@ actor BlockDownloaderImpl {
throw ZcashError.blockDownloadSyncRangeNotSet
}
let latestDownloadedBlockHeight = await internalSyncProgress.load(.latestDownloadedBlockHeight)
let latestDownloadedBlockHeight = try await internalSyncProgress.latestDownloadedBlockHeight
let downloadFrom = max(syncRange.lowerBound, latestDownloadedBlockHeight + 1)
let downloadTo = min(downloadToHeight, syncRange.upperBound)
@ -220,7 +220,7 @@ actor BlockDownloaderImpl {
if buffer.count >= maxBlockBufferSize {
let finishTime = Date()
try await storage.write(blocks: buffer)
await blocksBufferWritten(buffer)
try await blocksBufferWritten(buffer)
buffer.removeAll(keepingCapacity: true)
pushMetrics(block.height, startTime, finishTime)
@ -235,12 +235,12 @@ actor BlockDownloaderImpl {
}
try await storage.write(blocks: buffer)
await blocksBufferWritten(buffer)
try await blocksBufferWritten(buffer)
}
private func blocksBufferWritten(_ buffer: [ZcashCompactBlock]) async {
private func blocksBufferWritten(_ buffer: [ZcashCompactBlock]) async throws {
guard let lastBlock = buffer.last else { return }
await internalSyncProgress.set(lastBlock.height, .latestDownloadedBlockHeight)
try await internalSyncProgress.set(lastBlock.height, .latestDownloadedBlockHeight)
}
}
@ -282,13 +282,13 @@ extension BlockDownloaderImpl: BlockDownloader {
func waitUntilRequestedBlocksAreDownloaded(in range: CompactBlockRange) async throws {
logger.debug("Waiting until requested blocks are downloaded at \(range)")
var latestDownloadedBlock = await internalSyncProgress.load(.latestDownloadedBlockHeight)
var latestDownloadedBlock = try await internalSyncProgress.latestDownloadedBlockHeight
while latestDownloadedBlock < range.upperBound {
if let error = lastError {
throw error
}
try await Task.sleep(milliseconds: 10)
latestDownloadedBlock = await internalSyncProgress.load(.latestDownloadedBlockHeight)
latestDownloadedBlock = try await internalSyncProgress.latestDownloadedBlockHeight
}
logger.debug("Waiting done. Blocks are downloaded at \(range)")
}

View File

@ -52,7 +52,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
let transactions = try await transactionRepository.find(in: range, limit: Int.max, kind: .all)
guard !transactions.isEmpty else {
await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
try await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
logger.debug("no transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
return nil
}
@ -84,7 +84,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
await didEnhance(progress)
if let minedHeight = confirmedTx.minedHeight {
await internalSyncProgress.set(minedHeight, .latestEnhancedHeight)
try await internalSyncProgress.set(minedHeight, .latestEnhancedHeight)
}
} catch {
retries += 1
@ -112,7 +112,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
throw error
}
await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
try await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
if Task.isCancelled {
logger.debug("Warning: compactBlockEnhancement on range \(range) cancelled")

View File

@ -82,7 +82,7 @@ extension UTXOFetcherImpl: UTXOFetcher {
counter += 1
await didFetch(counter / all)
await internalSyncProgress.set(utxo.height, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(utxo.height, .latestUTXOFetchedHeight)
} catch {
logger.error("failed to put utxo - error: \(error)")
skipped.append(utxo)
@ -103,7 +103,7 @@ extension UTXOFetcherImpl: UTXOFetcher {
let result = (inserted: refreshed, skipped: skipped)
await internalSyncProgress.set(range.upperBound, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(range.upperBound, .latestUTXOFetchedHeight)
if Task.isCancelled {
logger.debug("Warning: fetchUnspentTxOutputs on range \(range) cancelled")

View File

@ -26,16 +26,13 @@ struct SyncRanges: Equatable {
}
protocol InternalSyncProgressStorage {
func bool(forKey defaultName: String) -> Bool
func integer(forKey defaultName: String) -> Int
func set(_ value: Int, forKey defaultName: String)
func set(_ value: Bool, forKey defaultName: String)
@discardableResult
func synchronize() -> Bool
func initialize() async throws
func bool(for key: String) async throws -> Bool
func integer(for key: String) async throws -> Int
func set(_ value: Int, for key: String) async throws
func set(_ value: Bool, for key: String) async throws
}
extension UserDefaults: InternalSyncProgressStorage { }
actor InternalSyncProgress {
enum Key: String, CaseIterable {
case latestDownloadedBlockHeight
@ -56,9 +53,15 @@ actor InternalSyncProgress {
private let storage: InternalSyncProgressStorage
let logger: Logger
var latestDownloadedBlockHeight: BlockHeight { load(.latestDownloadedBlockHeight) }
var latestEnhancedHeight: BlockHeight { load(.latestEnhancedHeight) }
var latestUTXOFetchedHeight: BlockHeight { load(.latestUTXOFetchedHeight) }
var latestDownloadedBlockHeight: BlockHeight {
get async throws { try await load(.latestDownloadedBlockHeight) }
}
var latestEnhancedHeight: BlockHeight {
get async throws { try await load(.latestEnhancedHeight) }
}
var latestUTXOFetchedHeight: BlockHeight {
get async throws { try await load(.latestUTXOFetchedHeight) }
}
init(alias: ZcashSynchronizerAlias, storage: InternalSyncProgressStorage, logger: Logger) {
self.alias = alias
@ -66,19 +69,22 @@ actor InternalSyncProgress {
self.logger = logger
}
func load(_ key: Key) -> BlockHeight {
storage.integer(forKey: key.with(alias))
func initialize() async throws {
try await storage.initialize()
}
func set(_ value: BlockHeight, _ key: Key) {
storage.set(value, forKey: key.with(alias))
storage.synchronize()
func load(_ key: Key) async throws -> BlockHeight {
return try await storage.integer(for: key.with(alias))
}
func rewind(to: BlockHeight) {
Key.allCases.forEach { key in
let finalRewindHeight = min(load(key), to)
self.set(finalRewindHeight, key)
func set(_ value: BlockHeight, _ key: Key) async throws {
try await storage.set(value, for: key.with(alias))
}
func rewind(to: BlockHeight) async throws {
for key in Key.allCases {
let finalRewindHeight = min(try await load(key), to)
try await self.set(finalRewindHeight, key)
}
}
@ -86,13 +92,24 @@ actor InternalSyncProgress {
/// track this. Because of this we have to migrate height of latest downloaded block from cache DB to here.
///
/// - Parameter latestDownloadedBlockHeight: Height of latest downloaded block from cache DB.
func migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB latestDownloadedBlockHeight: BlockHeight) {
let key = "InternalSyncProgressMigrated"
if !storage.bool(forKey: key) {
set(latestDownloadedBlockHeight, .latestDownloadedBlockHeight)
func migrateIfNeeded(
latestDownloadedBlockHeightFromCacheDB latestDownloadedBlockHeight: BlockHeight,
alias: ZcashSynchronizerAlias
) async throws {
// If no latest downloaded height is stored in storage store there latest downloaded height from blocks storage. If there are no blocks
// downloaded then it will be 0 anyway. If there are blocks downloaded real height is stored.
if try await storage.integer(for: Key.latestDownloadedBlockHeight.with(alias)) == 0 {
try await set(latestDownloadedBlockHeight, .latestDownloadedBlockHeight)
}
for key in Key.allCases {
let finalKey = key.with(alias)
let value = UserDefaults.standard.integer(forKey: finalKey)
if value > 0 {
try await storage.set(value, for: finalKey)
}
UserDefaults.standard.set(0, forKey: finalKey)
}
storage.set(true, forKey: key)
storage.synchronize()
}
/// Computes the next state for the sync process. Thanks to this it's possible to interrupt the sync process at any phase and then it can be safely
@ -115,7 +132,10 @@ actor InternalSyncProgress {
latestBlockHeight: BlockHeight,
latestScannedHeight: BlockHeight,
walletBirthday: BlockHeight
) -> CompactBlockProcessor.NextState {
) async throws -> CompactBlockProcessor.NextState {
let latestDownloadedBlockHeight = try await self.latestDownloadedBlockHeight
let latestEnhancedHeight = try await self.latestEnhancedHeight
let latestUTXOFetchedHeight = try await self.latestUTXOFetchedHeight
logger.debug("""
Init numbers:
latestBlockHeight: \(latestBlockHeight)
@ -134,7 +154,7 @@ actor InternalSyncProgress {
latestScannedHeight < latestBlockHeight ||
latestEnhancedHeight < latestEnhancedHeight ||
latestUTXOFetchedHeight < latestBlockHeight {
let ranges = computeSyncRanges(
let ranges = try await computeSyncRanges(
birthday: walletBirthday,
latestBlockHeight: latestBlockHeight,
latestScannedHeight: latestScannedHeight
@ -149,7 +169,11 @@ actor InternalSyncProgress {
birthday: BlockHeight,
latestBlockHeight: BlockHeight,
latestScannedHeight: BlockHeight
) -> SyncRanges {
) async throws -> SyncRanges {
let latestDownloadedBlockHeight = try await self.latestDownloadedBlockHeight
let latestEnhancedHeight = try await self.latestEnhancedHeight
let latestUTXOFetchedHeight = try await self.latestUTXOFetchedHeight
// If there is more downloaded then scanned blocks we have to range for these blocks. The sync process will then start with scanning these
// blocks instead of downloading new ones.
let downloadedButUnscannedRange: CompactBlockRange?

View File

@ -0,0 +1,97 @@
//
// InternalSyncProgressDiskStorage.swift
//
//
// Created by Michal Fousek on 21.05.2023.
//
import Foundation
actor InternalSyncProgressDiskStorage {
private let storageURL: URL
private let logger: Logger
init(storageURL: URL, logger: Logger) {
self.storageURL = storageURL
self.logger = logger
}
private func fileURL(for key: String) -> URL {
return storageURL.appendingPathComponent(key)
}
}
extension InternalSyncProgressDiskStorage: InternalSyncProgressStorage {
/// - If object on the file system at `generalStorageURL` exists and it is directory then do nothing.
/// - If object on the file system at `generalStorageURL` exists and it is file then throw error. Because `generalStorageURL` should be directory.
/// - If object on the file system at `generalStorageURL` doesn't exists then create directory at this URL. And set `isExcludedFromBackup` URL
/// flag to prevent backup of the progress information to system backup.
func initialize() async throws {
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
let exists = fileManager.fileExists(atPath: storageURL.pathExtension, isDirectory: &isDirectory)
if exists && !isDirectory.boolValue {
throw ZcashError.initializerGeneralStorageExistsButIsFile(storageURL)
} else if !exists {
do {
try fileManager.createDirectory(at: storageURL, withIntermediateDirectories: true)
} catch {
throw ZcashError.initializerGeneralStorageCantCreate(storageURL, error)
}
do {
// Prevent from backing up progress information to system backup.
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
var generalStorageURL = storageURL
try generalStorageURL.setResourceValues(resourceValues)
} catch {
throw ZcashError.initializerCantSetNoBackupFlagToGeneralStorageURL(storageURL, error)
}
}
}
func bool(for key: String) async throws -> Bool {
let fileURL = self.fileURL(for: key)
do {
return try Data(contentsOf: fileURL).toBool()
} catch {
if !FileManager.default.fileExists(atPath: fileURL.path) {
return false
} else {
throw ZcashError.ispStorageCantLoad(fileURL, error)
}
}
}
func integer(for key: String) async throws -> Int {
let fileURL = self.fileURL(for: key)
do {
return try Data(contentsOf: fileURL).toInt()
} catch {
if !FileManager.default.fileExists(atPath: fileURL.path) {
return 0
} else {
throw ZcashError.ispStorageCantLoad(fileURL, error)
}
}
}
func set(_ value: Int, for key: String) async throws {
let fileURL = self.fileURL(for: key)
do {
try value.toData().write(to: fileURL, options: [.atomic])
} catch {
throw ZcashError.ispStorageCantWrite(fileURL, error)
}
}
func set(_ value: Bool, for key: String) async throws {
let fileURL = self.fileURL(for: key)
do {
try value.toData().write(to: fileURL, options: [.atomic])
} catch {
throw ZcashError.ispStorageCantWrite(fileURL, error)
}
}
}

View File

@ -11,8 +11,9 @@ error originates. And it can help with debugging.
import Foundation
public enum ZcashError: Equatable, Error {
/// Some error happened that is not handled as `ZcashError`.
/// Use case is to map `any Error`(in the apps for example) to this one so it can be assured only `ZcashError` is floating through apps.
/// Some error happened that is not handled as `ZcashError`. All errors in the SDK are (should be) `ZcashError`.
/// This case is ideally not contructed directly or thrown by any SDK function, rather it's a wrapper for case clients expect ZcashErrot and want to pass it to a function/enum.
/// If this is the case, use `toZcashError()` extension of Error. This helper avoids to end up with Optional handling.
/// ZUNKWN0001
case unknown(_ error: Error)
/// Updating of paths in `Initilizer` according to alias failed.
@ -21,6 +22,15 @@ public enum ZcashError: Equatable, Error {
/// Alias used to create this instance of the `SDKSynchronizer` is already used by other instance.
/// ZINIT0002
case initializerAliasAlreadyInUse(_ alias: ZcashSynchronizerAlias)
/// Object on disk at `generalStorageURL` path exists. But it file not directory.
/// ZINIT0003
case initializerGeneralStorageExistsButIsFile(_ generalStorageURL: URL)
/// Can't create directory at `generalStorageURL` path.
/// ZINIT0004
case initializerGeneralStorageCantCreate(_ generalStorageURL: URL, _ error: Error)
/// Can't set `isExcludedFromBackup` flag to `generalStorageURL`.
/// ZINIT0005
case initializerCantSetNoBackupFlagToGeneralStorageURL(_ generalStorageURL: URL, _ error: Error)
/// Unknown GRPC Service error
/// ZSRVC0001
case serviceUnknownError(_ error: Error)
@ -509,12 +519,21 @@ public enum ZcashError: Equatable, Error {
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
/// ZSYNCO0006
case synchronizerDisconnected
/// `InternalSyncProgressDiskStorage` can't read data from specific file.
/// ZISPDS0001
case ispStorageCantLoad(_ fileURL: URL, _ error: Error)
/// `InternalSyncProgressDiskStorage` can't write data from specific file.
/// ZISPDS0002
case ispStorageCantWrite(_ fileURL: URL, _ error: Error)
public var message: String {
switch self {
case .unknown: return "Some error happened that is not handled as `ZcashError`."
case .unknown: return "Some error happened that is not handled as `ZcashError`. All errors in the SDK are (should be) `ZcashError`."
case .initializerCantUpdateURLWithAlias: return "Updating of paths in `Initilizer` according to alias failed."
case .initializerAliasAlreadyInUse: return "Alias used to create this instance of the `SDKSynchronizer` is already used by other instance."
case .initializerGeneralStorageExistsButIsFile: return "Object on disk at `generalStorageURL` path exists. But it file not directory."
case .initializerGeneralStorageCantCreate: return "Can't create directory at `generalStorageURL` path."
case .initializerCantSetNoBackupFlagToGeneralStorageURL: return "Can't set `isExcludedFromBackup` flag to `generalStorageURL`."
case .serviceUnknownError: return "Unknown GRPC Service error"
case .serviceGetInfoFailed: return "LightWalletService.getInfo failed."
case .serviceLatestBlockFailed: return "LightWalletService.latestBlock failed."
@ -659,6 +678,8 @@ public enum ZcashError: Equatable, Error {
case .synchronizerLatestUTXOsInvalidTAddress: return "LatestUTXOs for the address failed, invalid t-address."
case .synchronizerRewindUnknownArchorHeight: return "Rewind failed, unknown archor height"
case .synchronizerDisconnected: return "Indicates that this Synchronizer is disconnected from its lightwalletd server."
case .ispStorageCantLoad: return "`InternalSyncProgressDiskStorage` can't read data from specific file."
case .ispStorageCantWrite: return "`InternalSyncProgressDiskStorage` can't write data from specific file."
}
}
@ -667,6 +688,9 @@ public enum ZcashError: Equatable, Error {
case .unknown: return .unknown
case .initializerCantUpdateURLWithAlias: return .initializerCantUpdateURLWithAlias
case .initializerAliasAlreadyInUse: return .initializerAliasAlreadyInUse
case .initializerGeneralStorageExistsButIsFile: return .initializerGeneralStorageExistsButIsFile
case .initializerGeneralStorageCantCreate: return .initializerGeneralStorageCantCreate
case .initializerCantSetNoBackupFlagToGeneralStorageURL: return .initializerCantSetNoBackupFlagToGeneralStorageURL
case .serviceUnknownError: return .serviceUnknownError
case .serviceGetInfoFailed: return .serviceGetInfoFailed
case .serviceLatestBlockFailed: return .serviceLatestBlockFailed
@ -811,6 +835,8 @@ public enum ZcashError: Equatable, Error {
case .synchronizerLatestUTXOsInvalidTAddress: return .synchronizerLatestUTXOsInvalidTAddress
case .synchronizerRewindUnknownArchorHeight: return .synchronizerRewindUnknownArchorHeight
case .synchronizerDisconnected: return .synchronizerDisconnected
case .ispStorageCantLoad: return .ispStorageCantLoad
case .ispStorageCantWrite: return .ispStorageCantWrite
}
}

View File

@ -9,12 +9,18 @@ error originates. And it can help with debugging.
*/
public enum ZcashErrorCode: String {
/// Some error happened that is not handled as `ZcashError`.
/// Some error happened that is not handled as `ZcashError`. All errors in the SDK are (should be) `ZcashError`.
case unknown = "ZUNKWN0001"
/// Updating of paths in `Initilizer` according to alias failed.
case initializerCantUpdateURLWithAlias = "ZINIT0001"
/// Alias used to create this instance of the `SDKSynchronizer` is already used by other instance.
case initializerAliasAlreadyInUse = "ZINIT0002"
/// Object on disk at `generalStorageURL` path exists. But it file not directory.
case initializerGeneralStorageExistsButIsFile = "ZINIT0003"
/// Can't create directory at `generalStorageURL` path.
case initializerGeneralStorageCantCreate = "ZINIT0004"
/// Can't set `isExcludedFromBackup` flag to `generalStorageURL`.
case initializerCantSetNoBackupFlagToGeneralStorageURL = "ZINIT0005"
/// Unknown GRPC Service error
case serviceUnknownError = "ZSRVC0001"
/// LightWalletService.getInfo failed.
@ -303,4 +309,8 @@ public enum ZcashErrorCode: String {
case synchronizerRewindUnknownArchorHeight = "ZSYNCO0005"
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
case synchronizerDisconnected = "ZSYNCO0006"
/// `InternalSyncProgressDiskStorage` can't read data from specific file.
case ispStorageCantLoad = "ZISPDS0001"
/// `InternalSyncProgressDiskStorage` can't write data from specific file.
case ispStorageCantWrite = "ZISPDS0002"
}

View File

@ -38,6 +38,15 @@ enum ZcashErrorDefinition {
/// Alias used to create this instance of the `SDKSynchronizer` is already used by other instance.
// sourcery: code="ZINIT0002"
case initializerAliasAlreadyInUse(_ alias: ZcashSynchronizerAlias)
/// Object on disk at `generalStorageURL` path exists. But it file not directory.
// sourcery: code="ZINIT0003"
case initializerGeneralStorageExistsButIsFile(_ generalStorageURL: URL)
/// Can't create directory at `generalStorageURL` path.
// sourcery: code="ZINIT0004"
case initializerGeneralStorageCantCreate(_ generalStorageURL: URL, _ error: Error)
/// Can't set `isExcludedFromBackup` flag to `generalStorageURL`.
// sourcery: code="ZINIT0005"
case initializerCantSetNoBackupFlagToGeneralStorageURL(_ generalStorageURL: URL, _ error: Error)
// MARK: - LightWalletService
@ -593,4 +602,13 @@ enum ZcashErrorDefinition {
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
// sourcery: code="ZSYNCO0006"
case synchronizerDisconnected
// MARK: - InternalSyncProgressDiskStorage
/// `InternalSyncProgressDiskStorage` can't read data from specific file.
// sourcery: code="ZISPDS0001"
case ispStorageCantLoad(_ fileURL: URL, _ error: Error)
/// `InternalSyncProgressDiskStorage` can't write data from specific file.
// sourcery: code="ZISPDS0002"
case ispStorageCantWrite(_ fileURL: URL, _ error: Error)
}

View File

@ -0,0 +1,15 @@
//
// Bool+ToData.swift
//
//
// Created by Michal Fousek on 21.05.2023.
//
import Foundation
extension Bool {
func toData() -> Data {
var value = self
return withUnsafeBytes(of: &value) { Data($0) }
}
}

View File

@ -0,0 +1,18 @@
//
// Data+ToOtherTypes.swift
//
//
// Created by Michal Fousek on 21.05.2023.
//
import Foundation
extension Data {
func toInt() -> Int {
return self.withUnsafeBytes { $0.load(as: Int.self) }
}
func toBool() -> Bool {
return self.withUnsafeBytes { $0.load(as: Bool.self) }
}
}

View File

@ -0,0 +1,15 @@
//
// Int+ToData.swift
//
//
// Created by Michal Fousek on 21.05.2023.
//
import Foundation
extension Int {
func toData() -> Data {
var value = self
return withUnsafeBytes(of: &value) { Data($0) }
}
}

View File

@ -90,6 +90,7 @@ public class Initializer {
struct URLs {
let fsBlockDbRoot: URL
let dataDbURL: URL
let generalStorageURL: URL
let spendParamsURL: URL
let outputParamsURL: URL
}
@ -112,6 +113,7 @@ public class Initializer {
let alias: ZcashSynchronizerAlias
let endpoint: LightWalletEndpoint
let fsBlockDbRoot: URL
let generalStorageURL: URL
let dataDbURL: URL
let spendParamsURL: URL
let outputParamsURL: URL
@ -142,6 +144,10 @@ public class Initializer {
/// - cacheDbURL: previous location of the cacheDb. If you don't know what a cacheDb is and you are adopting this SDK for the first time then
/// just pass `nil` here.
/// - fsBlockDbRoot: location of the compact blocks cache
/// - generalStorageURL: Location of the directory where the SDK can store any information it needs. A directory doesn't have to exist. But the
/// SDK must be able to write to this location after it creates this directory. It is suggested that this directory is
/// a subdirectory of the `Documents` directory. If this information is stored in `Documents` then the system itself won't
/// remove these data.
/// - dataDbURL: Location of the data db
/// - endpoint: the endpoint representing the lightwalletd instance you want to point to
/// - spendParamsURL: location of the spend parameters
@ -151,6 +157,7 @@ public class Initializer {
convenience public init(
cacheDbURL: URL?,
fsBlockDbRoot: URL,
generalStorageURL: URL,
dataDbURL: URL,
endpoint: LightWalletEndpoint,
network: ZcashNetwork,
@ -169,6 +176,7 @@ public class Initializer {
container: container,
cacheDbURL: cacheDbURL,
fsBlockDbRoot: fsBlockDbRoot,
generalStorageURL: generalStorageURL,
dataDbURL: dataDbURL,
endpoint: endpoint,
network: network,
@ -198,6 +206,7 @@ public class Initializer {
container: DIContainer,
cacheDbURL: URL?,
fsBlockDbRoot: URL,
generalStorageURL: URL,
dataDbURL: URL,
endpoint: LightWalletEndpoint,
network: ZcashNetwork,
@ -214,6 +223,7 @@ public class Initializer {
container: container,
cacheDbURL: cacheDbURL,
fsBlockDbRoot: fsBlockDbRoot,
generalStorageURL: generalStorageURL,
dataDbURL: dataDbURL,
endpoint: endpoint,
network: network,
@ -253,6 +263,7 @@ public class Initializer {
self.cacheDbURL = cacheDbURL
self.rustBackend = container.resolve(ZcashRustBackendWelding.self)
self.fsBlockDbRoot = urls.fsBlockDbRoot
self.generalStorageURL = urls.generalStorageURL
self.dataDbURL = urls.dataDbURL
self.endpoint = endpoint
self.spendParamsURL = urls.spendParamsURL
@ -284,6 +295,7 @@ public class Initializer {
container: DIContainer,
cacheDbURL: URL?,
fsBlockDbRoot: URL,
generalStorageURL: URL,
dataDbURL: URL,
endpoint: LightWalletEndpoint,
network: ZcashNetwork,
@ -297,6 +309,7 @@ public class Initializer {
let urls = URLs(
fsBlockDbRoot: fsBlockDbRoot,
dataDbURL: dataDbURL,
generalStorageURL: generalStorageURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL
)
@ -368,10 +381,15 @@ public class Initializer {
return .failure(.initializerCantUpdateURLWithAlias(urls.outputParamsURL))
}
guard let updatedGeneralStorageURL = urls.generalStorageURL.updateLastPathComponent(with: alias) else {
return .failure(.initializerCantUpdateURLWithAlias(urls.generalStorageURL))
}
return .success(
URLs(
fsBlockDbRoot: updatedFsBlockDbRoot,
dataDbURL: updatedDataDbURL,
generalStorageURL: updatedGeneralStorageURL,
spendParamsURL: updatedSpendParamsURL,
outputParamsURL: updateOutputParamsURL
)

View File

@ -91,7 +91,8 @@ enum Dependencies {
container.register(type: InternalSyncProgress.self, isSingleton: true) { di in
let logger = di.resolve(Logger.self)
return InternalSyncProgress(alias: alias, storage: UserDefaults.standard, logger: logger)
let storage = InternalSyncProgressDiskStorage(storageURL: urls.generalStorageURL, logger: logger)
return InternalSyncProgress(alias: alias, storage: storage, logger: logger)
}
}

View File

@ -40,6 +40,7 @@ public class SDKSynchronizer: Synchronizer {
private let transactionEncoder: TransactionEncoder
private let transactionRepository: TransactionRepository
private let utxoRepository: UnspentTransactionOutputRepository
let internalSyncProgress: InternalSyncProgress
private let syncSessionIDGenerator: SyncSessionIDGenerator
private let syncSession: SyncSession
@ -87,6 +88,7 @@ public class SDKSynchronizer: Synchronizer {
self.syncSession = SyncSession(.nullID)
self.syncSessionTicker = syncSessionTicker
self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self)
internalSyncProgress = initializer.container.resolve(InternalSyncProgress.self)
initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in
self?.connectivityStateChanged(oldState: oldState, newState: newState)
@ -137,6 +139,7 @@ public class SDKSynchronizer: Synchronizer {
}
try await utxoRepository.initialise()
try await internalSyncProgress.initialize()
if case .seedRequired = try await self.initializer.initialize(with: seed, viewingKeys: viewingKeys, walletBirthday: walletBirthday) {
return .seedRequired
@ -503,7 +506,11 @@ public class SDKSynchronizer: Synchronizer {
}
)
await blockProcessor.rewind(context: context)
do {
try await blockProcessor.rewind(context: context)
} catch {
subject.send(completion: .failure(error))
}
}
return subject.eraseToAnyPublisher()
}
@ -533,7 +540,11 @@ public class SDKSynchronizer: Synchronizer {
}
)
await blockProcessor.wipe(context: context)
do {
try await blockProcessor.wipe(context: context)
} catch {
subject.send(completion: .failure(error))
}
}
return subject.eraseToAnyPublisher()

View File

@ -672,7 +672,7 @@ class BalanceTests: ZcashTestCase {
return
}
guard let changeOutput = outputs.first(where: { $0.isChange }) else {
guard outputs.first(where: { $0.isChange }) != nil else {
XCTFail("Sent transaction has no change")
return
}

View File

@ -85,13 +85,9 @@ final class InternalStateConsistencyTests: ZcashTestCase {
XCTAssertFalse(isSyncing, "SDKSynchronizer shouldn't be syncing")
XCTAssertEqual(status, .stopped)
let internalSyncState = InternalSyncProgress(
alias: .default,
storage: UserDefaults.standard,
logger: logger
)
let internalSyncState = coordinator.synchronizer.internalSyncProgress
let latestDownloadHeight = await internalSyncState.latestDownloadedBlockHeight
let latestDownloadHeight = try await internalSyncState.latestDownloadedBlockHeight
let latestScanHeight = try await coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight()
let dbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedDarksideCacheDb()!)
try dbHandle.setUp()
@ -99,14 +95,14 @@ final class InternalStateConsistencyTests: ZcashTestCase {
if latestDownloadHeight > latestScanHeight {
try await coordinator.synchronizer.blockProcessor.migrateCacheDb(dbHandle.readWriteDb)
let afterMigrationDownloadedHeight = await internalSyncState.latestDownloadedBlockHeight
let afterMigrationDownloadedHeight = try await internalSyncState.latestDownloadedBlockHeight
XCTAssertNotEqual(latestDownloadHeight, afterMigrationDownloadedHeight)
XCTAssertEqual(latestScanHeight, afterMigrationDownloadedHeight)
} else {
try await coordinator.synchronizer.blockProcessor.migrateCacheDb(dbHandle.readWriteDb)
let afterMigrationDownloadedHeight = await internalSyncState.latestDownloadedBlockHeight
let afterMigrationDownloadedHeight = try await internalSyncState.latestDownloadedBlockHeight
XCTAssertEqual(latestDownloadHeight, afterMigrationDownloadedHeight)
XCTAssertEqual(latestScanHeight, afterMigrationDownloadedHeight)

View File

@ -213,7 +213,9 @@ class SynchronizerDarksideTests: ZcashTestCase {
syncSessionID: uuids[0],
shieldedBalance: WalletBalance(verified: Zatoshi(100000), total: Zatoshi(200000)),
transparentBalance: .zero,
internalSyncStatus: .enhancing(EnhancementProgress(totalTransactions: 0, enhancedTransactions: 0, lastFoundTransaction: nil, range: 0...0, newlyMined: false)),
internalSyncStatus: .enhancing(
EnhancementProgress(totalTransactions: 0, enhancedTransactions: 0, lastFoundTransaction: nil, range: 0...0, newlyMined: false)
),
latestScannedHeight: 663189,
latestBlockHeight: 663189,
latestScannedTime: 1

View File

@ -160,7 +160,7 @@ final class SynchronizerTests: ZcashTestCase {
/*
Check that wipe cleared everything that is expected
*/
await checkThatWipeWorked()
try await checkThatWipeWorked()
}
func testWipeCalledWhileSyncRuns() async throws {
@ -217,10 +217,10 @@ final class SynchronizerTests: ZcashTestCase {
/*
Check that wipe cleared everything that is expected
*/
await checkThatWipeWorked()
try await checkThatWipeWorked()
}
private func checkThatWipeWorked() async {
private func checkThatWipeWorked() async throws {
let storage = await self.coordinator.synchronizer.blockProcessor.storage as! FSCompactBlockRepository
let fm = FileManager.default
print(coordinator.synchronizer.initializer.dataDbURL.path)
@ -229,15 +229,11 @@ final class SynchronizerTests: ZcashTestCase {
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")
let internalSyncProgress = InternalSyncProgress(
alias: .default,
storage: UserDefaults.standard,
logger: logger
)
let internalSyncProgress = coordinator.synchronizer.internalSyncProgress
let latestDownloadedBlockHeight = await internalSyncProgress.load(.latestDownloadedBlockHeight)
let latestEnhancedHeight = await internalSyncProgress.load(.latestEnhancedHeight)
let latestUTXOFetchedHeight = await internalSyncProgress.load(.latestUTXOFetchedHeight)
let latestDownloadedBlockHeight = try await internalSyncProgress.load(.latestDownloadedBlockHeight)
let latestEnhancedHeight = try await internalSyncProgress.load(.latestEnhancedHeight)
let latestUTXOFetchedHeight = try await internalSyncProgress.load(.latestUTXOFetchedHeight)
XCTAssertEqual(latestDownloadedBlockHeight, 0, "internalSyncProgress latestDownloadedBlockHeight should be 0")
XCTAssertEqual(latestEnhancedHeight, 0, "internalSyncProgress latestEnhancedHeight should be 0")

View File

@ -20,7 +20,6 @@ class TransactionEnhancementTests: ZcashTestCase {
let branchID = "2bb40e60"
let chainName = "main"
var testTempDirectory: URL!
let testFileManager = FileManager()
var initializer: Initializer!
@ -40,15 +39,7 @@ class TransactionEnhancementTests: ZcashTestCase {
override func setUp() async throws {
try await super.setUp()
testTempDirectory = Environment.uniqueTestTempDirectory
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
await InternalSyncProgress(
alias: .default,
storage: UserDefaults.standard,
logger: logger
).rewind(to: 0)
logger = OSLogger(logLevel: .debug)
syncStartedExpect = XCTestExpectation(description: "\(self.description) syncStartedExpect")
@ -140,6 +131,7 @@ class TransactionEnhancementTests: ZcashTestCase {
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: pathProvider.dataDbURL,
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: pathProvider.spendParamsURL,
outputParamsURL: pathProvider.outputParamsURL
),

View File

@ -30,7 +30,6 @@ class BlockScanTests: ZcashTestCase {
var network = ZcashNetworkBuilder.network(for: .testnet)
var blockRepository: BlockRepository!
var testTempDirectory: URL!
let testFileManager = FileManager()
@ -40,9 +39,6 @@ class BlockScanTests: ZcashTestCase {
dataDbURL = try! __dataDbURL()
spendParamsURL = try! __spendParamsURL()
outputParamsURL = try! __outputParamsURL()
testTempDirectory = Environment.uniqueTestTempDirectory
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
rustBackend = ZcashRustBackend.makeForTests(
dbData: dataDbURL,
@ -57,6 +53,7 @@ class BlockScanTests: ZcashTestCase {
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: dataDbURL,
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL
),
@ -80,7 +77,6 @@ class BlockScanTests: ZcashTestCase {
try? testFileManager.removeItem(at: dataDbURL)
try? testFileManager.removeItem(at: spendParamsURL)
try? testFileManager.removeItem(at: outputParamsURL)
try? testFileManager.removeItem(at: testTempDirectory)
cancelables = []
blockRepository = nil
testTempDirectory = nil

View File

@ -12,14 +12,11 @@ import XCTest
class BlockStreamingTest: ZcashTestCase {
let testFileManager = FileManager()
var rustBackend: ZcashRustBackendWelding!
var testTempDirectory: URL!
override func setUp() async throws {
try await super.setUp()
logger = OSLogger(logLevel: .debug)
testTempDirectory = Environment.uniqueTestTempDirectory
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet)
logger = OSLogger(logLevel: .debug)
@ -28,6 +25,7 @@ class BlockStreamingTest: ZcashTestCase {
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: try! __dataDbURL(),
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL()
),
@ -45,7 +43,6 @@ class BlockStreamingTest: ZcashTestCase {
try super.tearDownWithError()
rustBackend = nil
try? FileManager.default.removeItem(at: __dataDbURL())
try? testFileManager.removeItem(at: testTempDirectory)
testTempDirectory = nil
}

View File

@ -25,16 +25,10 @@ class CompactBlockProcessorTests: ZcashTestCase {
let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000
let testFileManager = FileManager()
var testTempDirectory: URL!
override func setUp() async throws {
try await super.setUp()
logger = OSLogger(logLevel: .debug)
testTempDirectory = Environment.uniqueTestTempDirectory
try? FileManager.default.removeItem(at: testTempDirectory)
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
let pathProvider = DefaultResourceProvider(network: network)
processorConfig = CompactBlockProcessor.Configuration(
@ -48,8 +42,6 @@ class CompactBlockProcessorTests: ZcashTestCase {
network: ZcashNetworkBuilder.network(for: .testnet)
)
await InternalSyncProgress(alias: .default, storage: UserDefaults.standard, logger: logger).rewind(to: 0)
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
let service = MockLightWalletService(
latestBlockHeight: mockLatestHeight,
@ -87,6 +79,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: processorConfig.dataDb,
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: processorConfig.spendParamsURL,
outputParamsURL: processorConfig.outputParamsURL
),
@ -130,7 +123,6 @@ class CompactBlockProcessorTests: ZcashTestCase {
override func tearDown() async throws {
try await super.tearDown()
await processor.stop()
try FileManager.default.removeItem(at: processorConfig.fsBlockCacheRoot)
try? FileManager.default.removeItem(at: processorConfig.dataDb)
cancellables = []
processor = nil
@ -190,7 +182,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
(abs(currentHeight - targetHeight) / batchSize)
}
func testNextBatchBlockRange() async {
func testNextBatchBlockRange() async throws {
// test first range
var latestDownloadedHeight = processorConfig.walletBirthday // this can be either this or Wallet Birthday.
var latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
@ -210,9 +202,9 @@ class CompactBlockProcessorTests: ZcashTestCase {
storage: InternalSyncProgressMemoryStorage(),
logger: logger
)
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight, alias: .default)
var syncRanges = await internalSyncProgress.computeSyncRanges(
var syncRanges = try await internalSyncProgress.computeSyncRanges(
birthday: processorConfig.walletBirthday,
latestBlockHeight: latestBlockchainHeight,
latestScannedHeight: 0
@ -243,9 +235,9 @@ class CompactBlockProcessorTests: ZcashTestCase {
storage: InternalSyncProgressMemoryStorage(),
logger: logger
)
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight, alias: .default)
syncRanges = await internalSyncProgress.computeSyncRanges(
syncRanges = try await internalSyncProgress.computeSyncRanges(
birthday: processorConfig.walletBirthday,
latestBlockHeight: latestBlockchainHeight,
latestScannedHeight: 0
@ -277,9 +269,9 @@ class CompactBlockProcessorTests: ZcashTestCase {
storage: InternalSyncProgressMemoryStorage(),
logger: logger
)
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight, alias: .default)
syncRanges = await internalSyncProgress.computeSyncRanges(
syncRanges = try await internalSyncProgress.computeSyncRanges(
birthday: processorConfig.walletBirthday,
latestBlockHeight: latestBlockchainHeight,
latestScannedHeight: 0

View File

@ -26,16 +26,10 @@ class CompactBlockReorgTests: ZcashTestCase {
var reorgNotificationExpectation: XCTestExpectation!
let network = ZcashNetworkBuilder.network(for: .testnet)
let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000
var testTempDirectory: URL!
override func setUp() async throws {
try await super.setUp()
testTempDirectory = Environment.uniqueTestTempDirectory
logger = OSLogger(logLevel: .debug)
try? FileManager.default.removeItem(at: testTempDirectory)
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
let pathProvider = DefaultResourceProvider(network: network)
processorConfig = CompactBlockProcessor.Configuration(
@ -49,8 +43,6 @@ class CompactBlockReorgTests: ZcashTestCase {
network: ZcashNetworkBuilder.network(for: .testnet)
)
await InternalSyncProgress(alias: .default, storage: UserDefaults.standard, logger: logger).rewind(to: 0)
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
let service = MockLightWalletService(
latestBlockHeight: mockLatestHeight,
@ -115,6 +107,7 @@ class CompactBlockReorgTests: ZcashTestCase {
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: processorConfig.dataDb,
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: processorConfig.spendParamsURL,
outputParamsURL: processorConfig.outputParamsURL
),
@ -153,7 +146,6 @@ class CompactBlockReorgTests: ZcashTestCase {
override func tearDown() async throws {
try await super.tearDown()
await processor.stop()
try! FileManager.default.removeItem(at: processorConfig.fsBlockCacheRoot)
try? FileManager.default.removeItem(at: processorConfig.dataDb)
cancellables = []
processorEventHandler = nil

View File

@ -14,21 +14,16 @@ import SQLite
class DownloadTests: ZcashTestCase {
let testFileManager = FileManager()
var network = ZcashNetworkBuilder.network(for: .testnet)
var testTempDirectory: URL!
override func setUp() async throws {
try await super.setUp()
testTempDirectory = Environment.uniqueTestTempDirectory
try? FileManager.default.removeItem(at: testTempDirectory)
await InternalSyncProgress(alias: .default, storage: UserDefaults.standard, logger: logger).rewind(to: 0)
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
Dependencies.setup(
in: mockContainer,
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: try! __dataDbURL(),
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL()
),
@ -43,8 +38,6 @@ class DownloadTests: ZcashTestCase {
override func tearDownWithError() throws {
try super.tearDownWithError()
try? testFileManager.removeItem(at: testTempDirectory)
testTempDirectory = nil
}
func testSingleDownload() async throws {

View File

@ -12,17 +12,15 @@ import XCTest
class BlockBatchValidationTests: ZcashTestCase {
let testFileManager = FileManager()
var rustBackend: ZcashRustBackendWelding!
var testTempDirectory: URL!
override func setUpWithError() throws {
try super.setUpWithError()
testTempDirectory = Environment.uniqueTestTempDirectory
Dependencies.setup(
in: mockContainer,
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: try! __dataDbURL(),
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL()
),
@ -34,13 +32,11 @@ class BlockBatchValidationTests: ZcashTestCase {
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() }
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet)
}
override func tearDownWithError() throws {
try super.tearDownWithError()
try? testFileManager.removeItem(at: testTempDirectory)
rustBackend = nil
testTempDirectory = nil
}
@ -405,7 +401,8 @@ class BlockBatchValidationTests: ZcashTestCase {
alias: .default,
storage: InternalSyncProgressMemoryStorage(),
logger: logger
)
),
alias: .default
)
XCTAssertFalse(Task.isCancelled)
} catch {
@ -496,7 +493,8 @@ class BlockBatchValidationTests: ZcashTestCase {
alias: .default,
storage: InternalSyncProgressMemoryStorage(),
logger: logger
)
),
alias: .default
)
XCTAssertFalse(Task.isCancelled)
} catch {
@ -554,8 +552,8 @@ class BlockBatchValidationTests: ZcashTestCase {
storage: InternalSyncProgressMemoryStorage(),
logger: logger
)
await internalSyncProgress.set(expectedStoredLatestHeight, .latestEnhancedHeight)
await internalSyncProgress.set(expectedStoredLatestHeight, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(expectedStoredLatestHeight, .latestEnhancedHeight)
try await internalSyncProgress.set(expectedStoredLatestHeight, .latestUTXOFetchedHeight)
var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight)
@ -580,7 +578,8 @@ class BlockBatchValidationTests: ZcashTestCase {
),
config: config,
rustBackend: mockBackend.rustBackendMock,
internalSyncProgress: internalSyncProgress
internalSyncProgress: internalSyncProgress,
alias: .default
)
XCTAssertFalse(Task.isCancelled)

View File

@ -11,17 +11,16 @@ import XCTest
class CompactBlockProcessorOfflineTests: ZcashTestCase {
let testFileManager = FileManager()
var testTempDirectory: URL!
override func setUpWithError() throws {
try super.setUpWithError()
testTempDirectory = Environment.uniqueTestTempDirectory
Dependencies.setup(
in: mockContainer,
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: try! __dataDbURL(),
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL()
),
@ -30,13 +29,10 @@ class CompactBlockProcessorOfflineTests: ZcashTestCase {
endpoint: LightWalletEndpointBuilder.default,
loggingPolicy: .default(.debug)
)
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
}
override func tearDownWithError() throws {
try super.tearDownWithError()
try FileManager.default.removeItem(at: testTempDirectory)
}
func testComputeProcessingRangeForSingleLoop() async throws {

View File

@ -11,24 +11,19 @@ import Foundation
@testable import ZcashLightClientKit
import XCTest
class CompactBlockRepositoryTests: XCTestCase {
class CompactBlockRepositoryTests: ZcashTestCase {
let network = ZcashNetworkBuilder.network(for: .testnet)
let testFileManager = FileManager()
var rustBackend: ZcashRustBackendWelding!
var testTempDirectory: URL!
override func setUpWithError() throws {
try super.setUpWithError()
testTempDirectory = Environment.uniqueTestTempDirectory
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet)
}
override func tearDownWithError() throws {
try super.tearDownWithError()
try? testFileManager.removeItem(at: testTempDirectory)
rustBackend = nil
testTempDirectory = nil
}
func testEmptyStorage() async throws {

View File

@ -10,18 +10,15 @@ import XCTest
var logger = OSLogger(logLevel: .debug)
final class FsBlockStorageTests: XCTestCase {
final class FsBlockStorageTests: ZcashTestCase {
let testFileManager = FileManager()
var fsBlockDb: URL!
var rustBackend: ZcashRustBackendWelding!
var testTempDirectory: URL!
override func setUpWithError() throws {
try super.setUpWithError()
testTempDirectory = Environment.uniqueTestTempDirectory
// Put setup code here. This method is called before the invocation of each test method in the class.
self.fsBlockDb = testTempDirectory.appendingPathComponent("FsBlockDb-\(Int.random(in: 0 ... .max))")
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
try self.testFileManager.createDirectory(at: self.fsBlockDb, withIntermediateDirectories: false)
rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet)
@ -29,9 +26,7 @@ final class FsBlockStorageTests: XCTestCase {
override func tearDownWithError() throws {
try super.tearDownWithError()
try? testFileManager.removeItem(at: testTempDirectory)
rustBackend = nil
testTempDirectory = nil
}
func testLatestHeightEmptyCache() async throws {

View File

@ -20,6 +20,7 @@ class InitializerOfflineTests: XCTestCase {
private func makeInitializer(
fsBlockDbRoot: URL,
dataDbURL: URL,
generalStorageURL: URL,
spendParamsURL: URL,
outputParamsURL: URL,
alias: ZcashSynchronizerAlias
@ -27,6 +28,7 @@ class InitializerOfflineTests: XCTestCase {
return Initializer(
cacheDbURL: nil,
fsBlockDbRoot: fsBlockDbRoot,
generalStorageURL: generalStorageURL,
dataDbURL: dataDbURL,
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: .testnet),
@ -52,6 +54,7 @@ class InitializerOfflineTests: XCTestCase {
private func genericTestForURLsParsingFailures(
fsBlockDbRoot: URL,
dataDbURL: URL,
generalStorageURL: URL,
spendParamsURL: URL,
outputParamsURL: URL,
alias: ZcashSynchronizerAlias,
@ -60,6 +63,7 @@ class InitializerOfflineTests: XCTestCase {
let initializer = makeInitializer(
fsBlockDbRoot: fsBlockDbRoot,
dataDbURL: dataDbURL,
generalStorageURL: generalStorageURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
alias: alias
@ -81,6 +85,7 @@ class InitializerOfflineTests: XCTestCase {
let initializer = makeInitializer(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
@ -97,6 +102,7 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: invalidPathURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
@ -107,6 +113,7 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: invalidPathURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
@ -117,6 +124,7 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: invalidPathURL,
outputParamsURL: validFileURL,
alias: .default
@ -127,17 +135,30 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: invalidPathURL,
alias: .default
)
}
func test__defaultAlias__invalidGeneralStorageURL__errorIsGenerated() {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: invalidPathURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .default
)
}
func test__customAlias__validURLs__updatedURLsAreAsExpected() {
let alias: ZcashSynchronizerAlias = .custom("alias")
let initializer = makeInitializer(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: alias
@ -154,6 +175,7 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: invalidPathURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
@ -164,6 +186,7 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: invalidPathURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
@ -174,6 +197,7 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: invalidPathURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
@ -184,9 +208,21 @@ class InitializerOfflineTests: XCTestCase {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: validDirectoryURL,
spendParamsURL: validFileURL,
outputParamsURL: invalidPathURL,
alias: .custom("alias")
)
}
func test__customAlias__invalidGeneralStorageURL__errorIsGenerated() {
genericTestForURLsParsingFailures(
fsBlockDbRoot: validDirectoryURL,
dataDbURL: validFileURL,
generalStorageURL: invalidPathURL,
spendParamsURL: validFileURL,
outputParamsURL: validFileURL,
alias: .custom("alias")
)
}
}

View File

@ -9,14 +9,19 @@
import XCTest
@testable import ZcashLightClientKit
class InternalSyncProgressTests: XCTestCase {
var storage: InternalSyncProgressMemoryStorage!
class InternalSyncProgressTests: ZcashTestCase {
var storage: InternalSyncProgressDiskStorage!
var internalSyncProgress: InternalSyncProgress!
override func setUp() {
super.setUp()
storage = InternalSyncProgressMemoryStorage()
override func setUp() async throws {
try await super.setUp()
for key in InternalSyncProgress.Key.allCases {
UserDefaults.standard.removeObject(forKey: key.with(.default))
}
storage = InternalSyncProgressDiskStorage(storageURL: testGeneralStorageDirectory, logger: logger)
internalSyncProgress = InternalSyncProgress(alias: .default, storage: storage, logger: logger)
try await internalSyncProgress.initialize()
}
override func tearDownWithError() throws {
@ -27,11 +32,11 @@ class InternalSyncProgressTests: XCTestCase {
func test__trackedValuesAreHigherThanLatestHeight__nextStateIsWait() async throws {
let latestHeight = 623000
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000)
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000, alias: .default)
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
let nextState = await internalSyncProgress.computeNextState(
let nextState = try await internalSyncProgress.computeNextState(
latestBlockHeight: latestHeight,
latestScannedHeight: 630000,
walletBirthday: 600000
@ -49,11 +54,11 @@ class InternalSyncProgressTests: XCTestCase {
func test__trackedValuesAreLowerThanLatestHeight__nextStateIsProcessNewBlocks() async throws {
let latestHeight = 640000
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000)
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000, alias: .default)
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
let nextState = await internalSyncProgress.computeNextState(
let nextState = try await internalSyncProgress.computeNextState(
latestBlockHeight: latestHeight,
latestScannedHeight: 620000,
walletBirthday: 600000
@ -73,11 +78,11 @@ class InternalSyncProgressTests: XCTestCase {
func test__trackedValuesAreSameAsLatestHeight__nextStateIsFinishProcessing() async throws {
let latestHeight = 630000
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000)
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000, alias: .default)
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
let nextState = await internalSyncProgress.computeNextState(
let nextState = try await internalSyncProgress.computeNextState(
latestBlockHeight: latestHeight,
latestScannedHeight: 630000,
walletBirthday: 600000
@ -93,76 +98,128 @@ class InternalSyncProgressTests: XCTestCase {
}
func test__rewindToHeightThatIsHigherThanTrackedHeight__rewindsToTrackedHeight() async throws {
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
await internalSyncProgress.rewind(to: 640000)
try await internalSyncProgress.rewind(to: 640000)
XCTAssertEqual(storage.integer(forKey: "latestEnhancedHeight"), 630000)
XCTAssertEqual(storage.integer(forKey: "latestUTXOFetchedHeight"), 630000)
let latestEnhancedHeight = try await storage.integer(for: "latestEnhancedHeight")
let latestUTXOFetchedHeight = try await storage.integer(for: "latestUTXOFetchedHeight")
XCTAssertEqual(latestEnhancedHeight, 630000)
XCTAssertEqual(latestUTXOFetchedHeight, 630000)
}
func test__rewindToHeightThatIsLowerThanTrackedHeight__rewindsToRewindHeight() async throws {
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
await internalSyncProgress.rewind(to: 620000)
try await internalSyncProgress.rewind(to: 620000)
XCTAssertEqual(storage.integer(forKey: "latestEnhancedHeight"), 620000)
XCTAssertEqual(storage.integer(forKey: "latestUTXOFetchedHeight"), 620000)
let latestEnhancedHeight = try await storage.integer(for: "latestEnhancedHeight")
let latestUTXOFetchedHeight = try await storage.integer(for: "latestUTXOFetchedHeight")
XCTAssertEqual(latestEnhancedHeight, 620000)
XCTAssertEqual(latestUTXOFetchedHeight, 620000)
}
func test__get__returnsStoredValue() async throws {
storage.set(621000, forKey: "latestEnhancedHeight")
let latestEnhancedHeight = await internalSyncProgress.latestEnhancedHeight
try await storage.set(621000, for: "latestEnhancedHeight")
let latestEnhancedHeight = try await internalSyncProgress.latestEnhancedHeight
XCTAssertEqual(latestEnhancedHeight, 621000)
storage.set(619000, forKey: "latestUTXOFetchedHeight")
let latestUTXOFetchedHeight = await internalSyncProgress.latestUTXOFetchedHeight
try await storage.set(619000, for: "latestUTXOFetchedHeight")
let latestUTXOFetchedHeight = try await internalSyncProgress.latestUTXOFetchedHeight
XCTAssertEqual(latestUTXOFetchedHeight, 619000)
}
func test__set__storeValue() async throws {
await internalSyncProgress.set(521000, .latestEnhancedHeight)
XCTAssertEqual(storage.integer(forKey: "latestEnhancedHeight"), 521000)
try await internalSyncProgress.set(521000, .latestEnhancedHeight)
let latestEnhancedHeight = try await storage.integer(for: "latestEnhancedHeight")
XCTAssertEqual(latestEnhancedHeight, 521000)
await internalSyncProgress.set(519000, .latestUTXOFetchedHeight)
XCTAssertEqual(storage.integer(forKey: "latestUTXOFetchedHeight"), 519000)
try await internalSyncProgress.set(519000, .latestUTXOFetchedHeight)
let latestUTXOFetchedHeight = try await storage.integer(for: "latestUTXOFetchedHeight")
XCTAssertEqual(latestUTXOFetchedHeight, 519000)
}
func test__whenUsingDefaultAliasKeysAreBackwardsCompatible() async {
await internalSyncProgress.set(630000, .latestDownloadedBlockHeight)
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
func test__whenUsingDefaultAliasKeysAreBackwardsCompatible() async throws {
try await internalSyncProgress.set(630000, .latestDownloadedBlockHeight)
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
XCTAssertEqual(storage.integer(forKey: InternalSyncProgress.Key.latestDownloadedBlockHeight.rawValue), 630000)
XCTAssertEqual(storage.integer(forKey: InternalSyncProgress.Key.latestUTXOFetchedHeight.rawValue), 630000)
XCTAssertEqual(storage.integer(forKey: InternalSyncProgress.Key.latestEnhancedHeight.rawValue), 630000)
let latestDownloadedBlockHeight = try await storage.integer(for: InternalSyncProgress.Key.latestDownloadedBlockHeight.rawValue)
let latestUTXOFetchedHeight = try await storage.integer(for: InternalSyncProgress.Key.latestUTXOFetchedHeight.rawValue)
let latestEnhancedHeight = try await storage.integer(for: InternalSyncProgress.Key.latestEnhancedHeight.rawValue)
XCTAssertEqual(latestDownloadedBlockHeight, 630000)
XCTAssertEqual(latestUTXOFetchedHeight, 630000)
XCTAssertEqual(latestEnhancedHeight, 630000)
}
func test__usingDifferentAliasesStoreValuesIndependently() async {
func test__usingDifferentAliasesStoreValuesIndependently() async throws {
let internalSyncProgress1 = InternalSyncProgress(alias: .custom("alias1"), storage: storage, logger: logger)
await internalSyncProgress1.set(121000, .latestDownloadedBlockHeight)
await internalSyncProgress1.set(121000, .latestUTXOFetchedHeight)
await internalSyncProgress1.set(121000, .latestEnhancedHeight)
try await internalSyncProgress1.set(121000, .latestDownloadedBlockHeight)
try await internalSyncProgress1.set(121000, .latestUTXOFetchedHeight)
try await internalSyncProgress1.set(121000, .latestEnhancedHeight)
let internalSyncProgress2 = InternalSyncProgress(alias: .custom("alias2"), storage: storage, logger: logger)
await internalSyncProgress2.set(630000, .latestDownloadedBlockHeight)
await internalSyncProgress2.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress2.set(630000, .latestEnhancedHeight)
try await internalSyncProgress2.set(630000, .latestDownloadedBlockHeight)
try await internalSyncProgress2.set(630000, .latestUTXOFetchedHeight)
try await internalSyncProgress2.set(630000, .latestEnhancedHeight)
let latestDownloadedBlockHeight1 = await internalSyncProgress1.load(.latestDownloadedBlockHeight)
let latestUTXOFetchedHeigh1 = await internalSyncProgress1.load(.latestUTXOFetchedHeight)
let latestEnhancedHeight1 = await internalSyncProgress1.load(.latestEnhancedHeight)
let latestDownloadedBlockHeight1 = try await internalSyncProgress1.load(.latestDownloadedBlockHeight)
let latestUTXOFetchedHeigh1 = try await internalSyncProgress1.load(.latestUTXOFetchedHeight)
let latestEnhancedHeight1 = try await internalSyncProgress1.load(.latestEnhancedHeight)
XCTAssertEqual(latestDownloadedBlockHeight1, 121000)
XCTAssertEqual(latestUTXOFetchedHeigh1, 121000)
XCTAssertEqual(latestEnhancedHeight1, 121000)
let latestDownloadedBlockHeight2 = await internalSyncProgress2.load(.latestDownloadedBlockHeight)
let latestUTXOFetchedHeigh2 = await internalSyncProgress2.load(.latestUTXOFetchedHeight)
let latestEnhancedHeight2 = await internalSyncProgress2.load(.latestEnhancedHeight)
let latestDownloadedBlockHeight2 = try await internalSyncProgress2.load(.latestDownloadedBlockHeight)
let latestUTXOFetchedHeigh2 = try await internalSyncProgress2.load(.latestUTXOFetchedHeight)
let latestEnhancedHeight2 = try await internalSyncProgress2.load(.latestEnhancedHeight)
XCTAssertEqual(latestDownloadedBlockHeight2, 630000)
XCTAssertEqual(latestUTXOFetchedHeigh2, 630000)
XCTAssertEqual(latestEnhancedHeight2, 630000)
}
func test__migrateFromUserDefaults__withDefaultAlias() async throws {
let userDefaults = UserDefaults.standard
userDefaults.set(113000, forKey: InternalSyncProgress.Key.latestDownloadedBlockHeight.with(.default))
userDefaults.set(114000, forKey: InternalSyncProgress.Key.latestEnhancedHeight.with(.default))
userDefaults.set(115000, forKey: InternalSyncProgress.Key.latestUTXOFetchedHeight.with(.default))
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 150000, alias: .default)
let latestDownloadedBlockHeight = try await internalSyncProgress.load(.latestDownloadedBlockHeight)
let latestUTXOFetchedHeigh = try await internalSyncProgress.load(.latestEnhancedHeight)
let latestEnhancedHeight = try await internalSyncProgress.load(.latestUTXOFetchedHeight)
XCTAssertEqual(latestDownloadedBlockHeight, 113000)
XCTAssertEqual(latestUTXOFetchedHeigh, 114000)
XCTAssertEqual(latestEnhancedHeight, 115000)
XCTAssertEqual(userDefaults.integer(forKey: InternalSyncProgress.Key.latestDownloadedBlockHeight.with(.default)), 0)
XCTAssertEqual(userDefaults.integer(forKey: InternalSyncProgress.Key.latestEnhancedHeight.with(.default)), 0)
XCTAssertEqual(userDefaults.integer(forKey: InternalSyncProgress.Key.latestUTXOFetchedHeight.with(.default)), 0)
}
func test__migrateFromUserDefaults__withAlias() async throws {
let userDefaults = UserDefaults.standard
let alias: ZcashSynchronizerAlias = .custom("something")
internalSyncProgress = InternalSyncProgress(alias: alias, storage: storage, logger: logger)
userDefaults.set(113000, forKey: InternalSyncProgress.Key.latestDownloadedBlockHeight.with(alias))
userDefaults.set(114000, forKey: InternalSyncProgress.Key.latestEnhancedHeight.with(alias))
userDefaults.set(115000, forKey: InternalSyncProgress.Key.latestUTXOFetchedHeight.with(alias))
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 150000, alias: alias)
let latestDownloadedBlockHeight = try await internalSyncProgress.load(.latestDownloadedBlockHeight)
let latestUTXOFetchedHeigh = try await internalSyncProgress.load(.latestEnhancedHeight)
let latestEnhancedHeight = try await internalSyncProgress.load(.latestUTXOFetchedHeight)
XCTAssertEqual(latestDownloadedBlockHeight, 113000)
XCTAssertEqual(latestUTXOFetchedHeigh, 114000)
XCTAssertEqual(latestEnhancedHeight, 115000)
XCTAssertEqual(userDefaults.integer(forKey: InternalSyncProgress.Key.latestDownloadedBlockHeight.with(alias)), 0)
XCTAssertEqual(userDefaults.integer(forKey: InternalSyncProgress.Key.latestEnhancedHeight.with(alias)), 0)
XCTAssertEqual(userDefaults.integer(forKey: InternalSyncProgress.Key.latestUTXOFetchedHeight.with(alias)), 0)
}
}

View File

@ -288,6 +288,7 @@ class SynchronizerOfflineTests: ZcashTestCase {
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: validDirectoryURL,
generalStorageURL: validDirectoryURL,
dataDbURL: invalidPathURL,
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: .testnet),
@ -328,6 +329,7 @@ class SynchronizerOfflineTests: ZcashTestCase {
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: validDirectoryURL,
generalStorageURL: validDirectoryURL,
dataDbURL: invalidPathURL,
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: .testnet),

View File

@ -11,9 +11,8 @@ import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
class WalletTests: XCTestCase {
class WalletTests: ZcashTestCase {
let testFileManager = FileManager()
var testTempDirectory: URL!
var dbData: URL! = nil
var paramDestination: URL! = nil
var network = ZcashNetworkBuilder.network(for: .testnet)
@ -21,9 +20,7 @@ class WalletTests: XCTestCase {
override func setUpWithError() throws {
try super.setUpWithError()
testTempDirectory = Environment.uniqueTestTempDirectory
dbData = try __dataDbURL()
try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false)
paramDestination = try __documentsDirectory().appendingPathComponent("parameters")
}
@ -32,7 +29,6 @@ class WalletTests: XCTestCase {
if testFileManager.fileExists(atPath: dbData.absoluteString) {
try testFileManager.trashItem(at: dbData, resultingItemURL: nil)
}
try? self.testFileManager.removeItem(at: testTempDirectory)
}
func testWalletInitialization() async throws {
@ -43,6 +39,7 @@ class WalletTests: XCTestCase {
let wallet = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: testTempDirectory,
generalStorageURL: testGeneralStorageDirectory,
dataDbURL: try __dataDbURL(),
endpoint: LightWalletEndpointBuilder.default,
network: network,

View File

@ -10,7 +10,7 @@ import XCTest
@testable import ZcashLightClientKit
@testable import TestUtils
class SynchronizerTests: XCTestCase {
class SynchronizerTests: ZcashTestCase {
class MockLatestBlockHeightProvider: LatestBlockHeightProvider {
let birthday: BlockHeight
@ -27,7 +27,6 @@ class SynchronizerTests: XCTestCase {
var cancellables: [AnyCancellable] = []
var sdkSynchronizerInternalSyncStatusHandler: SDKSynchronizerInternalSyncStatusHandler! = SDKSynchronizerInternalSyncStatusHandler()
var rustBackend: ZcashRustBackendWelding!
var testTempDirectory: URL!
let seedPhrase = """
wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame
@ -37,7 +36,6 @@ class SynchronizerTests: XCTestCase {
override func setUp() async throws {
try await super.setUp()
testTempDirectory = Environment.uniqueTestTempDirectory
rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .mainnet)
}
@ -47,7 +45,6 @@ class SynchronizerTests: XCTestCase {
cancellables = []
sdkSynchronizerInternalSyncStatusHandler = nil
rustBackend = nil
testTempDirectory = nil
}
func testHundredBlocksSync() async throws {
@ -71,6 +68,7 @@ class SynchronizerTests: XCTestCase {
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: databases.fsCacheDbRoot,
generalStorageURL: testGeneralStorageDirectory,
dataDbURL: databases.dataDB,
endpoint: endpoint,
network: network,
@ -94,12 +92,7 @@ class SynchronizerTests: XCTestCase {
let syncSyncedExpectation = XCTestExpectation(description: "synchronizerSynced Expectation")
sdkSynchronizerInternalSyncStatusHandler.subscribe(to: synchronizer.stateStream, expectations: [.synced: syncSyncedExpectation])
let internalSyncProgress = InternalSyncProgress(
alias: .default,
storage: UserDefaults.standard,
logger: logger
)
await internalSyncProgress.rewind(to: birthday)
try await resetDefaultInternalSyncProgress(to: birthday)
await (synchronizer.blockProcessor.service as? LightWalletGRPCService)?.latestBlockHeightProvider = MockLatestBlockHeightProvider(
birthday: self.birthday + 99
)

View File

@ -12,20 +12,22 @@ class InternalSyncProgressMemoryStorage: InternalSyncProgressStorage {
private var boolStorage: [String: Bool] = [:]
private var storage: [String: Int] = [:]
func bool(forKey defaultName: String) -> Bool {
return boolStorage[defaultName, default: false]
func initialize() async throws { }
func bool(for key: String) async throws -> Bool {
return boolStorage[key, default: false]
}
func integer(forKey defaultName: String) -> Int {
return storage[defaultName, default: 0]
func integer(for key: String) async throws -> Int {
return storage[key, default: 0]
}
func set(_ value: Int, forKey defaultName: String) {
storage[defaultName] = value
func set(_ value: Int, for key: String) async throws {
storage[key] = value
}
func set(_ value: Bool, forKey defaultName: String) {
boolStorage[defaultName] = value
func set(_ value: Bool, for key: String) async throws {
boolStorage[key] = value
}
func synchronize() -> Bool { true }

View File

@ -63,15 +63,19 @@ class TestCoordinator {
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 storage = InternalSyncProgressDiskStorage(storageURL: databases.generalStorageURL, logger: logger)
let internalSyncProgress = InternalSyncProgress(alias: alias, storage: storage, logger: logger)
try await internalSyncProgress.initialize()
try await internalSyncProgress.rewind(to: 0)
let initializer = Initializer(
container: container,
cacheDbURL: nil,
fsBlockDbRoot: databases.fsCacheDbRoot,
generalStorageURL: databases.generalStorageURL,
dataDbURL: databases.dataDB,
endpoint: endpoint,
network: network,
@ -81,7 +85,7 @@ class TestCoordinator {
alias: alias,
loggingPolicy: .default(.debug)
)
let derivationTool = DerivationTool(networkType: network.networkType)
self.spendingKey = try derivationTool.deriveUnifiedSpendingKey(
@ -243,6 +247,7 @@ extension TestCoordinator {
struct TemporaryTestDatabases {
var fsCacheDbRoot: URL
let generalStorageURL: URL
var dataDB: URL
}
@ -253,6 +258,7 @@ enum TemporaryDbBuilder {
return TemporaryTestDatabases(
fsCacheDbRoot: tempUrl.appendingPathComponent("fs_cache_\(timestamp)"),
generalStorageURL: tempUrl.appendingPathComponent("general_storage_\(timestamp)"),
dataDB: tempUrl.appendingPathComponent("data_db_\(timestamp).db")
)
}

View File

@ -33,6 +33,11 @@ enum Environment {
URL(fileURLWithPath: NSString(string: NSTemporaryDirectory())
.appendingPathComponent("tmp-\(Int.random(in: 0 ... .max))"))
}
static var uniqueGeneralStorageDirectory: URL {
URL(fileURLWithPath: NSString(string: NSTemporaryDirectory())
.appendingPathComponent("gens-\(Int.random(in: 0 ... .max))"))
}
}
public enum Constants {

View File

@ -15,6 +15,7 @@ class TestsData {
Initializer(
cacheDbURL: nil,
fsBlockDbRoot: URL(fileURLWithPath: "/"),
generalStorageURL: URL(fileURLWithPath: "/"),
dataDbURL: URL(fileURLWithPath: "/"),
endpoint: LightWalletEndpointBuilder.default,
network: ZcashNetworkBuilder.network(for: networkType),

View File

@ -11,6 +11,10 @@ import XCTest
class ZcashTestCase: XCTestCase {
var mockContainer: DIContainer!
var testTempDirectory: URL!
var testGeneralStorageDirectory: URL!
// MARK: - DI
private func createMockContainer() {
guard mockContainer == nil else { return }
@ -22,33 +26,84 @@ class ZcashTestCase: XCTestCase {
mockContainer = nil
}
// MARK: - Paths
private func create(path: URL!) throws {
try FileManager.default.createDirectory(at: path, withIntermediateDirectories: true)
}
private func delete(path: URL!) throws {
if path != nil && FileManager.default.fileExists(atPath: path.path) {
try FileManager.default.removeItem(at: path)
}
}
private func createPaths() throws {
if testTempDirectory == nil {
testTempDirectory = Environment.uniqueTestTempDirectory
try delete(path: testTempDirectory)
try create(path: testTempDirectory)
}
if testGeneralStorageDirectory == nil {
testGeneralStorageDirectory = Environment.uniqueGeneralStorageDirectory
try delete(path: testGeneralStorageDirectory)
try create(path: testGeneralStorageDirectory)
}
}
private func deletePaths() throws {
try delete(path: testTempDirectory)
testTempDirectory = nil
try delete(path: testGeneralStorageDirectory)
testGeneralStorageDirectory = nil
}
// MARK: - InternalSyncProgress
func resetDefaultInternalSyncProgress(to height: BlockHeight = 0) async throws {
let storage = InternalSyncProgressDiskStorage(storageURL: testGeneralStorageDirectory, logger: logger)
let internalSyncProgress = InternalSyncProgress(alias: .default, storage: storage, logger: logger)
try await internalSyncProgress.initialize()
try await internalSyncProgress.rewind(to: 0)
}
// MARK: - XCTestCase
override func setUp() async throws {
try await super.setUp()
createMockContainer()
try createPaths()
try await resetDefaultInternalSyncProgress()
}
override func setUp() {
super.setUp()
createMockContainer()
try? createPaths()
}
override func setUpWithError() throws {
try super.setUpWithError()
createMockContainer()
}
override func tearDown() {
super.tearDown()
destroyMockContainer()
try createPaths()
}
override func tearDown() async throws {
try await super.tearDown()
destroyMockContainer()
try deletePaths()
}
override func tearDown() {
super.tearDown()
destroyMockContainer()
try? deletePaths()
}
override func tearDownWithError() throws {
try super.tearDownWithError()
destroyMockContainer()
try deletePaths()
}
}