draft

[#1165] Step 1 - Download note commitment tree data from lightwalletd

- code cleanup after draft

[#1165] Step 1 - Download note commitment tree data from lightwalletd

- UpdateSubtreeRootsAction added, ensuring the roots are downloaded and stored in the DB

[#1165] Step 1 - Download note commitment tree data from lightwalletd

- added ZcashError for putSaplingSubtreeRoots failure
- cleaned up action

[#1165] Step 1 - Download note commitment tree data from lightwalletd

- demo app config temporarily updated to Nighthawk server

[#1165] Step 1 - Download note commitment tree data from lightwalletd

- file header updated

[#1165] Step 1 - Download note commitment tree data from lightwalletd (#1174)

- demo app config cleaned up

[#1165] Step 1 - Download note commitment tree data from lightwalletd (#1174)

- offline tests fixed
This commit is contained in:
Lukas Korba 2023-07-31 08:51:04 +02:00
parent d446c6d336
commit 7694b04d42
16 changed files with 263 additions and 47 deletions

View File

@ -18,14 +18,14 @@ enum DemoAppConfig {
let seed: [UInt8] let seed: [UInt8]
} }
static let host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co" static let host = ZcashSDK.isMainnet ? "mainnet.lightwalletd.com" : "testnet.lightwalletd.com"
static let port: Int = 9067 static let port: Int = 9067
static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000 static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """ static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
""") """)
static let otherSynchronizers: [SynchronizerInitData] = [ static let otherSynchronizers: [SynchronizerInitData] = [
SynchronizerInitData( SynchronizerInitData(
alias: .custom("alt-sync-1"), alias: .custom("alt-sync-1"),

View File

@ -34,6 +34,7 @@ enum CBPState: CaseIterable {
case idle case idle
case migrateLegacyCacheDB case migrateLegacyCacheDB
case validateServer case validateServer
case updateSubtreeRoots
case computeSyncControlData case computeSyncControlData
case download case download
case scan case scan

View File

@ -0,0 +1,68 @@
//
// UpdateSubtreeRootsAction.swift
//
//
// Created by Lukas Korba on 01.08.2023.
//
import Foundation
final class UpdateSubtreeRootsAction {
let rustBackend: ZcashRustBackendWelding
let service: LightWalletService
let logger: Logger
init(container: DIContainer) {
service = container.resolve(LightWalletService.self)
rustBackend = container.resolve(ZcashRustBackendWelding.self)
logger = container.resolve(Logger.self)
}
}
extension UpdateSubtreeRootsAction: Action {
var removeBlocksCacheWhenFailed: Bool { false }
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
var request = GetSubtreeRootsArg()
request.shieldedProtocol = .sapling
request.maxEntries = 65536
logger.info("Attempt to get subtree roots, this may fail because lightwalletd may not support DAG sync.")
let stream = service.getSubtreeRoots(request)
var roots: [SubtreeRoot] = []
var err: Error?
do {
for try await subtreeRoot in stream {
roots.append(subtreeRoot)
}
} catch {
logger.debug("getSubtreeRoots failed with error \(error.localizedDescription)")
err = error
}
// In case of error, the lightwalletd doesn't support DAG sync -> switching to linear sync.
// Likewise, no subtree roots results in switching to linear sync.
if err != nil || roots.isEmpty {
logger.info("DAG sync is not possible, switching to linear sync.")
await context.update(state: .computeSyncControlData)
} else {
logger.info("Sapling tree has \(roots.count) subtrees")
do {
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
// TODO: [#1167] Switching back to linear sync for now before step 3 & 4 are implemented
// https://github.com/zcash/ZcashLightClientKit/issues/1167
await context.update(state: .computeSyncControlData)
} catch {
logger.debug("putSaplingSubtreeRoots failed with error \(error.localizedDescription)")
throw ZcashError.compactBlockProcessorPutSaplingSubtreeRoots(error)
}
}
return context
}
func stop() async { }
}

View File

@ -52,7 +52,7 @@ extension ValidateServerAction: Action {
throw ZcashError.compactBlockProcessorWrongConsensusBranchId(localBranch, remoteBranchID) throw ZcashError.compactBlockProcessorWrongConsensusBranchId(localBranch, remoteBranchID)
} }
await context.update(state: .computeSyncControlData) await context.update(state: .updateSubtreeRoots)
return context return context
} }

View File

@ -214,6 +214,8 @@ actor CompactBlockProcessor {
action = MigrateLegacyCacheDBAction(container: container, configProvider: configProvider) action = MigrateLegacyCacheDBAction(container: container, configProvider: configProvider)
case .validateServer: case .validateServer:
action = ValidateServerAction(container: container, configProvider: configProvider) action = ValidateServerAction(container: container, configProvider: configProvider)
case .updateSubtreeRoots:
action = UpdateSubtreeRootsAction(container: container)
case .computeSyncControlData: case .computeSyncControlData:
action = ComputeSyncControlDataAction(container: container, configProvider: configProvider) action = ComputeSyncControlDataAction(container: container, configProvider: configProvider)
case .download: case .download:
@ -585,6 +587,8 @@ extension CompactBlockProcessor {
break break
case .validateServer: case .validateServer:
break break
case .updateSubtreeRoots:
break
case .computeSyncControlData: case .computeSyncControlData:
break break
case .download: case .download:

View File

@ -58,6 +58,9 @@ public enum ZcashError: Equatable, Error {
/// LightWalletService.blockStream failed. /// LightWalletService.blockStream failed.
/// ZSRVC0000 /// ZSRVC0000
case serviceBlockStreamFailed(_ error: LightWalletServiceError) case serviceBlockStreamFailed(_ error: LightWalletServiceError)
/// LightWalletService.getSubtreeRoots failed.
/// ZSRVC0009
case serviceSubtreeRootsStreamFailed(_ error: LightWalletServiceError)
/// SimpleConnectionProvider init of Connection failed. /// SimpleConnectionProvider init of Connection failed.
/// ZSCPC0001 /// ZSCPC0001
case simpleConnectionProvider(_ error: Error) case simpleConnectionProvider(_ error: Error)
@ -277,18 +280,22 @@ public enum ZcashError: Equatable, Error {
/// ZRUST0045 /// ZRUST0045
case rustGetTransparentReceiverInvalidReceiver case rustGetTransparentReceiverInvalidReceiver
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots /// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots
/// sourcery: code="ZRUST0046"
/// ZRUST0046 /// ZRUST0046
case rustPutSaplingSubtreeRootsAllocationProblem case rustPutSaplingSubtreeRootsAllocationProblem
/// Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots /// Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// sourcery: code="ZRUST0047"
/// ZRUST0047 /// ZRUST0047
case rustPutSaplingSubtreeRoots(_ rustError: String) case rustPutSaplingSubtreeRoots(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.updateChainTip /// Error from rust layer when calling ZcashRustBackend.updateChainTip
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// sourcery: code="ZRUST0048"
/// ZRUST0048 /// ZRUST0048
case rustUpdateChainTip(_ rustError: String) case rustUpdateChainTip(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.suggestScanRanges /// Error from rust layer when calling ZcashRustBackend.suggestScanRanges
/// - `rustError` contains error generated by the rust layer. /// - `rustError` contains error generated by the rust layer.
/// sourcery: code="ZRUST0049"
/// ZRUST0049 /// ZRUST0049
case rustSuggestScanRanges(_ rustError: String) case rustSuggestScanRanges(_ rustError: String)
/// Invalid transaction ID length when calling ZcashRustBackend.getMemo /// Invalid transaction ID length when calling ZcashRustBackend.getMemo
@ -530,6 +537,9 @@ public enum ZcashError: Equatable, Error {
/// Rewind of DownloadBlockAction failed as no action is possible to unwrapp. /// Rewind of DownloadBlockAction failed as no action is possible to unwrapp.
/// ZCBPEO0018 /// ZCBPEO0018
case compactBlockProcessorDownloadBlockActionRewind case compactBlockProcessorDownloadBlockActionRewind
/// Put sapling subtree roots to the DB failed.
/// ZCBPEO0019
case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error)
/// The synchronizer is unprepared. /// The synchronizer is unprepared.
/// ZSYNCO0001 /// ZSYNCO0001
case synchronizerNotPrepared case synchronizerNotPrepared
@ -566,6 +576,7 @@ public enum ZcashError: Equatable, Error {
case .serviceFetchTransactionFailed: return "LightWalletService.fetchTransaction failed." case .serviceFetchTransactionFailed: return "LightWalletService.fetchTransaction failed."
case .serviceFetchUTXOsFailed: return "LightWalletService.fetchUTXOs failed." case .serviceFetchUTXOsFailed: return "LightWalletService.fetchUTXOs failed."
case .serviceBlockStreamFailed: return "LightWalletService.blockStream failed." case .serviceBlockStreamFailed: return "LightWalletService.blockStream failed."
case .serviceSubtreeRootsStreamFailed: return "LightWalletService.getSubtreeRoots failed."
case .simpleConnectionProvider: return "SimpleConnectionProvider init of Connection failed." case .simpleConnectionProvider: return "SimpleConnectionProvider init of Connection failed."
case .saplingParamsInvalidSpendParams: return "Downloaded file with sapling spending parameters isn't valid." case .saplingParamsInvalidSpendParams: return "Downloaded file with sapling spending parameters isn't valid."
case .saplingParamsInvalidOutputParams: return "Downloaded file with sapling output parameters isn't valid." case .saplingParamsInvalidOutputParams: return "Downloaded file with sapling output parameters isn't valid."
@ -623,7 +634,7 @@ public enum ZcashError: Equatable, Error {
case .rustGetSaplingReceiverInvalidReceiver: return "Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver" case .rustGetSaplingReceiverInvalidReceiver: return "Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver"
case .rustGetTransparentReceiverInvalidAddress: return "Error from rust layer when calling ZcashRustBackend.getTransparentReceiver" case .rustGetTransparentReceiverInvalidAddress: return "Error from rust layer when calling ZcashRustBackend.getTransparentReceiver"
case .rustGetTransparentReceiverInvalidReceiver: return "Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver" case .rustGetTransparentReceiverInvalidReceiver: return "Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver"
case .rustPutSaplingSubtreeRootsAllocationProblem: return "Unable to allocate memory required to store subtree roots when calling ZcashRustBackend.putSaplingSubtreeRoots" case .rustPutSaplingSubtreeRootsAllocationProblem: return "Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots"
case .rustPutSaplingSubtreeRoots: return "Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots" case .rustPutSaplingSubtreeRoots: return "Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots"
case .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip" case .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip"
case .rustSuggestScanRanges: return "Error from rust layer when calling ZcashRustBackend.suggestScanRanges" case .rustSuggestScanRanges: return "Error from rust layer when calling ZcashRustBackend.suggestScanRanges"
@ -703,6 +714,7 @@ public enum ZcashError: Equatable, Error {
case .compactBlockProcessorChainName: return "Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error." case .compactBlockProcessorChainName: return "Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error."
case .compactBlockProcessorConsensusBranchID: return "Consensus BranchIDs don't match this is probably an API or programming error." case .compactBlockProcessorConsensusBranchID: return "Consensus BranchIDs don't match this is probably an API or programming error."
case .compactBlockProcessorDownloadBlockActionRewind: return "Rewind of DownloadBlockAction failed as no action is possible to unwrapp." case .compactBlockProcessorDownloadBlockActionRewind: return "Rewind of DownloadBlockAction failed as no action is possible to unwrapp."
case .compactBlockProcessorPutSaplingSubtreeRoots: return "Put sapling subtree roots to the DB failed."
case .synchronizerNotPrepared: return "The synchronizer is unprepared." case .synchronizerNotPrepared: return "The synchronizer is unprepared."
case .synchronizerSendMemoToTransparentAddress: return "Memos can't be sent to transparent addresses." case .synchronizerSendMemoToTransparentAddress: return "Memos can't be sent to transparent addresses."
case .synchronizerShieldFundsInsuficientTransparentFunds: return "There is not enough transparent funds to cover fee for the shielding." case .synchronizerShieldFundsInsuficientTransparentFunds: return "There is not enough transparent funds to cover fee for the shielding."
@ -729,6 +741,7 @@ public enum ZcashError: Equatable, Error {
case .serviceFetchTransactionFailed: return .serviceFetchTransactionFailed case .serviceFetchTransactionFailed: return .serviceFetchTransactionFailed
case .serviceFetchUTXOsFailed: return .serviceFetchUTXOsFailed case .serviceFetchUTXOsFailed: return .serviceFetchUTXOsFailed
case .serviceBlockStreamFailed: return .serviceBlockStreamFailed case .serviceBlockStreamFailed: return .serviceBlockStreamFailed
case .serviceSubtreeRootsStreamFailed: return .serviceSubtreeRootsStreamFailed
case .simpleConnectionProvider: return .simpleConnectionProvider case .simpleConnectionProvider: return .simpleConnectionProvider
case .saplingParamsInvalidSpendParams: return .saplingParamsInvalidSpendParams case .saplingParamsInvalidSpendParams: return .saplingParamsInvalidSpendParams
case .saplingParamsInvalidOutputParams: return .saplingParamsInvalidOutputParams case .saplingParamsInvalidOutputParams: return .saplingParamsInvalidOutputParams
@ -866,6 +879,7 @@ public enum ZcashError: Equatable, Error {
case .compactBlockProcessorChainName: return .compactBlockProcessorChainName case .compactBlockProcessorChainName: return .compactBlockProcessorChainName
case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID
case .compactBlockProcessorDownloadBlockActionRewind: return .compactBlockProcessorDownloadBlockActionRewind case .compactBlockProcessorDownloadBlockActionRewind: return .compactBlockProcessorDownloadBlockActionRewind
case .compactBlockProcessorPutSaplingSubtreeRoots: return .compactBlockProcessorPutSaplingSubtreeRoots
case .synchronizerNotPrepared: return .synchronizerNotPrepared case .synchronizerNotPrepared: return .synchronizerNotPrepared
case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress
case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds

View File

@ -39,6 +39,8 @@ public enum ZcashErrorCode: String {
case serviceFetchUTXOsFailed = "ZSRVC0008" case serviceFetchUTXOsFailed = "ZSRVC0008"
/// LightWalletService.blockStream failed. /// LightWalletService.blockStream failed.
case serviceBlockStreamFailed = "ZSRVC0000" case serviceBlockStreamFailed = "ZSRVC0000"
/// LightWalletService.getSubtreeRoots failed.
case serviceSubtreeRootsStreamFailed = "ZSRVC0009"
/// SimpleConnectionProvider init of Connection failed. /// SimpleConnectionProvider init of Connection failed.
case simpleConnectionProvider = "ZSCPC0001" case simpleConnectionProvider = "ZSCPC0001"
/// Downloaded file with sapling spending parameters isn't valid. /// Downloaded file with sapling spending parameters isn't valid.
@ -313,6 +315,8 @@ public enum ZcashErrorCode: String {
case compactBlockProcessorConsensusBranchID = "ZCBPEO0017" case compactBlockProcessorConsensusBranchID = "ZCBPEO0017"
/// Rewind of DownloadBlockAction failed as no action is possible to unwrapp. /// Rewind of DownloadBlockAction failed as no action is possible to unwrapp.
case compactBlockProcessorDownloadBlockActionRewind = "ZCBPEO0018" case compactBlockProcessorDownloadBlockActionRewind = "ZCBPEO0018"
/// Put sapling subtree roots to the DB failed.
case compactBlockProcessorPutSaplingSubtreeRoots = "ZCBPEO0019"
/// The synchronizer is unprepared. /// The synchronizer is unprepared.
case synchronizerNotPrepared = "ZSYNCO0001" case synchronizerNotPrepared = "ZSYNCO0001"
/// Memos can't be sent to transparent addresses. /// Memos can't be sent to transparent addresses.

View File

@ -77,6 +77,9 @@ enum ZcashErrorDefinition {
/// LightWalletService.blockStream failed. /// LightWalletService.blockStream failed.
// sourcery: code="ZSRVC0000" // sourcery: code="ZSRVC0000"
case serviceBlockStreamFailed(_ error: LightWalletServiceError) case serviceBlockStreamFailed(_ error: LightWalletServiceError)
/// LightWalletService.getSubtreeRoots failed.
// sourcery: code="ZSRVC0009"
case serviceSubtreeRootsStreamFailed(_ error: LightWalletServiceError)
// MARK: SQLite connection // MARK: SQLite connection
@ -307,6 +310,21 @@ enum ZcashErrorDefinition {
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver /// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver
// sourcery: code="ZRUST0045" // sourcery: code="ZRUST0045"
case rustGetTransparentReceiverInvalidReceiver case rustGetTransparentReceiverInvalidReceiver
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots
/// sourcery: code="ZRUST0046"
case rustPutSaplingSubtreeRootsAllocationProblem
/// Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots
/// - `rustError` contains error generated by the rust layer.
/// sourcery: code="ZRUST0047"
case rustPutSaplingSubtreeRoots(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.updateChainTip
/// - `rustError` contains error generated by the rust layer.
/// sourcery: code="ZRUST0048"
case rustUpdateChainTip(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.suggestScanRanges
/// - `rustError` contains error generated by the rust layer.
/// sourcery: code="ZRUST0049"
case rustSuggestScanRanges(_ rustError: String)
// MARK: - Account DAO // MARK: - Account DAO
@ -592,6 +610,9 @@ enum ZcashErrorDefinition {
/// Rewind of DownloadBlockAction failed as no action is possible to unwrapp. /// Rewind of DownloadBlockAction failed as no action is possible to unwrapp.
// sourcery: code="ZCBPEO0018" // sourcery: code="ZCBPEO0018"
case compactBlockProcessorDownloadBlockActionRewind case compactBlockProcessorDownloadBlockActionRewind
/// Put sapling subtree roots to the DB failed.
// sourcery: code="ZCBPEO0019"
case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error)
// MARK: - SDKSynchronizer // MARK: - SDKSynchronizer

View File

@ -262,6 +262,21 @@ extension LightWalletGRPCService: LightWalletService {
} }
} }
} }
func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream<SubtreeRoot, Error> {
let stream = compactTxStreamer.getSubtreeRoots(request)
var iterator = stream.makeAsyncIterator()
return AsyncThrowingStream() {
do {
guard let subtreeRoot = try await iterator.next() else { return nil }
return subtreeRoot
} catch {
let serviceError = error.mapToServiceError()
throw ZcashError.serviceSubtreeRootsStreamFailed(serviceError)
}
}
}
func closeConnection() { func closeConnection() {
_ = channel.close() _ = channel.close()

View File

@ -193,4 +193,11 @@ protocol LightWalletService: AnyObject {
) -> AsyncThrowingStream<ZcashCompactBlock, Error> ) -> AsyncThrowingStream<ZcashCompactBlock, Error>
func closeConnection() func closeConnection()
/// Returns a stream of information about roots of subtrees of the Sapling and Orchard
/// note commitment trees.
///
/// - Parameters:
/// - request: Request to send to GetSubtreeRoots.
func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream<SubtreeRoot, Error>
} }

View File

@ -166,6 +166,10 @@ protocol ZcashRustBackendWelding {
/// - Throws: `rustRewindCacheToHeight` if rust layer returns error. /// - Throws: `rustRewindCacheToHeight` if rust layer returns error.
func rewindCacheToHeight(height: Int32) async throws func rewindCacheToHeight(height: Int32) async throws
func putSaplingSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws
func updateChainTip(height: Int32) async throws
/// Returns a list of suggested scan ranges based upon the current wallet state. /// Returns a list of suggested scan ranges based upon the current wallet state.
/// ///
/// This method should only be used in cases where the `CompactBlock` data that will be /// This method should only be used in cases where the `CompactBlock` data that will be

View File

@ -141,7 +141,7 @@ public class SDKSynchronizer: Synchronizer {
if case .seedRequired = try await self.initializer.initialize(with: seed, viewingKeys: viewingKeys, walletBirthday: walletBirthday) { if case .seedRequired = try await self.initializer.initialize(with: seed, viewingKeys: viewingKeys, walletBirthday: walletBirthday) {
return .seedRequired return .seedRequired
} }
await latestBlocksDataProvider.updateWalletBirthday(initializer.walletBirthday) await latestBlocksDataProvider.updateWalletBirthday(initializer.walletBirthday)
await latestBlocksDataProvider.updateScannedData() await latestBlocksDataProvider.updateScannedData()

View File

@ -31,8 +31,8 @@ final class ValidateServerActionTests: ZcashTestCase {
let nextContext = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in } let nextContext = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
let nextState = await nextContext.state let nextState = await nextContext.state
XCTAssertTrue( XCTAssertTrue(
nextState == .computeSyncControlData, nextState == .updateSubtreeRoots,
"nextContext after .validateServer is expected to be .computeSyncControlData but received \(nextState)" "nextContext after .validateServer is expected to be .updateSubtreeRoots but received \(nextState)"
) )
} catch { } catch {
XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)") XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)")

View File

@ -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 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"
} }
@ -45,34 +45,34 @@ class DarksideWalletService: LightWalletService {
var channel: Channel var channel: Channel
var service: LightWalletService var service: LightWalletService
var darksideService: DarksideStreamerNIOClient var darksideService: DarksideStreamerNIOClient
init(endpoint: LightWalletEndpoint) { init(endpoint: LightWalletEndpoint) {
self.channel = ChannelProvider().channel(endpoint: endpoint) self.channel = ChannelProvider().channel(endpoint: endpoint)
self.service = LightWalletServiceFactory(endpoint: endpoint).make() self.service = LightWalletServiceFactory(endpoint: endpoint).make()
self.darksideService = DarksideStreamerNIOClient(channel: channel) self.darksideService = DarksideStreamerNIOClient(channel: channel)
} }
init(endpoint: LightWalletEndpoint, service: LightWalletService) { init(endpoint: LightWalletEndpoint, service: LightWalletService) {
self.channel = ChannelProvider().channel(endpoint: endpoint) self.channel = ChannelProvider().channel(endpoint: endpoint)
self.darksideService = DarksideStreamerNIOClient(channel: channel) self.darksideService = DarksideStreamerNIOClient(channel: channel)
self.service = service self.service = service
} }
convenience init() { convenience init() {
self.init(endpoint: LightWalletEndpointBuilder.default) self.init(endpoint: LightWalletEndpointBuilder.default)
} }
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> { func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
service.blockStream(startHeight: startHeight, endHeight: endHeight) service.blockStream(startHeight: startHeight, endHeight: endHeight)
} }
func latestBlock() async throws -> ZcashLightClientKit.BlockID { func latestBlock() async throws -> ZcashLightClientKit.BlockID {
throw "Not mocked" throw "Not mocked"
} }
func closeConnection() { func closeConnection() {
} }
func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> { func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
service.fetchUTXOs(for: tAddress, height: height) service.fetchUTXOs(for: tAddress, height: height)
} }
@ -80,40 +80,40 @@ class DarksideWalletService: LightWalletService {
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> { func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
service.fetchUTXOs(for: tAddresses, height: height) service.fetchUTXOs(for: tAddresses, height: height)
} }
func latestBlockHeight() async throws -> BlockHeight { func latestBlockHeight() async throws -> BlockHeight {
try await service.latestBlockHeight() try await service.latestBlockHeight()
} }
func useDataset(_ datasetUrl: String) throws { func useDataset(_ datasetUrl: String) throws {
try useDataset(from: datasetUrl) try useDataset(from: datasetUrl)
} }
func useDataset(from urlString: String) throws { func useDataset(from urlString: String) throws {
var blocksUrl = DarksideBlocksURL() var blocksUrl = DarksideBlocksURL()
blocksUrl.url = urlString blocksUrl.url = urlString
_ = try darksideService.stageBlocks(blocksUrl, callOptions: nil).response.wait() _ = try darksideService.stageBlocks(blocksUrl, callOptions: nil).response.wait()
} }
func applyStaged(nextLatestHeight: BlockHeight) throws { func applyStaged(nextLatestHeight: BlockHeight) throws {
var darksideHeight = DarksideHeight() var darksideHeight = DarksideHeight()
darksideHeight.height = Int32(nextLatestHeight) darksideHeight.height = Int32(nextLatestHeight)
_ = try darksideService.applyStaged(darksideHeight).response.wait() _ = try darksideService.applyStaged(darksideHeight).response.wait()
} }
func clearIncomingTransactions() throws { func clearIncomingTransactions() throws {
_ = try darksideService.clearIncomingTransactions(Empty()).response.wait() _ = try darksideService.clearIncomingTransactions(Empty()).response.wait()
} }
func getIncomingTransactions() throws -> [RawTransaction]? { func getIncomingTransactions() throws -> [RawTransaction]? {
var txs: [RawTransaction] = [] var txs: [RawTransaction] = []
let response = try darksideService.getIncomingTransactions( let response = try darksideService.getIncomingTransactions(
Empty(), Empty(),
handler: { txs.append($0) } handler: { txs.append($0) }
) )
.status .status
.wait() .wait()
switch response.code { switch response.code {
case .ok: case .ok:
return !txs.isEmpty ? txs : nil return !txs.isEmpty ? txs : nil
@ -121,7 +121,7 @@ class DarksideWalletService: LightWalletService {
throw response throw response
} }
} }
func reset(saplingActivation: BlockHeight, branchID: String = "d3adb33f", chainName: String = "test") 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)
@ -130,7 +130,7 @@ class DarksideWalletService: LightWalletService {
// TODO: [#718] complete meta state correctly, https://github.com/zcash/ZcashLightClientKit/issues/718 // TODO: [#718] complete meta state correctly, https://github.com/zcash/ZcashLightClientKit/issues/718
_ = try darksideService.reset(metaState).response.wait() _ = try darksideService.reset(metaState).response.wait()
} }
func stageBlocksCreate(from height: BlockHeight, count: Int = 1, nonce: Int = 0) throws { func stageBlocksCreate(from height: BlockHeight, count: Int = 1, nonce: Int = 0) throws {
var emptyBlocks = DarksideEmptyBlocks() var emptyBlocks = DarksideEmptyBlocks()
emptyBlocks.count = Int32(count) emptyBlocks.count = Int32(count)
@ -138,7 +138,7 @@ class DarksideWalletService: LightWalletService {
emptyBlocks.nonce = Int32(nonce) emptyBlocks.nonce = Int32(nonce)
_ = try darksideService.stageBlocksCreate(emptyBlocks).response.wait() _ = try darksideService.stageBlocksCreate(emptyBlocks).response.wait()
} }
func stageTransaction(_ rawTransaction: RawTransaction, at height: BlockHeight) throws { func stageTransaction(_ rawTransaction: RawTransaction, at height: BlockHeight) throws {
var transaction = rawTransaction var transaction = rawTransaction
transaction.height = UInt64(height) transaction.height = UInt64(height)
@ -146,18 +146,18 @@ class DarksideWalletService: LightWalletService {
.sendMessage(transaction) .sendMessage(transaction)
.wait() .wait()
} }
func stageTransaction(from url: String, at height: BlockHeight) throws { func stageTransaction(from url: String, at height: BlockHeight) throws {
var txUrl = DarksideTransactionsURL() var txUrl = DarksideTransactionsURL()
txUrl.height = Int32(height) txUrl.height = Int32(height)
txUrl.url = url txUrl.url = url
_ = try darksideService.stageTransactions(txUrl, callOptions: nil).response.wait() _ = try darksideService.stageTransactions(txUrl, callOptions: nil).response.wait()
} }
func addUTXO(_ utxo: GetAddressUtxosReply) throws { func addUTXO(_ utxo: GetAddressUtxosReply) throws {
_ = try darksideService.addAddressUtxo(utxo, callOptions: nil).response.wait() _ = try darksideService.addAddressUtxo(utxo, callOptions: nil).response.wait()
} }
func clearAddedUTXOs() throws { func clearAddedUTXOs() throws {
_ = try darksideService.clearAddressUtxo(Empty(), callOptions: nil).response.wait() _ = try darksideService.clearAddressUtxo(Empty(), callOptions: nil).response.wait()
} }
@ -165,7 +165,7 @@ class DarksideWalletService: LightWalletService {
func getInfo() async throws -> LightWalletdInfo { func getInfo() async throws -> LightWalletdInfo {
try await service.getInfo() try await service.getInfo()
} }
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> { func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
service.blockRange(range) service.blockRange(range)
} }
@ -178,27 +178,31 @@ class DarksideWalletService: LightWalletService {
func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched { func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched {
try await service.fetchTransaction(txId: txId) try await service.fetchTransaction(txId: txId)
} }
func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream<ZcashLightClientKit.SubtreeRoot, Error> {
service.getSubtreeRoots(request)
}
} }
enum DarksideWalletDConstants: NetworkConstants { enum DarksideWalletDConstants: NetworkConstants {
static let defaultFsBlockDbRootName = "fs_cache" static let defaultFsBlockDbRootName = "fs_cache"
static var saplingActivationHeight: BlockHeight { static var saplingActivationHeight: BlockHeight {
663150 663150
} }
static var defaultDataDbName: String { static var defaultDataDbName: String {
ZcashSDKMainnetConstants.defaultDataDbName ZcashSDKMainnetConstants.defaultDataDbName
} }
static var defaultCacheDbName: String { static var defaultCacheDbName: String {
ZcashSDKMainnetConstants.defaultCacheDbName ZcashSDKMainnetConstants.defaultCacheDbName
} }
static var defaultDbNamePrefix: String { static var defaultDbNamePrefix: String {
ZcashSDKMainnetConstants.defaultDbNamePrefix ZcashSDKMainnetConstants.defaultDbNamePrefix
} }
static var feeChangeHeight: BlockHeight { static var feeChangeHeight: BlockHeight {
ZcashSDKMainnetConstants.feeChangeHeight ZcashSDKMainnetConstants.feeChangeHeight
} }

View File

@ -78,4 +78,8 @@ class MockLightWalletService: LightWalletService {
func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched { func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched {
return ZcashTransaction.Fetched(rawID: Data(), minedHeight: -1, raw: Data()) return ZcashTransaction.Fetched(rawID: Data(), minedHeight: -1, raw: Data())
} }
func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream<ZcashLightClientKit.SubtreeRoot, Error> {
service.getSubtreeRoots(request)
}
} }

View File

@ -820,6 +820,26 @@ class LightWalletServiceMock: LightWalletService {
closeConnectionClosure!() closeConnectionClosure!()
} }
// MARK: - getSubtreeRoots
var getSubtreeRootsCallsCount = 0
var getSubtreeRootsCalled: Bool {
return getSubtreeRootsCallsCount > 0
}
var getSubtreeRootsReceivedRequest: GetSubtreeRootsArg?
var getSubtreeRootsReturnValue: AsyncThrowingStream<SubtreeRoot, Error>!
var getSubtreeRootsClosure: ((GetSubtreeRootsArg) -> AsyncThrowingStream<SubtreeRoot, Error>)?
func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream<SubtreeRoot, Error> {
getSubtreeRootsCallsCount += 1
getSubtreeRootsReceivedRequest = request
if let closure = getSubtreeRootsClosure {
return closure(request)
} else {
return getSubtreeRootsReturnValue
}
}
} }
class LightWalletdInfoMock: LightWalletdInfo { class LightWalletdInfoMock: LightWalletdInfo {
@ -2574,6 +2594,56 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {
try await rewindCacheToHeightHeightClosure!(height) try await rewindCacheToHeightHeightClosure!(height)
} }
// MARK: - putSaplingSubtreeRoots
var putSaplingSubtreeRootsStartIndexRootsThrowableError: Error?
func setPutSaplingSubtreeRootsStartIndexRootsThrowableError(_ param: Error?) async {
putSaplingSubtreeRootsStartIndexRootsThrowableError = param
}
var putSaplingSubtreeRootsStartIndexRootsCallsCount = 0
var putSaplingSubtreeRootsStartIndexRootsCalled: Bool {
return putSaplingSubtreeRootsStartIndexRootsCallsCount > 0
}
var putSaplingSubtreeRootsStartIndexRootsReceivedArguments: (startIndex: UInt64, roots: [SubtreeRoot])?
var putSaplingSubtreeRootsStartIndexRootsClosure: ((UInt64, [SubtreeRoot]) async throws -> Void)?
func setPutSaplingSubtreeRootsStartIndexRootsClosure(_ param: ((UInt64, [SubtreeRoot]) async throws -> Void)?) async {
putSaplingSubtreeRootsStartIndexRootsClosure = param
}
func putSaplingSubtreeRoots(startIndex: UInt64, roots: [SubtreeRoot]) async throws {
if let error = putSaplingSubtreeRootsStartIndexRootsThrowableError {
throw error
}
putSaplingSubtreeRootsStartIndexRootsCallsCount += 1
putSaplingSubtreeRootsStartIndexRootsReceivedArguments = (startIndex: startIndex, roots: roots)
try await putSaplingSubtreeRootsStartIndexRootsClosure!(startIndex, roots)
}
// MARK: - updateChainTip
var updateChainTipHeightThrowableError: Error?
func setUpdateChainTipHeightThrowableError(_ param: Error?) async {
updateChainTipHeightThrowableError = param
}
var updateChainTipHeightCallsCount = 0
var updateChainTipHeightCalled: Bool {
return updateChainTipHeightCallsCount > 0
}
var updateChainTipHeightReceivedHeight: Int32?
var updateChainTipHeightClosure: ((Int32) async throws -> Void)?
func setUpdateChainTipHeightClosure(_ param: ((Int32) async throws -> Void)?) async {
updateChainTipHeightClosure = param
}
func updateChainTip(height: Int32) async throws {
if let error = updateChainTipHeightThrowableError {
throw error
}
updateChainTipHeightCallsCount += 1
updateChainTipHeightReceivedHeight = height
try await updateChainTipHeightClosure!(height)
}
// MARK: - suggestScanRanges // MARK: - suggestScanRanges
var suggestScanRangesThrowableError: Error? var suggestScanRangesThrowableError: Error?