react-native-zcash 0.5.0 → 0.6.0
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 +17 -0
- package/README.md +6 -3
- package/android/build.gradle +5 -4
- package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2240000.json +8 -0
- package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2250000.json +8 -0
- package/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt +94 -121
- package/ios/RNZcash.m +1 -12
- package/ios/RNZcash.swift +119 -152
- 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 +59 -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 +421 -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 +7 -25
- 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/2230000.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/2240000.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/2250000.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/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/lib/rnzcash.rn.js +8 -30
- package/lib/rnzcash.rn.js.map +1 -1
- package/lib/src/react-native.d.ts +2 -4
- package/lib/src/types.d.ts +14 -14
- package/package.json +1 -1
- package/src/react-native.ts +11 -20
- package/src/types.ts +20 -23
- package/ios/ZCashLightClientKit/Block/Utils/InternalSyncProgress.swift +0 -200
- package/ios/ZCashLightClientKit/Block/Validate/BlockValidator.swift +0 -51
- package/ios/ZCashLightClientKit/DAO/BlockDao.swift +0 -112
- package/ios/ZCashLightClientKit/Entity/BlockProgress.swift +0 -24
|
@@ -5,144 +5,46 @@
|
|
|
5
5
|
// Created by Francisco Gindre on 18/09/2019.
|
|
6
6
|
// Copyright © 2019 Electric Coin Company. All rights reserved.
|
|
7
7
|
//
|
|
8
|
-
// swiftlint:disable file_length type_body_length
|
|
9
8
|
|
|
10
9
|
import Foundation
|
|
11
10
|
import Combine
|
|
12
11
|
|
|
13
12
|
public typealias RefreshedUTXOs = (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])
|
|
14
13
|
|
|
15
|
-
public enum CompactBlockProgress {
|
|
16
|
-
case syncing(_ progress: BlockProgress)
|
|
17
|
-
case enhance(_ progress: EnhancementProgress)
|
|
18
|
-
case fetch(_ progress: Float)
|
|
19
|
-
|
|
20
|
-
public var progress: Float {
|
|
21
|
-
switch self {
|
|
22
|
-
case .syncing(let blockProgress):
|
|
23
|
-
return blockProgress.progress
|
|
24
|
-
case .enhance(let enhancementProgress):
|
|
25
|
-
return enhancementProgress.progress
|
|
26
|
-
case .fetch(let fetchingProgress):
|
|
27
|
-
return fetchingProgress
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
public var progressHeight: BlockHeight? {
|
|
32
|
-
switch self {
|
|
33
|
-
case .syncing(let blockProgress):
|
|
34
|
-
return blockProgress.progressHeight
|
|
35
|
-
case .enhance(let enhancementProgress):
|
|
36
|
-
return enhancementProgress.lastFoundTransaction?.minedHeight
|
|
37
|
-
default:
|
|
38
|
-
return 0
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public var blockDate: Date? {
|
|
43
|
-
if case .enhance(let enhancementProgress) = self, let time = enhancementProgress.lastFoundTransaction?.blockTime {
|
|
44
|
-
return Date(timeIntervalSince1970: time)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return nil
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public var targetHeight: BlockHeight? {
|
|
51
|
-
switch self {
|
|
52
|
-
case .syncing(let blockProgress):
|
|
53
|
-
return blockProgress.targetHeight
|
|
54
|
-
default:
|
|
55
|
-
return nil
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public struct EnhancementProgress: Equatable {
|
|
61
|
-
/// total transactions that were detected in the `range`
|
|
62
|
-
public let totalTransactions: Int
|
|
63
|
-
/// enhanced transactions so far
|
|
64
|
-
public let enhancedTransactions: Int
|
|
65
|
-
/// last found transaction
|
|
66
|
-
public let lastFoundTransaction: ZcashTransaction.Overview?
|
|
67
|
-
/// block range that's being enhanced
|
|
68
|
-
public let range: CompactBlockRange
|
|
69
|
-
/// whether this transaction can be considered `newly mined` and not part of the
|
|
70
|
-
/// wallet catching up to stale and uneventful blocks.
|
|
71
|
-
public let newlyMined: Bool
|
|
72
|
-
|
|
73
|
-
public init(
|
|
74
|
-
totalTransactions: Int,
|
|
75
|
-
enhancedTransactions: Int,
|
|
76
|
-
lastFoundTransaction: ZcashTransaction.Overview?,
|
|
77
|
-
range: CompactBlockRange,
|
|
78
|
-
newlyMined: Bool
|
|
79
|
-
) {
|
|
80
|
-
self.totalTransactions = totalTransactions
|
|
81
|
-
self.enhancedTransactions = enhancedTransactions
|
|
82
|
-
self.lastFoundTransaction = lastFoundTransaction
|
|
83
|
-
self.range = range
|
|
84
|
-
self.newlyMined = newlyMined
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public var progress: Float {
|
|
88
|
-
totalTransactions > 0 ? Float(enhancedTransactions) / Float(totalTransactions) : 0
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
public static var zero: EnhancementProgress {
|
|
92
|
-
EnhancementProgress(totalTransactions: 0, enhancedTransactions: 0, lastFoundTransaction: nil, range: 0...0, newlyMined: false)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public static func == (lhs: EnhancementProgress, rhs: EnhancementProgress) -> Bool {
|
|
96
|
-
return
|
|
97
|
-
lhs.totalTransactions == rhs.totalTransactions &&
|
|
98
|
-
lhs.enhancedTransactions == rhs.enhancedTransactions &&
|
|
99
|
-
lhs.lastFoundTransaction?.id == rhs.lastFoundTransaction?.id &&
|
|
100
|
-
lhs.range == rhs.range
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
14
|
/// The compact block processor is in charge of orchestrating the download and caching of compact blocks from a LightWalletEndpoint
|
|
105
15
|
/// when started the processor downloads does a download - validate - scan cycle until it reaches latest height on the blockchain.
|
|
106
16
|
actor CompactBlockProcessor {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
case failed (Error)
|
|
112
|
-
|
|
113
|
-
/// Event sent when the CompactBlockProcessor has finished syncing the blockchain to latest height
|
|
114
|
-
case finished (_ lastScannedHeight: BlockHeight, _ foundBlocks: Bool)
|
|
115
|
-
|
|
116
|
-
/// Event sent when the CompactBlockProcessor found a newly mined transaction
|
|
117
|
-
case minedTransaction(ZcashTransaction.Overview)
|
|
118
|
-
|
|
119
|
-
/// Event sent when the CompactBlockProcessor enhanced a bunch of transactions in some range.
|
|
120
|
-
case foundTransactions ([ZcashTransaction.Overview], CompactBlockRange)
|
|
121
|
-
|
|
122
|
-
/// Event sent when the CompactBlockProcessor handled a ReOrg.
|
|
123
|
-
/// `reorgHeight` is the height on which the reorg was detected.
|
|
124
|
-
/// `rewindHeight` is the height that the processor backed to in order to solve the Reorg.
|
|
125
|
-
case handledReorg (_ reorgHeight: BlockHeight, _ rewindHeight: BlockHeight)
|
|
126
|
-
|
|
127
|
-
/// Event sent when progress of the sync process changes.
|
|
128
|
-
case progressUpdated (CompactBlockProgress)
|
|
129
|
-
|
|
130
|
-
/// Event sent when the CompactBlockProcessor fetched utxos from lightwalletd attempted to store them.
|
|
131
|
-
case storedUTXOs ((inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]))
|
|
17
|
+
// It would be better to use Combine here but Combine doesn't work great with async. When this runs regularly only one closure is stored here
|
|
18
|
+
// and that is one provided by `SDKSynchronizer`. But while running tests more "subscribers" is required here. Therefore it's required to handle
|
|
19
|
+
// more closures here.
|
|
20
|
+
private var eventClosures: [String: EventClosure] = [:]
|
|
132
21
|
|
|
133
|
-
|
|
134
|
-
case startedEnhancing
|
|
22
|
+
private var syncTask: Task<Void, Error>?
|
|
135
23
|
|
|
136
|
-
|
|
137
|
-
|
|
24
|
+
private let actions: [CBPState: Action]
|
|
25
|
+
private var context: ActionContext
|
|
138
26
|
|
|
139
|
-
|
|
140
|
-
|
|
27
|
+
private(set) var config: Configuration
|
|
28
|
+
private let configProvider: ConfigProvider
|
|
29
|
+
private var afterSyncHooksManager = AfterSyncHooksManager()
|
|
141
30
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
31
|
+
private let accountRepository: AccountRepository
|
|
32
|
+
let blockDownloaderService: BlockDownloaderService
|
|
33
|
+
private let latestBlocksDataProvider: LatestBlocksDataProvider
|
|
34
|
+
private let logger: Logger
|
|
35
|
+
private let metrics: SDKMetrics
|
|
36
|
+
private let rustBackend: ZcashRustBackendWelding
|
|
37
|
+
let service: LightWalletService
|
|
38
|
+
let storage: CompactBlockRepository
|
|
39
|
+
private let transactionRepository: TransactionRepository
|
|
40
|
+
private let fileManager: ZcashFileManager
|
|
145
41
|
|
|
42
|
+
private var retryAttempts: Int = 0
|
|
43
|
+
private var backoffTimer: Timer?
|
|
44
|
+
private var consecutiveChainValidationErrors: Int = 0
|
|
45
|
+
|
|
46
|
+
private var compactBlockProgress: CompactBlockProgress = .zero
|
|
47
|
+
|
|
146
48
|
/// Compact Block Processor configuration
|
|
147
49
|
///
|
|
148
50
|
/// - parameter fsBlockCacheRoot: absolute root path where the filesystem block cache will be stored.
|
|
@@ -156,8 +58,8 @@ actor CompactBlockProcessor {
|
|
|
156
58
|
let dataDb: URL
|
|
157
59
|
let spendParamsURL: URL
|
|
158
60
|
let outputParamsURL: URL
|
|
159
|
-
let
|
|
160
|
-
let
|
|
61
|
+
let enhanceBatchSize: Int
|
|
62
|
+
let batchSize: Int
|
|
161
63
|
let retries: Int
|
|
162
64
|
let maxBackoffInterval: TimeInterval
|
|
163
65
|
let maxReorgSize = ZcashSDK.maxReorgSize
|
|
@@ -171,7 +73,7 @@ actor CompactBlockProcessor {
|
|
|
171
73
|
var blockPollInterval: TimeInterval {
|
|
172
74
|
TimeInterval.random(in: ZcashSDK.defaultPollInterval / 2 ... ZcashSDK.defaultPollInterval * 1.5)
|
|
173
75
|
}
|
|
174
|
-
|
|
76
|
+
|
|
175
77
|
init(
|
|
176
78
|
alias: ZcashSynchronizerAlias,
|
|
177
79
|
cacheDbURL: URL? = nil,
|
|
@@ -180,11 +82,11 @@ actor CompactBlockProcessor {
|
|
|
180
82
|
spendParamsURL: URL,
|
|
181
83
|
outputParamsURL: URL,
|
|
182
84
|
saplingParamsSourceURL: SaplingParamsSourceURL,
|
|
183
|
-
|
|
85
|
+
enhanceBatchSize: Int = ZcashSDK.DefaultEnhanceBatch,
|
|
86
|
+
batchSize: Int = ZcashSDK.DefaultBatchSize,
|
|
184
87
|
retries: Int = ZcashSDK.defaultRetries,
|
|
185
88
|
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
|
|
186
89
|
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
|
|
187
|
-
scanningBatchSize: Int = ZcashSDK.DefaultScanningBatch,
|
|
188
90
|
walletBirthdayProvider: @escaping () -> BlockHeight,
|
|
189
91
|
saplingActivation: BlockHeight,
|
|
190
92
|
network: ZcashNetwork
|
|
@@ -196,17 +98,16 @@ actor CompactBlockProcessor {
|
|
|
196
98
|
self.outputParamsURL = outputParamsURL
|
|
197
99
|
self.saplingParamsSourceURL = saplingParamsSourceURL
|
|
198
100
|
self.network = network
|
|
199
|
-
self.
|
|
101
|
+
self.enhanceBatchSize = enhanceBatchSize
|
|
102
|
+
self.batchSize = batchSize
|
|
200
103
|
self.retries = retries
|
|
201
104
|
self.maxBackoffInterval = maxBackoffInterval
|
|
202
105
|
self.rewindDistance = rewindDistance
|
|
203
|
-
self.scanningBatchSize = scanningBatchSize
|
|
204
106
|
self.walletBirthdayProvider = walletBirthdayProvider
|
|
205
107
|
self.saplingActivation = saplingActivation
|
|
206
108
|
self.cacheDbURL = cacheDbURL
|
|
207
|
-
assert(downloadBatchSize >= scanningBatchSize)
|
|
208
109
|
}
|
|
209
|
-
|
|
110
|
+
|
|
210
111
|
init(
|
|
211
112
|
alias: ZcashSynchronizerAlias,
|
|
212
113
|
fsBlockCacheRoot: URL,
|
|
@@ -214,11 +115,11 @@ actor CompactBlockProcessor {
|
|
|
214
115
|
spendParamsURL: URL,
|
|
215
116
|
outputParamsURL: URL,
|
|
216
117
|
saplingParamsSourceURL: SaplingParamsSourceURL,
|
|
217
|
-
|
|
118
|
+
enhanceBatchSize: Int = ZcashSDK.DefaultEnhanceBatch,
|
|
119
|
+
batchSize: Int = ZcashSDK.DefaultBatchSize,
|
|
218
120
|
retries: Int = ZcashSDK.defaultRetries,
|
|
219
121
|
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
|
|
220
122
|
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
|
|
221
|
-
scanningBatchSize: Int = ZcashSDK.DefaultScanningBatch,
|
|
222
123
|
walletBirthdayProvider: @escaping () -> BlockHeight,
|
|
223
124
|
network: ZcashNetwork
|
|
224
125
|
) {
|
|
@@ -232,124 +133,21 @@ actor CompactBlockProcessor {
|
|
|
232
133
|
self.saplingActivation = network.constants.saplingActivationHeight
|
|
233
134
|
self.network = network
|
|
234
135
|
self.cacheDbURL = nil
|
|
235
|
-
self.
|
|
136
|
+
self.enhanceBatchSize = enhanceBatchSize
|
|
137
|
+
self.batchSize = batchSize
|
|
236
138
|
self.retries = retries
|
|
237
139
|
self.maxBackoffInterval = maxBackoffInterval
|
|
238
140
|
self.rewindDistance = rewindDistance
|
|
239
|
-
self.scanningBatchSize = scanningBatchSize
|
|
240
|
-
|
|
241
|
-
assert(downloadBatchSize >= scanningBatchSize)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
Represents the possible states of a CompactBlockProcessor
|
|
247
|
-
*/
|
|
248
|
-
enum State {
|
|
249
|
-
/**
|
|
250
|
-
connected and downloading blocks
|
|
251
|
-
*/
|
|
252
|
-
case syncing
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
was doing something but was paused
|
|
256
|
-
*/
|
|
257
|
-
case stopped
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
Processor is Enhancing transactions
|
|
261
|
-
*/
|
|
262
|
-
case enhancing
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
fetching utxos
|
|
266
|
-
*/
|
|
267
|
-
case fetching
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
was processing but erred
|
|
271
|
-
*/
|
|
272
|
-
case error(_ error: Error)
|
|
273
|
-
|
|
274
|
-
/// Download sapling param files if needed.
|
|
275
|
-
case handlingSaplingFiles
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
Processor is up to date with the blockchain and you can now make transactions.
|
|
279
|
-
*/
|
|
280
|
-
case synced
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private var afterSyncHooksManager = AfterSyncHooksManager()
|
|
284
|
-
|
|
285
|
-
let metrics: SDKMetrics
|
|
286
|
-
let logger: Logger
|
|
287
|
-
|
|
288
|
-
/// Don't update this variable directly. Use `updateState()` method.
|
|
289
|
-
var state: State = .stopped
|
|
290
|
-
|
|
291
|
-
private(set) var config: Configuration
|
|
292
|
-
|
|
293
|
-
var maxAttemptsReached: Bool {
|
|
294
|
-
self.retryAttempts >= self.config.retries
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
var shouldStart: Bool {
|
|
298
|
-
switch self.state {
|
|
299
|
-
case .stopped, .synced, .error:
|
|
300
|
-
return !maxAttemptsReached
|
|
301
|
-
default:
|
|
302
|
-
return false
|
|
303
141
|
}
|
|
304
142
|
}
|
|
305
143
|
|
|
306
|
-
// It would be better to use Combine here but Combine doesn't work great with async. When this runs regularly only one closure is stored here
|
|
307
|
-
// and that is one provided by `SDKSynchronizer`. But while running tests more "subscribers" is required here. Therefore it's required to handle
|
|
308
|
-
// more closures here.
|
|
309
|
-
var eventClosures: [String: EventClosure] = [:]
|
|
310
|
-
|
|
311
|
-
let blockDownloaderService: BlockDownloaderService
|
|
312
|
-
let blockDownloader: BlockDownloader
|
|
313
|
-
let blockValidator: BlockValidator
|
|
314
|
-
let blockScanner: BlockScanner
|
|
315
|
-
let blockEnhancer: BlockEnhancer
|
|
316
|
-
let utxoFetcher: UTXOFetcher
|
|
317
|
-
let saplingParametersHandler: SaplingParametersHandler
|
|
318
|
-
private let latestBlocksDataProvider: LatestBlocksDataProvider
|
|
319
|
-
|
|
320
|
-
let service: LightWalletService
|
|
321
|
-
let storage: CompactBlockRepository
|
|
322
|
-
let transactionRepository: TransactionRepository
|
|
323
|
-
let accountRepository: AccountRepository
|
|
324
|
-
let rustBackend: ZcashRustBackendWelding
|
|
325
|
-
private var retryAttempts: Int = 0
|
|
326
|
-
private var backoffTimer: Timer?
|
|
327
|
-
private var lastChainValidationFailure: BlockHeight?
|
|
328
|
-
private var consecutiveChainValidationErrors: Int = 0
|
|
329
|
-
var processingError: Error?
|
|
330
|
-
private var foundBlocks = false
|
|
331
|
-
private var maxAttempts: Int {
|
|
332
|
-
config.retries
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
var batchSize: BlockHeight {
|
|
336
|
-
BlockHeight(self.config.downloadBatchSize)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
private var cancelableTask: Task<Void, Error>?
|
|
340
|
-
|
|
341
|
-
private let internalSyncProgress: InternalSyncProgress
|
|
342
|
-
|
|
343
144
|
/// Initializes a CompactBlockProcessor instance
|
|
344
145
|
/// - Parameters:
|
|
345
146
|
/// - service: concrete implementation of `LightWalletService` protocol
|
|
346
147
|
/// - storage: concrete implementation of `CompactBlockRepository` protocol
|
|
347
148
|
/// - backend: a class that complies to `ZcashRustBackendWelding`
|
|
348
149
|
/// - config: `Configuration` struct for this processor
|
|
349
|
-
init(
|
|
350
|
-
container: DIContainer,
|
|
351
|
-
config: Configuration
|
|
352
|
-
) {
|
|
150
|
+
init(container: DIContainer, config: Configuration) {
|
|
353
151
|
self.init(
|
|
354
152
|
container: container,
|
|
355
153
|
config: config,
|
|
@@ -376,8 +174,8 @@ actor CompactBlockProcessor {
|
|
|
376
174
|
accountRepository: initializer.accountRepository
|
|
377
175
|
)
|
|
378
176
|
}
|
|
379
|
-
|
|
380
|
-
|
|
177
|
+
|
|
178
|
+
init(
|
|
381
179
|
container: DIContainer,
|
|
382
180
|
config: Configuration,
|
|
383
181
|
accountRepository: AccountRepository
|
|
@@ -388,172 +186,145 @@ actor CompactBlockProcessor {
|
|
|
388
186
|
accountRepository: accountRepository
|
|
389
187
|
)
|
|
390
188
|
|
|
189
|
+
let configProvider = ConfigProvider(config: config)
|
|
190
|
+
context = ActionContextImpl(state: .idle)
|
|
191
|
+
actions = Self.makeActions(container: container, configProvider: configProvider)
|
|
192
|
+
|
|
391
193
|
self.metrics = container.resolve(SDKMetrics.self)
|
|
392
194
|
self.logger = container.resolve(Logger.self)
|
|
393
195
|
self.latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self)
|
|
394
|
-
self.internalSyncProgress = container.resolve(InternalSyncProgress.self)
|
|
395
196
|
self.blockDownloaderService = container.resolve(BlockDownloaderService.self)
|
|
396
|
-
self.blockDownloader = container.resolve(BlockDownloader.self)
|
|
397
|
-
self.blockValidator = container.resolve(BlockValidator.self)
|
|
398
|
-
self.blockScanner = container.resolve(BlockScanner.self)
|
|
399
|
-
self.blockEnhancer = container.resolve(BlockEnhancer.self)
|
|
400
|
-
self.utxoFetcher = container.resolve(UTXOFetcher.self)
|
|
401
|
-
self.saplingParametersHandler = container.resolve(SaplingParametersHandler.self)
|
|
402
197
|
self.service = container.resolve(LightWalletService.self)
|
|
403
198
|
self.rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
|
404
199
|
self.storage = container.resolve(CompactBlockRepository.self)
|
|
405
200
|
self.config = config
|
|
406
201
|
self.transactionRepository = container.resolve(TransactionRepository.self)
|
|
407
202
|
self.accountRepository = accountRepository
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
deinit {
|
|
411
|
-
cancelableTask?.cancel()
|
|
203
|
+
self.fileManager = container.resolve(ZcashFileManager.self)
|
|
204
|
+
self.configProvider = configProvider
|
|
412
205
|
}
|
|
413
206
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
207
|
+
deinit {
|
|
208
|
+
syncTask?.cancel()
|
|
209
|
+
syncTask = nil
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// swiftlint:disable:next cyclomatic_complexity
|
|
213
|
+
private static func makeActions(container: DIContainer, configProvider: ConfigProvider) -> [CBPState: Action] {
|
|
214
|
+
let actionsDefinition = CBPState.allCases.compactMap { state -> (CBPState, Action)? in
|
|
215
|
+
let action: Action
|
|
216
|
+
switch state {
|
|
217
|
+
case .migrateLegacyCacheDB:
|
|
218
|
+
action = MigrateLegacyCacheDBAction(container: container, configProvider: configProvider)
|
|
219
|
+
case .validateServer:
|
|
220
|
+
action = ValidateServerAction(container: container, configProvider: configProvider)
|
|
221
|
+
case .updateSubtreeRoots:
|
|
222
|
+
action = UpdateSubtreeRootsAction(container: container, configProvider: configProvider)
|
|
223
|
+
case .updateChainTip:
|
|
224
|
+
action = UpdateChainTipAction(container: container)
|
|
225
|
+
case .processSuggestedScanRanges:
|
|
226
|
+
action = ProcessSuggestedScanRangesAction(container: container)
|
|
227
|
+
case .rewind:
|
|
228
|
+
action = RewindAction(container: container)
|
|
229
|
+
case .download:
|
|
230
|
+
action = DownloadAction(container: container, configProvider: configProvider)
|
|
231
|
+
case .scan:
|
|
232
|
+
action = ScanAction(container: container, configProvider: configProvider)
|
|
233
|
+
case .clearAlreadyScannedBlocks:
|
|
234
|
+
action = ClearAlreadyScannedBlocksAction(container: container)
|
|
235
|
+
case .enhance:
|
|
236
|
+
action = EnhanceAction(container: container, configProvider: configProvider)
|
|
237
|
+
case .fetchUTXO:
|
|
238
|
+
action = FetchUTXOsAction(container: container)
|
|
239
|
+
case .handleSaplingParams:
|
|
240
|
+
action = SaplingParamsAction(container: container)
|
|
241
|
+
case .clearCache:
|
|
242
|
+
action = ClearCacheAction(container: container)
|
|
243
|
+
case .finished, .failed, .stopped, .idle:
|
|
244
|
+
return nil
|
|
245
|
+
}
|
|
418
246
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
state = newState
|
|
422
|
-
await transitionState(from: oldState, to: newState)
|
|
423
|
-
}
|
|
247
|
+
return (state, action)
|
|
248
|
+
}
|
|
424
249
|
|
|
425
|
-
|
|
426
|
-
eventClosures[identifier] = closure
|
|
250
|
+
return Dictionary(uniqueKeysWithValues: actionsDefinition)
|
|
427
251
|
}
|
|
428
252
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
253
|
+
// This is currently used only in tests. And it should be used only in tests.
|
|
254
|
+
func update(config: Configuration) async {
|
|
255
|
+
self.config = config
|
|
256
|
+
await configProvider.update(config: config)
|
|
433
257
|
}
|
|
258
|
+
}
|
|
434
259
|
|
|
435
|
-
|
|
436
|
-
_ info: LightWalletdInfo,
|
|
437
|
-
saplingActivation: BlockHeight,
|
|
438
|
-
localNetwork: ZcashNetwork,
|
|
439
|
-
rustBackend: ZcashRustBackendWelding
|
|
440
|
-
) async throws {
|
|
441
|
-
// check network types
|
|
442
|
-
guard let remoteNetworkType = NetworkType.forChainName(info.chainName) else {
|
|
443
|
-
throw ZcashError.compactBlockProcessorChainName(info.chainName)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
guard remoteNetworkType == localNetwork.networkType else {
|
|
447
|
-
throw ZcashError.compactBlockProcessorNetworkMismatch(localNetwork.networkType, remoteNetworkType)
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
guard saplingActivation == info.saplingActivationHeight else {
|
|
451
|
-
throw ZcashError.compactBlockProcessorSaplingActivationMismatch(saplingActivation, BlockHeight(info.saplingActivationHeight))
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// check branch id
|
|
455
|
-
let localBranch = try rustBackend.consensusBranchIdFor(height: Int32(info.blockHeight))
|
|
456
|
-
|
|
457
|
-
guard let remoteBranchID = ConsensusBranchID.fromString(info.consensusBranchID) else {
|
|
458
|
-
throw ZcashError.compactBlockProcessorConsensusBranchID
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
guard remoteBranchID == localBranch else {
|
|
462
|
-
throw ZcashError.compactBlockProcessorWrongConsensusBranchId(localBranch, remoteBranchID)
|
|
463
|
-
}
|
|
464
|
-
}
|
|
260
|
+
// MARK: - "Public" API
|
|
465
261
|
|
|
466
|
-
|
|
467
|
-
///
|
|
468
|
-
/// triggers the blockProcessorStartedDownloading notification
|
|
469
|
-
///
|
|
470
|
-
/// - Important: subscribe to the notifications before calling this method
|
|
262
|
+
extension CompactBlockProcessor {
|
|
471
263
|
func start(retry: Bool = false) async {
|
|
472
264
|
if retry {
|
|
473
265
|
self.retryAttempts = 0
|
|
474
|
-
self.processingError = nil
|
|
475
266
|
self.backoffTimer?.invalidate()
|
|
476
267
|
self.backoffTimer = nil
|
|
477
268
|
}
|
|
478
269
|
|
|
479
|
-
guard
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
await notifyError(ZcashError.compactBlockProcessorMaxAttemptsReached(self.maxAttempts))
|
|
485
|
-
await updateState(.stopped)
|
|
486
|
-
case .stopped:
|
|
487
|
-
// max attempts have been reached
|
|
488
|
-
logger.info("max retry attempts reached")
|
|
489
|
-
await notifyError(ZcashError.compactBlockProcessorMaxAttemptsReached(self.maxAttempts))
|
|
490
|
-
case .synced:
|
|
491
|
-
// max attempts have been reached
|
|
492
|
-
logger.warn("max retry attempts reached on synced state, this indicates malfunction")
|
|
493
|
-
await notifyError(ZcashError.compactBlockProcessorMaxAttemptsReached(self.maxAttempts))
|
|
494
|
-
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
|
|
270
|
+
guard await canStartSync() else {
|
|
271
|
+
if await isIdle() {
|
|
272
|
+
logger.warn("max retry attempts reached on \(await context.state) state")
|
|
273
|
+
await send(event: .failed(ZcashError.compactBlockProcessorMaxAttemptsReached(config.retries)))
|
|
274
|
+
} else {
|
|
495
275
|
logger.debug("Warning: compact block processor was started while busy!!!!")
|
|
496
276
|
afterSyncHooksManager.insert(hook: .anotherSync)
|
|
497
277
|
}
|
|
498
278
|
return
|
|
499
279
|
}
|
|
500
280
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
try await self.migrateCacheDb(legacyCacheDbURL)
|
|
504
|
-
}
|
|
505
|
-
} catch {
|
|
506
|
-
await self.fail(error)
|
|
281
|
+
syncTask = Task(priority: .userInitiated) {
|
|
282
|
+
await run()
|
|
507
283
|
}
|
|
508
|
-
|
|
509
|
-
await self.nextBatch()
|
|
510
284
|
}
|
|
511
285
|
|
|
512
|
-
/**
|
|
513
|
-
Stops the CompactBlockProcessor
|
|
514
|
-
|
|
515
|
-
Note: retry count is reset
|
|
516
|
-
*/
|
|
517
286
|
func stop() async {
|
|
287
|
+
syncTask?.cancel()
|
|
518
288
|
self.backoffTimer?.invalidate()
|
|
519
289
|
self.backoffTimer = nil
|
|
290
|
+
await stopAllActions()
|
|
291
|
+
retryAttempts = 0
|
|
292
|
+
}
|
|
520
293
|
|
|
521
|
-
|
|
522
|
-
await
|
|
523
|
-
|
|
524
|
-
self.retryAttempts = 0
|
|
294
|
+
func latestHeight() async throws -> BlockHeight {
|
|
295
|
+
try await blockDownloaderService.latestBlockHeight()
|
|
525
296
|
}
|
|
297
|
+
}
|
|
526
298
|
|
|
527
|
-
|
|
299
|
+
// MARK: - Rewind
|
|
528
300
|
|
|
301
|
+
extension CompactBlockProcessor {
|
|
529
302
|
/// Rewinds to provided height.
|
|
530
303
|
/// - Parameter height: height to rewind to. If nil is provided, it will rescan to nearest height (quick rescan)
|
|
531
304
|
///
|
|
532
305
|
/// - Note: If this is called while sync is in progress then the sync process is stopped first and then rewind is executed.
|
|
533
|
-
func rewind(context: AfterSyncHooksManager.RewindContext) async {
|
|
306
|
+
func rewind(context: AfterSyncHooksManager.RewindContext) async throws {
|
|
534
307
|
logger.debug("Starting rewind")
|
|
535
|
-
|
|
536
|
-
|
|
308
|
+
if await isIdle() {
|
|
309
|
+
logger.debug("Sync doesn't run. Executing rewind.")
|
|
310
|
+
try await doRewind(context: context)
|
|
311
|
+
} else {
|
|
537
312
|
logger.debug("Stopping sync because of rewind")
|
|
538
313
|
afterSyncHooksManager.insert(hook: .rewind(context))
|
|
539
314
|
await stop()
|
|
540
|
-
|
|
541
|
-
case .stopped, .error, .synced:
|
|
542
|
-
logger.debug("Sync doesn't run. Executing rewind.")
|
|
543
|
-
await doRewind(context: context)
|
|
544
315
|
}
|
|
545
316
|
}
|
|
546
317
|
|
|
547
|
-
private func doRewind(context: AfterSyncHooksManager.RewindContext) async {
|
|
318
|
+
private func doRewind(context: AfterSyncHooksManager.RewindContext) async throws {
|
|
548
319
|
logger.debug("Executing rewind.")
|
|
549
|
-
let lastDownloaded = await
|
|
320
|
+
let lastDownloaded = await latestBlocksDataProvider.maxScannedHeight
|
|
550
321
|
let height = Int32(context.height ?? lastDownloaded)
|
|
551
322
|
|
|
552
323
|
let nearestHeight: Int32
|
|
553
324
|
do {
|
|
554
325
|
nearestHeight = try await rustBackend.getNearestRewindHeight(height: height)
|
|
555
326
|
} catch {
|
|
556
|
-
await
|
|
327
|
+
await failure(error)
|
|
557
328
|
return await context.completion(.failure(error))
|
|
558
329
|
}
|
|
559
330
|
|
|
@@ -561,9 +332,10 @@ actor CompactBlockProcessor {
|
|
|
561
332
|
let rewindHeight = max(Int32(nearestHeight - 1), Int32(config.walletBirthday))
|
|
562
333
|
|
|
563
334
|
do {
|
|
335
|
+
try await rewindDownloadBlockAction(to: BlockHeight(rewindHeight))
|
|
564
336
|
try await rustBackend.rewindToHeight(height: rewindHeight)
|
|
565
337
|
} catch {
|
|
566
|
-
await
|
|
338
|
+
await failure(error)
|
|
567
339
|
return await context.completion(.failure(error))
|
|
568
340
|
}
|
|
569
341
|
|
|
@@ -575,37 +347,43 @@ actor CompactBlockProcessor {
|
|
|
575
347
|
return await context.completion(.failure(error))
|
|
576
348
|
}
|
|
577
349
|
|
|
578
|
-
await internalSyncProgress.rewind(to: rewindBlockHeight)
|
|
579
|
-
|
|
580
|
-
self.lastChainValidationFailure = nil
|
|
581
350
|
await context.completion(.success(rewindBlockHeight))
|
|
582
351
|
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// MARK: - Actions
|
|
355
|
+
|
|
356
|
+
private extension CompactBlockProcessor {
|
|
357
|
+
func rewindDownloadBlockAction(to rewindHeight: BlockHeight?) async throws {
|
|
358
|
+
if let downloadAction = actions[.download] as? DownloadAction {
|
|
359
|
+
await downloadAction.downloader.rewind(latestDownloadedBlockHeight: rewindHeight)
|
|
360
|
+
} else {
|
|
361
|
+
throw ZcashError.compactBlockProcessorDownloadBlockActionRewind
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
583
365
|
|
|
584
|
-
|
|
366
|
+
// MARK: - Wipe
|
|
585
367
|
|
|
586
|
-
|
|
368
|
+
extension CompactBlockProcessor {
|
|
369
|
+
func wipe(context: AfterSyncHooksManager.WipeContext) async throws {
|
|
587
370
|
logger.debug("Starting wipe")
|
|
588
|
-
|
|
589
|
-
|
|
371
|
+
if await isIdle() {
|
|
372
|
+
logger.debug("Sync doesn't run. Executing wipe.")
|
|
373
|
+
try await doWipe(context: context)
|
|
374
|
+
} else {
|
|
590
375
|
logger.debug("Stopping sync because of wipe")
|
|
591
376
|
afterSyncHooksManager.insert(hook: .wipe(context))
|
|
592
377
|
await stop()
|
|
593
|
-
|
|
594
|
-
case .stopped, .error, .synced:
|
|
595
|
-
logger.debug("Sync doesn't run. Executing wipe.")
|
|
596
|
-
await doWipe(context: context)
|
|
597
378
|
}
|
|
598
379
|
}
|
|
599
380
|
|
|
600
|
-
private func doWipe(context: AfterSyncHooksManager.WipeContext) async {
|
|
381
|
+
private func doWipe(context: AfterSyncHooksManager.WipeContext) async throws {
|
|
601
382
|
logger.debug("Executing wipe.")
|
|
602
383
|
context.prewipe()
|
|
603
384
|
|
|
604
|
-
await updateState(.stopped)
|
|
605
|
-
|
|
606
385
|
do {
|
|
607
386
|
try await self.storage.clear()
|
|
608
|
-
await internalSyncProgress.rewind(to: 0)
|
|
609
387
|
|
|
610
388
|
wipeLegacyCacheDbIfNeeded()
|
|
611
389
|
|
|
@@ -614,437 +392,306 @@ actor CompactBlockProcessor {
|
|
|
614
392
|
try fileManager.removeItem(at: config.dataDb)
|
|
615
393
|
}
|
|
616
394
|
|
|
395
|
+
try await rewindDownloadBlockAction(to: nil)
|
|
396
|
+
|
|
617
397
|
await context.completion(nil)
|
|
618
398
|
} catch {
|
|
619
399
|
await context.completion(error)
|
|
620
400
|
}
|
|
621
401
|
}
|
|
622
402
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
let info = try await self.service.getInfo()
|
|
628
|
-
try await Self.validateServerInfo(
|
|
629
|
-
info,
|
|
630
|
-
saplingActivation: self.config.saplingActivation,
|
|
631
|
-
localNetwork: self.config.network,
|
|
632
|
-
rustBackend: self.rustBackend
|
|
633
|
-
)
|
|
634
|
-
} catch {
|
|
635
|
-
await self.severeFailure(error)
|
|
636
|
-
}
|
|
403
|
+
private func wipeLegacyCacheDbIfNeeded() {
|
|
404
|
+
guard let cacheDbURL = config.cacheDbURL else { return }
|
|
405
|
+
guard fileManager.isDeletableFile(atPath: cacheDbURL.pathExtension) else { return }
|
|
406
|
+
try? fileManager.removeItem(at: cacheDbURL)
|
|
637
407
|
}
|
|
638
|
-
|
|
639
|
-
/// Processes new blocks on the given range based on the configuration set for this instance
|
|
640
|
-
func processNewBlocks(ranges: SyncRanges) async {
|
|
641
|
-
self.foundBlocks = true
|
|
642
|
-
|
|
643
|
-
cancelableTask = Task(priority: .userInitiated) {
|
|
644
|
-
do {
|
|
645
|
-
let totalProgressRange = computeTotalProgressRange(from: ranges)
|
|
646
|
-
|
|
647
|
-
logger.debug("""
|
|
648
|
-
Syncing with ranges:
|
|
649
|
-
downloaded but not scanned: \
|
|
650
|
-
\(ranges.downloadedButUnscannedRange?.lowerBound ?? -1)...\(ranges.downloadedButUnscannedRange?.upperBound ?? -1)
|
|
651
|
-
download and scan: \(ranges.downloadAndScanRange?.lowerBound ?? -1)...\(ranges.downloadAndScanRange?.upperBound ?? -1)
|
|
652
|
-
enhance range: \(ranges.enhanceRange?.lowerBound ?? -1)...\(ranges.enhanceRange?.upperBound ?? -1)
|
|
653
|
-
fetchUTXO range: \(ranges.fetchUTXORange?.lowerBound ?? -1)...\(ranges.fetchUTXORange?.upperBound ?? -1)
|
|
654
|
-
total progress range: \(totalProgressRange.lowerBound)...\(totalProgressRange.upperBound)
|
|
655
|
-
""")
|
|
656
|
-
|
|
657
|
-
var anyActionExecuted = false
|
|
658
|
-
|
|
659
|
-
// clear any present cached state if needed.
|
|
660
|
-
// this checks if there was a sync in progress that was
|
|
661
|
-
// interrupted abruptly and cache was not able to be cleared
|
|
662
|
-
// properly and internal state set to the appropriate value
|
|
663
|
-
if let newLatestDownloadedHeight = ranges.shouldClearBlockCacheAndUpdateInternalState() {
|
|
664
|
-
try await storage.clear()
|
|
665
|
-
await internalSyncProgress.set(newLatestDownloadedHeight, .latestDownloadedBlockHeight)
|
|
666
|
-
} else {
|
|
667
|
-
try await storage.create()
|
|
668
|
-
}
|
|
408
|
+
}
|
|
669
409
|
|
|
670
|
-
|
|
671
|
-
logger.debug("Starting scan with downloaded but not scanned blocks with range: \(range.lowerBound)...\(range.upperBound)")
|
|
672
|
-
try await blockScanner.scanBlocks(at: range, totalProgressRange: totalProgressRange) { [weak self] lastScannedHeight in
|
|
673
|
-
let progress = BlockProgress(
|
|
674
|
-
startHeight: totalProgressRange.lowerBound,
|
|
675
|
-
targetHeight: totalProgressRange.upperBound,
|
|
676
|
-
progressHeight: lastScannedHeight
|
|
677
|
-
)
|
|
678
|
-
await self?.notifyProgress(.syncing(progress))
|
|
679
|
-
}
|
|
680
|
-
}
|
|
410
|
+
// MARK: - Events
|
|
681
411
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
try await blockDownloader.setSyncRange(range, batchSize: batchSize)
|
|
685
|
-
try await downloadAndScanBlocks(at: range, totalProgressRange: totalProgressRange)
|
|
686
|
-
// Side effect of calling stop is to delete last used download stream. To be sure that it doesn't keep any data in memory.
|
|
687
|
-
await blockDownloader.stopDownload()
|
|
688
|
-
}
|
|
412
|
+
extension CompactBlockProcessor {
|
|
413
|
+
typealias EventClosure = (Event) async -> Void
|
|
689
414
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
await updateState(.enhancing)
|
|
694
|
-
if let transactions = try await blockEnhancer.enhance(
|
|
695
|
-
at: range,
|
|
696
|
-
didEnhance: { [weak self] progress in
|
|
697
|
-
await self?.notifyProgress(.enhance(progress))
|
|
698
|
-
if
|
|
699
|
-
let foundTx = progress.lastFoundTransaction,
|
|
700
|
-
progress.newlyMined {
|
|
701
|
-
await self?.notifyMinedTransaction(foundTx)
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
) {
|
|
705
|
-
await notifyTransactions(transactions, in: range)
|
|
706
|
-
}
|
|
707
|
-
}
|
|
415
|
+
enum Event {
|
|
416
|
+
/// Event sent when the CompactBlockProcessor presented an error.
|
|
417
|
+
case failed(Error)
|
|
708
418
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
logger.debug("Fetching UTXO with range: \(range.lowerBound)...\(range.upperBound)")
|
|
712
|
-
await updateState(.fetching)
|
|
713
|
-
let result = try await utxoFetcher.fetch(at: range) { [weak self] progress in
|
|
714
|
-
await self?.notifyProgress(.fetch(progress))
|
|
715
|
-
}
|
|
716
|
-
await send(event: .storedUTXOs(result))
|
|
717
|
-
}
|
|
419
|
+
/// Event sent when the CompactBlockProcessor has finished syncing the blockchain to latest height
|
|
420
|
+
case finished(_ lastScannedHeight: BlockHeight)
|
|
718
421
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
try await saplingParametersHandler.handleIfNeeded()
|
|
422
|
+
/// Event sent when the CompactBlockProcessor found a newly mined transaction
|
|
423
|
+
case minedTransaction(ZcashTransaction.Overview)
|
|
722
424
|
|
|
723
|
-
|
|
724
|
-
|
|
425
|
+
/// Event sent when the CompactBlockProcessor enhanced a bunch of transactions in some range.
|
|
426
|
+
case foundTransactions([ZcashTransaction.Overview], CompactBlockRange)
|
|
725
427
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
} catch {
|
|
731
|
-
// Side effect of calling stop is to delete last used download stream. To be sure that it doesn't keep any data in memory.
|
|
732
|
-
await blockDownloader.stopDownload()
|
|
733
|
-
logger.error("Sync failed with error: \(error)")
|
|
428
|
+
/// Event sent when the CompactBlockProcessor handled a ReOrg.
|
|
429
|
+
/// `reorgHeight` is the height on which the reorg was detected.
|
|
430
|
+
/// `rewindHeight` is the height that the processor backed to in order to solve the Reorg.
|
|
431
|
+
case handledReorg(_ reorgHeight: BlockHeight, _ rewindHeight: BlockHeight)
|
|
734
432
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
await updateState(.stopped)
|
|
738
|
-
await handleAfterSyncHooks()
|
|
739
|
-
} else {
|
|
740
|
-
if case let ZcashError.rustValidateCombinedChainInvalidChain(height) = error {
|
|
741
|
-
await validationFailed(at: BlockHeight(height))
|
|
742
|
-
} else {
|
|
743
|
-
logger.error("processing failed with error: \(error)")
|
|
744
|
-
await fail(error)
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
433
|
+
/// Event sent when progress of some specific action happened.
|
|
434
|
+
case syncProgress(Float)
|
|
750
435
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
self.afterSyncHooksManager = AfterSyncHooksManager()
|
|
436
|
+
/// Event sent when progress of the sync process changes.
|
|
437
|
+
case progressUpdated(Float)
|
|
754
438
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
439
|
+
/// Event sent when the CompactBlockProcessor fetched utxos from lightwalletd attempted to store them.
|
|
440
|
+
case storedUTXOs((inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]))
|
|
441
|
+
|
|
442
|
+
/// Event sent when the CompactBlockProcessor starts enhancing of the transactions.
|
|
443
|
+
case startedEnhancing
|
|
444
|
+
|
|
445
|
+
/// Event sent when the CompactBlockProcessor starts fetching of the UTXOs.
|
|
446
|
+
case startedFetching
|
|
447
|
+
|
|
448
|
+
/// Event sent when the CompactBlockProcessor starts syncing.
|
|
449
|
+
case startedSyncing
|
|
450
|
+
|
|
451
|
+
/// Event sent when the CompactBlockProcessor stops syncing.
|
|
452
|
+
case stopped
|
|
763
453
|
}
|
|
764
454
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
455
|
+
func updateEventClosure(identifier: String, closure: @escaping (Event) async -> Void) async {
|
|
456
|
+
eventClosures[identifier] = closure
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private func send(event: Event) async {
|
|
460
|
+
for item in eventClosures {
|
|
461
|
+
await item.value(event)
|
|
772
462
|
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
773
465
|
|
|
774
|
-
|
|
775
|
-
for i in 0..<loopsCount {
|
|
776
|
-
let processingRange = computeSingleLoopDownloadRange(fullRange: range, loopCounter: i, batchSize: batchSize)
|
|
466
|
+
// MARK: - Main loop
|
|
777
467
|
|
|
778
|
-
|
|
468
|
+
extension CompactBlockProcessor {
|
|
469
|
+
// This is main loop of the sync process. It simply takes state and try to find action which handles it. If action is found it executes the
|
|
470
|
+
// action. If action is not found then loop finishes. Thanks to this it's super easy to identify start point of sync process and end points
|
|
471
|
+
// of sync process without any side effects.
|
|
472
|
+
//
|
|
473
|
+
// Check `docs/cbp_state_machine.puml` file and `docs/images/cbp_state_machine.png` file to see all the state tree. Also when you update state
|
|
474
|
+
// tree in the code update this documentation. Image is generated by plantuml tool.
|
|
475
|
+
//
|
|
476
|
+
// swiftlint:disable:next cyclomatic_complexity
|
|
477
|
+
private func run() async {
|
|
478
|
+
logger.debug("Starting run")
|
|
479
|
+
await resetContext()
|
|
480
|
+
|
|
481
|
+
while true {
|
|
482
|
+
// Sync is starting when the state is `idle`.
|
|
483
|
+
if await context.state == .idle {
|
|
484
|
+
// Side effect of calling stop is to delete last used download stream. To be sure that it doesn't keep any data in memory.
|
|
485
|
+
await stopAllActions()
|
|
486
|
+
// Update state to the first state in state machine that can be handled by action.
|
|
487
|
+
await context.update(state: .migrateLegacyCacheDB)
|
|
488
|
+
await syncStarted()
|
|
779
489
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
490
|
+
if backoffTimer == nil {
|
|
491
|
+
await setTimer()
|
|
492
|
+
}
|
|
493
|
+
}
|
|
783
494
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
await blockDownloader.startDownload(maxBlockBufferSize: config.downloadBufferSize)
|
|
495
|
+
let state = await context.state
|
|
496
|
+
logger.debug("Handling state: \(state)")
|
|
787
497
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
498
|
+
// Try to find action for state.
|
|
499
|
+
guard let action = actions[state] else {
|
|
500
|
+
// Side effect of calling stop is to delete last used download stream. To be sure that it doesn't keep any data in memory.
|
|
501
|
+
await stopAllActions()
|
|
502
|
+
if await syncFinished() {
|
|
503
|
+
await resetContext()
|
|
504
|
+
continue
|
|
505
|
+
} else {
|
|
506
|
+
break
|
|
507
|
+
}
|
|
792
508
|
}
|
|
793
509
|
|
|
794
510
|
do {
|
|
795
|
-
try
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
511
|
+
try Task.checkCancellation()
|
|
512
|
+
|
|
513
|
+
// Execute action.
|
|
514
|
+
context = try await action.run(with: context) { [weak self] event in
|
|
515
|
+
await self?.send(event: event)
|
|
516
|
+
if let progressChanged = await self?.compactBlockProgress.hasProgressUpdated(event), progressChanged {
|
|
517
|
+
if let progress = await self?.compactBlockProgress.progress {
|
|
518
|
+
await self?.send(event: .progressUpdated(progress))
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
801
522
|
|
|
802
|
-
|
|
803
|
-
|
|
523
|
+
await didFinishAction()
|
|
524
|
+
} catch {
|
|
525
|
+
// Side effect of calling stop is to delete last used download stream. To be sure that it doesn't keep any data in memory.
|
|
526
|
+
await stopAllActions()
|
|
527
|
+
logger.error("Sync failed with error: \(error)")
|
|
804
528
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
529
|
+
if Task.isCancelled {
|
|
530
|
+
logger.info("Processing cancelled.")
|
|
531
|
+
do {
|
|
532
|
+
if try await syncTaskWasCancelled() {
|
|
533
|
+
// Start sync all over again
|
|
534
|
+
await resetContext()
|
|
535
|
+
} else {
|
|
536
|
+
// end the sync loop
|
|
537
|
+
break
|
|
538
|
+
}
|
|
539
|
+
} catch {
|
|
540
|
+
await failure(error)
|
|
541
|
+
break
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
if await handleSyncFailure(action: action, error: error) {
|
|
545
|
+
// Start sync all over again
|
|
546
|
+
await resetContext()
|
|
547
|
+
} else {
|
|
548
|
+
// end the sync loop
|
|
549
|
+
break
|
|
550
|
+
}
|
|
816
551
|
}
|
|
817
|
-
} catch {
|
|
818
|
-
logger.error("Scanning failed with error: \(error)")
|
|
819
|
-
await ifTaskIsNotCanceledClearCompactBlockCache(lastScannedHeight: lastScannedHeight)
|
|
820
|
-
throw error
|
|
821
552
|
}
|
|
822
|
-
|
|
823
|
-
try await clearCompactBlockCache(upTo: lastScannedHeight)
|
|
824
|
-
|
|
825
|
-
let progress = BlockProgress(
|
|
826
|
-
startHeight: totalProgressRange.lowerBound,
|
|
827
|
-
targetHeight: totalProgressRange.upperBound,
|
|
828
|
-
progressHeight: processingRange.upperBound
|
|
829
|
-
)
|
|
830
|
-
await notifyProgress(.syncing(progress))
|
|
831
553
|
}
|
|
554
|
+
|
|
555
|
+
logger.debug("Run ended")
|
|
556
|
+
syncTask = nil
|
|
832
557
|
}
|
|
833
558
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
200...299
|
|
840
|
-
300...399
|
|
841
|
-
...
|
|
842
|
-
900...999
|
|
843
|
-
1000...1000
|
|
844
|
-
*/
|
|
845
|
-
func computeSingleLoopDownloadRange(fullRange: CompactBlockRange, loopCounter: Int, batchSize: BlockHeight) -> CompactBlockRange {
|
|
846
|
-
let lowerBound = fullRange.lowerBound + (loopCounter * batchSize)
|
|
847
|
-
let upperBound = min(fullRange.lowerBound + ((loopCounter + 1) * batchSize) - 1, fullRange.upperBound)
|
|
848
|
-
return lowerBound...upperBound
|
|
559
|
+
private func syncTaskWasCancelled() async throws -> Bool {
|
|
560
|
+
logger.info("Sync cancelled.")
|
|
561
|
+
await context.update(state: .stopped)
|
|
562
|
+
await send(event: .stopped)
|
|
563
|
+
return try await handleAfterSyncHooks()
|
|
849
564
|
}
|
|
850
565
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
guard syncRanges.downloadedButUnscannedRange != nil || syncRanges.downloadAndScanRange != nil else {
|
|
855
|
-
// In this case we are sure that no downloading or scanning happens so this returned range won't be even used. And it's easier to return
|
|
856
|
-
// this "fake" range than to handle nil.
|
|
857
|
-
return 0...0
|
|
566
|
+
private func handleSyncFailure(action: Action, error: Error) async -> Bool {
|
|
567
|
+
if action.removeBlocksCacheWhenFailed {
|
|
568
|
+
await ifTaskIsNotCanceledClearCompactBlockCache()
|
|
858
569
|
}
|
|
859
570
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
let upperBound = syncRanges.downloadAndScanRange?.upperBound ?? syncRanges.downloadedButUnscannedRange?.upperBound ?? 0
|
|
571
|
+
logger.error("Sync failed with error: \(error)")
|
|
572
|
+
await failure(error)
|
|
863
573
|
|
|
864
|
-
return
|
|
574
|
+
return false
|
|
865
575
|
}
|
|
866
576
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
577
|
+
// swiftlint:disable:next cyclomatic_complexity
|
|
578
|
+
private func didFinishAction() async {
|
|
579
|
+
// This is evalution of the state setup by previous action.
|
|
580
|
+
switch await context.state {
|
|
581
|
+
case .idle:
|
|
582
|
+
break
|
|
583
|
+
case .migrateLegacyCacheDB:
|
|
584
|
+
break
|
|
585
|
+
case .validateServer:
|
|
586
|
+
break
|
|
587
|
+
case .updateSubtreeRoots:
|
|
588
|
+
break
|
|
589
|
+
case .updateChainTip:
|
|
590
|
+
break
|
|
591
|
+
case .processSuggestedScanRanges:
|
|
592
|
+
break
|
|
593
|
+
case .rewind:
|
|
594
|
+
break
|
|
595
|
+
case .download:
|
|
596
|
+
break
|
|
597
|
+
case .scan:
|
|
598
|
+
break
|
|
599
|
+
case .clearAlreadyScannedBlocks:
|
|
600
|
+
break
|
|
601
|
+
case .enhance:
|
|
602
|
+
await send(event: .startedEnhancing)
|
|
603
|
+
case .fetchUTXO:
|
|
604
|
+
await send(event: .startedFetching)
|
|
605
|
+
case .handleSaplingParams:
|
|
606
|
+
break
|
|
607
|
+
case .clearCache:
|
|
608
|
+
break
|
|
609
|
+
case .finished:
|
|
610
|
+
break
|
|
611
|
+
case .failed:
|
|
612
|
+
break
|
|
613
|
+
case .stopped:
|
|
614
|
+
break
|
|
615
|
+
}
|
|
879
616
|
}
|
|
880
617
|
|
|
881
|
-
func
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
) -> BlockHeight {
|
|
886
|
-
let offset = min(ZcashSDK.maxReorgSize, ZcashSDK.defaultRewindDistance * (consecutiveErrors + 1))
|
|
887
|
-
return max(errorHeight - offset, walletBirthday - ZcashSDK.maxReorgSize)
|
|
618
|
+
private func resetContext() async {
|
|
619
|
+
let lastEnhancedheight = await context.lastEnhancedHeight
|
|
620
|
+
context = ActionContextImpl(state: .idle)
|
|
621
|
+
await context.update(lastEnhancedHeight: lastEnhancedheight)
|
|
888
622
|
}
|
|
889
623
|
|
|
890
|
-
func
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
self.backoffTimer?.invalidate()
|
|
895
|
-
self.retryAttempts = config.retries
|
|
896
|
-
self.processingError = error
|
|
897
|
-
await updateState(.error(error))
|
|
898
|
-
await self.notifyError(error)
|
|
624
|
+
private func syncStarted() async {
|
|
625
|
+
logger.debug("Sync started")
|
|
626
|
+
// handle start of the sync process
|
|
627
|
+
await send(event: .startedSyncing)
|
|
899
628
|
}
|
|
900
629
|
|
|
901
|
-
func
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
switch self.state {
|
|
909
|
-
case .error:
|
|
910
|
-
await notifyError(error)
|
|
911
|
-
default:
|
|
912
|
-
break
|
|
913
|
-
}
|
|
914
|
-
await updateState(.error(error))
|
|
915
|
-
guard self.maxAttemptsReached else { return }
|
|
916
|
-
// don't set a new timer if there are no more attempts.
|
|
917
|
-
await self.setTimer()
|
|
918
|
-
}
|
|
630
|
+
private func syncFinished() async -> Bool {
|
|
631
|
+
logger.debug("Sync finished")
|
|
632
|
+
let latestBlockHeightWhenSyncing = await context.syncControlData.latestBlockHeight
|
|
633
|
+
let latestBlockHeight = await latestBlocksDataProvider.latestBlockHeight
|
|
634
|
+
// If `latestBlockHeightWhenSyncing` is 0 then it means that there was nothing to sync in last sync process.
|
|
635
|
+
let newerBlocksWereMinedDuringSync =
|
|
636
|
+
latestBlockHeightWhenSyncing > 0 && latestBlockHeightWhenSyncing < latestBlockHeight
|
|
919
637
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
throw ZcashError.compactBlockProcessorMissingDbPath(config.fsBlockCacheRoot.absoluteString)
|
|
923
|
-
}
|
|
638
|
+
retryAttempts = 0
|
|
639
|
+
consecutiveChainValidationErrors = 0
|
|
924
640
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
641
|
+
let lastScannedHeight = await latestBlocksDataProvider.maxScannedHeight
|
|
642
|
+
// Some actions may not run. For example there are no transactions to enhance and therefore there is no enhance progress. And in
|
|
643
|
+
// cases like this computation of final progress won't work properly. So let's fake 100% progress at the end of the sync process.
|
|
644
|
+
await send(event: .progressUpdated(1))
|
|
645
|
+
await send(event: .finished(lastScannedHeight))
|
|
646
|
+
await context.update(state: .finished)
|
|
929
647
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
downloaderService: blockDownloaderService,
|
|
937
|
-
latestBlocksDataProvider: latestBlocksDataProvider,
|
|
938
|
-
config: self.config,
|
|
939
|
-
rustBackend: self.rustBackend,
|
|
940
|
-
internalSyncProgress: internalSyncProgress
|
|
941
|
-
)
|
|
942
|
-
switch nextState {
|
|
943
|
-
case .finishProcessing(let height):
|
|
944
|
-
await self.processingFinished(height: height)
|
|
945
|
-
case .processNewBlocks(let ranges):
|
|
946
|
-
await self.processNewBlocks(ranges: ranges)
|
|
947
|
-
case let .wait(latestHeight, latestDownloadHeight):
|
|
948
|
-
// Lightwalletd might be syncing
|
|
949
|
-
logger.info(
|
|
950
|
-
"Lightwalletd might be syncing: latest downloaded block height is: \(latestDownloadHeight) " +
|
|
951
|
-
"while latest blockheight is reported at: \(latestHeight)"
|
|
952
|
-
)
|
|
953
|
-
await self.processingFinished(height: latestDownloadHeight)
|
|
954
|
-
}
|
|
955
|
-
} catch {
|
|
956
|
-
await self.severeFailure(error)
|
|
648
|
+
// If new blocks were mined during previous sync run the sync process again
|
|
649
|
+
if newerBlocksWereMinedDuringSync {
|
|
650
|
+
return true
|
|
651
|
+
} else {
|
|
652
|
+
await setTimer()
|
|
653
|
+
return false
|
|
957
654
|
}
|
|
958
655
|
}
|
|
959
656
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
cancelableTask?.cancel()
|
|
963
|
-
await blockDownloader.stopDownload()
|
|
657
|
+
private func failure(_ error: Error) async {
|
|
658
|
+
await context.update(state: .failed)
|
|
964
659
|
|
|
965
|
-
|
|
966
|
-
self.lastChainValidationFailure = height
|
|
967
|
-
|
|
968
|
-
// rewind
|
|
969
|
-
let rewindHeight = determineLowerBound(
|
|
970
|
-
errorHeight: height,
|
|
971
|
-
consecutiveErrors: consecutiveChainValidationErrors,
|
|
972
|
-
walletBirthday: self.config.walletBirthday
|
|
973
|
-
)
|
|
974
|
-
|
|
975
|
-
self.consecutiveChainValidationErrors += 1
|
|
976
|
-
|
|
977
|
-
do {
|
|
978
|
-
try await rustBackend.rewindToHeight(height: Int32(rewindHeight))
|
|
979
|
-
} catch {
|
|
980
|
-
await fail(error)
|
|
981
|
-
return
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
do {
|
|
985
|
-
try await blockDownloaderService.rewind(to: rewindHeight)
|
|
986
|
-
await internalSyncProgress.rewind(to: rewindHeight)
|
|
660
|
+
logger.error("Fail with error: \(error)")
|
|
987
661
|
|
|
988
|
-
|
|
662
|
+
self.retryAttempts += 1
|
|
663
|
+
await send(event: .failed(error))
|
|
989
664
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
await self.fail(error)
|
|
665
|
+
// don't set a new timer if there are no more attempts.
|
|
666
|
+
if hasRetryAttempt() {
|
|
667
|
+
await self.setTimer()
|
|
994
668
|
}
|
|
995
669
|
}
|
|
996
670
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
671
|
+
private func handleAfterSyncHooks() async throws -> Bool {
|
|
672
|
+
let afterSyncHooksManager = self.afterSyncHooksManager
|
|
673
|
+
self.afterSyncHooksManager = AfterSyncHooksManager()
|
|
1000
674
|
|
|
1001
|
-
if let
|
|
1002
|
-
await
|
|
675
|
+
if let wipeContext = afterSyncHooksManager.shouldExecuteWipeHook() {
|
|
676
|
+
try await doWipe(context: wipeContext)
|
|
677
|
+
return false
|
|
678
|
+
} else if let rewindContext = afterSyncHooksManager.shouldExecuteRewindHook() {
|
|
679
|
+
try await doRewind(context: rewindContext)
|
|
680
|
+
return false
|
|
681
|
+
} else if afterSyncHooksManager.shouldExecuteAnotherSyncHook() {
|
|
682
|
+
logger.debug("Starting new sync.")
|
|
683
|
+
return true
|
|
1003
684
|
} else {
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
private func processingFinished(height: BlockHeight) async {
|
|
1009
|
-
await send(event: .finished(height, foundBlocks))
|
|
1010
|
-
await updateState(.synced)
|
|
1011
|
-
await setTimer()
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
private func ifTaskIsNotCanceledClearCompactBlockCache(lastScannedHeight: BlockHeight) async {
|
|
1015
|
-
guard !Task.isCancelled else { return }
|
|
1016
|
-
do {
|
|
1017
|
-
// Blocks download work in parallel with scanning. So imagine this scenario:
|
|
1018
|
-
//
|
|
1019
|
-
// Scanning is done until height 10300. Blocks are downloaded until height 10400.
|
|
1020
|
-
// And now validation fails and this method is called. And `.latestDownloadedBlockHeight` in `internalSyncProgress` is set to 10400. And
|
|
1021
|
-
// all the downloaded blocks are removed here.
|
|
1022
|
-
//
|
|
1023
|
-
// If this line doesn't happen then when sync starts next time it thinks that all the blocks are downloaded until 10400. But all were
|
|
1024
|
-
// removed. So blocks between 10300 and 10400 wouldn't ever be scanned.
|
|
1025
|
-
//
|
|
1026
|
-
// Scanning is done until 10300 so the SDK can be sure that blocks with height below 10300 are not required. So it makes sense to set
|
|
1027
|
-
// `.latestDownloadedBlockHeight` to `lastScannedHeight`. And sync will work fine in next run.
|
|
1028
|
-
await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
|
|
1029
|
-
try await clearCompactBlockCache()
|
|
1030
|
-
} catch {
|
|
1031
|
-
logger.error("`clearCompactBlockCache` failed after error: \(error)")
|
|
685
|
+
return false
|
|
1032
686
|
}
|
|
1033
687
|
}
|
|
688
|
+
}
|
|
1034
689
|
|
|
1035
|
-
|
|
1036
|
-
try await storage.clear(upTo: height)
|
|
1037
|
-
logger.info("Cache removed upTo \(height)")
|
|
1038
|
-
}
|
|
690
|
+
// MARK: - Utils
|
|
1039
691
|
|
|
1040
|
-
|
|
1041
|
-
await blockDownloader.stopDownload()
|
|
1042
|
-
try await storage.clear()
|
|
1043
|
-
logger.info("Cache removed")
|
|
1044
|
-
}
|
|
1045
|
-
|
|
692
|
+
extension CompactBlockProcessor {
|
|
1046
693
|
private func setTimer() async {
|
|
1047
|
-
let interval =
|
|
694
|
+
let interval = config.blockPollInterval
|
|
1048
695
|
self.backoffTimer?.invalidate()
|
|
1049
696
|
let timer = Timer(
|
|
1050
697
|
timeInterval: interval,
|
|
@@ -1052,78 +699,66 @@ actor CompactBlockProcessor {
|
|
|
1052
699
|
block: { [weak self] _ in
|
|
1053
700
|
Task { [weak self] in
|
|
1054
701
|
guard let self else { return }
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
await self.latestBlocksDataProvider.updateBlockData()
|
|
1058
|
-
case .stopped, .error, .synced:
|
|
1059
|
-
if await self.shouldStart {
|
|
702
|
+
if await self.isIdle() {
|
|
703
|
+
if await self.canStartSync() {
|
|
1060
704
|
self.logger.debug(
|
|
1061
705
|
"""
|
|
1062
706
|
Timer triggered: Starting compact Block processor!.
|
|
1063
|
-
Processor State: \(await self.state)
|
|
1064
|
-
latestHeight: \(try await self.transactionRepository.lastScannedHeight())
|
|
707
|
+
Processor State: \(await self.context.state)
|
|
1065
708
|
attempts: \(await self.retryAttempts)
|
|
1066
709
|
"""
|
|
1067
710
|
)
|
|
1068
711
|
await self.start()
|
|
1069
|
-
} else if await self.
|
|
1070
|
-
await self.
|
|
712
|
+
} else if await self.hasRetryAttempt() {
|
|
713
|
+
await self.failure(ZcashError.compactBlockProcessorMaxAttemptsReached(self.config.retries))
|
|
1071
714
|
}
|
|
715
|
+
} else {
|
|
716
|
+
await self.latestBlocksDataProvider.updateBlockData()
|
|
1072
717
|
}
|
|
1073
718
|
}
|
|
1074
719
|
}
|
|
1075
720
|
)
|
|
1076
721
|
RunLoop.main.add(timer, forMode: .default)
|
|
1077
|
-
|
|
1078
722
|
self.backoffTimer = timer
|
|
1079
723
|
}
|
|
1080
|
-
|
|
1081
|
-
private func transitionState(from oldValue: State, to newValue: State) async {
|
|
1082
|
-
guard oldValue != newValue else {
|
|
1083
|
-
return
|
|
1084
|
-
}
|
|
1085
724
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
await notifyError(err)
|
|
1089
|
-
case .stopped:
|
|
1090
|
-
await send(event: .stopped)
|
|
1091
|
-
case .enhancing:
|
|
1092
|
-
await send(event: .startedEnhancing)
|
|
1093
|
-
case .fetching:
|
|
1094
|
-
await send(event: .startedFetching)
|
|
1095
|
-
case .handlingSaplingFiles:
|
|
1096
|
-
// We don't report this to outside world as separate phase for now.
|
|
1097
|
-
break
|
|
1098
|
-
case .synced:
|
|
1099
|
-
// transition to this state is handled by `processingFinished(height: BlockHeight)`
|
|
1100
|
-
break
|
|
1101
|
-
case .syncing:
|
|
1102
|
-
await send(event: .startedSyncing)
|
|
1103
|
-
}
|
|
725
|
+
private func isIdle() async -> Bool {
|
|
726
|
+
return syncTask == nil
|
|
1104
727
|
}
|
|
1105
728
|
|
|
1106
|
-
private func
|
|
1107
|
-
await
|
|
729
|
+
private func canStartSync() async -> Bool {
|
|
730
|
+
return await isIdle() && hasRetryAttempt()
|
|
1108
731
|
}
|
|
1109
|
-
// TODO: [#713] encapsulate service errors better, https://github.com/zcash/ZcashLightClientKit/issues/713
|
|
1110
|
-
}
|
|
1111
732
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
733
|
+
private func hasRetryAttempt() -> Bool {
|
|
734
|
+
retryAttempts < config.retries
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
func determineLowerBound(errorHeight: Int, consecutiveErrors: Int, walletBirthday: BlockHeight) -> BlockHeight {
|
|
738
|
+
let offset = min(ZcashSDK.maxReorgSize, ZcashSDK.defaultRewindDistance * (consecutiveErrors + 1))
|
|
739
|
+
return max(errorHeight - offset, walletBirthday - ZcashSDK.maxReorgSize)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
private func stopAllActions() async {
|
|
743
|
+
for action in actions.values {
|
|
744
|
+
await action.stop()
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
private func ifTaskIsNotCanceledClearCompactBlockCache() async {
|
|
749
|
+
guard !Task.isCancelled else { return }
|
|
750
|
+
do {
|
|
751
|
+
try await clearCompactBlockCache()
|
|
752
|
+
} catch {
|
|
753
|
+
logger.error("`clearCompactBlockCache` failed after error: \(error)")
|
|
1125
754
|
}
|
|
1126
755
|
}
|
|
756
|
+
|
|
757
|
+
private func clearCompactBlockCache() async throws {
|
|
758
|
+
await stopAllActions()
|
|
759
|
+
try await storage.clear()
|
|
760
|
+
logger.info("Cache removed")
|
|
761
|
+
}
|
|
1127
762
|
}
|
|
1128
763
|
|
|
1129
764
|
extension CompactBlockProcessor {
|
|
@@ -1198,168 +833,17 @@ extension CompactBlockProcessor {
|
|
|
1198
833
|
}
|
|
1199
834
|
}
|
|
1200
835
|
|
|
1201
|
-
|
|
1202
|
-
enum NextState: Equatable {
|
|
1203
|
-
case finishProcessing(height: BlockHeight)
|
|
1204
|
-
case processNewBlocks(ranges: SyncRanges)
|
|
1205
|
-
case wait(latestHeight: BlockHeight, latestDownloadHeight: BlockHeight)
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
@discardableResult
|
|
1209
|
-
func figureNextBatch(
|
|
1210
|
-
downloaderService: BlockDownloaderService
|
|
1211
|
-
) async throws -> NextState {
|
|
1212
|
-
try Task.checkCancellation()
|
|
1213
|
-
|
|
1214
|
-
do {
|
|
1215
|
-
return try await CompactBlockProcessor.NextStateHelper.nextState(
|
|
1216
|
-
service: service,
|
|
1217
|
-
downloaderService: downloaderService,
|
|
1218
|
-
latestBlocksDataProvider: latestBlocksDataProvider,
|
|
1219
|
-
config: config,
|
|
1220
|
-
rustBackend: rustBackend,
|
|
1221
|
-
internalSyncProgress: internalSyncProgress
|
|
1222
|
-
)
|
|
1223
|
-
} catch {
|
|
1224
|
-
throw error
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
extension CompactBlockProcessor {
|
|
1230
|
-
enum NextStateHelper {
|
|
1231
|
-
// swiftlint:disable:next function_parameter_count
|
|
1232
|
-
static func nextState(
|
|
1233
|
-
service: LightWalletService,
|
|
1234
|
-
downloaderService: BlockDownloaderService,
|
|
1235
|
-
latestBlocksDataProvider: LatestBlocksDataProvider,
|
|
1236
|
-
config: Configuration,
|
|
1237
|
-
rustBackend: ZcashRustBackendWelding,
|
|
1238
|
-
internalSyncProgress: InternalSyncProgress
|
|
1239
|
-
) async throws -> CompactBlockProcessor.NextState {
|
|
1240
|
-
// It should be ok to not create new Task here because this method is already async. But for some reason something not good happens
|
|
1241
|
-
// when Task is not created here. For example tests start failing. Reason is unknown at this time.
|
|
1242
|
-
let task = Task(priority: .userInitiated) {
|
|
1243
|
-
let info = try await service.getInfo()
|
|
1244
|
-
|
|
1245
|
-
try await CompactBlockProcessor.validateServerInfo(
|
|
1246
|
-
info,
|
|
1247
|
-
saplingActivation: config.saplingActivation,
|
|
1248
|
-
localNetwork: config.network,
|
|
1249
|
-
rustBackend: rustBackend
|
|
1250
|
-
)
|
|
1251
|
-
|
|
1252
|
-
let latestDownloadHeight = try await downloaderService.lastDownloadedBlockHeight()
|
|
1253
|
-
|
|
1254
|
-
await internalSyncProgress.migrateIfNeeded(latestDownloadedBlockHeightFromCacheDB: latestDownloadHeight)
|
|
1255
|
-
|
|
1256
|
-
await latestBlocksDataProvider.updateScannedData()
|
|
1257
|
-
await latestBlocksDataProvider.updateBlockData()
|
|
1258
|
-
|
|
1259
|
-
return await internalSyncProgress.computeNextState(
|
|
1260
|
-
latestBlockHeight: latestBlocksDataProvider.latestBlockHeight,
|
|
1261
|
-
latestScannedHeight: latestBlocksDataProvider.latestScannedHeight,
|
|
1262
|
-
walletBirthday: config.walletBirthday
|
|
1263
|
-
)
|
|
1264
|
-
}
|
|
836
|
+
// MARK: - Config provider
|
|
1265
837
|
|
|
1266
|
-
return try await task.value
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
/// This extension contains asociated types and functions needed to clean up the
|
|
1272
|
-
/// `cacheDb` in favor of `FsBlockDb`. Once this cleanup functionality is deprecated,
|
|
1273
|
-
/// delete the whole extension and reference to it in other parts of the code including tests.
|
|
1274
838
|
extension CompactBlockProcessor {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
case failedToSetDownloadHeight(Error)
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
/// Deletes the SQLite cacheDb and attempts to initialize the fsBlockDbRoot
|
|
1283
|
-
/// - parameter legacyCacheDbURL: the URL where the cache Db used to be stored.
|
|
1284
|
-
/// - Throws: `InitializerError.fsCacheInitFailedSameURL` when the given URL
|
|
1285
|
-
/// is the same URL than the one provided as `self.fsBlockDbRoot` assuming that's a
|
|
1286
|
-
/// programming error being the `legacyCacheDbURL` a sqlite database file and not a
|
|
1287
|
-
/// directory. Also throws errors from initializing the fsBlockDbRoot.
|
|
1288
|
-
///
|
|
1289
|
-
/// - Note: Errors from deleting the `legacyCacheDbURL` won't be throwns.
|
|
1290
|
-
func migrateCacheDb(_ legacyCacheDbURL: URL) async throws {
|
|
1291
|
-
guard legacyCacheDbURL != config.fsBlockCacheRoot else {
|
|
1292
|
-
throw ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
// Instance with alias `default` is same as instance before the Alias was introduced. So it makes sense that only this instance handles
|
|
1296
|
-
// legacy cache DB. Any instance with different than `default` alias was created after the Alias was introduced and at this point legacy
|
|
1297
|
-
// cache DB is't anymore. So there is nothing to migrate for instances with not default Alias.
|
|
1298
|
-
guard config.alias == .default else {
|
|
1299
|
-
return
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
// if the URL provided is not readable, it means that the client has a reference
|
|
1303
|
-
// to the cacheDb file but it has been deleted in a prior sync cycle. there's
|
|
1304
|
-
// nothing to do here.
|
|
1305
|
-
guard FileManager.default.isReadableFile(atPath: legacyCacheDbURL.path) else {
|
|
1306
|
-
return
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
do {
|
|
1310
|
-
// if there's a readable file at the provided URL, delete it.
|
|
1311
|
-
try FileManager.default.removeItem(at: legacyCacheDbURL)
|
|
1312
|
-
} catch {
|
|
1313
|
-
throw ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(error)
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
// create the storage
|
|
1317
|
-
try await self.storage.create()
|
|
1318
|
-
|
|
1319
|
-
// The database has been deleted, so we have adjust the internal state of the
|
|
1320
|
-
// `CompactBlockProcessor` so that it doesn't rely on download heights set
|
|
1321
|
-
// by a previous processing cycle.
|
|
1322
|
-
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
|
|
1323
|
-
|
|
1324
|
-
await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
func wipeLegacyCacheDbIfNeeded() {
|
|
1328
|
-
guard let cacheDbURL = config.cacheDbURL else {
|
|
1329
|
-
return
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
guard FileManager.default.isDeletableFile(atPath: cacheDbURL.pathExtension) else {
|
|
1333
|
-
return
|
|
839
|
+
actor ConfigProvider {
|
|
840
|
+
var config: Configuration
|
|
841
|
+
init(config: Configuration) {
|
|
842
|
+
self.config = config
|
|
1334
843
|
}
|
|
1335
844
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
extension SyncRanges {
|
|
1341
|
-
/// Tells whether the state represented by these sync ranges evidence some sort of
|
|
1342
|
-
/// outdated state on the cache or the internal state of the compact block processor.
|
|
1343
|
-
///
|
|
1344
|
-
/// - Note: this can mean that the processor has synced over the height that the internal
|
|
1345
|
-
/// state knows of because the sync process was interrupted before it could reflect
|
|
1346
|
-
/// it in the internal state storage. This could happen because of many factors, the
|
|
1347
|
-
/// most feasible being OS shutting down a background process or the user abruptly
|
|
1348
|
-
/// exiting the app.
|
|
1349
|
-
/// - Returns: an ``Optional<BlockHeight>`` where Some represents what's the
|
|
1350
|
-
/// new state the internal state should reflect and indicating that the cache should be cleared
|
|
1351
|
-
/// as well. c`None` means that no action is required.
|
|
1352
|
-
func shouldClearBlockCacheAndUpdateInternalState() -> BlockHeight? {
|
|
1353
|
-
guard self.downloadedButUnscannedRange != nil else {
|
|
1354
|
-
return nil
|
|
845
|
+
func update(config: Configuration) async {
|
|
846
|
+
self.config = config
|
|
1355
847
|
}
|
|
1356
|
-
|
|
1357
|
-
guard
|
|
1358
|
-
let latestScannedHeight = self.latestScannedHeight,
|
|
1359
|
-
let latestDownloadedHeight = self.latestDownloadedBlockHeight,
|
|
1360
|
-
latestScannedHeight > latestDownloadedHeight
|
|
1361
|
-
else { return nil }
|
|
1362
|
-
|
|
1363
|
-
return latestScannedHeight
|
|
1364
848
|
}
|
|
1365
849
|
}
|