react-native-zcash 0.6.11 → 0.6.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/ios/ZCashLightClientKit/Block/Actions/Action.swift +98 -0
- package/ios/ZCashLightClientKit/Block/Actions/ClearAlreadyScannedBlocksAction.swift +35 -0
- package/ios/ZCashLightClientKit/Block/Actions/ClearCacheAction.swift +30 -0
- package/ios/ZCashLightClientKit/Block/Actions/DownloadAction.swift +67 -0
- package/ios/ZCashLightClientKit/Block/Actions/EnhanceAction.swift +97 -0
- package/ios/ZCashLightClientKit/Block/Actions/FetchUTXOsAction.swift +33 -0
- package/ios/ZCashLightClientKit/Block/Actions/MigrateLegacyCacheDBAction.swift +70 -0
- package/ios/ZCashLightClientKit/Block/Actions/ProcessSuggestedScanRangesAction.swift +60 -0
- package/ios/ZCashLightClientKit/Block/Actions/RewindAction.swift +48 -0
- package/ios/ZCashLightClientKit/Block/Actions/SaplingParamsAction.swift +33 -0
- package/ios/ZCashLightClientKit/Block/Actions/ScanAction.swift +95 -0
- package/ios/ZCashLightClientKit/Block/Actions/UpdateChainTipAction.swift +55 -0
- package/ios/ZCashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift +58 -0
- package/ios/ZCashLightClientKit/Block/Actions/ValidateServerAction.swift +60 -0
- package/ios/ZCashLightClientKit/Block/CompactBlockProcessor.swift +425 -937
- package/ios/ZCashLightClientKit/Block/Download/BlockDownloader.swift +31 -17
- package/ios/ZCashLightClientKit/Block/Download/BlockDownloaderService.swift +2 -2
- package/ios/ZCashLightClientKit/Block/Enhance/BlockEnhancer.swift +46 -15
- package/ios/ZCashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +4 -15
- package/ios/ZCashLightClientKit/Block/FilesystemStorage/FSCompactBlockRepository.swift +4 -4
- package/ios/ZCashLightClientKit/Block/Scan/BlockScanner.swift +10 -35
- package/ios/ZCashLightClientKit/Block/Utils/CompactBlockProgress.swift +24 -0
- package/ios/ZCashLightClientKit/Block/Utils/SyncControlData.swift +25 -0
- package/ios/ZCashLightClientKit/ClosureSynchronizer.swift +1 -2
- package/ios/ZCashLightClientKit/CombineSynchronizer.swift +2 -5
- package/ios/ZCashLightClientKit/Constants/ZcashSDK.swift +13 -26
- package/ios/ZCashLightClientKit/DAO/TransactionDao.swift +40 -42
- package/ios/ZCashLightClientKit/DAO/UnspentTransactionOutputDao.swift +13 -4
- package/ios/ZCashLightClientKit/Entity/AccountEntity.swift +9 -0
- package/ios/ZCashLightClientKit/Entity/TransactionEntity.swift +7 -10
- package/ios/ZCashLightClientKit/Error/Sourcery/generateErrorCode.sh +1 -1
- package/ios/ZCashLightClientKit/Error/ZcashError.swift +121 -12
- package/ios/ZCashLightClientKit/Error/ZcashErrorCode.swift +43 -5
- package/ios/ZCashLightClientKit/Error/ZcashErrorCodeDefinition.swift +72 -6
- package/ios/ZCashLightClientKit/Extensions/Bool+ToData.swift +15 -0
- package/ios/ZCashLightClientKit/Extensions/Data+ToOtherTypes.swift +18 -0
- package/ios/ZCashLightClientKit/Extensions/Int+ToData.swift +15 -0
- package/ios/ZCashLightClientKit/Initializer.swift +47 -26
- package/ios/ZCashLightClientKit/Metrics/SDKMetrics.swift +0 -12
- package/ios/ZCashLightClientKit/Model/Checkpoint.swift +12 -0
- package/ios/ZCashLightClientKit/Model/ScanProgress.swift +29 -0
- package/ios/ZCashLightClientKit/Model/ScanRange.swift +31 -0
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +15 -0
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/compact_formats.pb.swift +150 -46
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto +30 -16
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/service.proto +32 -6
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.grpc.swift +259 -22
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.pb.swift +193 -7
- package/ios/ZCashLightClientKit/Modules/Service/LightWalletService.swift +8 -0
- package/ios/ZCashLightClientKit/Providers/LatestBlocksDataProvider.swift +18 -28
- package/ios/ZCashLightClientKit/Repository/CompactBlockRepository.swift +1 -1
- package/ios/ZCashLightClientKit/Repository/TransactionRepository.swift +2 -6
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2092500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2095000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2097500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2102500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2105000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2107500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2112500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2115000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2117500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2122500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2125000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2127500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2132500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2135000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2137500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2142500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2145000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2147500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2152500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2155000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2157500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2162500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2165000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2167500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2172500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2175000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2177500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2182500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2185000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2187500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2192500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2195000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2197500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2202500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2205000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2207500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2212500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2215000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2217500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2222500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2225000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2227500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2232500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2235000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2237500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2242500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2245000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2247500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2252500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2255000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2257500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2262500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2265000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2267500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2350000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2360000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2370000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2380000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2390000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2400000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2410000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2420000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2430000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2440000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2450000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2460000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2470000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2480000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2490000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2500000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2510000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2520000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2530000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2540000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/testnet/2550000.json +8 -0
- package/ios/ZCashLightClientKit/Rust/ZcashRustBackend.swift +293 -158
- package/ios/ZCashLightClientKit/Rust/ZcashRustBackendWelding.swift +58 -64
- package/ios/ZCashLightClientKit/Rust/zcashlc.h +618 -512
- package/ios/ZCashLightClientKit/Synchronizer/ClosureSDKSynchronizer.swift +2 -8
- package/ios/ZCashLightClientKit/Synchronizer/CombineSDKSynchronizer.swift +3 -15
- package/ios/ZCashLightClientKit/Synchronizer/Dependencies.swift +11 -30
- package/ios/ZCashLightClientKit/Synchronizer/SDKSynchronizer.swift +41 -50
- package/ios/ZCashLightClientKit/Synchronizer.swift +51 -65
- package/ios/ZCashLightClientKit/Transaction/TransactionEncoder.swift +2 -2
- package/ios/ZCashLightClientKit/Transaction/WalletTransactionEncoder.swift +7 -7
- package/ios/ZCashLightClientKit/Utils/OSLogger.swift +3 -3
- package/ios/ZCashLightClientKit/Utils/ZcashFileManager.swift +16 -0
- package/ios/libzcashlc.xcframework/Info.plist +9 -5
- package/ios/libzcashlc.xcframework/ios-arm64/libzcashlc.a +0 -0
- package/ios/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.a +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Action.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
protocol ActionContext {
|
|
11
|
+
var state: CBPState { get async }
|
|
12
|
+
var prevState: CBPState? { get async }
|
|
13
|
+
var syncControlData: SyncControlData { get async }
|
|
14
|
+
var requestedRewindHeight: BlockHeight? { get async }
|
|
15
|
+
var processedHeight: BlockHeight { get async }
|
|
16
|
+
var lastChainTipUpdateTime: TimeInterval { get async }
|
|
17
|
+
var lastScannedHeight: BlockHeight? { get async }
|
|
18
|
+
var lastEnhancedHeight: BlockHeight? { get async }
|
|
19
|
+
|
|
20
|
+
func update(state: CBPState) async
|
|
21
|
+
func update(syncControlData: SyncControlData) async
|
|
22
|
+
func update(processedHeight: BlockHeight) async
|
|
23
|
+
func update(lastChainTipUpdateTime: TimeInterval) async
|
|
24
|
+
func update(lastScannedHeight: BlockHeight) async
|
|
25
|
+
func update(lastDownloadedHeight: BlockHeight) async
|
|
26
|
+
func update(lastEnhancedHeight: BlockHeight?) async
|
|
27
|
+
func update(requestedRewindHeight: BlockHeight) async
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
actor ActionContextImpl: ActionContext {
|
|
31
|
+
var state: CBPState
|
|
32
|
+
var prevState: CBPState?
|
|
33
|
+
var syncControlData: SyncControlData
|
|
34
|
+
var requestedRewindHeight: BlockHeight?
|
|
35
|
+
/// Amount of blocks that have been processed so far
|
|
36
|
+
var processedHeight: BlockHeight = 0
|
|
37
|
+
/// Update chain tip must be called repeatadly, this value stores the previous update and help to decide when to call it again
|
|
38
|
+
var lastChainTipUpdateTime: TimeInterval = 0.0
|
|
39
|
+
var lastScannedHeight: BlockHeight?
|
|
40
|
+
var lastDownloadedHeight: BlockHeight?
|
|
41
|
+
var lastEnhancedHeight: BlockHeight?
|
|
42
|
+
|
|
43
|
+
init(state: CBPState) {
|
|
44
|
+
self.state = state
|
|
45
|
+
syncControlData = SyncControlData.empty
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func update(state: CBPState) async {
|
|
49
|
+
prevState = self.state
|
|
50
|
+
self.state = state
|
|
51
|
+
}
|
|
52
|
+
func update(syncControlData: SyncControlData) async { self.syncControlData = syncControlData }
|
|
53
|
+
func update(processedHeight: BlockHeight) async { self.processedHeight = processedHeight }
|
|
54
|
+
func update(lastChainTipUpdateTime: TimeInterval) async { self.lastChainTipUpdateTime = lastChainTipUpdateTime }
|
|
55
|
+
func update(lastScannedHeight: BlockHeight) async { self.lastScannedHeight = lastScannedHeight }
|
|
56
|
+
func update(lastDownloadedHeight: BlockHeight) async { self.lastDownloadedHeight = lastDownloadedHeight }
|
|
57
|
+
func update(lastEnhancedHeight: BlockHeight?) async { self.lastEnhancedHeight = lastEnhancedHeight }
|
|
58
|
+
func update(requestedRewindHeight: BlockHeight) async { self.requestedRewindHeight = requestedRewindHeight }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
enum CBPState: CaseIterable {
|
|
62
|
+
case idle
|
|
63
|
+
case migrateLegacyCacheDB
|
|
64
|
+
case validateServer
|
|
65
|
+
case updateSubtreeRoots
|
|
66
|
+
case updateChainTip
|
|
67
|
+
case processSuggestedScanRanges
|
|
68
|
+
case rewind
|
|
69
|
+
case download
|
|
70
|
+
case scan
|
|
71
|
+
case clearAlreadyScannedBlocks
|
|
72
|
+
case enhance
|
|
73
|
+
case fetchUTXO
|
|
74
|
+
case handleSaplingParams
|
|
75
|
+
case clearCache
|
|
76
|
+
case finished
|
|
77
|
+
case failed
|
|
78
|
+
case stopped
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protocol Action {
|
|
82
|
+
/// If this is true and action fails with error then blocks cache is cleared.
|
|
83
|
+
var removeBlocksCacheWhenFailed: Bool { get }
|
|
84
|
+
|
|
85
|
+
// When any action is created it can get `DIContainer` and resolve any depedencies it requires.
|
|
86
|
+
// Every action uses `context` to get some informartion like download range.
|
|
87
|
+
//
|
|
88
|
+
// `didUpdate` is closure that action use to tell CBP that some part of the work is done. For example if download action would like to
|
|
89
|
+
// update progress on every block downloaded it can use this closure. Also if action doesn't need to update progress on partial work it doesn't
|
|
90
|
+
// need to use this closure at all.
|
|
91
|
+
//
|
|
92
|
+
// Each action updates context accordingly. It should at least set new state. Reason for this is that action can return different states for
|
|
93
|
+
// different conditions. And action is the thing that knows these conditions.
|
|
94
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext
|
|
95
|
+
|
|
96
|
+
// Should be called on each existing action when processor wants to stop. Some actions may do it's own background work.
|
|
97
|
+
func stop() async
|
|
98
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ClearCacheForLastScannedBatch.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 08.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class ClearAlreadyScannedBlocksAction {
|
|
11
|
+
let storage: CompactBlockRepository
|
|
12
|
+
let transactionRepository: TransactionRepository
|
|
13
|
+
|
|
14
|
+
init(container: DIContainer) {
|
|
15
|
+
storage = container.resolve(CompactBlockRepository.self)
|
|
16
|
+
transactionRepository = container.resolve(TransactionRepository.self)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
extension ClearAlreadyScannedBlocksAction: Action {
|
|
21
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
22
|
+
|
|
23
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
24
|
+
guard let lastScannedHeight = await context.lastScannedHeight else {
|
|
25
|
+
throw ZcashError.compactBlockProcessorLastScannedHeight
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try await storage.clear(upTo: lastScannedHeight)
|
|
29
|
+
|
|
30
|
+
await context.update(state: .enhance)
|
|
31
|
+
return context
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func stop() async { }
|
|
35
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ClearCacheAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class ClearCacheAction {
|
|
11
|
+
let storage: CompactBlockRepository
|
|
12
|
+
|
|
13
|
+
init(container: DIContainer) {
|
|
14
|
+
storage = container.resolve(CompactBlockRepository.self)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
extension ClearCacheAction: Action {
|
|
19
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
20
|
+
|
|
21
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
22
|
+
try await storage.clear()
|
|
23
|
+
|
|
24
|
+
await context.update(state: .processSuggestedScanRanges)
|
|
25
|
+
|
|
26
|
+
return context
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func stop() async { }
|
|
30
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// DownloadAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class DownloadAction {
|
|
11
|
+
let configProvider: CompactBlockProcessor.ConfigProvider
|
|
12
|
+
let downloader: BlockDownloader
|
|
13
|
+
let transactionRepository: TransactionRepository
|
|
14
|
+
let logger: Logger
|
|
15
|
+
|
|
16
|
+
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
|
17
|
+
self.configProvider = configProvider
|
|
18
|
+
downloader = container.resolve(BlockDownloader.self)
|
|
19
|
+
transactionRepository = container.resolve(TransactionRepository.self)
|
|
20
|
+
logger = container.resolve(Logger.self)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private func update(context: ActionContext) async -> ActionContext {
|
|
24
|
+
await context.update(state: .scan)
|
|
25
|
+
return context
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
extension DownloadAction: Action {
|
|
30
|
+
var removeBlocksCacheWhenFailed: Bool { true }
|
|
31
|
+
|
|
32
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
33
|
+
guard let lastScannedHeight = await context.lastScannedHeight else {
|
|
34
|
+
return await update(context: context)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let config = await configProvider.config
|
|
38
|
+
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
|
39
|
+
// This action is executed for each batch (batch size is 100 blocks by default) until all the blocks in whole `downloadRange` are downloaded.
|
|
40
|
+
// So the right range for this batch must be computed.
|
|
41
|
+
let batchRangeStart = lastScannedHeight
|
|
42
|
+
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
|
43
|
+
|
|
44
|
+
guard batchRangeStart <= batchRangeEnd else {
|
|
45
|
+
return await update(context: context)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let batchRange = batchRangeStart...batchRangeEnd
|
|
49
|
+
let potentialDownloadLimit = batchRange.upperBound + (2 * config.batchSize)
|
|
50
|
+
let downloadLimit = await context.syncControlData.latestBlockHeight >= potentialDownloadLimit ? potentialDownloadLimit : batchRangeEnd
|
|
51
|
+
|
|
52
|
+
logger.debug("Starting download with range: \(batchRangeStart)...\(batchRangeEnd)")
|
|
53
|
+
|
|
54
|
+
await downloader.update(latestDownloadedBlockHeight: batchRangeStart, force: true)
|
|
55
|
+
try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
|
|
56
|
+
await downloader.setDownloadLimit(downloadLimit)
|
|
57
|
+
await downloader.startDownload(maxBlockBufferSize: config.downloadBufferSize)
|
|
58
|
+
|
|
59
|
+
try await downloader.waitUntilRequestedBlocksAreDownloaded(in: batchRange)
|
|
60
|
+
|
|
61
|
+
return await update(context: context)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func stop() async {
|
|
65
|
+
await downloader.stopDownload()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
//
|
|
2
|
+
// EnhanceAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class EnhanceAction {
|
|
11
|
+
let blockEnhancer: BlockEnhancer
|
|
12
|
+
let configProvider: CompactBlockProcessor.ConfigProvider
|
|
13
|
+
let logger: Logger
|
|
14
|
+
|
|
15
|
+
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
|
16
|
+
blockEnhancer = container.resolve(BlockEnhancer.self)
|
|
17
|
+
self.configProvider = configProvider
|
|
18
|
+
logger = container.resolve(Logger.self)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
|
22
|
+
guard await context.syncControlData.latestScannedHeight != nil else {
|
|
23
|
+
await context.update(state: .clearCache)
|
|
24
|
+
return context
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
|
28
|
+
if lastScannedHeight >= latestBlockHeight {
|
|
29
|
+
await context.update(state: .clearCache)
|
|
30
|
+
} else {
|
|
31
|
+
await context.update(state: .updateChainTip)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return context
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
extension EnhanceAction: Action {
|
|
39
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
40
|
+
|
|
41
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
42
|
+
// Use `BlockEnhancer` to enhance blocks.
|
|
43
|
+
// This action is executed on each downloaded and scanned batch (typically each 100 blocks). But we want to run enhancement each 1000 blocks.
|
|
44
|
+
|
|
45
|
+
// if latestScannedHeight >= context.scanRanges.scanRange.upperBound then everything is processed and sync process should continue to end.
|
|
46
|
+
// If latestScannedHeight < context.scanRanges.scanRange.upperBound then set state to `download` because there are blocks to
|
|
47
|
+
// download and scan.
|
|
48
|
+
|
|
49
|
+
let config = await configProvider.config
|
|
50
|
+
guard let lastScannedHeight = await context.lastScannedHeight else {
|
|
51
|
+
throw ZcashError.compactBlockProcessorLastScannedHeight
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
guard let firstUnenhancedHeight = await context.syncControlData.firstUnenhancedHeight else {
|
|
55
|
+
return await decideWhatToDoNext(context: context, lastScannedHeight: lastScannedHeight)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
|
59
|
+
let lastEnhancedHeight: BlockHeight
|
|
60
|
+
if let lastEnhancedHeightInContext = await context.lastEnhancedHeight {
|
|
61
|
+
lastEnhancedHeight = lastEnhancedHeightInContext
|
|
62
|
+
} else {
|
|
63
|
+
lastEnhancedHeight = -1
|
|
64
|
+
}
|
|
65
|
+
let enhanceRangeStart = max(firstUnenhancedHeight, lastEnhancedHeight + 1)
|
|
66
|
+
let enhanceRangeEnd = min(latestBlockHeight, lastScannedHeight)
|
|
67
|
+
|
|
68
|
+
// This may happen:
|
|
69
|
+
// For example whole enhance range is 0...2100 Without this force enhance is done for ranges: 0...1000, 1001...2000. And that's it.
|
|
70
|
+
// Last 100 blocks isn't enhanced.
|
|
71
|
+
//
|
|
72
|
+
// This force makes sure that all the blocks are enhanced even when last enhance happened < 1000 blocks ago.
|
|
73
|
+
let forceEnhance = enhanceRangeEnd == latestBlockHeight && enhanceRangeEnd - enhanceRangeStart <= config.enhanceBatchSize
|
|
74
|
+
|
|
75
|
+
if enhanceRangeStart <= enhanceRangeEnd && (forceEnhance || (lastScannedHeight - lastEnhancedHeight >= config.enhanceBatchSize)) {
|
|
76
|
+
let enhanceRange = enhanceRangeStart...enhanceRangeEnd
|
|
77
|
+
let transactions = try await blockEnhancer.enhance(
|
|
78
|
+
at: enhanceRange,
|
|
79
|
+
didEnhance: { progress in
|
|
80
|
+
if let foundTx = progress.lastFoundTransaction, progress.newlyMined {
|
|
81
|
+
await didUpdate(.minedTransaction(foundTx))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
await context.update(lastEnhancedHeight: enhanceRange.upperBound)
|
|
87
|
+
|
|
88
|
+
if let transactions {
|
|
89
|
+
await didUpdate(.foundTransactions(transactions, enhanceRange))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return await decideWhatToDoNext(context: context, lastScannedHeight: lastScannedHeight)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func stop() async { }
|
|
97
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FetchUTXOsAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class FetchUTXOsAction {
|
|
11
|
+
let utxoFetcher: UTXOFetcher
|
|
12
|
+
let logger: Logger
|
|
13
|
+
|
|
14
|
+
init(container: DIContainer) {
|
|
15
|
+
utxoFetcher = container.resolve(UTXOFetcher.self)
|
|
16
|
+
logger = container.resolve(Logger.self)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
extension FetchUTXOsAction: Action {
|
|
21
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
22
|
+
|
|
23
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
24
|
+
logger.debug("Fetching UTXOs")
|
|
25
|
+
let result = try await utxoFetcher.fetch() { _ in }
|
|
26
|
+
await didUpdate(.storedUTXOs(result))
|
|
27
|
+
|
|
28
|
+
await context.update(state: .handleSaplingParams)
|
|
29
|
+
return context
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func stop() async { }
|
|
33
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MigrateLegacyCacheDB.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 10.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class MigrateLegacyCacheDBAction {
|
|
11
|
+
private let configProvider: CompactBlockProcessor.ConfigProvider
|
|
12
|
+
private let storage: CompactBlockRepository
|
|
13
|
+
private let transactionRepository: TransactionRepository
|
|
14
|
+
private let fileManager: ZcashFileManager
|
|
15
|
+
|
|
16
|
+
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
|
17
|
+
self.configProvider = configProvider
|
|
18
|
+
storage = container.resolve(CompactBlockRepository.self)
|
|
19
|
+
transactionRepository = container.resolve(TransactionRepository.self)
|
|
20
|
+
fileManager = container.resolve(ZcashFileManager.self)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private func updateState(_ context: ActionContext) async -> ActionContext {
|
|
24
|
+
await context.update(state: .validateServer)
|
|
25
|
+
return context
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
extension MigrateLegacyCacheDBAction: Action {
|
|
30
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
31
|
+
|
|
32
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
33
|
+
let config = await configProvider.config
|
|
34
|
+
guard let legacyCacheDbURL = config.cacheDbURL else {
|
|
35
|
+
return await updateState(context)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
guard legacyCacheDbURL != config.fsBlockCacheRoot else {
|
|
39
|
+
throw ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Instance with alias `default` is same as instance before the Alias was introduced. So it makes sense that only this instance handles
|
|
43
|
+
// legacy cache DB. Any instance with different than `default` alias was created after the Alias was introduced and at this point legacy
|
|
44
|
+
// cache DB doesn't exist anymore. So there is nothing to migrate for instances with not default Alias.
|
|
45
|
+
guard config.alias == .default else {
|
|
46
|
+
return await updateState(context)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// if the URL provided is not readable, it means that the client has a reference
|
|
50
|
+
// to the cacheDb file but it has been deleted in a prior sync cycle. there's
|
|
51
|
+
// nothing to do here.
|
|
52
|
+
guard fileManager.isReadableFile(atPath: legacyCacheDbURL.path) else {
|
|
53
|
+
return await updateState(context)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
do {
|
|
57
|
+
// if there's a readable file at the provided URL, delete it.
|
|
58
|
+
try fileManager.removeItem(at: legacyCacheDbURL)
|
|
59
|
+
} catch {
|
|
60
|
+
throw ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(error)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// create the storage
|
|
64
|
+
try await self.storage.create()
|
|
65
|
+
|
|
66
|
+
return await updateState(context)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func stop() { }
|
|
70
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ProcessSuggestedScanRangesAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Lukáš Korba on 02.08.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class ProcessSuggestedScanRangesAction {
|
|
11
|
+
let rustBackend: ZcashRustBackendWelding
|
|
12
|
+
let service: LightWalletService
|
|
13
|
+
let logger: Logger
|
|
14
|
+
|
|
15
|
+
init(container: DIContainer) {
|
|
16
|
+
service = container.resolve(LightWalletService.self)
|
|
17
|
+
rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
|
18
|
+
logger = container.resolve(Logger.self)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
extension ProcessSuggestedScanRangesAction: Action {
|
|
23
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
24
|
+
|
|
25
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
26
|
+
logger.info("Getting the suggested scan ranges from the wallet database.")
|
|
27
|
+
let scanRanges = try await rustBackend.suggestScanRanges()
|
|
28
|
+
|
|
29
|
+
if let firstRange = scanRanges.first {
|
|
30
|
+
let rangeStartExclusive = firstRange.range.lowerBound - 1
|
|
31
|
+
let rangeEndInclusive = firstRange.range.upperBound - 1
|
|
32
|
+
|
|
33
|
+
let syncControlData = SyncControlData(
|
|
34
|
+
latestBlockHeight: rangeEndInclusive,
|
|
35
|
+
latestScannedHeight: rangeStartExclusive,
|
|
36
|
+
firstUnenhancedHeight: rangeStartExclusive + 1
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
logger.debug("""
|
|
40
|
+
Init numbers:
|
|
41
|
+
latestBlockHeight [BC]: \(rangeEndInclusive)
|
|
42
|
+
latestScannedHeight [DB]: \(rangeStartExclusive)
|
|
43
|
+
firstUnenhancedHeight [DB]: \(rangeStartExclusive + 1)
|
|
44
|
+
""")
|
|
45
|
+
|
|
46
|
+
await context.update(lastEnhancedHeight: nil)
|
|
47
|
+
await context.update(lastScannedHeight: rangeStartExclusive)
|
|
48
|
+
await context.update(lastDownloadedHeight: rangeStartExclusive)
|
|
49
|
+
await context.update(syncControlData: syncControlData)
|
|
50
|
+
|
|
51
|
+
await context.update(state: .download)
|
|
52
|
+
} else {
|
|
53
|
+
await context.update(state: .finished)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return context
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func stop() async { }
|
|
60
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RewindAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Lukáš Korba on 09.08.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class RewindAction {
|
|
11
|
+
let downloader: BlockDownloader
|
|
12
|
+
let rustBackend: ZcashRustBackendWelding
|
|
13
|
+
let downloaderService: BlockDownloaderService
|
|
14
|
+
let logger: Logger
|
|
15
|
+
|
|
16
|
+
init(container: DIContainer) {
|
|
17
|
+
downloader = container.resolve(BlockDownloader.self)
|
|
18
|
+
rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
|
19
|
+
downloaderService = container.resolve(BlockDownloaderService.self)
|
|
20
|
+
logger = container.resolve(Logger.self)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private func update(context: ActionContext) async -> ActionContext {
|
|
24
|
+
await context.update(state: .processSuggestedScanRanges)
|
|
25
|
+
return context
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
extension RewindAction: Action {
|
|
30
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
31
|
+
|
|
32
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
33
|
+
guard let rewindHeight = await context.requestedRewindHeight else {
|
|
34
|
+
return await update(context: context)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
logger.debug("Executing rewind.")
|
|
38
|
+
await downloader.rewind(latestDownloadedBlockHeight: rewindHeight)
|
|
39
|
+
try await rustBackend.rewindToHeight(height: Int32(rewindHeight))
|
|
40
|
+
|
|
41
|
+
// clear cache
|
|
42
|
+
try await downloaderService.rewind(to: rewindHeight)
|
|
43
|
+
|
|
44
|
+
return await update(context: context)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func stop() async { }
|
|
48
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// SaplingParamsAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class SaplingParamsAction {
|
|
11
|
+
let saplingParametersHandler: SaplingParametersHandler
|
|
12
|
+
let logger: Logger
|
|
13
|
+
|
|
14
|
+
init(container: DIContainer) {
|
|
15
|
+
saplingParametersHandler = container.resolve(SaplingParametersHandler.self)
|
|
16
|
+
logger = container.resolve(Logger.self)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
extension SaplingParamsAction: Action {
|
|
21
|
+
var removeBlocksCacheWhenFailed: Bool { false }
|
|
22
|
+
|
|
23
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
24
|
+
logger.debug("Fetching sapling parameters")
|
|
25
|
+
try await saplingParametersHandler.handleIfNeeded()
|
|
26
|
+
|
|
27
|
+
await context.update(state: .updateSubtreeRoots)
|
|
28
|
+
|
|
29
|
+
return context
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func stop() async { }
|
|
33
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ScanAction.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Michal Fousek on 05.05.2023.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
final class ScanAction {
|
|
11
|
+
let configProvider: CompactBlockProcessor.ConfigProvider
|
|
12
|
+
let blockScanner: BlockScanner
|
|
13
|
+
let rustBackend: ZcashRustBackendWelding
|
|
14
|
+
let latestBlocksDataProvider: LatestBlocksDataProvider
|
|
15
|
+
let logger: Logger
|
|
16
|
+
|
|
17
|
+
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
|
18
|
+
self.configProvider = configProvider
|
|
19
|
+
blockScanner = container.resolve(BlockScanner.self)
|
|
20
|
+
rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
|
21
|
+
latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
|
|
22
|
+
logger = container.resolve(Logger.self)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private func update(context: ActionContext) async -> ActionContext {
|
|
26
|
+
await context.update(state: .clearAlreadyScannedBlocks)
|
|
27
|
+
return context
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
extension ScanAction: Action {
|
|
32
|
+
var removeBlocksCacheWhenFailed: Bool { true }
|
|
33
|
+
|
|
34
|
+
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
|
35
|
+
guard let lastScannedHeight = await context.lastScannedHeight else {
|
|
36
|
+
return await update(context: context)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let config = await configProvider.config
|
|
40
|
+
let latestBlockHeight = await context.syncControlData.latestBlockHeight
|
|
41
|
+
// This action is executed for each batch (batch size is 100 blocks by default) until all the blocks in whole `scanRange` are scanned.
|
|
42
|
+
// So the right range for this batch must be computed.
|
|
43
|
+
let batchRangeStart = lastScannedHeight
|
|
44
|
+
let batchRangeEnd = min(latestBlockHeight, batchRangeStart + config.batchSize)
|
|
45
|
+
|
|
46
|
+
guard batchRangeStart <= batchRangeEnd else {
|
|
47
|
+
return await update(context: context)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let batchRange = batchRangeStart...batchRangeEnd
|
|
51
|
+
|
|
52
|
+
logger.debug("Starting scan blocks with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
|
53
|
+
|
|
54
|
+
do {
|
|
55
|
+
try await blockScanner.scanBlocks(at: batchRange) { [weak self] lastScannedHeight, increment in
|
|
56
|
+
let processedHeight = await context.processedHeight
|
|
57
|
+
let incrementedprocessedHeight = processedHeight + BlockHeight(increment)
|
|
58
|
+
await context.update(processedHeight: incrementedprocessedHeight)
|
|
59
|
+
await self?.latestBlocksDataProvider.updateScannedData()
|
|
60
|
+
|
|
61
|
+
// report scan progress only if it's available
|
|
62
|
+
if let scanProgress = try? await self?.rustBackend.getScanProgress() {
|
|
63
|
+
let progress = try scanProgress.progress()
|
|
64
|
+
self?.logger.debug("progress: \(progress)")
|
|
65
|
+
await didUpdate(.syncProgress(progress))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ScanAction is controlled locally so it must report back the updated scanned height
|
|
69
|
+
await context.update(lastScannedHeight: lastScannedHeight)
|
|
70
|
+
}
|
|
71
|
+
} catch ZcashError.rustScanBlocks(let errorMsg) {
|
|
72
|
+
if isContinuityError(errorMsg) {
|
|
73
|
+
await context.update(requestedRewindHeight: batchRange.lowerBound - 10)
|
|
74
|
+
await context.update(state: .rewind)
|
|
75
|
+
return context
|
|
76
|
+
} else {
|
|
77
|
+
throw ZcashError.rustScanBlocks(errorMsg)
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
throw error
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return await update(context: context)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func stop() async { }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private extension ScanAction {
|
|
90
|
+
func isContinuityError(_ errorMsg: String) -> Bool {
|
|
91
|
+
errorMsg.contains("The parent hash of proposed block does not correspond to the block hash at height")
|
|
92
|
+
|| errorMsg.contains("Block height discontinuity at height")
|
|
93
|
+
|| errorMsg.contains("note commitment tree size provided by a compact block did not match the expected size at height")
|
|
94
|
+
}
|
|
95
|
+
}
|