Merge branch 'master' into merge_master_to_zip_316

This commit is contained in:
Francisco Gindre 2022-09-01 17:51:46 -03:00
commit d737ddefa9
32 changed files with 1987 additions and 809 deletions

View File

@ -30,7 +30,7 @@ enum DemoAppConfig {
}()
static var endpoint: LightWalletEndpoint {
return LightWalletEndpoint(address: self.host, port: self.port, secure: true)
return LightWalletEndpoint(address: self.host, port: self.port, secure: true, streamingCallTimeoutInMillis: 10 * 60 * 60 * 1000)
}
}

View File

@ -28,18 +28,13 @@ class LatestHeightViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
service.latestBlockHeight { result in
switch result {
case .success(let height):
DispatchQueue.main.async { [weak self] in
self?.model = height
}
case .failure(let error):
DispatchQueue.main.async { [weak self] in
self?.fail(error)
}
/// Note: It's safe to modify model or call fail() because all methods of a UIViewController are MainActor methods by default.
Task {
do {
model = try await service.latestBlockHeightAsync()
} catch {
fail(error as? LightWalletServiceError ?? .unknown)
}
}
}

View File

@ -1,11 +1,11 @@
// swift-tools-version:5.5
// swift-tools-version:5.6
import PackageDescription
let package = Package(
name: "ZcashLightClientKit",
platforms: [
.iOS(.v13),
.macOS(.v10_12)
.macOS(.v10_15)
],
products: [
.library(

View File

@ -85,39 +85,28 @@ extension CompactBlockStorage: CompactBlockRepository {
try latestBlockHeight()
}
func latestHeight(result: @escaping (Swift.Result<BlockHeight, Error>) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
do {
result(.success(try self.latestBlockHeight()))
} catch {
result(.failure(error))
}
func latestHeightAsync() async throws -> BlockHeight {
let task = Task(priority: .userInitiated) {
try latestBlockHeight()
}
return try await task.value
}
func write(blocks: [ZcashCompactBlock]) throws {
try insert(blocks)
}
func write(blocks: [ZcashCompactBlock], completion: ((Error?) -> Void)?) {
DispatchQueue.global(qos: .userInitiated).async {
do {
try self.insert(blocks)
completion?(nil)
} catch {
completion?(error)
}
func writeAsync(blocks: [ZcashCompactBlock]) async throws {
let task = Task(priority: .userInitiated) {
try insert(blocks)
}
try await task.value
}
func rewind(to height: BlockHeight, completion: ((Error?) -> Void)?) {
DispatchQueue.global(qos: .userInitiated).async {
do {
try self.rewind(to: height)
completion?(nil)
} catch {
completion?(error)
}
func rewindAsync(to height: BlockHeight) async throws {
let task = Task(priority: .userInitiated) {
try rewind(to: height)
}
try await task.value
}
}

View File

@ -16,59 +16,55 @@ enum CompactBlockDownloadError: Error {
Represents what a compact block downloaded should provide to its clients
*/
public protocol CompactBlockDownloading {
/**
Downloads and stores the given block range.
Non-Blocking
*/
func downloadBlockRange(
_ heightRange: CompactBlockRange,
completion: @escaping (Error?) -> Void
)
/**
Remove newer blocks and go back to the given height
- Parameters:
- height: the given height to rewind to
- completion: block to be executed after completing rewind
*/
func rewind(to height: BlockHeight, completion: @escaping (Error?) -> Void)
/**
returns the height of the latest compact block stored locally
BlockHeight.empty() if no blocks are stored yet
non-blocking
*/
func lastDownloadedBlockHeight(result: @escaping (Result<BlockHeight, Error>) -> Void)
/**
Returns the last height on the blockchain
Non-blocking
*/
func latestBlockHeight(result: @escaping (Result<BlockHeight, Error>) -> Void)
/**
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
/**
Restore the download progress up to the given height.
*/
func rewind(to height: BlockHeight) throws
/**
Remove newer blocks and go back to the given height
- Parameter height: the given height to rewind to
*/
func rewindAsync(to height: BlockHeight) async throws
/**
Returns the height of the latest compact block stored locally.
BlockHeight.empty() if no blocks are stored yet
Blocking
*/
func lastDownloadedBlockHeight() throws -> BlockHeight
/**
returns the height of the latest compact block stored locally
BlockHeight.empty() if no blocks are stored yet
non-blocking
*/
func lastDownloadedBlockHeightAsync() async throws -> BlockHeight
/**
Returns the latest block height
Blocking
*/
func latestBlockHeight() throws -> BlockHeight
/**
Returns the last height on the blockchain
Non-blocking
*/
func latestBlockHeightAsync() async throws -> BlockHeight
/**
Gets the transaction for the Id given
@ -81,17 +77,30 @@ public protocol CompactBlockDownloading {
/**
Gets the transaction for the Id given
- Parameter txId: Data representing the transaction Id
- Parameter result: a handler for the result of the operation
*/
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, Error>) -> Void)
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 fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
func fetchUnspentTransactionOutputs(tAddresses: [String], startHeight: BlockHeight) throws -> [UnspentTransactionOutputEntity]
func fetchUnspentTransactionOutputs(tAddresses: [String], startHeight: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], Error>) -> Void)
// 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>
func closeConnection()
}
@ -137,6 +146,10 @@ extension CompactBlockDownloader: CompactBlockDownloading {
}
}
}
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)
@ -153,68 +166,54 @@ extension CompactBlockDownloader: CompactBlockDownloading {
}
}
func latestBlockHeight(result: @escaping (Result<BlockHeight, Error>) -> Void) {
lightwalletService.latestBlockHeight { fetchResult in
switch fetchResult {
case .failure(let error):
result(.failure(error))
case .success(let height):
result(.success(height))
}
}
func fetchUnspentTransactionOutputs(tAddress: String, startHeight: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
lightwalletService.fetchUTXOs(for: tAddress, height: startHeight)
}
func latestBlockHeightAsync() async throws -> BlockHeight {
try await lightwalletService.latestBlockHeightAsync()
}
func latestBlockHeight() throws -> BlockHeight {
try lightwalletService.latestBlockHeight()
}
/**
Downloads and stores the given block range.
Non-Blocking
*/
func downloadBlockRange(
_ heightRange: CompactBlockRange,
completion: @escaping (Error?) -> Void
) {
lightwalletService.blockRange(heightRange) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .failure(let error):
completion(error)
case .success(let compactBlocks):
self.storage.write(blocks: compactBlocks) { storeError in
completion(storeError)
}
}
}
}
func downloadBlockRange(_ range: CompactBlockRange) throws {
let blocks = try lightwalletService.blockRange(range)
try storage.write(blocks: blocks)
}
func rewind(to height: BlockHeight, completion: @escaping (Error?) -> Void) {
storage.rewind(to: height) { e in
completion(e)
}
}
func lastDownloadedBlockHeight(result: @escaping (Result<BlockHeight, Error>) -> Void) {
storage.latestHeight { heightResult in
switch heightResult {
case .failure(let e):
result(.failure(CompactBlockDownloadError.generalError(error: e)))
return
case .success(let height):
result(.success(height))
func downloadBlockRangeAsync( _ 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)
} catch {
throw error
}
}
func rewindAsync(to height: BlockHeight) async throws {
do {
try await storage.rewindAsync(to: height)
} catch {
throw error
}
}
func lastDownloadedBlockHeightAsync() async throws -> BlockHeight {
do {
let latestHeight = try await storage.latestHeightAsync()
return latestHeight
} catch {
throw CompactBlockDownloadError.generalError(error: error)
}
}
func rewind(to height: BlockHeight) throws {
try self.storage.rewind(to: height)
}
@ -227,14 +226,7 @@ extension CompactBlockDownloader: CompactBlockDownloading {
try lightwalletService.fetchTransaction(txId: txId)
}
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, Error>) -> Void) {
lightwalletService.fetchTransaction(txId: txId) { txResult in
switch txResult {
case .failure(let error):
result(.failure(error))
case .success(let transaction):
result(.success(transaction))
}
}
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity {
try await lightwalletService.fetchTransactionAsync(txId: txId)
}
}

View File

@ -14,7 +14,9 @@ class CompactBlockDownloadOperation: ZcashOperation {
private var downloader: CompactBlockDownloading
private var range: CompactBlockRange
private var cancelableTask: Task<Void, Error>?
private var done = false
required init(downloader: CompactBlockDownloading, range: CompactBlockRange) {
self.range = range
self.downloader = downloader
@ -28,12 +30,29 @@ class CompactBlockDownloadOperation: ZcashOperation {
return
}
self.startedHandler?()
do {
try downloader.downloadBlockRange(range)
} catch {
self.error = error
self.fail()
cancelableTask = Task {
do {
try await downloader.downloadBlockRangeAsync(range)
self.done = true
} catch {
self.fail(error: error)
}
}
while !done && !isCancelled {
sleep(1)
}
}
override func fail(error: Error? = nil) {
self.cancelableTask?.cancel()
super.fail(error: error)
}
override func cancel() {
self.cancelableTask?.cancel()
super.cancel()
}
}
@ -52,7 +71,7 @@ class CompactBlockStreamDownloadOperation: ZcashOperation {
private var storage: CompactBlockStorage
private var service: LightWalletService
private var done = false
private var cancelable: CancellableCall?
private var cancelableTask: Task<Void, Error>?
private var startHeight: BlockHeight?
private var targetHeight: BlockHeight?
private var blockBufferSize: Int
@ -93,71 +112,61 @@ class CompactBlockStreamDownloadOperation: ZcashOperation {
self.name = "Download Stream Operation"
}
// swiftlint:disable cyclomatic_complexity
override func main() {
guard !shouldCancel() else {
cancel()
return
}
self.startedHandler?()
do {
if self.targetHeight == nil {
self.targetHeight = try service.latestBlockHeight()
}
guard let latestHeight = self.targetHeight else {
throw LightWalletServiceError.generalError(message: "missing target height on block stream operation")
}
let latestDownloaded = try storage.latestHeight()
let startHeight = max(self.startHeight ?? BlockHeight.empty(), latestDownloaded)
self.cancelable = self.service.blockStream(startHeight: startHeight, endHeight: latestHeight) { [weak self] blockResult in
switch blockResult {
case .success(let result):
switch result {
case .success:
do {
try self?.flush()
self?.done = true
} catch {
self?.fail(error: error)
}
return
case .error(let e):
self?.fail(error: e)
}
case .failure(let e):
if case .userCancelled = e {
self?.done = true
} else {
self?.fail(error: e)
}
cancelableTask = Task {
do {
if self.targetHeight == nil {
self.targetHeight = try await service.latestBlockHeightAsync()
}
} handler: {[weak self] block in
guard let self = self else { return }
do {
try self.cache(block, flushCache: false)
} catch {
guard let latestHeight = self.targetHeight else {
throw LightWalletServiceError.generalError(message: "missing target height on block stream operation")
}
let latestDownloaded = try await storage.latestHeightAsync()
let startHeight = max(self.startHeight ?? BlockHeight.empty(), latestDownloaded)
let stream = service.blockStream(
startHeight: startHeight,
endHeight: latestHeight
)
for try await zcashCompactBlock in stream {
try self.cache(zcashCompactBlock, flushCache: false)
let progress = BlockProgress(
startHeight: startHeight,
targetHeight: latestHeight,
progressHeight: zcashCompactBlock.height
)
self.progressDelegate?.progressUpdated(.download(progress))
}
try self.flush()
self.done = true
} catch {
if let err = error as? LightWalletServiceError, case .userCancelled = err {
self.done = true
} else {
self.fail(error: error)
}
} progress: { progress in
self.progressDelegate?.progressUpdated(.download(progress))
}
while !done && !isCancelled {
sleep(1)
}
} catch {
self.fail(error: error)
}
while !done && !isCancelled {
sleep(1)
}
}
override func fail(error: Error? = nil) {
self.cancelable?.cancel()
self.cancelableTask?.cancel()
super.fail(error: error)
}
override func cancel() {
self.cancelable?.cancel()
self.cancelableTask?.cancel()
super.cancel()
}
@ -185,10 +194,11 @@ class CompactBlockBatchDownloadOperation: ZcashOperation {
override var isAsynchronous: Bool { false }
private var batch: Int
private var done = false
private var maxRetries: Int
private var storage: CompactBlockStorage
private var service: LightWalletService
private var cancelable: CancellableCall?
private var cancelableTask: Task<Void, Error>?
private var startHeight: BlockHeight
private var targetHeight: BlockHeight
@ -220,72 +230,85 @@ class CompactBlockBatchDownloadOperation: ZcashOperation {
return
}
self.startedHandler?()
do {
let localDownloadedHeight = try self.storage.latestHeight()
if localDownloadedHeight != BlockHeight.empty() && localDownloadedHeight > startHeight {
LoggerProxy.warn("provided startHeight (\(startHeight)) differs from local latest downloaded height (\(localDownloadedHeight))")
startHeight = localDownloadedHeight + 1
}
var currentHeight = startHeight
self.progressDelegate?.progressUpdated(
.download(
BlockProgress(
startHeight: currentHeight,
targetHeight: targetHeight,
progressHeight: currentHeight
)
)
)
while !isCancelled && currentHeight <= targetHeight {
var retries = 0
var success = true
var localError: Error?
let range = nextRange(currentHeight: currentHeight, targetHeight: targetHeight)
cancelableTask = Task {
do {
let localDownloadedHeight = try await self.storage.latestHeightAsync()
repeat {
do {
let blocks = try service.blockRange(range)
try storage.insert(blocks)
success = true
} catch {
success = false
localError = error
retries += 1
}
} while !isCancelled && !success && retries < maxRetries
if retries >= maxRetries {
throw CompactBlockBatchDownloadOperationError.batchDownloadFailed(range: range, error: localError)
if localDownloadedHeight != BlockHeight.empty() && localDownloadedHeight > startHeight {
LoggerProxy.warn("provided startHeight (\(startHeight)) differs from local latest downloaded height (\(localDownloadedHeight))")
startHeight = localDownloadedHeight + 1
}
var currentHeight = startHeight
self.progressDelegate?.progressUpdated(
.download(
BlockProgress(
startHeight: startHeight,
startHeight: currentHeight,
targetHeight: targetHeight,
progressHeight: range.upperBound
progressHeight: currentHeight
)
)
)
while !isCancelled && currentHeight <= targetHeight {
var retries = 0
var success = true
var localError: Error?
let range = nextRange(currentHeight: currentHeight, targetHeight: targetHeight)
repeat {
do {
let stream: AsyncThrowingStream<ZcashCompactBlock, Error> = service.blockRange(range)
currentHeight = range.upperBound + 1
var blocks: [ZcashCompactBlock] = []
for try await compactBlock in stream {
blocks.append(compactBlock)
}
try storage.insert(blocks)
success = true
} catch {
success = false
localError = error
retries += 1
}
} while !isCancelled && !success && retries < maxRetries
if retries >= maxRetries {
throw CompactBlockBatchDownloadOperationError.batchDownloadFailed(range: range, error: localError)
}
self.progressDelegate?.progressUpdated(
.download(
BlockProgress(
startHeight: startHeight,
targetHeight: targetHeight,
progressHeight: range.upperBound
)
)
)
currentHeight = range.upperBound + 1
}
self.done = true
} catch {
self.fail(error: error)
}
} catch {
self.fail(error: error)
}
while !done && !isCancelled {
sleep(1)
}
}
override func fail(error: Error? = nil) {
self.cancelable?.cancel()
self.cancelableTask?.cancel()
super.fail(error: error)
}
override func cancel() {
self.cancelable?.cancel()
self.cancelableTask?.cancel()
super.cancel()
}

View File

@ -29,7 +29,9 @@ class CompactBlockEnhancementOperation: ZcashOperation {
private(set) var network: NetworkType
private weak var progressDelegate: CompactBlockProgressDelegate?
private var dataDb: URL
private var cancelableTask: Task<Void, Error>?
private var done = false
init(
rustWelding: ZcashRustBackendWelding.Type,
dataDb: URL,
@ -58,44 +60,50 @@ class CompactBlockEnhancementOperation: ZcashOperation {
self.startedHandler?()
// fetch transactions
do {
guard let transactions = try repository.findTransactions(in: self.range, limit: Int.max), !transactions.isEmpty else {
LoggerProxy.debug("no transactions detected on range: \(range.printRange)")
return
}
for index in 0 ..< transactions.count {
let transaction = transactions[index]
var retry = true
while retry && self.retries < maxRetries {
do {
let confirmedTx = try enhance(transaction: transaction)
retry = false
self.reportProgress(
totalTransactions: transactions.count,
enhanced: index + 1,
txEnhanced: confirmedTx
)
} catch {
self.retries += 1
LoggerProxy.error("could not enhance txId \(transaction.transactionId.toHexStringTxId()) - Error: \(error)")
if retries > maxRetries {
throw error
cancelableTask = Task {
// fetch transactions
do {
guard let transactions = try repository.findTransactions(in: self.range, limit: Int.max), !transactions.isEmpty else {
LoggerProxy.debug("no transactions detected on range: \(range.printRange)")
return
}
for index in 0 ..< transactions.count {
let transaction = transactions[index]
var retry = true
while retry && self.retries < maxRetries {
do {
let confirmedTx = try await enhance(transaction: transaction)
retry = false
self.reportProgress(
totalTransactions: transactions.count,
enhanced: index + 1,
txEnhanced: confirmedTx
)
} catch {
self.retries += 1
LoggerProxy.error("could not enhance txId \(transaction.transactionId.toHexStringTxId()) - Error: \(error)")
if retries > maxRetries {
throw error
}
}
}
}
} catch {
LoggerProxy.error("error enhancing transactions! \(error)")
self.fail(error: error)
return
}
} catch {
LoggerProxy.error("error enhancing transactions! \(error)")
self.error = error
self.fail()
return
if let handler = self.txFoundHandler, let foundTxs = try? repository.findConfirmedTransactions(in: self.range, offset: 0, limit: Int.max) {
handler(foundTxs, self.range)
}
self.done = true
}
if let handler = self.txFoundHandler, let foundTxs = try? repository.findConfirmedTransactions(in: self.range, offset: 0, limit: Int.max) {
handler(foundTxs, self.range)
while !done && !isCancelled {
sleep(1)
}
}
@ -112,10 +120,10 @@ class CompactBlockEnhancementOperation: ZcashOperation {
)
}
func enhance(transaction: TransactionEntity) throws -> ConfirmedTransactionEntity {
func enhance(transaction: TransactionEntity) async throws -> ConfirmedTransactionEntity {
LoggerProxy.debug("Zoom.... Enhance... Tx: \(transaction.transactionId.toHexStringTxId())")
let transaction = try downloader.fetchTransaction(txId: transaction.transactionId)
let transaction = try await downloader.fetchTransactionAsync(txId: transaction.transactionId)
let transactionID = transaction.transactionId.toHexStringTxId()
let block = String(describing: transaction.minedHeight)
@ -148,6 +156,16 @@ class CompactBlockEnhancementOperation: ZcashOperation {
}
return confirmedTx
}
override func fail(error: Error? = nil) {
self.cancelableTask?.cancel()
super.fail(error: error)
}
override func cancel() {
self.cancelableTask?.cancel()
super.cancel()
}
}
private extension BlockRange {

View File

@ -125,6 +125,8 @@ class CompactBlockBatchScanningOperation: ZcashOperation {
private var blockRange: CompactBlockRange
private var transactionRepository: TransactionRepository
private var network: NetworkType
private var cancelableTask: Task<Void, Error>?
private var done = false
private weak var progressDelegate: CompactBlockProgressDelegate?
@ -157,82 +159,94 @@ class CompactBlockBatchScanningOperation: ZcashOperation {
self.startedHandler?()
do {
if batchSize == 0 {
let scanStartTime = Date()
guard self.rustBackend.scanBlocks(dbCache: self.cacheDb, dbData: self.dataDb, limit: batchSize, networkType: network) else {
self.scanFailed(self.rustBackend.lastError() ?? ZcashOperationError.unknown)
return
}
let scanFinishTime = Date()
NotificationCenter.default.post(
SDKMetrics.progressReportNotification(
progress: BlockProgress(
startHeight: self.blockRange.lowerBound,
targetHeight: self.blockRange.upperBound,
progressHeight: self.blockRange.upperBound
),
start: scanStartTime,
end: scanFinishTime,
task: .scanBlocks
)
)
let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate
LoggerProxy.debug("Scanned \(blockRange.count) blocks in \(seconds) seconds")
} else {
let scanStartHeight = try transactionRepository.lastScannedHeight()
let targetScanHeight = blockRange.upperBound
var scannedNewBlocks = false
var lastScannedHeight = scanStartHeight
repeat {
guard !shouldCancel() else {
cancel()
return
}
let previousScannedHeight = lastScannedHeight
cancelableTask = Task {
do {
if batchSize == 0 {
let scanStartTime = Date()
guard self.rustBackend.scanBlocks(
dbCache: self.cacheDb,
dbData: self.dataDb,
limit: batchSize,
networkType: network
) else {
guard self.rustBackend.scanBlocks(dbCache: self.cacheDb, dbData: self.dataDb, limit: batchSize, networkType: network) else {
self.scanFailed(self.rustBackend.lastError() ?? ZcashOperationError.unknown)
return
}
let scanFinishTime = Date()
lastScannedHeight = try transactionRepository.lastScannedHeight()
scannedNewBlocks = previousScannedHeight != lastScannedHeight
if scannedNewBlocks {
let progress = BlockProgress(startHeight: scanStartHeight, targetHeight: targetScanHeight, progressHeight: lastScannedHeight)
progressDelegate?.progressUpdated(.scan(progress))
NotificationCenter.default.post(
SDKMetrics.progressReportNotification(
progress: progress,
start: scanStartTime,
end: scanFinishTime,
task: .scanBlocks
)
NotificationCenter.default.post(
SDKMetrics.progressReportNotification(
progress: BlockProgress(
startHeight: self.blockRange.lowerBound,
targetHeight: self.blockRange.upperBound,
progressHeight: self.blockRange.upperBound
),
start: scanStartTime,
end: scanFinishTime,
task: .scanBlocks
)
let heightCount = lastScannedHeight - previousScannedHeight
let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate
LoggerProxy.debug("Scanned \(heightCount) blocks in \(seconds) seconds")
}
} while !self.isCancelled && scannedNewBlocks && lastScannedHeight < targetScanHeight
)
let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate
LoggerProxy.debug("Scanned \(blockRange.count) blocks in \(seconds) seconds")
} else {
let scanStartHeight = try transactionRepository.lastScannedHeight()
let targetScanHeight = blockRange.upperBound
var scannedNewBlocks = false
var lastScannedHeight = scanStartHeight
repeat {
guard !shouldCancel() else {
cancel()
return
}
let previousScannedHeight = lastScannedHeight
let scanStartTime = Date()
guard self.rustBackend.scanBlocks(
dbCache: self.cacheDb,
dbData: self.dataDb,
limit: batchSize,
networkType: network
) else {
self.scanFailed(self.rustBackend.lastError() ?? ZcashOperationError.unknown)
return
}
let scanFinishTime = Date()
lastScannedHeight = try transactionRepository.lastScannedHeight()
scannedNewBlocks = previousScannedHeight != lastScannedHeight
if scannedNewBlocks {
let progress = BlockProgress(startHeight: scanStartHeight, targetHeight: targetScanHeight, progressHeight: lastScannedHeight)
progressDelegate?.progressUpdated(.scan(progress))
NotificationCenter.default.post(
SDKMetrics.progressReportNotification(
progress: progress,
start: scanStartTime,
end: scanFinishTime,
task: .scanBlocks
)
)
let heightCount = lastScannedHeight - previousScannedHeight
let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate
LoggerProxy.debug("Scanned \(heightCount) blocks in \(seconds) seconds")
}
} while !self.isCancelled && scannedNewBlocks && lastScannedHeight < targetScanHeight
self.done = true
}
} catch {
scanFailed(error)
}
} catch {
scanFailed(error)
}
while !done && !isCancelled {
sleep(1)
}
}
func scanFailed(_ error: Error) {
self.error = error
self.cancelableTask?.cancel()
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.fail()
super.fail(error: error)
}
override func cancel() {
self.cancelableTask?.cancel()
super.cancel()
}
}

View File

@ -22,7 +22,9 @@ class CompactBlockValidationOperation: ZcashOperation {
private var cacheDb: URL
private var dataDb: URL
private var network: NetworkType
private var cancelableTask: Task<Void, Error>?
private var done = false
init(
rustWelding: ZcashRustBackendWelding.Type,
cacheDb: URL,
@ -44,23 +46,40 @@ class CompactBlockValidationOperation: ZcashOperation {
self.startedHandler?()
let result = self.rustBackend.validateCombinedChain(dbCache: cacheDb, dbData: dataDb, networkType: self.network)
switch result {
case 0:
let error = CompactBlockValidationError.failedWithError(rustBackend.lastError())
self.error = error
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.fail(error: error)
cancelableTask = Task {
let result = self.rustBackend.validateCombinedChain(dbCache: cacheDb, dbData: dataDb, networkType: self.network)
case ZcashRustBackendWeldingConstants.validChain:
break
default:
let error = CompactBlockValidationError.validationFailed(height: BlockHeight(result))
self.error = error
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.fail(error: error)
switch result {
case 0:
let error = CompactBlockValidationError.failedWithError(rustBackend.lastError())
self.error = error
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.fail(error: error)
case ZcashRustBackendWeldingConstants.validChain:
self.done = true
break
default:
let error = CompactBlockValidationError.validationFailed(height: BlockHeight(result))
self.error = error
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.fail(error: error)
}
}
while !done && !isCancelled {
sleep(1)
}
}
override func fail(error: Error? = nil) {
self.cancelableTask?.cancel()
super.fail(error: error)
}
override func cancel() {
self.cancelableTask?.cancel()
super.cancel()
}
}

View File

@ -24,7 +24,9 @@ class FetchUnspentTxOutputsOperation: ZcashOperation {
private var startHeight: BlockHeight
private var network: NetworkType
private var dataDb: URL
private var cancelableTask: Task<Void, Error>?
private var done = false
init(
accountRepository: AccountRepository,
downloader: CompactBlockDownloading,
@ -49,30 +51,41 @@ class FetchUnspentTxOutputsOperation: ZcashOperation {
self.startedHandler?()
do {
let tAddresses = try accountRepository.getAll().map({ $0.transparentAddress })
cancelableTask = Task {
do {
for tAddress in tAddresses {
guard try self.rustbackend.clearUtxos(
dbData: dataDb,
address: tAddress,
sinceHeight: startHeight - 1,
networkType: network
) >= 0 else {
throw rustbackend.lastError() ?? RustWeldingError.genericError(message: "attempted to clear utxos but -1 was returned")
let tAddresses = try accountRepository.getAll().map({ $0.transparentAddress })
do {
for tAddress in tAddresses {
guard try self.rustbackend.clearUtxos(
dbData: dataDb,
address: tAddress,
sinceHeight: startHeight - 1,
networkType: network
) >= 0 else {
throw rustbackend.lastError() ?? RustWeldingError.genericError(message: "attempted to clear utxos but -1 was returned")
}
}
} catch {
throw FetchUTXOError.clearingFailed(error)
}
var utxos: [UnspentTransactionOutputEntity] = []
let stream: AsyncThrowingStream<UnspentTransactionOutputEntity, Error> = downloader.fetchUnspentTransactionOutputs(tAddresses: tAddresses, startHeight: startHeight)
for try await transaction in stream {
utxos.append(transaction)
}
let result = storeUTXOs(utxos, in: dataDb)
self.fetchedUTXOsHandler?(result)
self.done = true
} catch {
throw FetchUTXOError.clearingFailed(error)
self.fail(error: error)
}
let utxos = try downloader.fetchUnspentTransactionOutputs(tAddresses: tAddresses, startHeight: startHeight)
let result = storeUTXOs(utxos, in: dataDb)
self.fetchedUTXOsHandler?(result)
} catch {
self.fail(error: error)
}
while !done && !isCancelled {
sleep(1)
}
}
@ -99,4 +112,14 @@ class FetchUnspentTxOutputsOperation: ZcashOperation {
return (inserted: refreshed, skipped: skipped)
}
override func fail(error: Error? = nil) {
self.cancelableTask?.cancel()
super.fail(error: error)
}
override func cancel() {
self.cancelableTask?.cancel()
super.cancel()
}
}

View File

@ -28,11 +28,9 @@ protocol CompactBlockRepository {
/**
Gets the highest block that is currently stored.
Non-Blocking
- Parameter result: closure resulting on either the latest height or an error
*/
func latestHeight(result: @escaping (Result<BlockHeight, Error>) -> Void)
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
@ -46,10 +44,9 @@ protocol CompactBlockRepository {
Non-Blocking
- Parameters:
- Parameter blocks: array of blocks to be written to storage
- Parameter completion: a closure that will be called after storing the blocks
*/
func write(blocks: [ZcashCompactBlock], completion: ((Error?) -> Void)?)
func writeAsync(blocks: [ZcashCompactBlock]) async throws
/**
Remove every block above and including the given height.
@ -66,10 +63,7 @@ protocol CompactBlockRepository {
After this operation, the data store will look the same as one that has not yet stored the given block height.
Meaning, if max height is 100 block and rewindTo(50) is called, then the highest block remaining will be 49.
- Parameters:
- Parameter height: the height to rewind to
- Parameter completion: a closure that will be called after storing the blocks
*/
func rewind(to height: BlockHeight, completion: ((Error?) -> Void)?)
func rewindAsync(to height: BlockHeight) async throws
}

View File

@ -0,0 +1,8 @@
{
"network": "main",
"height": "1775000",
"hash": "000000000019cdcd93e8a0704c40cd74e65424e4bfaaa2a21227db81355ccb46",
"time": 1660646308,
"saplingTree": "01597a94eed997b611665ea28eb0fbebf872dc4187b81baf2d7b061d9498e4be07001901289049f3d2fb297ec300241c8ca66400d8cb214272da11c7dddbf249a83b363200019448787f294385a1cf41c26b5ecc3a9f2b552d85dd959d71ad8fb79ab82f315e0000015692b62820a0affe568750d52d557ae99781acf19545f95404b3d7122e00e50d014c13e6f6cd1938716b4870921418d553fbc13ad5e174c720556e43eeedff992b01d5af984301be600c7cf840dc1fbbcdedb2d2e801813bb68a01be00c6822d5a6a017e978b347c274b38ee016285dd14132e83d59786019d1a46ce0163771dfdce0c000000017b24ec22b8e69bfebe3a24693645c8905f2c3756dd67d646d4266c5624ce29520001887d900f0f1c22d239b925d77200c12a7a637dfc7723f08aa34c0808cd1740200001e9358e668fd2780e13975fa8d46cd6ea762252a2ce849e2c82affb1c58edcb190000013eca32c92486829a47625ea4acb38897025abb7af0d1aba2d12c9e828a8d0a4f000000000139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
"orchardTree": "0182a273f144dba41ed805d348a8eeae5692b737863da75ac3384af983d54fd109001f01f72799b4c4c3ec3048b5c724b0edf553fdac716a6f2e771b8e40be6f9ece050301eec827aaef0952edafa106766d905c03b29a58b8f502b579e18d6b8a36b9a2120001997dd960a2474458cc8d914bf46232ec351eb5b3ff614e455d0fe6cf1c0d961f01726cc90e9a3d35324d807825bc4eceb7c976735bf8d5072f24f916f11231aa2901e4d4b64f108c937551964335b41b8a6e43bca7b2f2c468f86bb27a938ffb912001395fc86db243265b7a2722b3a49c1ac5449d671980bfaaebd1f8b26d4324451f0001056bee64e920ba6e8604fa32b294591abb40b5fc35b9b6f257b3b465fce371270001ea6a4ce4349b49c0b9788bcf94d27cb8c95f8f5a640919110387cd66f063e113017d6446e58ba6f7f8e7f60594b91bb67739986c89c670ad3996205ca9df7eac1100000001188cb8c8ad6ec4d5a9f90689123894bc1d021fb63d53347246ef21657c04f3110000014c58c6ffa8b0fdd89c604006318b7901020edcaca498e3afd81af7579dafcd340001f791f26a6ace2c09ba3deee8db26c3299e8e9ea6f4fdcac0846b2121721c310100000000000000000000"
}

View File

@ -0,0 +1,8 @@
{
"network": "main",
"height": "1777500",
"hash": "000000000114eb90f65670123d685d4e1ac4208dcc39a9dc1111060789a2c418",
"time": 1660835117,
"saplingTree": "0179c12f2782a76e421834ecfcd800a5cbd1edc79663d3c58d25d14200c60a2b050019000000000000000001091c24ca6d7180e15a22a46f9fa2d0a90783ce8e8bf4346097b36c469f7859220113d4335546307bb7558b2bfa8a309abb341da3f88acfc3b7d0523519a993662701d99f7f7123501008b4b145d772c66240aa9bdfe1fadd8cd420a3f159126950350185b933c8fc56ff06180fc5d2dbaf7234d615c724b1dbcdd4130ddff0ac732e6e000000019fe9488e3027bf007c951a3706ac7422d242c3ece25c20d5fd8254fdc77d883c000177ac81d2104e4de33629aaf3187c8daff10899ef791b1fbb18d2f2c17296870a0000018679bff98f1311c6b1c4df01647556064d2cd34087c8f83fac1ee99babc986600000000139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
"orchardTree": "01dde5b2585ed32435eba299a594f328983090c620d3e40c58e2c457c5adbc7835001f01430c6ec1ba7a552b02f897a8789639760786e29fc4ce7e133f4d8768fba4032b0001b99142688612333a2926ebf2b271c50018a4240c9522cdee66b68c4ffa8c3616000172a6fbb0176b644a4a11f6c658324fc5d8140845959cb3c924b190a8ef86e22a0153250a8de904795ee993882737ee5f13458561a0f78d6ab1432e165f0b19ed2f01b54e695a6d674420b22d8b52727140dbde2d83ed45966a2cde91393d89ea6a0101d3f0b23da54e9066a2e4621baa67d255a69f90f788e35975e5fc14ae62eb9b3d012d59e25ff1850760f04238d20341db41c77a069ce6146af0c66693c5a7e84c04014caf0366131977fe12e0ef793ad22fa3f75df7a2bca71acba67bbb206797ff3c0001ce3e5c7e4f546304954681fd0e8f0e68dc38e5c788c99c1cc2a5404c160df93a0001038ca3c0ad85cbc76b94531a2c8d7efc43af084abdc88d2d28b30bc3a8ba4d39000001aa7b1980e5656743063400012808a5a2920431959e0b721c9a915b37ca3b450801c4accd50986a74b77eddf2e5191d20e40035f38bc7c9b91a03339af16c5a7032014c58c6ffa8b0fdd89c604006318b7901020edcaca498e3afd81af7579dafcd340001f791f26a6ace2c09ba3deee8db26c3299e8e9ea6f4fdcac0846b2121721c310100000000000000000000"
}

View File

@ -0,0 +1,8 @@
{
"network": "main",
"height": "1780000",
"hash": "000000000092cd2f7d70451422ec93bc0c32dbb4b7ba7bfab4c6bb2f646621cf",
"time": 1661023245,
"saplingTree": "01eb57dc27bf63678d8d3a9d19b422a4694a9d43765bd308abd6ac1ee838dc4524018572cb62805c814962aee358456f07f5494cf3fa7b9ec1bc35dcd8f217e0920b19014241b45704d5126780eede03872b8f87501e4184dc68f977aab8e20d1e83da330001be1052772495777a8224029288e3cbce5f6528380c7dfab653661fa068e43267000000000120d655140e8a7069e4bb227ef3be10337d1fe805f6c143042f39e04047dcaa4801edee36cd129d61db744d5c70d749a066ae0903b5310f309609d014d349b7e435000001e4730f055d123a824efda2abf609b64bca407df2582fb188f15876c17a93183f01428fe4cdc82c312139518ae3cfed4fc769329bc192c724029a38dbf221557a56000176138376dab12f9063c0d4be21178499d1f912756be2dda3cb7540469376905901b261f10e8b41673b7707c93d6a38c9b3ca331d7beaf312b17dc5536bf9ac262500018fc9b4ef24688bf42b782c41232f9ec501b1c4ab6c950b0c41d34eb23b17402800017412114010d9ffb2f7387eda6e29942c0c80b6330788102467e4a0afe325382d018679bff98f1311c6b1c4df01647556064d2cd34087c8f83fac1ee99babc986600000000139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
"orchardTree": "01b02222e4afcd845dc497c76d04b9f6ddb9081d01b9f9d5a2aa05fbc8d1443b1601a0331c1517205b7e943e9668d61e77e875c662dc96ef7cc96299b6a83890b41d1f0000013c6b5a330ccb2ba9442273535baa2dc7fa6dbf999b72b31afe032c168f4bd7080001e74ec029cd1c152dc8b09f65985d6c0c2c78e56da80c941538520a28587d9f160181a58cf9d3ea78a5685698258689ebe5fa4ce66d0ef9eceb6ae478750ef63139012e734c4d341726e1c02519bca7c04706db73fede3e95e9eb81208265d7b5c51e01e12417482428167d1173531eb01c4f05257c8915bd7b3c81668f7e4c0525d91901673db13a5adedf791c6b644470c5f61d202abec03de1ad93ad9b534e93925e0c01256c8aa3be887192ee742261edd0c2788d1b625442150145f4d3b8ed4905c218017fcf9eb3250652f2d0edd5af7d812909570a857529d71182f1e67fe20131b43a01c11b33655a2af90f359b1372d26da8e92bd8885107b6cc623e96243d56ec051700010d01fcaf34b9d9127ad8aade1bc05ed6c70040a7146a9fd596073bfcea1aad390137638774e0047f7be64682876e9fa29021fb428b367731ff1dc7701b58633e390000000001081565fb9592a4fab10edbfb15d2aa45c89eb85837f4f298c586cb21d45b772901f791f26a6ace2c09ba3deee8db26c3299e8e9ea6f4fdcac0846b2121721c310100000000000000000000"
}

View File

@ -0,0 +1,8 @@
{
"network": "main",
"height": "1782500",
"hash": "0000000000c96f27e270aa37db650cf15c6b75f1fa59a947cca4f41abf004676",
"time": 1661211894,
"saplingTree": "01ed3ce95d71c92565819babefc0f1026beac767dd6a162ac0ae9b15f619446545001900000001ac44d394a0bac1701dd6b74ab80f53729963826696a1da89aae59e12cf4c1b5f01dacf1de9631ba5147d124a2502808ee3eabdccbcaee03b03521cf765324f52710001efe591a8f59c1b2271ff973d8204c588b21747c305a7603cac7476f62424ed0d000000000138279c0da3d1576d92933860f5de697d212036da77df1af780bebe1b0fb1df2700000001734a94d73f408cf9aa047cb69f2b67382cd2eac7ad9fd8d4dca6b9ef4706d93a01323df8cc8affced4411de770643c86329441d276e3d0af469a5387baf14b702e000196ea06c6c9b1bffd35111b29addc63a564d780556def4814bd2bdf44568add0000000190fd9fe569252b58449910aeb7a9d5bed775699b04b2b0d2373b948728ce291b00000139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
"orchardTree": "0173e8d2617537311c62aa77f610f752e4553fac6d8f8cdb05248941a2afc25f0b001f00011093e632be4376c29ca004fa22ceeb426c283d9006436eba5185b63ec4adab09000001ed13450a8b52b2467727d09d721f8054d408cc007a3574f090bca255d94da90800012d81bec0d20bb2214b91f3caf8d03c66c779b67f06a9908bfb239e25b2156c1400019971eb95fe89873ce0271ed1e70b8ea4498e54a81d4db7b092fefc7b9f540212000146181485362325772638425fe945f012e9b3884886dfc0f5c689f556d40d8032000001cbc56207feb2d10de3c2dffcc93040d4ed95a1d0c653c9587739681a162a601401aad0d336606800990785d124bdfe61ffbd399167bbc344f5855d3d4de468de190001464e545a0f5a15e6c09f82335c092a66ff8f10d0abe8dd0db230ca90dbcc0d2401f11d7f967046007bf41c33b30c0dc3b52c1a4a9cc3a7a85657a2fc11a39d393a0001081565fb9592a4fab10edbfb15d2aa45c89eb85837f4f298c586cb21d45b772901f791f26a6ace2c09ba3deee8db26c3299e8e9ea6f4fdcac0846b2121721c310100000000000000000000"
}

View File

@ -0,0 +1,8 @@
{
"network": "main",
"height": "1785000",
"hash": "000000000048a2a2cc1132d171fd58ae0483322d33a4ca4e5814d2f928358ddb",
"time": 1661400251,
"saplingTree": "019cdc2862d4223e86a3b2780f9cdf19b2b22431faa4c849d39f681bd01d32a0630145fdfaf100b993c09d16b34c6bb30834fbd9519be36b7cf5adc645fd2727805a190196199419320b54a8acf9a9b6883abf64a7e80fe75a17a6227bd47b8a2636b84700000001c5c6f40ccdd6c8c7c8b8257c1dbed8c9ab5b99320c0b367bd80c471f676dfe3d01324e46f8529d6becc636a9b57eb864b3cc6a19f9e1cf3da6fcd4b96456fdf1350111fe0737c21b236b783ca356947277888a785e569d1c8953d341741df944742800010e4303d3df4670374eec9b5fff8b9b7cead7c3720ccfc57bc05e9f15e005420a019a74161a408604546f401b8616c50dcd38a10efca83d1a5f0bc87f43ba3359670187130c30b7023f7d9a80fe75fcc1c7864007ae29e63c9a85dff0cc60e9055f5201d7a644ef58eeed93ab36be7ef27d90dbe43696ce2c0c2d1969e3de9f1cc9e51c013dc034d5b0eff46aa39a5461cdc285a75705eadf417e14fcb6a07feb4ed3a0530001f3a3cab7e488e34450427f914284cff92aca699a2e3bee42c42212419bb72a00000001c9c6bba2bb2a19be1e993af5f9bc35979454afa6237a44ad46ed0cd06c7eca4901e1a9472dadbe9da7956818483b066069ad0030dee06a787200e5519a5e0b96600001757bfb5957a0fa9b4fc9e8c105df1e9f0f9e156d653ba54a5fd2ebaa237afa510190fd9fe569252b58449910aeb7a9d5bed775699b04b2b0d2373b948728ce291b00000139af7ec003ac4e37c263bab5642fcc4ec294a1e328ff025832eec45239041f6e",
"orchardTree": "01fcefe6872d4fa4b8bf4cde106616dd137593b43fd761478eb4d1dd6715a4cc21001f0000000118ef33685241afb399b6d9c0697404998528096ae550dcd75365912cfb64953100019ccc0dc23d60dddcadb86ecabcfe1e5d42829a741f7e6705a368ead59ac492260158cda00196a2f05b86d770724944d4db0ff55d7442962dec634e3968822df01b019f815362606a86082ca9c3a332d4fbea2faef7c6be9cf4f248fa09672ab6012301ce1619c190c7fb2f62560a1723b9daf6ab97d03406bc4537ef4da5dbe4ea993e0000000001cbd9a766f5802ce880a9741919503a6b5b2e58104d1331b7c477d758aad8fe1400018ea310e6e8006b04172395acba6ef11cbef90cf053489a7f8b3287be3935ed3d0000019886882316b2f5a810dda1ece4d983ed360175e2cc5ed2b3781ee1d9f1e05f2801081565fb9592a4fab10edbfb15d2aa45c89eb85837f4f298c586cb21d45b772901f791f26a6ace2c09ba3deee8db26c3299e8e9ea6f4fdcac0846b2121721c310100000000000000000000"
}

View File

@ -0,0 +1,8 @@
{
"network": "test",
"height": "2000000",
"hash": "000bb3c6d575a155a56b1aab0e7f14a1141d25c1f36aa6f15c1cb1ef1d7d498c",
"time": 1660621966,
"saplingTree": "0133914238639e4f22ddc8ca2cab7e2d7ec676b06f12a0bf42bc757909cec4f47200100103217f60b7d9bdc625ecd5d6170e3ab57a81eb29f7beacecd07c4f3f0d61ef2801066673d6ab6b62a91f6e04a49fc0e74083cfcccdc851db681e01db333ee1cb470000018da55ded8e55006c2a6c41474da220a9a179a85a99a48df0a31a74291317720e00012233753a4db3b97f44b3bb4d77034fd77c8e5c7938092724cb4a3bf3491bc1180160a5822bf1407657b82e959214634ec4c36a092bc1ab7615286058d8bc06152c01d878f6e29835fd77030d6c7395e85231d74c3a4d72659c8f301d65dfc2f3c51f018bd9ca6b4e00aac24d384b8d85b32481e0b4335ab3de5a0dea5c61aa32366153019aa3b71d9ce27ab88add19fda2e50caf313de20a04c6b72f5fbe1783980431730122c55fdffb446e39b73f1606907b2889d18b01ac818a0cbd4b2c661ad6a5a170000117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
"orchardTree": "01a682706317caa5aec999385ac580445ff4eff6347e4a3c844ac18fcb5fe9bf1c01cca6f37237f27037fa7f8fe5ec8d2cc251b791cfb9cdd08cd1215229fa9435221f0001590d3e7e3f4cd572274f79f4a95b41fa72ed9b42a7c6dbcaec9637eaf368ac0e0000018843337920418307fa7699d506bb0f47a79aea7f6fe8efc1e25b9dde8966e22f013b5a8ef020d8b30fa8beb8406dd30b2a1944755f5549713e4fe24de78ab72e12000001a46523754a6d3fbc3226d6221dafca357d930e183297a0ba1cfa2db5d0500e1f01b6fd291e9d6068bc24e99aefe49f8f29836ed1223deabc23871f1a1288f9240300016fc552915a0d5bc5c0c0cdf29453edf081d9a2de396535e6084770c38dcff838019518d88883e466a41ca67d6b986739fb2f601d77bb957398ed899de70b2a9f0801cd4871c1f545e7f5d844cc65fb00b8a162e316c3d1a435b00c435032b732c4280000000000000000000000000000000000"
}

View File

@ -0,0 +1,8 @@
{
"network": "test",
"height": "2010000",
"hash": "0013159a578c874aeecddad8707b8e274a018078fd80fc3b9e2d04065abeb05d",
"time": 1661274815,
"saplingTree": "012bd51ef4da530bb488d43a0f770109df7186ef164a64f618038bb00b7861840a00100001d3a6dfc2fac7c968ce7f96efdddc9764bc320230d8ce166637f8b73b6453e8670000000197e98f0a36d7a5ccac4b4ec963223a4eaab38188eb2a658ff01da4880839996e012233753a4db3b97f44b3bb4d77034fd77c8e5c7938092724cb4a3bf3491bc1180160a5822bf1407657b82e959214634ec4c36a092bc1ab7615286058d8bc06152c01d878f6e29835fd77030d6c7395e85231d74c3a4d72659c8f301d65dfc2f3c51f018bd9ca6b4e00aac24d384b8d85b32481e0b4335ab3de5a0dea5c61aa32366153019aa3b71d9ce27ab88add19fda2e50caf313de20a04c6b72f5fbe1783980431730122c55fdffb446e39b73f1606907b2889d18b01ac818a0cbd4b2c661ad6a5a170000117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
"orchardTree": "014b0ab9afb38a6c819d64806b3de5397c6469ca0f7e58274f5c779ecb75d94217018646890daed861a8f66a3966e884da1b1c8240bb47327a348fae9169eb8205051f000118681f82320257f5a5cfc5c846fd4e88641d4ed7b87fcd5ec8e65e68e3ab0636000155cd8735904f69800ba500bf8af6fc70bfb2abb3d7ca1c75a1cce183b763f30400016e57cbad21bab804109f19479a88046b5c7f8da0d21e8217ac467801235c52200001a8773a2b01e9efd1011ea7d8d1071ad0a01c6c73e2fb6df802e307df17b4ac0601a46523754a6d3fbc3226d6221dafca357d930e183297a0ba1cfa2db5d0500e1f01b6fd291e9d6068bc24e99aefe49f8f29836ed1223deabc23871f1a1288f9240300016fc552915a0d5bc5c0c0cdf29453edf081d9a2de396535e6084770c38dcff838019518d88883e466a41ca67d6b986739fb2f601d77bb957398ed899de70b2a9f0801cd4871c1f545e7f5d844cc65fb00b8a162e316c3d1a435b00c435032b732c4280000000000000000000000000000000000"
}

View File

@ -90,7 +90,8 @@ public extension BlockProgress {
public class LightWalletGRPCService {
let channel: Channel
let connectionManager: ConnectionStatusManager
let compactTxStreamer: CompactTxStreamerClient
let compactTxStreamer: CompactTxStreamerNIOClient
let compactTxStreamerAsync: CompactTxStreamerAsyncClient
let singleCallTimeout: TimeLimit
let streamingCallTimeout: TimeLimit
@ -135,7 +136,14 @@ public class LightWalletGRPCService {
self.channel = channel
compactTxStreamer = CompactTxStreamerClient(
compactTxStreamer = CompactTxStreamerNIOClient(
channel: self.channel,
defaultCallOptions: Self.callOptions(
timeLimit: self.singleCallTimeout
)
)
compactTxStreamerAsync = CompactTxStreamerAsyncClient(
channel: self.channel,
defaultCallOptions: Self.callOptions(
timeLimit: self.singleCallTimeout
@ -146,6 +154,7 @@ public class LightWalletGRPCService {
deinit {
_ = channel.close()
_ = compactTxStreamer.channel.close()
_ = compactTxStreamerAsync.channel.close()
}
func stop() {
@ -155,7 +164,7 @@ public class LightWalletGRPCService {
func blockRange(startHeight: BlockHeight, endHeight: BlockHeight? = nil, result: @escaping (CompactBlock) -> Void) throws -> ServerStreamingCall<BlockRange, CompactBlock> {
compactTxStreamer.getBlockRange(BlockRange(startHeight: startHeight, endHeight: endHeight), handler: result)
}
func latestBlock() throws -> BlockID {
try compactTxStreamer.getLatestBlock(ChainSpec()).response.wait()
}
@ -179,122 +188,18 @@ public class LightWalletGRPCService {
}
}
extension LightWalletGRPCService: LightWalletService {
@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
}
// MARK: - LightWalletServiceBlockingAPI
extension LightWalletGRPCService: LightWalletServiceBlockingAPI {
public func getInfo() throws -> LightWalletdInfo {
try compactTxStreamer.getLightdInfo(Empty()).response.wait()
}
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 closeConnection() {
_ = channel.close()
}
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 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 submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
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 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 latestBlockHeight() throws -> BlockHeight {
guard let height = try? compactTxStreamer.getLatestBlock(ChainSpec()).response.wait().compactBlockHeight() else {
throw LightWalletServiceError.timeOut
}
return height
}
public func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] {
@ -315,51 +220,30 @@ extension LightWalletGRPCService: LightWalletService {
}
}
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))
public func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
let rawTx = RawTransaction.with { raw in
raw.data = spendTransaction
}
response.whenFailureBlocking(onto: queue) { error in
result(.failure(error.mapToServiceError()))
do {
return try compactTxStreamer.sendTransaction(rawTx).response.wait()
} catch {
throw error.mapToServiceError()
}
}
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()))
}
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 latestBlockHeight() throws -> BlockHeight {
guard let height = try? latestBlock().compactBlockHeight() else {
throw LightWalletServiceError.timeOut
}
return height
}
public func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
let arg = GetAddressUtxosArg.with { utxoArgs in
utxoArgs.addresses = [tAddress]
@ -383,7 +267,168 @@ extension LightWalletGRPCService: LightWalletService {
}
}
public func fetchUTXOs(for tAddress: String, height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) {
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()))
}
}
}
public func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
let stream = compactTxStreamerAsync.getBlockRange(range.blockRange())
return AsyncThrowingStream { continuation in
Task {
do {
for try await block in stream {
continuation.yield(ZcashCompactBlock(compactBlock: block))
}
continuation.finish(throwing: nil)
} catch {
continuation.finish(throwing: error)
}
}
}
}
public func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
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)
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 {
var txFilter = TxFilter()
txFilter.hash = txId
let rawTx = try await compactTxStreamerAsync.getTransaction(txFilter)
return TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx)
}
public func fetchUTXOs(
for tAddress: String,
height: BlockHeight,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>
) -> Void) {
queue.async { [weak self] in
guard let self = self else { return }
let arg = GetAddressUtxosArg.with { utxoArgs in
@ -420,36 +465,13 @@ extension LightWalletGRPCService: LightWalletService {
}
}
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
public func fetchUTXOs(
for tAddress: String,
height: BlockHeight
) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
return fetchUTXOs(for: [tAddress], height: height)
}
public func fetchUTXOs(
for tAddresses: [String],
height: BlockHeight,
@ -495,6 +517,119 @@ extension LightWalletGRPCService: LightWalletService {
}
}
}
public func fetchUTXOs(
for tAddresses: [String],
height: BlockHeight
) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
guard !tAddresses.isEmpty else {
return AsyncThrowingStream { _ in }
}
let args = GetAddressUtxosArg.with { utxoArgs in
utxoArgs.addresses = tAddresses
utxoArgs.startHeight = UInt64(height)
}
let stream = compactTxStreamerAsync.getAddressUtxosStream(args)
return AsyncThrowingStream { continuation in
Task {
do {
for try await reply in stream {
continuation.yield(
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
)
)
}
continuation.finish(throwing: nil)
} catch {
continuation.finish(throwing: error)
}
}
}
}
@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
) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
let stream = compactTxStreamerAsync.getBlockRange(
BlockRange(
startHeight: startHeight,
endHeight: endHeight
),
callOptions: Self.callOptions(timeLimit: self.streamingCallTimeout)
)
return AsyncThrowingStream { continuation in
Task {
do {
for try await compactBlock in stream {
continuation.yield(ZcashCompactBlock(compactBlock: compactBlock))
}
continuation.finish(throwing: nil)
} catch {
continuation.finish(throwing: error)
}
}
}
}
}
extension LightWalletGRPCService: LightWalletService {
public func closeConnection() {
_ = channel.close()
}
}
// MARK: - Extensions

View File

@ -101,49 +101,102 @@ public protocol LightWalletServiceResponse {
extension SendResponse: LightWalletServiceResponse {}
public protocol LightWalletService {
/**
returns the info for this lightwalletd server (blocking)
*/
/// Blocking API - used for the testing purposes
public protocol LightWalletServiceBlockingAPI {
/// Returns the info for this lightwalletd server (blocking)
func getInfo() throws -> LightWalletdInfo
/**
returns the info for this lightwalletd server
*/
func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void)
/**
Return the latest block height known to the service.
- Parameter result: a result containing the height or an Error
*/
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void)
/**
Return the latest block height known to the service.
- Parameter result: a result containing the height or an Error
*/
///
/// Return the latest block height known to the service.
/// - Parameter result: a result containing the height or an Error
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.
Non blocking
*/
func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void )
/**
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.
blocking
*/
/// 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
/// 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
@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,
@ -153,62 +206,12 @@ public protocol LightWalletService {
progress: @escaping (BlockProgress) -> Void
) -> CancellableCall
/**
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
*/
func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void)
/**
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
/**
Gets a transaction by id
- Parameter txId: data representing the transaction ID
- Parameter result: handler for the result
- Throws: LightWalletServiceError
- Returns: LightWalletServiceResponse
*/
func fetchTransaction(
txId: Data,
result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void
)
func fetchUTXOs(
for tAddress: String,
height: BlockHeight
) throws -> [UnspentTransactionOutputEntity]
func fetchUTXOs(
for tAddress: String,
height: BlockHeight,
result: @escaping(Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
)
func fetchUTXOs(
for tAddresses: [String],
height: BlockHeight
) throws -> [UnspentTransactionOutputEntity]
func fetchUTXOs(
for tAddresses: [String],
height: BlockHeight,
result: @escaping(Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
)
func blockStream(
startHeight: BlockHeight,
endHeight: BlockHeight
) -> AsyncThrowingStream<ZcashCompactBlock, Error>
}
public protocol LightWalletService: LightWalletServiceNonBlockingAPI, LightWalletServiceBlockingAPI {
func closeConnection()
}

View File

@ -22,11 +22,15 @@
//
import GRPC
import NIO
import NIOConcurrencyHelpers
import SwiftProtobuf
/// Usage: instantiate CompactTxStreamerClient, then call methods of this protocol to make API calls.
/// Usage: instantiate `CompactTxStreamerClient`, then call methods of this protocol to make API calls.
internal protocol CompactTxStreamerClientProtocol: GRPCClient {
var serviceName: String { get }
var interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? { get }
func getLatestBlock(
_ request: ChainSpec,
callOptions: CallOptions?
@ -99,10 +103,12 @@ internal protocol CompactTxStreamerClientProtocol: GRPCClient {
_ request: Duration,
callOptions: CallOptions?
) -> UnaryCall<Duration, PingResponse>
}
extension CompactTxStreamerClientProtocol {
internal var serviceName: String {
return "cash.z.wallet.sdk.rpc.CompactTxStreamer"
}
/// Return the height of the tip of the best chain
///
@ -115,9 +121,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<ChainSpec, BlockID> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLatestBlock",
path: CompactTxStreamerClientMetadata.Methods.getLatestBlock.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetLatestBlockInterceptors() ?? []
)
}
@ -132,9 +139,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<BlockID, CompactBlock> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlock",
path: CompactTxStreamerClientMetadata.Methods.getBlock.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetBlockInterceptors() ?? []
)
}
@ -151,9 +159,10 @@ extension CompactTxStreamerClientProtocol {
handler: @escaping (CompactBlock) -> Void
) -> ServerStreamingCall<BlockRange, CompactBlock> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlockRange",
path: CompactTxStreamerClientMetadata.Methods.getBlockRange.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetBlockRangeInterceptors() ?? [],
handler: handler
)
}
@ -169,9 +178,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<TxFilter, RawTransaction> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTransaction",
path: CompactTxStreamerClientMetadata.Methods.getTransaction.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTransactionInterceptors() ?? []
)
}
@ -186,9 +196,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<RawTransaction, SendResponse> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/SendTransaction",
path: CompactTxStreamerClientMetadata.Methods.sendTransaction.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeSendTransactionInterceptors() ?? []
)
}
@ -205,9 +216,10 @@ extension CompactTxStreamerClientProtocol {
handler: @escaping (RawTransaction) -> Void
) -> ServerStreamingCall<TransparentAddressBlockFilter, RawTransaction> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressTxids",
path: CompactTxStreamerClientMetadata.Methods.getTaddressTxids.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressTxidsInterceptors() ?? [],
handler: handler
)
}
@ -223,9 +235,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<AddressList, Balance> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalance",
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalance.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceInterceptors() ?? []
)
}
@ -241,8 +254,9 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> ClientStreamingCall<Address, Balance> {
return self.makeClientStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalanceStream",
callOptions: callOptions ?? self.defaultCallOptions
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalanceStream.path,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceStreamInterceptors() ?? []
)
}
@ -267,9 +281,10 @@ extension CompactTxStreamerClientProtocol {
handler: @escaping (CompactTx) -> Void
) -> ServerStreamingCall<Exclude, CompactTx> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolTx",
path: CompactTxStreamerClientMetadata.Methods.getMempoolTx.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetMempoolTxInterceptors() ?? [],
handler: handler
)
}
@ -288,9 +303,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<BlockID, TreeState> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTreeState",
path: CompactTxStreamerClientMetadata.Methods.getTreeState.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTreeStateInterceptors() ?? []
)
}
@ -305,9 +321,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<GetAddressUtxosArg, GetAddressUtxosReplyList> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxos",
path: CompactTxStreamerClientMetadata.Methods.getAddressUtxos.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetAddressUtxosInterceptors() ?? []
)
}
@ -324,9 +341,10 @@ extension CompactTxStreamerClientProtocol {
handler: @escaping (GetAddressUtxosReply) -> Void
) -> ServerStreamingCall<GetAddressUtxosArg, GetAddressUtxosReply> {
return self.makeServerStreamingCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream",
path: CompactTxStreamerClientMetadata.Methods.getAddressUtxosStream.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetAddressUtxosStreamInterceptors() ?? [],
handler: handler
)
}
@ -342,9 +360,10 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<Empty, LightdInfo> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLightdInfo",
path: CompactTxStreamerClientMetadata.Methods.getLightdInfo.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetLightdInfoInterceptors() ?? []
)
}
@ -359,25 +378,679 @@ extension CompactTxStreamerClientProtocol {
callOptions: CallOptions? = nil
) -> UnaryCall<Duration, PingResponse> {
return self.makeUnaryCall(
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/Ping",
path: CompactTxStreamerClientMetadata.Methods.ping.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePingInterceptors() ?? []
)
}
}
#if compiler(>=5.6)
@available(*, deprecated)
extension CompactTxStreamerClient: @unchecked Sendable {}
#endif // compiler(>=5.6)
@available(*, deprecated, renamed: "CompactTxStreamerNIOClient")
internal final class CompactTxStreamerClient: CompactTxStreamerClientProtocol {
private let lock = Lock()
private var _defaultCallOptions: CallOptions
private var _interceptors: CompactTxStreamerClientInterceptorFactoryProtocol?
internal let channel: GRPCChannel
internal var defaultCallOptions: CallOptions
internal var defaultCallOptions: CallOptions {
get { self.lock.withLock { return self._defaultCallOptions } }
set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }
}
internal var interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? {
get { self.lock.withLock { return self._interceptors } }
set { self.lock.withLockVoid { self._interceptors = newValue } }
}
/// Creates a client for the cash.z.wallet.sdk.rpc.CompactTxStreamer service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
internal init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
/// - interceptors: A factory providing interceptors for each RPC.
internal init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self._defaultCallOptions = defaultCallOptions
self._interceptors = interceptors
}
}
internal struct CompactTxStreamerNIOClient: CompactTxStreamerClientProtocol {
internal var channel: GRPCChannel
internal var defaultCallOptions: CallOptions
internal var interceptors: CompactTxStreamerClientInterceptorFactoryProtocol?
/// Creates a client for the cash.z.wallet.sdk.rpc.CompactTxStreamer service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
/// - interceptors: A factory providing interceptors for each RPC.
internal init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
#if compiler(>=5.6)
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
internal protocol CompactTxStreamerAsyncClientProtocol: GRPCClient {
static var serviceDescriptor: GRPCServiceDescriptor { get }
var interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? { get }
func makeGetLatestBlockCall(
_ request: ChainSpec,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<ChainSpec, BlockID>
func makeGetBlockCall(
_ request: BlockID,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<BlockID, CompactBlock>
func makeGetBlockRangeCall(
_ request: BlockRange,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<BlockRange, CompactBlock>
func makeGetTransactionCall(
_ request: TxFilter,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<TxFilter, RawTransaction>
func makeSendTransactionCall(
_ request: RawTransaction,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<RawTransaction, SendResponse>
func makeGetTaddressTxidsCall(
_ request: TransparentAddressBlockFilter,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<TransparentAddressBlockFilter, RawTransaction>
func makeGetTaddressBalanceCall(
_ request: AddressList,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<AddressList, Balance>
func makeGetTaddressBalanceStreamCall(
callOptions: CallOptions?
) -> GRPCAsyncClientStreamingCall<Address, Balance>
func makeGetMempoolTxCall(
_ request: Exclude,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<Exclude, CompactTx>
func makeGetTreeStateCall(
_ request: BlockID,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<BlockID, TreeState>
func makeGetAddressUtxosCall(
_ request: GetAddressUtxosArg,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<GetAddressUtxosArg, GetAddressUtxosReplyList>
func makeGetAddressUtxosStreamCall(
_ request: GetAddressUtxosArg,
callOptions: CallOptions?
) -> GRPCAsyncServerStreamingCall<GetAddressUtxosArg, GetAddressUtxosReply>
func makeGetLightdInfoCall(
_ request: Empty,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Empty, LightdInfo>
func makePingCall(
_ request: Duration,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Duration, PingResponse>
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension CompactTxStreamerAsyncClientProtocol {
internal static var serviceDescriptor: GRPCServiceDescriptor {
return CompactTxStreamerClientMetadata.serviceDescriptor
}
internal var interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? {
return nil
}
internal func makeGetLatestBlockCall(
_ request: ChainSpec,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<ChainSpec, BlockID> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getLatestBlock.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetLatestBlockInterceptors() ?? []
)
}
internal func makeGetBlockCall(
_ request: BlockID,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<BlockID, CompactBlock> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getBlock.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetBlockInterceptors() ?? []
)
}
internal func makeGetBlockRangeCall(
_ request: BlockRange,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<BlockRange, CompactBlock> {
return self.makeAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getBlockRange.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetBlockRangeInterceptors() ?? []
)
}
internal func makeGetTransactionCall(
_ request: TxFilter,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<TxFilter, RawTransaction> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getTransaction.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTransactionInterceptors() ?? []
)
}
internal func makeSendTransactionCall(
_ request: RawTransaction,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<RawTransaction, SendResponse> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.sendTransaction.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeSendTransactionInterceptors() ?? []
)
}
internal func makeGetTaddressTxidsCall(
_ request: TransparentAddressBlockFilter,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<TransparentAddressBlockFilter, RawTransaction> {
return self.makeAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressTxids.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressTxidsInterceptors() ?? []
)
}
internal func makeGetTaddressBalanceCall(
_ request: AddressList,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<AddressList, Balance> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalance.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceInterceptors() ?? []
)
}
internal func makeGetTaddressBalanceStreamCall(
callOptions: CallOptions? = nil
) -> GRPCAsyncClientStreamingCall<Address, Balance> {
return self.makeAsyncClientStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalanceStream.path,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceStreamInterceptors() ?? []
)
}
internal func makeGetMempoolTxCall(
_ request: Exclude,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<Exclude, CompactTx> {
return self.makeAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getMempoolTx.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetMempoolTxInterceptors() ?? []
)
}
internal func makeGetTreeStateCall(
_ request: BlockID,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<BlockID, TreeState> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getTreeState.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTreeStateInterceptors() ?? []
)
}
internal func makeGetAddressUtxosCall(
_ request: GetAddressUtxosArg,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<GetAddressUtxosArg, GetAddressUtxosReplyList> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getAddressUtxos.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetAddressUtxosInterceptors() ?? []
)
}
internal func makeGetAddressUtxosStreamCall(
_ request: GetAddressUtxosArg,
callOptions: CallOptions? = nil
) -> GRPCAsyncServerStreamingCall<GetAddressUtxosArg, GetAddressUtxosReply> {
return self.makeAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getAddressUtxosStream.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetAddressUtxosStreamInterceptors() ?? []
)
}
internal func makeGetLightdInfoCall(
_ request: Empty,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Empty, LightdInfo> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getLightdInfo.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetLightdInfoInterceptors() ?? []
)
}
internal func makePingCall(
_ request: Duration,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Duration, PingResponse> {
return self.makeAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.ping.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePingInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension CompactTxStreamerAsyncClientProtocol {
internal func getLatestBlock(
_ request: ChainSpec,
callOptions: CallOptions? = nil
) async throws -> BlockID {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getLatestBlock.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetLatestBlockInterceptors() ?? []
)
}
internal func getBlock(
_ request: BlockID,
callOptions: CallOptions? = nil
) async throws -> CompactBlock {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getBlock.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetBlockInterceptors() ?? []
)
}
internal func getBlockRange(
_ request: BlockRange,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<CompactBlock> {
return self.performAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getBlockRange.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetBlockRangeInterceptors() ?? []
)
}
internal func getTransaction(
_ request: TxFilter,
callOptions: CallOptions? = nil
) async throws -> RawTransaction {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getTransaction.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTransactionInterceptors() ?? []
)
}
internal func sendTransaction(
_ request: RawTransaction,
callOptions: CallOptions? = nil
) async throws -> SendResponse {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.sendTransaction.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeSendTransactionInterceptors() ?? []
)
}
internal func getTaddressTxids(
_ request: TransparentAddressBlockFilter,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<RawTransaction> {
return self.performAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressTxids.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressTxidsInterceptors() ?? []
)
}
internal func getTaddressBalance(
_ request: AddressList,
callOptions: CallOptions? = nil
) async throws -> Balance {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalance.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceInterceptors() ?? []
)
}
internal func getTaddressBalanceStream<RequestStream>(
_ requests: RequestStream,
callOptions: CallOptions? = nil
) async throws -> Balance where RequestStream: Sequence, RequestStream.Element == Address {
return try await self.performAsyncClientStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalanceStream.path,
requests: requests,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceStreamInterceptors() ?? []
)
}
internal func getTaddressBalanceStream<RequestStream>(
_ requests: RequestStream,
callOptions: CallOptions? = nil
) async throws -> Balance where RequestStream: AsyncSequence & Sendable, RequestStream.Element == Address {
return try await self.performAsyncClientStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getTaddressBalanceStream.path,
requests: requests,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTaddressBalanceStreamInterceptors() ?? []
)
}
internal func getMempoolTx(
_ request: Exclude,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<CompactTx> {
return self.performAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getMempoolTx.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetMempoolTxInterceptors() ?? []
)
}
internal func getTreeState(
_ request: BlockID,
callOptions: CallOptions? = nil
) async throws -> TreeState {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getTreeState.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetTreeStateInterceptors() ?? []
)
}
internal func getAddressUtxos(
_ request: GetAddressUtxosArg,
callOptions: CallOptions? = nil
) async throws -> GetAddressUtxosReplyList {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getAddressUtxos.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetAddressUtxosInterceptors() ?? []
)
}
internal func getAddressUtxosStream(
_ request: GetAddressUtxosArg,
callOptions: CallOptions? = nil
) -> GRPCAsyncResponseStream<GetAddressUtxosReply> {
return self.performAsyncServerStreamingCall(
path: CompactTxStreamerClientMetadata.Methods.getAddressUtxosStream.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetAddressUtxosStreamInterceptors() ?? []
)
}
internal func getLightdInfo(
_ request: Empty,
callOptions: CallOptions? = nil
) async throws -> LightdInfo {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.getLightdInfo.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetLightdInfoInterceptors() ?? []
)
}
internal func ping(
_ request: Duration,
callOptions: CallOptions? = nil
) async throws -> PingResponse {
return try await self.performAsyncUnaryCall(
path: CompactTxStreamerClientMetadata.Methods.ping.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makePingInterceptors() ?? []
)
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
internal struct CompactTxStreamerAsyncClient: CompactTxStreamerAsyncClientProtocol {
internal var channel: GRPCChannel
internal var defaultCallOptions: CallOptions
internal var interceptors: CompactTxStreamerClientInterceptorFactoryProtocol?
internal init(
channel: GRPCChannel,
defaultCallOptions: CallOptions = CallOptions(),
interceptors: CompactTxStreamerClientInterceptorFactoryProtocol? = nil
) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
self.interceptors = interceptors
}
}
#endif // compiler(>=5.6)
internal protocol CompactTxStreamerClientInterceptorFactoryProtocol: GRPCSendable {
/// - Returns: Interceptors to use when invoking 'getLatestBlock'.
func makeGetLatestBlockInterceptors() -> [ClientInterceptor<ChainSpec, BlockID>]
/// - Returns: Interceptors to use when invoking 'getBlock'.
func makeGetBlockInterceptors() -> [ClientInterceptor<BlockID, CompactBlock>]
/// - Returns: Interceptors to use when invoking 'getBlockRange'.
func makeGetBlockRangeInterceptors() -> [ClientInterceptor<BlockRange, CompactBlock>]
/// - Returns: Interceptors to use when invoking 'getTransaction'.
func makeGetTransactionInterceptors() -> [ClientInterceptor<TxFilter, RawTransaction>]
/// - Returns: Interceptors to use when invoking 'sendTransaction'.
func makeSendTransactionInterceptors() -> [ClientInterceptor<RawTransaction, SendResponse>]
/// - Returns: Interceptors to use when invoking 'getTaddressTxids'.
func makeGetTaddressTxidsInterceptors() -> [ClientInterceptor<TransparentAddressBlockFilter, RawTransaction>]
/// - Returns: Interceptors to use when invoking 'getTaddressBalance'.
func makeGetTaddressBalanceInterceptors() -> [ClientInterceptor<AddressList, Balance>]
/// - Returns: Interceptors to use when invoking 'getTaddressBalanceStream'.
func makeGetTaddressBalanceStreamInterceptors() -> [ClientInterceptor<Address, Balance>]
/// - Returns: Interceptors to use when invoking 'getMempoolTx'.
func makeGetMempoolTxInterceptors() -> [ClientInterceptor<Exclude, CompactTx>]
/// - Returns: Interceptors to use when invoking 'getTreeState'.
func makeGetTreeStateInterceptors() -> [ClientInterceptor<BlockID, TreeState>]
/// - Returns: Interceptors to use when invoking 'getAddressUtxos'.
func makeGetAddressUtxosInterceptors() -> [ClientInterceptor<GetAddressUtxosArg, GetAddressUtxosReplyList>]
/// - Returns: Interceptors to use when invoking 'getAddressUtxosStream'.
func makeGetAddressUtxosStreamInterceptors() -> [ClientInterceptor<GetAddressUtxosArg, GetAddressUtxosReply>]
/// - Returns: Interceptors to use when invoking 'getLightdInfo'.
func makeGetLightdInfoInterceptors() -> [ClientInterceptor<Empty, LightdInfo>]
/// - Returns: Interceptors to use when invoking 'ping'.
func makePingInterceptors() -> [ClientInterceptor<Duration, PingResponse>]
}
internal enum CompactTxStreamerClientMetadata {
internal static let serviceDescriptor = GRPCServiceDescriptor(
name: "CompactTxStreamer",
fullName: "cash.z.wallet.sdk.rpc.CompactTxStreamer",
methods: [
CompactTxStreamerClientMetadata.Methods.getLatestBlock,
CompactTxStreamerClientMetadata.Methods.getBlock,
CompactTxStreamerClientMetadata.Methods.getBlockRange,
CompactTxStreamerClientMetadata.Methods.getTransaction,
CompactTxStreamerClientMetadata.Methods.sendTransaction,
CompactTxStreamerClientMetadata.Methods.getTaddressTxids,
CompactTxStreamerClientMetadata.Methods.getTaddressBalance,
CompactTxStreamerClientMetadata.Methods.getTaddressBalanceStream,
CompactTxStreamerClientMetadata.Methods.getMempoolTx,
CompactTxStreamerClientMetadata.Methods.getTreeState,
CompactTxStreamerClientMetadata.Methods.getAddressUtxos,
CompactTxStreamerClientMetadata.Methods.getAddressUtxosStream,
CompactTxStreamerClientMetadata.Methods.getLightdInfo,
CompactTxStreamerClientMetadata.Methods.ping,
]
)
internal enum Methods {
internal static let getLatestBlock = GRPCMethodDescriptor(
name: "GetLatestBlock",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLatestBlock",
type: GRPCCallType.unary
)
internal static let getBlock = GRPCMethodDescriptor(
name: "GetBlock",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlock",
type: GRPCCallType.unary
)
internal static let getBlockRange = GRPCMethodDescriptor(
name: "GetBlockRange",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetBlockRange",
type: GRPCCallType.serverStreaming
)
internal static let getTransaction = GRPCMethodDescriptor(
name: "GetTransaction",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTransaction",
type: GRPCCallType.unary
)
internal static let sendTransaction = GRPCMethodDescriptor(
name: "SendTransaction",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/SendTransaction",
type: GRPCCallType.unary
)
internal static let getTaddressTxids = GRPCMethodDescriptor(
name: "GetTaddressTxids",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressTxids",
type: GRPCCallType.serverStreaming
)
internal static let getTaddressBalance = GRPCMethodDescriptor(
name: "GetTaddressBalance",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalance",
type: GRPCCallType.unary
)
internal static let getTaddressBalanceStream = GRPCMethodDescriptor(
name: "GetTaddressBalanceStream",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTaddressBalanceStream",
type: GRPCCallType.clientStreaming
)
internal static let getMempoolTx = GRPCMethodDescriptor(
name: "GetMempoolTx",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolTx",
type: GRPCCallType.serverStreaming
)
internal static let getTreeState = GRPCMethodDescriptor(
name: "GetTreeState",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTreeState",
type: GRPCCallType.unary
)
internal static let getAddressUtxos = GRPCMethodDescriptor(
name: "GetAddressUtxos",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxos",
type: GRPCCallType.unary
)
internal static let getAddressUtxosStream = GRPCMethodDescriptor(
name: "GetAddressUtxosStream",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream",
type: GRPCCallType.serverStreaming
)
internal static let getLightdInfo = GRPCMethodDescriptor(
name: "GetLightdInfo",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLightdInfo",
type: GRPCCallType.unary
)
internal static let ping = GRPCMethodDescriptor(
name: "Ping",
path: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/Ping",
type: GRPCCallType.unary
)
}
}

View File

@ -33,7 +33,7 @@ struct BlockID {
var height: UInt64 = 0
var hash: Data = SwiftProtobuf.Internal.emptyData
var hash: Data = Data()
var unknownFields = SwiftProtobuf.UnknownStorage()
@ -95,7 +95,7 @@ struct TxFilter {
var index: UInt64 = 0
/// transaction ID (hash, txid)
var hash: Data = SwiftProtobuf.Internal.emptyData
var hash: Data = Data()
var unknownFields = SwiftProtobuf.UnknownStorage()
@ -112,7 +112,7 @@ struct RawTransaction {
// methods supported on all messages.
/// exact data returned by Zcash 'getrawtransaction'
var data: Data = SwiftProtobuf.Internal.emptyData
var data: Data = Data()
/// height that the transaction was mined (or -1)
var height: UInt64 = 0
@ -367,11 +367,11 @@ struct GetAddressUtxosReply {
var address: String = String()
var txid: Data = SwiftProtobuf.Internal.emptyData
var txid: Data = Data()
var index: Int32 = 0
var script: Data = SwiftProtobuf.Internal.emptyData
var script: Data = Data()
var valueZat: Int64 = 0
@ -394,6 +394,28 @@ struct GetAddressUtxosReplyList {
init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension BlockID: @unchecked Sendable {}
extension BlockRange: @unchecked Sendable {}
extension TxFilter: @unchecked Sendable {}
extension RawTransaction: @unchecked Sendable {}
extension SendResponse: @unchecked Sendable {}
extension ChainSpec: @unchecked Sendable {}
extension Empty: @unchecked Sendable {}
extension LightdInfo: @unchecked Sendable {}
extension TransparentAddressBlockFilter: @unchecked Sendable {}
extension Duration: @unchecked Sendable {}
extension PingResponse: @unchecked Sendable {}
extension Address: @unchecked Sendable {}
extension AddressList: @unchecked Sendable {}
extension Balance: @unchecked Sendable {}
extension Exclude: @unchecked Sendable {}
extension TreeState: @unchecked Sendable {}
extension GetAddressUtxosArg: @unchecked Sendable {}
extension GetAddressUtxosReply: @unchecked Sendable {}
extension GetAddressUtxosReplyList: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "cash.z.wallet.sdk.rpc"
@ -407,9 +429,12 @@ extension BlockID: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularUInt64Field(value: &self.height)
case 2: try decoder.decodeSingularBytesField(value: &self.hash)
case 1: try { try decoder.decodeSingularUInt64Field(value: &self.height) }()
case 2: try { try decoder.decodeSingularBytesField(value: &self.hash) }()
default: break
}
}
@ -442,21 +467,28 @@ extension BlockRange: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &self._start)
case 2: try decoder.decodeSingularMessageField(value: &self._end)
case 1: try { try decoder.decodeSingularMessageField(value: &self._start) }()
case 2: try { try decoder.decodeSingularMessageField(value: &self._end) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._start {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = self._start {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
if let v = self._end {
} }()
try { if let v = self._end {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
} }()
try unknownFields.traverse(visitor: &visitor)
}
@ -478,19 +510,26 @@ extension TxFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularMessageField(value: &self._block)
case 2: try decoder.decodeSingularUInt64Field(value: &self.index)
case 3: try decoder.decodeSingularBytesField(value: &self.hash)
case 1: try { try decoder.decodeSingularMessageField(value: &self._block) }()
case 2: try { try decoder.decodeSingularUInt64Field(value: &self.index) }()
case 3: try { try decoder.decodeSingularBytesField(value: &self.hash) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._block {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
try { if let v = self._block {
try visitor.visitSingularMessageField(value: v, fieldNumber: 1)
}
} }()
if self.index != 0 {
try visitor.visitSingularUInt64Field(value: self.index, fieldNumber: 2)
}
@ -518,9 +557,12 @@ extension RawTransaction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self.data)
case 2: try decoder.decodeSingularUInt64Field(value: &self.height)
case 1: try { try decoder.decodeSingularBytesField(value: &self.data) }()
case 2: try { try decoder.decodeSingularUInt64Field(value: &self.height) }()
default: break
}
}
@ -553,9 +595,12 @@ extension SendResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularInt32Field(value: &self.errorCode)
case 2: try decoder.decodeSingularStringField(value: &self.errorMessage)
case 1: try { try decoder.decodeSingularInt32Field(value: &self.errorCode) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.errorMessage) }()
default: break
}
}
@ -638,21 +683,24 @@ extension LightdInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.version)
case 2: try decoder.decodeSingularStringField(value: &self.vendor)
case 3: try decoder.decodeSingularBoolField(value: &self.taddrSupport)
case 4: try decoder.decodeSingularStringField(value: &self.chainName)
case 5: try decoder.decodeSingularUInt64Field(value: &self.saplingActivationHeight)
case 6: try decoder.decodeSingularStringField(value: &self.consensusBranchID)
case 7: try decoder.decodeSingularUInt64Field(value: &self.blockHeight)
case 8: try decoder.decodeSingularStringField(value: &self.gitCommit)
case 9: try decoder.decodeSingularStringField(value: &self.branch)
case 10: try decoder.decodeSingularStringField(value: &self.buildDate)
case 11: try decoder.decodeSingularStringField(value: &self.buildUser)
case 12: try decoder.decodeSingularUInt64Field(value: &self.estimatedHeight)
case 13: try decoder.decodeSingularStringField(value: &self.zcashdBuild)
case 14: try decoder.decodeSingularStringField(value: &self.zcashdSubversion)
case 1: try { try decoder.decodeSingularStringField(value: &self.version) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.vendor) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.taddrSupport) }()
case 4: try { try decoder.decodeSingularStringField(value: &self.chainName) }()
case 5: try { try decoder.decodeSingularUInt64Field(value: &self.saplingActivationHeight) }()
case 6: try { try decoder.decodeSingularStringField(value: &self.consensusBranchID) }()
case 7: try { try decoder.decodeSingularUInt64Field(value: &self.blockHeight) }()
case 8: try { try decoder.decodeSingularStringField(value: &self.gitCommit) }()
case 9: try { try decoder.decodeSingularStringField(value: &self.branch) }()
case 10: try { try decoder.decodeSingularStringField(value: &self.buildDate) }()
case 11: try { try decoder.decodeSingularStringField(value: &self.buildUser) }()
case 12: try { try decoder.decodeSingularUInt64Field(value: &self.estimatedHeight) }()
case 13: try { try decoder.decodeSingularStringField(value: &self.zcashdBuild) }()
case 14: try { try decoder.decodeSingularStringField(value: &self.zcashdSubversion) }()
default: break
}
}
@ -733,21 +781,28 @@ extension TransparentAddressBlockFilter: SwiftProtobuf.Message, SwiftProtobuf._M
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.address)
case 2: try decoder.decodeSingularMessageField(value: &self._range)
case 1: try { try decoder.decodeSingularStringField(value: &self.address) }()
case 2: try { try decoder.decodeSingularMessageField(value: &self._range) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !self.address.isEmpty {
try visitor.visitSingularStringField(value: self.address, fieldNumber: 1)
}
if let v = self._range {
try { if let v = self._range {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
}
} }()
try unknownFields.traverse(visitor: &visitor)
}
@ -767,8 +822,11 @@ extension Duration: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularInt64Field(value: &self.intervalUs)
case 1: try { try decoder.decodeSingularInt64Field(value: &self.intervalUs) }()
default: break
}
}
@ -797,9 +855,12 @@ extension PingResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularInt64Field(value: &self.entry)
case 2: try decoder.decodeSingularInt64Field(value: &self.exit)
case 1: try { try decoder.decodeSingularInt64Field(value: &self.entry) }()
case 2: try { try decoder.decodeSingularInt64Field(value: &self.exit) }()
default: break
}
}
@ -831,8 +892,11 @@ extension Address: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.address)
case 1: try { try decoder.decodeSingularStringField(value: &self.address) }()
default: break
}
}
@ -860,8 +924,11 @@ extension AddressList: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeRepeatedStringField(value: &self.addresses)
case 1: try { try decoder.decodeRepeatedStringField(value: &self.addresses) }()
default: break
}
}
@ -889,8 +956,11 @@ extension Balance: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularInt64Field(value: &self.valueZat)
case 1: try { try decoder.decodeSingularInt64Field(value: &self.valueZat) }()
default: break
}
}
@ -918,8 +988,11 @@ extension Exclude: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeRepeatedBytesField(value: &self.txid)
case 1: try { try decoder.decodeRepeatedBytesField(value: &self.txid) }()
default: break
}
}
@ -951,12 +1024,15 @@ extension TreeState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self.network)
case 2: try decoder.decodeSingularUInt64Field(value: &self.height)
case 3: try decoder.decodeSingularStringField(value: &self.hash)
case 4: try decoder.decodeSingularUInt32Field(value: &self.time)
case 5: try decoder.decodeSingularStringField(value: &self.tree)
case 1: try { try decoder.decodeSingularStringField(value: &self.network) }()
case 2: try { try decoder.decodeSingularUInt64Field(value: &self.height) }()
case 3: try { try decoder.decodeSingularStringField(value: &self.hash) }()
case 4: try { try decoder.decodeSingularUInt32Field(value: &self.time) }()
case 5: try { try decoder.decodeSingularStringField(value: &self.tree) }()
default: break
}
}
@ -1002,10 +1078,13 @@ extension GetAddressUtxosArg: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeRepeatedStringField(value: &self.addresses)
case 2: try decoder.decodeSingularUInt64Field(value: &self.startHeight)
case 3: try decoder.decodeSingularUInt32Field(value: &self.maxEntries)
case 1: try { try decoder.decodeRepeatedStringField(value: &self.addresses) }()
case 2: try { try decoder.decodeSingularUInt64Field(value: &self.startHeight) }()
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.maxEntries) }()
default: break
}
}
@ -1046,13 +1125,16 @@ extension GetAddressUtxosReply: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeSingularBytesField(value: &self.txid)
case 2: try decoder.decodeSingularInt32Field(value: &self.index)
case 3: try decoder.decodeSingularBytesField(value: &self.script)
case 4: try decoder.decodeSingularInt64Field(value: &self.valueZat)
case 5: try decoder.decodeSingularUInt64Field(value: &self.height)
case 6: try decoder.decodeSingularStringField(value: &self.address)
case 1: try { try decoder.decodeSingularBytesField(value: &self.txid) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.index) }()
case 3: try { try decoder.decodeSingularBytesField(value: &self.script) }()
case 4: try { try decoder.decodeSingularInt64Field(value: &self.valueZat) }()
case 5: try { try decoder.decodeSingularUInt64Field(value: &self.height) }()
case 6: try { try decoder.decodeSingularStringField(value: &self.address) }()
default: break
}
}
@ -1100,8 +1182,11 @@ extension GetAddressUtxosReplyList: SwiftProtobuf.Message, SwiftProtobuf._Messag
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try decoder.decodeRepeatedMessageField(value: &self.addressUtxos)
case 1: try { try decoder.decodeRepeatedMessageField(value: &self.addressUtxos) }()
default: break
}
}

View File

@ -632,7 +632,14 @@ public class SDKSynchronizer: Synchronizer {
}
public func latestHeight(result: @escaping (Result<BlockHeight, Error>) -> Void) {
blockProcessor.downloader.latestBlockHeight(result: result)
Task {
do {
let latestBlockHeight = try await blockProcessor.downloader.latestBlockHeightAsync()
result(.success(latestBlockHeight))
} catch {
result(.failure(error))
}
}
}
public func latestHeight() throws -> BlockHeight {

View File

@ -40,31 +40,23 @@ class BlockDownloaderTests: XCTestCase {
try? FileManager.default.removeItem(at: cacheDB)
}
func testSmallDownloadAsync() {
let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 3
func testSmallDownloadAsync() async {
let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
downloader.downloadBlockRange(range) { error in
expect.fulfill()
XCTAssertNil(error)
do {
try await downloader.downloadBlockRangeAsync(range)
// check what was 'stored'
self.storage.latestHeight { result in
expect.fulfill()
XCTAssertTrue(self.validate(result: result, against: upperRange))
self.downloader.lastDownloadedBlockHeight { resultHeight in
expect.fulfill()
XCTAssertTrue(self.validate(result: resultHeight, against: upperRange))
}
}
let latestHeight = try await self.storage.latestHeightAsync()
XCTAssertEqual(latestHeight, upperRange)
let resultHeight = try await self.downloader.lastDownloadedBlockHeightAsync()
XCTAssertEqual(resultHeight, upperRange)
} catch {
XCTFail("testSmallDownloadAsync() shouldn't fail")
}
wait(for: [expect], timeout: 2)
}
func testSmallDownload() {
@ -94,7 +86,7 @@ class BlockDownloaderTests: XCTestCase {
XCTAssertEqual(currentLatest, upperRange )
}
func testFailure() {
func testFailure() async {
let awfulDownloader = CompactBlockDownloader(
service: AwfulLightWalletService(
latestBlockHeight: self.network.constants.saplingActivationHeight + 1000,
@ -103,18 +95,16 @@ class BlockDownloaderTests: XCTestCase {
storage: ZcashConsoleFakeStorage()
)
let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 1
let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange, upperRange))
awfulDownloader.downloadBlockRange(range) { error in
expect.fulfill()
do {
try await awfulDownloader.downloadBlockRangeAsync(range)
} catch {
XCTAssertNotNil(error)
}
wait(for: [expect], timeout: 2)
}
}

View File

@ -61,6 +61,21 @@ class LightWalletServiceTests: XCTestCase {
wait(for: [expect], timeout: 10)
}
func testHundredBlocks() async throws {
let count = 99
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
let upperRange: BlockHeight = network.constants.saplingActivationHeight + count
let blockRange = lowerRange ... upperRange
var blocks: [ZcashCompactBlock] = []
for try await block in service.blockRange(blockRange) {
blocks.append(block)
}
XCTAssertEqual(blocks.count, blockRange.count)
XCTAssertEqual(blocks[0].height, lowerRange)
XCTAssertEqual(blocks.last!.height, upperRange)
}
func testSyncBlockRange() {
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
let upperRange: BlockHeight = network.constants.saplingActivationHeight + 99
@ -74,6 +89,18 @@ class LightWalletServiceTests: XCTestCase {
}
}
func testSyncBlockRange() async throws {
let lowerRange: BlockHeight = network.constants.saplingActivationHeight
let upperRange: BlockHeight = network.constants.saplingActivationHeight + 99
let blockRange = lowerRange ... upperRange
var blocks: [ZcashCompactBlock] = []
for try await block in service.blockRange(blockRange) {
blocks.append(block)
}
XCTAssertEqual(blocks.count, blockRange.count)
}
func testLatestBlock() {
let expect = XCTestExpectation(description: self.description)
service.latestBlockHeight { result in
@ -88,4 +115,9 @@ class LightWalletServiceTests: XCTestCase {
wait(for: [expect], timeout: 10)
}
func testLatestBlock() async throws {
let height = try await service.latestBlockHeightAsync()
XCTAssertTrue(height > self.network.constants.saplingActivationHeight)
}
}

View File

@ -19,7 +19,12 @@ class CompactBlockStorageTests: XCTestCase {
func testEmptyStorage() {
XCTAssertEqual(try! compactBlockDao.latestHeight(), BlockHeight.empty())
}
func testEmptyStorageAsync() async throws {
let latestHeight = try await compactBlockDao.latestHeightAsync()
XCTAssertEqual(latestHeight, BlockHeight.empty())
}
func testStoreThousandBlocks() {
let initialHeight = try! compactBlockDao.latestHeight()
let startHeight = self.network.constants.saplingActivationHeight
@ -38,6 +43,19 @@ class CompactBlockStorageTests: XCTestCase {
XCTAssertEqual(latestHeight, finalHeight)
}
func testStoreThousandBlocksAsync() async throws {
let initialHeight = try! compactBlockDao.latestHeight()
let startHeight = self.network.constants.saplingActivationHeight
let blockCount = Int(1_000)
let finalHeight = startHeight + blockCount
try TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
let latestHeight = try await compactBlockDao.latestHeightAsync()
XCTAssertNotEqual(initialHeight, latestHeight)
XCTAssertEqual(latestHeight, finalHeight)
}
func testStoreOneBlockFromEmpty() {
let initialHeight = try! compactBlockDao.latestHeight()
guard initialHeight == BlockHeight.empty() else {
@ -61,6 +79,24 @@ class CompactBlockStorageTests: XCTestCase {
}
}
func testStoreOneBlockFromEmptyAsync() async throws {
let initialHeight = try await compactBlockDao.latestHeightAsync()
guard initialHeight == BlockHeight.empty() else {
XCTFail("database not empty, latest height: \(initialHeight)")
return
}
let expectedHeight = BlockHeight(123_456)
guard let block = StubBlockCreator.createRandomDataBlock(with: expectedHeight) else {
XCTFail("could not create randem block with height: \(expectedHeight)")
return
}
try await compactBlockDao.writeAsync(blocks: [block])
let result = try await compactBlockDao.latestHeightAsync()
XCTAssertEqual(result, expectedHeight)
}
func testRewindTo() {
let startHeight = self.network.constants.saplingActivationHeight
let blockCount = Int(1_000)
@ -82,4 +118,17 @@ class CompactBlockStorageTests: XCTestCase {
XCTFail("Rewind latest block failed with error: \(error)")
}
}
func testRewindToAsync() async throws {
let startHeight = self.network.constants.saplingActivationHeight
let blockCount = Int(1_000)
let finalHeight = startHeight + blockCount
try TestDbBuilder.seed(db: compactBlockDao, with: startHeight...finalHeight)
let rewindHeight = BlockHeight(finalHeight - 233)
try await compactBlockDao.rewindAsync(to: rewindHeight)
let latestHeight = try await compactBlockDao.latestHeightAsync()
XCTAssertEqual(latestHeight, rewindHeight - 1)
}
}

View File

@ -75,6 +75,10 @@ class DarksideWalletService: LightWalletService {
)
}
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
service.blockStream(startHeight: startHeight, endHeight: endHeight)
}
func getInfo() throws -> LightWalletdInfo {
try service.getInfo()
}
@ -87,7 +91,11 @@ class DarksideWalletService: LightWalletService {
}
func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
return []
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(
@ -98,8 +106,8 @@ class DarksideWalletService: LightWalletService {
service.fetchUTXOs(for: tAddress, height: height, result: result)
}
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
try service.fetchUTXOs(for: tAddresses, height: height)
func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
service.fetchUTXOs(for: tAddress, height: height)
}
func fetchUTXOs(
@ -109,7 +117,10 @@ class DarksideWalletService: LightWalletService {
) {
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)
@ -222,6 +233,26 @@ class DarksideWalletService: LightWalletService {
func clearAddedUTXOs() throws {
_ = try darksideService.clearAddressUtxo(Empty(), callOptions: nil).response.wait()
}
func getInfoAsync() async throws -> LightWalletdInfo {
try service.getInfo()
}
func latestBlockHeightAsync() async throws -> BlockHeight {
try service.latestBlockHeight()
}
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
service.blockRange(range)
}
func submitAsync(spendTransaction: Data) async throws -> LightWalletServiceResponse {
try service.submit(spendTransaction: spendTransaction)
}
func fetchTransactionAsync(txId: Data) async throws -> TransactionEntity {
try service.fetchTransaction(txId: txId)
}
}
enum DarksideWalletDConstants: NetworkConstants {

View File

@ -35,6 +35,10 @@ class MockLightWalletService: LightWalletService {
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")
@ -59,28 +63,30 @@ class MockLightWalletService: LightWalletService {
[]
}
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 tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
[]
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 tAddress: String,
result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void
) {
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error> {
AsyncThrowingStream { _ in }
}
private var service: LightWalletService
@ -126,4 +132,35 @@ class MockLightWalletService: LightWalletService {
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) {
}
func getInfoAsync() async throws -> LightWalletdInfo {
guard let info = mockLightDInfo else {
throw LightWalletServiceError.generalError(message: "Not Implemented")
}
return info
}
func latestBlockHeightAsync() async throws -> BlockHeight {
latestHeight
}
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
service.blockRange(range)
}
func submitAsync(spendTransaction: Data) async throws -> LightWalletServiceResponse {
LightWalletServiceMockResponse(errorCode: 0, errorMessage: "", unknownFields: UnknownStorage())
}
func fetchTransactionAsync(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] {
[]
}
}

View File

@ -10,6 +10,18 @@ import Foundation
@testable import ZcashLightClientKit
class ZcashConsoleFakeStorage: CompactBlockRepository {
func latestHeightAsync() async throws -> BlockHeight {
latestBlockHeight
}
func writeAsync(blocks: [ZcashCompactBlock]) async throws {
fakeSave(blocks: blocks)
}
func rewindAsync(to height: BlockHeight) async throws {
fakeRewind(to: height)
}
func latestHeight() throws -> Int {
return self.latestBlockHeight
}
@ -29,12 +41,6 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
self.latestBlockHeight = latestBlockHeight
}
func latestHeight(result: @escaping (Result<BlockHeight, Error>) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
result(.success(self.latestBlockHeight))
}
}
private func fakeSave(blocks: [ZcashCompactBlock]) {
blocks.forEach {
LoggerProxy.debug("saving block \($0)")
@ -42,20 +48,6 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
}
}
func write(blocks: [ZcashCompactBlock], completion: ((Error?) -> Void)?) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.fakeSave(blocks: blocks)
completion?(nil)
}
}
func rewind(to height: BlockHeight, completion: ((Error?) -> Void)?) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.fakeRewind(to: height)
completion?(nil)
}
}
private func fakeRewind(to height: BlockHeight) {
LoggerProxy.debug("rewind to \(height)")
self.latestBlockHeight = min(self.latestBlockHeight, height)

View File

@ -33,6 +33,10 @@ class AwfulLightWalletService: MockLightWalletService {
}
}
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))

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'ZcashLightClientKit'
s.version = '0.16.7-beta'
s.version = '0.16.8-beta'
s.summary = 'Zcash Light Client wallet SDK for iOS'
s.description = <<-DESC

View File

@ -1,3 +1,20 @@
# 0.16.8-beta
Checkpoints added:
Mainnet
````
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1775000.json
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1777500.json
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1780000.json
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1782500.json
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/1785000.json
````
Testnet
````
Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2000000.json
Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2010000.json
````
# 0.16.7-beta
- [#455] revert queue priority downgrade changes from [#435] (#456)