Skip to content

Commit 58d65b8

Browse files
committed
fix(keycard): Improve exception handling
- src/app/modules/main/wallet_section/send_new/module.nim Verify if both password and pin are empty before emitting the `authenticationCancelled` signal - handle card disconnect/reconnect when the user input is needed (enter pin, puk etc) - treat keycard message with empty error as successful - src/app_service/service/keycard/service.nim - avoid modifying the json container with itself. We're currently iterating the container and modifying the service member - leading to a freeze.
1 parent f23b896 commit 58d65b8

File tree

5 files changed

+66
-2
lines changed

5 files changed

+66
-2
lines changed

src/app/modules/main/wallet_section/send_new/module.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ method authenticateAndTransfer*(self: Module, uuid: string, fromAddr: string) =
190190
self.controller.authenticate()
191191

192192
method onUserAuthenticated*(self: Module, password: string, pin: string) =
193-
if password.len == 0:
193+
if password.len == 0 and pin.len == 0:
194194
self.transactionWasSent(uuid = self.tmpSendTransactionDetails.uuid, chainId = 0, approvalTx = false, txHash = "", error = authenticationCanceled)
195195
self.clearTmpData()
196196
else:

src/app/modules/shared_modules/keycard_popup/internal/insert_keycard_state.nim

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ method resolveKeycardNextState*(self: InsertKeycardState, keycardFlowType: strin
4343
return nil
4444
if keycardFlowType == ResponseTypeValueCardInserted:
4545
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WronglyInsertedCard, add = false))
46+
47+
# Special handling for LoadAccount flow - return to the state we came from
48+
# (RepeatPin or PinSet) to continue waiting for ENTER_MNEMONIC event
49+
if (self.flowType == FlowType.SetupNewKeycard or
50+
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
51+
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase) and
52+
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount and
53+
not self.getBackState.isNil:
54+
let backStateType = self.getBackState.stateType
55+
if backStateType == StateType.RepeatPin or backStateType == StateType.PinSet:
56+
# Return to the previous state to continue waiting for mnemonic entry
57+
return self.getBackState
58+
59+
# Default behavior for other flows
4660
if self.flowType == FlowType.SetupNewKeycard:
4761
return createState(StateType.KeycardInserted, self.flowType, self.getBackState)
4862
return createState(StateType.KeycardInserted, self.flowType, nil)

src/app/modules/shared_modules/keycard_popup/internal/pin_set_state.nim

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,28 @@ method executeCancelCommand*(self: PinSetState, controller: Controller) =
3131
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
3232
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase or
3333
self.flowType == FlowType.UnlockKeycard:
34-
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
34+
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
35+
36+
method resolveKeycardNextState*(self: PinSetState, keycardFlowType: string, keycardEvent: KeycardEvent,
37+
controller: Controller): State =
38+
# Handle temporary card disconnection during LoadAccount flow (after card initialization)
39+
# This can happen if the user hasn't tapped "Continue" yet and the card disconnects
40+
if self.flowType == FlowType.SetupNewKeycard or
41+
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
42+
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase:
43+
# INSERT_CARD during LoadAccount flow means card is reconnecting after initialization
44+
if keycardFlowType == ResponseTypeValueInsertCard and
45+
keycardEvent.error.len > 0 and
46+
keycardEvent.error == ErrorConnection and
47+
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
48+
# Don't cancel the flow - transition to InsertKeycard state and wait for reconnection
49+
controller.reRunCurrentFlowLater()
50+
return createState(StateType.InsertKeycard, self.flowType, self)
51+
# CARD_INSERTED after temporary disconnection - stay in PinSet and continue
52+
if keycardFlowType == ResponseTypeValueCardInserted and
53+
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
54+
# Card reconnected successfully, stay in PinSet
55+
return nil
56+
57+
# No specific handling needed - this state transitions via primary button
58+
return nil

src/app/modules/shared_modules/keycard_popup/internal/repeat_pin_state.nim

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ method executeCancelCommand*(self: RepeatPinState, controller: Controller) =
4242

4343
method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
4444
controller: Controller): State =
45+
# Handle temporary card disconnection during LoadAccount flow (after card initialization)
46+
# This happens on Android/iOS when card is disconnected and needs to be re-detected
47+
if self.flowType == FlowType.SetupNewKeycard or
48+
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
49+
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase:
50+
# INSERT_CARD during LoadAccount flow means card is reconnecting after initialization
51+
if keycardFlowType == ResponseTypeValueInsertCard and
52+
keycardEvent.error.len > 0 and
53+
keycardEvent.error == ErrorConnection and
54+
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
55+
# Don't cancel the flow - transition to InsertKeycard state and wait for reconnection
56+
controller.reRunCurrentFlowLater()
57+
return createState(StateType.InsertKeycard, self.flowType, self)
58+
# CARD_INSERTED after temporary disconnection - stay in RepeatPin and continue waiting
59+
if keycardFlowType == ResponseTypeValueCardInserted and
60+
controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
61+
# Card reconnected successfully, continue waiting for ENTER_MNEMONIC event
62+
return nil
63+
4564
let state = ensureReaderAndCardPresence(self, keycardFlowType, keycardEvent, controller)
4665
if not state.isNil:
4766
return state

src/app_service/service/keycard/service.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,15 @@ QtObject:
151151
return seedPhrase
152152

153153
proc updateLocalPayloadForCurrentFlow(self: Service, obj: JsonNode, cleanBefore = false) {.featureGuard(KEYCARD_ENABLED).} =
154+
# CRITICAL FIX: Check if obj is the same reference as setPayloadForCurrentFlow
155+
# This happens when onTimeout calls startFlow(self.setPayloadForCurrentFlow)
156+
# If we iterate and modify the same object, the iterator gets corrupted!
157+
if cast[pointer](obj) == cast[pointer](self.setPayloadForCurrentFlow):
158+
return
159+
154160
if cleanBefore:
155161
self.setPayloadForCurrentFlow = %* {}
162+
156163
for k, v in obj:
157164
self.setPayloadForCurrentFlow[k] = v
158165

0 commit comments

Comments
 (0)