diff --git a/.travis.yml b/.travis.yml index 5db0a059..7e704432 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: swift os: osx -osx_image: xcode13.4 +osx_image: xcode14 xcode_project: ./Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj xcode_scheme: ZcashLightClientSample xcode_destination: platform=iOS Simulator,OS=15.5,name=iPhone 8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 72cc00d8..b8a6552d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,34 @@ # 0.17.0-alpha.1 See MIGRATING.md +# 0.16-13-beta +- [#597] SDK does not build with SQLite 0.14 +# 0.16.12-beta +Checkpoints added: +Mainnet +```` +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1832500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1835000.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1837500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1840000.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1842500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1845000.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1847500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1850000.json +```` +# 0.16.11-beta +Checkpoints added: +Mainnet +```` +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1812500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1815000.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1817500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1820000.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1822500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1825000.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1827500.json +Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1830000.json +```` # 0.16.10-beta - [#532] [0.16.x-beta] Download does not stop correctly diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cff90a2b..331c202e 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,149 +1,151 @@ { - "pins" : [ - { - "identity" : "grpc-swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/grpc/grpc-swift.git", - "state" : { - "revision" : "466cc881f1760ed8c0e685900ed62dab7846a571", - "version" : "1.8.0" + "object": { + "pins": [ + { + "package": "grpc-swift", + "repositoryURL": "https://github.com/grpc/grpc-swift.git", + "state": { + "branch": null, + "revision": "466cc881f1760ed8c0e685900ed62dab7846a571", + "version": "1.8.0" + } + }, + { + "package": "KRActivityIndicatorView", + "repositoryURL": "https://github.com/krimpedance/KRActivityIndicatorView.git", + "state": { + "branch": null, + "revision": "bcb0e841d6de0cd343a32bd5056580a56d06c0bc", + "version": "3.0.7" + } + }, + { + "package": "KRProgressHUD", + "repositoryURL": "https://github.com/krimpedance/KRProgressHUD.git", + "state": { + "branch": null, + "revision": "265142816d8f8ea93840accaf4ac7c49998e77c2", + "version": "3.4.7" + } + }, + { + "package": "MnemonicSwift", + "repositoryURL": "https://github.com/zcash-hackworks/MnemonicSwift.git", + "state": { + "branch": null, + "revision": "27711179a75a1172d6f04ceb5d86419cf0cba401", + "version": "2.1.0" + } + }, + { + "package": "NotificationBubbles", + "repositoryURL": "https://github.com/pacu/NotificationBubbles.git", + "state": { + "branch": null, + "revision": "ae6d47f3a415c9eec5daa8e04d040c0e68654f00", + "version": "1.0.0" + } + }, + { + "package": "PaginatedTableView", + "repositoryURL": "https://github.com/dh-ecc/PaginatedTableView", + "state": { + "branch": "master", + "revision": "a3fd9f079d6c9ac3095ee3ef2369a68c29ba04db", + "version": null + } + }, + { + "identity" : "sqlite.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/stephencelis/SQLite.swift.git", + "state" : { + "revision" : "60a65015f6402b7c34b9a924f755ca0a73afeeaa", + "version" : "0.13.1" + } + }, + { + "package": "swift-crypto", + "repositoryURL": "https://github.com/apple/swift-crypto.git", + "state": { + "branch": null, + "revision": "a8911e0fadc25aef1071d582355bd1037a176060", + "version": "2.0.4" + } + }, + { + "package": "swift-log", + "repositoryURL": "https://github.com/apple/swift-log.git", + "state": { + "branch": null, + "revision": "5d66f7ba25daf4f94100e7022febf3c75e37a6c7", + "version": "1.4.2" + } + }, + { + "package": "swift-nio", + "repositoryURL": "https://github.com/apple/swift-nio.git", + "state": { + "branch": null, + "revision": "51c3fc2e4a0fcdf4a25089b288dd65b73df1b0ef", + "version": "2.37.0" + } + }, + { + "package": "swift-nio-extras", + "repositoryURL": "https://github.com/apple/swift-nio-extras.git", + "state": { + "branch": null, + "revision": "f73ca5ee9c6806800243f1ac415fcf82de9a4c91", + "version": "1.10.2" + } + }, + { + "package": "swift-nio-http2", + "repositoryURL": "https://github.com/apple/swift-nio-http2.git", + "state": { + "branch": null, + "revision": "108ac15087ea9b79abb6f6742699cf31de0e8772", + "version": "1.22.0" + } + }, + { + "package": "swift-nio-ssl", + "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", + "state": { + "branch": null, + "revision": "52a486ff6de9bc3e26bf634c5413c41c5fa89ca5", + "version": "2.17.2" + } + }, + { + "package": "swift-nio-transport-services", + "repositoryURL": "https://github.com/apple/swift-nio-transport-services.git", + "state": { + "branch": null, + "revision": "8ab824b140d0ebcd87e9149266ddc353e3705a3e", + "version": "1.11.4" + } + }, + { + "package": "SwiftProtobuf", + "repositoryURL": "https://github.com/apple/swift-protobuf.git", + "state": { + "branch": null, + "revision": "e1499bc69b9040b29184f7f2996f7bab467c1639", + "version": "1.19.0" + } + }, + { + "identity" : "zcash-light-client-ffi", + "kind" : "remoteSourceControl", + "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", + "state" : { + "revision" : "4043e011f4d6a39b7671d48ca4a54c0085603b76", + "version" : "0.1.0-beta.1" + } } - }, - { - "identity" : "kractivityindicatorview", - "kind" : "remoteSourceControl", - "location" : "https://github.com/krimpedance/KRActivityIndicatorView.git", - "state" : { - "revision" : "bcb0e841d6de0cd343a32bd5056580a56d06c0bc", - "version" : "3.0.7" - } - }, - { - "identity" : "krprogresshud", - "kind" : "remoteSourceControl", - "location" : "https://github.com/krimpedance/KRProgressHUD.git", - "state" : { - "revision" : "265142816d8f8ea93840accaf4ac7c49998e77c2", - "version" : "3.4.7" - } - }, - { - "identity" : "mnemonicswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/zcash-hackworks/MnemonicSwift.git", - "state" : { - "revision" : "716a2c32ac2bbd8a1499ac834077df42b75edc85", - "version" : "2.2.4" - } - }, - { - "identity" : "notificationbubbles", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pacu/NotificationBubbles.git", - "state" : { - "revision" : "ae6d47f3a415c9eec5daa8e04d040c0e68654f00", - "version" : "1.0.0" - } - }, - { - "identity" : "paginatedtableview", - "kind" : "remoteSourceControl", - "location" : "https://github.com/dh-ecc/PaginatedTableView", - "state" : { - "branch" : "master", - "revision" : "a3fd9f079d6c9ac3095ee3ef2369a68c29ba04db" - } - }, - { - "identity" : "sqlite.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/stephencelis/SQLite.swift.git", - "state" : { - "revision" : "60a65015f6402b7c34b9a924f755ca0a73afeeaa", - "version" : "0.13.1" - } - }, - { - "identity" : "swift-crypto", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-crypto.git", - "state" : { - "revision" : "d9825fa541df64b1a7b182178d61b9a82730d01f", - "version" : "2.1.0" - } - }, - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", - "state" : { - "revision" : "5d66f7ba25daf4f94100e7022febf3c75e37a6c7", - "version" : "1.4.2" - } - }, - { - "identity" : "swift-nio", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio.git", - "state" : { - "revision" : "51c3fc2e4a0fcdf4a25089b288dd65b73df1b0ef", - "version" : "2.37.0" - } - }, - { - "identity" : "swift-nio-extras", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-extras.git", - "state" : { - "revision" : "f73ca5ee9c6806800243f1ac415fcf82de9a4c91", - "version" : "1.10.2" - } - }, - { - "identity" : "swift-nio-http2", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-http2.git", - "state" : { - "revision" : "108ac15087ea9b79abb6f6742699cf31de0e8772", - "version" : "1.22.0" - } - }, - { - "identity" : "swift-nio-ssl", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-ssl.git", - "state" : { - "revision" : "52a486ff6de9bc3e26bf634c5413c41c5fa89ca5", - "version" : "2.17.2" - } - }, - { - "identity" : "swift-nio-transport-services", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-nio-transport-services.git", - "state" : { - "revision" : "8ab824b140d0ebcd87e9149266ddc353e3705a3e", - "version" : "1.11.4" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639", - "version" : "1.19.0" - } - }, - { - "identity" : "zcash-light-client-ffi", - "kind" : "remoteSourceControl", - "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi", - "state" : { - "revision" : "4043e011f4d6a39b7671d48ca4a54c0085603b76", - "version" : "0.1.0-beta.1" - } - } - ], - "version" : 2 + ] + }, + "version": 1 } diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift index fd589065..1c4a07cb 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift @@ -16,6 +16,7 @@ enum DemoAppConfig { static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000 static var seed = try! Mnemonic.deterministicSeedBytes(from: "live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor") + static var address: String { "\(host):\(port)" } diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift index 2d9dadfc..38bb94fa 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift @@ -28,11 +28,13 @@ class GetUTXOsViewController: UIViewController { self.transparentAddressLabel.text = tAddress - // swiftlint:disable:next force_try - let balance = try! synchronizer.getTransparentBalance(accountIndex: 0) - - self.totalBalanceLabel.text = NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: balance.total.amount)) - self.verifiedBalanceLabel.text = NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: balance.verified.amount)) + Task { @MainActor in + // swiftlint:disable:next force_try + let balance = try! await AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0) + + self.totalBalanceLabel.text = NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: balance.total.amount)) + self.verifiedBalanceLabel.text = NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: balance.verified.amount)) + } } @IBAction func shieldFunds(_ sender: Any) { diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift index 53cd29c0..9768db81 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift @@ -33,30 +33,27 @@ class SendViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() synchronizer = AppDelegate.shared.sharedSynchronizer - // swiftlint:disable:next force_try - _ = try! synchronizer.prepare(with: DemoAppConfig.seed) let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped(_:))) self.view.addGestureRecognizer(tapRecognizer) setUp() + Task { @MainActor in + // swiftlint:disable:next force_try + try! await synchronizer.prepare() + } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - do { - try synchronizer.start(retry: false) - self.synchronizerStatusLabel.text = SDKSynchronizer.textFor(state: synchronizer.status) - } catch { - self.synchronizerStatusLabel.text = SDKSynchronizer.textFor(state: synchronizer.status) - fail(error) + Task { @MainActor in + do { + try await synchronizer.start(retry: false) + self.synchronizerStatusLabel.text = SDKSynchronizer.textFor(state: synchronizer.status) + } catch { + self.synchronizerStatusLabel.text = SDKSynchronizer.textFor(state: synchronizer.status) + fail(error) + } } } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - - // swiftlint:disable:next force_try - try! synchronizer.stop() // Fail on purpose if this throws. - } @objc func viewTapped(_ recognizer: UITapGestureRecognizer) { let point = recognizer.location(in: self.view) diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Sync Blocks/SyncBlocksViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Sync Blocks/SyncBlocksViewController.swift index 72b547b9..a5dd9943 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Sync Blocks/SyncBlocksViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Sync Blocks/SyncBlocksViewController.swift @@ -31,7 +31,9 @@ class SyncBlocksViewController: UIViewController { // swiftlint:disable:next force_try _ = try! wallet.initialize(with: DemoAppConfig.seed) processor = CompactBlockProcessor(initializer: wallet) - statusLabel.text = textFor(state: processor?.state.getState() ?? .stopped) + Task { @MainActor in + statusLabel.text = textFor(state: await processor?.state ?? .stopped) + } progressBar.progress = 0 NotificationCenter.default.addObserver( @@ -47,14 +49,16 @@ class SyncBlocksViewController: UIViewController { NotificationCenter.default.removeObserver(self) guard let processor = self.processor else { return } - processor.stop() + Task { + await processor.stop() + } } @objc func processorNotification(_ notification: Notification) { - DispatchQueue.main.async { + Task { @MainActor in guard self.processor != nil else { return } - self.updateUI() + await self.updateUI() switch notification.name { case let not where not == Notification.Name.blockProcessorUpdated: @@ -70,30 +74,28 @@ class SyncBlocksViewController: UIViewController { @IBAction func startStop() { guard let processor = processor else { return } - switch processor.state.getState() { - case .stopped: - startProcessor() - default: - stopProcessor() + Task { @MainActor in + switch await processor.state { + case .stopped: + await startProcessor() + default: + await stopProcessor() + } } } - func startProcessor() { + func startProcessor() async { guard let processor = processor else { return } - do { - try processor.start() - updateUI() - } catch { - fail(error: error) - } + await processor.start() + await updateUI() } - func stopProcessor() { + func stopProcessor() async { guard let processor = processor else { return } - processor.stop() - updateUI() + await processor.stop() + await updateUI() } func fail(error: Error) { @@ -110,11 +112,13 @@ class SyncBlocksViewController: UIViewController { ) self.present(alert, animated: true, completion: nil) - updateUI() + Task { @MainActor in + await updateUI() + } } - func updateUI() { - guard let state = processor?.state.getState() else { return } + func updateUI() async { + guard let state = await processor?.state else { return } statusLabel.text = textFor(state: state) startPause.setTitle(buttonText(for: state), for: .normal) diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockDownload.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockDownload.swift index 4a78030e..f02b8e07 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockDownload.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockDownload.swift @@ -16,7 +16,7 @@ extension CompactBlockProcessor { ) async throws { try Task.checkCancellation() - setState(.downloading) + state = .downloading var buffer: [ZcashCompactBlock] = [] var targetHeightInternal: BlockHeight? diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockEnhancement.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockEnhancement.swift index 008a0101..2fab29cc 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockEnhancement.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockEnhancement.swift @@ -56,8 +56,8 @@ extension CompactBlockProcessor { try Task.checkCancellation() LoggerProxy.debug("Started Enhancing range: \(range)") - setState(.enhancing) - + state = .enhancing + let blockRange = range.blockRange() var retries = 0 let maxRetries = 5 diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift index 112f5482..d5c3c4f8 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift @@ -103,7 +103,7 @@ public enum CompactBlockProgress { } protocol EnhancementStreamDelegate: AnyObject { - func transactionEnhancementProgressUpdated(_ progress: EnhancementProgress) + func transactionEnhancementProgressUpdated(_ progress: EnhancementProgress) async } public protocol EnhancementProgress { @@ -213,7 +213,7 @@ public extension Notification.Name { /// The compact block processor is in charge of orchestrating the download and caching of compact blocks from a LightWalletEndpoint /// when started the processor downloads does a download - validate - scan cycle until it reaches latest height on the blockchain. -public class CompactBlockProcessor { +public actor CompactBlockProcessor { /// Compact Block Processor configuration /// @@ -312,51 +312,13 @@ public class CompactBlockProcessor { case synced } - // TODO: this isn't an Actor even though it looks like a good candidate, the reason: - // `state` lives in both sync and async environments. An Actor is demanding async context only - // so we can't take the advantage unless we encapsulate all `state` reads/writes to async context. - // Therefore solution with class + lock works for us butr eventually will be replaced. - // The future of CompactBlockProcessor is an actor (we won't need to encapsulate the state separately), issue 523, - // https://github.com/zcash/ZcashLightClientKit/issues/523 - public class ThreadSafeState { - private var state: State = .stopped - let lock = NSLock() - - func setState(_ newState: State) { - lock.lock() - defer { lock.unlock() } - state = newState - } - - public func getState() -> State { - lock.lock() - defer { lock.unlock() } - return state + public internal(set) var state: State = .stopped { + didSet { + transitionState(from: oldValue, to: self.state) } } - public internal(set) var state = ThreadSafeState() - - /// This will be gone when processor becomes actor. - public class ThreadSafeNeedsToStartScanning { - private var needsToStartScanning = false - let lock = NSLock() - - func set(_ newValue: Bool) { - lock.lock() - defer { lock.unlock() } - needsToStartScanning = newValue - } - - public func get() -> Bool { - lock.lock() - defer { lock.unlock() } - return needsToStartScanning - } - } - - private let needsToStartScanningWhenStopped = ThreadSafeNeedsToStartScanning() - + private var needsToStartScanningWhenStopped = false var config: Configuration { willSet { self.stop() @@ -368,7 +330,7 @@ public class CompactBlockProcessor { } var shouldStart: Bool { - switch self.state.getState() { + switch self.state { case .stopped, .synced, .error: return !maxAttemptsReached default: @@ -406,7 +368,7 @@ public class CompactBlockProcessor { /// - storage: concrete implementation of `CompactBlockStorage` protocol /// - backend: a class that complies to `ZcashRustBackendWelding` /// - config: `Configuration` struct for this processor - convenience init( + init( service: LightWalletService, storage: CompactBlockStorage, backend: ZcashRustBackendWelding.Type, @@ -427,7 +389,7 @@ public class CompactBlockProcessor { /// Initializes a CompactBlockProcessor instance from an Initialized object /// - Parameters: /// - initializer: an instance that complies to CompactBlockDownloading protocol - public convenience init(initializer: Initializer) { + public init(initializer: Initializer) { self.init( service: initializer.lightWalletService, storage: initializer.storage, @@ -468,12 +430,6 @@ public class CompactBlockProcessor { cancelableTask?.cancel() } - func setState(_ newState: State) { - let oldValue = state.getState() - state.setState(newState) - transitionState(from: oldValue, to: newState) - } - static func validateServerInfo( _ info: LightWalletdInfo, saplingActivation: BlockHeight, @@ -520,7 +476,7 @@ public class CompactBlockProcessor { /// triggers the blockProcessorStartedDownloading notification /// /// - Important: subscribe to the notifications before calling this method - public func start(retry: Bool = false) throws { + public func start(retry: Bool = false) async { if retry { self.retryAttempts = 0 self.processingError = nil @@ -529,12 +485,12 @@ public class CompactBlockProcessor { } guard shouldStart else { - switch self.state.getState() { + switch self.state { case .error(let e): // max attempts have been reached LoggerProxy.info("max retry attempts reached with error: \(e)") notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts)) - setState(.stopped) + state = .stopped case .stopped: // max attempts have been reached LoggerProxy.info("max retry attempts reached") @@ -545,12 +501,12 @@ public class CompactBlockProcessor { notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts)) case .downloading, .validating, .scanning, .enhancing, .fetching: LoggerProxy.debug("Warning: compact block processor was started while busy!!!!") - needsToStartScanningWhenStopped.set(true) + self.`needsToStartScanningWhenStopped` = true } return } - self.nextBatch() + await self.nextBatch() } /** @@ -571,7 +527,7 @@ public class CompactBlockProcessor { Rewinds to provided height. If nil is provided, it will rescan to nearest height (quick rescan) */ - public func rewindTo(_ height: BlockHeight?) throws -> BlockHeight { + public func rewindTo(_ height: BlockHeight?) async throws -> BlockHeight { self.stop() let lastDownloaded = try downloader.lastDownloadedBlockHeight() @@ -586,7 +542,7 @@ public class CompactBlockProcessor { let error = rustBackend.lastError() ?? RustWeldingError.genericError( message: "unknown error getting nearest rewind height for height: \(height)" ) - fail(error) + await fail(error) throw error } @@ -594,7 +550,7 @@ public class CompactBlockProcessor { let rewindHeight = max(Int32(nearestHeight - 1), Int32(config.walletBirthday)) guard rustBackend.rewindToHeight(dbData: config.dataDb, height: rewindHeight, networkType: self.config.network.networkType) else { let error = rustBackend.lastError() ?? RustWeldingError.genericError(message: "unknown error rewinding to height \(height)") - fail(error) + await fail(error) throw error } @@ -611,7 +567,7 @@ public class CompactBlockProcessor { - Throws CompactBlockProcessorError.invalidConfiguration if block height is invalid or if processor is already started */ func setStartHeight(_ startHeight: BlockHeight) throws { - guard self.state.getState() == .stopped, startHeight >= config.network.constants.saplingActivationHeight else { + guard self.state == .stopped, startHeight >= config.network.constants.saplingActivationHeight else { throw CompactBlockProcessorError.invalidConfiguration } @@ -620,32 +576,29 @@ public class CompactBlockProcessor { self.config = config } - func validateServer(completionBlock: @escaping (() -> Void)) { - Task { @MainActor in - do { - let info = try await self.service.getInfo() - try Self.validateServerInfo( - info, - saplingActivation: self.config.saplingActivation, - localNetwork: self.config.network, - rustBackend: self.rustBackend - ) - completionBlock() - } catch let error as LightWalletServiceError { - self.severeFailure(error.mapToProcessorError()) - } catch { - self.severeFailure(error) - } + func validateServer() async { + do { + let info = try await self.service.getInfo() + try Self.validateServerInfo( + info, + saplingActivation: self.config.saplingActivation, + localNetwork: self.config.network, + rustBackend: self.rustBackend + ) + } catch let error as LightWalletServiceError { + self.severeFailure(error.mapToProcessorError()) + } catch { + self.severeFailure(error) } } /// Processes new blocks on the given range based on the configuration set for this instance - func processNewBlocks(range: CompactBlockRange) { + func processNewBlocks(range: CompactBlockRange) async { self.foundBlocks = true self.backoffTimer?.invalidate() self.backoffTimer = nil - cancelableTask = Task(priority: .userInitiated) { [weak self] in + cancelableTask = Task(priority: .userInitiated) { do { try await compactBlockStreamDownload( blockBufferSize: config.downloadBufferSize, @@ -657,13 +610,13 @@ public class CompactBlockProcessor { try await compactBlockEnhancement(range: range) try await fetchUnspentTxOutputs(range: range) } catch { - if Task.isCancelled { - setState(.stopped) - if self?.needsToStartScanningWhenStopped.get() ?? false { - self?.nextBatch() - } + if !(Task.isCancelled) { + await fail(error) } else { - fail(error) + state = .stopped + if needsToStartScanningWhenStopped { + await nextBatch() + } } } } @@ -682,7 +635,7 @@ public class CompactBlockProcessor { LoggerProxy.debug("progress: \(progress)") - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: Notification.Name.blockProcessorUpdated, object: self, userInfo: userInfo @@ -690,7 +643,7 @@ public class CompactBlockProcessor { } func notifyTransactions(_ txs: [ConfirmedTransactionEntity], in range: BlockRange) { - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .blockProcessorFoundTransactions, object: self, userInfo: [ @@ -715,29 +668,29 @@ public class CompactBlockProcessor { self.backoffTimer?.invalidate() self.retryAttempts = config.retries self.processingError = error - setState(.error(error)) + state = .error(error) self.notifyError(error) } - func fail(_ error: Error) { + func fail(_ error: Error) async { // todo specify: failure LoggerProxy.error("\(error)") cancelableTask?.cancel() self.retryAttempts += 1 self.processingError = error - switch self.state.getState() { + switch self.state { case .error: notifyError(error) default: break } - setState(.error(error)) + state = .error(error) guard self.maxAttemptsReached else { return } // don't set a new timer if there are no more attempts. - self.setTimer() + await self.setTimer() } - func retryProcessing(range: CompactBlockRange) { + func retryProcessing(range: CompactBlockRange) async { cancelableTask?.cancel() // update retries self.retryAttempts += 1 @@ -752,10 +705,9 @@ public class CompactBlockProcessor { try downloader.rewind(to: max(range.lowerBound, self.config.walletBirthday)) // process next batch - // processNewBlocks(range: Self.nextBatchBlockRange(latestHeight: latestBlockHeight, latestDownloadedHeight: try downloader.lastDownloadedBlockHeight(), walletBirthday: config.walletBirthday)) - nextBatch() + await nextBatch() } catch { - self.fail(error) + await self.fail(error) } } @@ -790,41 +742,39 @@ public class CompactBlockProcessor { } } - private func nextBatch() { - setState(.downloading) - Task { @MainActor [self] in - do { - let nextState = try await NextStateHelper.nextStateAsync( - service: self.service, - downloader: self.downloader, - config: self.config, - rustBackend: self.rustBackend + private func nextBatch() async { + state = .downloading + do { + let nextState = try await NextStateHelper.nextStateAsync( + service: self.service, + downloader: self.downloader, + config: self.config, + rustBackend: self.rustBackend + ) + switch nextState { + case .finishProcessing(let height): + self.latestBlockHeight = height + await self.processingFinished(height: height) + case .processNewBlocks(let range): + self.latestBlockHeight = range.upperBound + self.lowerBoundHeight = range.lowerBound + await self.processNewBlocks(range: range) + case let .wait(latestHeight, latestDownloadHeight): + // Lightwalletd might be syncing + self.lowerBoundHeight = latestDownloadHeight + self.latestBlockHeight = latestHeight + LoggerProxy.info( + "Lightwalletd might be syncing: latest downloaded block height is: \(latestDownloadHeight)" + + "while latest blockheight is reported at: \(latestHeight)" ) - switch nextState { - case .finishProcessing(let height): - self.latestBlockHeight = height - self.processingFinished(height: height) - case .processNewBlocks(let range): - self.latestBlockHeight = range.upperBound - self.lowerBoundHeight = range.lowerBound - self.processNewBlocks(range: range) - case let .wait(latestHeight, latestDownloadHeight): - // Lightwalletd might be syncing - self.lowerBoundHeight = latestDownloadHeight - self.latestBlockHeight = latestHeight - LoggerProxy.info( - "Lightwalletd might be syncing: latest downloaded block height is: \(latestDownloadHeight)" + - "while latest blockheight is reported at: \(latestHeight)" - ) - self.processingFinished(height: latestDownloadHeight) - } - } catch { - self.severeFailure(error) + await self.processingFinished(height: latestDownloadHeight) } + } catch { + self.severeFailure(error) } } - internal func validationFailed(at height: BlockHeight) { + internal func validationFailed(at height: BlockHeight) async { // cancel all Tasks cancelableTask?.cancel() @@ -840,7 +790,7 @@ public class CompactBlockProcessor { ) guard rustBackend.rewindToHeight(dbData: config.dataDb, height: Int32(rewindHeight), networkType: self.config.network.networkType) else { - fail(rustBackend.lastError() ?? RustWeldingError.genericError(message: "unknown error rewinding to height \(height)")) + await fail(rustBackend.lastError() ?? RustWeldingError.genericError(message: "unknown error rewinding to height \(height)")) return } @@ -848,7 +798,7 @@ public class CompactBlockProcessor { try downloader.rewind(to: rewindHeight) // notify reorg - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: Notification.Name.blockProcessorHandledReOrg, object: self, userInfo: [ @@ -857,15 +807,15 @@ public class CompactBlockProcessor { ) // process next batch - self.nextBatch() + await self.nextBatch() } catch { - self.fail(error) + await self.fail(error) } } - internal func processBatchFinished(range: CompactBlockRange) { + internal func processBatchFinished(range: CompactBlockRange) async { guard processingError == nil else { - retryProcessing(range: range) + await retryProcessing(range: range) return } @@ -873,15 +823,15 @@ public class CompactBlockProcessor { consecutiveChainValidationErrors = 0 guard !range.isEmpty else { - processingFinished(height: range.upperBound) + await processingFinished(height: range.upperBound) return } - nextBatch() + await nextBatch() } - private func processingFinished(height: BlockHeight) { - NotificationCenter.default.post( + private func processingFinished(height: BlockHeight) async { + NotificationCenter.default.mainThreadPost( name: Notification.Name.blockProcessorFinished, object: self, userInfo: [ @@ -889,40 +839,38 @@ public class CompactBlockProcessor { CompactBlockProcessorNotificationKey.foundBlocks: self.foundBlocks ] ) - setState(.synced) - setTimer() - NotificationCenter.default.post( + state = .synced + await setTimer() + NotificationCenter.default.mainThreadPost( name: Notification.Name.blockProcessorIdle, object: self, userInfo: nil ) } - private func setTimer() { + private func setTimer() async { let interval = self.config.blockPollInterval self.backoffTimer?.invalidate() let timer = Timer( timeInterval: interval, repeats: true, block: { [weak self] _ in - guard let self = self else { return } - do { - if self.shouldStart { + Task { [self] in + guard let self = self else { return } + if await self.shouldStart { LoggerProxy.debug( - """ - Timer triggered: Starting compact Block processor!. - Processor State: \(self.state) - latestHeight: \(self.latestBlockHeight) - attempts: \(self.retryAttempts) - lowerbound: \(String(describing: self.lowerBoundHeight)) - """ + """ + Timer triggered: Starting compact Block processor!. + Processor State: \(await self.state) + latestHeight: \(await self.latestBlockHeight) + attempts: \(await self.retryAttempts) + lowerbound: \(String(describing: await self.lowerBoundHeight)) + """ ) - try self.start() - } else if self.maxAttemptsReached { - self.fail(CompactBlockProcessorError.maxAttemptsReached(attempts: self.config.retries)) + await self.start() + } else if await self.maxAttemptsReached { + await self.fail(CompactBlockProcessorError.maxAttemptsReached(attempts: self.config.retries)) } - } catch { - self.fail(error) } } ) @@ -936,7 +884,7 @@ public class CompactBlockProcessor { return } - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .blockProcessorStatusChanged, object: self, userInfo: [ @@ -947,27 +895,27 @@ public class CompactBlockProcessor { switch newValue { case .downloading: - NotificationCenter.default.post(name: Notification.Name.blockProcessorStartedDownloading, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.blockProcessorStartedDownloading, object: self) case .synced: // transition to this state is handled by `processingFinished(height: BlockHeight)` break case .error(let err): notifyError(err) case .scanning: - NotificationCenter.default.post(name: Notification.Name.blockProcessorStartedScanning, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.blockProcessorStartedScanning, object: self) case .stopped: - NotificationCenter.default.post(name: Notification.Name.blockProcessorStopped, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.blockProcessorStopped, object: self) case .validating: - NotificationCenter.default.post(name: Notification.Name.blockProcessorStartedValidating, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.blockProcessorStartedValidating, object: self) case .enhancing: - NotificationCenter.default.post(name: Notification.Name.blockProcessorStartedEnhancing, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.blockProcessorStartedEnhancing, object: self) case .fetching: - NotificationCenter.default.post(name: Notification.Name.blockProcessorStartedFetching, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.blockProcessorStartedFetching, object: self) } } private func notifyError(_ err: Error) { - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: Notification.Name.blockProcessorFailed, object: self, userInfo: [CompactBlockProcessorNotificationKey.error: mapError(err)] @@ -1171,7 +1119,7 @@ extension CompactBlockProcessorError: LocalizedError { extension CompactBlockProcessor: EnhancementStreamDelegate { func transactionEnhancementProgressUpdated(_ progress: EnhancementProgress) { - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .blockProcessorEnhancementProgress, object: self, userInfo: [CompactBlockProcessorNotificationKey.enhancementProgress: progress] diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockScanning.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockScanning.swift index 45b2b34c..f4324d86 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockScanning.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockScanning.swift @@ -12,7 +12,7 @@ extension CompactBlockProcessor { func compactBlockBatchScanning(range: CompactBlockRange) async throws { try Task.checkCancellation() - setState(.scanning) + state = .scanning // TODO: remove this arbitrary batch size https://github.com/zcash/ZcashLightClientKit/issues/576 let batchSize = scanBatchSize(for: range, network: self.config.network.networkType) @@ -26,7 +26,7 @@ extension CompactBlockProcessor { throw error } let scanFinishTime = Date() - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPostNotification( SDKMetrics.progressReportNotification( progress: BlockProgress( startHeight: range.lowerBound, @@ -70,7 +70,7 @@ extension CompactBlockProcessor { if scannedNewBlocks { let progress = BlockProgress(startHeight: scanStartHeight, targetHeight: targetScanHeight, progressHeight: lastScannedHeight) notifyProgress(.scan(progress)) - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPostNotification( SDKMetrics.progressReportNotification( progress: progress, start: scanStartTime, @@ -83,9 +83,11 @@ extension CompactBlockProcessor { let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate LoggerProxy.debug("Scanned \(heightCount) blocks in \(seconds) seconds") } + + await Task.yield() } while !Task.isCancelled && scannedNewBlocks && lastScannedHeight < targetScanHeight if Task.isCancelled { - setState(.stopped) + state = .stopped LoggerProxy.debug("Warning: compactBlockBatchScanning cancelled") } } diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockValidationInformation.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockValidationInformation.swift index fccf1fec..f7bf6853 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockValidationInformation.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockValidationInformation.swift @@ -17,7 +17,7 @@ extension CompactBlockProcessor { func compactBlockValidation() async throws { try Task.checkCancellation() - setState(.validating) + state = .validating let result = rustBackend.validateCombinedChain(dbCache: config.cacheDb, dbData: config.dataDb, networkType: config.network.networkType) @@ -30,7 +30,7 @@ extension CompactBlockProcessor { case ZcashRustBackendWeldingConstants.validChain: if Task.isCancelled { - setState(.stopped) + state = .stopped LoggerProxy.debug("Warning: compactBlockValidation cancelled") } LoggerProxy.debug("validateChainFinished") @@ -50,11 +50,11 @@ extension CompactBlockProcessor { switch validationError { case .validationFailed(let height): LoggerProxy.debug("chain validation at height: \(height)") - validationFailed(at: height) + await validationFailed(at: height) case .failedWithError(let err): guard let validationFailure = err else { LoggerProxy.error("validation failed without a specific error") - self.fail(CompactBlockProcessorError.generalError(message: "validation failed without a specific error")) + await self.fail(CompactBlockProcessorError.generalError(message: "validation failed without a specific error")) return } diff --git a/Sources/ZcashLightClientKit/Block/Processor/FetchUnspentTxOutputs.swift b/Sources/ZcashLightClientKit/Block/Processor/FetchUnspentTxOutputs.swift index 108c40a6..b9cc2e31 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/FetchUnspentTxOutputs.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/FetchUnspentTxOutputs.swift @@ -16,7 +16,7 @@ extension CompactBlockProcessor { func fetchUnspentTxOutputs(range: CompactBlockRange) async throws { try Task.checkCancellation() - setState(.fetching) + state = .fetching do { let tAddresses = try accountRepository.getAll() @@ -57,8 +57,8 @@ extension CompactBlockProcessor { } let result = (inserted: refreshed, skipped: skipped) - - NotificationCenter.default.post( + + NotificationCenter.default.mainThreadPost( name: .blockProcessorStoredUTXOs, object: self, userInfo: [CompactBlockProcessorNotificationKey.refreshedUTXOs: result] @@ -67,7 +67,7 @@ extension CompactBlockProcessor { if Task.isCancelled { LoggerProxy.debug("Warning: fetchUnspentTxOutputs on range \(range) cancelled") } else { - processBatchFinished(range: range) + await processBatchFinished(range: range) } } catch { throw error diff --git a/Sources/ZcashLightClientKit/Block/Utils/NotificationCenter+Post.swift b/Sources/ZcashLightClientKit/Block/Utils/NotificationCenter+Post.swift new file mode 100644 index 00000000..5193640b --- /dev/null +++ b/Sources/ZcashLightClientKit/Block/Utils/NotificationCenter+Post.swift @@ -0,0 +1,30 @@ +// +// NotificationCenter+Post.swift +// +// +// Created by Lukáš Korba on 12.10.2022. +// + +import Foundation + +extension NotificationCenter { + func mainThreadPost( + name aName: NSNotification.Name, + object anObject: Any?, + userInfo aUserInfo: [AnyHashable : Any]? = nil + ) { + DispatchQueue.main.async { + NotificationCenter.default.post( + name: aName, + object: anObject, + userInfo: aUserInfo + ) + } + } + + func mainThreadPostNotification(_ notification: Notification) { + DispatchQueue.main.async { + NotificationCenter.default.post(notification) + } + } +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1812500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1812500.json new file mode 100644 index 00000000..72e51cf5 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1812500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1812500", + "hash": "00000000008f6e5ac009390e7daa092a3207581c211e2be70b60ce04b88505c3", + "time": 1663471780, + "saplingTree": "0180d672d65ad56265eeb9632dbe62473447bc7bf3e598d2e896cdcb798887b53d00190139d37c3a694b0104d15e447f51d96800d260a5a1053558cd4b7134b8c77f803400017175873eec18949e09f6df019a3d20530716d2307ba1f00fc0c09bae6656022400000198c1229f3df738745986e8aabc1d3ec92944865b43a910b8c74221f99b8ee0320000014c8a851d7fdb90d17b97621005c826c8b3ce8ee463062e324e2ad886f202b6440158720dd7a931f66eac2ff934b0765f807c9504ea318c18f606831c80feb3770401252adc68c56eae8331764a2d9d72851e7352bd33364963b6618f47d56bc21c6900017d8e001c7be91297f727edf47b650f44cbfbebb535b4f409e357b37c43a621040001ba60d34966fcefe1b8058d28b784f87089e693fde8b99902a4fc956021f7ee04017547ef39c632d9bfd0cf48e3e909dbc2e0890f2791a6929ca0d6674484f4c046000190ca77d38f0a408e33a8633f140441488cdedf7508a06d04771b53e4e4de9861019db47a02109d9232f4c4fa044613a8a0cc508865b185ee28318c86d16829895801acc2d2141fe4e5c43c11710e48bd0b12c0493b1721fad3b03470a4400157e020000001f416eb7e062c981dbbf76f8845fda959b948bc742fc62d9edb2f36bae852ba4e01c5d9822e7aa76d3a758e8dc25ffd8b8c9d4051c66fb92fd7aa263905e238920e0139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e", + "orchardTree": "01cfc873591e05c184abdc8a1a78659e28df418ea5320a5bd81d2d761c1af5ed0a001f01244feb50d761d34ab22b04045ae9eface10df4b17bbded9d1da9fb71e48cf01400018a07d6cf54e4da1fb1f501850cba2a98ff5a8a8a5722896c3906c09447500f26014be19370bcb96d495a7c1e9719ccc057104a66a5ef6ea217f3fe29e26459131d000172482cc1f1fad25e86299334a24a63e6e86c0b04d319a230bd3aba2dd38279110124de7fbfe4a050ae1427d73491bdddedda1e2e7db48d2365b3cb108eb9833b34000000000000018eb08300830f838c7d212857b7e045a05458264f44f33a9f5d726631634da70201778055c0e5ef68c30c7fa81eb67f0cbf32b64dc5d6ff8138f380cb8f115418260000010fb2cb03705826ca086f605974429cfb0dc60df50a0d15d06c2cfef80017902f01c7146e487b3ae97b190ebf93eac554968e683d31115d13fe83dd620859d9a92d000001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1815000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1815000.json new file mode 100644 index 00000000..4805a8fe --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1815000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1815000", + "hash": "0000000000ff971a219416d483ec48af6e055182ab566572fdda7fedb2d15368", + "time": 1663659973, + "saplingTree": "013010baaf80742269ab7061e012c90e4ef805cc07bb62ef71869fe0badc141f6901d003b1d99e5dfc26894e0b1f393399560374425c34f89853df55a4e32be616251901b34b07fcc9a91eb89bc409a51f452bd21327cfdf3328e9913289760e464824710122ccb843dd5775365b143f0cd1cf309c1940a7c3ba7070c92d041f35b8fc3f4601da8ae93a665218fe154683bb6c75e5ded94f1780358591ce7524ca324f22a53601962ea341367529954ac02e5479c129499aae960e8448ec5697f216b45986b3040168c82ca2a31ca7b833886ee0b3dbb60c519a917b2dfcb6590175b38a0cf17944000196c2704fda3d5e124118cc6cbb1514bbdbdff5bab3a334bda2af27b3e1c7f42a0103bdd297e11f1bbf4d438079aefdc797b012e2e90903194011f9f23cd7ed5b510198ba7189c4d8aeaa58402a655214c69bfa4d09c7818a9691629a8643cca9c91d0000016fbc427c11055083f9330cb82d07c7ce7ca37ee43c7bcc687683f6a50cfdfa3e01201e7639e97113adcb84691cca72efb6f8f44293109ccf02f1beabe46412b75c01046ace0bc25d6c3889da89753306be90ce721bc463e9a6662f3ea10141b5db2801ac5fca146985e234d3ae22d84b09f7924d8baeee82fba8235dd9db60db8f6565000001f513025ec4ad6f392fa842db7cb2f0be10e1bc3662046db7cb6d5de314abe33a01ff0944072256da35186a74026d56db14a2c80ffc4dc7a3b2b5a93885c2622b5201471bdfaa9efa8b9adce9654553a07278f7e37156c73b03394ca8288d8983403001bc76949736399cf6d26d21a468ad6c8ee3e563e0ccef01d615c45a91bc9a612e0001f416eb7e062c981dbbf76f8845fda959b948bc742fc62d9edb2f36bae852ba4e01c5d9822e7aa76d3a758e8dc25ffd8b8c9d4051c66fb92fd7aa263905e238920e0139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e", + "orchardTree": "013f012dabdfb54100613c405576e16bbc19a802910fde444e98827f568143ed0c001f010853c8f2d2423d321b9630781d679b884cde7b3160108ddc1233f955e1bd9628000001b8ef98edd02dbc2b74a63778968d66cb1361cc01f7b4434b141ec35c12a2741901c5a9a64d994fbe4c8d044c80f8f551595ff8a33b2bf98857058ab080aa2fbf31000000000116e9a922d1b143d61c100ff15dc2ffdf92e70721aed5a6c37270d7136fb38a1b013dedd8fa148f4930ad5fc0ac019753a21886453af5c94e8f8817a4e13b7ef810000001f69a0bb2a07942810983d469f50f2285c1ba6f0b3355378356cc62a17860cf170175dca061b72ede5cfcb867aa38ce3a37fc84b7586cbeac75d077c5e0eb33b3250001ccf521db485f5532163391b715519775dc86bbb7b938687551a8547a7c81b425010fb2cb03705826ca086f605974429cfb0dc60df50a0d15d06c2cfef80017902f01c7146e487b3ae97b190ebf93eac554968e683d31115d13fe83dd620859d9a92d000001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1817500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1817500.json new file mode 100644 index 00000000..bf6ac1e4 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1817500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1817500", + "hash": "00000000013757a4fc04d1817f08be13cb68212185a21938235d7f9685ef7133", + "time": 1663848803, + "saplingTree": "012b6c4df5794284a452d6486dc5c3a6e15e7daceed97674fb8990242a07d34a1301de28191ca049c8668eb11fb60a6e2d64b496e2c22620a3a3ee31bc0ab496613c190001ddb3f202a4fd2d677c0e4aa44f8f51ab116a7233f6a620ccbc35e27c4043626d0001eda65f389f39694d0a54c893b3d53d32f03b684c3d182198a934834064ed1302000001b235783e1d216027badf69ee580c0890801d7689b52767f0367ab446f7c2ff4701e5cd3f5440dd9f741ff9c12bf94face46d65d4ad396f08944db4b3b8e282b24601a2d8377a97563fdad5693d7e09b1ba3ac87de413e122fb202c826cc46f6d3b0b0000012945d8546f0aca5aef67fef1bf30e463c2fbb03d90e1f505c223f7170739982a0161bd0e7d1808fcbc370ace2968fb64589b4d5a545fe30f5ed671e38c45859f000001506579c448460360aee9a6da1a0bbba5d040a1f72aac41c4f5b443b0eca8743800016930c8a959bd90b1b62e9802f5cbcc3cb3d870517dbb13e929968e90dbc03966000001b0fe8fd198af091f423e1f6c055671b04a35ee33729cfbf33ad1b83b109a78570001b72a593c1c89f26daa1d1c5f5f3f24737ca0848b67a97b0c3a2cf9a0e163091f01f416eb7e062c981dbbf76f8845fda959b948bc742fc62d9edb2f36bae852ba4e01c5d9822e7aa76d3a758e8dc25ffd8b8c9d4051c66fb92fd7aa263905e238920e0139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e", + "orchardTree": "01d74889ff71b2f7a95bb7f4fec78bc0e63148642ea50c3c353a7be5ba25788e2e001f0131419f315d75aea60effee990fee293d35664d941866ad3bdf3efdad9d44bd2e000000013eb62102cf5d6119d411bee8e92c3af1a5337fe11e8c0760920fe9b3ec6ed90d016aacd5fc71a5527c7b9789bb97078c16c9d1444b84a2a2f5a8d5306cece20f0d00011ebb565f5458dc9e3b49f69f64733873eaff2663f5a5ab4f0651a1835b67622800000001c6dedd851cf25be522f8794de3473dc3169d6e620580f11c8458a844f8aea13501ec6da1180a001154528a755ff85883761ab63a8cda1de497045cfe8235e81e260000000000000118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1820000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1820000.json new file mode 100644 index 00000000..80df4523 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1820000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1820000", + "hash": "0000000000835c776b73524bd19ba4f31e0a2433a1b127d3fc249b76796678b0", + "time": 1664037050, + "saplingTree": "01f32c0a3edd77fb718880da85cb80fd6d700afdcb0c8d941d9cf83fba5af2870200190000012e2a332b86ca3c557c064a8d6e785316c5fa29a8ff0f9f2bf206c2a79273311e00000000000001f97daa24c7111f4d953c4f12ed983e9ff5024ee0bc9b5307cacd62809a63d263012f1870cbe92ff9b955331241ceef9c096782f9deae047f154d45dbe6e06e7a680001bda24aa178c8464c3f7d0f37f8f7b254ab2a28b0ebd0e1cad2b4461585ed650100000001bea76640d0f3a644f7c48d548d61082b1e59888ea45983588ead11372d9a7e220000016ee1f850fb81314c5cb719df85d19a05d71ab03ad0674c290bbedaaaf13f5830011a89f88b2081f74f07b1e3bfb9fa2dad973d38a2778ca9caf804269b1569e51a01b72a593c1c89f26daa1d1c5f5f3f24737ca0848b67a97b0c3a2cf9a0e163091f01f416eb7e062c981dbbf76f8845fda959b948bc742fc62d9edb2f36bae852ba4e01c5d9822e7aa76d3a758e8dc25ffd8b8c9d4051c66fb92fd7aa263905e238920e0139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e", + "orchardTree": "018b4e53c9b54665efb59b6a574061e4e8c22719e77298207ec59eb450beabc236001f0000000001e571d044e6017197dfadb085a24e0ffb4db345f76802fcc5f855b983ac95ce3b01445284ba4559992af06ab7e0bd62fb6a0586d2c87e2e08538f4e3047fb8f5c270114468166c8102b612e569ed90c38e14d33dc9899974ede1dc1d1a9d537b3f6060140157de80b79ace710090ded467cf09f382c03a57391060b4d80e6caa83ca8290178d8f1f35f97aacd0a3ed6a488eb04a295d068e1101808005a8b991347df0d0e0188ead086968b6763947d155c8607050b9d86fe394563e404a808a4dfd042a21c01da5f438bf911526bc11ab824bf18c29f092a7057650fe30e741c098622fdfe360113696c5ac5b2b94583b6cc6cc01f4be7f9614afcd5a856690ebb18a7b325321f000116ce53cbd2f57591e271c4004c3b7e569383a34c2d0950777b7ee0133b022d05000001db0c50e709ec287e6423e6daa582695335b02a53ad1d5e06bf1499a2ccf5100400000118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1822500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1822500.json new file mode 100644 index 00000000..c555e4e7 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1822500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1822500", + "hash": "0000000000a714468e7fbb0e3804f4ca38362f55cb0d9560a4d0c4e82f27643a", + "time": 1664225376, + "saplingTree": "01e55a3c5796b2057c7d4566262467f321bee7a49446831979c2fe7f00c514be2b001a0114f298f871efa88e8e3f0cf75332800066add4957252cb706c3e0a4a4a9cf812000000011f2ad5179ad1500123ba9ff6e4e3495a00aa51974d1185f7d8071eb63de396460001b2dc078bad9b7941458f393f54af4fefbda6648097a0d7db168dbf838318711801d7bf4d62710e875cbd9b376b9f4ef15b01c5e3a190947b9ee8326a4bba7fd00301f95116e45179eeed5481b4fd815ede7458f06ccde88af55c4e9ce8700875731600000001dfc746f32e8af2b7946f316f61f4b381af6a5d32edac92dbdf0832de6f9f430400000001afb52e8a8582fb920a38445d5dac564825de368d2a57d106b7e378427019176c000122761291bf884b6f5ee5c0b224ca06990534a932241ca10b99b7b978ebc33e6d0000000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01393f7dbe3707eda351d5ab2326369b818f6b6dfc5f81bb5228e085b2c7707323010c2f0639c08029304a03629453ee97d21c2ed7391edab49a77a5ed925a44f9051f000001c7b9e0d295ab3e74b1aafc5adfb2a1c6829108f8c3bf871edc721f2a78c5b40e0001a8758f3d483121113d71c2826f7a26b8a89017df94710146a844731852ba4d1501223105cff5efd73d74fc593f79f08ace789a6d2df4d6e3e98eb229d3cbe135140135e7a09bf3f8ee46559cc3917af50a5252ba98d51302f4958c4dea04e9f29b3e012a097d3bffa77778f5d2ed0bc6455c148f8d9f33b27e53e13ad71e83077ae408018a9af15099d6903002b63e90e7e2759f107dc427fe722a2e8a47caf811af8819018f54abcc86127e81658368df0e582d2f31595bae46b445caa49a4896ea16633b0001f1e08466d0331539a333be73620fc1f9ef3760be0807e8c087f19f8c5609341a010f099166cd67ccd6af73ada73f726b8667c1999ec18d16f32591ec073091ec0000014e69eda5e0a6dae5c0e259edc1a4883ad9f705bbfa67a19e89253c5318bd4f22000001aa1af2205aa6e2d1dbacb3b0ee2113a8bd8ab9bc326e82c1d0f412517b882b2a000118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1825000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1825000.json new file mode 100644 index 00000000..4b81f8ac --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1825000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1825000", + "hash": "00000000010bcacc0fc5d5c19ad9f7d8ed48611efc59a263e390b7616f874d19", + "time": 1664414144, + "saplingTree": "012e52a9a21c8fcae78103b11f846815a63c33e455347e7813152e9be8b42f6814001a01e67b5e015bbe91da7b4903c9252cf175b64c16b85027ad533357f66d80887a580000012a20a2299b66e1fcd4ca84774727025db6d667c3057198b929490495e4ec355701982e0e26ffa4c89f04e5c463b5499a1eab6566320ee4882602ba9b177b46d05c01b088dad9568f994021da7d4df0708efc28ce412be4006d9249a757d51737d70f000102ac23f0271b5c42a994cdbc0a234e5ef04ad8bb4cd478b770527d646a8f2353000000019870b43adfb257413d26f58805b078ab69bb5cd494b8e94a1d9e5c6843d1486e00000139061f9d2c783f9b9e8575b26a78924295737e997935f0b75c0cf125d1fd7a310000000001505fc602840d05f9586470bb490afbccd86fdf5ac4d0e77e5a9449524dbb732100000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "018c73fb76a5c7d45cb95a5868d4e0464168d48a574824c572e7c1b14092485d33001f01d9f0e6d791cc1843a126113ec3df17afe78c4aa8b272a9a378a2be551477233b00019f01ba3ba2ada7b9fbc9d8f41fd952b19331ff68d5b5697400ab2191d5143939010f84108bf6e3ff0f1e122b52f871296142f56b2fedf210cf4a19d60a35c3ad090000000001624baf89d0e9674562f99abcfcb93dc90139515849e15e2d161b1ecd18a8cd2e016956a06c642fa1ce07638a0034f1621ba90d3745149c2cf71937af51b8487733017a74469c0a99a842c0ab1372538d4f02c2a4aad6bb4e361fc02e36e8e0b25325018503c3bacb212c18b19244328dc6a97f2b8a93e5bd92dd55d7a6dc49d0df642a0000000001f20860c3563401082d950e84fc8735d8925677da13f40f6cd7a38a8c0198d60801aa1af2205aa6e2d1dbacb3b0ee2113a8bd8ab9bc326e82c1d0f412517b882b2a000118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1827500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1827500.json new file mode 100644 index 00000000..2f014acb --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1827500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1827500", + "hash": "00000000002e6ec32020c160a205314eb7646170871ed96e087e21f79d0e211e", + "time": 1664602807, + "saplingTree": "019fb653789bc45e8ef60fe2f657cea80bc835089532652cdcc3ecd637fff37f6d001a00011beea3559174bd9611c5ae3b7492b48bd391a994f418ed56bfae625a2a251b5101abbb8acfc1ea7a2042bc3e022317b05163dd452aa1ebf20dad24fc28c11197170122da960988f7ce15a31ca4d983c99e096c367e8bb937c1784c1b9fe7bc3daa60000124e4eaf466a658118e62307b6e5a8d1f41bdefdd20a312e992060ac8be16866f000000013f2bdccdc5efd952aaad933a00e34af7104b492979988d3a05e7c2460518881000000001b1fcf215ed122ec3fe2a7d939c358571465c971173b178de878c89c6cb969f6000011ed08164a8db56dbd04cad0b6d5cc0db6f2145cc9fc003ccb2094c70a925662500000001505fc602840d05f9586470bb490afbccd86fdf5ac4d0e77e5a9449524dbb732100000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "0128e91d3bea4e0e5075ea8443ff7d9e77f02f2eff009ecdf58782d6202b129114001f0000000001b801335384470e47a439ba9259062f0e914e7cb667fc44ef2af2259dd74a2a1f00016b684c958451f62a81ec79ea5f2ba03b65b2ae0b104706106946d9e396d4553201f7ed8044b12ec079446e47659a8ef170aa7cf0efc323bea8b8a15a336e41941801b4922700d16f0f31fe71f7f0ef427d550795351260b03168725da632d307081a0114b1074e3c2ee621a922a0a60d2aa74573be0e0387d5df7e53c5592b04e5fa0401d5cf6467ad8e2adae024a94ceff61d195410229ce80ae604a8ef66c5c222100d000001cb1bbbee0c509595b73a98bfd941691cddd4432d934207c2d015900dff7d7618000001f20860c3563401082d950e84fc8735d8925677da13f40f6cd7a38a8c0198d60801aa1af2205aa6e2d1dbacb3b0ee2113a8bd8ab9bc326e82c1d0f412517b882b2a000118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1830000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1830000.json new file mode 100644 index 00000000..f37840c1 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1830000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1830000", + "hash": "000000000004def347efa56ae1f4fe002a2a07ad16ca936ff6aeb4f41f3ec0d9", + "time": 1664791147, + "saplingTree": "0178aa88a229972f3f73511e2dbb786855d80fd65f75c0127f3bd4092daaafad0d001a00014d6a6a1d3c1b045c69c680050bc919d86792c2ba284155c198c032d553eb4900019c99d10c165d8221f235330241652a5fb301cdaf2810b15fc420cf914ef9495e0000000109ad606535d23460ec3ec992fa297618128847ff33b05984550e1c47404dbe0900000001cbe7b575c0b2f73f247dbb0cf0f624e465a8afe418d4806fbf1e7855e08fd029000001b1fcf215ed122ec3fe2a7d939c358571465c971173b178de878c89c6cb969f6000011ed08164a8db56dbd04cad0b6d5cc0db6f2145cc9fc003ccb2094c70a925662500000001505fc602840d05f9586470bb490afbccd86fdf5ac4d0e77e5a9449524dbb732100000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "011ad796243f725e67de2145997c30f3def6b7efb15f721327a27790a47773d119001f011da0e512c975812b0923b71f7e43c893d27c70cb2d2d396130d0f516c907aa2c0001d9ca3845b8eff9f9f87091d368282a6147e79b1cdab855d3d77968c78ad3aa0301a0d3959c6722d9dfd0f452629e4696d34167c1dbe0b35f79ba8f13aeb688d00d000106e7a8d22f77f6a3dc8a445beb9aaa29d03212bcf082d2473c4ff99a81596128016b684c958451f62a81ec79ea5f2ba03b65b2ae0b104706106946d9e396d4553201f7ed8044b12ec079446e47659a8ef170aa7cf0efc323bea8b8a15a336e41941801b4922700d16f0f31fe71f7f0ef427d550795351260b03168725da632d307081a0114b1074e3c2ee621a922a0a60d2aa74573be0e0387d5df7e53c5592b04e5fa0401d5cf6467ad8e2adae024a94ceff61d195410229ce80ae604a8ef66c5c222100d000001cb1bbbee0c509595b73a98bfd941691cddd4432d934207c2d015900dff7d7618000001f20860c3563401082d950e84fc8735d8925677da13f40f6cd7a38a8c0198d60801aa1af2205aa6e2d1dbacb3b0ee2113a8bd8ab9bc326e82c1d0f412517b882b2a000118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1832500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1832500.json new file mode 100644 index 00000000..1962899c --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1832500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1832500", + "hash": "00000000009e1bfb5c6b2bd724c919a18596b29c5ac48a30be29754fcdf001a3", + "time": 1664979856, + "saplingTree": "017d35c8b1fe8353fa65b77775deb2a25db4e77ec8198500f3223a4bc3e64d5231010f05785c5acaf53e4e9586000f41bab3048bde42771298e85867c008107a57261a000119d3ef4ec7b507059f4ca2741c339ea4307d50b9ea42616971c030f522390c4f017bb7a761d76618f986273f4f052493fcff21b58666f0b22b5a978281050967570001c7d62da2c0e245bea72e2cc622f03a7cf05905b1fea13f0a4a3b84bccf6134700001c268a889c7578fc7f6f180169117421eac63a993cb5c4608df4d8bbda061f263017119f88798b428978de3d80bc5520861a9c6c3eaaf0b432eadebaa9f2135da280001a712a6184599a51e8aab9749c1c6cc590ec2e91943016143571a6ef5d136164801dbdd827172d09d214ff210c063240e21e3b326c033771314438ecf82b56fb1490001f8bcc88bbd4826d499fb50b5690ebcbb4bf13e8fa94326d7766985c6fc75e7060170e7688fe48251420d10d5e3c793dcc47d75d7994ebb248831f0cd0743bd7f4d000147c0bdf0b09ebcb0ea103f80e6cbe9b02984c2ec7c0523feec25809d4ad599610127c9b9938654aa0663abda2287bfffe3cc54937995c245d097f740153b65012b0000000115e1f20e49fb7935b31b20ea4ee150567e407cfaac8ccdb01048822e64049228000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "018c23927dda449704b1aebdbd99b8c883fc18dc89b9cf843112596ecefef72a19001f000001c039a9bd13c31443457ee252c77b24a80e12c105a81275af484494d43e254810012a442d12b5b33755b5593a64c6844a9a382dc453944bd7f2901909c0febc071e000189302d6ef7e07804db53b968c9751b8bf9a4780f81d81b3d42ca5b2558541f32012005aa830beee12f6906f73489c2fddee52f1a75278a6ac31f8b2fe678c599380001439e5950a544b0ac1967c73130044b57ffb7bf11fb35126fcc632318f4b8cd0d000001886e2f44043fe89abad72ba1543ccd920ad9455e13d8aeef22f783f757dac92101d9ce8e05739fa694e98cdc0f3e0947944606dc5cbe143e30e90e77725aa5321c0147b69b1780056e107c2169154dfad1afd097fe1c12f61a7e627c3fb627a10d120130f9e5f272c54bff347179b604867bc9fbeac88313c3351f89e55c641acbd33401b8d5dfe4cc3065beafa9b04aa3a8009104f7df8b340169e397d6d59673344d070112c5b16c486f2d31e33b79cbbc4453fe52a373e5d7dad19e606bd21a188e88300001b7c55911e9d02efe6a9644edb48e603699a68a9b370fae484ea8af48772ba9340118b1d1d9f37857fd4b5b4eb5fca3d9246d9af2ff57dc556456777486cd8a943a0001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1835000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1835000.json new file mode 100644 index 00000000..d4b5ae56 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1835000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1835000", + "hash": "0000000001103df2732af30631a2f49c5ab605554e9fe2c38f4651e3291ea9bd", + "time": 1665168048, + "saplingTree": "01074ebb332b5fccfed914dfc1a202b5d2a9ddfc114a1e02c275f5750f54238e0f001a0149e6a8d59b2acb7a2f1d9bae9907cb578b2b15e204db734d869ff01f2dbbdf39015a05e3ce4f28e6d8973dc26e4bf56bd40f8d2f2d97fd66217597bb6084508c0a01648930e7524412f434f42b61b6573edde1a4627c3bbab2648379cff9c886d40b0000014b5219957d0a9a35900e4ffb8ade76b2fe962c4ae4c331ff741ce893234a3f1801283589a038b24c537b3fa1db35951586c1c8f08e0c7e7c9d333fd2b399e108690001a25a1c53d083ba71c776cb1fe70d9dfef04786cac96c640c2c0fbbf833028f560001dde05573b84e569e7dde13ba5524abd8d71cda8c3f242c5f5c320fb581db22330000011e83a98f73bd6bb2fdeee9324c0694b295ea1da95f64abf45398890fd476fe5d016c3ab232b9106e87602df81d84416223e173cdbe325158ee7005fe37cbea293c0001e17cfb9ca8a03e9a702563f4c2cb66c4439498c8ae5846b9a750a597e79b4c6e01843bb589116c1fdda2027c517ee89c434b801d141011844df24147eb040b5d6100019b81ed79d6c2b55e902decee63ed1b1c26df85d9c814a74d4e053aba8f8c36560115e1f20e49fb7935b31b20ea4ee150567e407cfaac8ccdb01048822e64049228000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01488b69c6ae788b6e70812a1662a86df617e9e1c90ec1103aea126d9b77b1322c001f01af126999194487703ca8c7ac015c2cb228538638c9252504903792ab73f4963901e81d8e5b63692593a5180ad6a0e88b44c89b64188f695f63ede66a23f89c65260121f6819a12feda45cf97805120596d3fac5ac133dbb270d5bba2389c598278380157b4834291d7e1fd8fdfce1c2d2594e0e894da3e763b301cf10be71998781f11016ccf9fabf4a575fd371dcb121aa505531b5e09383f4831cbd1b8a07845e91314019d07c968c2b776bf4a81003640be8e36c2736d8b3109564e8cdba8146c78be1d014e53816914b86c5fedab78f38a29c842471ed8b162c9a8d29baac4fe599cde3b01fe3b2f92c9c33cf1b872df89abe09ede252fd848e5c2a93f08f7de4a64070a2001d4a3bd717758b2f5a13d23d2d88526e723259764b640aa8a0a564fee754061050000011fd00a10523c934360f323e75810931fc2f02423a144323dfeebb3773f93d33e01b61c124cb2dbac265a0a93236793532dc850731022e87eeb335837261b84500000010278ad82f36c3eb59257919611ca46d8f752518954fd259315495cb0d3d39e37017720873c334452d2e25834e13f33a70495462e5bfd35ddc3682a66d35aa6a90900000000019038c488e9bf16e4cbea3f12d056e2db48296d81c075f314b40542b1a21dcd2401020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1837500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1837500.json new file mode 100644 index 00000000..373e2361 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1837500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1837500", + "hash": "00000000000f877e2a531cc93bdd3131bfaf3b0c05c64b225407f29fd09ef4b7", + "time": 1665356771, + "saplingTree": "011e47f9e3c4b031d671196398bf1dcf16b1a136c5d3bff1522622e1a8cf72cd67001a010eab6056ec7a976f9d49e64af89b3968c0b09d1425ef80bc9013bd3ed9174d660102624a22664951600d137e99b0eef3ffac30b10cde98ade7dd72b896c0b2315d01ede04410d2cbbfd48045108b2fca80857331a059bd735621c8b9129a63ce6d6f000001de1b5fe2626fc7faef37e3bbe82d8629cade50e7808d9fdbf91d08e95feeee16000000000116ffa08dc733175d806958ca7043714802b4c7e36479f686ebab9d7971f24c04016a220b9d29ae06cc77a9b288fc6461d54442c6b9a7336b7b0bd6ca602da7e2690001f91d5afaa3997435b65ae3dfdddfc943cbffd40bb480f936bb28120b8503146c0001654a9c702e5e09cfa0bf35e03e2214c53c346006c7ed7fdbedc3459b14000958017ecdd8bafd85940f76c4d4355dd78e76649ff32f18334c813e5053ec57474e1601d91eb3d1fb1b40ddf6cda125ec8f9340d6a804a53fb842f54088eb0d686c3b6a019e925ac0dce1bdb9f4b8564bd9c7e0413c61f2229749bb4fd1aa8cf3c99f7a61019b81ed79d6c2b55e902decee63ed1b1c26df85d9c814a74d4e053aba8f8c36560115e1f20e49fb7935b31b20ea4ee150567e407cfaac8ccdb01048822e64049228000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "013af54fe790ea1acdf876302eca0e919d10d2add04d914fd592e91655c1f69a2d001f01991b7c0d0944a24478dc16c86924171a6203ce543433c5467272962dcc07523201b756ab3d547f0b1150af4992edb67eaa64ca7b48cb3a169b87c09da4bcf4661601e1b4ecfc1855f4cfb68f5eae6734714e1862e1d369bc9f3b00322be5973370090145b404daed53f95be97ea912c067fec2858cbdc09f70143764c611877eae030d011fa2b687d8456263e12b2949b6d079a55d4c55dfb0f85e153a6645707dd9e01301e5f09ecee154fe022b8e8e0bf06c21b39734723b69a952e075eca0a1dc1a611801b13a0640abc81d688c0c55943945ff680ac514ff8ebecef1dcef86e3768cca1400016ea52315040cfe27c15034c18b2a20e8679bee0fa40632d86cec84082310a9160001414c1227bfa2a90214586a740c34143e095ada26e3554225348ebdedd7cf422400013351bfe677ae28702c7083addefe2057f9561857710c0a0be9bbab8de4756a38011a7e755efdaeedcce837096200c5fd636ca4eab9d8d254d88b3a185325ee9b200000000001d83d50c25ac8bd6b3d7ea1683a59b18771c72d85b43641652d84e319d8be720d00019038c488e9bf16e4cbea3f12d056e2db48296d81c075f314b40542b1a21dcd2401020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1840000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1840000.json new file mode 100644 index 00000000..a5dfa3de --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1840000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1840000", + "hash": "0000000000223fa66e8eebb3be09c7bc1e327018c1bfab8bc87a1ac4756b609d", + "time": 1665544594, + "saplingTree": "01857ee7416e6ca3a65c590a912e48648f9ce87ccd1cdb162cde4c8ca5aee2031a001a01e7bb2d86ffb2cf2634fd532e70c529dce92f891ca69d18d32a015f47153f891d015e330e7e1e3214e1cbdd5e85fdfcc39178d74e75a48b4c68dfa36ad9dd8b3005015a1d4f4a559ec1afabbd8e6f88db90b98d609be056c8b9d7c81fd5e63bc5936d016312886e35fd2e65eb758fd077d94d20f6089f53bafab72af633935d3724490301af79b49472899af91e9c6946eee90aac52443a444ebdfa12f1525000916af5570125ff259f5af38bb899502dee5dc34ac14532f847d15a608c5bff2b0e5c4dec24000151360605e535fe2265c67dfe962ecd1b8f071b28177f6ee6ac98369218d1ee4801b984ae125de01fc0f1acc745a7d2ed4237f7c4c6c3ad45ff866ca6954c7bea5f01325df68fb084fa513051a9ebcfabb543055c4e7799b9eb87bf501954e21fcc7100017429a77956ae1275402a408ee286d4927f57ea4073ab53d8f7dcf2f7f760822501b877a72312b8ca0c0d0d00408706e6014d8e291499ba1bb2da7e99167fec49590001b085695ecfea237f02fdd97e66641c19f82146753e23e8d0a50227daf7160e4501654a9c702e5e09cfa0bf35e03e2214c53c346006c7ed7fdbedc3459b14000958017ecdd8bafd85940f76c4d4355dd78e76649ff32f18334c813e5053ec57474e1601d91eb3d1fb1b40ddf6cda125ec8f9340d6a804a53fb842f54088eb0d686c3b6a019e925ac0dce1bdb9f4b8564bd9c7e0413c61f2229749bb4fd1aa8cf3c99f7a61019b81ed79d6c2b55e902decee63ed1b1c26df85d9c814a74d4e053aba8f8c36560115e1f20e49fb7935b31b20ea4ee150567e407cfaac8ccdb01048822e64049228000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01711e3f4649b675556aecdc4d6658edfee8a99ed3dacae944fc7b385206545225001f000169bc9b81ec14c51edc88e8120c0a7a8238e2a46cb50290e42df86446c6a61a0701274bf921b871c04f28cbdc43cc64694a3c2036a19e4a46bb0b423625cb4f9c0d014ccebdc56f1f516893afd6179b6d319c25bf9e83071ffdacdb4e10a249c5c826015bd9fb52cb016acd1726d91cd99a6cfd5aff846be7d58e3dc11499d42fe8fb32019ad2de520c6ab6761b6fbe75dced8c3b81ce839d30409c1d57f00da42f17a11a01a8ed9b5c8fcd08aa77346029799e0925878bb5e63d6abe209685469fae120401000177de1f17aeb5b0df31b6dc3456435bca969ce2765b645ba256ccccde9244fb36012e52dc44175af3cf903cbb53e47a441590b142a3ecf50bab08a214945f1cd13401917e41edc5417a71ef857fef69f81179fc2e8cb782b2467a73dfdc87f5cc283900013be7f41ae067ada4cbfa2e36c364a51e62b552a7a74ac79f4cd093524d4c0c1200000000019194605343086da1d812d5c0ebcbf1b933523e156b4c542d728b573f9a2b51240001e08fd3be67691a163c5d9b379dee67aac66231338d337b1847d414d0405ad109019038c488e9bf16e4cbea3f12d056e2db48296d81c075f314b40542b1a21dcd2401020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1842500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1842500.json new file mode 100644 index 00000000..a7d8b817 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1842500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1842500", + "hash": "0000000000399a69b0beaa3320c1bfe61dd7f1b52bf4af1d2fab28694588966d", + "time": 1665733194, + "saplingTree": "014bdf33ed5c693e6a988ae5e315f359951f4350c4c9238a118fc52e4fa6df2e42001a000001375fb8ee8454657a07dd02e27cbbc5f2ef316f90c749d613ff3f93721fbb4205019cf68f5059a52c45184065cfdd351d5ccffaa89eab1c9416e5ab41966b11a26f0155d7d639da967d62403f5b60540d2fdc8b618fb0f31b1e40bf05bf7445d0355e0001ec927b4bc50f2b366446ce51699b70f8cd2b6841c08ab138a87e4c2f095f7d4e01d0f7bfa7f5dc2bc9e02da593effc8f7690f9c59ef631c5e970d67982dfefb6400187b091189fc6be68385778ba02e46a9f6beb52ba43e340a3c326d6e8afc1442c0001c36f357664a4f98e64de8a3589df8ba9eab6c1456ad76f009669314478b58544017429a77956ae1275402a408ee286d4927f57ea4073ab53d8f7dcf2f7f760822501b877a72312b8ca0c0d0d00408706e6014d8e291499ba1bb2da7e99167fec49590001b085695ecfea237f02fdd97e66641c19f82146753e23e8d0a50227daf7160e4501654a9c702e5e09cfa0bf35e03e2214c53c346006c7ed7fdbedc3459b14000958017ecdd8bafd85940f76c4d4355dd78e76649ff32f18334c813e5053ec57474e1601d91eb3d1fb1b40ddf6cda125ec8f9340d6a804a53fb842f54088eb0d686c3b6a019e925ac0dce1bdb9f4b8564bd9c7e0413c61f2229749bb4fd1aa8cf3c99f7a61019b81ed79d6c2b55e902decee63ed1b1c26df85d9c814a74d4e053aba8f8c36560115e1f20e49fb7935b31b20ea4ee150567e407cfaac8ccdb01048822e64049228000000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01bcf67b86b55901731268ecf83eb2beb10e6da821b7dfd3935ceb69158aea8917019961f93887986e00acffb5b59ed5d6b1d33a13a1473f0cd2e1f3aca973b22c011f0124ebf2c03ee8a2cfc6124a8815843d8ee7a5859273dec9470296dfe2b2ab1728000001785aaade10d232553ed944f61426321e4745b3d2eeb52688a891d8e901e73204000000000001f3749ea86fc0e833fead5d9909c6bd3ccfb83fdff02760bf3f2c3cdff7c1d4340110136ae02b4a36b3621b64626afa6429b5db7277339095550ccfb8610da2420b01842da7ac0e25d444be6e4daa6e453a318b0714772d1ecf15d90b1ef38f6add34019ac96beaa59b7ef829d5ef4d11ed0ed7e2bc4e51ec6d3eeff2103ad848fdad370000011ab64a94431fc2840d761fb5dbf86378b3ad42eca6f64b12933612902644c334000000000000017912a4e2f4dcabdba64cb381a3df33406db480a480c9f3dcf8a5c509804b313d0000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1845000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1845000.json new file mode 100644 index 00000000..98b3f2df --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1845000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1845000", + "hash": "0000000000f5a2bc3b7762af88801f728bfcf288f68ac69b2a18b02076ca9794", + "time": 1665921106, + "saplingTree": "019d26046a50688bce14a0bbeb3aa444e22b5066998d42096c9d6eb911f2d9644c014f0e283b0d6e51929d2aa241a7a141927b88e852af11d8026d6b69fab50165561a0000000001aac99b0ff1e97f054ffb6abfe5ec682f8de5da53af12e100fa948ce0e8d7b84101a2a93738fee0ac3b6824b92ea32180489ded0c686a33544fe6650724e2cc0b600141d4dd8f4facb551081238fcc162ef666e29213135ec6465791eef6b08c011350000000001f94055447c1abde88173bebd79f038fde41fe4f68cea5baa49c7ff7ee3039445000000000000000000011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "014f38c1948feb179123d524a41a3311b4bc9c8f47b85026ae9ba2f843f7fa7018001f013889806c104d4c33a7b7b0f9258705cf10d76e3137c66537979a817b522a3720010fcc558250f53bc348213cd9d413a3d40d802eea0c9202202454ad7e4b464f29011d42da37e66d92f52554d80379cba8119576caefd4dbfe0a1ecceb089a270639010b6fdc8c06c36e15efe0f1fa69f1938c01814bb687dcd3f7c487d7568dc2e93c014f1ffa059a7ee1a3613a5a89c484bb86e0ddc92274ca0c6bd6f17ed04f32f02f0174fe3be65cfab7ee11e1356574dbcc6d9d6565d893618ed0f7ed9404a6705b0c0175b4f51d12f0b59cb8681c55128d49cc99da012638ad3dc3cc8af874e0261116010b1f7300abb2d54774fd91490b7557808cdb20a221abb6f800e60bf8815f7f1a000179a838662e881000a097029323f10959e6b2ea6bde92a5699c61b91cc7c5031801fd0ef4d4a41eea63029c8d4b10ba03b4731b8d6b74eccc8ef38911184bcbcc1e0111e07e142952f8c5822186f5a2b49cdd82c343f7cd963c72212bec368ccc550d01454191efd5452c67c8131f8b6be5ff8bd2fe281b22bb6208860af93cb1c8eb3801802ed7757badfc6f1770a5eeb3b7de0764c2fa47cfe88935e82e3b96797686130001c85cff1cf283d1185df96957ba7144dd39c1ce3cbe8f2c9751ca7adc1a38d5240001effece4db23774e28a0d2b5a4186344d564bff1fb33b0328640aa7b8deeba1330191e0f307afd0eb763ca3e38e501bb22825457000c7d5e26a1f038da44110480e000000017912a4e2f4dcabdba64cb381a3df33406db480a480c9f3dcf8a5c509804b313d0000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1847500.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1847500.json new file mode 100644 index 00000000..f47612bd --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1847500.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1847500", + "hash": "00000000015111c7f227c28f8388276950a2620ed1e45b5fbd1d630e1d496c34", + "time": 1666110079, + "saplingTree": "01f0f1dc7cc3b35e13ea03581cc9c10551bdc6663f87ece9ff8d2f1401511b993301cb658fc164c9fe6de844c72fc111f0bfdd208653a0b9226e5878a747f4dc6c6a1a000000016bf21d8bd0ceca9cf6deb036fc284b8c331bf926363172edfd61fa95c782ad0c00016f48c06ff0a773902c689003f9ffdefe10329bd957f04c79cf3e18ad4b1224350000000001bc8b862431d75642d34ee03c642dad35c3048582036781f8614d24e8b050d40701f5e7a27e93dd3e86b4cbf4be383df45b1394b9e6cc9bee489a47b775700b034f00018ef950dc76c9a46c255c0b68bfc2e997d7c94b94dc95609da2020ce448ff81160123d4b12c4de65c87bb2b4d7ea7716c279202d8af8a612d6882d72b173a8ce863000000000000011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "01efbdf8d91ddbe82aea16f8b95c9b9cffa38ba4bea58feb734ca9fce899096712001f011ade282ecbbf3fc17d4d6479d4fc3d5f761f7038f1f1b5d822587eb442adf720000001529f257d7869d9ece35f6bc089cb4bff0477e7027c8b5e1c2186b206778bd8150123d376274cc690625117ecbea0c3b5910d880331088cb4c450a03427091410360001e410649cda393f50729ebe137bffaf7c559dae2ea82d5712a6cd2b058e330f1401d80ea561a8f13ab5beb9f08c43056ac3a96eccb2270ed11876b4d98587eb7722000154b687eb1d5a6230ec29e587b7aa025cf1ca1c92ceccb31685ce37606c6f611900019859858dd37ae0f62be01bbb976106c7ff5c716c2220e1a674968f88d4a84832000001719bf63ec1d2d116dc1f78e20819408e7e03c0cf7c60ad90180bfb68be32791b015a96543adf51a9938c770417dd7815501251033f1d3fbb08f036cff15615b909000001382eaedd95be3cd7bf424f0b2992735fb542d4dd5a8c8c2e3d76d20e3cd0f6300104e9e9ad2aef9cef887765208a8bca0da58543c1a3bde7bcc88c92280bd2b30b0000017912a4e2f4dcabdba64cb381a3df33406db480a480c9f3dcf8a5c509804b313d0000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1850000.json b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1850000.json new file mode 100644 index 00000000..5b4e55f8 --- /dev/null +++ b/Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1850000.json @@ -0,0 +1,8 @@ +{ + "network": "main", + "height": "1850000", + "hash": "0000000000801f2c54b8fbe2f90496ea9dc0c0c992dd44d465c0ad3254d08066", + "time": 1666298056, + "saplingTree": "013778854e8aeb653352d1012fbeec74549c339d74332744820bdb5eb0d173636d001a016550aebcf982e60191e22ae349bfd7af0343e63f52b2d0648090ebc92feb575501c1b5e06a96ff169650015725ab9f1320eaeee71de34ee0b353792a690548d240010a977f9695ccffbc6dd1d8f8add32fb6295104bd4efc0deb7841635657b4f6720000011edc84073fb7e5428d031ef7d2cf71da367b04b641d1beae3d942900e070c3130118edb07e8e3de97cba932b09ca0bb37621aba1047dddb8b0a707751d9077f00201c237a7fab2a1339da31fc1155560929ea6c0a1d15b697908feb2f208665c03220001a43ecd6cd81187462b72251758504ce86cd69b77b2db3707280d354fac9a863e00000161809ba4619dab88fb42a8af5bb1f2b8f6dd5a48bbd8ec37482ff400c9e59f3c010dc073684263cb36b4cc1392af7287bf691433a7018f3778a49478a83d6e39050001d1020dfaef715ebcdab3a87327ea51ff75af24daa0ee45738941c69c2f7d225b0000000000011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e", + "orchardTree": "0142b513693c44e6ee3190620981368e394cf8ff366e174465bc9dd395c2cd7829016a488c69835a1a8443faeef51d678b63171063e53537eced682ca1fb228bfb371f00011f1bf47c60b0bfbfa7855625fc32b2f9c8193d771be77ddae5812deb2e11332d017e11bddb81f36691aed35d7924c571d9720013bc64934abf6b414dda6e8a711901cfcc4f4e287c41afe7dd46df23732f55e1118c062aa48644d3791d5c9774a50601dc1e5280c2619a5e8cbe56de4c943c4fc7892a9d3dc98c3722ea50ae88c6023c0001405cd14f0159b70e8c5cc3af59ef9103dd7d37bae7ace2b53e8565dfeb460f0b014b39bca2a42615434bdac8042f373d5c5874025d99fbf1b34702dd6e46e9c73a01e5744f5c39d2ad4b6c57dee68097bc0ef3148b2865689376a70c6187d0559a26010138877a4bba42daada8e17045a8205cc89993d8167271c83de50c57943fe23c0001545921e1a8e2149f83c0edbcc76f803fd72b55a9554b799a72fcc201b8ac413501e13a71fdf4151f7e1ea31fb333c3a71a40c3488e6c1773f277ecac498a3f00150105012c93c4325d8c302d78ab98b206742504d14354e97a1c4f1b4c7b9804311a01d37e48d4f74c5d13ec873ec09680f556732a223e3173eb9cc377aba8bed86e15000001616c27d1588b1e06f59e6d53898217e11a4a66749d1682168dc4d1af7df7e10a000001ca59317d757278a838b884b284b60aa986c43a1324e394312ad3b2203759651d00017912a4e2f4dcabdba64cb381a3df33406db480a480c9f3dcf8a5c509804b313d0000000000000000" +} diff --git a/Sources/ZcashLightClientKit/Service/LightWalletGRPCService.swift b/Sources/ZcashLightClientKit/Service/LightWalletGRPCService.swift index d3f8828d..d78ab118 100644 --- a/Sources/ZcashLightClientKit/Service/LightWalletGRPCService.swift +++ b/Sources/ZcashLightClientKit/Service/LightWalletGRPCService.swift @@ -417,7 +417,7 @@ extension LightWalletServiceError { class ConnectionStatusManager: ConnectivityStateDelegate { func connectivityStateDidChange(from oldState: ConnectivityState, to newState: ConnectivityState) { LoggerProxy.event("Connection Changed from \(oldState) to \(newState)") - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .blockProcessorConnectivityStateChanged, object: self, userInfo: [ diff --git a/Sources/ZcashLightClientKit/Synchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer.swift index 535fcf5f..e88c4b69 100644 --- a/Sources/ZcashLightClientKit/Synchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer.swift @@ -79,7 +79,7 @@ public protocol Synchronizer { /// prepares this initializer to operate. Initializes the internal state with the given /// Extended Viewing Keys and a wallet birthday found in the initializer object - func prepare(with seed: [UInt8]?) throws -> Initializer.InitializationResult + func prepare(with seed: [UInt8]?) async throws -> Initializer.InitializationResult ///Starts this synchronizer within the given scope. /// @@ -93,17 +93,17 @@ public protocol Synchronizer { /// Gets the sapling shielded address for the given account. /// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used. /// - Returns the address or nil if account index is incorrect - func getSaplingAddress(accountIndex: Int) -> SaplingAddress? + func getSaplingAddress(accountIndex: Int) async -> SaplingAddress? /// Gets the unified address for the given account. /// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used. /// - Returns the address or nil if account index is incorrect - func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? + func getUnifiedAddress(accountIndex: Int) async -> UnifiedAddress? /// Gets the transparent address for the given account. /// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used. /// - Returns the address or nil if account index is incorrect - func getTransparentAddress(accountIndex: Int) -> TransparentAddress? + func getTransparentAddress(accountIndex: Int) async -> TransparentAddress? /// Sends zatoshi. /// - Parameter spendingKey: the `UnifiedSpendingKey` that allows spends to occur. @@ -156,7 +156,7 @@ public protocol Synchronizer { func allConfirmedTransactions(from transaction: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]? /// Returns the latest downloaded height from the compact block cache - func latestDownloadedHeight() throws -> BlockHeight + func latestDownloadedHeight() async throws -> BlockHeight /// Returns the latest block height from the provided Lightwallet endpoint @@ -164,13 +164,14 @@ public protocol Synchronizer { /// Returns the latest block height from the provided Lightwallet endpoint /// Blocking - func latestHeight() throws -> BlockHeight + func latestHeight() async throws -> BlockHeight + /// Returns the latests UTXOs for the given address from the specified height on func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs /// Returns the last stored transparent balance - func getTransparentBalance(accountIndex: Int) throws -> WalletBalance + func getTransparentBalance(accountIndex: Int) async throws -> WalletBalance /// Returns the shielded total balance (includes verified and unverified balance) @available(*, deprecated, message: "This function will be removed soon, use the one returning a `Zatoshi` value instead") @@ -193,7 +194,7 @@ public protocol Synchronizer { /// - Throws rewindErrorUnknownArchorHeight when the rewind points to an invalid height /// - Throws rewindError for other errors /// - Note rewind does not trigger notifications as a reorg would. You need to restart the synchronizer afterwards - func rewind(_ policy: RewindPolicy) throws + func rewind(_ policy: RewindPolicy) async throws } public enum SyncStatus: Equatable { diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index cdbbf324..e24a4207 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -151,15 +151,17 @@ public class SDKSynchronizer: Synchronizer { deinit { NotificationCenter.default.removeObserver(self) - self.blockProcessor.stop() + Task { [blockProcessor] in + await blockProcessor.stop() + } } - public func prepare(with seed: [UInt8]?) throws -> Initializer.InitializationResult { + public func prepare(with seed: [UInt8]?) async throws -> Initializer.InitializationResult { if case .seedRequired = try self.initializer.initialize(with: seed) { return .seedRequired } - try self.blockProcessor.setStartHeight(initializer.walletBirthday) + try await self.blockProcessor.setStartHeight(initializer.walletBirthday) self.status = .disconnected return .success @@ -178,10 +180,8 @@ public class SDKSynchronizer: Synchronizer { return case .stopped, .synced, .disconnected, .error: - do { - try blockProcessor.start(retry: retry) - } catch { - throw mapError(error) + Task { + await blockProcessor.start(retry: retry) } } } @@ -193,8 +193,10 @@ public class SDKSynchronizer: Synchronizer { return } - blockProcessor.stop() - self.status = .stopped + Task(priority: .high) { + await blockProcessor.stop() + self.status = .stopped + } } private func subscribeToProcessorNotifications(_ processor: CompactBlockProcessor) { @@ -308,7 +310,7 @@ public class SDKSynchronizer: Synchronizer { } let currentState = ConnectionState(current) - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .synchronizerConnectionStateChanged, object: self, userInfo: [ @@ -330,7 +332,7 @@ public class SDKSynchronizer: Synchronizer { return } - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .synchronizerFoundTransactions, object: self, userInfo: [ @@ -476,7 +478,7 @@ public class SDKSynchronizer: Synchronizer { let accountIndex = Int(spendingKey.account) do { - let tBalance = try self.getTransparentBalance(accountIndex: accountIndex) + let tBalance = try await self.getTransparentBalance(accountIndex: accountIndex) // Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet. guard tBalance.verified >= self.network.constants.defaultFee(for: self.latestScannedHeight) else { @@ -550,8 +552,8 @@ public class SDKSynchronizer: Synchronizer { PagedTransactionRepositoryBuilder.build(initializer: initializer, kind: .all) } - public func latestDownloadedHeight() throws -> BlockHeight { - try blockProcessor.downloader.lastDownloadedBlockHeight() + public func latestDownloadedHeight() async throws -> BlockHeight { + try await blockProcessor.downloader.lastDownloadedBlockHeight() } public func latestHeight(result: @escaping (Result) -> Void) { @@ -565,8 +567,8 @@ public class SDKSynchronizer: Synchronizer { } } - public func latestHeight() throws -> BlockHeight { - try blockProcessor.downloader.latestBlockHeight() + public func latestHeight() async throws -> BlockHeight { + try await blockProcessor.downloader.latestBlockHeight() } public func latestUTXOs(address: String) async throws -> [UnspentTransactionOutputEntity] { @@ -610,25 +612,25 @@ public class SDKSynchronizer: Synchronizer { initializer.getVerifiedBalance(account: accountIndex) } - public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? { - blockProcessor.getSaplingAddress(accountIndex: accountIndex) + public func getSaplingAddress(accountIndex: Int) async -> SaplingAddress? { + await blockProcessor.getSaplingAddress(accountIndex: accountIndex) } - public func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? { - blockProcessor.getUnifiedAddress(accountIndex: accountIndex) + public func getUnifiedAddress(accountIndex: Int) async -> UnifiedAddress? { + await blockProcessor.getUnifiedAddress(accountIndex: accountIndex) } - public func getTransparentAddress(accountIndex: Int) -> TransparentAddress? { - blockProcessor.getTransparentAddress(accountIndex: accountIndex) + public func getTransparentAddress(accountIndex: Int) async -> TransparentAddress? { + await blockProcessor.getTransparentAddress(accountIndex: accountIndex) } /// Returns the last stored transparent balance - public func getTransparentBalance(accountIndex: Int) throws -> WalletBalance { - try blockProcessor.getTransparentBalance(accountIndex: accountIndex) + public func getTransparentBalance(accountIndex: Int) async throws -> WalletBalance { + try await blockProcessor.getTransparentBalance(accountIndex: accountIndex) } - public func rewind(_ policy: RewindPolicy) throws { + public func rewind(_ policy: RewindPolicy) async throws { self.stop() var height: BlockHeight? @@ -638,7 +640,7 @@ public class SDKSynchronizer: Synchronizer { break case .birthday: - let birthday = self.blockProcessor.config.walletBirthday + let birthday = await self.blockProcessor.config.walletBirthday height = birthday case .height(let rewindHeight): @@ -652,7 +654,7 @@ public class SDKSynchronizer: Synchronizer { } do { - let rewindHeight = try self.blockProcessor.rewindTo(height) + let rewindHeight = try await self.blockProcessor.rewindTo(height) try self.transactionManager.handleReorg(at: rewindHeight) } catch { throw SynchronizerError.rewindError(underlyingError: error) @@ -666,11 +668,11 @@ public class SDKSynchronizer: Synchronizer { userInfo[NotificationKeys.blockHeight] = progress.progressHeight self.status = SyncStatus(progress) - NotificationCenter.default.post(name: Notification.Name.synchronizerProgressUpdated, object: self, userInfo: userInfo) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerProgressUpdated, object: self, userInfo: userInfo) } private func notifyStatusChange(newValue: SyncStatus, oldValue: SyncStatus) { - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: .synchronizerStatusWillUpdate, object: self, userInfo: @@ -684,38 +686,40 @@ public class SDKSynchronizer: Synchronizer { private func notify(status: SyncStatus) { switch status { case .disconnected: - NotificationCenter.default.post(name: Notification.Name.synchronizerDisconnected, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerDisconnected, object: self) case .stopped: - NotificationCenter.default.post(name: Notification.Name.synchronizerStopped, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerStopped, object: self) case .synced: - NotificationCenter.default.post( - name: Notification.Name.synchronizerSynced, - object: self, - userInfo: [ - SDKSynchronizer.NotificationKeys.blockHeight: self.latestScannedHeight, - SDKSynchronizer.NotificationKeys.synchronizerState: SynchronizerState( - shieldedBalance: WalletBalance( - verified: initializer.getVerifiedBalance(), - total: initializer.getBalance() - ), - transparentBalance: (try? self.getTransparentBalance(accountIndex: 0)) ?? WalletBalance.zero, - syncStatus: status, - latestScannedHeight: self.latestScannedHeight - ) - ] - ) + Task { + NotificationCenter.default.mainThreadPost( + name: Notification.Name.synchronizerSynced, + object: self, + userInfo: [ + SDKSynchronizer.NotificationKeys.blockHeight: self.latestScannedHeight, + SDKSynchronizer.NotificationKeys.synchronizerState: SynchronizerState( + shieldedBalance: WalletBalance( + verified: initializer.getVerifiedBalance(), + total: initializer.getBalance() + ), + transparentBalance: (try? await self.getTransparentBalance(accountIndex: 0)) ?? WalletBalance.zero, + syncStatus: status, + latestScannedHeight: self.latestScannedHeight + ) + ] + ) + } case .unprepared: break case .downloading: - NotificationCenter.default.post(name: Notification.Name.synchronizerDownloading, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerDownloading, object: self) case .validating: - NotificationCenter.default.post(name: Notification.Name.synchronizerValidating, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerValidating, object: self) case .scanning: - NotificationCenter.default.post(name: Notification.Name.synchronizerScanning, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerScanning, object: self) case .enhancing: - NotificationCenter.default.post(name: Notification.Name.synchronizerEnhancing, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerEnhancing, object: self) case .fetching: - NotificationCenter.default.post(name: Notification.Name.synchronizerFetching, object: self) + NotificationCenter.default.mainThreadPost(name: Notification.Name.synchronizerFetching, object: self) case .error(let e): self.notifyFailure(e) } @@ -757,7 +761,7 @@ public class SDKSynchronizer: Synchronizer { DispatchQueue.main.async { [weak self] in guard let self = self else { return } - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: Notification.Name.synchronizerMinedTransaction, object: self, userInfo: [NotificationKeys.minedTransaction: transaction] @@ -807,7 +811,7 @@ public class SDKSynchronizer: Synchronizer { private func notifyFailure(_ error: Error) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } - NotificationCenter.default.post( + NotificationCenter.default.mainThreadPost( name: Notification.Name.synchronizerFailed, object: self, userInfo: [NotificationKeys.error: self.mapError(error)] diff --git a/Tests/DarksideTests/AdvancedReOrgTests.swift b/Tests/DarksideTests/AdvancedReOrgTests.swift index 6a5904bb..5b8f6068 100644 --- a/Tests/DarksideTests/AdvancedReOrgTests.swift +++ b/Tests/DarksideTests/AdvancedReOrgTests.swift @@ -31,16 +31,18 @@ class AdvancedReOrgTests: XCTestCase { let branchID = "2bb40e60" let chainName = "main" let network = DarksideWalletDNetwork() - + override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday + 50, //don't use an exact birthday, users never do. - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: seedPhrase, + walletBirthday: birthday + 50, //don't use an exact birthday, users never do. + channelProvider: ChannelProvider(), + network: network + ) + try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) + } } override func tearDownWithError() throws { @@ -82,7 +84,7 @@ class AdvancedReOrgTests: XCTestCase { /// 9. verify that balance equals initial balance /// 10. sync up to received_Tx_height + 3 /// 11. verify that balance equals initial balance + tx amount - func testReOrgChangesInboundTxMinedHeight() throws { + func testReOrgChangesInboundTxMinedHeight() async throws { hookToReOrgNotification() try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName) var shouldContinue = false @@ -100,16 +102,23 @@ class AdvancedReOrgTests: XCTestCase { var synchronizer: SDKSynchronizer? - try coordinator.sync( - completion: { synchro in - synchronizer = synchro - initialVerifiedBalance = synchro.initializer.getVerifiedBalance() - initialTotalBalance = synchro.initializer.getBalance() - preTxExpectation.fulfill() - shouldContinue = true - }, - error: self.handleError - ) + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync( + completion: { synchro in + synchronizer = synchro + initialVerifiedBalance = synchro.initializer.getVerifiedBalance() + initialTotalBalance = synchro.initializer.getBalance() + preTxExpectation.fulfill() + shouldContinue = true + continuation.resume() + }, + error: self.handleError + ) + } catch { + continuation.resume(with: .failure(error)) + } + } wait(for: [preTxExpectation], timeout: 10) @@ -131,12 +140,20 @@ class AdvancedReOrgTests: XCTestCase { var receivedTxTotalBalance = Zatoshi(-1) var receivedTxVerifiedBalance = Zatoshi(-1) - try coordinator.sync(completion: { synchro in - synchronizer = synchro - receivedTxVerifiedBalance = synchro.initializer.getVerifiedBalance() - receivedTxTotalBalance = synchro.initializer.getBalance() - receivedTxExpectation.fulfill() - }, error: self.handleError) + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync(completion: { synchro in + synchronizer = synchro + receivedTxVerifiedBalance = synchro.initializer.getVerifiedBalance() + receivedTxTotalBalance = synchro.initializer.getBalance() + receivedTxExpectation.fulfill() + continuation.resume() + }, error: self.handleError) + } catch { + continuation.resume(with: .failure(error)) + } + } + sleep(2) wait(for: [receivedTxExpectation], timeout: 10) @@ -195,11 +212,21 @@ class AdvancedReOrgTests: XCTestCase { var afterReorgTxTotalBalance = Zatoshi(-1) var afterReorgTxVerifiedBalance = Zatoshi(-1) - try coordinator.sync(completion: { synchronizer in - afterReorgTxTotalBalance = synchronizer.initializer.getBalance() - afterReorgTxVerifiedBalance = synchronizer.initializer.getVerifiedBalance() - reorgSyncexpectation.fulfill() - }, error: self.handleError(_:)) + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync( + completion: { synchronizer in + afterReorgTxTotalBalance = synchronizer.initializer.getBalance() + afterReorgTxVerifiedBalance = synchronizer.initializer.getVerifiedBalance() + reorgSyncexpectation.fulfill() + continuation.resume() + }, + error: self.handleError + ) + } catch { + continuation.resume(with: .failure(error)) + } + } /* 8. assert that reorg happened at received_Tx_height @@ -223,11 +250,22 @@ class AdvancedReOrgTests: XCTestCase { try coordinator.applyStaged(blockheight: reorgedTxheight + 1) sleep(3) - try coordinator.sync(completion: { synchronizer in - finalReorgTxTotalBalance = synchronizer.initializer.getBalance() - finalReorgTxVerifiedBalance = synchronizer.initializer.getVerifiedBalance() - finalsyncExpectation.fulfill() - }, error: self.handleError(_:)) + + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync( + completion: { synchronizer in + finalReorgTxTotalBalance = synchronizer.initializer.getBalance() + finalReorgTxVerifiedBalance = synchronizer.initializer.getVerifiedBalance() + finalsyncExpectation.fulfill() + continuation.resume() + }, + error: self.handleError + ) + } catch { + continuation.resume(with: .failure(error)) + } + } wait(for: [finalsyncExpectation], timeout: 5) sleep(3) @@ -1228,7 +1266,7 @@ class AdvancedReOrgTests: XCTestCase { XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), initialTotalBalance) } - func testLongSync() throws { + func testLongSync() async throws { hookToReOrgNotification() /* @@ -1247,21 +1285,31 @@ class AdvancedReOrgTests: XCTestCase { /* sync to latest height */ - try coordinator.sync(completion: { _ in - firstSyncExpectation.fulfill() - }, error: { error in - _ = try? self.coordinator.stop() - firstSyncExpectation.fulfill() - guard let testError = error else { - XCTFail("failed with nil error") - return + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync( + completion: { _ in + firstSyncExpectation.fulfill() + continuation.resume() + }, error: { error in + _ = try? self.coordinator.stop() + firstSyncExpectation.fulfill() + guard let testError = error else { + XCTFail("failed with nil error") + return + } + XCTFail("Failed with error: \(testError)") + } + ) + } catch { + continuation.resume(throwing: error) } - XCTFail("Failed with error: \(testError)") - }) + } wait(for: [firstSyncExpectation], timeout: 500) - XCTAssertEqual(try coordinator.synchronizer.latestDownloadedHeight(), birthday + fullSyncLength) + let latestDownloadedHeight = try await coordinator.synchronizer.latestDownloadedHeight() + XCTAssertEqual(latestDownloadedHeight, birthday + fullSyncLength) } func handleError(_ error: Error?) { diff --git a/Tests/DarksideTests/BalanceTests.swift b/Tests/DarksideTests/BalanceTests.swift index c1a69836..940e50c6 100644 --- a/Tests/DarksideTests/BalanceTests.swift +++ b/Tests/DarksideTests/BalanceTests.swift @@ -30,13 +30,16 @@ class BalanceTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) + + try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + } } /** diff --git a/Tests/DarksideTests/BlockDownloaderTests.swift b/Tests/DarksideTests/BlockDownloaderTests.swift index b78ed5e1..ceb1fafd 100644 --- a/Tests/DarksideTests/BlockDownloaderTests.swift +++ b/Tests/DarksideTests/BlockDownloaderTests.swift @@ -31,6 +31,8 @@ class BlockDownloaderTests: XCTestCase { try FakeChainBuilder.buildChain(darksideWallet: darksideWalletService, branchID: branchID, chainName: chainName) try darksideWalletService.applyStaged(nextLatestHeight: 663250) + + sleep(2) } override func tearDown() { diff --git a/Tests/DarksideTests/DarksideSanityCheckTests.swift b/Tests/DarksideTests/DarksideSanityCheckTests.swift index 0df6bf6b..500bd574 100644 --- a/Tests/DarksideTests/DarksideSanityCheckTests.swift +++ b/Tests/DarksideTests/DarksideSanityCheckTests.swift @@ -33,14 +33,17 @@ class DarksideSanityCheckTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) - try coordinator.resetBlocks(dataset: .default) + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) + + try self.coordinator.reset(saplingActivation: self.birthday, branchID: self.branchID, chainName: self.chainName) + try self.coordinator.resetBlocks(dataset: .default) + } } override func tearDownWithError() throws { @@ -74,7 +77,7 @@ class DarksideSanityCheckTests: XCTestCase { wait(for: [syncExpectation], timeout: 5) - let blocksDao = BlockSQLDAO(dbProvider: SimpleConnectionProvider(path: coordinator.databases.dataDB.absoluteString, readonly: true)) + let blocksDao = BlockSQLDAO(dbProvider: SimpleConnectionProvider(path: coordinator.databases.dataDB.absoluteString, readonly: false)) let firstBlock = try blocksDao.block(at: expectedFirstBlock.height) let lastBlock = try blocksDao.block(at: expectedLastBlock.height) diff --git a/Tests/DarksideTests/PendingTransactionUpdatesTest.swift b/Tests/DarksideTests/PendingTransactionUpdatesTest.swift index 4756be85..1baab7c7 100644 --- a/Tests/DarksideTests/PendingTransactionUpdatesTest.swift +++ b/Tests/DarksideTests/PendingTransactionUpdatesTest.swift @@ -31,17 +31,19 @@ class PendingTransactionUpdatesTest: XCTestCase { let network = DarksideWalletDNetwork() override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + wait { + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) + + try self.coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + } } override func tearDownWithError() throws { - try super.tearDownWithError() NotificationCenter.default.removeObserver(self) try coordinator.stop() try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) diff --git a/Tests/DarksideTests/ReOrgTests.swift b/Tests/DarksideTests/ReOrgTests.swift index d0063979..afae7c0b 100644 --- a/Tests/DarksideTests/ReOrgTests.swift +++ b/Tests/DarksideTests/ReOrgTests.swift @@ -47,24 +47,28 @@ class ReOrgTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - NotificationCenter.default.addObserver( - self, - selector: #selector(handleReOrgNotification(_:)), - name: Notification.Name.blockProcessorHandledReOrg, - object: nil - ) - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) - try coordinator.resetBlocks(dataset: .default) + wait { [self] in + NotificationCenter.default.addObserver( + self, + selector: #selector(self.handleReOrgNotification(_:)), + name: Notification.Name.blockProcessorHandledReOrg, + object: nil + ) + + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) + + try self.coordinator.reset(saplingActivation: self.birthday, branchID: self.branchID, chainName: self.chainName) + + try self.coordinator.resetBlocks(dataset: .default) + } } override func tearDownWithError() throws { - try super.tearDownWithError() try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.dataDB) try? FileManager.default.removeItem(at: coordinator.databases.pendingDB) diff --git a/Tests/DarksideTests/RewindRescanTests.swift b/Tests/DarksideTests/RewindRescanTests.swift index 04c61eec..5e54fd5b 100644 --- a/Tests/DarksideTests/RewindRescanTests.swift +++ b/Tests/DarksideTests/RewindRescanTests.swift @@ -34,20 +34,20 @@ class RewindRescanTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - - try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + try self.coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + } } override func tearDownWithError() throws { try super.tearDownWithError() - NotificationCenter.default.removeObserver(self) try coordinator.stop() @@ -65,7 +65,7 @@ class RewindRescanTests: XCTestCase { XCTFail("Failed with error: \(testError)") } - func testBirthdayRescan() throws { + func testBirthdayRescan() async throws { // 1 sync and get spendable funds try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName) @@ -87,7 +87,7 @@ class RewindRescanTests: XCTestCase { XCTAssertEqual(verifiedBalance, totalBalance) // rewind to birthday - try coordinator.synchronizer.rewind(.birthday) + try await coordinator.synchronizer.rewind(.birthday) // assert that after the new height is XCTAssertEqual(try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight(), self.birthday) @@ -146,7 +146,7 @@ class RewindRescanTests: XCTestCase { height: Int32(targetHeight), networkType: network.networkType ) - try coordinator.synchronizer.rewind(.height(blockheight: targetHeight)) + try await coordinator.synchronizer.rewind(.height(blockheight: targetHeight)) guard rewindHeight > 0 else { XCTFail("get nearest height failed error: \(ZcashRustBackend.getLastError() ?? "null")") @@ -191,7 +191,7 @@ class RewindRescanTests: XCTestCase { wait(for: [sendExpectation], timeout: 15) } - func testRescanToTransaction() throws { + func testRescanToTransaction() async throws { // 1 sync and get spendable funds try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName) @@ -217,7 +217,7 @@ class RewindRescanTests: XCTestCase { return } - try coordinator.synchronizer.rewind(.transaction(transaction.transactionEntity)) + try await coordinator.synchronizer.rewind(.transaction(transaction.transactionEntity)) // assert that after the new height is XCTAssertEqual( @@ -364,7 +364,7 @@ class RewindRescanTests: XCTestCase { // rewind 5 blocks prior to sending - try coordinator.synchronizer.rewind(.height(blockheight: sentTxHeight - 5)) + try await coordinator.synchronizer.rewind(.height(blockheight: sentTxHeight - 5)) guard let pendingEntity = try coordinator.synchronizer.allPendingTransactions() diff --git a/Tests/DarksideTests/ShieldFundsTests.swift b/Tests/DarksideTests/ShieldFundsTests.swift index 61febc14..1721d6dd 100644 --- a/Tests/DarksideTests/ShieldFundsTests.swift +++ b/Tests/DarksideTests/ShieldFundsTests.swift @@ -30,12 +30,14 @@ class ShieldFundsTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) + Task { @MainActor [self] in + self.coordinator = try await TestCoordinator( + seed: seedPhrase, + walletBirthday: birthday, + channelProvider: ChannelProvider(), + network: network + ) + } try coordinator.reset(saplingActivation: birthday, branchID: self.branchID, chainName: self.chainName) try coordinator.service.clearAddedUTXOs() } @@ -93,7 +95,7 @@ class ShieldFundsTests: XCTestCase { var shouldContinue = false var initialTotalBalance = Zatoshi(-1) var initialVerifiedBalance = Zatoshi(-1) - var initialTransparentBalance: WalletBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + var initialTransparentBalance: WalletBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) let utxo = try GetAddressUtxosReply(jsonString: """ { @@ -135,7 +137,7 @@ class ShieldFundsTests: XCTestCase { // at this point the balance should be all zeroes for transparent and shielded funds XCTAssertEqual(initialTotalBalance, Zatoshi.zero) XCTAssertEqual(initialVerifiedBalance, Zatoshi.zero) - initialTransparentBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + initialTransparentBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) XCTAssertEqual(initialTransparentBalance.total, .zero) XCTAssertEqual(initialTransparentBalance.verified, .zero) @@ -169,7 +171,7 @@ class ShieldFundsTests: XCTestCase { // at this point the balance should be zero for shielded, then zero verified transparent funds // and 10000 zatoshi of total (not verified) transparent funds. - let tFundsDetectedBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + let tFundsDetectedBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) XCTAssertEqual(tFundsDetectedBalance.total, Zatoshi(10000)) XCTAssertEqual(tFundsDetectedBalance.verified, Zatoshi(10000)) //FIXME: this should be zero @@ -199,7 +201,7 @@ class ShieldFundsTests: XCTestCase { wait(for: [tFundsConfirmationSyncExpectation], timeout: 5) // the transparent funds should be 10000 zatoshis both total and verified - let confirmedTFundsBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + let confirmedTFundsBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) XCTAssertEqual(confirmedTFundsBalance.total, Zatoshi(10000)) XCTAssertEqual(confirmedTFundsBalance.verified, Zatoshi(10000)) @@ -229,7 +231,7 @@ class ShieldFundsTests: XCTestCase { guard shouldContinue else { return } - let postShieldingBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + let postShieldingBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) // when funds are shielded the UTXOs should be marked as spend and not shown on the balance. // now balance should be zero shielded, zero transaparent. // verify that the balance has been marked as spent regardless of confirmation @@ -280,7 +282,7 @@ class ShieldFundsTests: XCTestCase { // Now it should verify that the balance has been shielded. The resulting balance should be zero // transparent funds and `10000 - fee` total shielded funds, zero verified shielded funds. // Fees at the time of writing the tests are 1000 zatoshi as defined on ZIP-313 - let postShieldingShieldedBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + let postShieldingShieldedBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) XCTAssertEqual(postShieldingShieldedBalance.total, Zatoshi(10000)) //FIXME: this should be zero XCTAssertEqual(postShieldingShieldedBalance.verified, Zatoshi(10000)) //FIXME: this should be zero @@ -317,7 +319,7 @@ class ShieldFundsTests: XCTestCase { XCTAssertNotNil(clearedTransaction) XCTAssertEqual(coordinator.synchronizer.getShieldedBalance(), Zatoshi(9000)) - let postShieldingConfirmationShieldedBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + let postShieldingConfirmationShieldedBalance = try await coordinator.synchronizer.getTransparentBalance(accountIndex: 0) XCTAssertEqual(postShieldingConfirmationShieldedBalance.total, .zero) XCTAssertEqual(postShieldingConfirmationShieldedBalance.verified, .zero) diff --git a/Tests/DarksideTests/SychronizerDarksideTests.swift b/Tests/DarksideTests/SychronizerDarksideTests.swift index 53645c2c..2a45e40b 100644 --- a/Tests/DarksideTests/SychronizerDarksideTests.swift +++ b/Tests/DarksideTests/SychronizerDarksideTests.swift @@ -34,13 +34,16 @@ class SychronizerDarksideTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) + + try self.coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") + } } override func tearDownWithError() throws { diff --git a/Tests/DarksideTests/SynchronizerTests.swift b/Tests/DarksideTests/SynchronizerTests.swift index d12c3fc1..bd0ef520 100644 --- a/Tests/DarksideTests/SynchronizerTests.swift +++ b/Tests/DarksideTests/SynchronizerTests.swift @@ -34,13 +34,16 @@ final class SynchronizerTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday + 50, //don't use an exact birthday, users never do. - channelProvider: ChannelProvider(), - network: network - ) - try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday:self.birthday + 50, //don't use an exact birthday, users never do. + channelProvider: ChannelProvider(), + network: self.network + ) + + try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) + } } override func tearDownWithError() throws { @@ -67,7 +70,7 @@ final class SynchronizerTests: XCTestCase { reorgExpectation.fulfill() } - func testSynchronizerStops() throws { + func testSynchronizerStops() async throws { hookToReOrgNotification() /* @@ -102,14 +105,14 @@ final class SynchronizerTests: XCTestCase { XCTFail("Failed with error: \(testError)") }) - DispatchQueue.main.asyncAfter(deadline: .now() + 5) { - self.coordinator.synchronizer.stop() - } + try await Task.sleep(nanoseconds: 5_000_000_000) + self.coordinator.synchronizer.stop() - wait(for: [processorStoppedExpectation,syncStoppedExpectation], timeout: 6, enforceOrder: true) + wait(for: [syncStoppedExpectation, processorStoppedExpectation], timeout: 6, enforceOrder: true) XCTAssertEqual(coordinator.synchronizer.status, .stopped) - XCTAssertEqual(coordinator.synchronizer.blockProcessor.state.getState(), .stopped) + let state = await coordinator.synchronizer.blockProcessor.state + XCTAssertEqual(state, .stopped) } func handleError(_ error: Error?) { diff --git a/Tests/DarksideTests/TransactionEnhancementTests.swift b/Tests/DarksideTests/TransactionEnhancementTests.swift index aad86315..10040ae0 100644 --- a/Tests/DarksideTests/TransactionEnhancementTests.swift +++ b/Tests/DarksideTests/TransactionEnhancementTests.swift @@ -85,8 +85,6 @@ class TransactionEnhancementTests: XCTestCase { return } - - guard case .success = dbInit else { XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))") return @@ -137,7 +135,7 @@ class TransactionEnhancementTests: XCTestCase { NotificationCenter.default.removeObserver(self) } - private func startProcessing() throws { + private func startProcessing() async throws { XCTAssertNotNil(processor) // Subscribe to notifications @@ -149,10 +147,10 @@ class TransactionEnhancementTests: XCTestCase { txFoundNotificationExpectation.subscribe(to: .blockProcessorFoundTransactions, object: processor) idleNotificationExpectation.subscribe(to: .blockProcessorIdle, object: processor) - try processor.start() + await processor.start() } - func testBasicEnhacement() throws { + func testBasicEnhancement() async throws { let targetLatestHeight = BlockHeight(663200) do { @@ -181,7 +179,7 @@ class TransactionEnhancementTests: XCTestCase { download and sync blocks from walletBirthday to firstLatestHeight */ do { - try startProcessing() + try await startProcessing() } catch { XCTFail("Error: \(error)") } diff --git a/Tests/DarksideTests/Z2TReceiveTests.swift b/Tests/DarksideTests/Z2TReceiveTests.swift index 5dc356ba..000330dd 100644 --- a/Tests/DarksideTests/Z2TReceiveTests.swift +++ b/Tests/DarksideTests/Z2TReceiveTests.swift @@ -30,20 +30,20 @@ class Z2TReceiveTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() + wait { [self] in + self.coordinator = try await TestCoordinator( + seed: self.seedPhrase, + walletBirthday: self.birthday, + channelProvider: ChannelProvider(), + network: self.network + ) - coordinator = try TestCoordinator( - seed: seedPhrase, - walletBirthday: birthday, - channelProvider: ChannelProvider(), - network: network - ) - - try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) + try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) + } } override func tearDownWithError() throws { try super.tearDownWithError() - NotificationCenter.default.removeObserver(self) try coordinator.stop() diff --git a/Tests/NetworkTests/BlockScanTests.swift b/Tests/NetworkTests/BlockScanTests.swift index 2de43c43..525c74a4 100644 --- a/Tests/NetworkTests/BlockScanTests.swift +++ b/Tests/NetworkTests/BlockScanTests.swift @@ -87,7 +87,7 @@ class BlockScanTests: XCTestCase { range: range ) XCTAssertFalse(Task.isCancelled) - try compactBlockProcessor.compactBlockScanning( + try await compactBlockProcessor.compactBlockScanning( rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, diff --git a/Tests/NetworkTests/BlockStreamingTest.swift b/Tests/NetworkTests/BlockStreamingTest.swift index 7928a53e..8b497750 100644 --- a/Tests/NetworkTests/BlockStreamingTest.swift +++ b/Tests/NetworkTests/BlockStreamingTest.swift @@ -79,7 +79,7 @@ class BlockStreamingTest: XCTestCase { startHeight: startHeight ) } catch { - XCTAssertNotNil(error as? CancellationError) + XCTAssertTrue(Task.isCancelled) } } diff --git a/Tests/NetworkTests/CompactBlockProcessorTests.swift b/Tests/NetworkTests/CompactBlockProcessorTests.swift index 13c39768..d9bec25a 100644 --- a/Tests/NetworkTests/CompactBlockProcessorTests.swift +++ b/Tests/NetworkTests/CompactBlockProcessorTests.swift @@ -102,7 +102,7 @@ class CompactBlockProcessorTests: XCTestCase { } } - private func startProcessing() { + private func startProcessing() async { XCTAssertNotNil(processor) // Subscribe to notifications @@ -113,12 +113,16 @@ class CompactBlockProcessorTests: XCTestCase { startedScanningNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedScanning, object: processor) idleNotificationExpectation.subscribe(to: Notification.Name.blockProcessorIdle, object: processor) - XCTAssertNoThrow(try processor.start()) + do { + try await processor.start() + } catch { + XCTFail("shouldn't fail") + } } // FIXME: disabled see https://github.com/zcash/ZcashLightClientKit/issues/590 - func testStartNotifiesSuscriptors() { - startProcessing() + func testStartNotifiesSuscriptors() async { + await startProcessing() wait( for: [ @@ -133,7 +137,7 @@ class CompactBlockProcessorTests: XCTestCase { } // FIXME: disabled see https://github.com/zcash/ZcashLightClientKit/issues/590 - func testProgressNotifications() { + func testProgressNotifications() async { let expectedUpdates = expectedBatches( currentHeight: processorConfig.walletBirthday, targetHeight: mockLatestHeight, @@ -141,7 +145,7 @@ class CompactBlockProcessorTests: XCTestCase { ) updatedNotificationExpectation.expectedFulfillmentCount = expectedUpdates - startProcessing() + await startProcessing() wait(for: [updatedNotificationExpectation], timeout: 300) } @@ -197,23 +201,23 @@ class CompactBlockProcessorTests: XCTestCase { ) } - func testDetermineLowerBoundPastBirthday() { + func testDetermineLowerBoundPastBirthday() async { let errorHeight = 781_906 let walletBirthday = 781_900 - let result = processor.determineLowerBound(errorHeight: errorHeight, consecutiveErrors: 1, walletBirthday: walletBirthday) + let result = await processor.determineLowerBound(errorHeight: errorHeight, consecutiveErrors: 1, walletBirthday: walletBirthday) let expected = 781_886 XCTAssertEqual(result, expected) } - func testDetermineLowerBound() { + func testDetermineLowerBound() async { let errorHeight = 781_906 let walletBirthday = 780_900 - let result = processor.determineLowerBound(errorHeight: errorHeight, consecutiveErrors: 0, walletBirthday: walletBirthday) + let result = await processor.determineLowerBound(errorHeight: errorHeight, consecutiveErrors: 0, walletBirthday: walletBirthday) let expected = 781_896 XCTAssertEqual(result, expected) diff --git a/Tests/NetworkTests/CompactBlockReorgTests.swift b/Tests/NetworkTests/CompactBlockReorgTests.swift new file mode 100644 index 00000000..18f5cc28 --- /dev/null +++ b/Tests/NetworkTests/CompactBlockReorgTests.swift @@ -0,0 +1,167 @@ +// +// CompactBlockReorgTests.swift +// ZcashLightClientKit-Unit-Tests +// +// Created by Francisco Gindre on 11/13/19. +// +// Copyright © 2019 Electric Coin Company. All rights reserved. + +import XCTest +@testable import TestUtils +@testable import ZcashLightClientKit + +// swiftlint:disable implicitly_unwrapped_optional force_try +class CompactBlockReorgTests: XCTestCase { + let processorConfig = CompactBlockProcessor.Configuration.standard( + for: ZcashNetworkBuilder.network(for: .testnet), + walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + ) + var processor: CompactBlockProcessor! + var downloadStartedExpect: XCTestExpectation! + var updatedNotificationExpectation: XCTestExpectation! + var stopNotificationExpectation: XCTestExpectation! + var startedScanningNotificationExpectation: XCTestExpectation! + var startedValidatingNotificationExpectation: XCTestExpectation! + var idleNotificationExpectation: XCTestExpectation! + var reorgNotificationExpectation: XCTestExpectation! + let network = ZcashNetworkBuilder.network(for: .testnet) + let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000 + + override func setUpWithError() throws { + try super.setUpWithError() + logger = SampleLogger(logLevel: .debug) + + let service = MockLightWalletService( + latestBlockHeight: mockLatestHeight, + service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet) + ) + let branchID = try ZcashRustBackend.consensusBranchIdFor(height: Int32(mockLatestHeight), networkType: network.networkType) + service.mockLightDInfo = LightdInfo.with { info in + info.blockHeight = UInt64(mockLatestHeight) + info.branch = "asdf" + info.buildDate = "today" + info.buildUser = "testUser" + info.chainName = "test" + info.consensusBranchID = branchID.toString() + info.estimatedHeight = UInt64(mockLatestHeight) + info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) + } + + guard case .success = try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, seed: nil, networkType: .testnet) else { + XCTFail("initDataDb failed. Expected Success but got .seedRequired") + return + } + + let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString)) + try! storage.createTable() + + let mockBackend = MockRustBackend.self + mockBackend.mockValidateCombinedChainFailAfterAttempts = 3 + mockBackend.mockValidateCombinedChainKeepFailing = false + mockBackend.mockValidateCombinedChainFailureHeight = self.network.constants.saplingActivationHeight + 320 + + processor = CompactBlockProcessor( + service: service, + storage: storage, + backend: mockBackend, + config: processorConfig + ) + + downloadStartedExpect = XCTestExpectation(description: "\(self.description) downloadStartedExpect") + stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation") + updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation") + startedValidatingNotificationExpectation = XCTestExpectation( + description: "\(self.description) startedValidatingNotificationExpectation" + ) + startedScanningNotificationExpectation = XCTestExpectation( + description: "\(self.description) startedScanningNotificationExpectation" + ) + idleNotificationExpectation = XCTestExpectation(description: "\(self.description) idleNotificationExpectation") + reorgNotificationExpectation = XCTestExpectation(description: "\(self.description) reorgNotificationExpectation") + + NotificationCenter.default.addObserver( + self, + selector: #selector(processorHandledReorg(_:)), + name: Notification.Name.blockProcessorHandledReOrg, + object: processor + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(processorFailed(_:)), + name: Notification.Name.blockProcessorFailed, + object: processor + ) + } + + override func tearDown() { + super.tearDown() + try! FileManager.default.removeItem(at: processorConfig.cacheDb) + try? FileManager.default.removeItem(at: processorConfig.dataDb) + downloadStartedExpect.unsubscribeFromNotifications() + stopNotificationExpectation.unsubscribeFromNotifications() + updatedNotificationExpectation.unsubscribeFromNotifications() + startedScanningNotificationExpectation.unsubscribeFromNotifications() + startedValidatingNotificationExpectation.unsubscribeFromNotifications() + idleNotificationExpectation.unsubscribeFromNotifications() + reorgNotificationExpectation.unsubscribeFromNotifications() + NotificationCenter.default.removeObserver(self) + } + + @objc func processorHandledReorg(_ notification: Notification) { + XCTAssertNotNil(notification.userInfo) + if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight, + let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight { + XCTAssertTrue( reorg == 0 || reorg > self.network.constants.saplingActivationHeight) + XCTAssertTrue( rewind == 0 || rewind > self.network.constants.saplingActivationHeight) + XCTAssertTrue( rewind <= reorg ) + reorgNotificationExpectation.fulfill() + } else { + XCTFail("CompactBlockProcessor reorg notification is malformed") + } + } + + @objc func processorFailed(_ notification: Notification) { + XCTAssertNotNil(notification.userInfo) + if let error = notification.userInfo?["error"] { + XCTFail("CompactBlockProcessor failed with Error: \(error)") + } else { + XCTFail("CompactBlockProcessor failed") + } + } + + private func startProcessing() async { + XCTAssertNotNil(processor) + + // Subscribe to notifications + downloadStartedExpect.subscribe(to: Notification.Name.blockProcessorStartedDownloading, object: processor) + stopNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStopped, object: processor) + updatedNotificationExpectation.subscribe(to: Notification.Name.blockProcessorUpdated, object: processor) + startedValidatingNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedValidating, object: processor) + startedScanningNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedScanning, object: processor) + idleNotificationExpectation.subscribe(to: Notification.Name.blockProcessorFinished, object: processor) + reorgNotificationExpectation.subscribe(to: Notification.Name.blockProcessorHandledReOrg, object: processor) + + await processor.start() + } + + func testNotifiesReorg() async { + await startProcessing() + + wait( + for: [ + downloadStartedExpect, + startedValidatingNotificationExpectation, + startedScanningNotificationExpectation, + reorgNotificationExpectation, + idleNotificationExpectation + ], + timeout: 300, + enforceOrder: true + ) + } + + private func expectedBatches(currentHeight: BlockHeight, targetHeight: BlockHeight, batchSize: Int) -> Int { + (abs(currentHeight - targetHeight) / batchSize) + } +} diff --git a/Tests/OfflineTests/WalletTests.swift b/Tests/OfflineTests/WalletTests.swift index 700b5ccb..b939c15b 100644 --- a/Tests/OfflineTests/WalletTests.swift +++ b/Tests/OfflineTests/WalletTests.swift @@ -33,7 +33,7 @@ class WalletTests: XCTestCase { } } - func testWalletInitialization() throws { + func testWalletInitialization() async throws { let derivationTool = DerivationTool(networkType: network.networkType) let ufvk = try derivationTool.deriveUnifiedSpendingKey(seed: seedData.bytes, accountIndex: 0) .map( { try derivationTool.deriveUnifiedFullViewingKey(from: $0) }) @@ -50,14 +50,13 @@ class WalletTests: XCTestCase { ) let synchronizer = try SDKSynchronizer(initializer: wallet) - - var dbInit: Initializer.InitializationResult! - - XCTAssertNoThrow(try { dbInit = try synchronizer.prepare(with: nil) }()) - - guard case .success = dbInit else { - XCTFail("Failed to initDataDb. Expected `.success` got: \(String(describing: dbInit))") - return + do { + guard case .success = try await synchronizer.prepare(with: seedData.bytes) else { + XCTFail("Failed to initDataDb. Expected `.success` got: `.seedRequired`") + return + } + } catch { + XCTFail("shouldn't fail here") } // fileExists actually sucks, so attempting to delete the file and checking what happens is far better :) diff --git a/Tests/TestUtils/FakeService.swift b/Tests/TestUtils/FakeService.swift index db061395..5a3f7389 100644 --- a/Tests/TestUtils/FakeService.swift +++ b/Tests/TestUtils/FakeService.swift @@ -25,18 +25,18 @@ class MockLightWalletService: LightWalletService { var queue = DispatchQueue(label: "mock service queue") func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream { - AsyncThrowingStream { _ in } + service.blockStream(startHeight: startHeight, endHeight: endHeight) } func closeConnection() { } func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream { - AsyncThrowingStream { _ in } + service.fetchUTXOs(for: tAddress, height: height) } func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream { - AsyncThrowingStream { _ in } + service.fetchUTXOs(for: tAddresses, height: height) } private var service: LightWalletService diff --git a/Tests/TestUtils/TestCoordinator.swift b/Tests/TestUtils/TestCoordinator.swift index 77c8c19d..f2f517db 100644 --- a/Tests/TestUtils/TestCoordinator.swift +++ b/Tests/TestUtils/TestCoordinator.swift @@ -50,7 +50,7 @@ class TestCoordinator { walletBirthday: BlockHeight, channelProvider: ChannelProvider, network: ZcashNetwork - ) throws { + ) async throws { let derivationTool = DerivationTool(networkType: network.networkType) let spendingKey = try derivationTool.deriveUnifiedSpendingKey( @@ -60,7 +60,7 @@ class TestCoordinator { let ufvk = try derivationTool.deriveUnifiedFullViewingKey(from: spendingKey) - try self.init( + await try self.init( spendingKey: spendingKey, unifiedFullViewingKey: ufvk, walletBirthday: walletBirthday, @@ -75,7 +75,7 @@ class TestCoordinator { walletBirthday: BlockHeight, channelProvider: ChannelProvider, network: ZcashNetwork - ) throws { + ) async throws { self.spendingKey = spendingKey self.birthday = walletBirthday self.channelProvider = channelProvider @@ -93,7 +93,7 @@ class TestCoordinator { let storage = CompactBlockStorage(url: databases.cacheDB, readonly: false) try storage.createTable() - let buildResult = try TestSynchronizerBuilder.build( + let buildResult = try await TestSynchronizerBuilder.build( rustBackend: ZcashRustBackend.self, lowerBoundHeight: self.birthday, cacheDbURL: databases.cacheDB, @@ -186,6 +186,12 @@ class TestCoordinator { } } +extension CompactBlockProcessor { + public func setConfig(_ config: Configuration) { + self.config = config + } +} + extension TestCoordinator { func resetBlocks(dataset: DarksideData) throws { switch dataset { @@ -219,19 +225,25 @@ extension TestCoordinator { } func reset(saplingActivation: BlockHeight, branchID: String, chainName: String) throws { - let config = self.synchronizer.blockProcessor.config - - self.synchronizer.blockProcessor.config = CompactBlockProcessor.Configuration( - cacheDb: config.cacheDb, - dataDb: config.dataDb, - downloadBatchSize: config.downloadBatchSize, - retries: config.retries, - maxBackoffInterval: config.maxBackoffInterval, - rewindDistance: config.rewindDistance, - walletBirthday: config.walletBirthday, - saplingActivation: config.saplingActivation, - network: config.network - ) + Task { + await self.synchronizer.blockProcessor.stop() + let config = await self.synchronizer.blockProcessor.config + + let newConfig = CompactBlockProcessor.Configuration( + cacheDb: config.cacheDb, + dataDb: config.dataDb, + downloadBatchSize: config.downloadBatchSize, + retries: config.retries, + maxBackoffInterval: config.maxBackoffInterval, + rewindDistance: config.rewindDistance, + walletBirthday: config.walletBirthday, + saplingActivation: config.saplingActivation, + network: config.network + ) + + await self.synchronizer.blockProcessor.setConfig(newConfig) + } + try service.reset(saplingActivation: saplingActivation, branchID: branchID, chainName: chainName) } @@ -279,7 +291,7 @@ enum TestSynchronizerBuilder { network: ZcashNetwork, seed: [UInt8]? = nil, loggerProxy: Logger? = nil - ) throws -> (spendingKeys: [UnifiedSpendingKey]?, synchronizer: SDKSynchronizer) { + ) async throws -> (spendingKeys: [UnifiedSpendingKey]?, synchronizer: SDKSynchronizer) { let initializer = Initializer( cacheDbURL: cacheDbURL, dataDbURL: dataDbURL, @@ -295,7 +307,7 @@ enum TestSynchronizerBuilder { ) let synchronizer = try SDKSynchronizer(initializer: initializer) - if case .seedRequired = try synchronizer.prepare(with: seed) { + if case .seedRequired = try await synchronizer.prepare(with: seed) { throw TestCoordinator.CoordinatorError.seedRequiredForMigration } @@ -319,7 +331,7 @@ enum TestSynchronizerBuilder { walletBirthday: BlockHeight, network: ZcashNetwork, loggerProxy: Logger? = nil - ) throws -> (spendingKeys: [UnifiedSpendingKey]?, synchronizer: SDKSynchronizer) { + ) async throws -> (spendingKeys: [UnifiedSpendingKey]?, synchronizer: SDKSynchronizer) { let spendingKey = try DerivationTool(networkType: network.networkType) .deriveUnifiedSpendingKey(seed: seedBytes, accountIndex: 0) @@ -327,7 +339,7 @@ enum TestSynchronizerBuilder { let uvk = try DerivationTool(networkType: network.networkType) .deriveUnifiedFullViewingKey(from: spendingKey) - return try build( + return try await build( rustBackend: rustBackend, lowerBoundHeight: lowerBoundHeight, cacheDbURL: cacheDbURL, diff --git a/Tests/TestUtils/XCAsyncTestCase.swift b/Tests/TestUtils/XCAsyncTestCase.swift new file mode 100644 index 00000000..14c25de4 --- /dev/null +++ b/Tests/TestUtils/XCAsyncTestCase.swift @@ -0,0 +1,22 @@ +// +// XCAsyncTestCase.swift +// +// +// credits: https://betterprogramming.pub/async-setup-and-teardown-in-xctestcase-dc7a2cdb9fb +// +/// +/// A subclass of ``XCTestCase`` that supports async setup and teardown. +/// +import Foundation +import XCTest + +extension XCTestCase { + func wait(asyncBlock: @escaping (() async throws -> Void)) { + let semaphore = DispatchSemaphore(value: 0) + Task.init { + try await asyncBlock() + semaphore.signal() + } + semaphore.wait() + } +}