2020-04-06 08:54:31 -07:00
|
|
|
//
|
|
|
|
// ReOrgTests.swift
|
|
|
|
// ZcashLightClientKit-Unit-Tests
|
|
|
|
//
|
|
|
|
// Created by Francisco Gindre on 3/23/20.
|
|
|
|
//
|
|
|
|
|
|
|
|
import XCTest
|
|
|
|
@testable import ZcashLightClientKit
|
|
|
|
/**
|
|
|
|
basic reorg test. Scan, get a reorg and then reach latest height.
|
|
|
|
|
|
|
|
* connect to dLWD
|
|
|
|
* request latest height -> receive 663250
|
|
|
|
* download and sync blocks from 663150 to 663250
|
|
|
|
* trigger reorg by calling API (no need to pass params)**
|
|
|
|
* request latest height -> receive 663251!
|
|
|
|
* download that block
|
|
|
|
* observe that the prev hash of that block does not match the hash that we have for 663250
|
|
|
|
* rewind 10 blocks and request blocks 663241 to 663251
|
|
|
|
*/
|
|
|
|
class ReOrgTests: XCTestCase {
|
2020-06-03 16:18:57 -07:00
|
|
|
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" //TODO: Parameterize this from environment
|
|
|
|
|
|
|
|
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
|
|
|
|
var reorgExpectation: XCTestExpectation = XCTestExpectation(description: "reorg")
|
|
|
|
override func setUpWithError() throws {
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(handleReOrgNotification(_:)), name: Notification.Name.blockProcessorHandledReOrg, object: nil)
|
|
|
|
coordinator = try TestCoordinator(
|
|
|
|
seed: seedPhrase,
|
|
|
|
walletBirthday: birthday,
|
|
|
|
channelProvider: ChannelProvider()
|
|
|
|
)
|
|
|
|
try coordinator.reset(saplingActivation: birthday)
|
|
|
|
try coordinator.resetBlocks(dataset: .default)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tearDownWithError() throws {
|
|
|
|
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
|
|
|
|
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
|
|
|
|
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
|
|
|
|
}
|
2020-04-06 08:54:31 -07:00
|
|
|
let mockLatestHeight = BlockHeight(663250)
|
|
|
|
let targetLatestHeight = BlockHeight(663251)
|
|
|
|
let walletBirthday = BlockHeight(663150)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@objc func handleReOrgNotification(_ notification: Notification) {
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
reorgExpectation.fulfill()
|
2020-04-06 08:54:31 -07:00
|
|
|
guard let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
|
|
|
|
let rewindHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else {
|
|
|
|
XCTFail("malformed reorg userInfo")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
print("reorgHeight: \(reorgHeight)")
|
|
|
|
print("rewindHeight: \(rewindHeight)")
|
|
|
|
|
|
|
|
XCTAssertTrue(reorgHeight > 0)
|
|
|
|
XCTAssertNoThrow(rewindHeight > 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testBasicReOrg() throws {
|
|
|
|
let mockLatestHeight = BlockHeight(663200)
|
2020-06-03 16:18:57 -07:00
|
|
|
let targetLatestHeight = BlockHeight(663202)
|
2020-04-06 08:54:31 -07:00
|
|
|
let reOrgHeight = BlockHeight(663195)
|
2020-06-03 16:18:57 -07:00
|
|
|
let walletBirthday = WalletBirthday.birthday(with: 663150).height
|
|
|
|
|
|
|
|
try basicReOrgTest(baseDataset: .beforeReOrg,
|
|
|
|
reorgDataset: .afterSmallReorg,
|
|
|
|
firstLatestHeight: mockLatestHeight,
|
|
|
|
reorgHeight: reOrgHeight,
|
|
|
|
walletBirthday: walletBirthday,
|
|
|
|
targetHeight: targetLatestHeight)
|
2020-04-06 08:54:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func testTenPlusBlockReOrg() throws {
|
|
|
|
let mockLatestHeight = BlockHeight(663200)
|
|
|
|
let targetLatestHeight = BlockHeight(663250)
|
|
|
|
let reOrgHeight = BlockHeight(663180)
|
|
|
|
let walletBirthday = WalletBirthday.birthday(with: BlockHeight(663150)).height
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
try basicReOrgTest(baseDataset: .beforeReOrg,
|
|
|
|
reorgDataset: .afterLargeReorg,
|
|
|
|
firstLatestHeight: mockLatestHeight,
|
|
|
|
reorgHeight: reOrgHeight,
|
|
|
|
walletBirthday: walletBirthday,
|
|
|
|
targetHeight: targetLatestHeight)
|
2020-04-06 08:54:31 -07:00
|
|
|
}
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
func basicReOrgTest(baseDataset: DarksideDataset,
|
|
|
|
reorgDataset: DarksideDataset,
|
|
|
|
firstLatestHeight: BlockHeight,
|
|
|
|
reorgHeight: BlockHeight,
|
|
|
|
walletBirthday: BlockHeight,
|
|
|
|
targetHeight: BlockHeight) throws {
|
2020-04-06 08:54:31 -07:00
|
|
|
|
|
|
|
do {
|
2020-06-03 16:18:57 -07:00
|
|
|
try coordinator.reset(saplingActivation: 663150)
|
|
|
|
try coordinator.resetBlocks(dataset: .predefined(dataset: .beforeReOrg))
|
|
|
|
try coordinator.applyStaged(blockheight: firstLatestHeight)
|
2020-04-06 08:54:31 -07:00
|
|
|
} catch {
|
|
|
|
XCTFail("Error: \(error)")
|
|
|
|
return
|
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
let firstSyncExpectation = XCTestExpectation(description: "firstSyncExpectation")
|
2020-04-06 08:54:31 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
download and sync blocks from walletBirthday to firstLatestHeight
|
|
|
|
*/
|
2020-06-03 16:18:57 -07:00
|
|
|
var synchronizer: SDKSynchronizer?
|
|
|
|
try coordinator.sync(completion: { (s) in
|
|
|
|
synchronizer = s
|
|
|
|
firstSyncExpectation.fulfill()
|
|
|
|
}, error: self.handleError)
|
2020-04-06 08:54:31 -07:00
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
wait(for: [firstSyncExpectation], timeout: 5)
|
2020-04-06 08:54:31 -07:00
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
guard let syncedSynchronizer = synchronizer else {
|
|
|
|
XCTFail("nil synchronizer")
|
|
|
|
return
|
|
|
|
}
|
2020-04-06 08:54:31 -07:00
|
|
|
/**
|
|
|
|
verify that mock height has been reached
|
|
|
|
*/
|
|
|
|
var latestDownloadedHeight = BlockHeight(0)
|
2020-06-03 16:18:57 -07:00
|
|
|
XCTAssertNoThrow(try {latestDownloadedHeight = try syncedSynchronizer.initializer.downloader.latestBlockHeight()}())
|
2020-04-06 08:54:31 -07:00
|
|
|
XCTAssertTrue(latestDownloadedHeight > 0)
|
|
|
|
|
|
|
|
/**
|
|
|
|
trigger reorg!
|
|
|
|
*/
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
try coordinator.resetBlocks(dataset: .predefined(dataset: reorgDataset))
|
|
|
|
try coordinator.applyStaged(blockheight: targetHeight)
|
|
|
|
|
2020-04-06 08:54:31 -07:00
|
|
|
/**
|
|
|
|
request latest height -> receive targetHeight!
|
|
|
|
download that block
|
|
|
|
observe that the prev hash of that block does not match the hash that we have for firstLatestHeight
|
|
|
|
rewind 10 blocks and request blocks targetHeight-10 to targetHeight
|
|
|
|
*/
|
2020-06-03 16:18:57 -07:00
|
|
|
|
|
|
|
let secondSyncExpectation = XCTestExpectation(description: "second sync")
|
|
|
|
|
|
|
|
sleep(2)
|
|
|
|
try coordinator.sync(completion: { (_) in
|
|
|
|
secondSyncExpectation.fulfill()
|
|
|
|
}, error: self.handleError)
|
2020-04-06 08:54:31 -07:00
|
|
|
|
|
|
|
// now reorg should happen and reorg notifications and idle notification should be triggered
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
wait(for: [reorgExpectation,secondSyncExpectation], timeout: 5)
|
2020-04-06 08:54:31 -07:00
|
|
|
|
|
|
|
// now everything should be fine. latest block should be targetHeight
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
XCTAssertNoThrow(try {latestDownloadedHeight = try syncedSynchronizer.initializer.downloader.latestBlockHeight()}())
|
2020-04-06 08:54:31 -07:00
|
|
|
XCTAssertEqual(latestDownloadedHeight, targetHeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func processorHandledReorg(_ notification: Notification) {
|
|
|
|
|
|
|
|
XCTAssertNotNil(notification.userInfo)
|
|
|
|
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
|
|
|
|
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
|
2020-04-06 08:54:31 -07:00
|
|
|
XCTAssertTrue( rewind <= reorg )
|
2020-06-03 16:18:57 -07:00
|
|
|
reorgExpectation.fulfill()
|
2020-04-06 08:54:31 -07:00
|
|
|
} else {
|
|
|
|
XCTFail("CompactBlockProcessor reorg notification is malformed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-03 16:18:57 -07:00
|
|
|
func handleError(_ error: Error?) {
|
|
|
|
guard let testError = error else {
|
|
|
|
XCTFail("failed with nil error")
|
|
|
|
return
|
2020-04-06 08:54:31 -07:00
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
XCTFail("Failed with error: \(testError)")
|
2020-04-06 08:54:31 -07:00
|
|
|
}
|
2020-06-03 16:18:57 -07:00
|
|
|
|
2020-04-06 08:54:31 -07:00
|
|
|
}
|