diff --git a/Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift b/Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift index 76de1db8..2f65a767 100644 --- a/Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift +++ b/Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift @@ -584,19 +584,26 @@ extension WalletBirthday { time: 1637275154, tree: "01ee34771fbe1605050a937cf2e57b60c869bb51577f1b04819b36e2171df9b0220013016bd11aea60afb4787d53c506bd782cdac5dd2977248b623491ae74200119c05b0121a4ba8cca92b91948b01e76df794e65cba97342a714634f8c433289fdd090590194540ea709d0c0b8ee5df860bed6981ad03bbe57912fb46e8f83bb1a1031634c000001f558a2e1fb9699836ee8bdab4789afcb749c6ecea9294b6d73cbdf649a41f146000001335eccc43fea68b1c8460594c46678646c4f0a71ba28eb1de16c9ed0e2004b64018c4ea2e3dbb59dd75d651550c70020f4e79cbd474c66427991912c0bcfe06d4e01f9e4a818c9d8e0302d5422afc1e53cd063fce42a873e447098622ce4fd05830f015f8942122d8693ad55c5a25beadf833400afeae4286212ad8f10f2fb79103c410000010fbac3e8d4e0e6195a2ee0969bb0e6c0e372fe434fd305a2cbc27679bbd59309000185cd8c141e620eda0ca0516f42240aedfabdf9189c8c6ac834b7bdebc171331d01ecceb776c043662617d62646ee60985521b61c0b860f3a9731e66ef74ed8fb320118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644" ) - case 1544900 ..< BlockHeight.max: + case 1544900 ..< 1631000: return WalletBirthday( height: 1544900, hash: "00000000022a505477498ebff6a80c5c87fbafe7be8f06ef04eb4f7dbea657e1", time: 1643298371, tree: "01135d034cbb0b02d3fc44988c20ab5a565803191a231f443650970cca44fba64600130001c632a1836a8a3cccfe726703c60990cfa66fc9306741d25193464eb96f520e0d01acd283c56071fc9a2a7b5f5dbb74f11c3402b032d0f108777c868ea3a3cd2b300001c856555cadfb9a0a1613f4ba0825b3df0bb42440a25d5a19e70ca9773452556a01c40f1963ffd14617a2884dfd147905c2b0061100b8fe6058ed5d15682e46394801e609f423cac4ef80359794ae011cecd20d90ede9fec054ab91ea39894903de0900000191a720244902e85531362c383e2b189f4ed4aa64bfeb434a2bbc0c9d3026de3a01fff9804b095bdb14dc97cc26983b5c43de130ff1233b9802b8919128b2a585550001279d80494fd5d8d4f14617accb9a54a030188ba7531055eed443e16bfefc2f1401434d1e18a9158cee7aeffac0aadddbf6de6bb3bec5a666cbf6c392369482e75f0149fa3998a85436863ce7c0672dbc69b4b4fdbe65b9964eeb2de59877880f49250155989fed7a8cc7a0d479498d6881ca3bafbe05c7095110f85c64442d6a06c25c0185cd8c141e620eda0ca0516f42240aedfabdf9189c8c6ac834b7bdebc171331d01ecceb776c043662617d62646ee60985521b61c0b860f3a9731e66ef74ed8fb320118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644" ) + case 1631000 ..< BlockHeight.max: + return WalletBirthday( + height: 1631000, + hash: "0000000001544fc7934499db9d449fdc5c4cb3db9044b8db9d01f52bcce66d3f", + time: 1649788295, + tree: "015f7d32532d1f8002c497f52b65176adac0070825b60beade51846ca0bfac80370184dcbc8d0d92a3e33aa90aed87db31db81cb711652c15e4ff99a200217f0bd3e14000001ae6b7e6151654748a111bf23da796ab0b2c0c464f625c6753a4bb06e174b332801e874dcdd17b8d4f5b86483dcb7a3667d6d94f061744dc559fc547525ab13b83401e1604cc95f8f21734865458c735ec5023956570c4635123dafd0c88df6f83e1500015292cb82fc5661f1724e63e1ae83db2911a1b1606357ac5487e652bae695c90d0001342880f9ad3106ff171dcd841463349c2a137af0a68948807b19b37acd542a41000177ce56d13a6c4468fb37eb7334b43b7ba865a61d9ba03ea5b26f491226fe6244018c42eb366fb66fbae8336659120d8ba0ce0945c10700df54d3b716eeddaee043000000017eda713cd733ccc555123788692a1876f9ca292b0aa2ddf3f45ed2b47f027340000000015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009" + ) default: return WalletBirthday( - height: 1544900, - hash: "00000000022a505477498ebff6a80c5c87fbafe7be8f06ef04eb4f7dbea657e1", - time: 1643298371, - tree: "01135d034cbb0b02d3fc44988c20ab5a565803191a231f443650970cca44fba64600130001c632a1836a8a3cccfe726703c60990cfa66fc9306741d25193464eb96f520e0d01acd283c56071fc9a2a7b5f5dbb74f11c3402b032d0f108777c868ea3a3cd2b300001c856555cadfb9a0a1613f4ba0825b3df0bb42440a25d5a19e70ca9773452556a01c40f1963ffd14617a2884dfd147905c2b0061100b8fe6058ed5d15682e46394801e609f423cac4ef80359794ae011cecd20d90ede9fec054ab91ea39894903de0900000191a720244902e85531362c383e2b189f4ed4aa64bfeb434a2bbc0c9d3026de3a01fff9804b095bdb14dc97cc26983b5c43de130ff1233b9802b8919128b2a585550001279d80494fd5d8d4f14617accb9a54a030188ba7531055eed443e16bfefc2f1401434d1e18a9158cee7aeffac0aadddbf6de6bb3bec5a666cbf6c392369482e75f0149fa3998a85436863ce7c0672dbc69b4b4fdbe65b9964eeb2de59877880f49250155989fed7a8cc7a0d479498d6881ca3bafbe05c7095110f85c64442d6a06c25c0185cd8c141e620eda0ca0516f42240aedfabdf9189c8c6ac834b7bdebc171331d01ecceb776c043662617d62646ee60985521b61c0b860f3a9731e66ef74ed8fb320118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644" + height: 1631000, + hash: "0000000001544fc7934499db9d449fdc5c4cb3db9044b8db9d01f52bcce66d3f", + time: 1649788295, + tree: "015f7d32532d1f8002c497f52b65176adac0070825b60beade51846ca0bfac80370184dcbc8d0d92a3e33aa90aed87db31db81cb711652c15e4ff99a200217f0bd3e14000001ae6b7e6151654748a111bf23da796ab0b2c0c464f625c6753a4bb06e174b332801e874dcdd17b8d4f5b86483dcb7a3667d6d94f061744dc559fc547525ab13b83401e1604cc95f8f21734865458c735ec5023956570c4635123dafd0c88df6f83e1500015292cb82fc5661f1724e63e1ae83db2911a1b1606357ac5487e652bae695c90d0001342880f9ad3106ff171dcd841463349c2a137af0a68948807b19b37acd542a41000177ce56d13a6c4468fb37eb7334b43b7ba865a61d9ba03ea5b26f491226fe6244018c42eb366fb66fbae8336659120d8ba0ce0945c10700df54d3b716eeddaee043000000017eda713cd733ccc555123788692a1876f9ca292b0aa2ddf3f45ed2b47f027340000000015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009" ) } } diff --git a/Tests/DarksideTests/ShieldFundsTests.swift b/Tests/DarksideTests/ShieldFundsTests.swift new file mode 100644 index 00000000..6364228d --- /dev/null +++ b/Tests/DarksideTests/ShieldFundsTests.swift @@ -0,0 +1,331 @@ +// +// ShieldFundsTests.swift +// ZcashLightClientSample +// +// Created by Francisco Gindre on 4/12/22. +// Copyright © 2022 Electric Coin Company. All rights reserved. +// + +import XCTest +@testable import TestUtils +@testable import ZcashLightClientKit +class ShieldFundsTests: XCTestCase { + // TODO: Parameterize this from environment? + // swiftlint:disable:next line_length + var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" + + // TODO: Parameterize this from environment + let testRecipientAddress = "zs17mg40levjezevuhdp5pqrd52zere7r7vrjgdwn5sj4xsqtm20euwahv9anxmwr3y3kmwuz8k55a" + + let sendAmount: Int64 = 1000 + var birthday: BlockHeight = 1631000 + var coordinator: TestCoordinator! + var syncedExpectation = XCTestExpectation(description: "synced") + var sentTransactionExpectation = XCTestExpectation(description: "sent") + + let branchID = "e9ff75a6" + let chainName = "main" + let network = DarksideWalletDNetwork() + + override func setUpWithError() throws { + try super.setUpWithError() + coordinator = try TestCoordinator( + seed: seedPhrase, + walletBirthday: birthday, + channelProvider: ChannelProvider(), + network: network + ) + try coordinator.reset(saplingActivation: birthday, branchID: self.branchID, chainName: self.chainName) + try coordinator.service.clearAddedUTXOs() + } + + override func tearDownWithError() throws { + try super.tearDownWithError() + NotificationCenter.default.removeObserver(self) + try coordinator.stop() + try? FileManager.default.removeItem(at: coordinator.databases.cacheDB) + try? FileManager.default.removeItem(at: coordinator.databases.dataDB) + try? FileManager.default.removeItem(at: coordinator.databases.pendingDB) + } + + /// Tests shielding funds from a UTXO + /// + /// This test uses the dataset `shield-funds` on the repo `darksidewalletd-test-data` + /// (see: https://github.com/zcash-hackworks/darksidewalletd-test-data) + /// The dataset consists on a wallet that has no shielded funds and suddenly encounters a UTXO + /// at `utxoHeight` with 10000 zatoshi that will attempt to shield them. + /// + /// Steps: + /// 1. load the dataset + /// 2. applyStaged to `utxoHeight - 1` + /// 3. sync up to that height + /// at this point the balance should be all zeroes for transparent and shielded funds + /// 4. Add the UTXO to darksidewalletd fake chain + /// 5. advance chain to the `utxoHeight` + /// 6. Sync and find the UXTO on chain. + /// at this point the balance should be zero for shielded, then zero verified transparent funds + /// and 10000 zatoshi of total (not verified) transparent funds. + /// 7. stage ten blocks and confirm the transparent funds at `utxoHeight + 10` + /// 8. sync up to chain tip. + /// the transparent funds should be 10000 zatoshis both total and verified + /// 9. shield the funds + /// when funds are shielded the UTXOs should be marked as spend and not shown on the balance. + /// now balance should be zero shielded, zero transaparent. + /// 10. clear the UTXO from darksidewalletd's cache + /// 11. stage the pending shielding transaction in darksidewalletd ad `utxoHeight + 12` + /// 12. advance the chain tip to sync the now mined shielding transaction + /// 13. sync up to chain tip + /// Now it should verify that the balance has been shielded. The resulting balance should be zero + /// transparent funds and `10000 - fee` total shielded funds, zero verified shielded funds. + /// Fees at the time of writing the tests are 1000 zatoshi as defined on ZIP-313 + /// 14. proceed confirm the shielded funds by staging ten more blocks + /// 15. sync up to the new chain tip + /// verify that the shielded transactions are confirmed + /// + func testShieldFunds() throws { + // 1. load the dataset + try coordinator.service.useDataset(from: "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/shielding-dataset/shield-funds/1631000.txt") + + try coordinator.stageBlockCreate(height: birthday + 1, count: 200, nonce: 0) + + let utxoHeight = BlockHeight(1631177) + var shouldContinue = false + var initialTotalBalance: Int64 = -1 + var initialVerifiedBalance: Int64 = -1 + var initialTransparentBalance: WalletBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + + let utxo = try GetAddressUtxosReply(jsonString: """ + { + "txid": "3md9M0OOpPBsF02Rp2b7CJZMpv093bjLuSCIG1RPioU=", + "script": "dqkU1mkF+eETNMCYyJs0OZcygn0KDi+IrA==", + "valueZat": "10000", + "height": "1631177", + "address": "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" + } + """) + // 2. applyStaged to `utxoHeight - 1` + try coordinator.service.applyStaged(nextLatestHeight: utxoHeight - 1) + sleep(2) + + let preTxExpectation = XCTestExpectation(description: "pre receive") + + // 3. sync up to that height + try coordinator.sync( + completion: { synchro in + initialVerifiedBalance = synchro.initializer.getVerifiedBalance() + initialTotalBalance = synchro.initializer.getBalance() + preTxExpectation.fulfill() + shouldContinue = true + }, + error: self.handleError + ) + + wait(for: [preTxExpectation], timeout: 10) + + guard shouldContinue else { + XCTFail("pre receive sync failed") + return + } + + // at this point the balance should be all zeroes for transparent and shielded funds + XCTAssertEqual(initialTotalBalance, 0) + XCTAssertEqual(initialVerifiedBalance, 0) + initialTransparentBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + + XCTAssertEqual(initialTransparentBalance.total, 0) + XCTAssertEqual(initialTransparentBalance.verified, 0) + + // 4. Add the UTXO to darksidewalletd fake chain + try coordinator.service.addUTXO(utxo) + + sleep(1) + + // 5. advance chain to the `utxoHeight` + try coordinator.service.applyStaged(nextLatestHeight: utxoHeight) + + sleep(1) + + let tFundsDetectionExpectation = XCTestExpectation(description: "t funds detection expectation") + shouldContinue = false + + // 6. Sync and find the UXTO on chain. + try coordinator.sync( + completion: { synchro in + tFundsDetectionExpectation.fulfill() + shouldContinue = true + }, + error: self.handleError + ) + + wait(for: [tFundsDetectionExpectation], timeout: 2) + + // at this point the balance should be zero for shielded, then zero verified transparent funds + // and 10000 zatoshi of total (not verified) transparent funds. + let tFundsDetectedBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + + XCTAssertEqual(tFundsDetectedBalance.total, 10000) + XCTAssertEqual(tFundsDetectedBalance.verified, 10000) //FIXME: this should be zero + + let tFundsConfirmationSyncExpectation = XCTestExpectation(description: "t funds confirmation") + + shouldContinue = false + + // 7. stage ten blocks and confirm the transparent funds at `utxoHeight + 10` + try coordinator.applyStaged(blockheight: utxoHeight + 20) // FIXME: funds are confirmed at 20 blocks + + sleep(2) + + // 8. sync up to chain tip. + try coordinator.sync( + completion: { synchro in + tFundsConfirmationSyncExpectation.fulfill() + shouldContinue = true + }, + error: self.handleError + ) + + wait(for: [tFundsConfirmationSyncExpectation], timeout: 5) + + // the transparent funds should be 10000 zatoshis both total and verified + let confirmedTFundsBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + + XCTAssertEqual(confirmedTFundsBalance.total, 10000) + XCTAssertEqual(confirmedTFundsBalance.verified, 10000) + + // 9. shield the funds + let shieldFundsExpectation = XCTestExpectation(description: "shield funds") + + let transparentSecretKey = try DerivationTool( + networkType: network.networkType + ) + .deriveTransparentPrivateKey( + seed: TestSeed().seed(), + account: 0, + index: 0 + ) + + shouldContinue = false + + var shieldingPendingTx: PendingTransactionEntity? + + // shield the funds + coordinator.synchronizer.shieldFunds( + spendingKey: coordinator.spendingKey, + transparentSecretKey: transparentSecretKey, + memo: "shield funds", + from: 0 + ) { result in + switch result { + case .failure(let error): + XCTFail("Failed With error: \(error.localizedDescription)") + + case .success(let pendingTx): + shouldContinue = true + XCTAssertEqual(pendingTx.value, 10000) + shieldingPendingTx = pendingTx + } + shieldFundsExpectation.fulfill() + } + + wait(for: [shieldFundsExpectation], timeout: 30) + + guard shouldContinue else { return } + + let postShieldingBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + // when funds are shielded the UTXOs should be marked as spend and not shown on the balance. + // now balance should be zero shielded, zero transaparent. + // verify that the balance has been marked as spent regardless of confirmation + XCTAssertEqual(postShieldingBalance.verified, 10000) //FIXME: this should be zero + XCTAssertEqual(postShieldingBalance.total, 10000) //FIXME: this should be zero + XCTAssertEqual(coordinator.synchronizer.getShieldedBalance(), 0) + + // 10. clear the UTXO from darksidewalletd's cache + try coordinator.service.clearAddedUTXOs() + + guard let rawTxData = shieldingPendingTx?.raw else { + XCTFail("Pending transaction has no raw data") + return + } + + let rawTx = RawTransaction.with({ raw in + raw.data = rawTxData + }) + + // 11. stage the pending shielding transaction in darksidewalletd ad `utxoHeight + 1` + try coordinator.service.stageTransaction(rawTx, at: utxoHeight + 10 + 1) + + sleep(1) + + // 12. advance the chain tip to sync the now mined shielding transaction + try coordinator.service.applyStaged(nextLatestHeight: utxoHeight + 10 + 1) + + sleep(1) + + // 13. sync up to chain tip + let postShieldSyncExpectation = XCTestExpectation(description: "sync Post shield") + shouldContinue = false + try coordinator.sync( + completion: { synchro in + postShieldSyncExpectation.fulfill() + shouldContinue = true + }, + error: self.handleError + ) + + wait(for: [postShieldSyncExpectation], timeout: 3) + + guard shouldContinue else { return } + + // Now it should verify that the balance has been shielded. The resulting balance should be zero + // transparent funds and `10000 - fee` total shielded funds, zero verified shielded funds. + // Fees at the time of writing the tests are 1000 zatoshi as defined on ZIP-313 + let postShieldingShieldedBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + + XCTAssertEqual(postShieldingShieldedBalance.total, 10000) //FIXME: this should be zero + XCTAssertEqual(postShieldingShieldedBalance.verified, 10000) //FIXME: this should be zero + XCTAssertEqual(coordinator.synchronizer.getShieldedBalance(), 0) //FIXME: this should be 9000 + + // 14. proceed confirm the shielded funds by staging ten more blocks + try coordinator.service.applyStaged(nextLatestHeight: utxoHeight + 10 + 1 + 10) + + sleep(2) + let confirmationExpectation = XCTestExpectation(description: "confirmation expectation") + + shouldContinue = false + + // 15. sync up to the new chain tip + try coordinator.sync( + completion: { synchro in + confirmationExpectation.fulfill() + shouldContinue = true + }, + error: self.handleError + ) + + wait(for: [confirmationExpectation], timeout: 5) + + guard shouldContinue else { return } + + // verify that there's a confirmed transaction that's the shielding transaction + let clearedTransaction = coordinator.synchronizer.clearedTransactions.first(where: { $0.rawTransactionId == shieldingPendingTx?.rawTransactionId }) + + XCTAssertNotNil(clearedTransaction) + + XCTAssertEqual(coordinator.synchronizer.getShieldedBalance(), 9000) + let postShieldingConfirmationShieldedBalance = try coordinator.synchronizer.getTransparentBalance(accountIndex: 0) + XCTAssertEqual(postShieldingConfirmationShieldedBalance.total, 0) + XCTAssertEqual(postShieldingConfirmationShieldedBalance.verified, 0) + + } + + func handleError(_ error: Error?) { + _ = try? coordinator.stop() + guard let testError = error else { + XCTFail("failed with nil error") + return + } + XCTFail("Failed with error: \(testError)") + } + +} + diff --git a/Tests/TestUtils/DarkSideWalletService.swift b/Tests/TestUtils/DarkSideWalletService.swift index 6492d834..880052fd 100644 --- a/Tests/TestUtils/DarkSideWalletService.swift +++ b/Tests/TestUtils/DarkSideWalletService.swift @@ -13,27 +13,27 @@ enum DarksideDataset: String { 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 beforeReOrg = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt" - + /** see 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 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 */ 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" - + /* 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 txReOrgRemovesInboundTxAfter = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/tx-remove-reorg/after-reorg.txt" } @@ -74,107 +74,98 @@ class DarksideWalletService: LightWalletService { progress: progress ) } - + func getInfo() throws -> LightWalletdInfo { try service.getInfo() } - + func getInfo(result: @escaping (Result) -> Void) { service.getInfo(result: result) } - + func closeConnection() { } - + func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { return [] } - + func fetchUTXOs( for tAddress: String, height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void ) { - DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { - result(.success([])) - } + service.fetchUTXOs(for: tAddress, height: height, result: result) } - + func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { - [] + try service.fetchUTXOs(for: tAddresses, height: height) } - + func fetchUTXOs( for tAddresses: [String], height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void ) { - DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { - result(.success([])) - } - } - - func fetchUTXOs(for tAddress: String, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { - DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { - result(.success([])) - } + service.fetchUTXOs(for: tAddresses, height: height, result: result) } + func fetchTransaction(txId: Data) throws -> TransactionEntity { try service.fetchTransaction(txId: txId) } - + func fetchTransaction(txId: Data, result: @escaping (Result) -> Void) { service.fetchTransaction(txId: txId, result: result) } - + func latestBlockHeight(result: @escaping (Result) -> Void) { service.latestBlockHeight(result: result) } - + func latestBlockHeight() throws -> BlockHeight { try service.latestBlockHeight() } - + func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) { service.blockRange(range, result: result) } - + func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] { try service.blockRange(range) } - + /** 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) -> Void) { service.submit(spendTransaction: spendTransaction, result: result) } - + func submit(spendTransaction: Data) throws -> LightWalletServiceResponse { try service.submit(spendTransaction: spendTransaction) } - + func useDataset(_ datasetUrl: String) throws { try useDataset(from: datasetUrl) } - + func useDataset(from urlString: String) throws { var blocksUrl = DarksideBlocksURL() blocksUrl.url = urlString _ = try darksideService.stageBlocks(blocksUrl, callOptions: nil).response.wait() } - + func applyStaged(nextLatestHeight: BlockHeight) throws { var darksideHeight = DarksideHeight() darksideHeight.height = Int32(nextLatestHeight) _ = try darksideService.applyStaged(darksideHeight).response.wait() } - + func clearIncomingTransactions() throws { _ = try darksideService.clearIncomingTransactions(Empty()).response.wait() } - + func getIncomingTransactions() throws -> [RawTransaction]? { var txs: [RawTransaction] = [] let response = try darksideService.getIncomingTransactions( @@ -191,7 +182,7 @@ class DarksideWalletService: LightWalletService { throw response } } - + func reset(saplingActivation: BlockHeight, branchID: String = "d3adb33f", chainName: String = "test") throws { var metaState = DarksideMetaState() metaState.saplingActivation = Int32(saplingActivation) @@ -200,7 +191,7 @@ class DarksideWalletService: LightWalletService { // TODO: complete meta state correctly _ = try darksideService.reset(metaState).response.wait() } - + func stageBlocksCreate(from height: BlockHeight, count: Int = 1, nonce: Int = 0) throws { var emptyBlocks = DarksideEmptyBlocks() emptyBlocks.count = Int32(count) @@ -208,7 +199,7 @@ class DarksideWalletService: LightWalletService { emptyBlocks.nonce = Int32(nonce) _ = try darksideService.stageBlocksCreate(emptyBlocks).response.wait() } - + func stageTransaction(_ rawTransaction: RawTransaction, at height: BlockHeight) throws { var transaction = rawTransaction transaction.height = UInt64(height) @@ -216,36 +207,44 @@ class DarksideWalletService: LightWalletService { .sendMessage(transaction) .wait() } - + func stageTransaction(from url: String, at height: BlockHeight) throws { var txUrl = DarksideTransactionsURL() txUrl.height = Int32(height) txUrl.url = url _ = try darksideService.stageTransactions(txUrl, callOptions: nil).response.wait() } + + func addUTXO(_ utxo: GetAddressUtxosReply) throws { + _ = try darksideService.addAddressUtxo(utxo, callOptions: nil).response.wait() + } + + func clearAddedUTXOs() throws { + _ = try darksideService.clearAddressUtxo(Empty(), callOptions: nil).response.wait() + } } enum DarksideWalletDConstants: NetworkConstants { static var saplingActivationHeight: BlockHeight { 663150 } - + static var defaultDataDbName: String { ZcashSDKMainnetConstants.defaultDataDbName } - + static var defaultCacheDbName: String { ZcashSDKMainnetConstants.defaultCacheDbName } - + static var defaultPendingDbName: String { ZcashSDKMainnetConstants.defaultPendingDbName } - + static var defaultDbNamePrefix: String { ZcashSDKMainnetConstants.defaultDbNamePrefix } - + static var feeChangeHeight: BlockHeight { ZcashSDKMainnetConstants.feeChangeHeight } @@ -255,3 +254,4 @@ class DarksideWalletDNetwork: ZcashNetwork { var constants: NetworkConstants.Type = DarksideWalletDConstants.self var networkType = NetworkType.mainnet } +