SwiftLint Enabled on Test Folder

This commit is contained in:
Adam Stener 2021-09-23 08:26:41 -05:00
parent db5f02dbf9
commit 78b1f937ba
41 changed files with 3068 additions and 2246 deletions

View File

@ -3,12 +3,14 @@
excluded: excluded:
- Pods - Pods
- ZcashLightClientKitTests
- ZcashLightClientKit/Service/ProtoBuf - ZcashLightClientKit/Service/ProtoBuf
- ZcashLightClientKitTests/proto
- ZcashLightClientKitTests/Constants.generated.swift
included: included:
- Example/ZcashLightClientSample/ZcashLIghtClientSample - Example/ZcashLightClientSample/ZcashLIghtClientSample
- ZcashLightClientKit - ZcashLightClientKit
- ZcashLightClientKitTests
disabled_rules: disabled_rules:
- notification_center_detachment - notification_center_detachment

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,27 +8,45 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable force_try type_body_length
class BlockBatchValidationTests: XCTestCase { class BlockBatchValidationTests: XCTestCase {
var queue: OperationQueue = { var queue: OperationQueue = {
let q = OperationQueue() let queue = OperationQueue()
q.name = "Test Queue" queue.name = "Test Queue"
q.maxConcurrentOperationCount = 1 queue.maxConcurrentOperationCount = 1
return q return queue
}() }()
override func setUpWithError() throws {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
} }
override func tearDownWithError() throws { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class. // Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
} }
func testBranchIdFailure() throws { func testBranchIdFailure() throws {
let network = ZcashNetworkBuilder.network(for: .mainnet) let network = ZcashNetworkBuilder.network(for: .mainnet)
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: 1210000,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: 1210000,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
@ -40,13 +58,15 @@ class BlockBatchValidationTests: XCTestCase {
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = Int32(0xd34d) mockRust.consensusBranchID = Int32(0xd34d)
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let expectation = XCTestExpectation(description: "failure expectation") let expectation = XCTestExpectation(description: "failure expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in operation.errorHandler = { error in
expectation.fulfill() expectation.fulfill()
switch error { switch error {
@ -58,17 +78,30 @@ class BlockBatchValidationTests: XCTestCase {
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,expectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, expectation], timeout: 1, enforceOrder: true)
XCTAssertNotNil(operation.error) XCTAssertNotNil(operation.error)
XCTAssertTrue(operation.isCancelled) XCTAssertTrue(operation.isCancelled)
} }
func testBranchNetworkMismatchFailure() throws { func testBranchNetworkMismatchFailure() throws {
let network = ZcashNetworkBuilder.network(for: .mainnet) let network = ZcashNetworkBuilder.network(for: .mainnet)
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: 1210000,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: 1210000,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
@ -76,17 +109,20 @@ class BlockBatchValidationTests: XCTestCase {
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = 0xd34db4d mockRust.consensusBranchID = 0xd34db4d
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let expectation = XCTestExpectation(description: "failure expectation") let expectation = XCTestExpectation(description: "failure expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in operation.errorHandler = { error in
expectation.fulfill() expectation.fulfill()
switch error { switch error {
@ -96,20 +132,33 @@ class BlockBatchValidationTests: XCTestCase {
XCTFail("Expected CompactBlockProcessorError.networkMismatch but found \(error)") XCTFail("Expected CompactBlockProcessorError.networkMismatch but found \(error)")
} }
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,expectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, expectation], timeout: 1, enforceOrder: true)
XCTAssertNotNil(operation.error) XCTAssertNotNil(operation.error)
XCTAssertTrue(operation.isCancelled) XCTAssertTrue(operation.isCancelled)
} }
func testBranchNetworkTypeWrongFailure() throws { func testBranchNetworkTypeWrongFailure() throws {
let network = ZcashNetworkBuilder.network(for: .testnet) let network = ZcashNetworkBuilder.network(for: .testnet)
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: 1210000,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: 1210000,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
@ -117,17 +166,20 @@ class BlockBatchValidationTests: XCTestCase {
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = 0xd34db4d mockRust.consensusBranchID = 0xd34db4d
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let expectation = XCTestExpectation(description: "failure expectation") let expectation = XCTestExpectation(description: "failure expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in operation.errorHandler = { error in
expectation.fulfill() expectation.fulfill()
switch error { switch error {
@ -137,19 +189,34 @@ class BlockBatchValidationTests: XCTestCase {
XCTFail("Expected CompactBlockProcessorError.generalError but found \(error)") XCTFail("Expected CompactBlockProcessorError.generalError but found \(error)")
} }
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,expectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, expectation], timeout: 1, enforceOrder: true)
XCTAssertNotNil(operation.error) XCTAssertNotNil(operation.error)
XCTAssertTrue(operation.isCancelled) XCTAssertTrue(operation.isCancelled)
} }
func testSaplingActivationHeightMismatch() throws { func testSaplingActivationHeightMismatch() throws {
let network = ZcashNetworkBuilder.network(for: .mainnet) let network = ZcashNetworkBuilder.network(for: .mainnet)
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: 1210000,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: 1210000,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
@ -157,29 +224,36 @@ class BlockBatchValidationTests: XCTestCase {
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(3434343) info.saplingActivationHeight = UInt64(3434343)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = 0xd34db4d mockRust.consensusBranchID = 0xd34db4d
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let expectation = XCTestExpectation(description: "failure expectation") let expectation = XCTestExpectation(description: "failure expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in operation.errorHandler = { error in
expectation.fulfill() expectation.fulfill()
switch error { switch error {
case CompactBlockProcessorError.saplingActivationMismatch(expected: network.constants.saplingActivationHeight, found: BlockHeight(info.saplingActivationHeight)): case CompactBlockProcessorError.saplingActivationMismatch(
expected: network.constants.saplingActivationHeight,
found: BlockHeight(info.saplingActivationHeight)
):
break break
default: default:
XCTFail("Expected CompactBlockProcessorError.saplingActivationMismatch but found \(error)") XCTFail("Expected CompactBlockProcessorError.saplingActivationMismatch but found \(error)")
} }
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,expectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, expectation], timeout: 1, enforceOrder: true)
XCTAssertNotNil(operation.error) XCTAssertNotNil(operation.error)
XCTAssertTrue(operation.isCancelled) XCTAssertTrue(operation.isCancelled)
} }
@ -188,12 +262,29 @@ class BlockBatchValidationTests: XCTestCase {
let network = ZcashNetworkBuilder.network(for: .mainnet) let network = ZcashNetworkBuilder.network(for: .mainnet)
let expectedLatestHeight = BlockHeight(1210000) let expectedLatestHeight = BlockHeight(1210000)
let service = MockLightWalletService(latestBlockHeight: expectedLatestHeight, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: expectedLatestHeight,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let expectedStoreLatestHeight = BlockHeight(1220000) let expectedStoreLatestHeight = BlockHeight(1220000)
let expectedResult = FigureNextBatchOperation.NextState.wait(latestHeight: expectedLatestHeight, latestDownloadHeight: expectedLatestHeight) let expectedResult = FigureNextBatchOperation.NextState.wait(
latestHeight: expectedLatestHeight,
latestDownloadHeight: expectedLatestHeight
)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight) let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: 1210000,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight) info.blockHeight = UInt64(expectedLatestHeight)
info.branch = "d34db33f" info.branch = "d34db33f"
@ -201,56 +292,84 @@ class BlockBatchValidationTests: XCTestCase {
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = 0xd34db4d mockRust.consensusBranchID = 0xd34db4d
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let completedExpectation = XCTestExpectation(description: "completed expectation") let completedExpectation = XCTestExpectation(description: "completed expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in operation.errorHandler = { error in
XCTFail("this shouldn't happen: \(error)") XCTFail("this shouldn't happen: \(error)")
} }
operation.completionHandler = { (finished, cancelled) in
operation.completionHandler = { finished, cancelled in
completedExpectation.fulfill() completedExpectation.fulfill()
XCTAssertTrue(finished) XCTAssertTrue(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,completedExpectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, completedExpectation], timeout: 1, enforceOrder: true)
XCTAssertNil(operation.error) XCTAssertNil(operation.error)
XCTAssertFalse(operation.isCancelled) XCTAssertFalse(operation.isCancelled)
guard let result = operation.result else { guard let result = operation.result else {
XCTFail("result should not be nil") XCTFail("result should not be nil")
return return
} }
XCTAssertTrue({ XCTAssertTrue(
switch result { {
case .wait(latestHeight: expectedLatestHeight, latestDownloadHeight: expectedLatestHeight): switch result {
return true case .wait(latestHeight: expectedLatestHeight, latestDownloadHeight: expectedLatestHeight):
default: return true
return false default:
} return false
}(), "Expected \(expectedResult) got: \(result)") }
}(),
"Expected \(expectedResult) got: \(result)"
)
} }
func testResultProcessNew() throws { func testResultProcessNew() throws {
let network = ZcashNetworkBuilder.network(for: .mainnet) let network = ZcashNetworkBuilder.network(for: .mainnet)
let expectedLatestHeight = BlockHeight(1230000) let expectedLatestHeight = BlockHeight(1230000)
let service = MockLightWalletService(latestBlockHeight: expectedLatestHeight, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: expectedLatestHeight,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let expectedStoreLatestHeight = BlockHeight(1220000) let expectedStoreLatestHeight = BlockHeight(1220000)
let walletBirthday = BlockHeight(1210000) let walletBirthday = BlockHeight(1210000)
let expectedResult = FigureNextBatchOperation.NextState.processNewBlocks(range: CompactBlockProcessor.nextBatchBlockRange(latestHeight: expectedLatestHeight, latestDownloadedHeight: expectedStoreLatestHeight, walletBirthday: walletBirthday)) let expectedResult = FigureNextBatchOperation.NextState.processNewBlocks(
range: CompactBlockProcessor.nextBatchBlockRange(
latestHeight: expectedLatestHeight,
latestDownloadedHeight: expectedStoreLatestHeight,
walletBirthday: walletBirthday
)
)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight) let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: walletBirthday, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: walletBirthday,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight) info.blockHeight = UInt64(expectedLatestHeight)
info.branch = "d34db33f" info.branch = "d34db33f"
@ -258,56 +377,78 @@ class BlockBatchValidationTests: XCTestCase {
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = 0xd34db4d mockRust.consensusBranchID = 0xd34db4d
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let completedExpectation = XCTestExpectation(description: "completed expectation") let completedExpectation = XCTestExpectation(description: "completed expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in
operation.errorHandler = { _ in
XCTFail("this shouldn't happen") XCTFail("this shouldn't happen")
} }
operation.completionHandler = { (finished, cancelled) in
operation.completionHandler = { finished, cancelled in
completedExpectation.fulfill() completedExpectation.fulfill()
XCTAssertTrue(finished) XCTAssertTrue(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,completedExpectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, completedExpectation], timeout: 1, enforceOrder: true)
XCTAssertNil(operation.error) XCTAssertNil(operation.error)
XCTAssertFalse(operation.isCancelled) XCTAssertFalse(operation.isCancelled)
guard let result = operation.result else { guard let result = operation.result else {
XCTFail("result should not be nil") XCTFail("result should not be nil")
return return
} }
XCTAssertTrue({ XCTAssertTrue(
switch result { {
case .processNewBlocks(range: CompactBlockRange(uncheckedBounds: (expectedStoreLatestHeight + 1, expectedLatestHeight))): switch result {
return true case .processNewBlocks(range: CompactBlockRange(uncheckedBounds: (expectedStoreLatestHeight + 1, expectedLatestHeight))):
default: return true
return false default:
} return false
}(), "Expected \(expectedResult) got: \(result)") }
}(),
"Expected \(expectedResult) got: \(result)"
)
} }
func testResultProcessorFinished() throws { func testResultProcessorFinished() throws {
let network = ZcashNetworkBuilder.network(for: .mainnet) let network = ZcashNetworkBuilder.network(for: .mainnet)
let expectedLatestHeight = BlockHeight(1230000) let expectedLatestHeight = BlockHeight(1230000)
let service = MockLightWalletService(latestBlockHeight: expectedLatestHeight, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(
latestBlockHeight: expectedLatestHeight,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
)
let expectedStoreLatestHeight = BlockHeight(1230000) let expectedStoreLatestHeight = BlockHeight(1230000)
let walletBirthday = BlockHeight(1210000) let walletBirthday = BlockHeight(1210000)
let expectedResult = FigureNextBatchOperation.NextState.finishProcessing(height: expectedStoreLatestHeight) let expectedResult = FigureNextBatchOperation.NextState.finishProcessing(height: expectedStoreLatestHeight)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight) let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: walletBirthday, saplingActivation: network.constants.saplingActivationHeight, network: network) let config = CompactBlockProcessor.Configuration(
cacheDb: try! __cacheDbURL(),
dataDb: try! __dataDbURL(),
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
rewindDistance: 100,
walletBirthday: walletBirthday,
saplingActivation: network.constants.saplingActivationHeight,
network: network
)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight) info.blockHeight = UInt64(expectedLatestHeight)
info.branch = "d34db33f" info.branch = "d34db33f"
@ -315,43 +456,51 @@ class BlockBatchValidationTests: XCTestCase {
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
mockRust.consensusBranchID = 0xd34db4d mockRust.consensusBranchID = 0xd34db4d
let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust) let operation = FigureNextBatchOperation(downloader: downloader, service: service, config: config, rustBackend: mockRust)
let completedExpectation = XCTestExpectation(description: "completed expectation") let completedExpectation = XCTestExpectation(description: "completed expectation")
let startedExpectation = XCTestExpectation(description: "start Expectation") let startedExpectation = XCTestExpectation(description: "start Expectation")
operation.startedHandler = { operation.startedHandler = {
startedExpectation.fulfill() startedExpectation.fulfill()
} }
operation.errorHandler = { error in
operation.errorHandler = { _ in
XCTFail("this shouldn't happen") XCTFail("this shouldn't happen")
} }
operation.completionHandler = { (finished, cancelled) in
operation.completionHandler = { finished, cancelled in
completedExpectation.fulfill() completedExpectation.fulfill()
XCTAssertTrue(finished) XCTAssertTrue(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
} }
queue.addOperations([operation], waitUntilFinished: false) queue.addOperations([operation], waitUntilFinished: false)
wait(for: [startedExpectation,completedExpectation], timeout: 1, enforceOrder: true) wait(for: [startedExpectation, completedExpectation], timeout: 1, enforceOrder: true)
XCTAssertNil(operation.error) XCTAssertNil(operation.error)
XCTAssertFalse(operation.isCancelled) XCTAssertFalse(operation.isCancelled)
guard let result = operation.result else { guard let result = operation.result else {
XCTFail("result should not be nil") XCTFail("result should not be nil")
return return
} }
XCTAssertTrue({ XCTAssertTrue(
switch result { {
case .finishProcessing(height: expectedLatestHeight): switch result {
return true case .finishProcessing(height: expectedLatestHeight):
default: return true
return false default:
} return false
}(), "Expected \(expectedResult) got: \(result)") }
}(),
"Expected \(expectedResult) got: \(result)"
)
} }
} }

View File

@ -8,17 +8,21 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_cast force_try
class BlockDownloaderTests: XCTestCase { class BlockDownloaderTests: XCTestCase {
let branchID = "2bb40e60"
let chainName = "main"
var darksideWalletService: DarksideWalletService!
var downloader: CompactBlockDownloading! var downloader: CompactBlockDownloading!
var service: LightWalletService! var service: LightWalletService!
var storage: CompactBlockRepository! var storage: CompactBlockRepository!
var cacheDB = try! __cacheDbURL() var cacheDB = try! __cacheDbURL()
var network = DarksideWalletDNetwork() var network = DarksideWalletDNetwork()
var darksideWalletService: DarksideWalletService!
let branchID = "2bb40e60"
let chainName = "main"
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default) service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)
storage = try! TestDbBuilder.diskCompactBlockStorage(at: cacheDB) storage = try! TestDbBuilder.diskCompactBlockStorage(at: cacheDB)
downloader = CompactBlockDownloader(service: service, storage: storage) downloader = CompactBlockDownloader(service: service, storage: storage)
@ -29,7 +33,7 @@ class BlockDownloaderTests: XCTestCase {
} }
override func tearDown() { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class. try super.tearDown()
service = nil service = nil
storage = nil storage = nil
downloader = nil downloader = nil
@ -37,24 +41,23 @@ class BlockDownloaderTests: XCTestCase {
} }
func testSmallDownloadAsync() { func testSmallDownloadAsync() {
let expect = XCTestExpectation(description: self.description) let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 3 expect.expectedFulfillmentCount = 3
let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99 let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange)) let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
downloader.downloadBlockRange(range) { (error) in downloader.downloadBlockRange(range) { error in
expect.fulfill() expect.fulfill()
XCTAssertNil(error) XCTAssertNil(error)
// check what was 'stored' // check what was 'stored'
self.storage.latestHeight { (result) in self.storage.latestHeight { result in
expect.fulfill() expect.fulfill()
XCTAssertTrue(self.validate(result: result, against: upperRange)) XCTAssertTrue(self.validate(result: result, against: upperRange))
self.downloader.lastDownloadedBlockHeight { (resultHeight) in self.downloader.lastDownloadedBlockHeight { resultHeight in
expect.fulfill() expect.fulfill()
XCTAssertTrue(self.validate(result: resultHeight, against: upperRange)) XCTAssertTrue(self.validate(result: resultHeight, against: upperRange))
} }
@ -65,11 +68,10 @@ class BlockDownloaderTests: XCTestCase {
} }
func testSmallDownload() { func testSmallDownload() {
let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99 let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange)) let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
var latest: BlockHeight = 0 var latest: BlockHeight = 0
do { do {
@ -84,26 +86,31 @@ class BlockDownloaderTests: XCTestCase {
var currentLatest: BlockHeight = 0 var currentLatest: BlockHeight = 0
do { do {
currentLatest = try downloader.lastDownloadedBlockHeight() currentLatest = try downloader.lastDownloadedBlockHeight()
} catch { } catch {
XCTFail("latest block failed") XCTFail("latest block failed")
return return
} }
XCTAssertEqual(currentLatest,upperRange )
XCTAssertEqual(currentLatest, upperRange )
} }
func testFailure() { func testFailure() {
let awfulDownloader = CompactBlockDownloader(service: AwfulLightWalletService(latestBlockHeight: self.network.constants.saplingActivationHeight + 1000, service: darksideWalletService), storage: ZcashConsoleFakeStorage()) let awfulDownloader = CompactBlockDownloader(
service: AwfulLightWalletService(
latestBlockHeight: self.network.constants.saplingActivationHeight + 1000,
service: darksideWalletService
),
storage: ZcashConsoleFakeStorage()
)
let expect = XCTestExpectation(description: self.description) let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 1 expect.expectedFulfillmentCount = 1
let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99 let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange)) let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
awfulDownloader.downloadBlockRange(range) { (error) in awfulDownloader.downloadBlockRange(range) { error in
expect.fulfill() expect.fulfill()
XCTAssertNotNil(error) XCTAssertNotNil(error)
} }
@ -114,14 +121,12 @@ class BlockDownloaderTests: XCTestCase {
/// Helper functions /// Helper functions
extension BlockDownloaderTests { extension BlockDownloaderTests {
func validate(result: Result<BlockHeight,Error> ,against height: BlockHeight) -> Bool { func validate(result: Result<BlockHeight, Error>, against height: BlockHeight) -> Bool {
switch result { switch result {
case .success(let resultHeight): case .success(let resultHeight):
return resultHeight == height return resultHeight == height
default: default:
return false return false
} }
} }
} }

View File

@ -9,23 +9,31 @@
import XCTest import XCTest
import SQLite import SQLite
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_try force_unwrapping print_function_usage
class BlockScanOperationTests: XCTestCase { class BlockScanOperationTests: XCTestCase {
let rustWelding = ZcashRustBackend.self
var operationQueue = OperationQueue() var operationQueue = OperationQueue()
var cacheDbURL: URL! var cacheDbURL: URL!
var dataDbURL: URL! var dataDbURL: URL!
let rustWelding = ZcashRustBackend.self
var uvk = UVFakeKey(extfvk: "zxviewtestsapling1qw88ayg8qqqqpqyhg7jnh9mlldejfqwu46pm40ruwstd8znq3v3l4hjf33qcu2a5e36katshcfhcxhzgyfugj2lkhmt40j45cv38rv3frnghzkxcx73k7m7afw9j7ujk7nm4dx5mv02r26umxqgar7v3x390w2h3crqqgjsjly7jy4vtwzrmustm5yudpgcydw7x78awca8wqjvkqj8p8e3ykt7lrgd7xf92fsfqjs5vegfsja4ekzpfh5vtccgvs5747xqm6qflmtqpr8s9u",
extpub: "02075a7f5f7507d64022dad5954849f216b0f1b09b2d588be663d8e7faeb5aaf61")
var uvk = UVFakeKey(
var walletBirthDay = WalletBirthday.birthday(with: 1386000, network: ZcashNetworkBuilder.network(for: .testnet)) extfvk: "zxviewtestsapling1qw88ayg8qqqqpqyhg7jnh9mlldejfqwu46pm40ruwstd8znq3v3l4hjf33qcu2a5e36katshcfhcxhzgyfugj2lkhmt40j45cv38rv3frnghzkxcx73k7m7afw9j7ujk7nm4dx5mv02r26umxqgar7v3x390w2h3crqqgjsjly7jy4vtwzrmustm5yudpgcydw7x78awca8wqjvkqj8p8e3ykt7lrgd7xf92fsfqjs5vegfsja4ekzpfh5vtccgvs5747xqm6qflmtqpr8s9u", // swiftlint:disable:this line_length
extpub: "02075a7f5f7507d64022dad5954849f216b0f1b09b2d588be663d8e7faeb5aaf61"
)
var walletBirthDay = WalletBirthday.birthday(
with: 1386000,
network: ZcashNetworkBuilder.network(for: .testnet)
)
var network = ZcashNetworkBuilder.network(for: .testnet) var network = ZcashNetworkBuilder.network(for: .testnet)
var blockRepository: BlockRepository! var blockRepository: BlockRepository!
override func setUp() { override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class. // 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.cacheDbURL = try! __cacheDbURL()
self.dataDbURL = try! __dataDbURL() self.dataDbURL = try! __dataDbURL()
@ -37,8 +45,10 @@ class BlockScanOperationTests: XCTestCase {
try? FileManager.default.removeItem(at: cacheDbURL) try? FileManager.default.removeItem(at: cacheDbURL)
try? FileManager.default.removeItem(at: dataDbURL) try? FileManager.default.removeItem(at: dataDbURL)
} }
override func tearDown() { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class. // Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
operationQueue.cancelAllOperations() operationQueue.cancelAllOperations()
try? FileManager.default.removeItem(at: cacheDbURL) try? FileManager.default.removeItem(at: cacheDbURL)
@ -47,29 +57,47 @@ class BlockScanOperationTests: XCTestCase {
func testSingleDownloadAndScanOperation() { func testSingleDownloadAndScanOperation() {
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
XCTAssertNoThrow(try rustWelding.initDataDb(dbData: dataDbURL, networkType: network.networkType)) XCTAssertNoThrow(try rustWelding.initDataDb(dbData: dataDbURL, networkType: network.networkType))
let downloadStartedExpect = XCTestExpectation(description: self.description + "download started")
let downloadExpect = XCTestExpectation(description: self.description + "download") let downloadStartedExpect = XCTestExpectation(description: "\(self.description) download started")
let scanStartedExpect = XCTestExpectation(description: self.description + "scan started") let downloadExpect = XCTestExpectation(description: "\(self.description) download")
let scanExpect = XCTestExpectation(description: self.description + "scan") let scanStartedExpect = XCTestExpectation(description: "\(self.description) scan started")
let latestScannedBlockExpect = XCTestExpectation(description: self.description + "latestScannedHeight") let scanExpect = XCTestExpectation(description: "\(self.description) scan")
let service = LightWalletGRPCService(endpoint: LightWalletEndpoint(address: "lightwalletd.testnet.electriccoin.co", port: 9067)) let latestScannedBlockExpect = XCTestExpectation(description: "\(self.description) latestScannedHeight")
let service = LightWalletGRPCService(
endpoint: LightWalletEndpoint(
address: "lightwalletd.testnet.electriccoin.co",
port: 9067
)
)
let blockCount = 100 let blockCount = 100
let range = network.constants.saplingActivationHeight ... network.constants.saplingActivationHeight + blockCount let range = network.constants.saplingActivationHeight ... network.constants.saplingActivationHeight + blockCount
let downloadOperation = CompactBlockDownloadOperation(downloader: CompactBlockDownloader.sqlDownloader(service: service, at: cacheDbURL)!, range: range) let downloadOperation = CompactBlockDownloadOperation(
let scanOperation = CompactBlockScanningOperation(rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, networkType: network.networkType) downloader: CompactBlockDownloader.sqlDownloader(
service: service,
at: cacheDbURL
)!,
range: range
)
let scanOperation = CompactBlockScanningOperation(
rustWelding: rustWelding,
cacheDb: cacheDbURL,
dataDb: dataDbURL,
networkType: network.networkType
)
downloadOperation.startedHandler = { downloadOperation.startedHandler = {
downloadStartedExpect.fulfill() downloadStartedExpect.fulfill()
} }
downloadOperation.completionHandler = { (finished, cancelled) in downloadOperation.completionHandler = { finished, cancelled in
downloadExpect.fulfill() downloadExpect.fulfill()
XCTAssertTrue(finished) XCTAssertTrue(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
} }
downloadOperation.errorHandler = { (error) in downloadOperation.errorHandler = { error in
XCTFail("Download Operation failed with Error: \(error)") XCTFail("Download Operation failed with Error: \(error)")
} }
@ -77,13 +105,13 @@ class BlockScanOperationTests: XCTestCase {
scanStartedExpect.fulfill() scanStartedExpect.fulfill()
} }
scanOperation.completionHandler = { (finished, cancelled) in scanOperation.completionHandler = { finished, cancelled in
scanExpect.fulfill() scanExpect.fulfill()
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
XCTAssertTrue(finished) XCTAssertTrue(finished)
} }
scanOperation.errorHandler = { (error) in scanOperation.errorHandler = { error in
XCTFail("Scan Operation failed with Error: \(error)") XCTFail("Scan Operation failed with Error: \(error)")
} }
@ -101,10 +129,16 @@ class BlockScanOperationTests: XCTestCase {
latestScannedBlockOperation.addDependency(scanOperation) latestScannedBlockOperation.addDependency(scanOperation)
operationQueue.addOperations([downloadOperation,scanOperation,latestScannedBlockOperation], waitUntilFinished: false) operationQueue.addOperations(
[downloadOperation, scanOperation, latestScannedBlockOperation],
waitUntilFinished: false
wait(for: [downloadStartedExpect, downloadExpect, scanStartedExpect, scanExpect,latestScannedBlockExpect], timeout: 10, enforceOrder: true) )
wait(
for: [downloadStartedExpect, downloadExpect, scanStartedExpect, scanExpect, latestScannedBlockExpect],
timeout: 10,
enforceOrder: true
)
} }
@objc func observeBenchmark(_ notification: Notification) { @objc func observeBenchmark(_ notification: Notification) {
guard let report = SDKMetrics.blockReportFromNotification(notification) else { guard let report = SDKMetrics.blockReportFromNotification(notification) else {
@ -112,20 +146,32 @@ class BlockScanOperationTests: XCTestCase {
} }
print("observed benchmark: \(report)") print("observed benchmark: \(report)")
} }
func testScanValidateDownload() throws { func testScanValidateDownload() throws {
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
NotificationCenter.default.addObserver(self, selector: #selector(observeBenchmark(_:)), name: SDKMetrics.notificationName, object: nil) NotificationCenter.default.addObserver(
self,
selector: #selector(observeBenchmark(_:)),
name: SDKMetrics.notificationName,
object: nil
)
try self.rustWelding.initDataDb(dbData: dataDbURL, networkType: network.networkType) try self.rustWelding.initDataDb(dbData: dataDbURL, networkType: network.networkType)
guard try self.rustWelding.initAccountsTable(dbData: self.dataDbURL, uvks: [uvk], networkType: network.networkType) else { guard try self.rustWelding.initAccountsTable(dbData: self.dataDbURL, uvks: [uvk], networkType: network.networkType) else {
XCTFail("failed to init account table") XCTFail("failed to init account table")
return return
} }
try self.rustWelding.initBlocksTable(dbData: dataDbURL, height: Int32(walletBirthDay.height), hash: walletBirthDay.hash, time: walletBirthDay.time, saplingTree: walletBirthDay.tree, networkType: network.networkType) try self.rustWelding.initBlocksTable(
dbData: dataDbURL,
height: Int32(walletBirthDay.height),
hash: walletBirthDay.hash,
time: walletBirthDay.time,
saplingTree: walletBirthDay.tree,
networkType: network.networkType
)
let service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet) let service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet)
let storage = CompactBlockStorage(url: cacheDbURL, readonly: false) let storage = CompactBlockStorage(url: cacheDbURL, readonly: false)
@ -135,13 +181,15 @@ class BlockScanOperationTests: XCTestCase {
let validateExpectation = XCTestExpectation(description: "validate expectation") let validateExpectation = XCTestExpectation(description: "validate expectation")
let scanExpectation = XCTestExpectation(description: "scan expectation") let scanExpectation = XCTestExpectation(description: "scan expectation")
let downloadOperation = CompactBlockStreamDownloadOperation(service: service, let downloadOperation = CompactBlockStreamDownloadOperation(
storage: storage, service: service,
startHeight: walletBirthDay.height, storage: storage,
targetHeight: walletBirthDay.height + 10000, startHeight: walletBirthDay.height,
progressDelegate: self) targetHeight: walletBirthDay.height + 10000,
progressDelegate: self
)
downloadOperation.completionHandler = { (finished, cancelled) in downloadOperation.completionHandler = { finished, cancelled in
XCTAssert(finished) XCTAssert(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
downloadExpectation.fulfill() downloadExpectation.fulfill()
@ -160,40 +208,55 @@ class BlockScanOperationTests: XCTestCase {
} }
} }
let validationOperation = CompactBlockValidationOperation(
let validationOperation = CompactBlockValidationOperation(rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, networkType: network.networkType) rustWelding: rustWelding,
cacheDb: cacheDbURL,
dataDb: dataDbURL,
networkType: network.networkType
)
validationOperation.errorHandler = { error in validationOperation.errorHandler = { error in
self.operationQueue.cancelAllOperations() self.operationQueue.cancelAllOperations()
XCTFail("failed with error \(error)") XCTFail("failed with error \(error)")
} }
validationOperation.completionHandler = { (finished,cancelled) in
validationOperation.completionHandler = { finished, cancelled in
XCTAssert(finished) XCTAssert(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
validateExpectation.fulfill() validateExpectation.fulfill()
} }
let transactionRepository = TransactionRepositoryBuilder.build(dataDbURL: dataDbURL) let transactionRepository = TransactionRepositoryBuilder.build(dataDbURL: dataDbURL)
let scanningOperation = CompactBlockBatchScanningOperation(rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, transactionRepository: transactionRepository, range: CompactBlockRange(uncheckedBounds: (walletBirthDay.height, walletBirthDay.height + 10000)), batchSize: 1000, networkType: network.networkType, progressDelegate: self) let scanningOperation = CompactBlockBatchScanningOperation(
rustWelding: rustWelding,
cacheDb: cacheDbURL,
dataDb: dataDbURL,
transactionRepository: transactionRepository,
range: CompactBlockRange(
uncheckedBounds: (walletBirthDay.height, walletBirthDay.height + 10000)
),
batchSize: 1000,
networkType: network.networkType,
progressDelegate: self
)
scanningOperation.completionHandler = { (finished,cancelled) in scanningOperation.completionHandler = { finished, cancelled in
XCTAssert(finished) XCTAssert(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
scanExpectation.fulfill() scanExpectation.fulfill()
} }
operationQueue.addOperations([downloadOperation, validationOperation, scanningOperation], waitUntilFinished: false) operationQueue.addOperations([downloadOperation, validationOperation, scanningOperation], waitUntilFinished: false)
wait(for: [downloadExpectation,validateExpectation,scanExpectation], timeout: 300, enforceOrder: true) wait(for: [downloadExpectation, validateExpectation, scanExpectation], timeout: 300, enforceOrder: true)
} }
} }
extension BlockScanOperationTests: CompactBlockProgressDelegate { extension BlockScanOperationTests: CompactBlockProgressDelegate {
func progressUpdated(_ progress: CompactBlockProgress) { func progressUpdated(_ progress: CompactBlockProgress) {
// print("progressHeight: \(progress.progressHeight) startHeight: \(progress.startHeight), targetHeight: \(progress.targetHeight)")
} }
} }
struct UVFakeKey: UnifiedViewingKey { struct UVFakeKey: UnifiedViewingKey {
var extfvk: ExtendedFullViewingKey var extfvk: ExtendedFullViewingKey
var extpub: ExtendedPublicKey var extpub: ExtendedPublicKey

View File

@ -7,36 +7,40 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable print_function_usage
class BlockStreamingTest: XCTestCase { class BlockStreamingTest: XCTestCase {
var queue: OperationQueue = { var queue: OperationQueue = {
let q = OperationQueue() let queue = OperationQueue()
q.maxConcurrentOperationCount = 1 queue.maxConcurrentOperationCount = 1
return q return queue
}() }()
override func setUpWithError() throws { override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class. try super.setUpWithError()
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class. try super.tearDownWithError()
try? FileManager.default.removeItem(at: __dataDbURL()) try? FileManager.default.removeItem(at: __dataDbURL())
} }
func testStreamOperation() throws { func testStreamOperation() throws {
let expectation = XCTestExpectation(description: "blockstream expectation") let expectation = XCTestExpectation(description: "blockstream expectation")
let service = LightWalletGRPCService(host: LightWalletEndpointBuilder.eccTestnet.host, let service = LightWalletGRPCService(
port: 9067, host: LightWalletEndpointBuilder.eccTestnet.host,
secure: true, port: 9067,
singleCallTimeout: 1000, secure: true,
streamingCallTimeout: 100000) singleCallTimeout: 1000,
streamingCallTimeout: 100000
)
let latestHeight = try service.latestBlockHeight() let latestHeight = try service.latestBlockHeight()
let startHeight = latestHeight - 100_000 let startHeight = latestHeight - 100_000
var blocks = [ZcashCompactBlock]() var blocks: [ZcashCompactBlock] = []
service.blockStream(startHeight: startHeight, endHeight: latestHeight) { result in service.blockStream(startHeight: startHeight, endHeight: latestHeight) { result in
expectation.fulfill() expectation.fulfill()
switch result { switch result {
@ -49,29 +53,34 @@ class BlockStreamingTest: XCTestCase {
print("received block \(compactBlock.height)") print("received block \(compactBlock.height)")
blocks.append(compactBlock) blocks.append(compactBlock)
} progress: { progressReport in } progress: { progressReport in
print("progressHeight: \(progressReport.progressHeight) startHeight: \(progressReport.startHeight), targetHeight: \(progressReport.targetHeight)") print("progressHeight: \(progressReport.progressHeight)")
print("startHeight: \(progressReport.startHeight)")
print("targetHeight: \(progressReport.targetHeight)")
} }
wait(for: [expectation], timeout: 1000) wait(for: [expectation], timeout: 1000)
} }
func testStreamOperationCancellation() throws { func testStreamOperationCancellation() throws {
let expectation = XCTestExpectation(description: "blockstream expectation") let expectation = XCTestExpectation(description: "blockstream expectation")
let service = LightWalletGRPCService(host: LightWalletEndpointBuilder.eccTestnet.host, let service = LightWalletGRPCService(
port: 9067, host: LightWalletEndpointBuilder.eccTestnet.host,
secure: true, port: 9067,
singleCallTimeout: 10000, secure: true,
streamingCallTimeout: 10000) singleCallTimeout: 10000,
streamingCallTimeout: 10000
)
let storage = try TestDbBuilder.inMemoryCompactBlockStorage() let storage = try TestDbBuilder.inMemoryCompactBlockStorage()
let startHeight = try service.latestBlockHeight() - 100_000 let startHeight = try service.latestBlockHeight() - 100_000
let operation = CompactBlockStreamDownloadOperation(service: service, let operation = CompactBlockStreamDownloadOperation(
storage: storage, service: service,
startHeight: startHeight, storage: storage,
progressDelegate: self) startHeight: startHeight,
progressDelegate: self
)
operation.completionHandler = { (finished, cancelled) in operation.completionHandler = { _, cancelled in
XCTAssert(cancelled) XCTAssert(cancelled)
expectation.fulfill() expectation.fulfill()
} }
@ -91,20 +100,24 @@ class BlockStreamingTest: XCTestCase {
func testStreamOperationTimeout() throws { func testStreamOperationTimeout() throws {
let expectation = XCTestExpectation(description: "blockstream expectation") let expectation = XCTestExpectation(description: "blockstream expectation")
let errorExpectation = XCTestExpectation(description: "blockstream error expectation") let errorExpectation = XCTestExpectation(description: "blockstream error expectation")
let service = LightWalletGRPCService(host: LightWalletEndpointBuilder.eccTestnet.host, let service = LightWalletGRPCService(
port: 9067, host: LightWalletEndpointBuilder.eccTestnet.host,
secure: true, port: 9067,
singleCallTimeout: 1000, secure: true,
streamingCallTimeout: 3000) singleCallTimeout: 1000,
streamingCallTimeout: 3000
)
let storage = try TestDbBuilder.inMemoryCompactBlockStorage() let storage = try TestDbBuilder.inMemoryCompactBlockStorage()
let startHeight = try service.latestBlockHeight() - 100_000 let startHeight = try service.latestBlockHeight() - 100_000
let operation = CompactBlockStreamDownloadOperation(service: service, let operation = CompactBlockStreamDownloadOperation(
storage: storage, service: service,
startHeight: startHeight, storage: storage,
progressDelegate: self) startHeight: startHeight,
progressDelegate: self
)
operation.completionHandler = { (finished, cancelled) in operation.completionHandler = { finished, _ in
XCTAssert(finished) XCTAssert(finished)
expectation.fulfill() expectation.fulfill()
@ -136,20 +149,25 @@ class BlockStreamingTest: XCTestCase {
func testBatchOperation() throws { func testBatchOperation() throws {
let expectation = XCTestExpectation(description: "blockbatch expectation") let expectation = XCTestExpectation(description: "blockbatch expectation")
let service = LightWalletGRPCService(host: LightWalletEndpointBuilder.eccTestnet.host, let service = LightWalletGRPCService(
port: 9067, host: LightWalletEndpointBuilder.eccTestnet.host,
secure: true, port: 9067,
singleCallTimeout: 300000, secure: true,
streamingCallTimeout: 10000) singleCallTimeout: 300000,
streamingCallTimeout: 10000
)
let storage = try TestDbBuilder.diskCompactBlockStorage(at: __dataDbURL() ) let storage = try TestDbBuilder.diskCompactBlockStorage(at: __dataDbURL() )
let targetHeight = try service.latestBlockHeight() let targetHeight = try service.latestBlockHeight()
let startHeight = targetHeight - 10_000 let startHeight = targetHeight - 10_000
let operation = CompactBlockBatchDownloadOperation(service: service, let operation = CompactBlockBatchDownloadOperation(
storage: storage, service: service,
startHeight: startHeight, targetHeight: targetHeight, storage: storage,
progressDelegate: self) startHeight: startHeight,
targetHeight: targetHeight,
progressDelegate: self
)
operation.completionHandler = { (finished, cancelled) in operation.completionHandler = { _, cancelled in
if cancelled { if cancelled {
XCTFail("operation cancelled") XCTFail("operation cancelled")
} }
@ -169,20 +187,25 @@ class BlockStreamingTest: XCTestCase {
func testBatchOperationCancellation() throws { func testBatchOperationCancellation() throws {
let expectation = XCTestExpectation(description: "blockbatch expectation") let expectation = XCTestExpectation(description: "blockbatch expectation")
let service = LightWalletGRPCService(host: LightWalletEndpointBuilder.eccTestnet.host, let service = LightWalletGRPCService(
port: 9067, host: LightWalletEndpointBuilder.eccTestnet.host,
secure: true, port: 9067,
singleCallTimeout: 300000, secure: true,
streamingCallTimeout: 10000) singleCallTimeout: 300000,
streamingCallTimeout: 10000
)
let storage = try TestDbBuilder.diskCompactBlockStorage(at: __dataDbURL() ) let storage = try TestDbBuilder.diskCompactBlockStorage(at: __dataDbURL() )
let targetHeight = try service.latestBlockHeight() let targetHeight = try service.latestBlockHeight()
let startHeight = targetHeight - 100_000 let startHeight = targetHeight - 100_000
let operation = CompactBlockBatchDownloadOperation(service: service, let operation = CompactBlockBatchDownloadOperation(
storage: storage, service: service,
startHeight: startHeight, targetHeight: targetHeight, storage: storage,
progressDelegate: self) startHeight: startHeight,
targetHeight: targetHeight,
progressDelegate: self
)
operation.completionHandler = { (finished, cancelled) in operation.completionHandler = { _, cancelled in
XCTAssert(cancelled) XCTAssert(cancelled)
expectation.fulfill() expectation.fulfill()
} }
@ -201,8 +224,9 @@ class BlockStreamingTest: XCTestCase {
} }
extension BlockStreamingTest: CompactBlockProgressDelegate { extension BlockStreamingTest: CompactBlockProgressDelegate {
func progressUpdated(_ progress: CompactBlockProgress) { func progressUpdated(_ progress: CompactBlockProgress) {
print("progressHeight: \(String(describing: progress.progressHeight)) startHeight: \(progress.progress), targetHeight: \(String(describing: progress.targetHeight))") print("progressHeight: \(String(describing: progress.progressHeight))")
print("startHeight: \(progress.progress)")
print("targetHeight: \(String(describing: progress.targetHeight))")
} }
} }

View File

@ -8,9 +8,13 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable force_try implicitly_unwrapped_optional
class CompactBlockProcessorTests: XCTestCase { class CompactBlockProcessorTests: XCTestCase {
let processorConfig = CompactBlockProcessor.Configuration.standard(
let processorConfig = CompactBlockProcessor.Configuration.standard(for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight) for: ZcashNetworkBuilder.network(for: .testnet),
walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight
)
var processor: CompactBlockProcessor! var processor: CompactBlockProcessor!
var downloadStartedExpect: XCTestExpectation! var downloadStartedExpect: XCTestExpectation!
var updatedNotificationExpectation: XCTestExpectation! var updatedNotificationExpectation: XCTestExpectation!
@ -22,10 +26,13 @@ class CompactBlockProcessorTests: XCTestCase {
let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000 let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000
override func setUpWithError() throws { override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class. try super.setUpWithError()
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
let service = MockLightWalletService(latestBlockHeight: mockLatestHeight, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet)) let service = MockLightWalletService(
latestBlockHeight: mockLatestHeight,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet)
)
let branchID = try ZcashRustBackend.consensusBranchIdFor(height: Int32(mockLatestHeight), networkType: network.networkType) let branchID = try ZcashRustBackend.consensusBranchIdFor(height: Int32(mockLatestHeight), networkType: network.networkType)
service.mockLightDInfo = LightdInfo.with({ info in service.mockLightDInfo = LightdInfo.with({ info in
info.blockHeight = UInt64(mockLatestHeight) info.blockHeight = UInt64(mockLatestHeight)
@ -41,23 +48,33 @@ class CompactBlockProcessorTests: XCTestCase {
let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString)) let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString))
try! storage.createTable() try! storage.createTable()
processor = CompactBlockProcessor(
processor = CompactBlockProcessor(service: service, service: service,
storage: storage, storage: storage,
backend: ZcashRustBackend.self, backend: ZcashRustBackend.self,
config: processorConfig) config: processorConfig
)
try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet) try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet)
downloadStartedExpect = XCTestExpectation(description: self.description + " downloadStartedExpect") downloadStartedExpect = XCTestExpectation(description: "\(self.description) downloadStartedExpect")
stopNotificationExpectation = XCTestExpectation(description: self.description + " stopNotificationExpectation") stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation")
updatedNotificationExpectation = XCTestExpectation(description: self.description + " updatedNotificationExpectation") updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation")
startedValidatingNotificationExpectation = XCTestExpectation(description: self.description + " startedValidatingNotificationExpectation") startedValidatingNotificationExpectation = XCTestExpectation(
startedScanningNotificationExpectation = XCTestExpectation(description: self.description + " startedScanningNotificationExpectation") description: "\(self.description) startedValidatingNotificationExpectation"
idleNotificationExpectation = XCTestExpectation(description: self.description + " idleNotificationExpectation") )
NotificationCenter.default.addObserver(self, selector: #selector(processorFailed(_:)), name: Notification.Name.blockProcessorFailed, object: processor) startedScanningNotificationExpectation = XCTestExpectation(
description: "\(self.description) startedScanningNotificationExpectation"
)
idleNotificationExpectation = XCTestExpectation(description: "\(self.description) idleNotificationExpectation")
NotificationCenter.default.addObserver(
self,
selector: #selector(processorFailed(_:)),
name: Notification.Name.blockProcessorFailed,
object: processor
)
} }
override func tearDown() { override func tearDown() {
super.tearDown()
try! FileManager.default.removeItem(at: processorConfig.cacheDb) try! FileManager.default.removeItem(at: processorConfig.cacheDb)
try? FileManager.default.removeItem(at: processorConfig.dataDb) try? FileManager.default.removeItem(at: processorConfig.dataDb)
downloadStartedExpect.unsubscribeFromNotifications() downloadStartedExpect.unsubscribeFromNotifications()
@ -78,7 +95,7 @@ class CompactBlockProcessorTests: XCTestCase {
} }
} }
fileprivate func startProcessing() { private func startProcessing() {
XCTAssertNotNil(processor) XCTAssertNotNil(processor)
// Subscribe to notifications // Subscribe to notifications
@ -93,41 +110,51 @@ class CompactBlockProcessorTests: XCTestCase {
} }
func testStartNotifiesSuscriptors() { func testStartNotifiesSuscriptors() {
startProcessing() startProcessing()
wait(for: [ wait(
downloadStartedExpect, for: [
startedValidatingNotificationExpectation, downloadStartedExpect,
startedScanningNotificationExpectation, startedValidatingNotificationExpectation,
idleNotificationExpectation, startedScanningNotificationExpectation,
], timeout: 30,enforceOrder: true) idleNotificationExpectation
],
timeout: 30,
enforceOrder: true
)
} }
func testProgressNotifications() { func testProgressNotifications() {
let expectedUpdates = expectedBatches(
let expectedUpdates = expectedBatches(currentHeight: processorConfig.walletBirthday, targetHeight: mockLatestHeight, batchSize: processorConfig.downloadBatchSize) currentHeight: processorConfig.walletBirthday,
targetHeight: mockLatestHeight,
batchSize: processorConfig.downloadBatchSize
)
updatedNotificationExpectation.expectedFulfillmentCount = expectedUpdates updatedNotificationExpectation.expectedFulfillmentCount = expectedUpdates
startProcessing() startProcessing()
wait(for: [updatedNotificationExpectation], timeout: 300) wait(for: [updatedNotificationExpectation], timeout: 300)
} }
private func expectedBatches(currentHeight: BlockHeight, targetHeight: BlockHeight, batchSize: Int) -> Int { private func expectedBatches(currentHeight: BlockHeight, targetHeight: BlockHeight, batchSize: Int) -> Int {
(abs(currentHeight-targetHeight)/batchSize) (abs(currentHeight - targetHeight) / batchSize)
} }
func testNextBatchBlockRange() { func testNextBatchBlockRange() {
// test first range // test first range
var latestDownloadedHeight = processorConfig.walletBirthday // this can be either this or Wallet Birthday. var latestDownloadedHeight = processorConfig.walletBirthday // this can be either this or Wallet Birthday.
var latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000) var latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
var expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight, upper:latestBlockchainHeight)) var expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight, upper:latestBlockchainHeight))
XCTAssertEqual(expectedBatchRange, CompactBlockProcessor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight, walletBirthday: processorConfig.walletBirthday)) XCTAssertEqual(
expectedBatchRange,
CompactBlockProcessor.nextBatchBlockRange(
latestHeight: latestBlockchainHeight,
latestDownloadedHeight: latestDownloadedHeight,
walletBirthday: processorConfig.walletBirthday
)
)
// Test mid-range // Test mid-range
latestDownloadedHeight = BlockHeight(network.constants.saplingActivationHeight + ZcashSDK.DefaultBatchSize) latestDownloadedHeight = BlockHeight(network.constants.saplingActivationHeight + ZcashSDK.DefaultBatchSize)
@ -135,7 +162,14 @@ class CompactBlockProcessorTests: XCTestCase {
expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight)) expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight))
XCTAssertEqual(expectedBatchRange, CompactBlockProcessor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight, walletBirthday: processorConfig.walletBirthday)) XCTAssertEqual(
expectedBatchRange,
CompactBlockProcessor.nextBatchBlockRange(
latestHeight: latestBlockchainHeight,
latestDownloadedHeight: latestDownloadedHeight,
walletBirthday: processorConfig.walletBirthday
)
)
// Test last batch range // Test last batch range
@ -144,7 +178,14 @@ class CompactBlockProcessorTests: XCTestCase {
expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight)) expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight))
XCTAssertEqual(expectedBatchRange, CompactBlockProcessor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight, walletBirthday: processorConfig.walletBirthday)) XCTAssertEqual(
expectedBatchRange,
CompactBlockProcessor.nextBatchBlockRange(
latestHeight: latestBlockchainHeight,
latestDownloadedHeight: latestDownloadedHeight,
walletBirthday: processorConfig.walletBirthday
)
)
} }
func testDetermineLowerBoundPastBirthday() { func testDetermineLowerBoundPastBirthday() {
@ -156,7 +197,6 @@ class CompactBlockProcessorTests: XCTestCase {
let expected = 781_886 let expected = 781_886
XCTAssertEqual(result, expected) XCTAssertEqual(result, expected)
} }
func testDetermineLowerBound() { func testDetermineLowerBound() {
@ -168,6 +208,5 @@ class CompactBlockProcessorTests: XCTestCase {
let expected = 781_896 let expected = 781_896
XCTAssertEqual(result, expected) XCTAssertEqual(result, expected)
} }
} }

View File

@ -6,12 +6,15 @@
// //
// Copyright © 2019 Electric Coin Company. All rights reserved. // Copyright © 2019 Electric Coin Company. All rights reserved.
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_try
class CompactBlockReorgTests: XCTestCase { class CompactBlockReorgTests: XCTestCase {
let processorConfig = CompactBlockProcessor.Configuration.standard(
let processorConfig = CompactBlockProcessor.Configuration.standard(for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight) for: ZcashNetworkBuilder.network(for: .testnet),
walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight
)
var processor: CompactBlockProcessor! var processor: CompactBlockProcessor!
var downloadStartedExpect: XCTestExpectation! var downloadStartedExpect: XCTestExpectation!
var updatedNotificationExpectation: XCTestExpectation! var updatedNotificationExpectation: XCTestExpectation!
@ -24,13 +27,15 @@ class CompactBlockReorgTests: XCTestCase {
let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000 let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000
override func setUpWithError() throws { override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class. try super.setUpWithError()
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
let service = MockLightWalletService(latestBlockHeight: mockLatestHeight, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet)) let service = MockLightWalletService(
latestBlockHeight: mockLatestHeight,
service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet)
)
let branchID = try ZcashRustBackend.consensusBranchIdFor(height: Int32(mockLatestHeight), networkType: network.networkType) let branchID = try ZcashRustBackend.consensusBranchIdFor(height: Int32(mockLatestHeight), networkType: network.networkType)
service.mockLightDInfo = LightdInfo.with({ info in service.mockLightDInfo = LightdInfo.with { info in
info.blockHeight = UInt64(mockLatestHeight) info.blockHeight = UInt64(mockLatestHeight)
info.branch = "asdf" info.branch = "asdf"
info.buildDate = "today" info.buildDate = "today"
@ -39,7 +44,7 @@ class CompactBlockReorgTests: XCTestCase {
info.consensusBranchID = branchID.toString() info.consensusBranchID = branchID.toString()
info.estimatedHeight = UInt64(mockLatestHeight) info.estimatedHeight = UInt64(mockLatestHeight)
info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
}) }
try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet) try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet)
@ -51,24 +56,42 @@ class CompactBlockReorgTests: XCTestCase {
mockBackend.mockValidateCombinedChainKeepFailing = false mockBackend.mockValidateCombinedChainKeepFailing = false
mockBackend.mockValidateCombinedChainFailureHeight = self.network.constants.saplingActivationHeight + 320 mockBackend.mockValidateCombinedChainFailureHeight = self.network.constants.saplingActivationHeight + 320
processor = CompactBlockProcessor(service: service, processor = CompactBlockProcessor(
storage: storage, service: service,
backend: mockBackend, storage: storage,
config: processorConfig) backend: mockBackend,
config: processorConfig
)
downloadStartedExpect = XCTestExpectation(description: self.description + " downloadStartedExpect") downloadStartedExpect = XCTestExpectation(description: "\(self.description) downloadStartedExpect")
stopNotificationExpectation = XCTestExpectation(description: self.description + " stopNotificationExpectation") stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation")
updatedNotificationExpectation = XCTestExpectation(description: self.description + " updatedNotificationExpectation") updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation")
startedValidatingNotificationExpectation = XCTestExpectation(description: self.description + " startedValidatingNotificationExpectation") startedValidatingNotificationExpectation = XCTestExpectation(
startedScanningNotificationExpectation = XCTestExpectation(description: self.description + " startedScanningNotificationExpectation") description: "\(self.description) startedValidatingNotificationExpectation"
idleNotificationExpectation = XCTestExpectation(description: self.description + " idleNotificationExpectation") )
reorgNotificationExpectation = XCTestExpectation(description: self.description + " reorgNotificationExpectation") startedScanningNotificationExpectation = XCTestExpectation(
description: "\(self.description) startedScanningNotificationExpectation"
)
idleNotificationExpectation = XCTestExpectation(description: "\(self.description) idleNotificationExpectation")
reorgNotificationExpectation = XCTestExpectation(description: "\(self.description) reorgNotificationExpectation")
NotificationCenter.default.addObserver(self, selector: #selector(processorHandledReorg(_:)), name: Notification.Name.blockProcessorHandledReOrg, object: processor) NotificationCenter.default.addObserver(
NotificationCenter.default.addObserver(self, selector: #selector(processorFailed(_:)), name: Notification.Name.blockProcessorFailed, object: processor) self,
selector: #selector(processorHandledReorg(_:)),
name: Notification.Name.blockProcessorHandledReOrg,
object: processor
)
NotificationCenter.default.addObserver(
self,
selector: #selector(processorFailed(_:)),
name: Notification.Name.blockProcessorFailed,
object: processor
)
} }
override func tearDown() { override func tearDown() {
super.tearDown()
try! FileManager.default.removeItem(at: processorConfig.cacheDb) try! FileManager.default.removeItem(at: processorConfig.cacheDb)
try? FileManager.default.removeItem(at: processorConfig.dataDb) try? FileManager.default.removeItem(at: processorConfig.dataDb)
downloadStartedExpect.unsubscribeFromNotifications() downloadStartedExpect.unsubscribeFromNotifications()
@ -82,31 +105,28 @@ class CompactBlockReorgTests: XCTestCase {
} }
@objc func processorHandledReorg(_ notification: Notification) { @objc func processorHandledReorg(_ notification: Notification) {
XCTAssertNotNil(notification.userInfo)
XCTAssertNotNil(notification.userInfo) if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight, let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight { XCTAssertTrue( reorg == 0 || reorg > self.network.constants.saplingActivationHeight)
XCTAssertTrue( reorg == 0 || reorg > self.network.constants.saplingActivationHeight) XCTAssertTrue( rewind == 0 || rewind > self.network.constants.saplingActivationHeight)
XCTAssertTrue( rewind == 0 || rewind > self.network.constants.saplingActivationHeight) XCTAssertTrue( rewind <= reorg )
XCTAssertTrue( rewind <= reorg ) reorgNotificationExpectation.fulfill()
reorgNotificationExpectation.fulfill() } else {
} else { XCTFail("CompactBlockProcessor reorg notification is malformed")
XCTFail("CompactBlockProcessor reorg notification is malformed") }
}
} }
@objc func processorFailed(_ notification: Notification) { @objc func processorFailed(_ notification: Notification) {
XCTAssertNotNil(notification.userInfo)
XCTAssertNotNil(notification.userInfo) if let error = notification.userInfo?["error"] {
if let error = notification.userInfo?["error"] { XCTFail("CompactBlockProcessor failed with Error: \(error)")
XCTFail("CompactBlockProcessor failed with Error: \(error)") } else {
} else { XCTFail("CompactBlockProcessor failed")
XCTFail("CompactBlockProcessor failed") }
}
} }
fileprivate func startProcessing() { private func startProcessing() {
XCTAssertNotNil(processor) XCTAssertNotNil(processor)
// Subscribe to notifications // Subscribe to notifications
@ -122,20 +142,22 @@ class CompactBlockReorgTests: XCTestCase {
} }
func testNotifiesReorg() { func testNotifiesReorg() {
startProcessing() startProcessing()
wait(for: [ wait(
downloadStartedExpect, for: [
startedValidatingNotificationExpectation, downloadStartedExpect,
startedScanningNotificationExpectation, startedValidatingNotificationExpectation,
reorgNotificationExpectation, startedScanningNotificationExpectation,
idleNotificationExpectation, reorgNotificationExpectation,
], timeout: 300,enforceOrder: true) idleNotificationExpectation
],
timeout: 300,
enforceOrder: true
)
} }
private func expectedBatches(currentHeight: BlockHeight, targetHeight: BlockHeight, batchSize: Int) -> Int { private func expectedBatches(currentHeight: BlockHeight, targetHeight: BlockHeight, batchSize: Int) -> Int {
(abs(currentHeight-targetHeight)/batchSize) (abs(currentHeight - targetHeight) / batchSize)
} }
} }

View File

@ -8,12 +8,13 @@
import Foundation import Foundation
import XCTest import XCTest
// swiftlint:disable force_try
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
class CompactBlockStorageTests: XCTestCase { class CompactBlockStorageTests: XCTestCase {
var compactBlockDao: CompactBlockRepository = try! TestDbBuilder.inMemoryCompactBlockStorage()
let network = ZcashNetworkBuilder.network(for: .testnet) let network = ZcashNetworkBuilder.network(for: .testnet)
var compactBlockDao: CompactBlockRepository = try! TestDbBuilder.inMemoryCompactBlockStorage()
func testEmptyStorage() { func testEmptyStorage() {
XCTAssertEqual(try! compactBlockDao.latestHeight(), BlockHeight.empty()) XCTAssertEqual(try! compactBlockDao.latestHeight(), BlockHeight.empty())
} }
@ -34,11 +35,9 @@ class CompactBlockStorageTests: XCTestCase {
let latestHeight = try! compactBlockDao.latestHeight() let latestHeight = try! compactBlockDao.latestHeight()
XCTAssertNotEqual(initialHeight, latestHeight) XCTAssertNotEqual(initialHeight, latestHeight)
XCTAssertEqual(latestHeight, finalHeight) XCTAssertEqual(latestHeight, finalHeight)
} }
func testStoreOneBlockFromEmpty() { func testStoreOneBlockFromEmpty() {
let initialHeight = try! compactBlockDao.latestHeight() let initialHeight = try! compactBlockDao.latestHeight()
guard initialHeight == BlockHeight.empty() else { guard initialHeight == BlockHeight.empty() else {
XCTFail("database not empty, latest height: \(initialHeight)") XCTFail("database not empty, latest height: \(initialHeight)")
@ -62,7 +61,6 @@ class CompactBlockStorageTests: XCTestCase {
} }
func testRewindTo() { func testRewindTo() {
let startHeight = self.network.constants.saplingActivationHeight let startHeight = self.network.constants.saplingActivationHeight
let blockCount = Int(1_000) let blockCount = Int(1_000)
let finalHeight = startHeight + blockCount let finalHeight = startHeight + blockCount
@ -79,10 +77,8 @@ class CompactBlockStorageTests: XCTestCase {
do { do {
let latestHeight = try compactBlockDao.latestHeight() let latestHeight = try compactBlockDao.latestHeight()
XCTAssertEqual(latestHeight, rewindHeight - 1) XCTAssertEqual(latestHeight, rewindHeight - 1)
} catch { } catch {
XCTFail("Rewind latest block failed with error: \(error)") XCTFail("Rewind latest block failed with error: \(error)")
} }
} }
} }

View File

@ -8,12 +8,15 @@
import Foundation import Foundation
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional
class DarksideSanityCheckTests: XCTestCase { class DarksideSanityCheckTests: XCTestCase {
// 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? // 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"
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a" //TODO: Parameterize this from environment // TODO: Parameterize this from environment
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a"
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 663150 var birthday: BlockHeight = 663150
let defaultLatestHeight: BlockHeight = 663175 let defaultLatestHeight: BlockHeight = 663175
@ -23,12 +26,12 @@ class DarksideSanityCheckTests: XCTestCase {
var expectedReorgHeight: BlockHeight = 665188 var expectedReorgHeight: BlockHeight = 665188
var expectedRewindHeight: BlockHeight = 665188 var expectedRewindHeight: BlockHeight = 665188
var network = DarksideWalletDNetwork() var network = DarksideWalletDNetwork()
var reorgExpectation: XCTestExpectation = XCTestExpectation(description: "reorg") var reorgExpectation = XCTestExpectation(description: "reorg")
let branchID = "2bb40e60" let branchID = "2bb40e60"
let chainName = "main" let chainName = "main"
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
coordinator = try TestCoordinator( coordinator = try TestCoordinator(
seed: seedPhrase, seed: seedPhrase,
walletBirthday: birthday, walletBirthday: birthday,
@ -37,10 +40,10 @@ class DarksideSanityCheckTests: XCTestCase {
) )
try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName)
try coordinator.resetBlocks(dataset: .default) try coordinator.resetBlocks(dataset: .default)
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB) try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB) try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
@ -54,17 +57,19 @@ class DarksideSanityCheckTests: XCTestCase {
let syncExpectation = XCTestExpectation(description: "sync to \(expectedLastBlock.height)") let syncExpectation = XCTestExpectation(description: "sync to \(expectedLastBlock.height)")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(
completion: { _ in
syncExpectation.fulfill() syncExpectation.fulfill()
}, error: { (error) in },
guard let e = error else { error: { error in
XCTFail("failed with unknown error") guard let e = error else {
XCTFail("failed with unknown error")
return
}
XCTFail("failed with error: \(e)")
return return
} }
XCTFail("failed with error: \(e)") )
return
})
wait(for: [syncExpectation], timeout: 5) wait(for: [syncExpectation], timeout: 5)
@ -75,6 +80,5 @@ class DarksideSanityCheckTests: XCTestCase {
XCTAssertEqual(firstBlock?.hash.toHexStringTxId(), expectedFirstBlock.hash) XCTAssertEqual(firstBlock?.hash.toHexStringTxId(), expectedFirstBlock.hash)
XCTAssertEqual(lastBlock?.hash.toHexStringTxId(), expectedLastBlock.hash) XCTAssertEqual(lastBlock?.hash.toHexStringTxId(), expectedLastBlock.hash)
} }
} }

View File

@ -9,12 +9,14 @@
import XCTest import XCTest
import SQLite import SQLite
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable force_try
class DownloadOperationTests: XCTestCase { class DownloadOperationTests: XCTestCase {
var operationQueue = OperationQueue() var operationQueue = OperationQueue()
var network = ZcashNetworkBuilder.network(for: .testnet) var network = ZcashNetworkBuilder.network(for: .testnet)
override func tearDown() { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown()
operationQueue.cancelAllOperations() operationQueue.cancelAllOperations()
} }
@ -29,13 +31,13 @@ class DownloadOperationTests: XCTestCase {
let range = activationHeight ... activationHeight + blockCount let range = activationHeight ... activationHeight + blockCount
let downloadOperation = CompactBlockDownloadOperation(downloader: downloader, range: range) let downloadOperation = CompactBlockDownloadOperation(downloader: downloader, range: range)
downloadOperation.completionHandler = { (finished, cancelled) in downloadOperation.completionHandler = { finished, cancelled in
expect.fulfill() expect.fulfill()
XCTAssertTrue(finished) XCTAssertTrue(finished)
XCTAssertFalse(cancelled) XCTAssertFalse(cancelled)
} }
downloadOperation.errorHandler = { (error) in downloadOperation.errorHandler = { error in
XCTFail("Donwload Operation failed with error: \(error)") XCTFail("Donwload Operation failed with error: \(error)")
} }
@ -43,7 +45,6 @@ class DownloadOperationTests: XCTestCase {
wait(for: [expect], timeout: 10) wait(for: [expect], timeout: 10)
XCTAssertEqual(try! storage.latestHeight(),range.upperBound) XCTAssertEqual(try! storage.latestHeight(), range.upperBound)
} }
} }

View File

@ -9,21 +9,21 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
import GRPC import GRPC
// swiftlint:disable implicitly_unwrapped_optional force_unwrapping
class LightWalletServiceTests: XCTestCase { class LightWalletServiceTests: XCTestCase {
let network: ZcashNetwork = ZcashNetworkBuilder.network(for: .testnet)
var service: LightWalletService! var service: LightWalletService!
var channel: Channel! var channel: Channel!
let network: ZcashNetwork = ZcashNetworkBuilder.network(for: .testnet)
override func setUp() { override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
channel = ChannelProvider().channel() channel = ChannelProvider().channel()
service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet) service = LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.eccTestnet)
} }
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
/// FIXME: check whether this test is stil valid on in memory lwd implementatiojn /// FIXME: check whether this test is stil valid on in memory lwd implementatiojn
// func testFailure() { // func testFailure() {
// //
@ -44,7 +44,7 @@ class LightWalletServiceTests: XCTestCase {
let upperRange: BlockHeight = network.constants.saplingActivationHeight + count let upperRange: BlockHeight = network.constants.saplingActivationHeight + count
let blockRange = lowerRange ... upperRange let blockRange = lowerRange ... upperRange
service.blockRange(blockRange) { (result) in service.blockRange(blockRange) { result in
expect.fulfill() expect.fulfill()
switch result { switch result {
case .failure(let error): case .failure(let error):
@ -73,9 +73,9 @@ class LightWalletServiceTests: XCTestCase {
} }
} }
func testLatestBlock(){ func testLatestBlock() {
let expect = XCTestExpectation(description: self.description) let expect = XCTestExpectation(description: self.description)
service.latestBlockHeight { (result) in service.latestBlockHeight { result in
expect.fulfill() expect.fulfill()
switch result { switch result {
case .failure(let e): case .failure(let e):
@ -87,5 +87,4 @@ class LightWalletServiceTests: XCTestCase {
wait(for: [expect], timeout: 10) wait(for: [expect], timeout: 10)
} }
} }

View File

@ -7,32 +7,30 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable force_unwrapping print_function_usage
class MemoTests: XCTestCase { class MemoTests: XCTestCase {
/** /**
Non-utf8 memos are properly ignored Non-utf8 memos are properly ignored
*/ */
func testNonUnicodeMemos() throws { func testNonUnicodeMemos() throws {
XCTAssertNil(Self.randomMemoData()!.asZcashTransactionMemo()) XCTAssertNil(Self.randomMemoData()!.asZcashTransactionMemo())
} }
/** /**
Memo length is correct, padding characters are ignored Memo length is correct, padding characters are ignored
*/ */
func testMemoLength() throws { func testMemoLength() throws {
XCTAssertEqual(validMemoData.count, 512) XCTAssertEqual(validMemoData.count, 512)
XCTAssertEqual(validMemoData.asZcashTransactionMemo()!.count, Self.validMemoDataExpectedString.count) XCTAssertEqual(validMemoData.asZcashTransactionMemo()!.count, Self.validMemoDataExpectedString.count)
} }
/** /**
Verify support for common unicode characters Verify support for common unicode characters
*/ */
func testUnicodeCharacters() throws { func testUnicodeCharacters() throws {
let memo = validMemoData.asZcashTransactionMemo() let memo = validMemoData.asZcashTransactionMemo()
XCTAssertNotNil(memo) XCTAssertNotNil(memo)
XCTAssertEqual(memo!, Self.validMemoDataExpectedString) XCTAssertEqual(memo!, Self.validMemoDataExpectedString)
} }
func testEmojiUnicodeCharacters() throws { func testEmojiUnicodeCharacters() throws {
@ -42,32 +40,34 @@ class MemoTests: XCTestCase {
} }
/** /**
Blank memos are ignored Blank memos are ignored
*/ */
func testBlankMemos() throws { func testBlankMemos() throws {
// This is an example of a functional test case. // This is an example of a functional test case.
XCTAssertNil(emptyMemoData.asZcashTransactionMemo()) XCTAssertNil(emptyMemoData.asZcashTransactionMemo())
} }
/** /**
test canonical memos test canonical memos
*/ */
func testCanonicalBlankMemos() throws { func testCanonicalBlankMemos() throws {
XCTAssertNil(Self.canonicalEmptyMemo().asZcashTransactionMemo()) XCTAssertNil(Self.canonicalEmptyMemo().asZcashTransactionMemo())
} }
/** /**
******* *******
* mocked memos * mocked memos
* ****** * ******
*/ */
/** /**
Real text: "Here's gift from the Zec Fairy @ ECC!" Real text: "Here's gift from the Zec Fairy @ ECC!"
*/ */
static let validMemoDataExpectedString = "Here's gift from the Zec Fairy @ ECC!" static let validMemoDataExpectedString = "Here's gift from the Zec Fairy @ ECC!"
static let validMemoDataBase64 = "SGVyZSdzIGdpZnQgZnJvbSB0aGUgWmVjIEZhaXJ5IEAgRUNDIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" static let validMemoDataBase64 =
// swiftlint:disable:next line_length
"SGVyZSdzIGdpZnQgZnJvbSB0aGUgWmVjIEZhaXJ5IEAgRUNDIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
let validMemoData = Data(base64Encoded: validMemoDataBase64)! let validMemoData = Data(base64Encoded: validMemoDataBase64)!
@ -75,8 +75,9 @@ class MemoTests: XCTestCase {
let totallyRandomDataMemo = randomMemoData()! let totallyRandomDataMemo = randomMemoData()!
static let emojiDataBase64 =
static let emojiDataBase64 = "8J+SlfCfkpXwn5KV8J+mk/CfppPwn6aT8J+bofCfm6Hwn5uhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" // swiftlint:disable:next line_length
"8J+SlfCfkpXwn5KV8J+mk/CfppPwn6aT8J+bofCfm6Hwn5uhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
static let emojiMemoData = Data(base64Encoded: emojiDataBase64)! static let emojiMemoData = Data(base64Encoded: emojiDataBase64)!
@ -102,4 +103,3 @@ class MemoTests: XCTestCase {
} }
} }
} }

View File

@ -7,32 +7,31 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional type_body_length force_unwrapping
class NetworkUpgradeTests: XCTestCase { class NetworkUpgradeTests: XCTestCase {
let activationHeight: BlockHeight = 1028500 let activationHeight: BlockHeight = 1028500
var spendingKey = "secret-extended-key-test1qv2vf437qqqqpqpfc0arpv55ncq33p2p895hlcx0ra6d0g739v93luqdjpxun3kt050j9qnrqjyp8d7fdxgedfyxpjmuyha2ulxa6hmqvm2gnvuc3tvs3enpxwuz768qfkd286vr3jgyrgr5ddx2ukrdl95ak3tzqylzjeqw3pnmgtmwsvemrj3sk6vqgwxm9khlv46wccn33ayw52prr233ea069c9u8m3839dvw30sdf6k32xddhpte6p6qsuxval6usyh6lr55pgypkgtz" let spendingKey =
// swiftlint:disable:next line_length
let testRecipientAddress = "ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc" //TODO: Parameterize this from environment "secret-extended-key-test1qv2vf437qqqqpqpfc0arpv55ncq33p2p895hlcx0ra6d0g739v93luqdjpxun3kt050j9qnrqjyp8d7fdxgedfyxpjmuyha2ulxa6hmqvm2gnvuc3tvs3enpxwuz768qfkd286vr3jgyrgr5ddx2ukrdl95ak3tzqylzjeqw3pnmgtmwsvemrj3sk6vqgwxm9khlv46wccn33ayw52prr233ea069c9u8m3839dvw30sdf6k32xddhpte6p6qsuxval6usyh6lr55pgypkgtz"
// TODO: Parameterize this from environment
let testRecipientAddress = "ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc"
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 1013250
let branchID = "2bb40e60" let branchID = "2bb40e60"
let chainName = "main" let chainName = "main"
var birthday: BlockHeight = 1013250
var coordinator: TestCoordinator! var coordinator: TestCoordinator!
var network = ZcashNetworkBuilder.network(for: .testnet) var network = ZcashNetworkBuilder.network(for: .testnet)
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
// coordinator = try TestCoordinator(
// spendingKey: spendingKey,
// unifiedViewingKey: <#UnifiedViewingKey#>,
// walletBirthday: birthday,
// channelProvider: ChannelProvider()
// )
try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName)
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
try coordinator.stop() try coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
@ -40,22 +39,26 @@ class NetworkUpgradeTests: XCTestCase {
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB) try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
} }
/** /**
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() throws {
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, branchID: branchID, chainName: chainName, length: 15300) try FakeChainBuilder.buildChain(
darksideWallet: coordinator.service,
birthday: birthday,
networkActivationHeight: activationHeight,
branchID: branchID,
chainName: chainName,
length: 15300
)
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.defaultStaleTolerance) try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.defaultStaleTolerance)
sleep(5) sleep(5)
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [firstSyncExpectation], timeout: 120) wait(for: [firstSyncExpectation], timeout: 120)
@ -69,31 +72,40 @@ class NetworkUpgradeTests: XCTestCase {
sleep(2) sleep(2)
let sendExpectation = XCTestExpectation(description: "send expectation") let sendExpectation = XCTestExpectation(description: "send expectation")
var p: PendingTransactionEntity? = nil var pendingEntity: PendingTransactionEntity?
let spendAmount: Int64 = 10000 let spendAmount: Int64 = 10000
/* /*
send transaction to recipient address send transaction to recipient address
*/ */
coordinator.synchronizer.sendToAddress(spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, toAddress: self.testRecipientAddress, memo: "this is a test", from: 0, resultBlock: { (result) in coordinator.synchronizer.sendToAddress(
switch result { spendingKey: self.coordinator.spendingKeys!.first!,
case .failure(let e): zatoshi: spendAmount,
self.handleError(e) toAddress: self.testRecipientAddress,
case .success(let pendingTx): memo: "this is a test",
p = pendingTx from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
} }
sendExpectation.fulfill() )
})
wait(for: [sendExpectation], timeout: 11) wait(for: [sendExpectation], timeout: 11)
guard let _ = p else { guard pendingEntity != nil else {
XCTFail("no pending transaction after sending") XCTFail("no pending transaction after sending")
try coordinator.stop() try coordinator.stop()
return return
} }
/* /*
getIncomingTransaction getIncomingTransaction
*/ */
guard let incomingTx = try coordinator.getIncomingTransactions()?.first else { guard let incomingTx = try coordinator.getIncomingTransactions()?.first else {
XCTFail("no incoming transaction") XCTFail("no incoming transaction")
try coordinator.stop() try coordinator.stop()
@ -103,45 +115,44 @@ class NetworkUpgradeTests: XCTestCase {
let sentTxHeight: BlockHeight = activationHeight + 2 let sentTxHeight: BlockHeight = activationHeight + 2
/* /*
stage transaction at sentTxHeight stage transaction at sentTxHeight
*/ */
try coordinator.stageTransaction(incomingTx, at: sentTxHeight) try coordinator.stageTransaction(incomingTx, at: sentTxHeight)
try coordinator.applyStaged(blockheight: activationHeight + 20) try coordinator.applyStaged(blockheight: activationHeight + 20)
sleep(1) sleep(1)
let afterSendExpectation = XCTestExpectation(description: "aftersend") let afterSendExpectation = XCTestExpectation(description: "aftersend")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
afterSendExpectation.fulfill() afterSendExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [afterSendExpectation], timeout: 10) wait(for: [afterSendExpectation], timeout: 10)
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), verifiedBalance - spendAmount) XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), verifiedBalance - spendAmount)
} }
/** /**
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() throws {
try FakeChainBuilder.buildChainPostActivationFunds(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, length: 15300) try FakeChainBuilder.buildChainPostActivationFunds(
darksideWallet: coordinator.service,
birthday: birthday,
networkActivationHeight: activationHeight,
length: 15300
)
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
try coordinator.applyStaged(blockheight: activationHeight + 10) try coordinator.applyStaged(blockheight: activationHeight + 10)
sleep(3) sleep(3)
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [firstSyncExpectation], timeout: 120) wait(for: [firstSyncExpectation], timeout: 120)
guard try coordinator.synchronizer.allReceivedTransactions().filter({$0.minedHeight > activationHeight}).count > 0 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")
return return
} }
@ -149,26 +160,33 @@ class NetworkUpgradeTests: XCTestCase {
try coordinator.applyStaged(blockheight: activationHeight + 20) try coordinator.applyStaged(blockheight: activationHeight + 20)
sleep(2) sleep(2)
let sendExpectation = XCTestExpectation(description: "send expectation") let sendExpectation = XCTestExpectation(description: "send expectation")
var p: PendingTransactionEntity? = nil var pendingEntity: PendingTransactionEntity?
let spendAmount: Int64 = 10000 let spendAmount: Int64 = 10000
/* /*
send transaction to recipient address send transaction to recipient address
*/ */
coordinator.synchronizer.sendToAddress(spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, toAddress: self.testRecipientAddress, memo: "this is a test", from: 0, resultBlock: { (result) in coordinator.synchronizer.sendToAddress(
switch result { spendingKey: self.coordinator.spendingKeys!.first!,
case .failure(let e): zatoshi: spendAmount,
self.handleError(e) toAddress: self.testRecipientAddress,
case .success(let pendingTx): memo: "this is a test",
p = pendingTx from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
} }
sendExpectation.fulfill() )
})
wait(for: [sendExpectation], timeout: 11) wait(for: [sendExpectation], timeout: 11)
guard let _ = p else { guard pendingEntity != nil else {
XCTFail("no pending transaction after sending") XCTFail("no pending transaction after sending")
try coordinator.stop() try coordinator.stop()
return return
@ -178,66 +196,74 @@ class NetworkUpgradeTests: XCTestCase {
let afterSendExpectation = XCTestExpectation(description: "aftersend") let afterSendExpectation = XCTestExpectation(description: "aftersend")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
afterSendExpectation.fulfill() afterSendExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [afterSendExpectation], timeout: 10) wait(for: [afterSendExpectation], timeout: 10)
} }
/**
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() throws {
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, branchID: branchID, chainName: chainName, length: 15300) try FakeChainBuilder.buildChain(
darksideWallet: coordinator.service,
birthday: birthday,
networkActivationHeight: activationHeight,
branchID: branchID,
chainName: chainName,
length: 15300
)
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
try coordinator.applyStaged(blockheight: activationHeight - 10) try coordinator.applyStaged(blockheight: activationHeight - 10)
sleep(3) sleep(3)
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [firstSyncExpectation], timeout: 120) wait(for: [firstSyncExpectation], timeout: 120)
let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance() let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: activationHeight)) XCTAssertTrue(verifiedBalance > network.constants.defaultFee(for: activationHeight))
let sendExpectation = XCTestExpectation(description: "send expectation") let sendExpectation = XCTestExpectation(description: "send expectation")
var p: PendingTransactionEntity? = nil var pendingEntity: PendingTransactionEntity?
let spendAmount: Int64 = 10000 let spendAmount: Int64 = 10000
/* /*
send transaction to recipient address send transaction to recipient address
*/ */
coordinator.synchronizer.sendToAddress(spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, toAddress: self.testRecipientAddress, memo: "this is a test", from: 0, resultBlock: { (result) in coordinator.synchronizer.sendToAddress(
switch result { spendingKey: self.coordinator.spendingKeys!.first!,
case .failure(let e): zatoshi: spendAmount,
self.handleError(e) toAddress: self.testRecipientAddress,
case .success(let pendingTx): memo: "this is a test",
p = pendingTx from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
} }
sendExpectation.fulfill() )
})
wait(for: [sendExpectation], timeout: 11) wait(for: [sendExpectation], timeout: 11)
guard let pendingTx = p else { guard let pendingTx = pendingEntity else {
XCTFail("no pending transaction after sending") XCTFail("no pending transaction after sending")
try coordinator.stop() try coordinator.stop()
return return
} }
/* /*
getIncomingTransaction getIncomingTransaction
*/ */
guard let incomingTx = try coordinator.getIncomingTransactions()?.first else { guard let incomingTx = try coordinator.getIncomingTransactions()?.first else {
XCTFail("no incoming transaction") XCTFail("no incoming transaction")
try coordinator.stop() try coordinator.stop()
@ -246,12 +272,9 @@ class NetworkUpgradeTests: XCTestCase {
let sentTxHeight: BlockHeight = activationHeight - 5 let sentTxHeight: BlockHeight = activationHeight - 5
/* /*
stage transaction at sentTxHeight stage transaction at sentTxHeight
*/ */
try coordinator.stageTransaction(incomingTx, at: sentTxHeight) try coordinator.stageTransaction(incomingTx, at: sentTxHeight)
try coordinator.applyStaged(blockheight: activationHeight + 5) try coordinator.applyStaged(blockheight: activationHeight + 5)
@ -259,15 +282,16 @@ class NetworkUpgradeTests: XCTestCase {
let afterSendExpectation = XCTestExpectation(description: "aftersend") let afterSendExpectation = XCTestExpectation(description: "aftersend")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
afterSendExpectation.fulfill() afterSendExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [afterSendExpectation], timeout: 10) wait(for: [afterSendExpectation], timeout: 10)
guard let confirmedTx = try coordinator.synchronizer.allConfirmedTransactions(from: nil, limit: Int.max)?.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId }) else { guard
let confirmedTx = try coordinator.synchronizer.allConfirmedTransactions(from: nil, limit: Int.max)?
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
else {
XCTFail("the sent transaction is not listed as a confirmed transaction") XCTFail("the sent transaction is not listed as a confirmed transaction")
return return
} }
@ -276,11 +300,17 @@ 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() throws {
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, branchID: branchID, chainName: chainName, length: 15300) try FakeChainBuilder.buildChain(
darksideWallet: coordinator.service,
birthday: birthday,
networkActivationHeight: activationHeight,
branchID: branchID,
chainName: chainName,
length: 15300
)
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
let offset = 5 let offset = 5
@ -289,10 +319,8 @@ class NetworkUpgradeTests: XCTestCase {
let verifiedBalancePreActivation = coordinator.synchronizer.initializer.getVerifiedBalance() let verifiedBalancePreActivation = coordinator.synchronizer.initializer.getVerifiedBalance()
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [firstSyncExpectation], timeout: 120) wait(for: [firstSyncExpectation], timeout: 120)
@ -303,57 +331,64 @@ class NetworkUpgradeTests: XCTestCase {
} }
let sendExpectation = XCTestExpectation(description: "send expectation") let sendExpectation = XCTestExpectation(description: "send expectation")
var p: PendingTransactionEntity? = nil var pendingEntity: PendingTransactionEntity?
let spendAmount: Int64 = 10000 let spendAmount: Int64 = 10000
/* /*
send transaction to recipient address send transaction to recipient address
*/ */
coordinator.synchronizer.sendToAddress(spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, toAddress: self.testRecipientAddress, memo: "this is a test", from: 0, resultBlock: { (result) in coordinator.synchronizer.sendToAddress(
switch result { spendingKey: self.coordinator.spendingKeys!.first!,
case .failure(let e): zatoshi: spendAmount,
self.handleError(e) toAddress: self.testRecipientAddress,
case .success(let pendingTx): memo: "this is a test",
p = pendingTx from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
} }
sendExpectation.fulfill() )
})
wait(for: [sendExpectation], timeout: 11) wait(for: [sendExpectation], timeout: 11)
guard let pendingTx = p else { guard let pendingTx = pendingEntity else {
XCTFail("no pending transaction after sending") XCTFail("no pending transaction after sending")
try coordinator.stop() try coordinator.stop()
return return
} }
/* /*
getIncomingTransaction getIncomingTransaction
*/ */
guard let _ = try coordinator.getIncomingTransactions()?.first else { guard try coordinator.getIncomingTransactions()?.first != nil else {
XCTFail("no incoming transaction") XCTFail("no incoming transaction")
try coordinator.stop() try coordinator.stop()
return return
} }
/* /*
don't stage transaction don't stage transaction
*/ */
try coordinator.applyStaged(blockheight: activationHeight + offset) try coordinator.applyStaged(blockheight: activationHeight + offset)
sleep(2) sleep(2)
let afterSendExpectation = XCTestExpectation(description: "aftersend") let afterSendExpectation = XCTestExpectation(description: "aftersend")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
afterSendExpectation.fulfill() afterSendExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [afterSendExpectation], timeout: 10) wait(for: [afterSendExpectation], timeout: 10)
guard try coordinator.synchronizer.allConfirmedTransactions(from: nil, limit: Int.max)?.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId }) == nil else { guard
try coordinator.synchronizer.allConfirmedTransactions(from: nil, limit: Int.max)?
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId }) == nil
else {
XCTFail("the sent transaction should not be not listed as a confirmed transaction") XCTFail("the sent transaction should not be not listed as a confirmed transaction")
return return
} }
@ -362,20 +397,25 @@ 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() throws {
try FakeChainBuilder.buildChainMixedFunds(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, branchID: branchID, chainName: chainName, length: 15300) try FakeChainBuilder.buildChainMixedFunds(
darksideWallet: coordinator.service,
birthday: birthday,
networkActivationHeight: activationHeight,
branchID: branchID,
chainName: chainName,
length: 15300
)
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
try coordinator.applyStaged(blockheight: activationHeight - 1) try coordinator.applyStaged(blockheight: activationHeight - 1)
sleep(3) sleep(3)
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [firstSyncExpectation], timeout: 120) wait(for: [firstSyncExpectation], timeout: 120)
@ -386,14 +426,12 @@ class NetworkUpgradeTests: XCTestCase {
sleep(2) sleep(2)
let secondSyncExpectation = XCTestExpectation(description: "second sync") let secondSyncExpectation = XCTestExpectation(description: "second sync")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
secondSyncExpectation.fulfill() secondSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [secondSyncExpectation], timeout: 10) wait(for: [secondSyncExpectation], timeout: 10)
guard try coordinator.synchronizer.allReceivedTransactions().filter({$0.minedHeight > activationHeight}).count > 0 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")
return return
} }
@ -401,27 +439,34 @@ class NetworkUpgradeTests: XCTestCase {
XCTAssertTrue(preActivationBalance < postActivationBalance, "This test requires that funds post activation are greater that pre activation") XCTAssertTrue(preActivationBalance < postActivationBalance, "This test requires that funds post activation are greater that pre activation")
let sendExpectation = XCTestExpectation(description: "send expectation") let sendExpectation = XCTestExpectation(description: "send expectation")
var p: PendingTransactionEntity? = nil var pendingEntity: PendingTransactionEntity?
// spend all the funds // spend all the funds
let spendAmount: Int64 = postActivationBalance - Int64(network.constants.defaultFee(for: activationHeight)) let spendAmount: Int64 = postActivationBalance - Int64(network.constants.defaultFee(for: activationHeight))
/* /*
send transaction to recipient address send transaction to recipient address
*/ */
coordinator.synchronizer.sendToAddress(spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, toAddress: self.testRecipientAddress, memo: "this is a test", from: 0, resultBlock: { (result) in coordinator.synchronizer.sendToAddress(
switch result { spendingKey: self.coordinator.spendingKeys!.first!,
case .failure(let e): zatoshi: spendAmount,
self.handleError(e) toAddress: self.testRecipientAddress,
case .success(let pendingTx): memo: "this is a test",
p = pendingTx from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
} }
sendExpectation.fulfill() )
})
wait(for: [sendExpectation], timeout: 15) wait(for: [sendExpectation], timeout: 15)
guard let _ = p else { guard pendingEntity != nil else {
XCTFail("no pending transaction after sending") XCTFail("no pending transaction after sending")
try coordinator.stop() try coordinator.stop()
return return

View File

@ -8,16 +8,19 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional
class NotesRepositoryTests: XCTestCase { class NotesRepositoryTests: XCTestCase {
var sentNotesRepository: SentNotesRepository! var sentNotesRepository: SentNotesRepository!
var receivedNotesRepository: ReceivedNoteRepository! var receivedNotesRepository: ReceivedNoteRepository!
override func setUp() { override func setUp() {
super.setUp()
sentNotesRepository = TestDbBuilder.sentNotesRepository() sentNotesRepository = TestDbBuilder.sentNotesRepository()
receivedNotesRepository = TestDbBuilder.receivedNotesRepository() receivedNotesRepository = TestDbBuilder.receivedNotesRepository()
} }
override func tearDown() { override func tearDown() {
super.tearDown()
sentNotesRepository = nil sentNotesRepository = nil
receivedNotesRepository = nil receivedNotesRepository = nil
} }
@ -32,6 +35,5 @@ class NotesRepositoryTests: XCTestCase {
var count: Int? var count: Int?
XCTAssertNoThrow(try { count = try receivedNotesRepository.count() }()) XCTAssertNoThrow(try { count = try receivedNotesRepository.count() }())
XCTAssertEqual(count, 27) XCTAssertEqual(count, 27)
} }
} }

View File

@ -7,19 +7,24 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
class NullBytesTests: XCTestCase { class NullBytesTests: XCTestCase {
let networkType = NetworkType.mainnet let networkType = NetworkType.mainnet
func testZaddrNullBytes() throws { func testZaddrNullBytes() throws {
let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp" // this is a valid zAddr. if you send ZEC to it, you will be contributing to Human Rights Foundation. see more ways to help at https://paywithz.cash/ // this is a valid zAddr. if you send ZEC to it, you will be contributing to Human Rights Foundation. see more ways to help at https://paywithz.cash/
let ZaddrWithNullBytes = "\(validZaddr)\0something else that makes the address invalid" let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
let zAddrWithNullBytes = "\(validZaddr)\0something else that makes the address invalid"
XCTAssertFalse(try ZcashRustBackend.isValidShieldedAddress(ZaddrWithNullBytes, networkType: networkType)) XCTAssertFalse(try ZcashRustBackend.isValidShieldedAddress(zAddrWithNullBytes, networkType: networkType))
} }
func testTaddrNullBytes() throws { func testTaddrNullBytes() throws {
let validTAddr = "t1J5pTRzJi7j8Xw9VJTrPxPEkaigr69gKVT" // this is a valid tAddr. if you send ZEC to it, you will be contributing to Human Rights Foundation. see more ways to help at https://paywithz.cash/ // this is a valid tAddr. if you send ZEC to it, you will be contributing to Human Rights Foundation. see more ways to help at https://paywithz.cash/
let TaddrWithNullBytes = "\(validTAddr)\0fasdfasdf" let validTAddr = "t1J5pTRzJi7j8Xw9VJTrPxPEkaigr69gKVT"
XCTAssertFalse(try ZcashRustBackend.isValidTransparentAddress(TaddrWithNullBytes, networkType: networkType)) let tAddrWithNullBytes = "\(validTAddr)\0fasdfasdf"
XCTAssertFalse(try ZcashRustBackend.isValidTransparentAddress(tAddrWithNullBytes, networkType: networkType))
} }
func testInitAccountTableNullBytes() throws { func testInitAccountTableNullBytes() throws {
@ -27,11 +32,24 @@ class NullBytesTests: XCTestCase {
let goodHash = "00000000015c597fab53f58b9e1ededbe8bd83ca0203788e2039eceeb0d65ca6" let goodHash = "00000000015c597fab53f58b9e1ededbe8bd83ca0203788e2039eceeb0d65ca6"
let time: UInt32 = 1582235356 let time: UInt32 = 1582235356
let height: Int32 = 735000 let height: Int32 = 735000
// swiftlint:disable:next line_length
let wrongTree = "0161f2ff97ff6ac6a90f9bce76c11710460f4944d8695aecc7dc99e34cad0131040011015325b185e23e82562db27817be996ffade9597181244f67efc40561aeb9dde1101daeffadc9e38f755bcb55a847a1278518a0ba4a2ef33b2fe01bbb3eb242ab0070000000000011c51f9077e3f7e28e8e337eaf4bb99b41acbc853a37dcc1e172467a1c919fe4100010bb1f55481b2268ef31997dc0fb6b48a530bc17870220f156d832326c433eb0a010b3768d3bf7868a67823e022f49be67982d0588e7041c498a756024\0750065a4a0001a9e1bf4bccb48b14b544e770f21d48f2d3ad8d6ca54eccc92f60634e3078eb48013a1f7fb005388ac6f04099b647ed85d8b025d8ae4b178c2376b473b121b8c052000001d2ea556f49fb934dc76f087935a5c07788000b4e3aae24883adfec51b5f4d260" let wrongTree = "0161f2ff97ff6ac6a90f9bce76c11710460f4944d8695aecc7dc99e34cad0131040011015325b185e23e82562db27817be996ffade9597181244f67efc40561aeb9dde1101daeffadc9e38f755bcb55a847a1278518a0ba4a2ef33b2fe01bbb3eb242ab0070000000000011c51f9077e3f7e28e8e337eaf4bb99b41acbc853a37dcc1e172467a1c919fe4100010bb1f55481b2268ef31997dc0fb6b48a530bc17870220f156d832326c433eb0a010b3768d3bf7868a67823e022f49be67982d0588e7041c498a756024\0750065a4a0001a9e1bf4bccb48b14b544e770f21d48f2d3ad8d6ca54eccc92f60634e3078eb48013a1f7fb005388ac6f04099b647ed85d8b025d8ae4b178c2376b473b121b8c052000001d2ea556f49fb934dc76f087935a5c07788000b4e3aae24883adfec51b5f4d260"
// swiftlint:disable:next line_length
let goodTree = "0161f2ff97ff6ac6a90f9bce76c11710460f4944d8695aecc7dc99e34cad0131040011015325b185e23e82562db27817be996ffade9597181244f67efc40561aeb9dde1101daeffadc9e38f755bcb55a847a1278518a0ba4a2ef33b2fe01bbb3eb242ab0070000000000011c51f9077e3f7e28e8e337eaf4bb99b41acbc853a37dcc1e172467a1c919fe4100010bb1f55481b2268ef31997dc0fb6b48a530bc17870220f156d832326c433eb0a010b3768d3bf7868a67823e022f49be67982d0588e7041c498a756024750065a4a0001a9e1bf4bccb48b14b544e770f21d48f2d3ad8d6ca54eccc92f60634e3078eb48013a1f7fb005388ac6f04099b647ed85d8b025d8ae4b178c2376b473b121b8c052000001d2ea556f49fb934dc76f087935a5c07788000b4e3aae24883adfec51b5f4d260" let goodTree = "0161f2ff97ff6ac6a90f9bce76c11710460f4944d8695aecc7dc99e34cad0131040011015325b185e23e82562db27817be996ffade9597181244f67efc40561aeb9dde1101daeffadc9e38f755bcb55a847a1278518a0ba4a2ef33b2fe01bbb3eb242ab0070000000000011c51f9077e3f7e28e8e337eaf4bb99b41acbc853a37dcc1e172467a1c919fe4100010bb1f55481b2268ef31997dc0fb6b48a530bc17870220f156d832326c433eb0a010b3768d3bf7868a67823e022f49be67982d0588e7041c498a756024750065a4a0001a9e1bf4bccb48b14b544e770f21d48f2d3ad8d6ca54eccc92f60634e3078eb48013a1f7fb005388ac6f04099b647ed85d8b025d8ae4b178c2376b473b121b8c052000001d2ea556f49fb934dc76f087935a5c07788000b4e3aae24883adfec51b5f4d260"
XCTAssertThrowsError(try ZcashRustBackend.initBlocksTable(dbData: __dataDbURL(), height: height , hash: wrongHash, time: time, saplingTree: goodTree, networkType: networkType), "InitBlocksTable with Null bytes on hash string should have failed") { (error) in XCTAssertThrowsError(
try ZcashRustBackend.initBlocksTable(
dbData: __dataDbURL(),
height: height,
hash: wrongHash,
time: time,
saplingTree: goodTree,
networkType: networkType
),
"InitBlocksTable with Null bytes on hash string should have failed"
) { error in
guard let rustError = error as? RustWeldingError else { guard let rustError = error as? RustWeldingError else {
XCTFail("Expected RustWeldingError") XCTFail("Expected RustWeldingError")
return return
@ -45,8 +63,17 @@ class NullBytesTests: XCTestCase {
} }
} }
XCTAssertThrowsError(try ZcashRustBackend.initBlocksTable(dbData: __dataDbURL(), height: height , hash: goodHash, time: time, saplingTree: wrongTree, networkType: networkType), "InitBlocksTable with Null bytes on saplingTree string should have failed") { (error) in XCTAssertThrowsError(
try ZcashRustBackend.initBlocksTable(
dbData: __dataDbURL(),
height: height,
hash: goodHash,
time: time,
saplingTree: wrongTree,
networkType: networkType
),
"InitBlocksTable with Null bytes on saplingTree string should have failed"
) { error in
guard let rustError = error as? RustWeldingError else { guard let rustError = error as? RustWeldingError else {
XCTFail("Expected RustWeldingError") XCTFail("Expected RustWeldingError")
return return
@ -62,12 +89,16 @@ class NullBytesTests: XCTestCase {
} }
func testderiveExtendedFullViewingKeyWithNullBytes() throws { func testderiveExtendedFullViewingKeyWithNullBytes() throws {
let wrongSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mq\0uy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" //this spending key corresponds to the "demo app reference seed" // swiftlint:disable:next line_length
let wrongSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mq\0uy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" // this spending key corresponds to the "demo app reference seed"
// swiftlint:disable:next line_length
let goodSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" let goodSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv"
XCTAssertThrowsError(try ZcashRustBackend.deriveExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType),"Should have thrown an error but didn't! this is dangerous!") { (error) in XCTAssertThrowsError(
try ZcashRustBackend.deriveExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType),
"Should have thrown an error but didn't! this is dangerous!"
) { error in
guard let rustError = error as? RustWeldingError else { guard let rustError = error as? RustWeldingError else {
XCTFail("Expected RustWeldingError") XCTFail("Expected RustWeldingError")
return return
@ -82,15 +113,17 @@ class NullBytesTests: XCTestCase {
} }
XCTAssertNoThrow(try ZcashRustBackend.deriveExtendedFullViewingKey(goodSpendingKeys, networkType: networkType)) XCTAssertNoThrow(try ZcashRustBackend.deriveExtendedFullViewingKey(goodSpendingKeys, networkType: networkType))
} }
func testCheckNullBytes() throws { func testCheckNullBytes() throws {
let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp" // this is a valid zAddr. if you send ZEC to it, you will be contributing to Human Rights Foundation. see more ways to help at https://paywithz.cash/ // this is a valid zAddr. if you send ZEC to it, you will be contributing to Human Rights Foundation. see more ways to help at https://paywithz.cash/
let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
XCTAssertFalse(validZaddr.containsCStringNullBytesBeforeStringEnding()) XCTAssertFalse(validZaddr.containsCStringNullBytesBeforeStringEnding())
XCTAssertTrue(
XCTAssertTrue("zs1gqtfu59z20s\09t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp".containsCStringNullBytesBeforeStringEnding()) "zs1gqtfu59z20s\09t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
.containsCStringNullBytesBeforeStringEnding()
)
XCTAssertTrue("\0".containsCStringNullBytesBeforeStringEnding()) XCTAssertTrue("\0".containsCStringNullBytesBeforeStringEnding())
XCTAssertFalse("".containsCStringNullBytesBeforeStringEnding()) XCTAssertFalse("".containsCStringNullBytesBeforeStringEnding())
} }

View File

@ -7,13 +7,20 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional
class PagedTransactionRepositoryTests: XCTestCase { class PagedTransactionRepositoryTests: XCTestCase {
var pagedTransactionRepository: PaginatedTransactionRepository! var pagedTransactionRepository: PaginatedTransactionRepository!
var transactionRepository: TransactionRepository! var transactionRepository: TransactionRepository!
override func setUp() { override func setUp() {
transactionRepository = MockTransactionRepository(unminedCount: 5, receivedCount: 150, sentCount: 100, network: ZcashNetworkBuilder.network(for: .testnet)) super.setUp()
transactionRepository = MockTransactionRepository(
unminedCount: 5,
receivedCount: 150,
sentCount: 100,
network: ZcashNetworkBuilder.network(for: .testnet)
)
pagedTransactionRepository = PagedTransactionDAO(repository: transactionRepository) pagedTransactionRepository = PagedTransactionDAO(repository: transactionRepository)
} }
@ -21,18 +28,18 @@ class PagedTransactionRepositoryTests: XCTestCase {
let pageSize = pagedTransactionRepository.pageSize let pageSize = pagedTransactionRepository.pageSize
let pageCount = pagedTransactionRepository.pageCount let pageCount = pagedTransactionRepository.pageCount
let totalItems = pagedTransactionRepository.itemCount let totalItems = pagedTransactionRepository.itemCount
for i in 0 ..< pageCount/pageSize {
guard let page = try? pagedTransactionRepository.page(i) else { for index in 0 ..< pageCount / pageSize {
XCTFail("page failed to get page \(i)") guard let page = try? pagedTransactionRepository.page(index) else {
XCTFail("page failed to get page \(index)")
return return
} }
if i < pageCount { if index < pageCount {
XCTAssert(page.count == pageSize) XCTAssert(page.count == pageSize)
} else { } else {
// last page has to have the remainding items // last page has to have the remainding items
XCTAssertEqual(page.count, totalItems - (pageSize * pageCount)) XCTAssertEqual(page.count, totalItems - (pageSize * pageCount))
} }
} }
} }
} }

View File

@ -7,22 +7,24 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable force_try force_unwrapping implicitly_unwrapped_optional
class PendingTransactionRepositoryTests: XCTestCase { class PendingTransactionRepositoryTests: XCTestCase {
var pendingRepository: PendingTransactionRepository!
let dbUrl = try! TestDbBuilder.pendingTransactionsDbURL() let dbUrl = try! TestDbBuilder.pendingTransactionsDbURL()
let recipientAddress = "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6" let recipientAddress = "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6"
var pendingRepository: PendingTransactionRepository!
override func setUp() { override func setUp() {
super.setUp()
cleanUpDb() cleanUpDb()
let dao = PendingTransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: try! TestDbBuilder.pendingTransactionsDbURL().absoluteString)) let dao = PendingTransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: try! TestDbBuilder.pendingTransactionsDbURL().absoluteString))
try! dao.createrTableIfNeeded() try! dao.createrTableIfNeeded()
pendingRepository = dao pendingRepository = dao
} }
override func tearDown() { override func tearDown() {
super.tearDown()
cleanUpDb() cleanUpDb()
} }
@ -31,77 +33,79 @@ class PendingTransactionRepositoryTests: XCTestCase {
} }
func testCreate() { func testCreate() {
let transaction = createAndStoreMockedTransaction()
let tx = createAndStoreMockedTransaction() guard let id = transaction.id, id >= 0 else {
guard let id = tx.id, id >= 0 else {
XCTFail("failed to create mocked transaction that was just inserted") XCTFail("failed to create mocked transaction that was just inserted")
return return
} }
var expectedTx: PendingTransactionEntity? var expectedTx: PendingTransactionEntity?
XCTAssertNoThrow(try { expectedTx = try pendingRepository.find(by: id)}()) XCTAssertNoThrow(try { expectedTx = try pendingRepository.find(by: id) }())
guard let expected = expectedTx else { guard let expected = expectedTx else {
XCTFail("failed to retrieve mocked transaction by id \(id) that was just inserted") XCTFail("failed to retrieve mocked transaction by id \(id) that was just inserted")
return return
} }
XCTAssertEqual(tx.accountIndex, expected.accountIndex) XCTAssertEqual(transaction.accountIndex, expected.accountIndex)
XCTAssertEqual(tx.value, expected.value) XCTAssertEqual(transaction.value, expected.value)
XCTAssertEqual(tx.toAddress, expected.toAddress) XCTAssertEqual(transaction.toAddress, expected.toAddress)
} }
func testFindById() { func testFindById() {
let tx = createAndStoreMockedTransaction() let transaction = createAndStoreMockedTransaction()
var expected: PendingTransactionEntity? var expected: PendingTransactionEntity?
guard let id = tx.id else { guard let id = transaction.id else {
XCTFail("transaction with no id") XCTFail("transaction with no id")
return return
} }
XCTAssertNoThrow(try { expected = try pendingRepository.find(by: id)}())
XCTAssertNoThrow(try { expected = try pendingRepository.find(by: id) }())
XCTAssertNotNil(expected) XCTAssertNotNil(expected)
} }
func testCancel() { func testCancel() {
let tx = createAndStoreMockedTransaction() let transaction = createAndStoreMockedTransaction()
guard let id = tx.id else {
XCTFail("transaction with no id") guard let id = transaction.id else {
return XCTFail("transaction with no id")
} return
}
guard id >= 0 else { guard id >= 0 else {
XCTFail("failed to create mocked transaction that was just inserted") XCTFail("failed to create mocked transaction that was just inserted")
return return
} }
XCTAssertNoThrow(try pendingRepository.cancel(tx)) XCTAssertNoThrow(try pendingRepository.cancel(transaction))
} }
func testDelete() { func testDelete() {
let tx = createAndStoreMockedTransaction() let transaction = createAndStoreMockedTransaction()
guard let id = tx.id else {
XCTFail("transaction with no id") guard let id = transaction.id else {
return XCTFail("transaction with no id")
} return
}
guard id >= 0 else { guard id >= 0 else {
XCTFail("failed to create mocked transaction that was just inserted") XCTFail("failed to create mocked transaction that was just inserted")
return return
} }
XCTAssertNoThrow(try pendingRepository.delete(tx)) XCTAssertNoThrow(try pendingRepository.delete(transaction))
var unexpectedTx: PendingTransactionEntity? var unexpectedTx: PendingTransactionEntity?
XCTAssertNoThrow(try { unexpectedTx = try pendingRepository.find(by: id) }()) XCTAssertNoThrow(try { unexpectedTx = try pendingRepository.find(by: id) }())
XCTAssertNil(unexpectedTx) XCTAssertNil(unexpectedTx)
} }
func testGetAll() { func testGetAll() {
var mockTransactions = [PendingTransactionEntity]() var mockTransactions: [PendingTransactionEntity] = []
for _ in 1...100 { for _ in 1...100 {
mockTransactions.append(createAndStoreMockedTransaction()) mockTransactions.append(createAndStoreMockedTransaction())
} }
@ -116,20 +120,21 @@ class PendingTransactionRepositoryTests: XCTestCase {
} }
XCTAssertEqual(mockTransactions.count, allTxs.count) XCTAssertEqual(mockTransactions.count, allTxs.count)
} }
func testUpdate() { func testUpdate() {
let newAccountIndex = 1 let newAccountIndex = 1
let newValue: Int = 123_456 let newValue: Int = 123_456
let tx = createAndStoreMockedTransaction() let transaction = createAndStoreMockedTransaction()
guard let id = tx.id else {
XCTFail("transaction with no id") guard let id = transaction.id else {
return XCTFail("transaction with no id")
} return
}
var stored: PendingTransactionEntity? var stored: PendingTransactionEntity?
XCTAssertNoThrow(try { stored = try pendingRepository.find(by: id)}()) XCTAssertNoThrow(try { stored = try pendingRepository.find(by: id) }())
guard stored != nil else { guard stored != nil else {
XCTFail("failed to store tx") XCTFail("failed to store tx")
@ -152,12 +157,12 @@ class PendingTransactionRepositoryTests: XCTestCase {
} }
func createAndStoreMockedTransaction() -> PendingTransactionEntity { func createAndStoreMockedTransaction() -> PendingTransactionEntity {
var tx = mockTransaction() var transaction = mockTransaction()
var id: Int? var id: Int?
XCTAssertNoThrow(try { id = try pendingRepository.create(tx) }()) XCTAssertNoThrow(try { id = try pendingRepository.create(transaction) }())
tx.id = Int(id ?? -1) transaction.id = Int(id ?? -1)
return tx return transaction
} }
func testPerformanceExample() { func testPerformanceExample() {
@ -170,5 +175,4 @@ class PendingTransactionRepositoryTests: XCTestCase {
private func mockTransaction() -> PendingTransactionEntity { private func mockTransaction() -> PendingTransactionEntity {
PendingTransaction(value: Int.random(in: 1 ... 1_000_000), toAddress: recipientAddress, memo: nil, account: 0) PendingTransaction(value: Int.random(in: 1 ... 1_000_000), toAddress: recipientAddress, memo: nil, account: 0)
} }
} }

View File

@ -7,10 +7,14 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional
class PendingTransactionUpdatesTest: XCTestCase { class PendingTransactionUpdatesTest: 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? // TODO: Parameterize this from environment?
// swiftlint:disable:next line_length
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a" //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
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a"
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 663150 var birthday: BlockHeight = 663150
@ -20,12 +24,12 @@ class PendingTransactionUpdatesTest: XCTestCase {
var sentTransactionExpectation = XCTestExpectation(description: "sent") var sentTransactionExpectation = XCTestExpectation(description: "sent")
var expectedReorgHeight: BlockHeight = 665188 var expectedReorgHeight: BlockHeight = 665188
var expectedRewindHeight: BlockHeight = 665188 var expectedRewindHeight: BlockHeight = 665188
var reorgExpectation: XCTestExpectation = XCTestExpectation(description: "reorg") var reorgExpectation = XCTestExpectation(description: "reorg")
let branchID = "2bb40e60" let branchID = "2bb40e60"
let chainName = "main" let chainName = "main"
let network = DarksideWalletDNetwork() let network = DarksideWalletDNetwork()
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
coordinator = try TestCoordinator( coordinator = try TestCoordinator(
seed: seedPhrase, seed: seedPhrase,
walletBirthday: birthday, walletBirthday: birthday,
@ -36,6 +40,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
try coordinator.stop() try coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
@ -44,23 +49,21 @@ class PendingTransactionUpdatesTest: XCTestCase {
} }
@objc func handleReorg(_ notification: Notification) { @objc func handleReorg(_ notification: Notification) {
guard
guard let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight
// let rewindHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else {
else { XCTFail("empty reorg notification")
XCTFail("empty reorg notification") return
return
} }
// XCTAssertEqual(rewindHeight, expectedRewindHeight)
XCTAssertEqual(reorgHeight, expectedReorgHeight) XCTAssertEqual(reorgHeight, expectedReorgHeight)
reorgExpectation.fulfill() reorgExpectation.fulfill()
} }
func testPendingTransactionMinedHeightUpdated() throws { func testPendingTransactionMinedHeightUpdated() throws {
/* /*
1. create fake chain 1. create fake chain
*/ */
LoggerProxy.info("1. create fake chain") LoggerProxy.info("1. create fake chain")
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName) try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName)
@ -69,13 +72,12 @@ class PendingTransactionUpdatesTest: XCTestCase {
sleep(2) sleep(2)
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
/* /*
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: { (s) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
@ -84,37 +86,55 @@ class PendingTransactionUpdatesTest: XCTestCase {
sleep(1) sleep(1)
let sendExpectation = XCTestExpectation(description: "send expectation") let sendExpectation = XCTestExpectation(description: "send expectation")
var p: PendingTransactionEntity? = nil var pendingEntity: PendingTransactionEntity?
/* /*
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(spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: 20000, toAddress: self.testRecipientAddress, memo: "this is a test", from: 0, resultBlock: { (result) in coordinator.synchronizer.sendToAddress(
switch result { // swiftlint:disable:next force_unwrapping
case .failure(let e): spendingKey: self.coordinator.spendingKeys!.first!,
self.handleError(e) zatoshi: 20000,
case .success(let pendingTx): toAddress: self.testRecipientAddress,
p = pendingTx memo: "this is a test",
from: 0,
resultBlock: { result in
switch result {
case .failure(let e):
self.handleError(e)
case .success(let pendingTx):
pendingEntity = pendingTx
}
sendExpectation.fulfill()
} }
sendExpectation.fulfill() )
})
wait(for: [sendExpectation], timeout: 11) wait(for: [sendExpectation], timeout: 11)
guard let pendingUnconfirmedTx = p else { guard let pendingUnconfirmedTx = pendingEntity else {
XCTFail("no pending transaction after sending") XCTFail("no pending transaction after sending")
try coordinator.stop() try coordinator.stop()
return return
} }
XCTAssertFalse(pendingUnconfirmedTx.isConfirmed(currentHeight: 663188), "pending transaction evaluated as confirmed when it shouldn't") XCTAssertFalse(
XCTAssertFalse(pendingUnconfirmedTx.isMined, "pending transaction evaluated as mined when it shouldn't") 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"
)
XCTAssertTrue(pendingUnconfirmedTx.isPending(currentHeight: 663188), "pending transaction evaluated as not pending when it should be") XCTAssertTrue(
pendingUnconfirmedTx.isPending(currentHeight: 663188),
"pending transaction evaluated as not pending when it should be"
)
/** /**
3. getIncomingTransaction 3. getIncomingTransaction
*/ */
LoggerProxy.info("3. getIncomingTransaction") LoggerProxy.info("3. getIncomingTransaction")
guard let incomingTx = try coordinator.getIncomingTransactions()?.first else { guard let incomingTx = try coordinator.getIncomingTransactions()?.first else {
XCTFail("no incoming transaction") XCTFail("no incoming transaction")
@ -124,31 +144,34 @@ class PendingTransactionUpdatesTest: XCTestCase {
let sentTxHeight: BlockHeight = 663189 let sentTxHeight: BlockHeight = 663189
/* /*
4. stage transaction at sentTxHeight 4. stage transaction at sentTxHeight
*/ */
LoggerProxy.info("4. stage transaction at \(sentTxHeight)") LoggerProxy.info("4. stage transaction at \(sentTxHeight)")
try coordinator.stageBlockCreate(height: sentTxHeight) try coordinator.stageBlockCreate(height: sentTxHeight)
try coordinator.stageTransaction(incomingTx, at: sentTxHeight) try coordinator.stageTransaction(incomingTx, at: sentTxHeight)
/* /*
5. applyHeight(sentTxHeight) 5. applyHeight(sentTxHeight)
*/ */
LoggerProxy.info("5. applyHeight(\(sentTxHeight))") LoggerProxy.info("5. applyHeight(\(sentTxHeight))")
try coordinator.applyStaged(blockheight: sentTxHeight) try coordinator.applyStaged(blockheight: sentTxHeight)
sleep(2) sleep(2)
/* /*
6. sync to latest height 6. sync to latest height
*/ */
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(completion: { (s) in try coordinator.sync(
secondSyncExpectation.fulfill() completion: { _ in
}, error: self.handleError) secondSyncExpectation.fulfill()
},
error: self.handleError
)
wait(for: [secondSyncExpectation], timeout: 5) wait(for: [secondSyncExpectation], timeout: 5)
@ -158,16 +181,16 @@ class PendingTransactionUpdatesTest: XCTestCase {
} }
/* /*
6a. verify that there's a pending transaction with a mined height of sentTxHeight 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)") LoggerProxy.info("6a. verify that there's a pending transaction with a mined height of \(sentTxHeight)")
XCTAssertEqual(afterStagePendingTx.minedHeight, sentTxHeight) XCTAssertEqual(afterStagePendingTx.minedHeight, sentTxHeight)
XCTAssertTrue(afterStagePendingTx.isMined, "pending transaction shown as unmined when it has been mined") XCTAssertTrue(afterStagePendingTx.isMined, "pending transaction shown as unmined when it has been mined")
XCTAssertTrue(afterStagePendingTx.isPending(currentHeight: sentTxHeight)) XCTAssertTrue(afterStagePendingTx.isPending(currentHeight: sentTxHeight))
/* /*
7. stage 15 blocks from sentTxHeight 7. stage 15 blocks from sentTxHeight
*/ */
LoggerProxy.info("7. stage 15 blocks from \(sentTxHeight)") LoggerProxy.info("7. stage 15 blocks from \(sentTxHeight)")
try coordinator.stageBlockCreate(height: sentTxHeight + 1, count: 15) try coordinator.stageBlockCreate(height: sentTxHeight + 1, count: 15)
sleep(2) sleep(2)
@ -183,20 +206,18 @@ class PendingTransactionUpdatesTest: XCTestCase {
*/ */
LoggerProxy.info("last sync to latest height: \(lastStageHeight)") LoggerProxy.info("last sync to latest height: \(lastStageHeight)")
try coordinator.sync(completion: { (s) in try coordinator.sync(completion: { _ in
syncToConfirmExpectation.fulfill() syncToConfirmExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [syncToConfirmExpectation], timeout: 6) wait(for: [syncToConfirmExpectation], timeout: 6)
var supposedlyPendingUnexistingTransaction: PendingTransactionEntity? = nil var supposedlyPendingUnexistingTransaction: PendingTransactionEntity?
XCTAssertNoThrow(try { supposedlyPendingUnexistingTransaction = try coordinator.synchronizer.allPendingTransactions().first }()) XCTAssertNoThrow(try { supposedlyPendingUnexistingTransaction = try coordinator.synchronizer.allPendingTransactions().first }())
XCTAssertNil(supposedlyPendingUnexistingTransaction) XCTAssertNil(supposedlyPendingUnexistingTransaction)
} }
func handleError(_ error: Error?) { func handleError(_ error: Error?) {
_ = try? coordinator.stop() _ = try? coordinator.stop()
guard let testError = error else { guard let testError = error else {
@ -207,6 +228,11 @@ class PendingTransactionUpdatesTest: XCTestCase {
} }
func hookToReOrgNotification() { func hookToReOrgNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(handleReorg(_:)), name: .blockProcessorHandledReOrg, object: nil) NotificationCenter.default.addObserver(
self,
selector: #selector(handleReorg(_:)),
name: .blockProcessorHandledReOrg,
object: nil
)
} }
} }

View File

@ -8,29 +8,35 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
@testable import SwiftProtobuf @testable import SwiftProtobuf
// swiftlint:disable implicitly_unwrapped_optional
class RawTransactionTests: XCTestCase { class RawTransactionTests: XCTestCase {
var rawTx: Data! var rawTx: Data!
var transactionRepository: TransactionSQLDAO! var transactionRepository: TransactionSQLDAO!
override func setUp() { override func setUp() {
super.setUp()
rawTx = Data(base64Encoded: txBase64String) rawTx = Data(base64Encoded: txBase64String)
} }
func testDeserialize() { func testDeserialize() {
guard let raw = Data(base64Encoded: txFromAndroidSDK) else { guard let raw = Data(base64Encoded: txFromAndroidSDK) else {
XCTFail("no raw data") XCTFail("no raw data")
return return
} }
let rawTransaction = RawTransaction.with({ (r) in let rawTransaction = RawTransaction.with({ rawTr in
r.data = raw rawTr.data = raw
}) })
XCTAssertNotNil(rawTransaction) XCTAssertNotNil(rawTransaction)
} }
let txFromAndroidSDK =
// swiftlint:disable:next line_length
"BAAAgIUgL4kAAAAAAACqoAoAECcAAAAAAAAB25ACAriMhxsTPYM1Lit6Ob0O1PssJwZ8e3/rLA+epll+1eKFT4lvPvDHjzY5udeJfHtvCcbFr+WL6rQGAjdrK7Y6Lu+Ofn1DOSOuVtv6z4FMSBB2EsrYkjsHkYTz93xpwTPnB2J42JrYdrq3qviFGaT3T06/dZGmuIxZVYqKFWaCKjniLNYh5epX3U33l7fjKzLKXiFXcJjAFmElvzbjEcEdMTvcDcno0swmE9XNPZ2iMNyeIX1TqEQJCvnTfK26D+ig768BZqdzYMDXu8sSDa0SdeHwJWmRUPoPUG1AbFQZPmzp3ZYITbpUigsKFJhB+lQ+DP62qKIP/uJq8wLCevMgfcVLFk6rzkgMGkz5/ySm4R5yFcj4GLQ65GcX8EvnMbM0WT4nwpjhttTSb6Fquzxb38VfqGoVBnH5VrRLwBrSsucr7dZtMcLxZCyZ29JPtfVf2o/BrEaigYGF0vLQnkDl4fWTnuafIMFPa4uO7tUtPGU6DwmRXX33Twz7P7ACAh8O6RK6jOJ+2vZWHXx+P0hqIHrYL/5lip60hQ9llRVYCSCiT7tvm57lIfDP5whUda6zwpyp4p7tjE4HvTMlOV0uZIuxN8J14gsrDnDbpn+hR+FCdEXS88of4UIe4oRNxjel70OAmMVCPFd2Y1O47jZo2BjHx4+1e9mou2SyjGSCepsbGizfn+5xz6jH2dYLGbWGlaNQyosCLlY6/UHswRV7y1NZcaFY5zh9ytMsdfYC20I3Zv1MyyDk1Jd6mlscRksP/iAjAUGr/PSCGEvOZ1z1dp2XIBism9NHi6Gy2uCFhOqpxfCM7DaKeqUYhH9R1Gfc5Vpy0LKAFVxOaTSha4odsOlgAE5GffwzPA11g43wR0scKGR0JImRVRfxyYVaKHggRaP/UnEsvXIiE+e3Mx0CA1aKfwbl9n0AlLuUVttAgZxUxnULHtC3jRt0l4gippyxD658jInGnbJfi3dKcI4H6OGeH7wznXpaUP7wfXIUa1kyv8FuZwkktXPLOW+1XKd9hAs+5UDqSajq/TZ0bNaCxOJQruASxqtUq+678hBmAhhqjk7IJ99KSy5y07bRnxVnDfVj5ZpEXBzkK08/G8pBA5qSGXjgEqXKRXFTgXzuW+m7c2jVCxpM5lDy5Adej7H61sz6T0N9OXwN8SjssWtS65unp6pKg1psowwNAAVdv/bvtyQOidwnf20j6LlitvVNNY3vGedRxCaeatvcDYb9cdBqqxzb3ki/Sn7beLOS/LF2YnYtLhdT8qB40dILmyOMeXOA1L2wzeCnbx9fIuCrGZDPjTRfIWWCMZVJ4RuJ5CqKFcYbYN+GrxtKkMmprwbcE8XxW71cUEsE9M5GtM8uDvmpTGFQKQTV2EcYoN0Go47CSPx4kfFSybGWsRir2aX+7i5QHNYvicfoWOXWSY60I7OHIOR8xs2qYBgfbb+7/ml1xxCm7WDdO6NfyJib9d6YpbIanSYh77ytwVlOu4HMhrs9K+NuSQYE7gKiHcoWOJkNYqW4VmLKiuw51kmm3Q0238VTOBOzba1oUz1ukR+9bPXjUhFmoY/Ueen9aZdobcE33YYu8iqua6E1zyknnsL+h/Zc7fT5FBpcQGEfYqsKKZKNsuCA+N6IRyq3OtmRVFhL1wFU1YdVQeKQfd69CTNpPJbnDkNVN7Img/+cEgMj5H5qDD4rE1vsq1cx48RnxV5lMa/T9WHnpcaAZ4/1qkFCKGhw6yRBcoGTWuH0vbS6071/iPJV5AhjlqNJMUw1s7BpojmxPOaWhAV3ztOOi3ZhiyPrtRJnUmfwfjYZO7FQq8eF4MDP7njiICsP74skUJoe0BBAcUgTatSIST2MkYgpa0QJAragwcRg1ZHFmTZYoEWhYp9K8FP8WLl1XrlSetPrEgo2zCrnb6GoOLSjnvF9CIdzXB2lpB8c71azvFAO3Kc58QDsIAFTN5cWMFMZTrmQi/AqsSVwaF92FZeNdAL5lVNfZ2LePrmXGSvGrDFjt3Wql/9KYebrGwBXpHdUOWRIF8FJMplg+jN2lz/0GGp39EkDZBXUlJUsCgnCInDyeKxI0rMJY8w5cVXyN9FW3/v6ns3UtYkFqNv2dcOT7WUBFgI0ZJ+M3i/L88MKga/dtntmZrXOxD6LXQ3hApLcm06t40cOVZdVlQ2doIMNzx1I66lXXjq8ij6L6Z40qwgrsFzX1RYUXooM+1HvmEmaffglOdqHkwtk3hK+4OWNoxdb8QFG5EbGCcahIpvRg0Wj60/zhzv6LfQC6/Yqd65QVjgYorV0Uy8eGGlD/aDDQp7nIu/1+EA5Aav0isl3TPBd/qmjGxdTI6BANQrB8rl58V+0rAY2AdBuXTM0bo8Ak/QcVDMp7arN8cViLCb3rq1Job34GZE8OzyRDuk2E499JqxClQPtPeJa47AKGV4OofPN6qOnbCEB2DlqrW4cPVDE95Ty+I1qUS2eDD0lZS0Ll/czErJ9H4cqFpFKUbXF6yZ+JbcPyMWqgaZHCatmM2rc6yxgWP8R6/KW/xdx++N/SGi3zQW2Zk7om26VZiBaGP36de0hut6VuHS54bobzSjZChwL4tcHcsgGW3cF+iWL3bHJ/Yo2b7r1r/lRboArd6c+8Ap2HFqCJYxPfdnqG5M8WfkbHecWkf4uAqn0aAWoFeBS7w+PGOuBE0RdQTt14UkwUu2OVfcxWyVjfDZ5DggMJ5bwszMzz2FVivIDZ7fUnZ5ftUkuALx4PcN58vJGycgWpBk55ZhIdFKTD7hx/Psv5myn2y7ZJarlMAS6PLGjjd7XKqi/3Q5f0RCR6QSB8ImecdwJfECwN80Dx6sNCy2eZAnx2kjXCYq5lJiGQ4feDvp/GBuf3SnggsFa4YZHThRWQH/83qe8RgTYcSuPc6p2MTfE2RVCHg4Ek8usMLcrORtH6a53/slMp10DW4i9fMF2GZroorM762i6y2OQq7/YzwWzsISBupQANlPGwRwFrqwXY2BSqNPgt1r7p68SFUZ8KtzWov1QWl9cDjFRAeISXo7CcjLyWgDQwBPKo4otCxnJ0tHIQpttJYwPx/39bLbaiou5HXFRYeBE1JxlyRQv1Be5KCGoL0kOdoe4psIKEZ5ijIYhCqoC"
let txFromAndroidSDK = "BAAAgIUgL4kAAAAAAACqoAoAECcAAAAAAAAB25ACAriMhxsTPYM1Lit6Ob0O1PssJwZ8e3/rLA+epll+1eKFT4lvPvDHjzY5udeJfHtvCcbFr+WL6rQGAjdrK7Y6Lu+Ofn1DOSOuVtv6z4FMSBB2EsrYkjsHkYTz93xpwTPnB2J42JrYdrq3qviFGaT3T06/dZGmuIxZVYqKFWaCKjniLNYh5epX3U33l7fjKzLKXiFXcJjAFmElvzbjEcEdMTvcDcno0swmE9XNPZ2iMNyeIX1TqEQJCvnTfK26D+ig768BZqdzYMDXu8sSDa0SdeHwJWmRUPoPUG1AbFQZPmzp3ZYITbpUigsKFJhB+lQ+DP62qKIP/uJq8wLCevMgfcVLFk6rzkgMGkz5/ySm4R5yFcj4GLQ65GcX8EvnMbM0WT4nwpjhttTSb6Fquzxb38VfqGoVBnH5VrRLwBrSsucr7dZtMcLxZCyZ29JPtfVf2o/BrEaigYGF0vLQnkDl4fWTnuafIMFPa4uO7tUtPGU6DwmRXX33Twz7P7ACAh8O6RK6jOJ+2vZWHXx+P0hqIHrYL/5lip60hQ9llRVYCSCiT7tvm57lIfDP5whUda6zwpyp4p7tjE4HvTMlOV0uZIuxN8J14gsrDnDbpn+hR+FCdEXS88of4UIe4oRNxjel70OAmMVCPFd2Y1O47jZo2BjHx4+1e9mou2SyjGSCepsbGizfn+5xz6jH2dYLGbWGlaNQyosCLlY6/UHswRV7y1NZcaFY5zh9ytMsdfYC20I3Zv1MyyDk1Jd6mlscRksP/iAjAUGr/PSCGEvOZ1z1dp2XIBism9NHi6Gy2uCFhOqpxfCM7DaKeqUYhH9R1Gfc5Vpy0LKAFVxOaTSha4odsOlgAE5GffwzPA11g43wR0scKGR0JImRVRfxyYVaKHggRaP/UnEsvXIiE+e3Mx0CA1aKfwbl9n0AlLuUVttAgZxUxnULHtC3jRt0l4gippyxD658jInGnbJfi3dKcI4H6OGeH7wznXpaUP7wfXIUa1kyv8FuZwkktXPLOW+1XKd9hAs+5UDqSajq/TZ0bNaCxOJQruASxqtUq+678hBmAhhqjk7IJ99KSy5y07bRnxVnDfVj5ZpEXBzkK08/G8pBA5qSGXjgEqXKRXFTgXzuW+m7c2jVCxpM5lDy5Adej7H61sz6T0N9OXwN8SjssWtS65unp6pKg1psowwNAAVdv/bvtyQOidwnf20j6LlitvVNNY3vGedRxCaeatvcDYb9cdBqqxzb3ki/Sn7beLOS/LF2YnYtLhdT8qB40dILmyOMeXOA1L2wzeCnbx9fIuCrGZDPjTRfIWWCMZVJ4RuJ5CqKFcYbYN+GrxtKkMmprwbcE8XxW71cUEsE9M5GtM8uDvmpTGFQKQTV2EcYoN0Go47CSPx4kfFSybGWsRir2aX+7i5QHNYvicfoWOXWSY60I7OHIOR8xs2qYBgfbb+7/ml1xxCm7WDdO6NfyJib9d6YpbIanSYh77ytwVlOu4HMhrs9K+NuSQYE7gKiHcoWOJkNYqW4VmLKiuw51kmm3Q0238VTOBOzba1oUz1ukR+9bPXjUhFmoY/Ueen9aZdobcE33YYu8iqua6E1zyknnsL+h/Zc7fT5FBpcQGEfYqsKKZKNsuCA+N6IRyq3OtmRVFhL1wFU1YdVQeKQfd69CTNpPJbnDkNVN7Img/+cEgMj5H5qDD4rE1vsq1cx48RnxV5lMa/T9WHnpcaAZ4/1qkFCKGhw6yRBcoGTWuH0vbS6071/iPJV5AhjlqNJMUw1s7BpojmxPOaWhAV3ztOOi3ZhiyPrtRJnUmfwfjYZO7FQq8eF4MDP7njiICsP74skUJoe0BBAcUgTatSIST2MkYgpa0QJAragwcRg1ZHFmTZYoEWhYp9K8FP8WLl1XrlSetPrEgo2zCrnb6GoOLSjnvF9CIdzXB2lpB8c71azvFAO3Kc58QDsIAFTN5cWMFMZTrmQi/AqsSVwaF92FZeNdAL5lVNfZ2LePrmXGSvGrDFjt3Wql/9KYebrGwBXpHdUOWRIF8FJMplg+jN2lz/0GGp39EkDZBXUlJUsCgnCInDyeKxI0rMJY8w5cVXyN9FW3/v6ns3UtYkFqNv2dcOT7WUBFgI0ZJ+M3i/L88MKga/dtntmZrXOxD6LXQ3hApLcm06t40cOVZdVlQ2doIMNzx1I66lXXjq8ij6L6Z40qwgrsFzX1RYUXooM+1HvmEmaffglOdqHkwtk3hK+4OWNoxdb8QFG5EbGCcahIpvRg0Wj60/zhzv6LfQC6/Yqd65QVjgYorV0Uy8eGGlD/aDDQp7nIu/1+EA5Aav0isl3TPBd/qmjGxdTI6BANQrB8rl58V+0rAY2AdBuXTM0bo8Ak/QcVDMp7arN8cViLCb3rq1Job34GZE8OzyRDuk2E499JqxClQPtPeJa47AKGV4OofPN6qOnbCEB2DlqrW4cPVDE95Ty+I1qUS2eDD0lZS0Ll/czErJ9H4cqFpFKUbXF6yZ+JbcPyMWqgaZHCatmM2rc6yxgWP8R6/KW/xdx++N/SGi3zQW2Zk7om26VZiBaGP36de0hut6VuHS54bobzSjZChwL4tcHcsgGW3cF+iWL3bHJ/Yo2b7r1r/lRboArd6c+8Ap2HFqCJYxPfdnqG5M8WfkbHecWkf4uAqn0aAWoFeBS7w+PGOuBE0RdQTt14UkwUu2OVfcxWyVjfDZ5DggMJ5bwszMzz2FVivIDZ7fUnZ5ftUkuALx4PcN58vJGycgWpBk55ZhIdFKTD7hx/Psv5myn2y7ZJarlMAS6PLGjjd7XKqi/3Q5f0RCR6QSB8ImecdwJfECwN80Dx6sNCy2eZAnx2kjXCYq5lJiGQ4feDvp/GBuf3SnggsFa4YZHThRWQH/83qe8RgTYcSuPc6p2MTfE2RVCHg4Ek8usMLcrORtH6a53/slMp10DW4i9fMF2GZroorM762i6y2OQq7/YzwWzsISBupQANlPGwRwFrqwXY2BSqNPgt1r7p68SFUZ8KtzWov1QWl9cDjFRAeISXo7CcjLyWgDQwBPKo4otCxnJ0tHIQpttJYwPx/39bLbaiou5HXFRYeBE1JxlyRQv1Be5KCGoL0kOdoe4psIKEZ5ijIYhCqoC" let txBase64String =
// swiftlint:disable:next line_length
let txBase64String = "BAAAgIUgL4kAAAAAAACz/goAECcAAAAAAAABXcTpMQ7kFhTifSbypMLlmgvAh/mR9z78+mocYt7rvJvLFjfBGp9aDf/066bWOsKt4N78Kovjr5mPuVNSMUegJuwZuAxnaO+iu+z17zABU4TUdixiCq97W74jgttjhG+Nxe+HeXvlqX7NmDJJmRO0anXaL1pBcPxOcb1pXcfO5VG1SFGLFIhLNyDlCoYa42sPHnQ4WsjMUv7Jh8rIH/tJ/vhHv9mGEg+zfld5KkMQ1JSykSen34KAPjc/em+2KMN6qAKrUWRPYlQvkz/QTxXeXI1OoCLCmqdsbHEG5ffSdmkAJfJpKfRsI04DQdTF03eVxpSVxlmz7gfeEmwVowKGV284KwmGHsnuHawjcBVb2tWIU9tBfQtONE1HgA5B6wt8ynZGSrlit0gFaTxCxzFcRmYgkgLbJeqx1lu+wUBVqAqvbkA4wGTvNX28b3SWmTx40eTu251bzqzy/Ip/7tSgh6QupSzatDF2gHcpb91EY6r3E3Jyhm2zfu38JSHTctMMArFWhOyfzBwrMPFjrLKDc9IiX7zjMBluTTMgQosb81o4ZF+2ZIKbplfugQICjuQjC5UFvihNCNF6+j8ITfwwD105VO52qVFiAdNAvCsymmPGI0Wr2w9PdBweIhUsrDLz11wi1pnydHdRwArU82CCqcVd5rwixomZrjwP8IFL0wd5CKho7QdU4rXF39VN2nffD+hwAPl0X3TCntws3/8Q1066bRTJLhaZcSzaVB0Bq0GKRAMS/WflBIlkd4KG6+/dDw01B0/lobAJIXfa+UrGKbDxXjLQlW8R8Bi9I+Y7S1h8I/gYOQiuuoCpJyV//4sKi1/zI1nnDcTu1q7P1eDYv30Y1g31FK9IWoXIQaZJ8ehKJqSx/FDBvhsEEh2aHvRolFTwHvxiVV3b9L3txePJRTEBq/RDUvW1BjAST/xfd3TTUILEZ962Ix3hu+ZfKWDSFCb/YW1fHlf98O05QunwBwDnaC3qgzNbqDjF9mldOFWZ84XE6IJVCHTN04L4hR8dl4bltpjnVnQmy34bcKLNtsFoRXot9ckOcrbX8Bf9lfneichrYtWZi6bVt6irwFo4+qXwABsUQGA2TMgsKpw9z4KysdSqoMXOEe+k/wPPQIqTPtFp4jrkMIntSokR6hRPZFdJJ23OWHjOoIKxSZUeOgkbKlqSrcQa/IHZQ8cpEof8T1PyNx/VDgEf6oIxnk2+E/iosraQpunYgAtQCm1pC8tF5oI6MshGj2nfonSug9PS2ORf0xk2RJLnSbg3kdQGx85ulMrOojkci80RIeyta3vX527MKDANTsN6W2y8fT9oDVA4l11lm4j31TyK754eTb1VPsy0YOo1L5Vr0LOOZtpF+tCaojz6Kx980jkzlEJOQ+imLERLAckclxFCWYyQ+9UOJnuXS5YCy32cdkzm17e2SwmeYGmNrTGUm6eaT0/ZqqsPAQEo+h/z4Bgt9eUEUvkRaDhz/vZjJkNfHdLA2c3CI5iTA0WUJQa5J4XNbFSQmEl4RYNHLHz0utsHcgFC9czxtDZkLcTUcQ2i/dQv07E84FhrMG5bJ818SaABpMlX99gaMYRpW3SWSuU0++/apORrIOSqonXunqroBtnAzeAIGY30ikpmkuf/BYTxAADZHCLkUBNVFmkASV91In4r6nfDbwMVOXCffBBHf2Cr/9hcd5TNL10MG0qhSsYrpv1FpCxg56hYiglS3BQ1uj63bzf4lmRbAvwXdI99lgoPQqjws3hIDIAImobsRWJoGSjT6Gq2hd40CVmmFjlXxdPpo6+m4L2PFD97LnHjF9X/VA+VWc+ihPCLrzgVl63NJcsEf/0bNt/iNWkIILM38WUSMEWj4CaSp1wI8ank+JfE7elapFRza98CGb52g+gZSkEv/xWRn/SjL3RvqQwSdWh38N/ijzpRfL48MiwYd15U7HZ2pc/0WtYVe7JSH6BHBXExBUzhqnZqRxK+1jZflKsYFbCAaZOjpeLSy5l8AvOjDfGR5+Uk+7dIs97lSGHZd+EQWgV/bmH+NwicyMMImJQ6ZSZgiBWx/JU0kB0Vz3Mm5t8a7aWG3/AhUSwYTwLgzbYG4MyiCfV3UAUB81yMxirGLIOqkb/CguZCrQO9/oMhTA9Ek2GkHtG6qOdDEMXSpN1StPwqgQUaSb3PgW1rlPUC3jLGPYyv2CCF3+5d3Q6RkyZaOq5jIM0uaV7sUCqinnCs78yL8yRkQAEPJTAT8oH9J0YEaempSz1df5LnluJ3r2CsFK/by8745lQlWtm8jdmRBojPG9JDrVTkO2Z/hudc1uQqz9N0X7l3VYlXSpYty4QtQMDTy3LxAzo8Ecot84Wc7kZbtXhqMKwQRGJOUSHNBEZwjAw0SwfyS0ajnCUbhw08U2UguoLzHsW0XVFQ3bc4pUr0X04vFj5/w/hVGflo9uLssHxxmdfH2WgSltEZR3yX0/T5GlDj8qctgryO06ubRYZkola0uEkTmIZWtgL4TmFAKYwPVmBQ7eHl7/nZV5PQY8p68ubQZmQTYlQEOc36+Gj2TDHCLAXF1aGPqVcsyJDHULSSQ3CT17XGJEABI5eHCEzTrsBsVywUNpBcNQItghd18IMCkv9b2gw+N0NKJJjmuyRgIF5NXOXS8XupSuvPT857eIdx7tFmmzPh+5Gu6V/8MqREYWf4281x0ydKl6Eq5oURFVqFGtAQZcizcNoBn4OMVduvBRRyb81cNrNvMcveAQTRCj4eqvP9C7cBfPByELIullTRLXwAOaoYJ0c/rimHMCNDZqwBWVw+HNo0kZmjHjT3PF/CExoLkpS0ThBV97NhiNaRuJ9U4IidKLDwE19q2ptO76WXybzKJegFycWnsUxOkxyst9Nb5ur08Z+4+i07ePNAbj6ACufdZGVmu1Vtd5cPMw9PjJyM7BcxHwF30YxlhV4M0lRkatlGleALP2CXLG994wZ+SCA1n0/09wwD11G1ibX5l4TNdFCDDRdcq7Yyb08J/JBfsAyQNJrrNdWslND8nRMdGraxycrMfkMgq4dIDgCgs++1SPri1JMyv02WeIzCGjnHnhvphMxyFbFJqRzeA1oKF3dE9w95zItNagaY9PKLyx00g1C4ViaYgkagB6gG" "BAAAgIUgL4kAAAAAAACz/goAECcAAAAAAAABXcTpMQ7kFhTifSbypMLlmgvAh/mR9z78+mocYt7rvJvLFjfBGp9aDf/066bWOsKt4N78Kovjr5mPuVNSMUegJuwZuAxnaO+iu+z17zABU4TUdixiCq97W74jgttjhG+Nxe+HeXvlqX7NmDJJmRO0anXaL1pBcPxOcb1pXcfO5VG1SFGLFIhLNyDlCoYa42sPHnQ4WsjMUv7Jh8rIH/tJ/vhHv9mGEg+zfld5KkMQ1JSykSen34KAPjc/em+2KMN6qAKrUWRPYlQvkz/QTxXeXI1OoCLCmqdsbHEG5ffSdmkAJfJpKfRsI04DQdTF03eVxpSVxlmz7gfeEmwVowKGV284KwmGHsnuHawjcBVb2tWIU9tBfQtONE1HgA5B6wt8ynZGSrlit0gFaTxCxzFcRmYgkgLbJeqx1lu+wUBVqAqvbkA4wGTvNX28b3SWmTx40eTu251bzqzy/Ip/7tSgh6QupSzatDF2gHcpb91EY6r3E3Jyhm2zfu38JSHTctMMArFWhOyfzBwrMPFjrLKDc9IiX7zjMBluTTMgQosb81o4ZF+2ZIKbplfugQICjuQjC5UFvihNCNF6+j8ITfwwD105VO52qVFiAdNAvCsymmPGI0Wr2w9PdBweIhUsrDLz11wi1pnydHdRwArU82CCqcVd5rwixomZrjwP8IFL0wd5CKho7QdU4rXF39VN2nffD+hwAPl0X3TCntws3/8Q1066bRTJLhaZcSzaVB0Bq0GKRAMS/WflBIlkd4KG6+/dDw01B0/lobAJIXfa+UrGKbDxXjLQlW8R8Bi9I+Y7S1h8I/gYOQiuuoCpJyV//4sKi1/zI1nnDcTu1q7P1eDYv30Y1g31FK9IWoXIQaZJ8ehKJqSx/FDBvhsEEh2aHvRolFTwHvxiVV3b9L3txePJRTEBq/RDUvW1BjAST/xfd3TTUILEZ962Ix3hu+ZfKWDSFCb/YW1fHlf98O05QunwBwDnaC3qgzNbqDjF9mldOFWZ84XE6IJVCHTN04L4hR8dl4bltpjnVnQmy34bcKLNtsFoRXot9ckOcrbX8Bf9lfneichrYtWZi6bVt6irwFo4+qXwABsUQGA2TMgsKpw9z4KysdSqoMXOEe+k/wPPQIqTPtFp4jrkMIntSokR6hRPZFdJJ23OWHjOoIKxSZUeOgkbKlqSrcQa/IHZQ8cpEof8T1PyNx/VDgEf6oIxnk2+E/iosraQpunYgAtQCm1pC8tF5oI6MshGj2nfonSug9PS2ORf0xk2RJLnSbg3kdQGx85ulMrOojkci80RIeyta3vX527MKDANTsN6W2y8fT9oDVA4l11lm4j31TyK754eTb1VPsy0YOo1L5Vr0LOOZtpF+tCaojz6Kx980jkzlEJOQ+imLERLAckclxFCWYyQ+9UOJnuXS5YCy32cdkzm17e2SwmeYGmNrTGUm6eaT0/ZqqsPAQEo+h/z4Bgt9eUEUvkRaDhz/vZjJkNfHdLA2c3CI5iTA0WUJQa5J4XNbFSQmEl4RYNHLHz0utsHcgFC9czxtDZkLcTUcQ2i/dQv07E84FhrMG5bJ818SaABpMlX99gaMYRpW3SWSuU0++/apORrIOSqonXunqroBtnAzeAIGY30ikpmkuf/BYTxAADZHCLkUBNVFmkASV91In4r6nfDbwMVOXCffBBHf2Cr/9hcd5TNL10MG0qhSsYrpv1FpCxg56hYiglS3BQ1uj63bzf4lmRbAvwXdI99lgoPQqjws3hIDIAImobsRWJoGSjT6Gq2hd40CVmmFjlXxdPpo6+m4L2PFD97LnHjF9X/VA+VWc+ihPCLrzgVl63NJcsEf/0bNt/iNWkIILM38WUSMEWj4CaSp1wI8ank+JfE7elapFRza98CGb52g+gZSkEv/xWRn/SjL3RvqQwSdWh38N/ijzpRfL48MiwYd15U7HZ2pc/0WtYVe7JSH6BHBXExBUzhqnZqRxK+1jZflKsYFbCAaZOjpeLSy5l8AvOjDfGR5+Uk+7dIs97lSGHZd+EQWgV/bmH+NwicyMMImJQ6ZSZgiBWx/JU0kB0Vz3Mm5t8a7aWG3/AhUSwYTwLgzbYG4MyiCfV3UAUB81yMxirGLIOqkb/CguZCrQO9/oMhTA9Ek2GkHtG6qOdDEMXSpN1StPwqgQUaSb3PgW1rlPUC3jLGPYyv2CCF3+5d3Q6RkyZaOq5jIM0uaV7sUCqinnCs78yL8yRkQAEPJTAT8oH9J0YEaempSz1df5LnluJ3r2CsFK/by8745lQlWtm8jdmRBojPG9JDrVTkO2Z/hudc1uQqz9N0X7l3VYlXSpYty4QtQMDTy3LxAzo8Ecot84Wc7kZbtXhqMKwQRGJOUSHNBEZwjAw0SwfyS0ajnCUbhw08U2UguoLzHsW0XVFQ3bc4pUr0X04vFj5/w/hVGflo9uLssHxxmdfH2WgSltEZR3yX0/T5GlDj8qctgryO06ubRYZkola0uEkTmIZWtgL4TmFAKYwPVmBQ7eHl7/nZV5PQY8p68ubQZmQTYlQEOc36+Gj2TDHCLAXF1aGPqVcsyJDHULSSQ3CT17XGJEABI5eHCEzTrsBsVywUNpBcNQItghd18IMCkv9b2gw+N0NKJJjmuyRgIF5NXOXS8XupSuvPT857eIdx7tFmmzPh+5Gu6V/8MqREYWf4281x0ydKl6Eq5oURFVqFGtAQZcizcNoBn4OMVduvBRRyb81cNrNvMcveAQTRCj4eqvP9C7cBfPByELIullTRLXwAOaoYJ0c/rimHMCNDZqwBWVw+HNo0kZmjHjT3PF/CExoLkpS0ThBV97NhiNaRuJ9U4IidKLDwE19q2ptO76WXybzKJegFycWnsUxOkxyst9Nb5ur08Z+4+i07ePNAbj6ACufdZGVmu1Vtd5cPMw9PjJyM7BcxHwF30YxlhV4M0lRkatlGleALP2CXLG994wZ+SCA1n0/09wwD11G1ibX5l4TNdFCDDRdcq7Yyb08J/JBfsAyQNJrrNdWslND8nRMdGraxycrMfkMgq4dIDgCgs++1SPri1JMyv02WeIzCGjnHnhvphMxyFbFJqRzeA1oKF3dE9w95zItNagaY9PKLyx00g1C4ViaYgkagB6gG"
} }

View File

@ -7,61 +7,69 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @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 {
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
/**
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
*/
// swiftlint:disable implicitly_unwrapped_optional print_function_usage function_parameter_count
class ReOrgTests: XCTestCase {
// 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"
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 663150
let defaultLatestHeight: BlockHeight = 663175 let defaultLatestHeight: BlockHeight = 663175
let network = DarksideWalletDNetwork()
let branchID = "2bb40e60"
let chainName = "main"
let mockLatestHeight = BlockHeight(663250)
let targetLatestHeight = BlockHeight(663251)
let walletBirthday = BlockHeight(663150)
var birthday: BlockHeight = 663150
var reorgExpectation = XCTestExpectation(description: "reorg")
var coordinator: TestCoordinator! var coordinator: TestCoordinator!
var syncedExpectation = XCTestExpectation(description: "synced") var syncedExpectation = XCTestExpectation(description: "synced")
var sentTransactionExpectation = XCTestExpectation(description: "sent") var sentTransactionExpectation = XCTestExpectation(description: "sent")
var expectedReorgHeight: BlockHeight = 665188 var expectedReorgHeight: BlockHeight = 665188
var expectedRewindHeight: BlockHeight = 665188 var expectedRewindHeight: BlockHeight = 665188
let network = DarksideWalletDNetwork()
let branchID = "2bb40e60"
let chainName = "main"
var reorgExpectation: XCTestExpectation = XCTestExpectation(description: "reorg")
override func setUpWithError() throws { override func setUpWithError() throws {
NotificationCenter.default.addObserver(self, selector: #selector(handleReOrgNotification(_:)), name: Notification.Name.blockProcessorHandledReOrg, object: nil) try super.setUpWithError()
coordinator = try TestCoordinator( NotificationCenter.default.addObserver(
seed: seedPhrase, self,
walletBirthday: birthday, selector: #selector(handleReOrgNotification(_:)),
channelProvider: ChannelProvider(), name: Notification.Name.blockProcessorHandledReOrg,
network: network object: nil
) )
coordinator = try TestCoordinator(
seed: seedPhrase,
walletBirthday: birthday,
channelProvider: ChannelProvider(),
network: network
)
try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName)
try coordinator.resetBlocks(dataset: .default) try coordinator.resetBlocks(dataset: .default)
}
}
override func tearDownWithError() throws {
override func tearDownWithError() throws { try super.tearDownWithError()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB) try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB) try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
} }
let mockLatestHeight = BlockHeight(663250)
let targetLatestHeight = BlockHeight(663251)
let walletBirthday = BlockHeight(663150)
@objc func handleReOrgNotification(_ notification: Notification) { @objc func handleReOrgNotification(_ notification: Notification) {
reorgExpectation.fulfill() reorgExpectation.fulfill()
guard let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight, guard let reorgHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewindHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else { let rewindHeight = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else {
@ -81,12 +89,14 @@ class ReOrgTests: XCTestCase {
let reOrgHeight = BlockHeight(663195) let reOrgHeight = BlockHeight(663195)
let walletBirthday = WalletBirthday.birthday(with: 663150, network: network).height let walletBirthday = WalletBirthday.birthday(with: 663150, network: network).height
try basicReOrgTest(baseDataset: .beforeReOrg, try basicReOrgTest(
reorgDataset: .afterSmallReorg, baseDataset: .beforeReOrg,
firstLatestHeight: mockLatestHeight, reorgDataset: .afterSmallReorg,
reorgHeight: reOrgHeight, firstLatestHeight: mockLatestHeight,
walletBirthday: walletBirthday, reorgHeight: reOrgHeight,
targetHeight: targetLatestHeight) walletBirthday: walletBirthday,
targetHeight: targetLatestHeight
)
} }
func testTenPlusBlockReOrg() throws { func testTenPlusBlockReOrg() throws {
@ -95,26 +105,29 @@ class ReOrgTests: XCTestCase {
let reOrgHeight = BlockHeight(663180) let reOrgHeight = BlockHeight(663180)
let walletBirthday = WalletBirthday.birthday(with: BlockHeight(663150), network: network).height let walletBirthday = WalletBirthday.birthday(with: BlockHeight(663150), network: network).height
try basicReOrgTest(baseDataset: .beforeReOrg, try basicReOrgTest(
reorgDataset: .afterLargeReorg, baseDataset: .beforeReOrg,
firstLatestHeight: mockLatestHeight, reorgDataset: .afterLargeReorg,
reorgHeight: reOrgHeight, firstLatestHeight: mockLatestHeight,
walletBirthday: walletBirthday, reorgHeight: reOrgHeight,
targetHeight: targetLatestHeight) walletBirthday: walletBirthday,
targetHeight: targetLatestHeight
)
} }
func basicReOrgTest(baseDataset: DarksideDataset, func basicReOrgTest(
reorgDataset: DarksideDataset, baseDataset: DarksideDataset,
firstLatestHeight: BlockHeight, reorgDataset: DarksideDataset,
reorgHeight: BlockHeight, firstLatestHeight: BlockHeight,
walletBirthday: BlockHeight, reorgHeight: BlockHeight,
targetHeight: BlockHeight) throws { walletBirthday: BlockHeight,
targetHeight: BlockHeight
) throws {
do { do {
try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) try coordinator.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName)
try coordinator.resetBlocks(dataset: .predefined(dataset: .beforeReOrg)) try coordinator.resetBlocks(dataset: .predefined(dataset: .beforeReOrg))
try coordinator.applyStaged(blockheight: firstLatestHeight) try coordinator.applyStaged(blockheight: firstLatestHeight)
} catch { } catch {
XCTFail("Error: \(error)") XCTFail("Error: \(error)")
return return
} }
@ -122,11 +135,11 @@ class ReOrgTests: XCTestCase {
let firstSyncExpectation = XCTestExpectation(description: "firstSyncExpectation") let firstSyncExpectation = XCTestExpectation(description: "firstSyncExpectation")
/** /**
download and sync blocks from walletBirthday to firstLatestHeight download and sync blocks from walletBirthday to firstLatestHeight
*/ */
var synchronizer: SDKSynchronizer? var synchronizer: SDKSynchronizer?
try coordinator.sync(completion: { (s) in try coordinator.sync(completion: { synchro in
synchronizer = s synchronizer = synchro
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
@ -136,51 +149,50 @@ class ReOrgTests: XCTestCase {
XCTFail("nil synchronizer") XCTFail("nil synchronizer")
return return
} }
/** /**
verify that mock height has been reached verify that mock height has been reached
*/ */
var latestDownloadedHeight = BlockHeight(0) var latestDownloadedHeight = BlockHeight(0)
XCTAssertNoThrow(try {latestDownloadedHeight = try syncedSynchronizer.initializer.downloader.latestBlockHeight()}()) XCTAssertNoThrow(try { latestDownloadedHeight = try syncedSynchronizer.initializer.downloader.latestBlockHeight() }())
XCTAssertTrue(latestDownloadedHeight > 0) XCTAssertTrue(latestDownloadedHeight > 0)
/** /**
trigger reorg! trigger reorg!
*/ */
try coordinator.resetBlocks(dataset: .predefined(dataset: reorgDataset)) try coordinator.resetBlocks(dataset: .predefined(dataset: reorgDataset))
try coordinator.applyStaged(blockheight: targetHeight) try coordinator.applyStaged(blockheight: targetHeight)
/** /**
request latest height -> receive targetHeight! request latest height -> receive targetHeight!
download that block download that block
observe that the prev hash of that block does not match the hash that we have for firstLatestHeight 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 rewind 10 blocks and request blocks targetHeight-10 to targetHeight
*/ */
let secondSyncExpectation = XCTestExpectation(description: "second sync") let secondSyncExpectation = XCTestExpectation(description: "second sync")
sleep(2) sleep(2)
try coordinator.sync(completion: { (_) in try coordinator.sync(
secondSyncExpectation.fulfill() completion: { _ in
}, error: self.handleError) secondSyncExpectation.fulfill()
},
error: self.handleError
)
// now reorg should happen and reorg notifications and idle notification should be triggered // now reorg should happen and reorg notifications and idle notification should be triggered
wait(for: [reorgExpectation,secondSyncExpectation], timeout: 5) wait(for: [reorgExpectation, secondSyncExpectation], timeout: 5)
// now everything should be fine. latest block should be targetHeight // now everything should be fine. latest block should be targetHeight
XCTAssertNoThrow(try {latestDownloadedHeight = try syncedSynchronizer.initializer.downloader.latestBlockHeight()}()) XCTAssertNoThrow(try { latestDownloadedHeight = try syncedSynchronizer.initializer.downloader.latestBlockHeight() }())
XCTAssertEqual(latestDownloadedHeight, targetHeight) XCTAssertEqual(latestDownloadedHeight, targetHeight)
} }
@objc func processorHandledReorg(_ notification: Notification) { @objc func processorHandledReorg(_ notification: Notification) {
XCTAssertNotNil(notification.userInfo) XCTAssertNotNil(notification.userInfo)
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight, if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight { let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
XCTAssertTrue( rewind <= reorg ) XCTAssertTrue( rewind <= reorg )
reorgExpectation.fulfill() reorgExpectation.fulfill()
} else { } else {
@ -195,5 +207,4 @@ class ReOrgTests: XCTestCase {
} }
XCTFail("Failed with error: \(testError)") XCTFail("Failed with error: \(testError)")
} }
} }

View File

@ -7,48 +7,59 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable type_body_length implicitly_unwrapped_optional
class RewindRescanTests: XCTestCase { class RewindRescanTests: 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? // TODO: Parameterize this from environment?
// swiftlint:disable:next line_length
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a" //TODO: Parameterize this from environment 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"
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 663150
let defaultLatestHeight: BlockHeight = 663175 let defaultLatestHeight: BlockHeight = 663175
let branchID = "2bb40e60"
let chainName = "main"
var birthday: BlockHeight = 663150
var coordinator: TestCoordinator! var coordinator: TestCoordinator!
var syncedExpectation = XCTestExpectation(description: "synced") var syncedExpectation = XCTestExpectation(description: "synced")
var sentTransactionExpectation = XCTestExpectation(description: "sent") var sentTransactionExpectation = XCTestExpectation(description: "sent")
var expectedReorgHeight: BlockHeight = 665188 var expectedReorgHeight: BlockHeight = 665188
var expectedRewindHeight: BlockHeight = 665188 var expectedRewindHeight: BlockHeight = 665188
var reorgExpectation: XCTestExpectation = XCTestExpectation(description: "reorg") var reorgExpectation = XCTestExpectation(description: "reorg")
let branchID = "2bb40e60"
let chainName = "main"
var network = ZcashNetworkBuilder.network(for: .mainnet) var network = ZcashNetworkBuilder.network(for: .mainnet)
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
coordinator = try TestCoordinator( coordinator = try TestCoordinator(
seed: seedPhrase, seed: seedPhrase,
walletBirthday: birthday, walletBirthday: birthday,
channelProvider: ChannelProvider(), channelProvider: ChannelProvider(),
network: network network: network
) )
try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main")
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
try coordinator.stop() try coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB) try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB) try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
} }
func handleError(_ error: Error?) { func handleError(_ error: Error?) {
guard let testError = error else { guard let testError = error else {
XCTFail("failed with nil error") XCTFail("failed with nil error")
return return
} }
XCTFail("Failed with error: \(testError)") XCTFail("Failed with error: \(testError)")
} }
@ -62,7 +73,7 @@ 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: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: handleError) }, error: handleError)
@ -77,14 +88,14 @@ class RewindRescanTests: XCTestCase {
try coordinator.synchronizer.rewind(.birthday) try coordinator.synchronizer.rewind(.birthday)
// assert that after the new height is // assert that after the new height is
XCTAssertEqual(try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight(),self.birthday) XCTAssertEqual(try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight(), self.birthday)
// check that the balance is cleared // check that the balance is cleared
XCTAssertEqual(initialVerifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance()) XCTAssertEqual(initialVerifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance())
XCTAssertEqual(initialTotalBalance, coordinator.synchronizer.initializer.getBalance()) XCTAssertEqual(initialTotalBalance, coordinator.synchronizer.initializer.getBalance())
let secondScanExpectation = XCTestExpectation(description: "rescan") let secondScanExpectation = XCTestExpectation(description: "rescan")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
secondScanExpectation.fulfill() secondScanExpectation.fulfill()
}, error: handleError) }, error: handleError)
@ -93,24 +104,28 @@ class RewindRescanTests: XCTestCase {
// verify that the balance still adds up // verify that the balance still adds up
XCTAssertEqual(verifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance()) XCTAssertEqual(verifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance())
XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance()) XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance())
} }
func testRescanToHeight() throws { func testRescanToHeight() throws {
// 1 sync and get spendable funds // 1 sync and get spendable funds
try FakeChainBuilder.buildChainWithTxsFarFromEachOther(darksideWallet: coordinator.service, branchID: branchID, chainName: chainName, length: 10000) try FakeChainBuilder.buildChainWithTxsFarFromEachOther(
darksideWallet: coordinator.service,
branchID: branchID,
chainName: chainName,
length: 10000
)
let newChaintTip = defaultLatestHeight + 10000 let newChaintTip = defaultLatestHeight + 10000
try coordinator.applyStaged(blockheight: newChaintTip) try coordinator.applyStaged(blockheight: newChaintTip)
sleep(3) sleep(3)
let initialVerifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance() let initialVerifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
// let initialTotalBalance = coordinator.synchronizer.initializer.getBalance()
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation") let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(
firstSyncExpectation.fulfill() completion: { _ in
}, error: handleError) firstSyncExpectation.fulfill()
},
error: handleError
)
wait(for: [firstSyncExpectation], timeout: 20) wait(for: [firstSyncExpectation], timeout: 20)
let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance() let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
@ -121,23 +136,24 @@ class RewindRescanTests: XCTestCase {
// rewind to birthday // rewind to birthday
let targetHeight: BlockHeight = newChaintTip - 8000 let targetHeight: BlockHeight = newChaintTip - 8000
let rewindHeight = ZcashRustBackend.getNearestRewindHeight(dbData: coordinator.databases.dataDB, height: Int32(targetHeight), networkType: network.networkType) let rewindHeight = ZcashRustBackend.getNearestRewindHeight(
dbData: coordinator.databases.dataDB,
height: Int32(targetHeight),
networkType: network.networkType
)
try coordinator.synchronizer.rewind(.height(blockheight: targetHeight)) try coordinator.synchronizer.rewind(.height(blockheight: targetHeight))
guard rewindHeight > 0 else { guard rewindHeight > 0 else {
XCTFail("get nearest height failed error: \(ZcashRustBackend.getLastError() ?? "null")") XCTFail("get nearest height failed error: \(ZcashRustBackend.getLastError() ?? "null")")
return return
} }
// assert that after the new height is
// XCTAssertEqual(try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight(), BlockHeight(rewindHeight))
// check that the balance is cleared // check that the balance is cleared
XCTAssertEqual(initialVerifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance()) XCTAssertEqual(initialVerifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance())
let secondScanExpectation = XCTestExpectation(description: "rescan") let secondScanExpectation = XCTestExpectation(description: "rescan")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
secondScanExpectation.fulfill() secondScanExpectation.fulfill()
}, error: handleError) }, error: handleError)
@ -149,7 +165,13 @@ 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(spendingKey: coordinator.spendingKey, zatoshi: 1000, toAddress: testRecipientAddress, memo: nil, from: 0) { result in coordinator.synchronizer.sendToAddress(
spendingKey: coordinator.spendingKey,
zatoshi: 1000,
toAddress: testRecipientAddress,
memo: nil,
from: 0
) { result in
sendExpectation.fulfill() sendExpectation.fulfill()
switch result { switch result {
case .success(let pendingTx): case .success(let pendingTx):
@ -159,9 +181,6 @@ class RewindRescanTests: XCTestCase {
} }
} }
wait(for: [sendExpectation], timeout: 15) wait(for: [sendExpectation], timeout: 15)
} }
func testRescanToTransaction() throws { func testRescanToTransaction() throws {
@ -173,7 +192,7 @@ 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: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: handleError) }, error: handleError)
@ -191,14 +210,16 @@ class RewindRescanTests: XCTestCase {
} }
try coordinator.synchronizer.rewind(.transaction(transaction.transactionEntity)) try coordinator.synchronizer.rewind(.transaction(transaction.transactionEntity))
// assert that after the new height is // assert that after the new height is
XCTAssertEqual(try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight(),transaction.transactionEntity.anchor(network: network)) XCTAssertEqual(
try coordinator.synchronizer.initializer.transactionRepository.lastScannedHeight(),
transaction.transactionEntity.anchor(network: network)
)
let secondScanExpectation = XCTestExpectation(description: "rescan") let secondScanExpectation = XCTestExpectation(description: "rescan")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
secondScanExpectation.fulfill() secondScanExpectation.fulfill()
}, error: handleError) }, error: handleError)
@ -207,7 +228,6 @@ class RewindRescanTests: XCTestCase {
// verify that the balance still adds up // verify that the balance still adds up
XCTAssertEqual(verifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance()) XCTAssertEqual(verifiedBalance, coordinator.synchronizer.initializer.getVerifiedBalance())
XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance()) XCTAssertEqual(totalBalance, coordinator.synchronizer.initializer.getBalance())
} }
func testRewindAfterSendingTransaction() throws { func testRewindAfterSendingTransaction() throws {
@ -225,7 +245,7 @@ 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: { (synchronizer) in try coordinator.sync(completion: { _ in
firstSyncExpectation.fulfill() firstSyncExpectation.fulfill()
}, error: handleError) }, error: handleError)
@ -246,11 +266,13 @@ class RewindRescanTests: XCTestCase {
return return
} }
var pendingTx: PendingTransactionEntity? var pendingTx: PendingTransactionEntity?
coordinator.synchronizer.sendToAddress(spendingKey: spendingKey, coordinator.synchronizer.sendToAddress(
zatoshi: maxBalance, spendingKey: spendingKey,
toAddress: testRecipientAddress, zatoshi: maxBalance,
memo: "test send \(self.description) \(Date().description)", toAddress: testRecipientAddress,
from: 0) { result in memo: "test send \(self.description) \(Date().description)",
from: 0
) { result in
switch result { switch result {
case .failure(let error): case .failure(let error):
XCTFail("sendToAddress failed: \(error)") XCTFail("sendToAddress failed: \(error)")
@ -265,14 +287,13 @@ class RewindRescanTests: XCTestCase {
return return
} }
notificationHandler.synchronizerMinedTransaction = { tx in notificationHandler.synchronizerMinedTransaction = { transaction in
XCTAssertNotNil(tx.rawTransactionId) XCTAssertNotNil(transaction.rawTransactionId)
XCTAssertNotNil(pendingTx.rawTransactionId) XCTAssertNotNil(pendingTx.rawTransactionId)
XCTAssertEqual(tx.rawTransactionId, pendingTx.rawTransactionId) XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId)
transactionMinedExpectation.fulfill() transactionMinedExpectation.fulfill()
} }
// 5 apply to height // 5 apply to height
// 6 mine the block // 6 mine the block
guard let rawTx = try coordinator.getIncomingTransactions()?.first else { guard let rawTx = try coordinator.getIncomingTransactions()?.first else {
@ -284,7 +305,7 @@ class RewindRescanTests: XCTestCase {
let sentTxHeight = latestHeight + 1 let sentTxHeight = latestHeight + 1
notificationHandler.transactionsFound = { txs in notificationHandler.transactionsFound = { txs in
let foundTx = txs.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId}) let foundTx = txs.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
XCTAssertNotNil(foundTx) XCTAssertNotNil(foundTx)
XCTAssertEqual(foundTx?.minedHeight, sentTxHeight) XCTAssertEqual(foundTx?.minedHeight, sentTxHeight)
@ -296,24 +317,26 @@ class RewindRescanTests: XCTestCase {
try coordinator.applyStaged(blockheight: sentTxHeight) try coordinator.applyStaged(blockheight: sentTxHeight)
sleep(2) sleep(2)
let mineExpectation = XCTestExpectation(description: "mineTxExpectation") let mineExpectation = XCTestExpectation(description: "mineTxExpectation")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(
let p = synchronizer.pendingTransactions.first(where: {$0.rawTransactionId == pendingTx.rawTransactionId}) completion: { synchronizer in
XCTAssertNotNil(p, "pending transaction should have been mined by now") let pendingTransaction = synchronizer.pendingTransactions
XCTAssertTrue(p?.isMined ?? false) .first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
XCTAssertEqual(p?.minedHeight, sentTxHeight) XCTAssertNotNil(pendingTransaction, "pending transaction should have been mined by now")
mineExpectation.fulfill() XCTAssertTrue(pendingTransaction?.isMined ?? false)
XCTAssertEqual(pendingTransaction?.minedHeight, sentTxHeight)
mineExpectation.fulfill()
},
error: { error in
guard let e = error else {
XCTFail("unknown error syncing after sending transaction")
return
}
}, error: { (error) in XCTFail("Error: \(e)")
guard let e = error else {
XCTFail("unknown error syncing after sending transaction")
return
} }
)
XCTFail("Error: \(e)")
})
wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5) wait(for: [mineExpectation, transactionMinedExpectation, foundTransactionsExpectation], timeout: 5)
@ -327,36 +350,45 @@ class RewindRescanTests: XCTestCase {
try coordinator.synchronizer.rewind(.height(blockheight: sentTxHeight - 5)) try coordinator.synchronizer.rewind(.height(blockheight: sentTxHeight - 5))
guard let np = try coordinator.synchronizer.allPendingTransactions().first(where: { $0.rawTransactionId == pendingTx.rawTransactionId}) else { guard
let pendingEntity = try coordinator.synchronizer.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
else {
XCTFail("sent pending transaction not found after rewind") XCTFail("sent pending transaction not found after rewind")
return return
} }
XCTAssertFalse(np.isMined) XCTAssertFalse(pendingEntity.isMined)
let confirmExpectation = XCTestExpectation(description: "confirm expectation") let confirmExpectation = XCTestExpectation(description: "confirm expectation")
notificationHandler.transactionsFound = { txs in notificationHandler.transactionsFound = { txs in
XCTAssertEqual(txs.count, 1) XCTAssertEqual(txs.count, 1)
guard let t = txs.first else { guard let transaction = txs.first else {
XCTFail("should have found sent transaction but didn't") XCTFail("should have found sent transaction but didn't")
return return
} }
XCTAssertEqual(t.rawTransactionId, pendingTx.rawTransactionId,"should have mined sent transaction but didn't") XCTAssertEqual(transaction.rawTransactionId, pendingTx.rawTransactionId, "should have mined sent transaction but didn't")
} }
notificationHandler.synchronizerMinedTransaction = { tx in
XCTFail("We shouldn't find any mined transactions at this point but found \(tx)") notificationHandler.synchronizerMinedTransaction = { transaction in
XCTFail("We shouldn't find any mined transactions at this point but found \(transaction)")
} }
try coordinator.sync(completion: { synchronizer in
confirmExpectation.fulfill() try coordinator.sync(
}, error: { e in completion: { _ in
self.handleError(e) confirmExpectation.fulfill()
}) },
error: { e in
self.handleError(e)
}
)
wait(for: [confirmExpectation], timeout: 10) wait(for: [confirmExpectation], timeout: 10)
let confirmedPending = try coordinator.synchronizer.allPendingTransactions().first(where: { $0.rawTransactionId == pendingTx.rawTransactionId}) let confirmedPending = try coordinator.synchronizer.allPendingTransactions()
.first(where: { $0.rawTransactionId == pendingTx.rawTransactionId })
XCTAssertNil(confirmedPending, "pending, now confirmed transaction found") XCTAssertNil(confirmedPending, "pending, now confirmed transaction found")
XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), 0) XCTAssertEqual(coordinator.synchronizer.initializer.getBalance(), 0)
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), 0) XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), 0)
} }

View File

@ -7,26 +7,32 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional
class SychronizerDarksideTests: XCTestCase { class SychronizerDarksideTests: 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? // TODO: Parameterize this from environment?
// swiftlint:disable:next line_length
let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a" //TODO: Parameterize this from environment 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"
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 663150
let defaultLatestHeight: BlockHeight = 663175 let defaultLatestHeight: BlockHeight = 663175
let branchID = "2bb40e60"
let chainName = "main"
let network = DarksideWalletDNetwork()
var birthday: BlockHeight = 663150
var coordinator: TestCoordinator! var coordinator: TestCoordinator!
var syncedExpectation = XCTestExpectation(description: "synced") var syncedExpectation = XCTestExpectation(description: "synced")
var sentTransactionExpectation = XCTestExpectation(description: "sent") var sentTransactionExpectation = XCTestExpectation(description: "sent")
var expectedReorgHeight: BlockHeight = 665188 var expectedReorgHeight: BlockHeight = 665188
var expectedRewindHeight: BlockHeight = 665188 var expectedRewindHeight: BlockHeight = 665188
var reorgExpectation: XCTestExpectation = XCTestExpectation(description: "reorg") var reorgExpectation = XCTestExpectation(description: "reorg")
let branchID = "2bb40e60" var foundTransactions: [ConfirmedTransactionEntity] = []
let chainName = "main"
let network = DarksideWalletDNetwork()
var foundTransactions = [ConfirmedTransactionEntity]()
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
coordinator = try TestCoordinator( coordinator = try TestCoordinator(
seed: seedPhrase, seed: seedPhrase,
walletBirthday: birthday, walletBirthday: birthday,
@ -37,6 +43,7 @@ class SychronizerDarksideTests: XCTestCase {
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
try coordinator.stop() try coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
@ -45,20 +52,22 @@ class SychronizerDarksideTests: XCTestCase {
} }
func testFoundTransactions() throws { func testFoundTransactions() throws {
NotificationCenter.default.addObserver(self, selector: #selector(handleFoundTransactions(_:)), name: Notification.Name.synchronizerFoundTransactions, object: nil) NotificationCenter.default.addObserver(
self,
selector: #selector(handleFoundTransactions(_:)),
name: Notification.Name.synchronizerFoundTransactions,
object: nil
)
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
try coordinator.applyStaged(blockheight: receivedTxHeight + 1) try coordinator.applyStaged(blockheight: receivedTxHeight + 1)
sleep(2) sleep(2)
let preTxExpectation = XCTestExpectation(description: "pre receive") let preTxExpectation = XCTestExpectation(description: "pre receive")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
preTxExpectation.fulfill() preTxExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
@ -68,20 +77,22 @@ class SychronizerDarksideTests: XCTestCase {
} }
func testFoundManyTransactions() throws { func testFoundManyTransactions() throws {
NotificationCenter.default.addObserver(self, selector: #selector(handleFoundTransactions(_:)), name: Notification.Name.synchronizerFoundTransactions, object: nil) NotificationCenter.default.addObserver(
self,
selector: #selector(handleFoundTransactions(_:)),
name: Notification.Name.synchronizerFoundTransactions,
object: nil
)
try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName,length: 1000) try FakeChainBuilder.buildChain(darksideWallet: self.coordinator.service, branchID: branchID, chainName: chainName, length: 1000)
let receivedTxHeight: BlockHeight = 663229 let receivedTxHeight: BlockHeight = 663229
try coordinator.applyStaged(blockheight: receivedTxHeight + 1) try coordinator.applyStaged(blockheight: receivedTxHeight + 1)
sleep(2) sleep(2)
let firsTxExpectation = XCTestExpectation(description: "first sync") let firsTxExpectation = XCTestExpectation(description: "first sync")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
firsTxExpectation.fulfill() firsTxExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
@ -95,36 +106,34 @@ class SychronizerDarksideTests: XCTestCase {
sleep(2) sleep(2)
let preTxExpectation = XCTestExpectation(description: "intermediate sync") let preTxExpectation = XCTestExpectation(description: "intermediate sync")
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
preTxExpectation.fulfill() preTxExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [preTxExpectation], timeout: 10) wait(for: [preTxExpectation], timeout: 10)
XCTAssertTrue(self.foundTransactions.count == 0) XCTAssertTrue(self.foundTransactions.isEmpty)
let findManyTxExpectation = XCTestExpectation(description: "final sync") let findManyTxExpectation = XCTestExpectation(description: "final sync")
try coordinator.applyStaged(blockheight: 664010) try coordinator.applyStaged(blockheight: 664010)
sleep(2) sleep(2)
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { _ in
findManyTxExpectation.fulfill() findManyTxExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [findManyTxExpectation], timeout: 10) wait(for: [findManyTxExpectation], timeout: 10)
XCTAssertEqual(self.foundTransactions.count, 2) XCTAssertEqual(self.foundTransactions.count, 2)
} }
@objc func handleFoundTransactions(_ notification: Notification) { @objc func handleFoundTransactions(_ notification: Notification) {
guard let userInfo = notification.userInfo, guard
let transactions = userInfo[SDKSynchronizer.NotificationKeys.foundTransactions] as? [ConfirmedTransactionEntity] else { let userInfo = notification.userInfo,
let transactions = userInfo[SDKSynchronizer.NotificationKeys.foundTransactions] as? [ConfirmedTransactionEntity]
else {
return return
} }
self.foundTransactions.append(contentsOf: transactions) self.foundTransactions.append(contentsOf: transactions)

View File

@ -9,13 +9,13 @@ import Foundation
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
/** /**
This is the TestCoordinator This is the TestCoordinator
What does it do? quite a lot. What does it do? quite a lot.
Is it a nice "SOLID" "Clean Code" piece of source code? Is it a nice "SOLID" "Clean Code" piece of source code?
Hell no. It's your testing overlord and you will be grateful it is. Hell no. It's your testing overlord and you will be grateful it is.
*/ */
// swiftlint:disable force_try function_parameter_count
class TestCoordinator { class TestCoordinator {
enum CoordinatorError: Error { enum CoordinatorError: Error {
case notDarksideWallet case notDarksideWallet
case notificationFromUnknownSynchronizer case notificationFromUnknownSynchronizer
@ -44,22 +44,43 @@ class TestCoordinator {
var spendingKeys: [String]? var spendingKeys: [String]?
var databases: TemporaryTestDatabases var databases: TemporaryTestDatabases
let network: ZcashNetwork let network: ZcashNetwork
convenience init(seed: String, convenience init(
walletBirthday: BlockHeight, seed: String,
channelProvider: ChannelProvider, walletBirthday: BlockHeight,
network: ZcashNetwork) throws { channelProvider: ChannelProvider,
network: ZcashNetwork
) throws {
let derivationTool = DerivationTool(networkType: network.networkType) let derivationTool = DerivationTool(networkType: network.networkType)
guard let spendingKey = try derivationTool.deriveSpendingKeys(
seed: TestSeed().seed(), guard
numberOfAccounts: 1).first else { let spendingKey = try derivationTool
.deriveSpendingKeys(
seed: TestSeed().seed(),
numberOfAccounts: 1
)
.first
else {
throw CoordinatorError.builderError throw CoordinatorError.builderError
} }
guard let uvk = try derivationTool.deriveUnifiedViewingKeysFromSeed(TestSeed().seed(), numberOfAccounts: 1).first else { guard
let uvk = try derivationTool
.deriveUnifiedViewingKeysFromSeed(
TestSeed().seed(),
numberOfAccounts: 1
)
.first
else {
throw CoordinatorError.builderError throw CoordinatorError.builderError
} }
try self.init(spendingKey: spendingKey, unifiedViewingKey: uvk, walletBirthday: walletBirthday, channelProvider: channelProvider, network: network) try self.init(
spendingKey: spendingKey,
unifiedViewingKey: uvk,
walletBirthday: walletBirthday,
channelProvider: channelProvider,
network: network
)
} }
required init( required init(
@ -67,35 +88,47 @@ class TestCoordinator {
unifiedViewingKey: UnifiedViewingKey, unifiedViewingKey: UnifiedViewingKey,
walletBirthday: BlockHeight, walletBirthday: BlockHeight,
channelProvider: ChannelProvider, channelProvider: ChannelProvider,
network: ZcashNetwork) throws { network: ZcashNetwork
) throws {
self.spendingKey = spendingKey self.spendingKey = spendingKey
self.birthday = walletBirthday self.birthday = walletBirthday
self.channelProvider = channelProvider self.channelProvider = channelProvider
self.databases = TemporaryDbBuilder.build() self.databases = TemporaryDbBuilder.build()
self.network = network self.network = network
self.service = DarksideWalletService(service: LightWalletGRPCService(host: Constants.address, port: 9067, secure: false, singleCallTimeout: 10000, streamingCallTimeout: 1000000)) self.service = DarksideWalletService(
service: LightWalletGRPCService(
host: Constants.address,
port: 9067,
secure: false,
singleCallTimeout: 10000,
streamingCallTimeout: 1000000
)
)
let storage = CompactBlockStorage(url: databases.cacheDB, readonly: false) let storage = CompactBlockStorage(url: databases.cacheDB, readonly: false)
try storage.createTable() try storage.createTable()
let buildResult = try TestSynchronizerBuilder.build( let buildResult = try TestSynchronizerBuilder.build(
rustBackend: ZcashRustBackend.self, rustBackend: ZcashRustBackend.self,
lowerBoundHeight: self.birthday, lowerBoundHeight: self.birthday,
cacheDbURL: databases.cacheDB, cacheDbURL: databases.cacheDB,
dataDbURL: databases.dataDB, dataDbURL: databases.dataDB,
pendingDbURL: databases.pendingDB, pendingDbURL: databases.pendingDB,
endpoint: LightWalletEndpointBuilder.default, endpoint: LightWalletEndpointBuilder.default,
service: self.service, service: self.service,
repository: TransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: databases.dataDB.absoluteString)), repository: TransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: databases.dataDB.absoluteString)),
accountRepository: AccountRepositoryBuilder.build(dataDbURL: databases.dataDB, accountRepository: AccountRepositoryBuilder.build(
readOnly: true), dataDbURL: databases.dataDB,
storage: storage, readOnly: true
spendParamsURL: try __spendParamsURL(), ),
outputParamsURL: try __outputParamsURL(), storage: storage,
spendingKey: spendingKey, spendParamsURL: try __spendParamsURL(),
unifiedViewingKey: unifiedViewingKey, outputParamsURL: try __outputParamsURL(),
walletBirthday: WalletBirthday.birthday(with: birthday, network: network), spendingKey: spendingKey,
network: network, unifiedViewingKey: unifiedViewingKey,
loggerProxy: SampleLogger(logLevel: .debug)) walletBirthday: WalletBirthday.birthday(with: birthday, network: network),
network: network,
loggerProxy: SampleLogger(logLevel: .debug)
)
self.synchronizer = buildResult.synchronizer self.synchronizer = buildResult.synchronizer
self.spendingKeys = buildResult.spendingKeys self.spendingKeys = buildResult.spendingKeys
@ -114,7 +147,7 @@ class TestCoordinator {
try service.useDataset(DarksideDataset.beforeReOrg.rawValue) try service.useDataset(DarksideDataset.beforeReOrg.rawValue)
case .predefined(let dataset): case .predefined(let dataset):
try service.useDataset(dataset.rawValue) try service.useDataset(dataset.rawValue)
case .url(let urlString,_): case .url(let urlString, _):
try service.useDataset(from: urlString) try service.useDataset(from: urlString)
} }
} }
@ -130,15 +163,12 @@ class TestCoordinator {
try synchronizer.start(retry: true) try synchronizer.start(retry: true)
} }
/** /**
Notifications Notifications
*/ */
func subscribeToNotifications(synchronizer: Synchronizer) { func subscribeToNotifications(synchronizer: Synchronizer) {
NotificationCenter.default.addObserver(self, selector: #selector(synchronizerFailed(_:)), name: .synchronizerFailed, object: synchronizer) NotificationCenter.default.addObserver(self, selector: #selector(synchronizerFailed(_:)), name: .synchronizerFailed, object: synchronizer)
NotificationCenter.default.addObserver(self, selector: #selector(synchronizerSynced(_:)), name: .synchronizerSynced, object: synchronizer) NotificationCenter.default.addObserver(self, selector: #selector(synchronizerSynced(_:)), name: .synchronizerSynced, object: synchronizer)
} }
@objc func synchronizerFailed(_ notification: Notification) { @objc func synchronizerFailed(_ notification: Notification) {
@ -172,13 +202,12 @@ class TestCoordinator {
extension TestCoordinator { extension TestCoordinator {
func resetBlocks(dataset: DarksideData) throws { func resetBlocks(dataset: DarksideData) throws {
switch dataset { switch dataset {
case .default: case .default:
try service.useDataset(DarksideDataset.beforeReOrg.rawValue) try service.useDataset(DarksideDataset.beforeReOrg.rawValue)
case .predefined(let blocks): case .predefined(let blocks):
try service.useDataset(blocks.rawValue) try service.useDataset(blocks.rawValue)
case .url(let urlString,_): case .url(let urlString, _):
try service.useDataset(urlString) try service.useDataset(urlString)
} }
} }
@ -191,8 +220,8 @@ extension TestCoordinator {
try service.applyStaged(nextLatestHeight: blockheight) try service.applyStaged(nextLatestHeight: blockheight)
} }
func stageTransaction(_ tx: RawTransaction, at height: BlockHeight) throws { func stageTransaction(_ transaction: RawTransaction, at height: BlockHeight) throws {
try service.stageTransaction(tx, at: height) try service.stageTransaction(transaction, at: height)
} }
func stageTransaction(url: String, at height: BlockHeight) throws { func stageTransaction(url: String, at height: BlockHeight) throws {
@ -206,17 +235,17 @@ extension TestCoordinator {
func reset(saplingActivation: BlockHeight, branchID: String, chainName: String) throws { func reset(saplingActivation: BlockHeight, branchID: String, chainName: String) throws {
let config = self.synchronizer.blockProcessor.config let config = self.synchronizer.blockProcessor.config
self.synchronizer.blockProcessor.config = CompactBlockProcessor.Configuration( self.synchronizer.blockProcessor.config = CompactBlockProcessor.Configuration(
cacheDb: config.cacheDb, cacheDb: config.cacheDb,
dataDb: config.dataDb, dataDb: config.dataDb,
downloadBatchSize: config.downloadBatchSize, downloadBatchSize: config.downloadBatchSize,
retries: config.retries, retries: config.retries,
maxBackoffInterval: config.maxBackoffInterval, maxBackoffInterval: config.maxBackoffInterval,
rewindDistance: config.rewindDistance, rewindDistance: config.rewindDistance,
walletBirthday: config.walletBirthday, walletBirthday: config.walletBirthday,
saplingActivation: config.saplingActivation, saplingActivation: config.saplingActivation,
network: config.network) network: config.network
)
try service.reset(saplingActivation: saplingActivation, branchID: branchID, chainName: chainName) try service.reset(saplingActivation: saplingActivation, branchID: branchID, chainName: chainName)
} }
@ -226,24 +255,25 @@ extension TestCoordinator {
} }
struct TemporaryTestDatabases { struct TemporaryTestDatabases {
var cacheDB: URL var cacheDB: URL
var dataDB: URL var dataDB: URL
var pendingDB: URL var pendingDB: URL
} }
class TemporaryDbBuilder { enum TemporaryDbBuilder {
static func build() -> TemporaryTestDatabases { static func build() -> TemporaryTestDatabases {
let tempUrl = try! __documentsDirectory() let tempUrl = try! __documentsDirectory()
let timestamp = String(Int(Date().timeIntervalSince1970)) let timestamp = String(Int(Date().timeIntervalSince1970))
return TemporaryTestDatabases(cacheDB: tempUrl.appendingPathComponent("cache_db_\(timestamp).db"), return TemporaryTestDatabases(
dataDB: tempUrl.appendingPathComponent("data_db_\(timestamp).db"), cacheDB: tempUrl.appendingPathComponent("cache_db_\(timestamp).db"),
pendingDB: tempUrl.appendingPathComponent("pending_db_\(timestamp).db")) dataDB: tempUrl.appendingPathComponent("data_db_\(timestamp).db"),
pendingDB: tempUrl.appendingPathComponent("pending_db_\(timestamp).db")
)
} }
} }
class TestSynchronizerBuilder { enum TestSynchronizerBuilder {
static func build( static func build(
rustBackend: ZcashRustBackendWelding.Type, rustBackend: ZcashRustBackendWelding.Type,
lowerBoundHeight: BlockHeight, lowerBoundHeight: BlockHeight,
@ -262,7 +292,7 @@ class TestSynchronizerBuilder {
walletBirthday: WalletBirthday, walletBirthday: WalletBirthday,
network: ZcashNetwork, network: ZcashNetwork,
loggerProxy: Logger? = nil loggerProxy: Logger? = nil
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
let initializer = Initializer( let initializer = Initializer(
rustBackend: rustBackend, rustBackend: rustBackend,
lowerBoundHeight: lowerBoundHeight, lowerBoundHeight: lowerBoundHeight,
@ -281,37 +311,42 @@ class TestSynchronizerBuilder {
walletBirthday: walletBirthday.height, walletBirthday: walletBirthday.height,
loggerProxy: loggerProxy loggerProxy: loggerProxy
) )
let config = CompactBlockProcessor.Configuration( let config = CompactBlockProcessor.Configuration(
cacheDb: initializer.cacheDbURL, cacheDb: initializer.cacheDbURL,
dataDb: initializer.dataDbURL, dataDb: initializer.dataDbURL,
downloadBatchSize: 100, downloadBatchSize: 100,
retries: 5, retries: 5,
maxBackoffInterval: ZcashSDK.defaultMaxBackOffInterval, maxBackoffInterval: ZcashSDK.defaultMaxBackOffInterval,
rewindDistance: ZcashSDK.defaultRewindDistance, rewindDistance: ZcashSDK.defaultRewindDistance,
walletBirthday: walletBirthday.height, walletBirthday: walletBirthday.height,
saplingActivation: lowerBoundHeight, saplingActivation: lowerBoundHeight,
network: network) network: network
)
let processor = CompactBlockProcessor(service: service, let processor = CompactBlockProcessor(
storage: storage, service: service,
backend: rustBackend, storage: storage,
config: config, backend: rustBackend,
repository: repository, config: config,
accountRepository: accountRepository) repository: repository,
accountRepository: accountRepository
)
let synchronizer = try SDKSynchronizer(
let synchronizer = try SDKSynchronizer(status: .unprepared, status: .unprepared,
initializer: initializer, initializer: initializer,
transactionManager: OutboundTransactionManagerBuilder.build(initializer: initializer), transactionManager: OutboundTransactionManagerBuilder.build(initializer: initializer),
transactionRepository: repository, transactionRepository: repository,
utxoRepository: UTXORepositoryBuilder.build(initializer: initializer), utxoRepository: UTXORepositoryBuilder.build(initializer: initializer),
blockProcessor: processor blockProcessor: processor
) )
try synchronizer.prepare() try synchronizer.prepare()
return ([spendingKey], synchronizer) return ([spendingKey], synchronizer)
} }
static func build( static func build(
rustBackend: ZcashRustBackendWelding.Type, rustBackend: ZcashRustBackendWelding.Type,
lowerBoundHeight: BlockHeight, lowerBoundHeight: BlockHeight,
@ -330,14 +365,23 @@ class TestSynchronizerBuilder {
network: ZcashNetwork, network: ZcashNetwork,
loggerProxy: Logger? = nil loggerProxy: Logger? = nil
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
guard let spendingKey = try DerivationTool(networkType: network.networkType).deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1).first else { guard
let spendingKey = try DerivationTool(networkType: network.networkType)
.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1)
.first
else {
throw TestCoordinator.CoordinatorError.builderError throw TestCoordinator.CoordinatorError.builderError
} }
guard let uvk = try DerivationTool(networkType: network.networkType).deriveUnifiedViewingKeysFromSeed(seedBytes, numberOfAccounts: 1).first else { guard let uvk = try DerivationTool(networkType: network.networkType)
.deriveUnifiedViewingKeysFromSeed(seedBytes, numberOfAccounts: 1)
.first
else {
throw TestCoordinator.CoordinatorError.builderError throw TestCoordinator.CoordinatorError.builderError
} }
return try build(rustBackend: rustBackend,
return try build(
rustBackend: rustBackend,
lowerBoundHeight: lowerBoundHeight, lowerBoundHeight: lowerBoundHeight,
cacheDbURL: cacheDbURL, cacheDbURL: cacheDbURL,
dataDbURL: dataDbURL, dataDbURL: dataDbURL,
@ -352,8 +396,7 @@ class TestSynchronizerBuilder {
spendingKey: spendingKey, spendingKey: spendingKey,
unifiedViewingKey: uvk, unifiedViewingKey: uvk,
walletBirthday: walletBirthday, walletBirthday: walletBirthday,
network: network) network: network
)
} }
} }

View File

@ -7,7 +7,16 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_try
class TransactionEnhancementTests: XCTestCase { class TransactionEnhancementTests: XCTestCase {
let mockLatestHeight = BlockHeight(663250)
let targetLatestHeight = BlockHeight(663251)
let walletBirthday = BlockHeight(663150)
let network = DarksideWalletDNetwork()
let branchID = "2bb40e60"
let chainName = "main"
var initializer: Initializer! var initializer: Initializer!
var processorConfig: CompactBlockProcessor.Configuration! var processorConfig: CompactBlockProcessor.Configuration!
var processor: CompactBlockProcessor! var processor: CompactBlockProcessor!
@ -23,26 +32,26 @@ class TransactionEnhancementTests: XCTestCase {
var afterReorgIdleNotification: XCTestExpectation! var afterReorgIdleNotification: XCTestExpectation!
var txFoundNotificationExpectation: XCTestExpectation! var txFoundNotificationExpectation: XCTestExpectation!
var waitExpectation: XCTestExpectation! var waitExpectation: XCTestExpectation!
let mockLatestHeight = BlockHeight(663250)
let targetLatestHeight = BlockHeight(663251)
let walletBirthday = BlockHeight(663150)
let network = DarksideWalletDNetwork()
let branchID = "2bb40e60"
let chainName = "main"
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
logger = SampleLogger(logLevel: .debug) logger = SampleLogger(logLevel: .debug)
downloadStartedExpect = XCTestExpectation(description: self.description + " downloadStartedExpect") downloadStartedExpect = XCTestExpectation(description: "\(self.description) downloadStartedExpect")
stopNotificationExpectation = XCTestExpectation(description: self.description + " stopNotificationExpectation") stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation")
updatedNotificationExpectation = XCTestExpectation(description: self.description + " updatedNotificationExpectation") updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation")
startedValidatingNotificationExpectation = XCTestExpectation(description: self.description + " startedValidatingNotificationExpectation") startedValidatingNotificationExpectation = XCTestExpectation(
startedScanningNotificationExpectation = XCTestExpectation(description: self.description + " startedScanningNotificationExpectation") description: "\(self.description) startedValidatingNotificationExpectation"
idleNotificationExpectation = XCTestExpectation(description: self.description + " idleNotificationExpectation") )
afterReorgIdleNotification = XCTestExpectation(description: self.description + " afterReorgIdleNotification") startedScanningNotificationExpectation = XCTestExpectation(
reorgNotificationExpectation = XCTestExpectation(description: self.description + " reorgNotificationExpectation") description: "\(self.description) startedScanningNotificationExpectation"
txFoundNotificationExpectation = XCTestExpectation(description: self.description + "txFoundNotificationExpectation") )
idleNotificationExpectation = XCTestExpectation(description: "\(self.description) idleNotificationExpectation")
afterReorgIdleNotification = XCTestExpectation(description: "\(self.description) afterReorgIdleNotification")
reorgNotificationExpectation = XCTestExpectation(description: "\(self.description) reorgNotificationExpectation")
txFoundNotificationExpectation = XCTestExpectation(description: "\(self.description) txFoundNotificationExpectation")
waitExpectation = XCTestExpectation(description: self.description + "waitExpectation") waitExpectation = XCTestExpectation(description: "\(self.description) waitExpectation")
let birthday = WalletBirthday.birthday(with: walletBirthday, network: network) let birthday = WalletBirthday.birthday(with: walletBirthday, network: network)
@ -50,14 +59,19 @@ class TransactionEnhancementTests: XCTestCase {
let rustBackend = ZcashRustBackend.self let rustBackend = ZcashRustBackend.self
processorConfig = config processorConfig = config
try? FileManager.default.removeItem(at: processorConfig.cacheDb) try? FileManager.default.removeItem(at: processorConfig.cacheDb)
try? FileManager.default.removeItem(at: processorConfig.dataDb) try? FileManager.default.removeItem(at: processorConfig.dataDb)
_ = rustBackend.initAccountsTable(dbData: processorConfig.dataDb, seed: TestSeed().seed(), accounts: 1,networkType: network.networkType) _ = rustBackend.initAccountsTable(dbData: processorConfig.dataDb, seed: TestSeed().seed(), accounts: 1, networkType: network.networkType)
_ = try rustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: network.networkType) _ = try rustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: network.networkType)
_ = try rustBackend.initBlocksTable(dbData: processorConfig.dataDb, height: Int32(birthday.height), hash: birthday.hash, time: birthday.time, saplingTree: birthday.tree, networkType: network.networkType) _ = try rustBackend.initBlocksTable(
dbData: processorConfig.dataDb,
height: Int32(birthday.height),
hash: birthday.hash,
time: birthday.time,
saplingTree: birthday.tree,
networkType: network.networkType
)
let service = DarksideWalletService() let service = DarksideWalletService()
darksideWalletService = service darksideWalletService = service
@ -65,17 +79,23 @@ class TransactionEnhancementTests: XCTestCase {
try! storage.createTable() try! storage.createTable()
downloader = CompactBlockDownloader(service: service, storage: storage) downloader = CompactBlockDownloader(service: service, storage: storage)
processor = CompactBlockProcessor(service: service, processor = CompactBlockProcessor(
storage: storage, service: service,
backend: rustBackend, storage: storage,
config: processorConfig) backend: rustBackend,
config: processorConfig
)
NotificationCenter.default.addObserver(
self,
NotificationCenter.default.addObserver(self, selector: #selector(processorFailed(_:)), name: Notification.Name.blockProcessorFailed, object: processor) selector: #selector(processorFailed(_:)),
name: Notification.Name.blockProcessorFailed,
object: processor
)
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
try? FileManager.default.removeItem(at: processorConfig.cacheDb) try? FileManager.default.removeItem(at: processorConfig.cacheDb)
try? FileManager.default.removeItem(at: processorConfig.dataDb) try? FileManager.default.removeItem(at: processorConfig.dataDb)
downloadStartedExpect.unsubscribeFromNotifications() downloadStartedExpect.unsubscribeFromNotifications()
@ -89,7 +109,7 @@ class TransactionEnhancementTests: XCTestCase {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
} }
fileprivate func startProcessing() throws { private func startProcessing() throws {
XCTAssertNotNil(processor) XCTAssertNotNil(processor)
// Subscribe to notifications // Subscribe to notifications
@ -98,13 +118,11 @@ class TransactionEnhancementTests: XCTestCase {
updatedNotificationExpectation.subscribe(to: Notification.Name.blockProcessorUpdated, object: processor) updatedNotificationExpectation.subscribe(to: Notification.Name.blockProcessorUpdated, object: processor)
startedValidatingNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedValidating, object: processor) startedValidatingNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedValidating, object: processor)
startedScanningNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedScanning, object: processor) startedScanningNotificationExpectation.subscribe(to: Notification.Name.blockProcessorStartedScanning, object: processor)
try processor.start() try processor.start()
} }
func testBasicEnhacement() throws { func testBasicEnhacement() throws {
let targetLatestHeight = BlockHeight(663250) let targetLatestHeight = BlockHeight(663250)
let walletBirthday = WalletBirthday.birthday(with: 663151, network: network).height let walletBirthday = WalletBirthday.birthday(with: 663151, network: network).height
@ -112,47 +130,48 @@ class TransactionEnhancementTests: XCTestCase {
} }
func basicEnhancementTest(latestHeight: BlockHeight, walletBirthday: BlockHeight) throws { func basicEnhancementTest(latestHeight: BlockHeight, walletBirthday: BlockHeight) throws {
do { do {
try darksideWalletService.reset(saplingActivation: 663150, branchID: branchID, chainName: chainName) try darksideWalletService.reset(saplingActivation: 663150, branchID: branchID, chainName: chainName)
try darksideWalletService.useDataset(DarksideDataset.beforeReOrg.rawValue) try darksideWalletService.useDataset(DarksideDataset.beforeReOrg.rawValue)
try darksideWalletService.applyStaged(nextLatestHeight: 663200) try darksideWalletService.applyStaged(nextLatestHeight: 663200)
} catch { } catch {
XCTFail("Error: \(error)") XCTFail("Error: \(error)")
return return
} }
sleep(3) sleep(3)
/** /**
connect to dLWD connect to dLWD
request latest height -> receive firstLatestHeight request latest height -> receive firstLatestHeight
*/ */
do { do {
print("first latest height: \(try darksideWalletService.latestBlockHeight())") dump("first latest height: \(try darksideWalletService.latestBlockHeight())")
} catch { } catch {
XCTFail("Error: \(error)") XCTFail("Error: \(error)")
return return
} }
/** /**
download and sync blocks from walletBirthday to firstLatestHeight download and sync blocks from walletBirthday to firstLatestHeight
*/ */
do { do {
try startProcessing() try startProcessing()
} catch { } catch {
XCTFail("Error: \(error)") XCTFail("Error: \(error)")
} }
wait(for: [downloadStartedExpect,
startedValidatingNotificationExpectation,
startedScanningNotificationExpectation,
txFoundNotificationExpectation,
idleNotificationExpectation], timeout: 30)
idleNotificationExpectation.unsubscribeFromNotifications()
wait(
for: [
downloadStartedExpect,
startedValidatingNotificationExpectation,
startedScanningNotificationExpectation,
txFoundNotificationExpectation,
idleNotificationExpectation
],
timeout: 30
)
idleNotificationExpectation.unsubscribeFromNotifications()
} }
@objc func processorFailed(_ notification: Notification) { @objc func processorFailed(_ notification: Notification) {
@ -163,6 +182,4 @@ class TransactionEnhancementTests: XCTestCase {
XCTFail("CompactBlockProcessor failed") XCTFail("CompactBlockProcessor failed")
} }
} }
} }

View File

@ -7,37 +7,39 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_unwrapping
class TransactionRepositoryTests: XCTestCase { class TransactionRepositoryTests: XCTestCase {
var transactionRepository: TransactionRepository! var transactionRepository: TransactionRepository!
override func setUp() { override func setUp() {
super.setUp()
transactionRepository = TestDbBuilder.transactionRepository() transactionRepository = TestDbBuilder.transactionRepository()
} }
override func tearDown() { override func tearDown() {
super.tearDown()
transactionRepository = nil transactionRepository = nil
} }
func testCount() { func testCount() {
var count: Int? var count: Int?
XCTAssertNoThrow(try { count = try self.transactionRepository.countAll()}()) XCTAssertNoThrow(try { count = try self.transactionRepository.countAll() }())
XCTAssertNotNil(count) XCTAssertNotNil(count)
XCTAssertEqual(count, 27) XCTAssertEqual(count, 27)
} }
func testCountUnmined() { func testCountUnmined() {
var count: Int? var count: Int?
XCTAssertNoThrow(try { count = try self.transactionRepository.countUnmined()}()) XCTAssertNoThrow(try { count = try self.transactionRepository.countUnmined() }())
XCTAssertNotNil(count) XCTAssertNotNil(count)
XCTAssertEqual(count, 0) XCTAssertEqual(count, 0)
} }
func testFindById() { func testFindById() {
var tx: TransactionEntity? var transaction: TransactionEntity?
XCTAssertNoThrow(try { tx = try self.transactionRepository.findBy(id: 10)}()) XCTAssertNoThrow(try { transaction = try self.transactionRepository.findBy(id: 10) }())
guard let transaction = tx else { guard let transaction = transaction else {
XCTFail("transaction is nil") XCTFail("transaction is nil")
return return
} }
@ -45,16 +47,18 @@ class TransactionRepositoryTests: XCTestCase {
XCTAssertEqual(transaction.id, 10) XCTAssertEqual(transaction.id, 10)
XCTAssertEqual(transaction.minedHeight, 652812) XCTAssertEqual(transaction.minedHeight, 652812)
XCTAssertEqual(transaction.transactionIndex, 5) XCTAssertEqual(transaction.transactionIndex, 5)
} }
func testFindByTxId() { func testFindByTxId() {
var tx: TransactionEntity? var transaction: TransactionEntity?
let id = Data(fromHexEncodedString: "0BAFC5B83F5B39A5270144ECD98DBC65115055927EDDA8FF20F081FFF13E4780")! let id = Data(fromHexEncodedString: "0BAFC5B83F5B39A5270144ECD98DBC65115055927EDDA8FF20F081FFF13E4780")!
XCTAssertNoThrow(try { tx = try self.transactionRepository.findBy(rawId: id)}()) XCTAssertNoThrow(
guard let transaction = tx else { try { transaction = try self.transactionRepository.findBy(rawId: id) }()
)
guard let transaction = transaction else {
XCTFail("transaction is nil") XCTFail("transaction is nil")
return return
} }
@ -111,16 +115,17 @@ class TransactionRepositoryTests: XCTestCase {
} }
func testFindAllFrom() throws { func testFindAllFrom() throws {
guard let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max), guard
let allFromNil = try self.transactionRepository.findAll(from: nil, limit: Int.max) let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max),
let allFromNil = try self.transactionRepository.findAll(from: nil, limit: Int.max)
else { else {
return XCTFail("find all failed") return XCTFail("find all failed")
} }
XCTAssertEqual(transactions.count, allFromNil.count) XCTAssertEqual(transactions.count, allFromNil.count)
for t in transactions { for transaction in transactions {
guard allFromNil.first(where: { $0.rawTransactionId == t.rawTransactionId}) != nil else { guard allFromNil.first(where: { $0.rawTransactionId == transaction.rawTransactionId }) != nil else {
XCTFail("not equal") XCTFail("not equal")
return return
} }
@ -128,11 +133,11 @@ class TransactionRepositoryTests: XCTestCase {
} }
func testFindAllFromSlice() throws { func testFindAllFromSlice() throws {
let limit = 4 let limit = 4
let start = 7 let start = 7
guard let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max), guard
let allFromNil = try self.transactionRepository.findAll(from: transactions[start], limit: limit) let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max),
let allFromNil = try self.transactionRepository.findAll(from: transactions[start], limit: limit)
else { else {
return XCTFail("find all failed") return XCTFail("find all failed")
} }
@ -141,29 +146,28 @@ class TransactionRepositoryTests: XCTestCase {
let slice = transactions[start + 1 ... start + limit] let slice = transactions[start + 1 ... start + limit]
XCTAssertEqual(slice.count, allFromNil.count) XCTAssertEqual(slice.count, allFromNil.count)
for t in slice { for transaction in slice {
guard allFromNil.first(where: { $0.rawTransactionId == t.rawTransactionId}) != nil else { guard allFromNil.first(where: { $0.rawTransactionId == transaction.rawTransactionId }) != nil else {
XCTFail("not equal") XCTFail("not equal")
return return
} }
} }
} }
func testFindAllFromLastSlice() throws { func testFindAllFromLastSlice() throws {
let limit = 10 let limit = 10
let start = 20 let start = 20
guard let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max), guard
let allFromNil = try self.transactionRepository.findAll(from: transactions[start], limit: limit) let transactions = try self.transactionRepository.findAll(offset: 0, limit: Int.max),
let allFromNil = try self.transactionRepository.findAll(from: transactions[start], limit: limit)
else { else {
return XCTFail("find all failed") return XCTFail("find all failed")
} }
let slice = transactions[start + 1 ..< transactions.count] let slice = transactions[start + 1 ..< transactions.count]
XCTAssertEqual(slice.count, allFromNil.count) XCTAssertEqual(slice.count, allFromNil.count)
for t in slice { for transaction in slice {
guard allFromNil.first(where: { $0.rawTransactionId == t.rawTransactionId}) != nil else { guard allFromNil.first(where: { $0.rawTransactionId == transaction.rawTransactionId }) != nil else {
XCTFail("not equal") XCTFail("not equal")
return return
} }
@ -172,36 +176,34 @@ class TransactionRepositoryTests: XCTestCase {
} }
extension Data { extension Data {
init?(fromHexEncodedString string: String) { init?(fromHexEncodedString string: String) {
// Convert 0 ... 9, a ... f, A ...F to their decimal value, // Convert 0 ... 9, a ... f, A ...F to their decimal value,
// return nil for all other input characters // return nil for all other input characters
func decodeNibble(u: UInt16) -> UInt8? { func decodeNibble(bytes: UInt16) -> UInt8? {
switch(u) { switch bytes {
case 0x30 ... 0x39: case 0x30 ... 0x39:
return UInt8(u - 0x30) return UInt8(bytes - 0x30)
case 0x41 ... 0x46: case 0x41 ... 0x46:
return UInt8(u - 0x41 + 10) return UInt8(bytes - 0x41 + 10)
case 0x61 ... 0x66: case 0x61 ... 0x66:
return UInt8(u - 0x61 + 10) return UInt8(bytes - 0x61 + 10)
default: default:
return nil return nil
} }
} }
self.init(capacity: string.utf16.count/2) self.init(capacity: string.utf16.count / 2)
var even = true var even = true
var byte: UInt8 = 0 var byte: UInt8 = 0
for c in string.utf16 { for char in string.utf16 {
guard let val = decodeNibble(u: c) else { return nil } guard let val = decodeNibble(bytes: char) else { return nil }
if even { if even {
byte = val << 4 byte = val << 4
} else { } else {
byte += val byte += val
self.append(byte) self.append(byte)
} }
even = !even even.toggle()
} }
guard even else { return nil } guard even else { return nil }
} }

View File

@ -7,13 +7,12 @@
import XCTest import XCTest
// swiftlint:disable force_unwrapping
class TxIdTests: XCTestCase { class TxIdTests: XCTestCase {
func testTxIdAsString() { func testTxIdAsString() {
let transactionId = "5cf915c5d01007c39d602e08ab59d98aba366e2fb7ac01f2cdad4bf4f8f300bb" let transactionId = "5cf915c5d01007c39d602e08ab59d98aba366e2fb7ac01f2cdad4bf4f8f300bb"
let expectedTxIdString = "bb00f3f8f44badcdf201acb72f6e36ba8ad959ab082e609dc30710d0c515f95c" let expectedTxIdString = "bb00f3f8f44badcdf201acb72f6e36ba8ad959ab082e609dc30710d0c515f95c"
XCTAssertEqual(Data(fromHexEncodedString: transactionId)!.toHexStringTxId(), expectedTxIdString) XCTAssertEqual(Data(fromHexEncodedString: transactionId)!.toHexStringTxId(), expectedTxIdString)
} }
} }

View File

@ -10,41 +10,42 @@ import Foundation
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_try force_unwrapping
class WalletTests: XCTestCase { class WalletTests: XCTestCase {
var dbData: URL! = nil var dbData: URL! = nil
var paramDestination: URL! = nil var paramDestination: URL! = nil
var cacheData: URL! = nil var cacheData: URL! = nil
var network = ZcashNetworkBuilder.network(for: .testnet) var network = ZcashNetworkBuilder.network(for: .testnet)
var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")! var seedData = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")!
override func setUp() { override func setUp() {
super.setUp()
dbData = try! __dataDbURL() dbData = try! __dataDbURL()
cacheData = try! __cacheDbURL() cacheData = try! __cacheDbURL()
paramDestination = try! __documentsDirectory().appendingPathComponent("parameters") paramDestination = try! __documentsDirectory().appendingPathComponent("parameters")
} }
override func tearDown() { override func tearDown() {
super.tearDown()
if FileManager.default.fileExists(atPath: dbData.absoluteString) { if FileManager.default.fileExists(atPath: dbData.absoluteString) {
try! FileManager.default.trashItem(at: dbData, resultingItemURL: nil) try! FileManager.default.trashItem(at: dbData, resultingItemURL: nil)
} }
} }
func testWalletInitialization() throws { func testWalletInitialization() throws {
let derivationTool = DerivationTool(networkType: network.networkType) let derivationTool = DerivationTool(networkType: network.networkType)
let uvk = try derivationTool.deriveUnifiedViewingKeysFromSeed(seedData.bytes, numberOfAccounts: 1) let uvk = try derivationTool.deriveUnifiedViewingKeysFromSeed(seedData.bytes, numberOfAccounts: 1)
let wallet = Initializer(cacheDbURL: try __cacheDbURL(), let wallet = Initializer(
dataDbURL: try __dataDbURL(), cacheDbURL: try __cacheDbURL(),
pendingDbURL: try TestDbBuilder.pendingTransactionsDbURL(), dataDbURL: try __dataDbURL(),
endpoint: LightWalletEndpointBuilder.default, pendingDbURL: try TestDbBuilder.pendingTransactionsDbURL(),
network: network, endpoint: LightWalletEndpointBuilder.default,
spendParamsURL: try __spendParamsURL(), network: network,
outputParamsURL: try __outputParamsURL(), spendParamsURL: try __spendParamsURL(),
viewingKeys: uvk, outputParamsURL: try __outputParamsURL(),
walletBirthday: 663194) viewingKeys: uvk,
walletBirthday: 663194
)
let synchronizer = try SDKSynchronizer(initializer: wallet) let synchronizer = try SDKSynchronizer(initializer: wallet)
XCTAssertNoThrow(try synchronizer.prepare()) XCTAssertNoThrow(try synchronizer.prepare())
@ -56,7 +57,7 @@ class WalletTests: XCTestCase {
} }
} }
struct WalletBirthdayProvider { enum WalletBirthdayProvider {
static var testBirthday: WalletBirthday { static var testBirthday: WalletBirthday {
WalletBirthday() WalletBirthday()
} }

View File

@ -7,11 +7,13 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
class Z2TReceiveTests: 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? // swiftlint:disable force_unwrapping implicitly_unwrapped_optional
class Z2TReceiveTests: XCTestCase {
// 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 = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" //TODO: Parameterize this from environment let testRecipientAddress = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" // TODO: Parameterize this from environment
let sendAmount: Int64 = 1000 let sendAmount: Int64 = 1000
var birthday: BlockHeight = 663150 var birthday: BlockHeight = 663150
@ -26,18 +28,23 @@ class Z2TReceiveTests: XCTestCase {
let network = DarksideWalletDNetwork() let network = DarksideWalletDNetwork()
override func setUpWithError() throws { override func setUpWithError() throws {
try super.setUpWithError()
coordinator = try TestCoordinator( coordinator = try TestCoordinator(
seed: seedPhrase, seed: seedPhrase,
walletBirthday: birthday, walletBirthday: birthday,
channelProvider: ChannelProvider(), channelProvider: ChannelProvider(),
network: network network: network
) )
try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName)
} }
override func tearDownWithError() throws { override func tearDownWithError() throws {
try super.tearDownWithError()
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
try coordinator.stop() try coordinator.stop()
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
try? FileManager.default.removeItem(at: coordinator.databases.dataDB) try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
@ -45,108 +52,116 @@ class Z2TReceiveTests: XCTestCase {
} }
func subscribeToFoundTransactions() { func subscribeToFoundTransactions() {
NotificationCenter.default.addObserver(self, selector: #selector(foundTransactions(_:)), name: .synchronizerFoundTransactions, object: nil) NotificationCenter.default.addObserver(
self,
selector: #selector(foundTransactions(_:)),
name: .synchronizerFoundTransactions,
object: nil
)
} }
@objc func foundTransactions(_ notification: Notification) { @objc func foundTransactions(_ notification: Notification) {
guard let transactions = notification.userInfo?[SDKSynchronizer.NotificationKeys.foundTransactions] else { guard notification.userInfo?[SDKSynchronizer.NotificationKeys.foundTransactions] != nil else {
XCTFail("found transactions notification is empty") XCTFail("found transactions notification is empty")
return return
} }
self.foundTransactionsExpectation.fulfill() self.foundTransactionsExpectation.fulfill()
} }
func testFoundTransactions() throws { func testFoundTransactions() 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
var initialTotalBalance: Int64 = -1
/* /*
2. applyStaged(received_Tx_height) 2. applyStaged(received_Tx_height)
*/ */
try coordinator.applyStaged(blockheight: receivedTxHeight) try coordinator.applyStaged(blockheight: receivedTxHeight)
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 coordinator.sync(
initialTotalBalance = synchronizer.initializer.getBalance() completion: { _ in
preTxExpectation.fulfill() preTxExpectation.fulfill()
}, error: self.handleError) },
error: self.handleError
)
wait(for: [preTxExpectation, foundTransactionsExpectation], timeout: 5) wait(for: [preTxExpectation, foundTransactionsExpectation], timeout: 5)
let sendExpectation = XCTestExpectation(description: "sendToAddress") let sendExpectation = XCTestExpectation(description: "sendToAddress")
var p: PendingTransactionEntity? var pendingEntity: PendingTransactionEntity?
var error: Error? = nil var error: Error?
let sendAmount: Int64 = 10000 let sendAmount: Int64 = 10000
/* /*
4. create transaction 4. create transaction
*/ */
coordinator.synchronizer.sendToAddress(spendingKey: coordinator.spendingKeys!.first!, zatoshi: sendAmount, toAddress: testRecipientAddress, memo: "test transaction", from: 0) { (result) in coordinator.synchronizer.sendToAddress(
spendingKey: coordinator.spendingKeys!.first!,
zatoshi: sendAmount,
toAddress: testRecipientAddress,
memo: "test transaction",
from: 0
) { result in
switch result { switch result {
case .success(let pending): case .success(let pending):
p = pending pendingEntity = pending
case .failure(let e): case .failure(let e):
error = e error = e
} }
sendExpectation.fulfill() sendExpectation.fulfill()
} }
wait(for: [sendExpectation], timeout: 12) wait(for: [sendExpectation], timeout: 12)
guard let pendingTx = p else { guard pendingEntity != nil else {
XCTFail("error sending to address. Error: \(String(describing: error))") XCTFail("error sending to address. Error: \(String(describing: error))")
return return
} }
/* /*
5. stage 10 empty blocks 5. stage 10 empty blocks
*/ */
try coordinator.stageBlockCreate(height: receivedTxHeight + 1, count: 10) try coordinator.stageBlockCreate(height: receivedTxHeight + 1, count: 10)
let sentTxHeight = receivedTxHeight + 1 let sentTxHeight = receivedTxHeight + 1
/* /*
6. stage sent tx at sentTxHeight 6. stage sent tx at sentTxHeight
*/ */
guard let sentTx = try coordinator.getIncomingTransactions()?.first else { guard let sentTx = try coordinator.getIncomingTransactions()?.first else {
XCTFail("sent transaction not present on Darksidewalletd") XCTFail("sent transaction not present on Darksidewalletd")
return return
} }
try coordinator.stageTransaction(sentTx, at: sentTxHeight) try coordinator.stageTransaction(sentTx, at: sentTxHeight)
/* /*
6a. applyheight(sentTxHeight + 1 ) 6a. applyheight(sentTxHeight + 1 )
*/ */
try coordinator.applyStaged(blockheight: sentTxHeight + 1) try coordinator.applyStaged(blockheight: sentTxHeight + 1)
sleep(2) sleep(2)
self.foundTransactionsExpectation = XCTestExpectation(description: "inbound expectation") self.foundTransactionsExpectation = XCTestExpectation(description: "inbound expectation")
/* /*
7. sync to sentTxHeight + 1 7. sync to sentTxHeight + 1
*/ */
let sentTxSyncExpectation = XCTestExpectation(description: "sent tx sync expectation") let sentTxSyncExpectation = XCTestExpectation(description: "sent tx sync expectation")
try coordinator.sync(completion: { (s) in try coordinator.sync(completion: { synchronizer in
let pMinedHeight = synchronizer.pendingTransactions.first?.minedHeight
let pMinedHeight = s.pendingTransactions.first?.minedHeight
XCTAssertEqual(pMinedHeight, sentTxHeight) XCTAssertEqual(pMinedHeight, sentTxHeight)
sentTxSyncExpectation.fulfill() sentTxSyncExpectation.fulfill()
}, error: self.handleError) }, error: self.handleError)
wait(for: [sentTxSyncExpectation, foundTransactionsExpectation], timeout: 5) wait(for: [sentTxSyncExpectation, foundTransactionsExpectation], timeout: 5)
} }
func handleError(_ error: Error?) { func handleError(_ error: Error?) {
_ = try? coordinator.stop() _ = try? coordinator.stop()

View File

@ -8,14 +8,13 @@
import XCTest import XCTest
import GRPC import GRPC
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable implicitly_unwrapped_optional force_try force_unwrapping
class ZcashLightClientKitTests: XCTestCase { class ZcashLightClientKitTests: XCTestCase {
var latestBlockHeight: BlockHeight! var latestBlockHeight: BlockHeight!
var service: LightWalletGRPCService! var service: LightWalletGRPCService!
override func setUp() { override func setUp() {
super.setUp() super.setUp()
service = LightWalletGRPCService(endpoint: LightWalletEndpoint(address: Constants.address, port: 9067)) service = LightWalletGRPCService(endpoint: LightWalletEndpoint(address: Constants.address, port: 9067))
@ -30,17 +29,14 @@ class ZcashLightClientKitTests: XCTestCase {
} }
func testEnvironmentLaunch() { func testEnvironmentLaunch() {
let address = Constants.address let address = Constants.address
XCTAssertFalse(address.isEmpty, "Your \'\(Environment.lightwalletdKey)\' key is missing from your launch environment variables") XCTAssertFalse(address.isEmpty, "Your \'\(Environment.lightwalletdKey)\' key is missing from your launch environment variables")
} }
func testService() { func testService() {
// and that it has a non-zero size // and that it has a non-zero size
XCTAssert(latestBlockHeight > 0) XCTAssert(latestBlockHeight > 0)
} }
func testBlockRangeServiceTilLastest() { func testBlockRangeServiceTilLastest() {
@ -49,11 +45,10 @@ class ZcashLightClientKitTests: XCTestCase {
let startHeight = latestBlockHeight - expectedCount let startHeight = latestBlockHeight - expectedCount
let endHeight = latestBlockHeight! let endHeight = latestBlockHeight!
var blocks = [CompactBlock]() var blocks: [CompactBlock] = []
guard let call = try? service!.blockRange(startHeight: startHeight, endHeight: endHeight, result: { guard let call = try? service!.blockRange(startHeight: startHeight, endHeight: endHeight, result: {
blocks.append($0) blocks.append($0)
count += 1 count += 1
}) else { }) else {
XCTFail("failed to create getBlockRange( \(startHeight) ..<= \(endHeight)") XCTFail("failed to create getBlockRange( \(startHeight) ..<= \(endHeight)")
return return
@ -61,11 +56,9 @@ class ZcashLightClientKitTests: XCTestCase {
_ = try! call.status.wait() _ = try! call.status.wait()
XCTAssertEqual(expectedCount + 1, count) XCTAssertEqual(expectedCount + 1, count)
} }
} }
class Environment { enum Environment {
static let lightwalletdKey = "LIGHTWALLETD_ADDRESS" static let lightwalletdKey = "LIGHTWALLETD_ADDRESS"
} }

View File

@ -9,23 +9,27 @@
import XCTest import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable force_unwrapping implicitly_unwrapped_optional force_try
class ZcashRustBackendTests: XCTestCase { class ZcashRustBackendTests: XCTestCase {
var dbData: URL! var dbData: URL!
var dataDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedDataDbURL()!) var dataDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedDataDbURL()!)
var cacheDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedCacheDbURL()!) var cacheDbHandle = TestDbHandle(originalDb: TestDbBuilder.prePopulatedCacheDbURL()!)
let spendingKey = "secret-extended-key-test1qvpevftsqqqqpqy52ut2vv24a2qh7nsukew7qg9pq6djfwyc3xt5vaxuenshp2hhspp9qmqvdh0gs2ljpwxders5jkwgyhgln0drjqaguaenfhehz4esdl4kwlm5t9q0l6wmzcrvcf5ed6dqzvct3e2ge7f6qdvzhp02m7sp5a0qjssrwpdh7u6tq89hl3wchuq8ljq8r8rwd6xdwh3nry9at80z7amnj3s6ah4jevnvfr08gxpws523z95g6dmn4wm6l3658kd4xcq9rc0qn" let spendingKey =
// swiftlint:disable:next line_length
"secret-extended-key-test1qvpevftsqqqqpqy52ut2vv24a2qh7nsukew7qg9pq6djfwyc3xt5vaxuenshp2hhspp9qmqvdh0gs2ljpwxders5jkwgyhgln0drjqaguaenfhehz4esdl4kwlm5t9q0l6wmzcrvcf5ed6dqzvct3e2ge7f6qdvzhp02m7sp5a0qjssrwpdh7u6tq89hl3wchuq8ljq8r8rwd6xdwh3nry9at80z7amnj3s6ah4jevnvfr08gxpws523z95g6dmn4wm6l3658kd4xcq9rc0qn"
let recipientAddress = "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6" let recipientAddress = "ztestsapling1ctuamfer5xjnnrdr3xdazenljx0mu0gutcf9u9e74tr2d3jwjnt0qllzxaplu54hgc2tyjdc2p6"
let zpend: Int = 500_000 let zpend: Int = 500_000
let networkType = NetworkType.testnet let networkType = NetworkType.testnet
override func setUp() { override func setUp() {
super.setUp()
dbData = try! __dataDbURL() dbData = try! __dataDbURL()
try? dataDbHandle.setUp() try? dataDbHandle.setUp()
} }
override func tearDown() { override func tearDown() {
super.tearDown()
try? FileManager.default.removeItem(at: dbData!) try? FileManager.default.removeItem(at: dbData!)
dataDbHandle.dispose() dataDbHandle.dispose()
} }
@ -35,27 +39,33 @@ class ZcashRustBackendTests: XCTestCase {
XCTAssertNoThrow(try ZcashRustBackend.initDataDb(dbData: dbData!, networkType: networkType)) XCTAssertNoThrow(try ZcashRustBackend.initDataDb(dbData: dbData!, networkType: networkType))
let _ = ZcashRustBackend.initAccountsTable(dbData: dbData!, seed: Array(seed.utf8), accounts: 1, networkType: networkType) _ = ZcashRustBackend.initAccountsTable(dbData: dbData!, seed: Array(seed.utf8), accounts: 1, networkType: networkType)
XCTAssertNotNil(ZcashRustBackend.getLastError()) XCTAssertNotNil(ZcashRustBackend.getLastError())
} }
func testDeriveExtendedSpendingKeys() { func testDeriveExtendedSpendingKeys() {
let seed = Array("testreferencealicetestreferencealice".utf8) let seed = Array("testreferencealicetestreferencealice".utf8)
var spendingKeys: [String]? = nil var spendingKeys: [String]?
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
XCTAssertNotNil(spendingKeys) XCTAssertNotNil(spendingKeys)
XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true) XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true)
} }
func testDeriveExtendedFullViewingKeys() { func testDeriveExtendedFullViewingKeys() {
let seed = Array("testreferencealicetestreferencealice".utf8) let seed = Array("testreferencealicetestreferencealice".utf8)
var fullViewingKeys: [String]? = nil var fullViewingKeys: [String]?
XCTAssertNoThrow(try { fullViewingKeys = try ZcashRustBackend.deriveExtendedFullViewingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNoThrow(
try {
fullViewingKeys = try ZcashRustBackend.deriveExtendedFullViewingKeys(
seed: seed,
accounts: 1,
networkType: networkType
)
}()
)
XCTAssertNotNil(fullViewingKeys) XCTAssertNotNil(fullViewingKeys)
XCTAssertFalse(fullViewingKeys?.first?.isEmpty ?? true) XCTAssertFalse(fullViewingKeys?.first?.isEmpty ?? true)
@ -63,10 +73,9 @@ class ZcashRustBackendTests: XCTestCase {
func testDeriveExtendedFullViewingKey() { func testDeriveExtendedFullViewingKey() {
let seed = Array("testreferencealicetestreferencealice".utf8) let seed = Array("testreferencealicetestreferencealice".utf8)
var fullViewingKey: String? = nil var fullViewingKey: String?
var spendingKeys: [String]?
var spendingKeys: [String]? = nil
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
XCTAssertNotNil(spendingKeys) XCTAssertNotNil(spendingKeys)
@ -100,57 +109,81 @@ class ZcashRustBackendTests: XCTestCase {
XCTAssertEqual(addr, Optional("ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc")) XCTAssertEqual(addr, Optional("ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc"))
XCTAssertTrue(ZcashRustBackend.scanBlocks(dbCache: cacheDb, dbData: dbData, networkType: networkType)) XCTAssertTrue(ZcashRustBackend.scanBlocks(dbCache: cacheDb, dbData: dbData, networkType: networkType))
} }
func testIsValidTransparentAddressFalse() { func testIsValidTransparentAddressFalse() {
var isValid: Bool? = nil var isValid: Bool?
XCTAssertNoThrow(try { isValid = try ZcashRustBackend.isValidTransparentAddress("ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc", networkType: networkType) }()) XCTAssertNoThrow(
try {
isValid = try ZcashRustBackend.isValidTransparentAddress(
"ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc",
networkType: networkType
)
}()
)
if let valid = isValid { if let valid = isValid {
XCTAssertFalse(valid) XCTAssertFalse(valid)
} else { } else {
XCTFail() XCTFail("Failed as invalid")
} }
} }
func testIsValidTransparentAddressTrue() { func testIsValidTransparentAddressTrue() {
var isValid: Bool? = nil var isValid: Bool?
XCTAssertNoThrow(try { isValid = try ZcashRustBackend.isValidTransparentAddress("tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7", networkType: networkType) }()) XCTAssertNoThrow(
try {
isValid = try ZcashRustBackend.isValidTransparentAddress(
"tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7",
networkType: networkType
)
}()
)
if let valid = isValid { if let valid = isValid {
XCTAssertTrue(valid) XCTAssertTrue(valid)
} else { } else {
XCTFail() XCTFail("Failed as invalid")
} }
} }
func testIsValidShieldedAddressTrue() { func testIsValidShieldedAddressTrue() {
var isValid: Bool? = nil var isValid: Bool?
XCTAssertNoThrow(try { isValid = try ZcashRustBackend.isValidShieldedAddress("ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc", networkType: networkType) }()) XCTAssertNoThrow(
try {
isValid = try ZcashRustBackend.isValidShieldedAddress(
"ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc",
networkType: networkType
)
}()
)
if let valid = isValid { if let valid = isValid {
XCTAssertTrue(valid) XCTAssertTrue(valid)
} else { } else {
XCTFail() XCTFail("Failed as invalid")
} }
} }
func testIsValidShieldedAddressFalse() { func testIsValidShieldedAddressFalse() {
var isValid: Bool? = nil var isValid: Bool?
XCTAssertNoThrow(try { isValid = try ZcashRustBackend.isValidShieldedAddress("tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7", networkType: networkType) }()) XCTAssertNoThrow(
try {
isValid = try ZcashRustBackend.isValidShieldedAddress(
"tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7",
networkType: networkType
)
}()
)
if let valid = isValid { if let valid = isValid {
XCTAssertFalse(valid) XCTAssertFalse(valid)
} else { } else {
XCTFail() XCTFail("Failed as invalid")
} }
} }
} }

View File

@ -11,35 +11,68 @@ import GRPC
enum DarksideDataset: String { enum DarksideDataset: String {
case afterLargeReorg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/after-large-large.txt" case afterLargeReorg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/after-large-large.txt"
case afterSmallReorg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/after-small-reorg.txt" case afterSmallReorg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/after-small-reorg.txt"
case beforeReOrg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt" case beforeReOrg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt"
/** /**
see see
https://github.com/zcash-hackworks/darksidewalletd-test-data/tree/master/tx-index-reorg https://github.com/zcash-hackworks/darksidewalletd-test-data/tree/master/tx-index-reorg
*/ */
case txIndexChangeBefore = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-index-reorg/before-reorg.txt" case txIndexChangeBefore = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-index-reorg/before-reorg.txt"
case txIndexChangeAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-index-reorg/after-reorg.txt" case txIndexChangeAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-index-reorg/after-reorg.txt"
/** /**
See https://github.com/zcash-hackworks/darksidewalletd-test-data/tree/master/tx-height-reorg See https://github.com/zcash-hackworks/darksidewalletd-test-data/tree/master/tx-height-reorg
*/ */
case txHeightReOrgBefore = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-height-reorg/before-reorg.txt" case txHeightReOrgBefore = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-height-reorg/before-reorg.txt"
case txHeightReOrgAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-height-reorg/after-reorg.txt" case txHeightReOrgAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-height-reorg/after-reorg.txt"
/* /*
see: https://github.com/zcash-hackworks/darksidewalletd-test-data/tree/master/tx-remove-reorg see: https://github.com/zcash-hackworks/darksidewalletd-test-data/tree/master/tx-remove-reorg
*/ */
case txReOrgRemovesInboundTxBefore = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-remove-reorg/before-reorg.txt" case txReOrgRemovesInboundTxBefore = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-remove-reorg/before-reorg.txt"
case txReOrgRemovesInboundTxAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-remove-reorg/after-reorg.txt" case txReOrgRemovesInboundTxAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-remove-reorg/after-reorg.txt"
} }
class DarksideWalletService: LightWalletService { class DarksideWalletService: LightWalletService {
@discardableResult func blockStream(startHeight: BlockHeight, endHeight: BlockHeight, result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void, handler: @escaping (ZcashCompactBlock) -> Void, progress: @escaping (BlockProgressReporting) -> Void) -> CancellableCall { var channel: Channel
return service.blockStream(startHeight: startHeight, endHeight: endHeight, result: result, handler: handler, progress: progress) var service: LightWalletGRPCService
var darksideService: DarksideStreamerClient
init(endpoint: LightWalletEndpoint) {
self.channel = ChannelProvider().channel()
self.service = LightWalletGRPCService(endpoint: endpoint)
self.darksideService = DarksideStreamerClient(channel: channel)
}
init(service: LightWalletGRPCService) {
self.channel = ChannelProvider().channel()
self.darksideService = DarksideStreamerClient(channel: channel)
self.service = service
}
convenience init() {
self.init(endpoint: LightWalletEndpointBuilder.default)
}
@discardableResult
func blockStream(
startHeight: BlockHeight,
endHeight: BlockHeight,
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
handler: @escaping (ZcashCompactBlock) -> Void,
progress: @escaping (BlockProgress) -> Void
) -> CancellableCall {
return service.blockStream(
startHeight: startHeight,
endHeight: endHeight,
result: result,
handler: handler,
progress: progress
)
} }
func getInfo() throws -> LightWalletdInfo { func getInfo() throws -> LightWalletdInfo {
@ -51,15 +84,18 @@ class DarksideWalletService: LightWalletService {
} }
func closeConnection() { func closeConnection() {
} }
func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
return [] return []
} }
func fetchUTXOs(for tAddress: String, height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { func fetchUTXOs(
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1){ for tAddress: String,
height: BlockHeight,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
) {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
result(.success([])) result(.success([]))
} }
} }
@ -68,14 +104,18 @@ class DarksideWalletService: LightWalletService {
[] []
} }
func fetchUTXOs(for tAddresses: [String], height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { func fetchUTXOs(
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1){ for tAddresses: [String],
height: BlockHeight,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
) {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
result(.success([])) result(.success([]))
} }
} }
func fetchUTXOs(for tAddress: String, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { func fetchUTXOs(for tAddress: String, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1){ DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
result(.success([])) result(.success([]))
} }
} }
@ -88,28 +128,6 @@ class DarksideWalletService: LightWalletService {
service.fetchTransaction(txId: txId, result: result) service.fetchTransaction(txId: txId, result: result)
} }
var channel: Channel
init(endpoint: LightWalletEndpoint) {
self.channel = ChannelProvider().channel()
self.service = LightWalletGRPCService(endpoint: endpoint)
self.darksideService = DarksideStreamerClient(channel: channel)
}
init(service: LightWalletGRPCService) {
self.channel = ChannelProvider().channel()
self.darksideService = DarksideStreamerClient(channel: channel)
self.service = service
}
convenience init() {
self.init(endpoint: LightWalletEndpointBuilder.default)
}
var service: LightWalletGRPCService
var darksideService: DarksideStreamerClient
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) { func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
service.latestBlockHeight(result: result) service.latestBlockHeight(result: result)
} }
@ -127,8 +145,8 @@ class DarksideWalletService: LightWalletService {
} }
/** /**
Darskside lightwalletd should do a fake submission, by sending over the tx, retrieving it and including it in a new block Darskside lightwalletd should do a fake submission, by sending over the tx, retrieving it and including it in a new block
*/ */
func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) { func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
service.submit(spendTransaction: spendTransaction, result: result) service.submit(spendTransaction: spendTransaction, result: result)
} }
@ -158,11 +176,17 @@ class DarksideWalletService: LightWalletService {
} }
func getIncomingTransactions() throws -> [RawTransaction]? { func getIncomingTransactions() throws -> [RawTransaction]? {
var txs = [RawTransaction]() var txs: [RawTransaction] = []
let response = try darksideService.getIncomingTransactions(Empty(), handler: { txs.append($0) }).status.wait() let response = try darksideService.getIncomingTransactions(
Empty(),
handler: { txs.append($0) }
)
.status
.wait()
switch response.code { switch response.code {
case .success: case .ok:
return txs.count > 0 ? txs : nil return !txs.isEmpty ? txs : nil
default: default:
throw response throw response
} }
@ -186,9 +210,11 @@ class DarksideWalletService: LightWalletService {
} }
func stageTransaction(_ rawTransaction: RawTransaction, at height: BlockHeight) throws { func stageTransaction(_ rawTransaction: RawTransaction, at height: BlockHeight) throws {
var tx = rawTransaction var transaction = rawTransaction
tx.height = UInt64(height) transaction.height = UInt64(height)
_ = try darksideService.stageTransactionsStream().sendMessage(tx).wait() _ = try darksideService.stageTransactionsStream()
.sendMessage(transaction)
.wait()
} }
func stageTransaction(from url: String, at height: BlockHeight) throws { func stageTransaction(from url: String, at height: BlockHeight) throws {
@ -197,12 +223,9 @@ class DarksideWalletService: LightWalletService {
txUrl.url = url txUrl.url = url
_ = try darksideService.stageTransactions(txUrl, callOptions: nil).response.wait() _ = try darksideService.stageTransactions(txUrl, callOptions: nil).response.wait()
} }
} }
enum DarksideWalletDConstants: NetworkConstants {
class DarksideWalletDConstants: NetworkConstants {
static var saplingActivationHeight: BlockHeight { static var saplingActivationHeight: BlockHeight {
663150 663150
} }
@ -227,9 +250,8 @@ class DarksideWalletDConstants: NetworkConstants {
ZcashSDKMainnetConstants.feeChangeHeight ZcashSDKMainnetConstants.feeChangeHeight
} }
} }
class DarksideWalletDNetwork: ZcashNetwork { class DarksideWalletDNetwork: ZcashNetwork {
var constants: NetworkConstants.Type = DarksideWalletDConstants.self var constants: NetworkConstants.Type = DarksideWalletDConstants.self
var networkType = NetworkType.mainnet var networkType = NetworkType.mainnet
} }

View File

@ -11,7 +11,9 @@ import Foundation
enum FakeChainBuilderError: Error { enum FakeChainBuilderError: Error {
case fakeHexDataConversionFailed case fakeHexDataConversionFailed
} }
class FakeChainBuilder {
// swiftlint:disable force_unwrapping function_parameter_count
enum FakeChainBuilder {
static let someOtherTxUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/transactions/t-shielded-spend.txt" static let someOtherTxUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/transactions/t-shielded-spend.txt"
static let txMainnetBlockUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/663150.txt" static let txMainnetBlockUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/663150.txt"
@ -29,7 +31,6 @@ class FakeChainBuilder {
try darksideWallet.stageBlocksCreate(from: 663151, count: 100) try darksideWallet.stageBlocksCreate(from: 663151, count: 100)
try darksideWallet.stageTransaction(from: txUrls[663174]!, at: 663174) try darksideWallet.stageTransaction(from: txUrls[663174]!, at: 663174)
} }
static func buildChain(darksideWallet: DarksideWalletService, branchID: String, chainName: String) throws { static func buildChain(darksideWallet: DarksideWalletService, branchID: String, chainName: String) throws {
@ -41,7 +42,6 @@ class FakeChainBuilder {
try darksideWallet.stageTransaction(from: txUrls[663174]!, at: 663174) try darksideWallet.stageTransaction(from: txUrls[663174]!, at: 663174)
try darksideWallet.stageTransaction(from: txUrls[663188]!, at: 663188) try darksideWallet.stageTransaction(from: txUrls[663188]!, at: 663188)
} }
static func buildChainWithTxsFarFromEachOther(darksideWallet: DarksideWalletService, branchID: String, chainName: String, length: Int) throws { static func buildChainWithTxsFarFromEachOther(darksideWallet: DarksideWalletService, branchID: String, chainName: String, length: Int) throws {
@ -53,10 +53,8 @@ class FakeChainBuilder {
try darksideWallet.stageTransaction(from: txUrls[663188]!, at: 663188) try darksideWallet.stageTransaction(from: txUrls[663188]!, at: 663188)
try darksideWallet.stageTransaction(from: txUrls[663974]!, at: 663974) try darksideWallet.stageTransaction(from: txUrls[663974]!, at: 663974)
} }
static func buildChain(darksideWallet: DarksideWalletService, branchID: String, chainName: String, length: Int) throws { static func buildChain(darksideWallet: DarksideWalletService, branchID: String, chainName: String, length: Int) throws {
try darksideWallet.reset(saplingActivation: 663150, branchID: branchID, chainName: chainName) try darksideWallet.reset(saplingActivation: 663150, branchID: branchID, chainName: chainName)
try darksideWallet.useDataset(from: txMainnetBlockUrl) try darksideWallet.useDataset(from: txMainnetBlockUrl)
@ -70,34 +68,49 @@ class FakeChainBuilder {
try darksideWallet.stageTransaction(from: txUrls[663953]!, at: 663953) try darksideWallet.stageTransaction(from: txUrls[663953]!, at: 663953)
try darksideWallet.stageTransaction(from: txUrls[663974]!, at: 663974) try darksideWallet.stageTransaction(from: txUrls[663974]!, at: 663974)
} }
static func buildChain(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, branchID: String, chainName: String, length: Int) throws { static func buildChain(
darksideWallet: DarksideWalletService,
birthday: BlockHeight,
networkActivationHeight: BlockHeight,
branchID: String,
chainName: String,
length: Int
) throws {
try darksideWallet.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName) try darksideWallet.reset(saplingActivation: birthday, branchID: branchID, chainName: chainName)
try darksideWallet.useDataset(testnetCanopyStartBlock) try darksideWallet.useDataset(testnetCanopyStartBlock)
try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length) try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length)
try darksideWallet.stageTransaction(from: testnetPreCanopyTx, at: networkActivationHeight - ZcashSDK.expiryOffset) try darksideWallet.stageTransaction(from: testnetPreCanopyTx, at: networkActivationHeight - ZcashSDK.expiryOffset)
} }
static func buildChainPostActivationFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, length: Int) throws { static func buildChainPostActivationFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, length: Int) throws {
try darksideWallet.reset(saplingActivation: birthday, branchID: "e9ff75a6", chainName: "testnet")
try darksideWallet.reset(saplingActivation: birthday, branchID: "e9ff75a6" , chainName: "testnet")
try darksideWallet.useDataset(testnetCanopyStartBlock) try darksideWallet.useDataset(testnetCanopyStartBlock)
try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length) try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length)
try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + 1) try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + 1)
} }
static func buildChainMixedFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, branchID: String, chainName: String, length: Int) throws { static func buildChainMixedFunds(
try buildChain(darksideWallet: darksideWallet, birthday: birthday, networkActivationHeight: networkActivationHeight, branchID: branchID, chainName: chainName, length: length) darksideWallet: DarksideWalletService,
birthday: BlockHeight,
networkActivationHeight: BlockHeight,
branchID: String,
chainName: String,
length: Int
) throws {
try buildChain(
darksideWallet: darksideWallet,
birthday: birthday,
networkActivationHeight: networkActivationHeight,
branchID: branchID,
chainName: chainName,
length: length
)
try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + ZcashSDK.expiryOffset) try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + ZcashSDK.expiryOffset)
} }
static func buildTxUrl(for id: String) -> String { static func buildTxUrl(for id: String) -> String {
@ -105,86 +118,85 @@ class FakeChainBuilder {
} }
static var txUrls = [ static var txUrls = [
663174 : buildTxUrl(for: "8f064d23c66dc36e32445e5f3b50e0f32ac3ddb78cff21fb521eb6c19c07c99a"), 663174: buildTxUrl(for: "8f064d23c66dc36e32445e5f3b50e0f32ac3ddb78cff21fb521eb6c19c07c99a"),
663188 : buildTxUrl(for: "15a677b6770c5505fb47439361d3d3a7c21238ee1a6874fdedad18ae96850590"), 663188: buildTxUrl(for: "15a677b6770c5505fb47439361d3d3a7c21238ee1a6874fdedad18ae96850590"),
663202 : buildTxUrl(for: "d2e7be14bbb308f9d4d68de424d622cbf774226d01cd63cc6f155fafd5cd212c"), 663202: buildTxUrl(for: "d2e7be14bbb308f9d4d68de424d622cbf774226d01cd63cc6f155fafd5cd212c"),
663218 : buildTxUrl(for: "e6566be3a4f9a80035dab8e1d97e40832a639e3ea938fb7972ea2f8482ff51ce"), 663218: buildTxUrl(for: "e6566be3a4f9a80035dab8e1d97e40832a639e3ea938fb7972ea2f8482ff51ce"),
663229 : buildTxUrl(for: "0821a89be7f2fc1311792c3fa1dd2171a8cdfb2effd98590cbd5ebcdcfcf491f"), 663229: buildTxUrl(for: "0821a89be7f2fc1311792c3fa1dd2171a8cdfb2effd98590cbd5ebcdcfcf491f"),
663849 : buildTxUrl(for: "c9e35e6ff444b071d63bf9bab6480409d6361760445c8a28d24179adb35c2495"), 663849: buildTxUrl(for: "c9e35e6ff444b071d63bf9bab6480409d6361760445c8a28d24179adb35c2495"),
663891 : buildTxUrl(for: "72a29d7db511025da969418880b749f7fc0fc910cdb06f52193b5fa5c0401d9d"), 663891: buildTxUrl(for: "72a29d7db511025da969418880b749f7fc0fc910cdb06f52193b5fa5c0401d9d"),
663922 : buildTxUrl(for: "ff6ea36765dc29793775c7aa71de19fca039c5b5b873a0497866e9c4bc48af01"), 663922: buildTxUrl(for: "ff6ea36765dc29793775c7aa71de19fca039c5b5b873a0497866e9c4bc48af01"),
663938 : buildTxUrl(for: "34e507cab780546f980176f3ff2695cd404917508c7e5ee18cc1d2ff3858cb08"), 663938: buildTxUrl(for: "34e507cab780546f980176f3ff2695cd404917508c7e5ee18cc1d2ff3858cb08"),
663942 : buildTxUrl(for: "6edf869063eccff3345676b0fed9f1aa6988fb2524e3d9ca7420a13cfadcd76c"), 663942: buildTxUrl(for: "6edf869063eccff3345676b0fed9f1aa6988fb2524e3d9ca7420a13cfadcd76c"),
663947 : buildTxUrl(for: "de97394ae220c28a33ba78b944e82dabec8cb404a4407650b134b3d5950358c0"), 663947: buildTxUrl(for: "de97394ae220c28a33ba78b944e82dabec8cb404a4407650b134b3d5950358c0"),
663949 : buildTxUrl(for: "4eaa902279f8380914baf5bcc470d8b7c11d84fda809f67f517a7cb48912b87b"), 663949: buildTxUrl(for: "4eaa902279f8380914baf5bcc470d8b7c11d84fda809f67f517a7cb48912b87b"),
663953 : buildTxUrl(for: "e9527891b5d43d1ac72f2c0a3ac18a33dc5a0529aec04fa600616ed35f8123f8"), 663953: buildTxUrl(for: "e9527891b5d43d1ac72f2c0a3ac18a33dc5a0529aec04fa600616ed35f8123f8"),
663956 : buildTxUrl(for: "73c5edf8ffba774d99155121ccf07e67fbcf14284458f7e732751fea60d3bcbc"), 663956: buildTxUrl(for: "73c5edf8ffba774d99155121ccf07e67fbcf14284458f7e732751fea60d3bcbc"),
663974 : buildTxUrl(for: "4dcc95dd0a2f1f51bd64bb9f729b423c6de1690664a1b6614c75925e781662f7"), 663974: buildTxUrl(for: "4dcc95dd0a2f1f51bd64bb9f729b423c6de1690664a1b6614c75925e781662f7"),
664003 : buildTxUrl(for: "d2e859e8ef8ab27355c7a6caf643065d2d7a720e334c4a84943f6d1ae3919b5d"), 664003: buildTxUrl(for: "d2e859e8ef8ab27355c7a6caf643065d2d7a720e334c4a84943f6d1ae3919b5d"),
664012 : buildTxUrl(for: "547784f746eef2f164bbb1a56882723dde744157a21e4fdfeadee763f73fee84"), 664012: buildTxUrl(for: "547784f746eef2f164bbb1a56882723dde744157a21e4fdfeadee763f73fee84"),
664022 : buildTxUrl(for: "981638bb7ac08e31ee6db5c70d98ad6b137a448716b19245f9454b450c07c911"), 664022: buildTxUrl(for: "981638bb7ac08e31ee6db5c70d98ad6b137a448716b19245f9454b450c07c911"),
664037 : buildTxUrl(for: "36505ab3c78c62981c8111d143cd57dcfe6cafcb2c3cdc258b023ae5210d53f1"), 664037: buildTxUrl(for: "36505ab3c78c62981c8111d143cd57dcfe6cafcb2c3cdc258b023ae5210d53f1"),
664038 : buildTxUrl(for: "0ffc55af750bb10a9e6a7e425138cc5acb5f7ddca68bf9d0c4606437bd692622"), 664038: buildTxUrl(for: "0ffc55af750bb10a9e6a7e425138cc5acb5f7ddca68bf9d0c4606437bd692622"),
678828 : buildTxUrl(for: "cfd3bce9fdeeae12b99fdb977a997177e183c2312871f0454bdf61640cc03d93"), 678828: buildTxUrl(for: "cfd3bce9fdeeae12b99fdb977a997177e183c2312871f0454bdf61640cc03d93"),
678836 : buildTxUrl(for: "5af3bc9818e5fabcc691f319d7354cc4194f17727f6303d59a94c3e5f0daf560"), 678836: buildTxUrl(for: "5af3bc9818e5fabcc691f319d7354cc4194f17727f6303d59a94c3e5f0daf560"),
682588 : buildTxUrl(for: "b1f566dec94048ff81306884b6ed92eb73cdb768b738d9c8cbd94babc1f0a9c9"), 682588: buildTxUrl(for: "b1f566dec94048ff81306884b6ed92eb73cdb768b738d9c8cbd94babc1f0a9c9"),
683689 : buildTxUrl(for: "3b568a1547832ac28bfcaf4c269f85fd68083735790f7949aa3a548ab53acf65"), 683689: buildTxUrl(for: "3b568a1547832ac28bfcaf4c269f85fd68083735790f7949aa3a548ab53acf65"),
683791 : buildTxUrl(for: "9e2eb538207ab47356a3723fd0e6f44b9349ea944d9c2d7be0d4e3a6a02c2c29"), 683791: buildTxUrl(for: "9e2eb538207ab47356a3723fd0e6f44b9349ea944d9c2d7be0d4e3a6a02c2c29"),
683795 : buildTxUrl(for: "15d2f32494271f0a60f3928e4fc79c2cea337e06fbbbe7f6fb4a0d36002a0d42"), 683795: buildTxUrl(for: "15d2f32494271f0a60f3928e4fc79c2cea337e06fbbbe7f6fb4a0d36002a0d42"),
683809 : buildTxUrl(for: "76be7c244c37e1710bbb9f162baab265eebc8a379ad1843435ba5e7a2c21a600"), 683809: buildTxUrl(for: "76be7c244c37e1710bbb9f162baab265eebc8a379ad1843435ba5e7a2c21a600"),
684374 : buildTxUrl(for: "3640a35c02cf4d9e0fa178380173b193873d8a0ef4bad57dd43e7d95db450c89"), 684374: buildTxUrl(for: "3640a35c02cf4d9e0fa178380173b193873d8a0ef4bad57dd43e7d95db450c89"),
685126 : buildTxUrl(for: "86f3457bdb8793a413c009a8a7e128b5a82723f41ebe557327bbe555fd47fbf3"), 685126: buildTxUrl(for: "86f3457bdb8793a413c009a8a7e128b5a82723f41ebe557327bbe555fd47fbf3"),
687746 : buildTxUrl(for: "edb32a55d5fa18fc5c6bf09f5f1de198b219b6780ca71bbc4fd321b655bbfe42"), 687746: buildTxUrl(for: "edb32a55d5fa18fc5c6bf09f5f1de198b219b6780ca71bbc4fd321b655bbfe42"),
687900 : buildTxUrl(for: "855af341c14b94fec67e5eb56bb801a59551df33e9d955982672f5f62e76f72e"), 687900: buildTxUrl(for: "855af341c14b94fec67e5eb56bb801a59551df33e9d955982672f5f62e76f72e"),
688059 : buildTxUrl(for: "57c226f77ad01ecf833515612e7cba7abe64500fa891144c2c89c59af8c36c22"), 688059: buildTxUrl(for: "57c226f77ad01ecf833515612e7cba7abe64500fa891144c2c89c59af8c36c22"),
691540 : buildTxUrl(for: "9a74cd7f170f6c8cef04f3327fdcf63ec69dd1263f80c9bf0b3002c871950ddd"), 691540: buildTxUrl(for: "9a74cd7f170f6c8cef04f3327fdcf63ec69dd1263f80c9bf0b3002c871950ddd"),
691593 : buildTxUrl(for: "6b64134034ec282092501f85bf8955006894dbcac402fa5e6c85ee867334cd3d"), 691593: buildTxUrl(for: "6b64134034ec282092501f85bf8955006894dbcac402fa5e6c85ee867334cd3d"),
691632 : buildTxUrl(for: "75f2cdd2ff6a94535326abb5d9e663d53cbfa5f31ebb24b4d7e420e9440d41a2"), 691632: buildTxUrl(for: "75f2cdd2ff6a94535326abb5d9e663d53cbfa5f31ebb24b4d7e420e9440d41a2"),
692981 : buildTxUrl(for: "f98f2c75785f110203930c7fd4115019ec70af6470db1be052985b469906fe98"), 692981: buildTxUrl(for: "f98f2c75785f110203930c7fd4115019ec70af6470db1be052985b469906fe98"),
692984 : buildTxUrl(for: "67138ad7e5e97216124c2bbcda8edb7687c2cfbf5d644df2af2a86344437a661"), 692984: buildTxUrl(for: "67138ad7e5e97216124c2bbcda8edb7687c2cfbf5d644df2af2a86344437a661"),
692992 : buildTxUrl(for: "6cf507ab4d3255fa51679c0256a1be1d668786bd3f558000f9e90ec442514212"), 692992: buildTxUrl(for: "6cf507ab4d3255fa51679c0256a1be1d668786bd3f558000f9e90ec442514212"),
693248 : buildTxUrl(for: "d1278d74424807b830256ccbd4d7624dc9e68a50760f870a55c8e99715072ef1"), 693248: buildTxUrl(for: "d1278d74424807b830256ccbd4d7624dc9e68a50760f870a55c8e99715072ef1"),
693268 : buildTxUrl(for: "e56c84718de5dee049b31c89832f4bf1694268e2664a04df182a8797cb00b52e"), 693268: buildTxUrl(for: "e56c84718de5dee049b31c89832f4bf1694268e2664a04df182a8797cb00b52e"),
693325 : buildTxUrl(for: "5635f48dc99adfebb0be105231b9383bd2d0df64e43a780d11620390640b8d3d"), 693325: buildTxUrl(for: "5635f48dc99adfebb0be105231b9383bd2d0df64e43a780d11620390640b8d3d"),
693398 : buildTxUrl(for: "26c41d5dbbaaa3934b37109645b0aece9600248c5f51404d1f4ea7b711ac3312"), 693398: buildTxUrl(for: "26c41d5dbbaaa3934b37109645b0aece9600248c5f51404d1f4ea7b711ac3312"),
693460 : buildTxUrl(for: "8d381a3d993c8d424db0907bab3fef6000bc8de9efe7186846d44dd6d6a014b1"), 693460: buildTxUrl(for: "8d381a3d993c8d424db0907bab3fef6000bc8de9efe7186846d44dd6d6a014b1"),
693475 : buildTxUrl(for: "900f2a406c1546126e1dba0e4e6ca0e092ebe697a2f7b0abee4e9771e1038f0b"), 693475: buildTxUrl(for: "900f2a406c1546126e1dba0e4e6ca0e092ebe697a2f7b0abee4e9771e1038f0b"),
693718 : buildTxUrl(for: "7690c8ec740c1be3c50e2aedae8bf907ac81141ae8b6a134c1811706c73f49a6"), 693718: buildTxUrl(for: "7690c8ec740c1be3c50e2aedae8bf907ac81141ae8b6a134c1811706c73f49a6"),
693736 : buildTxUrl(for: "34a4d630f120e4c1e7d2b9844c69fd4d3be71532ade1aaf7147566f05162c316"), 693736: buildTxUrl(for: "34a4d630f120e4c1e7d2b9844c69fd4d3be71532ade1aaf7147566f05162c316"),
696005 : buildTxUrl(for: "076d30ca62082dda9a760e0d004393cd96830056c6dca643fccdbe500053e355"), 696005: buildTxUrl(for: "076d30ca62082dda9a760e0d004393cd96830056c6dca643fccdbe500053e355"),
696012 : buildTxUrl(for: "e2da49325057b2232e85b0228955234f4a3538df2ebf4cd121589bac9771f6f2"), 696012: buildTxUrl(for: "e2da49325057b2232e85b0228955234f4a3538df2ebf4cd121589bac9771f6f2"),
696040 : buildTxUrl(for: "4f6ef63bd3be8338c902901daf77ab5aa23dd97c160ee91b00950accf7f0b194"), 696040: buildTxUrl(for: "4f6ef63bd3be8338c902901daf77ab5aa23dd97c160ee91b00950accf7f0b194"),
698361 : buildTxUrl(for: "d275a9e96e6c68dfb8fe6ec3fd39737ce5fa880f86552b3ed993048373d6e8ad"), 698361: buildTxUrl(for: "d275a9e96e6c68dfb8fe6ec3fd39737ce5fa880f86552b3ed993048373d6e8ad"),
710823 : buildTxUrl(for: "bac04ad7734628e70a57408c65403ec845bce575197e7984435976e1ac64ae4f"), 710823: buildTxUrl(for: "bac04ad7734628e70a57408c65403ec845bce575197e7984435976e1ac64ae4f"),
710896 : buildTxUrl(for: "56c63ef496f633418f0576cc34a0730c74023d78003b95aff731e0448c8b9203"), 710896: buildTxUrl(for: "56c63ef496f633418f0576cc34a0730c74023d78003b95aff731e0448c8b9203"),
711847 : buildTxUrl(for: "82439eade5d1deba7606f3db53bf33588677b1bd9765a5eb5f4d3f6980ecb3d4"), 711847: buildTxUrl(for: "82439eade5d1deba7606f3db53bf33588677b1bd9765a5eb5f4d3f6980ecb3d4"),
727486 : buildTxUrl(for: "b5877c7f7dd3856bae679f7ccb37ddf3fcd2fafe72a081878ee9069fc25934cd"), 727486: buildTxUrl(for: "b5877c7f7dd3856bae679f7ccb37ddf3fcd2fafe72a081878ee9069fc25934cd"),
728159 : buildTxUrl(for: "5d6a0c4879a244d2c0a6e2d26c4d0d26dee5a5c1f3f13f42436253272d4b8a03"), 728159: buildTxUrl(for: "5d6a0c4879a244d2c0a6e2d26c4d0d26dee5a5c1f3f13f42436253272d4b8a03"),
736102 : buildTxUrl(for: "be3a3a3fe10b9a1976410e5aaf425b24695dcdd04df926a23d9f3f8ed43178c6"), 736102: buildTxUrl(for: "be3a3a3fe10b9a1976410e5aaf425b24695dcdd04df926a23d9f3f8ed43178c6"),
736254 : buildTxUrl(for: "acc0685aee04f7b7c6a12c969c1646038ea4a3b940d00b28d1eaf7643602d49e"), 736254: buildTxUrl(for: "acc0685aee04f7b7c6a12c969c1646038ea4a3b940d00b28d1eaf7643602d49e"),
736262 : buildTxUrl(for: "fed00ff5cb6ee057d00ec70f1f5f1b189d591903c1e1cbde654ad39c8477808c"), 736262: buildTxUrl(for: "fed00ff5cb6ee057d00ec70f1f5f1b189d591903c1e1cbde654ad39c8477808c"),
736301 : buildTxUrl(for: "f3ef9f3adedf2b66e438c9d7d878ed72886b62b70e68547bb47d5b6033519dcd"), 736301: buildTxUrl(for: "f3ef9f3adedf2b66e438c9d7d878ed72886b62b70e68547bb47d5b6033519dcd"),
736574 : buildTxUrl(for: "34574442629a2378eccd216385d8bc99859e214e79265941319599130de2c69a"), 736574: buildTxUrl(for: "34574442629a2378eccd216385d8bc99859e214e79265941319599130de2c69a"),
739582 : buildTxUrl(for: "71935e29127a7de0b96081f4c8a42a9c11584d83adedfaab414362a6f3d965cf"), 739582: buildTxUrl(for: "71935e29127a7de0b96081f4c8a42a9c11584d83adedfaab414362a6f3d965cf"),
741148 : buildTxUrl(for: "5eff7f15b39b9ab463767b768e23f90b4a23239ed873fdfbd4afa286027f7b57"), 741148: buildTxUrl(for: "5eff7f15b39b9ab463767b768e23f90b4a23239ed873fdfbd4afa286027f7b57"),
741154 : buildTxUrl(for: "b05c3df882ccff4f58acc1e3dbe2520213159d584bac01ff0199c37c25451430"), 741154: buildTxUrl(for: "b05c3df882ccff4f58acc1e3dbe2520213159d584bac01ff0199c37c25451430"),
741156 : buildTxUrl(for: "1a3bb3d4fece0fcde1a47ef8271511cefcdb67f2698afc2c63297fbeab2003d8"), 741156: buildTxUrl(for: "1a3bb3d4fece0fcde1a47ef8271511cefcdb67f2698afc2c63297fbeab2003d8"),
741158 : buildTxUrl(for: "a979dc83f55d9114dcab2eb5694bbf4fbb84602ceb27af6e287d6af8775d92c7"), 741158: buildTxUrl(for: "a979dc83f55d9114dcab2eb5694bbf4fbb84602ceb27af6e287d6af8775d92c7"),
741162 : buildTxUrl(for: "23278a3c1bf03f20f67299ed0b8dc4d577909d2344f1f02971c8890c6341d79d"), 741162: buildTxUrl(for: "23278a3c1bf03f20f67299ed0b8dc4d577909d2344f1f02971c8890c6341d79d"),
741170 : buildTxUrl(for: "db4101f3cccb1671dc1557670fa8b4e64c958008778b8ab1779a4a2969fe1153"), 741170: buildTxUrl(for: "db4101f3cccb1671dc1557670fa8b4e64c958008778b8ab1779a4a2969fe1153"),
741171 : buildTxUrl(for: "74a94aceedb3a22eedb0b5d450487340b3783e1d22ef47af2359c45d0804d9ff"), 741171: buildTxUrl(for: "74a94aceedb3a22eedb0b5d450487340b3783e1d22ef47af2359c45d0804d9ff"),
741172 : buildTxUrl(for: "2899ccaea26e4c873a09965e0c268c96a86b1931d896b8622f36422d32c234c2"), 741172: buildTxUrl(for: "2899ccaea26e4c873a09965e0c268c96a86b1931d896b8622f36422d32c234c2"),
741174 : buildTxUrl(for: "819009ec1d0cfb50d30c944a41bde545ee631663af39f8a17c31255ada12de13"), 741174: buildTxUrl(for: "819009ec1d0cfb50d30c944a41bde545ee631663af39f8a17c31255ada12de13"),
775018 : buildTxUrl(for: "85b3b64903b1873f5b7578eb2f167752b6a66ba64bb5c4cb8a4d75072219678b"), 775018: buildTxUrl(for: "85b3b64903b1873f5b7578eb2f167752b6a66ba64bb5c4cb8a4d75072219678b"),
775021 : buildTxUrl(for: "6d69d23c8db7736efdd38090c3cd032f8e68431272964157c52a924315e1a3f5"), 775021: buildTxUrl(for: "6d69d23c8db7736efdd38090c3cd032f8e68431272964157c52a924315e1a3f5"),
775267 : buildTxUrl(for: "daf24871749c8360028a19e4d82ddb0d573d7c765a894d601aa241f1e040ac5f"), 775267: buildTxUrl(for: "daf24871749c8360028a19e4d82ddb0d573d7c765a894d601aa241f1e040ac5f"),
776019 : buildTxUrl(for: "f64378feb08c30b28a90f31e8cd84a932ed064108fb17a3e0aee1585ff994138"), 776019: buildTxUrl(for: "f64378feb08c30b28a90f31e8cd84a932ed064108fb17a3e0aee1585ff994138"),
776158 : buildTxUrl(for: "9339a0a231f88b3067f3378c7ae70170fdf4246e0e70f442552a6e3961391b56"), 776158: buildTxUrl(for: "9339a0a231f88b3067f3378c7ae70170fdf4246e0e70f442552a6e3961391b56"),
776233 : buildTxUrl(for: "c9c33e44468c1fa0ee5f9d411b43748f8882915640b3b13c6e48c56e9cdde798"), 776233: buildTxUrl(for: "c9c33e44468c1fa0ee5f9d411b43748f8882915640b3b13c6e48c56e9cdde798"),
776240 : buildTxUrl(for: "0e1c70fc67d3b9ae29a98996d4363b512d51d7b8422a6fa58f5803bebb247e7a"), 776240: buildTxUrl(for: "0e1c70fc67d3b9ae29a98996d4363b512d51d7b8422a6fa58f5803bebb247e7a"),
820691 : buildTxUrl(for: "1948bc40226e53d2652f593ebe4f34c5d81550eeb16fe2ed797b7ef3c1083899"), 820691: buildTxUrl(for: "1948bc40226e53d2652f593ebe4f34c5d81550eeb16fe2ed797b7ef3c1083899"),
822410 : buildTxUrl(for: "f3f8684be8d77367d099a38f30e3652410cdebe35c006d0599d86d8ec640867f"), 822410: buildTxUrl(for: "f3f8684be8d77367d099a38f30e3652410cdebe35c006d0599d86d8ec640867f"),
828933 : buildTxUrl(for: "1fd394257d1c10c8a70fb760cf73f6d0e96e61edcf1ffca6da12d733a59221a4") 828933: buildTxUrl(for: "1fd394257d1c10c8a70fb760cf73f6d0e96e61edcf1ffca6da12d733a59221a4")
] ]
} }

View File

@ -14,15 +14,24 @@ struct LightWalletServiceMockResponse: LightWalletServiceResponse {
var errorCode: Int32 var errorCode: Int32
var errorMessage: String var errorMessage: String
var unknownFields: UnknownStorage var unknownFields: UnknownStorage
} }
struct MockCancellable: CancellableCall { struct MockCancellable: CancellableCall {
func cancel() {} func cancel() {}
} }
class MockLightWalletService: LightWalletService { class MockLightWalletService: LightWalletService {
var mockLightDInfo: LightWalletdInfo? var mockLightDInfo: LightWalletdInfo?
var queue = DispatchQueue(label: "mock service queue") var queue = DispatchQueue(label: "mock service queue")
@discardableResult func blockStream(startHeight: BlockHeight, endHeight: BlockHeight, result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void, handler: @escaping (ZcashCompactBlock) -> Void, progress: @escaping (BlockProgressReporting) -> Void) -> CancellableCall {
@discardableResult
func blockStream(
startHeight: BlockHeight,
endHeight: BlockHeight,
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
handler: @escaping (ZcashCompactBlock) -> Void,
progress: @escaping (BlockProgress) -> Void
) -> CancellableCall {
return MockCancellable() return MockCancellable()
} }
@ -35,7 +44,6 @@ class MockLightWalletService: LightWalletService {
func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) { func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) {
queue.async { [weak self] in queue.async { [weak self] in
guard let info = self?.mockLightDInfo else { guard let info = self?.mockLightDInfo else {
result(.failure(LightWalletServiceError.generalError(message: "Not Implemented"))) result(.failure(LightWalletServiceError.generalError(message: "Not Implemented")))
return return
@ -45,27 +53,34 @@ class MockLightWalletService: LightWalletService {
} }
func closeConnection() { func closeConnection() {
} }
func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
[] []
} }
func fetchUTXOs(for tAddress: String, height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { func fetchUTXOs(
for tAddress: String,
height: BlockHeight,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
) {
} }
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
[] []
} }
func fetchUTXOs(for tAddresses: [String], height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { func fetchUTXOs(
for tAddresses: [String],
height: BlockHeight,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
) {
} }
func fetchUTXOs(for tAddress: String, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { func fetchUTXOs(
for tAddress: String,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
) {
} }
private var service: LightWalletService private var service: LightWalletService
@ -110,7 +125,5 @@ class MockLightWalletService: LightWalletService {
} }
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) { func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) {
} }
} }

View File

@ -35,7 +35,7 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
} }
} }
fileprivate func fakeSave(blocks: [ZcashCompactBlock]) { private func fakeSave(blocks: [ZcashCompactBlock]) {
blocks.forEach { blocks.forEach {
LoggerProxy.debug("saving block \($0)") LoggerProxy.debug("saving block \($0)")
self.latestBlockHeight = $0.height self.latestBlockHeight = $0.height
@ -60,5 +60,4 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
LoggerProxy.debug("rewind to \(height)") LoggerProxy.debug("rewind to \(height)")
self.latestBlockHeight = min(self.latestBlockHeight, height) self.latestBlockHeight = min(self.latestBlockHeight, height)
} }
} }

View File

@ -6,123 +6,50 @@
// //
import Foundation import Foundation
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
class MockTransactionRepository: TransactionRepository { class MockTransactionRepository {
func findConfirmedTransactionBy(rawId: Data) throws -> ConfirmedTransactionEntity? { enum Kind {
nil case sent
} case received
func blockForHeight(_ height: BlockHeight) throws -> Block? {
nil
}
func findConfirmedTransactions(in range: BlockRange, offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
nil
}
func findAll(from: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]? {
nil
}
func findTransactions(in range: BlockRange, limit: Int) throws -> [TransactionEntity]? {
nil
} }
var unminedCount: Int var unminedCount: Int
var receivedCount: Int var receivedCount: Int
var sentCount: Int var sentCount: Int
var transactions: [ConfirmedTransactionEntity] = [] var transactions: [ConfirmedTransactionEntity] = []
var reference: [Kind] = [] var reference: [Kind] = []
var sentTransactions: [ConfirmedTransaction] = [] var sentTransactions: [ConfirmedTransaction] = []
var receivedTransactions: [ConfirmedTransaction] = [] var receivedTransactions: [ConfirmedTransaction] = []
var network: ZcashNetwork
var allCount: Int { var allCount: Int {
receivedCount + sentCount receivedCount + sentCount
} }
var network: ZcashNetwork init(
init(unminedCount: Int, unminedCount: Int,
receivedCount: Int, receivedCount: Int,
sentCount: Int, sentCount: Int,
network: ZcashNetwork) { network: ZcashNetwork
) {
self.unminedCount = unminedCount self.unminedCount = unminedCount
self.receivedCount = receivedCount self.receivedCount = receivedCount
self.sentCount = sentCount self.sentCount = sentCount
self.network = network self.network = network
} }
func generate() { func generate() {
var txArray: [ConfirmedTransactionEntity] = []
var txArray = [ConfirmedTransactionEntity]()
reference = referenceArray() reference = referenceArray()
for i in 0 ..< reference.count { for index in 0 ..< reference.count {
txArray.append(mockTx(index: i, kind: reference[i])) txArray.append(mockTx(index: index, kind: reference[index]))
} }
transactions = txArray transactions = txArray
} }
func countAll() throws -> Int {
allCount
}
func countUnmined() throws -> Int {
unminedCount
}
func findBy(id: Int) throws -> TransactionEntity? {
transactions.first(where: {$0.id == id})?.transactionEntity
}
func findBy(rawId: Data) throws -> TransactionEntity? {
transactions.first(where: {$0.rawTransactionId == rawId})?.transactionEntity
}
func findAllSentTransactions(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
guard let indices = reference.indices(where: { $0 == .sent }) else { return nil }
let sentTxs = indices.map { (idx) -> ConfirmedTransactionEntity in
transactions[idx]
}
return slice(txs: sentTxs, offset: offset, limit: limit)
}
func findAllReceivedTransactions(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
guard let indices = reference.indices(where: { $0 == .received }) else { return nil }
let receivedTxs = indices.map { (idx) -> ConfirmedTransactionEntity in
transactions[idx]
}
return slice(txs: receivedTxs, offset: offset, limit: limit)
}
func findAll(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
transactions
}
func lastScannedHeight() throws -> BlockHeight {
return 700000
}
func isInitialized() throws -> Bool {
true
}
func findEncodedTransactionBy(txId: Int) -> EncodedTransaction? {
nil
}
enum Kind {
case sent
case received
}
func referenceArray() -> [Kind] { func referenceArray() -> [Kind] {
var template = [Kind]() var template: [Kind] = []
for _ in 0 ..< sentCount { for _ in 0 ..< sentCount {
template.append(.sent) template.append(.sent)
@ -130,10 +57,10 @@ class MockTransactionRepository: TransactionRepository {
for _ in 0 ..< receivedCount { for _ in 0 ..< receivedCount {
template.append(.received) template.append(.received)
} }
return template.shuffled() return template.shuffled()
} }
func mockTx(index: Int, kind: Kind) -> ConfirmedTransactionEntity { func mockTx(index: Int, kind: Kind) -> ConfirmedTransactionEntity {
switch kind { switch kind {
case .received: case .received:
@ -144,17 +71,41 @@ class MockTransactionRepository: TransactionRepository {
} }
func mockSent(_ index: Int) -> ConfirmedTransactionEntity { func mockSent(_ index: Int) -> ConfirmedTransactionEntity {
ConfirmedTransaction(toAddress: "some_address", expiryHeight: BlockHeight.max, minedHeight: randomBlockHeight(), noteId: index, blockTimeInSeconds: randomTimeInterval(), transactionIndex: index, raw: Data(), id: index, value: Int.random(in: 1 ... ZcashSDK.zatoshiPerZEC), memo: nil, rawTransactionId: Data()) ConfirmedTransaction(
toAddress: "some_address",
expiryHeight: BlockHeight.max,
minedHeight: randomBlockHeight(),
noteId: index,
blockTimeInSeconds: randomTimeInterval(),
transactionIndex: index,
raw: Data(),
id: index,
value: Int.random(in: 1 ... ZcashSDK.zatoshiPerZEC),
memo: nil,
rawTransactionId: Data()
)
} }
func mockReceived(_ index: Int) -> ConfirmedTransactionEntity { func mockReceived(_ index: Int) -> ConfirmedTransactionEntity {
ConfirmedTransaction(toAddress: nil, expiryHeight: BlockHeight.max, minedHeight: randomBlockHeight(), noteId: index, blockTimeInSeconds: randomTimeInterval(), transactionIndex: index, raw: Data(), id: index, value: Int.random(in: 1 ... ZcashSDK.zatoshiPerZEC), memo: nil, rawTransactionId: Data()) ConfirmedTransaction(
toAddress: nil,
expiryHeight: BlockHeight.max,
minedHeight: randomBlockHeight(),
noteId: index,
blockTimeInSeconds: randomTimeInterval(),
transactionIndex: index,
raw: Data(),
id: index,
value: Int.random(in: 1 ... ZcashSDK.zatoshiPerZEC),
memo: nil,
rawTransactionId: Data()
)
} }
func randomBlockHeight() -> BlockHeight { func randomBlockHeight() -> BlockHeight {
BlockHeight.random(in: network.constants.saplingActivationHeight ... 1_000_000) BlockHeight.random(in: network.constants.saplingActivationHeight ... 1_000_000)
} }
func randomTimeInterval() -> TimeInterval { func randomTimeInterval() -> TimeInterval {
Double.random(in: Date().timeIntervalSince1970 - 1000000.0 ... Date().timeIntervalSince1970) Double.random(in: Date().timeIntervalSince1970 - 1000000.0 ... Date().timeIntervalSince1970)
} }
@ -168,18 +119,93 @@ class MockTransactionRepository: TransactionRepository {
extension MockTransactionRepository.Kind: Equatable {} extension MockTransactionRepository.Kind: Equatable {}
// MARK: - TransactionRepository
extension MockTransactionRepository: TransactionRepository {
func countAll() throws -> Int {
allCount
}
func countUnmined() throws -> Int {
unminedCount
}
func blockForHeight(_ height: BlockHeight) throws -> Block? {
nil
}
func findBy(id: Int) throws -> TransactionEntity? {
transactions.first(where: { $0.id == id })?.transactionEntity
}
func findBy(rawId: Data) throws -> TransactionEntity? {
transactions.first(where: { $0.rawTransactionId == rawId })?.transactionEntity
}
func findAllSentTransactions(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
guard let indices = reference.indices(where: { $0 == .sent }) else { return nil }
let sentTxs = indices.map { idx -> ConfirmedTransactionEntity in
transactions[idx]
}
return slice(txs: sentTxs, offset: offset, limit: limit)
}
func findAllReceivedTransactions(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
guard let indices = reference.indices(where: { $0 == .received }) else { return nil }
let receivedTxs = indices.map { idx -> ConfirmedTransactionEntity in
transactions[idx]
}
return slice(txs: receivedTxs, offset: offset, limit: limit)
}
func findAll(offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
transactions
}
func findAll(from: ConfirmedTransactionEntity?, limit: Int) throws -> [ConfirmedTransactionEntity]? {
nil
}
func lastScannedHeight() throws -> BlockHeight {
return 700000
}
func isInitialized() throws -> Bool {
true
}
func findEncodedTransactionBy(txId: Int) -> EncodedTransaction? {
nil
}
func findTransactions(in range: BlockRange, limit: Int) throws -> [TransactionEntity]? {
nil
}
func findConfirmedTransactionBy(rawId: Data) throws -> ConfirmedTransactionEntity? {
nil
}
func findConfirmedTransactions(in range: BlockRange, offset: Int, limit: Int) throws -> [ConfirmedTransactionEntity]? {
nil
}
}
extension Array { extension Array {
func indices(where f: (_ element: Element) -> Bool) -> [Int]? { func indices(where function: (_ element: Element) -> Bool) -> [Int]? {
guard self.count > 0 else { return nil } guard !self.isEmpty else { return nil }
var idx = [Int]()
for i in 0 ..< self.count { var idx: [Int] = []
if f(self[i]) {
idx.append(i) for index in 0 ..< self.count {
if function(self[index]) {
idx.append(index)
} }
} }
guard idx.count > 0 else { return nil } guard !idx.isEmpty else { return nil }
return idx return idx
} }
} }

View File

@ -10,6 +10,7 @@ import Foundation
import ZcashLightClientKit import ZcashLightClientKit
import os import os
// swiftlint:disable force_unwrapping print_function_usage
class SampleLogger: ZcashLightClientKit.Logger { class SampleLogger: ZcashLightClientKit.Logger {
enum LogLevel: Int { enum LogLevel: Int {
case debug case debug
@ -46,8 +47,8 @@ class SampleLogger: ZcashLightClientKit.Logger {
} }
func warn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) { func warn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.warning.rawValue else { return } guard level.rawValue <= LogLevel.warning.rawValue else { return }
log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line) log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line)
} }
func event(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) { func event(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
@ -66,7 +67,14 @@ class SampleLogger: ZcashLightClientKit.Logger {
case .printerLog: case .printerLog:
print("[\(level)] \(fileName) - \(function) - line: \(line) -> \(message)") print("[\(level)] \(fileName) - \(function) - line: \(line) -> \(message)")
default: default:
os_log("[%{public}@] %{public}@ - %{public}@ - Line: %{public}d -> %{public}@", level, fileName, String(describing: function), line, message) os_log(
"[%{public}@] %{public}@ - %{public}@ - Line: %{public}d -> %{public}@",
level,
fileName,
String(describing: function),
line,
message
)
} }
} }
} }

View File

@ -11,6 +11,7 @@ import GRPC
import SwiftProtobuf import SwiftProtobuf
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
// swiftlint:disable function_parameter_count identifier_name
class AwfulLightWalletService: MockLightWalletService { class AwfulLightWalletService: MockLightWalletService {
override func latestBlockHeight() throws -> BlockHeight { override func latestBlockHeight() throws -> BlockHeight {
throw LightWalletServiceError.criticalError throw LightWalletServiceError.criticalError
@ -24,7 +25,6 @@ class AwfulLightWalletService: MockLightWalletService {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
result(.failure(LightWalletServiceError.invalidBlock)) result(.failure(LightWalletServiceError.invalidBlock))
} }
} }
override func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) { override func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
@ -33,56 +33,54 @@ class AwfulLightWalletService: MockLightWalletService {
} }
} }
override func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse,LightWalletServiceError>) -> Void) { override func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
result(.failure(LightWalletServiceError.invalidBlock)) result(.failure(LightWalletServiceError.invalidBlock))
} }
} }
/** /**
Submits a raw transaction over lightwalletd. Blocking Submits a raw transaction over lightwalletd. Blocking
*/ */
override func submit(spendTransaction: Data) throws -> LightWalletServiceResponse { override func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
throw LightWalletServiceError.invalidBlock throw LightWalletServiceError.invalidBlock
} }
} }
class SlightlyBadLightWalletService: MockLightWalletService { class SlightlyBadLightWalletService: MockLightWalletService {
override func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
override func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse,LightWalletServiceError>) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
result(.success(LightWalletServiceMockResponse.error)) result(.success(LightWalletServiceMockResponse.error))
} }
} }
/** /**
Submits a raw transaction over lightwalletd. Blocking Submits a raw transaction over lightwalletd. Blocking
*/ */
override func submit(spendTransaction: Data) throws -> LightWalletServiceResponse { override func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
LightWalletServiceMockResponse.error LightWalletServiceMockResponse.error
} }
} }
extension LightWalletServiceMockResponse { extension LightWalletServiceMockResponse {
static var error: LightWalletServiceMockResponse { static var error: LightWalletServiceMockResponse {
LightWalletServiceMockResponse(errorCode: -100, errorMessage: "Ohhh this is bad dude, really bad, you lost all your internet money", unknownFields: UnknownStorage()) LightWalletServiceMockResponse(
errorCode: -100,
errorMessage: "Ohhh this is bad, really bad, you lost all your internet money",
unknownFields: UnknownStorage()
)
} }
static var success: LightWalletServiceMockResponse { static var success: LightWalletServiceMockResponse {
LightWalletServiceMockResponse(errorCode: 0, errorMessage: "", unknownFields: UnknownStorage()) LightWalletServiceMockResponse(errorCode: 0, errorMessage: "", unknownFields: UnknownStorage())
} }
} }
class MockRustBackend: ZcashRustBackendWelding { class MockRustBackend: ZcashRustBackendWelding {
static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 { static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 {
-1 -1
} }
static func getNearestRewindHeight(dbData: URL, height: Int32, networkType: NetworkType) -> Int32 { static func getNearestRewindHeight(dbData: URL, height: Int32, networkType: NetworkType) -> Int32 {
-1 -1
} }
@ -103,7 +101,16 @@ class MockRustBackend: ZcashRustBackendWelding {
-1 -1
} }
static func putUnspentTransparentOutput(dbData: URL, address: String, txid: [UInt8], index: Int, script: [UInt8], value: Int64, height: BlockHeight, networkType: NetworkType) throws -> Bool { static func putUnspentTransparentOutput(
dbData: URL,
address: String,
txid: [UInt8],
index: Int,
script: [UInt8],
value: Int64,
height: BlockHeight,
networkType: NetworkType
) throws -> Bool {
false false
} }
@ -111,11 +118,31 @@ class MockRustBackend: ZcashRustBackendWelding {
throw RustWeldingError.genericError(message: "unimplemented") throw RustWeldingError.genericError(message: "unimplemented")
} }
static func createToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 { static func createToAddress(
dbData: URL,
account: Int32,
extsk: String,
to address: String,
value: Int64,
memo: String?,
spendParamsPath: String,
outputParamsPath: String,
networkType: NetworkType
) -> Int64 {
-1 -1
} }
static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, tsk: String, extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 { static func shieldFunds(
dbCache: URL,
dbData: URL,
account: Int32,
tsk: String,
extsk: String,
memo: String?,
spendParamsPath: String,
outputParamsPath: String,
networkType: NetworkType
) -> Int64 {
-1 -1
} }
@ -171,12 +198,11 @@ class MockRustBackend: ZcashRustBackendWelding {
nil nil
} }
static func consensusBranchIdFor(height: Int32, networkType: NetworkType) throws -> Int32 { static func consensusBranchIdFor(height: Int32, networkType: NetworkType) throws -> Int32 {
guard let c = consensusBranchID else { guard let consensus = consensusBranchID else {
return try rustBackend.consensusBranchIdFor(height: height, networkType: networkType) return try rustBackend.consensusBranchIdFor(height: height, networkType: networkType)
} }
return c return consensus
} }
static var networkType = NetworkType.testnet static var networkType = NetworkType.testnet
@ -225,9 +251,23 @@ class MockRustBackend: ZcashRustBackendWelding {
mockAccounts ?? rustBackend.initAccountsTable(dbData: dbData, seed: seed, accounts: accounts, networkType: networkType) mockAccounts ?? rustBackend.initAccountsTable(dbData: dbData, seed: seed, accounts: accounts, networkType: networkType)
} }
static func initBlocksTable(dbData: URL, height: Int32, hash: String, time: UInt32, saplingTree: String, networkType: NetworkType) throws { static func initBlocksTable(
dbData: URL,
height: Int32,
hash: String,
time: UInt32,
saplingTree: String,
networkType: NetworkType
) throws {
if !mockDataDb { if !mockDataDb {
try rustBackend.initBlocksTable(dbData: dbData, height: height, hash: hash, time: time, saplingTree: saplingTree, networkType: networkType) try rustBackend.initBlocksTable(
dbData: dbData,
height: height,
hash: hash,
time: time,
saplingTree: saplingTree,
networkType: networkType
)
} }
} }
@ -263,7 +303,6 @@ class MockRustBackend: ZcashRustBackendWelding {
if attempts > 0 { if attempts > 0 {
return validationResult(dbCache: dbCache, dbData: dbData, networkType: networkType) return validationResult(dbCache: dbCache, dbData: dbData, networkType: networkType)
} else { } else {
if attempts == 0 { if attempts == 0 {
return Int32(mockValidateCombinedChainFailureHeight) return Int32(mockValidateCombinedChainFailureHeight)
} else if attempts < 0 && mockValidateCombinedChainKeepFailing { } else if attempts < 0 && mockValidateCombinedChainKeepFailing {
@ -276,7 +315,7 @@ class MockRustBackend: ZcashRustBackendWelding {
return rustBackend.validateCombinedChain(dbCache: dbCache, dbData: dbData, networkType: networkType) return rustBackend.validateCombinedChain(dbCache: dbCache, dbData: dbData, networkType: networkType)
} }
private static func validationResult(dbCache: URL, dbData: URL, networkType: NetworkType) -> Int32{ private static func validationResult(dbCache: URL, dbData: URL, networkType: NetworkType) -> Int32 {
if mockDataDb { if mockDataDb {
return -1 return -1
} else { } else {
@ -290,7 +329,6 @@ class MockRustBackend: ZcashRustBackendWelding {
static func scanBlocks(dbCache: URL, dbData: URL, limit: UInt32, networkType: NetworkType) -> Bool { static func scanBlocks(dbCache: URL, dbData: URL, limit: UInt32, networkType: NetworkType) -> Bool {
if let rate = mockScanblocksSuccessRate { if let rate = mockScanblocksSuccessRate {
if shouldSucceed(successRate: rate) { if shouldSucceed(successRate: rate) {
return mockDataDb ? true : rustBackend.scanBlocks(dbCache: dbCache, dbData: dbData, networkType: networkType) return mockDataDb ? true : rustBackend.scanBlocks(dbCache: dbCache, dbData: dbData, networkType: networkType)
} else { } else {
@ -300,8 +338,18 @@ class MockRustBackend: ZcashRustBackendWelding {
return rustBackend.scanBlocks(dbCache: dbCache, dbData: dbData, networkType: Self.networkType) return rustBackend.scanBlocks(dbCache: dbCache, dbData: dbData, networkType: Self.networkType)
} }
static func createToAddress(dbData: URL, account: Int32, extsk: String, consensusBranchId: Int32, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 { static func createToAddress(
// mockCreateToAddress ?? rustBackend.createToAddress(dbData: dbData, account: account, extsk: extsk, consensusBranchId: consensusBranchId, to: to, value: value, memo: memo, spendParamsPath: spendParamsPath, outputParamsPath: outputParamsPath) dbData: URL,
account: Int32,
extsk: String,
consensusBranchId: Int32,
to address: String,
value: Int64,
memo: String?,
spendParamsPath: String,
outputParamsPath: String,
networkType: NetworkType
) -> Int64 {
-1 -1
} }
@ -325,5 +373,4 @@ class MockRustBackend: ZcashRustBackendWelding {
static func decryptAndStoreTransaction(dbData: URL, txBytes: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool { static func decryptAndStoreTransaction(dbData: URL, txBytes: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool {
false false
} }
} }

View File

@ -16,7 +16,11 @@ struct TestDbHandle {
init(originalDb: URL) { init(originalDb: URL) {
self.originalDb = originalDb self.originalDb = originalDb
self.readWriteDb = FileManager.default.temporaryDirectory.appendingPathComponent(self.originalDb.lastPathComponent.appending("_\(Date().timeIntervalSince1970)")) // avoid files clashing because crashing tests failed to remove previous ones by incrementally changing the filename // avoid files clashing because crashing tests failed to remove previous ones by incrementally changing the filename
self.readWriteDb = FileManager.default.temporaryDirectory
.appendingPathComponent(
self.originalDb.lastPathComponent.appending("_\(Date().timeIntervalSince1970)")
)
} }
func setUp() throws { func setUp() throws {
@ -32,8 +36,9 @@ struct TestDbHandle {
} }
} }
// This requires reference semantics, an enum cannot be used
// swiftlint:disable:next convenience_type
class TestDbBuilder { class TestDbBuilder {
enum TestBuilderError: Error { enum TestBuilderError: Error {
case generalError case generalError
} }
@ -41,18 +46,21 @@ class TestDbBuilder {
static func inMemoryCompactBlockStorage() throws -> CompactBlockStorage { static func inMemoryCompactBlockStorage() throws -> CompactBlockStorage {
let compactBlockDao = CompactBlockStorage(connectionProvider: try InMemoryDbProvider()) let compactBlockDao = CompactBlockStorage(connectionProvider: try InMemoryDbProvider())
try compactBlockDao.createTable() try compactBlockDao.createTable()
return compactBlockDao return compactBlockDao
} }
static func diskCompactBlockStorage(at url: URL) throws -> CompactBlockStorage { static func diskCompactBlockStorage(at url: URL) throws -> CompactBlockStorage {
let compactBlockDao = CompactBlockStorage(connectionProvider: SimpleConnectionProvider(path: url.absoluteString)) let compactBlockDao = CompactBlockStorage(connectionProvider: SimpleConnectionProvider(path: url.absoluteString))
try compactBlockDao.createTable() try compactBlockDao.createTable()
return compactBlockDao return compactBlockDao
} }
static func pendingTransactionsDbURL() throws -> URL { static func pendingTransactionsDbURL() throws -> URL {
try __documentsDirectory().appendingPathComponent("pending.db") try __documentsDirectory().appendingPathComponent("pending.db")
} }
static func prePopulatedCacheDbURL() -> URL? { static func prePopulatedCacheDbURL() -> URL? {
Bundle(for: TestDbBuilder.self).url(forResource: "cache", withExtension: "db") Bundle(for: TestDbBuilder.self).url(forResource: "cache", withExtension: "db")
} }
@ -65,6 +73,7 @@ class TestDbBuilder {
let bundle = Bundle(for: TestDbBuilder.self) let bundle = Bundle(for: TestDbBuilder.self)
guard let url = bundle.url(forResource: "ZcashSdk_Data", withExtension: "db") else { return nil } guard let url = bundle.url(forResource: "ZcashSdk_Data", withExtension: "db") else { return nil }
let provider = SimpleConnectionProvider(path: url.absoluteString, readonly: true) let provider = SimpleConnectionProvider(path: url.absoluteString, readonly: true)
return provider return provider
} }
@ -85,7 +94,6 @@ class TestDbBuilder {
} }
static func seed(db: CompactBlockRepository, with blockRange: CompactBlockRange) throws { static func seed(db: CompactBlockRepository, with blockRange: CompactBlockRange) throws {
guard let blocks = StubBlockCreator.createBlockRange(blockRange) else { guard let blocks = StubBlockCreator.createBlockRange(blockRange) else {
throw TestBuilderError.generalError throw TestBuilderError.generalError
} }
@ -96,8 +104,8 @@ class TestDbBuilder {
struct InMemoryDbProvider: ConnectionProvider { struct InMemoryDbProvider: ConnectionProvider {
var readonly: Bool var readonly: Bool
var conn: Connection var conn: Connection
init(readonly: Bool = false) throws { init(readonly: Bool = false) throws {
self.readonly = readonly self.readonly = readonly
self.conn = try Connection(.inMemory, readonly: readonly) self.conn = try Connection(.inMemory, readonly: readonly)
@ -108,7 +116,7 @@ struct InMemoryDbProvider: ConnectionProvider {
} }
} }
struct StubBlockCreator { enum StubBlockCreator {
static func createRandomDataBlock(with height: BlockHeight) -> ZcashCompactBlock? { static func createRandomDataBlock(with height: BlockHeight) -> ZcashCompactBlock? {
guard let data = randomData(ofLength: 100) else { guard let data = randomData(ofLength: 100) else {
LoggerProxy.debug("error creating stub block") LoggerProxy.debug("error creating stub block")
@ -116,9 +124,9 @@ struct StubBlockCreator {
} }
return ZcashCompactBlock(height: height, data: data) return ZcashCompactBlock(height: height, data: data)
} }
static func createBlockRange(_ range: CompactBlockRange) -> [ZcashCompactBlock]? { static func createBlockRange(_ range: CompactBlockRange) -> [ZcashCompactBlock]? {
var blocks: [ZcashCompactBlock] = []
var blocks = [ZcashCompactBlock]()
for height in range { for height in range {
guard let block = createRandomDataBlock(with: height) else { guard let block = createRandomDataBlock(with: height) else {
return nil return nil
@ -136,8 +144,7 @@ struct StubBlockCreator {
return Data(bytes: &bytes, count: bytes.count) return Data(bytes: &bytes, count: bytes.count)
} }
LoggerProxy.debug("Status \(status)") LoggerProxy.debug("Status \(status)")
return nil return nil
} }
} }

View File

@ -11,7 +11,9 @@ import GRPC
import ZcashLightClientKit import ZcashLightClientKit
import XCTest import XCTest
import NIO import NIO
class LightWalletEndpointBuilder {
// swiftlint:disable identifier_name
enum LightWalletEndpointBuilder {
static var `default`: LightWalletEndpoint { static var `default`: LightWalletEndpoint {
LightWalletEndpoint(address: Constants.address, port: 9067, secure: false) LightWalletEndpoint(address: Constants.address, port: 9067, secure: false)
} }
@ -29,23 +31,25 @@ class ChannelProvider {
func channel(secure: Bool = false) -> GRPCChannel { func channel(secure: Bool = false) -> GRPCChannel {
let endpoint = LightWalletEndpointBuilder.default let endpoint = LightWalletEndpointBuilder.default
let configuration = ClientConnection.Configuration(target: .hostAndPort(endpoint.host, endpoint.port), eventLoopGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1), tls: secure ? .init() : nil) let configuration = ClientConnection.Configuration(
target: .hostAndPort(endpoint.host, endpoint.port),
eventLoopGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1),
tls: secure ? .init() : nil
)
return ClientConnection(configuration: configuration) return ClientConnection(configuration: configuration)
} }
} }
struct MockDbInit { enum MockDbInit {
@discardableResult static func emptyFile(at path: String) -> Bool { @discardableResult
static func emptyFile(at path: String) -> Bool {
FileManager.default.createFile(atPath: path, contents: Data("".utf8), attributes: nil) FileManager.default.createFile(atPath: path, contents: Data("".utf8), attributes: nil)
} }
static func destroy(at path: String) throws { static func destroy(at path: String) throws {
try FileManager.default.removeItem(atPath: path) try FileManager.default.removeItem(atPath: path)
} }
} }
extension XCTestExpectation { extension XCTestExpectation {
@ -83,7 +87,6 @@ func __outputParamsURL() throws -> URL {
} }
func copyParametersToDocuments() throws -> (spend: URL, output: URL) { func copyParametersToDocuments() throws -> (spend: URL, output: URL) {
let spendURL = try __documentsDirectory().appendingPathComponent("sapling-spend.params", isDirectory: false) let spendURL = try __documentsDirectory().appendingPathComponent("sapling-spend.params", isDirectory: false)
let outputURL = try __documentsDirectory().appendingPathComponent("sapling-output.params", isDirectory: false) let outputURL = try __documentsDirectory().appendingPathComponent("sapling-output.params", isDirectory: false)
try FileManager.default.copyItem(at: try __spendParamsURL(), to: spendURL) try FileManager.default.copyItem(at: try __spendParamsURL(), to: spendURL)
@ -94,37 +97,42 @@ func copyParametersToDocuments() throws -> (spend: URL, output: URL) {
func deleteParametersFromDocuments() throws { func deleteParametersFromDocuments() throws {
let documents = try __documentsDirectory() let documents = try __documentsDirectory()
deleteParamsFrom(spend: documents.appendingPathComponent("sapling-spend.params"), output: documents.appendingPathComponent("sapling-output.params")) deleteParamsFrom(
spend: documents.appendingPathComponent("sapling-spend.params"),
output: documents.appendingPathComponent("sapling-output.params")
)
} }
func deleteParamsFrom(spend: URL, output: URL) { func deleteParamsFrom(spend: URL, output: URL) {
try? FileManager.default.removeItem(at: spend) try? FileManager.default.removeItem(at: spend)
try? FileManager.default.removeItem(at: output) try? FileManager.default.removeItem(at: output)
} }
func parametersReady() -> Bool { func parametersReady() -> Bool {
guard
guard let output = try? __outputParamsURL(), let output = try? __outputParamsURL(),
let spend = try? __spendParamsURL(), let spend = try? __spendParamsURL(),
FileManager.default.isReadableFile(atPath: output.absoluteString), FileManager.default.isReadableFile(atPath: output.absoluteString),
FileManager.default.isReadableFile(atPath: spend.absoluteString) else { FileManager.default.isReadableFile(atPath: spend.absoluteString)
return false else {
return false
} }
return true return true
} }
class StubTest: XCTestCase {} class StubTest: XCTestCase {}
extension Bundle { extension Bundle {
static var testBundle: Bundle { static var testBundle: Bundle {
Bundle(for: StubTest.self) Bundle(for: StubTest.self)
} }
} }
// swiftlint:disable force_unwrapping
class TestSeed { class TestSeed {
/** /**
test account: "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" test account: "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 seedString = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")! let seedString = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")!
func seed() -> [UInt8] { func seed() -> [UInt8] {