// // BlockScanTests.swift // ZcashLightClientKitTests // // Created by Francisco Gindre on 10/17/19. // Copyright © 2019 Electric Coin Company. All rights reserved. // import XCTest import SQLite @testable import TestUtils @testable import ZcashLightClientKit // swiftlint:disable implicitly_unwrapped_optional force_try force_unwrapping print_function_usage class BlockScanTests: XCTestCase { let rustWelding = ZcashRustBackend.self var cacheDbURL: URL! var dataDbURL: URL! var saplingExtendedKey = SaplingExtendedFullViewingKey(validatedEncoding: "zxviewtestsapling1qw88ayg8qqqqpqyhg7jnh9mlldejfqwu46pm40ruwstd8znq3v3l4hjf33qcu2a5e36katshcfhcxhzgyfugj2lkhmt40j45cv38rv3frnghzkxcx73k7m7afw9j7ujk7nm4dx5mv02r26umxqgar7v3x390w2h3crqqgjsjly7jy4vtwzrmustm5yudpgcydw7x78awca8wqjvkqj8p8e3ykt7lrgd7xf92fsfqjs5vegfsja4ekzpfh5vtccgvs5747xqm6qflmtqpr8s9u") var walletBirthDay = Checkpoint.birthday( with: 1386000, network: ZcashNetworkBuilder.network(for: .testnet) ) var network = ZcashNetworkBuilder.network(for: .testnet) var blockRepository: BlockRepository! override func setUp() { // Put setup code here. This method is called before the invocation of each test method in the class. super.setUp() self.cacheDbURL = try! __cacheDbURL() self.dataDbURL = try! __dataDbURL() deleteDBs() } private func deleteDBs() { try? FileManager.default.removeItem(at: cacheDbURL) try? FileManager.default.removeItem(at: dataDbURL) } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() try? FileManager.default.removeItem(at: cacheDbURL) try? FileManager.default.removeItem(at: dataDbURL) } func testSingleDownloadAndScan() async throws { logger = SampleLogger(logLevel: .debug) XCTAssertNoThrow(try rustWelding.initDataDb(dbData: dataDbURL, seed: nil, networkType: network.networkType)) let storage = try! TestDbBuilder.inMemoryCompactBlockStorage() let service = LightWalletGRPCService( endpoint: LightWalletEndpoint( address: "lightwalletd.testnet.electriccoin.co", port: 9067 ) ) let blockCount = 100 let range = network.constants.saplingActivationHeight ... network.constants.saplingActivationHeight + blockCount let downloader = CompactBlockDownloader.sqlDownloader( service: service, at: cacheDbURL )! let processorConfig = CompactBlockProcessor.Configuration.standard( for: network, walletBirthday: network.constants.saplingActivationHeight ) let compactBlockProcessor = CompactBlockProcessor( service: service, storage: storage, backend: ZcashRustBackend.self, config: processorConfig ) let repository = BlockSQLDAO(dbProvider: SimpleConnectionProvider.init(path: self.dataDbURL.absoluteString, readonly: true)) var latestScannedheight = BlockHeight.empty() do { try await compactBlockProcessor.compactBlockDownload( downloader: downloader, range: range ) XCTAssertFalse(Task.isCancelled) try await compactBlockProcessor.compactBlockScanning( rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, networkType: network.networkType ) latestScannedheight = repository.lastScannedBlockHeight() XCTAssertEqual(latestScannedheight, range.upperBound) } catch { XCTFail("Download failed with error: \(error)") } } @objc func observeBenchmark(_ notification: Notification) { guard let report = SDKMetrics.blockReportFromNotification(notification) else { return } print("observed benchmark: \(report)") } func testScanValidateDownload() async throws { let seed = "testreferencealicetestreferencealice" logger = SampleLogger(logLevel: .debug) NotificationCenter.default.addObserver( self, selector: #selector(observeBenchmark(_:)), name: SDKMetrics.notificationName, object: nil ) guard try self.rustWelding.initDataDb(dbData: dataDbURL, seed: nil, networkType: network.networkType) == .success else { XCTFail("Seed should not be required for this test") return } let derivationTool = DerivationTool(networkType: .testnet) let ufvk = try derivationTool .deriveUnifiedSpendingKey(seed: Array(seed.utf8), accountIndex: 0) .map { try derivationTool.deriveUnifiedFullViewingKey(from: $0) } do { try self.rustWelding.initAccountsTable( dbData: self.dataDbURL, ufvks: [ufvk], networkType: network.networkType ) } catch { XCTFail("failed to init account table. error: \(self.rustWelding.getLastError() ?? "no error found")") return } try self.rustWelding.initBlocksTable( dbData: dataDbURL, height: Int32(walletBirthDay.height), hash: walletBirthDay.hash, time: walletBirthDay.time, saplingTree: walletBirthDay.saplingTree, networkType: network.networkType ) let service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet) let storage = CompactBlockStorage(url: cacheDbURL, readonly: false) try storage.createTable() var processorConfig = CompactBlockProcessor.Configuration( cacheDb: cacheDbURL, dataDb: dataDbURL, walletBirthday: network.constants.saplingActivationHeight, network: network ) processorConfig.scanningBatchSize = 1000 let compactBlockProcessor = CompactBlockProcessor( service: service, storage: storage, backend: rustWelding, config: processorConfig ) let range = CompactBlockRange( uncheckedBounds: (walletBirthDay.height, walletBirthDay.height + 10000) ) do { try await compactBlockProcessor.compactBlockStreamDownload( blockBufferSize: 10, startHeight: range.lowerBound, targetHeight: range.upperBound ) XCTAssertFalse(Task.isCancelled) try await compactBlockProcessor.compactBlockValidation() XCTAssertFalse(Task.isCancelled) try await compactBlockProcessor.compactBlockBatchScanning(range: range) XCTAssertFalse(Task.isCancelled) } catch { if let lwdError = error as? LightWalletServiceError { switch lwdError { case .timeOut: XCTAssert(true) default: XCTFail("LWD Service error found, but should have been a timeLimit reached Error - \(lwdError)") } } else { XCTFail("Error should have been a timeLimit reached Error - \(error)") } } } }