[#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:
parent
18141e6e4b
commit
d956e9940b
|
@ -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
|
||||
|
|
|
@ -53,7 +53,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
spendParamsURL: try! spendParamsURLHelper(),
|
||||
outputParamsURL: try! outputParamsURLHelper(),
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||
syncAlgorithm: .spendBeforeSync,
|
||||
enableBackendTracing: true
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 { }
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -27,7 +27,6 @@ struct BlockScannerImpl {
|
|||
let transactionRepository: TransactionRepository
|
||||
let metrics: SDKMetrics
|
||||
let logger: Logger
|
||||
let latestBlocksDataProvider: LatestBlocksDataProvider
|
||||
}
|
||||
|
||||
extension BlockScannerImpl: BlockScanner {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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."
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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.")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue