[#1469] Use Tor for single-shot lightwalletd requests
- TorLwdConn refactored to resolve SwiftLint issues and best practives - TorLwdConn getInfo, getTreeState and latestBlockHeight methids implemented - LightWalletGRPCService enhanced to use available TOR methods - Code cleaned up - Changelog updated - documented tor connection methods - torConnection initialization when fails, fallback to classic compactTxStreamer
This commit is contained in:
parent
1a2db6b167
commit
16a80d2efb
|
@ -9,6 +9,9 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## Added
|
## Added
|
||||||
- `SDKSynchronizer.estimateBirthdayHeight(for date: Date)`: Get an estimated height for a given date, typically used for estimating birthday.
|
- `SDKSynchronizer.estimateBirthdayHeight(for date: Date)`: Get an estimated height for a given date, typically used for estimating birthday.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
- `LightWalletGRPCService` updated to use TOR connection for: fetching and submission of the transaction, getting server info, latest block height and tree state.
|
||||||
|
|
||||||
# 2.2.11 - 2025-04-03
|
# 2.2.11 - 2025-04-03
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
|
@ -71,8 +71,6 @@ class SyncBlocksViewController: UIViewController {
|
||||||
case let .syncing(syncProgress, areFundsSpendable):
|
case let .syncing(syncProgress, areFundsSpendable):
|
||||||
enhancingStarted = false
|
enhancingStarted = false
|
||||||
|
|
||||||
print("__LD syncProgress \(syncProgress) areFundsSpendable \(areFundsSpendable)")
|
|
||||||
|
|
||||||
progressBar.progress = syncProgress
|
progressBar.progress = syncProgress
|
||||||
progressLabel.text = "\(floor(syncProgress * 1000) / 10)% spendable: \(areFundsSpendable)"
|
progressLabel.text = "\(floor(syncProgress * 1000) / 10)% spendable: \(areFundsSpendable)"
|
||||||
let progressText = """
|
let progressText = """
|
||||||
|
|
|
@ -778,7 +778,7 @@ extension CompactBlockProcessor {
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
if await self.isIdle() {
|
if await self.isIdle() {
|
||||||
if await self.canStartSync() {
|
if await self.canStartSync() {
|
||||||
self.logger.debug(
|
await self.logger.debug(
|
||||||
"""
|
"""
|
||||||
Timer triggered: Starting compact Block processor!.
|
Timer triggered: Starting compact Block processor!.
|
||||||
Processor State: \(await self.context.state)
|
Processor State: \(await self.context.state)
|
||||||
|
|
|
@ -420,6 +420,18 @@ public enum ZcashError: Equatable, Error {
|
||||||
/// - `rustError` contains error generated by the rust layer.
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
/// ZRUST0080
|
/// ZRUST0080
|
||||||
case rustTorLwdSubmit(_ rustError: String)
|
case rustTorLwdSubmit(_ rustError: String)
|
||||||
|
/// Error from rust layer when calling TorLwdConn.getInfo
|
||||||
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
|
/// ZRUST0081
|
||||||
|
case rustTorLwdGetInfo(_ rustError: String)
|
||||||
|
/// Error from rust layer when calling TorLwdConn.latestBlockHeight
|
||||||
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
|
/// ZRUST0082
|
||||||
|
case rustTorLwdLatestBlockHeight(_ rustError: String)
|
||||||
|
/// Error from rust layer when calling TorLwdConn.getTreeState
|
||||||
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
|
/// ZRUST0083
|
||||||
|
case rustTorLwdGetTreeState(_ rustError: String)
|
||||||
/// SQLite query failed when fetching all accounts from the database.
|
/// SQLite query failed when fetching all accounts from the database.
|
||||||
/// - `sqliteError` is error produced by SQLite library.
|
/// - `sqliteError` is error produced by SQLite library.
|
||||||
/// ZADAO0001
|
/// ZADAO0001
|
||||||
|
@ -802,6 +814,9 @@ public enum ZcashError: Equatable, Error {
|
||||||
case .rustTorConnectToLightwalletd: return "Error from rust layer when calling TorClient.connectToLightwalletd"
|
case .rustTorConnectToLightwalletd: return "Error from rust layer when calling TorClient.connectToLightwalletd"
|
||||||
case .rustTorLwdFetchTransaction: return "Error from rust layer when calling TorLwdConn.fetchTransaction"
|
case .rustTorLwdFetchTransaction: return "Error from rust layer when calling TorLwdConn.fetchTransaction"
|
||||||
case .rustTorLwdSubmit: return "Error from rust layer when calling TorLwdConn.submit"
|
case .rustTorLwdSubmit: return "Error from rust layer when calling TorLwdConn.submit"
|
||||||
|
case .rustTorLwdGetInfo: return "Error from rust layer when calling TorLwdConn.getInfo"
|
||||||
|
case .rustTorLwdLatestBlockHeight: return "Error from rust layer when calling TorLwdConn.latestBlockHeight"
|
||||||
|
case .rustTorLwdGetTreeState: return "Error from rust layer when calling TorLwdConn.getTreeState"
|
||||||
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
|
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
|
||||||
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
|
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
|
||||||
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
|
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
|
||||||
|
@ -1001,6 +1016,9 @@ public enum ZcashError: Equatable, Error {
|
||||||
case .rustTorConnectToLightwalletd: return .rustTorConnectToLightwalletd
|
case .rustTorConnectToLightwalletd: return .rustTorConnectToLightwalletd
|
||||||
case .rustTorLwdFetchTransaction: return .rustTorLwdFetchTransaction
|
case .rustTorLwdFetchTransaction: return .rustTorLwdFetchTransaction
|
||||||
case .rustTorLwdSubmit: return .rustTorLwdSubmit
|
case .rustTorLwdSubmit: return .rustTorLwdSubmit
|
||||||
|
case .rustTorLwdGetInfo: return .rustTorLwdGetInfo
|
||||||
|
case .rustTorLwdLatestBlockHeight: return .rustTorLwdLatestBlockHeight
|
||||||
|
case .rustTorLwdGetTreeState: return .rustTorLwdGetTreeState
|
||||||
case .accountDAOGetAll: return .accountDAOGetAll
|
case .accountDAOGetAll: return .accountDAOGetAll
|
||||||
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
|
||||||
case .accountDAOFindBy: return .accountDAOFindBy
|
case .accountDAOFindBy: return .accountDAOFindBy
|
||||||
|
|
|
@ -223,6 +223,12 @@ public enum ZcashErrorCode: String {
|
||||||
case rustTorLwdFetchTransaction = "ZRUST0079"
|
case rustTorLwdFetchTransaction = "ZRUST0079"
|
||||||
/// Error from rust layer when calling TorLwdConn.submit
|
/// Error from rust layer when calling TorLwdConn.submit
|
||||||
case rustTorLwdSubmit = "ZRUST0080"
|
case rustTorLwdSubmit = "ZRUST0080"
|
||||||
|
/// Error from rust layer when calling TorLwdConn.getInfo
|
||||||
|
case rustTorLwdGetInfo = "ZRUST0081"
|
||||||
|
/// Error from rust layer when calling TorLwdConn.latestBlockHeight
|
||||||
|
case rustTorLwdLatestBlockHeight = "ZRUST0082"
|
||||||
|
/// Error from rust layer when calling TorLwdConn.getTreeState
|
||||||
|
case rustTorLwdGetTreeState = "ZRUST0083"
|
||||||
/// SQLite query failed when fetching all accounts from the database.
|
/// SQLite query failed when fetching all accounts from the database.
|
||||||
case accountDAOGetAll = "ZADAO0001"
|
case accountDAOGetAll = "ZADAO0001"
|
||||||
/// Fetched accounts from SQLite but can't decode them.
|
/// Fetched accounts from SQLite but can't decode them.
|
||||||
|
|
|
@ -442,6 +442,18 @@ enum ZcashErrorDefinition {
|
||||||
/// - `rustError` contains error generated by the rust layer.
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
// sourcery: code="ZRUST0080"
|
// sourcery: code="ZRUST0080"
|
||||||
case rustTorLwdSubmit(_ rustError: String)
|
case rustTorLwdSubmit(_ rustError: String)
|
||||||
|
/// Error from rust layer when calling TorLwdConn.getInfo
|
||||||
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
|
// sourcery: code="ZRUST0081"
|
||||||
|
case rustTorLwdGetInfo(_ rustError: String)
|
||||||
|
/// Error from rust layer when calling TorLwdConn.latestBlockHeight
|
||||||
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
|
// sourcery: code="ZRUST0082"
|
||||||
|
case rustTorLwdLatestBlockHeight(_ rustError: String)
|
||||||
|
/// Error from rust layer when calling TorLwdConn.getTreeState
|
||||||
|
/// - `rustError` contains error generated by the rust layer.
|
||||||
|
// sourcery: code="ZRUST0083"
|
||||||
|
case rustTorLwdGetTreeState(_ rustError: String)
|
||||||
|
|
||||||
// MARK: - Account DAO
|
// MARK: - Account DAO
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,7 @@ public struct LightWalletEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlString: String {
|
var urlString: String {
|
||||||
return String(
|
String(format: "%@://%@:%d", secure ? "https" : "http", host, port)
|
||||||
format: "%@://%@:%d", secure ? "https" : "http", host, port)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ public struct SaplingParamsSourceURL {
|
||||||
public let outputParamFileURL: URL
|
public let outputParamFileURL: URL
|
||||||
|
|
||||||
public static var `default`: SaplingParamsSourceURL {
|
public static var `default`: SaplingParamsSourceURL {
|
||||||
return SaplingParamsSourceURL(spendParamFileURL: ZcashSDK.spendParamFileURL, outputParamFileURL: ZcashSDK.outputParamFileURL)
|
SaplingParamsSourceURL(spendParamFileURL: ZcashSDK.spendParamFileURL, outputParamFileURL: ZcashSDK.outputParamFileURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ class LightWalletGRPCService {
|
||||||
let singleCallTimeout: TimeLimit
|
let singleCallTimeout: TimeLimit
|
||||||
let streamingCallTimeout: TimeLimit
|
let streamingCallTimeout: TimeLimit
|
||||||
var latestBlockHeightProvider: LatestBlockHeightProvider = LiveLatestBlockHeightProvider()
|
var latestBlockHeightProvider: LatestBlockHeightProvider = LiveLatestBlockHeightProvider()
|
||||||
|
let torConn: TorLwdConn?
|
||||||
|
|
||||||
var connectionStateChange: ((_ from: ConnectionState, _ to: ConnectionState) -> Void)? {
|
var connectionStateChange: ((_ from: ConnectionState, _ to: ConnectionState) -> Void)? {
|
||||||
get { connectionManager.connectionStateChange }
|
get { connectionManager.connectionStateChange }
|
||||||
|
@ -69,13 +70,14 @@ class LightWalletGRPCService {
|
||||||
|
|
||||||
let queue: DispatchQueue
|
let queue: DispatchQueue
|
||||||
|
|
||||||
convenience init(endpoint: LightWalletEndpoint) {
|
convenience init(endpoint: LightWalletEndpoint, torURL: URL?) {
|
||||||
self.init(
|
self.init(
|
||||||
host: endpoint.host,
|
host: endpoint.host,
|
||||||
port: endpoint.port,
|
port: endpoint.port,
|
||||||
secure: endpoint.secure,
|
secure: endpoint.secure,
|
||||||
singleCallTimeout: endpoint.singleCallTimeoutInMillis,
|
singleCallTimeout: endpoint.singleCallTimeoutInMillis,
|
||||||
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis
|
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis,
|
||||||
|
torURL: torURL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +93,8 @@ class LightWalletGRPCService {
|
||||||
port: Int = 9067,
|
port: Int = 9067,
|
||||||
secure: Bool = true,
|
secure: Bool = true,
|
||||||
singleCallTimeout: Int64,
|
singleCallTimeout: Int64,
|
||||||
streamingCallTimeout: Int64
|
streamingCallTimeout: Int64,
|
||||||
|
torURL: URL?
|
||||||
) {
|
) {
|
||||||
self.connectionManager = ConnectionStatusManager()
|
self.connectionManager = ConnectionStatusManager()
|
||||||
self.queue = DispatchQueue.init(label: "LightWalletGRPCService")
|
self.queue = DispatchQueue.init(label: "LightWalletGRPCService")
|
||||||
|
@ -114,6 +117,13 @@ class LightWalletGRPCService {
|
||||||
timeLimit: self.singleCallTimeout
|
timeLimit: self.singleCallTimeout
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var tor: TorClient? = nil
|
||||||
|
if let torURL {
|
||||||
|
tor = try? TorClient(torDir: torURL)
|
||||||
|
}
|
||||||
|
let endpointString = String(format: "%@://%@:%d", secure ? "https" : "http", host, port)
|
||||||
|
self.torConn = try? tor?.connectToLightwalletd(endpoint: endpointString)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
@ -151,15 +161,23 @@ class LightWalletGRPCService {
|
||||||
extension LightWalletGRPCService: LightWalletService {
|
extension LightWalletGRPCService: LightWalletService {
|
||||||
func getInfo() async throws -> LightWalletdInfo {
|
func getInfo() async throws -> LightWalletdInfo {
|
||||||
do {
|
do {
|
||||||
return try await compactTxStreamer.getLightdInfo(Empty())
|
if let torConn {
|
||||||
|
return try torConn.getInfo()
|
||||||
|
} else {
|
||||||
|
return try await compactTxStreamer.getLightdInfo(Empty())
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
let serviceError = error.mapToServiceError()
|
let serviceError = error.mapToServiceError()
|
||||||
throw ZcashError.serviceGetInfoFailed(serviceError)
|
throw ZcashError.serviceGetInfoFailed(serviceError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func latestBlockHeight() async throws -> BlockHeight {
|
func latestBlockHeight() async throws -> BlockHeight {
|
||||||
try await latestBlockHeightProvider.latestBlockHeight(streamer: compactTxStreamer)
|
if let torConn {
|
||||||
|
return try torConn.latestBlockHeight()
|
||||||
|
} else {
|
||||||
|
return try await latestBlockHeightProvider.latestBlockHeight(streamer: compactTxStreamer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
||||||
|
@ -176,11 +194,15 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
|
||||||
do {
|
do {
|
||||||
let transaction = RawTransaction.with { $0.data = spendTransaction }
|
if let torConn {
|
||||||
return try await compactTxStreamer.sendTransaction(transaction)
|
return try torConn.submit(spendTransaction: spendTransaction)
|
||||||
|
} else {
|
||||||
|
let transaction = RawTransaction.with { $0.data = spendTransaction }
|
||||||
|
return try await compactTxStreamer.sendTransaction(transaction)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
let serviceError = error.mapToServiceError()
|
let serviceError = error.mapToServiceError()
|
||||||
throw ZcashError.serviceSubmitFailed(serviceError)
|
throw ZcashError.serviceSubmitFailed(serviceError)
|
||||||
|
@ -188,37 +210,41 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTransaction(txId: Data) async throws -> (tx: ZcashTransaction.Fetched?, status: TransactionStatus) {
|
func fetchTransaction(txId: Data) async throws -> (tx: ZcashTransaction.Fetched?, status: TransactionStatus) {
|
||||||
var txFilter = TxFilter()
|
if let torConn {
|
||||||
txFilter.hash = txId
|
return try torConn.fetchTransaction(txId: txId)
|
||||||
|
} else {
|
||||||
do {
|
var txFilter = TxFilter()
|
||||||
let rawTx = try await compactTxStreamer.getTransaction(txFilter)
|
txFilter.hash = txId
|
||||||
|
|
||||||
let isNotMined = rawTx.height == 0 || rawTx.height > UInt32.max
|
do {
|
||||||
|
let rawTx = try await compactTxStreamer.getTransaction(txFilter)
|
||||||
return (
|
|
||||||
tx:
|
let isNotMined = rawTx.height == 0 || rawTx.height > UInt32.max
|
||||||
ZcashTransaction.Fetched(
|
|
||||||
rawID: txId,
|
return (
|
||||||
minedHeight: isNotMined ? nil : UInt32(rawTx.height),
|
tx:
|
||||||
raw: rawTx.data
|
ZcashTransaction.Fetched(
|
||||||
),
|
rawID: txId,
|
||||||
status: isNotMined ? .notInMainChain : .mined(Int(rawTx.height))
|
minedHeight: isNotMined ? nil : UInt32(rawTx.height),
|
||||||
)
|
raw: rawTx.data
|
||||||
} catch let error as GRPCStatus {
|
),
|
||||||
if error.makeGRPCStatus().code == .notFound {
|
status: isNotMined ? .notInMainChain : .mined(Int(rawTx.height))
|
||||||
return (tx: nil, .txidNotRecognized)
|
)
|
||||||
} else if let notFound = error.message?.contains("Transaction not found"), notFound {
|
} catch let error as GRPCStatus {
|
||||||
return (tx: nil, .txidNotRecognized)
|
if error.makeGRPCStatus().code == .notFound {
|
||||||
} else if let notFound = error.message?.contains("No such mempool or blockchain transaction. Use gettransaction for wallet transactions."), notFound {
|
return (tx: nil, .txidNotRecognized)
|
||||||
return (tx: nil, .txidNotRecognized)
|
} else if let notFound = error.message?.contains("Transaction not found"), notFound {
|
||||||
} else {
|
return (tx: nil, .txidNotRecognized)
|
||||||
|
} else if let notFound = error.message?.contains("No such mempool or blockchain transaction. Use gettransaction for wallet transactions."), notFound {
|
||||||
|
return (tx: nil, .txidNotRecognized)
|
||||||
|
} else {
|
||||||
|
let serviceError = error.mapToServiceError()
|
||||||
|
throw ZcashError.serviceFetchTransactionFailed(serviceError)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
let serviceError = error.mapToServiceError()
|
let serviceError = error.mapToServiceError()
|
||||||
throw ZcashError.serviceFetchTransactionFailed(serviceError)
|
throw ZcashError.serviceFetchTransactionFailed(serviceError)
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
let serviceError = error.mapToServiceError()
|
|
||||||
throw ZcashError.serviceFetchTransactionFailed(serviceError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +325,11 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTreeState(_ id: BlockID) async throws -> TreeState {
|
func getTreeState(_ id: BlockID) async throws -> TreeState {
|
||||||
try await compactTxStreamer.getTreeState(id)
|
if let torConn {
|
||||||
|
return try torConn.getTreeState(height: BlockHeight(id.height))
|
||||||
|
} else {
|
||||||
|
return try await compactTxStreamer.getTreeState(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTaddressTxids(_ request: TransparentAddressBlockFilter) -> AsyncThrowingStream<RawTransaction, Error> {
|
func getTaddressTxids(_ request: TransparentAddressBlockFilter) -> AsyncThrowingStream<RawTransaction, Error> {
|
||||||
|
|
|
@ -136,9 +136,10 @@ protocol LightWalletServiceResponse {
|
||||||
|
|
||||||
struct LightWalletServiceFactory {
|
struct LightWalletServiceFactory {
|
||||||
let endpoint: LightWalletEndpoint
|
let endpoint: LightWalletEndpoint
|
||||||
|
let torURL: URL?
|
||||||
|
|
||||||
func make() -> LightWalletService {
|
func make() -> LightWalletService {
|
||||||
return LightWalletGRPCService(endpoint: endpoint)
|
return LightWalletGRPCService(endpoint: endpoint, torURL: torURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,8 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
|
||||||
)
|
)
|
||||||
|
|
||||||
guard let ffiAddressPtr else {
|
guard let ffiAddressPtr else {
|
||||||
throw ZcashError.rustDeriveAddressFromUfvk(ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveAddressFromUfvk` failed with unknown error"))
|
throw ZcashError.rustDeriveAddressFromUfvk(ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveAddressFromUfvk` failed with unknown error")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer { zcashlc_free_ffi_address(ffiAddressPtr) }
|
defer { zcashlc_free_ffi_address(ffiAddressPtr) }
|
||||||
|
@ -126,7 +127,8 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
|
||||||
|
|
||||||
guard let boxedSlice = boxedSlicePtr?.pointee else {
|
guard let boxedSlice = boxedSlicePtr?.pointee else {
|
||||||
throw ZcashError.rustDeriveUnifiedSpendingKey(
|
throw ZcashError.rustDeriveUnifiedSpendingKey(
|
||||||
ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveUnifiedSpendingKey` failed with unknown error"))
|
ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveUnifiedSpendingKey` failed with unknown error")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return boxedSlice.unsafeToUnifiedSpendingKey(network: networkType)
|
return boxedSlice.unsafeToUnifiedSpendingKey(network: networkType)
|
||||||
|
@ -207,7 +209,8 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
|
||||||
|
|
||||||
guard let key = boxedSlicePtr?.pointee else {
|
guard let key = boxedSlicePtr?.pointee else {
|
||||||
throw ZcashError.rustDeriveArbitraryWalletKey(
|
throw ZcashError.rustDeriveArbitraryWalletKey(
|
||||||
ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveArbitraryWalletKey` failed with unknown error"))
|
ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveArbitraryWalletKey` failed with unknown error")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key.ptr.toByteArray(
|
return key.ptr.toByteArray(
|
||||||
|
@ -237,7 +240,8 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
|
||||||
|
|
||||||
guard let key = boxedSlicePtr?.pointee else {
|
guard let key = boxedSlicePtr?.pointee else {
|
||||||
throw ZcashError.rustDeriveArbitraryAccountKey(
|
throw ZcashError.rustDeriveArbitraryAccountKey(
|
||||||
ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveArbitraryAccountKey` failed with unknown error"))
|
ZcashKeyDerivationBackend.lastErrorMessage(fallback: "`deriveArbitraryAccountKey` failed with unknown error")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key.ptr.toByteArray(
|
return key.ptr.toByteArray(
|
||||||
|
|
|
@ -76,7 +76,7 @@ enum Dependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
container.register(type: LightWalletService.self, isSingleton: true) { _ in
|
container.register(type: LightWalletService.self, isSingleton: true) { _ in
|
||||||
LightWalletGRPCService(endpoint: endpoint)
|
LightWalletGRPCService(endpoint: endpoint, torURL: urls.torDirURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.register(type: TransactionRepository.self, isSingleton: true) { _ in
|
container.register(type: TransactionRepository.self, isSingleton: true) { _ in
|
||||||
|
|
|
@ -180,8 +180,8 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
var areFundsSpendable = false
|
var areFundsSpendable = false
|
||||||
|
|
||||||
if let scanProgress = walletSummary?.scanProgress {
|
if let scanProgress = walletSummary?.scanProgress {
|
||||||
let composedNumerator: Float = Float(scanProgress.numerator) + Float(recoveryProgress?.numerator ?? 0)
|
let composedNumerator = Float(scanProgress.numerator) + Float(recoveryProgress?.numerator ?? 0)
|
||||||
let composedDenominator: Float = Float(scanProgress.denominator) + Float(recoveryProgress?.denominator ?? 0)
|
let composedDenominator = Float(scanProgress.denominator) + Float(recoveryProgress?.denominator ?? 0)
|
||||||
|
|
||||||
let progress: Float
|
let progress: Float
|
||||||
if composedDenominator == 0 {
|
if composedDenominator == 0 {
|
||||||
|
@ -749,7 +749,8 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
port: $0.port,
|
port: $0.port,
|
||||||
secure: $0.secure,
|
secure: $0.secure,
|
||||||
singleCallTimeout: 5000,
|
singleCallTimeout: 5000,
|
||||||
streamingCallTimeout: Int64(fetchThresholdSeconds) * 1000
|
streamingCallTimeout: Int64(fetchThresholdSeconds) * 1000,
|
||||||
|
torURL: initializer.torDirURL
|
||||||
),
|
),
|
||||||
url: "\($0.host):\($0.port)"
|
url: "\($0.host):\($0.port)"
|
||||||
)
|
)
|
||||||
|
@ -907,13 +908,14 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
|
|
||||||
// Validation of the server is first because any custom endpoint can be passed here
|
// Validation of the server is first because any custom endpoint can be passed here
|
||||||
// Extra instance of the service is created with lower timeout ofr a single call
|
// Extra instance of the service is created with lower timeout ofr a single call
|
||||||
initializer.container.register(type: LightWalletService.self, isSingleton: true) { _ in
|
initializer.container.register(type: LightWalletService.self, isSingleton: true) { [torURL = initializer.torDirURL] _ in
|
||||||
LightWalletGRPCService(
|
LightWalletGRPCService(
|
||||||
host: endpoint.host,
|
host: endpoint.host,
|
||||||
port: endpoint.port,
|
port: endpoint.port,
|
||||||
secure: endpoint.secure,
|
secure: endpoint.secure,
|
||||||
singleCallTimeout: 5000,
|
singleCallTimeout: 5000,
|
||||||
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis
|
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis,
|
||||||
|
torURL: torURL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,8 +936,8 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
// SWITCH TO NEW ENDPOINT
|
// SWITCH TO NEW ENDPOINT
|
||||||
|
|
||||||
// LightWalletService dependency update
|
// LightWalletService dependency update
|
||||||
initializer.container.register(type: LightWalletService.self, isSingleton: true) { _ in
|
initializer.container.register(type: LightWalletService.self, isSingleton: true) { [torURL = initializer.torDirURL] _ in
|
||||||
LightWalletGRPCService(endpoint: endpoint)
|
LightWalletGRPCService(endpoint: endpoint, torURL: torURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPENDENCIES
|
// DEPENDENCIES
|
||||||
|
|
|
@ -12,7 +12,7 @@ public class TorClient {
|
||||||
private let runtime: OpaquePointer
|
private let runtime: OpaquePointer
|
||||||
public var cachedFiatCurrencyResult: FiatCurrencyResult?
|
public var cachedFiatCurrencyResult: FiatCurrencyResult?
|
||||||
|
|
||||||
init(torDir: URL) async throws {
|
init(torDir: URL) throws {
|
||||||
// Ensure that the directory exists.
|
// Ensure that the directory exists.
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
if !fileManager.fileExists(atPath: torDir.path) {
|
if !fileManager.fileExists(atPath: torDir.path) {
|
||||||
|
@ -41,20 +41,19 @@ public class TorClient {
|
||||||
zcashlc_free_tor_runtime(runtime)
|
zcashlc_free_tor_runtime(runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isolatedClient() async throws -> TorClient {
|
public func isolatedClient() throws -> TorClient {
|
||||||
let isolatedPtr = zcashlc_tor_isolated_client(runtime)
|
let isolatedPtr = zcashlc_tor_isolated_client(runtime)
|
||||||
|
|
||||||
guard let isolatedPtr else {
|
guard let isolatedPtr else {
|
||||||
throw ZcashError.rustTorIsolatedClient(
|
throw ZcashError.rustTorIsolatedClient(
|
||||||
lastErrorMessage(
|
lastErrorMessage(fallback: "`TorClient.isolatedClient` failed with unknown error")
|
||||||
fallback:
|
)
|
||||||
"`TorClient.isolatedClient` failed with unknown error"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TorClient(runtimePtr: isolatedPtr)
|
return TorClient(runtimePtr: isolatedPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getExchangeRateUSD() async throws -> FiatCurrencyResult {
|
public func getExchangeRateUSD() throws -> FiatCurrencyResult {
|
||||||
let rate = zcashlc_get_exchange_rate_usd(runtime)
|
let rate = zcashlc_get_exchange_rate_usd(runtime)
|
||||||
|
|
||||||
if rate.is_sign_negative {
|
if rate.is_sign_negative {
|
||||||
|
@ -64,8 +63,10 @@ public class TorClient {
|
||||||
let newValue = FiatCurrencyResult(
|
let newValue = FiatCurrencyResult(
|
||||||
date: Date(),
|
date: Date(),
|
||||||
rate: NSDecimalNumber(
|
rate: NSDecimalNumber(
|
||||||
mantissa: rate.mantissa, exponent: rate.exponent,
|
mantissa: rate.mantissa,
|
||||||
isNegative: rate.is_sign_negative),
|
exponent: rate.exponent,
|
||||||
|
isNegative: rate.is_sign_negative
|
||||||
|
),
|
||||||
state: .success
|
state: .success
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,23 +75,19 @@ public class TorClient {
|
||||||
return newValue
|
return newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
public func connectToLightwalletd(endpoint: String) async throws
|
public func connectToLightwalletd(endpoint: String) throws -> TorLwdConn {
|
||||||
-> TorLwdConn
|
|
||||||
{
|
|
||||||
guard !endpoint.containsCStringNullBytesBeforeStringEnding() else {
|
guard !endpoint.containsCStringNullBytesBeforeStringEnding() else {
|
||||||
throw ZcashError.rustTorConnectToLightwalletd(
|
throw ZcashError.rustTorConnectToLightwalletd("endpoint string contains null bytes")
|
||||||
"endpoint string contains null bytes")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lwdConnPtr = zcashlc_tor_connect_to_lightwalletd(
|
let lwdConnPtr = zcashlc_tor_connect_to_lightwalletd(
|
||||||
runtime, [CChar](endpoint.utf8CString))
|
runtime, [CChar](endpoint.utf8CString)
|
||||||
|
)
|
||||||
|
|
||||||
guard let lwdConnPtr else {
|
guard let lwdConnPtr else {
|
||||||
throw ZcashError.rustTorConnectToLightwalletd(
|
throw ZcashError.rustTorConnectToLightwalletd(
|
||||||
lastErrorMessage(
|
lastErrorMessage(fallback: "`TorClient.connectToLightwalletd` failed with unknown error")
|
||||||
fallback:
|
)
|
||||||
"`TorClient.connectToLightwalletd` failed with unknown error"
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TorLwdConn(connPtr: lwdConnPtr)
|
return TorLwdConn(connPtr: lwdConnPtr)
|
||||||
|
@ -100,7 +97,7 @@ public class TorClient {
|
||||||
public class TorLwdConn {
|
public class TorLwdConn {
|
||||||
private let conn: OpaquePointer
|
private let conn: OpaquePointer
|
||||||
|
|
||||||
fileprivate init(connPtr: OpaquePointer) {
|
init(connPtr: OpaquePointer) {
|
||||||
conn = connPtr
|
conn = connPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,9 +108,7 @@ public class TorLwdConn {
|
||||||
/// Submits a raw transaction over lightwalletd.
|
/// Submits a raw transaction over lightwalletd.
|
||||||
/// - Parameter spendTransaction: data representing the transaction to be sent
|
/// - Parameter spendTransaction: data representing the transaction to be sent
|
||||||
/// - Throws: `serviceSubmitFailed` when GRPC call fails.
|
/// - Throws: `serviceSubmitFailed` when GRPC call fails.
|
||||||
func submit(spendTransaction: Data) async throws
|
func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
|
||||||
-> LightWalletServiceResponse
|
|
||||||
{
|
|
||||||
let success = zcashlc_tor_lwd_conn_submit_transaction(
|
let success = zcashlc_tor_lwd_conn_submit_transaction(
|
||||||
conn,
|
conn,
|
||||||
spendTransaction.bytes,
|
spendTransaction.bytes,
|
||||||
|
@ -121,18 +116,21 @@ public class TorLwdConn {
|
||||||
)
|
)
|
||||||
|
|
||||||
var response = SendResponse()
|
var response = SendResponse()
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
let err = lastErrorMessage(
|
let err = lastErrorMessage(fallback: "`TorLwdConn.submit` failed with unknown error")
|
||||||
fallback: "`TorLwdConn.submit` failed with unknown error")
|
|
||||||
if err.hasPrefix("Failed to submit transaction (")
|
if err.hasPrefix("Failed to submit transaction (") && err.contains(")") {
|
||||||
&& err.contains(")")
|
guard let startOfCode = err.firstIndex(of: "(") else {
|
||||||
{
|
throw ZcashError.rustTorLwdSubmit(err)
|
||||||
let startOfCode = err.firstIndex(of: "(")!
|
}
|
||||||
let endOfCode = err.firstIndex(of: ")")!
|
guard let endOfCode = err.firstIndex(of: ")") else {
|
||||||
let errorCode = Int32(
|
throw ZcashError.rustTorLwdSubmit(err)
|
||||||
err[err.index(startOfCode, offsetBy: 1)..<endOfCode])!
|
}
|
||||||
let errorMessage = String(
|
guard let errorCode = Int32(err[err.index(startOfCode, offsetBy: 1)..<endOfCode]) else {
|
||||||
err[err.index(endOfCode, offsetBy: 3)...])
|
throw ZcashError.rustTorLwdSubmit(err)
|
||||||
|
}
|
||||||
|
let errorMessage = String(err[err.index(endOfCode, offsetBy: 3)...])
|
||||||
|
|
||||||
response.errorCode = errorCode
|
response.errorCode = errorCode
|
||||||
response.errorMessage = errorMessage
|
response.errorMessage = errorMessage
|
||||||
|
@ -140,6 +138,7 @@ public class TorLwdConn {
|
||||||
throw ZcashError.rustTorLwdSubmit(err)
|
throw ZcashError.rustTorLwdSubmit(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,9 +147,7 @@ public class TorLwdConn {
|
||||||
/// - Throws: LightWalletServiceError
|
/// - Throws: LightWalletServiceError
|
||||||
/// - Returns: LightWalletServiceResponse
|
/// - Returns: LightWalletServiceResponse
|
||||||
/// - Throws: `serviceFetchTransactionFailed` when GRPC call fails.
|
/// - Throws: `serviceFetchTransactionFailed` when GRPC call fails.
|
||||||
func fetchTransaction(txId: Data) async throws -> (
|
func fetchTransaction(txId: Data) throws -> (tx: ZcashTransaction.Fetched?, status: TransactionStatus) {
|
||||||
tx: ZcashTransaction.Fetched?, status: TransactionStatus
|
|
||||||
) {
|
|
||||||
guard txId.count == 32 else {
|
guard txId.count == 32 else {
|
||||||
throw ZcashError.rustGetMemoInvalidTxIdLength
|
throw ZcashError.rustGetMemoInvalidTxIdLength
|
||||||
}
|
}
|
||||||
|
@ -161,10 +158,7 @@ public class TorLwdConn {
|
||||||
|
|
||||||
guard let txPtr else {
|
guard let txPtr else {
|
||||||
throw ZcashError.rustTorLwdFetchTransaction(
|
throw ZcashError.rustTorLwdFetchTransaction(
|
||||||
lastErrorMessage(
|
lastErrorMessage(fallback: "`TorLwdConn.fetchTransaction` failed with unknown error")
|
||||||
fallback:
|
|
||||||
"`TorLwdConn.fetchTransaction` failed with unknown error"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,4 +179,81 @@ public class TorLwdConn {
|
||||||
status: isNotMined ? .notInMainChain : .mined(Int(height))
|
status: isNotMined ? .notInMainChain : .mined(Int(height))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a lightwalletd server info
|
||||||
|
/// - Returns: LightWalletdInfo
|
||||||
|
func getInfo() throws -> LightWalletdInfo {
|
||||||
|
let infoPtr = zcashlc_tor_lwd_conn_get_info(conn)
|
||||||
|
|
||||||
|
guard let infoPtr else {
|
||||||
|
throw ZcashError.rustTorLwdGetInfo(
|
||||||
|
lastErrorMessage(fallback: "`TorLwdConn.getInfo` failed with unknown error")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer { zcashlc_free_boxed_slice(infoPtr) }
|
||||||
|
|
||||||
|
let slice = infoPtr.pointee
|
||||||
|
guard let rawPtr = slice.ptr else {
|
||||||
|
throw ZcashError.rustTorLwdGetInfo("`TorLwdConn.getInfo` Null pointer in FfiBoxedSlice")
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = UnsafeBufferPointer<UInt8>(start: rawPtr, count: Int(slice.len))
|
||||||
|
let data = Data(buffer: buffer)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let info = try LightdInfo(serializedBytes: data)
|
||||||
|
return info
|
||||||
|
} catch {
|
||||||
|
throw ZcashError.rustTorLwdGetInfo("`TorLwdConn.getInfo` Failed to decode protobuf LightdInfo: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a chain tip of the blockchain (latest block height)
|
||||||
|
/// - Returns: BlockHeight
|
||||||
|
func latestBlockHeight() throws -> BlockHeight {
|
||||||
|
var height: UInt32 = 0
|
||||||
|
|
||||||
|
let blockIDPtr = zcashlc_tor_lwd_conn_latest_block(conn, &height)
|
||||||
|
|
||||||
|
guard let blockIDPtr else {
|
||||||
|
throw ZcashError.rustTorLwdLatestBlockHeight(
|
||||||
|
lastErrorMessage(fallback: "`TorLwdConn.latestBlockHeight` failed with unknown error")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer { zcashlc_free_boxed_slice(blockIDPtr) }
|
||||||
|
|
||||||
|
return BlockHeight(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a tree state for a given height
|
||||||
|
/// - Parameter height: heght for what a tree state is requested
|
||||||
|
/// - Returns: TreeState
|
||||||
|
func getTreeState(height: BlockHeight) throws -> TreeState {
|
||||||
|
let treeStatePtr = zcashlc_tor_lwd_conn_get_tree_state(conn, UInt32(height))
|
||||||
|
|
||||||
|
guard let treeStatePtr else {
|
||||||
|
throw ZcashError.rustTorLwdGetTreeState(
|
||||||
|
lastErrorMessage(fallback: "`TorLwdConn.getTreeState` failed with unknown error")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer { zcashlc_free_boxed_slice(treeStatePtr) }
|
||||||
|
|
||||||
|
let slice = treeStatePtr.pointee
|
||||||
|
guard let rawPtr = slice.ptr else {
|
||||||
|
throw ZcashError.rustTorLwdGetTreeState("`TorLwdConn.getTreeState` Null pointer in FfiBoxedSlice")
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = UnsafeBufferPointer<UInt8>(start: rawPtr, count: Int(slice.len))
|
||||||
|
let data = Data(buffer: buffer)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let treeState = try TreeState(serializedBytes: data)
|
||||||
|
return treeState
|
||||||
|
} catch {
|
||||||
|
throw ZcashError.rustTorLwdGetTreeState("`TorLwdConn.getTreeState` Failed to decode protobuf TreeState: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class BlockDownloaderTests: XCTestCase {
|
||||||
try await super.setUp()
|
try await super.setUp()
|
||||||
testTempDirectory = Environment.uniqueTestTempDirectory
|
testTempDirectory = Environment.uniqueTestTempDirectory
|
||||||
|
|
||||||
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make()
|
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default, torURL: nil).make()
|
||||||
|
|
||||||
rustBackend = ZcashRustBackend.makeForTests(
|
rustBackend = ZcashRustBackend.makeForTests(
|
||||||
fsBlockDbRoot: testTempDirectory,
|
fsBlockDbRoot: testTempDirectory,
|
||||||
|
|
|
@ -97,7 +97,7 @@ class TransactionEnhancementTests: ZcashTestCase {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let service = DarksideWalletService()
|
let service = DarksideWalletService(torURL: nil)
|
||||||
darksideWalletService = service
|
darksideWalletService = service
|
||||||
|
|
||||||
let storage = FSCompactBlockRepository(
|
let storage = FSCompactBlockRepository(
|
||||||
|
|
|
@ -52,7 +52,7 @@ class BlockStreamingTest: ZcashTestCase {
|
||||||
singleCallTimeoutInMillis: 10000,
|
singleCallTimeoutInMillis: 10000,
|
||||||
streamingCallTimeoutInMillis: 10000
|
streamingCallTimeoutInMillis: 10000
|
||||||
)
|
)
|
||||||
let service = LightWalletServiceFactory(endpoint: endpoint).make()
|
let service = LightWalletServiceFactory(endpoint: endpoint, torURL: nil).make()
|
||||||
|
|
||||||
latestBlockHeight = try await service.latestBlockHeight()
|
latestBlockHeight = try await service.latestBlockHeight()
|
||||||
startHeight = latestBlockHeight - 10_000
|
startHeight = latestBlockHeight - 10_000
|
||||||
|
@ -77,7 +77,7 @@ class BlockStreamingTest: ZcashTestCase {
|
||||||
streamingCallTimeoutInMillis: timeout
|
streamingCallTimeoutInMillis: timeout
|
||||||
)
|
)
|
||||||
self.endpoint = endpoint
|
self.endpoint = endpoint
|
||||||
service = LightWalletServiceFactory(endpoint: endpoint).make()
|
service = LightWalletServiceFactory(endpoint: endpoint, torURL: nil).make()
|
||||||
storage = FSCompactBlockRepository(
|
storage = FSCompactBlockRepository(
|
||||||
fsBlockDbRoot: testTempDirectory,
|
fsBlockDbRoot: testTempDirectory,
|
||||||
metadataStore: FSMetadataStore.live(
|
metadataStore: FSMetadataStore.live(
|
||||||
|
@ -97,7 +97,7 @@ class BlockStreamingTest: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in
|
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in
|
||||||
LightWalletServiceFactory(endpoint: endpoint).make()
|
LightWalletServiceFactory(endpoint: endpoint, torURL: nil).make()
|
||||||
}
|
}
|
||||||
|
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||||
|
|
|
@ -43,7 +43,7 @@ class CompactBlockProcessorTests: ZcashTestCase {
|
||||||
network: ZcashNetworkBuilder.network(for: .testnet)
|
network: ZcashNetworkBuilder.network(for: .testnet)
|
||||||
)
|
)
|
||||||
|
|
||||||
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, torURL: nil).make()
|
||||||
let service = MockLightWalletService(
|
let service = MockLightWalletService(
|
||||||
latestBlockHeight: mockLatestHeight,
|
latestBlockHeight: mockLatestHeight,
|
||||||
service: liveService
|
service: liveService
|
||||||
|
|
|
@ -44,7 +44,7 @@ class CompactBlockReorgTests: ZcashTestCase {
|
||||||
network: ZcashNetworkBuilder.network(for: .testnet)
|
network: ZcashNetworkBuilder.network(for: .testnet)
|
||||||
)
|
)
|
||||||
|
|
||||||
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
let liveService = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, torURL: nil).make()
|
||||||
let service = MockLightWalletService(
|
let service = MockLightWalletService(
|
||||||
latestBlockHeight: mockLatestHeight,
|
latestBlockHeight: mockLatestHeight,
|
||||||
service: liveService
|
service: liveService
|
||||||
|
|
|
@ -46,7 +46,7 @@ class DownloadTests: ZcashTestCase {
|
||||||
ZcashRustBackend.makeForTests(fsBlockDbRoot: self.testTempDirectory, networkType: self.network.networkType)
|
ZcashRustBackend.makeForTests(fsBlockDbRoot: self.testTempDirectory, networkType: self.network.networkType)
|
||||||
}
|
}
|
||||||
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in
|
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in
|
||||||
LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, torURL: nil).make()
|
||||||
}
|
}
|
||||||
let storage = mockContainer.resolve(CompactBlockRepository.self)
|
let storage = mockContainer.resolve(CompactBlockRepository.self)
|
||||||
try await storage.create()
|
try await storage.create()
|
||||||
|
|
|
@ -19,7 +19,7 @@ class LightWalletServiceTests: XCTestCase {
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
super.setUp()
|
super.setUp()
|
||||||
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make()
|
service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet, torURL: nil).make()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
override func tearDownWithError() throws {
|
||||||
|
|
|
@ -14,23 +14,23 @@ import XCTest
|
||||||
class TorClientTests: ZcashTestCase {
|
class TorClientTests: ZcashTestCase {
|
||||||
let network: ZcashNetwork = ZcashNetworkBuilder.network(for: .testnet)
|
let network: ZcashNetwork = ZcashNetworkBuilder.network(for: .testnet)
|
||||||
|
|
||||||
func testLwdCanFetchAndSubmitTx() async throws {
|
func testLwdCanFetchAndSubmitTx() throws {
|
||||||
// Spin up a new Tor client.
|
// Spin up a new Tor client.
|
||||||
let client = try await TorClient(torDir: testTempDirectory)
|
let client = try TorClient(torDir: testTempDirectory)
|
||||||
|
|
||||||
// Connect to a testnet lightwalletd server.
|
// Connect to a testnet lightwalletd server.
|
||||||
let lwdConn = try await client.connectToLightwalletd(
|
let lwdConn = try client.connectToLightwalletd(
|
||||||
endpoint: LightWalletEndpointBuilder.publicTestnet.urlString)
|
endpoint: LightWalletEndpointBuilder.publicTestnet.urlString)
|
||||||
|
|
||||||
// Fetch a known testnet transaction.
|
// Fetch a known testnet transaction.
|
||||||
let txId =
|
let txId =
|
||||||
"9e309d29a99f06e6dcc7aee91dca23c0efc2cf5083cc483463ddbee19c1fadf1"
|
"9e309d29a99f06e6dcc7aee91dca23c0efc2cf5083cc483463ddbee19c1fadf1"
|
||||||
.toTxIdString().hexadecimal!
|
.toTxIdString().hexadecimal!
|
||||||
let (tx, status) = try await lwdConn.fetchTransaction(txId: txId)
|
let (tx, status) = try lwdConn.fetchTransaction(txId: txId)
|
||||||
XCTAssertEqual(status, .mined(1_234_567))
|
XCTAssertEqual(status, .mined(1_234_567))
|
||||||
|
|
||||||
// We should fail to resubmit the already-mined transaction.
|
// We should fail to resubmit the already-mined transaction.
|
||||||
let result = try await lwdConn.submit(spendTransaction: tx!.raw)
|
let result = try lwdConn.submit(spendTransaction: tx!.raw)
|
||||||
XCTAssertEqual(result.errorCode, -25)
|
XCTAssertEqual(result.errorCode, -25)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
result.errorMessage,
|
result.errorMessage,
|
||||||
|
|
|
@ -50,9 +50,9 @@ class DarksideWalletService: LightWalletService {
|
||||||
var service: LightWalletService
|
var service: LightWalletService
|
||||||
var darksideService: DarksideStreamerNIOClient
|
var darksideService: DarksideStreamerNIOClient
|
||||||
|
|
||||||
init(endpoint: LightWalletEndpoint) {
|
init(endpoint: LightWalletEndpoint, torURL: URL?) {
|
||||||
self.channel = ChannelProvider().channel(endpoint: endpoint)
|
self.channel = ChannelProvider().channel(endpoint: endpoint)
|
||||||
self.service = LightWalletServiceFactory(endpoint: endpoint).make()
|
self.service = LightWalletServiceFactory(endpoint: endpoint, torURL: torURL).make()
|
||||||
self.darksideService = DarksideStreamerNIOClient(channel: channel)
|
self.darksideService = DarksideStreamerNIOClient(channel: channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ class DarksideWalletService: LightWalletService {
|
||||||
self.service = service
|
self.service = service
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init() {
|
convenience init(torURL: URL?) {
|
||||||
self.init(endpoint: LightWalletEndpointBuilder.default)
|
self.init(endpoint: LightWalletEndpointBuilder.default, torURL: torURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
|
||||||
|
|
|
@ -93,7 +93,7 @@ class TestCoordinator {
|
||||||
self.birthday = walletBirthday
|
self.birthday = walletBirthday
|
||||||
self.network = network
|
self.network = network
|
||||||
|
|
||||||
let liveService = LightWalletServiceFactory(endpoint: endpoint).make()
|
let liveService = LightWalletServiceFactory(endpoint: endpoint, torURL: databases.torDir).make()
|
||||||
self.service = DarksideWalletService(endpoint: endpoint, service: liveService)
|
self.service = DarksideWalletService(endpoint: endpoint, service: liveService)
|
||||||
self.synchronizer = SDKSynchronizer(initializer: initializer)
|
self.synchronizer = SDKSynchronizer(initializer: initializer)
|
||||||
subscribeToState(synchronizer: self.synchronizer)
|
subscribeToState(synchronizer: self.synchronizer)
|
||||||
|
|
Loading…
Reference in New Issue