Merge pull request #1195 from LukasKorba/1189-Implement-continuity-check-and-RewindAction

[#1189] implement continuity check and rewind action
This commit is contained in:
Lukas Korba 2023-08-10 12:11:01 +02:00 committed by GitHub
commit 97201f1314
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 10 deletions

View File

@ -13,6 +13,7 @@ actor ActionContext {
var syncControlData: SyncControlData
let preferredSyncAlgorithm: SyncAlgorithm
var supportedSyncAlgorithm: SyncAlgorithm?
var requestedRewindHeight: BlockHeight?
var totalProgressRange: CompactBlockRange = 0...0
var lastScannedHeight: BlockHeight?
var lastDownloadedHeight: BlockHeight?
@ -34,6 +35,7 @@ actor ActionContext {
func update(lastDownloadedHeight: BlockHeight) async { self.lastDownloadedHeight = lastDownloadedHeight }
func update(lastEnhancedHeight: BlockHeight?) async { self.lastEnhancedHeight = lastEnhancedHeight }
func update(supportedSyncAlgorithm: SyncAlgorithm) async { self.supportedSyncAlgorithm = supportedSyncAlgorithm }
func update(requestedRewindHeight: BlockHeight) async { self.requestedRewindHeight = requestedRewindHeight }
}
enum CBPState: CaseIterable {
@ -43,6 +45,7 @@ enum CBPState: CaseIterable {
case updateSubtreeRoots
case updateChainTip
case processSuggestedScanRanges
case rewind
case computeSyncControlData
case download
case scan

View File

@ -27,13 +27,6 @@ extension ProcessSuggestedScanRangesAction: Action {
let scanRanges = try await rustBackend.suggestScanRanges()
if let firstRange = scanRanges.first {
// If there is a range of blocks that needs to be verified, it will always
// be returned as the first element of the vector of suggested ranges.
if firstRange.priority == .verify {
// TODO: [#1189] handle rewind, https://github.com/zcash/ZcashLightClientKit/issues/1189
// REWIND to download.start height HERE
}
let lowerBound = firstRange.range.lowerBound - 1
let upperBound = firstRange.range.upperBound - 1
@ -55,7 +48,14 @@ extension ProcessSuggestedScanRangesAction: Action {
await context.update(syncControlData: syncControlData)
await context.update(totalProgressRange: lowerBound...upperBound)
await context.update(state: .download)
// If there is a range of blocks that needs to be verified, it will always
// be returned as the first element of the vector of suggested ranges.
if firstRange.priority == .verify {
await context.update(requestedRewindHeight: lowerBound + 1)
await context.update(state: .rewind)
} else {
await context.update(state: .download)
}
} else {
await context.update(state: .finished)
}

View File

@ -0,0 +1,48 @@
//
// RewindAction.swift
//
//
// Created by Lukáš Korba on 09.08.2023.
//
import Foundation
final class RewindAction {
let downloader: BlockDownloader
let rustBackend: ZcashRustBackendWelding
let downloaderService: BlockDownloaderService
let logger: Logger
init(container: DIContainer) {
downloader = container.resolve(BlockDownloader.self)
rustBackend = container.resolve(ZcashRustBackendWelding.self)
downloaderService = container.resolve(BlockDownloaderService.self)
logger = container.resolve(Logger.self)
}
private func update(context: ActionContext) async -> ActionContext {
await context.update(state: .download)
return context
}
}
extension RewindAction: Action {
var removeBlocksCacheWhenFailed: Bool { false }
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
guard let rewindHeight = await context.requestedRewindHeight else {
return await update(context: context)
}
logger.debug("Executing rewind.")
await downloader.rewind(latestDownloadedBlockHeight: rewindHeight)
try await rustBackend.rewindToHeight(height: Int32(rewindHeight))
// clear cache
try await downloaderService.rewind(to: rewindHeight)
return await update(context: context)
}
func stop() async { }
}

View File

@ -63,9 +63,15 @@ extension ScanAction: Action {
// ScanAction is controlled locally so it must report back the updated scanned height
await context.update(lastScannedHeight: lastScannedHeight)
}
} catch ZcashError.rustScanBlocks(let errorMsg) {
if isContinuityError(errorMsg) {
await context.update(requestedRewindHeight: batchRange.lowerBound - 10)
await context.update(state: .download)
return context
} else {
throw ZcashError.rustScanBlocks(errorMsg)
}
} catch {
// TODO: [#1189] check isContinuityError, https://github.com/zcash/ZcashLightClientKit/issues/1189
// if YES, REWIND to height at what error occured - at least 1 block
throw error
}
@ -74,3 +80,11 @@ extension ScanAction: Action {
func stop() async { }
}
private extension ScanAction {
func isContinuityError(_ errorMsg: String) -> Bool {
errorMsg.contains("The parent hash of proposed block does not correspond to the block hash at height")
|| errorMsg.contains("Block height discontinuity at height")
|| errorMsg.contains("note commitment tree size provided by a compact block did not match the expected size at height")
}
}

View File

@ -230,6 +230,8 @@ actor CompactBlockProcessor {
action = UpdateChainTipAction(container: container)
case .processSuggestedScanRanges:
action = ProcessSuggestedScanRangesAction(container: container)
case .rewind:
action = RewindAction(container: container)
case .computeSyncControlData:
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
case .download:
@ -607,6 +609,8 @@ extension CompactBlockProcessor {
break
case .processSuggestedScanRanges:
break
case .rewind:
break
case .computeSyncControlData:
break
case .download: