Migrate to Rust backend with fast spendability support
This commit is contained in:
parent
6bb054ba2f
commit
6eb39561ec
|
@ -104,7 +104,7 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "cdbc06f10b2d7cbe0d6362b30f68167825942e86"
|
||||
"revision" : "57eb3bd4db3c26bf44d2d8d8b0d6f09f7602a125"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -16,7 +16,7 @@ let package = Package(
|
|||
dependencies: [
|
||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.14.0"),
|
||||
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
|
||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "cdbc06f10b2d7cbe0d6362b30f68167825942e86")
|
||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "57eb3bd4db3c26bf44d2d8d8b0d6f09f7602a125")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
|
@ -36,7 +36,6 @@ enum CBPState: CaseIterable {
|
|||
case validateServer
|
||||
case computeSyncControlData
|
||||
case download
|
||||
case validate
|
||||
case scan
|
||||
case clearAlreadyScannedBlocks
|
||||
case enhance
|
||||
|
|
|
@ -21,7 +21,7 @@ final class DownloadAction {
|
|||
}
|
||||
|
||||
private func update(context: ActionContext) async -> ActionContext {
|
||||
await context.update(state: .validate)
|
||||
await context.update(state: .scan)
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
//
|
||||
// ValidateAction.swift
|
||||
//
|
||||
//
|
||||
// Created by Michal Fousek on 05.05.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class ValidateAction {
|
||||
let validator: BlockValidator
|
||||
|
||||
init(container: DIContainer) {
|
||||
validator = container.resolve(BlockValidator.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension ValidateAction: Action {
|
||||
var removeBlocksCacheWhenFailed: Bool { true }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
try await validator.validate()
|
||||
await context.update(state: .scan)
|
||||
return context
|
||||
}
|
||||
|
||||
func stop() async { }
|
||||
}
|
|
@ -218,8 +218,6 @@ actor CompactBlockProcessor {
|
|||
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
|
||||
case .download:
|
||||
action = DownloadAction(container: container, configProvider: configProvider)
|
||||
case .validate:
|
||||
action = ValidateAction(container: container)
|
||||
case .scan:
|
||||
action = ScanAction(container: container, configProvider: configProvider)
|
||||
case .clearAlreadyScannedBlocks:
|
||||
|
@ -591,8 +589,6 @@ extension CompactBlockProcessor {
|
|||
break
|
||||
case .download:
|
||||
break
|
||||
case .validate:
|
||||
break
|
||||
case .scan:
|
||||
break
|
||||
case .clearAlreadyScannedBlocks:
|
||||
|
|
|
@ -50,13 +50,14 @@ extension BlockScannerImpl: BlockScanner {
|
|||
try Task.checkCancellation()
|
||||
|
||||
let previousScannedHeight = lastScannedHeight
|
||||
let startHeight = previousScannedHeight + 1
|
||||
|
||||
// TODO: [#576] remove this arbitrary batch size https://github.com/zcash/ZcashLightClientKit/issues/576
|
||||
let batchSize = scanBatchSize(startScanHeight: previousScannedHeight + 1, network: config.networkType)
|
||||
let batchSize = scanBatchSize(startScanHeight: startHeight, network: config.networkType)
|
||||
|
||||
let scanStartTime = Date()
|
||||
do {
|
||||
try await self.rustBackend.scanBlocks(limit: batchSize)
|
||||
try await self.rustBackend.scanBlocks(fromHeight: Int32(startHeight), limit: batchSize)
|
||||
} catch {
|
||||
logger.debug("block scanning failed with error: \(String(describing: error))")
|
||||
throw error
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
//
|
||||
// CompactBlockValidationInformation.swift
|
||||
// ZcashLightClientKit
|
||||
//
|
||||
// Created by Francisco Gindre on 10/30/19.
|
||||
// Copyright © 2019 Electric Coin Company. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol BlockValidator {
|
||||
/// Validate all the downloaded blocks that haven't been yet validated.
|
||||
func validate() async throws
|
||||
}
|
||||
|
||||
struct BlockValidatorImpl {
|
||||
let rustBackend: ZcashRustBackendWelding
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
}
|
||||
|
||||
extension BlockValidatorImpl: BlockValidator {
|
||||
/// - Throws:
|
||||
/// - `rustValidateCombinedChainValidationFailed` if there was an error during validation unrelated to chain validity.
|
||||
/// - `rustValidateCombinedChainInvalidChain(upperBound)` if the combined chain is invalid. `upperBound` is the height of the highest invalid
|
||||
/// block(on the assumption that the highest block in the cache database is correct).
|
||||
func validate() async throws {
|
||||
try Task.checkCancellation()
|
||||
|
||||
let startTime = Date()
|
||||
do {
|
||||
try await rustBackend.validateCombinedChain(limit: 0)
|
||||
pushProgressReport(startTime: startTime, finishTime: Date())
|
||||
logger.debug("validateChainFinished")
|
||||
} catch {
|
||||
logger.debug("Validate chain failed with \(error)")
|
||||
pushProgressReport(startTime: startTime, finishTime: Date())
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private func pushProgressReport(startTime: Date, finishTime: Date) {
|
||||
metrics.pushProgressReport(
|
||||
progress: BlockProgress(startHeight: 0, targetHeight: 0, progressHeight: 0),
|
||||
start: startTime,
|
||||
end: finishTime,
|
||||
batchSize: 0,
|
||||
operation: .validateBlocks
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// SubtreeRoot.swift
|
||||
//
|
||||
//
|
||||
// Created by Jack Grigg on 19/07/2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct SubtreeRoot {
|
||||
public let rootHash: Data
|
||||
public let completingBlockHeight: BlockHeight
|
||||
}
|
|
@ -276,6 +276,21 @@ public enum ZcashError: Equatable, Error {
|
|||
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver
|
||||
/// ZRUST0045
|
||||
case rustGetTransparentReceiverInvalidReceiver
|
||||
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots
|
||||
/// ZRUST0046
|
||||
case rustPutSaplingSubtreeRootsAllocationProblem
|
||||
/// Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// ZRUST0047
|
||||
case rustPutSaplingSubtreeRoots(_ rustError: String)
|
||||
/// Error from rust layer when calling ZcashRustBackend.updateChainTip
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// ZRUST0048
|
||||
case rustUpdateChainTip(_ rustError: String)
|
||||
/// Error from rust layer when calling ZcashRustBackend.suggestScanRanges
|
||||
/// - `rustError` contains error generated by the rust layer.
|
||||
/// ZRUST0049
|
||||
case rustSuggestScanRanges(_ rustError: String)
|
||||
/// SQLite query failed when fetching all accounts from the database.
|
||||
/// - `sqliteError` is error produced by SQLite library.
|
||||
/// ZADAO0001
|
||||
|
@ -605,6 +620,10 @@ public enum ZcashError: Equatable, Error {
|
|||
case .rustGetSaplingReceiverInvalidReceiver: return "Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver"
|
||||
case .rustGetTransparentReceiverInvalidAddress: return "Error from rust layer when calling ZcashRustBackend.getTransparentReceiver"
|
||||
case .rustGetTransparentReceiverInvalidReceiver: return "Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver"
|
||||
case .rustPutSaplingSubtreeRootsAllocationProblem: return "Unable to allocate memory required to store subtree roots when calling ZcashRustBackend.putSaplingSubtreeRoots"
|
||||
case .rustPutSaplingSubtreeRoots: return "Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots"
|
||||
case .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip"
|
||||
case .rustSuggestScanRanges: return "Error from rust layer when calling ZcashRustBackend.suggestScanRanges"
|
||||
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
|
||||
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
|
||||
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
|
||||
|
@ -763,6 +782,10 @@ public enum ZcashError: Equatable, Error {
|
|||
case .rustGetSaplingReceiverInvalidReceiver: return .rustGetSaplingReceiverInvalidReceiver
|
||||
case .rustGetTransparentReceiverInvalidAddress: return .rustGetTransparentReceiverInvalidAddress
|
||||
case .rustGetTransparentReceiverInvalidReceiver: return .rustGetTransparentReceiverInvalidReceiver
|
||||
case .rustPutSaplingSubtreeRootsAllocationProblem: return .rustPutSaplingSubtreeRootsAllocationProblem
|
||||
case .rustPutSaplingSubtreeRoots: return .rustPutSaplingSubtreeRoots
|
||||
case .rustUpdateChainTip: return .rustUpdateChainTip
|
||||
case .rustSuggestScanRanges: return .rustSuggestScanRanges
|
||||
case .accountDAOGetAll: return .accountDAOGetAll
|
||||
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
||||
case .accountDAOFindBy: return .accountDAOFindBy
|
||||
|
|
|
@ -153,6 +153,14 @@ public enum ZcashErrorCode: String {
|
|||
case rustGetTransparentReceiverInvalidAddress = "ZRUST0044"
|
||||
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver
|
||||
case rustGetTransparentReceiverInvalidReceiver = "ZRUST0045"
|
||||
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots
|
||||
case rustPutSaplingSubtreeRootsAllocationProblem = "ZRUST0046"
|
||||
/// Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots
|
||||
case rustPutSaplingSubtreeRoots = "ZRUST0047"
|
||||
/// Error from rust layer when calling ZcashRustBackend.updateChainTip
|
||||
case rustUpdateChainTip = "ZRUST0048"
|
||||
/// Error from rust layer when calling ZcashRustBackend.suggestScanRanges
|
||||
case rustSuggestScanRanges = "ZRUST0049"
|
||||
/// SQLite query failed when fetching all accounts from the database.
|
||||
case accountDAOGetAll = "ZADAO0001"
|
||||
/// Fetched accounts from SQLite but can't decode them.
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// ScanRange.swift
|
||||
//
|
||||
//
|
||||
// Created by Jack Grigg on 17/07/2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ScanRange {
|
||||
let range: Range<BlockHeight>
|
||||
let priority: UInt8
|
||||
}
|
|
@ -490,21 +490,6 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
func validateCombinedChain(limit: UInt32 = 0) async throws {
|
||||
let result = zcashlc_validate_combined_chain(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, limit, networkType.networkId)
|
||||
|
||||
switch result {
|
||||
case -1:
|
||||
return
|
||||
case 0:
|
||||
throw ZcashError.rustValidateCombinedChainValidationFailed(
|
||||
lastErrorMessage(fallback: "`validateCombinedChain` failed with unknown error")
|
||||
)
|
||||
default:
|
||||
throw ZcashError.rustValidateCombinedChainInvalidChain(result)
|
||||
}
|
||||
}
|
||||
|
||||
func rewindToHeight(height: Int32) async throws {
|
||||
let result = zcashlc_rewind_to_height(dbData.0, dbData.1, height, networkType.networkId)
|
||||
|
||||
|
@ -521,8 +506,97 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
func scanBlocks(limit: UInt32 = 0) async throws {
|
||||
let result = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, limit, networkType.networkId)
|
||||
func putSaplingSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws {
|
||||
var ffiSubtreeRootsVec: [FfiSubtreeRoot] = []
|
||||
|
||||
for root in roots {
|
||||
let hashPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: root.rootHash.count)
|
||||
|
||||
let contiguousHashBytes = ContiguousArray(root.rootHash.bytes)
|
||||
|
||||
let result: Void? = contiguousHashBytes.withContiguousStorageIfAvailable { hashBytesPtr in
|
||||
// swiftlint:disable:next force_unwrapping
|
||||
hashPtr.initialize(from: hashBytesPtr.baseAddress!, count: hashBytesPtr.count)
|
||||
}
|
||||
|
||||
guard result != nil else {
|
||||
defer {
|
||||
hashPtr.deallocate()
|
||||
ffiSubtreeRootsVec.deallocateElements()
|
||||
}
|
||||
throw ZcashError.rustPutSaplingSubtreeRootsAllocationProblem
|
||||
}
|
||||
|
||||
ffiSubtreeRootsVec.append(
|
||||
FfiSubtreeRoot(
|
||||
root_hash_ptr: hashPtr,
|
||||
root_hash_ptr_len: UInt(contiguousHashBytes.count),
|
||||
completing_block_height: UInt32(root.completingBlockHeight)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var contiguousFfiRoots = ContiguousArray(ffiSubtreeRootsVec)
|
||||
|
||||
let len = UInt(contiguousFfiRoots.count)
|
||||
|
||||
let rootsPtr = UnsafeMutablePointer<FfiSubtreeRoots>.allocate(capacity: 1)
|
||||
|
||||
defer { ffiSubtreeRootsVec.deallocateElements() }
|
||||
|
||||
try contiguousFfiRoots.withContiguousMutableStorageIfAvailable { ptr in
|
||||
var roots = FfiSubtreeRoots()
|
||||
roots.ptr = ptr.baseAddress
|
||||
roots.len = len
|
||||
|
||||
rootsPtr.initialize(to: roots)
|
||||
|
||||
let res = zcashlc_put_sapling_subtree_roots(dbData.0, dbData.1, startIndex, rootsPtr, networkType.networkId)
|
||||
|
||||
guard res else {
|
||||
throw ZcashError.rustPutSaplingSubtreeRoots(lastErrorMessage(fallback: "`putSaplingSubtreeRoots` failed with unknown error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateChainTip(height: Int32) async throws {
|
||||
let result = zcashlc_update_chain_tip(dbData.0, dbData.1, height, networkType.networkId)
|
||||
|
||||
guard result else {
|
||||
throw ZcashError.rustUpdateChainTip(lastErrorMessage(fallback: "`updateChainTip` failed with unknown error"))
|
||||
}
|
||||
}
|
||||
|
||||
func suggestScanRanges() async throws -> [ScanRange] {
|
||||
let scanRangesPtr = zcashlc_suggest_scan_ranges(dbData.0, dbData.1, networkType.networkId)
|
||||
|
||||
guard let scanRangesPtr else {
|
||||
throw ZcashError.rustSuggestScanRanges(lastErrorMessage(fallback: "`suggestScanRanges` failed with unknown error"))
|
||||
}
|
||||
|
||||
defer { zcashlc_free_scan_ranges(scanRangesPtr) }
|
||||
|
||||
var scanRanges: [ScanRange] = []
|
||||
|
||||
for i in (0 ..< Int(scanRangesPtr.pointee.len)) {
|
||||
let scanRange = scanRangesPtr.pointee.ptr.advanced(by: i).pointee
|
||||
|
||||
scanRanges.append(
|
||||
ScanRange(
|
||||
range: Range(uncheckedBounds: (
|
||||
BlockHeight(scanRange.start),
|
||||
BlockHeight(scanRange.end)
|
||||
)),
|
||||
priority: scanRange.priority
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return scanRanges
|
||||
}
|
||||
|
||||
func scanBlocks(fromHeight: Int32, limit: UInt32 = 0) async throws {
|
||||
let result = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, fromHeight, limit, networkType.networkId)
|
||||
|
||||
guard result != 0 else {
|
||||
throw ZcashError.rustScanBlocks(lastErrorMessage(fallback: "`scanBlocks` failed with unknown error"))
|
||||
|
@ -657,3 +731,11 @@ extension Array where Element == FFIBlockMeta {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == FfiSubtreeRoot {
|
||||
func deallocateElements() {
|
||||
self.forEach { element in
|
||||
element.root_hash_ptr.deallocate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,26 +159,6 @@ protocol ZcashRustBackendWelding {
|
|||
/// - `rustGetVerifiedTransparentBalance` if rust layer returns error.
|
||||
func getVerifiedTransparentBalance(account: Int32) async throws -> Int64
|
||||
|
||||
/// Checks that the scanned blocks in the data database, when combined with the recent
|
||||
/// `CompactBlock`s in the cache database, form a valid chain.
|
||||
/// This function is built on the core assumption that the information provided in the
|
||||
/// cache database is more likely to be accurate than the previously-scanned information.
|
||||
/// This follows from the design (and trust) assumption that the `lightwalletd` server
|
||||
/// provides accurate block information as of the time it was requested.
|
||||
/// - parameter fsBlockDbRoot: `URL` pointing to the filesystem root directory where the fsBlock cache is.
|
||||
/// this directory is expected to contain a `/blocks` sub-directory with the blocks stored in the convened filename
|
||||
/// format `{height}-{hash}-block`. This directory has must be granted both write and read permissions.
|
||||
/// - parameter dbData: location of the data db file
|
||||
/// - parameter networkType: the network type
|
||||
/// - parameter limit: a limit to validate a fixed number of blocks instead of the whole cache.
|
||||
/// - Throws:
|
||||
/// - `rustValidateCombinedChainValidationFailed` if there was an error during validation unrelated to chain validity.
|
||||
/// - `rustValidateCombinedChainInvalidChain(upperBound)` if the combined chain is invalid. `upperBound` is the height of the highest invalid
|
||||
/// block(on the assumption that the highest block in the cache database is correct).
|
||||
///
|
||||
/// - Important: This function does not mutate either of the databases.
|
||||
func validateCombinedChain(limit: UInt32) async throws
|
||||
|
||||
/// Resets the state of the database to only contain block and transaction information up to the given height. clears up all derived data as well
|
||||
/// - parameter height: height to rewind to.
|
||||
/// - Throws: `rustRewindToHeight` if rust layer returns error.
|
||||
|
@ -190,21 +170,35 @@ protocol ZcashRustBackendWelding {
|
|||
/// - Throws: `rustRewindCacheToHeight` if rust layer returns error.
|
||||
func rewindCacheToHeight(height: Int32) async throws
|
||||
|
||||
/// Returns a list of suggested scan ranges based upon the current wallet state.
|
||||
///
|
||||
/// This method should only be used in cases where the `CompactBlock` data that will be
|
||||
/// made available to `scanBlocks` for the requested block ranges includes note
|
||||
/// commitment tree size information for each block; or else the scan is likely to fail if
|
||||
/// notes belonging to the wallet are detected.
|
||||
func suggestScanRanges() async throws -> [ScanRange]
|
||||
|
||||
/// Scans new blocks added to the cache for any transactions received by the tracked
|
||||
/// accounts.
|
||||
/// This function pays attention only to cached blocks with heights greater than the
|
||||
/// highest scanned block in `db_data`. Cached blocks with lower heights are not verified
|
||||
/// against previously-scanned blocks. In particular, this function **assumes** that the
|
||||
/// caller is handling rollbacks.
|
||||
/// accounts, while checking that they form a valid chan.
|
||||
///
|
||||
/// This function is built on the core assumption that the information provided in the
|
||||
/// block cache is more likely to be accurate than the previously-scanned information.
|
||||
/// This follows from the design (and trust) assumption that the `lightwalletd` server
|
||||
/// provides accurate block information as of the time it was requested.
|
||||
///
|
||||
/// This function **assumes** that the caller is handling rollbacks.
|
||||
///
|
||||
/// For brand-new light client databases, this function starts scanning from the Sapling
|
||||
/// activation height. This height can be fast-forwarded to a more recent block by calling
|
||||
/// [`initBlocksTable`] before this function.
|
||||
///
|
||||
/// Scanned blocks are required to be height-sequential. If a block is missing from the
|
||||
/// cache, an error will be signalled.
|
||||
///
|
||||
/// - parameter limit: scan up to limit blocks. pass 0 to set no limit.
|
||||
/// - parameter fromHeight: scan starting from the given height.
|
||||
/// - parameter limit: scan up to limit blocks.
|
||||
/// - Throws: `rustScanBlocks` if rust layer returns error.
|
||||
func scanBlocks(limit: UInt32) async throws
|
||||
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws
|
||||
|
||||
/// Upserts a UTXO into the data db database
|
||||
/// - parameter txid: the txid bytes for the UTXO
|
||||
|
|
|
@ -114,18 +114,6 @@ enum Dependencies {
|
|||
logger: logger
|
||||
)
|
||||
}
|
||||
|
||||
container.register(type: BlockValidator.self, isSingleton: true) { di in
|
||||
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
|
||||
let metrics = di.resolve(SDKMetrics.self)
|
||||
let logger = di.resolve(Logger.self)
|
||||
|
||||
return BlockValidatorImpl(
|
||||
rustBackend: rustBackend,
|
||||
metrics: metrics,
|
||||
logger: logger
|
||||
)
|
||||
}
|
||||
|
||||
container.register(type: BlockScanner.self, isSingleton: true) { di in
|
||||
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
|
||||
|
|
|
@ -48,8 +48,8 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validate,
|
||||
"nextContext after .download is expected to be .validate but received \(nextState)"
|
||||
nextState == .scan,
|
||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testDownloadAction_NextAction is not expected to fail. \(error)")
|
||||
|
@ -84,8 +84,8 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validate,
|
||||
"nextContext after .download is expected to be .validate but received \(nextState)"
|
||||
nextState == .scan,
|
||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testDownloadAction_NoDownloadAndScanRange is not expected to fail. \(error)")
|
||||
|
@ -122,8 +122,8 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validate,
|
||||
"nextContext after .download is expected to be .validate but received \(nextState)"
|
||||
nextState == .scan,
|
||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testDownloadAction_NothingMoreToDownload is not expected to fail. \(error)")
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// ValidateActionTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Lukáš Korba on 17.05.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import TestUtils
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
final class ValidateActionTests: ZcashTestCase {
|
||||
func testValidateAction_NextAction() async throws {
|
||||
let blockValidatorMock = BlockValidatorMock()
|
||||
|
||||
blockValidatorMock.validateClosure = { }
|
||||
|
||||
mockContainer.mock(type: BlockValidator.self, isSingleton: true) { _ in blockValidatorMock }
|
||||
|
||||
let validateAction = ValidateAction(
|
||||
container: mockContainer
|
||||
)
|
||||
|
||||
do {
|
||||
let nextContext = try await validateAction.run(with: .init(state: .validate)) { _ in }
|
||||
XCTAssertTrue(blockValidatorMock.validateCalled, "validator.validate() is expected to be called.")
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .scan,
|
||||
"nextContext after .validate is expected to be .scan but received \(nextState)"
|
||||
)
|
||||
} catch {
|
||||
XCTFail("testValidateAction_NextAction is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@ extension BlockDownloader { }
|
|||
extension BlockDownloaderService { }
|
||||
extension BlockEnhancer { }
|
||||
extension BlockScanner { }
|
||||
extension BlockValidator { }
|
||||
extension CompactBlockRepository { }
|
||||
extension LatestBlocksDataProvider { }
|
||||
extension LightWalletdInfo { }
|
||||
|
|
|
@ -378,31 +378,6 @@ class BlockScannerMock: BlockScanner {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
class BlockValidatorMock: BlockValidator {
|
||||
|
||||
|
||||
init(
|
||||
) {
|
||||
}
|
||||
|
||||
// MARK: - validate
|
||||
|
||||
var validateThrowableError: Error?
|
||||
var validateCallsCount = 0
|
||||
var validateCalled: Bool {
|
||||
return validateCallsCount > 0
|
||||
}
|
||||
var validateClosure: (() async throws -> Void)?
|
||||
|
||||
func validate() async throws {
|
||||
if let error = validateThrowableError {
|
||||
throw error
|
||||
}
|
||||
validateCallsCount += 1
|
||||
try await validateClosure!()
|
||||
}
|
||||
|
||||
}
|
||||
class CompactBlockRepositoryMock: CompactBlockRepository {
|
||||
|
||||
|
@ -2555,31 +2530,6 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - validateCombinedChain
|
||||
|
||||
var validateCombinedChainLimitThrowableError: Error?
|
||||
func setValidateCombinedChainLimitThrowableError(_ param: Error?) async {
|
||||
validateCombinedChainLimitThrowableError = param
|
||||
}
|
||||
var validateCombinedChainLimitCallsCount = 0
|
||||
var validateCombinedChainLimitCalled: Bool {
|
||||
return validateCombinedChainLimitCallsCount > 0
|
||||
}
|
||||
var validateCombinedChainLimitReceivedLimit: UInt32?
|
||||
var validateCombinedChainLimitClosure: ((UInt32) async throws -> Void)?
|
||||
func setValidateCombinedChainLimitClosure(_ param: ((UInt32) async throws -> Void)?) async {
|
||||
validateCombinedChainLimitClosure = param
|
||||
}
|
||||
|
||||
func validateCombinedChain(limit: UInt32) async throws {
|
||||
if let error = validateCombinedChainLimitThrowableError {
|
||||
throw error
|
||||
}
|
||||
validateCombinedChainLimitCallsCount += 1
|
||||
validateCombinedChainLimitReceivedLimit = limit
|
||||
try await validateCombinedChainLimitClosure!(limit)
|
||||
}
|
||||
|
||||
// MARK: - rewindToHeight
|
||||
|
||||
var rewindToHeightHeightThrowableError: Error?
|
||||
|
@ -2630,6 +2580,37 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
|||
try await rewindCacheToHeightHeightClosure!(height)
|
||||
}
|
||||
|
||||
// MARK: - suggestScanRanges
|
||||
|
||||
var suggestScanRangesThrowableError: Error?
|
||||
func setSuggestScanRangesThrowableError(_ param: Error?) async {
|
||||
suggestScanRangesThrowableError = param
|
||||
}
|
||||
var suggestScanRangesCallsCount = 0
|
||||
var suggestScanRangesCalled: Bool {
|
||||
return suggestScanRangesCallsCount > 0
|
||||
}
|
||||
var suggestScanRangesReturnValue: [ScanRange]!
|
||||
func setSuggestScanRangesReturnValue(_ param: [ScanRange]) async {
|
||||
suggestScanRangesReturnValue = param
|
||||
}
|
||||
var suggestScanRangesClosure: (() async throws -> [ScanRange])?
|
||||
func setSuggestScanRangesClosure(_ param: (() async throws -> [ScanRange])?) async {
|
||||
suggestScanRangesClosure = param
|
||||
}
|
||||
|
||||
func suggestScanRanges() async throws -> [ScanRange] {
|
||||
if let error = suggestScanRangesThrowableError {
|
||||
throw error
|
||||
}
|
||||
suggestScanRangesCallsCount += 1
|
||||
if let closure = suggestScanRangesClosure {
|
||||
return try await closure()
|
||||
} else {
|
||||
return suggestScanRangesReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - scanBlocks
|
||||
|
||||
var scanBlocksLimitThrowableError: Error?
|
||||
|
@ -2640,19 +2621,21 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
|
|||
var scanBlocksLimitCalled: Bool {
|
||||
return scanBlocksLimitCallsCount > 0
|
||||
}
|
||||
var scanBlocksLimitReceivedFromHeight: Int32?
|
||||
var scanBlocksLimitReceivedLimit: UInt32?
|
||||
var scanBlocksLimitClosure: ((UInt32) async throws -> Void)?
|
||||
func setScanBlocksLimitClosure(_ param: ((UInt32) async throws -> Void)?) async {
|
||||
var scanBlocksLimitClosure: ((Int32, UInt32) async throws -> Void)?
|
||||
func setScanBlocksLimitClosure(_ param: ((Int32, UInt32) async throws -> Void)?) async {
|
||||
scanBlocksLimitClosure = param
|
||||
}
|
||||
|
||||
func scanBlocks(limit: UInt32) async throws {
|
||||
func scanBlocks(fromHeight: Int32, limit: UInt32) async throws {
|
||||
if let error = scanBlocksLimitThrowableError {
|
||||
throw error
|
||||
}
|
||||
scanBlocksLimitCallsCount += 1
|
||||
scanBlocksLimitReceivedFromHeight = fromHeight
|
||||
scanBlocksLimitReceivedLimit = limit
|
||||
try await scanBlocksLimitClosure!(limit)
|
||||
try await scanBlocksLimitClosure!(fromHeight, limit)
|
||||
}
|
||||
|
||||
// MARK: - putUnspentTransparentOutput
|
||||
|
|
|
@ -132,40 +132,18 @@ class RustBackendMockHelper {
|
|||
return try await rustBackend.getVerifiedBalance(account: account)
|
||||
}
|
||||
|
||||
await rustBackendMock.setValidateCombinedChainLimitClosure() { [weak self] limit in
|
||||
guard let self else { throw ZcashError.rustValidateCombinedChainValidationFailed("Self is nil") }
|
||||
if let rate = mockValidateCombinedChainSuccessRate {
|
||||
if Self.shouldSucceed(successRate: rate) {
|
||||
return try await rustBackend.validateCombinedChain(limit: limit)
|
||||
} else {
|
||||
throw mockValidateCombinedChainFailureError
|
||||
}
|
||||
} else if let attempts = self.mockValidateCombinedChainFailAfterAttempts {
|
||||
self.mockValidateCombinedChainFailAfterAttempts = attempts - 1
|
||||
if attempts > 0 {
|
||||
return try await rustBackend.validateCombinedChain(limit: limit)
|
||||
} else {
|
||||
if attempts == 0 {
|
||||
throw mockValidateCombinedChainFailureError
|
||||
} else if attempts < 0 && mockValidateCombinedChainKeepFailing {
|
||||
throw mockValidateCombinedChainFailureError
|
||||
} else {
|
||||
return try await rustBackend.validateCombinedChain(limit: limit)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return try await rustBackend.validateCombinedChain(limit: limit)
|
||||
}
|
||||
}
|
||||
|
||||
await rustBackendMock.setRewindToHeightHeightClosure() { height in
|
||||
try await rustBackend.rewindToHeight(height: height)
|
||||
}
|
||||
|
||||
await rustBackendMock.setRewindCacheToHeightHeightClosure() { _ in }
|
||||
|
||||
await rustBackendMock.setScanBlocksLimitClosure() { limit in
|
||||
try await rustBackend.scanBlocks(limit: limit)
|
||||
await rustBackendMock.setSuggestScanRangesClosure() {
|
||||
try await rustBackend.suggestScanRanges()
|
||||
}
|
||||
|
||||
await rustBackendMock.setScanBlocksLimitClosure() { fromHeight, limit in
|
||||
try await rustBackend.scanBlocks(fromHeight: fromHeight, limit: limit)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue