2020-06-03 16:18:57 -07:00
|
|
|
|
//
|
|
|
|
|
// BalanceTests.swift
|
|
|
|
|
// ZcashLightClientKit-Unit-Tests
|
|
|
|
|
//
|
|
|
|
|
// Created by Francisco Gindre on 4/28/20.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import XCTest
|
2022-02-28 09:03:20 -08:00
|
|
|
|
@testable import TestUtils
|
2020-06-03 16:18:57 -07:00
|
|
|
|
@testable import ZcashLightClientKit
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
|
|
|
|
// swiftlint:disable type_body_length implicitly_unwrapped_optional force_unwrapping file_length
|
2020-06-03 16:18:57 -07:00
|
|
|
|
class BalanceTests: XCTestCase {
|
2021-09-23 06:26:41 -07:00
|
|
|
|
// TODO: Parameterize this from environment?
|
|
|
|
|
// 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"
|
|
|
|
|
|
|
|
|
|
// TODO: Parameterize this from environment
|
|
|
|
|
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a"
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let sendAmount = Zatoshi(1000)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
let defaultLatestHeight: BlockHeight = 663188
|
2021-05-18 14:22:29 -07:00
|
|
|
|
let branchID = "2bb40e60"
|
|
|
|
|
let chainName = "main"
|
2021-07-28 09:59:10 -07:00
|
|
|
|
let network: ZcashNetwork = DarksideWalletDNetwork()
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
|
|
|
|
var birthday: BlockHeight = 663150
|
|
|
|
|
var sentTransactionExpectation = XCTestExpectation(description: "sent")
|
|
|
|
|
var syncedExpectation = XCTestExpectation(description: "synced")
|
|
|
|
|
var coordinator: TestCoordinator!
|
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
override func setUpWithError() throws {
|
2021-09-23 06:26:41 -07:00
|
|
|
|
try super.setUpWithError()
|
2020-06-03 16:18:57 -07:00
|
|
|
|
coordinator = try TestCoordinator(
|
|
|
|
|
seed: seedPhrase,
|
|
|
|
|
walletBirthday: birthday,
|
2021-07-28 09:59:10 -07:00
|
|
|
|
channelProvider: ChannelProvider(),
|
|
|
|
|
network: network
|
2020-06-03 16:18:57 -07:00
|
|
|
|
)
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main")
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
verify that when sending the maximum amount, the transactions are broadcasted properly
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testMaxAmountSend() async throws {
|
2021-03-25 10:43:20 -07:00
|
|
|
|
let notificationHandler = SDKSynchonizerListener()
|
|
|
|
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
|
|
|
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
|
|
|
|
|
|
|
|
|
// 0 subscribe to updated transactions events
|
|
|
|
|
notificationHandler.subscribeToSynchronizer(coordinator.synchronizer)
|
|
|
|
|
// 1 sync and get spendable funds
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight + 10)
|
|
|
|
|
|
|
|
|
|
sleep(1)
|
|
|
|
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
firstSyncExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [firstSyncExpectation], timeout: 12)
|
|
|
|
|
// 2 check that there are no unconfirmed funds
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
let totalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
2021-07-28 09:59:10 -07:00
|
|
|
|
XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: defaultLatestHeight))
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertEqual(verifiedBalance, totalBalance)
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let maxBalance = verifiedBalance - network.constants.defaultFee(for: defaultLatestHeight)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
// 3 create a transaction for the max amount possible
|
|
|
|
|
// 4 send the transaction
|
|
|
|
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("failed to create spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
2022-09-13 03:19:56 -07:00
|
|
|
|
do {
|
|
|
|
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
|
zatoshi: maxBalance,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: try Memo(string: "this is a test")
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = transaction
|
|
|
|
|
self.sentTransactionExpectation.fulfill()
|
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("sendToAddress failed: \(error)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
|
|
|
|
guard let pendingTx = pendingTx else {
|
|
|
|
|
XCTFail("transaction creation failed")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
|
|
|
|
XCTAssertNotNil(transaction.rawTransactionId)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertNotNil(pendingTx.rawTransactionId)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
transactionMinedExpectation.fulfill()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5 apply to height
|
|
|
|
|
// 6 mine the block
|
|
|
|
|
guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
|
|
|
|
|
XCTFail("no incoming transaction after")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let latestHeight = try coordinator.latestHeight()
|
|
|
|
|
let sentTxHeight = latestHeight + 1
|
|
|
|
|
|
|
|
|
|
notificationHandler.transactionsFound = { txs in
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let foundTx = txs.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertNotNil(foundTx)
|
|
|
|
|
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
|
|
|
|
|
|
|
|
|
|
foundTransactionsExpectation.fulfill()
|
|
|
|
|
}
|
|
|
|
|
try coordinator.stageBlockCreate(height: sentTxHeight, count: 100)
|
|
|
|
|
sleep(1)
|
|
|
|
|
try coordinator.stageTransaction(rawTx, at: sentTxHeight)
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight)
|
|
|
|
|
sleep(2) // add enhance breakpoint here
|
|
|
|
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(
|
|
|
|
|
completion: { synchronizer in
|
|
|
|
|
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
|
|
|
|
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
|
|
|
|
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
|
|
|
|
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
|
|
|
|
mineExpectation.fulfill()
|
|
|
|
|
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)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
// 7 advance to confirmation
|
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight + 10)
|
|
|
|
|
|
|
|
|
|
sleep(2)
|
|
|
|
|
|
|
|
|
|
let confirmExpectation = XCTestExpectation(description: "confirm expectation")
|
|
|
|
|
notificationHandler.transactionsFound = { txs in
|
|
|
|
|
XCTFail("We shouldn't find any transactions at this point but found \(txs)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
|
|
|
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
confirmExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
wait(for: [confirmExpectation], timeout: 5)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let confirmedPending = try coordinator.synchronizer.allPendingTransactions()
|
|
|
|
|
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), .zero)
|
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), .zero)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
verify that when sending the maximum amount minus one zatoshi, the transactions are broadcasted properly
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testMaxAmountMinusOneSend() async throws {
|
2021-03-25 10:43:20 -07:00
|
|
|
|
let notificationHandler = SDKSynchonizerListener()
|
|
|
|
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
|
|
|
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
|
|
|
|
|
|
|
|
|
// 0 subscribe to updated transactions events
|
|
|
|
|
notificationHandler.subscribeToSynchronizer(coordinator.synchronizer)
|
|
|
|
|
// 1 sync and get spendable funds
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight + 10)
|
|
|
|
|
|
|
|
|
|
sleep(1)
|
|
|
|
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
firstSyncExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [firstSyncExpectation], timeout: 12)
|
|
|
|
|
// 2 check that there are no unconfirmed funds
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
let totalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
2021-07-28 09:59:10 -07:00
|
|
|
|
XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: defaultLatestHeight))
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertEqual(verifiedBalance, totalBalance)
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let maxBalanceMinusOne = verifiedBalance - network.constants.defaultFee(for: defaultLatestHeight) - Zatoshi(1)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
// 3 create a transaction for the max amount possible
|
|
|
|
|
// 4 send the transaction
|
|
|
|
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("failed to create spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
2022-09-13 03:19:56 -07:00
|
|
|
|
do {
|
|
|
|
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
|
zatoshi: maxBalanceMinusOne,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: try Memo(string: "\(self.description) \(Date().description)")
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = transaction
|
|
|
|
|
self.sentTransactionExpectation.fulfill()
|
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("sendToAddress failed: \(error)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
|
|
|
|
guard let pendingTx = pendingTx else {
|
|
|
|
|
XCTFail("transaction creation failed")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
|
|
|
|
XCTAssertNotNil(transaction.rawTransactionId)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertNotNil(pendingTx.rawTransactionId)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
transactionMinedExpectation.fulfill()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5 apply to height
|
|
|
|
|
// 6 mine the block
|
|
|
|
|
guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
|
|
|
|
|
XCTFail("no incoming transaction after")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let latestHeight = try coordinator.latestHeight()
|
|
|
|
|
let sentTxHeight = latestHeight + 1
|
|
|
|
|
|
|
|
|
|
notificationHandler.transactionsFound = { txs in
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let foundTx = txs.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertNotNil(foundTx)
|
|
|
|
|
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
|
|
|
|
|
|
|
|
|
|
foundTransactionsExpectation.fulfill()
|
|
|
|
|
}
|
|
|
|
|
try coordinator.stageBlockCreate(height: sentTxHeight, count: 100)
|
|
|
|
|
sleep(1)
|
|
|
|
|
try coordinator.stageTransaction(rawTx, at: sentTxHeight)
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight)
|
|
|
|
|
sleep(2) // add enhance breakpoint here
|
|
|
|
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(
|
|
|
|
|
completion: { synchronizer in
|
|
|
|
|
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
|
|
|
|
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
|
|
|
|
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
|
|
|
|
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
|
|
|
|
mineExpectation.fulfill()
|
|
|
|
|
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)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
// 7 advance to confirmation
|
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight + 10)
|
|
|
|
|
|
|
|
|
|
sleep(2)
|
|
|
|
|
|
|
|
|
|
let confirmExpectation = XCTestExpectation(description: "confirm expectation")
|
|
|
|
|
notificationHandler.transactionsFound = { txs in
|
|
|
|
|
XCTFail("We shouldn't find any transactions at this point but found \(txs)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
|
|
|
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
confirmExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [confirmExpectation], timeout: 5)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let confirmedPending = try coordinator.synchronizer
|
|
|
|
|
.allPendingTransactions()
|
|
|
|
|
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), Zatoshi(1))
|
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), Zatoshi(1))
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
verify that when sending the a no change transaction, the transactions are broadcasted properly
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testSingleNoteNoChangeTransaction() async throws {
|
2021-03-25 10:43:20 -07:00
|
|
|
|
let notificationHandler = SDKSynchonizerListener()
|
|
|
|
|
let foundTransactionsExpectation = XCTestExpectation(description: "found transactions expectation")
|
|
|
|
|
let transactionMinedExpectation = XCTestExpectation(description: "transaction mined expectation")
|
|
|
|
|
|
|
|
|
|
// 0 subscribe to updated transactions events
|
|
|
|
|
notificationHandler.subscribeToSynchronizer(coordinator.synchronizer)
|
|
|
|
|
// 1 sync and get spendable funds
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight + 10)
|
|
|
|
|
|
|
|
|
|
sleep(1)
|
|
|
|
|
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
firstSyncExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
wait(for: [firstSyncExpectation], timeout: 12)
|
|
|
|
|
// 2 check that there are no unconfirmed funds
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let verifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
let totalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
2021-07-28 09:59:10 -07:00
|
|
|
|
XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: defaultLatestHeight))
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertEqual(verifiedBalance, totalBalance)
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let maxBalanceMinusOne = Zatoshi(100000) - network.constants.defaultFee(for: defaultLatestHeight)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
// 3 create a transaction for the max amount possible
|
|
|
|
|
// 4 send the transaction
|
|
|
|
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("failed to create spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
2022-09-13 03:19:56 -07:00
|
|
|
|
do {
|
|
|
|
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
|
zatoshi: maxBalanceMinusOne,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: try Memo(string: "test send \(self.description) \(Date().description)")
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = transaction
|
|
|
|
|
self.sentTransactionExpectation.fulfill()
|
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("sendToAddress failed: \(error)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2021-03-25 10:43:20 -07:00
|
|
|
|
wait(for: [sentTransactionExpectation], timeout: 20)
|
|
|
|
|
guard let pendingTx = pendingTx else {
|
|
|
|
|
XCTFail("transaction creation failed")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
|
|
|
|
XCTAssertNotNil(transaction.rawTransactionId)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertNotNil(pendingTx.rawTransactionId)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
transactionMinedExpectation.fulfill()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5 apply to height
|
|
|
|
|
// 6 mine the block
|
|
|
|
|
guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
|
|
|
|
|
XCTFail("no incoming transaction after")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let latestHeight = try coordinator.latestHeight()
|
|
|
|
|
let sentTxHeight = latestHeight + 1
|
|
|
|
|
|
|
|
|
|
notificationHandler.transactionsFound = { txs in
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let foundTx = txs.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTAssertNotNil(foundTx)
|
|
|
|
|
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
|
|
|
|
|
|
|
|
|
|
foundTransactionsExpectation.fulfill()
|
|
|
|
|
}
|
|
|
|
|
try coordinator.stageBlockCreate(height: sentTxHeight, count: 100)
|
|
|
|
|
sleep(1)
|
|
|
|
|
try coordinator.stageTransaction(rawTx, at: sentTxHeight)
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight)
|
|
|
|
|
sleep(2) // add enhance breakpoint here
|
|
|
|
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(
|
|
|
|
|
completion: { synchronizer in
|
|
|
|
|
let pendingEntity = synchronizer.pendingTransactions.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
|
|
|
|
XCTAssertNotNil(pendingEntity, "pending transaction should have been mined by now")
|
|
|
|
|
XCTAssertTrue(pendingEntity?.isMined ?? false)
|
|
|
|
|
XCTAssertEqual(pendingEntity?.minedHeight, sentTxHeight)
|
|
|
|
|
mineExpectation.fulfill()
|
|
|
|
|
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)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
// 7 advance to confirmation
|
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight + 10)
|
|
|
|
|
|
|
|
|
|
sleep(2)
|
|
|
|
|
|
|
|
|
|
let confirmExpectation = XCTestExpectation(description: "confirm expectation")
|
|
|
|
|
notificationHandler.transactionsFound = { txs in
|
|
|
|
|
XCTFail("We shouldn't find any transactions at this point but found \(txs)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
notificationHandler.synchronizerMinedTransaction = { transaction in
|
|
|
|
|
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
confirmExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [confirmExpectation], timeout: 5)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let confirmedPending = try coordinator.synchronizer
|
|
|
|
|
.allPendingTransactions()
|
|
|
|
|
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), Zatoshi(100000))
|
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), Zatoshi(100000))
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Verify available balance is correct in all wallet states during a send
|
|
|
|
|
|
|
|
|
|
This can be either a Wallet test or a Synchronizer test. The latter is supposed to be simpler because it involves no UI testing whatsoever.
|
|
|
|
|
|
|
|
|
|
Precondition:
|
|
|
|
|
Account has spendable funds
|
|
|
|
|
Librustzcash is ‘synced’ up to ‘current tip’
|
|
|
|
|
|
|
|
|
|
Action:
|
|
|
|
|
Send Amount(*) to zAddr
|
|
|
|
|
|
|
|
|
|
Success per state:
|
|
|
|
|
Sent: (previous available funds - spent note + change) equals to (previous available funds - sent amount)
|
|
|
|
|
Error: previous available funds equals to current funds
|
|
|
|
|
*/
|
|
|
|
|
// swiftlint:disable cyclomatic_complexity
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testVerifyAvailableBalanceDuringSend() async throws {
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
self.syncedExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [syncedExpectation], timeout: 60)
|
|
|
|
|
|
|
|
|
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("failed to create spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let presendVerifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
there's more zatoshi to send than network fee
|
|
|
|
|
*/
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertTrue(presendVerifiedBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
2022-09-13 03:19:56 -07:00
|
|
|
|
do {
|
|
|
|
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
|
zatoshi: sendAmount,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: try Memo(string: "this is a test")
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = transaction
|
|
|
|
|
self.sentTransactionExpectation.fulfill()
|
|
|
|
|
} catch {
|
|
|
|
|
/*
|
|
|
|
|
balance should be the same as before sending if transaction failed
|
|
|
|
|
*/
|
|
|
|
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), presendVerifiedBalance)
|
|
|
|
|
XCTFail("sendToAddress failed: \(error)")
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertTrue(coordinator.synchronizer.initializer.getVerifiedBalance() > .zero)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
wait(for: [sentTransactionExpectation], timeout: 12)
|
|
|
|
|
|
|
|
|
|
// sync and mine
|
|
|
|
|
|
|
|
|
|
guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
|
|
|
|
|
XCTFail("no incoming transaction after")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
let latestHeight = try coordinator.latestHeight()
|
|
|
|
|
let sentTxHeight = latestHeight + 1
|
|
|
|
|
try coordinator.stageBlockCreate(height: sentTxHeight)
|
|
|
|
|
|
|
|
|
|
try coordinator.stageTransaction(rawTx, at: sentTxHeight)
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight)
|
|
|
|
|
sleep(1)
|
|
|
|
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(
|
|
|
|
|
completion: { synchronizer in
|
|
|
|
|
mineExpectation.fulfill()
|
|
|
|
|
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)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [mineExpectation], timeout: 5)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
presendVerifiedBalance - self.sendAmount - network.constants.defaultFee(for: defaultLatestHeight),
|
|
|
|
|
coordinator.synchronizer.initializer.getBalance()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
presendVerifiedBalance - self.sendAmount - network.constants.defaultFee(for: defaultLatestHeight),
|
|
|
|
|
coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
guard let transaction = pendingTx else {
|
|
|
|
|
XCTFail("pending transaction nil")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
basic health check
|
|
|
|
|
*/
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(transaction.value, self.sendAmount)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
build up repos to get data
|
|
|
|
|
*/
|
2020-06-03 16:18:57 -07:00
|
|
|
|
guard let txid = transaction.rawTransactionId else {
|
|
|
|
|
XCTFail("sent transaction has no internal id")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
|
|
|
|
let sentNoteDAO = SentNotesSQLDAO(
|
|
|
|
|
dbProvider: SimpleConnectionProvider(
|
|
|
|
|
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
|
|
|
|
|
readonly: true
|
|
|
|
|
)
|
|
|
|
|
)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let receivedNoteDAO = ReceivedNotesSQLDAO(
|
|
|
|
|
dbProvider: SimpleConnectionProvider(
|
|
|
|
|
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
|
|
|
|
|
readonly: true
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
var sentEntity: SentNoteEntity?
|
2020-06-03 16:18:57 -07:00
|
|
|
|
do {
|
2021-09-23 06:26:41 -07:00
|
|
|
|
sentEntity = try sentNoteDAO.sentNote(byRawTransactionId: txid)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("error retrieving sent note: \(error)")
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
guard let sentNote = sentEntity else {
|
2020-06-03 16:18:57 -07:00
|
|
|
|
XCTFail("could not find sent note for this transaction")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
|
|
|
|
var receivedEntity: ReceivedNoteEntity?
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
do {
|
2021-09-23 06:26:41 -07:00
|
|
|
|
receivedEntity = try receivedNoteDAO.receivedNote(byRawTransactionId: txid)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("error retrieving received note: \(error)")
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
guard let receivedNote = receivedEntity else {
|
2020-06-03 16:18:57 -07:00
|
|
|
|
XCTFail("could not find sent note for this transaction")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
// (previous available funds - spent note + change) equals to (previous available funds - sent amount)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
self.verifiedBalanceValidation(
|
|
|
|
|
previousBalance: presendVerifiedBalance,
|
2022-06-22 12:45:37 -07:00
|
|
|
|
spentNoteValue: Zatoshi(Int64(sentNote.value)),
|
|
|
|
|
changeValue: Zatoshi(Int64(receivedNote.value)),
|
|
|
|
|
sentAmount: self.sendAmount,
|
2021-09-23 06:26:41 -07:00
|
|
|
|
currentVerifiedBalance: self.coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Verify total balance in all wallet states during a send
|
|
|
|
|
This can be either a Wallet test or a Synchronizer test. The latter is supposed to be simpler because it involves no UI testing whatsoever.
|
|
|
|
|
|
|
|
|
|
Precondition:
|
|
|
|
|
Account has spendable funds
|
|
|
|
|
Librustzcash is ‘synced’ up to ‘current tip’
|
|
|
|
|
|
|
|
|
|
Action:
|
|
|
|
|
Send Amount to zAddr
|
|
|
|
|
|
|
|
|
|
Success per state:
|
|
|
|
|
Sent: (total balance funds - sentAmount) equals to (previous available funds - sent amount)
|
|
|
|
|
Error: previous total balance funds equals to current total balance
|
|
|
|
|
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testVerifyTotalBalanceDuringSend() async throws {
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2020-06-04 14:36:25 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
|
|
|
|
|
|
|
|
|
sleep(2)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
self.syncedExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [syncedExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("failed to create spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let presendBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
|
|
|
|
// there's more zatoshi to send than network fee
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertTrue(presendBalance >= network.constants.defaultFee(for: defaultLatestHeight) + sendAmount)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
var testError: Error?
|
|
|
|
|
do {
|
|
|
|
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
|
zatoshi: sendAmount,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: try Memo(string: "test send \(self.description) \(Date().description)")
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = transaction
|
|
|
|
|
self.sentTransactionExpectation.fulfill()
|
|
|
|
|
} catch {
|
|
|
|
|
// balance should be the same as before sending if transaction failed
|
|
|
|
|
testError = error
|
|
|
|
|
XCTFail("sendToAddress failed: \(error)")
|
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertTrue(coordinator.synchronizer.initializer.getVerifiedBalance() > .zero)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
wait(for: [sentTransactionExpectation], timeout: 12)
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
if let testError {
|
2020-06-03 16:18:57 -07:00
|
|
|
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), presendBalance)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
XCTFail("error: \(testError)")
|
2020-06-03 16:18:57 -07:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard let transaction = pendingTx else {
|
|
|
|
|
XCTFail("pending transaction nil after send")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(transaction.value, self.sendAmount)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
self.coordinator.synchronizer.initializer.getBalance(),
|
2022-06-22 12:45:37 -07:00
|
|
|
|
presendBalance - self.sendAmount - network.constants.defaultFee(for: defaultLatestHeight)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
)
|
2020-06-04 14:36:25 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
XCTAssertNil(transaction.errorCode)
|
|
|
|
|
|
|
|
|
|
let latestHeight = try coordinator.latestHeight()
|
|
|
|
|
let sentTxHeight = latestHeight + 1
|
|
|
|
|
try coordinator.stageBlockCreate(height: sentTxHeight)
|
|
|
|
|
guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
|
|
|
|
|
XCTFail("no incoming transaction after send")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
try coordinator.stageTransaction(rawTx, at: latestHeight + 1)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
try coordinator.applyStaged(blockheight: latestHeight + 1)
|
|
|
|
|
sleep(2)
|
|
|
|
|
let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(
|
|
|
|
|
completion: { synchronizer in
|
|
|
|
|
mineExpectation.fulfill()
|
|
|
|
|
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)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
2022-09-13 03:19:56 -07:00
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [mineExpectation], timeout: 5)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
XCTAssertEqual(
|
2022-06-22 12:45:37 -07:00
|
|
|
|
presendBalance - self.sendAmount - network.constants.defaultFee(for: defaultLatestHeight),
|
2021-09-23 06:26:41 -07:00
|
|
|
|
coordinator.synchronizer.initializer.getBalance()
|
|
|
|
|
)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Verify incoming transactions
|
|
|
|
|
This can be either a Wallet test or a Synchronizer test. The latter is supposed to be simpler because it involves no UI testing whatsoever.
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Precondition:
|
|
|
|
|
Librustzcash is ‘synced’ up to ‘current tip’
|
|
|
|
|
Known list of expected transactions on the block range to sync the wallet up to.
|
|
|
|
|
Known expected balance on the block range to sync the wallet up to.
|
|
|
|
|
Action:
|
|
|
|
|
sync to latest height
|
|
|
|
|
Success criteria:
|
|
|
|
|
The transaction list matches the expected one
|
|
|
|
|
Balance matches expected balance
|
|
|
|
|
|
|
|
|
|
*/
|
2020-06-03 16:18:57 -07:00
|
|
|
|
func testVerifyIncomingTransaction() throws {
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
try coordinator.sync(completion: { _ in
|
2020-06-03 16:18:57 -07:00
|
|
|
|
self.syncedExpectation.fulfill()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
|
|
|
|
|
wait(for: [syncedExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.clearedTransactions.count, 2)
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), Zatoshi(200000))
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Verify change transactions
|
|
|
|
|
|
|
|
|
|
This can be either a Wallet test or a Synchronizer test. The latter is supposed to be simpler because it involves no UI testing whatsoever.
|
|
|
|
|
|
|
|
|
|
Precondition
|
|
|
|
|
Librustzcash is ‘synced’ up to ‘current tip’
|
|
|
|
|
Known list of expected transactions on the block range to sync the wallet up to.
|
|
|
|
|
Known expected balance on the block range to sync the wallet up to.
|
|
|
|
|
There’s a spendable note with value > send amount that generates change
|
|
|
|
|
|
|
|
|
|
Action:
|
|
|
|
|
Send amount to zAddr
|
|
|
|
|
sync to minedHeight + 1
|
|
|
|
|
|
|
|
|
|
Success Criteria:
|
|
|
|
|
There’s a sent transaction matching the amount sent to the given zAddr
|
|
|
|
|
minedHeight is not -1
|
|
|
|
|
Balance meets verified Balance and total balance criteria
|
|
|
|
|
There’s a change note of value (previous note value - sent amount)
|
|
|
|
|
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testVerifyChangeTransaction() async throws {
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildSingleNoteChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: defaultLatestHeight)
|
|
|
|
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
|
|
|
|
let createToAddressExpectation = XCTestExpectation(description: "create to address")
|
|
|
|
|
|
|
|
|
|
try coordinator.setLatestHeight(height: defaultLatestHeight)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
sync to current tip
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
self.syncedExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [syncedExpectation], timeout: 6)
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let previousVerifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
let previousTotalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
guard let spendingKeys = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("null spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Send
|
|
|
|
|
*/
|
2022-05-31 05:27:24 -07:00
|
|
|
|
let memo = try Memo(string: "shielding is fun!")
|
2020-06-03 16:18:57 -07:00
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
2022-09-13 03:19:56 -07:00
|
|
|
|
do {
|
|
|
|
|
let transaction = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKeys,
|
|
|
|
|
zatoshi: sendAmount,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: memo
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = transaction
|
|
|
|
|
sendExpectation.fulfill()
|
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("error sending \(error)")
|
|
|
|
|
}
|
2021-02-15 16:56:23 -08:00
|
|
|
|
wait(for: [createToAddressExpectation], timeout: 30)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
let syncToMinedheightExpectation = XCTestExpectation(description: "sync to mined height + 1")
|
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
include sent transaction in block
|
|
|
|
|
*/
|
2020-06-03 16:18:57 -07:00
|
|
|
|
guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
|
|
|
|
|
XCTFail("pending transaction nil after send")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let latestHeight = try coordinator.latestHeight()
|
|
|
|
|
let sentTxHeight = latestHeight + 1
|
|
|
|
|
try coordinator.stageBlockCreate(height: sentTxHeight, count: 12)
|
|
|
|
|
try coordinator.stageTransaction(rawTx, at: sentTxHeight)
|
|
|
|
|
try coordinator.applyStaged(blockheight: sentTxHeight + 11 )
|
|
|
|
|
sleep(2)
|
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Sync to that block
|
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
let confirmedTx: ConfirmedTransactionEntity!
|
|
|
|
|
do {
|
|
|
|
|
confirmedTx = try synchronizer.allClearedTransactions().first(where: { confirmed -> Bool in
|
|
|
|
|
confirmed.transactionEntity.transactionId == pendingTx?.transactionEntity.transactionId
|
|
|
|
|
})
|
|
|
|
|
} catch {
|
|
|
|
|
XCTFail("Error retrieving cleared transactions")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
/*
|
|
|
|
|
There’s a sent transaction matching the amount sent to the given zAddr
|
|
|
|
|
*/
|
|
|
|
|
XCTAssertEqual(confirmedTx.value, self.sendAmount)
|
|
|
|
|
XCTAssertEqual(confirmedTx.toAddress, self.testRecipientAddress)
|
2022-10-27 16:09:08 -07:00
|
|
|
|
let confirmedMemo = try confirmedTx.memo?.intoMemoBytes()?.intoMemo()
|
2022-05-31 05:27:24 -07:00
|
|
|
|
XCTAssertEqual(confirmedMemo, memo)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
guard let transactionId = confirmedTx.rawTransactionId else {
|
|
|
|
|
XCTFail("no raw transaction id")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
/*
|
|
|
|
|
Find out what note was used
|
|
|
|
|
*/
|
|
|
|
|
let sentNotesRepo = SentNotesSQLDAO(
|
|
|
|
|
dbProvider: SimpleConnectionProvider(
|
|
|
|
|
path: synchronizer.initializer.dataDbURL.absoluteString,
|
|
|
|
|
readonly: true
|
|
|
|
|
)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
)
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
guard let sentNote = try? sentNotesRepo.sentNote(byRawTransactionId: transactionId) else {
|
|
|
|
|
XCTFail("Could not finde sent note with transaction Id \(transactionId)")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
let receivedNotesRepo = ReceivedNotesSQLDAO(
|
|
|
|
|
dbProvider: SimpleConnectionProvider(
|
|
|
|
|
path: self.coordinator.synchronizer.initializer.dataDbURL.absoluteString,
|
|
|
|
|
readonly: true
|
|
|
|
|
)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
)
|
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
/*
|
|
|
|
|
get change note
|
|
|
|
|
*/
|
|
|
|
|
guard let receivedNote = try? receivedNotesRepo.receivedNote(byRawTransactionId: transactionId) else {
|
|
|
|
|
XCTFail("Could not find received not with change for transaction Id \(transactionId)")
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
/*
|
|
|
|
|
There’s a change note of value (previous note value - sent amount)
|
|
|
|
|
*/
|
|
|
|
|
XCTAssertEqual(
|
|
|
|
|
previousVerifiedBalance - self.sendAmount - self.network.constants.defaultFee(for: self.defaultLatestHeight),
|
|
|
|
|
Zatoshi(Int64(receivedNote.value))
|
|
|
|
|
)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
/*
|
|
|
|
|
Balance meets verified Balance and total balance criteria
|
|
|
|
|
*/
|
|
|
|
|
self.verifiedBalanceValidation(
|
|
|
|
|
previousBalance: previousVerifiedBalance,
|
|
|
|
|
spentNoteValue: Zatoshi(Int64(sentNote.value)),
|
|
|
|
|
changeValue: Zatoshi(Int64(receivedNote.value)),
|
|
|
|
|
sentAmount: self.sendAmount,
|
|
|
|
|
currentVerifiedBalance: synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
self.totalBalanceValidation(
|
|
|
|
|
totalBalance: synchronizer.initializer.getBalance(),
|
|
|
|
|
previousTotalbalance: previousTotalBalance,
|
|
|
|
|
sentAmount: self.sendAmount
|
|
|
|
|
)
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
|
syncToMinedheightExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
wait(for: [syncToMinedheightExpectation], timeout: 5)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
erify transactions that expire are reflected accurately in balance
|
|
|
|
|
This test requires the transaction to expire.
|
|
|
|
|
|
|
|
|
|
How can we mock or cause this? Would createToAddress and faking a network submission through lightwalletService and syncing 10 more blocks work?
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Precondition:
|
|
|
|
|
Account has spendable funds
|
|
|
|
|
Librustzcash is ‘synced’ up to ‘current tip’ †
|
|
|
|
|
Current tip can be scanned 10 blocks past the generated to be expired transaction
|
|
|
|
|
|
|
|
|
|
Action:
|
|
|
|
|
Sync to current tip
|
|
|
|
|
Create transaction to zAddr
|
|
|
|
|
Mock send success
|
|
|
|
|
Sync 10 blocks more
|
|
|
|
|
|
|
|
|
|
Success Criteria:
|
|
|
|
|
There’s a pending transaction that has expired
|
|
|
|
|
Total Balance is equal to total balance previously shown before sending the expired transaction
|
|
|
|
|
Verified Balance is equal to verified balance previously shown before sending the expired transaction
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
*/
|
2022-09-13 03:19:56 -07:00
|
|
|
|
func testVerifyBalanceAfterExpiredTransaction() async throws {
|
2021-05-18 14:22:29 -07:00
|
|
|
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
try coordinator.applyStaged(blockheight: self.defaultLatestHeight)
|
|
|
|
|
sleep(2)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
self.syncedExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
wait(for: [syncedExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
guard let spendingKey = coordinator.spendingKeys?.first else {
|
|
|
|
|
XCTFail("no synchronizer or spending keys")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
|
let previousVerifiedBalance: Zatoshi = coordinator.synchronizer.initializer.getVerifiedBalance()
|
|
|
|
|
let previousTotalBalance: Zatoshi = coordinator.synchronizer.initializer.getBalance()
|
2020-06-03 16:18:57 -07:00
|
|
|
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
|
|
|
|
var pendingTx: PendingTransactionEntity?
|
2022-09-13 03:19:56 -07:00
|
|
|
|
do {
|
|
|
|
|
let pending = try await coordinator.synchronizer.sendToAddress(
|
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
|
zatoshi: sendAmount,
|
2022-09-30 05:53:34 -07:00
|
|
|
|
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
2022-10-02 19:11:17 -07:00
|
|
|
|
memo: try Memo(string: "test send \(self.description)")
|
|
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
pendingTx = pending
|
|
|
|
|
} catch {
|
|
|
|
|
// balance should be the same as before sending if transaction failed
|
|
|
|
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getVerifiedBalance(), previousVerifiedBalance)
|
|
|
|
|
XCTAssertEqual(self.coordinator.synchronizer.initializer.getBalance(), previousTotalBalance)
|
|
|
|
|
XCTFail("sendToAddress failed: \(error)")
|
|
|
|
|
}
|
2021-09-23 06:26:41 -07:00
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
wait(for: [sendExpectation], timeout: 12)
|
|
|
|
|
|
|
|
|
|
guard let pendingTransaction = pendingTx, pendingTransaction.expiryHeight > defaultLatestHeight else {
|
|
|
|
|
XCTFail("No pending transaction")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let expirationSyncExpectation = XCTestExpectation(description: "expiration sync expectation")
|
|
|
|
|
let expiryHeight = pendingTransaction.expiryHeight
|
|
|
|
|
let blockCount = abs(self.defaultLatestHeight - expiryHeight)
|
2020-06-04 14:36:25 -07:00
|
|
|
|
try coordinator.stageBlockCreate(height: self.defaultLatestHeight + 1, count: blockCount)
|
|
|
|
|
try coordinator.applyStaged(blockheight: expiryHeight + 1)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
|
|
sleep(2)
|
2022-09-13 03:19:56 -07:00
|
|
|
|
try await withCheckedThrowingContinuation { continuation in
|
|
|
|
|
do {
|
|
|
|
|
try coordinator.sync(completion: { synchronizer in
|
|
|
|
|
expirationSyncExpectation.fulfill()
|
|
|
|
|
continuation.resume()
|
|
|
|
|
}, error: self.handleError)
|
|
|
|
|
} catch {
|
|
|
|
|
continuation.resume(throwing: error)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-04 14:36:25 -07:00
|
|
|
|
wait(for: [expirationSyncExpectation], timeout: 5)
|
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Verified Balance is equal to verified balance previously shown before sending the expired transaction
|
|
|
|
|
*/
|
2020-06-04 14:36:25 -07:00
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), previousVerifiedBalance)
|
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
Total Balance is equal to total balance previously shown before sending the expired transaction
|
|
|
|
|
*/
|
2020-06-04 14:36:25 -07:00
|
|
|
|
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), previousTotalBalance)
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
let pendingRepo = PendingTransactionSQLDAO(
|
|
|
|
|
dbProvider: SimpleConnectionProvider(
|
|
|
|
|
path: coordinator.synchronizer.initializer.pendingDbURL.absoluteString
|
|
|
|
|
)
|
|
|
|
|
)
|
2020-06-04 14:36:25 -07:00
|
|
|
|
|
|
|
|
|
guard let expiredPending = try? pendingRepo.find(by: pendingTransaction.id!),
|
|
|
|
|
let id = expiredPending.id else {
|
|
|
|
|
XCTFail("pending transaction not found")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
there no sent transaction displayed
|
|
|
|
|
*/
|
|
|
|
|
XCTAssertNil( try coordinator.synchronizer.allSentTransactions().first(where: { $0.id == id }))
|
|
|
|
|
|
2020-06-04 14:36:25 -07:00
|
|
|
|
/*
|
2021-09-23 06:26:41 -07:00
|
|
|
|
There’s a pending transaction that has expired
|
|
|
|
|
*/
|
2020-06-04 14:36:25 -07:00
|
|
|
|
XCTAssertEqual(expiredPending.minedHeight, -1)
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleError(_ error: Error?) {
|
|
|
|
|
guard let testError = error else {
|
|
|
|
|
XCTFail("failed with nil error")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
XCTFail("Failed with error: \(testError)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-23 06:26:41 -07:00
|
|
|
|
check if (previous available funds - spent note + change) equals to (previous available funds - sent amount)
|
|
|
|
|
*/
|
|
|
|
|
func verifiedBalanceValidation(
|
2022-06-22 12:45:37 -07:00
|
|
|
|
previousBalance: Zatoshi,
|
|
|
|
|
spentNoteValue: Zatoshi,
|
|
|
|
|
changeValue: Zatoshi,
|
|
|
|
|
sentAmount: Zatoshi,
|
|
|
|
|
currentVerifiedBalance: Zatoshi
|
2021-09-23 06:26:41 -07:00
|
|
|
|
) {
|
2020-06-03 16:18:57 -07:00
|
|
|
|
XCTAssertEqual(previousBalance - spentNoteValue + changeValue, currentVerifiedBalance - sentAmount)
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
func totalBalanceValidation(
|
2022-06-22 12:45:37 -07:00
|
|
|
|
totalBalance: Zatoshi,
|
|
|
|
|
previousTotalbalance: Zatoshi,
|
|
|
|
|
sentAmount: Zatoshi
|
2021-09-23 06:26:41 -07:00
|
|
|
|
) {
|
2022-06-22 12:45:37 -07:00
|
|
|
|
XCTAssertEqual(totalBalance, previousTotalbalance - sentAmount - network.constants.defaultFee(for: defaultLatestHeight))
|
2020-06-03 16:18:57 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
class SDKSynchonizerListener {
|
2021-09-23 06:26:41 -07:00
|
|
|
|
var transactionsFound: (([ConfirmedTransactionEntity]) -> Void)?
|
|
|
|
|
var synchronizerMinedTransaction: ((PendingTransactionEntity) -> Void)?
|
2021-03-25 10:43:20 -07:00
|
|
|
|
|
|
|
|
|
func subscribeToSynchronizer(_ synchronizer: SDKSynchronizer) {
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(txFound(_:)), name: .synchronizerFoundTransactions, object: synchronizer)
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(txMined(_:)), name: .synchronizerMinedTransaction, object: synchronizer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unsubscribe() {
|
|
|
|
|
NotificationCenter.default.removeObserver(self)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func txFound(_ notification: Notification) {
|
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
|
guard let txs = notification.userInfo?[SDKSynchronizer.NotificationKeys.foundTransactions] as? [ConfirmedTransactionEntity] else {
|
|
|
|
|
XCTFail("expected [ConfirmedTransactionEntity] array")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self?.transactionsFound?(txs)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func txMined(_ notification: Notification) {
|
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
2021-09-23 06:26:41 -07:00
|
|
|
|
guard let transaction = notification.userInfo?[SDKSynchronizer.NotificationKeys.minedTransaction] as? PendingTransactionEntity else {
|
2021-03-25 10:43:20 -07:00
|
|
|
|
XCTFail("expected transaction")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 06:26:41 -07:00
|
|
|
|
self?.synchronizerMinedTransaction?(transaction)
|
2021-03-25 10:43:20 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|