From 75a9e3622e671f619969b78f89d5081ae12a7838 Mon Sep 17 00:00:00 2001 From: Michal Fousek Date: Thu, 11 May 2023 13:28:43 +0200 Subject: [PATCH] [#1042] Implement ComputeSyncRangesAction Closes #1042 --- .../Block/Actions/Action.swift | 7 +- .../Actions/ComputeSyncRangesAction.swift | 71 ++++++++++++++++++- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/Sources/ZcashLightClientKit/Block/Actions/Action.swift b/Sources/ZcashLightClientKit/Block/Actions/Action.swift index ff4636bf..a0484ff7 100644 --- a/Sources/ZcashLightClientKit/Block/Actions/Action.swift +++ b/Sources/ZcashLightClientKit/Block/Actions/Action.swift @@ -10,15 +10,16 @@ import Foundation actor ActionContext { var state: CBPState var syncRanges: SyncRanges + var totalProgressRange: CompactBlockRange = 0...0 init(state: CBPState) { self.state = state syncRanges = SyncRanges.empty } - func update(state: CBPState) async { - self.state = state - } + func update(state: CBPState) async { self.state = state } + func update(syncRanges: SyncRanges) async { self.syncRanges = syncRanges } + func update(totalProgressRange: CompactBlockRange) async { self.totalProgressRange = totalProgressRange } } enum CBPState: CaseIterable { diff --git a/Sources/ZcashLightClientKit/Block/Actions/ComputeSyncRangesAction.swift b/Sources/ZcashLightClientKit/Block/Actions/ComputeSyncRangesAction.swift index 30a5874b..0e8f6f66 100644 --- a/Sources/ZcashLightClientKit/Block/Actions/ComputeSyncRangesAction.swift +++ b/Sources/ZcashLightClientKit/Block/Actions/ComputeSyncRangesAction.swift @@ -8,7 +8,34 @@ import Foundation class ComputeSyncRangesAction { - init(container: DIContainer) { } + let config: CompactBlockProcessorNG.Configuration + let downloaderService: BlockDownloaderService + let internalSyncProgress: InternalSyncProgress + let latestBlocksDataProvider: LatestBlocksDataProvider + let logger: Logger + init(container: DIContainer, config: CompactBlockProcessorNG.Configuration) { + self.config = config + downloaderService = container.resolve(BlockDownloaderService.self) + internalSyncProgress = container.resolve(InternalSyncProgress.self) + latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self) + logger = container.resolve(Logger.self) + } + + /// It may happen that sync process start with syncing blocks that were downloaded but not synced in previous run of the sync process. This + /// methods analyses what must be done and computes range that should be used to compute reported progress. + private func computeTotalProgressRange(from syncRanges: SyncRanges) -> CompactBlockRange { + guard syncRanges.downloadedButUnscannedRange != nil || syncRanges.downloadAndScanRange != 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.downloadedButUnscannedRange?.lowerBound ?? syncRanges.downloadAndScanRange?.lowerBound ?? 0 + let upperBound = syncRanges.downloadAndScanRange?.upperBound ?? syncRanges.downloadedButUnscannedRange?.upperBound ?? 0 + + return lowerBound...upperBound + } } extension ComputeSyncRangesAction: Action { @@ -16,7 +43,47 @@ extension ComputeSyncRangesAction: Action { // call internalSyncProgress and compute sync ranges and store them in context // if there is nothing sync just switch to finished state - await context.update(state: .checksBeforeSync) + let latestDownloadHeight = try await downloaderService.lastDownloadedBlockHeight() + + await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadHeight) + + await latestBlocksDataProvider.updateScannedData() + await latestBlocksDataProvider.updateBlockData() + + let nextState = 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: + downloaded but not scanned: \ + \(ranges.downloadedButUnscannedRange?.lowerBound ?? -1)...\(ranges.downloadedButUnscannedRange?.upperBound ?? -1) + download and scan: \(ranges.downloadAndScanRange?.lowerBound ?? -1)...\(ranges.downloadAndScanRange?.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 }