commit
fb226d951f
|
@ -10,7 +10,7 @@ import Foundation
|
||||||
import ZcashLightClientKit
|
import ZcashLightClientKit
|
||||||
import MnemonicSwift
|
import MnemonicSwift
|
||||||
struct DemoAppConfig {
|
struct DemoAppConfig {
|
||||||
static var host = ZcashSDK.isMainnet ? "localhost" : "localhost"
|
static var host = ZcashSDK.isMainnet ? "localhost" : "lightwalletd.testnet.electriccoin.co"
|
||||||
static var port: Int = 9067
|
static var port: Int = 9067
|
||||||
static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 663174 : 620_000
|
static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 663174 : 620_000
|
||||||
static var network = ZcashSDK.isMainnet ? ZcashNetwork.mainNet : ZcashNetwork.testNet
|
static var network = ZcashSDK.isMainnet ? ZcashNetwork.mainNet : ZcashNetwork.testNet
|
||||||
|
|
|
@ -352,13 +352,27 @@ public extension WalletBirthday {
|
||||||
time: 1585012771,
|
time: 1585012771,
|
||||||
tree: "01e3bd906376b563d184bfbf6616e220f895001b7ef9d26bf38c6cb5c71e57a42b001001d848adf8c38d113140bb30d306b0761da6987e25ffc0d82faa63c2764aab120301de3e6a35d09192cde3430860c70a534d7b63e95a726fab052de2a9befa3cc3320189b958fa030131bb83385a3e3a8b187a166dc1b3a02050f2d2fc20788536c30e0001cb8770ef198e7de60093a339afbc561c16c16749f9f96751c2fc58a22d0ff36f01f86ff70dd512f7075d02c5ee6e28a8824832d08025a4cfaf4c1854f1fba5da10019bcac1b44a27de2c4528fa6f4b3432913511b219cb3b29d137cac0236a3d244800000000016f6df9b95ef63866bdf0e8b4b97701cd09232ec3e4e240808c0546d01bc7bb0501e3ec5d790cc9acc2586fc6e9ce5aae5f5aba32d33e386165c248c4a03ec8ed670000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
tree: "01e3bd906376b563d184bfbf6616e220f895001b7ef9d26bf38c6cb5c71e57a42b001001d848adf8c38d113140bb30d306b0761da6987e25ffc0d82faa63c2764aab120301de3e6a35d09192cde3430860c70a534d7b63e95a726fab052de2a9befa3cc3320189b958fa030131bb83385a3e3a8b187a166dc1b3a02050f2d2fc20788536c30e0001cb8770ef198e7de60093a339afbc561c16c16749f9f96751c2fc58a22d0ff36f01f86ff70dd512f7075d02c5ee6e28a8824832d08025a4cfaf4c1854f1fba5da10019bcac1b44a27de2c4528fa6f4b3432913511b219cb3b29d137cac0236a3d244800000000016f6df9b95ef63866bdf0e8b4b97701cd09232ec3e4e240808c0546d01bc7bb0501e3ec5d790cc9acc2586fc6e9ce5aae5f5aba32d33e386165c248c4a03ec8ed670000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||||
)
|
)
|
||||||
case 950000 ..< 1130000:
|
case 950000 ..< 1013250:
|
||||||
return WalletBirthday(
|
return WalletBirthday(
|
||||||
height: 950000,
|
height: 950000,
|
||||||
hash: "0005050d2ce31b9b925c7ef4ab3d3166d5833bdcfee251294f29072a2bc0f75d",
|
hash: "0005050d2ce31b9b925c7ef4ab3d3166d5833bdcfee251294f29072a2bc0f75d",
|
||||||
time: 1591609525,
|
time: 1591609525,
|
||||||
tree: "01f0637235c4a699d49ba996457a6c4eb7c67edd8270948065683deb19ef218363019f65a9692cefc7b90b42c1538ac1f38f7a7598549089c4561315b482f378523010000000000000018d30d0039277b05ab9e0c3990d53037c45892bf17af2d04fef40ed48c164ad2201ff5d86bbbe360e31378e783b740f8b05db2cf4246b95aa3851d22ed45554750300010cefb25743d5dd6062ef3afba438731cd5b35befc1038ecca3076fd205829e550001c19052386d8bbe3c07a1faf302281d67946cc9547e7e1890ff56b3a3ec69c0310001be53a6cd33da0442c7c6362c0172241f42e13c6dc893436a661a1cbf49775c1f00011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
tree: "01f0637235c4a699d49ba996457a6c4eb7c67edd8270948065683deb19ef218363019f65a9692cefc7b90b42c1538ac1f38f7a7598549089c4561315b482f378523010000000000000018d30d0039277b05ab9e0c3990d53037c45892bf17af2d04fef40ed48c164ad2201ff5d86bbbe360e31378e783b740f8b05db2cf4246b95aa3851d22ed45554750300010cefb25743d5dd6062ef3afba438731cd5b35befc1038ecca3076fd205829e550001c19052386d8bbe3c07a1faf302281d67946cc9547e7e1890ff56b3a3ec69c0310001be53a6cd33da0442c7c6362c0172241f42e13c6dc893436a661a1cbf49775c1f00011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||||
)
|
)
|
||||||
|
case 1013250 ..< 1028400:
|
||||||
|
return WalletBirthday(
|
||||||
|
height: 1013250,
|
||||||
|
hash: "0017b3c49eea14e93f69adbc7a8f24eef3e9645f92e3b8d5e4091e1d5a4824b7",
|
||||||
|
time: 1595516064,
|
||||||
|
tree: "01d45e95007f8f97fe3c6a297a9d4bca917772e545fbcbb61b4f42d8a743eae31b0010013d7b7da2e6792dbfc6360f0c13a1ba879aa51a498c6dbab87aa57cef558cc35a00000001f945c15602617327026e3e0f231daf91570b32f7bb766f65e7e82131a911cc3c0000013173e9983fbd7a396e192e520d163be06cdd28abcfeba46c59dc62a400f589080001e7d5b00f0758cd3b7407c6d13e23d1a59e3f510c3dd3a4a8fc367a5305673b3f000001c787f900940720a3692e5694b085d5409dfa966017a6a48441c7e4b423b3144701be53a6cd33da0442c7c6362c0172241f42e13c6dc893436a661a1cbf49775c1f00011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||||
|
)
|
||||||
|
case 1028400 ..< 1130000:
|
||||||
|
return WalletBirthday(
|
||||||
|
height: 1028400,
|
||||||
|
hash: "0006b94b304009f8a6287aa48021aed26582ce74d387c2c452aede86566179a6",
|
||||||
|
time: 1596480151,
|
||||||
|
tree: "01ac95378779cf56f0726655d248f95d63e07316ab67651ce357b346f9a7adba300107d7544edbe7d0522fe523df2ff804fc9f33f4844dd29e86a23fc7aa1818e237100001f8779a159482fd3a28c640e6d48ddfb37a44a26a102aae3822f32d2de22a1c70011f97534315f1800961bc78fcd1495e3b8047bb227e2776080fb31be6cae14e73000000000001fe15295e017fc2b8ba4ea1b7e53dd15f19860d10e7952905b7598373e143413e00019289038516ce3d6038cebe852ab5e32f7a1966fc79a1e41c7cb6e67e5a71c33d0001c787f900940720a3692e5694b085d5409dfa966017a6a48441c7e4b423b3144701be53a6cd33da0442c7c6362c0172241f42e13c6dc893436a661a1cbf49775c1f00011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||||
|
)
|
||||||
case 1130000 ..< BlockHeight.max:
|
case 1130000 ..< BlockHeight.max:
|
||||||
return WalletBirthday(
|
return WalletBirthday(
|
||||||
height: 1130000,
|
height: 1130000,
|
||||||
|
|
|
@ -0,0 +1,438 @@
|
||||||
|
//
|
||||||
|
// NetworkUpgradeTests.swift
|
||||||
|
// ZcashLightClientKit-Unit-Tests
|
||||||
|
//
|
||||||
|
// Created by Francisco Gindre on 10/30/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
class NetworkUpgradeTests: XCTestCase {
|
||||||
|
|
||||||
|
let activationHeight: BlockHeight = 1028500
|
||||||
|
var spendingKey = "secret-extended-key-test1qv2vf437qqqqpqpfc0arpv55ncq33p2p895hlcx0ra6d0g739v93luqdjpxun3kt050j9qnrqjyp8d7fdxgedfyxpjmuyha2ulxa6hmqvm2gnvuc3tvs3enpxwuz768qfkd286vr3jgyrgr5ddx2ukrdl95ak3tzqylzjeqw3pnmgtmwsvemrj3sk6vqgwxm9khlv46wccn33ayw52prr233ea069c9u8m3839dvw30sdf6k32xddhpte6p6qsuxval6usyh6lr55pgypkgtz"
|
||||||
|
|
||||||
|
let testRecipientAddress = "ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc" //TODO: Parameterize this from environment
|
||||||
|
|
||||||
|
let sendAmount: Int64 = 1000
|
||||||
|
var birthday: BlockHeight = 1013250
|
||||||
|
|
||||||
|
var coordinator: TestCoordinator!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
|
||||||
|
coordinator = try TestCoordinator(
|
||||||
|
spendingKey: spendingKey,
|
||||||
|
walletBirthday: birthday,
|
||||||
|
channelProvider: ChannelProvider()
|
||||||
|
)
|
||||||
|
try coordinator.reset(saplingActivation: birthday)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
try coordinator.stop()
|
||||||
|
try? FileManager.default.removeItem(at: coordinator.databases.cacheDB)
|
||||||
|
try? FileManager.default.removeItem(at: coordinator.databases.dataDB)
|
||||||
|
try? FileManager.default.removeItem(at: coordinator.databases.pendingDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given that a wallet had funds prior to activation it can spend them after activation
|
||||||
|
*/
|
||||||
|
func testSpendPriorFundsAfterActivation() throws {
|
||||||
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, length: 15300)
|
||||||
|
|
||||||
|
let firstSyncExpectation = XCTestExpectation(description: "first sync")
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.DEFAULT_STALE_TOLERANCE)
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
|
let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
guard verifiedBalance > ZcashSDK.MINERS_FEE_ZATOSHI else {
|
||||||
|
XCTFail("not enough balance to continue test")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 1)
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
||||||
|
var p: PendingTransactionEntity? = nil
|
||||||
|
let spendAmount: Int64 = 10000
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
switch result {
|
||||||
|
case .failure(let e):
|
||||||
|
self.handleError(e)
|
||||||
|
case .success(let pendingTx):
|
||||||
|
p = pendingTx
|
||||||
|
}
|
||||||
|
sendExpectation.fulfill()
|
||||||
|
})
|
||||||
|
|
||||||
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
guard let _ = p else {
|
||||||
|
XCTFail("no pending transaction after sending")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
getIncomingTransaction
|
||||||
|
*/
|
||||||
|
guard let incomingTx = try coordinator.getIncomingTransactions()?.first else {
|
||||||
|
XCTFail("no incoming transaction")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let sentTxHeight: BlockHeight = activationHeight + 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
stage transaction at sentTxHeight
|
||||||
|
*/
|
||||||
|
|
||||||
|
try coordinator.stageTransaction(incomingTx, at: sentTxHeight)
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 20)
|
||||||
|
sleep(1)
|
||||||
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
|
|
||||||
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), verifiedBalance - spendAmount)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given that a wallet receives funds after activation it can spend them when confirmed
|
||||||
|
*/
|
||||||
|
func testSpendPostActivationFundsAfterConfirmation() throws {
|
||||||
|
try FakeChainBuilder.buildChainPostActivationFunds(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, length: 15300)
|
||||||
|
|
||||||
|
let firstSyncExpectation = XCTestExpectation(description: "first sync")
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 10)
|
||||||
|
sleep(3)
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
|
guard try coordinator.synchronizer.allReceivedTransactions().filter({$0.minedHeight > activationHeight}).count > 0 else {
|
||||||
|
XCTFail("this test requires funds received after activation height")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 20)
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
||||||
|
var p: PendingTransactionEntity? = nil
|
||||||
|
let spendAmount: Int64 = 10000
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
switch result {
|
||||||
|
case .failure(let e):
|
||||||
|
self.handleError(e)
|
||||||
|
case .success(let pendingTx):
|
||||||
|
p = pendingTx
|
||||||
|
}
|
||||||
|
sendExpectation.fulfill()
|
||||||
|
})
|
||||||
|
|
||||||
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
guard let _ = p else {
|
||||||
|
XCTFail("no pending transaction after sending")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 1 + 10)
|
||||||
|
|
||||||
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
func testSpendMinedSpendThatExpiresOnActivation() throws {
|
||||||
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, length: 15300)
|
||||||
|
|
||||||
|
let firstSyncExpectation = XCTestExpectation(description: "first sync")
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight - 10)
|
||||||
|
sleep(3)
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
|
let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
XCTAssertTrue(verifiedBalance > ZcashSDK.MINERS_FEE_ZATOSHI)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
||||||
|
var p: PendingTransactionEntity? = nil
|
||||||
|
let spendAmount: Int64 = 10000
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
switch result {
|
||||||
|
case .failure(let e):
|
||||||
|
self.handleError(e)
|
||||||
|
case .success(let pendingTx):
|
||||||
|
p = pendingTx
|
||||||
|
}
|
||||||
|
sendExpectation.fulfill()
|
||||||
|
})
|
||||||
|
|
||||||
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
guard let pendingTx = p else {
|
||||||
|
XCTFail("no pending transaction after sending")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getIncomingTransaction
|
||||||
|
*/
|
||||||
|
guard let incomingTx = try coordinator.getIncomingTransactions()?.first else {
|
||||||
|
XCTFail("no incoming transaction")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let sentTxHeight: BlockHeight = activationHeight - 5
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
stage transaction at sentTxHeight
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
try coordinator.stageTransaction(incomingTx, at: sentTxHeight)
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 5)
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
|
|
||||||
|
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")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(confirmedTx.minedHeight, sentTxHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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 {
|
||||||
|
try FakeChainBuilder.buildChain(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, length: 15300)
|
||||||
|
|
||||||
|
let firstSyncExpectation = XCTestExpectation(description: "first sync")
|
||||||
|
let offset = 5
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight - 10)
|
||||||
|
sleep(3)
|
||||||
|
|
||||||
|
let verifiedBalancePreActivation = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
|
let verifiedBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
guard verifiedBalance > ZcashSDK.MINERS_FEE_ZATOSHI else {
|
||||||
|
XCTFail("balance is not enough to continue with this test")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
||||||
|
var p: PendingTransactionEntity? = nil
|
||||||
|
let spendAmount: Int64 = 10000
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
switch result {
|
||||||
|
case .failure(let e):
|
||||||
|
self.handleError(e)
|
||||||
|
case .success(let pendingTx):
|
||||||
|
p = pendingTx
|
||||||
|
}
|
||||||
|
sendExpectation.fulfill()
|
||||||
|
})
|
||||||
|
|
||||||
|
wait(for: [sendExpectation], timeout: 11)
|
||||||
|
|
||||||
|
guard let pendingTx = p else {
|
||||||
|
XCTFail("no pending transaction after sending")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getIncomingTransaction
|
||||||
|
*/
|
||||||
|
guard let _ = try coordinator.getIncomingTransactions()?.first else {
|
||||||
|
XCTFail("no incoming transaction")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
don't stage transaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + offset)
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
let afterSendExpectation = XCTestExpectation(description: "aftersend")
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
afterSendExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [afterSendExpectation], timeout: 10)
|
||||||
|
|
||||||
|
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")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(verifiedBalancePreActivation, coordinator.synchronizer.initializer.getVerifiedBalance())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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 {
|
||||||
|
try FakeChainBuilder.buildChainMixedFunds(darksideWallet: coordinator.service, birthday: birthday, networkActivationHeight: activationHeight, length: 15300)
|
||||||
|
|
||||||
|
let firstSyncExpectation = XCTestExpectation(description: "first sync")
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight - 1)
|
||||||
|
sleep(3)
|
||||||
|
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
firstSyncExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [firstSyncExpectation], timeout: 120)
|
||||||
|
|
||||||
|
let preActivationBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
|
||||||
|
try coordinator.applyStaged(blockheight: activationHeight + 30)
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
let secondSyncExpectation = XCTestExpectation(description: "second sync")
|
||||||
|
try coordinator.sync(completion: { (synchronizer) in
|
||||||
|
|
||||||
|
secondSyncExpectation.fulfill()
|
||||||
|
|
||||||
|
}, error: self.handleError)
|
||||||
|
|
||||||
|
wait(for: [secondSyncExpectation], timeout: 10)
|
||||||
|
guard try coordinator.synchronizer.allReceivedTransactions().filter({$0.minedHeight > activationHeight}).count > 0 else {
|
||||||
|
XCTFail("this test requires funds received after activation height")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let postActivationBalance = coordinator.synchronizer.initializer.getVerifiedBalance()
|
||||||
|
|
||||||
|
XCTAssertTrue(preActivationBalance < postActivationBalance, "This test requires that funds post activation are greater that pre activation")
|
||||||
|
let sendExpectation = XCTestExpectation(description: "send expectation")
|
||||||
|
var p: PendingTransactionEntity? = nil
|
||||||
|
|
||||||
|
// spend all the funds
|
||||||
|
let spendAmount: Int64 = postActivationBalance - Int64(ZcashSDK.MINERS_FEE_ZATOSHI)
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
switch result {
|
||||||
|
case .failure(let e):
|
||||||
|
self.handleError(e)
|
||||||
|
case .success(let pendingTx):
|
||||||
|
p = pendingTx
|
||||||
|
}
|
||||||
|
sendExpectation.fulfill()
|
||||||
|
})
|
||||||
|
|
||||||
|
wait(for: [sendExpectation], timeout: 15)
|
||||||
|
|
||||||
|
guard let _ = p else {
|
||||||
|
XCTFail("no pending transaction after sending")
|
||||||
|
try coordinator.stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(coordinator.synchronizer.initializer.getVerifiedBalance(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(_ error: Error?) {
|
||||||
|
_ = try? coordinator.stop()
|
||||||
|
guard let testError = error else {
|
||||||
|
XCTFail("failed with nil error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
XCTFail("Failed with error: \(testError)")
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ class TestCoordinator {
|
||||||
case notDarksideWallet
|
case notDarksideWallet
|
||||||
case notificationFromUnknownSynchronizer
|
case notificationFromUnknownSynchronizer
|
||||||
case notMockLightWalletService
|
case notMockLightWalletService
|
||||||
|
case builderError
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SyncThreshold {
|
enum SyncThreshold {
|
||||||
|
@ -35,7 +36,7 @@ class TestCoordinator {
|
||||||
|
|
||||||
var completionHandler: ((SDKSynchronizer) -> Void)?
|
var completionHandler: ((SDKSynchronizer) -> Void)?
|
||||||
var errorHandler: ((Error?) -> Void)?
|
var errorHandler: ((Error?) -> Void)?
|
||||||
var seed: String
|
var spendingKey: String
|
||||||
var birthday: BlockHeight
|
var birthday: BlockHeight
|
||||||
var channelProvider: ChannelProvider
|
var channelProvider: ChannelProvider
|
||||||
var synchronizer: SDKSynchronizer
|
var synchronizer: SDKSynchronizer
|
||||||
|
@ -43,10 +44,20 @@ class TestCoordinator {
|
||||||
var spendingKeys: [String]?
|
var spendingKeys: [String]?
|
||||||
var databases: TemporaryTestDatabases
|
var databases: TemporaryTestDatabases
|
||||||
|
|
||||||
init(seed: String,
|
convenience init(seed: String,
|
||||||
walletBirthday: BlockHeight,
|
walletBirthday: BlockHeight,
|
||||||
channelProvider: ChannelProvider) throws {
|
channelProvider: ChannelProvider) throws {
|
||||||
self.seed = seed
|
guard let spendingKey = try DerivationTool.default.deriveSpendingKeys(seed: TestSeed().seed(),// todo: fix this
|
||||||
|
numberOfAccounts: 1).first else {
|
||||||
|
throw CoordinatorError.builderError
|
||||||
|
}
|
||||||
|
try self.init(spendingKey: spendingKey, walletBirthday: walletBirthday, channelProvider: channelProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init(spendingKey: String,
|
||||||
|
walletBirthday: BlockHeight,
|
||||||
|
channelProvider: ChannelProvider) throws {
|
||||||
|
self.spendingKey = spendingKey
|
||||||
self.birthday = walletBirthday
|
self.birthday = walletBirthday
|
||||||
self.channelProvider = channelProvider
|
self.channelProvider = channelProvider
|
||||||
self.databases = TemporaryDbBuilder.build()
|
self.databases = TemporaryDbBuilder.build()
|
||||||
|
@ -69,7 +80,7 @@ class TestCoordinator {
|
||||||
downloader: downloader,
|
downloader: downloader,
|
||||||
spendParamsURL: try __spendParamsURL(),
|
spendParamsURL: try __spendParamsURL(),
|
||||||
outputParamsURL: try __outputParamsURL(),
|
outputParamsURL: try __outputParamsURL(),
|
||||||
seedBytes: TestSeed().seed(),
|
spendingKey: spendingKey,
|
||||||
walletBirthday: WalletBirthday.birthday(with: birthday),
|
walletBirthday: WalletBirthday.birthday(with: birthday),
|
||||||
loggerProxy: SampleLogger(logLevel: .debug))
|
loggerProxy: SampleLogger(logLevel: .debug))
|
||||||
|
|
||||||
|
@ -210,7 +221,41 @@ class TemporaryDbBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestSynchronizerBuilder {
|
class TestSynchronizerBuilder {
|
||||||
|
static func build(
|
||||||
|
rustBackend: ZcashRustBackendWelding.Type,
|
||||||
|
lowerBoundHeight: BlockHeight,
|
||||||
|
cacheDbURL: URL,
|
||||||
|
dataDbURL: URL,
|
||||||
|
pendingDbURL: URL,
|
||||||
|
endpoint: LightWalletEndpoint,
|
||||||
|
service: LightWalletService,
|
||||||
|
repository: TransactionRepository,
|
||||||
|
downloader: CompactBlockDownloader,
|
||||||
|
spendParamsURL: URL,
|
||||||
|
outputParamsURL: URL,
|
||||||
|
spendingKey: String,
|
||||||
|
walletBirthday: WalletBirthday,
|
||||||
|
loggerProxy: Logger? = nil
|
||||||
|
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
|
||||||
|
let initializer = Initializer(
|
||||||
|
rustBackend: rustBackend,
|
||||||
|
lowerBoundHeight: lowerBoundHeight,
|
||||||
|
cacheDbURL: cacheDbURL,
|
||||||
|
dataDbURL: dataDbURL,
|
||||||
|
pendingDbURL: pendingDbURL,
|
||||||
|
endpoint: endpoint,
|
||||||
|
service: service,
|
||||||
|
repository: repository,
|
||||||
|
downloader: downloader,
|
||||||
|
spendParamsURL: spendParamsURL,
|
||||||
|
outputParamsURL: outputParamsURL,
|
||||||
|
loggerProxy: loggerProxy
|
||||||
|
)
|
||||||
|
try initializer.initialize(viewingKeys: [try DerivationTool().deriveViewingKey(spendingKey: spendingKey)], walletBirthday: walletBirthday.height)
|
||||||
|
|
||||||
|
return ([spendingKey], try SDKSynchronizer(initializer: initializer)
|
||||||
|
)
|
||||||
|
}
|
||||||
static func build(
|
static func build(
|
||||||
rustBackend: ZcashRustBackendWelding.Type,
|
rustBackend: ZcashRustBackendWelding.Type,
|
||||||
lowerBoundHeight: BlockHeight,
|
lowerBoundHeight: BlockHeight,
|
||||||
|
@ -227,9 +272,10 @@ class TestSynchronizerBuilder {
|
||||||
walletBirthday: WalletBirthday,
|
walletBirthday: WalletBirthday,
|
||||||
loggerProxy: Logger? = nil
|
loggerProxy: Logger? = nil
|
||||||
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
|
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
|
||||||
|
guard let spendingKey = try DerivationTool().deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1).first else {
|
||||||
let initializer = Initializer(
|
throw TestCoordinator.CoordinatorError.builderError
|
||||||
rustBackend: rustBackend,
|
}
|
||||||
|
return try build(rustBackend: rustBackend,
|
||||||
lowerBoundHeight: lowerBoundHeight,
|
lowerBoundHeight: lowerBoundHeight,
|
||||||
cacheDbURL: cacheDbURL,
|
cacheDbURL: cacheDbURL,
|
||||||
dataDbURL: dataDbURL,
|
dataDbURL: dataDbURL,
|
||||||
|
@ -240,12 +286,9 @@ class TestSynchronizerBuilder {
|
||||||
downloader: downloader,
|
downloader: downloader,
|
||||||
spendParamsURL: spendParamsURL,
|
spendParamsURL: spendParamsURL,
|
||||||
outputParamsURL: outputParamsURL,
|
outputParamsURL: outputParamsURL,
|
||||||
loggerProxy: loggerProxy
|
spendingKey: spendingKey,
|
||||||
)
|
walletBirthday: walletBirthday)
|
||||||
try initializer.initialize(viewingKeys: try DerivationTool().deriveViewingKeys(seed: seedBytes, numberOfAccounts: 1), walletBirthday: walletBirthday.height)
|
|
||||||
let credentials = try DerivationTool().deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1)
|
|
||||||
return (credentials, try SDKSynchronizer(initializer: initializer)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ class DarksideWalletService: LightWalletService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reset(saplingActivation: BlockHeight) throws {
|
func reset(saplingActivation: BlockHeight, branchID: String = "d3adb33f", chainName: String = "test") throws {
|
||||||
var metaState = DarksideMetaState()
|
var metaState = DarksideMetaState()
|
||||||
metaState.saplingActivation = Int32(saplingActivation)
|
metaState.saplingActivation = Int32(saplingActivation)
|
||||||
metaState.branchID = "d3adb33f"
|
metaState.branchID = "d3adb33f"
|
||||||
|
|
|
@ -14,6 +14,14 @@ enum FakeChainBuilderError: Error {
|
||||||
class FakeChainBuilder {
|
class 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"
|
||||||
|
|
||||||
|
static let testnetCanopyUpdateUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/testnet-canopy/1028400-1028600.txt"
|
||||||
|
|
||||||
|
static let testnetCanopyStartBlock = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/testnet-canopy/1013250.txt"
|
||||||
|
static let testnetPreCanopyTx = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/testnet-canopy/pre-activation-txs/61088726aaa7c25b0568dd7bf19955f4a57f7173034e720c924107ff05cd3649.txt"
|
||||||
|
|
||||||
|
static let testnetPostCanopyTx = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/testnet-canopy/post-activation-txs/ecaa6c03709d70aa25446a81690b18ddb11daac96a03fe4b5cfd0d89a49fb963.txt"
|
||||||
|
|
||||||
static func buildChain(darksideWallet: DarksideWalletService) throws {
|
static func buildChain(darksideWallet: DarksideWalletService) throws {
|
||||||
try darksideWallet.reset(saplingActivation: 663150)
|
try darksideWallet.reset(saplingActivation: 663150)
|
||||||
try darksideWallet.useDataset(from: txMainnetBlockUrl)
|
try darksideWallet.useDataset(from: txMainnetBlockUrl)
|
||||||
|
@ -42,6 +50,34 @@ class FakeChainBuilder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func buildChain(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, length: Int) throws {
|
||||||
|
|
||||||
|
try darksideWallet.reset(saplingActivation: birthday, branchID: "e9ff75a6" , chainName: "testnet")
|
||||||
|
|
||||||
|
try darksideWallet.useDataset(testnetCanopyStartBlock)
|
||||||
|
try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length)
|
||||||
|
try darksideWallet.stageTransaction(from: testnetPreCanopyTx, at: networkActivationHeight - ZcashSDK.EXPIRY_OFFSET)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static func buildChainPostActivationFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, length: Int) throws {
|
||||||
|
|
||||||
|
try darksideWallet.reset(saplingActivation: birthday, branchID: "e9ff75a6" , chainName: "testnet")
|
||||||
|
|
||||||
|
try darksideWallet.useDataset(testnetCanopyStartBlock)
|
||||||
|
try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length)
|
||||||
|
try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static func buildChainMixedFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, length: Int) throws {
|
||||||
|
try buildChain(darksideWallet: darksideWallet, birthday: birthday, networkActivationHeight: networkActivationHeight, length: length)
|
||||||
|
|
||||||
|
try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + ZcashSDK.EXPIRY_OFFSET)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static func buildTxUrl(for id: String) -> String {
|
static func buildTxUrl(for id: String) -> String {
|
||||||
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/transactions/recv/\(id).txt"
|
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/transactions/recv/\(id).txt"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue