diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift index 25f66818..3281eae9 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift @@ -10,7 +10,7 @@ import Foundation import ZcashLightClientKit import MnemonicSwift struct DemoAppConfig { - static var host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "localhost" + static var host = ZcashSDK.isMainnet ? "localhost" : "localhost" static var port: Int = 9067 static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 663174 : 620_000 static var network = ZcashSDK.isMainnet ? ZcashNetwork.mainNet : ZcashNetwork.testNet diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Sapling Parameters/SaplingParametersViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Sapling Parameters/SaplingParametersViewController.swift index 3aa0f94e..e8997e4f 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Sapling Parameters/SaplingParametersViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Sapling Parameters/SaplingParametersViewController.swift @@ -59,32 +59,18 @@ class SaplingParametersViewController: UIViewController { @IBAction func download(_ sender: Any) { let outputParameter = try! __outputParamsURL() let spendParameter = try! __spendParamsURL() - if !FileManager.default.isReadableFile(atPath: outputParameter.absoluteString) { - SaplingParameterDownloader.downloadOutputParameter(outputParameter) { [weak self] result in + SaplingParameterDownloader.downloadParamsIfnotPresent(spendURL: spendParameter, outputURL: outputParameter) { (result) in + DispatchQueue.main.async { [weak self] in guard let self = self else { return } - DispatchQueue.main.async { - switch result{ - case .success: - self.updateButtons() - self.updateColor() - case .failure(let error): - self.showError(error) - } - } - } - } - - if !FileManager.default.isReadableFile(atPath: spendParameter.absoluteString) { - SaplingParameterDownloader.downloadSpendParameter(try! __spendParamsURL()) { [weak self] result in - guard let self = self else { return } - DispatchQueue.main.async { - switch result{ - case .success: - self.updateButtons() - self.updateColor() - case .failure(let error): - self.showError(error) - } + switch result { + case .success(let urls): + self.spendPath.text = urls.spend.path + self.outputPath.text = urls.output.path + self.updateColor() + self.updateButtons() + + case .failure(let error): + self.showError(error) } } } @@ -109,14 +95,4 @@ class SaplingParametersViewController: UIViewController { self.updateColor() self.updateButtons() } - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. - } - */ - } diff --git a/ZcashLightClientKit/Initializer.swift b/ZcashLightClientKit/Initializer.swift index 3fe0a05b..a317e69a 100644 --- a/ZcashLightClientKit/Initializer.swift +++ b/ZcashLightClientKit/Initializer.swift @@ -227,11 +227,58 @@ public class Initializer { } func isSpendParameterPresent() -> Bool { - FileManager.default.isReadableFile(atPath: self.spendParamsURL.absoluteString) + FileManager.default.isReadableFile(atPath: self.spendParamsURL.path) } func isOutputParameterPresent() -> Bool { - FileManager.default.isExecutableFile(atPath: self.outputParamsURL.absoluteString) + FileManager.default.isReadableFile(atPath: self.outputParamsURL.path) + } + + + + func downloadParametersIfNeeded(result: @escaping (Result) -> Void) { + let spendParameterPresent = isSpendParameterPresent() + let outputParameterPresent = isOutputParameterPresent() + + if spendParameterPresent && outputParameterPresent { + result(.success(true)) + return + } + + let outputURL = self.outputParamsURL + let spendURL = self.spendParamsURL + + + if !outputParameterPresent { + SaplingParameterDownloader.downloadOutputParameter(outputURL) { outputResult in + switch outputResult { + case .failure(let e): + result(.failure(e)) + case .success: + guard !spendParameterPresent else { + result(.success(false)) + return + } + SaplingParameterDownloader.downloadSpendParameter(spendURL) { (spendResult) in + switch spendResult { + case .failure(let e): + result(.failure(e)) + case .success: + result(.success(false)) + } + } + } + } + } else if !spendParameterPresent { + SaplingParameterDownloader.downloadSpendParameter(spendURL) { (spendResult) in + switch spendResult { + case .failure(let e): + result(.failure(e)) + case .success: + result(.success(false)) + } + } + } } } diff --git a/ZcashLightClientKit/Synchronizer.swift b/ZcashLightClientKit/Synchronizer.swift index 1962ffce..4a0ad28d 100644 --- a/ZcashLightClientKit/Synchronizer.swift +++ b/ZcashLightClientKit/Synchronizer.swift @@ -21,6 +21,7 @@ public enum SynchronizerError: Error { case networkTimeout case uncategorized(underlyingError: Error) case criticalError + case parameterMissing(underlyingError: Error) } /** diff --git a/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift b/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift index 513279e9..6d7ec619 100644 --- a/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift +++ b/ZcashLightClientKit/UIKit/Synchronizer/SDKSynchronizer.swift @@ -392,12 +392,6 @@ public class SDKSynchronizer: Synchronizer { @objc func applicationWillResignActive(_ notification: Notification) { registerBackgroundActivity() LoggerProxy.debug("applicationWillResignActive") -// do { -// -// try stop() -// } catch { -// LoggerProxy.debug("stop failed with error: \(error)") -// } } @objc func applicationWillTerminate(_ notification: Notification) { @@ -408,6 +402,20 @@ public class SDKSynchronizer: Synchronizer { public func sendToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result) -> Void) { + initializer.downloadParametersIfNeeded { (downloadResult) in + DispatchQueue.main.async { [weak self] in + switch downloadResult { + case .success: + self?.createToAddress(spendingKey: spendingKey, zatoshi: zatoshi, toAddress: toAddress, memo: memo, from: accountIndex, resultBlock: resultBlock) + case .failure(let error): + resultBlock(.failure(SynchronizerError.parameterMissing(underlyingError: error))) + } + } + } + } + + func createToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result) -> Void) { + do { let spend = try transactionManager.initSpend(zatoshi: Int(zatoshi), toAddress: toAddress, memo: memo, from: accountIndex) @@ -435,7 +443,6 @@ public class SDKSynchronizer: Synchronizer { resultBlock(.failure(error)) } } - public func getAddress(accountIndex: Int) -> String { initializer.getAddress(index: accountIndex) ?? "" } @@ -464,7 +471,7 @@ public class SDKSynchronizer: Synchronizer { PagedTransactionRepositoryBuilder.build(initializer: initializer, kind: .all) } - public func latestDownloadedHeight() throws -> BlockHeight { + public func latestDownloadedHeight() throws -> BlockHeight { try initializer.downloader.lastDownloadedBlockHeight() } diff --git a/ZcashLightClientKit/Utils/SaplingParameterDownloader.swift b/ZcashLightClientKit/Utils/SaplingParameterDownloader.swift index da5d4abc..c6b7ef35 100644 --- a/ZcashLightClientKit/Utils/SaplingParameterDownloader.swift +++ b/ZcashLightClientKit/Utils/SaplingParameterDownloader.swift @@ -6,13 +6,21 @@ // import Foundation - +/** + Helper class to handle the download of Sapling parameters + */ public class SaplingParameterDownloader { public enum Errors: Error { case invalidURL(url: String) case failed(error: Error) } + /** + Download a Spend parameter from default host and stores it at given URL + - Parameters: + - at: The destination URL for the download + - result: block to handle the download success or error + */ public static func downloadSpendParameter(_ at: URL, result: @escaping (Result) -> Void) { guard let url = URL(string: spendParamsURLString) else { @@ -21,7 +29,12 @@ public class SaplingParameterDownloader { } downloadFileWithRequest(URLRequest(url: url), at: at, result: result) } - + /** + Download an Output parameter from default host and stores it at given URL + - Parameters: + - at: The destination URL for the download + - result: block to handle the download success or error + */ public static func downloadOutputParameter(_ at: URL, result: @escaping (Result) -> Void) { guard let url = URL(string: outputParamsURLString) else { result(.failure(Errors.invalidURL(url: outputParamsURLString))) @@ -46,6 +59,55 @@ public class SaplingParameterDownloader { } task.resume() } + /** + Downloads the parameters if not present and provides the resulting URLs for both parameters + - Parameters: + - spendURL: URL to check whether the parameter is already downloaded + - outputURL: URL to check whether the parameter is already downloaded + - result: block to handle success or error + */ + public static func downloadParamsIfnotPresent(spendURL: URL, outputURL: URL, result: @escaping (Result<(spend: URL, output: URL),Error>) -> Void) { + + ensureSpendParameter(at: spendURL) { (spendResult) in + switch spendResult { + case .success(let spendResultURL): + ensureOutputParameter(at: outputURL) { (outputResult) in + switch outputResult { + case .success(let outputResultURL): + result(.success((spendResultURL,outputResultURL))) + case .failure(let outputResultError): + result(.failure(Errors.failed(error: outputResultError))) + } + } + case .failure(let spendResultError): + result(.failure(Errors.failed(error: spendResultError))) + } + } + } + + static func ensureSpendParameter(at url: URL, result: @escaping (Result) -> Void) { + if isFilePresent(url: url) { + DispatchQueue.global().async { + result(.success(url)) + } + } else { + downloadSpendParameter(url, result: result) + } + } + + static func ensureOutputParameter(at url: URL, result: @escaping (Result) -> Void) { + if isFilePresent(url: url) { + DispatchQueue.global().async { + result(.success(url)) + } + } else { + downloadOutputParameter(url, result: result) + } + } + + static func isFilePresent(url: URL) -> Bool { + (try? FileManager.default.attributesOfItem(atPath: url.path)) != nil + } static var spendParamsURLString: String { return ZcashSDK.CLOUD_PARAM_DIR_URL + ZcashSDK.SPEND_PARAM_FILE_NAME