[#1188] Working prototype of SbS
- cleaned up the code - ScanAlgorithm enum added to the SDK - preferred sync algorithm set to .linear as default but can be changed to Spend before Sync as the Initializer.init parameter [#1188] Working prototype of SbS - error codes for failure states in the SbS State Machine changes added [#1188] Working prototype of SbS (#1192) - offline tests fixed
This commit is contained in:
parent
458aeeea4c
commit
ff3af58a81
|
@ -53,6 +53,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
spendParamsURL: try! spendParamsURLHelper(),
|
spendParamsURL: try! spendParamsURLHelper(),
|
||||||
outputParamsURL: try! outputParamsURLHelper(),
|
outputParamsURL: try! outputParamsURLHelper(),
|
||||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||||
|
syncAlgorithm: .spendBeforeSync,
|
||||||
enableBackendTracing: true
|
enableBackendTracing: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ class SyncBlocksListViewController: UIViewController {
|
||||||
outputParamsURL: try! outputParamsURLHelper(),
|
outputParamsURL: try! outputParamsURLHelper(),
|
||||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||||
alias: data.alias,
|
alias: data.alias,
|
||||||
|
syncAlgorithm: .spendBeforeSync,
|
||||||
loggingPolicy: .default(.debug),
|
loggingPolicy: .default(.debug),
|
||||||
enableBackendTracing: true
|
enableBackendTracing: true
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,13 +11,16 @@ actor ActionContext {
|
||||||
var state: CBPState
|
var state: CBPState
|
||||||
var prevState: CBPState?
|
var prevState: CBPState?
|
||||||
var syncControlData: SyncControlData
|
var syncControlData: SyncControlData
|
||||||
|
let preferredSyncAlgorithm: SyncAlgorithm
|
||||||
|
var supportedSyncAlgorithm: SyncAlgorithm?
|
||||||
var totalProgressRange: CompactBlockRange = 0...0
|
var totalProgressRange: CompactBlockRange = 0...0
|
||||||
var lastScannedHeight: BlockHeight?
|
var lastScannedHeight: BlockHeight?
|
||||||
var lastDownloadedHeight: BlockHeight?
|
var lastDownloadedHeight: BlockHeight?
|
||||||
var lastEnhancedHeight: BlockHeight?
|
var lastEnhancedHeight: BlockHeight?
|
||||||
|
|
||||||
init(state: CBPState) {
|
init(state: CBPState, preferredSyncAlgorithm: SyncAlgorithm = .linear) {
|
||||||
self.state = state
|
self.state = state
|
||||||
|
self.preferredSyncAlgorithm = preferredSyncAlgorithm
|
||||||
syncControlData = SyncControlData.empty
|
syncControlData = SyncControlData.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +33,7 @@ actor ActionContext {
|
||||||
func update(lastScannedHeight: BlockHeight) async { self.lastScannedHeight = lastScannedHeight }
|
func update(lastScannedHeight: BlockHeight) async { self.lastScannedHeight = lastScannedHeight }
|
||||||
func update(lastDownloadedHeight: BlockHeight) async { self.lastDownloadedHeight = lastDownloadedHeight }
|
func update(lastDownloadedHeight: BlockHeight) async { self.lastDownloadedHeight = lastDownloadedHeight }
|
||||||
func update(lastEnhancedHeight: BlockHeight?) async { self.lastEnhancedHeight = lastEnhancedHeight }
|
func update(lastEnhancedHeight: BlockHeight?) async { self.lastEnhancedHeight = lastEnhancedHeight }
|
||||||
|
func update(supportedSyncAlgorithm: SyncAlgorithm) async { self.supportedSyncAlgorithm = supportedSyncAlgorithm }
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CBPState: CaseIterable {
|
enum CBPState: CaseIterable {
|
||||||
|
@ -38,7 +42,7 @@ enum CBPState: CaseIterable {
|
||||||
case validateServer
|
case validateServer
|
||||||
case updateSubtreeRoots
|
case updateSubtreeRoots
|
||||||
case updateChainTip
|
case updateChainTip
|
||||||
case validatePreviousWalletSession
|
case processSuggestedScanRanges
|
||||||
case computeSyncControlData
|
case computeSyncControlData
|
||||||
case download
|
case download
|
||||||
case scan
|
case scan
|
||||||
|
|
|
@ -22,11 +22,9 @@ extension ClearAlreadyScannedBlocksAction: Action {
|
||||||
|
|
||||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||||
guard let lastScannedHeight = await context.lastScannedHeight else {
|
guard let lastScannedHeight = await context.lastScannedHeight else {
|
||||||
fatalError("it must be valid")
|
throw ZcashError.compactBlockProcessorLastScannedHeight
|
||||||
return context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//let lastScannedHeight = //try await transactionRepository.lastScannedHeight()
|
|
||||||
try await storage.clear(upTo: lastScannedHeight)
|
try await storage.clear(upTo: lastScannedHeight)
|
||||||
|
|
||||||
await context.update(state: .enhance)
|
await context.update(state: .enhance)
|
||||||
|
|
|
@ -23,8 +23,19 @@ extension ClearCacheAction: Action {
|
||||||
if await context.prevState == .idle {
|
if await context.prevState == .idle {
|
||||||
await context.update(state: .migrateLegacyCacheDB)
|
await context.update(state: .migrateLegacyCacheDB)
|
||||||
} else {
|
} else {
|
||||||
//await context.update(state: .finished) // Linear
|
if context.preferredSyncAlgorithm == .linear {
|
||||||
await context.update(state: .validatePreviousWalletSession)
|
await context.update(state: .finished)
|
||||||
|
} else {
|
||||||
|
if let supportedSyncAlgorithm = await context.supportedSyncAlgorithm {
|
||||||
|
if supportedSyncAlgorithm == .linear {
|
||||||
|
await context.update(state: .finished)
|
||||||
|
} else {
|
||||||
|
await context.update(state: .processSuggestedScanRanges)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw ZcashError.compactBlockProcessorSupportedSyncAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,10 @@ extension DownloadAction: Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = await configProvider.config
|
let config = await configProvider.config
|
||||||
// let lastScannedHeightDB = try await transactionRepository.lastScannedHeight()
|
|
||||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
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.
|
// 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.
|
// So the right range for this batch must be computed.
|
||||||
let batchRangeStart = lastScannedHeight//max(lastScannedHeightDB, lastScannedHeight)
|
let batchRangeStart = lastScannedHeight
|
||||||
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
||||||
|
|
||||||
guard batchRangeStart <= batchRangeEnd else {
|
guard batchRangeStart <= batchRangeEnd else {
|
||||||
|
|
|
@ -22,18 +22,15 @@ final class EnhanceAction {
|
||||||
|
|
||||||
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
||||||
guard await context.syncControlData.latestScannedHeight != nil else {
|
guard await context.syncControlData.latestScannedHeight != nil else {
|
||||||
await context.update(state: .clearCache) // linear
|
await context.update(state: .clearCache)
|
||||||
// await context.update(state: .validatePreviousWalletSession) // SbS
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
||||||
if lastScannedHeight >= latestBlockHeight {
|
if lastScannedHeight >= latestBlockHeight {
|
||||||
await context.update(state: .clearCache) // linear
|
await context.update(state: .clearCache)
|
||||||
// await context.update(state: .validatePreviousWalletSession) // SbS
|
|
||||||
} else {
|
} else {
|
||||||
await context.update(state: .download) // Linear
|
await context.update(state: .download)
|
||||||
// await context.update(state: .validatePreviousWalletSession) // SbS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
@ -52,10 +49,8 @@ extension EnhanceAction: Action {
|
||||||
// download and scan.
|
// download and scan.
|
||||||
|
|
||||||
let config = await configProvider.config
|
let config = await configProvider.config
|
||||||
//let lastScannedHeight = try await transactionRepository.lastScannedHeight()
|
|
||||||
guard let lastScannedHeight = await context.lastScannedHeight else {
|
guard let lastScannedHeight = await context.lastScannedHeight else {
|
||||||
await context.update(state: .validatePreviousWalletSession)
|
throw ZcashError.compactBlockProcessorLastScannedHeight
|
||||||
return context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let firstUnenhancedHeight = await context.syncControlData.firstUnenhancedHeight else {
|
guard let firstUnenhancedHeight = await context.syncControlData.firstUnenhancedHeight else {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// ProcessSuggestedScanRangesAction.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 02.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
final class ProcessSuggestedScanRangesAction {
|
||||||
|
let rustBackend: ZcashRustBackendWelding
|
||||||
|
let service: LightWalletService
|
||||||
|
let logger: Logger
|
||||||
|
|
||||||
|
init(container: DIContainer) {
|
||||||
|
service = container.resolve(LightWalletService.self)
|
||||||
|
rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
||||||
|
logger = container.resolve(Logger.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProcessSuggestedScanRangesAction: Action {
|
||||||
|
var removeBlocksCacheWhenFailed: Bool { false }
|
||||||
|
|
||||||
|
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||||
|
logger.info("Getting the suggested scan ranges from the wallet database.")
|
||||||
|
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
|
||||||
|
|
||||||
|
let syncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: upperBound,
|
||||||
|
latestScannedHeight: lowerBound,
|
||||||
|
firstUnenhancedHeight: lowerBound + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("""
|
||||||
|
Init numbers:
|
||||||
|
latestBlockHeight [BC]: \(upperBound)
|
||||||
|
latestScannedHeight [DB]: \(lowerBound)
|
||||||
|
firstUnenhancedHeight [DB]: \(lowerBound + 1)
|
||||||
|
""")
|
||||||
|
|
||||||
|
await context.update(lastScannedHeight: lowerBound)
|
||||||
|
await context.update(lastDownloadedHeight: lowerBound)
|
||||||
|
await context.update(syncControlData: syncControlData)
|
||||||
|
await context.update(totalProgressRange: lowerBound...upperBound)
|
||||||
|
|
||||||
|
await context.update(state: .download)
|
||||||
|
} else {
|
||||||
|
await context.update(state: .finished)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() async { }
|
||||||
|
}
|
|
@ -23,8 +23,12 @@ extension SaplingParamsAction: Action {
|
||||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||||
logger.debug("Fetching sapling parameters")
|
logger.debug("Fetching sapling parameters")
|
||||||
try await saplingParametersHandler.handleIfNeeded()
|
try await saplingParametersHandler.handleIfNeeded()
|
||||||
// await context.update(state: .computeSyncControlData) // Linear
|
|
||||||
await context.update(state: .updateSubtreeRoots) // SbS
|
if context.preferredSyncAlgorithm == .spendBeforeSync {
|
||||||
|
await context.update(state: .updateSubtreeRoots)
|
||||||
|
} else {
|
||||||
|
await context.update(state: .computeSyncControlData)
|
||||||
|
}
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,10 @@ extension ScanAction: Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = await configProvider.config
|
let config = await configProvider.config
|
||||||
//let lastScannedHeightDB = try await transactionRepository.lastScannedHeight()
|
|
||||||
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
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.
|
// 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.
|
// So the right range for this batch must be computed.
|
||||||
let batchRangeStart = lastScannedHeight//max(lastScannedHeightDB, lastScannedHeight)
|
let batchRangeStart = lastScannedHeight
|
||||||
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
||||||
|
|
||||||
guard batchRangeStart <= batchRangeEnd else {
|
guard batchRangeStart <= batchRangeEnd else {
|
||||||
|
@ -50,6 +49,8 @@ extension ScanAction: Action {
|
||||||
|
|
||||||
logger.debug("Starting scan blocks with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
logger.debug("Starting scan blocks with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
||||||
let totalProgressRange = await context.totalProgressRange
|
let totalProgressRange = await context.totalProgressRange
|
||||||
|
|
||||||
|
do {
|
||||||
try await blockScanner.scanBlocks(at: batchRange, totalProgressRange: totalProgressRange) { [weak self] lastScannedHeight in
|
try await blockScanner.scanBlocks(at: batchRange, totalProgressRange: totalProgressRange) { [weak self] lastScannedHeight in
|
||||||
let progress = BlockProgress(
|
let progress = BlockProgress(
|
||||||
startHeight: totalProgressRange.lowerBound,
|
startHeight: totalProgressRange.lowerBound,
|
||||||
|
@ -61,13 +62,11 @@ extension ScanAction: Action {
|
||||||
|
|
||||||
// ScanAction is controlled locally so it must report back the updated scanned height
|
// ScanAction is controlled locally so it must report back the updated scanned height
|
||||||
await context.update(lastScannedHeight: lastScannedHeight)
|
await context.update(lastScannedHeight: lastScannedHeight)
|
||||||
// let prevSyncControlData = await context.syncControlData
|
}
|
||||||
// let newSyncControlData = SyncControlData(
|
} catch {
|
||||||
// latestBlockHeight: prevSyncControlData.latestBlockHeight,
|
// TODO: [#1189] check isContinuityError, https://github.com/zcash/ZcashLightClientKit/issues/1189
|
||||||
// latestScannedHeight: lastScannedHeight,
|
// if YES, REWIND to height at what error occured - at least 1 block
|
||||||
// firstUnenhancedHeight: prevSyncControlData.firstUnenhancedHeight
|
throw error
|
||||||
// )
|
|
||||||
// await context.update(syncControlData: newSyncControlData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await update(context: context)
|
return await update(context: context)
|
||||||
|
|
|
@ -28,7 +28,7 @@ extension UpdateChainTipAction: Action {
|
||||||
logger.info("Latest block height is \(latestBlockHeight)")
|
logger.info("Latest block height is \(latestBlockHeight)")
|
||||||
try await rustBackend.updateChainTip(height: Int32(latestBlockHeight))
|
try await rustBackend.updateChainTip(height: Int32(latestBlockHeight))
|
||||||
|
|
||||||
await context.update(state: .validatePreviousWalletSession)
|
await context.update(state: .processSuggestedScanRanges)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,10 @@ extension UpdateSubtreeRootsAction: Action {
|
||||||
// Likewise, no subtree roots results in switching to linear sync.
|
// Likewise, no subtree roots results in switching to linear sync.
|
||||||
if err != nil || roots.isEmpty {
|
if err != nil || roots.isEmpty {
|
||||||
logger.info("Spend before Sync is not possible, switching to linear sync.")
|
logger.info("Spend before Sync is not possible, switching to linear sync.")
|
||||||
|
await context.update(supportedSyncAlgorithm: .linear)
|
||||||
await context.update(state: .computeSyncControlData)
|
await context.update(state: .computeSyncControlData)
|
||||||
} else {
|
} else {
|
||||||
|
await context.update(supportedSyncAlgorithm: .spendBeforeSync)
|
||||||
logger.info("Sapling tree has \(roots.count) subtrees")
|
logger.info("Sapling tree has \(roots.count) subtrees")
|
||||||
do {
|
do {
|
||||||
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
|
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
//
|
|
||||||
// ValidatePreviousWalletSessionAction.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Lukáš Korba on 02.08.2023.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
final class ValidatePreviousWalletSessionAction {
|
|
||||||
let rustBackend: ZcashRustBackendWelding
|
|
||||||
let service: LightWalletService
|
|
||||||
let logger: Logger
|
|
||||||
|
|
||||||
init(container: DIContainer) {
|
|
||||||
service = container.resolve(LightWalletService.self)
|
|
||||||
rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
|
||||||
logger = container.resolve(Logger.self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ValidatePreviousWalletSessionAction: Action {
|
|
||||||
var removeBlocksCacheWhenFailed: Bool { false }
|
|
||||||
|
|
||||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
||||||
logger.info("Getting the suggested scan ranges from the wallet database.")
|
|
||||||
let scanRanges = try await rustBackend.suggestScanRanges()
|
|
||||||
|
|
||||||
print("__LD count \(scanRanges.count) first range \(scanRanges.first)")
|
|
||||||
|
|
||||||
// Run the following loop until the wallet's view of the chain tip
|
|
||||||
// as of the previous wallet session is valid.
|
|
||||||
// while true {
|
|
||||||
// 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 let firstRange = scanRanges.first {
|
|
||||||
//if firstRange.priority == .verify {
|
|
||||||
let lowerBound = firstRange.range.lowerBound - 1
|
|
||||||
let upperBound = firstRange.range.upperBound - 1
|
|
||||||
|
|
||||||
let syncControlData = SyncControlData(
|
|
||||||
latestBlockHeight: upperBound,
|
|
||||||
latestScannedHeight: lowerBound,
|
|
||||||
firstUnenhancedHeight: lowerBound + 1
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug("""
|
|
||||||
Init numbers:
|
|
||||||
latestBlockHeight [BC]: \(upperBound)
|
|
||||||
latestScannedHeight [DB]: \(lowerBound)
|
|
||||||
firstUnenhancedHeight [DB]: \(lowerBound + 1)
|
|
||||||
""")
|
|
||||||
|
|
||||||
if scanRanges.count == 1 {
|
|
||||||
print("cool")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await context.update(lastScannedHeight: lowerBound)
|
|
||||||
await context.update(lastDownloadedHeight: lowerBound)
|
|
||||||
await context.update(syncControlData: syncControlData)
|
|
||||||
await context.update(totalProgressRange: lowerBound...upperBound)
|
|
||||||
|
|
||||||
await context.update(state: .download)
|
|
||||||
// } else {
|
|
||||||
// print("cool")
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
await context.update(state: .finished)
|
|
||||||
}
|
|
||||||
// } else {
|
|
||||||
// // Nothing to verify; break out of the loop
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: [#1171] Switching back to linear sync for now before step 7 are implemented
|
|
||||||
// https://github.com/zcash/ZcashLightClientKit/issues/1171
|
|
||||||
// await context.update(state: .computeSyncControlData)
|
|
||||||
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop() async { }
|
|
||||||
}
|
|
|
@ -70,6 +70,7 @@ actor CompactBlockProcessor {
|
||||||
let network: ZcashNetwork
|
let network: ZcashNetwork
|
||||||
let saplingActivation: BlockHeight
|
let saplingActivation: BlockHeight
|
||||||
let cacheDbURL: URL?
|
let cacheDbURL: URL?
|
||||||
|
let syncAlgorithm: SyncAlgorithm
|
||||||
var blockPollInterval: TimeInterval {
|
var blockPollInterval: TimeInterval {
|
||||||
TimeInterval.random(in: ZcashSDK.defaultPollInterval / 2 ... ZcashSDK.defaultPollInterval * 1.5)
|
TimeInterval.random(in: ZcashSDK.defaultPollInterval / 2 ... ZcashSDK.defaultPollInterval * 1.5)
|
||||||
}
|
}
|
||||||
|
@ -89,6 +90,7 @@ actor CompactBlockProcessor {
|
||||||
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
|
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
|
||||||
walletBirthdayProvider: @escaping () -> BlockHeight,
|
walletBirthdayProvider: @escaping () -> BlockHeight,
|
||||||
saplingActivation: BlockHeight,
|
saplingActivation: BlockHeight,
|
||||||
|
syncAlgorithm: SyncAlgorithm = .linear,
|
||||||
network: ZcashNetwork
|
network: ZcashNetwork
|
||||||
) {
|
) {
|
||||||
self.alias = alias
|
self.alias = alias
|
||||||
|
@ -106,6 +108,7 @@ actor CompactBlockProcessor {
|
||||||
self.walletBirthdayProvider = walletBirthdayProvider
|
self.walletBirthdayProvider = walletBirthdayProvider
|
||||||
self.saplingActivation = saplingActivation
|
self.saplingActivation = saplingActivation
|
||||||
self.cacheDbURL = cacheDbURL
|
self.cacheDbURL = cacheDbURL
|
||||||
|
self.syncAlgorithm = syncAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
init(
|
init(
|
||||||
|
@ -121,6 +124,7 @@ actor CompactBlockProcessor {
|
||||||
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
|
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
|
||||||
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
|
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
|
||||||
walletBirthdayProvider: @escaping () -> BlockHeight,
|
walletBirthdayProvider: @escaping () -> BlockHeight,
|
||||||
|
syncAlgorithm: SyncAlgorithm = .linear,
|
||||||
network: ZcashNetwork
|
network: ZcashNetwork
|
||||||
) {
|
) {
|
||||||
self.alias = alias
|
self.alias = alias
|
||||||
|
@ -138,6 +142,7 @@ actor CompactBlockProcessor {
|
||||||
self.retries = retries
|
self.retries = retries
|
||||||
self.maxBackoffInterval = maxBackoffInterval
|
self.maxBackoffInterval = maxBackoffInterval
|
||||||
self.rewindDistance = rewindDistance
|
self.rewindDistance = rewindDistance
|
||||||
|
self.syncAlgorithm = syncAlgorithm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,13 +174,18 @@ actor CompactBlockProcessor {
|
||||||
outputParamsURL: initializer.outputParamsURL,
|
outputParamsURL: initializer.outputParamsURL,
|
||||||
saplingParamsSourceURL: initializer.saplingParamsSourceURL,
|
saplingParamsSourceURL: initializer.saplingParamsSourceURL,
|
||||||
walletBirthdayProvider: walletBirthdayProvider,
|
walletBirthdayProvider: walletBirthdayProvider,
|
||||||
|
syncAlgorithm: initializer.syncAlgorithm,
|
||||||
network: initializer.network
|
network: initializer.network
|
||||||
),
|
),
|
||||||
accountRepository: initializer.accountRepository
|
accountRepository: initializer.accountRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(container: DIContainer, config: Configuration, accountRepository: AccountRepository) {
|
init(
|
||||||
|
container: DIContainer,
|
||||||
|
config: Configuration,
|
||||||
|
accountRepository: AccountRepository
|
||||||
|
) {
|
||||||
Dependencies.setupCompactBlockProcessor(
|
Dependencies.setupCompactBlockProcessor(
|
||||||
in: container,
|
in: container,
|
||||||
config: config,
|
config: config,
|
||||||
|
@ -183,7 +193,7 @@ actor CompactBlockProcessor {
|
||||||
)
|
)
|
||||||
|
|
||||||
let configProvider = ConfigProvider(config: config)
|
let configProvider = ConfigProvider(config: config)
|
||||||
context = ActionContext(state: .idle)
|
context = ActionContext(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
|
||||||
actions = Self.makeActions(container: container, configProvider: configProvider)
|
actions = Self.makeActions(container: container, configProvider: configProvider)
|
||||||
|
|
||||||
self.metrics = container.resolve(SDKMetrics.self)
|
self.metrics = container.resolve(SDKMetrics.self)
|
||||||
|
@ -218,8 +228,8 @@ actor CompactBlockProcessor {
|
||||||
action = UpdateSubtreeRootsAction(container: container)
|
action = UpdateSubtreeRootsAction(container: container)
|
||||||
case .updateChainTip:
|
case .updateChainTip:
|
||||||
action = UpdateChainTipAction(container: container)
|
action = UpdateChainTipAction(container: container)
|
||||||
case .validatePreviousWalletSession:
|
case .processSuggestedScanRanges:
|
||||||
action = ValidatePreviousWalletSessionAction(container: container)
|
action = ProcessSuggestedScanRangesAction(container: container)
|
||||||
case .computeSyncControlData:
|
case .computeSyncControlData:
|
||||||
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
|
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
|
||||||
case .download:
|
case .download:
|
||||||
|
@ -595,7 +605,7 @@ extension CompactBlockProcessor {
|
||||||
break
|
break
|
||||||
case .updateChainTip:
|
case .updateChainTip:
|
||||||
break
|
break
|
||||||
case .validatePreviousWalletSession:
|
case .processSuggestedScanRanges:
|
||||||
break
|
break
|
||||||
case .computeSyncControlData:
|
case .computeSyncControlData:
|
||||||
break
|
break
|
||||||
|
@ -624,7 +634,7 @@ extension CompactBlockProcessor {
|
||||||
|
|
||||||
private func resetContext() async {
|
private func resetContext() async {
|
||||||
let lastEnhancedheight = await context.lastEnhancedHeight
|
let lastEnhancedheight = await context.lastEnhancedHeight
|
||||||
context = ActionContext(state: .idle)
|
context = ActionContext(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
|
||||||
await context.update(lastEnhancedHeight: lastEnhancedheight)
|
await context.update(lastEnhancedHeight: lastEnhancedheight)
|
||||||
await compactBlockProgress.reset()
|
await compactBlockProgress.reset()
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ extension BlockScannerImpl: BlockScanner {
|
||||||
logger.debug("Going to scan blocks in range: \(range)")
|
logger.debug("Going to scan blocks in range: \(range)")
|
||||||
try Task.checkCancellation()
|
try Task.checkCancellation()
|
||||||
|
|
||||||
let scanStartHeight = range.lowerBound//try await transactionRepository.lastScannedHeight()
|
let scanStartHeight = range.lowerBound
|
||||||
let targetScanHeight = range.upperBound
|
let targetScanHeight = range.upperBound
|
||||||
|
|
||||||
var scannedNewBlocks = false
|
var scannedNewBlocks = false
|
||||||
|
@ -65,13 +65,7 @@ extension BlockScannerImpl: BlockScanner {
|
||||||
|
|
||||||
let scanFinishTime = Date()
|
let scanFinishTime = Date()
|
||||||
|
|
||||||
// if let lastScannedBlock = try await transactionRepository.lastScannedBlock() {
|
|
||||||
// lastScannedHeight = lastScannedBlock.height
|
|
||||||
lastScannedHeight = startHeight + Int(batchSize) - 1
|
lastScannedHeight = startHeight + Int(batchSize) - 1
|
||||||
await latestBlocksDataProvider.updateLatestScannedHeight(lastScannedHeight)
|
|
||||||
// await latestBlocksDataProvider.updateLatestScannedTime(TimeInterval(lastScannedBlock.time))
|
|
||||||
// }
|
|
||||||
// lastScannedHeight = targetScanHeight
|
|
||||||
|
|
||||||
scannedNewBlocks = previousScannedHeight != lastScannedHeight
|
scannedNewBlocks = previousScannedHeight != lastScannedHeight
|
||||||
if scannedNewBlocks {
|
if scannedNewBlocks {
|
||||||
|
|
|
@ -540,6 +540,12 @@ public enum ZcashError: Equatable, Error {
|
||||||
/// Put sapling subtree roots to the DB failed.
|
/// Put sapling subtree roots to the DB failed.
|
||||||
/// ZCBPEO0019
|
/// ZCBPEO0019
|
||||||
case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error)
|
case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error)
|
||||||
|
/// Getting the `lastScannedHeight` failed but it's supposed to always provide some value.
|
||||||
|
/// ZCBPEO0020
|
||||||
|
case compactBlockProcessorLastScannedHeight
|
||||||
|
/// Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value.
|
||||||
|
/// ZCBPEO0021
|
||||||
|
case compactBlockProcessorSupportedSyncAlgorithm
|
||||||
/// The synchronizer is unprepared.
|
/// The synchronizer is unprepared.
|
||||||
/// ZSYNCO0001
|
/// ZSYNCO0001
|
||||||
case synchronizerNotPrepared
|
case synchronizerNotPrepared
|
||||||
|
@ -715,6 +721,8 @@ public enum ZcashError: Equatable, Error {
|
||||||
case .compactBlockProcessorConsensusBranchID: return "Consensus BranchIDs don't match 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 .compactBlockProcessorDownloadBlockActionRewind: return "Rewind of DownloadBlockAction failed as no action is possible to unwrapp."
|
||||||
case .compactBlockProcessorPutSaplingSubtreeRoots: return "Put sapling subtree roots to the DB failed."
|
case .compactBlockProcessorPutSaplingSubtreeRoots: return "Put sapling subtree roots to the DB failed."
|
||||||
|
case .compactBlockProcessorLastScannedHeight: return "Getting the `lastScannedHeight` failed but it's supposed to always provide some value."
|
||||||
|
case .compactBlockProcessorSupportedSyncAlgorithm: return "Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value."
|
||||||
case .synchronizerNotPrepared: return "The synchronizer is unprepared."
|
case .synchronizerNotPrepared: return "The synchronizer is unprepared."
|
||||||
case .synchronizerSendMemoToTransparentAddress: return "Memos can't be sent to transparent addresses."
|
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 .synchronizerShieldFundsInsuficientTransparentFunds: return "There is not enough transparent funds to cover fee for the shielding."
|
||||||
|
@ -880,6 +888,8 @@ public enum ZcashError: Equatable, Error {
|
||||||
case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID
|
case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID
|
||||||
case .compactBlockProcessorDownloadBlockActionRewind: return .compactBlockProcessorDownloadBlockActionRewind
|
case .compactBlockProcessorDownloadBlockActionRewind: return .compactBlockProcessorDownloadBlockActionRewind
|
||||||
case .compactBlockProcessorPutSaplingSubtreeRoots: return .compactBlockProcessorPutSaplingSubtreeRoots
|
case .compactBlockProcessorPutSaplingSubtreeRoots: return .compactBlockProcessorPutSaplingSubtreeRoots
|
||||||
|
case .compactBlockProcessorLastScannedHeight: return .compactBlockProcessorLastScannedHeight
|
||||||
|
case .compactBlockProcessorSupportedSyncAlgorithm: return .compactBlockProcessorSupportedSyncAlgorithm
|
||||||
case .synchronizerNotPrepared: return .synchronizerNotPrepared
|
case .synchronizerNotPrepared: return .synchronizerNotPrepared
|
||||||
case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress
|
case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress
|
||||||
case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds
|
case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds
|
||||||
|
|
|
@ -317,6 +317,10 @@ public enum ZcashErrorCode: String {
|
||||||
case compactBlockProcessorDownloadBlockActionRewind = "ZCBPEO0018"
|
case compactBlockProcessorDownloadBlockActionRewind = "ZCBPEO0018"
|
||||||
/// Put sapling subtree roots to the DB failed.
|
/// Put sapling subtree roots to the DB failed.
|
||||||
case compactBlockProcessorPutSaplingSubtreeRoots = "ZCBPEO0019"
|
case compactBlockProcessorPutSaplingSubtreeRoots = "ZCBPEO0019"
|
||||||
|
/// Getting the `lastScannedHeight` failed but it's supposed to always provide some value.
|
||||||
|
case compactBlockProcessorLastScannedHeight = "ZCBPEO0020"
|
||||||
|
/// Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value.
|
||||||
|
case compactBlockProcessorSupportedSyncAlgorithm = "ZCBPEO0021"
|
||||||
/// The synchronizer is unprepared.
|
/// The synchronizer is unprepared.
|
||||||
case synchronizerNotPrepared = "ZSYNCO0001"
|
case synchronizerNotPrepared = "ZSYNCO0001"
|
||||||
/// Memos can't be sent to transparent addresses.
|
/// Memos can't be sent to transparent addresses.
|
||||||
|
|
|
@ -613,6 +613,12 @@ enum ZcashErrorDefinition {
|
||||||
/// Put sapling subtree roots to the DB failed.
|
/// Put sapling subtree roots to the DB failed.
|
||||||
// sourcery: code="ZCBPEO0019"
|
// sourcery: code="ZCBPEO0019"
|
||||||
case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error)
|
case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error)
|
||||||
|
/// Getting the `lastScannedHeight` failed but it's supposed to always provide some value.
|
||||||
|
// sourcery: code="ZCBPEO0020"
|
||||||
|
case compactBlockProcessorLastScannedHeight
|
||||||
|
/// Getting the `supportedSyncAlgorithm` failed but it's supposed to always provide some value.
|
||||||
|
// sourcery: code="ZCBPEO0021"
|
||||||
|
case compactBlockProcessorSupportedSyncAlgorithm
|
||||||
|
|
||||||
// MARK: - SDKSynchronizer
|
// MARK: - SDKSynchronizer
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ public class Initializer {
|
||||||
let network: ZcashNetwork
|
let network: ZcashNetwork
|
||||||
let logger: Logger
|
let logger: Logger
|
||||||
let rustBackend: ZcashRustBackendWelding
|
let rustBackend: ZcashRustBackendWelding
|
||||||
|
let syncAlgorithm: SyncAlgorithm
|
||||||
|
|
||||||
/// The effective birthday of the wallet based on the height provided when initializing and the checkpoints available on this SDK.
|
/// The effective birthday of the wallet based on the height provided when initializing and the checkpoints available on this SDK.
|
||||||
///
|
///
|
||||||
|
@ -165,6 +166,7 @@ public class Initializer {
|
||||||
outputParamsURL: URL,
|
outputParamsURL: URL,
|
||||||
saplingParamsSourceURL: SaplingParamsSourceURL,
|
saplingParamsSourceURL: SaplingParamsSourceURL,
|
||||||
alias: ZcashSynchronizerAlias = .default,
|
alias: ZcashSynchronizerAlias = .default,
|
||||||
|
syncAlgorithm: SyncAlgorithm = .linear,
|
||||||
loggingPolicy: LoggingPolicy = .default(.debug),
|
loggingPolicy: LoggingPolicy = .default(.debug),
|
||||||
enableBackendTracing: Bool = false
|
enableBackendTracing: Bool = false
|
||||||
) {
|
) {
|
||||||
|
@ -197,6 +199,7 @@ public class Initializer {
|
||||||
saplingParamsSourceURL: saplingParamsSourceURL,
|
saplingParamsSourceURL: saplingParamsSourceURL,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
urlsParsingError: parsingError,
|
urlsParsingError: parsingError,
|
||||||
|
syncAlgorithm: syncAlgorithm,
|
||||||
loggingPolicy: loggingPolicy
|
loggingPolicy: loggingPolicy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -257,6 +260,7 @@ public class Initializer {
|
||||||
saplingParamsSourceURL: SaplingParamsSourceURL,
|
saplingParamsSourceURL: SaplingParamsSourceURL,
|
||||||
alias: ZcashSynchronizerAlias,
|
alias: ZcashSynchronizerAlias,
|
||||||
urlsParsingError: ZcashError?,
|
urlsParsingError: ZcashError?,
|
||||||
|
syncAlgorithm: SyncAlgorithm = .linear,
|
||||||
loggingPolicy: LoggingPolicy = .default(.debug)
|
loggingPolicy: LoggingPolicy = .default(.debug)
|
||||||
) {
|
) {
|
||||||
self.container = container
|
self.container = container
|
||||||
|
@ -284,6 +288,7 @@ public class Initializer {
|
||||||
self.walletBirthday = Checkpoint.birthday(with: 0, network: network).height
|
self.walletBirthday = Checkpoint.birthday(with: 0, network: network).height
|
||||||
self.urlsParsingError = urlsParsingError
|
self.urlsParsingError = urlsParsingError
|
||||||
self.logger = container.resolve(Logger.self)
|
self.logger = container.resolve(Logger.self)
|
||||||
|
self.syncAlgorithm = syncAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {
|
private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {
|
||||||
|
|
|
@ -120,6 +120,9 @@ public protocol Synchronizer: AnyObject {
|
||||||
/// An object that when enabled collects mertrics from the synchronizer
|
/// An object that when enabled collects mertrics from the synchronizer
|
||||||
var metrics: SDKMetrics { get }
|
var metrics: SDKMetrics { get }
|
||||||
|
|
||||||
|
/// Default algorithm used to sync the stored wallet with the blockchain.
|
||||||
|
var syncAlgorithm: SyncAlgorithm { get }
|
||||||
|
|
||||||
/// Initialize the wallet. The ZIP-32 seed bytes can optionally be passed to perform
|
/// Initialize the wallet. The ZIP-32 seed bytes can optionally be passed to perform
|
||||||
/// database migrations. most of the times the seed won't be needed. If they do and are
|
/// database migrations. most of the times the seed won't be needed. If they do and are
|
||||||
/// not provided this will fail with `InitializationResult.seedRequired`. It could
|
/// not provided this will fail with `InitializationResult.seedRequired`. It could
|
||||||
|
@ -427,6 +430,15 @@ enum InternalSyncStatus: Equatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Algorithm used to sync the sdk with the blockchain
|
||||||
|
public enum SyncAlgorithm: Equatable {
|
||||||
|
/// Linear sync processes the unsynced blocks in a linear way up to the chain tip
|
||||||
|
case linear
|
||||||
|
/// Spend before Sync processes the unsynced blocks non-lineary, in prioritised ranges relevant to the stored wallet.
|
||||||
|
/// Note: This feature is in development (alpha version) so use carefully.
|
||||||
|
case spendBeforeSync
|
||||||
|
}
|
||||||
|
|
||||||
/// Kind of transactions handled by a Synchronizer
|
/// Kind of transactions handled by a Synchronizer
|
||||||
public enum TransactionKind {
|
public enum TransactionKind {
|
||||||
case sent
|
case sent
|
||||||
|
|
|
@ -24,6 +24,8 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
|
|
||||||
public let metrics: SDKMetrics
|
public let metrics: SDKMetrics
|
||||||
public let logger: Logger
|
public let logger: Logger
|
||||||
|
public var syncAlgorithm: SyncAlgorithm = .linear
|
||||||
|
private var requestedSyncAlgorithm: SyncAlgorithm?
|
||||||
|
|
||||||
// Don't read this variable directly. Use `status` instead. And don't update this variable directly use `updateStatus()` methods instead.
|
// Don't read this variable directly. Use `status` instead. And don't update this variable directly use `updateStatus()` methods instead.
|
||||||
private var underlyingStatus: GenericActor<InternalSyncStatus>
|
private var underlyingStatus: GenericActor<InternalSyncStatus>
|
||||||
|
@ -87,6 +89,7 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
self.syncSession = SyncSession(.nullID)
|
self.syncSession = SyncSession(.nullID)
|
||||||
self.syncSessionTicker = syncSessionTicker
|
self.syncSessionTicker = syncSessionTicker
|
||||||
self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self)
|
self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self)
|
||||||
|
self.syncAlgorithm = initializer.syncAlgorithm
|
||||||
|
|
||||||
initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in
|
initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in
|
||||||
self?.connectivityStateChanged(oldState: oldState, newState: newState)
|
self?.connectivityStateChanged(oldState: oldState, newState: newState)
|
||||||
|
|
|
@ -25,9 +25,11 @@ final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await clearAlreadyScannedBlocksAction.run(with: .init(state: .clearAlreadyScannedBlocks)) { _ in }
|
let context = ActionContext(state: .clearAlreadyScannedBlocks)
|
||||||
|
await context.update(lastScannedHeight: -1)
|
||||||
|
|
||||||
|
let nextContext = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }
|
||||||
XCTAssertTrue(compactBlockRepositoryMock.clearUpToCalled, "storage.clear(upTo:) is expected to be called.")
|
XCTAssertTrue(compactBlockRepositoryMock.clearUpToCalled, "storage.clear(upTo:) is expected to be called.")
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
let nextState = await nextContext.state
|
let nextState = await nextContext.state
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(
|
||||||
nextState == .enhance,
|
nextState == .enhance,
|
||||||
|
|
|
@ -22,7 +22,7 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
blockDownloaderMock.setDownloadLimitClosure = { _ in }
|
blockDownloaderMock.setDownloadLimitClosure = { _ in }
|
||||||
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
|
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
|
||||||
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInClosure = { _ in }
|
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInClosure = { _ in }
|
||||||
blockDownloaderMock.updateLatestDownloadedBlockHeightClosure = { _ in }
|
blockDownloaderMock.updateLatestDownloadedBlockHeightForceClosure = { _, _ in }
|
||||||
|
|
||||||
let downloadAction = setupAction(
|
let downloadAction = setupAction(
|
||||||
blockDownloaderMock,
|
blockDownloaderMock,
|
||||||
|
@ -33,11 +33,11 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = await setupActionContext()
|
||||||
|
await syncContext.update(lastScannedHeight: 1000)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
||||||
|
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertTrue(blockDownloaderMock.setSyncRangeBatchSizeCalled, "downloader.setSyncRange() is expected to be called.")
|
XCTAssertTrue(blockDownloaderMock.setSyncRangeBatchSizeCalled, "downloader.setSyncRange() is expected to be called.")
|
||||||
XCTAssertTrue(blockDownloaderMock.setDownloadLimitCalled, "downloader.setDownloadLimit() is expected to be called.")
|
XCTAssertTrue(blockDownloaderMock.setDownloadLimitCalled, "downloader.setDownloadLimit() is expected to be called.")
|
||||||
XCTAssertTrue(blockDownloaderMock.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is expected to be called.")
|
XCTAssertTrue(blockDownloaderMock.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is expected to be called.")
|
||||||
|
@ -111,7 +111,6 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
do {
|
do {
|
||||||
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
||||||
|
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertFalse(blockDownloaderMock.setSyncRangeBatchSizeCalled, "downloader.setSyncRange() is not expected to be called.")
|
XCTAssertFalse(blockDownloaderMock.setSyncRangeBatchSizeCalled, "downloader.setSyncRange() is not expected to be called.")
|
||||||
XCTAssertFalse(blockDownloaderMock.setDownloadLimitCalled, "downloader.setDownloadLimit() is not expected to be called.")
|
XCTAssertFalse(blockDownloaderMock.setDownloadLimitCalled, "downloader.setDownloadLimit() is not expected to be called.")
|
||||||
XCTAssertFalse(blockDownloaderMock.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is not expected to be called.")
|
XCTAssertFalse(blockDownloaderMock.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is not expected to be called.")
|
||||||
|
|
|
@ -80,7 +80,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
_ = 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(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testEnhanceAction_NoEnhanceRange is not expected to fail. \(error)")
|
XCTFail("testEnhanceAction_NoEnhanceRange is not expected to fail. \(error)")
|
||||||
|
@ -104,7 +103,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
_ = 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(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testEnhanceAction_1000BlocksConditionNotFulfilled is not expected to fail. \(error)")
|
XCTFail("testEnhanceAction_1000BlocksConditionNotFulfilled is not expected to fail. \(error)")
|
||||||
|
@ -163,8 +161,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
XCTAssertEqual(receivedTransaction.expiryHeight, transaction.expiryHeight, "ReceivedTransaction differs from mocked one.")
|
XCTAssertEqual(receivedTransaction.expiryHeight, transaction.expiryHeight, "ReceivedTransaction differs from mocked one.")
|
||||||
}
|
}
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertTrue(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is expected to be called.")
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions is not expected to fail. \(error)")
|
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -226,8 +222,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertEqual(minedTransaction.expiryHeight, transaction.expiryHeight, "MinedTransaction differs from mocked one.")
|
XCTAssertEqual(minedTransaction.expiryHeight, transaction.expiryHeight, "MinedTransaction differs from mocked one.")
|
||||||
}
|
}
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertTrue(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is expected to be called.")
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction is not expected to fail. \(error)")
|
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -289,8 +283,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertEqual(minedTransaction.expiryHeight, transaction.expiryHeight, "MinedTransaction differs from mocked one.")
|
XCTAssertEqual(minedTransaction.expiryHeight, transaction.expiryHeight, "MinedTransaction differs from mocked one.")
|
||||||
}
|
}
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertTrue(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is expected to be called.")
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction is not expected to fail. \(error)")
|
XCTFail("testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -307,6 +299,7 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
await syncContext.update(syncControlData: syncControlData)
|
await syncContext.update(syncControlData: syncControlData)
|
||||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||||
|
await syncContext.update(lastScannedHeight: underlyingScanRange?.lowerBound ?? -1)
|
||||||
|
|
||||||
return syncContext
|
return syncContext
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ final class SaplingParamsActionTests: ZcashTestCase {
|
||||||
XCTAssertTrue(saplingParametersHandlerMock.handleIfNeededCalled, "saplingParametersHandler.handleIfNeeded() is expected to be called.")
|
XCTAssertTrue(saplingParametersHandlerMock.handleIfNeededCalled, "saplingParametersHandler.handleIfNeeded() is expected to be called.")
|
||||||
let nextState = await nextContext.state
|
let nextState = await nextContext.state
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(
|
||||||
nextState == .updateSubtreeRoots,
|
nextState == .computeSyncControlData,
|
||||||
"nextContext after .handleSaplingParams is expected to be .updateSubtreeRoots but received \(nextState)"
|
"nextContext after .handleSaplingParams is expected to be .computeSyncControlData but received \(nextState)"
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
|
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
|
||||||
|
|
|
@ -22,6 +22,8 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = await setupActionContext()
|
||||||
|
|
||||||
|
await syncContext.update(lastScannedHeight: 1500)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await scanAction.run(with: syncContext) { event in
|
let nextContext = try await scanAction.run(with: syncContext) { event in
|
||||||
guard case .progressPartialUpdate(.syncing(let progress)) = event else {
|
guard case .progressPartialUpdate(.syncing(let progress)) = event else {
|
||||||
|
@ -32,7 +34,6 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
XCTAssertEqual(progress.targetHeight, BlockHeight(2000))
|
XCTAssertEqual(progress.targetHeight, BlockHeight(2000))
|
||||||
XCTAssertEqual(progress.progressHeight, BlockHeight(1500))
|
XCTAssertEqual(progress.progressHeight, BlockHeight(1500))
|
||||||
}
|
}
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||||
XCTAssertTrue(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
|
XCTAssertTrue(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
|
||||||
let nextState = await nextContext.state
|
let nextState = await nextContext.state
|
||||||
|
@ -78,7 +79,6 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await scanAction.run(with: syncContext) { _ in }
|
_ = try await scanAction.run(with: syncContext) { _ in }
|
||||||
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
|
|
||||||
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is not expected to be called.")
|
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is not expected to be called.")
|
||||||
XCTAssertFalse(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is not expected to be called.")
|
XCTAssertFalse(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is not expected to be called.")
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -1043,6 +1043,10 @@ class SynchronizerMock: Synchronizer {
|
||||||
get { return underlyingMetrics }
|
get { return underlyingMetrics }
|
||||||
}
|
}
|
||||||
var underlyingMetrics: SDKMetrics!
|
var underlyingMetrics: SDKMetrics!
|
||||||
|
var syncAlgorithm: SyncAlgorithm {
|
||||||
|
get { return underlyingSyncAlgorithm }
|
||||||
|
}
|
||||||
|
var underlyingSyncAlgorithm: SyncAlgorithm!
|
||||||
var pendingTransactions: [ZcashTransaction.Overview] {
|
var pendingTransactions: [ZcashTransaction.Overview] {
|
||||||
get async { return underlyingPendingTransactions }
|
get async { return underlyingPendingTransactions }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue