[#1111] Change storage for InternalSyncProgress
Closes #1111 - `InternalSyncProgress` has now new disk storage implementation: `InternalSyncProgressDiskStorage`. - When then `UserDefaults` was used as disk storage there were no IO errors to handle. But now the code in `InternalSyncProgressDiskStorage` writes to files and reads from files. So there are errors to handle. Because of this protocol `InternalSyncProgressStorage` changed a bit and API of `InternalSyncProgress` changed a bit. Now all the functions throws. - It is possible to make progress storage IO errors silent but I think that storing of the progress is very essential to the sync process. So I think that when progress storage fails then the sync process should fail. - `Initializer` has new parameter `generalStorageURL`. It is directory where `InternalSyncProgressDiskStorage` stores progress files. In future this can be used for storing any data the SDK wants.
This commit is contained in:
parent
c1e0740519
commit
c0c0c426d5
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
@ -172,6 +173,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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
|
@ -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) }
|
||||
}
|
||||
}
|
|
@ -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) }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
@ -149,6 +155,7 @@ public class Initializer {
|
|||
convenience public init(
|
||||
cacheDbURL: URL?,
|
||||
fsBlockDbRoot: URL,
|
||||
generalStorageURL: URL,
|
||||
dataDbURL: URL,
|
||||
endpoint: LightWalletEndpoint,
|
||||
network: ZcashNetwork,
|
||||
|
@ -166,6 +173,7 @@ public class Initializer {
|
|||
container: container,
|
||||
cacheDbURL: cacheDbURL,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
generalStorageURL: generalStorageURL,
|
||||
dataDbURL: dataDbURL,
|
||||
endpoint: endpoint,
|
||||
network: network,
|
||||
|
@ -194,6 +202,7 @@ public class Initializer {
|
|||
container: DIContainer,
|
||||
cacheDbURL: URL?,
|
||||
fsBlockDbRoot: URL,
|
||||
generalStorageURL: URL,
|
||||
dataDbURL: URL,
|
||||
endpoint: LightWalletEndpoint,
|
||||
network: ZcashNetwork,
|
||||
|
@ -209,6 +218,7 @@ public class Initializer {
|
|||
container: container,
|
||||
cacheDbURL: cacheDbURL,
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
generalStorageURL: generalStorageURL,
|
||||
dataDbURL: dataDbURL,
|
||||
endpoint: endpoint,
|
||||
network: network,
|
||||
|
@ -247,6 +257,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
|
||||
|
@ -278,6 +289,7 @@ public class Initializer {
|
|||
container: DIContainer,
|
||||
cacheDbURL: URL?,
|
||||
fsBlockDbRoot: URL,
|
||||
generalStorageURL: URL,
|
||||
dataDbURL: URL,
|
||||
endpoint: LightWalletEndpoint,
|
||||
network: ZcashNetwork,
|
||||
|
@ -290,6 +302,7 @@ public class Initializer {
|
|||
let urls = URLs(
|
||||
fsBlockDbRoot: fsBlockDbRoot,
|
||||
dataDbURL: dataDbURL,
|
||||
generalStorageURL: generalStorageURL,
|
||||
spendParamsURL: spendParamsURL,
|
||||
outputParamsURL: outputParamsURL
|
||||
)
|
||||
|
@ -360,10 +373,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
|
||||
)
|
||||
|
|
|
@ -89,7 +89,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue