2019-10-18 11:45:19 -07:00
|
|
|
//
|
|
|
|
// ServiceHelper.swift
|
|
|
|
// gRPC-PoC
|
|
|
|
//
|
|
|
|
// Created by Francisco Gindre on 29/08/2019.
|
|
|
|
// Copyright © 2019 Electric Coin Company. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2020-04-09 15:25:43 -07:00
|
|
|
import GRPC
|
|
|
|
import NIO
|
|
|
|
public typealias Channel = GRPC.GRPCChannel
|
2019-10-18 11:45:19 -07:00
|
|
|
|
2020-02-26 08:54:48 -08:00
|
|
|
/**
|
|
|
|
Swift GRPC implementation of Lightwalletd service */
|
2019-10-31 15:43:09 -07:00
|
|
|
public class LightWalletGRPCService {
|
2019-10-18 11:45:19 -07:00
|
|
|
|
|
|
|
var queue = DispatchQueue.init(label: "LightWalletGRPCService")
|
|
|
|
let channel: Channel
|
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
let compactTxStreamer: CompactTxStreamerClient
|
2019-10-18 11:45:19 -07:00
|
|
|
|
2019-10-31 15:43:09 -07:00
|
|
|
public init(channel: Channel) {
|
2019-10-18 11:45:19 -07:00
|
|
|
self.channel = channel
|
2020-04-09 15:25:43 -07:00
|
|
|
compactTxStreamer = CompactTxStreamerClient(channel: self.channel)
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
2019-11-14 06:38:54 -08:00
|
|
|
public convenience init(endpoint: LightWalletEndpoint) {
|
2020-04-09 15:25:43 -07:00
|
|
|
self.init(host: endpoint.host, port: endpoint.port, secure: endpoint.secure)
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
public convenience init(host: String, port: Int = 9067, secure: Bool = true) {
|
|
|
|
let configuration = ClientConnection.Configuration(target: .hostAndPort(host, port), eventLoopGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1), tls: secure ? .init() : nil)
|
|
|
|
let channel = ClientConnection(configuration: configuration)
|
2019-10-31 15:43:09 -07:00
|
|
|
self.init(channel: channel)
|
|
|
|
}
|
|
|
|
|
2020-03-13 17:00:01 -07:00
|
|
|
func stop() {
|
2020-04-09 15:25:43 -07:00
|
|
|
_ = channel.close()
|
2020-03-13 17:00:01 -07:00
|
|
|
}
|
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
func blockRange(startHeight: BlockHeight, endHeight: BlockHeight? = nil, result: @escaping (CompactBlock) -> Void) throws -> ServerStreamingCall<BlockRange, CompactBlock> {
|
|
|
|
compactTxStreamer.getBlockRange(BlockRange(startHeight: startHeight, endHeight: endHeight), handler: result)
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func latestBlock() throws -> BlockID {
|
2020-04-09 15:25:43 -07:00
|
|
|
try compactTxStreamer.getLatestBlock(ChainSpec()).response.wait()
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
2019-10-18 13:09:13 -07:00
|
|
|
func getTx(hash: String) throws -> RawTransaction {
|
2019-10-18 11:45:19 -07:00
|
|
|
var filter = TxFilter()
|
|
|
|
filter.hash = Data(hash.utf8)
|
2020-04-09 15:25:43 -07:00
|
|
|
return try compactTxStreamer.getTransaction(filter).response.wait()
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension LightWalletGRPCService: LightWalletService {
|
2020-04-23 10:11:03 -07:00
|
|
|
public func fetchTransaction(txId: Data) throws -> TransactionEntity {
|
|
|
|
var txFilter = TxFilter()
|
|
|
|
txFilter.hash = txId
|
|
|
|
let rawTx = try compactTxStreamer.getTransaction(txFilter).response.wait()
|
|
|
|
|
|
|
|
return TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx)
|
|
|
|
}
|
|
|
|
|
|
|
|
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(LightWalletServiceError.genericError(error: error)))
|
|
|
|
case .success(let rawTx):
|
|
|
|
result(.success(TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx)))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-12-03 07:19:44 -08:00
|
|
|
public func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
|
2020-04-09 15:25:43 -07:00
|
|
|
do {
|
|
|
|
let tx = try RawTransaction(serializedData: spendTransaction)
|
|
|
|
let response = self.compactTxStreamer.sendTransaction(tx).response
|
2019-12-03 07:19:44 -08:00
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
response.whenComplete { (responseResult) in
|
|
|
|
switch responseResult {
|
|
|
|
case .failure(let e):
|
|
|
|
result(.failure(LightWalletServiceError.genericError(error: e)))
|
|
|
|
case .success(let s):
|
|
|
|
result(.success(s))
|
|
|
|
}
|
2019-12-03 07:19:44 -08:00
|
|
|
}
|
2020-04-09 15:25:43 -07:00
|
|
|
} catch {
|
|
|
|
result(.failure(LightWalletServiceError.genericError(error: error)))
|
2019-12-03 07:19:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
|
2019-12-16 14:25:45 -08:00
|
|
|
|
|
|
|
let rawTx = RawTransaction.with { (raw) in
|
|
|
|
raw.data = spendTransaction
|
|
|
|
}
|
2020-04-09 15:25:43 -07:00
|
|
|
return try compactTxStreamer.sendTransaction(rawTx).response.wait()
|
2019-12-03 07:19:44 -08:00
|
|
|
}
|
|
|
|
|
2019-10-31 15:43:09 -07:00
|
|
|
public func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] {
|
2020-04-09 15:25:43 -07:00
|
|
|
var blocks = [CompactBlock]()
|
2019-10-18 11:45:19 -07:00
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
let response = compactTxStreamer.getBlockRange(range.blockRange(), handler: {
|
|
|
|
blocks.append($0)
|
2020-04-23 10:11:03 -07:00
|
|
|
})
|
2019-10-18 11:45:19 -07:00
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
do {
|
2020-04-23 10:11:03 -07:00
|
|
|
_ = try response.status.wait()
|
2020-04-09 15:25:43 -07:00
|
|
|
} catch {
|
|
|
|
throw LightWalletServiceError.genericError(error: error)
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
do {
|
|
|
|
return try blocks.asZcashCompactBlocks()
|
|
|
|
} catch {
|
|
|
|
LoggerProxy.error("invalid block in range: \(range) - Error: \(error)")
|
|
|
|
throw LightWalletServiceError.genericError(error: error)
|
|
|
|
}
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
2019-10-31 15:43:09 -07:00
|
|
|
public func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
|
2020-04-09 15:25:43 -07:00
|
|
|
let response = compactTxStreamer.getLatestBlock(ChainSpec()).response
|
|
|
|
|
|
|
|
response.whenSuccess { (blockID) in
|
|
|
|
guard let blockHeight = Int(exactly: blockID.height) else {
|
|
|
|
result(.failure(LightWalletServiceError.generalError))
|
|
|
|
return
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
2020-04-09 15:25:43 -07:00
|
|
|
result(.success(blockHeight))
|
|
|
|
}
|
|
|
|
|
|
|
|
response.whenFailure { (error) in
|
|
|
|
result(.failure(LightWalletServiceError.genericError(error: error)))
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
2020-04-09 15:25:43 -07:00
|
|
|
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Make cancellable
|
2019-10-31 15:43:09 -07:00
|
|
|
public func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
|
2020-04-23 10:11:03 -07:00
|
|
|
|
2020-02-26 08:54:48 -08:00
|
|
|
queue.async { [weak self] in
|
|
|
|
|
|
|
|
guard let self = self else { return }
|
|
|
|
|
2019-10-18 11:45:19 -07:00
|
|
|
var blocks = [CompactBlock]()
|
2020-04-09 15:25:43 -07:00
|
|
|
|
|
|
|
let response = self.compactTxStreamer.getBlockRange(range.blockRange(), handler: { blocks.append($0) })
|
|
|
|
|
2019-10-18 11:45:19 -07:00
|
|
|
do {
|
2020-04-09 15:25:43 -07:00
|
|
|
let status = try response.status.wait()
|
|
|
|
switch status.code {
|
|
|
|
case .ok:
|
|
|
|
do {
|
|
|
|
result(.success(try blocks.asZcashCompactBlocks()))
|
|
|
|
} catch {
|
|
|
|
result(.failure(LightWalletServiceError.generalError))
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
2020-04-23 10:11:03 -07:00
|
|
|
|
2020-04-09 15:25:43 -07:00
|
|
|
default:
|
2020-04-23 10:11:03 -07:00
|
|
|
result(Result.failure(LightWalletServiceError.failed(statusCode: status.code.rawValue, message: status.message ?? "No Message")))
|
2020-04-09 15:25:43 -07:00
|
|
|
}
|
2019-10-18 11:45:19 -07:00
|
|
|
|
|
|
|
} catch {
|
2020-04-09 15:25:43 -07:00
|
|
|
result(.failure(LightWalletServiceError.genericError(error: error)))
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
2020-04-09 15:25:43 -07:00
|
|
|
|
2019-10-18 11:45:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-31 15:43:09 -07:00
|
|
|
public func latestBlockHeight() throws -> BlockHeight {
|
2019-10-18 11:45:19 -07:00
|
|
|
|
2019-10-18 13:09:13 -07:00
|
|
|
guard let height = try? latestBlock().compactBlockHeight() else {
|
2019-10-18 11:45:19 -07:00
|
|
|
throw LightWalletServiceError.invalidBlock
|
|
|
|
}
|
|
|
|
return height
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|