- blocking API removed, only latestBlockHeight() stayed - non-blocking closure based API removed - unit tests updated to use async API [#492] Get rid of blocking API (#551) - forgotten commented code cleaned up - some comments were still mentioning result (completion closure), removed
This commit is contained in:
parent
fa5bbbb2bf
commit
9b6ff51b29
|
@ -91,12 +91,8 @@ extension CompactBlockStorage: CompactBlockRepository {
|
|||
}
|
||||
return try await task.value
|
||||
}
|
||||
|
||||
func write(blocks: [ZcashCompactBlock]) throws {
|
||||
try insert(blocks)
|
||||
}
|
||||
|
||||
func writeAsync(blocks: [ZcashCompactBlock]) async throws {
|
||||
func write(blocks: [ZcashCompactBlock]) async throws {
|
||||
let task = Task(priority: .userInitiated) {
|
||||
try insert(blocks)
|
||||
}
|
||||
|
|
|
@ -16,18 +16,11 @@ enum CompactBlockDownloadError: Error {
|
|||
Represents what a compact block downloaded should provide to its clients
|
||||
*/
|
||||
public protocol CompactBlockDownloading {
|
||||
|
||||
/**
|
||||
Downloads and stores the given block range.
|
||||
Blocking
|
||||
*/
|
||||
func downloadBlockRange(_ range: CompactBlockRange) throws
|
||||
|
||||
/**
|
||||
Downloads and stores the given block range.
|
||||
Non-Blocking
|
||||
*/
|
||||
func downloadBlockRangeAsync(_ heightRange: CompactBlockRange) async throws
|
||||
func downloadBlockRange(_ heightRange: CompactBlockRange) async throws
|
||||
|
||||
/**
|
||||
Restore the download progress up to the given height.
|
||||
|
@ -69,36 +62,10 @@ public protocol CompactBlockDownloading {
|
|||
/**
|
||||
Gets the transaction for the Id given
|
||||
- Parameter txId: Data representing the transaction Id
|
||||
- Returns: a transaction entity with the requested transaction
|
||||
- Throws: An error if the fetch failed
|
||||
*/
|
||||
func fetchTransaction(txId: Data) throws -> TransactionEntity
|
||||
|
||||
/**
|
||||
Gets the transaction for the Id given
|
||||
- Parameter txId: Data representing the transaction Id
|
||||
*/
|
||||
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight) throws -> [UnspentTransactionOutputEntity]
|
||||
|
||||
// TODO: will be removed with the issue 474
|
||||
// https://github.com/zcash/ZcashLightClientKit/issues/474
|
||||
// Use the new API fetchUnspentTransactionOutputs(...) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
|
||||
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void)
|
||||
func fetchTransaction(txId: Data) async throws -> TransactionEntity
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddresses: [String], startHeight: BlockHeight) throws -> [UnspentTransactionOutputEntity]
|
||||
|
||||
// TODO: will be removed with the issue 474
|
||||
// https://github.com/zcash/ZcashLightClientKit/issues/474
|
||||
// Use the new API fetchUnspentTransactionOutputs(...) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
|
||||
func fetchUnspentTransactionOutputs(
|
||||
tAddresses: [String],
|
||||
startHeight: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void
|
||||
)
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddresses: [String], startHeight: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
|
||||
|
||||
|
@ -127,45 +94,11 @@ extension CompactBlockDownloader: CompactBlockDownloading {
|
|||
func closeConnection() {
|
||||
lightwalletService.closeConnection()
|
||||
}
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddresses: [String], startHeight: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
try lightwalletService.fetchUTXOs(for: tAddresses, height: startHeight)
|
||||
}
|
||||
|
||||
func fetchUnspentTransactionOutputs(
|
||||
tAddresses: [String],
|
||||
startHeight: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void
|
||||
) {
|
||||
lightwalletService.fetchUTXOs(for: tAddresses, height: startHeight) { fetchResult in
|
||||
switch fetchResult {
|
||||
case .success(let utxos):
|
||||
result(.success(utxos))
|
||||
case .failure(let error):
|
||||
result(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddresses: [String], startHeight: BlockHeight ) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
|
||||
lightwalletService.fetchUTXOs(for: tAddresses, height: startHeight)
|
||||
}
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
try lightwalletService.fetchUTXOs(for: tAddress, height: startHeight)
|
||||
}
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void) {
|
||||
lightwalletService.fetchUTXOs(for: tAddress, height: startHeight) { fetchResult in
|
||||
switch fetchResult {
|
||||
case .success(let utxos):
|
||||
result(.success(utxos))
|
||||
case .failure(let error):
|
||||
result(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
|
||||
lightwalletService.fetchUTXOs(for: tAddress, height: startHeight)
|
||||
}
|
||||
|
@ -177,20 +110,15 @@ extension CompactBlockDownloader: CompactBlockDownloading {
|
|||
func latestBlockHeight() throws -> BlockHeight {
|
||||
try lightwalletService.latestBlockHeight()
|
||||
}
|
||||
|
||||
func downloadBlockRange(_ range: CompactBlockRange) throws {
|
||||
let blocks = try lightwalletService.blockRange(range)
|
||||
try storage.write(blocks: blocks)
|
||||
}
|
||||
|
||||
func downloadBlockRangeAsync( _ heightRange: CompactBlockRange) async throws {
|
||||
func downloadBlockRange( _ heightRange: CompactBlockRange) async throws {
|
||||
let stream: AsyncThrowingStream<ZcashCompactBlock, Error> = lightwalletService.blockRange(heightRange)
|
||||
do {
|
||||
var compactBlocks: [ZcashCompactBlock] = []
|
||||
for try await compactBlock in stream {
|
||||
compactBlocks.append(compactBlock)
|
||||
}
|
||||
try await self.storage.writeAsync(blocks: compactBlocks)
|
||||
try await self.storage.write(blocks: compactBlocks)
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
|
@ -213,7 +141,6 @@ extension CompactBlockDownloader: CompactBlockDownloading {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func rewind(to height: BlockHeight) throws {
|
||||
try self.storage.rewind(to: height)
|
||||
}
|
||||
|
@ -222,11 +149,7 @@ extension CompactBlockDownloader: CompactBlockDownloading {
|
|||
try self.storage.latestHeight()
|
||||
}
|
||||
|
||||
func fetchTransaction(txId: Data) throws -> TransactionEntity {
|
||||
try lightwalletService.fetchTransaction(txId: txId)
|
||||
}
|
||||
|
||||
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity {
|
||||
try await lightwalletService.fetchTransactionAsync(txId: txId)
|
||||
func fetchTransaction(txId: Data) async throws -> TransactionEntity {
|
||||
try await lightwalletService.fetchTransaction(txId: txId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,7 @@ extension CompactBlockProcessor {
|
|||
try Task.checkCancellation()
|
||||
buffer.append(zcashCompactBlock)
|
||||
if buffer.count >= blockBufferSize {
|
||||
// TODO: writeAsync doesn't make sense here, awaiting it or calling blocking API have the same result and impact
|
||||
try storage.write(blocks: buffer)
|
||||
try await storage.write(blocks: buffer)
|
||||
buffer.removeAll(keepingCapacity: true)
|
||||
}
|
||||
|
||||
|
@ -54,8 +53,7 @@ extension CompactBlockProcessor {
|
|||
)
|
||||
notifyProgress(.download(progress))
|
||||
}
|
||||
// TODO: writeAsync doesn't make sense here, awaiting it or calling blocking API have the same result and impact
|
||||
try storage.write(blocks: buffer)
|
||||
try await storage.write(blocks: buffer)
|
||||
buffer.removeAll(keepingCapacity: true)
|
||||
} catch {
|
||||
guard let err = error as? LightWalletServiceError, case .userCancelled = err else {
|
||||
|
@ -73,7 +71,7 @@ extension CompactBlockProcessor {
|
|||
try Task.checkCancellation()
|
||||
|
||||
do {
|
||||
try await downloader.downloadBlockRangeAsync(range)
|
||||
try await downloader.downloadBlockRange(range)
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ extension CompactBlockProcessor {
|
|||
private func enhance(transaction: TransactionEntity) async throws -> ConfirmedTransactionEntity {
|
||||
LoggerProxy.debug("Zoom.... Enhance... Tx: \(transaction.transactionId.toHexStringTxId())")
|
||||
|
||||
let transaction = try await downloader.fetchTransactionAsync(txId: transaction.transactionId)
|
||||
let transaction = try await downloader.fetchTransaction(txId: transaction.transactionId)
|
||||
|
||||
let transactionID = transaction.transactionId.toHexStringTxId()
|
||||
let block = String(describing: transaction.minedHeight)
|
||||
|
|
|
@ -598,31 +598,23 @@ public class CompactBlockProcessor {
|
|||
}
|
||||
|
||||
func validateServer(completionBlock: @escaping (() -> Void)) {
|
||||
self.service.getInfo(result: { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let info):
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
do {
|
||||
try Self.validateServerInfo(
|
||||
info,
|
||||
saplingActivation: self.config.saplingActivation,
|
||||
localNetwork: self.config.network,
|
||||
rustBackend: self.rustBackend
|
||||
)
|
||||
completionBlock()
|
||||
} catch {
|
||||
self.severeFailure(error)
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let info = try await self.service.getInfo()
|
||||
try Self.validateServerInfo(
|
||||
info,
|
||||
saplingActivation: self.config.saplingActivation,
|
||||
localNetwork: self.config.network,
|
||||
rustBackend: self.rustBackend
|
||||
)
|
||||
completionBlock()
|
||||
} catch let error as LightWalletServiceError {
|
||||
self.severeFailure(error.mapToProcessorError())
|
||||
} catch {
|
||||
self.severeFailure(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Processes new blocks on the given range based on the configuration set for this instance
|
||||
func processNewBlocks(range: CompactBlockRange) {
|
||||
|
@ -1182,51 +1174,39 @@ extension CompactBlockProcessor {
|
|||
rustBackend: ZcashRustBackendWelding.Type
|
||||
) async throws -> NextState {
|
||||
let task = Task(priority: .userInitiated) {
|
||||
// TODO: refactor to async call, issue 463, PR 493
|
||||
// https://github.com/zcash/ZcashLightClientKit/issues/463
|
||||
try nextState(
|
||||
service: service,
|
||||
downloader: downloader,
|
||||
config: config,
|
||||
rustBackend: rustBackend
|
||||
)
|
||||
do {
|
||||
let info = try await service.getInfo()
|
||||
|
||||
try CompactBlockProcessor.validateServerInfo(
|
||||
info,
|
||||
saplingActivation: config.saplingActivation,
|
||||
localNetwork: config.network,
|
||||
rustBackend: rustBackend
|
||||
)
|
||||
|
||||
// get latest block height
|
||||
let latestDownloadedBlockHeight: BlockHeight = max(config.walletBirthday, try downloader.lastDownloadedBlockHeight())
|
||||
|
||||
let latestBlockheight = try service.latestBlockHeight()
|
||||
|
||||
if latestDownloadedBlockHeight < latestBlockheight {
|
||||
return NextState.processNewBlocks(
|
||||
range: CompactBlockProcessor.nextBatchBlockRange(
|
||||
latestHeight: latestBlockheight,
|
||||
latestDownloadedHeight: latestDownloadedBlockHeight,
|
||||
walletBirthday: config.walletBirthday
|
||||
)
|
||||
)
|
||||
} else if latestBlockheight == latestDownloadedBlockHeight {
|
||||
return .finishProcessing(height: latestBlockheight)
|
||||
}
|
||||
|
||||
return .wait(latestHeight: latestBlockheight, latestDownloadHeight: latestBlockheight)
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
return try await task.value
|
||||
}
|
||||
|
||||
static func nextState(
|
||||
service: LightWalletService,
|
||||
downloader: CompactBlockDownloading,
|
||||
config: Configuration,
|
||||
rustBackend: ZcashRustBackendWelding.Type
|
||||
) throws -> NextState {
|
||||
let info = try service.getInfo()
|
||||
|
||||
try CompactBlockProcessor.validateServerInfo(
|
||||
info,
|
||||
saplingActivation: config.saplingActivation,
|
||||
localNetwork: config.network,
|
||||
rustBackend: rustBackend
|
||||
)
|
||||
|
||||
// get latest block height
|
||||
let latestDownloadedBlockHeight: BlockHeight = max(config.walletBirthday, try downloader.lastDownloadedBlockHeight())
|
||||
|
||||
let latestBlockheight = try service.latestBlockHeight()
|
||||
|
||||
if latestDownloadedBlockHeight < latestBlockheight {
|
||||
return .processNewBlocks(
|
||||
range: CompactBlockProcessor.nextBatchBlockRange(
|
||||
latestHeight: latestBlockheight,
|
||||
latestDownloadedHeight: latestDownloadedBlockHeight,
|
||||
walletBirthday: config.walletBirthday
|
||||
)
|
||||
)
|
||||
} else if latestBlockheight == latestDownloadedBlockHeight {
|
||||
return .finishProcessing(height: latestBlockheight)
|
||||
}
|
||||
|
||||
return .wait(latestHeight: latestBlockheight, latestDownloadHeight: latestBlockheight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,21 +31,14 @@ protocol CompactBlockRepository {
|
|||
*/
|
||||
func latestHeightAsync() async throws -> BlockHeight
|
||||
|
||||
/**
|
||||
Write the given blocks to this store, which may be anything from an in-memory cache to a DB.
|
||||
Blocking
|
||||
- Parameter blocks: the compact blocks that will be written to storage
|
||||
- Throws: an error when there's a failure
|
||||
*/
|
||||
func write(blocks: [ZcashCompactBlock]) throws
|
||||
|
||||
/**
|
||||
Write the given blocks to this store, which may be anything from an in-memory cache to a DB.
|
||||
Non-Blocking
|
||||
- Parameters:
|
||||
- Parameter blocks: array of blocks to be written to storage
|
||||
- Throws: an error when there's a failure
|
||||
*/
|
||||
func writeAsync(blocks: [ZcashCompactBlock]) async throws
|
||||
func write(blocks: [ZcashCompactBlock]) async throws
|
||||
|
||||
/**
|
||||
Remove every block above and including the given height.
|
||||
|
|
|
@ -189,11 +189,11 @@ public class LightWalletGRPCService {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - LightWalletServiceBlockingAPI
|
||||
// MARK: - LightWalletService
|
||||
|
||||
extension LightWalletGRPCService: LightWalletServiceBlockingAPI {
|
||||
public func getInfo() throws -> LightWalletdInfo {
|
||||
try compactTxStreamer.getLightdInfo(Empty()).response.wait()
|
||||
extension LightWalletGRPCService: LightWalletService {
|
||||
public func getInfo() async throws -> LightWalletdInfo {
|
||||
try await compactTxStreamerAsync.getLightdInfo(Empty())
|
||||
}
|
||||
|
||||
public func latestBlockHeight() throws -> BlockHeight {
|
||||
|
@ -202,161 +202,13 @@ extension LightWalletGRPCService: LightWalletServiceBlockingAPI {
|
|||
}
|
||||
return height
|
||||
}
|
||||
|
||||
public func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] {
|
||||
var blocks: [CompactBlock] = []
|
||||
|
||||
let response = compactTxStreamer.getBlockRange(
|
||||
range.blockRange(),
|
||||
handler: { blocks.append($0) }
|
||||
)
|
||||
|
||||
let status = try response.status.wait()
|
||||
|
||||
switch status.code {
|
||||
case .ok:
|
||||
return blocks.asZcashCompactBlocks()
|
||||
default:
|
||||
throw LightWalletServiceError.mapCode(status)
|
||||
}
|
||||
}
|
||||
|
||||
public func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
|
||||
let rawTx = RawTransaction.with { raw in
|
||||
raw.data = spendTransaction
|
||||
}
|
||||
do {
|
||||
return try compactTxStreamer.sendTransaction(rawTx).response.wait()
|
||||
} catch {
|
||||
throw error.mapToServiceError()
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchTransaction(txId: Data) throws -> TransactionEntity {
|
||||
var txFilter = TxFilter()
|
||||
txFilter.hash = txId
|
||||
|
||||
do {
|
||||
let rawTx = try compactTxStreamer.getTransaction(txFilter).response.wait()
|
||||
|
||||
return TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx)
|
||||
} catch {
|
||||
throw error.mapToServiceError()
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
let arg = GetAddressUtxosArg.with { utxoArgs in
|
||||
utxoArgs.addresses = [tAddress]
|
||||
utxoArgs.startHeight = UInt64(height)
|
||||
}
|
||||
do {
|
||||
return try self.compactTxStreamer.getAddressUtxos(arg).response.wait().addressUtxos.map { reply in
|
||||
UTXO(
|
||||
id: nil,
|
||||
address: tAddress,
|
||||
prevoutTxId: reply.txid,
|
||||
prevoutIndex: Int(reply.index),
|
||||
script: reply.script,
|
||||
valueZat: Int(reply.valueZat),
|
||||
height: Int(reply.height),
|
||||
spentInTx: nil
|
||||
)
|
||||
}
|
||||
} catch {
|
||||
throw error.mapToServiceError()
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
guard !tAddresses.isEmpty else {
|
||||
return [] // FIXME: throw a real error
|
||||
}
|
||||
|
||||
var utxos: [UnspentTransactionOutputEntity] = []
|
||||
|
||||
let arg = GetAddressUtxosArg.with { utxoArgs in
|
||||
utxoArgs.addresses = tAddresses
|
||||
utxoArgs.startHeight = UInt64(height)
|
||||
}
|
||||
utxos.append(
|
||||
contentsOf:
|
||||
try self.compactTxStreamer.getAddressUtxos(arg).response.wait().addressUtxos.map { reply in
|
||||
UTXO(
|
||||
id: nil,
|
||||
address: reply.address,
|
||||
prevoutTxId: reply.txid,
|
||||
prevoutIndex: Int(reply.index),
|
||||
script: reply.script,
|
||||
valueZat: Int(reply.valueZat),
|
||||
height: Int(reply.height),
|
||||
spentInTx: nil
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
return utxos
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LightWalletServiceNonBlockingAPI
|
||||
|
||||
extension LightWalletGRPCService: LightWalletServiceNonBlockingAPI {
|
||||
public func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) {
|
||||
compactTxStreamer.getLightdInfo(Empty()).response.whenComplete { completionResult in
|
||||
switch completionResult {
|
||||
case .success(let info):
|
||||
result(.success(info))
|
||||
case .failure(let error):
|
||||
result(.failure(error.mapToServiceError()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func getInfoAsync() async throws -> LightWalletdInfo {
|
||||
try await compactTxStreamerAsync.getLightdInfo(Empty())
|
||||
}
|
||||
|
||||
public func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
|
||||
let response = compactTxStreamer.getLatestBlock(ChainSpec()).response
|
||||
|
||||
response.whenSuccessBlocking(onto: queue) { blockID in
|
||||
guard let blockHeight = Int(exactly: blockID.height) else {
|
||||
result(.failure(LightWalletServiceError.generalError(message: "error creating blockheight from BlockID \(blockID)")))
|
||||
return
|
||||
}
|
||||
result(.success(blockHeight))
|
||||
}
|
||||
|
||||
response.whenFailureBlocking(onto: queue) { error in
|
||||
result(.failure(error.mapToServiceError()))
|
||||
}
|
||||
}
|
||||
|
||||
public func latestBlockHeightAsync() async throws -> BlockHeight {
|
||||
try await BlockHeight(compactTxStreamerAsync.getLatestBlock(ChainSpec()).height)
|
||||
}
|
||||
|
||||
public func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
|
||||
queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
var blocks: [CompactBlock] = []
|
||||
let response = self.compactTxStreamer.getBlockRange(range.blockRange(), handler: { blocks.append($0) })
|
||||
|
||||
do {
|
||||
let status = try response.status.wait()
|
||||
switch status.code {
|
||||
case .ok:
|
||||
result(.success(blocks.asZcashCompactBlocks()))
|
||||
|
||||
default:
|
||||
result(.failure(.mapCode(status)))
|
||||
}
|
||||
} catch {
|
||||
result(.failure(error.mapToServiceError()))
|
||||
}
|
||||
let blockID = try await compactTxStreamerAsync.getLatestBlock(ChainSpec())
|
||||
guard let blockHeight = Int(exactly: blockID.height) else {
|
||||
throw LightWalletServiceError.generalError(message: "error creating blockheight from BlockID \(blockID)")
|
||||
}
|
||||
return blockHeight
|
||||
}
|
||||
|
||||
public func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
||||
|
@ -376,48 +228,16 @@ extension LightWalletGRPCService: LightWalletServiceNonBlockingAPI {
|
|||
}
|
||||
}
|
||||
|
||||
public func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
|
||||
public func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
do {
|
||||
let transaction = try RawTransaction(serializedData: spendTransaction)
|
||||
let response = self.compactTxStreamer.sendTransaction(transaction).response
|
||||
|
||||
response.whenComplete { responseResult in
|
||||
switch responseResult {
|
||||
case .failure(let error):
|
||||
result(.failure(LightWalletServiceError.sentFailed(error: error)))
|
||||
case .success(let success):
|
||||
result(.success(success))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
result(.failure(error.mapToServiceError()))
|
||||
}
|
||||
}
|
||||
|
||||
public func submitAsync(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
do {
|
||||
let transaction = try RawTransaction(serializedData: spendTransaction)
|
||||
let transaction = RawTransaction.with { $0.data = spendTransaction }
|
||||
return try await compactTxStreamerAsync.sendTransaction(transaction)
|
||||
} catch {
|
||||
throw LightWalletServiceError.sentFailed(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) {
|
||||
var txFilter = TxFilter()
|
||||
txFilter.hash = txId
|
||||
|
||||
compactTxStreamer.getTransaction(txFilter).response.whenComplete { response in
|
||||
switch response {
|
||||
case .failure(let error):
|
||||
result(.failure(error.mapToServiceError()))
|
||||
case .success(let rawTx):
|
||||
result(.success(TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity {
|
||||
public func fetchTransaction(txId: Data) async throws -> TransactionEntity {
|
||||
var txFilter = TxFilter()
|
||||
txFilter.hash = txId
|
||||
|
||||
|
@ -473,52 +293,6 @@ extension LightWalletGRPCService: LightWalletServiceNonBlockingAPI {
|
|||
return fetchUTXOs(for: [tAddress], height: height)
|
||||
}
|
||||
|
||||
public func fetchUTXOs(
|
||||
for tAddresses: [String],
|
||||
height: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
) {
|
||||
guard !tAddresses.isEmpty else {
|
||||
return result(.success([])) // FIXME: throw a real error
|
||||
}
|
||||
|
||||
var utxos: [UnspentTransactionOutputEntity] = []
|
||||
self.queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let args = GetAddressUtxosArg.with { utxoArgs in
|
||||
utxoArgs.addresses = tAddresses
|
||||
utxoArgs.startHeight = UInt64(height)
|
||||
}
|
||||
do {
|
||||
let response = try self.compactTxStreamer.getAddressUtxosStream(args) { reply in
|
||||
utxos.append(
|
||||
UTXO(
|
||||
id: nil,
|
||||
address: reply.address,
|
||||
prevoutTxId: reply.txid,
|
||||
prevoutIndex: Int(reply.index),
|
||||
script: reply.script,
|
||||
valueZat: Int(reply.valueZat),
|
||||
height: Int(reply.height),
|
||||
spentInTx: nil
|
||||
)
|
||||
)
|
||||
}
|
||||
.status
|
||||
.wait()
|
||||
|
||||
switch response.code {
|
||||
case .ok:
|
||||
result(.success(utxos))
|
||||
default:
|
||||
result(.failure(.mapCode(response)))
|
||||
}
|
||||
} catch {
|
||||
result(.failure(error.mapToServiceError()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func fetchUTXOs(
|
||||
for tAddresses: [String],
|
||||
height: BlockHeight
|
||||
|
@ -558,48 +332,6 @@ extension LightWalletGRPCService: LightWalletServiceNonBlockingAPI {
|
|||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func blockStream(
|
||||
startHeight: BlockHeight,
|
||||
endHeight: BlockHeight,
|
||||
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
|
||||
handler: @escaping (ZcashCompactBlock) -> Void,
|
||||
progress: @escaping (BlockProgress) -> Void
|
||||
) -> CancellableCall {
|
||||
let future = compactTxStreamer.getBlockRange(
|
||||
BlockRange(
|
||||
startHeight: startHeight,
|
||||
endHeight: endHeight
|
||||
),
|
||||
callOptions: Self.callOptions(timeLimit: self.streamingCallTimeout),
|
||||
handler: { compactBlock in
|
||||
handler(ZcashCompactBlock(compactBlock: compactBlock))
|
||||
progress(
|
||||
BlockProgress(
|
||||
startHeight: startHeight,
|
||||
targetHeight: endHeight,
|
||||
progressHeight: BlockHeight(compactBlock.height)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
future.status.whenComplete { completionResult in
|
||||
switch completionResult {
|
||||
case .success(let status):
|
||||
switch status.code {
|
||||
case .ok:
|
||||
result(.success(GRPCResult.success))
|
||||
default:
|
||||
result(.failure(LightWalletServiceError.mapCode(status)))
|
||||
}
|
||||
case .failure(let error):
|
||||
result(.failure(LightWalletServiceError.genericError(error: error)))
|
||||
}
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
public func blockStream(
|
||||
startHeight: BlockHeight,
|
||||
endHeight: BlockHeight
|
||||
|
@ -625,9 +357,7 @@ extension LightWalletGRPCService: LightWalletServiceNonBlockingAPI {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LightWalletGRPCService: LightWalletService {
|
||||
public func closeConnection() {
|
||||
_ = channel.close()
|
||||
}
|
||||
|
|
|
@ -101,117 +101,40 @@ public protocol LightWalletServiceResponse {
|
|||
|
||||
extension SendResponse: LightWalletServiceResponse {}
|
||||
|
||||
/// Blocking API - used for the testing purposes
|
||||
public protocol LightWalletServiceBlockingAPI {
|
||||
/// Returns the info for this lightwalletd server (blocking)
|
||||
func getInfo() throws -> LightWalletdInfo
|
||||
public protocol LightWalletService {
|
||||
/// Returns the info for this lightwalletd server
|
||||
func getInfo() async throws -> LightWalletdInfo
|
||||
|
||||
///
|
||||
/// Return the latest block height known to the service.
|
||||
/// - Parameter result: a result containing the height or an Error
|
||||
/// Blocking API
|
||||
func latestBlockHeight() throws -> BlockHeight
|
||||
|
||||
/// Return the given range of blocks.
|
||||
///
|
||||
/// - Parameter range: the inclusive range to fetch.
|
||||
/// For instance if 1..5 is given, then every block in that will be fetched, including 1 and 5.
|
||||
func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock]
|
||||
|
||||
|
||||
/// Submits a raw transaction over lightwalletd. Blocking
|
||||
/// - Parameter spendTransaction: data representing the transaction to be sent
|
||||
/// - Throws: LightWalletServiceError
|
||||
/// - Returns: LightWalletServiceResponse
|
||||
func submit(spendTransaction: Data) throws -> LightWalletServiceResponse
|
||||
|
||||
/// Gets a transaction by id
|
||||
/// - Parameter txId: data representing the transaction ID
|
||||
/// - Throws: LightWalletServiceError
|
||||
/// - Returns: LightWalletServiceResponse
|
||||
func fetchTransaction(txId: Data) throws -> TransactionEntity
|
||||
|
||||
func fetchUTXOs(
|
||||
for tAddress: String,
|
||||
height: BlockHeight
|
||||
) throws -> [UnspentTransactionOutputEntity]
|
||||
|
||||
func fetchUTXOs(
|
||||
for tAddresses: [String],
|
||||
height: BlockHeight
|
||||
) throws -> [UnspentTransactionOutputEntity]
|
||||
}
|
||||
|
||||
public protocol LightWalletServiceNonBlockingAPI {
|
||||
/// Returns the info for this lightwalletd server
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `getInfoAsync()` instead.")
|
||||
func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void)
|
||||
func getInfoAsync() async throws -> LightWalletdInfo
|
||||
|
||||
///
|
||||
/// Return the latest block height known to the service.
|
||||
/// - Parameter result: a result containing the height or an Error
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `latestBlockHeightAsync()` instead.")
|
||||
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void)
|
||||
func latestBlockHeightAsync() async throws -> BlockHeight
|
||||
|
||||
/// Return the given range of blocks.
|
||||
/// - Parameter range: the inclusive range to fetch.
|
||||
/// For instance if 1..5 is given, then every block in that will be fetched, including 1 and 5.
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `blockRange(...) -> AsyncThrowingStream<ZcashCompactBlock, Error>` instead.")
|
||||
func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void)
|
||||
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error>
|
||||
|
||||
/// Submits a raw transaction over lightwalletd. Non-Blocking
|
||||
/// - Parameter spendTransaction: data representing the transaction to be sent
|
||||
/// - Parameter result: escaping closure that takes a result containing either LightWalletServiceResponse or LightWalletServiceError
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `submitAsync(spendTransaction: Data)` instead.")
|
||||
func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void)
|
||||
func submitAsync(spendTransaction: Data) async throws -> LightWalletServiceResponse
|
||||
func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse
|
||||
|
||||
/// Gets a transaction by id
|
||||
/// - Parameter txId: data representing the transaction ID
|
||||
/// - Parameter result: handler for the result
|
||||
/// - Throws: LightWalletServiceError
|
||||
/// - Returns: LightWalletServiceResponse
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `fetchTransactionAsync(txId: Data)` instead.")
|
||||
func fetchTransaction(
|
||||
txId: Data,
|
||||
result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void
|
||||
)
|
||||
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity
|
||||
func fetchTransaction(txId: Data) async throws -> TransactionEntity
|
||||
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `fetchUTXOs(for tAddress:...) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>` instead.")
|
||||
func fetchUTXOs(
|
||||
for tAddress: String,
|
||||
height: BlockHeight,
|
||||
result: @escaping(Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
)
|
||||
func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
|
||||
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `fetchUTXOs(for tAddresses:...) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>` instead.")
|
||||
func fetchUTXOs(
|
||||
for tAddresses: [String],
|
||||
height: BlockHeight,
|
||||
result: @escaping(Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
)
|
||||
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
|
||||
|
||||
@available(*, deprecated, message: "This function will be removed soon. Use the `blockStream(...) -> AsyncThrowingStream<ZcashCompactBlock, Error>` instead.")
|
||||
@discardableResult
|
||||
func blockStream(
|
||||
startHeight: BlockHeight,
|
||||
endHeight: BlockHeight,
|
||||
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
|
||||
handler: @escaping (ZcashCompactBlock) -> Void,
|
||||
progress: @escaping (BlockProgress) -> Void
|
||||
) -> CancellableCall
|
||||
|
||||
func blockStream(
|
||||
startHeight: BlockHeight,
|
||||
endHeight: BlockHeight
|
||||
) -> AsyncThrowingStream<ZcashCompactBlock, Error>
|
||||
}
|
||||
|
||||
public protocol LightWalletService: LightWalletServiceNonBlockingAPI, LightWalletServiceBlockingAPI {
|
||||
func closeConnection()
|
||||
}
|
||||
|
|
|
@ -585,26 +585,23 @@ public class SDKSynchronizer: Synchronizer {
|
|||
try blockProcessor.downloader.latestBlockHeight()
|
||||
}
|
||||
|
||||
public func latestUTXOs(address: String, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void) {
|
||||
public func latestUTXOs(address: String) async throws -> [UnspentTransactionOutputEntity] {
|
||||
guard initializer.isValidTransparentAddress(address) else {
|
||||
result(.failure(SynchronizerError.generalError(message: "invalid t-address")))
|
||||
return
|
||||
throw SynchronizerError.generalError(message: "invalid t-address")
|
||||
}
|
||||
|
||||
initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.saplingActivationHeight) { [weak self] fetchResult in
|
||||
guard let self = self else { return }
|
||||
switch fetchResult {
|
||||
case .success(let utxos):
|
||||
do {
|
||||
try self.utxoRepository.clearAll(address: address)
|
||||
try self.utxoRepository.store(utxos: utxos)
|
||||
result(.success(utxos))
|
||||
} catch {
|
||||
result(.failure(SynchronizerError.generalError(message: "\(error)")))
|
||||
}
|
||||
case .failure(let error):
|
||||
result(.failure(SynchronizerError.connectionFailed(message: error)))
|
||||
let stream = initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.saplingActivationHeight)
|
||||
|
||||
do {
|
||||
var utxos: [UnspentTransactionOutputEntity] = []
|
||||
for try await transactionEntity in stream {
|
||||
utxos.append(transactionEntity)
|
||||
}
|
||||
try self.utxoRepository.clearAll(address: address)
|
||||
try self.utxoRepository.store(utxos: utxos)
|
||||
return utxos
|
||||
} catch {
|
||||
throw SynchronizerError.generalError(message: "\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
throw TransactionManagerError.internalInconsistency(storedTx)
|
||||
}
|
||||
|
||||
let response = try self.service.submit(spendTransaction: raw)
|
||||
let response = try await self.service.submit(spendTransaction: raw)
|
||||
let transaction = try self.update(transaction: storedTx, on: response)
|
||||
|
||||
guard response.errorCode >= 0 else {
|
||||
|
|
|
@ -46,7 +46,7 @@ class BlockDownloaderTests: XCTestCase {
|
|||
|
||||
let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
|
||||
do {
|
||||
try await downloader.downloadBlockRangeAsync(range)
|
||||
try await downloader.downloadBlockRange(range)
|
||||
|
||||
// check what was 'stored'
|
||||
let latestHeight = try await self.storage.latestHeightAsync()
|
||||
|
@ -59,33 +59,6 @@ class BlockDownloaderTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
func testSmallDownload() {
|
||||
let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
|
||||
let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
|
||||
|
||||
let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
|
||||
var latest: BlockHeight = 0
|
||||
|
||||
do {
|
||||
latest = try downloader.lastDownloadedBlockHeight()
|
||||
} catch {
|
||||
XCTFail(error.localizedDescription)
|
||||
}
|
||||
|
||||
XCTAssertEqual(latest, BlockHeight.empty())
|
||||
XCTAssertNoThrow(try downloader.downloadBlockRange(range))
|
||||
|
||||
var currentLatest: BlockHeight = 0
|
||||
do {
|
||||
currentLatest = try downloader.lastDownloadedBlockHeight()
|
||||
} catch {
|
||||
XCTFail("latest block failed")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(currentLatest, upperRange )
|
||||
}
|
||||
|
||||
func testFailure() async {
|
||||
let awfulDownloader = CompactBlockDownloader(
|
||||
service: AwfulLightWalletService(
|
||||
|
@ -101,7 +74,7 @@ class BlockDownloaderTests: XCTestCase {
|
|||
let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
|
||||
|
||||
do {
|
||||
try await awfulDownloader.downloadBlockRangeAsync(range)
|
||||
try await awfulDownloader.downloadBlockRange(range)
|
||||
} catch {
|
||||
XCTAssertNotNil(error)
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@ class BlockStreamingTest: XCTestCase {
|
|||
try? FileManager.default.removeItem(at: __dataDbURL())
|
||||
}
|
||||
|
||||
func testStream() throws {
|
||||
let expectation = XCTestExpectation(description: "blockstream expectation")
|
||||
|
||||
func testStream() async throws {
|
||||
let service = LightWalletGRPCService(
|
||||
host: LightWalletEndpointBuilder.eccTestnet.host,
|
||||
port: 9067,
|
||||
|
@ -36,23 +34,19 @@ class BlockStreamingTest: XCTestCase {
|
|||
|
||||
let startHeight = latestHeight - 100_000
|
||||
var blocks: [ZcashCompactBlock] = []
|
||||
service.blockStream(startHeight: startHeight, endHeight: latestHeight) { result in
|
||||
expectation.fulfill()
|
||||
switch result {
|
||||
case .success(let status):
|
||||
XCTAssertEqual(GRPCResult.success, status)
|
||||
case .failure(let error):
|
||||
XCTFail("failed with error: \(error)")
|
||||
let stream = service.blockStream(startHeight: startHeight, endHeight: latestHeight)
|
||||
|
||||
do {
|
||||
for try await compactBlock in stream {
|
||||
print("received block \(compactBlock.height)")
|
||||
blocks.append(compactBlock)
|
||||
print("progressHeight: \(compactBlock.height)")
|
||||
print("startHeight: \(startHeight)")
|
||||
print("targetHeight: \(latestHeight)")
|
||||
}
|
||||
} handler: { compactBlock in
|
||||
print("received block \(compactBlock.height)")
|
||||
blocks.append(compactBlock)
|
||||
} progress: { progressReport in
|
||||
print("progressHeight: \(progressReport.progressHeight)")
|
||||
print("startHeight: \(progressReport.startHeight)")
|
||||
print("targetHeight: \(progressReport.targetHeight)")
|
||||
} catch {
|
||||
XCTFail("failed with error: \(error)")
|
||||
}
|
||||
wait(for: [expectation], timeout: 1000)
|
||||
}
|
||||
|
||||
func testStreamCancellation() async throws {
|
||||
|
|
|
@ -38,29 +38,6 @@ class LightWalletServiceTests: XCTestCase {
|
|||
// wait(for: [expect], timeout: 20)
|
||||
// }
|
||||
|
||||
func testHundredBlocks() {
|
||||
let expect = XCTestExpectation(description: self.description)
|
||||
let count = 99
|
||||
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
|
||||
let upperRange: BlockHeight = network.constants.saplingActivationHeight + count
|
||||
let blockRange = lowerRange ... upperRange
|
||||
|
||||
service.blockRange(blockRange) { result in
|
||||
expect.fulfill()
|
||||
switch result {
|
||||
case .failure(let error):
|
||||
XCTFail("failed with error \(error)")
|
||||
|
||||
case .success(let blocks):
|
||||
XCTAssertEqual(blocks.count, blockRange.count)
|
||||
XCTAssertEqual(blocks[0].height, lowerRange)
|
||||
XCTAssertEqual(blocks.last!.height, upperRange)
|
||||
}
|
||||
}
|
||||
|
||||
wait(for: [expect], timeout: 10)
|
||||
}
|
||||
|
||||
func testHundredBlocks() async throws {
|
||||
let count = 99
|
||||
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
|
||||
|
@ -76,19 +53,6 @@ class LightWalletServiceTests: XCTestCase {
|
|||
XCTAssertEqual(blocks.last!.height, upperRange)
|
||||
}
|
||||
|
||||
func testSyncBlockRange() {
|
||||
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
|
||||
let upperRange: BlockHeight = network.constants.saplingActivationHeight + 99
|
||||
let blockRange = lowerRange ... upperRange
|
||||
|
||||
do {
|
||||
let blocks = try service.blockRange(blockRange)
|
||||
XCTAssertEqual(blocks.count, blockRange.count)
|
||||
} catch {
|
||||
XCTFail("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testSyncBlockRange() async throws {
|
||||
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
|
||||
let upperRange: BlockHeight = network.constants.saplingActivationHeight + 99
|
||||
|
@ -101,21 +65,6 @@ class LightWalletServiceTests: XCTestCase {
|
|||
XCTAssertEqual(blocks.count, blockRange.count)
|
||||
}
|
||||
|
||||
func testLatestBlock() {
|
||||
let expect = XCTestExpectation(description: self.description)
|
||||
service.latestBlockHeight { result in
|
||||
expect.fulfill()
|
||||
switch result {
|
||||
case .failure(let e):
|
||||
XCTFail("error: \(e)")
|
||||
case .success(let height):
|
||||
XCTAssertTrue(height > self.network.constants.saplingActivationHeight)
|
||||
}
|
||||
}
|
||||
|
||||
wait(for: [expect], timeout: 10)
|
||||
}
|
||||
|
||||
func testLatestBlock() async throws {
|
||||
let height = try await service.latestBlockHeightAsync()
|
||||
XCTAssertTrue(height > self.network.constants.saplingActivationHeight)
|
||||
|
|
|
@ -25,14 +25,14 @@ class CompactBlockStorageTests: XCTestCase {
|
|||
XCTAssertEqual(latestHeight, BlockHeight.empty())
|
||||
}
|
||||
|
||||
func testStoreThousandBlocks() {
|
||||
func testStoreThousandBlocks() async {
|
||||
let initialHeight = try! compactBlockDao.latestHeight()
|
||||
let startHeight = self.network.constants.saplingActivationHeight
|
||||
let blockCount = Int(1_000)
|
||||
let finalHeight = startHeight + blockCount
|
||||
|
||||
do {
|
||||
try TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
try await TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
} catch {
|
||||
XCTFail("seed faild with error: \(error)")
|
||||
return
|
||||
|
@ -49,14 +49,14 @@ class CompactBlockStorageTests: XCTestCase {
|
|||
let blockCount = Int(1_000)
|
||||
let finalHeight = startHeight + blockCount
|
||||
|
||||
try TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
try await TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
|
||||
let latestHeight = try await compactBlockDao.latestHeightAsync()
|
||||
XCTAssertNotEqual(initialHeight, latestHeight)
|
||||
XCTAssertEqual(latestHeight, finalHeight)
|
||||
}
|
||||
|
||||
func testStoreOneBlockFromEmpty() {
|
||||
func testStoreOneBlockFromEmpty() async {
|
||||
let initialHeight = try! compactBlockDao.latestHeight()
|
||||
guard initialHeight == BlockHeight.empty() else {
|
||||
XCTFail("database not empty, latest height: \(initialHeight)")
|
||||
|
@ -68,7 +68,11 @@ class CompactBlockStorageTests: XCTestCase {
|
|||
XCTFail("could not create randem block with height: \(expectedHeight)")
|
||||
return
|
||||
}
|
||||
XCTAssertNoThrow(try compactBlockDao.write(blocks: [block]))
|
||||
do {
|
||||
try await compactBlockDao.write(blocks: [block])
|
||||
} catch {
|
||||
XCTFail("unexpected testStoreOneBlockFromEmpty fail")
|
||||
}
|
||||
|
||||
do {
|
||||
let result = try compactBlockDao.latestHeight()
|
||||
|
@ -91,27 +95,27 @@ class CompactBlockStorageTests: XCTestCase {
|
|||
XCTFail("could not create randem block with height: \(expectedHeight)")
|
||||
return
|
||||
}
|
||||
try await compactBlockDao.writeAsync(blocks: [block])
|
||||
try await compactBlockDao.write(blocks: [block])
|
||||
|
||||
let result = try await compactBlockDao.latestHeightAsync()
|
||||
XCTAssertEqual(result, expectedHeight)
|
||||
}
|
||||
|
||||
func testRewindTo() {
|
||||
func testRewindTo() async {
|
||||
let startHeight = self.network.constants.saplingActivationHeight
|
||||
let blockCount = Int(1_000)
|
||||
let finalHeight = startHeight + blockCount
|
||||
|
||||
do {
|
||||
try TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
try await TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
} catch {
|
||||
XCTFail("seed faild with error: \(error)")
|
||||
return
|
||||
}
|
||||
let rewindHeight = BlockHeight(finalHeight - 233)
|
||||
|
||||
XCTAssertNoThrow(try compactBlockDao.rewind(to: rewindHeight))
|
||||
do {
|
||||
try await compactBlockDao.rewindAsync(to: rewindHeight)
|
||||
let latestHeight = try compactBlockDao.latestHeight()
|
||||
XCTAssertEqual(latestHeight, rewindHeight - 1)
|
||||
} catch {
|
||||
|
@ -124,7 +128,7 @@ class CompactBlockStorageTests: XCTestCase {
|
|||
let blockCount = Int(1_000)
|
||||
let finalHeight = startHeight + blockCount
|
||||
|
||||
try TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
try await TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
|
||||
let rewindHeight = BlockHeight(finalHeight - 233)
|
||||
|
||||
try await compactBlockDao.rewindAsync(to: rewindHeight)
|
||||
|
|
|
@ -58,105 +58,25 @@ class DarksideWalletService: LightWalletService {
|
|||
self.init(endpoint: LightWalletEndpointBuilder.default)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func blockStream(
|
||||
startHeight: BlockHeight,
|
||||
endHeight: BlockHeight,
|
||||
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
|
||||
handler: @escaping (ZcashCompactBlock) -> Void,
|
||||
progress: @escaping (BlockProgress) -> Void
|
||||
) -> CancellableCall {
|
||||
return service.blockStream(
|
||||
startHeight: startHeight,
|
||||
endHeight: endHeight,
|
||||
result: result,
|
||||
handler: handler,
|
||||
progress: progress
|
||||
)
|
||||
}
|
||||
|
||||
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
||||
service.blockStream(startHeight: startHeight, endHeight: endHeight)
|
||||
}
|
||||
|
||||
func getInfo() throws -> LightWalletdInfo {
|
||||
try service.getInfo()
|
||||
}
|
||||
|
||||
func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) {
|
||||
service.getInfo(result: result)
|
||||
}
|
||||
|
||||
func closeConnection() {
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
try service.fetchUTXOs(for: tAddress, height: height)
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
try service.fetchUTXOs(for: tAddresses, height: height)
|
||||
}
|
||||
|
||||
func fetchUTXOs(
|
||||
for tAddress: String,
|
||||
height: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
) {
|
||||
service.fetchUTXOs(for: tAddress, height: height, result: result)
|
||||
func closeConnection() {
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
|
||||
service.fetchUTXOs(for: tAddress, height: height)
|
||||
}
|
||||
|
||||
func fetchUTXOs(
|
||||
for tAddresses: [String],
|
||||
height: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
) {
|
||||
service.fetchUTXOs(for: tAddresses, height: height, result: result)
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
|
||||
service.fetchUTXOs(for: tAddresses, height: height)
|
||||
}
|
||||
|
||||
func fetchTransaction(txId: Data) throws -> TransactionEntity {
|
||||
try service.fetchTransaction(txId: txId)
|
||||
}
|
||||
|
||||
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) {
|
||||
service.fetchTransaction(txId: txId, result: result)
|
||||
}
|
||||
|
||||
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> 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<LightWalletServiceResponse, LightWalletServiceError>) -> 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)
|
||||
}
|
||||
|
@ -234,10 +154,10 @@ class DarksideWalletService: LightWalletService {
|
|||
_ = try darksideService.clearAddressUtxo(Empty(), callOptions: nil).response.wait()
|
||||
}
|
||||
|
||||
func getInfoAsync() async throws -> LightWalletdInfo {
|
||||
try service.getInfo()
|
||||
func getInfo() async throws -> LightWalletdInfo {
|
||||
try await service.getInfo()
|
||||
}
|
||||
|
||||
|
||||
func latestBlockHeightAsync() async throws -> BlockHeight {
|
||||
try service.latestBlockHeight()
|
||||
}
|
||||
|
@ -246,12 +166,13 @@ class DarksideWalletService: LightWalletService {
|
|||
service.blockRange(range)
|
||||
}
|
||||
|
||||
func submitAsync(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
try service.submit(spendTransaction: spendTransaction)
|
||||
/// 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) async throws -> LightWalletServiceResponse {
|
||||
try await service.submit(spendTransaction: spendTransaction)
|
||||
}
|
||||
|
||||
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity {
|
||||
try service.fetchTransaction(txId: txId)
|
||||
func fetchTransaction(txId: Data) async throws -> TransactionEntity {
|
||||
try await service.fetchTransaction(txId: txId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,67 +24,17 @@ class MockLightWalletService: LightWalletService {
|
|||
var mockLightDInfo: LightWalletdInfo?
|
||||
var queue = DispatchQueue(label: "mock service queue")
|
||||
|
||||
@discardableResult
|
||||
func blockStream(
|
||||
startHeight: BlockHeight,
|
||||
endHeight: BlockHeight,
|
||||
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
|
||||
handler: @escaping (ZcashCompactBlock) -> Void,
|
||||
progress: @escaping (BlockProgress) -> Void
|
||||
) -> CancellableCall {
|
||||
return MockCancellable()
|
||||
}
|
||||
|
||||
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
||||
AsyncThrowingStream { _ in }
|
||||
}
|
||||
|
||||
func getInfo() throws -> LightWalletdInfo {
|
||||
guard let info = mockLightDInfo else {
|
||||
throw LightWalletServiceError.generalError(message: "Not Implemented")
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) {
|
||||
queue.async { [weak self] in
|
||||
guard let info = self?.mockLightDInfo else {
|
||||
result(.failure(LightWalletServiceError.generalError(message: "Not Implemented")))
|
||||
return
|
||||
}
|
||||
result(.success(info))
|
||||
}
|
||||
}
|
||||
|
||||
func closeConnection() {
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
[]
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
|
||||
[]
|
||||
}
|
||||
|
||||
func fetchUTXOs(
|
||||
for tAddress: String,
|
||||
height: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
) {
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
|
||||
AsyncThrowingStream { _ in }
|
||||
}
|
||||
|
||||
func fetchUTXOs(
|
||||
for tAddresses: [String],
|
||||
height: BlockHeight,
|
||||
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
|
||||
) {
|
||||
}
|
||||
|
||||
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
|
||||
AsyncThrowingStream { _ in }
|
||||
}
|
||||
|
@ -98,48 +48,17 @@ class MockLightWalletService: LightWalletService {
|
|||
self.service = service
|
||||
}
|
||||
|
||||
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
|
||||
result(.success(self.latestHeight))
|
||||
}
|
||||
}
|
||||
|
||||
func latestBlockHeight() throws -> BlockHeight {
|
||||
return self.latestHeight
|
||||
}
|
||||
|
||||
func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
|
||||
self.service.blockRange(range, result: result)
|
||||
}
|
||||
|
||||
func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] {
|
||||
try self.service.blockRange(range)
|
||||
}
|
||||
|
||||
func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
|
||||
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 1) {
|
||||
result(.success(LightWalletServiceMockResponse(errorCode: 0, errorMessage: "", unknownFields: UnknownStorage())))
|
||||
}
|
||||
}
|
||||
|
||||
func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
|
||||
return LightWalletServiceMockResponse(errorCode: 0, errorMessage: "", unknownFields: UnknownStorage())
|
||||
}
|
||||
|
||||
func fetchTransaction(txId: Data) throws -> TransactionEntity {
|
||||
Transaction(id: 1, transactionId: Data(), created: "Today", transactionIndex: 1, expiryHeight: -1, minedHeight: -1, raw: nil)
|
||||
}
|
||||
|
||||
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) {
|
||||
}
|
||||
|
||||
func getInfoAsync() async throws -> LightWalletdInfo {
|
||||
func getInfo() async throws -> LightWalletdInfo {
|
||||
guard let info = mockLightDInfo else {
|
||||
throw LightWalletServiceError.generalError(message: "Not Implemented")
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
|
||||
func latestBlockHeightAsync() async throws -> BlockHeight {
|
||||
latestHeight
|
||||
}
|
||||
|
@ -148,19 +67,11 @@ class MockLightWalletService: LightWalletService {
|
|||
service.blockRange(range)
|
||||
}
|
||||
|
||||
func submitAsync(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
LightWalletServiceMockResponse(errorCode: 0, errorMessage: "", unknownFields: UnknownStorage())
|
||||
}
|
||||
|
||||
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity {
|
||||
func fetchTransaction(txId: Data) async throws -> TransactionEntity {
|
||||
Transaction(id: 1, transactionId: Data(), created: "Today", transactionIndex: 1, expiryHeight: -1, minedHeight: -1, raw: nil)
|
||||
}
|
||||
|
||||
func fetchUTXOsAsync(for tAddress: String, height: BlockHeight) async throws -> [UnspentTransactionOutputEntity] {
|
||||
[]
|
||||
}
|
||||
|
||||
func fetchUTXOsAsync(for tAddresses: [String], height: BlockHeight) async throws -> [UnspentTransactionOutputEntity] {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
|
|||
latestBlockHeight
|
||||
}
|
||||
|
||||
func writeAsync(blocks: [ZcashCompactBlock]) async throws {
|
||||
func write(blocks: [ZcashCompactBlock]) async throws {
|
||||
fakeSave(blocks: blocks)
|
||||
}
|
||||
|
||||
|
@ -26,10 +26,6 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
|
|||
return self.latestBlockHeight
|
||||
}
|
||||
|
||||
func write(blocks: [ZcashCompactBlock]) throws {
|
||||
fakeSave(blocks: blocks)
|
||||
}
|
||||
|
||||
func rewind(to height: BlockHeight) throws {
|
||||
fakeRewind(to: height)
|
||||
}
|
||||
|
|
|
@ -17,52 +17,26 @@ class AwfulLightWalletService: MockLightWalletService {
|
|||
throw LightWalletServiceError.criticalError
|
||||
}
|
||||
|
||||
override func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] {
|
||||
override func latestBlockHeightAsync() async throws -> BlockHeight {
|
||||
throw LightWalletServiceError.invalidBlock
|
||||
}
|
||||
|
||||
override func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
result(.failure(LightWalletServiceError.invalidBlock))
|
||||
}
|
||||
}
|
||||
|
||||
override func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
result(.failure(LightWalletServiceError.invalidBlock))
|
||||
}
|
||||
}
|
||||
|
||||
override func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
||||
AsyncThrowingStream { continuation in continuation.finish(throwing: LightWalletServiceError.invalidBlock) }
|
||||
}
|
||||
|
||||
override func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
result(.failure(LightWalletServiceError.invalidBlock))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Submits a raw transaction over lightwalletd. Blocking
|
||||
*/
|
||||
override func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
|
||||
/// Submits a raw transaction over lightwalletd.
|
||||
override func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
throw LightWalletServiceError.invalidBlock
|
||||
}
|
||||
}
|
||||
|
||||
class SlightlyBadLightWalletService: MockLightWalletService {
|
||||
override func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
result(.success(LightWalletServiceMockResponse.error))
|
||||
}
|
||||
}
|
||||
extension LightWalletServiceMockResponse: Error { }
|
||||
|
||||
/**
|
||||
Submits a raw transaction over lightwalletd. Blocking
|
||||
*/
|
||||
override func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
|
||||
LightWalletServiceMockResponse.error
|
||||
class SlightlyBadLightWalletService: MockLightWalletService {
|
||||
/// Submits a raw transaction over lightwalletd.
|
||||
override func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||
throw LightWalletServiceMockResponse.error
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,12 +92,12 @@ class TestDbBuilder {
|
|||
return ReceivedNotesSQLDAO(dbProvider: provider)
|
||||
}
|
||||
|
||||
static func seed(db: CompactBlockRepository, with blockRange: CompactBlockRange) throws {
|
||||
static func seed(db: CompactBlockRepository, with blockRange: CompactBlockRange) async throws {
|
||||
guard let blocks = StubBlockCreator.createBlockRange(blockRange) else {
|
||||
throw TestBuilderError.generalError
|
||||
}
|
||||
|
||||
try db.write(blocks: blocks)
|
||||
try await db.write(blocks: blocks)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue