chore(@desktop/keycard): improvement in terms of avoiding overlapping keycard library flows

This commit is contained in:
Sale Djenic 2023-11-30 18:10:59 +01:00 committed by saledjenic
parent e552a01d2b
commit 608bb38874
5 changed files with 104 additions and 10 deletions

View File

@ -170,6 +170,14 @@ proc disconnectAll*(self: Controller) =
proc delete*(self: Controller) =
self.disconnectAll()
proc checkKeycardAvailability*(self: Controller) =
if self.keycardService.isBusy():
self.keycardService.registerForKeycardAvailability(proc () =
self.delegate.keycardReady()
)
else:
self.delegate.keycardReady()
proc init*(self: Controller, fullConnect = true) =
self.connectKeycardReponseSignal()

View File

@ -106,6 +106,9 @@ type
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method keycardReady*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -35,6 +35,15 @@ type
authenticationPopupIsAlreadyRunning: bool
runningFlow: FlowType # in general used to mark the global shared flow that is being running (`Authentication` or `Sign`)
# temporary variables used to store data while we're wiating for keycard lib to get ready
tmpFlowToRun: FlowType
tmpKeyUid: string
tmpBip44Paths: seq[string]
tmpTxHash: string
tmpForceFlow: bool
tmpReturnToFlow: FlowType
tmpPin: string
proc newModule*[T](delegate: T,
uniqueIdentifier: string,
events: EventEmitter,
@ -58,6 +67,10 @@ proc newModule*[T](delegate: T,
{.push warning[Deprecated]: off.}
## Forward declarations
proc proceedWithSyncKeycardBasedOnAppState[T](self: Module[T], keyUid: string, pin: string)
proc proceedWithRunFlow[T](self: Module[T], flowToRun: FlowType, keyUid: string, bip44Paths: seq[string], txHash: string, forceFlow: bool, returnToFlow: FlowType)
method delete*[T](self: Module[T]) =
self.view.delete
self.viewVariant.delete
@ -69,6 +82,12 @@ proc init[T](self: Module[T], fullConnect = true) =
self.controller.cleanReceivedKeycardData()
self.controller.init(fullConnect)
method keycardReady*[T](self: Module[T]) =
if self.tmpPin.len > 0:
self.proceedWithSyncKeycardBasedOnAppState(self.tmpKeyUid, self.tmpPin)
else:
self.proceedWithRunFlow(self.tmpFlowToRun, self.tmpKeyUid, self.tmpBip44Paths, self.tmpTxHash, self.tmpForceFlow, self.tmpReturnToFlow)
method getModuleAsVariant*[T](self: Module[T]): QVariant =
return self.viewVariant
@ -318,6 +337,11 @@ method syncKeycardBasedOnAppState*[T](self: Module[T], keyUid: string, pin: stri
if keyUid.len == 0:
debug "cannot sync with the empty keyUid"
return
self.tmpKeyUid = keyUid
self.tmpPin = pin
self.controller.checkKeycardAvailability()
proc proceedWithSyncKeycardBasedOnAppState[T](self: Module[T], keyUid: string, pin: string) =
self.init(fullConnect = false)
self.controller.setKeyUidWhichIsBeingSyncing(keyUid)
self.controller.setPin(pin)
@ -478,6 +502,15 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Paths
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
error "sm_cannot run an general flow"
return
self.tmpFlowToRun = flowToRun
self.tmpKeyUid = keyUid
self.tmpBip44Paths = bip44Paths
self.tmpTxHash = txHash
self.tmpForceFlow = forceFlow
self.tmpReturnToFlow = returnToFlow
self.controller.checkKeycardAvailability()
proc proceedWithRunFlow[T](self: Module[T], flowToRun: FlowType, keyUid: string, bip44Paths: seq[string], txHash: string, forceFlow: bool, returnToFlow: FlowType) =
self.init()
self.view.setForceFlow(forceFlow)
self.view.setReturnToFlow(returnToFlow)

View File

@ -5,8 +5,9 @@
type
TimerTaskArg = ref object of QObjectTaskArg
timeoutInMilliseconds: int
reason: string
const timerTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[TimerTaskArg](argEncoded)
sleep(arg.timeoutInMilliseconds)
arg.finish("")
arg.finish(arg.reason)

View File

@ -37,10 +37,15 @@ const SupportedMnemonicLength18* = 18
const SupportedMnemonicLength24* = 24
const MnemonicLengthForStatusApp = SupportedMnemonicLength12
const TimerIntervalInMilliseconds = 3 * 1000 # 3 seconds
const ReRunCurrentFlowInterval = 3 * 1000 # 3 seconds
const CheckKeycardAvailabilityInterval = 1000 # 1 seconds
const SIGNAL_KEYCARD_RESPONSE* = "keycardResponse"
type TimerReason {.pure.} = enum
ReRunCurrentFlowLater = "ReRunCurrentFlowLater"
WaitForKeycardAvailability = "WaitForKeycardAvailability"
logScope:
topics = "keycard-service"
@ -63,6 +68,16 @@ QtObject:
lastReceivedKeycardData: tuple[flowType: string, flowEvent: KeycardEvent]
setPayloadForCurrentFlow: JsonNode
doLogging: bool
busy: bool
waitingFlows: seq[tuple[flow: KCSFlowType, payload: JsonNode]]
registeredCallback: proc ()
## Forward declaration
proc startFlow(self: Service, payload: JsonNode)
proc runTimer(self: Service, timeoutInMilliseconds: int, reason: string)
proc isBusy*(self: Service): bool =
return self.busy
proc delete*(self: Service) =
self.closingApp = true
@ -108,7 +123,13 @@ QtObject:
self.events.emit(SIGNAL_KEYCARD_RESPONSE, KeycardLibArgs(flowType: flowType, flowEvent: flowEvent))
proc receiveKeycardSignal(self: Service, signal: string) {.slot.} =
self.busy = false
self.processSignal(signal)
if self.waitingFlows.len > 0:
let (flow, payload) = self.waitingFlows[0]
self.waitingFlows.delete(0)
self.currentFlow = flow
self.startFlow(payload)
proc getLastReceivedKeycardData*(self: Service): tuple[flowType: string, flowEvent: KeycardEvent] =
return self.lastReceivedKeycardData
@ -132,18 +153,28 @@ QtObject:
return self.currentFlow
proc startFlow(self: Service, payload: JsonNode) =
if self.busy:
self.waitingFlows.add((flow: self.currentFlow, payload: payload))
return
self.busy = true
self.updateLocalPayloadForCurrentFlow(payload, cleanBefore = true)
let response = keycard_go.keycardStartFlow(self.currentFlow.int, $payload)
if self.doLogging:
debug "keycardStartFlow", kcServiceCurrFlow=($self.currentFlow), payload=payload, response=response
proc resumeFlow(self: Service, payload: JsonNode) =
if self.busy:
return
self.busy = true
self.updateLocalPayloadForCurrentFlow(payload)
let response = keycard_go.keycardResumeFlow($payload)
if self.doLogging:
debug "keycardResumeFlow", kcServiceCurrFlow=($self.currentFlow), payload=payload, response=response
proc cancelCurrentFlow*(self: Service) =
if self.busy:
return
writeStackTrace()
let response = keycard_go.keycardCancelFlow()
self.currentFlow = KCSFlowType.NoFlow
if self.doLogging:
@ -199,13 +230,23 @@ QtObject:
result = result & $rand(0 .. 9)
proc onTimeout(self: Service, response: string) {.slot.} =
if(self.closingApp or self.currentFlow == KCSFlowType.NoFlow):
return
if self.doLogging:
debug "onTimeout, about to start flow: ", kcServiceCurrFlow=($self.currentFlow)
self.startFlow(self.setPayloadForCurrentFlow)
if response == $TimerReason.ReRunCurrentFlowLater:
if(self.closingApp or self.currentFlow == KCSFlowType.NoFlow):
return
if self.doLogging:
debug "onTimeout, about to start flow: ", kcServiceCurrFlow=($self.currentFlow)
self.startFlow(self.setPayloadForCurrentFlow)
elif response == $TimerReason.WaitForKeycardAvailability:
if self.busy:
self.runTimer(CheckKeycardAvailabilityInterval, $TimerReason.WaitForKeycardAvailability)
return
if self.registeredCallback != nil:
self.registeredCallback()
self.registeredCallback = nil
else:
error "unknown timer reason", reason = response
proc runTimer(self: Service) =
proc runTimer(self: Service, timeoutInMilliseconds: int, reason: string) =
if(self.closingApp or self.currentFlow == KCSFlowType.NoFlow):
return
@ -213,7 +254,8 @@ QtObject:
tptr: cast[ByteAddress](timerTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onTimeout",
timeoutInMilliseconds: TimerIntervalInMilliseconds
timeoutInMilliseconds: timeoutInMilliseconds,
reason: reason
)
self.threadpool.start(arg)
@ -440,4 +482,11 @@ QtObject:
let tmpFlow = self.currentFlow
self.cancelCurrentFlow()
self.currentFlow = tmpFlow
self.runTimer()
self.runTimer(ReRunCurrentFlowInterval, "reRunCurrentFlowLater")
proc registerForKeycardAvailability*(self: Service, p: proc()) =
if not self.busy:
error "registerForKeycardAvailability can be called only when keycard is busy"
return
self.registeredCallback = p
self.runTimer(CheckKeycardAvailabilityInterval, $TimerReason.WaitForKeycardAvailability)