Merge branch 'master' into merge-master-to-zip-316
This commit is contained in:
commit
ca5221ac75
|
@ -45,24 +45,17 @@ class GetUTXOsViewController: UIViewController {
|
||||||
|
|
||||||
KRProgressHUD.showMessage("🛡 Shielding 🛡")
|
KRProgressHUD.showMessage("🛡 Shielding 🛡")
|
||||||
|
|
||||||
AppDelegate.shared.sharedSynchronizer.shieldFunds(
|
Task { @MainActor in
|
||||||
transparentAccountPrivateKey: transparentSecretKey,
|
let transaction = try await AppDelegate.shared.sharedSynchronizer.shieldFunds(
|
||||||
memo: "shielding is fun!",
|
transparentAccountPrivateKey: transparentSecretKey,
|
||||||
from: 0,
|
memo: "shielding is fun!",
|
||||||
resultBlock: { result in
|
from: 0
|
||||||
DispatchQueue.main.async {
|
)
|
||||||
KRProgressHUD.dismiss()
|
KRProgressHUD.dismiss()
|
||||||
switch result {
|
self.messageLabel.text = "funds shielded \(transaction)"
|
||||||
case .success(let transaction):
|
}
|
||||||
self.messageLabel.text = "funds shielded \(transaction)"
|
|
||||||
case .failure(let error):
|
|
||||||
self.messageLabel.text = "Shielding failed: \(error)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
self.messageLabel.text = "Error \(error)"
|
self.messageLabel.text = "Shielding failed \(error)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,25 +79,21 @@ class SaplingParametersViewController: UIViewController {
|
||||||
@IBAction func download(_ sender: Any) {
|
@IBAction func download(_ sender: Any) {
|
||||||
let outputParameter = try! outputParamsURLHelper()
|
let outputParameter = try! outputParamsURLHelper()
|
||||||
let spendParameter = try! spendParamsURLHelper()
|
let spendParameter = try! spendParamsURLHelper()
|
||||||
SaplingParameterDownloader.downloadParamsIfnotPresent(
|
|
||||||
spendURL: spendParameter,
|
Task { @MainActor in
|
||||||
outputURL: outputParameter,
|
do {
|
||||||
result: { result in
|
let urls = try await SaplingParameterDownloader.downloadParamsIfnotPresent(
|
||||||
DispatchQueue.main.async { [weak self] in
|
spendURL: spendParameter,
|
||||||
guard let self = self else { return }
|
outputURL: outputParameter
|
||||||
switch result {
|
)
|
||||||
case .success(let urls):
|
spendPath.text = urls.spend.path
|
||||||
self.spendPath.text = urls.spend.path
|
outputPath.text = urls.output.path
|
||||||
self.outputPath.text = urls.output.path
|
updateColor()
|
||||||
self.updateColor()
|
updateButtons()
|
||||||
self.updateButtons()
|
} catch {
|
||||||
|
showError(error)
|
||||||
case .failure(let error):
|
|
||||||
self.showError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileExists(_ path: String) -> Bool {
|
func fileExists(_ path: String) -> Bool {
|
||||||
|
|
|
@ -220,28 +220,21 @@ class SendViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
KRProgressHUD.show()
|
KRProgressHUD.show()
|
||||||
|
|
||||||
synchronizer.sendToAddress(
|
Task { @MainActor in
|
||||||
spendingKey: spendingKey,
|
do {
|
||||||
zatoshi: zec,
|
let pendingTransaction = try await synchronizer.sendToAddress(
|
||||||
// swiftlint:disable:next force_try
|
spendingKey: spendingKey,
|
||||||
toAddress: try! Recipient(recipient, network: kZcashNetwork.networkType),
|
zatoshi: zec,
|
||||||
memo: !self.memoField.text.isEmpty ? self.memoField.text : nil,
|
toAddress: recipient,
|
||||||
from: 0
|
memo: !self.memoField.text.isEmpty ? self.memoField.text : nil,
|
||||||
) { [weak self] result in
|
from: 0
|
||||||
DispatchQueue.main.async {
|
)
|
||||||
KRProgressHUD.dismiss()
|
KRProgressHUD.dismiss()
|
||||||
}
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
case .success(let pendingTransaction):
|
|
||||||
loggerProxy.info("transaction created: \(pendingTransaction)")
|
loggerProxy.info("transaction created: \(pendingTransaction)")
|
||||||
|
} catch {
|
||||||
case .failure(let error):
|
fail(error)
|
||||||
DispatchQueue.main.async {
|
loggerProxy.error("SEND FAILED: \(error)")
|
||||||
self?.fail(error)
|
|
||||||
loggerProxy.error("SEND FAILED: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ extension CompactBlockProcessor {
|
||||||
guard let latestHeight = targetHeightInternal else {
|
guard let latestHeight = targetHeightInternal else {
|
||||||
throw LightWalletServiceError.generalError(message: "missing target height on compactBlockStreamDownload")
|
throw LightWalletServiceError.generalError(message: "missing target height on compactBlockStreamDownload")
|
||||||
}
|
}
|
||||||
|
try Task.checkCancellation()
|
||||||
let latestDownloaded = try await storage.latestHeightAsync()
|
let latestDownloaded = try await storage.latestHeightAsync()
|
||||||
let startHeight = max(startHeight ?? BlockHeight.empty(), latestDownloaded)
|
let startHeight = max(startHeight ?? BlockHeight.empty(), latestDownloaded)
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ extension CompactBlockProcessor {
|
||||||
)
|
)
|
||||||
|
|
||||||
for try await zcashCompactBlock in stream {
|
for try await zcashCompactBlock in stream {
|
||||||
|
try Task.checkCancellation()
|
||||||
buffer.append(zcashCompactBlock)
|
buffer.append(zcashCompactBlock)
|
||||||
if buffer.count >= blockBufferSize {
|
if buffer.count >= blockBufferSize {
|
||||||
// TODO: writeAsync doesn't make sense here, awaiting it or calling blocking API have the same result and impact
|
// TODO: writeAsync doesn't make sense here, awaiting it or calling blocking API have the same result and impact
|
||||||
|
|
|
@ -329,48 +329,35 @@ public class Initializer {
|
||||||
FileManager.default.isReadableFile(atPath: self.outputParamsURL.path)
|
FileManager.default.isReadableFile(atPath: self.outputParamsURL.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadParametersIfNeeded(result: @escaping (Result<Bool, Error>) -> Void) {
|
@discardableResult
|
||||||
|
func downloadParametersIfNeeded() async throws -> Bool {
|
||||||
let spendParameterPresent = isSpendParameterPresent()
|
let spendParameterPresent = isSpendParameterPresent()
|
||||||
let outputParameterPresent = isOutputParameterPresent()
|
let outputParameterPresent = isOutputParameterPresent()
|
||||||
|
|
||||||
if spendParameterPresent && outputParameterPresent {
|
if spendParameterPresent && outputParameterPresent {
|
||||||
result(.success(true))
|
return true
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let outputURL = self.outputParamsURL
|
let outputURL = self.outputParamsURL
|
||||||
let spendURL = self.spendParamsURL
|
let spendURL = self.spendParamsURL
|
||||||
|
|
||||||
if !outputParameterPresent {
|
do {
|
||||||
SaplingParameterDownloader.downloadOutputParameter(outputURL) { outputResult in
|
if !outputParameterPresent && !spendParameterPresent {
|
||||||
switch outputResult {
|
async let outputURLRequest = SaplingParameterDownloader.downloadOutputParameter(outputURL)
|
||||||
case .failure(let error):
|
async let spendURLRequest = SaplingParameterDownloader.downloadSpendParameter(spendURL)
|
||||||
result(.failure(error))
|
_ = try await [outputURLRequest, spendURLRequest]
|
||||||
case .success:
|
return false
|
||||||
guard !spendParameterPresent else {
|
} else if !outputParameterPresent {
|
||||||
result(.success(false))
|
try await SaplingParameterDownloader.downloadOutputParameter(outputURL)
|
||||||
return
|
return false
|
||||||
}
|
} else if !spendParameterPresent {
|
||||||
SaplingParameterDownloader.downloadSpendParameter(spendURL) { spendResult in
|
try await SaplingParameterDownloader.downloadSpendParameter(spendURL)
|
||||||
switch spendResult {
|
return false
|
||||||
case .failure(let error):
|
|
||||||
result(.failure(error))
|
|
||||||
case .success:
|
|
||||||
result(.success(false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if !spendParameterPresent {
|
|
||||||
SaplingParameterDownloader.downloadSpendParameter(spendURL) { spendResult in
|
|
||||||
switch spendResult {
|
|
||||||
case .failure(let error):
|
|
||||||
result(.failure(error))
|
|
||||||
case .success:
|
|
||||||
result(.success(false))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"network": "main",
|
||||||
|
"height": "1807500",
|
||||||
|
"hash": "0000000000b92234ffe6efd360a2fb1528e2f5cf891a54e58a9b83cd6c513ad9",
|
||||||
|
"time": 1663095814,
|
||||||
|
"saplingTree": "0146b9cd1dc80b44c19108d53ecf72b5bacd405e8ed5c8038c413ad2d7bf9f486201fb61737c80f0c3816ddc487ff9ba360c60b1663151cbd07c08dd8c09c648760d190177aa27c08a1ff4754710984f2f94c5a101a10f57eb6ad49f7b1df47f7e8a5730014c0fb4e36c1b7968082d0355f318a67e265e5a5533ffcc01b939e4bf1779841b000000019315fe44f132446949cd9ea1f34ec20f32c0dd0ba4f732e563c040c692fd5d49010e6b6ae8956bd722b0f5e3f355adfc7c9b6a39542c07fb0bcd317d0eff8d99080001382db97fabb726a88e7a7baab050a1c9169d2142a9ce0b7f3022349acf74981a0175d44cdd04e213c2d95fb281f96fc7d734c65be70a77b7aa4fef8a4b6fb2475b015fe654f11132fc9c133b46dbbf19b0113cc45715f52dfd3282ede76f6880740c01d14f83f0fd7d09f4f52c8ae9d39c63b57c59a37666d211b9a2deb290808c02720001701df279d9a2270a82379486df546fafeaeb831993cde1cd9e5c9cb17be5191f01cc6ae86e9147b0b1c3f5fb32fb7acc012b4cb4384a1a1331ae3c2324a804483e00017fb2e2890b05355ba797af2f77e38cab3e8ec1623d29a912bdd0dc4a78cf554a0001de17a599d0c6d73eaf1a5939e95af4427f75b89f703749e00d853cce3d6af84f00014f6313837ed19d2b480ec531529fb6b425006f2c1d981077640be21627659410018c2d6adea2ad4faf20eccfc2c2a2c59192fb53d3204b3a2757f1c247dadec16b0001c5d9822e7aa76d3a758e8dc25ffd8b8c9d4051c66fb92fd7aa263905e238920e0139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
|
||||||
|
"orchardTree": "013de3a4ad28b5df77a979fc925643e69b7dd26ba787a3122fcd6a445c47d9280e01b68287983d323c90e6a1ed5980ab5b1a846d49340ee6d40d2349795c132a382c1f0001c2db91c5b9baa1c09623429cb4005ae12e521a018eb8df2d051d6793a307eb3e01c6b2f8d93635310d470d4a6d011ea77f59e28bee6ffca3df88f5f2a98980331a01777860ffe739b8047045e2dff8ba77070666075214a0f7702568205410351b39019ed7c5c3e958cb8b9d5324c290ff384dc3ca6cbc870950002f64398478ff1904000114b5ad56c8f210854a1688f47116b5d272fea09559646cee33ad3e6958306a15000001110e689714d772b170f63bfbf4b144c6a48e0a57c4a2780513633d5c799a0d1300010cf94eaf4d5268d9e0878064a458eaa3363dfcfcf2602681c443baf989d5de20000000019776dec2ea06cc5ecd2d212d37023972f526cb2ffa7ce1e8cf8eb4ef04700b01000001c7146e487b3ae97b190ebf93eac554968e683d31115d13fe83dd620859d9a92d000001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000"
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"network": "main",
|
||||||
|
"height": "1810000",
|
||||||
|
"hash": "0000000000bdf01be068ff1f0b21b8b266f839b31ed066b72888b67f672c9800",
|
||||||
|
"time": 1663283618,
|
||||||
|
"saplingTree": "019dbc466ad114f2b4a6d0d91198c0a2c5c20c1dec7c2e7f932c9f9197d76b80020019015b272118101cac0ee6b9b8cf26d5104ab42913d2f5253388bac28e7998f2c41b0001868d5008c587f0fa3d26fc42097d34df49a70508a85a6acccf1263d39cb62c28000001b2325a6ecbf023f3ce4b74c1c14bac8d9c462560dce9611bd7b08cd55ecf4b0100000001cd62e4e2142c664656f956fd0ea8d1de1027c327580398fe8912e6264801650201ccc9b305f6e65d641a4e461ea5e853354a3ac4b2acf2591849e00421bd58fb48000000013a591632f71e1fe214ab46b464112520e98f15d171da599a350f7d8dba79595d018d254447626cf40828102a60e2b433d05498a780599cdf56a14f3888c2f42008000148af2e64d92d1944a451180f1738c9f468f608525b0273967db19029b53ba16d0000000001f416eb7e062c981dbbf76f8845fda959b948bc742fc62d9edb2f36bae852ba4e01c5d9822e7aa76d3a758e8dc25ffd8b8c9d4051c66fb92fd7aa263905e238920e0139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
|
||||||
|
"orchardTree": "0180c286284cd52360af960fce56fe7dc339667c35580c75e9bc339483230e932701260b8984058125b5e3558fb65172d59718427111cbc06891017cc7633f74e1001f00000000000001cde58ba8e982a34499406e06a763ede11353d39ab93a5af1b34905cf268c033701226087d6a9bb28c2ed6890b989224791093cd5012a27285040025c0a72b2ce06000001ba71f8a1897b754e9fe37a29eb2c1a93ddc1678298498b4a84da732ebf056f15018073f4aff677a24eaac68c20d271ea228041f1e77710b0504d3f6c0b71d63d24000146b37a3e6167ae7f07725ab4e32247619c37e2a91c87182dd68b8feb99d5a22201e18dad85447ef2e9b8d647c9b9f1e6cef3e1d03f908975cd5e1d5c5808e443010001898b4a8f384f342a67efb3f6c4afd87310df4ff1532b86ca8d1394975aab5a1e0001c7146e487b3ae97b190ebf93eac554968e683d31115d13fe83dd620859d9a92d000001020d51b05be2e276efa73f2977f5a82defed553b1086897f0bea9b03f4dbd41a000000000000000000"
|
||||||
|
}
|
|
@ -104,22 +104,20 @@ public protocol Synchronizer {
|
||||||
/// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used.
|
/// - 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
|
/// - Returns the address or nil if account index is incorrect
|
||||||
func getTransparentAddress(accountIndex: Int) -> TransparentAddress?
|
func getTransparentAddress(accountIndex: Int) -> TransparentAddress?
|
||||||
|
|
||||||
/// Sends zatoshi.
|
/// Sends zatoshi.
|
||||||
/// - Parameter spendingKey: the key that allows spends to occur.
|
/// - Parameter spendingKey: the key that allows spends to occur.
|
||||||
/// - Parameter zatoshi: the amount to send in Zatoshi.
|
/// - Parameter zatoshi: the amount to send in Zatoshi.
|
||||||
/// - Parameter toAddress: the recipient's address.
|
/// - Parameter toAddress: the recipient's address.
|
||||||
/// - Parameter memo: the memo to include as part of the transaction.
|
/// - Parameter memo: the memo to include as part of the transaction.
|
||||||
/// - Parameter accountIndex: the optional account id to use. By default, the first account is used.
|
/// - Parameter accountIndex: the optional account id to use. By default, the first account is used.
|
||||||
// swiftlint:disable:next function_parameter_count
|
|
||||||
func sendToAddress(
|
func sendToAddress(
|
||||||
spendingKey: SaplingExtendedSpendingKey,
|
spendingKey: SaplingExtendedSpendingKey,
|
||||||
zatoshi: Zatoshi,
|
zatoshi: Zatoshi,
|
||||||
toAddress: Recipient,
|
toAddress: Recipient,
|
||||||
memo: Memo,
|
memo: Memo,
|
||||||
from accountIndex: Int,
|
from accountIndex: Int
|
||||||
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void
|
) async throws -> PendingTransactionEntity
|
||||||
)
|
|
||||||
|
|
||||||
/// Shields transparent funds from the given private key into the best shielded pool of the given account.
|
/// Shields transparent funds from the given private key into the best shielded pool of the given account.
|
||||||
/// - Parameter transparentAccountPrivateKey: the key that allows to spend transparent funds
|
/// - Parameter transparentAccountPrivateKey: the key that allows to spend transparent funds
|
||||||
|
@ -128,9 +126,8 @@ public protocol Synchronizer {
|
||||||
func shieldFunds(
|
func shieldFunds(
|
||||||
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
||||||
memo: Memo,
|
memo: Memo,
|
||||||
from accountIndex: Int,
|
from accountIndex: Int
|
||||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
) async throws -> PendingTransactionEntity
|
||||||
)
|
|
||||||
|
|
||||||
/// Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only
|
/// 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.
|
/// an option if the transaction has not yet been submitted to the server.
|
||||||
|
|
|
@ -77,7 +77,6 @@ public extension Notification.Name {
|
||||||
/// Synchronizer implementation for UIKit and iOS 13+
|
/// Synchronizer implementation for UIKit and iOS 13+
|
||||||
// swiftlint:disable type_body_length
|
// swiftlint:disable type_body_length
|
||||||
public class SDKSynchronizer: Synchronizer {
|
public class SDKSynchronizer: Synchronizer {
|
||||||
|
|
||||||
public struct SynchronizerState {
|
public struct SynchronizerState {
|
||||||
public var shieldedBalance: WalletBalance
|
public var shieldedBalance: WalletBalance
|
||||||
public var transparentBalance: WalletBalance
|
public var transparentBalance: WalletBalance
|
||||||
|
@ -457,41 +456,34 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Synchronizer methods
|
// MARK: Synchronizer methods
|
||||||
|
|
||||||
// swiftlint:disable:next function_parameter_count
|
|
||||||
public func sendToAddress(
|
public func sendToAddress(
|
||||||
spendingKey: SaplingExtendedSpendingKey,
|
spendingKey: SaplingExtendedSpendingKey,
|
||||||
zatoshi: Zatoshi,
|
zatoshi: Zatoshi,
|
||||||
toAddress: Recipient,
|
toAddress: Recipient,
|
||||||
memo: Memo,
|
memo: Memo,
|
||||||
from accountIndex: Int,
|
from accountIndex: Int
|
||||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
) async throws -> PendingTransactionEntity {
|
||||||
) {
|
do {
|
||||||
initializer.downloadParametersIfNeeded { downloadResult in
|
try await initializer.downloadParametersIfNeeded()
|
||||||
DispatchQueue.main.async { [weak self] in
|
} catch {
|
||||||
switch downloadResult {
|
throw SynchronizerError.parameterMissing(underlyingError: error)
|
||||||
case .success:
|
|
||||||
self?.createToAddress(
|
|
||||||
spendingKey: spendingKey,
|
|
||||||
zatoshi: zatoshi,
|
|
||||||
toAddress: toAddress.stringEncoded,
|
|
||||||
memo: memo,
|
|
||||||
from: accountIndex,
|
|
||||||
resultBlock: resultBlock
|
|
||||||
)
|
|
||||||
case .failure(let error):
|
|
||||||
resultBlock(.failure(SynchronizerError.parameterMissing(underlyingError: error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
return try await createToAddress(
|
||||||
|
spendingKey: spendingKey,
|
||||||
|
zatoshi: zatoshi,
|
||||||
|
toAddress: toAddress.stringEncoded,
|
||||||
|
memo: memo,
|
||||||
|
from: accountIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public func shieldFunds(
|
public func shieldFunds(
|
||||||
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
||||||
memo: Memo,
|
memo: Memo,
|
||||||
from accountIndex: Int,
|
from accountIndex: Int
|
||||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
) async throws -> PendingTransactionEntity {
|
||||||
) {
|
|
||||||
// let's see if there are funds to shield
|
// let's see if there are funds to shield
|
||||||
let derivationTool = DerivationTool(networkType: self.network.networkType)
|
let derivationTool = DerivationTool(networkType: self.network.networkType)
|
||||||
|
|
||||||
|
@ -502,47 +494,36 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
|
|
||||||
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
|
// 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 {
|
guard tBalance.verified >= self.network.constants.defaultFee(for: self.latestScannedHeight) else {
|
||||||
resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds))
|
throw ShieldFundsError.insuficientTransparentFunds
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Define who's the recipient of a shielding transaction #521
|
// FIXME: Define who's the recipient of a shielding transaction #521
|
||||||
// https://github.com/zcash/ZcashLightClientKit/issues/521
|
// https://github.com/zcash/ZcashLightClientKit/issues/521
|
||||||
guard let uAddr = self.getUnifiedAddress(accountIndex: accountIndex) else {
|
guard let uAddr = self.getUnifiedAddress(accountIndex: accountIndex) else {
|
||||||
resultBlock(.failure(ShieldFundsError.shieldingFailed(underlyingError: KeyEncodingError.invalidEncoding)))
|
throw ShieldFundsError.shieldingFailed(underlyingError: KeyEncodingError.invalidEncoding)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr.stringEncoded, memo: try memo.asMemoBytes(), from: accountIndex)
|
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr.stringEncoded, memo: try memo.asMemoBytes(), from: accountIndex)
|
||||||
|
|
||||||
// TODO: Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
|
// TODO: Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
|
||||||
Task {
|
let transaction = try await transactionManager.encodeShieldingTransaction(
|
||||||
do {
|
xprv: transparentAccountPrivateKey,
|
||||||
let transaction = try await transactionManager.encodeShieldingTransaction(
|
pendingTransaction: shieldingSpend
|
||||||
xprv: transparentAccountPrivateKey,
|
)
|
||||||
pendingTransaction: shieldingSpend
|
|
||||||
)
|
|
||||||
|
|
||||||
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
|
return try await transactionManager.submit(pendingTransaction: transaction)
|
||||||
resultBlock(.success(submittedTx))
|
|
||||||
} catch {
|
|
||||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// swiftlint:disable:next function_parameter_count
|
|
||||||
func createToAddress(
|
func createToAddress(
|
||||||
spendingKey: SaplingExtendedSpendingKey,
|
spendingKey: SaplingExtendedSpendingKey,
|
||||||
zatoshi: Zatoshi,
|
zatoshi: Zatoshi,
|
||||||
toAddress: String,
|
toAddress: String,
|
||||||
memo: Memo,
|
memo: Memo,
|
||||||
from accountIndex: Int,
|
from accountIndex: Int
|
||||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
) async throws -> PendingTransactionEntity {
|
||||||
) {
|
|
||||||
do {
|
do {
|
||||||
let spend = try transactionManager.initSpend(
|
let spend = try transactionManager.initSpend(
|
||||||
zatoshi: zatoshi,
|
zatoshi: zatoshi,
|
||||||
|
@ -551,21 +532,14 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
from: accountIndex
|
from: accountIndex
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
|
let transaction = try await transactionManager.encode(
|
||||||
Task {
|
spendingKey: spendingKey,
|
||||||
do {
|
pendingTransaction: spend
|
||||||
let transaction = try await transactionManager.encode(
|
)
|
||||||
spendingKey: spendingKey,
|
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
|
||||||
pendingTransaction: spend
|
return submittedTx
|
||||||
)
|
|
||||||
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
|
|
||||||
resultBlock(.success(submittedTx))
|
|
||||||
} catch {
|
|
||||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
resultBlock(.failure(SynchronizerError.uncategorized(underlyingError: error)))
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,33 +15,46 @@ public enum SaplingParameterDownloader {
|
||||||
case failed(error: Error)
|
case failed(error: Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Download a Spend parameter from default host and stores it at given URL
|
||||||
Download a Spend parameter from default host and stores it at given URL
|
/// - Parameters:
|
||||||
- Parameters:
|
/// - at: The destination URL for the download
|
||||||
- at: The destination URL for the download
|
@discardableResult
|
||||||
- result: block to handle the download success or error
|
public static func downloadSpendParameter(_ at: URL) async throws -> URL {
|
||||||
*/
|
|
||||||
public static func downloadSpendParameter(_ at: URL, result: @escaping (Result<URL, Error>) -> Void) {
|
|
||||||
guard let url = URL(string: spendParamsURLString) else {
|
guard let url = URL(string: spendParamsURLString) else {
|
||||||
result(.failure(Errors.invalidURL(url: spendParamsURLString)))
|
throw Errors.invalidURL(url: spendParamsURLString)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFileWithRequest(URLRequest(url: url), at: at, result: result)
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
downloadFileWithRequest(URLRequest(url: url), at: at) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let outputResultURL):
|
||||||
|
continuation.resume(returning: outputResultURL)
|
||||||
|
case .failure(let outputResultError):
|
||||||
|
continuation.resume(throwing: outputResultError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
Download an Output parameter from default host and stores it at given URL
|
/// Download an Output parameter from default host and stores it at given URL
|
||||||
- Parameters:
|
/// - Parameters:
|
||||||
- at: The destination URL for the download
|
/// - at: The destination URL for the download
|
||||||
- result: block to handle the download success or error
|
@discardableResult
|
||||||
*/
|
public static func downloadOutputParameter(_ at: URL) async throws -> URL {
|
||||||
public static func downloadOutputParameter(_ at: URL, result: @escaping (Result<URL, Error>) -> Void) {
|
|
||||||
guard let url = URL(string: outputParamsURLString) else {
|
guard let url = URL(string: outputParamsURLString) else {
|
||||||
result(.failure(Errors.invalidURL(url: outputParamsURLString)))
|
throw Errors.invalidURL(url: outputParamsURLString)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFileWithRequest(URLRequest(url: url), at: at, result: result)
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
downloadFileWithRequest(URLRequest(url: url), at: at) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let outputResultURL):
|
||||||
|
continuation.resume(returning: outputResultURL)
|
||||||
|
case .failure(let outputResultError):
|
||||||
|
continuation.resume(throwing: outputResultError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func downloadFileWithRequest(_ request: URLRequest, at destination: URL, result: @escaping (Result<URL, Error>) -> Void) {
|
private static func downloadFileWithRequest(_ request: URLRequest, at destination: URL, result: @escaping (Result<URL, Error>) -> Void) {
|
||||||
|
@ -61,52 +74,39 @@ public enum SaplingParameterDownloader {
|
||||||
|
|
||||||
task.resume()
|
task.resume()
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
Downloads the parameters if not present and provides the resulting URLs for both parameters
|
/// Downloads the parameters if not present and provides the resulting URLs for both parameters
|
||||||
- Parameters:
|
/// - Parameters:
|
||||||
- spendURL: URL to check whether the parameter is already downloaded
|
/// - spendURL: URL to check whether the parameter is already downloaded
|
||||||
- outputURL: 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(
|
public static func downloadParamsIfnotPresent(
|
||||||
spendURL: URL,
|
spendURL: URL,
|
||||||
outputURL: URL,
|
outputURL: URL
|
||||||
result: @escaping (Result<(spend: URL, output: URL), Error>) -> Void
|
) async throws -> (spend: URL, output: URL) {
|
||||||
) {
|
do {
|
||||||
ensureSpendParameter(at: spendURL) { spendResult in
|
async let spendResultURL = ensureSpendParameter(at: spendURL)
|
||||||
switch spendResult {
|
async let outputResultURL = ensureOutputParameter(at: outputURL)
|
||||||
case .success(let spendResultURL):
|
|
||||||
ensureOutputParameter(at: outputURL) { outputResult in
|
let results = try await [spendResultURL, outputResultURL]
|
||||||
switch outputResult {
|
return (spend: results[0], output: results[1])
|
||||||
case .success(let outputResultURL):
|
} catch {
|
||||||
result(.success((spendResultURL, outputResultURL)))
|
throw Errors.failed(error: error)
|
||||||
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<URL, Error>) -> Void) {
|
static func ensureSpendParameter(at url: URL) async throws -> URL {
|
||||||
if isFilePresent(url: url) {
|
if isFilePresent(url: url) {
|
||||||
DispatchQueue.global().async {
|
return url
|
||||||
result(.success(url))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
downloadSpendParameter(url, result: result)
|
return try await downloadSpendParameter(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ensureOutputParameter(at url: URL, result: @escaping (Result<URL, Error>) -> Void) {
|
static func ensureOutputParameter(at url: URL) async throws -> URL {
|
||||||
if isFilePresent(url: url) {
|
if isFilePresent(url: url) {
|
||||||
DispatchQueue.global().async {
|
return url
|
||||||
result(.success(url))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
downloadOutputParameter(url, result: result)
|
return try await downloadOutputParameter(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import XCTest
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
// swiftlint:disable implicitly_unwrapped_optional force_unwrapping type_body_length
|
// swiftlint:disable implicitly_unwrapped_optional force_unwrapping type_body_length
|
||||||
|
//@MainActor
|
||||||
class AdvancedReOrgTests: XCTestCase {
|
class AdvancedReOrgTests: XCTestCase {
|
||||||
// TODO: Parameterize this from environment?
|
// TODO: Parameterize this from environment?
|
||||||
// swiftlint:disable:next line_length
|
// swiftlint:disable:next line_length
|
||||||
|
@ -266,7 +267,7 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/// 11. verify that the sent tx is mined and balance is correct
|
/// 11. verify that the sent tx is mined and balance is correct
|
||||||
/// 12. applyStaged(sentTx + 10)
|
/// 12. applyStaged(sentTx + 10)
|
||||||
/// 13. verify that there's no more pending transaction
|
/// 13. verify that there's no more pending transaction
|
||||||
func testReorgChangesOutboundTxIndex() throws {
|
func testReorgChangesOutboundTxIndex() async throws {
|
||||||
try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName)
|
try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName)
|
||||||
let receivedTxHeight: BlockHeight = 663188
|
let receivedTxHeight: BlockHeight = 663188
|
||||||
var initialTotalBalance = Zatoshi(-1)
|
var initialTotalBalance = Zatoshi(-1)
|
||||||
|
@ -278,44 +279,51 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
let preTxExpectation = XCTestExpectation(description: "pre receive")
|
let preTxExpectation = XCTestExpectation(description: "pre receive")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
3. sync up to received_Tx_height
|
3. sync up to received_Tx_height
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(completion: { synchronizer in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
initialTotalBalance = synchronizer.initializer.getBalance()
|
do {
|
||||||
preTxExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
}, error: self.handleError)
|
initialTotalBalance = synchronizer.initializer.getBalance()
|
||||||
|
continuation.resume()
|
||||||
|
preTxExpectation.fulfill()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [preTxExpectation], timeout: 5)
|
wait(for: [preTxExpectation], timeout: 5)
|
||||||
|
|
||||||
let sendExpectation = XCTestExpectation(description: "sendToAddress")
|
let sendExpectation = XCTestExpectation(description: "sendToAddress")
|
||||||
var pendingEntity: PendingTransactionEntity?
|
var pendingEntity: PendingTransactionEntity?
|
||||||
var error: Error?
|
var testError: Error?
|
||||||
let sendAmount = Zatoshi(10000)
|
let sendAmount = Zatoshi(10000)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
4. create transaction
|
4. create transaction
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: sendAmount,
|
spendingKey: coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: sendAmount,
|
||||||
memo: try Memo(string: "test transaction"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0
|
memo: try Memo(string: "test transaction"),
|
||||||
) { result in
|
from: 0
|
||||||
switch result {
|
)
|
||||||
case .success(let pending):
|
pendingEntity = pendingTx
|
||||||
pendingEntity = pending
|
|
||||||
case .failure(let e):
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
sendExpectation.fulfill()
|
||||||
}
|
} catch {
|
||||||
wait(for: [sendExpectation], timeout: 12)
|
testError = error
|
||||||
|
|
||||||
guard let pendingTx = pendingEntity else {
|
|
||||||
XCTFail("error sending to address. Error: \(String(describing: error))")
|
XCTFail("error sending to address. Error: \(String(describing: error))")
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(for: [sendExpectation], timeout: 2)
|
||||||
|
|
||||||
|
guard let pendingTx = pendingEntity else {
|
||||||
|
XCTFail("error sending to address. Error: \(String(describing: testError))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,15 +355,18 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
*/
|
*/
|
||||||
let sentTxSyncExpectation = XCTestExpectation(description: "sent tx sync expectation")
|
let sentTxSyncExpectation = XCTestExpectation(description: "sent tx sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchronizer in
|
do {
|
||||||
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
|
try coordinator.sync(completion: { synchronizer in
|
||||||
XCTAssertEqual(pMinedHeight, sentTxHeight)
|
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
|
||||||
|
XCTAssertEqual(pMinedHeight, sentTxHeight)
|
||||||
sentTxSyncExpectation.fulfill()
|
continuation.resume()
|
||||||
},
|
sentTxSyncExpectation.fulfill()
|
||||||
error: self.handleError
|
}, error: self.handleError)
|
||||||
)
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [sentTxSyncExpectation], timeout: 5)
|
wait(for: [sentTxSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -373,18 +384,22 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
let afterReOrgExpectation = XCTestExpectation(description: "after ReOrg Expectation")
|
let afterReOrgExpectation = XCTestExpectation(description: "after ReOrg Expectation")
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchronizer in
|
do {
|
||||||
/*
|
try coordinator.sync(completion: { synchronizer in
|
||||||
11. verify that the sent tx is mined and balance is correct
|
/*
|
||||||
*/
|
11. verify that the sent tx is mined and balance is correct
|
||||||
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
|
*/
|
||||||
XCTAssertEqual(pMinedHeight, sentTxHeight)
|
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
|
||||||
XCTAssertEqual(initialTotalBalance - sendAmount - Zatoshi(1000), synchronizer.initializer.getBalance()) // fee change on this branch
|
XCTAssertEqual(pMinedHeight, sentTxHeight)
|
||||||
afterReOrgExpectation.fulfill()
|
XCTAssertEqual(initialTotalBalance - sendAmount - Zatoshi(1000), synchronizer.initializer.getBalance()) // fee change on this branch
|
||||||
},
|
continuation.resume()
|
||||||
error: self.handleError
|
afterReOrgExpectation.fulfill()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [afterReOrgExpectation], timeout: 5)
|
wait(for: [afterReOrgExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -398,10 +413,16 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
13. verify that there's no more pending transaction
|
13. verify that there's no more pending transaction
|
||||||
*/
|
*/
|
||||||
let lastSyncExpectation = XCTestExpectation(description: "sync to confirmation")
|
let lastSyncExpectation = XCTestExpectation(description: "sync to confirmation")
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
try coordinator.sync(completion: { _ in
|
do {
|
||||||
lastSyncExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
}, error: self.handleError)
|
lastSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [lastSyncExpectation], timeout: 5)
|
wait(for: [lastSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -670,7 +691,7 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/// 13. apply height(sentTxHeight + 15)
|
/// 13. apply height(sentTxHeight + 15)
|
||||||
/// 14. sync to latest height
|
/// 14. sync to latest height
|
||||||
/// 15. verify that there's no pending transaction and that the tx is displayed on the sentTransactions collection
|
/// 15. verify that there's no pending transaction and that the tx is displayed on the sentTransactions collection
|
||||||
func testReOrgChangesOutboundTxMinedHeight() throws {
|
func testReOrgChangesOutboundTxMinedHeight() async throws {
|
||||||
hookToReOrgNotification()
|
hookToReOrgNotification()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -685,9 +706,16 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
1a. sync to latest height
|
1a. sync to latest height
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 5)
|
wait(for: [firstSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -700,22 +728,18 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
2. send transaction to recipient address
|
2. send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: Zatoshi(20000),
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: Zatoshi(20000),
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -755,10 +779,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
*/
|
*/
|
||||||
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
|
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
secondSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
secondSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [secondSyncExpectation], timeout: 5)
|
wait(for: [secondSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
XCTAssertEqual(coordinator.synchronizer.pendingTransactions.count, 1)
|
XCTAssertEqual(coordinator.synchronizer.pendingTransactions.count, 1)
|
||||||
|
@ -793,10 +824,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
self.expectedReorgHeight = sentTxHeight + 1
|
self.expectedReorgHeight = sentTxHeight + 1
|
||||||
let afterReorgExpectation = XCTestExpectation(description: "after reorg sync")
|
let afterReorgExpectation = XCTestExpectation(description: "after reorg sync")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
afterReorgExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
afterReorgExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [reorgExpectation, afterReorgExpectation], timeout: 5)
|
wait(for: [reorgExpectation, afterReorgExpectation], timeout: 5)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -821,10 +859,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
11a. sync to latest height
|
11a. sync to latest height
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
yetAnotherExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
yetAnotherExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [yetAnotherExpectation], timeout: 5)
|
wait(for: [yetAnotherExpectation], timeout: 5)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -851,10 +896,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
14. sync to latest height
|
14. sync to latest height
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
thisIsTheLastExpectationIPromess.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
thisIsTheLastExpectationIPromess.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [thisIsTheLastExpectationIPromess], timeout: 5)
|
wait(for: [thisIsTheLastExpectationIPromess], timeout: 5)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1023,7 +1075,7 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/// 7. stage 15 blocks from sentTxHeigth to cause a reorg
|
/// 7. stage 15 blocks from sentTxHeigth to cause a reorg
|
||||||
/// 8. sync to latest height
|
/// 8. sync to latest height
|
||||||
/// 9. verify that there's an expired transaction as a pending transaction
|
/// 9. verify that there's an expired transaction as a pending transaction
|
||||||
func testReOrgRemovesOutboundTxAndIsNeverMined() throws {
|
func testReOrgRemovesOutboundTxAndIsNeverMined() async throws {
|
||||||
hookToReOrgNotification()
|
hookToReOrgNotification()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1040,10 +1092,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
1a. sync to latest height
|
1a. sync to latest height
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 5)
|
wait(for: [firstSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
@ -1055,22 +1114,18 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
2. send transaction to recipient address
|
2. send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: Zatoshi(20000),
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: Zatoshi(20000),
|
||||||
memo: try! Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try! Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -1110,10 +1165,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
*/
|
*/
|
||||||
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
|
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
secondSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
secondSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [secondSyncExpectation], timeout: 5)
|
wait(for: [secondSyncExpectation], timeout: 5)
|
||||||
let extraBlocks = 25
|
let extraBlocks = 25
|
||||||
try coordinator.stageBlockCreate(height: sentTxHeight, count: extraBlocks, nonce: 5)
|
try coordinator.stageBlockCreate(height: sentTxHeight, count: extraBlocks, nonce: 5)
|
||||||
|
@ -1123,10 +1185,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
sleep(2)
|
sleep(2)
|
||||||
let reorgSyncExpectation = XCTestExpectation(description: "reorg sync expectation")
|
let reorgSyncExpectation = XCTestExpectation(description: "reorg sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
reorgSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
reorgSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [reorgExpectation, reorgSyncExpectation], timeout: 5)
|
wait(for: [reorgExpectation, reorgSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
guard let pendingTx = coordinator.synchronizer.pendingTransactions.first else {
|
guard let pendingTx = coordinator.synchronizer.pendingTransactions.first else {
|
||||||
|
@ -1143,10 +1212,17 @@ class AdvancedReOrgTests: XCTestCase {
|
||||||
|
|
||||||
let lastSyncExpectation = XCTestExpectation(description: "last sync expectation")
|
let lastSyncExpectation = XCTestExpectation(description: "last sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
lastSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
lastSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [lastSyncExpectation], timeout: 5)
|
wait(for: [lastSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), initialTotalBalance)
|
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), initialTotalBalance)
|
||||||
|
|
|
@ -42,7 +42,7 @@ class BalanceTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
verify that when sending the maximum amount, the transactions are broadcasted properly
|
verify that when sending the maximum amount, the transactions are broadcasted properly
|
||||||
*/
|
*/
|
||||||
func testMaxAmountSend() throws {
|
func testMaxAmountSend() async throws {
|
||||||
let notificationHandler = SDKSynchonizerListener()
|
let notificationHandler = SDKSynchonizerListener()
|
||||||
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
||||||
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
||||||
|
@ -57,9 +57,16 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(1)
|
sleep(1)
|
||||||
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 12)
|
wait(for: [firstSyncExpectation], timeout: 12)
|
||||||
// 2 check that there are no unconfirmed funds
|
// 2 check that there are no unconfirmed funds
|
||||||
|
@ -79,22 +86,18 @@ class BalanceTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: maxBalance,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: maxBalance,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = transaction
|
||||||
case .failure(let error):
|
self.sentTransactionExpectation.fulfill()
|
||||||
XCTFail("sendToAddress failed: \(error)")
|
} catch {
|
||||||
case .success(let transaction):
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
pendingTx = transaction
|
}
|
||||||
}
|
|
||||||
self.sentTransactionExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sentTransactionExpectation], timeout: 20)
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
||||||
guard let pendingTx = pendingTx else {
|
guard let pendingTx = pendingTx else {
|
||||||
|
@ -133,23 +136,29 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(2) // add enhance breakpoint here
|
sleep(2) // add enhance breakpoint here
|
||||||
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
||||||
|
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchronizer in
|
do {
|
||||||
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
try coordinator.sync(
|
||||||
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
completion: { synchronizer in
|
||||||
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
||||||
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
||||||
mineExpectation.fulfill()
|
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
||||||
},
|
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
||||||
error: { error in
|
mineExpectation.fulfill()
|
||||||
guard let error = error else {
|
continuation.resume()
|
||||||
XCTFail("unknown error syncing after sending transaction")
|
}, error: { error in
|
||||||
return
|
guard let error = error else {
|
||||||
}
|
XCTFail("unknown error syncing after sending transaction")
|
||||||
|
return
|
||||||
XCTFail("Error: \(error)")
|
}
|
||||||
|
|
||||||
|
XCTFail("Error: \(error)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -166,12 +175,17 @@ class BalanceTests: XCTestCase {
|
||||||
notificationHandler.synchronizerMinedTransaction = { transaction in
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
||||||
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
||||||
}
|
}
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
confirmExpectation.fulfill()
|
do {
|
||||||
}, error: { e in
|
try coordinator.sync(completion: { synchronizer in
|
||||||
self.handleError(e)
|
confirmExpectation.fulfill()
|
||||||
})
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [confirmExpectation], timeout: 5)
|
wait(for: [confirmExpectation], timeout: 5)
|
||||||
|
|
||||||
let confirmedPending = try coordinator.synchronizer.allPendingTransactions()
|
let confirmedPending = try coordinator.synchronizer.allPendingTransactions()
|
||||||
|
@ -186,7 +200,7 @@ class BalanceTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
verify that when sending the maximum amount minus one zatoshi, the transactions are broadcasted properly
|
verify that when sending the maximum amount minus one zatoshi, the transactions are broadcasted properly
|
||||||
*/
|
*/
|
||||||
func testMaxAmountMinusOneSend() throws {
|
func testMaxAmountMinusOneSend() async throws {
|
||||||
let notificationHandler = SDKSynchonizerListener()
|
let notificationHandler = SDKSynchonizerListener()
|
||||||
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
||||||
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
||||||
|
@ -201,9 +215,16 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(1)
|
sleep(1)
|
||||||
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 12)
|
wait(for: [firstSyncExpectation], timeout: 12)
|
||||||
// 2 check that there are no unconfirmed funds
|
// 2 check that there are no unconfirmed funds
|
||||||
|
@ -221,24 +242,20 @@ class BalanceTests: XCTestCase {
|
||||||
XCTFail("failed to create spending keys")
|
XCTFail("failed to create spending keys")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: maxBalanceMinusOne,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: maxBalanceMinusOne,
|
||||||
memo: try Memo(string: "\(self.description) \(Date().description)"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "\(self.description) \(Date().description)"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = transaction
|
||||||
case .failure(let error):
|
self.sentTransactionExpectation.fulfill()
|
||||||
XCTFail("sendToAddress failed: \(error)")
|
} catch {
|
||||||
case .success(let transaction):
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
pendingTx = transaction
|
}
|
||||||
}
|
|
||||||
self.sentTransactionExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sentTransactionExpectation], timeout: 20)
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
||||||
guard let pendingTx = pendingTx else {
|
guard let pendingTx = pendingTx else {
|
||||||
|
@ -277,20 +294,29 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(2) // add enhance breakpoint here
|
sleep(2) // add enhance breakpoint here
|
||||||
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { synchronizer in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
do {
|
||||||
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
try coordinator.sync(
|
||||||
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
completion: { synchronizer in
|
||||||
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
||||||
mineExpectation.fulfill()
|
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
||||||
}, error: { error in
|
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
||||||
guard let e = error else {
|
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
||||||
XCTFail("unknown error syncing after sending transaction")
|
mineExpectation.fulfill()
|
||||||
return
|
continuation.resume()
|
||||||
|
}, error: { error in
|
||||||
|
guard let error = error else {
|
||||||
|
XCTFail("unknown error syncing after sending transaction")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTFail("Error: \(error)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
XCTFail("Error: \(e)")
|
|
||||||
})
|
|
||||||
|
|
||||||
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -307,11 +333,16 @@ class BalanceTests: XCTestCase {
|
||||||
notificationHandler.synchronizerMinedTransaction = { transaction in
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
||||||
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
||||||
}
|
}
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
confirmExpectation.fulfill()
|
do {
|
||||||
}, error: { e in
|
try coordinator.sync(completion: { synchronizer in
|
||||||
self.handleError(e)
|
confirmExpectation.fulfill()
|
||||||
})
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [confirmExpectation], timeout: 5)
|
wait(for: [confirmExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -328,7 +359,7 @@ class BalanceTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
verify that when sending the a no change transaction, the transactions are broadcasted properly
|
verify that when sending the a no change transaction, the transactions are broadcasted properly
|
||||||
*/
|
*/
|
||||||
func testSingleNoteNoChangeTransaction() throws {
|
func testSingleNoteNoChangeTransaction() async throws {
|
||||||
let notificationHandler = SDKSynchonizerListener()
|
let notificationHandler = SDKSynchonizerListener()
|
||||||
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
||||||
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
||||||
|
@ -343,10 +374,16 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(1)
|
sleep(1)
|
||||||
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [firstSyncExpectation], timeout: 12)
|
wait(for: [firstSyncExpectation], timeout: 12)
|
||||||
// 2 check that there are no unconfirmed funds
|
// 2 check that there are no unconfirmed funds
|
||||||
|
|
||||||
|
@ -364,22 +401,18 @@ class BalanceTests: XCTestCase {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: maxBalanceMinusOne,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: maxBalanceMinusOne,
|
||||||
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = transaction
|
||||||
case .failure(let error):
|
self.sentTransactionExpectation.fulfill()
|
||||||
XCTFail("sendToAddress failed: \(error)")
|
} catch {
|
||||||
case .success(let transaction):
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
pendingTx = transaction
|
}
|
||||||
}
|
|
||||||
self.sentTransactionExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sentTransactionExpectation], timeout: 20)
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
||||||
guard let pendingTx = pendingTx else {
|
guard let pendingTx = pendingTx else {
|
||||||
|
@ -418,20 +451,29 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(2) // add enhance breakpoint here
|
sleep(2) // add enhance breakpoint here
|
||||||
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { synchronizer in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
do {
|
||||||
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
try coordinator.sync(
|
||||||
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
completion: { synchronizer in
|
||||||
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
||||||
mineExpectation.fulfill()
|
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
||||||
}, error: { error in
|
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
||||||
guard let e = error else {
|
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
||||||
XCTFail("unknown error syncing after sending transaction")
|
mineExpectation.fulfill()
|
||||||
return
|
continuation.resume()
|
||||||
|
}, error: { error in
|
||||||
|
guard let error = error else {
|
||||||
|
XCTFail("unknown error syncing after sending transaction")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTFail("Error: \(error)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
XCTFail("Error: \(e)")
|
|
||||||
})
|
|
||||||
|
|
||||||
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -448,11 +490,16 @@ class BalanceTests: XCTestCase {
|
||||||
notificationHandler.synchronizerMinedTransaction = { transaction in
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
||||||
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
||||||
}
|
}
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
confirmExpectation.fulfill()
|
do {
|
||||||
}, error: { e in
|
try coordinator.sync(completion: { synchronizer in
|
||||||
self.handleError(e)
|
confirmExpectation.fulfill()
|
||||||
})
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [confirmExpectation], timeout: 5)
|
wait(for: [confirmExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -483,14 +530,21 @@ class BalanceTests: XCTestCase {
|
||||||
Error: previous available funds equals to current funds
|
Error: previous available funds equals to current funds
|
||||||
*/
|
*/
|
||||||
// swiftlint:disable cyclomatic_complexity
|
// swiftlint:disable cyclomatic_complexity
|
||||||
func testVerifyAvailableBalanceDuringSend() throws {
|
func testVerifyAvailableBalanceDuringSend() async throws {
|
||||||
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
||||||
|
|
||||||
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
self.syncedExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
self.syncedExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [syncedExpectation], timeout: 60)
|
wait(for: [syncedExpectation], timeout: 60)
|
||||||
|
|
||||||
|
@ -507,27 +561,23 @@ class BalanceTests: XCTestCase {
|
||||||
XCTAssertTrue(presendVerifiedBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
|
XCTAssertTrue(presendVerifiedBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
|
||||||
|
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: sendAmount,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: sendAmount,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = transaction
|
||||||
case .failure(let error):
|
self.sentTransactionExpectation.fulfill()
|
||||||
/*
|
} catch {
|
||||||
balance should be the same as before sending if transaction failed
|
/*
|
||||||
*/
|
balance should be the same as before sending if transaction failed
|
||||||
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), presendVerifiedBalance)
|
*/
|
||||||
XCTFail("sendToAddress failed: \(error)")
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), presendVerifiedBalance)
|
||||||
case .success(let transaction):
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
|
|
||||||
pendingTx = transaction
|
}
|
||||||
}
|
|
||||||
self.sentTransactionExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertTrue(coordinator.synchronizer.initializer.getVerifiedBalance() > .zero)
|
XCTAssertTrue(coordinator.synchronizer.initializer.getVerifiedBalance() > .zero)
|
||||||
wait(for: [sentTransactionExpectation], timeout: 12)
|
wait(for: [sentTransactionExpectation], timeout: 12)
|
||||||
|
@ -548,16 +598,25 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(1)
|
sleep(1)
|
||||||
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
mineExpectation.fulfill()
|
do {
|
||||||
}, error: { error in
|
try coordinator.sync(
|
||||||
guard let e = error else {
|
completion: { synchronizer in
|
||||||
XCTFail("unknown error syncing after sending transaction")
|
mineExpectation.fulfill()
|
||||||
return
|
continuation.resume()
|
||||||
|
}, error: { error in
|
||||||
|
guard let error else {
|
||||||
|
XCTFail("unknown error syncing after sending transaction")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTFail("Error: \(error)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
XCTFail("Error: \(e)")
|
|
||||||
})
|
|
||||||
|
|
||||||
wait(for: [mineExpectation], timeout: 5)
|
wait(for: [mineExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -654,15 +713,22 @@ class BalanceTests: XCTestCase {
|
||||||
Error: previous total balance funds equals to current total balance
|
Error: previous total balance funds equals to current total balance
|
||||||
|
|
||||||
*/
|
*/
|
||||||
func testVerifyTotalBalanceDuringSend() throws {
|
func testVerifyTotalBalanceDuringSend() async throws {
|
||||||
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
||||||
|
|
||||||
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
self.syncedExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
self.syncedExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [syncedExpectation], timeout: 5)
|
wait(for: [syncedExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -677,33 +743,28 @@ class BalanceTests: XCTestCase {
|
||||||
XCTAssertTrue(presendBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
|
XCTAssertTrue(presendBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
|
|
||||||
var error: Error?
|
var testError: Error?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: sendAmount,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: sendAmount,
|
||||||
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = transaction
|
||||||
case .failure(let e):
|
self.sentTransactionExpectation.fulfill()
|
||||||
// balance should be the same as before sending if transaction failed
|
} catch {
|
||||||
error = e
|
// balance should be the same as before sending if transaction failed
|
||||||
XCTFail("sendToAddress failed: \(e)")
|
testError = error
|
||||||
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
case .success(let transaction):
|
}
|
||||||
pendingTx = transaction
|
|
||||||
}
|
|
||||||
self.sentTransactionExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertTrue(coordinator.synchronizer.initializer.getVerifiedBalance() > .zero)
|
XCTAssertTrue(coordinator.synchronizer.initializer.getVerifiedBalance() > .zero)
|
||||||
wait(for: [sentTransactionExpectation], timeout: 12)
|
wait(for: [sentTransactionExpectation], timeout: 12)
|
||||||
|
|
||||||
if let e = error {
|
if let testError {
|
||||||
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), presendBalance)
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), presendBalance)
|
||||||
XCTFail("error: \(e)")
|
XCTFail("error: \(testError)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let transaction = pendingTx else {
|
guard let transaction = pendingTx else {
|
||||||
|
@ -733,16 +794,25 @@ class BalanceTests: XCTestCase {
|
||||||
sleep(2)
|
sleep(2)
|
||||||
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
mineExpectation.fulfill()
|
do {
|
||||||
}, error: { error in
|
try coordinator.sync(
|
||||||
guard let e = error else {
|
completion: { synchronizer in
|
||||||
XCTFail("unknown error syncing after sending transaction")
|
mineExpectation.fulfill()
|
||||||
return
|
continuation.resume()
|
||||||
|
}, error: { error in
|
||||||
|
guard let error else {
|
||||||
|
XCTFail("unknown error syncing after sending transaction")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTFail("Error: \(error)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
XCTFail("Error: \(e)")
|
|
||||||
})
|
|
||||||
|
|
||||||
wait(for: [mineExpectation], timeout: 5)
|
wait(for: [mineExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -802,7 +872,7 @@ class BalanceTests: XCTestCase {
|
||||||
There’s a change note of value (previous note value - sent amount)
|
There’s a change note of value (previous note value - sent amount)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
func testVerifyChangeTransaction() throws {
|
func testVerifyChangeTransaction() async throws {
|
||||||
try FakeChainBuilder.buildSingleNoteChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
try FakeChainBuilder.buildSingleNoteChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
||||||
|
|
||||||
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
||||||
|
@ -814,9 +884,16 @@ class BalanceTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
sync to current tip
|
sync to current tip
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
self.syncedExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
self.syncedExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [syncedExpectation], timeout: 6)
|
wait(for: [syncedExpectation], timeout: 6)
|
||||||
|
|
||||||
|
@ -833,26 +910,18 @@ class BalanceTests: XCTestCase {
|
||||||
*/
|
*/
|
||||||
let memo = try Memo(string: "shielding is fun!")
|
let memo = try Memo(string: "shielding is fun!")
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKeys,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: sendAmount,
|
spendingKey: spendingKeys,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: sendAmount,
|
||||||
memo: memo,
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: memo,
|
||||||
resultBlock: { sendResult in
|
from: 0)
|
||||||
DispatchQueue.main.async {
|
pendingTx = transaction
|
||||||
switch sendResult {
|
sendExpectation.fulfill()
|
||||||
case .failure(let sendError):
|
} catch {
|
||||||
XCTFail("error sending \(sendError)")
|
XCTFail("error sending \(error)")
|
||||||
|
}
|
||||||
case .success(let transaction):
|
|
||||||
pendingTx = transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
wait(for: [createToAddressExpectation], timeout: 30)
|
wait(for: [createToAddressExpectation], timeout: 30)
|
||||||
|
|
||||||
let syncToMinedheightExpectation = XCTestExpectation(description: "sync to mined height + 1")
|
let syncToMinedheightExpectation = XCTestExpectation(description: "sync to mined height + 1")
|
||||||
|
@ -875,94 +944,94 @@ class BalanceTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
Sync to that block
|
Sync to that block
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchronizer in
|
do {
|
||||||
let confirmedTx: ConfirmedTransactionEntity!
|
try coordinator.sync(completion: { synchronizer in
|
||||||
do {
|
let confirmedTx: ConfirmedTransactionEntity!
|
||||||
confirmedTx = try synchronizer.allClearedTransactions().first(where: { confirmed -> Bool in
|
do {
|
||||||
confirmed.transactionEntity.transactionId == pendingTx?.transactionEntity.transactionId
|
confirmedTx = try synchronizer.allClearedTransactions().first(where: { confirmed -> Bool in
|
||||||
})
|
confirmed.transactionEntity.transactionId == pendingTx?.transactionEntity.transactionId
|
||||||
} catch {
|
})
|
||||||
XCTFail("Error retrieving cleared transactions")
|
} catch {
|
||||||
return
|
XCTFail("Error retrieving cleared transactions")
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There’s a sent transaction matching the amount sent to the given zAddr
|
There’s a sent transaction matching the amount sent to the given zAddr
|
||||||
*/
|
*/
|
||||||
XCTAssertEqual(confirmedTx.value, self.sendAmount)
|
XCTAssertEqual(confirmedTx.value, self.sendAmount)
|
||||||
XCTAssertEqual(confirmedTx.toAddress, self.testRecipientAddress)
|
XCTAssertEqual(confirmedTx.toAddress, self.testRecipientAddress)
|
||||||
do {
|
|
||||||
let confirmedMemo = try confirmedTx.memo.intoMemoBytes().intoMemo()
|
let confirmedMemo = try confirmedTx.memo.intoMemoBytes().intoMemo()
|
||||||
XCTAssertEqual(confirmedMemo, memo)
|
XCTAssertEqual(confirmedMemo, memo)
|
||||||
} catch {
|
|
||||||
XCTFail("failed retrieving memo from confirmed transaction. Error: \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let transactionId = confirmedTx.rawTransactionId else {
|
guard let transactionId = confirmedTx.rawTransactionId else {
|
||||||
XCTFail("no raw transaction id")
|
XCTFail("no raw transaction id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Find out what note was used
|
Find out what note was used
|
||||||
*/
|
*/
|
||||||
let sentNotesRepo = SentNotesSQLDAO(
|
let sentNotesRepo = SentNotesSQLDAO(
|
||||||
dbProvider: SimpleConnectionProvider(
|
dbProvider: SimpleConnectionProvider(
|
||||||
path: synchronizer.initializer.dataDbURL.absoluteString,
|
path: synchronizer.initializer.dataDbURL.absoluteString,
|
||||||
readonly: true
|
readonly: true
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
guard let sentNote = try? sentNotesRepo.sentNote(byRawTransactionId: transactionId) else {
|
guard let sentNote = try? sentNotesRepo.sentNote(byRawTransactionId: transactionId) else {
|
||||||
XCTFail("Could not finde sent note with transaction Id \(transactionId)")
|
XCTFail("Could not finde sent note with transaction Id \(transactionId)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let receivedNotesRepo = ReceivedNotesSQLDAO(
|
let receivedNotesRepo = ReceivedNotesSQLDAO(
|
||||||
dbProvider: SimpleConnectionProvider(
|
dbProvider: SimpleConnectionProvider(
|
||||||
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
|
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
|
||||||
readonly: true
|
readonly: true
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
get change note
|
get change note
|
||||||
*/
|
*/
|
||||||
guard let receivedNote = try? receivedNotesRepo.receivedNote(byRawTransactionId: transactionId) else {
|
guard let receivedNote = try? receivedNotesRepo.receivedNote(byRawTransactionId: transactionId) else {
|
||||||
XCTFail("Could not find received not with change for transaction Id \(transactionId)")
|
XCTFail("Could not find received not with change for transaction Id \(transactionId)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There’s a change note of value (previous note value - sent amount)
|
There’s a change note of value (previous note value - sent amount)
|
||||||
*/
|
*/
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
previousVerifiedBalance - self.sendAmount - self.network.constants.defaultFee(for: self.defaultLatestHeight),
|
previousVerifiedBalance - self.sendAmount - self.network.constants.defaultFee(for: self.defaultLatestHeight),
|
||||||
Zatoshi(Int64(receivedNote.value))
|
Zatoshi(Int64(receivedNote.value))
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Balance meets verified Balance and total balance criteria
|
Balance meets verified Balance and total balance criteria
|
||||||
*/
|
*/
|
||||||
self.verifiedBalanceValidation(
|
self.verifiedBalanceValidation(
|
||||||
previousBalance: previousVerifiedBalance,
|
previousBalance: previousVerifiedBalance,
|
||||||
spentNoteValue: Zatoshi(Int64(sentNote.value)),
|
spentNoteValue: Zatoshi(Int64(sentNote.value)),
|
||||||
changeValue: Zatoshi(Int64(receivedNote.value)),
|
changeValue: Zatoshi(Int64(receivedNote.value)),
|
||||||
sentAmount: self.sendAmount,
|
sentAmount: self.sendAmount,
|
||||||
currentVerifiedBalance: synchronizer.initializer.getVerifiedBalance()
|
currentVerifiedBalance: synchronizer.initializer.getVerifiedBalance()
|
||||||
)
|
)
|
||||||
|
|
||||||
self.totalBalanceValidation(
|
self.totalBalanceValidation(
|
||||||
totalBalance: synchronizer.initializer.getBalance(),
|
totalBalance: synchronizer.initializer.getBalance(),
|
||||||
previousTotalbalance: previousTotalBalance,
|
previousTotalbalance: previousTotalBalance,
|
||||||
sentAmount: self.sendAmount
|
sentAmount: self.sendAmount
|
||||||
)
|
)
|
||||||
|
|
||||||
syncToMinedheightExpectation.fulfill()
|
syncToMinedheightExpectation.fulfill()
|
||||||
},
|
continuation.resume()
|
||||||
error: self.handleError
|
}, error: self.handleError)
|
||||||
)
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [syncToMinedheightExpectation], timeout: 5)
|
wait(for: [syncToMinedheightExpectation], timeout: 5)
|
||||||
}
|
}
|
||||||
|
@ -990,15 +1059,21 @@ class BalanceTests: XCTestCase {
|
||||||
Verified Balance is equal to verified balance previously shown before sending the expired transaction
|
Verified Balance is equal to verified balance previously shown before sending the expired transaction
|
||||||
|
|
||||||
*/
|
*/
|
||||||
func testVerifyBalanceAfterExpiredTransaction() throws {
|
func testVerifyBalanceAfterExpiredTransaction() async throws {
|
||||||
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
||||||
|
|
||||||
try coordinator.applyStaged(blockheight: self.defaultLatestHeight)
|
try coordinator.applyStaged(blockheight: self.defaultLatestHeight)
|
||||||
sleep(2)
|
sleep(2)
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
self.syncedExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
self.syncedExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [syncedExpectation], timeout: 5)
|
wait(for: [syncedExpectation], timeout: 5)
|
||||||
|
|
||||||
guard let spendingKey = coordinator.spendingKeys?.first else {
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
||||||
|
@ -1010,24 +1085,20 @@ class BalanceTests: XCTestCase {
|
||||||
let previousTotalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
let previousTotalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
||||||
let sendExpectation = XCTestExpectation(description: "send expectation")
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let pending = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: sendAmount,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: sendAmount,
|
||||||
memo: try Memo(string: "test send \(self.description)"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "test send \(self.description)"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = pending
|
||||||
case .failure(let error):
|
} catch {
|
||||||
// balance should be the same as before sending if transaction failed
|
// balance should be the same as before sending if transaction failed
|
||||||
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), previousVerifiedBalance)
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), previousVerifiedBalance)
|
||||||
XCTAssertEqual(self.coordinator.synchronizer.initializer.getBalance(), previousTotalBalance)
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getBalance(), previousTotalBalance)
|
||||||
XCTFail("sendToAddress failed: \(error)")
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
case .success(let pending):
|
}
|
||||||
pendingTx = pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 12)
|
wait(for: [sendExpectation], timeout: 12)
|
||||||
|
|
||||||
|
@ -1043,10 +1114,16 @@ class BalanceTests: XCTestCase {
|
||||||
try coordinator.applyStaged(blockheight: expiryHeight + 1)
|
try coordinator.applyStaged(blockheight: expiryHeight + 1)
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
expirationSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
expirationSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [expirationSyncExpectation], timeout: 5)
|
wait(for: [expirationSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -43,7 +43,7 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
Given that a wallet had funds prior to activation it can spend them after activation
|
Given that a wallet had funds prior to activation it can spend them after activation
|
||||||
*/
|
*/
|
||||||
func testSpendPriorFundsAfterActivation() throws {
|
func testSpendPriorFundsAfterActivation() async throws {
|
||||||
try FakeChainBuilder.buildChain(
|
try FakeChainBuilder.buildChain(
|
||||||
darksideWallet: coordinator.service,
|
darksideWallet: coordinator.service,
|
||||||
birthday: birthday,
|
birthday: birthday,
|
||||||
|
@ -58,10 +58,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.defaultStaleTolerance)
|
try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.defaultStaleTolerance)
|
||||||
sleep(5)
|
sleep(5)
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [firstSyncExpectation], timeout: 120)
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
guard verifiedBalance > network.constants.defaultFee(for: activationHeight) else {
|
guard verifiedBalance > network.constants.defaultFee(for: activationHeight) else {
|
||||||
|
@ -79,22 +85,18 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
send transaction to recipient address
|
send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: spendAmount,
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: spendAmount,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -123,10 +125,17 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
sleep(1)
|
sleep(1)
|
||||||
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
afterSendExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [afterSendExpectation], timeout: 10)
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
|
|
||||||
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), verifiedBalance - spendAmount)
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), verifiedBalance - spendAmount)
|
||||||
|
@ -135,7 +144,7 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
Given that a wallet receives funds after activation it can spend them when confirmed
|
Given that a wallet receives funds after activation it can spend them when confirmed
|
||||||
*/
|
*/
|
||||||
func testSpendPostActivationFundsAfterConfirmation() throws {
|
func testSpendPostActivationFundsAfterConfirmation() async throws {
|
||||||
try FakeChainBuilder.buildChainPostActivationFunds(
|
try FakeChainBuilder.buildChainPostActivationFunds(
|
||||||
darksideWallet: coordinator.service,
|
darksideWallet: coordinator.service,
|
||||||
birthday: birthday,
|
birthday: birthday,
|
||||||
|
@ -148,9 +157,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
try coordinator.applyStaged(blockheight: activationHeight + 10)
|
try coordinator.applyStaged(blockheight: activationHeight + 10)
|
||||||
sleep(3)
|
sleep(3)
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 120)
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
guard try !coordinator.synchronizer.allReceivedTransactions().filter({ $0.minedHeight > activationHeight }).isEmpty else {
|
guard try !coordinator.synchronizer.allReceivedTransactions().filter({ $0.minedHeight > activationHeight }).isEmpty else {
|
||||||
|
@ -168,22 +184,18 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
send transaction to recipient address
|
send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: spendAmount,
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: spendAmount,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -197,9 +209,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
|
|
||||||
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
afterSendExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [afterSendExpectation], timeout: 10)
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +226,7 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
Given that a wallet sends funds some between (activation - expiry_height) and activation, those funds are shown as sent if mined.
|
Given that a wallet sends funds some between (activation - expiry_height) and activation, those funds are shown as sent if mined.
|
||||||
*/
|
*/
|
||||||
func testSpendMinedSpendThatExpiresOnActivation() throws {
|
func testSpendMinedSpendThatExpiresOnActivation() async throws {
|
||||||
try FakeChainBuilder.buildChain(
|
try FakeChainBuilder.buildChain(
|
||||||
darksideWallet: coordinator.service,
|
darksideWallet: coordinator.service,
|
||||||
birthday: birthday,
|
birthday: birthday,
|
||||||
|
@ -222,9 +241,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
try coordinator.applyStaged(blockheight: activationHeight - 10)
|
try coordinator.applyStaged(blockheight: activationHeight - 10)
|
||||||
sleep(3)
|
sleep(3)
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 120)
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
@ -237,22 +263,18 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
send transaction to recipient address
|
send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: spendAmount,
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: spendAmount,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -283,9 +305,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
|
|
||||||
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
afterSendExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [afterSendExpectation], timeout: 10)
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
|
|
||||||
|
@ -303,7 +332,7 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
Given that a wallet sends funds somewhere between (activation - expiry_height) and activation, those funds are available if expired after expiration height.
|
Given that a wallet sends funds somewhere between (activation - expiry_height) and activation, those funds are available if expired after expiration height.
|
||||||
*/
|
*/
|
||||||
func testExpiredSpendAfterActivation() throws {
|
func testExpiredSpendAfterActivation() async throws {
|
||||||
try FakeChainBuilder.buildChain(
|
try FakeChainBuilder.buildChain(
|
||||||
darksideWallet: coordinator.service,
|
darksideWallet: coordinator.service,
|
||||||
birthday: birthday,
|
birthday: birthday,
|
||||||
|
@ -320,9 +349,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
|
|
||||||
let verifiedBalancePreActivation: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
let verifiedBalancePreActivation: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 120)
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
@ -338,22 +374,18 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
send transaction to recipient address
|
send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: spendAmount,
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: spendAmount,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -380,9 +412,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
|
|
||||||
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
afterSendExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [afterSendExpectation], timeout: 10)
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
|
|
||||||
|
@ -400,7 +439,7 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/**
|
/**
|
||||||
Given that a wallet has notes both received prior and after activation these can be combined to supply a larger amount spend.
|
Given that a wallet has notes both received prior and after activation these can be combined to supply a larger amount spend.
|
||||||
*/
|
*/
|
||||||
func testCombinePreActivationNotesAndPostActivationNotesOnSpend() throws {
|
func testCombinePreActivationNotesAndPostActivationNotesOnSpend() async throws {
|
||||||
try FakeChainBuilder.buildChainMixedFunds(
|
try FakeChainBuilder.buildChainMixedFunds(
|
||||||
darksideWallet: coordinator.service,
|
darksideWallet: coordinator.service,
|
||||||
birthday: birthday,
|
birthday: birthday,
|
||||||
|
@ -415,9 +454,16 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
try coordinator.applyStaged(blockheight: activationHeight - 1)
|
try coordinator.applyStaged(blockheight: activationHeight - 1)
|
||||||
sleep(3)
|
sleep(3)
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [firstSyncExpectation], timeout: 120)
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
|
|
||||||
|
@ -427,10 +473,17 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
sleep(2)
|
sleep(2)
|
||||||
|
|
||||||
let secondSyncExpectation = XCTestExpectation(description: "second sync")
|
let secondSyncExpectation = XCTestExpectation(description: "second sync")
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
secondSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
secondSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [secondSyncExpectation], timeout: 10)
|
wait(for: [secondSyncExpectation], timeout: 10)
|
||||||
guard try !coordinator.synchronizer.allReceivedTransactions().filter({ $0.minedHeight > activationHeight }).isEmpty else {
|
guard try !coordinator.synchronizer.allReceivedTransactions().filter({ $0.minedHeight > activationHeight }).isEmpty else {
|
||||||
XCTFail("this test requires funds received after activation height")
|
XCTFail("this test requires funds received after activation height")
|
||||||
|
@ -450,22 +503,18 @@ class NetworkUpgradeTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
send transaction to recipient address
|
send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: spendAmount,
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: spendAmount,
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 15)
|
wait(for: [sendExpectation], timeout: 15)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
||||||
reorgExpectation.fulfill()
|
reorgExpectation.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPendingTransactionMinedHeightUpdated() throws {
|
func testPendingTransactionMinedHeightUpdated() async throws {
|
||||||
/*
|
/*
|
||||||
1. create fake chain
|
1. create fake chain
|
||||||
*/
|
*/
|
||||||
|
@ -78,10 +78,16 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
||||||
1a. sync to latest height
|
1a. sync to latest height
|
||||||
*/
|
*/
|
||||||
LoggerProxy.info("1a. sync to latest height")
|
LoggerProxy.info("1a. sync to latest height")
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [firstSyncExpectation], timeout: 5)
|
wait(for: [firstSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
@ -93,23 +99,19 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
||||||
2. send transaction to recipient address
|
2. send transaction to recipient address
|
||||||
*/
|
*/
|
||||||
LoggerProxy.info("2. send transaction to recipient address")
|
LoggerProxy.info("2. send transaction to recipient address")
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
// swiftlint:disable:next force_unwrapping
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
// swiftlint:disable:next force_unwrapping
|
||||||
zatoshi: Zatoshi(20000),
|
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: Zatoshi(20000),
|
||||||
memo: try Memo(string: "this is a test"),
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0,
|
memo: try Memo(string: "this is a test"),
|
||||||
resultBlock: { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pendingTx
|
||||||
case .failure(let e):
|
sendExpectation.fulfill()
|
||||||
self.handleError(e)
|
} catch {
|
||||||
case .success(let pendingTx):
|
self.handleError(error)
|
||||||
pendingEntity = pendingTx
|
}
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 11)
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
@ -167,13 +169,17 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
||||||
LoggerProxy.info("6. sync to latest height")
|
LoggerProxy.info("6. sync to latest height")
|
||||||
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
|
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
|
||||||
|
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { _ in
|
do {
|
||||||
secondSyncExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
},
|
secondSyncExpectation.fulfill()
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [secondSyncExpectation], timeout: 5)
|
wait(for: [secondSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
XCTAssertEqual(coordinator.synchronizer.pendingTransactions.count, 1)
|
XCTAssertEqual(coordinator.synchronizer.pendingTransactions.count, 1)
|
||||||
|
@ -207,10 +213,17 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
||||||
*/
|
*/
|
||||||
LoggerProxy.info("last sync to latest height: \(lastStageHeight)")
|
LoggerProxy.info("last sync to latest height: \(lastStageHeight)")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
syncToConfirmExpectation.fulfill()
|
do {
|
||||||
}, error: self.handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
syncToConfirmExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [syncToConfirmExpectation], timeout: 6)
|
wait(for: [syncToConfirmExpectation], timeout: 6)
|
||||||
var supposedlyPendingUnexistingTransaction: PendingTransactionEntity?
|
var supposedlyPendingUnexistingTransaction: PendingTransactionEntity?
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,14 @@ import XCTest
|
||||||
@testable import TestUtils
|
@testable import TestUtils
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
// swiftlint:disable type_body_length implicitly_unwrapped_optional
|
// swiftlint:disable type_body_length implicitly_unwrapped_optional force_try
|
||||||
class RewindRescanTests: XCTestCase {
|
class RewindRescanTests: XCTestCase {
|
||||||
// TODO: Parameterize this from environment?
|
// TODO: Parameterize this from environment?
|
||||||
// swiftlint:disable:next line_length
|
// swiftlint:disable:next line_length
|
||||||
let seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
|
let seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
|
||||||
|
|
||||||
// TODO: Parameterize this from environment
|
// TODO: Parameterize this from environment
|
||||||
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a"
|
let testRecipientAddress = try! Recipient("zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a", network: .mainnet)
|
||||||
let sendAmount: Int64 = 1000
|
let sendAmount: Int64 = 1000
|
||||||
let defaultLatestHeight: BlockHeight = 663175
|
let defaultLatestHeight: BlockHeight = 663175
|
||||||
let branchID = "2bb40e60"
|
let branchID = "2bb40e60"
|
||||||
|
@ -107,7 +107,7 @@ class RewindRescanTests: XCTestCase {
|
||||||
XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance())
|
XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRescanToHeight() throws {
|
func testRescanToHeight() async throws {
|
||||||
// 1 sync and get spendable funds
|
// 1 sync and get spendable funds
|
||||||
try FakeChainBuilder.buildChainWithTxsFarFromEachOther(
|
try FakeChainBuilder.buildChainWithTxsFarFromEachOther(
|
||||||
darksideWallet: coordinator.service,
|
darksideWallet: coordinator.service,
|
||||||
|
@ -121,13 +121,16 @@ class RewindRescanTests: XCTestCase {
|
||||||
let initialVerifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
let initialVerifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { _ in
|
do {
|
||||||
firstSyncExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
},
|
firstSyncExpectation.fulfill()
|
||||||
error: handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [firstSyncExpectation], timeout: 20)
|
wait(for: [firstSyncExpectation], timeout: 20)
|
||||||
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
let totalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
let totalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
||||||
|
@ -154,10 +157,17 @@ class RewindRescanTests: XCTestCase {
|
||||||
|
|
||||||
let secondScanExpectation = XCTestExpectation(description: "rescan")
|
let secondScanExpectation = XCTestExpectation(description: "rescan")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
secondScanExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
secondScanExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [secondScanExpectation], timeout: 20)
|
wait(for: [secondScanExpectation], timeout: 20)
|
||||||
|
|
||||||
// verify that the balance still adds up
|
// verify that the balance still adds up
|
||||||
|
@ -166,20 +176,16 @@ class RewindRescanTests: XCTestCase {
|
||||||
|
|
||||||
// try to spend the funds
|
// try to spend the funds
|
||||||
let sendExpectation = XCTestExpectation(description: "after rewind expectation")
|
let sendExpectation = XCTestExpectation(description: "after rewind expectation")
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: coordinator.spendingKey,
|
let pendingTx = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: Zatoshi(1000),
|
spendingKey: coordinator.spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: Zatoshi(1000),
|
||||||
memo: .empty,
|
toAddress: testRecipientAddress,
|
||||||
from: 0
|
memo: .empty,
|
||||||
) { result in
|
from: 0)
|
||||||
sendExpectation.fulfill()
|
XCTAssertEqual(Zatoshi(1000), pendingTx.value)
|
||||||
switch result {
|
} catch {
|
||||||
case .success(let pendingTx):
|
XCTFail("sending fail: \(error)")
|
||||||
XCTAssertEqual(Zatoshi(1000), pendingTx.value)
|
|
||||||
case .failure(let error):
|
|
||||||
XCTFail("sending fail: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
wait(for: [sendExpectation], timeout: 15)
|
wait(for: [sendExpectation], timeout: 15)
|
||||||
}
|
}
|
||||||
|
@ -231,7 +237,7 @@ class RewindRescanTests: XCTestCase {
|
||||||
XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance())
|
XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRewindAfterSendingTransaction() throws {
|
func testRewindAfterSendingTransaction() async throws {
|
||||||
let notificationHandler = SDKSynchonizerListener()
|
let notificationHandler = SDKSynchonizerListener()
|
||||||
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
||||||
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
||||||
|
@ -246,10 +252,16 @@ class RewindRescanTests: XCTestCase {
|
||||||
sleep(1)
|
sleep(1)
|
||||||
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { _ in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
firstSyncExpectation.fulfill()
|
do {
|
||||||
}, error: handleError)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [firstSyncExpectation], timeout: 12)
|
wait(for: [firstSyncExpectation], timeout: 12)
|
||||||
// 2 check that there are no unconfirmed funds
|
// 2 check that there are no unconfirmed funds
|
||||||
|
|
||||||
|
@ -267,20 +279,17 @@ class RewindRescanTests: XCTestCase {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var pendingTx: PendingTransactionEntity?
|
var pendingTx: PendingTransactionEntity?
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: spendingKey,
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: maxBalance,
|
spendingKey: spendingKey,
|
||||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: maxBalance,
|
||||||
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
toAddress: testRecipientAddress,
|
||||||
from: 0
|
memo: try Memo(string: "test send \(self.description) \(Date().description)"),
|
||||||
) { result in
|
from: 0)
|
||||||
switch result {
|
pendingTx = transaction
|
||||||
case .failure(let error):
|
|
||||||
XCTFail("sendToAddress failed: \(error)")
|
|
||||||
case .success(let transaction):
|
|
||||||
pendingTx = transaction
|
|
||||||
}
|
|
||||||
self.sentTransactionExpectation.fulfill()
|
self.sentTransactionExpectation.fulfill()
|
||||||
|
} catch {
|
||||||
|
XCTFail("sendToAddress failed: \(error)")
|
||||||
}
|
}
|
||||||
wait(for: [sentTransactionExpectation], timeout: 20)
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
||||||
guard let pendingTx = pendingTx else {
|
guard let pendingTx = pendingTx else {
|
||||||
|
@ -320,25 +329,30 @@ class RewindRescanTests: XCTestCase {
|
||||||
|
|
||||||
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
||||||
|
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchronizer in
|
do {
|
||||||
let pendingTransaction = synchronizer.pendingTransactions
|
try coordinator.sync(
|
||||||
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
completion: { synchronizer in
|
||||||
XCTAssertNotNil(pendingTransaction, "pending transaction should have been mined by now")
|
let pendingTransaction = synchronizer.pendingTransactions
|
||||||
XCTAssertTrue(pendingTransaction?.isMined ?? false)
|
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
||||||
XCTAssertEqual(pendingTransaction?.minedHeight, sentTxHeight)
|
XCTAssertNotNil(pendingTransaction, "pending transaction should have been mined by now")
|
||||||
mineExpectation.fulfill()
|
XCTAssertTrue(pendingTransaction?.isMined ?? false)
|
||||||
},
|
XCTAssertEqual(pendingTransaction?.minedHeight, sentTxHeight)
|
||||||
error: { error in
|
mineExpectation.fulfill()
|
||||||
guard let e = error else {
|
continuation.resume()
|
||||||
XCTFail("unknown error syncing after sending transaction")
|
}, error: { error in
|
||||||
return
|
guard let error else {
|
||||||
}
|
XCTFail("unknown error syncing after sending transaction")
|
||||||
|
return
|
||||||
XCTFail("Error: \(e)")
|
}
|
||||||
|
|
||||||
|
XCTFail("Error: \(error)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
||||||
|
|
||||||
// 7 advance to confirmation
|
// 7 advance to confirmation
|
||||||
|
@ -375,15 +389,16 @@ class RewindRescanTests: XCTestCase {
|
||||||
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
||||||
}
|
}
|
||||||
|
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { _ in
|
do {
|
||||||
confirmExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
},
|
confirmExpectation.fulfill()
|
||||||
error: { e in
|
continuation.resume()
|
||||||
self.handleError(e)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
wait(for: [confirmExpectation], timeout: 10)
|
wait(for: [confirmExpectation], timeout: 10)
|
||||||
|
|
||||||
let confirmedPending = try coordinator.synchronizer.allPendingTransactions()
|
let confirmedPending = try coordinator.synchronizer.allPendingTransactions()
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import TestUtils
|
@testable import TestUtils
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
class ShieldFundsTests: XCTestCase {
|
class ShieldFundsTests: XCTestCase {
|
||||||
// TODO: Parameterize this from environment?
|
// TODO: Parameterize this from environment?
|
||||||
// swiftlint:disable:next line_length
|
// swiftlint:disable:next line_length
|
||||||
|
@ -82,7 +83,7 @@ class ShieldFundsTests: XCTestCase {
|
||||||
/// 15. sync up to the new chain tip
|
/// 15. sync up to the new chain tip
|
||||||
/// verify that the shielded transactions are confirmed
|
/// verify that the shielded transactions are confirmed
|
||||||
///
|
///
|
||||||
func testShieldFunds() throws {
|
func testShieldFunds() async throws {
|
||||||
// 1. load the dataset
|
// 1. load the dataset
|
||||||
try coordinator.service.useDataset(from: "https://github.com/zcash-hackworks/darksidewalletd-test-data/blob/master/shield-funds/1631000.txt")
|
try coordinator.service.useDataset(from: "https://github.com/zcash-hackworks/darksidewalletd-test-data/blob/master/shield-funds/1631000.txt")
|
||||||
|
|
||||||
|
@ -110,15 +111,19 @@ class ShieldFundsTests: XCTestCase {
|
||||||
let preTxExpectation = XCTestExpectation(description: "pre receive")
|
let preTxExpectation = XCTestExpectation(description: "pre receive")
|
||||||
|
|
||||||
// 3. sync up to that height
|
// 3. sync up to that height
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchro in
|
do {
|
||||||
initialVerifiedBalance = synchro.initializer.getVerifiedBalance()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
initialTotalBalance = synchro.initializer.getBalance()
|
initialVerifiedBalance = synchronizer.initializer.getVerifiedBalance()
|
||||||
preTxExpectation.fulfill()
|
initialTotalBalance = synchronizer.initializer.getBalance()
|
||||||
shouldContinue = true
|
preTxExpectation.fulfill()
|
||||||
},
|
shouldContinue = true
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [preTxExpectation], timeout: 10)
|
wait(for: [preTxExpectation], timeout: 10)
|
||||||
|
|
||||||
|
@ -149,14 +154,17 @@ class ShieldFundsTests: XCTestCase {
|
||||||
shouldContinue = false
|
shouldContinue = false
|
||||||
|
|
||||||
// 6. Sync and find the UXTO on chain.
|
// 6. Sync and find the UXTO on chain.
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchro in
|
do {
|
||||||
tFundsDetectionExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
shouldContinue = true
|
shouldContinue = true
|
||||||
},
|
tFundsDetectionExpectation.fulfill()
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [tFundsDetectionExpectation], timeout: 2)
|
wait(for: [tFundsDetectionExpectation], timeout: 2)
|
||||||
|
|
||||||
// at this point the balance should be zero for shielded, then zero verified transparent funds
|
// at this point the balance should be zero for shielded, then zero verified transparent funds
|
||||||
|
@ -176,13 +184,17 @@ class ShieldFundsTests: XCTestCase {
|
||||||
sleep(2)
|
sleep(2)
|
||||||
|
|
||||||
// 8. sync up to chain tip.
|
// 8. sync up to chain tip.
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchro in
|
do {
|
||||||
tFundsConfirmationSyncExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
shouldContinue = true
|
shouldContinue = true
|
||||||
},
|
tFundsConfirmationSyncExpectation.fulfill()
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [tFundsConfirmationSyncExpectation], timeout: 5)
|
wait(for: [tFundsConfirmationSyncExpectation], timeout: 5)
|
||||||
|
|
||||||
|
@ -208,21 +220,17 @@ class ShieldFundsTests: XCTestCase {
|
||||||
var shieldingPendingTx: PendingTransactionEntity?
|
var shieldingPendingTx: PendingTransactionEntity?
|
||||||
|
|
||||||
// shield the funds
|
// shield the funds
|
||||||
coordinator.synchronizer.shieldFunds(
|
do {
|
||||||
transparentAccountPrivateKey: transparentAccountPrivateKey,
|
let pendingTx = try await coordinator.synchronizer.shieldFunds(
|
||||||
memo: try Memo(string: "shield funds"),
|
transparentAccountPrivateKey: transparentAccountPrivateKey,
|
||||||
from: 0
|
memo: try Memo(string: "shield funds"),
|
||||||
) { result in
|
from: 0)
|
||||||
switch result {
|
shouldContinue = true
|
||||||
case .failure(let error):
|
XCTAssertEqual(pendingTx.value, Zatoshi(10000))
|
||||||
XCTFail("Failed With error: \(error.localizedDescription)")
|
shieldingPendingTx = pendingTx
|
||||||
|
|
||||||
case .success(let pendingTx):
|
|
||||||
shouldContinue = true
|
|
||||||
XCTAssertEqual(pendingTx.value, Zatoshi(10000))
|
|
||||||
shieldingPendingTx = pendingTx
|
|
||||||
}
|
|
||||||
shieldFundsExpectation.fulfill()
|
shieldFundsExpectation.fulfill()
|
||||||
|
} catch {
|
||||||
|
XCTFail("Failed With error: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
|
|
||||||
wait(for: [shieldFundsExpectation], timeout: 30)
|
wait(for: [shieldFundsExpectation], timeout: 30)
|
||||||
|
@ -262,14 +270,17 @@ class ShieldFundsTests: XCTestCase {
|
||||||
// 13. sync up to chain tip
|
// 13. sync up to chain tip
|
||||||
let postShieldSyncExpectation = XCTestExpectation(description: "sync Post shield")
|
let postShieldSyncExpectation = XCTestExpectation(description: "sync Post shield")
|
||||||
shouldContinue = false
|
shouldContinue = false
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchro in
|
do {
|
||||||
postShieldSyncExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
shouldContinue = true
|
shouldContinue = true
|
||||||
},
|
postShieldSyncExpectation.fulfill()
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [postShieldSyncExpectation], timeout: 3)
|
wait(for: [postShieldSyncExpectation], timeout: 3)
|
||||||
|
|
||||||
guard shouldContinue else { return }
|
guard shouldContinue else { return }
|
||||||
|
@ -292,13 +303,17 @@ class ShieldFundsTests: XCTestCase {
|
||||||
shouldContinue = false
|
shouldContinue = false
|
||||||
|
|
||||||
// 15. sync up to the new chain tip
|
// 15. sync up to the new chain tip
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { synchro in
|
do {
|
||||||
confirmationExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
shouldContinue = true
|
shouldContinue = true
|
||||||
},
|
confirmationExpectation.fulfill()
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [confirmationExpectation], timeout: 5)
|
wait(for: [confirmationExpectation], timeout: 5)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
//
|
||||||
|
// SynchronizerTests.swift
|
||||||
|
// DarksideTests
|
||||||
|
//
|
||||||
|
// Created by Francisco Gindre on 9/16/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import TestUtils
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
// swiftlint:disable implicitly_unwrapped_optional force_unwrapping type_body_length
|
||||||
|
final class SynchronizerTests: XCTestCase {
|
||||||
|
|
||||||
|
// TODO: Parameterize this from environment?
|
||||||
|
// swiftlint:disable:next line_length
|
||||||
|
var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
|
||||||
|
|
||||||
|
// TODO: Parameterize this from environment
|
||||||
|
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a"
|
||||||
|
|
||||||
|
let sendAmount = Zatoshi(1000)
|
||||||
|
var birthday: BlockHeight = 663150
|
||||||
|
let defaultLatestHeight: BlockHeight = 663175
|
||||||
|
var coordinator: TestCoordinator!
|
||||||
|
var syncedExpectation = XCTestExpectation(description: "synced")
|
||||||
|
var sentTransactionExpectation = XCTestExpectation(description: "sent")
|
||||||
|
var expectedReorgHeight: BlockHeight = 665188
|
||||||
|
var expectedRewindHeight: BlockHeight = 665188
|
||||||
|
var reorgExpectation = XCTestExpectation(description: "reorg")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
try super.tearDownWithError()
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
try coordinator.stop()
|
||||||
|
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
|
||||||
|
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
|
||||||
|
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleReorg(_ notification: Notification) {
|
||||||
|
guard
|
||||||
|
let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
|
||||||
|
let rewindHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight
|
||||||
|
else {
|
||||||
|
XCTFail("empty reorg notification")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger!.debug("--- REORG DETECTED \(reorgHeight)--- RewindHeight: \(rewindHeight)", file: #file, function: #function, line: #line)
|
||||||
|
|
||||||
|
XCTAssertEqual(reorgHeight, expectedReorgHeight)
|
||||||
|
reorgExpectation.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSynchronizerStops() throws {
|
||||||
|
hookToReOrgNotification()
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. create fake chain
|
||||||
|
*/
|
||||||
|
let fullSyncLength = 100_000
|
||||||
|
|
||||||
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName, length: fullSyncLength)
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: birthday + fullSyncLength)
|
||||||
|
|
||||||
|
sleep(10)
|
||||||
|
|
||||||
|
let syncStoppedExpectation = XCTestExpectation(description: "SynchronizerStopped Expectation")
|
||||||
|
syncStoppedExpectation.subscribe(to: .synchronizerStopped, object: nil)
|
||||||
|
|
||||||
|
let processorStoppedExpectation = XCTestExpectation(description: "ProcessorStopped Expectation")
|
||||||
|
processorStoppedExpectation.subscribe(to: .blockProcessorStopped, object: nil)
|
||||||
|
|
||||||
|
/*
|
||||||
|
sync to latest height
|
||||||
|
*/
|
||||||
|
try coordinator.sync(completion: { _ in
|
||||||
|
XCTFail("Sync should have stopped")
|
||||||
|
}, error: { error in
|
||||||
|
_ = try? self.coordinator.stop()
|
||||||
|
|
||||||
|
guard let testError = error else {
|
||||||
|
XCTFail("failed with nil error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTFail("Failed with error: \(testError)")
|
||||||
|
})
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
|
||||||
|
self.coordinator.synchronizer.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(for: [processorStoppedExpectation,syncStoppedExpectation], timeout: 6, enforceOrder: true)
|
||||||
|
|
||||||
|
XCTAssertEqual(coordinator.synchronizer.status, .stopped)
|
||||||
|
XCTAssertEqual(coordinator.synchronizer.blockProcessor.state.getState(), .stopped)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(_ error: Error?) {
|
||||||
|
_ = try? coordinator.stop()
|
||||||
|
guard let testError = error else {
|
||||||
|
XCTFail("failed with nil error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTFail("Failed with error: \(testError)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func hookToReOrgNotification() {
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(handleReorg(_:)), name: .blockProcessorHandledReOrg, object: nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,7 +69,7 @@ class Z2TReceiveTests: XCTestCase {
|
||||||
self.foundTransactionsExpectation.fulfill()
|
self.foundTransactionsExpectation.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFoundTransactions() throws {
|
func testFoundTransactions() async throws {
|
||||||
subscribeToFoundTransactions()
|
subscribeToFoundTransactions()
|
||||||
try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName)
|
try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName)
|
||||||
let receivedTxHeight: BlockHeight = 663188
|
let receivedTxHeight: BlockHeight = 663188
|
||||||
|
@ -85,42 +85,42 @@ class Z2TReceiveTests: XCTestCase {
|
||||||
/*
|
/*
|
||||||
3. sync up to received_Tx_height
|
3. sync up to received_Tx_height
|
||||||
*/
|
*/
|
||||||
try coordinator.sync(
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
completion: { _ in
|
do {
|
||||||
preTxExpectation.fulfill()
|
try coordinator.sync(completion: { synchronizer in
|
||||||
},
|
preTxExpectation.fulfill()
|
||||||
error: self.handleError
|
continuation.resume()
|
||||||
)
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
wait(for: [preTxExpectation, foundTransactionsExpectation], timeout: 5)
|
wait(for: [preTxExpectation, foundTransactionsExpectation], timeout: 5)
|
||||||
|
|
||||||
let sendExpectation = XCTestExpectation(description: "sendToAddress")
|
let sendExpectation = XCTestExpectation(description: "sendToAddress")
|
||||||
var pendingEntity: PendingTransactionEntity?
|
var pendingEntity: PendingTransactionEntity?
|
||||||
var error: Error?
|
var testError: Error?
|
||||||
let sendAmount = Zatoshi(10000)
|
let sendAmount = Zatoshi(10000)
|
||||||
/*
|
/*
|
||||||
4. create transaction
|
4. create transaction
|
||||||
*/
|
*/
|
||||||
coordinator.synchronizer.sendToAddress(
|
do {
|
||||||
spendingKey: coordinator.spendingKeys!.first!,
|
let pending = try await coordinator.synchronizer.sendToAddress(
|
||||||
zatoshi: sendAmount,
|
spendingKey: coordinator.spendingKeys!.first!,
|
||||||
toAddress: try! Recipient(testRecipientAddress, network: self.network.networkType),
|
zatoshi: sendAmount,
|
||||||
memo: try Memo(string: "test transaction"),
|
toAddress: try! Recipient(testRecipientAddress, network: self.network.networkType),
|
||||||
from: 0
|
memo: try Memo(string: "test transaction"),
|
||||||
) { result in
|
from: 0)
|
||||||
switch result {
|
pendingEntity = pending
|
||||||
case .success(let pending):
|
|
||||||
pendingEntity = pending
|
|
||||||
case .failure(let e):
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
sendExpectation.fulfill()
|
sendExpectation.fulfill()
|
||||||
|
} catch {
|
||||||
|
testError = error
|
||||||
}
|
}
|
||||||
|
|
||||||
wait(for: [sendExpectation], timeout: 12)
|
wait(for: [sendExpectation], timeout: 12)
|
||||||
|
|
||||||
guard pendingEntity != nil else {
|
guard pendingEntity != nil else {
|
||||||
XCTFail("error sending to address. Error: \(String(describing: error))")
|
XCTFail("error sending to address. Error: \(String(describing: testError))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,13 +154,20 @@ class Z2TReceiveTests: XCTestCase {
|
||||||
*/
|
*/
|
||||||
let sentTxSyncExpectation = XCTestExpectation(description: "sent tx sync expectation")
|
let sentTxSyncExpectation = XCTestExpectation(description: "sent tx sync expectation")
|
||||||
|
|
||||||
try coordinator.sync(completion: { synchronizer in
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
|
do {
|
||||||
XCTAssertEqual(pMinedHeight, sentTxHeight)
|
try coordinator.sync(completion: { synchronizer in
|
||||||
|
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
|
||||||
sentTxSyncExpectation.fulfill()
|
XCTAssertEqual(pMinedHeight, sentTxHeight)
|
||||||
}, error: self.handleError)
|
|
||||||
|
sentTxSyncExpectation.fulfill()
|
||||||
|
continuation.resume()
|
||||||
|
}, error: self.handleError)
|
||||||
|
} catch {
|
||||||
|
continuation.resume(throwing: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wait(for: [sentTxSyncExpectation, foundTransactionsExpectation], timeout: 5)
|
wait(for: [sentTxSyncExpectation, foundTransactionsExpectation], timeout: 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import XCTest
|
||||||
class DerivationToolTestnetTests: XCTestCase {
|
class DerivationToolTestnetTests: XCTestCase {
|
||||||
var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" //TODO: Parameterize this from environment?
|
var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" //TODO: Parameterize this from environment?
|
||||||
var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")!
|
var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")!
|
||||||
let testRecipientAddress = UnifiedAddress(validatedEncoding: "utest1uqmec4a2njqz2z2rwppchsd06qe7a0jh4jmsqr0yy99m9er9646zlxunf3v8qr0hncgv86e8a62vxy0qa32qzetmj8s57yudmyx9zav6f52nurclsqjkqtjtpz6vg679p6wkczpl2wu", network: .mainnet) //TODO: Parameterize this from environment
|
let testRecipientAddress = UnifiedAddress(validatedEncoding: "utest1uqmec4a2njqz2z2rwppchsd06qe7a0jh4jmsqr0yy99m9er9646zlxunf3v8qr0hncgv86e8a62vxy0qa32qzetmj8s57yudmyx9zav6f52nurclsqjkqtjtpz6vg679p6wkczpl2wu", network: .testnet) //TODO: Parameterize this from environment
|
||||||
|
|
||||||
let expectedSpendingKey = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-test1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6lk8xce3d4jw7s8ln5yjp6fqv2g0nzue2hc0kv5t004vklvlenncscq9flwh5vf5qnv0hnync72n7gjn70u47765v3kyrxytx50g730svvmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqe49swv")
|
let expectedSpendingKey = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-test1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6lk8xce3d4jw7s8ln5yjp6fqv2g0nzue2hc0kv5t004vklvlenncscq9flwh5vf5qnv0hnync72n7gjn70u47765v3kyrxytx50g730svvmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqe49swv")
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class TestCoordinator {
|
||||||
case url(urlString: String, startHeigth: BlockHeight)
|
case url(urlString: String, startHeigth: BlockHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
var completionHandler: ((SDKSynchronizer) -> Void)?
|
var completionHandler: ((SDKSynchronizer) throws -> Void)?
|
||||||
var errorHandler: ((Error?) -> Void)?
|
var errorHandler: ((Error?) -> Void)?
|
||||||
var spendingKey: SaplingExtendedSpendingKey
|
var spendingKey: SaplingExtendedSpendingKey
|
||||||
var birthday: BlockHeight
|
var birthday: BlockHeight
|
||||||
|
@ -157,7 +157,7 @@ class TestCoordinator {
|
||||||
try service.applyStaged(nextLatestHeight: height)
|
try service.applyStaged(nextLatestHeight: height)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sync(completion: @escaping (SDKSynchronizer) -> Void, error: @escaping (Error?) -> Void) throws {
|
func sync(completion: @escaping (SDKSynchronizer) throws -> Void, error: @escaping (Error?) -> Void) throws {
|
||||||
self.completionHandler = completion
|
self.completionHandler = completion
|
||||||
self.errorHandler = error
|
self.errorHandler = error
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ class TestCoordinator {
|
||||||
LoggerProxy.debug("WARNING: notification received after synchronizer was stopped")
|
LoggerProxy.debug("WARNING: notification received after synchronizer was stopped")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.completionHandler?(self.synchronizer)
|
try? self.completionHandler?(self.synchronizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func synchronizerDisconnected(_ notification: Notification) {
|
@objc func synchronizerDisconnected(_ notification: Notification) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'ZcashLightClientKit'
|
s.name = 'ZcashLightClientKit'
|
||||||
s.version = '0.16.9-beta'
|
s.version = '0.16.10-beta'
|
||||||
s.summary = 'Zcash Light Client wallet SDK for iOS'
|
s.summary = 'Zcash Light Client wallet SDK for iOS'
|
||||||
|
|
||||||
s.description = <<-DESC
|
s.description = <<-DESC
|
||||||
|
|
22
changelog.md
22
changelog.md
|
@ -1,3 +1,25 @@
|
||||||
|
# 0.16.10-beta
|
||||||
|
- [#532] [0.16.x-beta] Download does not stop correctly
|
||||||
|
|
||||||
|
Issue Reported:
|
||||||
|
|
||||||
|
When the synchronizer is stopped, the processor does not cancel
|
||||||
|
the download correctly. Then when attempting to resume sync, the
|
||||||
|
synchronizer is not on `.stopped` and can't be resumed
|
||||||
|
|
||||||
|
this doesn't appear to happen in `master` branch that uses
|
||||||
|
structured concurrency for operations.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
This commit makes sure that the download streamer checks cancelation
|
||||||
|
before processing any block, or getting called back to report progress
|
||||||
|
|
||||||
|
Checkpoints added:
|
||||||
|
Mainnet
|
||||||
|
````
|
||||||
|
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1807500.json
|
||||||
|
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1810000.json
|
||||||
|
````
|
||||||
# 0.16.9-beta
|
# 0.16.9-beta
|
||||||
Checkpoints added:
|
Checkpoints added:
|
||||||
Mainnet
|
Mainnet
|
||||||
|
|
Loading…
Reference in New Issue