2019-11-14 06:38:54 -08:00
|
|
|
//
|
|
|
|
// SDKSynchronizer.swift
|
|
|
|
// ZcashLightClientKit
|
|
|
|
//
|
|
|
|
// Created by Francisco Gindre on 11/6/19.
|
|
|
|
// Copyright © 2019 Electric Coin Company. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2022-11-11 09:46:13 -08:00
|
|
|
import Combine
|
2019-11-14 06:38:54 -08:00
|
|
|
|
2023-03-15 04:17:43 -07:00
|
|
|
extension Notification.Name {
|
2021-06-14 16:38:05 -07:00
|
|
|
static let synchronizerConnectionStateChanged = Notification.Name("SynchronizerConnectionStateChanged")
|
2019-12-16 14:25:45 -08:00
|
|
|
}
|
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
/// Synchronizer implementation for UIKit and iOS 13+
|
2021-09-15 05:21:29 -07:00
|
|
|
// swiftlint:disable type_body_length
|
2019-11-14 06:38:54 -08:00
|
|
|
public class SDKSynchronizer: Synchronizer {
|
2021-09-15 05:21:29 -07:00
|
|
|
public enum NotificationKeys {
|
2021-06-14 16:38:05 -07:00
|
|
|
public static let currentConnectionState = "SDKSynchronizer.currentConnectionState"
|
|
|
|
public static let previousConnectionState = "SDKSynchronizer.previousConnectionState"
|
2019-12-16 14:25:45 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-15 04:17:43 -07:00
|
|
|
private let streamsUpdateQueue = DispatchQueue(label: "streamsUpdateQueue")
|
|
|
|
private let stateSubject = CurrentValueSubject<SynchronizerState, Never>(.zero)
|
|
|
|
public var stateStream: AnyPublisher<SynchronizerState, Never> { stateSubject.eraseToAnyPublisher() }
|
|
|
|
public private(set) var latestState: SynchronizerState = .zero
|
|
|
|
|
|
|
|
private let eventSubject = PassthroughSubject<SynchronizerEvent, Never>()
|
|
|
|
public var eventStream: AnyPublisher<SynchronizerEvent, Never> { eventSubject.eraseToAnyPublisher() }
|
|
|
|
|
|
|
|
private let statusUpdateLock = NSRecursiveLock()
|
2022-11-21 07:22:43 -08:00
|
|
|
private var underlyingStatus: SyncStatus
|
2023-03-15 04:17:43 -07:00
|
|
|
var status: SyncStatus {
|
2022-11-21 07:22:43 -08:00
|
|
|
get {
|
|
|
|
statusUpdateLock.lock()
|
|
|
|
defer { statusUpdateLock.unlock() }
|
|
|
|
return underlyingStatus
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
statusUpdateLock.lock()
|
2023-03-15 04:17:43 -07:00
|
|
|
let oldValue = underlyingStatus
|
2022-11-21 07:22:43 -08:00
|
|
|
underlyingStatus = newValue
|
2023-03-15 04:17:43 -07:00
|
|
|
notify(oldStatus: oldValue, newStatus: newValue)
|
2022-11-21 07:22:43 -08:00
|
|
|
statusUpdateLock.unlock()
|
2019-12-16 14:25:45 -08:00
|
|
|
}
|
|
|
|
}
|
2023-02-07 05:22:28 -08:00
|
|
|
|
2023-01-12 04:05:11 -08:00
|
|
|
let blockProcessor: CompactBlockProcessor
|
2023-02-07 05:22:28 -08:00
|
|
|
let blockProcessorEventProcessingQueue = DispatchQueue(label: "blockProcessorEventProcessingQueue")
|
|
|
|
|
2019-11-14 06:38:54 -08:00
|
|
|
public private(set) var initializer: Initializer
|
2023-03-10 03:58:28 -08:00
|
|
|
// Valid value is stored here after `prepare` is called.
|
|
|
|
public private(set) var latestScannedHeight: BlockHeight = .zero
|
2021-06-15 14:53:21 -07:00
|
|
|
public private(set) var connectionState: ConnectionState
|
2021-07-28 09:59:10 -07:00
|
|
|
public private(set) var network: ZcashNetwork
|
2019-12-06 04:38:47 -08:00
|
|
|
private var transactionManager: OutboundTransactionManager
|
|
|
|
private var transactionRepository: TransactionRepository
|
2020-12-11 12:15:29 -08:00
|
|
|
private var utxoRepository: UnspentTransactionOutputRepository
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2022-12-21 04:30:05 -08:00
|
|
|
private var syncStartDate: Date?
|
2023-02-07 05:22:28 -08:00
|
|
|
|
|
|
|
private var longLivingCancelables: [AnyCancellable] = []
|
2022-12-21 04:30:05 -08:00
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
/// Creates an SDKSynchronizer instance
|
|
|
|
/// - Parameter initializer: a wallet Initializer object
|
2023-03-10 03:58:28 -08:00
|
|
|
public convenience init(initializer: Initializer) {
|
|
|
|
self.init(
|
2021-09-17 06:49:58 -07:00
|
|
|
status: .unprepared,
|
|
|
|
initializer: initializer,
|
2023-03-10 03:58:28 -08:00
|
|
|
transactionManager: OutboundTransactionManagerBuilder.build(initializer: initializer),
|
2021-09-17 06:49:58 -07:00
|
|
|
transactionRepository: initializer.transactionRepository,
|
2023-03-10 03:58:28 -08:00
|
|
|
utxoRepository: UTXORepositoryBuilder.build(initializer: initializer),
|
|
|
|
blockProcessor: CompactBlockProcessor(
|
|
|
|
initializer: initializer,
|
|
|
|
walletBirthdayProvider: { initializer.walletBirthday }
|
|
|
|
)
|
2021-09-17 06:49:58 -07:00
|
|
|
)
|
2021-09-15 05:21:29 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2021-09-15 05:21:29 -07:00
|
|
|
init(
|
|
|
|
status: SyncStatus,
|
|
|
|
initializer: Initializer,
|
|
|
|
transactionManager: OutboundTransactionManager,
|
|
|
|
transactionRepository: TransactionRepository,
|
|
|
|
utxoRepository: UnspentTransactionOutputRepository,
|
|
|
|
blockProcessor: CompactBlockProcessor
|
2023-03-10 03:58:28 -08:00
|
|
|
) {
|
2021-06-15 14:53:21 -07:00
|
|
|
self.connectionState = .idle
|
2022-11-21 07:22:43 -08:00
|
|
|
self.underlyingStatus = status
|
2020-06-03 16:18:57 -07:00
|
|
|
self.initializer = initializer
|
|
|
|
self.transactionManager = transactionManager
|
|
|
|
self.transactionRepository = transactionRepository
|
2020-12-11 12:15:29 -08:00
|
|
|
self.utxoRepository = utxoRepository
|
2021-03-08 10:47:36 -08:00
|
|
|
self.blockProcessor = blockProcessor
|
2021-07-26 16:22:30 -07:00
|
|
|
self.network = initializer.network
|
2022-11-11 09:46:13 -08:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
subscribeToProcessorNotifications(blockProcessor)
|
|
|
|
|
|
|
|
Task(priority: .high) { [weak self] in await self?.subscribeToProcessorEvents(blockProcessor) }
|
2020-06-03 16:18:57 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-11-14 06:38:54 -08:00
|
|
|
deinit {
|
|
|
|
NotificationCenter.default.removeObserver(self)
|
2022-10-27 03:51:38 -07:00
|
|
|
Task { [blockProcessor] in
|
|
|
|
await blockProcessor.stop()
|
|
|
|
}
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-10 03:58:28 -08:00
|
|
|
public func prepare(
|
|
|
|
with seed: [UInt8]?,
|
|
|
|
viewingKeys: [UnifiedFullViewingKey],
|
|
|
|
walletBirthday: BlockHeight
|
|
|
|
) throws -> Initializer.InitializationResult {
|
|
|
|
guard status == .unprepared else { return .success }
|
|
|
|
|
|
|
|
try utxoRepository.initialise()
|
|
|
|
|
|
|
|
if case .seedRequired = try self.initializer.initialize(with: seed, viewingKeys: viewingKeys, walletBirthday: walletBirthday) {
|
2022-08-24 08:38:42 -07:00
|
|
|
return .seedRequired
|
|
|
|
}
|
|
|
|
|
2023-03-10 03:58:28 -08:00
|
|
|
latestScannedHeight = (try? transactionRepository.lastScannedHeight()) ?? initializer.walletBirthday
|
|
|
|
|
2023-03-15 04:17:43 -07:00
|
|
|
self.status = .disconnected
|
|
|
|
|
2022-08-24 08:38:42 -07:00
|
|
|
return .success
|
2021-05-05 12:08:57 -07:00
|
|
|
}
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
/// Starts the synchronizer
|
|
|
|
/// - Throws: CompactBlockProcessorError when failures occur
|
2020-03-13 17:00:01 -07:00
|
|
|
public func start(retry: Bool = false) throws {
|
2021-05-05 12:08:57 -07:00
|
|
|
switch status {
|
|
|
|
case .unprepared:
|
|
|
|
throw SynchronizerError.notPrepared
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2022-12-01 08:57:13 -08:00
|
|
|
case .syncing, .enhancing, .fetching:
|
2023-01-02 01:20:08 -08:00
|
|
|
LoggerProxy.warn("warning: Synchronizer started when already running. Next sync process will be started when the current one stops.")
|
|
|
|
Task {
|
|
|
|
/// This may look strange but `CompactBlockProcessor` has mechanisms which can handle this situation. So we are fine with calling
|
|
|
|
/// it's start here.
|
|
|
|
await blockProcessor.start(retry: retry)
|
|
|
|
}
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2021-09-15 05:21:29 -07:00
|
|
|
case .stopped, .synced, .disconnected, .error:
|
2022-10-27 03:51:38 -07:00
|
|
|
Task {
|
2023-03-15 04:17:43 -07:00
|
|
|
status = .syncing(.nullProgress)
|
2022-12-21 04:30:05 -08:00
|
|
|
syncStartDate = Date()
|
2022-10-27 03:51:38 -07:00
|
|
|
await blockProcessor.start(retry: retry)
|
2021-05-05 12:08:57 -07:00
|
|
|
}
|
2020-08-10 15:19:59 -07:00
|
|
|
}
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2022-06-22 12:45:37 -07:00
|
|
|
|
|
|
|
/// Stops the synchronizer
|
2020-08-10 15:19:59 -07:00
|
|
|
public func stop() {
|
2021-05-05 12:08:57 -07:00
|
|
|
guard status != .stopped, status != .disconnected else {
|
|
|
|
LoggerProxy.info("attempted to stop when status was: \(status)")
|
|
|
|
return
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-10-27 03:51:38 -07:00
|
|
|
Task(priority: .high) {
|
|
|
|
await blockProcessor.stop()
|
|
|
|
}
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-11-14 06:38:54 -08:00
|
|
|
private func subscribeToProcessorNotifications(_ processor: CompactBlockProcessor) {
|
|
|
|
let center = NotificationCenter.default
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2021-09-15 05:21:29 -07:00
|
|
|
center.addObserver(
|
|
|
|
self,
|
|
|
|
selector: #selector(connectivityStateChanged(_:)),
|
2023-02-07 05:22:28 -08:00
|
|
|
name: Notification.Name.synchronizerConnectionStateChanged,
|
2021-09-15 05:21:29 -07:00
|
|
|
object: nil
|
|
|
|
)
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
// MARK: Connectivity State
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2021-06-14 16:38:05 -07:00
|
|
|
@objc func connectivityStateChanged(_ notification: Notification) {
|
2021-09-17 06:49:58 -07:00
|
|
|
guard
|
|
|
|
let userInfo = notification.userInfo,
|
2023-02-07 05:22:28 -08:00
|
|
|
let current = userInfo[NotificationKeys.currentConnectionState] as? ConnectionState
|
2021-09-17 06:49:58 -07:00
|
|
|
else {
|
|
|
|
LoggerProxy.error(
|
2023-02-07 05:22:28 -08:00
|
|
|
"Found \(notification.name) but lacks dictionary information." +
|
2021-09-17 06:49:58 -07:00
|
|
|
"This is probably a programming error"
|
|
|
|
)
|
2021-06-14 16:38:05 -07:00
|
|
|
return
|
|
|
|
}
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2023-02-03 02:23:35 -08:00
|
|
|
connectionState = current
|
2023-03-15 04:17:43 -07:00
|
|
|
streamsUpdateQueue.async { [weak self] in
|
|
|
|
self?.eventSubject.send(.connectionStateChanged)
|
|
|
|
}
|
2021-06-14 16:38:05 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
// MARK: Handle CompactBlockProcessor.Flow
|
|
|
|
|
|
|
|
private func subscribeToProcessorEvents(_ processor: CompactBlockProcessor) async {
|
|
|
|
let stream = await processor.eventStream
|
|
|
|
|
|
|
|
stream
|
|
|
|
.receive(on: blockProcessorEventProcessingQueue)
|
|
|
|
.sink { [weak self] event in
|
|
|
|
switch event {
|
|
|
|
case let .failed(error):
|
|
|
|
self?.failed(error: error)
|
|
|
|
|
|
|
|
case let .finished(height, foundBlocks):
|
|
|
|
self?.finished(lastScannedHeight: height, foundBlocks: foundBlocks)
|
|
|
|
|
|
|
|
case let .foundTransactions(transactions, range):
|
|
|
|
self?.foundTransactions(transactions: transactions, in: range)
|
|
|
|
|
|
|
|
case let .handledReorg(reorgHeight, rewindHeight):
|
|
|
|
self?.handledReorg(reorgHeight: reorgHeight, rewindHeight: rewindHeight)
|
|
|
|
|
|
|
|
case let .progressUpdated(progress):
|
|
|
|
self?.progressUpdated(progress: progress)
|
|
|
|
|
|
|
|
case let .storedUTXOs(utxos):
|
|
|
|
self?.storedUTXOs(utxos: utxos)
|
|
|
|
|
|
|
|
case .startedEnhancing:
|
2023-03-15 04:17:43 -07:00
|
|
|
self?.status = .enhancing(.zero)
|
2023-02-07 05:22:28 -08:00
|
|
|
|
|
|
|
case .startedFetching:
|
2023-03-15 04:17:43 -07:00
|
|
|
self?.status = .fetching
|
2023-02-07 05:22:28 -08:00
|
|
|
|
|
|
|
case .startedSyncing:
|
2023-03-15 04:17:43 -07:00
|
|
|
self?.status = .syncing(.nullProgress)
|
2023-02-07 05:22:28 -08:00
|
|
|
|
|
|
|
case .stopped:
|
2023-03-15 04:17:43 -07:00
|
|
|
self?.status = .stopped
|
2023-02-07 05:22:28 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.store(in: &longLivingCancelables)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func failed(error: CompactBlockProcessorError) {
|
2023-03-15 04:17:43 -07:00
|
|
|
status = .error(self.mapError(error))
|
2023-02-07 05:22:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private func finished(lastScannedHeight: BlockHeight, foundBlocks: Bool) {
|
|
|
|
// FIX: Pending transaction updates fail if done from another thread. Improvement needed: explicitly define queues for sql repositories see: https://github.com/zcash/ZcashLightClientKit/issues/450
|
|
|
|
self.latestScannedHeight = lastScannedHeight
|
|
|
|
self.refreshPendingTransactions()
|
|
|
|
self.status = .synced
|
|
|
|
|
|
|
|
if let syncStartDate {
|
|
|
|
SDKMetrics.shared.pushSyncReport(
|
|
|
|
start: syncStartDate,
|
|
|
|
end: Date()
|
|
|
|
)
|
2020-10-20 07:06:31 -07:00
|
|
|
}
|
2023-02-07 05:22:28 -08:00
|
|
|
}
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
private func foundTransactions(transactions: [ZcashTransaction.Overview], in range: CompactBlockRange) {
|
2023-03-15 04:17:43 -07:00
|
|
|
streamsUpdateQueue.async { [weak self] in
|
|
|
|
self?.eventSubject.send(.foundTransactions(transactions, range))
|
|
|
|
}
|
2020-10-20 07:06:31 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
private func handledReorg(reorgHeight: BlockHeight, rewindHeight: BlockHeight) {
|
|
|
|
LoggerProxy.debug("handling reorg at: \(reorgHeight) with rewind height: \(rewindHeight)")
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2019-12-17 09:12:07 -08:00
|
|
|
do {
|
2020-01-29 21:34:03 -08:00
|
|
|
try transactionManager.handleReorg(at: rewindHeight)
|
2019-12-17 09:12:07 -08:00
|
|
|
} catch {
|
2020-03-09 13:25:27 -07:00
|
|
|
LoggerProxy.debug("error handling reorg: \(error)")
|
2019-12-17 09:12:07 -08:00
|
|
|
}
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
private func progressUpdated(progress: CompactBlockProgress) {
|
2023-03-15 04:17:43 -07:00
|
|
|
switch progress {
|
|
|
|
case let .syncing(progress):
|
|
|
|
status = .syncing(progress)
|
|
|
|
case let .enhance(progress):
|
|
|
|
status = .enhancing(progress)
|
|
|
|
case .fetch:
|
|
|
|
status = .fetching
|
|
|
|
}
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-02-07 05:22:28 -08:00
|
|
|
private func storedUTXOs(utxos: (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])) {
|
2023-03-15 04:17:43 -07:00
|
|
|
streamsUpdateQueue.async { [weak self] in
|
|
|
|
self?.eventSubject.send(.storedUTXOs(utxos.inserted, utxos.skipped))
|
|
|
|
}
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-12-06 04:38:47 -08:00
|
|
|
// MARK: Synchronizer methods
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-06-22 12:45:37 -07:00
|
|
|
public func sendToAddress(
|
2022-10-02 19:11:17 -07:00
|
|
|
spendingKey: UnifiedSpendingKey,
|
2022-06-22 12:45:37 -07:00
|
|
|
zatoshi: Zatoshi,
|
2022-08-20 15:10:22 -07:00
|
|
|
toAddress: Recipient,
|
2022-10-24 05:38:47 -07:00
|
|
|
memo: Memo?
|
2022-09-13 03:19:56 -07:00
|
|
|
) async throws -> PendingTransactionEntity {
|
|
|
|
do {
|
2022-11-28 22:38:28 -08:00
|
|
|
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
|
|
|
|
spendURL: initializer.spendParamsURL,
|
2023-02-16 08:27:49 -08:00
|
|
|
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
|
|
|
|
outputURL: initializer.outputParamsURL,
|
|
|
|
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL
|
2022-11-28 22:38:28 -08:00
|
|
|
)
|
2022-09-13 03:19:56 -07:00
|
|
|
} catch {
|
|
|
|
throw SynchronizerError.parameterMissing(underlyingError: error)
|
2020-10-08 10:00:27 -07:00
|
|
|
}
|
2022-10-24 05:38:47 -07:00
|
|
|
|
2023-01-18 08:09:04 -08:00
|
|
|
if case Recipient.transparent = toAddress, memo != nil {
|
2022-10-24 05:38:47 -07:00
|
|
|
throw SynchronizerError.generalError(message: "Memos can't be sent to transparent addresses.")
|
|
|
|
}
|
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
return try await createToAddress(
|
2022-06-22 12:45:37 -07:00
|
|
|
spendingKey: spendingKey,
|
2022-09-13 03:19:56 -07:00
|
|
|
zatoshi: zatoshi,
|
2022-09-30 06:45:51 -07:00
|
|
|
recipient: toAddress,
|
2022-10-02 19:11:17 -07:00
|
|
|
memo: memo
|
2022-06-22 12:45:37 -07:00
|
|
|
)
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2021-09-15 05:21:29 -07:00
|
|
|
public func shieldFunds(
|
2022-10-02 19:11:17 -07:00
|
|
|
spendingKey: UnifiedSpendingKey,
|
2023-02-02 08:58:12 -08:00
|
|
|
memo: Memo,
|
|
|
|
shieldingThreshold: Zatoshi
|
2022-09-21 05:30:06 -07:00
|
|
|
) async throws -> PendingTransactionEntity {
|
2020-12-23 15:01:09 -08:00
|
|
|
// let's see if there are funds to shield
|
2022-10-02 19:11:17 -07:00
|
|
|
let accountIndex = Int(spendingKey.account)
|
2020-12-23 15:01:09 -08:00
|
|
|
do {
|
2022-10-31 05:34:38 -07:00
|
|
|
let tBalance = try await self.getTransparentBalance(accountIndex: accountIndex)
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
|
2021-07-26 16:22:30 -07:00
|
|
|
guard tBalance.verified >= self.network.constants.defaultFee(for: self.latestScannedHeight) else {
|
2022-09-21 05:30:06 -07:00
|
|
|
throw ShieldFundsError.insuficientTransparentFunds
|
2020-12-23 15:01:09 -08:00
|
|
|
}
|
2022-08-20 15:10:22 -07:00
|
|
|
|
2023-01-18 08:09:04 -08:00
|
|
|
let shieldingSpend = try transactionManager.initSpend(
|
|
|
|
zatoshi: tBalance.verified,
|
|
|
|
recipient: .internalAccount(spendingKey.account),
|
|
|
|
memo: try memo.asMemoBytes(),
|
|
|
|
from: accountIndex
|
|
|
|
)
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-18 08:09:04 -08:00
|
|
|
// TODO: [#487] Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
|
2022-09-21 05:30:06 -07:00
|
|
|
let transaction = try await transactionManager.encodeShieldingTransaction(
|
2022-10-02 19:11:17 -07:00
|
|
|
spendingKey: spendingKey,
|
2023-02-02 08:58:12 -08:00
|
|
|
shieldingThreshold: shieldingThreshold,
|
2022-09-21 05:30:06 -07:00
|
|
|
pendingTransaction: shieldingSpend
|
|
|
|
)
|
2022-09-12 10:00:40 -07:00
|
|
|
|
2022-09-21 05:30:06 -07:00
|
|
|
return try await transactionManager.submit(pendingTransaction: transaction)
|
2020-12-23 15:01:09 -08:00
|
|
|
} catch {
|
2022-09-21 05:30:06 -07:00
|
|
|
throw error
|
2020-12-23 15:01:09 -08:00
|
|
|
}
|
|
|
|
}
|
2021-09-17 06:49:58 -07:00
|
|
|
|
|
|
|
func createToAddress(
|
2022-10-02 19:11:17 -07:00
|
|
|
spendingKey: UnifiedSpendingKey,
|
2022-06-22 12:45:37 -07:00
|
|
|
zatoshi: Zatoshi,
|
2022-09-30 06:45:51 -07:00
|
|
|
recipient: Recipient,
|
2022-10-24 05:38:47 -07:00
|
|
|
memo: Memo?
|
2022-09-13 03:19:56 -07:00
|
|
|
) async throws -> PendingTransactionEntity {
|
2019-12-06 04:38:47 -08:00
|
|
|
do {
|
2021-09-17 06:49:58 -07:00
|
|
|
let spend = try transactionManager.initSpend(
|
2022-06-22 12:45:37 -07:00
|
|
|
zatoshi: zatoshi,
|
2022-10-18 09:17:07 -07:00
|
|
|
recipient: .address(recipient),
|
2022-10-24 05:38:47 -07:00
|
|
|
memo: memo?.asMemoBytes(),
|
2022-10-02 19:11:17 -07:00
|
|
|
from: Int(spendingKey.account)
|
2021-09-17 06:49:58 -07:00
|
|
|
)
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-09-13 03:19:56 -07:00
|
|
|
let transaction = try await transactionManager.encode(
|
|
|
|
spendingKey: spendingKey,
|
|
|
|
pendingTransaction: spend
|
|
|
|
)
|
|
|
|
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
|
|
|
|
return submittedTx
|
2019-12-06 04:38:47 -08:00
|
|
|
} catch {
|
2022-09-13 03:19:56 -07:00
|
|
|
throw error
|
2019-12-06 04:38:47 -08:00
|
|
|
}
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-12-06 04:38:47 -08:00
|
|
|
public func cancelSpend(transaction: PendingTransactionEntity) -> Bool {
|
|
|
|
transactionManager.cancel(pendingTransaction: transaction)
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func allReceivedTransactions() throws -> [ZcashTransaction.Received] {
|
2022-12-22 00:46:54 -08:00
|
|
|
try transactionRepository.findReceived(offset: 0, limit: Int.max)
|
2019-12-06 04:38:47 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2020-01-29 21:34:03 -08:00
|
|
|
public func allPendingTransactions() throws -> [PendingTransactionEntity] {
|
|
|
|
try transactionManager.allPendingTransactions() ?? [PendingTransactionEntity]()
|
2019-12-06 04:38:47 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func allClearedTransactions() throws -> [ZcashTransaction.Overview] {
|
2022-12-22 01:01:12 -08:00
|
|
|
return try transactionRepository.find(offset: 0, limit: Int.max, kind: .all)
|
2019-12-06 04:38:47 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func allSentTransactions() throws -> [ZcashTransaction.Sent] {
|
2022-12-21 03:51:58 -08:00
|
|
|
return try transactionRepository.findSent(offset: 0, limit: Int.max)
|
2019-12-16 14:25:45 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func allConfirmedTransactions(from transaction: ZcashTransaction.Overview, limit: Int) throws -> [ZcashTransaction.Overview] {
|
2022-12-22 01:01:12 -08:00
|
|
|
return try transactionRepository.find(from: transaction, limit: limit, kind: .all)
|
2020-10-19 17:01:46 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-12-16 14:25:45 -08:00
|
|
|
public func paginatedTransactions(of kind: TransactionKind = .all) -> PaginatedTransactionRepository {
|
|
|
|
PagedTransactionRepositoryBuilder.build(initializer: initializer, kind: .all)
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func getMemos(for transaction: ZcashTransaction.Overview) throws -> [Memo] {
|
2023-01-05 00:43:33 -08:00
|
|
|
return try transactionRepository.findMemos(for: transaction)
|
|
|
|
}
|
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func getMemos(for receivedTransaction: ZcashTransaction.Received) throws -> [Memo] {
|
2023-01-05 00:43:33 -08:00
|
|
|
return try transactionRepository.findMemos(for: receivedTransaction)
|
|
|
|
}
|
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public func getMemos(for sentTransaction: ZcashTransaction.Sent) throws -> [Memo] {
|
2023-01-05 00:43:33 -08:00
|
|
|
return try transactionRepository.findMemos(for: sentTransaction)
|
|
|
|
}
|
|
|
|
|
2023-02-07 12:03:02 -08:00
|
|
|
public func getRecipients(for transaction: ZcashTransaction.Overview) -> [TransactionRecipient] {
|
|
|
|
return transactionRepository.getRecipients(for: transaction.id)
|
|
|
|
}
|
|
|
|
|
|
|
|
public func getRecipients(for transaction: ZcashTransaction.Sent) -> [TransactionRecipient] {
|
|
|
|
return transactionRepository.getRecipients(for: transaction.id)
|
|
|
|
}
|
|
|
|
|
2020-10-06 16:35:17 -07:00
|
|
|
public func latestHeight(result: @escaping (Result<BlockHeight, Error>) -> Void) {
|
2022-08-29 12:31:01 -07:00
|
|
|
Task {
|
|
|
|
do {
|
2023-01-31 02:11:00 -08:00
|
|
|
let latestBlockHeight = try await blockProcessor.blockDownloaderService.latestBlockHeightAsync()
|
2022-08-29 12:31:01 -07:00
|
|
|
result(.success(latestBlockHeight))
|
|
|
|
} catch {
|
|
|
|
result(.failure(error))
|
|
|
|
}
|
|
|
|
}
|
2020-10-06 16:35:17 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-10-27 03:51:38 -07:00
|
|
|
public func latestHeight() async throws -> BlockHeight {
|
2023-01-31 02:11:00 -08:00
|
|
|
try await blockProcessor.blockDownloaderService.latestBlockHeightAsync()
|
2020-10-06 16:35:17 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-10-03 16:05:11 -07:00
|
|
|
public func latestUTXOs(address: String) async throws -> [UnspentTransactionOutputEntity] {
|
2020-12-11 12:15:29 -08:00
|
|
|
guard initializer.isValidTransparentAddress(address) else {
|
2022-10-03 16:05:11 -07:00
|
|
|
throw SynchronizerError.generalError(message: "invalid t-address")
|
2020-12-11 12:15:29 -08:00
|
|
|
}
|
2023-01-18 08:09:04 -08:00
|
|
|
|
2022-10-03 16:05:11 -07:00
|
|
|
let stream = initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.saplingActivationHeight)
|
2023-01-18 08:09:04 -08:00
|
|
|
|
2022-10-03 16:05:11 -07:00
|
|
|
do {
|
2023-02-02 08:58:12 -08:00
|
|
|
// swiftlint:disable:next array_constructor
|
2022-10-03 16:05:11 -07:00
|
|
|
var utxos: [UnspentTransactionOutputEntity] = []
|
|
|
|
for try await transactionEntity in stream {
|
|
|
|
utxos.append(transactionEntity)
|
2020-12-11 12:15:29 -08:00
|
|
|
}
|
2022-10-03 16:05:11 -07:00
|
|
|
try self.utxoRepository.clearAll(address: address)
|
|
|
|
try self.utxoRepository.store(utxos: utxos)
|
|
|
|
return utxos
|
|
|
|
} catch {
|
|
|
|
throw SynchronizerError.generalError(message: "\(error)")
|
2021-09-17 06:49:58 -07:00
|
|
|
}
|
2020-12-11 12:15:29 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-10-02 19:11:17 -07:00
|
|
|
public func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs {
|
2022-09-01 05:58:41 -07:00
|
|
|
try await blockProcessor.refreshUTXOs(tAddress: address, startHeight: height)
|
2021-03-08 10:47:36 -08:00
|
|
|
}
|
2022-06-22 12:45:37 -07:00
|
|
|
@available(*, deprecated, message: "This function will be removed soon, use the one returning a `Zatoshi` value instead")
|
2021-03-08 10:47:36 -08:00
|
|
|
public func getShieldedBalance(accountIndex: Int = 0) -> Int64 {
|
2022-06-22 12:45:37 -07:00
|
|
|
initializer.getBalance(account: accountIndex).amount
|
|
|
|
}
|
|
|
|
|
|
|
|
public func getShieldedBalance(accountIndex: Int = 0) -> Zatoshi {
|
2021-03-08 10:47:36 -08:00
|
|
|
initializer.getBalance(account: accountIndex)
|
|
|
|
}
|
2022-06-22 12:45:37 -07:00
|
|
|
|
|
|
|
@available(*, deprecated, message: "This function will be removed soon, use the one returning a `Zatoshi` value instead")
|
2021-03-08 10:47:36 -08:00
|
|
|
public func getShieldedVerifiedBalance(accountIndex: Int = 0) -> Int64 {
|
2022-06-22 12:45:37 -07:00
|
|
|
initializer.getVerifiedBalance(account: accountIndex).amount
|
|
|
|
}
|
|
|
|
|
|
|
|
public func getShieldedVerifiedBalance(accountIndex: Int = 0) -> Zatoshi {
|
2021-03-08 10:47:36 -08:00
|
|
|
initializer.getVerifiedBalance(account: accountIndex)
|
|
|
|
}
|
2022-06-22 12:45:37 -07:00
|
|
|
|
2022-10-31 05:34:38 -07:00
|
|
|
public func getSaplingAddress(accountIndex: Int) async -> SaplingAddress? {
|
|
|
|
await blockProcessor.getSaplingAddress(accountIndex: accountIndex)
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-10-27 03:51:38 -07:00
|
|
|
public func getUnifiedAddress(accountIndex: Int) async -> UnifiedAddress? {
|
2022-10-31 05:34:38 -07:00
|
|
|
await blockProcessor.getUnifiedAddress(accountIndex: accountIndex)
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2022-10-27 03:51:38 -07:00
|
|
|
public func getTransparentAddress(accountIndex: Int) async -> TransparentAddress? {
|
|
|
|
await blockProcessor.getTransparentAddress(accountIndex: accountIndex)
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|
2022-10-02 19:11:17 -07:00
|
|
|
|
|
|
|
/// Returns the last stored transparent balance
|
2022-10-27 03:51:38 -07:00
|
|
|
public func getTransparentBalance(accountIndex: Int) async throws -> WalletBalance {
|
2022-10-31 05:57:10 -07:00
|
|
|
try await blockProcessor.getTransparentBalance(accountIndex: accountIndex)
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
// MARK: Rewind
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
public func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error> {
|
|
|
|
let subject = PassthroughSubject<Void, Error>()
|
|
|
|
Task {
|
|
|
|
let height: BlockHeight?
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
switch policy {
|
|
|
|
case .quick:
|
|
|
|
height = nil
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
case .birthday:
|
|
|
|
let birthday = await self.blockProcessor.config.walletBirthday
|
|
|
|
height = birthday
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
case .height(let rewindHeight):
|
|
|
|
height = rewindHeight
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
case .transaction(let transaction):
|
|
|
|
guard let txHeight = transaction.anchor(network: self.network) else {
|
|
|
|
throw SynchronizerError.rewindErrorUnknownArchorHeight
|
|
|
|
}
|
|
|
|
height = txHeight
|
2021-03-26 15:56:51 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
let context = AfterSyncHooksManager.RewindContext(
|
|
|
|
height: height,
|
|
|
|
completion: { [weak self] result in
|
|
|
|
switch result {
|
|
|
|
case let .success(rewindHeight):
|
|
|
|
do {
|
|
|
|
try self?.transactionManager.handleReorg(at: rewindHeight)
|
|
|
|
subject.send(completion: .finished)
|
|
|
|
} catch {
|
|
|
|
subject.send(completion: .failure(SynchronizerError.rewindError(underlyingError: error)))
|
|
|
|
}
|
|
|
|
|
|
|
|
case let .failure(error):
|
|
|
|
subject.send(completion: .failure(error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
await blockProcessor.rewind(context: context)
|
2021-03-26 15:56:51 -07:00
|
|
|
}
|
2023-03-02 04:19:25 -08:00
|
|
|
return subject.eraseToAnyPublisher()
|
2021-03-26 15:56:51 -07:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
// MARK: Wipe
|
|
|
|
|
2023-02-20 01:53:04 -08:00
|
|
|
public func wipe() -> AnyPublisher<Void, Error> {
|
2023-03-02 04:19:25 -08:00
|
|
|
let subject = PassthroughSubject<Void, Error>()
|
2023-02-20 01:53:04 -08:00
|
|
|
Task(priority: .high) {
|
|
|
|
let context = AfterSyncHooksManager.WipeContext(
|
|
|
|
pendingDbURL: initializer.pendingDbURL,
|
|
|
|
prewipe: { [weak self] in
|
|
|
|
self?.transactionManager.closeDBConnection()
|
|
|
|
self?.transactionRepository.closeDBConnection()
|
|
|
|
},
|
|
|
|
completion: { [weak self] possibleError in
|
|
|
|
self?.status = .unprepared
|
|
|
|
if let error = possibleError {
|
2023-03-02 04:19:25 -08:00
|
|
|
subject.send(completion: .failure(error))
|
2023-02-20 01:53:04 -08:00
|
|
|
} else {
|
2023-03-02 04:19:25 -08:00
|
|
|
subject.send(completion: .finished)
|
2023-02-20 01:53:04 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2022-12-19 06:15:23 -08:00
|
|
|
|
2023-02-20 01:53:04 -08:00
|
|
|
await blockProcessor.wipe(context: context)
|
|
|
|
}
|
2022-12-19 06:15:23 -08:00
|
|
|
|
2023-03-02 04:19:25 -08:00
|
|
|
return subject.eraseToAnyPublisher()
|
2022-12-19 06:15:23 -08:00
|
|
|
}
|
|
|
|
|
2019-12-16 14:25:45 -08:00
|
|
|
// MARK: notify state
|
2022-11-11 09:46:13 -08:00
|
|
|
|
2023-03-15 04:17:43 -07:00
|
|
|
private func snapshotState(status: SyncStatus) async -> SynchronizerState {
|
2023-01-18 08:09:04 -08:00
|
|
|
SynchronizerState(
|
2022-11-11 09:46:13 -08:00
|
|
|
shieldedBalance: WalletBalance(
|
|
|
|
verified: initializer.getVerifiedBalance(),
|
|
|
|
total: initializer.getBalance()
|
|
|
|
),
|
|
|
|
transparentBalance: (try? await blockProcessor.getTransparentBalance(accountIndex: 0)) ?? .zero,
|
|
|
|
syncStatus: status,
|
|
|
|
latestScannedHeight: self.latestScannedHeight
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-03-15 04:17:43 -07:00
|
|
|
private func notify(oldStatus: SyncStatus, newStatus: SyncStatus) {
|
|
|
|
guard oldStatus != newStatus else { return }
|
|
|
|
|
|
|
|
// When the wipe happens status is switched to `unprepared`. And we expect that everything is deleted. All the databases including data DB.
|
|
|
|
// When new snapshot is created balance is checked. And when balance is checked and data DB doesn't exist then rust initialise new database.
|
|
|
|
// So it's necessary to not create new snapshot after status is switched to `unprepared` otherwise data DB exists after wipe
|
|
|
|
if newStatus == .unprepared {
|
|
|
|
latestState = SynchronizerState.zero
|
|
|
|
updateStateStream(with: latestState)
|
|
|
|
} else {
|
|
|
|
let didStatusChange = areTwoStatusesDifferent(firstStatus: oldStatus, secondStatus: newStatus)
|
|
|
|
|
|
|
|
if didStatusChange {
|
|
|
|
Task {
|
|
|
|
latestState = await snapshotState(status: newStatus)
|
|
|
|
updateStateStream(with: latestState)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
latestState = SynchronizerState(
|
|
|
|
shieldedBalance: latestState.shieldedBalance,
|
|
|
|
transparentBalance: latestState.transparentBalance,
|
|
|
|
syncStatus: newStatus,
|
|
|
|
latestScannedHeight: latestState.latestScannedHeight
|
2022-10-27 03:51:38 -07:00
|
|
|
)
|
2023-03-15 04:17:43 -07:00
|
|
|
updateStateStream(with: latestState)
|
2022-10-27 03:51:38 -07:00
|
|
|
}
|
2019-12-16 14:25:45 -08:00
|
|
|
}
|
|
|
|
}
|
2023-03-15 04:17:43 -07:00
|
|
|
|
|
|
|
private func areTwoStatusesDifferent(firstStatus: SyncStatus, secondStatus: SyncStatus) -> Bool {
|
|
|
|
switch (firstStatus, secondStatus) {
|
|
|
|
case (.unprepared, .unprepared): return false
|
|
|
|
case (.syncing, .syncing): return false
|
|
|
|
case (.enhancing, .enhancing): return false
|
|
|
|
case (.fetching, .fetching): return false
|
|
|
|
case (.synced, .synced): return false
|
|
|
|
case (.stopped, .stopped): return false
|
|
|
|
case (.disconnected, .disconnected): return false
|
|
|
|
case (.error, .error): return false
|
|
|
|
default: return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func updateStateStream(with newState: SynchronizerState) {
|
|
|
|
streamsUpdateQueue.async { [weak self] in
|
|
|
|
self?.stateSubject.send(newState)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-16 14:25:45 -08:00
|
|
|
// MARK: book keeping
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-12-17 13:16:26 -08:00
|
|
|
private func updateMinedTransactions() throws {
|
2021-09-17 06:49:58 -07:00
|
|
|
try transactionManager.allPendingTransactions()?
|
|
|
|
.filter { $0.isSubmitSuccess && !$0.isMined }
|
|
|
|
.forEach { pendingTx in
|
2022-12-15 03:27:59 -08:00
|
|
|
guard let rawID = pendingTx.rawTransactionId else { return }
|
|
|
|
let transaction = try transactionRepository.find(rawID: rawID)
|
2022-12-21 03:51:58 -08:00
|
|
|
guard let minedHeight = transaction.minedHeight else { return }
|
2021-09-17 06:49:58 -07:00
|
|
|
|
2022-12-21 03:51:58 -08:00
|
|
|
let minedTx = try transactionManager.applyMinedHeight(pendingTransaction: pendingTx, minedHeight: minedHeight)
|
2021-09-17 06:49:58 -07:00
|
|
|
|
|
|
|
notifyMinedTransaction(minedTx)
|
|
|
|
}
|
2019-12-17 13:16:26 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-12-17 13:16:26 -08:00
|
|
|
private func removeConfirmedTransactions() throws {
|
|
|
|
let latestHeight = try transactionRepository.lastScannedHeight()
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2021-09-17 06:49:58 -07:00
|
|
|
try transactionManager.allPendingTransactions()?
|
|
|
|
.filter { $0.minedHeight > 0 && abs($0.minedHeight - latestHeight) >= ZcashSDK.defaultStaleTolerance }
|
|
|
|
.forEach { try transactionManager.delete(pendingTransaction: $0) }
|
2019-12-17 13:16:26 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2019-12-16 14:25:45 -08:00
|
|
|
private func refreshPendingTransactions() {
|
|
|
|
do {
|
2019-12-17 13:16:26 -08:00
|
|
|
try updateMinedTransactions()
|
|
|
|
try removeConfirmedTransactions()
|
2019-12-16 14:25:45 -08:00
|
|
|
} catch {
|
2020-03-09 13:25:27 -07:00
|
|
|
LoggerProxy.debug("error refreshing pending transactions: \(error)")
|
2019-12-16 14:25:45 -08:00
|
|
|
}
|
2019-12-06 04:38:47 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2021-09-17 06:49:58 -07:00
|
|
|
private func notifyMinedTransaction(_ transaction: PendingTransactionEntity) {
|
2023-03-15 04:17:43 -07:00
|
|
|
streamsUpdateQueue.async { [weak self] in
|
|
|
|
self?.eventSubject.send(.minedTransaction(transaction))
|
2019-12-17 12:11:21 -08:00
|
|
|
}
|
|
|
|
}
|
2021-09-17 06:49:58 -07:00
|
|
|
|
|
|
|
// swiftlint:disable cyclomatic_complexity
|
2023-03-15 04:17:43 -07:00
|
|
|
private func mapError(_ error: Error) -> SynchronizerError {
|
2021-09-17 06:49:58 -07:00
|
|
|
if let compactBlockProcessorError = error as? CompactBlockProcessorError {
|
2020-03-13 17:00:01 -07:00
|
|
|
switch compactBlockProcessorError {
|
|
|
|
case .dataDbInitFailed(let path):
|
|
|
|
return SynchronizerError.initFailed(message: "DataDb init failed at path: \(path)")
|
|
|
|
case .connectionError(let message):
|
|
|
|
return SynchronizerError.connectionFailed(message: message)
|
|
|
|
case .invalidConfiguration:
|
|
|
|
return SynchronizerError.generalError(message: "Invalid Configuration")
|
|
|
|
case .missingDbPath(let path):
|
|
|
|
return SynchronizerError.initFailed(message: "missing Db path: \(path)")
|
|
|
|
case .generalError(let message):
|
|
|
|
return SynchronizerError.generalError(message: message)
|
|
|
|
case .maxAttemptsReached(attempts: let attempts):
|
2021-07-14 16:43:02 -07:00
|
|
|
return SynchronizerError.maxRetryAttemptsReached(attempts: attempts)
|
2021-09-17 06:49:58 -07:00
|
|
|
case let .grpcError(statusCode, message):
|
2020-04-09 15:25:43 -07:00
|
|
|
return SynchronizerError.connectionError(status: statusCode, message: message)
|
2020-08-10 15:19:59 -07:00
|
|
|
case .connectionTimeout:
|
|
|
|
return SynchronizerError.networkTimeout
|
|
|
|
case .unspecifiedError(let underlyingError):
|
|
|
|
return SynchronizerError.uncategorized(underlyingError: underlyingError)
|
|
|
|
case .criticalError:
|
|
|
|
return SynchronizerError.criticalError
|
2021-04-08 10:18:16 -07:00
|
|
|
case .invalidAccount:
|
|
|
|
return SynchronizerError.invalidAccount
|
2021-05-18 07:49:57 -07:00
|
|
|
case .wrongConsensusBranchId:
|
2021-05-17 14:14:59 -07:00
|
|
|
return SynchronizerError.lightwalletdValidationFailed(underlyingError: compactBlockProcessorError)
|
2021-05-18 07:49:57 -07:00
|
|
|
case .networkMismatch:
|
2021-05-17 14:14:59 -07:00
|
|
|
return SynchronizerError.lightwalletdValidationFailed(underlyingError: compactBlockProcessorError)
|
2021-05-18 07:49:57 -07:00
|
|
|
case .saplingActivationMismatch:
|
2021-05-17 14:14:59 -07:00
|
|
|
return SynchronizerError.lightwalletdValidationFailed(underlyingError: compactBlockProcessorError)
|
2022-11-28 07:14:06 -08:00
|
|
|
case .unknown:
|
|
|
|
break
|
2020-03-13 17:00:01 -07:00
|
|
|
}
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2020-08-10 15:19:59 -07:00
|
|
|
return SynchronizerError.uncategorized(underlyingError: error)
|
2020-03-13 17:00:01 -07:00
|
|
|
}
|
2019-11-14 06:38:54 -08:00
|
|
|
}
|
2020-01-29 21:34:03 -08:00
|
|
|
|
|
|
|
extension SDKSynchronizer {
|
|
|
|
public var pendingTransactions: [PendingTransactionEntity] {
|
|
|
|
(try? self.allPendingTransactions()) ?? [PendingTransactionEntity]()
|
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public var clearedTransactions: [ZcashTransaction.Overview] {
|
2022-12-22 01:01:12 -08:00
|
|
|
(try? self.allClearedTransactions()) ?? []
|
2020-01-29 21:34:03 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public var sentTransactions: [ZcashTransaction.Sent] {
|
2022-12-21 03:51:58 -08:00
|
|
|
(try? self.allSentTransactions()) ?? []
|
2020-01-29 21:34:03 -08:00
|
|
|
}
|
2022-11-21 07:22:43 -08:00
|
|
|
|
2023-01-23 01:01:07 -08:00
|
|
|
public var receivedTransactions: [ZcashTransaction.Received] {
|
2022-12-22 00:46:54 -08:00
|
|
|
(try? self.allReceivedTransactions()) ?? []
|
2020-01-29 21:34:03 -08:00
|
|
|
}
|
|
|
|
}
|
2021-06-14 16:38:05 -07:00
|
|
|
|
2022-11-09 13:01:43 -08:00
|
|
|
extension SDKSynchronizer {
|
|
|
|
public func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? {
|
|
|
|
self.initializer.getCurrentAddress(accountIndex: accountIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? {
|
|
|
|
self.getUnifiedAddress(accountIndex: accountIndex)?.saplingReceiver()
|
|
|
|
}
|
|
|
|
|
|
|
|
public func getTransparentAddress(accountIndex: Int) -> TransparentAddress? {
|
|
|
|
self.getUnifiedAddress(accountIndex: accountIndex)?.transparentReceiver()
|
|
|
|
}
|
|
|
|
}
|