ZcashLightClientKit/Tests/DarksideTests/PendingTransactionUpdatesTe...

240 lines
8.6 KiB
Swift
Raw Normal View History

//
// PendingTransactionUpdatesTest.swift
// ZcashLightClientKit-Unit-Tests
//
// Created by Francisco Gindre on 7/17/20.
//
import XCTest
2022-02-28 09:03:20 -08:00
@testable import TestUtils
@testable import ZcashLightClientKit
2021-09-23 06:26:41 -07:00
// swiftlint:disable implicitly_unwrapped_optional
class PendingTransactionUpdatesTest: XCTestCase {
2021-09-23 06:26:41 -07:00
// 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: Int64 = 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
2021-09-23 06:26:41 -07:00
var reorgExpectation = XCTestExpectation(description: "reorg")
2021-05-18 14:22:29 -07:00
let branchID = "2bb40e60"
let chainName = "main"
2021-07-28 09:59:10 -07:00
let network = DarksideWalletDNetwork()
override func setUpWithError() throws {
2021-09-23 06:26:41 -07:00
try super.setUpWithError()
coordinator = try TestCoordinator(
seed: seedPhrase,
walletBirthday: birthday,
2021-07-28 09:59:10 -07:00
channelProvider: ChannelProvider(),
network: network
)
2021-05-18 14:22:29 -07:00
try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main")
}
override func tearDownWithError() throws {
2021-09-23 06:26:41 -07:00
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) {
2021-09-23 06:26:41 -07:00
guard
let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight
else {
XCTFail("empty reorg notification")
return
}
XCTAssertEqual(reorgHeight, expectedReorgHeight)
reorgExpectation.fulfill()
}
func testPendingTransactionMinedHeightUpdated() throws {
/*
2021-09-23 06:26:41 -07:00
1. create fake chain
*/
LoggerProxy.info("1. create fake chain")
2021-05-18 14:22:29 -07:00
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
try coordinator.applyStaged(blockheight: 663188)
sleep(2)
let firstSyncExpectation = XCTestExpectation(description: "first sync")
2021-09-23 06:26:41 -07:00
/*
2021-09-23 06:26:41 -07:00
1a. sync to latest height
*/
LoggerProxy.info("1a. sync to latest height")
2021-09-23 06:26:41 -07:00
try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill()
}, error: self.handleError)
wait(for: [firstSyncExpectation], timeout: 5)
sleep(1)
let sendExpectation = XCTestExpectation(description: "send expectation")
2021-09-23 06:26:41 -07:00
var pendingEntity: PendingTransactionEntity?
/*
2021-09-23 06:26:41 -07:00
2. send transaction to recipient address
*/
LoggerProxy.info("2. send transaction to recipient address")
2021-09-23 06:26:41 -07:00
coordinator.synchronizer.sendToAddress(
// swiftlint:disable:next force_unwrapping
spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: Zatoshi(20000),
[#461] Adopt a Type-Safe Keys and Addresses API This PR creates data types for Addresses and Keys so that they are not represented by Strings anymore. This avoids mistakenly use the wrong keys because they are all alike for the type system. New Protocols: ============= StringEncoded -> Protocol that makes a type can be expressed in an string-encoded fashion either for UI or Interchange purposes. Undescribable -> A protocol that implements methods that override default decriptions used by debuggers, loggers and event trackers to avoid types conforming to it to be leaked to logs. Deleted Protocols: ================== UnifiedFullViewingKey --> turned into a struct. UnifiedAddress --> turned into a struct new Error Type: ================ ```` enum KeyEncodingError: Error { case invalidEncoding } ```` This error is thrown when an Address or Key type (addresses are public keys in the end) can be decoded from their String representation, typically upon initialization from a User input. New Types: ========= SaplingExtendedSpendingKey -> Type for Sapling Extended Full Viewing Keys this type will be replaced with Unified Spending Keys soon. SaplingExtendedFullViewingKey -> Extended Full Viewing Key for Sapling. Maintains existing funcionality. Will be probably deprecated in favor of UFVK. TransparentAccountPrivKey -> Private key for transparent account. Used only for shielding operations. Note: this will probably be deprecated soon. UnifiedFullViewingKey -> Replaces the protocol that had the same name. TransparentAddress -> Replaces a type alias with a struct SaplingAddress --> Represents a Sapling receiver address. Comonly called zAddress. This address corresponds to the Zcash Sapling shielded pool. Although this it is fully functional, we encourage developers to choose `UnifiedAddress` before Sapling or Transparent ones. UnifiedAddress -> Represents a UA. String-encodable and Equatable. Use of UAs must be favored instead of individual receivers for different pools. This type can't be decomposed into their Receiver types yet. Recipient -> This represents all valid receiver types to be used as inputs for outgoing transactions. ```` public enum Recipient: Equatable, StringEncoded { case transparent(TransparentAddress) case sapling(SaplingAddress) case unified(UnifiedAddress) ```` The wrapped concrete receiver is a valid receiver type. Deleted Type Aliases: ===================== The following aliases were deleted and turned into types ```` public typealias TransparentAddress = String public typealias SaplingShieldedAddress = String ```` Changes to Derivation Tool ========================== DerivationTool has been changed to accomodate this new types and remove Strings whenever possible. Changes to Synchronizer and CompactBlockProcessor ================================================= Accordingly these to components have been modified to accept the new types intead of strings when possible. Changes to Demo App =================== The demo App has been patch to compile and work with the new types. Developers must consider that the use (and abuse) of forced_try and forced unwrapping is a "license" that maintainers are using for the sake of brevity. We consider that clients of this SDK do know how to handle Errors and Optional and it is not the objective of the demo code to show good practices on those matters. Closes #461
2022-08-20 15:10:22 -07:00
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: try Memo(string: "this is a test"),
2021-09-23 06:26:41 -07:00
from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
}
2021-09-23 06:26:41 -07:00
)
wait(for: [sendExpectation], timeout: 11)
2021-09-23 06:26:41 -07:00
guard let pendingUnconfirmedTx = pendingEntity else {
XCTFail("no pending transaction after sending")
try coordinator.stop()
return
}
2021-09-23 06:26:41 -07:00
XCTAssertFalse(
pendingUnconfirmedTx.isConfirmed(currentHeight: 663188),
"pending transaction evaluated as confirmed when it shouldn't"
)
XCTAssertFalse(
pendingUnconfirmedTx.isMined,
"pending transaction evaluated as mined when it shouldn't"
)
2021-09-23 06:26:41 -07:00
XCTAssertTrue(
pendingUnconfirmedTx.isPending(currentHeight: 663188),
"pending transaction evaluated as not pending when it should be"
)
/**
2021-09-23 06:26:41 -07:00
3. getIncomingTransaction
*/
LoggerProxy.info("3. getIncomingTransaction")
guard let incomingTx = try coordinator.getIncomingTransactions()?.first else {
XCTFail("no incoming transaction")
try coordinator.stop()
return
}
let sentTxHeight: BlockHeight = 663189
/*
2021-09-23 06:26:41 -07:00
4. stage transaction at sentTxHeight
*/
LoggerProxy.info("4. stage transaction at \(sentTxHeight)")
try coordinator.stageBlockCreate(height: sentTxHeight)
try coordinator.stageTransaction(incomingTx, at: sentTxHeight)
2021-09-23 06:26:41 -07:00
/*
2021-09-23 06:26:41 -07:00
5. applyHeight(sentTxHeight)
*/
LoggerProxy.info("5. applyHeight(\(sentTxHeight))")
try coordinator.applyStaged(blockheight: sentTxHeight)
sleep(2)
/*
2021-09-23 06:26:41 -07:00
6. sync to latest height
*/
LoggerProxy.info("6. sync to latest height")
2021-09-23 06:26:41 -07:00
let secondSyncExpectation = XCTestExpectation(description: "after send expectation")
2021-09-23 06:26:41 -07:00
try coordinator.sync(
completion: { _ in
secondSyncExpectation.fulfill()
},
error: self.handleError
)
wait(for: [secondSyncExpectation], timeout: 5)
XCTAssertEqual(coordinator.synchronizer.pendingTransactions.count, 1)
guard let afterStagePendingTx = coordinator.synchronizer.pendingTransactions.first else {
return
}
/*
2021-09-23 06:26:41 -07:00
6a. verify that there's a pending transaction with a mined height of sentTxHeight
*/
LoggerProxy.info("6a. verify that there's a pending transaction with a mined height of \(sentTxHeight)")
XCTAssertEqual(afterStagePendingTx.minedHeight, sentTxHeight)
XCTAssertTrue(afterStagePendingTx.isMined, "pending transaction shown as unmined when it has been mined")
XCTAssertTrue(afterStagePendingTx.isPending(currentHeight: sentTxHeight))
/*
2021-09-23 06:26:41 -07:00
7. stage 15 blocks from sentTxHeight
*/
LoggerProxy.info("7. stage 15 blocks from \(sentTxHeight)")
try coordinator.stageBlockCreate(height: sentTxHeight + 1, count: 15)
sleep(2)
let lastStageHeight = sentTxHeight + 14
LoggerProxy.info("applyStaged(\(lastStageHeight))")
try coordinator.applyStaged(blockheight: lastStageHeight)
sleep(2)
let syncToConfirmExpectation = XCTestExpectation(description: "sync to confirm expectation")
/*
8. last sync to latest height
*/
LoggerProxy.info("last sync to latest height: \(lastStageHeight)")
2021-09-23 06:26:41 -07:00
try coordinator.sync(completion: { _ in
syncToConfirmExpectation.fulfill()
}, error: self.handleError)
wait(for: [syncToConfirmExpectation], timeout: 6)
2021-09-23 06:26:41 -07:00
var supposedlyPendingUnexistingTransaction: PendingTransactionEntity?
XCTAssertNoThrow(try { supposedlyPendingUnexistingTransaction = try coordinator.synchronizer.allPendingTransactions().first }())
XCTAssertNil(supposedlyPendingUnexistingTransaction)
}
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() {
2021-09-23 06:26:41 -07:00
NotificationCenter.default.addObserver(
self,
selector: #selector(handleReorg(_:)),
name: .blockProcessorHandledReOrg,
object: nil
)
}
}