Merge pull request #1148 from LukasKorba/1140-ClearCache-action-before-anything-starts
ClearCache action called right after the idle action, clearing out metadata so the sync process can be fully restored from the DB and live blockchain values only. InternalSyncProgress removed InternalSyncProgressStorage removed Sync process control logic updated, controlled by latestScannedHeight and firstUnenhancedHeight only ChecksBeforeSyncAction removed Offline tests fixed fixed injection of a wallet birthday, the sync range must start with wallet BD instead of lower bound Network tests fixed rewind actions extension in compact block processor added DarkSideTests fixed SyncRanges modified to be even less dependent on ranges, now it holds just 3 values (latest block height, latest scanned height if any, first unenhanced height if any), the rest is computed on the fly SyncRanges struct not anymore, refactored to SyncControlData, holding just 3 mentioned values code cleanup
This commit is contained in:
commit
d930517348
|
@ -20,6 +20,7 @@ enum DemoAppConfig {
|
|||
|
||||
static let host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co"
|
||||
static let port: Int = 9067
|
||||
|
||||
static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
|
||||
static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
|
||||
live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
|
||||
|
|
|
@ -29,6 +29,6 @@ class GetBalanceViewController: UIViewController {
|
|||
|
||||
extension Zatoshi {
|
||||
var formattedString: String? {
|
||||
NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: self.amount))
|
||||
decimalString(formatter: NumberFormatter.zcashNumberFormatter)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,28 +10,31 @@ import Foundation
|
|||
actor ActionContext {
|
||||
var state: CBPState
|
||||
var prevState: CBPState?
|
||||
var syncRanges: SyncRanges
|
||||
var syncControlData: SyncControlData
|
||||
var totalProgressRange: CompactBlockRange = 0...0
|
||||
var lastDownloadedHeight: BlockHeight?
|
||||
var lastEnhancedHeight: BlockHeight?
|
||||
|
||||
init(state: CBPState) {
|
||||
self.state = state
|
||||
syncRanges = SyncRanges.empty
|
||||
syncControlData = SyncControlData.empty
|
||||
}
|
||||
|
||||
func update(state: CBPState) async {
|
||||
prevState = self.state
|
||||
self.state = state
|
||||
}
|
||||
func update(syncRanges: SyncRanges) async { self.syncRanges = syncRanges }
|
||||
func update(syncControlData: SyncControlData) async { self.syncControlData = syncControlData }
|
||||
func update(totalProgressRange: CompactBlockRange) async { self.totalProgressRange = totalProgressRange }
|
||||
func update(lastDownloadedHeight: BlockHeight) async { self.lastDownloadedHeight = lastDownloadedHeight }
|
||||
func update(lastEnhancedHeight: BlockHeight?) async { self.lastEnhancedHeight = lastEnhancedHeight }
|
||||
}
|
||||
|
||||
enum CBPState: CaseIterable {
|
||||
case idle
|
||||
case migrateLegacyCacheDB
|
||||
case validateServer
|
||||
case computeSyncRanges
|
||||
case checksBeforeSync
|
||||
case computeSyncControlData
|
||||
case download
|
||||
case validate
|
||||
case scan
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// ChecksBeforeSyncAction.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 05.05.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class ChecksBeforeSyncAction {
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let storage: CompactBlockRepository
|
||||
|
||||
init(container: DIContainer) {
|
||||
internalSyncProgress = container.resolve(InternalSyncProgress.self)
|
||||
storage = container.resolve(CompactBlockRepository.self)
|
||||
}
|
||||
|
||||
/// Tells whether the state represented by these sync ranges evidence some sort of
|
||||
/// outdated state on the cache or the internal state of the compact block processor.
|
||||
///
|
||||
/// - Note: this can mean that the processor has synced over the height that the internal
|
||||
/// state knows of because the sync process was interrupted before it could reflect
|
||||
/// it in the internal state storage. This could happen because of many factors, the
|
||||
/// most feasible being OS shutting down a background process or the user abruptly
|
||||
/// exiting the app.
|
||||
/// - Returns: an ``Optional<BlockHeight>`` where Some represents what's the
|
||||
/// new state the internal state should reflect and indicating that the cache should be cleared
|
||||
/// as well. `nil` means that no action is required.
|
||||
func shouldClearBlockCacheAndUpdateInternalState(syncRange: SyncRanges) -> BlockHeight? {
|
||||
guard syncRange.downloadRange != nil, syncRange.scanRange != nil else { return nil }
|
||||
|
||||
guard
|
||||
let latestScannedHeight = syncRange.latestScannedHeight,
|
||||
let latestDownloadedHeight = syncRange.latestDownloadedBlockHeight,
|
||||
latestScannedHeight > latestDownloadedHeight
|
||||
else { return nil }
|
||||
|
||||
return latestScannedHeight
|
||||
}
|
||||
}
|
||||
|
||||
extension ChecksBeforeSyncAction: Action {
|
||||
var removeBlocksCacheWhenFailed: Bool { false }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
// clear any present cached state if needed.
|
||||
// this checks if there was a sync in progress that was
|
||||
// interrupted abruptly and cache was not able to be cleared
|
||||
// properly and internal state set to the appropriate value
|
||||
if let newLatestDownloadedHeight = shouldClearBlockCacheAndUpdateInternalState(syncRange: await context.syncRanges) {
|
||||
try await storage.clear()
|
||||
try await internalSyncProgress.set(newLatestDownloadedHeight, .latestDownloadedBlockHeight)
|
||||
} else {
|
||||
try await storage.create()
|
||||
}
|
||||
|
||||
await context.update(state: .fetchUTXO)
|
||||
return context
|
||||
}
|
||||
|
||||
func stop() async { }
|
||||
}
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
|
||||
final class ClearCacheAction {
|
||||
let storage: CompactBlockRepository
|
||||
|
||||
|
||||
init(container: DIContainer) {
|
||||
storage = container.resolve(CompactBlockRepository.self)
|
||||
}
|
||||
|
@ -20,7 +20,11 @@ extension ClearCacheAction: Action {
|
|||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
try await storage.clear()
|
||||
await context.update(state: .finished)
|
||||
if await context.prevState == .idle {
|
||||
await context.update(state: .migrateLegacyCacheDB)
|
||||
} else {
|
||||
await context.update(state: .finished)
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// ComputeSyncControlDataAction.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 05.05.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class ComputeSyncControlDataAction {
|
||||
let configProvider: CompactBlockProcessor.ConfigProvider
|
||||
let downloaderService: BlockDownloaderService
|
||||
let latestBlocksDataProvider: LatestBlocksDataProvider
|
||||
let logger: Logger
|
||||
|
||||
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
||||
self.configProvider = configProvider
|
||||
downloaderService = container.resolve(BlockDownloaderService.self)
|
||||
latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
|
||||
logger = container.resolve(Logger.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension ComputeSyncControlDataAction: Action {
|
||||
var removeBlocksCacheWhenFailed: Bool { false }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
let config = await configProvider.config
|
||||
|
||||
await latestBlocksDataProvider.updateScannedData()
|
||||
await latestBlocksDataProvider.updateBlockData()
|
||||
|
||||
// Here we know:
|
||||
// - latest scanned height from the DB, if none the wallet's birthday is automatically used
|
||||
// - first unenhanced height from the DB, could be nil = up to latestScannedHeight nothing to enhance
|
||||
// - latest downloaded height reported by downloaderService
|
||||
// - latest block height on the blockchain
|
||||
// - wallet birthday for the initial scan
|
||||
|
||||
let latestBlockHeight = await latestBlocksDataProvider.latestBlockHeight
|
||||
let latestScannedHeightDB = await latestBlocksDataProvider.latestScannedHeight
|
||||
let latestScannedHeight = latestScannedHeightDB < config.walletBirthday ? config.walletBirthday : latestScannedHeightDB
|
||||
let firstUnenhancedHeight = await latestBlocksDataProvider.firstUnenhancedHeight
|
||||
let enhanceStart: BlockHeight
|
||||
if let firstUnenhancedHeight {
|
||||
enhanceStart = min(firstUnenhancedHeight, latestScannedHeight)
|
||||
} else {
|
||||
enhanceStart = latestScannedHeight
|
||||
}
|
||||
|
||||
logger.debug("""
|
||||
Init numbers:
|
||||
latestBlockHeight [BC]: \(latestBlockHeight)
|
||||
latestScannedHeight [DB]: \(latestScannedHeight)
|
||||
firstUnenhancedHeight [DB]: \(firstUnenhancedHeight ?? -1)
|
||||
enhanceStart: \(enhanceStart)
|
||||
walletBirthday: \(config.walletBirthday)
|
||||
""")
|
||||
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: latestBlockHeight,
|
||||
latestScannedHeight: latestScannedHeight,
|
||||
firstUnenhancedHeight: enhanceStart
|
||||
)
|
||||
|
||||
await context.update(lastDownloadedHeight: latestScannedHeight)
|
||||
await context.update(syncControlData: syncControlData)
|
||||
await context.update(totalProgressRange: latestScannedHeight...latestBlockHeight)
|
||||
|
||||
// if there is nothing sync just switch to finished state
|
||||
if latestBlockHeight < latestScannedHeight || latestBlockHeight == latestScannedHeight {
|
||||
await context.update(state: .finished)
|
||||
} else {
|
||||
await context.update(state: .fetchUTXO)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func stop() async { }
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
//
|
||||
// ComputeSyncRangesAction.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 05.05.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class ComputeSyncRangesAction {
|
||||
let configProvider: CompactBlockProcessor.ConfigProvider
|
||||
let downloaderService: BlockDownloaderService
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let latestBlocksDataProvider: LatestBlocksDataProvider
|
||||
let logger: Logger
|
||||
|
||||
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
||||
self.configProvider = configProvider
|
||||
downloaderService = container.resolve(BlockDownloaderService.self)
|
||||
internalSyncProgress = container.resolve(InternalSyncProgress.self)
|
||||
latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
|
||||
logger = container.resolve(Logger.self)
|
||||
}
|
||||
|
||||
/// This method analyses what must be done and computes range that should be used to compute reported progress.
|
||||
func computeTotalProgressRange(from syncRanges: SyncRanges) -> CompactBlockRange {
|
||||
guard syncRanges.downloadRange != nil || syncRanges.scanRange != nil else {
|
||||
// In this case we are sure that no downloading or scanning happens so this returned range won't be even used. And it's easier to return
|
||||
// this "fake" range than to handle nil.
|
||||
return 0...0
|
||||
}
|
||||
|
||||
// Thanks to guard above we can be sure that one of these two ranges is not nil.
|
||||
let lowerBound = syncRanges.scanRange?.lowerBound ?? syncRanges.downloadRange?.lowerBound ?? 0
|
||||
let upperBound = syncRanges.scanRange?.upperBound ?? syncRanges.downloadRange?.upperBound ?? 0
|
||||
|
||||
return lowerBound...upperBound
|
||||
}
|
||||
}
|
||||
|
||||
extension ComputeSyncRangesAction: Action {
|
||||
var removeBlocksCacheWhenFailed: Bool { false }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
// call internalSyncProgress and compute sync ranges and store them in context
|
||||
// if there is nothing sync just switch to finished state
|
||||
|
||||
let config = await configProvider.config
|
||||
let latestDownloadHeight = try await downloaderService.lastDownloadedBlockHeight()
|
||||
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadHeight, alias: config.alias)
|
||||
|
||||
await latestBlocksDataProvider.updateScannedData()
|
||||
await latestBlocksDataProvider.updateBlockData()
|
||||
|
||||
let nextState = try await internalSyncProgress.computeNextState(
|
||||
latestBlockHeight: latestBlocksDataProvider.latestBlockHeight,
|
||||
latestScannedHeight: latestBlocksDataProvider.latestScannedHeight,
|
||||
walletBirthday: config.walletBirthday
|
||||
)
|
||||
|
||||
switch nextState {
|
||||
case .finishProcessing:
|
||||
await context.update(state: .finished)
|
||||
case .processNewBlocks(let ranges):
|
||||
let totalProgressRange = computeTotalProgressRange(from: ranges)
|
||||
await context.update(totalProgressRange: totalProgressRange)
|
||||
await context.update(syncRanges: ranges)
|
||||
await context.update(state: .checksBeforeSync)
|
||||
|
||||
logger.debug("""
|
||||
Syncing with ranges:
|
||||
download: \(ranges.downloadRange?.lowerBound ?? -1)...\(ranges.downloadRange?.upperBound ?? -1)
|
||||
scan: \(ranges.scanRange?.lowerBound ?? -1)...\(ranges.scanRange?.upperBound ?? -1)
|
||||
enhance range: \(ranges.enhanceRange?.lowerBound ?? -1)...\(ranges.enhanceRange?.upperBound ?? -1)
|
||||
fetchUTXO range: \(ranges.fetchUTXORange?.lowerBound ?? -1)...\(ranges.fetchUTXORange?.upperBound ?? -1)
|
||||
total progress range: \(totalProgressRange.lowerBound)...\(totalProgressRange.upperBound)
|
||||
""")
|
||||
|
||||
case let .wait(latestHeight, latestDownloadHeight):
|
||||
// Lightwalletd might be syncing
|
||||
logger.info(
|
||||
"Lightwalletd might be syncing: latest downloaded block height is: \(latestDownloadHeight) " +
|
||||
"while latest blockheight is reported at: \(latestHeight)"
|
||||
)
|
||||
await context.update(state: .finished)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func stop() async { }
|
||||
}
|
|
@ -30,16 +30,17 @@ extension DownloadAction: Action {
|
|||
var removeBlocksCacheWhenFailed: Bool { true }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
guard let downloadRange = await context.syncRanges.downloadRange else {
|
||||
guard let lastScannedHeight = await context.syncControlData.latestScannedHeight else {
|
||||
return await update(context: context)
|
||||
}
|
||||
|
||||
let config = await configProvider.config
|
||||
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
|
||||
let lastScannedHeightDB = try await transactionRepository.lastScannedHeight()
|
||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
||||
// This action is executed for each batch (batch size is 100 blocks by default) until all the blocks in whole `downloadRange` are downloaded.
|
||||
// So the right range for this batch must be computed.
|
||||
let batchRangeStart = max(downloadRange.lowerBound, lastScannedHeight)
|
||||
let batchRangeEnd = min(downloadRange.upperBound, batchRangeStart + config.batchSize)
|
||||
let batchRangeStart = max(lastScannedHeightDB, lastScannedHeight)
|
||||
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
||||
|
||||
guard batchRangeStart <= batchRangeEnd else {
|
||||
return await update(context: context)
|
||||
|
@ -49,7 +50,8 @@ extension DownloadAction: Action {
|
|||
let downloadLimit = batchRange.upperBound + (2 * config.batchSize)
|
||||
|
||||
logger.debug("Starting download with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
||||
try await downloader.setSyncRange(downloadRange, batchSize: config.batchSize)
|
||||
await downloader.update(latestDownloadedBlockHeight: batchRange.lowerBound)
|
||||
try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
|
||||
await downloader.setDownloadLimit(downloadLimit)
|
||||
await downloader.startDownload(maxBlockBufferSize: config.downloadBufferSize)
|
||||
|
||||
|
|
|
@ -10,25 +10,24 @@ import Foundation
|
|||
final class EnhanceAction {
|
||||
let blockEnhancer: BlockEnhancer
|
||||
let configProvider: CompactBlockProcessor.ConfigProvider
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let logger: Logger
|
||||
let transactionRepository: TransactionRepository
|
||||
|
||||
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
||||
blockEnhancer = container.resolve(BlockEnhancer.self)
|
||||
self.configProvider = configProvider
|
||||
internalSyncProgress = container.resolve(InternalSyncProgress.self)
|
||||
logger = container.resolve(Logger.self)
|
||||
transactionRepository = container.resolve(TransactionRepository.self)
|
||||
}
|
||||
|
||||
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
||||
guard let scanRange = await context.syncRanges.scanRange else {
|
||||
guard await context.syncControlData.latestScannedHeight != nil else {
|
||||
await context.update(state: .clearCache)
|
||||
return context
|
||||
}
|
||||
|
||||
if lastScannedHeight >= scanRange.upperBound {
|
||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
||||
if lastScannedHeight >= latestBlockHeight {
|
||||
await context.update(state: .clearCache)
|
||||
} else {
|
||||
await context.update(state: .download)
|
||||
|
@ -44,7 +43,6 @@ extension EnhanceAction: Action {
|
|||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
// Use `BlockEnhancer` to enhance blocks.
|
||||
// This action is executed on each downloaded and scanned batch (typically each 100 blocks). But we want to run enhancement each 1000 blocks.
|
||||
// This action can use `InternalSyncProgress` and last scanned height to compute when it should do work.
|
||||
|
||||
// if latestScannedHeight >= context.scanRanges.scanRange.upperBound then everything is processed and sync process should continue to end.
|
||||
// If latestScannedHeight < context.scanRanges.scanRange.upperBound then set state to `download` because there are blocks to
|
||||
|
@ -53,22 +51,28 @@ extension EnhanceAction: Action {
|
|||
let config = await configProvider.config
|
||||
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
|
||||
|
||||
guard let range = await context.syncRanges.enhanceRange else {
|
||||
guard let firstUnenhancedHeight = await context.syncControlData.firstUnenhancedHeight else {
|
||||
return await decideWhatToDoNext(context: context, lastScannedHeight: lastScannedHeight)
|
||||
}
|
||||
|
||||
let lastEnhancedHeight = try await internalSyncProgress.load(.latestEnhancedHeight)
|
||||
let enhanceRangeStart = max(range.lowerBound, lastEnhancedHeight)
|
||||
let enhanceRangeEnd = min(range.upperBound, lastScannedHeight)
|
||||
|
||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
||||
let lastEnhancedHeight: BlockHeight
|
||||
if let lastEnhancedHeightInContext = await context.lastEnhancedHeight {
|
||||
lastEnhancedHeight = lastEnhancedHeightInContext
|
||||
} else {
|
||||
lastEnhancedHeight = -1
|
||||
}
|
||||
let enhanceRangeStart = max(firstUnenhancedHeight, lastEnhancedHeight + 1)
|
||||
let enhanceRangeEnd = min(latestBlockHeight, lastScannedHeight)
|
||||
|
||||
// This may happen:
|
||||
// For example whole enhance range is 0...2100 Without this force enhance is done for ranges: 0...1000, 1001...2000. And that's it.
|
||||
// Last 100 blocks isn't enhanced.
|
||||
//
|
||||
// This force makes sure that all the blocks are enhanced even when last enhance happened < 1000 blocks ago.
|
||||
let forceEnhance = enhanceRangeEnd == range.upperBound && enhanceRangeEnd - enhanceRangeStart <= config.enhanceBatchSize
|
||||
let forceEnhance = enhanceRangeEnd == latestBlockHeight && enhanceRangeEnd - enhanceRangeStart <= config.enhanceBatchSize
|
||||
|
||||
if forceEnhance || (enhanceRangeStart <= enhanceRangeEnd && lastScannedHeight - lastEnhancedHeight >= config.enhanceBatchSize) {
|
||||
if enhanceRangeStart <= enhanceRangeEnd && (forceEnhance || (lastScannedHeight - lastEnhancedHeight >= config.enhanceBatchSize)) {
|
||||
let enhanceRange = enhanceRangeStart...enhanceRangeEnd
|
||||
let transactions = try await blockEnhancer.enhance(
|
||||
at: enhanceRange,
|
||||
|
@ -80,6 +84,8 @@ extension EnhanceAction: Action {
|
|||
}
|
||||
)
|
||||
|
||||
await context.update(lastEnhancedHeight: enhanceRange.upperBound)
|
||||
|
||||
if let transactions {
|
||||
await didUpdate(.foundTransactions(transactions, enhanceRange))
|
||||
}
|
||||
|
|
|
@ -21,14 +21,12 @@ extension FetchUTXOsAction: Action {
|
|||
var removeBlocksCacheWhenFailed: Bool { false }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
if let range = await context.syncRanges.fetchUTXORange {
|
||||
logger.debug("Fetching UTXO with range: \(range.lowerBound)...\(range.upperBound)")
|
||||
let result = try await utxoFetcher.fetch(at: range) { fetchProgress in
|
||||
await didUpdate(.progressPartialUpdate(.fetch(fetchProgress)))
|
||||
}
|
||||
await didUpdate(.storedUTXOs(result))
|
||||
logger.debug("Fetching UTXOs")
|
||||
let result = try await utxoFetcher.fetch() { fetchProgress in
|
||||
await didUpdate(.progressPartialUpdate(.fetch(fetchProgress)))
|
||||
}
|
||||
|
||||
await didUpdate(.storedUTXOs(result))
|
||||
|
||||
await context.update(state: .handleSaplingParams)
|
||||
return context
|
||||
}
|
||||
|
|
|
@ -9,14 +9,12 @@ import Foundation
|
|||
|
||||
final class MigrateLegacyCacheDBAction {
|
||||
private let configProvider: CompactBlockProcessor.ConfigProvider
|
||||
private let internalSyncProgress: InternalSyncProgress
|
||||
private let storage: CompactBlockRepository
|
||||
private let transactionRepository: TransactionRepository
|
||||
private let fileManager: ZcashFileManager
|
||||
|
||||
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
||||
self.configProvider = configProvider
|
||||
internalSyncProgress = container.resolve(InternalSyncProgress.self)
|
||||
storage = container.resolve(CompactBlockRepository.self)
|
||||
transactionRepository = container.resolve(TransactionRepository.self)
|
||||
fileManager = container.resolve(ZcashFileManager.self)
|
||||
|
@ -43,7 +41,7 @@ extension MigrateLegacyCacheDBAction: Action {
|
|||
|
||||
// Instance with alias `default` is same as instance before the Alias was introduced. So it makes sense that only this instance handles
|
||||
// legacy cache DB. Any instance with different than `default` alias was created after the Alias was introduced and at this point legacy
|
||||
// cache DB is't anymore. So there is nothing to migrate for instances with not default Alias.
|
||||
// cache DB doesn't exist anymore. So there is nothing to migrate for instances with not default Alias.
|
||||
guard config.alias == .default else {
|
||||
return await updateState(context)
|
||||
}
|
||||
|
@ -65,13 +63,6 @@ extension MigrateLegacyCacheDBAction: Action {
|
|||
// create the storage
|
||||
try await self.storage.create()
|
||||
|
||||
// The database has been deleted, so we have adjust the internal state of the
|
||||
// `CompactBlockProcessor` so that it doesn't rely on download heights set
|
||||
// by a previous processing cycle.
|
||||
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
|
||||
|
||||
try await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
|
||||
|
||||
return await updateState(context)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,16 +30,17 @@ extension ScanAction: Action {
|
|||
var removeBlocksCacheWhenFailed: Bool { true }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
guard let scanRange = await context.syncRanges.scanRange else {
|
||||
guard let lastScannedHeight = await context.syncControlData.latestScannedHeight else {
|
||||
return await update(context: context)
|
||||
}
|
||||
|
||||
let config = await configProvider.config
|
||||
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
|
||||
let lastScannedHeightDB = try await transactionRepository.lastScannedHeight()
|
||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
||||
// This action is executed for each batch (batch size is 100 blocks by default) until all the blocks in whole `scanRange` are scanned.
|
||||
// So the right range for this batch must be computed.
|
||||
let batchRangeStart = max(scanRange.lowerBound, lastScannedHeight)
|
||||
let batchRangeEnd = min(scanRange.upperBound, batchRangeStart + config.batchSize)
|
||||
let batchRangeStart = max(lastScannedHeightDB, lastScannedHeight)
|
||||
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
||||
|
||||
guard batchRangeStart <= batchRangeEnd else {
|
||||
return await update(context: context)
|
||||
|
|
|
@ -52,7 +52,7 @@ extension ValidateServerAction: Action {
|
|||
throw ZcashError.compactBlockProcessorWrongConsensusBranchId(localBranch, remoteBranchID)
|
||||
}
|
||||
|
||||
await context.update(state: .computeSyncRanges)
|
||||
await context.update(state: .computeSyncControlData)
|
||||
return context
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ actor CompactBlockProcessor {
|
|||
|
||||
private let accountRepository: AccountRepository
|
||||
let blockDownloaderService: BlockDownloaderService
|
||||
private let internalSyncProgress: InternalSyncProgress
|
||||
private let latestBlocksDataProvider: LatestBlocksDataProvider
|
||||
private let logger: Logger
|
||||
private let metrics: SDKMetrics
|
||||
|
@ -190,7 +189,6 @@ actor CompactBlockProcessor {
|
|||
self.metrics = container.resolve(SDKMetrics.self)
|
||||
self.logger = container.resolve(Logger.self)
|
||||
self.latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
|
||||
self.internalSyncProgress = container.resolve(InternalSyncProgress.self)
|
||||
self.blockDownloaderService = container.resolve(BlockDownloaderService.self)
|
||||
self.service = container.resolve(LightWalletService.self)
|
||||
self.rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
||||
|
@ -216,10 +214,8 @@ actor CompactBlockProcessor {
|
|||
action = MigrateLegacyCacheDBAction(container: container, configProvider: configProvider)
|
||||
case .validateServer:
|
||||
action = ValidateServerAction(container: container, configProvider: configProvider)
|
||||
case .computeSyncRanges:
|
||||
action = ComputeSyncRangesAction(container: container, configProvider: configProvider)
|
||||
case .checksBeforeSync:
|
||||
action = ChecksBeforeSyncAction(container: container)
|
||||
case .computeSyncControlData:
|
||||
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
|
||||
case .download:
|
||||
action = DownloadAction(container: container, configProvider: configProvider)
|
||||
case .validate:
|
||||
|
@ -313,7 +309,7 @@ extension CompactBlockProcessor {
|
|||
|
||||
private func doRewind(context: AfterSyncHooksManager.RewindContext) async throws {
|
||||
logger.debug("Executing rewind.")
|
||||
let lastDownloaded = try await internalSyncProgress.latestDownloadedBlockHeight
|
||||
let lastDownloaded = await latestBlocksDataProvider.latestScannedHeight
|
||||
let height = Int32(context.height ?? lastDownloaded)
|
||||
|
||||
let nearestHeight: Int32
|
||||
|
@ -328,6 +324,7 @@ extension CompactBlockProcessor {
|
|||
let rewindHeight = max(Int32(nearestHeight - 1), Int32(config.walletBirthday))
|
||||
|
||||
do {
|
||||
try await rewindDownloadBlockAction(to: BlockHeight(rewindHeight))
|
||||
try await rustBackend.rewindToHeight(height: rewindHeight)
|
||||
} catch {
|
||||
await failure(error)
|
||||
|
@ -342,12 +339,22 @@ extension CompactBlockProcessor {
|
|||
return await context.completion(.failure(error))
|
||||
}
|
||||
|
||||
try await internalSyncProgress.rewind(to: rewindBlockHeight)
|
||||
|
||||
await context.completion(.success(rewindBlockHeight))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private extension CompactBlockProcessor {
|
||||
func rewindDownloadBlockAction(to rewindHeight: BlockHeight?) async throws {
|
||||
if let downloadAction = actions[.download] as? DownloadAction {
|
||||
await downloadAction.downloader.rewind(latestDownloadedBlockHeight: rewindHeight)
|
||||
} else {
|
||||
throw ZcashError.compactBlockProcessorDownloadBlockActionRewind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Wipe
|
||||
|
||||
extension CompactBlockProcessor {
|
||||
|
@ -369,7 +376,6 @@ extension CompactBlockProcessor {
|
|||
|
||||
do {
|
||||
try await self.storage.clear()
|
||||
try await internalSyncProgress.rewind(to: 0)
|
||||
|
||||
wipeLegacyCacheDbIfNeeded()
|
||||
|
||||
|
@ -378,6 +384,8 @@ extension CompactBlockProcessor {
|
|||
try fileManager.removeItem(at: config.dataDb)
|
||||
}
|
||||
|
||||
try await rewindDownloadBlockAction(to: nil)
|
||||
|
||||
await context.completion(nil)
|
||||
} catch {
|
||||
await context.completion(error)
|
||||
|
@ -468,7 +476,7 @@ extension CompactBlockProcessor {
|
|||
// Side effect of calling stop is to delete last used download stream. To be sure that it doesn't keep any data in memory.
|
||||
await stopAllActions()
|
||||
// Update state to the first state in state machine that can be handled by action.
|
||||
await context.update(state: .migrateLegacyCacheDB)
|
||||
await context.update(state: .clearCache)
|
||||
await syncStarted()
|
||||
|
||||
if backoffTimer == nil {
|
||||
|
@ -579,9 +587,7 @@ extension CompactBlockProcessor {
|
|||
break
|
||||
case .validateServer:
|
||||
break
|
||||
case .computeSyncRanges:
|
||||
break
|
||||
case .checksBeforeSync:
|
||||
case .computeSyncControlData:
|
||||
break
|
||||
case .download:
|
||||
break
|
||||
|
@ -609,7 +615,9 @@ extension CompactBlockProcessor {
|
|||
}
|
||||
|
||||
private func resetContext() async {
|
||||
let lastEnhancedheight = await context.lastEnhancedHeight
|
||||
context = ActionContext(state: .idle)
|
||||
await context.update(lastEnhancedHeight: lastEnhancedheight)
|
||||
await compactBlockProgress.reset()
|
||||
}
|
||||
|
||||
|
@ -621,7 +629,7 @@ extension CompactBlockProcessor {
|
|||
|
||||
private func syncFinished() async -> Bool {
|
||||
logger.debug("Sync finished")
|
||||
let latestBlockHeightWhenSyncing = await context.syncRanges.latestBlockHeight
|
||||
let latestBlockHeightWhenSyncing = await context.syncControlData.latestBlockHeight
|
||||
let latestBlockHeight = await latestBlocksDataProvider.latestBlockHeight
|
||||
// If `latestBlockHeightWhenSyncing` is 0 then it means that there was nothing to sync in last sync process.
|
||||
let newerBlocksWereMinedDuringSync =
|
||||
|
@ -659,7 +667,8 @@ extension CompactBlockProcessor {
|
|||
try await rustBackend.rewindToHeight(height: Int32(rewindHeight))
|
||||
|
||||
try await blockDownloaderService.rewind(to: rewindHeight)
|
||||
try await internalSyncProgress.rewind(to: rewindHeight)
|
||||
|
||||
try await rewindDownloadBlockAction(to: rewindHeight)
|
||||
|
||||
await send(event: .handledReorg(height, rewindHeight))
|
||||
}
|
||||
|
@ -758,20 +767,7 @@ extension CompactBlockProcessor {
|
|||
|
||||
private func ifTaskIsNotCanceledClearCompactBlockCache() async {
|
||||
guard !Task.isCancelled else { return }
|
||||
let lastScannedHeight = await latestBlocksDataProvider.latestScannedHeight
|
||||
do {
|
||||
// Blocks download work in parallel with scanning. So imagine this scenario:
|
||||
//
|
||||
// Scanning is done until height 10300. Blocks are downloaded until height 10400.
|
||||
// And now validation fails and this method is called. And `.latestDownloadedBlockHeight` in `internalSyncProgress` is set to 10400. And
|
||||
// all the downloaded blocks are removed here.
|
||||
//
|
||||
// If this line doesn't happen then when sync starts next time it thinks that all the blocks are downloaded until 10400. But all were
|
||||
// removed. So blocks between 10300 and 10400 wouldn't ever be scanned.
|
||||
//
|
||||
// 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.
|
||||
try await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
|
||||
try await clearCompactBlockCache()
|
||||
} catch {
|
||||
logger.error("`clearCompactBlockCache` failed after error: \(error)")
|
||||
|
|
|
@ -52,6 +52,14 @@ protocol BlockDownloader {
|
|||
/// called before then nothing is downloaded.
|
||||
/// - Parameter range: Wait until blocks from `range` are downloaded.
|
||||
func waitUntilRequestedBlocksAreDownloaded(in range: CompactBlockRange) async throws
|
||||
|
||||
/// Updates the internal in memory value of latest downloaded block height. This way the `BlockDownloader` works with the current latest height and can
|
||||
/// continue on parallel downloading of next batch.
|
||||
func update(latestDownloadedBlockHeight: BlockHeight) async
|
||||
/// Provides the value of latest downloaded height.
|
||||
func latestDownloadedBlockHeight() async -> BlockHeight
|
||||
/// In case rewind is needed, the latestDownloadedBlockHeight is rewritten forcefully.
|
||||
func rewind(latestDownloadedBlockHeight: BlockHeight?) async
|
||||
}
|
||||
|
||||
actor BlockDownloaderImpl {
|
||||
|
@ -62,9 +70,9 @@ actor BlockDownloaderImpl {
|
|||
let service: LightWalletService
|
||||
let downloaderService: BlockDownloaderService
|
||||
let storage: CompactBlockRepository
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
var latestDownloadedBlockHeight: BlockHeight = -1
|
||||
|
||||
private var downloadStreamCreatedAtRange: CompactBlockRange = 0...0
|
||||
private var downloadStream: BlockDownloaderStream?
|
||||
|
@ -80,14 +88,12 @@ actor BlockDownloaderImpl {
|
|||
service: LightWalletService,
|
||||
downloaderService: BlockDownloaderService,
|
||||
storage: CompactBlockRepository,
|
||||
internalSyncProgress: InternalSyncProgress,
|
||||
metrics: SDKMetrics,
|
||||
logger: Logger
|
||||
) {
|
||||
self.service = service
|
||||
self.downloaderService = downloaderService
|
||||
self.storage = storage
|
||||
self.internalSyncProgress = internalSyncProgress
|
||||
self.metrics = metrics
|
||||
self.logger = logger
|
||||
}
|
||||
|
@ -100,8 +106,6 @@ actor BlockDownloaderImpl {
|
|||
throw ZcashError.blockDownloadSyncRangeNotSet
|
||||
}
|
||||
|
||||
let latestDownloadedBlockHeight = try await internalSyncProgress.latestDownloadedBlockHeight
|
||||
|
||||
let downloadFrom = max(syncRange.lowerBound, latestDownloadedBlockHeight + 1)
|
||||
let downloadTo = min(downloadToHeight, syncRange.upperBound)
|
||||
|
||||
|
@ -240,11 +244,25 @@ actor BlockDownloaderImpl {
|
|||
|
||||
private func blocksBufferWritten(_ buffer: [ZcashCompactBlock]) async throws {
|
||||
guard let lastBlock = buffer.last else { return }
|
||||
try await internalSyncProgress.set(lastBlock.height, .latestDownloadedBlockHeight)
|
||||
latestDownloadedBlockHeight = lastBlock.height
|
||||
}
|
||||
}
|
||||
|
||||
extension BlockDownloaderImpl: BlockDownloader {
|
||||
func rewind(latestDownloadedBlockHeight: BlockHeight?) async {
|
||||
self.latestDownloadedBlockHeight = latestDownloadedBlockHeight ?? -1
|
||||
}
|
||||
|
||||
func update(latestDownloadedBlockHeight: BlockHeight) async {
|
||||
if latestDownloadedBlockHeight >= self.latestDownloadedBlockHeight {
|
||||
self.latestDownloadedBlockHeight = latestDownloadedBlockHeight
|
||||
}
|
||||
}
|
||||
|
||||
func latestDownloadedBlockHeight() async -> BlockHeight {
|
||||
latestDownloadedBlockHeight
|
||||
}
|
||||
|
||||
func setDownloadLimit(_ limit: BlockHeight) async {
|
||||
downloadToHeight = limit
|
||||
}
|
||||
|
@ -283,13 +301,13 @@ extension BlockDownloaderImpl: BlockDownloader {
|
|||
|
||||
func waitUntilRequestedBlocksAreDownloaded(in range: CompactBlockRange) async throws {
|
||||
logger.debug("Waiting until requested blocks are downloaded at \(range)")
|
||||
var latestDownloadedBlock = try await internalSyncProgress.latestDownloadedBlockHeight
|
||||
var latestDownloadedBlock = latestDownloadedBlockHeight
|
||||
while latestDownloadedBlock < range.upperBound {
|
||||
if let error = lastError {
|
||||
throw error
|
||||
}
|
||||
try await Task.sleep(milliseconds: 10)
|
||||
latestDownloadedBlock = try await internalSyncProgress.latestDownloadedBlockHeight
|
||||
latestDownloadedBlock = latestDownloadedBlockHeight
|
||||
}
|
||||
logger.debug("Waiting done. Blocks are downloaded at \(range)")
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@ protocol BlockEnhancer {
|
|||
|
||||
struct BlockEnhancerImpl {
|
||||
let blockDownloaderService: BlockDownloaderService
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let rustBackend: ZcashRustBackendWelding
|
||||
let transactionRepository: TransactionRepository
|
||||
let metrics: SDKMetrics
|
||||
|
@ -96,7 +95,6 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
let transactions = try await transactionRepository.find(in: range, limit: Int.max, kind: .all)
|
||||
|
||||
guard !transactions.isEmpty else {
|
||||
try await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
|
||||
logger.debug("no transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
|
||||
return nil
|
||||
}
|
||||
|
@ -126,10 +124,6 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
)
|
||||
|
||||
await didEnhance(progress)
|
||||
|
||||
if let minedHeight = confirmedTx.minedHeight {
|
||||
try await internalSyncProgress.set(minedHeight, .latestEnhancedHeight)
|
||||
}
|
||||
} catch {
|
||||
retries += 1
|
||||
logger.error("could not enhance txId \(transaction.rawID.toHexStringTxId()) - Error: \(error)")
|
||||
|
@ -155,8 +149,6 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
logger.error("error enhancing transactions! \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
try await internalSyncProgress.set(range.upperBound, .latestEnhancedHeight)
|
||||
|
||||
if Task.isCancelled {
|
||||
logger.debug("Warning: compactBlockEnhancement on range \(range) cancelled")
|
||||
|
|
|
@ -18,7 +18,6 @@ struct UTXOFetcherConfig {
|
|||
|
||||
protocol UTXOFetcher {
|
||||
func fetch(
|
||||
at range: CompactBlockRange,
|
||||
didFetch: @escaping (Float) async -> Void
|
||||
) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])
|
||||
}
|
||||
|
@ -27,7 +26,6 @@ struct UTXOFetcherImpl {
|
|||
let accountRepository: AccountRepository
|
||||
let blockDownloaderService: BlockDownloaderService
|
||||
let config: UTXOFetcherConfig
|
||||
let internalSyncProgress: InternalSyncProgress
|
||||
let rustBackend: ZcashRustBackendWelding
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
|
@ -35,7 +33,6 @@ struct UTXOFetcherImpl {
|
|||
|
||||
extension UTXOFetcherImpl: UTXOFetcher {
|
||||
func fetch(
|
||||
at range: CompactBlockRange,
|
||||
didFetch: @escaping (Float) async -> Void
|
||||
) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) {
|
||||
try Task.checkCancellation()
|
||||
|
@ -82,7 +79,6 @@ extension UTXOFetcherImpl: UTXOFetcher {
|
|||
|
||||
counter += 1
|
||||
await didFetch(counter / all)
|
||||
try await internalSyncProgress.set(utxo.height, .latestUTXOFetchedHeight)
|
||||
} catch {
|
||||
logger.error("failed to put utxo - error: \(error)")
|
||||
skipped.append(utxo)
|
||||
|
@ -91,22 +87,20 @@ extension UTXOFetcherImpl: UTXOFetcher {
|
|||
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(
|
||||
startHeight: range.lowerBound,
|
||||
targetHeight: range.upperBound,
|
||||
progressHeight: range.upperBound
|
||||
startHeight: 0,
|
||||
targetHeight: 1,
|
||||
progressHeight: 1
|
||||
),
|
||||
start: startTime,
|
||||
end: Date(),
|
||||
batchSize: range.count,
|
||||
batchSize: 1,
|
||||
operation: .fetchUTXOs
|
||||
)
|
||||
|
||||
let result = (inserted: refreshed, skipped: skipped)
|
||||
|
||||
try await internalSyncProgress.set(range.upperBound, .latestUTXOFetchedHeight)
|
||||
|
||||
if Task.isCancelled {
|
||||
logger.debug("Warning: fetchUnspentTxOutputs on range \(range) cancelled")
|
||||
logger.debug("Warning: fetchUnspentTxOutputs cancelled")
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
//
|
||||
// InternalSyncProgress.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 23.11.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SyncRanges: Equatable {
|
||||
let latestBlockHeight: BlockHeight
|
||||
// Range of blocks that are not yet downloaded
|
||||
let downloadRange: CompactBlockRange?
|
||||
/// Range of blocks that are not yet scanned.
|
||||
let scanRange: CompactBlockRange?
|
||||
/// Range of blocks that are not enhanced yet.
|
||||
let enhanceRange: CompactBlockRange?
|
||||
/// Range of blocks for which no UTXOs are fetched yet.
|
||||
let fetchUTXORange: CompactBlockRange?
|
||||
|
||||
let latestScannedHeight: BlockHeight?
|
||||
let latestDownloadedBlockHeight: BlockHeight?
|
||||
|
||||
static var empty: SyncRanges {
|
||||
SyncRanges(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: nil,
|
||||
scanRange: nil,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: nil,
|
||||
latestDownloadedBlockHeight: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum NextState: Equatable {
|
||||
case finishProcessing(height: BlockHeight)
|
||||
case processNewBlocks(ranges: SyncRanges)
|
||||
case wait(latestHeight: BlockHeight, latestDownloadHeight: BlockHeight)
|
||||
}
|
||||
|
||||
protocol InternalSyncProgressStorage {
|
||||
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
|
||||
// sourcery: mockedName="setBool"
|
||||
func set(_ value: Bool, for key: String) async throws
|
||||
}
|
||||
|
||||
actor InternalSyncProgress {
|
||||
enum Key: String, CaseIterable {
|
||||
case latestDownloadedBlockHeight
|
||||
case latestEnhancedHeight
|
||||
case latestUTXOFetchedHeight
|
||||
|
||||
func with(_ alias: ZcashSynchronizerAlias) -> String {
|
||||
switch alias {
|
||||
case .`default`:
|
||||
return self.rawValue
|
||||
case let .custom(rawAlias):
|
||||
return "\(self.rawValue)_\(rawAlias)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let alias: ZcashSynchronizerAlias
|
||||
private let storage: InternalSyncProgressStorage
|
||||
let logger: Logger
|
||||
|
||||
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
|
||||
self.storage = storage
|
||||
self.logger = logger
|
||||
}
|
||||
|
||||
func initialize() async throws {
|
||||
try await storage.initialize()
|
||||
}
|
||||
|
||||
func load(_ key: Key) async throws -> BlockHeight {
|
||||
return try await storage.integer(for: key.with(alias))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// `InternalSyncProgress` is from now on used to track which block were already downloaded. Previous versions of the SDK were using cache DB to
|
||||
/// 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,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// resumed.
|
||||
///
|
||||
/// The sync process has 4 phases (download, scan, enhance, fetch UTXO). `InternalSyncProgress` tracks independently which blocks were already
|
||||
/// processed in each phase. To compute the next state these 4 numbers are compared with `latestBlockHeight`.
|
||||
///
|
||||
/// - If any of these numbers are larger than `latestBlockHeight` then `wait` is used as the next state. We have locally higher block heights than
|
||||
/// are currently available at LightWalletd.
|
||||
/// - If any of these numbers are lower than `latestBlockHeight` then `processNewBlocks` is used as the next state. The sync process should run.
|
||||
/// - Otherwise `finishProcessing` is used as the next state. It means that local data are synced with what is available at LightWalletd.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - latestBlockHeight: Latest height fetched from LightWalletd API.
|
||||
/// - latestScannedHeight: Latest height of latest block scanned.
|
||||
/// - walletBirthday: Wallet birthday.
|
||||
/// - Returns: Computed state.
|
||||
func computeNextState(
|
||||
latestBlockHeight: BlockHeight,
|
||||
latestScannedHeight: BlockHeight,
|
||||
walletBirthday: BlockHeight
|
||||
) async throws -> 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)
|
||||
latestDownloadedHeight: \(latestDownloadedBlockHeight)
|
||||
latestScannedHeight: \(latestScannedHeight)
|
||||
latestEnhancedHeight: \(latestEnhancedHeight)
|
||||
latestUTXOFetchedHeight: \(latestUTXOFetchedHeight)
|
||||
""")
|
||||
|
||||
if latestDownloadedBlockHeight > latestBlockHeight ||
|
||||
latestScannedHeight > latestBlockHeight ||
|
||||
latestEnhancedHeight > latestBlockHeight ||
|
||||
latestUTXOFetchedHeight > latestBlockHeight {
|
||||
return .wait(latestHeight: latestBlockHeight, latestDownloadHeight: latestDownloadedBlockHeight)
|
||||
} else if latestDownloadedBlockHeight < latestBlockHeight ||
|
||||
latestScannedHeight < latestBlockHeight ||
|
||||
latestEnhancedHeight < latestEnhancedHeight ||
|
||||
latestUTXOFetchedHeight < latestBlockHeight {
|
||||
let ranges = try await computeSyncRanges(
|
||||
birthday: walletBirthday,
|
||||
latestBlockHeight: latestBlockHeight,
|
||||
latestScannedHeight: latestScannedHeight
|
||||
)
|
||||
return .processNewBlocks(ranges: ranges)
|
||||
} else {
|
||||
return .finishProcessing(height: latestBlockHeight)
|
||||
}
|
||||
}
|
||||
|
||||
func computeSyncRanges(
|
||||
birthday: BlockHeight,
|
||||
latestBlockHeight: BlockHeight,
|
||||
latestScannedHeight: BlockHeight
|
||||
) async throws -> SyncRanges {
|
||||
let latestDownloadedBlockHeight = try await self.latestDownloadedBlockHeight
|
||||
let latestEnhancedHeight = try await self.latestEnhancedHeight
|
||||
let latestUTXOFetchedHeight = try await self.latestUTXOFetchedHeight
|
||||
|
||||
if latestScannedHeight > latestDownloadedBlockHeight {
|
||||
logger.warn("""
|
||||
InternalSyncProgress found inconsistent state.
|
||||
latestBlockHeight: \(latestBlockHeight)
|
||||
--> latestDownloadedHeight: \(latestDownloadedBlockHeight)
|
||||
latestScannedHeight: \(latestScannedHeight)
|
||||
latestEnhancedHeight: \(latestEnhancedHeight)
|
||||
latestUTXOFetchedHeight: \(latestUTXOFetchedHeight)
|
||||
""")
|
||||
}
|
||||
|
||||
let downloadRange = computeRange(latestHeight: latestDownloadedBlockHeight, birthday: birthday, latestBlockHeight: latestBlockHeight)
|
||||
let scanRange = computeRange(latestHeight: latestScannedHeight, birthday: birthday, latestBlockHeight: latestBlockHeight)
|
||||
|
||||
return SyncRanges(
|
||||
latestBlockHeight: latestBlockHeight,
|
||||
downloadRange: downloadRange,
|
||||
scanRange: scanRange,
|
||||
enhanceRange: computeRange(latestHeight: latestEnhancedHeight, birthday: birthday, latestBlockHeight: latestBlockHeight),
|
||||
fetchUTXORange: computeRange(latestHeight: latestUTXOFetchedHeight, birthday: birthday, latestBlockHeight: latestBlockHeight),
|
||||
latestScannedHeight: latestScannedHeight,
|
||||
latestDownloadedBlockHeight: latestDownloadedBlockHeight
|
||||
)
|
||||
}
|
||||
|
||||
private func computeRange(latestHeight: BlockHeight, birthday: BlockHeight, latestBlockHeight: BlockHeight) -> CompactBlockRange? {
|
||||
guard latestHeight < latestBlockHeight else { return nil }
|
||||
let lowerBound = latestHeight <= birthday ? birthday : latestHeight + 1
|
||||
return lowerBound...latestBlockHeight
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// SyncControlData.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 23.11.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SyncControlData: Equatable {
|
||||
/// The tip of the blockchain
|
||||
let latestBlockHeight: BlockHeight
|
||||
/// The last height that has been scanned
|
||||
let latestScannedHeight: BlockHeight?
|
||||
/// The height from the enhancement must start
|
||||
let firstUnenhancedHeight: BlockHeight?
|
||||
|
||||
static var empty: SyncControlData {
|
||||
SyncControlData(
|
||||
latestBlockHeight: 0,
|
||||
latestScannedHeight: nil,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
}
|
||||
}
|
|
@ -509,6 +509,9 @@ public enum ZcashError: Equatable, Error {
|
|||
/// Consensus BranchIDs don't match this is probably an API or programming error.
|
||||
/// ZCBPEO0017
|
||||
case compactBlockProcessorConsensusBranchID
|
||||
/// Rewind of DownloadBlockAction failed as no action is possible to unwrapp.
|
||||
/// ZCBPEO0018
|
||||
case compactBlockProcessorDownloadBlockActionRewind
|
||||
/// The synchronizer is unprepared.
|
||||
/// ZSYNCO0001
|
||||
case synchronizerNotPrepared
|
||||
|
@ -527,12 +530,6 @@ 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 {
|
||||
|
@ -682,14 +679,13 @@ public enum ZcashError: Equatable, Error {
|
|||
case .compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb: return "Deletion of readable file at the provided URL failed."
|
||||
case .compactBlockProcessorChainName: return "Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error."
|
||||
case .compactBlockProcessorConsensusBranchID: return "Consensus BranchIDs don't match this is probably an API or programming error."
|
||||
case .compactBlockProcessorDownloadBlockActionRewind: return "Rewind of DownloadBlockAction failed as no action is possible to unwrapp."
|
||||
case .synchronizerNotPrepared: return "The synchronizer is unprepared."
|
||||
case .synchronizerSendMemoToTransparentAddress: return "Memos can't be sent to transparent addresses."
|
||||
case .synchronizerShieldFundsInsuficientTransparentFunds: return "There is not enough transparent funds to cover fee for the shielding."
|
||||
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."
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -841,14 +837,13 @@ public enum ZcashError: Equatable, Error {
|
|||
case .compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb: return .compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb
|
||||
case .compactBlockProcessorChainName: return .compactBlockProcessorChainName
|
||||
case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID
|
||||
case .compactBlockProcessorDownloadBlockActionRewind: return .compactBlockProcessorDownloadBlockActionRewind
|
||||
case .synchronizerNotPrepared: return .synchronizerNotPrepared
|
||||
case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress
|
||||
case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds
|
||||
case .synchronizerLatestUTXOsInvalidTAddress: return .synchronizerLatestUTXOsInvalidTAddress
|
||||
case .synchronizerRewindUnknownArchorHeight: return .synchronizerRewindUnknownArchorHeight
|
||||
case .synchronizerDisconnected: return .synchronizerDisconnected
|
||||
case .ispStorageCantLoad: return .ispStorageCantLoad
|
||||
case .ispStorageCantWrite: return .ispStorageCantWrite
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -301,6 +301,8 @@ public enum ZcashErrorCode: String {
|
|||
case compactBlockProcessorChainName = "ZCBPEO0016"
|
||||
/// Consensus BranchIDs don't match this is probably an API or programming error.
|
||||
case compactBlockProcessorConsensusBranchID = "ZCBPEO0017"
|
||||
/// Rewind of DownloadBlockAction failed as no action is possible to unwrapp.
|
||||
case compactBlockProcessorDownloadBlockActionRewind = "ZCBPEO0018"
|
||||
/// The synchronizer is unprepared.
|
||||
case synchronizerNotPrepared = "ZSYNCO0001"
|
||||
/// Memos can't be sent to transparent addresses.
|
||||
|
@ -313,8 +315,4 @@ 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"
|
||||
}
|
||||
|
|
|
@ -589,7 +589,10 @@ enum ZcashErrorDefinition {
|
|||
/// Consensus BranchIDs don't match this is probably an API or programming error.
|
||||
// sourcery: code="ZCBPEO0017"
|
||||
case compactBlockProcessorConsensusBranchID
|
||||
|
||||
/// Rewind of DownloadBlockAction failed as no action is possible to unwrapp.
|
||||
// sourcery: code="ZCBPEO0018"
|
||||
case compactBlockProcessorDownloadBlockActionRewind
|
||||
|
||||
// MARK: - SDKSynchronizer
|
||||
|
||||
/// The synchronizer is unprepared.
|
||||
|
@ -610,13 +613,4 @@ 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)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ protocol LatestBlocksDataProvider {
|
|||
var latestScannedTime: TimeInterval { get async }
|
||||
var latestBlockHeight: BlockHeight { get async }
|
||||
var walletBirthday: BlockHeight { get async }
|
||||
var firstUnenhancedHeight: BlockHeight? { get async }
|
||||
|
||||
func updateScannedData() async
|
||||
func updateBlockData() async
|
||||
|
@ -27,6 +28,7 @@ actor LatestBlocksDataProviderImpl: LatestBlocksDataProvider {
|
|||
// Valid values are stored here after Synchronizer's `prepare` is called.
|
||||
private(set) var latestScannedHeight: BlockHeight = .zero
|
||||
private(set) var latestScannedTime: TimeInterval = 0.0
|
||||
private(set) var firstUnenhancedHeight: BlockHeight?
|
||||
// Valid value is stored here after block processor's `nextState` is called.
|
||||
private(set) var latestBlockHeight: BlockHeight = .zero
|
||||
// Valid values are stored here after Synchronizer's `prepare` is called.
|
||||
|
@ -54,7 +56,7 @@ actor LatestBlocksDataProviderImpl: LatestBlocksDataProvider {
|
|||
latestBlockHeight = newLatestBlockHeight
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func updateWalletBirthday(_ walletBirthday: BlockHeight) async {
|
||||
self.walletBirthday = walletBirthday
|
||||
}
|
||||
|
|
|
@ -88,12 +88,6 @@ enum Dependencies {
|
|||
container.register(type: SyncSessionIDGenerator.self, isSingleton: false) { _ in
|
||||
UniqueSyncSessionIDGenerator()
|
||||
}
|
||||
|
||||
container.register(type: InternalSyncProgress.self, isSingleton: true) { di in
|
||||
let logger = di.resolve(Logger.self)
|
||||
let storage = InternalSyncProgressDiskStorage(storageURL: urls.generalStorageURL, logger: logger)
|
||||
return InternalSyncProgress(alias: alias, storage: storage, logger: logger)
|
||||
}
|
||||
|
||||
container.register(type: ZcashFileManager.self, isSingleton: true) { _ in
|
||||
FileManager.default
|
||||
|
@ -109,7 +103,6 @@ enum Dependencies {
|
|||
let service = di.resolve(LightWalletService.self)
|
||||
let blockDownloaderService = di.resolve(BlockDownloaderService.self)
|
||||
let storage = di.resolve(CompactBlockRepository.self)
|
||||
let internalSyncProgress = di.resolve(InternalSyncProgress.self)
|
||||
let metrics = di.resolve(SDKMetrics.self)
|
||||
let logger = di.resolve(Logger.self)
|
||||
|
||||
|
@ -117,7 +110,6 @@ enum Dependencies {
|
|||
service: service,
|
||||
downloaderService: blockDownloaderService,
|
||||
storage: storage,
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
|
@ -159,7 +151,6 @@ enum Dependencies {
|
|||
|
||||
container.register(type: BlockEnhancer.self, isSingleton: true) { di in
|
||||
let blockDownloaderService = di.resolve(BlockDownloaderService.self)
|
||||
let internalSyncProgress = di.resolve(InternalSyncProgress.self)
|
||||
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
|
||||
let transactionRepository = di.resolve(TransactionRepository.self)
|
||||
let metrics = di.resolve(SDKMetrics.self)
|
||||
|
@ -167,7 +158,6 @@ enum Dependencies {
|
|||
|
||||
return BlockEnhancerImpl(
|
||||
blockDownloaderService: blockDownloaderService,
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
rustBackend: rustBackend,
|
||||
transactionRepository: transactionRepository,
|
||||
metrics: metrics,
|
||||
|
@ -178,7 +168,6 @@ enum Dependencies {
|
|||
container.register(type: UTXOFetcher.self, isSingleton: true) { di in
|
||||
let blockDownloaderService = di.resolve(BlockDownloaderService.self)
|
||||
let utxoFetcherConfig = UTXOFetcherConfig(walletBirthdayProvider: config.walletBirthdayProvider)
|
||||
let internalSyncProgress = di.resolve(InternalSyncProgress.self)
|
||||
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
|
||||
let metrics = di.resolve(SDKMetrics.self)
|
||||
let logger = di.resolve(Logger.self)
|
||||
|
@ -187,7 +176,6 @@ enum Dependencies {
|
|||
accountRepository: accountRepository,
|
||||
blockDownloaderService: blockDownloaderService,
|
||||
config: utxoFetcherConfig,
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
rustBackend: rustBackend,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
|
|
|
@ -40,7 +40,6 @@ 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
|
||||
|
@ -88,7 +87,6 @@ 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)
|
||||
|
@ -139,7 +137,6 @@ 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
|
||||
|
|
|
@ -231,16 +231,6 @@ 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 = coordinator.synchronizer.internalSyncProgress
|
||||
|
||||
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")
|
||||
XCTAssertEqual(latestUTXOFetchedHeight, 0, "internalSyncProgress latestUTXOFetchedHeight should be 0")
|
||||
|
||||
let status = await coordinator.synchronizer.status
|
||||
XCTAssertEqual(status, .unprepared, "SDKSynchronizer state should be unprepared")
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
var endpoint: LightWalletEndpoint!
|
||||
var service: LightWalletService!
|
||||
var storage: FSCompactBlockRepository!
|
||||
var internalSyncProgress: InternalSyncProgress!
|
||||
var processorConfig: CompactBlockProcessor.Configuration!
|
||||
var latestBlockHeight: BlockHeight!
|
||||
var startHeight: BlockHeight!
|
||||
|
@ -65,7 +64,6 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
endpoint = nil
|
||||
service = nil
|
||||
storage = nil
|
||||
internalSyncProgress = nil
|
||||
processorConfig = nil
|
||||
}
|
||||
|
||||
|
@ -92,10 +90,6 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
)
|
||||
try await storage.create()
|
||||
|
||||
let internalSyncProgressStorage = InternalSyncProgressMemoryStorage()
|
||||
try await internalSyncProgressStorage.set(startHeight, for: InternalSyncProgress.Key.latestDownloadedBlockHeight.rawValue)
|
||||
internalSyncProgress = InternalSyncProgress(alias: .default, storage: internalSyncProgressStorage, logger: logger)
|
||||
|
||||
processorConfig = CompactBlockProcessor.Configuration.standard(
|
||||
for: ZcashNetworkBuilder.network(for: .testnet),
|
||||
walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight
|
||||
|
@ -113,7 +107,6 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
service: service,
|
||||
downloaderService: BlockDownloaderServiceImpl(service: service, storage: storage),
|
||||
storage: storage,
|
||||
internalSyncProgress: internalSyncProgress,
|
||||
metrics: SDKMetrics(),
|
||||
logger: logger
|
||||
)
|
||||
|
@ -141,24 +134,21 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
try await makeDependencies(timeout: 10000)
|
||||
|
||||
let action = DownloadAction(container: mockContainer, configProvider: CompactBlockProcessor.ConfigProvider(config: processorConfig))
|
||||
let syncRanges = SyncRanges(
|
||||
let blockDownloader = mockContainer.resolve(BlockDownloader.self)
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: latestBlockHeight,
|
||||
downloadRange: startHeight...latestBlockHeight,
|
||||
scanRange: nil,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: startHeight,
|
||||
latestDownloadedBlockHeight: startHeight
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
let context = ActionContext(state: .download)
|
||||
await context.update(syncRanges: syncRanges)
|
||||
await context.update(syncControlData: syncControlData)
|
||||
|
||||
let expectation = XCTestExpectation()
|
||||
|
||||
let cancelableTask = Task {
|
||||
do {
|
||||
_ = try await action.run(with: context, didUpdate: { _ in })
|
||||
let lastDownloadedHeight = try await internalSyncProgress.latestDownloadedBlockHeight
|
||||
let lastDownloadedHeight = await blockDownloader.latestDownloadedBlockHeight()
|
||||
// Just to be sure that download was interrupted before download was finished.
|
||||
XCTAssertLessThan(lastDownloadedHeight, latestBlockHeight)
|
||||
expectation.fulfill()
|
||||
|
@ -180,17 +170,13 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
try await makeDependencies(timeout: 100)
|
||||
|
||||
let action = DownloadAction(container: mockContainer, configProvider: CompactBlockProcessor.ConfigProvider(config: processorConfig))
|
||||
let syncRanges = SyncRanges(
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: latestBlockHeight,
|
||||
downloadRange: startHeight...latestBlockHeight,
|
||||
scanRange: nil,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: startHeight,
|
||||
latestDownloadedBlockHeight: startHeight
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
let context = ActionContext(state: .download)
|
||||
await context.update(syncRanges: syncRanges)
|
||||
await context.update(syncControlData: syncControlData)
|
||||
|
||||
let date = Date()
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
|||
var processorEventHandler: CompactBlockProcessorEventHandler! = CompactBlockProcessorEventHandler()
|
||||
var rustBackend: ZcashRustBackendWelding!
|
||||
var processor: CompactBlockProcessor!
|
||||
var syncStartedExpect: XCTestExpectation!
|
||||
var syncStartedExpectation: XCTestExpectation!
|
||||
var updatedNotificationExpectation: XCTestExpectation!
|
||||
var stopNotificationExpectation: XCTestExpectation!
|
||||
var finishedNotificationExpectation: XCTestExpectation!
|
||||
|
@ -30,10 +30,6 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
|||
try await super.setUp()
|
||||
logger = OSLogger(logLevel: .debug)
|
||||
|
||||
for key in InternalSyncProgress.Key.allCases {
|
||||
UserDefaults.standard.set(0, forKey: key.with(.default))
|
||||
}
|
||||
|
||||
let pathProvider = DefaultResourceProvider(network: network)
|
||||
processorConfig = CompactBlockProcessor.Configuration(
|
||||
alias: .default,
|
||||
|
@ -109,7 +105,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
|||
return
|
||||
}
|
||||
|
||||
syncStartedExpect = XCTestExpectation(description: "\(self.description) syncStartedExpect")
|
||||
syncStartedExpectation = XCTestExpectation(description: "\(self.description) syncStartedExpectation")
|
||||
stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation")
|
||||
updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation")
|
||||
finishedNotificationExpectation = XCTestExpectation(description: "\(self.description) finishedNotificationExpectation")
|
||||
|
@ -147,7 +143,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
|||
XCTAssertNotNil(processor)
|
||||
|
||||
let expectations: [CompactBlockProcessorEventHandler.EventIdentifier: XCTestExpectation] = [
|
||||
.startedSyncing: syncStartedExpect,
|
||||
.startedSyncing: syncStartedExpectation,
|
||||
.stopped: stopNotificationExpectation,
|
||||
.progressUpdated: updatedNotificationExpectation,
|
||||
.finished: finishedNotificationExpectation
|
||||
|
@ -162,7 +158,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
|||
|
||||
await fulfillment(
|
||||
of: [
|
||||
syncStartedExpect,
|
||||
syncStartedExpectation,
|
||||
finishedNotificationExpectation
|
||||
],
|
||||
timeout: 30,
|
||||
|
@ -186,108 +182,6 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
|||
(abs(currentHeight - targetHeight) / batchSize)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
var expectedSyncRanges = SyncRanges(
|
||||
latestBlockHeight: latestBlockchainHeight,
|
||||
downloadRange: latestDownloadedHeight...latestBlockchainHeight,
|
||||
scanRange: latestDownloadedHeight...latestBlockchainHeight,
|
||||
enhanceRange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
fetchUTXORange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
latestScannedHeight: 0,
|
||||
latestDownloadedBlockHeight: latestDownloadedHeight
|
||||
)
|
||||
|
||||
var internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight, alias: .default)
|
||||
|
||||
var syncRanges = try await internalSyncProgress.computeSyncRanges(
|
||||
birthday: processorConfig.walletBirthday,
|
||||
latestBlockHeight: latestBlockchainHeight,
|
||||
latestScannedHeight: 0
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
expectedSyncRanges,
|
||||
syncRanges,
|
||||
"Failure when testing first range"
|
||||
)
|
||||
|
||||
// Test mid-range
|
||||
latestDownloadedHeight = BlockHeight(network.constants.saplingActivationHeight + ZcashSDK.DefaultBatchSize)
|
||||
latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
|
||||
|
||||
expectedSyncRanges = SyncRanges(
|
||||
latestBlockHeight: latestBlockchainHeight,
|
||||
downloadRange: latestDownloadedHeight + 1...latestBlockchainHeight,
|
||||
scanRange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
enhanceRange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
fetchUTXORange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
latestScannedHeight: 0,
|
||||
latestDownloadedBlockHeight: latestDownloadedHeight
|
||||
)
|
||||
|
||||
internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight, alias: .default)
|
||||
|
||||
syncRanges = try await internalSyncProgress.computeSyncRanges(
|
||||
birthday: processorConfig.walletBirthday,
|
||||
latestBlockHeight: latestBlockchainHeight,
|
||||
latestScannedHeight: 0
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
expectedSyncRanges,
|
||||
syncRanges,
|
||||
"Failure when testing mid range"
|
||||
)
|
||||
|
||||
// Test last batch range
|
||||
|
||||
latestDownloadedHeight = BlockHeight(network.constants.saplingActivationHeight + 950)
|
||||
latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
|
||||
|
||||
expectedSyncRanges = SyncRanges(
|
||||
latestBlockHeight: latestBlockchainHeight,
|
||||
downloadRange: latestDownloadedHeight + 1...latestBlockchainHeight,
|
||||
scanRange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
enhanceRange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
fetchUTXORange: processorConfig.walletBirthday...latestBlockchainHeight,
|
||||
latestScannedHeight: 0,
|
||||
latestDownloadedBlockHeight: latestDownloadedHeight
|
||||
)
|
||||
|
||||
internalSyncProgress = InternalSyncProgress(
|
||||
alias: .default,
|
||||
storage: InternalSyncProgressMemoryStorage(),
|
||||
logger: logger
|
||||
)
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadedHeight, alias: .default)
|
||||
|
||||
syncRanges = try await internalSyncProgress.computeSyncRanges(
|
||||
birthday: processorConfig.walletBirthday,
|
||||
latestBlockHeight: latestBlockchainHeight,
|
||||
latestScannedHeight: 0
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
expectedSyncRanges,
|
||||
syncRanges,
|
||||
"Failure when testing last range"
|
||||
)
|
||||
}
|
||||
|
||||
func testDetermineLowerBoundPastBirthday() async {
|
||||
let errorHeight = 781_906
|
||||
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
//
|
||||
// ChecksBeforeSyncActionTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Lukáš Korba on 22.05.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import TestUtils
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
final class ChecksBeforeSyncActionTests: ZcashTestCase {
|
||||
var underlyingDownloadRange: CompactBlockRange?
|
||||
var underlyingScanRange: CompactBlockRange?
|
||||
var underlyingLatestScannedHeight: BlockHeight?
|
||||
var underlyingLatestDownloadedBlockHeight: BlockHeight?
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
underlyingDownloadRange = nil
|
||||
underlyingScanRange = nil
|
||||
underlyingLatestScannedHeight = nil
|
||||
underlyingLatestDownloadedBlockHeight = nil
|
||||
}
|
||||
|
||||
func testChecksBeforeSyncAction_shouldClearBlockCacheAndUpdateInternalState_noDownloadNoScanRange() async throws {
|
||||
let checksBeforeSyncAction = setupAction()
|
||||
|
||||
let syncRanges = setupSyncRanges()
|
||||
|
||||
let latestScannedHeight = checksBeforeSyncAction.shouldClearBlockCacheAndUpdateInternalState(syncRange: syncRanges)
|
||||
XCTAssertNil(latestScannedHeight, "latestScannedHeight is expected to be nil.")
|
||||
}
|
||||
|
||||
func testChecksBeforeSyncAction_shouldClearBlockCacheAndUpdateInternalState_nothingToClear() async throws {
|
||||
let checksBeforeSyncAction = setupAction()
|
||||
|
||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingLatestScannedHeight = BlockHeight(2000)
|
||||
underlyingLatestDownloadedBlockHeight = BlockHeight(2000)
|
||||
|
||||
let syncRanges = setupSyncRanges()
|
||||
|
||||
let latestScannedHeight = checksBeforeSyncAction.shouldClearBlockCacheAndUpdateInternalState(syncRange: syncRanges)
|
||||
XCTAssertNil(latestScannedHeight, "latestScannedHeight is expected to be nil.")
|
||||
}
|
||||
|
||||
func testChecksBeforeSyncAction_shouldClearBlockCacheAndUpdateInternalState_somethingToClear() async throws {
|
||||
let checksBeforeSyncAction = setupAction()
|
||||
|
||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingLatestScannedHeight = BlockHeight(2000)
|
||||
underlyingLatestDownloadedBlockHeight = BlockHeight(1000)
|
||||
|
||||
let syncRanges = setupSyncRanges()
|
||||
|
||||
let latestScannedHeight = checksBeforeSyncAction.shouldClearBlockCacheAndUpdateInternalState(syncRange: syncRanges)
|
||||
XCTAssertNotNil(latestScannedHeight, "latestScannedHeight is not expected to be nil.")
|
||||
}
|
||||
|
||||
func testChecksBeforeSyncAction_NextAction_ClearStorage() async throws {
|
||||
let compactBlockRepository = CompactBlockRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
compactBlockRepository.clearClosure = { }
|
||||
internalSyncProgressStorageMock.setForClosure = { _, _ in }
|
||||
|
||||
let checksBeforeSyncAction = setupAction(
|
||||
compactBlockRepository,
|
||||
internalSyncProgressStorageMock
|
||||
)
|
||||
|
||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingLatestScannedHeight = BlockHeight(2000)
|
||||
underlyingLatestDownloadedBlockHeight = BlockHeight(1000)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await checksBeforeSyncAction.run(with: syncContext) { _ in }
|
||||
XCTAssertTrue(compactBlockRepository.clearCalled, "storage.clear() is expected to be called.")
|
||||
XCTAssertTrue(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is expected to be called.")
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .fetchUTXO,
|
||||
"nextContext after .checksBeforeSync is expected to be .fetchUTXO but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testChecksBeforeSyncAction_NextAction_ClearStorage is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testChecksBeforeSyncAction_NextAction_CreateStorage() async throws {
|
||||
let compactBlockRepository = CompactBlockRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
compactBlockRepository.createClosure = { }
|
||||
|
||||
let checksBeforeSyncAction = setupAction(compactBlockRepository)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await checksBeforeSyncAction.run(with: syncContext) { _ in }
|
||||
XCTAssertTrue(compactBlockRepository.createCalled, "storage.create() is expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is not expected to be called.")
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .fetchUTXO,
|
||||
"nextContext after .checksBeforeSync is expected to be .fetchUTXO but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testChecksBeforeSyncAction_NextAction_CreateStorage is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func setupAction(
|
||||
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock(),
|
||||
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ChecksBeforeSyncAction {
|
||||
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { _ in
|
||||
InternalSyncProgress(alias: .default, storage: internalSyncProgressStorageMock, logger: loggerMock)
|
||||
}
|
||||
|
||||
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
||||
|
||||
return ChecksBeforeSyncAction(
|
||||
container: mockContainer
|
||||
)
|
||||
}
|
||||
|
||||
private func setupSyncRanges() -> SyncRanges {
|
||||
SyncRanges(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: underlyingDownloadRange,
|
||||
scanRange: underlyingScanRange,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: underlyingLatestScannedHeight,
|
||||
latestDownloadedBlockHeight: underlyingLatestDownloadedBlockHeight
|
||||
)
|
||||
}
|
||||
|
||||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .checksBeforeSync)
|
||||
|
||||
await syncContext.update(syncRanges: setupSyncRanges())
|
||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||
|
||||
return syncContext
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// ComputeSyncControlDataActionTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Lukáš Korba on 22.05.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import TestUtils
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
final class ComputeSyncControlDataActionTests: ZcashTestCase {
|
||||
var underlyingDownloadRange: CompactBlockRange?
|
||||
var underlyingScanRange: CompactBlockRange?
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
underlyingDownloadRange = nil
|
||||
underlyingScanRange = nil
|
||||
}
|
||||
|
||||
func testComputeSyncControlDataAction_finishProcessingCase() async throws {
|
||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
let computeSyncControlDataAction = setupDefaultMocksAndReturnAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
loggerMock
|
||||
)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateBlockDataCalled,
|
||||
"latestBlocksDataProvider.updateBlockData() is expected to be called."
|
||||
)
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .finished,
|
||||
"nextContext after .computeSyncControlData is expected to be .finished but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testComputeSyncControlDataAction_finishProcessingCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testComputeSyncControlDataAction_fetchUTXOsCase() async throws {
|
||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
let computeSyncControlDataAction = setupDefaultMocksAndReturnAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
loggerMock
|
||||
)
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 10
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(latestBlocksDataProviderMock.updateBlockDataCalled, "latestBlocksDataProvider.updateBlockData() is expected to be called.")
|
||||
XCTAssertFalse(loggerMock.infoFileFunctionLineCalled, "logger.info() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .fetchUTXO,
|
||||
"nextContext after .computeSyncControlData is expected to be .fetchUTXO but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testComputeSyncControlDataAction_checksBeforeSyncCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func setupSyncControlData() -> SyncControlData {
|
||||
SyncControlData(
|
||||
latestBlockHeight: 0,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
}
|
||||
|
||||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .computeSyncControlData)
|
||||
|
||||
await syncContext.update(syncControlData: setupSyncControlData())
|
||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||
|
||||
return syncContext
|
||||
}
|
||||
|
||||
private func setupAction(
|
||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ComputeSyncControlDataAction {
|
||||
mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in blockDownloaderServiceMock }
|
||||
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in latestBlocksDataProviderMock }
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
|
||||
let config: CompactBlockProcessor.Configuration = .standard(
|
||||
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
|
||||
)
|
||||
|
||||
return ComputeSyncControlDataAction(
|
||||
container: mockContainer,
|
||||
configProvider: CompactBlockProcessor.ConfigProvider(config: config)
|
||||
)
|
||||
}
|
||||
|
||||
private func setupDefaultMocksAndReturnAction(
|
||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ComputeSyncControlDataAction {
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightReturnValue = 1
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1
|
||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 1
|
||||
latestBlocksDataProviderMock.updateScannedDataClosure = { }
|
||||
latestBlocksDataProviderMock.updateBlockDataClosure = { }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
return setupAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
loggerMock
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
//
|
||||
// ComputeSyncRangesActionTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Lukáš Korba on 22.05.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import TestUtils
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
final class ComputeSyncRangesActionTests: ZcashTestCase {
|
||||
var underlyingDownloadRange: CompactBlockRange?
|
||||
var underlyingScanRange: CompactBlockRange?
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
underlyingDownloadRange = nil
|
||||
underlyingScanRange = nil
|
||||
}
|
||||
|
||||
func testComputeSyncRangesAction_computeTotalProgressRange_noDownloadNoScanRange() async throws {
|
||||
let computeSyncRangesAction = setupAction()
|
||||
|
||||
let syncRanges = setupSyncRanges()
|
||||
|
||||
let totalProgressRange = computeSyncRangesAction.computeTotalProgressRange(from: syncRanges)
|
||||
|
||||
XCTAssertTrue(
|
||||
totalProgressRange == 0...0,
|
||||
"testComputeSyncRangesAction_computeTotalProgressRange_noDownloadNoScanRange is expected to be 0...0 but received \(totalProgressRange)"
|
||||
)
|
||||
}
|
||||
|
||||
func testComputeSyncRangesAction_computeTotalProgressRange_ValidRange() async throws {
|
||||
let computeSyncRangesAction = setupAction()
|
||||
|
||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncRanges = setupSyncRanges()
|
||||
let totalProgressRange = computeSyncRangesAction.computeTotalProgressRange(from: syncRanges)
|
||||
let expectedRange = 1000...2000
|
||||
|
||||
XCTAssertTrue(
|
||||
totalProgressRange == expectedRange,
|
||||
"testComputeSyncRangesAction_computeTotalProgressRange_ValidRange is expected to be \(expectedRange) but received \(totalProgressRange)"
|
||||
)
|
||||
}
|
||||
|
||||
func testComputeSyncRangesAction_finishProcessingCase() async throws {
|
||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
let computeSyncRangesAction = setupDefaultMocksAndReturnAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
internalSyncProgressStorageMock,
|
||||
loggerMock
|
||||
)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncRangesAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightCalled,
|
||||
"downloaderService.lastDownloadedBlockHeight() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(latestBlocksDataProviderMock.updateBlockDataCalled, "latestBlocksDataProvider.updateBlockData() is expected to be called.")
|
||||
XCTAssertFalse(loggerMock.infoFileFunctionLineCalled, "logger.info() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .finished,
|
||||
"nextContext after .computeSyncRanges is expected to be .finished but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testComputeSyncRangesAction_finishProcessingCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testComputeSyncRangesAction_checksBeforeSyncCase() async throws {
|
||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
let computeSyncRangesAction = setupDefaultMocksAndReturnAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
internalSyncProgressStorageMock,
|
||||
loggerMock
|
||||
)
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 10
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncRangesAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightCalled,
|
||||
"downloaderService.lastDownloadedBlockHeight() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(latestBlocksDataProviderMock.updateBlockDataCalled, "latestBlocksDataProvider.updateBlockData() is expected to be called.")
|
||||
XCTAssertFalse(loggerMock.infoFileFunctionLineCalled, "logger.info() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .checksBeforeSync,
|
||||
"nextContext after .computeSyncRanges is expected to be .checksBeforeSync but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testComputeSyncRangesAction_checksBeforeSyncCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testComputeSyncRangesAction_waitCase() async throws {
|
||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
let computeSyncRangesAction = setupDefaultMocksAndReturnAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
internalSyncProgressStorageMock,
|
||||
loggerMock
|
||||
)
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightReturnValue = 10
|
||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 10
|
||||
internalSyncProgressStorageMock.integerForReturnValue = 10
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncRangesAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightCalled,
|
||||
"downloaderService.lastDownloadedBlockHeight() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(latestBlocksDataProviderMock.updateBlockDataCalled, "latestBlocksDataProvider.updateBlockData() is expected to be called.")
|
||||
XCTAssertTrue(loggerMock.infoFileFunctionLineCalled, "logger.info() is expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .finished,
|
||||
"nextContext after .computeSyncRanges is expected to be .finished but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testComputeSyncRangesAction_waitCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func setupSyncRanges() -> SyncRanges {
|
||||
SyncRanges(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: underlyingDownloadRange,
|
||||
scanRange: underlyingScanRange,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: nil,
|
||||
latestDownloadedBlockHeight: nil
|
||||
)
|
||||
}
|
||||
|
||||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .computeSyncRanges)
|
||||
|
||||
await syncContext.update(syncRanges: setupSyncRanges())
|
||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||
|
||||
return syncContext
|
||||
}
|
||||
|
||||
private func setupAction(
|
||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ComputeSyncRangesAction {
|
||||
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { _ in
|
||||
InternalSyncProgress(alias: .default, storage: internalSyncProgressStorageMock, logger: loggerMock)
|
||||
}
|
||||
|
||||
mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in blockDownloaderServiceMock }
|
||||
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in latestBlocksDataProviderMock }
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
|
||||
let config: CompactBlockProcessor.Configuration = .standard(
|
||||
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
|
||||
)
|
||||
|
||||
return ComputeSyncRangesAction(
|
||||
container: mockContainer,
|
||||
configProvider: CompactBlockProcessor.ConfigProvider(config: config)
|
||||
)
|
||||
}
|
||||
|
||||
private func setupDefaultMocksAndReturnAction(
|
||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ComputeSyncRangesAction {
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightReturnValue = 1
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1
|
||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 1
|
||||
latestBlocksDataProviderMock.updateScannedDataClosure = { }
|
||||
latestBlocksDataProviderMock.updateBlockDataClosure = { }
|
||||
internalSyncProgressStorageMock.integerForReturnValue = 1
|
||||
internalSyncProgressStorageMock.boolForReturnValue = true
|
||||
internalSyncProgressStorageMock.setBoolClosure = { _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
return setupAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
internalSyncProgressStorageMock,
|
||||
loggerMock
|
||||
)
|
||||
}
|
||||
}
|
|
@ -17,11 +17,12 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
let blockDownloaderMock = BlockDownloaderMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1000
|
||||
blockDownloaderMock.setSyncRangeBatchSizeClosure = { _, _ in }
|
||||
blockDownloaderMock.setDownloadLimitClosure = { _ in }
|
||||
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
|
||||
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInClosure = { _ in }
|
||||
blockDownloaderMock.updateLatestDownloadedBlockHeightClosure = { _ in }
|
||||
|
||||
let downloadAction = setupAction(
|
||||
blockDownloaderMock,
|
||||
|
@ -146,17 +147,13 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .download)
|
||||
|
||||
let syncRanges = SyncRanges(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: underlyingDownloadRange,
|
||||
scanRange: underlyingScanRange,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: nil,
|
||||
latestDownloadedBlockHeight: nil
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
|
||||
await syncContext.update(syncRanges: syncRanges)
|
||||
await syncContext.update(syncControlData: syncControlData)
|
||||
|
||||
return syncContext
|
||||
}
|
||||
|
|
|
@ -68,14 +68,12 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
func testEnhanceAction_NoEnhanceRange() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock,
|
||||
internalSyncProgressStorageMock
|
||||
transactionRepositoryMock
|
||||
)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
@ -84,7 +82,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
||||
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.integerForCalled, "internalSyncProgress.load() is not expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testEnhanceAction_NoEnhanceRange is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -93,15 +90,12 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
func testEnhanceAction_1000BlocksConditionNotFulfilled() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
||||
internalSyncProgressStorageMock.integerForReturnValue = 1
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock,
|
||||
internalSyncProgressStorageMock
|
||||
transactionRepositoryMock
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
@ -111,7 +105,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
do {
|
||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
||||
XCTAssertTrue(internalSyncProgressStorageMock.integerForCalled, "internalSyncProgress.load() is expected to be called.")
|
||||
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testEnhanceAction_1000BlocksConditionNotFulfilled is not expected to fail. \(error)")
|
||||
|
@ -121,10 +114,8 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
func testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
|
||||
internalSyncProgressStorageMock.integerForReturnValue = 1
|
||||
|
||||
let transaction = ZcashTransaction.Overview(
|
||||
accountId: 0,
|
||||
|
@ -151,8 +142,7 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock,
|
||||
internalSyncProgressStorageMock
|
||||
transactionRepositoryMock
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
@ -174,7 +164,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
XCTAssertEqual(receivedTransaction.expiryHeight, transaction.expiryHeight, "ReceivedTransaction differs from mocked one.")
|
||||
}
|
||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
||||
XCTAssertTrue(internalSyncProgressStorageMock.integerForCalled, "internalSyncProgress.load() is expected to be called.")
|
||||
XCTAssertTrue(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions is not expected to fail. \(error)")
|
||||
|
@ -184,10 +173,8 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
func testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
|
||||
internalSyncProgressStorageMock.integerForReturnValue = 1
|
||||
|
||||
let transaction = ZcashTransaction.Overview(
|
||||
accountId: 0,
|
||||
|
@ -222,8 +209,7 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock,
|
||||
internalSyncProgressStorageMock
|
||||
transactionRepositoryMock
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
@ -241,7 +227,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
XCTAssertEqual(minedTransaction.expiryHeight, transaction.expiryHeight, "MinedTransaction differs from mocked one.")
|
||||
}
|
||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
||||
XCTAssertTrue(internalSyncProgressStorageMock.integerForCalled, "internalSyncProgress.load() is expected to be called.")
|
||||
XCTAssertTrue(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction is not expected to fail. \(error)")
|
||||
|
@ -251,10 +236,8 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
func testEnhanceAction_EnhancementOfBlocksCalled_usingSmallRange_minedTransaction() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 200
|
||||
internalSyncProgressStorageMock.integerForReturnValue = 1
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 2000
|
||||
|
||||
let transaction = ZcashTransaction.Overview(
|
||||
accountId: 0,
|
||||
|
@ -289,11 +272,10 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock,
|
||||
internalSyncProgressStorageMock
|
||||
transactionRepositoryMock
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (100, 200))
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1900, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
|
@ -308,7 +290,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
XCTAssertEqual(minedTransaction.expiryHeight, transaction.expiryHeight, "MinedTransaction differs from mocked one.")
|
||||
}
|
||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
||||
XCTAssertTrue(internalSyncProgressStorageMock.integerForCalled, "internalSyncProgress.load() is expected to be called.")
|
||||
XCTAssertTrue(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction is not expected to fail. \(error)")
|
||||
|
@ -318,17 +299,13 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .enhance)
|
||||
|
||||
let syncRanges = SyncRanges(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: underlyingDownloadRange,
|
||||
scanRange: underlyingScanRange,
|
||||
enhanceRange: underlyingEnhanceRange,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: nil,
|
||||
latestDownloadedBlockHeight: nil
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: underlyingEnhanceRange?.lowerBound
|
||||
)
|
||||
|
||||
await syncContext.update(syncRanges: syncRanges)
|
||||
await syncContext.update(syncControlData: syncControlData)
|
||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||
|
||||
return syncContext
|
||||
|
@ -337,13 +314,8 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
private func setupAction(
|
||||
_ blockEnhancerMock: BlockEnhancerMock = BlockEnhancerMock(),
|
||||
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
|
||||
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> EnhanceAction {
|
||||
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { _ in
|
||||
InternalSyncProgress(alias: .default, storage: internalSyncProgressStorageMock, logger: loggerMock)
|
||||
}
|
||||
|
||||
mockContainer.mock(type: BlockEnhancer.self, isSingleton: true) { _ in blockEnhancerMock }
|
||||
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
|
|
|
@ -17,7 +17,7 @@ final class FetchUTXOsActionTests: ZcashTestCase {
|
|||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
let insertedEntity = UnspentTransactionOutputEntityMock(address: "addr", txid: Data(), index: 0, script: Data(), valueZat: 1, height: 2)
|
||||
let skippedEntity = UnspentTransactionOutputEntityMock(address: "addr2", txid: Data(), index: 1, script: Data(), valueZat: 2, height: 3)
|
||||
uTXOFetcherMock.fetchAtDidFetchReturnValue = (inserted: [insertedEntity], skipped: [skippedEntity])
|
||||
uTXOFetcherMock.fetchDidFetchReturnValue = (inserted: [insertedEntity], skipped: [skippedEntity])
|
||||
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
mockContainer.mock(type: UTXOFetcher.self, isSingleton: true) { _ in uTXOFetcherMock }
|
||||
|
@ -26,17 +26,13 @@ final class FetchUTXOsActionTests: ZcashTestCase {
|
|||
|
||||
let syncContext: ActionContext = .init(state: .fetchUTXO)
|
||||
|
||||
let syncRanges = SyncRanges(
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: nil,
|
||||
scanRange: nil,
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: CompactBlockRange(uncheckedBounds: (1000, 2000)),
|
||||
latestScannedHeight: nil,
|
||||
latestDownloadedBlockHeight: nil
|
||||
latestScannedHeight: 0,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
|
||||
await syncContext.update(syncRanges: syncRanges)
|
||||
await syncContext.update(syncControlData: syncControlData)
|
||||
|
||||
do {
|
||||
let nextContext = try await fetchUTXOsAction.run(with: syncContext) { event in
|
||||
|
@ -48,7 +44,7 @@ final class FetchUTXOsActionTests: ZcashTestCase {
|
|||
XCTAssertEqual(result.skipped as! [UnspentTransactionOutputEntityMock], [skippedEntity])
|
||||
}
|
||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||
XCTAssertTrue(uTXOFetcherMock.fetchAtDidFetchCalled, "utxoFetcher.fetch() is expected to be called.")
|
||||
XCTAssertTrue(uTXOFetcherMock.fetchDidFetchCalled, "utxoFetcher.fetch() is expected to be called.")
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .handleSaplingParams,
|
||||
|
|
|
@ -26,13 +26,11 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let zcashFileManagerMock = ZcashFileManagerMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
let migrateLegacyCacheDBAction = setupAction(
|
||||
compactBlockRepositoryMock,
|
||||
transactionRepositoryMock,
|
||||
zcashFileManagerMock,
|
||||
internalSyncProgressStorageMock
|
||||
zcashFileManagerMock
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -45,7 +43,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
|
@ -61,15 +58,13 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let zcashFileManagerMock = ZcashFileManagerMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
|
||||
underlyingCacheDbURL = DefaultResourceProvider(network: ZcashNetworkBuilder.network(for: .testnet)).fsCacheURL
|
||||
|
||||
let migrateLegacyCacheDBAction = setupAction(
|
||||
compactBlockRepositoryMock,
|
||||
transactionRepositoryMock,
|
||||
zcashFileManagerMock,
|
||||
internalSyncProgressStorageMock
|
||||
zcashFileManagerMock
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -83,7 +78,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is not expected to be called.")
|
||||
} catch {
|
||||
XCTFail("""
|
||||
testMigrateLegacyCacheDBAction_noFsBlockCacheRoot is expected to fail with \
|
||||
|
@ -96,7 +90,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let zcashFileManagerMock = ZcashFileManagerMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
// any valid URL needed...
|
||||
underlyingCacheDbURL = DefaultResourceProvider(network: ZcashNetworkBuilder.network(for: .testnet)).fsCacheURL
|
||||
|
@ -107,8 +100,7 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let migrateLegacyCacheDBAction = setupAction(
|
||||
compactBlockRepositoryMock,
|
||||
transactionRepositoryMock,
|
||||
zcashFileManagerMock,
|
||||
internalSyncProgressStorageMock
|
||||
zcashFileManagerMock
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -121,7 +113,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
|
@ -137,7 +128,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let zcashFileManagerMock = ZcashFileManagerMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
// any valid URL needed...
|
||||
underlyingCacheDbURL = DefaultResourceProvider(network: ZcashNetworkBuilder.network(for: .testnet)).fsCacheURL
|
||||
|
@ -148,8 +138,7 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let migrateLegacyCacheDBAction = setupAction(
|
||||
compactBlockRepositoryMock,
|
||||
transactionRepositoryMock,
|
||||
zcashFileManagerMock,
|
||||
internalSyncProgressStorageMock
|
||||
zcashFileManagerMock
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -162,7 +151,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
|
@ -178,7 +166,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let zcashFileManagerMock = ZcashFileManagerMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
// any valid URL needed...
|
||||
underlyingCacheDbURL = DefaultResourceProvider(network: ZcashNetworkBuilder.network(for: .testnet)).fsCacheURL
|
||||
|
@ -190,8 +177,7 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let migrateLegacyCacheDBAction = setupAction(
|
||||
compactBlockRepositoryMock,
|
||||
transactionRepositoryMock,
|
||||
zcashFileManagerMock,
|
||||
internalSyncProgressStorageMock
|
||||
zcashFileManagerMock
|
||||
)
|
||||
|
||||
do {
|
||||
|
@ -204,7 +190,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||
XCTAssertTrue(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is expected to be called.")
|
||||
XCTAssertFalse(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is not expected to be called.")
|
||||
} catch {
|
||||
XCTFail("""
|
||||
testMigrateLegacyCacheDBAction_removeItemFailed is expected to fail with \
|
||||
|
@ -217,7 +202,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
let zcashFileManagerMock = ZcashFileManagerMock()
|
||||
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
|
||||
|
||||
// any valid URL needed...
|
||||
underlyingCacheDbURL = DefaultResourceProvider(network: ZcashNetworkBuilder.network(for: .testnet)).fsCacheURL
|
||||
|
@ -226,24 +210,19 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
zcashFileManagerMock.isReadableFileAtPathReturnValue = true
|
||||
zcashFileManagerMock.removeItemAtClosure = { _ in }
|
||||
compactBlockRepositoryMock.createClosure = { }
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
||||
internalSyncProgressStorageMock.setForClosure = { _, _ in }
|
||||
|
||||
let migrateLegacyCacheDBAction = setupAction(
|
||||
compactBlockRepositoryMock,
|
||||
transactionRepositoryMock,
|
||||
zcashFileManagerMock,
|
||||
internalSyncProgressStorageMock
|
||||
zcashFileManagerMock
|
||||
)
|
||||
|
||||
do {
|
||||
let nextContext = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
||||
|
||||
XCTAssertTrue(compactBlockRepositoryMock.createCalled, "storage.create() is expected to be called.")
|
||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
||||
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||
XCTAssertTrue(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is expected to be called.")
|
||||
XCTAssertTrue(internalSyncProgressStorageMock.setForCalled, "internalSyncProgress.set() is expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
|
@ -259,12 +238,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock(),
|
||||
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
|
||||
_ zcashFileManagerMock: ZcashFileManagerMock = ZcashFileManagerMock(),
|
||||
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> MigrateLegacyCacheDBAction {
|
||||
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { _ in
|
||||
InternalSyncProgress(alias: .default, storage: internalSyncProgressStorageMock, logger: loggerMock)
|
||||
}
|
||||
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
||||
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
|
||||
mockContainer.mock(type: ZcashFileManager.self, isSingleton: true) { _ in zcashFileManagerMock }
|
||||
|
|
|
@ -108,17 +108,13 @@ final class ScanActionTests: ZcashTestCase {
|
|||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .scan)
|
||||
|
||||
let syncRanges = SyncRanges(
|
||||
latestBlockHeight: 0,
|
||||
downloadRange: CompactBlockRange(uncheckedBounds: (1000, 2000)),
|
||||
scanRange: CompactBlockRange(uncheckedBounds: (1000, 2000)),
|
||||
enhanceRange: nil,
|
||||
fetchUTXORange: nil,
|
||||
latestScannedHeight: nil,
|
||||
latestDownloadedBlockHeight: nil
|
||||
let syncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: 1000,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
|
||||
await syncContext.update(syncRanges: syncRanges)
|
||||
await syncContext.update(syncControlData: syncControlData)
|
||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||
|
||||
return syncContext
|
||||
|
|
|
@ -31,8 +31,8 @@ final class ValidateServerActionTests: ZcashTestCase {
|
|||
let nextContext = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .computeSyncRanges,
|
||||
"nextContext after .validateServer is expected to be .computeSyncRanges but received \(nextState)"
|
||||
nextState == .computeSyncControlData,
|
||||
"nextContext after .validateServer is expected to be .computeSyncControlData but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)")
|
||||
|
|
|
@ -1,225 +0,0 @@
|
|||
//
|
||||
// InternalSyncProgressTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 30.11.2022.
|
||||
//
|
||||
|
||||
@testable import TestUtils
|
||||
import XCTest
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
class InternalSyncProgressTests: ZcashTestCase {
|
||||
var storage: InternalSyncProgressDiskStorage!
|
||||
var internalSyncProgress: InternalSyncProgress!
|
||||
|
||||
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 {
|
||||
try super.tearDownWithError()
|
||||
storage = nil
|
||||
internalSyncProgress = nil
|
||||
}
|
||||
|
||||
func test__trackedValuesAreHigherThanLatestHeight__nextStateIsWait() async throws {
|
||||
let latestHeight = 623000
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000, alias: .default)
|
||||
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
let nextState = try await internalSyncProgress.computeNextState(
|
||||
latestBlockHeight: latestHeight,
|
||||
latestScannedHeight: 630000,
|
||||
walletBirthday: 600000
|
||||
)
|
||||
|
||||
switch nextState {
|
||||
case let .wait(latestHeight, latestDownloadHeight):
|
||||
XCTAssertEqual(latestHeight, 623000)
|
||||
XCTAssertEqual(latestDownloadHeight, 630000)
|
||||
|
||||
default:
|
||||
XCTFail("State should be wait. Unexpected state: \(nextState)")
|
||||
}
|
||||
}
|
||||
|
||||
func test__trackedValuesAreLowerThanLatestHeight__nextStateIsProcessNewBlocks() async throws {
|
||||
let latestHeight = 640000
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000, alias: .default)
|
||||
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
let nextState = try await internalSyncProgress.computeNextState(
|
||||
latestBlockHeight: latestHeight,
|
||||
latestScannedHeight: 620000,
|
||||
walletBirthday: 600000
|
||||
)
|
||||
|
||||
switch nextState {
|
||||
case let .processNewBlocks(ranges):
|
||||
XCTAssertEqual(ranges.downloadRange, 630001...640000)
|
||||
XCTAssertEqual(ranges.scanRange, 620001...640000)
|
||||
XCTAssertEqual(ranges.enhanceRange, 630001...640000)
|
||||
XCTAssertEqual(ranges.fetchUTXORange, 630001...640000)
|
||||
|
||||
default:
|
||||
XCTFail("State should be processNewBlocks. Unexpected state: \(nextState)")
|
||||
}
|
||||
}
|
||||
|
||||
func test__trackedValuesAreSameAsLatestHeight__nextStateIsFinishProcessing() async throws {
|
||||
let latestHeight = 630000
|
||||
try await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: 630000, alias: .default)
|
||||
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
let nextState = try await internalSyncProgress.computeNextState(
|
||||
latestBlockHeight: latestHeight,
|
||||
latestScannedHeight: 630000,
|
||||
walletBirthday: 600000
|
||||
)
|
||||
|
||||
switch nextState {
|
||||
case let .finishProcessing(height):
|
||||
XCTAssertEqual(height, latestHeight)
|
||||
|
||||
default:
|
||||
XCTFail("State should be finishProcessing. Unexpected state: \(nextState)")
|
||||
}
|
||||
}
|
||||
|
||||
func test__rewindToHeightThatIsHigherThanTrackedHeight__rewindsToTrackedHeight() async throws {
|
||||
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
try await internalSyncProgress.rewind(to: 640000)
|
||||
|
||||
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 {
|
||||
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
try await internalSyncProgress.rewind(to: 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 {
|
||||
try await storage.set(621000, for: "latestEnhancedHeight")
|
||||
let latestEnhancedHeight = try await internalSyncProgress.latestEnhancedHeight
|
||||
XCTAssertEqual(latestEnhancedHeight, 621000)
|
||||
|
||||
try await storage.set(619000, for: "latestUTXOFetchedHeight")
|
||||
let latestUTXOFetchedHeight = try await internalSyncProgress.latestUTXOFetchedHeight
|
||||
XCTAssertEqual(latestUTXOFetchedHeight, 619000)
|
||||
}
|
||||
|
||||
func test__set__storeValue() async throws {
|
||||
try await internalSyncProgress.set(521000, .latestEnhancedHeight)
|
||||
let latestEnhancedHeight = try await storage.integer(for: "latestEnhancedHeight")
|
||||
XCTAssertEqual(latestEnhancedHeight, 521000)
|
||||
|
||||
try await internalSyncProgress.set(519000, .latestUTXOFetchedHeight)
|
||||
let latestUTXOFetchedHeight = try await storage.integer(for: "latestUTXOFetchedHeight")
|
||||
XCTAssertEqual(latestUTXOFetchedHeight, 519000)
|
||||
}
|
||||
|
||||
func test__whenUsingDefaultAliasKeysAreBackwardsCompatible() async throws {
|
||||
try await internalSyncProgress.set(630000, .latestDownloadedBlockHeight)
|
||||
try await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress.set(630000, .latestEnhancedHeight)
|
||||
|
||||
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 throws {
|
||||
let internalSyncProgress1 = InternalSyncProgress(alias: .custom("alias1"), storage: storage, logger: logger)
|
||||
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)
|
||||
try await internalSyncProgress2.set(630000, .latestDownloadedBlockHeight)
|
||||
try await internalSyncProgress2.set(630000, .latestUTXOFetchedHeight)
|
||||
try await internalSyncProgress2.set(630000, .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 = 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)
|
||||
}
|
||||
}
|
|
@ -92,7 +92,6 @@ class SynchronizerTests: ZcashTestCase {
|
|||
let syncSyncedExpectation = XCTestExpectation(description: "synchronizerSynced Expectation")
|
||||
sdkSynchronizerInternalSyncStatusHandler.subscribe(to: synchronizer.stateStream, expectations: [.synced: syncSyncedExpectation])
|
||||
|
||||
try await resetDefaultInternalSyncProgress(to: birthday)
|
||||
await (synchronizer.blockProcessor.service as? LightWalletGRPCService)?.latestBlockHeightProvider = MockLatestBlockHeightProvider(
|
||||
birthday: self.birthday + 99
|
||||
)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// InternalSyncProgressMemoryStorage.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 24.11.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
class InternalSyncProgressMemoryStorage: InternalSyncProgressStorage {
|
||||
private var boolStorage: [String: Bool] = [:]
|
||||
private var storage: [String: Int] = [:]
|
||||
|
||||
func initialize() async throws { }
|
||||
|
||||
func bool(for key: String) async throws -> Bool {
|
||||
return boolStorage[key, default: false]
|
||||
}
|
||||
|
||||
func integer(for key: String) async throws -> Int {
|
||||
return storage[key, default: 0]
|
||||
}
|
||||
|
||||
func set(_ value: Int, for key: String) async throws {
|
||||
storage[key] = value
|
||||
}
|
||||
|
||||
func set(_ value: Bool, for key: String) async throws {
|
||||
boolStorage[key] = value
|
||||
}
|
||||
|
||||
func synchronize() -> Bool { true }
|
||||
}
|
|
@ -18,7 +18,6 @@ extension BlockEnhancer { }
|
|||
extension BlockScanner { }
|
||||
extension BlockValidator { }
|
||||
extension CompactBlockRepository { }
|
||||
extension InternalSyncProgressStorage { }
|
||||
extension LatestBlocksDataProvider { }
|
||||
extension LightWalletdInfo { }
|
||||
extension LightWalletService { }
|
||||
|
|
|
@ -95,6 +95,54 @@ class BlockDownloaderMock: BlockDownloader {
|
|||
try await waitUntilRequestedBlocksAreDownloadedInClosure!(range)
|
||||
}
|
||||
|
||||
// MARK: - update
|
||||
|
||||
var updateLatestDownloadedBlockHeightCallsCount = 0
|
||||
var updateLatestDownloadedBlockHeightCalled: Bool {
|
||||
return updateLatestDownloadedBlockHeightCallsCount > 0
|
||||
}
|
||||
var updateLatestDownloadedBlockHeightReceivedLatestDownloadedBlockHeight: BlockHeight?
|
||||
var updateLatestDownloadedBlockHeightClosure: ((BlockHeight) async -> Void)?
|
||||
|
||||
func update(latestDownloadedBlockHeight: BlockHeight) async {
|
||||
updateLatestDownloadedBlockHeightCallsCount += 1
|
||||
updateLatestDownloadedBlockHeightReceivedLatestDownloadedBlockHeight = latestDownloadedBlockHeight
|
||||
await updateLatestDownloadedBlockHeightClosure!(latestDownloadedBlockHeight)
|
||||
}
|
||||
|
||||
// MARK: - latestDownloadedBlockHeight
|
||||
|
||||
var latestDownloadedBlockHeightCallsCount = 0
|
||||
var latestDownloadedBlockHeightCalled: Bool {
|
||||
return latestDownloadedBlockHeightCallsCount > 0
|
||||
}
|
||||
var latestDownloadedBlockHeightReturnValue: BlockHeight!
|
||||
var latestDownloadedBlockHeightClosure: (() async -> BlockHeight)?
|
||||
|
||||
func latestDownloadedBlockHeight() async -> BlockHeight {
|
||||
latestDownloadedBlockHeightCallsCount += 1
|
||||
if let closure = latestDownloadedBlockHeightClosure {
|
||||
return await closure()
|
||||
} else {
|
||||
return latestDownloadedBlockHeightReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - rewind
|
||||
|
||||
var rewindLatestDownloadedBlockHeightCallsCount = 0
|
||||
var rewindLatestDownloadedBlockHeightCalled: Bool {
|
||||
return rewindLatestDownloadedBlockHeightCallsCount > 0
|
||||
}
|
||||
var rewindLatestDownloadedBlockHeightReceivedLatestDownloadedBlockHeight: BlockHeight?
|
||||
var rewindLatestDownloadedBlockHeightClosure: ((BlockHeight?) async -> Void)?
|
||||
|
||||
func rewind(latestDownloadedBlockHeight: BlockHeight?) async {
|
||||
rewindLatestDownloadedBlockHeightCallsCount += 1
|
||||
rewindLatestDownloadedBlockHeightReceivedLatestDownloadedBlockHeight = latestDownloadedBlockHeight
|
||||
await rewindLatestDownloadedBlockHeightClosure!(latestDownloadedBlockHeight)
|
||||
}
|
||||
|
||||
}
|
||||
class BlockDownloaderServiceMock: BlockDownloaderService {
|
||||
|
||||
|
@ -472,117 +520,6 @@ class CompactBlockRepositoryMock: CompactBlockRepository {
|
|||
try await clearClosure!()
|
||||
}
|
||||
|
||||
}
|
||||
class InternalSyncProgressStorageMock: InternalSyncProgressStorage {
|
||||
|
||||
|
||||
init(
|
||||
) {
|
||||
}
|
||||
|
||||
// MARK: - initialize
|
||||
|
||||
var initializeThrowableError: Error?
|
||||
var initializeCallsCount = 0
|
||||
var initializeCalled: Bool {
|
||||
return initializeCallsCount > 0
|
||||
}
|
||||
var initializeClosure: (() async throws -> Void)?
|
||||
|
||||
func initialize() async throws {
|
||||
if let error = initializeThrowableError {
|
||||
throw error
|
||||
}
|
||||
initializeCallsCount += 1
|
||||
try await initializeClosure!()
|
||||
}
|
||||
|
||||
// MARK: - bool
|
||||
|
||||
var boolForThrowableError: Error?
|
||||
var boolForCallsCount = 0
|
||||
var boolForCalled: Bool {
|
||||
return boolForCallsCount > 0
|
||||
}
|
||||
var boolForReceivedKey: String?
|
||||
var boolForReturnValue: Bool!
|
||||
var boolForClosure: ((String) async throws -> Bool)?
|
||||
|
||||
func bool(for key: String) async throws -> Bool {
|
||||
if let error = boolForThrowableError {
|
||||
throw error
|
||||
}
|
||||
boolForCallsCount += 1
|
||||
boolForReceivedKey = key
|
||||
if let closure = boolForClosure {
|
||||
return try await closure(key)
|
||||
} else {
|
||||
return boolForReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - integer
|
||||
|
||||
var integerForThrowableError: Error?
|
||||
var integerForCallsCount = 0
|
||||
var integerForCalled: Bool {
|
||||
return integerForCallsCount > 0
|
||||
}
|
||||
var integerForReceivedKey: String?
|
||||
var integerForReturnValue: Int!
|
||||
var integerForClosure: ((String) async throws -> Int)?
|
||||
|
||||
func integer(for key: String) async throws -> Int {
|
||||
if let error = integerForThrowableError {
|
||||
throw error
|
||||
}
|
||||
integerForCallsCount += 1
|
||||
integerForReceivedKey = key
|
||||
if let closure = integerForClosure {
|
||||
return try await closure(key)
|
||||
} else {
|
||||
return integerForReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - set
|
||||
|
||||
var setForThrowableError: Error?
|
||||
var setForCallsCount = 0
|
||||
var setForCalled: Bool {
|
||||
return setForCallsCount > 0
|
||||
}
|
||||
var setForReceivedArguments: (value: Int, key: String)?
|
||||
var setForClosure: ((Int, String) async throws -> Void)?
|
||||
|
||||
func set(_ value: Int, for key: String) async throws {
|
||||
if let error = setForThrowableError {
|
||||
throw error
|
||||
}
|
||||
setForCallsCount += 1
|
||||
setForReceivedArguments = (value: value, key: key)
|
||||
try await setForClosure!(value, key)
|
||||
}
|
||||
|
||||
// MARK: - set
|
||||
|
||||
var setBoolThrowableError: Error?
|
||||
var setBoolCallsCount = 0
|
||||
var setBoolCalled: Bool {
|
||||
return setBoolCallsCount > 0
|
||||
}
|
||||
var setBoolReceivedArguments: (value: Bool, key: String)?
|
||||
var setBoolClosure: ((Bool, String) async throws -> Void)?
|
||||
|
||||
func set(_ value: Bool, for key: String) async throws {
|
||||
if let error = setBoolThrowableError {
|
||||
throw error
|
||||
}
|
||||
setBoolCallsCount += 1
|
||||
setBoolReceivedArguments = (value: value, key: key)
|
||||
try await setBoolClosure!(value, key)
|
||||
}
|
||||
|
||||
}
|
||||
class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
|
||||
|
||||
|
@ -606,6 +543,7 @@ class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
|
|||
get { return underlyingWalletBirthday }
|
||||
}
|
||||
var underlyingWalletBirthday: BlockHeight!
|
||||
var firstUnenhancedHeight: BlockHeight?
|
||||
|
||||
// MARK: - updateScannedData
|
||||
|
||||
|
@ -2027,25 +1965,25 @@ class UTXOFetcherMock: UTXOFetcher {
|
|||
|
||||
// MARK: - fetch
|
||||
|
||||
var fetchAtDidFetchThrowableError: Error?
|
||||
var fetchAtDidFetchCallsCount = 0
|
||||
var fetchAtDidFetchCalled: Bool {
|
||||
return fetchAtDidFetchCallsCount > 0
|
||||
var fetchDidFetchThrowableError: Error?
|
||||
var fetchDidFetchCallsCount = 0
|
||||
var fetchDidFetchCalled: Bool {
|
||||
return fetchDidFetchCallsCount > 0
|
||||
}
|
||||
var fetchAtDidFetchReceivedArguments: (range: CompactBlockRange, didFetch: (Float) async -> Void)?
|
||||
var fetchAtDidFetchReturnValue: (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])!
|
||||
var fetchAtDidFetchClosure: ((CompactBlockRange, @escaping (Float) async -> Void) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]))?
|
||||
var fetchDidFetchReceivedDidFetch: ((Float) async -> Void)?
|
||||
var fetchDidFetchReturnValue: (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])!
|
||||
var fetchDidFetchClosure: ((@escaping (Float) async -> Void) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]))?
|
||||
|
||||
func fetch(at range: CompactBlockRange, didFetch: @escaping (Float) async -> Void) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) {
|
||||
if let error = fetchAtDidFetchThrowableError {
|
||||
func fetch(didFetch: @escaping (Float) async -> Void) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) {
|
||||
if let error = fetchDidFetchThrowableError {
|
||||
throw error
|
||||
}
|
||||
fetchAtDidFetchCallsCount += 1
|
||||
fetchAtDidFetchReceivedArguments = (range: range, didFetch: didFetch)
|
||||
if let closure = fetchAtDidFetchClosure {
|
||||
return try await closure(range, didFetch)
|
||||
fetchDidFetchCallsCount += 1
|
||||
fetchDidFetchReceivedDidFetch = didFetch
|
||||
if let closure = fetchDidFetchClosure {
|
||||
return try await closure(didFetch)
|
||||
} else {
|
||||
return fetchAtDidFetchReturnValue
|
||||
return fetchDidFetchReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,6 @@ class TestCoordinator {
|
|||
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,
|
||||
|
|
|
@ -59,22 +59,12 @@ class ZcashTestCase: XCTestCase {
|
|||
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() {
|
||||
|
|
Loading…
Reference in New Issue