Replace Status for SyncStatus

This commit is contained in:
Francisco Gindre 2021-06-14 20:38:05 -03:00
parent a07fdc52dc
commit 558dbeb1ac
3 changed files with 213 additions and 53 deletions

View File

@ -90,8 +90,12 @@ struct BlockProgress: BlockProgressReporting {
var progressHeight: BlockHeight
}
extension Notification.Name {
static let connectionStatusChanged = Notification.Name("LightWalletServiceConnectivityStatusChanged")
}
public class LightWalletGRPCService {
var queue: DispatchQueue
let channel: Channel
let connectionDelegate: ConnectionStatusManager
@ -488,5 +492,12 @@ extension LightWalletServiceError {
class ConnectionStatusManager: ConnectivityStateDelegate {
func connectivityStateDidChange(from oldState: ConnectivityState, to newState: ConnectivityState) {
LoggerProxy.event("Connection Changed from \(oldState) to \(newState)")
NotificationCenter.default.post(
name: .blockProcessorConnectivityStateChanged,
object: self,
userInfo: [
CompactBlockProcessorNotificationKey.currentConnectivityStatus : newState,
CompactBlockProcessorNotificationKey.previousConnectivityStatus : oldState
])
}
}

View File

@ -48,6 +48,32 @@ extension ShieldFundsError: LocalizedError {
}
}
/**
Represent the connection state to the lightwalletd server
*/
public enum ConnectionState {
/**
not in use
*/
case idle
/**
there's a connection being attempted from a non error state
*/
case connecting
/**
connection is established, ready to use or in use
*/
case online
/**
the connection is being re-established after losing it temporarily
*/
case reconnecting
/**
the connection has been closed
*/
case shutdown
}
/**
Primary interface for interacting with the SDK. Defines the contract that specific
@ -57,17 +83,9 @@ implementations like SdkSynchronizer fulfill.
public protocol Synchronizer {
/**
Value representing the Status of this Synchronizer. As the status changes, a new
value will be emitted by KVO
Value representing the Status of this Synchronizer. As the status changes, it will be also notified
*/
var status: Status { get }
/**
A flow of progress values, typically corresponding to this Synchronizer downloading blocks.
Typically, any non-zero value below 1.0 indicates that progress indicators can be shown and
a value of 1.0 signals that progress is complete and any progress indicators can be hidden. KVO Compliant
*/
var progress: Float { get }
var status: SyncStatus { get }
/**
prepares this initializer to operate. Initializes the internal state with the given Extended Viewing Keys and a wallet birthday found in the initializer object
@ -215,73 +233,65 @@ public protocol Synchronizer {
func rewind(_ policy: RewindPolicy) throws
}
/**
The Status of the synchronizer
*/
public enum Status {
public enum SyncStatus: Equatable {
/**
Indicates that this Synchronizer is actively preparing to start, which usually involves
setting up database tables, migrations or taking other maintenance steps that need to
occur after an upgrade.
*/
case unprepared
/**
Indicates that [stop] has been called on this Synchronizer and it will no longer be used.
*/
case stopped
/**
Indicates that this Synchronizer is disconnected from its lightwalletd server.
When set, a UI element may want to turn red.
*/
case disconnected
/**
Indicates that this Synchronizer is actively downloading new blocks from the server.
*/
case downloading
case downloading(_ status: BlockProgressReporting)
/**
Indicates that this Synchronizer is actively validating new blocks that were downloaded
from the server. Blocks need to be verified before they are scanned. This confirms that
each block is chain-sequential, thereby detecting missing blocks and reorgs.
*/
case validating
/**
Indicates that this Synchronizer is actively scanning new valid blocks that were downloaded
from the server.
*/
case scanning
case scanning(_ progress: BlockProgressReporting)
/**
Indicates that this Synchronizer is actively enhancing newly scanned blocks with
additional transaction details, fetched from the server.
*/
case enhancing
case enhancing(_ progress: EnhancementProgress)
/**
fetches the transparent balance and stores it locally
*/
case fetching
/**
Indicates that this Synchronizer is fully up to date and ready for all wallet functions.
When set, a UI element may want to turn green.
*/
case synced
/**
Indicates that [stop] has been called on this Synchronizer and it will no longer be used.
*/
case stopped
/**
Indicates that this Synchronizer is disconnected from its lightwalletd server.
When set, a UI element may want to turn red.
*/
case disconnected
case error(_ error: Error)
public var isSyncing: Bool {
switch self {
case .disconnected, .synced, .synced, .unprepared:
return false
default:
case .downloading, .validating, .scanning, .enhancing, .fetching:
return true
default:
return false
}
}
}
/**
Kind of transactions handled by a Synchronizer
*/
@ -304,3 +314,73 @@ public enum RewindPolicy {
case transaction(_ transaction: TransactionEntity)
case quick
}
extension SyncStatus {
public static func == (lhs: SyncStatus, rhs: SyncStatus) -> Bool {
switch lhs {
case .unprepared:
if case .unprepared = rhs {
return true
} else {
return false
}
case .disconnected:
if case .disconnected = rhs {
return true
} else {
return false
}
case .downloading:
if case .downloading = rhs {
return true
} else {
return false
}
case .validating:
if case .validating = rhs {
return true
} else {
return false
}
case .scanning:
if case .scanning = rhs {
return true
} else {
return false
}
case .enhancing:
if case .enhancing = rhs {
return true
} else {
return false
}
case .fetching:
if case .fetching = rhs {
return true
} else {
return false
}
case .synced:
if case .synced = rhs {
return true
} else {
return false
}
case .stopped:
if case .stopped = rhs {
return true
} else {
return false
}
case .error:
if case .error = rhs {
return true
} else {
return false
}
}
}
}

View File

@ -91,6 +91,8 @@ public extension Notification.Name {
- Note: query userInfo on NotificationKeys.error for an error
*/
static let synchronizerFailed = Notification.Name("SDKSynchronizerFailed")
static let synchronizerConnectionStateChanged = Notification.Name("SynchronizerConnectionStateChanged")
}
/**
@ -107,9 +109,11 @@ public class SDKSynchronizer: Synchronizer {
public static let error = "SDKSynchronizer.error"
public static let currentStatus = "SDKSynchronizer.currentStatus"
public static let nextStatus = "SDKSynchronizer.nextStatus"
public static let currentConnectionState = "SDKSynchronizer.currentConnectionState"
public static let previousConnectionState = "SDKSynchronizer.previousConnectionState"
}
public private(set) var status: Status {
public private(set) var status: SyncStatus {
didSet {
notify(status: status)
}
@ -140,7 +144,7 @@ public class SDKSynchronizer: Synchronizer {
}
init(status: Status,
init(status: SyncStatus,
initializer: Initializer,
transactionManager: OutboundTransactionManager,
transactionRepository: TransactionRepository,
@ -187,7 +191,7 @@ public class SDKSynchronizer: Synchronizer {
assert(true,"warning: synchronizer started when already started") // TODO: remove this assertion sometime in the near future
LoggerProxy.debug("warning: synchronizer started when already started")
return
case .stopped, .synced,.disconnected:
case .stopped, .synced,.disconnected, .error:
do {
try blockProcessor.start(retry: retry)
} catch {
@ -269,9 +273,30 @@ public class SDKSynchronizer: Synchronizer {
name: Notification.Name.blockProcessorFoundTransactions,
object: processor)
center.addObserver(self,
selector: #selector(connectivityStateChanged(_:)),
name: Notification.Name.blockProcessorConnectivityStateChanged,
object: processor)
}
// MARK: Block Processor notifications
@objc func connectivityStateChanged(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let previous = userInfo[CompactBlockProcessorNotificationKey.previousConnectivityStatus] as? ConnectivityState,
let current = userInfo[CompactBlockProcessorNotificationKey.currentConnectivityStatus] as? ConnectivityState else {
LoggerProxy.error("found \(Notification.Name.blockProcessorConnectivityStateChanged) but lacks dictionary information. this is probably a programming error")
return
}
NotificationCenter.default.post(
name: .synchronizerConnectionStateChanged,
object: self,
userInfo: [
NotificationKeys.previousConnectionState : ConnectionState(previous),
NotificationKeys.currentConnectionState : ConnectionState(current)
])
}
@objc func transactionsFound(_ notification: Notification) {
guard let userInfo = notification.userInfo,
@ -310,41 +335,41 @@ public class SDKSynchronizer: Synchronizer {
@objc func processorStartedDownloading(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.status = .downloading
guard let self = self, self.status != .downloading(NullProgress()) else { return }
self.status = .downloading(NullProgress())
}
}
@objc func processorStartedValidating(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard let self = self, self.status != .validating else { return }
self.status = .validating
}
}
@objc func processorStartedScanning(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.status = .scanning
guard let self = self, self.status != .scanning(NullProgress()) else { return }
self.status = .scanning(NullProgress())
}
}
@objc func processorStartedEnhancing(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.status = .enhancing
guard let self = self, self.status != .enhancing(NullEnhancementProgress()) else { return }
self.status = .enhancing(NullEnhancementProgress())
}
}
@objc func processorStartedFetching(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard let self = self, self.status != .fetching else { return }
self.status = .fetching
}
}
@objc func processorStopped(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard let self = self, self.status != .stopped else { return }
self.status = .stopped
}
}
@ -355,10 +380,11 @@ public class SDKSynchronizer: Synchronizer {
guard let self = self else { return }
if let error = notification.userInfo?[CompactBlockProcessorNotificationKey.error] as? Error {
self.notifyFailure(error)
self.status = .error(self.mapError(error))
} else {
self.notifyFailure(CompactBlockProcessorError.generalError(message: "This is strange. processorFailed Call received no error message"))
self.status = .error(SynchronizerError.generalError(message: "This is strange. processorFailed Call received no error message"))
}
self.status = .disconnected
}
}
@ -619,7 +645,7 @@ public class SDKSynchronizer: Synchronizer {
NotificationCenter.default.post(name: Notification.Name.synchronizerProgressUpdated, object: self, userInfo: userInfo)
}
private func notifyStatusChange(newValue: Status, oldValue: Status) {
private func notifyStatusChange(newValue: SyncStatus, oldValue: SyncStatus) {
NotificationCenter.default.post(name: .synchronizerStatusWillUpdate,
object: self,
userInfo:
@ -627,7 +653,7 @@ public class SDKSynchronizer: Synchronizer {
NotificationKeys.nextStatus : newValue ])
}
private func notify(status: Status) {
private func notify(status: SyncStatus) {
switch status {
case .disconnected:
@ -649,6 +675,8 @@ public class SDKSynchronizer: Synchronizer {
NotificationCenter.default.post(name: Notification.Name.synchronizerEnhancing, object: self)
case .fetching:
NotificationCenter.default.post(name: Notification.Name.synchronizerFetching, object: self)
case .error(let e):
self.notifyFailure(e)
}
}
// MARK: book keeping
@ -760,3 +788,44 @@ extension SDKSynchronizer {
(try? self.allReceivedTransactions()) ?? [ConfirmedTransactionEntity]()
}
}
import GRPC
extension ConnectionState {
init(_ connectivityState: ConnectivityState) {
switch connectivityState {
case .connecting:
self = .connecting
case .idle:
self = .idle
case .ready:
self = .online
case .shutdown:
self = .shutdown
case .transientFailure:
self = .reconnecting
}
}
}
fileprivate struct NullEnhancementProgress: EnhancementProgress {
var totalTransactions: Int { 0 }
var enhancedTransactions: Int { 0 }
var lastFoundTransaction: ConfirmedTransactionEntity? { nil }
var range: CompactBlockRange { 0 ... 0 }
}
fileprivate struct NullProgress: BlockProgressReporting {
var startHeight: BlockHeight {
0
}
var targetHeight: BlockHeight {
0
}
var progressHeight: BlockHeight {
0
}
}