From 2c93802828044cdd8c29404ea2b19e01b9c645b7 Mon Sep 17 00:00:00 2001 From: Lukas Korba Date: Wed, 21 Sep 2022 14:30:06 +0200 Subject: [PATCH] [#487] shieldFunds to async/await - API refactored to async [#487] shieldFunds to async/await - unit tests refactored --- .../Get UTXOs/GetUTXOsViewController.swift | 28 ++-- .../ZcashLightClientKit/Synchronizer.swift | 7 +- .../Synchronizer/SDKSynchronizer.swift | 33 ++--- Tests/DarksideTests/ShieldFundsTests.swift | 125 ++++++++++-------- 4 files changed, 94 insertions(+), 99 deletions(-) diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift index 0d79d9df..a0ed553c 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift @@ -46,25 +46,17 @@ class GetUTXOsViewController: UIViewController { KRProgressHUD.showMessage("🛡 Shielding 🛡") - AppDelegate.shared.sharedSynchronizer.shieldFunds( - spendingKey: spendingKey, - transparentSecretKey: transparentSecretKey, - memo: "shielding is fun!", - from: 0, - resultBlock: { result in - DispatchQueue.main.async { - KRProgressHUD.dismiss() - switch result { - case .success(let transaction): - self.messageLabel.text = "funds shielded \(transaction)" - case .failure(let error): - self.messageLabel.text = "Shielding failed: \(error)" - } - } - } - ) + Task { @MainActor in + let transaction = try await AppDelegate.shared.sharedSynchronizer.shieldFunds( + spendingKey: spendingKey, + transparentSecretKey: transparentSecretKey, + memo: "shielding is fun!", + from: 0) + KRProgressHUD.dismiss() + self.messageLabel.text = "funds shielded \(transaction)" + } } catch { - self.messageLabel.text = "Error \(error)" + self.messageLabel.text = "Shielding failed \(error)" } } } diff --git a/Sources/ZcashLightClientKit/Synchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer.swift index c7948e47..10b86ffe 100644 --- a/Sources/ZcashLightClientKit/Synchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer.swift @@ -142,7 +142,7 @@ public protocol Synchronizer { resultBlock: @escaping (_ result: Result) -> Void ) - /// Sends zatoshi. + /// Shields zatoshi. /// - Parameter spendingKey: the key that allows spends to occur. /// - Parameter transparentSecretKey: the key that allows to spend transaprent funds /// - Parameter memo: the optional memo to include as part of the transaction. @@ -151,9 +151,8 @@ public protocol Synchronizer { spendingKey: String, transparentSecretKey: String, memo: String?, - from accountIndex: Int, - resultBlock: @escaping (_ result: Result) -> Void - ) + from accountIndex: Int + ) async throws -> PendingTransactionEntity /// Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only /// an option if the transaction has not yet been submitted to the server. diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index f6c27f44..ceb36fd1 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -507,9 +507,8 @@ public class SDKSynchronizer: Synchronizer { spendingKey: String, transparentSecretKey: String, memo: String?, - from accountIndex: Int, - resultBlock: @escaping (Result) -> Void - ) { + from accountIndex: Int + ) async throws -> PendingTransactionEntity { // let's see if there are funds to shield let derivationTool = DerivationTool(networkType: self.network.networkType) @@ -519,32 +518,22 @@ public class SDKSynchronizer: Synchronizer { // 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 { - resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds)) - return + throw ShieldFundsError.insuficientTransparentFunds } let viewingKey = try derivationTool.deriveViewingKey(spendingKey: spendingKey) let zAddr = try derivationTool.deriveShieldedAddress(viewingKey: viewingKey) let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: zAddr, memo: memo, from: 0) - // TODO: Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487 - Task { - do { - let transaction = try await transactionManager.encodeShieldingTransaction( - spendingKey: spendingKey, - tsk: transparentSecretKey, - pendingTransaction: shieldingSpend - ) - - let submittedTx = try await transactionManager.submit(pendingTransaction: transaction) - resultBlock(.success(submittedTx)) - } catch { - resultBlock(.failure(error)) - } - } + let transaction = try await transactionManager.encodeShieldingTransaction( + spendingKey: spendingKey, + tsk: transparentSecretKey, + pendingTransaction: shieldingSpend + ) + + return try await transactionManager.submit(pendingTransaction: transaction) } catch { - resultBlock(.failure(error)) - return + throw error } } diff --git a/Tests/DarksideTests/ShieldFundsTests.swift b/Tests/DarksideTests/ShieldFundsTests.swift index 836d4f13..c0a4463b 100644 --- a/Tests/DarksideTests/ShieldFundsTests.swift +++ b/Tests/DarksideTests/ShieldFundsTests.swift @@ -9,6 +9,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit + class ShieldFundsTests: XCTestCase { // TODO: Parameterize this from environment? // swiftlint:disable:next line_length @@ -82,7 +83,7 @@ class ShieldFundsTests: XCTestCase { /// 15. sync up to the new chain tip /// verify that the shielded transactions are confirmed /// - func testShieldFunds() throws { + func testShieldFunds() async throws { // 1. load the dataset try coordinator.service.useDataset(from: "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/shielding-dataset/shield-funds/1631000.txt") @@ -110,15 +111,19 @@ class ShieldFundsTests: XCTestCase { let preTxExpectation = XCTestExpectation(description: "pre receive") // 3. sync up to that height - try coordinator.sync( - completion: { synchro in - 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: { synchronizer in + initialVerifiedBalance = synchronizer.initializer.getVerifiedBalance() + initialTotalBalance = synchronizer.initializer.getBalance() + preTxExpectation.fulfill() + shouldContinue = true + continuation.resume() + }, error: self.handleError) + } catch { + continuation.resume(throwing: error) + } + } wait(for: [preTxExpectation], timeout: 10) @@ -149,14 +154,17 @@ class ShieldFundsTests: XCTestCase { shouldContinue = false // 6. Sync and find the UXTO on chain. - try coordinator.sync( - completion: { synchro in - tFundsDetectionExpectation.fulfill() - shouldContinue = true - }, - error: self.handleError - ) - + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync(completion: { synchronizer in + shouldContinue = true + tFundsDetectionExpectation.fulfill() + continuation.resume() + }, error: self.handleError) + } catch { + continuation.resume(throwing: error) + } + } wait(for: [tFundsDetectionExpectation], timeout: 2) // at this point the balance should be zero for shielded, then zero verified transparent funds @@ -176,13 +184,17 @@ class ShieldFundsTests: XCTestCase { sleep(2) // 8. sync up to chain tip. - try coordinator.sync( - completion: { synchro in - tFundsConfirmationSyncExpectation.fulfill() - shouldContinue = true - }, - error: self.handleError - ) + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync(completion: { synchronizer in + shouldContinue = true + tFundsConfirmationSyncExpectation.fulfill() + continuation.resume() + }, error: self.handleError) + } catch { + continuation.resume(throwing: error) + } + } wait(for: [tFundsConfirmationSyncExpectation], timeout: 5) @@ -209,22 +221,18 @@ class ShieldFundsTests: XCTestCase { var shieldingPendingTx: PendingTransactionEntity? // shield the funds - coordinator.synchronizer.shieldFunds( - spendingKey: coordinator.spendingKey, - transparentSecretKey: transparentSecretKey, - memo: "shield funds", - from: 0 - ) { result in - switch result { - case .failure(let error): - XCTFail("Failed With error: \(error.localizedDescription)") - - case .success(let pendingTx): - shouldContinue = true - XCTAssertEqual(pendingTx.value, Zatoshi(10000)) - shieldingPendingTx = pendingTx - } + do { + let pendingTx = try await coordinator.synchronizer.shieldFunds( + spendingKey: coordinator.spendingKey, + transparentSecretKey: transparentSecretKey, + memo: "shield funds", + from: 0) + shouldContinue = true + XCTAssertEqual(pendingTx.value, Zatoshi(10000)) + shieldingPendingTx = pendingTx shieldFundsExpectation.fulfill() + } catch { + XCTFail("Failed With error: \(error.localizedDescription)") } wait(for: [shieldFundsExpectation], timeout: 30) @@ -264,14 +272,17 @@ class ShieldFundsTests: XCTestCase { // 13. sync up to chain tip let postShieldSyncExpectation = XCTestExpectation(description: "sync Post shield") shouldContinue = false - try coordinator.sync( - completion: { synchro in - postShieldSyncExpectation.fulfill() - shouldContinue = true - }, - error: self.handleError - ) - + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync(completion: { synchronizer in + shouldContinue = true + postShieldSyncExpectation.fulfill() + continuation.resume() + }, error: self.handleError) + } catch { + continuation.resume(throwing: error) + } + } wait(for: [postShieldSyncExpectation], timeout: 3) guard shouldContinue else { return } @@ -294,13 +305,17 @@ class ShieldFundsTests: XCTestCase { shouldContinue = false // 15. sync up to the new chain tip - try coordinator.sync( - completion: { synchro in - confirmationExpectation.fulfill() - shouldContinue = true - }, - error: self.handleError - ) + try await withCheckedThrowingContinuation { continuation in + do { + try coordinator.sync(completion: { synchronizer in + shouldContinue = true + confirmationExpectation.fulfill() + continuation.resume() + }, error: self.handleError) + } catch { + continuation.resume(throwing: error) + } + } wait(for: [confirmationExpectation], timeout: 5)