[#1230] Remove linear sync from the SDK

- concept of linear syncing fully removed from the SDK, it's fully replaced with Spend-before-Sync
- BlockDAO - table blocks is no longer used, removed from the SDK and all it's associated getLastBlocks/ScannedHeights as with it
- concept of pending transactions removed from the SDK
- unit tests refactored
This commit is contained in:
Lukas Korba 2023-09-07 12:54:47 +02:00
parent 18141e6e4b
commit d956e9940b
39 changed files with 57 additions and 1186 deletions

View File

@ -19,6 +19,9 @@ system itself won't remove these data.
of syncing the amount of data provided is reduced so it's consistent. Spend before Sync is done in non-linear order
so both Height and Time don't make sense anymore.
### [#1230] Remove linear sync from the SDK
`latestScannedHeight` and `latestScannedTime` removed from the SynchronizerState. Concept of pending transaction changed, `func allPendingTransactions()` is no longer available. Use `public func allTransactions()` instead.
# 0.22.0-beta
## Checkpoints

View File

@ -53,7 +53,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
spendParamsURL: try! spendParamsURLHelper(),
outputParamsURL: try! outputParamsURLHelper(),
saplingParamsSourceURL: SaplingParamsSourceURL.default,
syncAlgorithm: .spendBeforeSync,
enableBackendTracing: true
)

View File

@ -12,7 +12,6 @@ import ZcashLightClientKit
class TransactionsDataSource: NSObject {
enum TransactionType {
case pending
case sent
case received
case cleared
@ -32,17 +31,9 @@ class TransactionsDataSource: NSObject {
self.synchronizer = synchronizer
}
// swiftlint:disable:next cyclomatic_complexity
func load() async throws {
transactions = []
switch status {
case .pending:
let rawTransactions = await synchronizer.pendingTransactions
for pendingTransaction in rawTransactions {
let memos = try await synchronizer.getMemos(for: pendingTransaction)
transactions.append(TransactionDetailModel(pendingTransaction: pendingTransaction, memos: memos))
}
case .cleared:
let rawTransactions = await synchronizer.transactions
for transaction in rawTransactions {
@ -62,12 +53,6 @@ class TransactionsDataSource: NSObject {
transactions.append(TransactionDetailModel(sendTransaction: transaction, memos: memos))
}
case .all:
let rawPendingTransactions = await synchronizer.pendingTransactions
for pendingTransaction in rawPendingTransactions {
let memos = try await synchronizer.getMemos(for: pendingTransaction)
transactions.append(TransactionDetailModel(pendingTransaction: pendingTransaction, memos: memos))
}
let rawClearedTransactions = await synchronizer.transactions
for transaction in rawClearedTransactions {
let memos = try await synchronizer.getMemos(for: transaction)

View File

@ -82,13 +82,7 @@ class MainTableViewController: UITableViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? TransactionsTableViewController {
if let id = segue.identifier, id == "Pending" {
destination.datasource = TransactionsDataSource(
status: .pending,
synchronizer: AppDelegate.shared.sharedSynchronizer
)
destination.title = "Pending Transactions"
} else if let id = segue.identifier, id == "Sent" {
if let id = segue.identifier, id == "Sent" {
destination.datasource = TransactionsDataSource(
status: .sent,
synchronizer: AppDelegate.shared.sharedSynchronizer

View File

@ -92,8 +92,6 @@ We don't like reinventing the wheel, so we gently borrowed swift lint rules from
## `Spend before Sync` synchronization algorithm
The CompactBlockProcessor is responsible for downloading and processing blocks from the lightwalletd. Since the inception of the SDK the blocks were processed in a linear order up to the chain tip. Latests SDK has introduced brand new algorithm for syncing of the blocks. It's called `Spend before Sync` and processes blocks in non-linear order so the spendable funds are discovered as soon as possible - allowing users to create a transaction while still syncing.
By default the syncing algorithm is set to `.linear` so users of the SDK are not affected by this new feature. To switch to the `Spend before Sync` synchronization, set `syncAlgorithm: .spendBeforeSync` in the Initializer's init.
# Versioning

View File

@ -11,8 +11,6 @@ protocol ActionContext {
var state: CBPState { get async }
var prevState: CBPState? { get async }
var syncControlData: SyncControlData { get async }
var preferredSyncAlgorithm: SyncAlgorithm { get }
var supportedSyncAlgorithm: SyncAlgorithm? { get async }
var requestedRewindHeight: BlockHeight? { get async }
var totalProgressRange: CompactBlockRange { get async }
var processedHeight: BlockHeight { get async }
@ -28,7 +26,6 @@ protocol ActionContext {
func update(lastScannedHeight: BlockHeight) async
func update(lastDownloadedHeight: BlockHeight) async
func update(lastEnhancedHeight: BlockHeight?) async
func update(supportedSyncAlgorithm: SyncAlgorithm) async
func update(requestedRewindHeight: BlockHeight) async
}
@ -36,8 +33,6 @@ actor ActionContextImpl: ActionContext {
var state: CBPState
var prevState: CBPState?
var syncControlData: SyncControlData
let preferredSyncAlgorithm: SyncAlgorithm
var supportedSyncAlgorithm: SyncAlgorithm?
var requestedRewindHeight: BlockHeight?
/// Represents the overall range of blocks that will be synced, `SyncAlgorithm` doesn't matter.
var totalProgressRange: CompactBlockRange = 0...0
@ -49,9 +44,8 @@ actor ActionContextImpl: ActionContext {
var lastDownloadedHeight: BlockHeight?
var lastEnhancedHeight: BlockHeight?
init(state: CBPState, preferredSyncAlgorithm: SyncAlgorithm = .linear) {
init(state: CBPState) {
self.state = state
self.preferredSyncAlgorithm = preferredSyncAlgorithm
syncControlData = SyncControlData.empty
}
@ -69,7 +63,6 @@ actor ActionContextImpl: ActionContext {
func update(lastScannedHeight: BlockHeight) async { self.lastScannedHeight = lastScannedHeight }
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 }
}
@ -81,7 +74,6 @@ enum CBPState: CaseIterable {
case updateChainTip
case processSuggestedScanRanges
case rewind
case computeSyncControlData
case download
case scan
case clearAlreadyScannedBlocks

View File

@ -24,19 +24,7 @@ extension ClearCacheAction: Action {
if await context.prevState == .idle {
await context.update(state: .migrateLegacyCacheDB)
} else {
if context.preferredSyncAlgorithm == .linear {
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
}
}
await context.update(state: .processSuggestedScanRanges)
}
return context

View File

@ -1,83 +0,0 @@
//
// ComputeSyncControlDataAction.swift
//
//
// Created by Michal Fousek on 05.05.2023.
//
import Foundation
final class ComputeSyncControlDataAction {
let configProvider: CompactBlockProcessor.ConfigProvider
let downloaderService: BlockDownloaderService
let latestBlocksDataProvider: LatestBlocksDataProvider
let logger: Logger
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
self.configProvider = configProvider
downloaderService = container.resolve(BlockDownloaderService.self)
latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
logger = container.resolve(Logger.self)
}
}
extension ComputeSyncControlDataAction: Action {
var removeBlocksCacheWhenFailed: Bool { false }
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
let config = await configProvider.config
await latestBlocksDataProvider.updateScannedData()
await latestBlocksDataProvider.updateBlockData()
await latestBlocksDataProvider.updateUnenhancedData()
// Here we know:
// - latest scanned height from the DB, if none the wallet's birthday is automatically used
// - first unenhanced height from the DB, could be nil = up to latestScannedHeight nothing to enhance
// - latest downloaded height reported by downloaderService
// - latest block height on the blockchain
// - wallet birthday for the initial scan
let latestBlockHeight = await latestBlocksDataProvider.latestBlockHeight
let latestScannedHeightDB = await latestBlocksDataProvider.latestScannedHeight
let latestScannedHeight = latestScannedHeightDB < config.walletBirthday ? config.walletBirthday : latestScannedHeightDB
let firstUnenhancedHeight = await latestBlocksDataProvider.firstUnenhancedHeight
let enhanceStart: BlockHeight
if let firstUnenhancedHeight {
enhanceStart = min(firstUnenhancedHeight, latestScannedHeight)
} else {
enhanceStart = latestScannedHeight
}
logger.debug("""
Init numbers:
latestBlockHeight [BC]: \(latestBlockHeight)
latestScannedHeight [DB]: \(latestScannedHeight)
firstUnenhancedHeight [DB]: \(firstUnenhancedHeight ?? -1)
enhanceStart: \(enhanceStart)
walletBirthday: \(config.walletBirthday)
""")
let syncControlData = SyncControlData(
latestBlockHeight: latestBlockHeight,
latestScannedHeight: latestScannedHeight,
firstUnenhancedHeight: enhanceStart
)
await context.update(lastScannedHeight: latestScannedHeight)
await context.update(lastDownloadedHeight: latestScannedHeight)
await context.update(syncControlData: syncControlData)
await context.update(totalProgressRange: latestScannedHeight...latestBlockHeight)
// if there is nothing sync just switch to finished state
if latestBlockHeight < latestScannedHeight || latestBlockHeight == latestScannedHeight {
await context.update(state: .finished)
} else {
await context.update(state: .download)
}
return context
}
func stop() async { }
}

View File

@ -24,11 +24,8 @@ extension SaplingParamsAction: Action {
logger.debug("Fetching sapling parameters")
try await saplingParametersHandler.handleIfNeeded()
if context.preferredSyncAlgorithm == .spendBeforeSync {
await context.update(state: .updateSubtreeRoots)
} else {
await context.update(state: .computeSyncControlData)
}
await context.update(state: .updateSubtreeRoots)
return context
}

View File

@ -33,7 +33,6 @@ extension UpdateSubtreeRootsAction: Action {
let stream = service.getSubtreeRoots(request)
var roots: [SubtreeRoot] = []
var err: Error?
do {
for try await subtreeRoot in stream {
@ -41,28 +40,16 @@ extension UpdateSubtreeRootsAction: Action {
}
} catch ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut) {
throw ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut)
} catch {
logger.debug("getSubtreeRoots failed with error \(error.localizedDescription)")
err = error
}
// In case of error, the lightwalletd doesn't support Spend before Sync -> switching to linear sync.
// Likewise, no subtree roots results in switching to linear sync.
if err != nil || roots.isEmpty {
logger.info("Spend before Sync is not possible, switching to linear sync.")
await context.update(supportedSyncAlgorithm: .linear)
await context.update(state: .computeSyncControlData)
} else {
await context.update(supportedSyncAlgorithm: .spendBeforeSync)
logger.info("Sapling tree has \(roots.count) subtrees")
do {
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
await context.update(state: .updateChainTip)
} catch {
logger.debug("putSaplingSubtreeRoots failed with error \(error.localizedDescription)")
throw ZcashError.compactBlockProcessorPutSaplingSubtreeRoots(error)
}
logger.info("Sapling tree has \(roots.count) subtrees")
do {
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
await context.update(state: .updateChainTip)
} catch {
logger.debug("putSaplingSubtreeRoots failed with error \(error.localizedDescription)")
throw ZcashError.compactBlockProcessorPutSaplingSubtreeRoots(error)
}
return context

View File

@ -70,7 +70,6 @@ actor CompactBlockProcessor {
let network: ZcashNetwork
let saplingActivation: BlockHeight
let cacheDbURL: URL?
let syncAlgorithm: SyncAlgorithm
var blockPollInterval: TimeInterval {
TimeInterval.random(in: ZcashSDK.defaultPollInterval / 2 ... ZcashSDK.defaultPollInterval * 1.5)
}
@ -90,7 +89,6 @@ actor CompactBlockProcessor {
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
walletBirthdayProvider: @escaping () -> BlockHeight,
saplingActivation: BlockHeight,
syncAlgorithm: SyncAlgorithm = .linear,
network: ZcashNetwork
) {
self.alias = alias
@ -108,7 +106,6 @@ actor CompactBlockProcessor {
self.walletBirthdayProvider = walletBirthdayProvider
self.saplingActivation = saplingActivation
self.cacheDbURL = cacheDbURL
self.syncAlgorithm = syncAlgorithm
}
init(
@ -124,7 +121,6 @@ actor CompactBlockProcessor {
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
walletBirthdayProvider: @escaping () -> BlockHeight,
syncAlgorithm: SyncAlgorithm = .linear,
network: ZcashNetwork
) {
self.alias = alias
@ -142,7 +138,6 @@ actor CompactBlockProcessor {
self.retries = retries
self.maxBackoffInterval = maxBackoffInterval
self.rewindDistance = rewindDistance
self.syncAlgorithm = syncAlgorithm
}
}
@ -174,7 +169,6 @@ actor CompactBlockProcessor {
outputParamsURL: initializer.outputParamsURL,
saplingParamsSourceURL: initializer.saplingParamsSourceURL,
walletBirthdayProvider: walletBirthdayProvider,
syncAlgorithm: initializer.syncAlgorithm,
network: initializer.network
),
accountRepository: initializer.accountRepository
@ -193,7 +187,7 @@ actor CompactBlockProcessor {
)
let configProvider = ConfigProvider(config: config)
context = ActionContextImpl(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
context = ActionContextImpl(state: .idle)
actions = Self.makeActions(container: container, configProvider: configProvider)
self.metrics = container.resolve(SDKMetrics.self)
@ -232,8 +226,6 @@ actor CompactBlockProcessor {
action = ProcessSuggestedScanRangesAction(container: container)
case .rewind:
action = RewindAction(container: container)
case .computeSyncControlData:
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
case .download:
action = DownloadAction(container: container, configProvider: configProvider)
case .scan:
@ -325,7 +317,7 @@ extension CompactBlockProcessor {
private func doRewind(context: AfterSyncHooksManager.RewindContext) async throws {
logger.debug("Executing rewind.")
let lastDownloaded = await latestBlocksDataProvider.latestScannedHeight
let lastDownloaded = await latestBlocksDataProvider.maxScannedHeight
let height = Int32(context.height ?? lastDownloaded)
let nearestHeight: Int32
@ -611,8 +603,6 @@ extension CompactBlockProcessor {
break
case .rewind:
break
case .computeSyncControlData:
break
case .download:
break
case .scan:
@ -638,7 +628,7 @@ extension CompactBlockProcessor {
private func resetContext() async {
let lastEnhancedheight = await context.lastEnhancedHeight
context = ActionContextImpl(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
context = ActionContextImpl(state: .idle)
await context.update(lastEnhancedHeight: lastEnhancedheight)
await compactBlockProgress.reset()
}
@ -660,7 +650,7 @@ extension CompactBlockProcessor {
retryAttempts = 0
consecutiveChainValidationErrors = 0
let lastScannedHeight = await latestBlocksDataProvider.latestScannedHeight
let lastScannedHeight = await latestBlocksDataProvider.maxScannedHeight
// Some actions may not run. For example there are no transactions to enhance and therefore there is no enhance progress. And in
// cases like this computation of final progress won't work properly. So let's fake 100% progress at the end of the sync process.
await send(event: .progressUpdated(1))
@ -746,7 +736,6 @@ extension CompactBlockProcessor {
"""
Timer triggered: Starting compact Block processor!.
Processor State: \(await self.context.state)
latestHeight: \(try await self.transactionRepository.lastScannedHeight())
attempts: \(await self.retryAttempts)
"""
)

View File

@ -27,7 +27,6 @@ struct BlockScannerImpl {
let transactionRepository: TransactionRepository
let metrics: SDKMetrics
let logger: Logger
let latestBlocksDataProvider: LatestBlocksDataProvider
}
extension BlockScannerImpl: BlockScanner {

View File

@ -50,7 +50,6 @@ public protocol ClosureSynchronizer {
completion: @escaping (Result<ZcashTransaction.Overview, Error>) -> Void
)
func pendingTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func clearedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func sentTranscations(completion: @escaping ([ZcashTransaction.Overview]) -> Void)
func receivedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void)

View File

@ -48,7 +48,6 @@ public protocol CombineSynchronizer {
) -> SinglePublisher<ZcashTransaction.Overview, Error>
var allTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var pendingTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var sentTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
var receivedTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> { get }
@ -58,8 +57,6 @@ public protocol CombineSynchronizer {
func getRecipients(for transaction: ZcashTransaction.Overview) -> SinglePublisher<[TransactionRecipient], Never>
func allPendingTransactions() -> SinglePublisher<[ZcashTransaction.Overview], Error>
func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error>
func latestHeight() -> SinglePublisher<BlockHeight, Error>

View File

@ -1,195 +0,0 @@
//
// BlockDao.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 10/16/19.
// Copyright © 2019 Electric Coin Company. All rights reserved.
//
import Foundation
import SQLite
protocol BlockDao {
func latestBlockHeight() throws -> BlockHeight
func latestBlock() throws -> Block?
func block(at height: BlockHeight) throws -> Block?
}
struct Block: Codable {
enum CodingKeys: String, CodingKey {
case height
case hash
case time
case saplingTree = "sapling_tree"
}
enum TableStructure {
static let height = Expression<Int>(Block.CodingKeys.height.rawValue)
static let hash = Expression<Blob>(Block.CodingKeys.hash.rawValue)
static let time = Expression<Int>(Block.CodingKeys.time.rawValue)
static let saplingTree = Expression<Blob>(Block.CodingKeys.saplingTree.rawValue)
}
let height: BlockHeight
let hash: Data
let time: Int
let saplingTree: Data
static let table = Table("blocks")
}
struct VTransaction: Codable {
enum CodingKeys: String, CodingKey {
case accountId = "account_id"
case idTx = "id_tx"
case minedHeight = "mined_height"
case txIndex = "tx_index"
case txId = "txid"
case expiryHeight = "expiry_height"
case raw = "raw"
case accountBalanceDelta = "account_balance_delta"
case feePaid = "fee_paid"
case expiredUnmined = "expired_unmined"
case hasChange = "has_change"
case sentNoteCount = "sent_note_count"
case recievedNoteCount = "received_note_count"
case memoCount = "memo_count"
case blockTime = "block_time"
}
enum TableStructure {
static let accountId = Expression<Int>(VTransaction.CodingKeys.accountId.rawValue)
static let idTx = Expression<Int>(VTransaction.CodingKeys.idTx.rawValue)
static let minedHeight = Expression<Int>(VTransaction.CodingKeys.minedHeight.rawValue)
static let txIndex = Expression<Int>(VTransaction.CodingKeys.txIndex.rawValue)
static let txId = Expression<Data>(VTransaction.CodingKeys.txId.rawValue)
static let expiryHeight = Expression<Int?>(VTransaction.CodingKeys.expiryHeight.rawValue)
static let raw = Expression<Data?>(VTransaction.CodingKeys.raw.rawValue)
static let accountBalanceDelta = Expression<Int>(VTransaction.CodingKeys.accountBalanceDelta.rawValue)
static let feePaid = Expression<Int?>(VTransaction.CodingKeys.feePaid.rawValue)
static let expiredUnmined = Expression<Int>(VTransaction.CodingKeys.expiredUnmined.rawValue)
static let hasChange = Expression<Bool>(VTransaction.CodingKeys.hasChange.rawValue)
static let sentNoteCount = Expression<Int>(VTransaction.CodingKeys.sentNoteCount.rawValue)
static let recievedNoteCount = Expression<Int>(VTransaction.CodingKeys.recievedNoteCount.rawValue)
static let memoCount = Expression<Int>(VTransaction.CodingKeys.memoCount.rawValue)
static let blockTime = Expression<Int>(VTransaction.CodingKeys.blockTime.rawValue)
}
let accountId: Int
let idTx: Int
let minedHeight: Int
let txIndex: Int
let txId: Data
let expiryHeight: Int?
let raw: Data?
let accountBalanceDelta: Int
let feePaid: Int?
let expiredUnmined: Int
let hasChange: Bool
let sentNoteCount: Int
let recievedNoteCount: Int
let memoCount: Int
let blockTime: Int
static let table = Table("v_transactions")
}
class BlockSQLDAO: BlockDao {
let dbProvider: ConnectionProvider
let table: Table
let height = Expression<Int>("height")
let minedHeight = Expression<Int>("mined_height")
let raw = Expression<Data?>("raw")
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
self.table = Table("Blocks")
}
/// - Throws:
/// - `blockDAOCantDecode` if block data loaded from DB can't be decoded to `Block` object.
/// - `blockDAOBlock` if sqlite query to load block metadata failed.
func block(at height: BlockHeight) throws -> Block? {
do {
return try dbProvider
.connection()
.prepare(Block.table.filter(Block.TableStructure.height == height).limit(1))
.map {
do {
return try $0.decode()
} catch {
throw ZcashError.blockDAOCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.blockDAOBlock(error)
}
}
}
/// - Throws: `blockDAOLatestBlockHeight` if sqlite to fetch height fails.
func latestBlockHeight() throws -> BlockHeight {
do {
return try dbProvider.connection().scalar(table.select(height.max)) ?? BlockHeight.empty()
} catch {
throw ZcashError.blockDAOLatestBlockHeight(error)
}
}
func latestBlock() throws -> Block? {
do {
return try dbProvider
.connection()
.prepare(Block.table.order(height.desc).limit(1))
.map {
do {
return try $0.decode()
} catch {
throw ZcashError.blockDAOLatestBlockCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.blockDAOLatestBlock(error)
}
}
}
func firstUnenhancedHeight(in range: CompactBlockRange? = nil) throws -> BlockHeight? {
do {
return try dbProvider
.connection()
.prepare(
VTransaction.table
.order(minedHeight.asc)
.filter(raw == nil)
.limit(1)
)
.map {
do {
let vTransaction: VTransaction = try $0.decode()
return vTransaction.minedHeight
} catch {
throw ZcashError.blockDAOFirstUnenhancedCantDecode(error)
}
}
.first
} catch {
throw ZcashError.blockDAOFirstUnenhancedHeight(error)
}
}
}
extension BlockSQLDAO: BlockRepository {
func lastScannedBlockHeight() -> BlockHeight {
(try? self.latestBlockHeight()) ?? BlockHeight.empty()
}
}

View File

@ -16,14 +16,12 @@ class TransactionSQLDAO: TransactionRepository {
let dbProvider: ConnectionProvider
private let blockDao: BlockSQLDAO
private let transactionsView = View("v_transactions")
private let txOutputsView = View("v_tx_outputs")
private let traceClosure: ((String) -> Void)?
init(dbProvider: ConnectionProvider, traceClosure: ((String) -> Void)? = nil) {
self.dbProvider = dbProvider
self.blockDao = BlockSQLDAO(dbProvider: dbProvider)
self.traceClosure = traceClosure
}
@ -37,22 +35,6 @@ class TransactionSQLDAO: TransactionRepository {
dbProvider.close()
}
func blockForHeight(_ height: BlockHeight) async throws -> Block? {
try blockDao.block(at: height)
}
func lastScannedHeight() async throws -> BlockHeight {
try blockDao.latestBlockHeight()
}
func lastScannedBlock() async throws -> Block? {
try blockDao.latestBlock()
}
func firstUnenhancedHeight() throws -> BlockHeight? {
try blockDao.firstUnenhancedHeight()
}
func isInitialized() async throws -> Bool {
true
}

View File

@ -126,7 +126,6 @@ public class Initializer {
let network: ZcashNetwork
let logger: Logger
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.
///
@ -166,7 +165,6 @@ public class Initializer {
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
alias: ZcashSynchronizerAlias = .default,
syncAlgorithm: SyncAlgorithm = .linear,
loggingPolicy: LoggingPolicy = .default(.debug),
enableBackendTracing: Bool = false
) {
@ -199,7 +197,6 @@ public class Initializer {
saplingParamsSourceURL: saplingParamsSourceURL,
alias: alias,
urlsParsingError: parsingError,
syncAlgorithm: syncAlgorithm,
loggingPolicy: loggingPolicy
)
}
@ -260,7 +257,6 @@ public class Initializer {
saplingParamsSourceURL: SaplingParamsSourceURL,
alias: ZcashSynchronizerAlias,
urlsParsingError: ZcashError?,
syncAlgorithm: SyncAlgorithm = .linear,
loggingPolicy: LoggingPolicy = .default(.debug)
) {
self.container = container
@ -288,7 +284,6 @@ public class Initializer {
self.walletBirthday = Checkpoint.birthday(with: 0, network: network).height
self.urlsParsingError = urlsParsingError
self.logger = container.resolve(Logger.self)
self.syncAlgorithm = syncAlgorithm
}
private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {

View File

@ -8,47 +8,36 @@
import Foundation
protocol LatestBlocksDataProvider {
var latestScannedHeight: BlockHeight { get async }
var latestScannedTime: TimeInterval { get async }
var fullyScannedHeight: BlockHeight { get async }
var maxScannedHeight: BlockHeight { get async }
var latestBlockHeight: BlockHeight { get async }
var walletBirthday: BlockHeight { get async }
var firstUnenhancedHeight: BlockHeight? { get async }
func updateScannedData() async
func updateBlockData() async
func updateUnenhancedData() async
func updateWalletBirthday(_ walletBirthday: BlockHeight) async
func updateLatestScannedHeight(_ latestScannedHeight: BlockHeight) async
func updateLatestScannedTime(_ latestScannedTime: TimeInterval) async
}
actor LatestBlocksDataProviderImpl: LatestBlocksDataProvider {
let service: LightWalletService
let transactionRepository: TransactionRepository
let rustBackend: ZcashRustBackendWelding
// Valid values are stored here after Synchronizer's `prepare` is called.
private(set) var latestScannedHeight: BlockHeight = .zero
private(set) var latestScannedTime: TimeInterval = 0.0
private(set) var firstUnenhancedHeight: BlockHeight?
private(set) var fullyScannedHeight: BlockHeight = .zero
private(set) var maxScannedHeight: BlockHeight = .zero
// Valid value is stored here after block processor's `nextState` is called.
private(set) var latestBlockHeight: BlockHeight = .zero
// Valid values are stored here after Synchronizer's `prepare` is called.
private(set) var walletBirthday: BlockHeight = .zero
init(service: LightWalletService, transactionRepository: TransactionRepository) {
init(service: LightWalletService, rustBackend: ZcashRustBackendWelding) {
self.service = service
self.transactionRepository = transactionRepository
self.rustBackend = rustBackend
}
/// Call of this function is potentially dangerous and can result in `database lock` errors.
/// Typical use is outside of a sync process. Example: Synchronizer's prepare function, call there is a safe one.
/// The update of `latestScannedHeight` and `latestScannedTime` during the syncing is done via
/// appropriate `updateX()` methods inside `BlockScanner` so `transactionRepository` is omitted.
func updateScannedData() async {
latestScannedHeight = (try? await transactionRepository.lastScannedHeight()) ?? walletBirthday
if let time = try? await transactionRepository.blockForHeight(latestScannedHeight)?.time {
latestScannedTime = TimeInterval(time)
}
fullyScannedHeight = (try? await rustBackend.fullyScannedHeight()) ?? walletBirthday
maxScannedHeight = (try? await rustBackend.maxScannedHeight()) ?? walletBirthday
}
func updateBlockData() async {
@ -58,19 +47,7 @@ actor LatestBlocksDataProviderImpl: LatestBlocksDataProvider {
}
}
func updateUnenhancedData() async {
firstUnenhancedHeight = try? transactionRepository.firstUnenhancedHeight()
}
func updateWalletBirthday(_ walletBirthday: BlockHeight) async {
self.walletBirthday = walletBirthday
}
func updateLatestScannedHeight(_ latestScannedHeight: BlockHeight) async {
self.latestScannedHeight = latestScannedHeight
}
func updateLatestScannedTime(_ latestScannedTime: TimeInterval) async {
self.latestScannedTime = latestScannedTime
}
}

View File

@ -11,10 +11,10 @@ protocol TransactionRepository {
func closeDBConnection()
func countAll() async throws -> Int
func countUnmined() async throws -> Int
func blockForHeight(_ height: BlockHeight) async throws -> Block?
func lastScannedHeight() async throws -> BlockHeight
func lastScannedBlock() async throws -> Block?
func firstUnenhancedHeight() throws -> BlockHeight?
// func blockForHeight(_ height: BlockHeight) async throws -> Block?
// func lastScannedHeight() async throws -> BlockHeight
// func lastScannedBlock() async throws -> Block?
// func firstUnenhancedHeight() throws -> BlockHeight?
func isInitialized() async throws -> Bool
func find(id: Int) async throws -> ZcashTransaction.Overview
func find(rawID: Data) async throws -> ZcashTransaction.Overview

View File

@ -52,9 +52,7 @@ public struct SynchronizerState: Equatable {
shieldedBalance: .zero,
transparentBalance: .zero,
internalSyncStatus: .unprepared,
latestScannedHeight: .zero,
latestBlockHeight: .zero,
latestScannedTime: 0
latestBlockHeight: .zero
)
}
@ -63,9 +61,7 @@ public struct SynchronizerState: Equatable {
shieldedBalance: WalletBalance,
transparentBalance: WalletBalance,
internalSyncStatus: InternalSyncStatus,
latestScannedHeight: BlockHeight,
latestBlockHeight: BlockHeight,
latestScannedTime: TimeInterval
latestBlockHeight: BlockHeight
) {
self.syncSessionID = syncSessionID
self.shieldedBalance = shieldedBalance
@ -112,9 +108,6 @@ public protocol Synchronizer: AnyObject {
/// An object that when enabled collects mertrics from the synchronizer
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
/// database migrations. most of the times the seed won't be needed. If they do and are
@ -193,9 +186,6 @@ public protocol Synchronizer: AnyObject {
shieldingThreshold: Zatoshi
) async throws -> ZcashTransaction.Overview
/// all outbound pending transactions that have been sent but are awaiting confirmations
var pendingTransactions: [ZcashTransaction.Overview] { get async }
/// all the transactions that are on the blockchain
var transactions: [ZcashTransaction.Overview] { get async }
@ -236,10 +226,6 @@ public protocol Synchronizer: AnyObject {
/// - Returns: an array with the given Transactions or an empty array
func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) async throws -> [ZcashTransaction.Overview]
/// Fetch all pending transactions
/// - Returns: an array of transactions which are considered pending confirmation. can be empty
func allPendingTransactions() async throws -> [ZcashTransaction.Overview]
/// Returns the latest block height from the provided Lightwallet endpoint
func latestHeight() async throws -> BlockHeight
@ -421,15 +407,6 @@ 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
public enum TransactionKind {
case sent

View File

@ -92,12 +92,6 @@ extension ClosureSDKSynchronizer: ClosureSynchronizer {
}
}
public func pendingTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.pendingTransactions
}
}
public func clearedTransactions(completion: @escaping ([ZcashTransaction.Overview]) -> Void) {
AsyncToClosureGateway.executeAction(completion) {
await self.synchronizer.transactions

View File

@ -89,12 +89,6 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
}
}
public var pendingTransactions: AnyPublisher<[ZcashTransaction.Overview], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.pendingTransactions
}
}
public var allTransactions: SinglePublisher<[ZcashTransaction.Overview], Never> {
AsyncToCombineGateway.executeAction() {
await self.synchronizer.transactions
@ -127,12 +121,6 @@ extension CombineSDKSynchronizer: CombineSynchronizer {
}
}
public func allPendingTransactions() -> AnyPublisher<[ZcashTransaction.Overview], Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.allPendingTransactions()
}
}
public func allTransactions(from transaction: ZcashTransaction.Overview, limit: Int) -> SinglePublisher<[ZcashTransaction.Overview], Error> {
AsyncToCombineGateway.executeThrowingAction() {
try await self.synchronizer.allTransactions(from: transaction, limit: limit)

View File

@ -80,9 +80,9 @@ enum Dependencies {
container.register(type: LatestBlocksDataProvider.self, isSingleton: true) { di in
let service = di.resolve(LightWalletService.self)
let transactionRepository = di.resolve(TransactionRepository.self)
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
return LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository)
return LatestBlocksDataProviderImpl(service: service, rustBackend: rustBackend)
}
container.register(type: SyncSessionIDGenerator.self, isSingleton: false) { _ in
@ -120,7 +120,6 @@ enum Dependencies {
let transactionRepository = di.resolve(TransactionRepository.self)
let metrics = di.resolve(SDKMetrics.self)
let logger = di.resolve(Logger.self)
let latestBlocksDataProvider = di.resolve(LatestBlocksDataProvider.self)
let blockScannerConfig = BlockScannerConfig(
networkType: config.network.networkType,
@ -132,8 +131,7 @@ enum Dependencies {
rustBackend: rustBackend,
transactionRepository: transactionRepository,
metrics: metrics,
logger: logger,
latestBlocksDataProvider: latestBlocksDataProvider
logger: logger
)
}

View File

@ -24,8 +24,6 @@ public class SDKSynchronizer: Synchronizer {
public let metrics: SDKMetrics
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.
private var underlyingStatus: GenericActor<InternalSyncStatus>
@ -89,7 +87,6 @@ public class SDKSynchronizer: Synchronizer {
self.syncSession = SyncSession(.nullID)
self.syncSessionTicker = syncSessionTicker
self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self)
self.syncAlgorithm = initializer.syncAlgorithm
initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in
self?.connectivityStateChanged(oldState: oldState, newState: newState)
@ -313,8 +310,8 @@ public class SDKSynchronizer: Synchronizer {
let accountIndex = Int(spendingKey.account)
let tBalance = try await self.getTransparentBalance(accountIndex: accountIndex)
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
guard tBalance.verified >= self.network.constants.defaultFee(for: await self.latestBlocksDataProvider.latestScannedHeight) else {
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
guard tBalance.verified >= self.network.constants.defaultFee(for: await self.latestBlocksDataProvider.maxScannedHeight) else {
throw ZcashError.synchronizerShieldFundsInsuficientTransparentFunds
}
@ -367,12 +364,6 @@ public class SDKSynchronizer: Synchronizer {
try await transactionRepository.findReceived(offset: 0, limit: Int.max)
}
public func allPendingTransactions() async throws -> [ZcashTransaction.Overview] {
let latestScannedHeight = try await transactionRepository.lastScannedHeight()
return try await transactionRepository.findPendingTransactions(latestHeight: latestScannedHeight, offset: 0, limit: .max)
}
public func allTransactions() async throws -> [ZcashTransaction.Overview] {
return try await transactionRepository.find(offset: 0, limit: Int.max, kind: .all)
}
@ -556,9 +547,7 @@ public class SDKSynchronizer: Synchronizer {
),
transparentBalance: (try? await blockProcessor.getTransparentBalance(accountIndex: 0)) ?? .zero,
internalSyncStatus: status,
latestScannedHeight: latestBlocksDataProvider.latestScannedHeight,
latestBlockHeight: latestBlocksDataProvider.latestBlockHeight,
latestScannedTime: latestBlocksDataProvider.latestScannedTime
latestBlockHeight: latestBlocksDataProvider.latestBlockHeight
)
}
@ -622,12 +611,6 @@ extension SDKSynchronizer {
(try? await allReceivedTransactions()) ?? []
}
}
public var pendingTransactions: [ZcashTransaction.Overview] {
get async {
(try? await allPendingTransactions()) ?? []
}
}
}
extension InternalSyncStatus {

View File

@ -408,20 +408,6 @@ class ClosureSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testPendingTransactionsSucceed() {
synchronizerMock.underlyingPendingTransactions = [data.pendingTransactionEntity]
let expectation = XCTestExpectation()
synchronizer.pendingTransactions() { transactions in
XCTAssertEqual(transactions.count, 1)
XCTAssertEqual(transactions[0].id, self.data.pendingTransactionEntity.id)
expectation.fulfill()
}
wait(for: [expectation], timeout: 0.5)
}
func testClearedTransactionsSucceed() {
synchronizerMock.underlyingTransactions = [data.clearedTransaction]

View File

@ -430,30 +430,6 @@ class CombineSynchronizerOfflineTests: XCTestCase {
wait(for: [expectation], timeout: 0.5)
}
func testPendingTransactionsSucceed() {
synchronizerMock.underlyingPendingTransactions = [data.pendingTransactionEntity]
let expectation = XCTestExpectation()
synchronizer.pendingTransactions
.sink(
receiveCompletion: { result in
switch result {
case .finished:
expectation.fulfill()
case let .failure(error):
XCTFail("Unpected failure with error: \(error)")
}
},
receiveValue: { value in
XCTAssertEqual(value.map { $0.id }, [self.data.pendingTransactionEntity.id])
}
)
.store(in: &cancellables)
wait(for: [expectation], timeout: 0.5)
}
func testClearedTransactionsSucceed() {
synchronizerMock.underlyingTransactions = [data.clearedTransaction]

View File

@ -32,15 +32,4 @@ final class ActionContextStateTests: XCTestCase {
XCTFail("syncContext.prevState is not expected to be nil.")
}
}
func testDefaultSyncAlgorith() async throws {
let syncContext = ActionContextImpl(state: .idle)
let preferredSyncAlgorithm = await syncContext.preferredSyncAlgorithm
XCTAssertTrue(
preferredSyncAlgorithm == .linear,
"ActionContext default preferredSyncAlgorithm is expected to be .linear but received \(preferredSyncAlgorithm)"
)
}
}

View File

@ -57,7 +57,6 @@ final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
let transactionRepositoryMock = TransactionRepositoryMock()
compactBlockRepositoryMock.clearUpToClosure = { _ in }
transactionRepositoryMock.lastScannedHeightReturnValue = 1
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }

View File

@ -30,90 +30,6 @@ final class ClearCacheActionTests: ZcashTestCase {
}
}
func testClearCacheAction_FinishedLinear() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
let clearCacheAction = setupAction(compactBlockRepositoryMock)
do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .linear
let nextContext = try await clearCacheAction.run(with: context) { _ in }
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
let acResult = nextContext.checkStateIs(.finished)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
}
}
func testClearCacheAction_PreferredSbSNoSupportedSyncAlgorithm() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
let clearCacheAction = setupAction(compactBlockRepositoryMock)
do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
_ = try await clearCacheAction.run(with: context) { _ in }
} catch ZcashError.compactBlockProcessorSupportedSyncAlgorithm {
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
} catch {
XCTFail(
"""
testClearCacheAction_PredferedSbSNoSupportedSyncAlgorithm is expected to fail
with ZcashError.compactBlockProcessorSupportedSyncAlgorithm but received \(error)
"""
)
}
}
func testClearCacheAction_PreferredSbSSupportedSyncAlgorithmLinear() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
let clearCacheAction = setupAction(compactBlockRepositoryMock)
do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
context.supportedSyncAlgorithm = .linear
let nextContext = try await clearCacheAction.run(with: context) { _ in }
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
let acResult = nextContext.checkStateIs(.finished)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
}
}
func testClearCacheAction_PreferedSbSSupportedSyncAlgorithmSbS() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
let clearCacheAction = setupAction(compactBlockRepositoryMock)
do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
context.supportedSyncAlgorithm = .spendBeforeSync
let nextContext = try await clearCacheAction.run(with: context) { _ in }
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
let acResult = nextContext.checkStateIs(.processSuggestedScanRanges)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
}
}
private func setupAction(
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock()
) -> ClearCacheAction {

View File

@ -1,166 +0,0 @@
//
// ComputeSyncControlDataActionTests.swift
//
//
// Created by Lukáš Korba on 22.05.2023.
//
import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
final class ComputeSyncControlDataActionTests: ZcashTestCase {
var underlyingDownloadRange: CompactBlockRange?
var underlyingScanRange: CompactBlockRange?
override func setUp() {
super.setUp()
underlyingDownloadRange = nil
underlyingScanRange = nil
}
func testComputeSyncControlDataAction_finishProcessingCase() async throws {
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
let loggerMock = LoggerMock()
let computeSyncControlDataAction = setupDefaultMocksAndReturnAction(
blockDownloaderServiceMock,
latestBlocksDataProviderMock,
loggerMock
)
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 123
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 123
let syncContext = await setupActionContext()
do {
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
checkLatestBlocksDataProvider(latestBlocksDataProviderMock)
checkActionContext(nextContext, expectedNextState: .finished)
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug() is expected to be called.")
} catch {
XCTFail("testComputeSyncControlDataAction_finishProcessingCase is not expected to fail. \(error)")
}
}
func testComputeSyncControlDataAction_DownloadCase() async throws {
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
let loggerMock = LoggerMock()
let computeSyncControlDataAction = setupDefaultMocksAndReturnAction(
blockDownloaderServiceMock,
latestBlocksDataProviderMock,
loggerMock
)
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1234
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 123
let syncContext = await setupActionContext()
do {
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
checkLatestBlocksDataProvider(latestBlocksDataProviderMock)
checkActionContext(nextContext, expectedNextState: .download)
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug() is expected to be called.")
} catch {
XCTFail("testComputeSyncControlDataAction_checksBeforeSyncCase is not expected to fail. \(error)")
}
}
private func setupActionContext() async -> ActionContextMock {
let syncContext = ActionContextMock()
syncContext.updateLastScannedHeightClosure = { _ in }
syncContext.updateLastDownloadedHeightClosure = { _ in }
syncContext.updateSyncControlDataClosure = { _ in }
syncContext.updateTotalProgressRangeClosure = { _ in }
syncContext.updateStateClosure = { _ in }
syncContext.underlyingState = .idle
return syncContext
}
private func setupDefaultMocksAndReturnAction(
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
_ loggerMock: LoggerMock = LoggerMock()
) -> ComputeSyncControlDataAction {
latestBlocksDataProviderMock.updateScannedDataClosure = { }
latestBlocksDataProviderMock.updateBlockDataClosure = { }
latestBlocksDataProviderMock.updateUnenhancedDataClosure = { }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
return setupAction(
blockDownloaderServiceMock,
latestBlocksDataProviderMock,
loggerMock
)
}
private func setupAction(
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
_ loggerMock: LoggerMock = LoggerMock()
) -> ComputeSyncControlDataAction {
mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in blockDownloaderServiceMock }
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in latestBlocksDataProviderMock }
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
let config: CompactBlockProcessor.Configuration = .standard(
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
)
return ComputeSyncControlDataAction(
container: mockContainer,
configProvider: CompactBlockProcessor.ConfigProvider(config: config)
)
}
private func checkLatestBlocksDataProvider(_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock) {
XCTAssertTrue(
latestBlocksDataProviderMock.updateScannedDataCalled,
"latestBlocksDataProvider.updateScannedData() is expected to be called."
)
XCTAssertTrue(
latestBlocksDataProviderMock.updateBlockDataCalled,
"latestBlocksDataProvider.updateBlockData() is expected to be called."
)
XCTAssertTrue(
latestBlocksDataProviderMock.updateUnenhancedDataCalled,
"latestBlocksDataProvider.updateUnenhancedData() is expected to be called."
)
}
private func checkActionContext(_ actionContext: ActionContext, expectedNextState: CBPState) {
guard let nextContextMock = actionContext as? ActionContextMock else {
return XCTFail("Result of run(with:) is expected to be an ActionContextMock")
}
XCTAssertTrue(nextContextMock.updateStateCallsCount == 1)
XCTAssertTrue(nextContextMock.updateStateReceivedState == expectedNextState)
XCTAssertTrue(
nextContextMock.updateLastScannedHeightCallsCount == 1,
"actionContext.update(lastScannedHeight:) is expected to be called."
)
XCTAssertTrue(
nextContextMock.updateLastDownloadedHeightCallsCount == 1,
"actionContext.update(lastDownloadedHeight:) is expected to be called."
)
XCTAssertTrue(
nextContextMock.updateSyncControlDataCallsCount == 1,
"actionContext.update(syncControlData:) is expected to be called."
)
XCTAssertTrue(
nextContextMock.updateTotalProgressRangeCallsCount == 1,
"actionContext.update(totalProgressRange:) is expected to be called."
)
}
}

View File

@ -17,7 +17,6 @@ final class DownloadActionTests: ZcashTestCase {
let blockDownloaderMock = BlockDownloaderMock()
let transactionRepositoryMock = TransactionRepositoryMock()
transactionRepositoryMock.lastScannedHeightReturnValue = 1000
blockDownloaderMock.setSyncRangeBatchSizeClosure = { _, _ in }
blockDownloaderMock.setDownloadLimitClosure = { _ in }
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
@ -120,10 +119,6 @@ final class DownloadActionTests: ZcashTestCase {
do {
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
XCTAssertFalse(
transactionRepositoryMock.lastScannedHeightCalled,
"transactionRepository.lastScannedHeight() 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.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is not expected to be called.")

View File

@ -38,10 +38,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
XCTAssertFalse(
transactionRepositoryMock.lastScannedHeightCalled,
"transactionRepository.lastScannedHeight() is not expected to be called."
)
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
@ -71,10 +67,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
XCTFail("testMigrateLegacyCacheDBAction_noFsBlockCacheRoot is expected to fail.")
} catch ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL {
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
XCTAssertFalse(
transactionRepositoryMock.lastScannedHeightCalled,
"transactionRepository.lastScannedHeight() is not expected to be called."
)
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
} catch {
@ -107,10 +99,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
XCTAssertFalse(
transactionRepositoryMock.lastScannedHeightCalled,
"transactionRepository.lastScannedHeight() is not expected to be called."
)
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
@ -143,10 +131,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
XCTAssertFalse(
transactionRepositoryMock.lastScannedHeightCalled,
"transactionRepository.lastScannedHeight() is not expected to be called."
)
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
@ -180,10 +164,6 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
_ = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
} catch ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb {
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
XCTAssertFalse(
transactionRepositoryMock.lastScannedHeightCalled,
"transactionRepository.lastScannedHeight() is not expected to be called."
)
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
XCTAssertTrue(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is expected to be called.")
} catch {

View File

@ -10,28 +10,6 @@ import XCTest
@testable import ZcashLightClientKit
final class SaplingParamsActionTests: ZcashTestCase {
func testSaplingParamsAction_NextAction_linearSync() async throws {
let loggerMock = LoggerMock()
let saplingParametersHandlerMock = SaplingParametersHandlerMock()
let saplingParamsActionAction = setupAction(saplingParametersHandlerMock, loggerMock)
do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .linear
let nextContext = try await saplingParamsActionAction.run(with: context) { _ in }
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
XCTAssertTrue(saplingParametersHandlerMock.handleIfNeededCalled, "saplingParametersHandler.handleIfNeeded() is expected to be called.")
let acResult = nextContext.checkStateIs(.computeSyncControlData)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
}
}
func testSaplingParamsAction_NextAction_SpendBeforeSync() async throws {
let loggerMock = LoggerMock()
let saplingParametersHandlerMock = SaplingParametersHandlerMock()
@ -40,7 +18,6 @@ final class SaplingParamsActionTests: ZcashTestCase {
do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
let nextContext = try await saplingParamsActionAction.run(with: context) { _ in }

View File

@ -23,55 +23,11 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
underlyingSaplingActivationHeight = nil
underlyingConsensusBranchID = "c2d6d0b4"
}
func testUpdateSubtreeRootsAction_getSubtreeRootsFailure() async throws {
let loggerMock = LoggerMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let tupple = setupAction(loggerMock)
let updateSubtreeRootsActionAction = tupple.action
tupple.serviceMock.getSubtreeRootsClosure = { _ in
AsyncThrowingStream { continuation in continuation.finish(throwing: ZcashError.serviceSubmitFailed(.invalidBlock)) }
}
do {
let context = ActionContextMock.default()
context.updateSupportedSyncAlgorithmClosure = { _ in }
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
if let nextContextMock = nextContext as? ActionContextMock {
if let supportedSyncAlgorithm = nextContextMock.updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm {
XCTAssertTrue(
supportedSyncAlgorithm == .linear,
"supportedSyncAlgorithm is expected to be .linear but received \(supportedSyncAlgorithm)"
)
} else {
XCTFail("`nextContextMock` supportedSyncAlgorithm not set")
}
} else {
XCTFail("`nextContext` is not the ActionContextMock")
}
if let debugArguments = loggerMock.debugFileFunctionLineReceivedArguments {
XCTAssertTrue(debugArguments.message.contains("getSubtreeRoots failed with error"))
} else {
XCTFail("`debugArguments` unavailable.")
}
let acResult = nextContext.checkStateIs(.computeSyncControlData)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testUpdateSubtreeRootsAction_getSubtreeRootsFailure is not expected to fail. \(error)")
}
}
func testUpdateSubtreeRootsAction_getSubtreeRootsTimeout() async throws {
let loggerMock = LoggerMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
let tupple = setupAction(loggerMock)
let updateSubtreeRootsActionAction = tupple.action
@ -81,7 +37,6 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
do {
let context = ActionContextMock.default()
context.updateSupportedSyncAlgorithmClosure = { _ in }
_ = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
XCTFail("The test is expected to fail but continued.")
@ -98,51 +53,11 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
}
}
func testUpdateSubtreeRootsAction_getSubtreeRootsEmpty() async throws {
let loggerMock = LoggerMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let tupple = setupAction(loggerMock)
let updateSubtreeRootsActionAction = tupple.action
tupple.serviceMock.getSubtreeRootsClosure = { _ in
AsyncThrowingStream { continuation in continuation.finish() }
}
do {
let context = ActionContextMock.default()
context.updateSupportedSyncAlgorithmClosure = { _ in }
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
if let nextContextMock = nextContext as? ActionContextMock {
if let supportedSyncAlgorithm = nextContextMock.updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm {
XCTAssertTrue(
supportedSyncAlgorithm == .linear,
"supportedSyncAlgorithm is expected to be .linear but received \(supportedSyncAlgorithm)"
)
} else {
XCTFail("`nextContextMock` supportedSyncAlgorithm not set")
}
} else {
XCTFail("`nextContext` is not the ActionContextMock")
}
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug() is not expected to be called.")
let acResult = nextContext.checkStateIs(.computeSyncControlData)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testUpdateSubtreeRootsAction_getSubtreeRootsEmpty is not expected to fail. \(error)")
}
}
func testUpdateSubtreeRootsAction_RootsAvailablePutRootsSuccess() async throws {
let loggerMock = LoggerMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let tupple = setupAction(loggerMock)
let updateSubtreeRootsActionAction = tupple.action
@ -152,27 +67,13 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
continuation.finish()
}
}
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsClosure( { _, _ in } )
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsClosure({ _, _ in })
do {
let context = ActionContextMock.default()
context.updateSupportedSyncAlgorithmClosure = { _ in }
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
if let nextContextMock = nextContext as? ActionContextMock {
if let supportedSyncAlgorithm = nextContextMock.updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm {
XCTAssertTrue(
supportedSyncAlgorithm == .spendBeforeSync,
"supportedSyncAlgorithm is expected to be .linear but received \(supportedSyncAlgorithm)"
)
} else {
XCTFail("`nextContextMock` supportedSyncAlgorithm not set")
}
} else {
XCTFail("`nextContext` is not the ActionContextMock")
}
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug() is not expected to be called.")
let acResult = nextContext.checkStateIs(.updateChainTip)
@ -185,8 +86,8 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
func testUpdateSubtreeRootsAction_RootsAvailablePutRootsFailure() async throws {
let loggerMock = LoggerMock()
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let tupple = setupAction(loggerMock)
let updateSubtreeRootsActionAction = tupple.action
@ -200,7 +101,6 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
do {
let context = ActionContextMock.default()
context.updateSupportedSyncAlgorithmClosure = { _ in }
_ = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
@ -247,7 +147,8 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
action:
UpdateSubtreeRootsAction(
container: mockContainer,
configProvider: CompactBlockProcessor.ConfigProvider(config: config)),
configProvider: CompactBlockProcessor.ConfigProvider(config: config)
),
serviceMock: serviceMock,
rustBackendMock: rustBackendMock
)

View File

@ -468,25 +468,6 @@ class SynchronizerOfflineTests: ZcashTestCase {
XCTFail("Syncing is expected to be 100% (1.0) but received \(data).")
}
}
func testLinearIsSetAsDefault() async throws {
let databases = TemporaryDbBuilder.build()
let initializer = Initializer(
cacheDbURL: nil,
fsBlockDbRoot: databases.fsCacheDbRoot,
generalStorageURL: testGeneralStorageDirectory,
dataDbURL: databases.dataDB,
endpoint: LightWalletEndpoint(address: "lightwalletd.electriccoin.co", port: 9067, secure: true),
network: ZcashNetworkBuilder.network(for: .mainnet),
spendParamsURL: try __spendParamsURL(),
outputParamsURL: try __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
alias: .default,
loggingPolicy: .default(.debug)
)
XCTAssertTrue(initializer.syncAlgorithm == .linear, "Spend before Sync is a beta feature so linear syncing is set to default.")
}
func synchronizerState(for internalSyncStatus: InternalSyncStatus) -> SynchronizerState {
SynchronizerState(
@ -494,9 +475,7 @@ class SynchronizerOfflineTests: ZcashTestCase {
shieldedBalance: .zero,
transparentBalance: .zero,
internalSyncStatus: internalSyncStatus,
latestScannedHeight: .zero,
latestBlockHeight: .zero,
latestScannedTime: 0
latestBlockHeight: .zero
)
}
}

View File

@ -35,16 +35,6 @@ class TransactionRepositoryTests: XCTestCase {
XCTAssertEqual(count, 0)
}
func testBlockForHeight() async throws {
let block = try await self.transactionRepository.blockForHeight(663150)
XCTAssertEqual(block?.height, 663150)
}
func testLastScannedHeight() async throws {
let height = try await self.transactionRepository.lastScannedHeight()
XCTAssertEqual(height, 665000)
}
func testFindInRange() async throws {
let transactions = try await self.transactionRepository.find(in: 663218...663974, limit: 3, kind: .received)
XCTAssertEqual(transactions.count, 3)

View File

@ -95,10 +95,6 @@ extension MockTransactionRepository: TransactionRepository {
unminedCount
}
func blockForHeight(_ height: BlockHeight) throws -> Block? {
nil
}
func findBy(id: Int) throws -> ZcashTransaction.Overview? {
transactions.first(where: { $0.id == id })
}
@ -111,10 +107,6 @@ extension MockTransactionRepository: TransactionRepository {
scannedHeight
}
func lastScannedBlock() throws -> Block? {
nil
}
func firstUnenhancedHeight() throws -> ZcashLightClientKit.BlockHeight? {
nil
}

View File

@ -22,11 +22,6 @@ class ActionContextMock: ActionContext {
get { return underlyingSyncControlData }
}
var underlyingSyncControlData: SyncControlData!
var preferredSyncAlgorithm: SyncAlgorithm {
get { return underlyingPreferredSyncAlgorithm }
}
var underlyingPreferredSyncAlgorithm: SyncAlgorithm!
var supportedSyncAlgorithm: SyncAlgorithm?
var requestedRewindHeight: BlockHeight?
var totalProgressRange: CompactBlockRange {
get { return underlyingTotalProgressRange }
@ -165,21 +160,6 @@ class ActionContextMock: ActionContext {
// MARK: - update
var updateSupportedSyncAlgorithmCallsCount = 0
var updateSupportedSyncAlgorithmCalled: Bool {
return updateSupportedSyncAlgorithmCallsCount > 0
}
var updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm: SyncAlgorithm?
var updateSupportedSyncAlgorithmClosure: ((SyncAlgorithm) async -> Void)?
func update(supportedSyncAlgorithm: SyncAlgorithm) async {
updateSupportedSyncAlgorithmCallsCount += 1
updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm = supportedSyncAlgorithm
await updateSupportedSyncAlgorithmClosure!(supportedSyncAlgorithm)
}
// MARK: - update
var updateRequestedRewindHeightCallsCount = 0
var updateRequestedRewindHeightCalled: Bool {
return updateRequestedRewindHeightCallsCount > 0
@ -693,14 +673,14 @@ class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
init(
) {
}
var latestScannedHeight: BlockHeight {
get { return underlyingLatestScannedHeight }
var fullyScannedHeight: BlockHeight {
get { return underlyingFullyScannedHeight }
}
var underlyingLatestScannedHeight: BlockHeight!
var latestScannedTime: TimeInterval {
get { return underlyingLatestScannedTime }
var underlyingFullyScannedHeight: BlockHeight!
var maxScannedHeight: BlockHeight {
get { return underlyingMaxScannedHeight }
}
var underlyingLatestScannedTime: TimeInterval!
var underlyingMaxScannedHeight: BlockHeight!
var latestBlockHeight: BlockHeight {
get { return underlyingLatestBlockHeight }
}
@ -709,7 +689,6 @@ class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
get { return underlyingWalletBirthday }
}
var underlyingWalletBirthday: BlockHeight!
var firstUnenhancedHeight: BlockHeight?
// MARK: - updateScannedData
@ -737,19 +716,6 @@ class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
await updateBlockDataClosure!()
}
// MARK: - updateUnenhancedData
var updateUnenhancedDataCallsCount = 0
var updateUnenhancedDataCalled: Bool {
return updateUnenhancedDataCallsCount > 0
}
var updateUnenhancedDataClosure: (() async -> Void)?
func updateUnenhancedData() async {
updateUnenhancedDataCallsCount += 1
await updateUnenhancedDataClosure!()
}
// MARK: - updateWalletBirthday
var updateWalletBirthdayCallsCount = 0
@ -765,36 +731,6 @@ class LatestBlocksDataProviderMock: LatestBlocksDataProvider {
await updateWalletBirthdayClosure!(walletBirthday)
}
// MARK: - updateLatestScannedHeight
var updateLatestScannedHeightCallsCount = 0
var updateLatestScannedHeightCalled: Bool {
return updateLatestScannedHeightCallsCount > 0
}
var updateLatestScannedHeightReceivedLatestScannedHeight: BlockHeight?
var updateLatestScannedHeightClosure: ((BlockHeight) async -> Void)?
func updateLatestScannedHeight(_ latestScannedHeight: BlockHeight) async {
updateLatestScannedHeightCallsCount += 1
updateLatestScannedHeightReceivedLatestScannedHeight = latestScannedHeight
await updateLatestScannedHeightClosure!(latestScannedHeight)
}
// MARK: - updateLatestScannedTime
var updateLatestScannedTimeCallsCount = 0
var updateLatestScannedTimeCalled: Bool {
return updateLatestScannedTimeCallsCount > 0
}
var updateLatestScannedTimeReceivedLatestScannedTime: TimeInterval?
var updateLatestScannedTimeClosure: ((TimeInterval) async -> Void)?
func updateLatestScannedTime(_ latestScannedTime: TimeInterval) async {
updateLatestScannedTimeCallsCount += 1
updateLatestScannedTimeReceivedLatestScannedTime = latestScannedTime
await updateLatestScannedTimeClosure!(latestScannedTime)
}
}
class LightWalletServiceMock: LightWalletService {
@ -1234,14 +1170,6 @@ class SynchronizerMock: Synchronizer {
get { return underlyingMetrics }
}
var underlyingMetrics: SDKMetrics!
var syncAlgorithm: SyncAlgorithm {
get { return underlyingSyncAlgorithm }
}
var underlyingSyncAlgorithm: SyncAlgorithm!
var pendingTransactions: [ZcashTransaction.Overview] {
get async { return underlyingPendingTransactions }
}
var underlyingPendingTransactions: [ZcashTransaction.Overview] = []
var transactions: [ZcashTransaction.Overview] {
get async { return underlyingTransactions }
}
@ -1539,28 +1467,6 @@ class SynchronizerMock: Synchronizer {
}
}
// MARK: - allPendingTransactions
var allPendingTransactionsThrowableError: Error?
var allPendingTransactionsCallsCount = 0
var allPendingTransactionsCalled: Bool {
return allPendingTransactionsCallsCount > 0
}
var allPendingTransactionsReturnValue: [ZcashTransaction.Overview]!
var allPendingTransactionsClosure: (() async throws -> [ZcashTransaction.Overview])?
func allPendingTransactions() async throws -> [ZcashTransaction.Overview] {
if let error = allPendingTransactionsThrowableError {
throw error
}
allPendingTransactionsCallsCount += 1
if let closure = allPendingTransactionsClosure {
return try await closure()
} else {
return allPendingTransactionsReturnValue
}
}
// MARK: - latestHeight
var latestHeightThrowableError: Error?
@ -1782,96 +1688,6 @@ class TransactionRepositoryMock: TransactionRepository {
}
}
// MARK: - blockForHeight
var blockForHeightThrowableError: Error?
var blockForHeightCallsCount = 0
var blockForHeightCalled: Bool {
return blockForHeightCallsCount > 0
}
var blockForHeightReceivedHeight: BlockHeight?
var blockForHeightReturnValue: Block?
var blockForHeightClosure: ((BlockHeight) async throws -> Block?)?
func blockForHeight(_ height: BlockHeight) async throws -> Block? {
if let error = blockForHeightThrowableError {
throw error
}
blockForHeightCallsCount += 1
blockForHeightReceivedHeight = height
if let closure = blockForHeightClosure {
return try await closure(height)
} else {
return blockForHeightReturnValue
}
}
// MARK: - lastScannedHeight
var lastScannedHeightThrowableError: Error?
var lastScannedHeightCallsCount = 0
var lastScannedHeightCalled: Bool {
return lastScannedHeightCallsCount > 0
}
var lastScannedHeightReturnValue: BlockHeight!
var lastScannedHeightClosure: (() async throws -> BlockHeight)?
func lastScannedHeight() async throws -> BlockHeight {
if let error = lastScannedHeightThrowableError {
throw error
}
lastScannedHeightCallsCount += 1
if let closure = lastScannedHeightClosure {
return try await closure()
} else {
return lastScannedHeightReturnValue
}
}
// MARK: - lastScannedBlock
var lastScannedBlockThrowableError: Error?
var lastScannedBlockCallsCount = 0
var lastScannedBlockCalled: Bool {
return lastScannedBlockCallsCount > 0
}
var lastScannedBlockReturnValue: Block?
var lastScannedBlockClosure: (() async throws -> Block?)?
func lastScannedBlock() async throws -> Block? {
if let error = lastScannedBlockThrowableError {
throw error
}
lastScannedBlockCallsCount += 1
if let closure = lastScannedBlockClosure {
return try await closure()
} else {
return lastScannedBlockReturnValue
}
}
// MARK: - firstUnenhancedHeight
var firstUnenhancedHeightThrowableError: Error?
var firstUnenhancedHeightCallsCount = 0
var firstUnenhancedHeightCalled: Bool {
return firstUnenhancedHeightCallsCount > 0
}
var firstUnenhancedHeightReturnValue: BlockHeight?
var firstUnenhancedHeightClosure: (() throws -> BlockHeight?)?
func firstUnenhancedHeight() throws -> BlockHeight? {
if let error = firstUnenhancedHeightThrowableError {
throw error
}
firstUnenhancedHeightCallsCount += 1
if let closure = firstUnenhancedHeightClosure {
return try closure()
} else {
return firstUnenhancedHeightReturnValue
}
}
// MARK: - isInitialized
var isInitializedThrowableError: Error?

View File

@ -172,9 +172,7 @@ extension SynchronizerState {
shieldedBalance: WalletBalance(verified: Zatoshi(100), total: Zatoshi(200)),
transparentBalance: WalletBalance(verified: Zatoshi(200), total: Zatoshi(300)),
internalSyncStatus: .syncing(0),
latestScannedHeight: 111111,
latestBlockHeight: 222222,
latestScannedTime: 12345678
latestBlockHeight: 222222
)
}
}