Skip to content

Commit

Permalink
Retry with error of all node failure
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyuehyang committed Oct 18, 2022
1 parent b962281 commit cdc8cb0
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class TIPDiagnosticViewController: SettingsTableViewController {
SettingsRow(title: "Fail PIN Update Client Once", accessory: .switch(isOn: TIPDiagnostic.failPINUpdateClientSideOnce, isEnabled: true)),
SettingsRow(title: "Fail Watch Once", accessory: .switch(isOn: TIPDiagnostic.failCounterWatchOnce, isEnabled: true)),
SettingsRow(title: "Crash After PIN Update", accessory: .switch(isOn: TIPDiagnostic.crashAfterUpdatePIN, isEnabled: true)),
SettingsRow(title: "Invalid Nonce Once", accessory: .switch(isOn: TIPDiagnostic.invalidNonceOnce, isEnabled: true)),
]),
SettingsSection(header: "UI Test", rows: [
SettingsRow(title: "UI Test On", accessory: .switch(isOn: TIPDiagnostic.uiTestOnly, isEnabled: true)),
Expand Down Expand Up @@ -45,6 +46,8 @@ class TIPDiagnosticViewController: SettingsTableViewController {
TIPDiagnostic.failCounterWatchOnce.toggle()
case dataSource.sections[0].rows[4]:
TIPDiagnostic.crashAfterUpdatePIN.toggle()
case dataSource.sections[0].rows[5]:
TIPDiagnostic.invalidNonceOnce.toggle()
case dataSource.sections[1].rows[0]:
TIPDiagnostic.uiTestOnly.toggle()
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class TIPActionViewController: UIViewController {
}

private func performAction() {
guard let accountCounterBefore = LoginManager.shared.account?.tipCounter else {
return
}
switch action {
case let .create(pin):
titleLabel.text = R.string.localizable.create_pin()
Expand All @@ -84,7 +87,7 @@ class TIPActionViewController: UIViewController {
finish()
}
} catch {
await handle(error: error)
await handle(error: error, accountCounterBefore: accountCounterBefore)
}
}
case let .change(old, new):
Expand Down Expand Up @@ -119,7 +122,7 @@ class TIPActionViewController: UIViewController {
finish()
}
} catch {
await handle(error: error)
await handle(error: error, accountCounterBefore: accountCounterBefore)
}
}
case let .migrate(pin):
Expand All @@ -142,7 +145,7 @@ class TIPActionViewController: UIViewController {
finish()
}
} catch {
await handle(error: error)
await handle(error: error, accountCounterBefore: accountCounterBefore)
}
}
}
Expand All @@ -165,7 +168,7 @@ class TIPActionViewController: UIViewController {
}
}

private func handle(error: Error) async {
private func handle(error: Error, accountCounterBefore: UInt64) async {
Logger.tip.error(category: "TIPAction", message: "Failed with: \(error)")
do {
if let context = try await TIP.checkCounter() {
Expand All @@ -174,9 +177,18 @@ class TIPActionViewController: UIViewController {
navigationController?.setViewControllers([intro], animated: true)
}
} else {
await MainActor.run {
Logger.tip.warn(category: "TIPAction", message: "No interruption is detected")
finish()
try await MainActor.run {
guard let accountCounterAfter = LoginManager.shared.account?.tipCounter else {
throw MixinAPIError.unauthorized
}
if accountCounterAfter == accountCounterBefore {
Logger.tip.error(category: "TIPAction", message: "Nothing changed")
let intro = TIPIntroViewController(action: action, changedNothingWith: error)
tipNavigationController?.setViewControllers([intro], animated: true)
} else {
Logger.tip.warn(category: "TIPAction", message: "No interruption is detected")
finish()
}
}
}
} catch {
Expand Down
66 changes: 47 additions & 19 deletions Mixin/UserInterface/Controllers/Wallet/TIPIntroViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class TIPIntroViewController: UIViewController {
enum Interruption {
case unknown
case none
case confirmed(TIP.InterruptionContext)
case inputNeeded(TIP.InterruptionContext)
case noInputNeeded(TIPActionViewController.Action, Error)
}

private enum Status {
Expand Down Expand Up @@ -44,26 +45,41 @@ class TIPIntroViewController: UIViewController {
navigationController as? TIPNavigationViewController
}

init(intent: TIP.Action) {
convenience init(intent: TIP.Action) {
Logger.tip.info(category: "TIPIntro", message: "Init with intent: \(intent)")
self.intent = intent
self.interruption = .unknown
let nib = R.nib.tipIntroView
super.init(nibName: nib.name, bundle: nib.bundle)
self.init(intent: intent, interruption: .unknown)
}

init(context: TIP.InterruptionContext) {
convenience init(context: TIP.InterruptionContext) {
Logger.tip.info(category: "TIPIntro", message: "Init with context: \(context)")
self.intent = context.action
self.interruption = .confirmed(context)
let nib = R.nib.tipIntroView
super.init(nibName: nib.name, bundle: nib.bundle)
self.init(intent: context.action, interruption: .inputNeeded(context))
}

convenience init(action: TIPActionViewController.Action, changedNothingWith error: Error) {
Logger.tip.info(category: "TIPIntro", message: "Init with action: \(action.debugDescription), error: \(error)")
let intent: TIP.Action
switch action {
case .create:
intent = .create
case .change:
intent = .change
case .migrate:
intent = .migrate
}
self.init(intent: intent, interruption: .noInputNeeded(action, error))
}

required init?(coder: NSCoder) {
fatalError("Storyboard is not supported")
}

private init(intent: TIP.Action, interruption: Interruption) {
self.intent = intent
self.interruption = interruption
let nib = R.nib.tipIntroView
super.init(nibName: nib.name, bundle: nib.bundle)
}

override func viewDidLoad() {
super.viewDidLoad()
contentStackView.setCustomSpacing(24, after: iconImageView)
Expand All @@ -76,7 +92,7 @@ class TIPIntroViewController: UIViewController {
switch interruption {
case .unknown, .none:
description = R.string.localizable.tip_creation_introduction()
case .confirmed:
case .inputNeeded, .noInputNeeded:
description = R.string.localizable.creating_wallet_terminated_unexpectedly()
}
setNoticeHidden(false)
Expand All @@ -85,7 +101,7 @@ class TIPIntroViewController: UIViewController {
switch interruption {
case .unknown, .none:
description = R.string.localizable.tip_introduction()
case .confirmed:
case .inputNeeded, .noInputNeeded:
description = R.string.localizable.changing_pin_terminated_unexpectedly()
}
setNoticeHidden(false)
Expand All @@ -94,7 +110,7 @@ class TIPIntroViewController: UIViewController {
switch interruption {
case .unknown, .none:
description = R.string.localizable.tip_introduction()
case .confirmed:
case .inputNeeded, .noInputNeeded:
description = R.string.localizable.upgrading_tip_terminated_unexpectedly()
}
setNoticeHidden(false)
Expand All @@ -112,11 +128,11 @@ class TIPIntroViewController: UIViewController {
case .unknown:
checkCounter()
descriptionTextLabel.additionalLinksMap = linksMap
case .confirmed:
updateNextButtonAndStatusLabel(with: .waitingForUser)
case .none:
descriptionTextLabel.additionalLinksMap = linksMap
updateNextButtonAndStatusLabel(with: .waitingForUser)
case .inputNeeded, .noInputNeeded:
updateNextButtonAndStatusLabel(with: .waitingForUser)
}
}

Expand Down Expand Up @@ -157,7 +173,7 @@ class TIPIntroViewController: UIViewController {
}))
present(validator, animated: true)
}
case .confirmed(let context):
case .inputNeeded(let context):
switch context.action {
case .migrate:
let validator = TIPPopupInputViewController(action: .migrate({ pin in
Expand All @@ -174,6 +190,9 @@ class TIPIntroViewController: UIViewController {
}))
present(validator, animated: true)
}
case let .noInputNeeded(action, _):
let viewController = TIPActionViewController(action: action)
navigationController?.setViewControllers([viewController], animated: true)
}
}

Expand Down Expand Up @@ -267,11 +286,20 @@ extension TIPIntroViewController {
switch interruption {
case .unknown, .none:
setNextButtonTitleByIntent()
case .confirmed:
actionDescriptionLabel.text = nil
case .inputNeeded:
nextButton.setTitle(R.string.localizable.continue(), for: .normal)
actionDescriptionLabel.text = nil
case let .noInputNeeded(_, error):
nextButton.setTitle(R.string.localizable.retry(), for: .normal)
if let error = error as? TIPNode.Error {
actionDescriptionLabel.text = error.description
} else {
actionDescriptionLabel.text = error.localizedDescription
}
actionDescriptionLabel.textColor = .mixinRed
}
nextButton.isBusy = false
actionDescriptionLabel.text = nil
}
}

Expand Down
4 changes: 3 additions & 1 deletion MixinServices/MixinServices/Services/Crypto/TIP/TIP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ extension TIP {
account = freshAccount
} else {
account = try await AccountAPI.me()
LoginManager.shared.setAccount(account)
await MainActor.run {
LoginManager.shared.setAccount(account)
}
}
guard let pinToken = AppGroupKeychain.pinToken else {
throw Error.missingPINToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,21 @@ public enum TIPDiagnostic {
}

@MainActor
public static var uiTestOnly = false {
public static var crashAfterUpdatePIN = false {
didSet {
updateDashboard()
}
}

@MainActor
public static var crashAfterUpdatePIN = false {
public static var invalidNonceOnce = false {
didSet {
updateDashboard()
}
}

@MainActor
public static var uiTestOnly = false {
didSet {
updateDashboard()
}
Expand Down Expand Up @@ -80,6 +87,7 @@ public enum TIPDiagnostic {
Fail PIN Update Client: \(failPINUpdateClientSideOnce ? "ONCE" : " OFF")
Fail Watch: \(failCounterWatchOnce ? "ONCE" : " OFF")
Crash After PIN Update: \(crashAfterUpdatePIN ? " ON" : " OFF")
Invalid Nonce: \(invalidNonceOnce ? "ONCE" : " OFF")
UI Test: \(uiTestOnly ? " ON" : " OFF")
"""
}
Expand Down
11 changes: 11 additions & 0 deletions MixinServices/MixinServices/Services/Crypto/TIP/TIPNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,18 @@ public enum TIPNode {
assignee: Data?,
progressHandler: (@MainActor (TIP.Progress) -> Void)?
) async throws -> [TIPSignResponseData] {
#if DEBUG
let nonce: UInt64 = await MainActor.run {
if TIPDiagnostic.invalidNonceOnce {
TIPDiagnostic.invalidNonceOnce = false
return 20
} else {
return UInt64(Date().timeIntervalSince1970)
}
}
#else
let nonce = UInt64(Date().timeIntervalSince1970)
#endif
let grace = ephemeralGrace
return await withTaskGroup(of: Result<TIPSignResponseData, Swift.Error>.self) { group in
let retries = Accumulator(maxValue: maximumRetries)
Expand Down

0 comments on commit cdc8cb0

Please sign in to comment.