From 7694b04d42eebdbd64f0b28f49f4e11bcce26544 Mon Sep 17 00:00:00 2001 From: Lukas Korba Date: Mon, 31 Jul 2023 08:51:04 +0200 Subject: [PATCH] [#1165] draft 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 --- .../DemoAppConfig.swift | 4 +- .../Block/Actions/Action.swift | 1 + .../Actions/UpdateSubtreeRootsAction.swift | 68 +++++++++++++++ .../Block/Actions/ValidateServerAction.swift | 2 +- .../Block/CompactBlockProcessor.swift | 4 + .../Error/ZcashError.swift | 16 +++- .../Error/ZcashErrorCode.swift | 4 + .../Error/ZcashErrorCodeDefinition.swift | 21 +++++ .../Service/GRPC/LightWalletGRPCService.swift | 15 ++++ .../Modules/Service/LightWalletService.swift | 7 ++ .../Rust/ZcashRustBackendWelding.swift | 4 + .../Synchronizer/SDKSynchronizer.swift | 2 +- .../ValidateServerActionTests.swift | 4 +- Tests/TestUtils/DarkSideWalletService.swift | 84 ++++++++++--------- Tests/TestUtils/FakeService.swift | 4 + .../AutoMockable.generated.swift | 70 ++++++++++++++++ 16 files changed, 263 insertions(+), 47 deletions(-) create mode 100644 Sources/ZcashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift index c4daa3bb..96957a3c 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/DemoAppConfig.swift @@ -18,14 +18,14 @@ enum DemoAppConfig { 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 defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000 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 """) - + static let otherSynchronizers: [SynchronizerInitData] = [ SynchronizerInitData( alias: .custom("alt-sync-1"), diff --git a/Sources/ZcashLightClientKit/Block/Actions/Action.swift b/Sources/ZcashLightClientKit/Block/Actions/Action.swift index b3b1f350..c643166f 100644 --- a/Sources/ZcashLightClientKit/Block/Actions/Action.swift +++ b/Sources/ZcashLightClientKit/Block/Actions/Action.swift @@ -34,6 +34,7 @@ enum CBPState: CaseIterable { case idle case migrateLegacyCacheDB case validateServer + case updateSubtreeRoots case computeSyncControlData case download case scan diff --git a/Sources/ZcashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift b/Sources/ZcashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift new file mode 100644 index 00000000..efb7d035 --- /dev/null +++ b/Sources/ZcashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift @@ -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 { } +} diff --git a/Sources/ZcashLightClientKit/Block/Actions/ValidateServerAction.swift b/Sources/ZcashLightClientKit/Block/Actions/ValidateServerAction.swift index 138995fc..e452c40e 100644 --- a/Sources/ZcashLightClientKit/Block/Actions/ValidateServerAction.swift +++ b/Sources/ZcashLightClientKit/Block/Actions/ValidateServerAction.swift @@ -52,7 +52,7 @@ extension ValidateServerAction: Action { throw ZcashError.compactBlockProcessorWrongConsensusBranchId(localBranch, remoteBranchID) } - await context.update(state: .computeSyncControlData) + await context.update(state: .updateSubtreeRoots) return context } diff --git a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift index aa08f09f..05bcda2c 100644 --- a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift @@ -214,6 +214,8 @@ actor CompactBlockProcessor { action = MigrateLegacyCacheDBAction(container: container, configProvider: configProvider) case .validateServer: action = ValidateServerAction(container: container, configProvider: configProvider) + case .updateSubtreeRoots: + action = UpdateSubtreeRootsAction(container: container) case .computeSyncControlData: action = ComputeSyncControlDataAction(container: container, configProvider: configProvider) case .download: @@ -585,6 +587,8 @@ extension CompactBlockProcessor { break case .validateServer: break + case .updateSubtreeRoots: + break case .computeSyncControlData: break case .download: diff --git a/Sources/ZcashLightClientKit/Error/ZcashError.swift b/Sources/ZcashLightClientKit/Error/ZcashError.swift index eb853a9e..a437dcd5 100644 --- a/Sources/ZcashLightClientKit/Error/ZcashError.swift +++ b/Sources/ZcashLightClientKit/Error/ZcashError.swift @@ -58,6 +58,9 @@ public enum ZcashError: Equatable, Error { /// LightWalletService.blockStream failed. /// ZSRVC0000 case serviceBlockStreamFailed(_ error: LightWalletServiceError) + /// LightWalletService.getSubtreeRoots failed. + /// ZSRVC0009 + case serviceSubtreeRootsStreamFailed(_ error: LightWalletServiceError) /// SimpleConnectionProvider init of Connection failed. /// ZSCPC0001 case simpleConnectionProvider(_ error: Error) @@ -277,18 +280,22 @@ public enum ZcashError: Equatable, Error { /// ZRUST0045 case rustGetTransparentReceiverInvalidReceiver /// Unable to allocate memory required to write blocks when calling ZcashRustBackend.putSaplingSubtreeRoots + /// sourcery: code="ZRUST0046" /// ZRUST0046 case rustPutSaplingSubtreeRootsAllocationProblem /// Error from rust layer when calling ZcashRustBackend.putSaplingSubtreeRoots /// - `rustError` contains error generated by the rust layer. + /// sourcery: code="ZRUST0047" /// ZRUST0047 case rustPutSaplingSubtreeRoots(_ rustError: String) /// Error from rust layer when calling ZcashRustBackend.updateChainTip /// - `rustError` contains error generated by the rust layer. + /// sourcery: code="ZRUST0048" /// ZRUST0048 case rustUpdateChainTip(_ rustError: String) /// Error from rust layer when calling ZcashRustBackend.suggestScanRanges /// - `rustError` contains error generated by the rust layer. + /// sourcery: code="ZRUST0049" /// ZRUST0049 case rustSuggestScanRanges(_ rustError: String) /// 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. /// ZCBPEO0018 case compactBlockProcessorDownloadBlockActionRewind + /// Put sapling subtree roots to the DB failed. + /// ZCBPEO0019 + case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error) /// The synchronizer is unprepared. /// ZSYNCO0001 case synchronizerNotPrepared @@ -566,6 +576,7 @@ public enum ZcashError: Equatable, Error { case .serviceFetchTransactionFailed: return "LightWalletService.fetchTransaction failed." case .serviceFetchUTXOsFailed: return "LightWalletService.fetchUTXOs failed." case .serviceBlockStreamFailed: return "LightWalletService.blockStream failed." + case .serviceSubtreeRootsStreamFailed: return "LightWalletService.getSubtreeRoots failed." case .simpleConnectionProvider: return "SimpleConnectionProvider init of Connection failed." case .saplingParamsInvalidSpendParams: return "Downloaded file with sapling spending 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 .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 .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 .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip" 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 .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 .compactBlockProcessorPutSaplingSubtreeRoots: return "Put sapling subtree roots to the DB failed." case .synchronizerNotPrepared: return "The synchronizer is unprepared." 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." @@ -729,6 +741,7 @@ public enum ZcashError: Equatable, Error { case .serviceFetchTransactionFailed: return .serviceFetchTransactionFailed case .serviceFetchUTXOsFailed: return .serviceFetchUTXOsFailed case .serviceBlockStreamFailed: return .serviceBlockStreamFailed + case .serviceSubtreeRootsStreamFailed: return .serviceSubtreeRootsStreamFailed case .simpleConnectionProvider: return .simpleConnectionProvider case .saplingParamsInvalidSpendParams: return .saplingParamsInvalidSpendParams case .saplingParamsInvalidOutputParams: return .saplingParamsInvalidOutputParams @@ -866,6 +879,7 @@ public enum ZcashError: Equatable, Error { case .compactBlockProcessorChainName: return .compactBlockProcessorChainName case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID case .compactBlockProcessorDownloadBlockActionRewind: return .compactBlockProcessorDownloadBlockActionRewind + case .compactBlockProcessorPutSaplingSubtreeRoots: return .compactBlockProcessorPutSaplingSubtreeRoots case .synchronizerNotPrepared: return .synchronizerNotPrepared case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds diff --git a/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift b/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift index 60f12f20..60e85271 100644 --- a/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift +++ b/Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift @@ -39,6 +39,8 @@ public enum ZcashErrorCode: String { case serviceFetchUTXOsFailed = "ZSRVC0008" /// LightWalletService.blockStream failed. case serviceBlockStreamFailed = "ZSRVC0000" + /// LightWalletService.getSubtreeRoots failed. + case serviceSubtreeRootsStreamFailed = "ZSRVC0009" /// SimpleConnectionProvider init of Connection failed. case simpleConnectionProvider = "ZSCPC0001" /// Downloaded file with sapling spending parameters isn't valid. @@ -313,6 +315,8 @@ public enum ZcashErrorCode: String { case compactBlockProcessorConsensusBranchID = "ZCBPEO0017" /// Rewind of DownloadBlockAction failed as no action is possible to unwrapp. case compactBlockProcessorDownloadBlockActionRewind = "ZCBPEO0018" + /// Put sapling subtree roots to the DB failed. + case compactBlockProcessorPutSaplingSubtreeRoots = "ZCBPEO0019" /// The synchronizer is unprepared. case synchronizerNotPrepared = "ZSYNCO0001" /// Memos can't be sent to transparent addresses. diff --git a/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift b/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift index 1f5fa8f6..3eccac23 100644 --- a/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift +++ b/Sources/ZcashLightClientKit/Error/ZcashErrorCodeDefinition.swift @@ -77,6 +77,9 @@ enum ZcashErrorDefinition { /// LightWalletService.blockStream failed. // sourcery: code="ZSRVC0000" case serviceBlockStreamFailed(_ error: LightWalletServiceError) + /// LightWalletService.getSubtreeRoots failed. + // sourcery: code="ZSRVC0009" + case serviceSubtreeRootsStreamFailed(_ error: LightWalletServiceError) // MARK: SQLite connection @@ -307,6 +310,21 @@ enum ZcashErrorDefinition { /// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver // sourcery: code="ZRUST0045" 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 @@ -592,6 +610,9 @@ enum ZcashErrorDefinition { /// Rewind of DownloadBlockAction failed as no action is possible to unwrapp. // sourcery: code="ZCBPEO0018" case compactBlockProcessorDownloadBlockActionRewind + /// Put sapling subtree roots to the DB failed. + // sourcery: code="ZCBPEO0019" + case compactBlockProcessorPutSaplingSubtreeRoots(_ error: Error) // MARK: - SDKSynchronizer diff --git a/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift b/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift index a6b47eca..c430786e 100644 --- a/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +++ b/Sources/ZcashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift @@ -262,6 +262,21 @@ extension LightWalletGRPCService: LightWalletService { } } } + + func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream { + 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() { _ = channel.close() diff --git a/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift b/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift index 6eed7374..2bbf307c 100644 --- a/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift +++ b/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift @@ -193,4 +193,11 @@ protocol LightWalletService: AnyObject { ) -> AsyncThrowingStream 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 } diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift index f601dd15..29747fe8 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift @@ -166,6 +166,10 @@ protocol ZcashRustBackendWelding { /// - Throws: `rustRewindCacheToHeight` if rust layer returns error. 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. /// /// This method should only be used in cases where the `CompactBlock` data that will be diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index 9db9a9bd..cb3cf57c 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -141,7 +141,7 @@ public class SDKSynchronizer: Synchronizer { if case .seedRequired = try await self.initializer.initialize(with: seed, viewingKeys: viewingKeys, walletBirthday: walletBirthday) { return .seedRequired } - + await latestBlocksDataProvider.updateWalletBirthday(initializer.walletBirthday) await latestBlocksDataProvider.updateScannedData() diff --git a/Tests/OfflineTests/CompactBlockProcessorActions/ValidateServerActionTests.swift b/Tests/OfflineTests/CompactBlockProcessorActions/ValidateServerActionTests.swift index b5122f74..d270c367 100644 --- a/Tests/OfflineTests/CompactBlockProcessorActions/ValidateServerActionTests.swift +++ b/Tests/OfflineTests/CompactBlockProcessorActions/ValidateServerActionTests.swift @@ -31,8 +31,8 @@ final class ValidateServerActionTests: ZcashTestCase { let nextContext = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in } let nextState = await nextContext.state XCTAssertTrue( - nextState == .computeSyncControlData, - "nextContext after .validateServer is expected to be .computeSyncControlData but received \(nextState)" + nextState == .updateSubtreeRoots, + "nextContext after .validateServer is expected to be .updateSubtreeRoots but received \(nextState)" ) } catch { XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)") diff --git a/Tests/TestUtils/DarkSideWalletService.swift b/Tests/TestUtils/DarkSideWalletService.swift index 8b655939..26eebf62 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 - */ + 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 - */ + 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 - */ + 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" } @@ -45,34 +45,34 @@ class DarksideWalletService: LightWalletService { var channel: Channel var service: LightWalletService var darksideService: DarksideStreamerNIOClient - + init(endpoint: LightWalletEndpoint) { self.channel = ChannelProvider().channel(endpoint: endpoint) self.service = LightWalletServiceFactory(endpoint: endpoint).make() self.darksideService = DarksideStreamerNIOClient(channel: channel) } - + init(endpoint: LightWalletEndpoint, service: LightWalletService) { self.channel = ChannelProvider().channel(endpoint: endpoint) self.darksideService = DarksideStreamerNIOClient(channel: channel) self.service = service } - + convenience init() { self.init(endpoint: LightWalletEndpointBuilder.default) } - + func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream { service.blockStream(startHeight: startHeight, endHeight: endHeight) } - + func latestBlock() async throws -> ZcashLightClientKit.BlockID { throw "Not mocked" } func closeConnection() { } - + func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream { service.fetchUTXOs(for: tAddress, height: height) } @@ -80,40 +80,40 @@ class DarksideWalletService: LightWalletService { func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream { service.fetchUTXOs(for: tAddresses, height: height) } - + func latestBlockHeight() async throws -> BlockHeight { try await service.latestBlockHeight() } - + 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( Empty(), handler: { txs.append($0) } ) - .status - .wait() - + .status + .wait() + switch response.code { case .ok: return !txs.isEmpty ? txs : nil @@ -121,7 +121,7 @@ class DarksideWalletService: LightWalletService { throw response } } - + func reset(saplingActivation: BlockHeight, branchID: String = "d3adb33f", chainName: String = "test") throws { var metaState = DarksideMetaState() metaState.saplingActivation = Int32(saplingActivation) @@ -130,7 +130,7 @@ class DarksideWalletService: LightWalletService { // TODO: [#718] complete meta state correctly, https://github.com/zcash/ZcashLightClientKit/issues/718 _ = 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) @@ -138,7 +138,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) @@ -146,18 +146,18 @@ 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() } @@ -165,7 +165,7 @@ class DarksideWalletService: LightWalletService { func getInfo() async throws -> LightWalletdInfo { try await service.getInfo() } - + func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream { service.blockRange(range) } @@ -178,27 +178,31 @@ class DarksideWalletService: LightWalletService { func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched { try await service.fetchTransaction(txId: txId) } + + func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream { + service.getSubtreeRoots(request) + } } enum DarksideWalletDConstants: NetworkConstants { static let defaultFsBlockDbRootName = "fs_cache" - + static var saplingActivationHeight: BlockHeight { 663150 } - + static var defaultDataDbName: String { ZcashSDKMainnetConstants.defaultDataDbName } - + static var defaultCacheDbName: String { ZcashSDKMainnetConstants.defaultCacheDbName } - + static var defaultDbNamePrefix: String { ZcashSDKMainnetConstants.defaultDbNamePrefix } - + static var feeChangeHeight: BlockHeight { ZcashSDKMainnetConstants.feeChangeHeight } diff --git a/Tests/TestUtils/FakeService.swift b/Tests/TestUtils/FakeService.swift index 47bf60a3..07e2d74b 100644 --- a/Tests/TestUtils/FakeService.swift +++ b/Tests/TestUtils/FakeService.swift @@ -78,4 +78,8 @@ class MockLightWalletService: LightWalletService { func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched { return ZcashTransaction.Fetched(rawID: Data(), minedHeight: -1, raw: Data()) } + + func getSubtreeRoots(_ request: ZcashLightClientKit.GetSubtreeRootsArg) -> AsyncThrowingStream { + service.getSubtreeRoots(request) + } } diff --git a/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift b/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift index 1db61f80..f7020b7b 100644 --- a/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift +++ b/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift @@ -820,6 +820,26 @@ class LightWalletServiceMock: LightWalletService { closeConnectionClosure!() } + // MARK: - getSubtreeRoots + + var getSubtreeRootsCallsCount = 0 + var getSubtreeRootsCalled: Bool { + return getSubtreeRootsCallsCount > 0 + } + var getSubtreeRootsReceivedRequest: GetSubtreeRootsArg? + var getSubtreeRootsReturnValue: AsyncThrowingStream! + var getSubtreeRootsClosure: ((GetSubtreeRootsArg) -> AsyncThrowingStream)? + + func getSubtreeRoots(_ request: GetSubtreeRootsArg) -> AsyncThrowingStream { + getSubtreeRootsCallsCount += 1 + getSubtreeRootsReceivedRequest = request + if let closure = getSubtreeRootsClosure { + return closure(request) + } else { + return getSubtreeRootsReturnValue + } + } + } class LightWalletdInfoMock: LightWalletdInfo { @@ -2574,6 +2594,56 @@ actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding { 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 var suggestScanRangesThrowableError: Error?