diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim
index 6c412e83559..6a1d1c9d180 100644
--- a/src/app/boot/app_controller.nim
+++ b/src/app/boot/app_controller.nim
@@ -178,7 +178,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
# Services
result.generalService = general_service.newService(statusFoundation.events, statusFoundation.threadpool)
result.keycardService = keycard_service.newService(statusFoundation.events, statusFoundation.threadpool)
- result.keycardServiceV2 = keycard_serviceV2.newService(statusFoundation.events, statusFoundation.threadpool)
+ result.keycardServiceV2 = keycard_serviceV2.newService(statusFoundation.events, statusFoundation.threadpool, result.keycardService)
result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration,
result.settingsService, statusFoundation.events)
result.keychainService = keychain_service.newService(statusFoundation.events)
@@ -440,8 +440,10 @@ proc mainDidLoad*(self: AppController) =
self.checkForStoringPasswordToKeychain()
proc start*(self: AppController) =
+ if self.shouldUseTheNewOnboardingModule():
+ self.keycardServiceV2.init()
+
self.keycardService.init()
- self.keycardServiceV2.init()
self.keychainService.init()
self.generalService.init()
self.accountsService.init()
diff --git a/src/app/modules/onboarding/controller.nim b/src/app/modules/onboarding/controller.nim
index f42ded98e71..5f1c438fb1f 100644
--- a/src/app/modules/onboarding/controller.nim
+++ b/src/app/modules/onboarding/controller.nim
@@ -1,4 +1,4 @@
-import chronicles, strutils
+import chronicles
import io_interface
import uuids
@@ -9,6 +9,7 @@ import app_service/service/accounts/service as accounts_service
import app_service/service/accounts/dto/image_crop_rectangle
import app_service/service/devices/service as devices_service
import app_service/service/keycardV2/service as keycard_serviceV2
+from app_service/service/keycardV2/dto import KeycardExportedKeysDto
logScope:
topics = "onboarding-controller"
@@ -60,9 +61,47 @@ proc init*(self: Controller) =
self.delegate.onLocalPairingStatusUpdate(args)
self.connectionIds.add(handlerId)
-proc setPin*(self: Controller, pin: string): bool =
- self.keycardServiceV2.setPin(pin)
- discard
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_STATE_UPDATED) do(e: Args):
+ let args = KeycardEventArg(e)
+ self.delegate.onKeycardStateUpdated(args.keycardEvent)
+ self.connectionIds.add(handlerId)
+
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_SET_PIN_FAILURE) do(e: Args):
+ let args = KeycardErrorArg(e)
+ self.delegate.onKeycardSetPinFailure(args.error)
+ self.connectionIds.add(handlerId)
+
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_AUTHORIZE_FAILURE) do(e: Args):
+ let args = KeycardErrorArg(e)
+ self.delegate.onKeycardAuthorizeFailure(args.error)
+ self.connectionIds.add(handlerId)
+
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_LOAD_MNEMONIC_FAILURE) do(e: Args):
+ let args = KeycardErrorArg(e)
+ self.delegate.onKeycardLoadMnemonicFailure(args.error)
+ self.connectionIds.add(handlerId)
+
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_LOAD_MNEMONIC_SUCCESS) do(e: Args):
+ let args = KeycardKeyUIDArg(e)
+ self.delegate.onKeycardLoadMnemonicSuccess(args.keyUID)
+ self.connectionIds.add(handlerId)
+
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_EXPORT_KEYS_FAILURE) do(e: Args):
+ let args = KeycardErrorArg(e)
+ self.delegate.onKeycardExportKeysFailure(args.error)
+ self.connectionIds.add(handlerId)
+
+ handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_EXPORT_KEYS_SUCCESS) do(e: Args):
+ let args = KeycardExportedKeysArg(e)
+ self.delegate.onKeycardExportKeysSuccess(args.exportedKeys)
+ self.connectionIds.add(handlerId)
+
+proc initialize*(self: Controller, pin: string) =
+ let puk = self.keycardServiceV2.generateRandomPUK()
+ self.keycardServiceV2.initialize(pin, puk)
+
+proc authorize*(self: Controller, pin: string) =
+ self.keycardServiceV2.asyncAuthorize(pin)
proc getPasswordStrengthScore*(self: Controller, password, userName: string): int =
return self.generalService.getPasswordStrengthScore(password, userName)
@@ -73,16 +112,11 @@ proc validMnemonic*(self: Controller, mnemonic: string): bool =
return true
return false
-proc buildSeedPhrasesFromIndexes(self: Controller, seedPhraseIndexes: seq[int]): string =
- if seedPhraseIndexes.len == 0:
- error "keycard error: cannot generate mnemonic"
- return
- let sp = self.keycardServiceV2.buildSeedPhrasesFromIndexes(seedPhraseIndexes)
- return sp.join(" ")
+proc loadMnemonic*(self: Controller, mnemonic: string) =
+ self.keycardServiceV2.loadMnemonic(mnemonic)
-proc getMnemonic*(self: Controller): string =
- let indexes = self.keycardServiceV2.getMnemonicIndexes()
- return self.buildSeedPhrasesFromIndexes(indexes)
+proc generateRandomPUK*(self: Controller): string =
+ return self.keycardServiceV2.generateRandomPUK()
proc validateLocalPairingConnectionString*(self: Controller, connectionString: string): bool =
let err = self.devicesService.validateConnectionString(connectionString)
@@ -110,6 +144,14 @@ proc restoreAccountAndLogin*(self: Controller, password, mnemonic: string, recov
keycardInstanceUID,
)
+proc restoreKeycardAccountAndLogin*(self: Controller, keyUid, instanceUid: string, keycardKeys: KeycardExportedKeysDto, recoverAccount: bool): string =
+ return self.accountsService.restoreKeycardAccountAndLoginV2(
+ keyUid,
+ instanceUid,
+ keycardKeys,
+ recoverAccount,
+ )
+
proc setLoggedInAccount*(self: Controller, account: AccountDto) =
self.accountsService.setLoggedInAccount(account)
self.accountsService.updateLoggedInAccount(account.name, account.images)
@@ -123,3 +165,12 @@ proc loginLocalPairingAccount*(self: Controller, account: AccountDto, password,
proc finishPairingThroughSeedPhraseProcess*(self: Controller, installationId: string) =
self.devicesService.finishPairingThroughSeedPhraseProcess(installationId)
+
+proc stopKeycardService*(self: Controller) =
+ self.keycardServiceV2.stop()
+
+proc generateMnemonic*(self: Controller, length: int): string =
+ return self.keycardServiceV2.generateMnemonic(length)
+
+proc exportRecoverKeysFromKeycard*(self: Controller) =
+ self.keycardServiceV2.asyncExportRecoverKeys()
diff --git a/src/app/modules/onboarding/io_interface.nim b/src/app/modules/onboarding/io_interface.nim
index 68346f1e3d9..db0d8fae1c3 100644
--- a/src/app/modules/onboarding/io_interface.nim
+++ b/src/app/modules/onboarding/io_interface.nim
@@ -3,6 +3,7 @@ type
from app_service/service/settings/dto/settings import SettingsDto
from app_service/service/accounts/dto/accounts import AccountDto
+from app_service/service/keycardV2/dto import KeycardEventDto, KeycardExportedKeysDto
from app_service/service/devices/dto/local_pairing_status import LocalPairingStatus
method delete*(self: AccessInterface) {.base.} =
@@ -17,7 +18,10 @@ method onNodeLogin*(self: AccessInterface, error: string, account: AccountDto, s
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
-method setPin*(self: AccessInterface, pin: string): bool {.base.} =
+method initialize*(self: AccessInterface, pin: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method authorize*(self: AccessInterface, pin: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getPasswordStrengthScore*(self: AccessInterface, password, userName: string): int {.base.} =
@@ -35,12 +39,39 @@ method validateLocalPairingConnectionString*(self: AccessInterface, connectionSt
method inputConnectionStringForBootstrapping*(self: AccessInterface, connectionString: string) {.base.} =
raise newException(ValueError, "No implementation available")
+method loadMnemonic*(self: AccessInterface, dataJson: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
method finishOnboardingFlow*(self: AccessInterface, flowInt: int, dataJson: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method onLocalPairingStatusUpdate*(self: AccessInterface, status: LocalPairingStatus) {.base.} =
raise newException(ValueError, "No implementation available")
+method onKeycardStateUpdated*(self: AccessInterface, keycardEvent: KeycardEventDto) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method onKeycardSetPinFailure*(self: AccessInterface, error: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method onKeycardAuthorizeFailure*(self: AccessInterface, error: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method onKeycardLoadMnemonicFailure*(self: AccessInterface, error: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method onKeycardLoadMnemonicSuccess*(self: AccessInterface, keyUID: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method onKeycardExportKeysFailure*(self: AccessInterface, error: string) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method onKeycardExportKeysSuccess*(self: AccessInterface, exportedKeys: KeycardExportedKeysDto) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
+method exportRecoverKeys*(self: AccessInterface) {.base.} =
+ raise newException(ValueError, "No implementation available")
+
# This way (using concepts) is used only for the modules managed by AppController
type
DelegateInterface* = concept c
diff --git a/src/app/modules/onboarding/module.nim b/src/app/modules/onboarding/module.nim
index 34fa87f28b6..bed5752df06 100644
--- a/src/app/modules/onboarding/module.nim
+++ b/src/app/modules/onboarding/module.nim
@@ -12,6 +12,7 @@ import app_service/service/devices/service as devices_service
import app_service/service/keycardV2/service as keycard_serviceV2
from app_service/service/settings/dto/settings import SettingsDto
from app_service/service/accounts/dto/accounts import AccountDto
+from app_service/service/keycardV2/dto import KeycardEventDto, KeycardExportedKeysDto, KeycardState
export io_interface
@@ -22,7 +23,6 @@ type SecondaryFlow* {.pure} = enum
Unknown = 0,
CreateProfileWithPassword,
CreateProfileWithSeedphrase,
- CreateProfileWithKeycard,
CreateProfileWithKeycardNewSeedphrase,
CreateProfileWithKeycardExistingSeedphrase,
LoginWithSeedphrase,
@@ -30,6 +30,12 @@ type SecondaryFlow* {.pure} = enum
LoginWithKeycard,
ActualLogin, # TODO get the real name and value for this when it's implemented on the front-end
+type ProgressState* {.pure.} = enum
+ Idle,
+ InProgress,
+ Success,
+ Failed
+
type
Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface
delegate: T
@@ -38,6 +44,7 @@ type
controller: Controller
localPairingStatus: LocalPairingStatus
currentFlow: SecondaryFlow
+ exportedKeys: KeycardExportedKeysDto
proc newModule*[T](
delegate: T,
@@ -82,8 +89,13 @@ method load*[T](self: Module[T]) =
self.controller.init()
self.delegate.onboardingDidLoad()
-method setPin*[T](self: Module[T], pin: string): bool =
- self.controller.setPin(pin)
+method initialize*[T](self: Module[T], pin: string) =
+ self.view.setPinSettingState(ProgressState.InProgress.int)
+ self.controller.initialize(pin)
+
+method authorize*[T](self: Module[T], pin: string) =
+ self.view.setAuthorizationState(ProgressState.InProgress.int)
+ self.controller.authorize(pin)
method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int =
self.controller.getPasswordStrengthScore(password, userName)
@@ -92,7 +104,7 @@ method validMnemonic*[T](self: Module[T], mnemonic: string): bool =
self.controller.validMnemonic(mnemonic)
method getMnemonic*[T](self: Module[T]): string =
- self.controller.getMnemonic()
+ return self.controller.generateMnemonic(SupportedMnemonicLength12)
method validateLocalPairingConnectionString*[T](self: Module[T], connectionString: string): bool =
self.controller.validateLocalPairingConnectionString(connectionString)
@@ -100,6 +112,10 @@ method validateLocalPairingConnectionString*[T](self: Module[T], connectionStrin
method inputConnectionStringForBootstrapping*[T](self: Module[T], connectionString: string) =
self.controller.inputConnectionStringForBootstrapping(connectionString)
+method loadMnemonic*[T](self: Module[T], mnemonic: string) =
+ self.view.setAddKeyPairState(ProgressState.InProgress.int)
+ self.controller.loadMnemonic(mnemonic)
+
method finishOnboardingFlow*[T](self: Module[T], flowInt: int, dataJson: string): string =
try:
self.currentFlow = SecondaryFlow(flowInt)
@@ -121,15 +137,24 @@ method finishOnboardingFlow*[T](self: Module[T], flowInt: int, dataJson: string)
recoverAccount = false,
keycardInstanceUID = "",
)
- of SecondaryFlow.CreateProfileWithKeycard:
- # TODO implement keycard function
- discard
of SecondaryFlow.CreateProfileWithKeycardNewSeedphrase:
- # TODO implement keycard function
- discard
+ # New user with a seedphrase we showed them
+ let keycardEvent = self.view.getKeycardEvent()
+ err = self.controller.restoreAccountAndLogin(
+ password = "", # For keycard it will be substituted with`encryption.publicKey` in status-go
+ seedPhrase,
+ recoverAccount = false,
+ keycardInstanceUID = keycardEvent.keycardInfo.instanceUID,
+ )
of SecondaryFlow.CreateProfileWithKeycardExistingSeedphrase:
- # TODO implement keycard function
- discard
+ # New user who entered their own seed phrase
+ let keycardEvent = self.view.getKeycardEvent()
+ err = self.controller.restoreAccountAndLogin(
+ password = "", # For keycard it will be substituted with`encryption.publicKey` in status-go
+ seedPhrase,
+ recoverAccount = false,
+ keycardInstanceUID = keycardEvent.keycardInfo.instanceUID,
+ )
# LOGIN FLOWS
of SecondaryFlow.LoginWithSeedphrase:
@@ -147,8 +172,12 @@ method finishOnboardingFlow*[T](self: Module[T], flowInt: int, dataJson: string)
self.localPairingStatus.chatKey,
)
of SecondaryFlow.LoginWithKeycard:
- # TODO implement keycard function
- discard
+ err = self.controller.restoreKeycardAccountAndLogin(
+ self.view.getKeycardEvent().keycardInfo.keyUID,
+ self.view.getKeycardEvent().keycardInfo.instanceUID,
+ self.exportedKeys,
+ recoverAccount = true
+ )
else:
raise newException(ValueError, "Unknown flow: " & $self.currentFlow)
@@ -167,6 +196,8 @@ proc finishAppLoading2[T](self: Module[T]) =
singletonInstance.globalEvents.addCentralizedMetricIfEnabled(eventType,
$(%*{"flowType": repr(self.currentFlow)}))
+ self.controller.stopKeycardService()
+
self.delegate.finishAppLoading()
method onNodeLogin*[T](self: Module[T], error: string, account: AccountDto, settings: SettingsDto) =
@@ -187,4 +218,38 @@ method onLocalPairingStatusUpdate*[T](self: Module[T], status: LocalPairingStatu
self.localPairingStatus = status
self.view.setSyncState(status.state.int)
+method onKeycardStateUpdated*[T](self: Module[T], keycardEvent: KeycardEventDto) =
+ self.view.setKeycardEvent(keycardEvent)
+
+ if keycardEvent.state == KeycardState.NotEmpty and self.view.getPinSettingState() == ProgressState.InProgress.int:
+ # We just finished setting the pin
+ self.view.setPinSettingState(ProgressState.Success.int)
+
+ if keycardEvent.state == KeycardState.Authorized and self.view.getAuthorizationState() == ProgressState.InProgress.int:
+ # We just finished authorizing
+ self.view.setAuthorizationState(ProgressState.Success.int)
+
+method onKeycardSetPinFailure*[T](self: Module[T], error: string) =
+ self.view.setPinSettingState(ProgressState.Failed.int)
+
+method onKeycardAuthorizeFailure*[T](self: Module[T], error: string) =
+ self.view.setAuthorizationState(ProgressState.Failed.int)
+
+method onKeycardLoadMnemonicFailure*[T](self: Module[T], error: string) =
+ self.view.setAddKeyPairState(ProgressState.Failed.int)
+
+method onKeycardLoadMnemonicSuccess*[T](self: Module[T], keyUID: string) =
+ self.view.setAddKeyPairState(ProgressState.Success.int)
+
+method onKeycardExportKeysFailure*[T](self: Module[T], error: string) =
+ self.view.setRestoreKeysExportState(ProgressState.Failed.int)
+
+method onKeycardExportKeysSuccess*[T](self: Module[T], exportedKeys: KeycardExportedKeysDto) =
+ self.exportedKeys = exportedKeys
+ self.view.setRestoreKeysExportState(ProgressState.Success.int)
+
+method exportRecoverKeys*[T](self: Module[T]) =
+ self.view.setRestoreKeysExportState(ProgressState.InProgress.int)
+ self.controller.exportRecoverKeysFromKeycard()
+
{.pop.}
diff --git a/src/app/modules/onboarding/view.nim b/src/app/modules/onboarding/view.nim
index 0146e9264b7..84589b6e37e 100644
--- a/src/app/modules/onboarding/view.nim
+++ b/src/app/modules/onboarding/view.nim
@@ -1,14 +1,17 @@
import NimQml
import io_interface
+from app_service/service/keycardV2/dto import KeycardEventDto
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
+ keycardEvent: KeycardEventDto
syncState: int
- keycardState: int
- keycardRemainingPinAttempts: int
addKeyPairState: int
+ pinSettingState: int
+ authorizationState: int
+ restoreKeysExportState: int
proc delete*(self: View) =
self.QObject.delete
@@ -35,25 +38,49 @@ QtObject:
self.syncState = syncState
self.syncStateChanged()
+ proc pinSettingStateChanged*(self: View) {.signal.}
+ proc getPinSettingState*(self: View): int {.slot.} =
+ return self.pinSettingState
+ QtProperty[int] pinSettingState:
+ read = getPinSettingState
+ notify = pinSettingStateChanged
+ proc setPinSettingState*(self: View, pinSettingState: int) =
+ self.pinSettingState = pinSettingState
+ self.pinSettingStateChanged()
+
+ proc authorizationStateChanged*(self: View) {.signal.}
+ proc getAuthorizationState*(self: View): int {.slot.} =
+ return self.authorizationState
+ QtProperty[int] authorizationState:
+ read = getAuthorizationState
+ notify = authorizationStateChanged
+ proc setAuthorizationState*(self: View, authorizationState: int) =
+ self.authorizationState = authorizationState
+ self.authorizationStateChanged()
+
+ proc restoreKeysExportStateChanged*(self: View) {.signal.}
+ proc getRestoreKeysExportState*(self: View): int {.slot.} =
+ return self.restoreKeysExportState
+ QtProperty[int] restoreKeysExportState:
+ read = getRestoreKeysExportState
+ notify = restoreKeysExportStateChanged
+ proc setRestoreKeysExportState*(self: View, restoreKeysExportState: int) =
+ self.restoreKeysExportState = restoreKeysExportState
+ self.restoreKeysExportStateChanged()
+
proc keycardStateChanged*(self: View) {.signal.}
proc getKeycardState(self: View): int {.slot.} =
- return self.keycardState
+ return self.keycardEvent.state.int
QtProperty[int] keycardState:
read = getKeycardState
notify = keycardStateChanged
- proc setKeycardState*(self: View, keycardState: int) =
- self.keycardState = keycardState
- self.keycardStateChanged()
proc keycardRemainingPinAttemptsChanged*(self: View) {.signal.}
proc getKeycardRemainingPinAttempts(self: View): int {.slot.} =
- return self.keycardRemainingPinAttempts
+ return self.keycardEvent.keycardStatus.remainingAttemptsPIN
QtProperty[int] keycardRemainingPinAttempts:
read = getKeycardRemainingPinAttempts
notify = keycardRemainingPinAttemptsChanged
- proc setKeycardRemainingPinAttempts*(self: View, keycardRemainingPinAttempts: int) =
- self.keycardRemainingPinAttempts = keycardRemainingPinAttempts
- self.keycardRemainingPinAttemptsChanged()
proc addKeyPairStateChanged*(self: View) {.signal.}
proc getAddKeyPairState(self: View): int {.slot.} =
@@ -65,15 +92,24 @@ QtObject:
self.addKeyPairState = addKeyPairState
self.addKeyPairStateChanged()
+ proc setKeycardEvent*(self: View, keycardEvent: KeycardEventDto) =
+ self.keycardEvent = keycardEvent
+ self.keycardStateChanged()
+ self.keycardRemainingPinAttemptsChanged()
+
+ proc getKeycardEvent*(self: View): KeycardEventDto =
+ return self.keycardEvent
### slots ###
- proc setPin(self: View, pin: string): bool {.slot.} =
- return self.delegate.setPin(pin)
+ proc setPin(self: View, pin: string) {.slot.} =
+ self.delegate.initialize(pin)
+
+ proc authorize(self: View, pin: string) {.slot.} =
+ self.delegate.authorize(pin)
- # TODO find what does this do
- # proc startKeypairTransfer(self: View) {.slot.} =
- # self.delegate.startKeypairTransfer()
+ proc loadMnemonic(self: View, mnemonic: string) {.slot.} =
+ self.delegate.loadMnemonic(mnemonic)
proc getPasswordStrengthScore(self: View, password: string, userName: string): int {.slot.} =
return self.delegate.getPasswordStrengthScore(password, userName)
@@ -90,9 +126,8 @@ QtObject:
proc inputConnectionStringForBootstrapping(self: View, connectionString: string) {.slot.} =
self.delegate.inputConnectionStringForBootstrapping(connectionString)
- # TODO find what does this do
- # proc mnemonicWasShown(self: View): string {.slot.} =
- # return self.delegate.getMnemonic()
+ proc exportRecoverKeys(self: View) {.slot.} =
+ self.delegate.exportRecoverKeys()
proc finishOnboardingFlow(self: View, flowInt: int, dataJson: string): string {.slot.} =
self.delegate.finishOnboardingFlow(flowInt, dataJson)
diff --git a/src/app_service/service/accounts/service.nim b/src/app_service/service/accounts/service.nim
index c068f46ee4a..83acc31ffed 100644
--- a/src/app_service/service/accounts/service.nim
+++ b/src/app_service/service/accounts/service.nim
@@ -8,6 +8,7 @@ import ./dto/login_request
import ./dto/restore_account_request
from ../keycard/service import KeycardEvent, KeyDetails
+from ../keycardV2/dto import KeycardExportedKeysDto, KeyDetailsV2
import ../../../backend/general as status_general
import ../../../backend/core as status_core
import ../../../backend/privacy as status_privacy
@@ -240,6 +241,7 @@ QtObject:
self.restoreAccountAndLogin(request)
+ # TODO remove this function when the old keycard service is removed
proc restoreKeycardAccountAndLogin*(self: Service,
keycardData: KeycardEvent,
recoverAccount: bool,
@@ -270,6 +272,35 @@ QtObject:
return self.restoreAccountAndLogin(request)
+ proc restoreKeycardAccountAndLoginV2*(self: Service,
+ keyUid: string,
+ instanceUid: string,
+ keycardKeys: KeycardExportedKeysDto,
+ recoverAccount: bool,
+ ): string =
+
+ let keycard = KeycardData(
+ keyUid: keyUid,
+ address: keycardKeys.masterKey.address,
+ whisperPrivateKey: keycardKeys.whisperKey.privateKey,
+ whisperPublicKey: keycardKeys.whisperKey.publicKey,
+ whisperAddress: keycardKeys.whisperKey.address,
+ walletPublicKey: keycardKeys.walletKey.publicKey,
+ walletAddress: keycardKeys.walletKey.address,
+ walletRootAddress: keycardKeys.walletRootKey.address,
+ eip1581Address: keycardKeys.eip1581Key.address,
+ encryptionPublicKey: keycardKeys.encryptionKey.publicKey,
+ )
+
+ var request = RestoreAccountRequest(
+ keycard: keycard,
+ fetchBackup: recoverAccount,
+ createAccountRequest: defaultCreateAccountRequest(),
+ )
+ request.createAccountRequest.keycardInstanceUID = instanceUid
+
+ return self.restoreAccountAndLogin(request)
+
proc restoreAccountAndLogin(self: Service, request: RestoreAccountRequest): string =
try:
let response = status_account.restoreAccountAndLogin(request)
diff --git a/src/app_service/service/keycard/internal.nim b/src/app_service/service/keycard/internal.nim
index ba786bbe9a2..a7d14c763c7 100644
--- a/src/app_service/service/keycard/internal.nim
+++ b/src/app_service/service/keycard/internal.nim
@@ -95,7 +95,7 @@ proc toTransactionSignature(jsonObj: JsonNode): TransactionSignature =
if v == 1:
result.v = "01"
-proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
+proc toKeycardEvent*(jsonObj: JsonNode): KeycardEvent =
discard jsonObj.getProp(ResponseParamErrorKey, result.error)
discard jsonObj.getProp(ResponseParamInstanceUID, result.instanceUID)
discard jsonObj.getProp(ResponseParamFreeSlots, result.freePairingSlots)
diff --git a/src/app_service/service/keycard/service.nim b/src/app_service/service/keycard/service.nim
index 80202dfa534..8b89c22e27c 100644
--- a/src/app_service/service/keycard/service.nim
+++ b/src/app_service/service/keycard/service.nim
@@ -127,7 +127,7 @@ QtObject:
self.lastReceivedKeycardData = (flowType: flowType, flowEvent: flowEvent)
self.events.emit(SIGNAL_KEYCARD_RESPONSE, KeycardLibArgs(flowType: flowType, flowEvent: flowEvent))
- proc receiveKeycardSignal(self: Service, signal: string) {.slot.} =
+ proc receiveKeycardSignal*(self: Service, signal: string) {.slot.} =
self.busy = false
self.processSignal(signal)
if self.waitingFlows.len > 0:
diff --git a/src/app_service/service/keycardV2/async_tasks.nim b/src/app_service/service/keycardV2/async_tasks.nim
index de621bbeb79..2f184224b40 100644
--- a/src/app_service/service/keycardV2/async_tasks.nim
+++ b/src/app_service/service/keycardV2/async_tasks.nim
@@ -1,13 +1,68 @@
type
- AsyncSetPinTaskArg = ref object of QObjectTaskArg
+ AsyncInitializeTaskArg = ref object of QObjectTaskArg
pin: string
+ puk: string
+ rpcCounter: int
-proc asyncSetPinTask(argEncoded: string) {.gcsafe, nimcall.} =
- let arg = decode[AsyncSetPinTaskArg](argEncoded)
+proc asyncInitializeTask(argEncoded: string) {.gcsafe, nimcall.} =
+ let arg = decode[AsyncInitializeTaskArg](argEncoded)
try:
- # TODO Call function from keycard_go
- echo "Set pin ", arg.pin
+ let response = callRPC(arg.rpcCounter, "Initialize", %*{"pin": arg.pin, "puk": arg.puk})
arg.finish(%*{
+ "response": response,
+ "error": ""
+ })
+ except Exception as e:
+ arg.finish(%* {
+ "error": e.msg,
+ })
+
+type
+ AsyncAuthorizeArg = ref object of QObjectTaskArg
+ pin: string
+ rpcCounter: int
+
+proc asyncAuthorizeTask(argEncoded: string) {.gcsafe, nimcall.} =
+ let arg = decode[AsyncInitializeTaskArg](argEncoded)
+ try:
+ let response = callRPC(arg.rpcCounter, "Authorize", %*{"pin": arg.pin})
+ arg.finish(%*{
+ "response": response,
+ "error": ""
+ })
+ except Exception as e:
+ arg.finish(%* {
+ "error": e.msg,
+ })
+
+type
+ AsyncLoadMnemonicArg = ref object of QObjectTaskArg
+ mnemonic: string
+ rpcCounter: int
+
+proc asyncLoadMnemonicTask(argEncoded: string) {.gcsafe, nimcall.} =
+ let arg = decode[AsyncLoadMnemonicArg](argEncoded)
+ try:
+ let response = callRPC(arg.rpcCounter, "LoadMnemonic", %*{"mnemonic": arg.mnemonic})
+ arg.finish(%*{
+ "response": response,
+ "error": ""
+ })
+ except Exception as e:
+ arg.finish(%* {
+ "error": e.msg,
+ })
+
+type
+ AsyncExportRecoverKeysArg = ref object of QObjectTaskArg
+ rpcCounter: int
+
+proc asyncExportRecoverKeysTask(argEncoded: string) {.gcsafe, nimcall.} =
+ let arg = decode[AsyncExportRecoverKeysArg](argEncoded)
+ try:
+ let response = callRPC(arg.rpcCounter, "ExportRecoverKeys")
+ arg.finish(%*{
+ "response": response,
"error": ""
})
except Exception as e:
diff --git a/src/app_service/service/keycardV2/dto.nim b/src/app_service/service/keycardV2/dto.nim
new file mode 100644
index 00000000000..b4672d2a353
--- /dev/null
+++ b/src/app_service/service/keycardV2/dto.nim
@@ -0,0 +1,169 @@
+import json, strutils
+include ../../common/json_utils
+
+
+
+type StateString* = enum
+ UnknownReaderState = "unknown"
+ NoPCSC = "no-pcsc"
+ InternalError = "internal-error"
+ WaitingForReader = "waiting-for-reader"
+ WaitingForCard = "waiting-for-card"
+ ConnectingCard = "connecting-card"
+ ConnectionError = "connection-error"
+ NotKeycard = "not-keycard"
+ PairingError = "pairing-error"
+ EmptyKeycard = "empty-keycard"
+ NoAvailablePairingSlots = "no-available-pairing-slots"
+ BlockedPIN = "blocked-pin" # PIN remaining attempts == 0
+ BlockedPUK = "blocked-puk" # PUK remaining attempts == 0
+ FactoryResetting = "factory-resetting"
+ Ready = "ready"
+ Authorized = "authorized"
+
+type KeycardState* = enum
+ UnknownReaderState = -1,
+ NoPCSCService,
+ PluginReader,
+ InsertKeycard,
+ ReadingKeycard,
+ WrongKeycard,
+ NotKeycard,
+ MaxPairingSlotsReached,
+ Locked,
+ NotEmpty,
+ Empty,
+ Authorized
+
+type KeycardInfoDto* = object
+ initialized*: bool
+ instanceUID*: string
+ version*: string
+ availableSlots*: int
+ keyUID*: string
+
+type KeycardStatusDto* = object
+ remainingAttemptsPIN*: int
+ remainingAttemptsPUK*: int
+ keyInitialized*: bool
+ path*: string
+
+type KeycardEventDto* = object
+ state*: KeycardState
+ keycardInfo*: KeycardInfoDto
+ keycardStatus*: KeycardStatusDto
+
+type
+ KeyDetailsV2* = object
+ address*: string
+ publicKey*: string
+ privateKey*: string
+
+type KeycardExportedKeysDto* = object
+ eip1581Key*: KeyDetailsV2
+ encryptionKey*: KeyDetailsV2
+ masterKey*: KeyDetailsV2
+ walletKey*: KeyDetailsV2
+ walletRootKey*: KeyDetailsV2
+ whisperKey*: KeyDetailsV2
+ masterKeyAddress*: string
+
+proc fromStringStateToInt*(state: StateString): KeycardState =
+ case state
+ of StateString.UnknownReaderState:
+ result = KeycardState.UnknownReaderState
+ of StateString.NoPCSC:
+ result = KeycardState.NoPCSCService
+ of StateString.InternalError:
+ result = KeycardState.UnknownReaderState
+ of StateString.WaitingForReader:
+ result = KeycardState.PluginReader
+ of StateString.WaitingForCard:
+ result = KeycardState.InsertKeycard
+ of StateString.ConnectingCard:
+ result = KeycardState.ReadingKeycard
+ of StateString.ConnectionError:
+ result = KeycardState.NoPCSCService # TODO Change the UI states to have a connection error state
+ of StateString.NotKeycard:
+ result = KeycardState.NotKeycard
+ of StateString.PairingError:
+ result = KeycardState.NoPCSCService # TODO Change the UI states to have a pairing error state
+ of StateString.EmptyKeycard:
+ result = KeycardState.Empty
+ of StateString.NoAvailablePairingSlots:
+ result = KeycardState.MaxPairingSlotsReached
+ of StateString.BlockedPIN:
+ result = KeycardState.Locked
+ of StateString.BlockedPUK:
+ result = KeycardState.Locked # TODO do we need a new state for the PUK lock or we don't use PUK anymore?
+ of StateString.FactoryResetting:
+ result = KeycardState.NotEmpty # TODO create a new UI state
+ of StateString.Ready:
+ result = KeycardState.NotEmpty
+ of StateString.Authorized:
+ result = KeycardState.Authorized
+ else:
+ result = KeycardState.UnknownReaderState
+
+proc toKeycardInfoDto*(jsonObj: JsonNode): KeycardInfoDto =
+ result = KeycardInfoDto()
+ discard jsonObj.getProp("initialized", result.initialized)
+ discard jsonObj.getProp("instanceUID", result.instanceUID)
+ discard jsonObj.getProp("version", result.version)
+ discard jsonObj.getProp("availableSlots", result.availableSlots)
+ if jsonObj.getProp("keyUID", result.keyUID) and
+ result.keyUID.len > 0 and
+ not result.keyUID.startsWith("0x"):
+ result.keyUID = "0x" & result.keyUID
+
+proc toKeycardStatusDto*(jsonObj: JsonNode): KeycardStatusDto =
+ result = KeycardStatusDto()
+ discard jsonObj.getProp("remainingAttemptsPIN", result.remainingAttemptsPIN)
+ discard jsonObj.getProp("remainingAttemptsPUK", result.remainingAttemptsPUK)
+ discard jsonObj.getProp("keyInitialized", result.keyInitialized)
+ discard jsonObj.getProp("path", result.path)
+
+proc toKeycardEventDto*(jsonObj: JsonNode): KeycardEventDto =
+ result = KeycardEventDto()
+
+ try:
+ result.state = parseEnum[StateString](jsonObj["state"].getStr).fromStringStateToInt
+ except:
+ result.state = KeycardState.UnknownReaderState
+
+ var keycardInfoObj: JsonNode
+ if jsonObj.getProp("keycardInfo", keycardInfoObj):
+ result.keycardInfo = keycardInfoObj.toKeycardInfoDto
+
+ var keycardStatusObj: JsonNode
+ if jsonObj.getProp("keycardStatus", keycardStatusObj):
+ result.keycardStatus = keycardStatusObj.toKeycardStatusDto
+
+proc toKeyDetails(jsonObj: JsonNode): KeyDetailsV2 =
+ discard jsonObj.getProp("address", result.address)
+ discard jsonObj.getProp("privateKey", result.privateKey)
+ if jsonObj.getProp("publicKey", result.publicKey):
+ result.publicKey = "0x" & result.publicKey
+
+proc toKeycardExportedKeysDto*(jsonObj: JsonNode): KeycardExportedKeysDto =
+ result = KeycardExportedKeysDto()
+
+ var obj: JsonNode
+
+ if jsonObj.getProp("eip1581", obj):
+ result.eip1581Key = toKeyDetails(obj)
+
+ if jsonObj.getProp("encryptionPrivateKey", obj):
+ result.encryptionKey = toKeyDetails(obj)
+
+ if jsonObj.getProp("masterKey", obj):
+ result.masterKey = toKeyDetails(obj)
+
+ if jsonObj.getProp("walletKey", obj):
+ result.walletKey = toKeyDetails(obj)
+
+ if jsonObj.getProp("walletRootKey", obj):
+ result.walletRootKey = toKeyDetails(obj)
+
+ if jsonObj.getProp("whisperPrivateKey", obj):
+ result.whisperKey = toKeyDetails(obj)
diff --git a/src/app_service/service/keycardV2/service.nim b/src/app_service/service/keycardV2/service.nim
index 13051e016dc..48682041f67 100644
--- a/src/app_service/service/keycardV2/service.nim
+++ b/src/app_service/service/keycardV2/service.nim
@@ -1,67 +1,239 @@
-import NimQml, json, chronicles, strutils
-# import keycard_go
+import NimQml, json, chronicles, strutils, random, json_serialization
+import keycard_go
import app/global/global_singleton
import app/core/eventemitter
import app/core/tasks/[qt, threadpool]
+import app_service/service/keycard/service as old_keycard_service
+import ../../../backend/response_type
+import ../../../constants as status_const
+import ./dto
+
+proc callRPC*(rpcCounter: int, methodName: string, params: JsonNode = %*{}): string =
+ var request = %*{
+ "id": rpcCounter,
+ "method": "keycard." & methodName,
+ "params": %*[ params ],
+ }
+ var response = keycard_go.keycardCallRPC($request)
+ return response
+
include ../../common/mnemonics
include async_tasks
logScope:
topics = "keycardV2-service"
+const SupportedMnemonicLength12* = 12
+const PUKLengthForStatusApp* = 12
+
+const SIGNAL_KEYCARD_STATE_UPDATED* = "keycardStateUpdated"
+const SIGNAL_KEYCARD_SET_PIN_FAILURE* = "keycardSetPinFailure"
+const SIGNAL_KEYCARD_AUTHORIZE_FAILURE* = "keycardAuthorizeFailure"
+const SIGNAL_KEYCARD_LOAD_MNEMONIC_FAILURE* = "keycardLoadMnemonicFailure"
+const SIGNAL_KEYCARD_LOAD_MNEMONIC_SUCCESS* = "keycardLoadMnemonicSuccess"
+const SIGNAL_KEYCARD_EXPORT_KEYS_FAILURE* = "keycardExportKeysFailure"
+const SIGNAL_KEYCARD_EXPORT_KEYS_SUCCESS* = "keycardExportKeysSuccess"
+
+type
+ KeycardEventArg* = ref object of Args
+ keycardEvent*: KeycardEventDto
+
+ KeycardErrorArg* = ref object of Args
+ error*: string
+
+ KeycardKeyUIDArg* = ref object of Args
+ keyUID*: string
+
+ KeycardExportedKeysArg* = ref object of Args
+ exportedKeys*: KeycardExportedKeysDto
+
QtObject:
type Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
+ rpcCounter: int
+ oldKeyCardService: old_keycard_service.Service
proc delete*(self: Service) =
self.QObject.delete
- proc newService*(events: EventEmitter, threadpool: ThreadPool): Service =
+ proc newService*(events: EventEmitter, threadpool: ThreadPool, oldKeyCardService: old_keycard_service.Service): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
+ result.rpcCounter = 0
+ result.oldKeyCardService = oldKeyCardService
+
+ proc initializeRPC(self: Service)
+ proc start*(self: Service, storageDir: string)
proc init*(self: Service) =
+ debug "KeycardServiceV2 init"
+ self.initializeRPC()
+ self.start(status_const.KEYCARDPAIRINGDATAFILE)
discard
- proc receiveKeycardSignal(self: Service, signal: string) {.slot.} =
- var jsonSignal: JsonNode
- try:
- jsonSignal = signal.parseJson
- except:
- error "Invalid signal received", data = signal
- return
+ proc initializeRPC(self: Service) {.slot.} =
+ var response = keycard_go.keycardInitializeRPC()
- debug "keycard_signal", response=signal
+ proc callRPC(self: Service, methodName: string, params: JsonNode = %*{}): string =
+ self.rpcCounter += 1
+ return callRPC(self.rpcCounter, methodName, params)
- proc buildSeedPhrasesFromIndexes*(self: Service, seedPhraseIndexes: seq[int]): seq[string] =
+ proc start*(self: Service, storageDir: string) =
+ discard self.callRPC("Start", %*{"storageFilePath": storageDir})
+
+ proc stop*(self: Service) =
+ discard self.callRPC("Stop")
+
+ proc buildSeedPhrasesFromIndexes*(seedPhraseIndexes: JsonNode): seq[string] =
var seedPhrase: seq[string]
- for ind in seedPhraseIndexes:
- seedPhrase.add(englishWords[ind])
+ for ind in seedPhraseIndexes.items:
+ seedPhrase.add(englishWords[ind.getInt])
return seedPhrase
- proc getMnemonicIndexes*(self: Service): seq[int] =
- # TODO call lib to get mnemonic indexes
- echo "Get mnemonic indexes"
- return @[]
+ proc generateMnemonic*(self: Service, length: int): string =
+ try:
+ let response = self.callRPC("GenerateMnemonic", %*{"length": length})
+ let rpcResponseObj = response.parseJson
+ if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
+ let error = Json.decode(rpcResponseObj["error"].getStr, RpcError)
+ raise newException(RpcException, "Error loading mnemonic: " & error.message)
+
+ let words = buildSeedPhrasesFromIndexes(rpcResponseObj["result"]["indexes"])
+ var jArray = newJArray()
+ for item in words:
+ jArray.add(%item)
+ return $jArray
+ except Exception as e:
+ error "error generating mnemonic", err=e.msg
- proc setPin*(self: Service, pin: string) =
- let arg = AsyncSetPinTaskArg(
- tptr: asyncSetPinTask,
+ proc loadMnemonic*(self: Service, mnemonic: string) =
+ self.rpcCounter += 1
+ let arg = AsyncLoadMnemonicArg(
+ tptr: asyncLoadMnemonicTask,
vptr: cast[uint](self.vptr),
- slot: "onAsyncSetPinResponse",
+ slot: "onAsyncLoadMnemonicResponse",
+ mnemonic: mnemonic,
+ rpcCounter: self.rpcCounter,
+ )
+ self.threadpool.start(arg)
+
+ proc onAsyncLoadMnemonicResponse(self: Service, response: string) {.slot.} =
+ try:
+ let responseObj = response.parseJson
+ if responseObj{"error"}.kind != JNull and responseObj{"error"}.getStr != "":
+ raise newException(CatchableError, responseObj{"error"}.getStr)
+
+ let rpcResponseObj = responseObj["response"].getStr().parseJson()
+
+ if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
+ let error = Json.decode(rpcResponseObj["error"].getStr, RpcError)
+ raise newException(RpcException, "Error loading mnemonic: " & error.message)
+
+ self.events.emit(SIGNAL_KEYCARD_LOAD_MNEMONIC_SUCCESS, KeycardKeyUIDArg(keyUID: rpcResponseObj["result"]["keyUID"].getStr))
+ except Exception as e:
+ error "error loading mnemonic", err=e.msg
+ self.events.emit(SIGNAL_KEYCARD_LOAD_MNEMONIC_FAILURE, KeycardErrorArg(error: e.msg))
+
+ proc asyncAuthorize*(self: Service, pin: string) =
+ self.rpcCounter += 1
+ let arg = AsyncAuthorizeArg(
+ tptr: asyncAuthorizeTask,
+ vptr: cast[uint](self.vptr),
+ slot: "onAsyncAuthorizeResponse",
pin: pin,
+ rpcCounter: self.rpcCounter,
)
self.threadpool.start(arg)
- proc onAsyncSetPinResponse*(self: Service, response: string) {.slot.} =
+ proc onAsyncAuthorizeResponse*(self: Service, response: string) {.slot.} =
try:
- let rpcResponseObj = response.parseJson
- echo "Set the pin ", response
+ let responseObj = response.parseJson
+
+ if responseObj{"error"}.kind != JNull and responseObj{"error"}.getStr != "":
+ raise newException(CatchableError, responseObj{"error"}.getStr)
- if (rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != ""):
- raise newException(CatchableError, rpcResponseObj{"error"}.getStr)
+ let rpcResponseObj = responseObj["response"].getStr().parseJson()
+ if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
+ let error = Json.decode(rpcResponseObj["error"].getStr, RpcError)
+ raise newException(RpcException, "Error authorizing: " & error.message)
except Exception as e:
- error "error set pin: ", msg = e.msg
\ No newline at end of file
+ error "error set pin: ", msg = e.msg
+ self.events.emit(SIGNAL_KEYCARD_AUTHORIZE_FAILURE, KeycardErrorArg(error: e.msg))
+
+ proc receiveKeycardSignalV2(self: Service, signal: string) {.slot.} =
+ try:
+ # Since only one service can register to signals, we pass the signal to the old service too
+ self.oldKeyCardService.receiveKeycardSignal(signal)
+ var jsonSignal = signal.parseJson
+
+ if jsonSignal["type"].getStr == "status-changed":
+ let keycardEvent = jsonSignal["event"].toKeycardEventDto()
+
+ self.events.emit(SIGNAL_KEYCARD_STATE_UPDATED, KeycardEventArg(keycardEvent: keycardEvent))
+ except Exception as e:
+ error "error receiving a keycard signal", err=e.msg, data = signal
+
+ proc initialize*(self: Service, pin: string, puk: string) =
+ self.rpcCounter += 1
+ let arg = AsyncInitializeTaskArg(
+ tptr: asyncInitializeTask,
+ vptr: cast[uint](self.vptr),
+ slot: "onAsyncInitializeResponse",
+ pin: pin,
+ puk: puk,
+ rpcCounter: self.rpcCounter,
+ )
+ self.threadpool.start(arg)
+
+ proc onAsyncInitializeResponse*(self: Service, response: string) {.slot.} =
+ try:
+ let responseObj = response.parseJson
+
+ if responseObj{"error"}.kind != JNull and responseObj{"error"}.getStr != "":
+ raise newException(CatchableError, responseObj{"error"}.getStr)
+
+ let rpcResponseObj = responseObj["response"].getStr().parseJson()
+ if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
+ let error = Json.decode(rpcResponseObj["error"].getStr, RpcError)
+ raise newException(RpcException, "Error authorizing: " & error.message)
+ except Exception as e:
+ error "error set pin: ", msg = e.msg
+ self.events.emit(SIGNAL_KEYCARD_SET_PIN_FAILURE, KeycardErrorArg(error: e.msg))
+
+ proc generateRandomPUK*(self: Service): string =
+ randomize()
+ for i in 0 ..< PUKLengthForStatusApp:
+ result = result & $rand(0 .. 9)
+
+ proc asyncExportRecoverKeys*(self: Service) =
+ self.rpcCounter += 1
+ let arg = AsyncExportRecoverKeysArg(
+ tptr: asyncExportRecoverKeysTask,
+ vptr: cast[uint](self.vptr),
+ slot: "onAsyncExportRecoverKeys",
+ rpcCounter: self.rpcCounter,
+ )
+ self.threadpool.start(arg)
+
+ proc onAsyncExportRecoverKeys*(self: Service, response: string) {.slot.} =
+ try:
+ let responseObj = response.parseJson
+
+ if responseObj{"error"}.kind != JNull and responseObj{"error"}.getStr != "":
+ raise newException(CatchableError, responseObj{"error"}.getStr)
+
+ let rpcResponseObj = responseObj["response"].getStr().parseJson()
+ if rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != "":
+ let error = Json.decode(rpcResponseObj["error"].getStr, RpcError)
+ raise newException(RpcException, "Error authorizing: " & error.message)
+
+ let keys = rpcResponseObj["result"]["keys"].toKeycardExportedKeysDto()
+ self.events.emit(SIGNAL_KEYCARD_EXPORT_KEYS_SUCCESS, KeycardExportedKeysArg(exportedKeys: keys))
+ except Exception as e:
+ error "error exporting recover keys", msg = e.msg
+ self.events.emit(SIGNAL_KEYCARD_EXPORT_KEYS_FAILURE, KeycardErrorArg(error: e.msg))
+
+
\ No newline at end of file
diff --git a/src/app_service/service/transaction/dto.nim b/src/app_service/service/transaction/dto.nim
index a62622e0186..8acce560514 100644
--- a/src/app_service/service/transaction/dto.nim
+++ b/src/app_service/service/transaction/dto.nim
@@ -25,6 +25,7 @@ type
type
PendingTransactionTypeDto* {.pure.} = enum
+ Unknown = "Unknown"
RegisterENS = "RegisterENS",
SetPubKey = "SetPubKey",
ReleaseENS = "ReleaseENS",
@@ -72,15 +73,15 @@ type
symbol*: string
proc getTotalFees(tip: string, baseFee: string, gasUsed: string, maxFee: string): string =
- var maxFees = stint.fromHex(Uint256, maxFee)
- var totalGasUsed = stint.fromHex(Uint256, tip) + stint.fromHex(Uint256, baseFee)
- if totalGasUsed > maxFees:
- totalGasUsed = maxFees
- var totalGasUsedInHex = (totalGasUsed * stint.fromHex(Uint256, gasUsed)).toHex
- return totalGasUsedInHex
+ var maxFees = stint.fromHex(Uint256, maxFee)
+ var totalGasUsed = stint.fromHex(Uint256, tip) + stint.fromHex(Uint256, baseFee)
+ if totalGasUsed > maxFees:
+ totalGasUsed = maxFees
+ var totalGasUsedInHex = (totalGasUsed * stint.fromHex(Uint256, gasUsed)).toHex
+ return totalGasUsedInHex
proc getMaxTotalFees(maxFee: string, gasLimit: string): string =
- return (stint.fromHex(Uint256, maxFee) * stint.fromHex(Uint256, gasLimit)).toHex
+ return (stint.fromHex(Uint256, maxFee) * stint.fromHex(Uint256, gasLimit)).toHex
proc toTransactionDto*(jsonObj: JsonNode): TransactionDto =
result = TransactionDto()
diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim
index ec169758ef4..0b1be1cea4b 100644
--- a/src/app_service/service/transaction/service.nim
+++ b/src/app_service/service/transaction/service.nim
@@ -268,7 +268,13 @@ QtObject:
toTokenKey: watchTxResult["toTokenKey"].getStr,
toAmount: watchTxResult["toAmount"].getStr,
)
- self.events.emit(parseEnum[PendingTransactionTypeDto](watchTxResult["trxType"].getStr).event, ev)
+ var transactionType = PendingTransactionTypeDto.Unknown
+ try:
+ transactionType = parseEnum[PendingTransactionTypeDto](watchTxResult["trxType"].getStr)
+ except:
+ discard
+
+ self.events.emit(transactionType.event, ev)
transactions.checkRecentHistory(@[chainId], @[address])
proc watchTransaction*(
diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim
index 242ffafe900..9b6728c9d9c 100644
--- a/src/nim_status_client.nim
+++ b/src/nim_status_client.nim
@@ -6,11 +6,10 @@ import app/core/main
import constants as main_constants
import statusq_bridge
-import app/global/global_singleton
+import app/global/[global_singleton, feature_flags]
import app/global/local_app_settings
import app/boot/app_controller
-
when defined(macosx) and defined(arm64):
import posix
@@ -77,9 +76,15 @@ proc setupRemoteSignalsHandling() =
signal_handler(signalsManagerQObjPointer, p0, "receiveSignal")
status_go.setSignalEventCallback(callbackStatusGo)
- var callbackKeycardGo: keycard_go.KeycardSignalCallback = proc(p0: cstring) {.cdecl.} =
- if keycardServiceQObjPointer != nil:
- signal_handler(keycardServiceQObjPointer, p0, "receiveKeycardSignal")
+ var callbackKeycardGo: keycard_go.KeycardSignalCallback
+ if singletonInstance.featureFlags().getOnboardingV2Enabled():
+ callbackKeycardGo = proc(p0: cstring) {.cdecl.} =
+ if keycardServiceQObjPointer != nil:
+ signal_handler(keycardServiceQObjPointer, p0, "receiveKeycardSignalV2")
+ else:
+ callbackKeycardGo = proc(p0: cstring) {.cdecl.} =
+ if keycardServiceQObjPointer != nil:
+ signal_handler(keycardServiceQObjPointer, p0, "receiveKeycardSignal")
keycard_go.setSignalEventCallback(callbackKeycardGo)
proc ensureDirectories*(dataDir, tmpDir, logDir: string) =
@@ -240,7 +245,10 @@ proc mainProc() =
# We need these global variables in order to be able to access the application
# from the non-closure callback passed to `statusgo_backend.setSignalEventCallback`
signalsManagerQObjPointer = cast[pointer](statusFoundation.signalsManager.vptr)
- keycardServiceQObjPointer = cast[pointer](appController.keycardService.vptr)
+ if singletonInstance.featureFlags().getOnboardingV2Enabled():
+ keycardServiceQObjPointer = cast[pointer](appController.keycardServiceV2.vptr)
+ else:
+ keycardServiceQObjPointer = cast[pointer](appController.keycardService.vptr)
setupRemoteSignalsHandling()
info "app info", version=APP_VERSION, commit=GIT_COMMIT, currentDateTime=now()
diff --git a/storybook/pages/KeycardAddKeyPairPagePage.qml b/storybook/pages/KeycardAddKeyPairPagePage.qml
index d9c6b898716..8e9c05eef0c 100644
--- a/storybook/pages/KeycardAddKeyPairPagePage.qml
+++ b/storybook/pages/KeycardAddKeyPairPagePage.qml
@@ -17,10 +17,10 @@ Item {
onKeypairAddTryAgainRequested: {
console.warn("!!! onKeypairAddTryAgainRequested")
- ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.AddKeyPairState.InProgress)
+ ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.ProgressState.InProgress)
Backpressure.debounce(root, 2000, function() {
console.warn("!!! SIMULATION: SUCCESS")
- ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.AddKeyPairState.Success)
+ ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.ProgressState.Success)
})()
}
onKeypairAddContinueRequested: console.warn("!!! onKeypairAddContinueRequested")
@@ -35,7 +35,7 @@ Item {
width: 350
textRole: "name"
valueRole: "value"
- model: Onboarding.getModelFromEnum("AddKeyPairState")
+ model: Onboarding.getModelFromEnum("ProgressState")
}
}
diff --git a/storybook/pages/KeycardEnterPinPagePage.qml b/storybook/pages/KeycardEnterPinPagePage.qml
index dd62ce98f9f..ad799973413 100644
--- a/storybook/pages/KeycardEnterPinPagePage.qml
+++ b/storybook/pages/KeycardEnterPinPagePage.qml
@@ -3,6 +3,7 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import AppLayouts.Onboarding2.pages 1.0
+import AppLayouts.Onboarding.enums 1.0
Item {
id: root
@@ -12,15 +13,12 @@ Item {
KeycardEnterPinPage {
id: page
anchors.fill: parent
- tryToSetPinFunction: (pin) => {
- const valid = pin === root.existingPin
- if (!valid)
- remainingAttempts--
- return valid
- }
+
remainingAttempts: 3
unblockWithPukAvailable: ctrlUnblockWithPUK.checked
- onKeycardPinEntered: (pin) => {
+ authorizationState: Onboarding.ProgressState.Idle
+ restoreKeysExportState: Onboarding.ProgressState.Idle
+ onAuthorizationRequested: (pin) => {
console.warn("!!! PIN:", pin)
console.warn("!!! RESETTING FLOW")
state = "entering"
diff --git a/storybook/pages/OnboardingLayoutPage.qml b/storybook/pages/OnboardingLayoutPage.qml
index f4273fd39fe..2ea1a4673c7 100644
--- a/storybook/pages/OnboardingLayoutPage.qml
+++ b/storybook/pages/OnboardingLayoutPage.qml
@@ -60,8 +60,11 @@ SplitView {
id: store
property int keycardState: Onboarding.KeycardState.NoPCSCService
- property int addKeyPairState: Onboarding.AddKeyPairState.InProgress
- property int syncState: Onboarding.SyncState.InProgress
+ property int addKeyPairState: Onboarding.ProgressState.Idle
+ property int pinSettingState: Onboarding.ProgressState.Idle
+ property int authorizationState: Onboarding.ProgressState.Idle
+ property int restoreKeysExportState: Onboarding.ProgressState.Idle
+ property int syncState: Onboarding.ProgressState.Idle
property int keycardRemainingPinAttempts: 2
property int keycardRemainingPukAttempts: 3
@@ -91,9 +94,16 @@ SplitView {
return valid
}
- function startKeypairTransfer() { // -> void
- logs.logEvent("OnboardingStore.startKeypairTransfer")
- addKeyPairState = Onboarding.AddKeyPairState.InProgress
+ function authorize(pin: string) {
+ logs.logEvent("OnboardingStore.authorize", ["pin"], arguments)
+ }
+
+ function loadMnemonic(mnemonic) { // -> void
+ logs.logEvent("OnboardingStore.loadMnemonic", ["mnemonic"], arguments)
+ }
+
+ function exportRecoverKeys() { // -> void
+ logs.logEvent("OnboardingStore.exportRecoverKeys")
}
// password
@@ -110,17 +120,13 @@ SplitView {
function getMnemonic() { // -> string
logs.logEvent("OnboardingStore.getMnemonic()")
- return mockDriver.seedWords.join(" ")
+ return JSON.stringify(mockDriver.seedWords)
}
function mnemonicWasShown() { // -> void
logs.logEvent("OnboardingStore.mnemonicWasShown()")
}
- function removeMnemonic() { // -> void
- logs.logEvent("OnboardingStore.removeMnemonic()")
- }
-
function validateLocalPairingConnectionString(connectionString: string) { // -> bool
logs.logEvent("OnboardingStore.validateLocalPairingConnectionString", ["connectionString"], arguments)
return !Number.isNaN(parseInt(connectionString))
@@ -477,7 +483,7 @@ SplitView {
}
Repeater {
- model: Onboarding.getModelFromEnum("AddKeyPairState")
+ model: Onboarding.getModelFromEnum("ProgressState")
RoundButton {
text: modelData.name
@@ -498,7 +504,6 @@ SplitView {
}
Flow {
- Layout.fillWidth: true
spacing: 2
ButtonGroup {
@@ -506,7 +511,7 @@ SplitView {
}
Repeater {
- model: Onboarding.getModelFromEnum("SyncState")
+ model: Onboarding.getModelFromEnum("ProgressState")
RoundButton {
text: modelData.name
@@ -519,6 +524,90 @@ SplitView {
}
}
}
+
+ ToolSeparator {}
+
+ Label {
+ text: "Pin Setting state:"
+ }
+
+ Flow {
+ spacing: 2
+
+ ButtonGroup {
+ id: pinSettingStateButtonGroup
+ }
+
+ Repeater {
+ model: Onboarding.getModelFromEnum("ProgressState")
+
+ RoundButton {
+ text: modelData.name
+ checkable: true
+ checked: store.pinSettingState === modelData.value
+
+ ButtonGroup.group: pinSettingStateButtonGroup
+
+ onClicked: store.pinSettingState = modelData.value
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ Label {
+ text: "Authorization state:"
+ }
+
+ Flow {
+ spacing: 2
+
+ ButtonGroup {
+ id: authorizationStateButtonGroup
+ }
+
+ Repeater {
+ model: Onboarding.getModelFromEnum("ProgressState")
+
+ RoundButton {
+ text: modelData.name
+ checkable: true
+ checked: store.authorizationState === modelData.value
+
+ ButtonGroup.group: authorizationStateButtonGroup
+
+ onClicked: store.authorizationState = modelData.value
+ }
+ }
+ }
+
+ ToolSeparator {}
+
+ Label {
+ text: "Restore Keys Export state:"
+ }
+
+ Flow {
+ spacing: 2
+
+ ButtonGroup {
+ id: restoreKeysExportStateButtonGroup
+ }
+
+ Repeater {
+ model: Onboarding.getModelFromEnum("ProgressState")
+
+ RoundButton {
+ text: modelData.name
+ checkable: true
+ checked: store.restoreKeysExportState === modelData.value
+
+ ButtonGroup.group: restoreKeysExportStateButtonGroup
+
+ onClicked: store.restoreKeysExportState = modelData.value
+ }
+ }
+ }
}
Item {
diff --git a/storybook/pages/SyncProgressPagePage.qml b/storybook/pages/SyncProgressPagePage.qml
index 53e71e2a3db..805f44030d2 100644
--- a/storybook/pages/SyncProgressPagePage.qml
+++ b/storybook/pages/SyncProgressPagePage.qml
@@ -15,10 +15,10 @@ Item {
syncState: ctrlState.currentValue
onRestartSyncRequested: {
console.warn("!!! RESTART SYNC REQUESTED")
- ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.SyncState.InProgress)
+ ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.ProgressState.InProgress)
Backpressure.debounce(root, 2000, function() {
console.warn("!!! SIMULATION: SUCCESS")
- ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.SyncState.Success)
+ ctrlState.currentIndex = ctrlState.indexOfValue(Onboarding.ProgressState.Success)
})()
}
onLoginToAppRequested: console.warn("!!! LOGIN TO APP REQUESTED")
@@ -32,7 +32,7 @@ Item {
width: 300
textRole: "name"
valueRole: "value"
- model: Onboarding.getModelFromEnum("SyncState")
+ model: Onboarding.getModelFromEnum("ProgressState")
}
}
diff --git a/storybook/qmlTests/tests/tst_OnboardingLayout.qml b/storybook/qmlTests/tests/tst_OnboardingLayout.qml
index aa6b1531800..59b3a9f5b05 100644
--- a/storybook/qmlTests/tests/tst_OnboardingLayout.qml
+++ b/storybook/qmlTests/tests/tst_OnboardingLayout.qml
@@ -24,6 +24,9 @@ Item {
QtObject {
id: mockDriver
property int keycardState // enum Onboarding.KeycardState
+ property int pinSettingState // enum Onboarding.ProgressState
+ property int authorizationState // enum Onboarding.ProgressState
+ property int restoreKeysExportState // enum Onboarding.ProgressState
property bool biometricsAvailable
property string existingPin
@@ -55,6 +58,9 @@ Item {
onboardingStore: OnboardingStore {
readonly property int keycardState: mockDriver.keycardState // enum Onboarding.KeycardState
+ readonly property int pinSettingState: mockDriver.pinSettingState // enum Onboarding.ProgressState
+ readonly property int authorizationState: mockDriver.authorizationState // enum Onboarding.ProgressState
+ readonly property int restoreKeysExportState: mockDriver.restoreKeysExportState // enum Onboarding.ProgressState
property int keycardRemainingPinAttempts: 5
function setPin(pin: string) {
@@ -64,25 +70,30 @@ Item {
return valid
}
- readonly property int addKeyPairState: Onboarding.AddKeyPairState.InProgress // enum Onboarding.AddKeyPairState
- function startKeypairTransfer() {}
+ function authorize(pin: string) {}
+
+ readonly property int addKeyPairState: Onboarding.ProgressState.InProgress // enum Onboarding.ProgressState
// password
function getPasswordStrengthScore(password: string) {
return Math.min(password.length-1, 4)
}
+ function finishOnboardingFlow(flow: int, data: Object) { // -> bool
+ return true
+ }
+
// seedphrase/mnemonic
function validMnemonic(mnemonic: string) {
return mnemonic === mockDriver.mnemonic
}
function getMnemonic() {
- return mockDriver.seedWords.join(" ")
+ return JSON.stringify(mockDriver.seedWords)
}
- function mnemonicWasShown() {}
- function removeMnemonic() {}
+ function loadMnemonic(mnemonic) {}
+ function exportRecoverKeys() {}
- readonly property int syncState: Onboarding.SyncState.InProgress // enum Onboarding.SyncState
+ readonly property int syncState: Onboarding.ProgressState.InProgress // enum Onboarding.ProgressState
function validateLocalPairingConnectionString(connectionString: string) {
return !Number.isNaN(parseInt(connectionString))
}
@@ -153,6 +164,9 @@ Item {
function cleanup() {
mockDriver.keycardState = -1
+ mockDriver.pinSettingState = 0
+ mockDriver.authorizationState = 0
+ mockDriver.restoreKeysExportState = 0
mockDriver.biometricsAvailable = false
mockDriver.existingPin = ""
dynamicSpy.cleanup()
@@ -179,13 +193,13 @@ Item {
// common variant data for all flow related TDD tests
function init_data() {
return [ { tag: "shareUsageData+bioEnabled", shareBtnName: "btnShare", shareResult: true, biometrics: true, bioEnabled: true },
- { tag: "dontShareUsageData+bioEnabled", shareBtnName: "btnDontShare", shareResult: false, biometrics: true, bioEnabled: true },
+ { tag: "dontShareUsageData+bioEnabled", shareBtnName: "btnDontShare", shareResult: false, biometrics: true, bioEnabled: true },
- { tag: "shareUsageData+bioDisabled", shareBtnName: "btnShare", shareResult: true, biometrics: true, bioEnabled: false },
- { tag: "dontShareUsageData+bioDisabled", shareBtnName: "btnDontShare", shareResult: false, biometrics: true, bioEnabled: false },
+ { tag: "shareUsageData+bioDisabled", shareBtnName: "btnShare", shareResult: true, biometrics: true, bioEnabled: false },
+ { tag: "dontShareUsageData+bioDisabled", shareBtnName: "btnDontShare", shareResult: false, biometrics: true, bioEnabled: false },
- { tag: "shareUsageData-bio", shareBtnName: "btnShare", shareResult: true, biometrics: false },
- { tag: "dontShareUsageData-bio", shareBtnName: "btnDontShare", shareResult: false, biometrics: false },
+ { tag: "shareUsageData-bio", shareBtnName: "btnShare", shareResult: true, biometrics: false },
+ { tag: "dontShareUsageData-bio", shareBtnName: "btnDontShare", shareResult: false, biometrics: false },
]
}
@@ -306,6 +320,7 @@ Item {
compare(resultData.seedphrase, "")
}
+
// FLOW: Create Profile -> Use a recovery phrase (create profile with seedphrase)
function test_flow_createProfile_withSeedphrase(data) {
verify(!!controlUnderTest)
@@ -454,6 +469,9 @@ Item {
keyClickSequence(newPin)
tryCompare(dynamicSpy, "count", 1)
compare(dynamicSpy.signalArguments[0][0], newPin)
+ dynamicSpy.setup(page, "keycardAuthorized")
+ mockDriver.authorizationState = Onboarding.ProgressState.Success
+ tryCompare(dynamicSpy, "count", 1)
// PAGE 7: Backup your recovery phrase (intro)
page = getCurrentPage(stack, BackupSeedphraseIntro)
@@ -520,8 +538,8 @@ Item {
// PAGE 12: Adding key pair to Keycard
page = getCurrentPage(stack, KeycardAddKeyPairPage)
- tryCompare(page, "addKeyPairState", Onboarding.AddKeyPairState.InProgress)
- page.addKeyPairState = Onboarding.AddKeyPairState.Success // SIMULATION
+ tryCompare(page, "addKeyPairState", Onboarding.ProgressState.InProgress)
+ page.addKeyPairState = Onboarding.ProgressState.Success // SIMULATION
btnContinue = findChild(page, "btnContinue")
verify(!!btnContinue)
compare(btnContinue.enabled, true)
@@ -546,7 +564,7 @@ Item {
compare(resultData.password, "")
compare(resultData.enableBiometrics, data.biometrics && data.bioEnabled)
compare(resultData.keycardPin, newPin)
- compare(resultData.seedphrase, "")
+ compare(resultData.seedphrase, mockDriver.seedWords.join(","))
}
// FLOW: Create Profile -> Use an empty Keycard -> Use an existing recovery phrase (create profile with keycard + existing seedphrase)
@@ -600,6 +618,10 @@ Item {
keyClickSequence(newPin)
tryCompare(dynamicSpy, "count", 1)
compare(dynamicSpy.signalArguments[0][0], newPin)
+ dynamicSpy.setup(page, "keycardPinSuccessfullySet")
+ mockDriver.pinSettingState = Onboarding.ProgressState.Success
+ tryCompare(dynamicSpy, "count", 1)
+
// PAGE 7: Create profile on empty Keycard using a recovery phrase
page = getCurrentPage(stack, SeedphrasePage)
@@ -613,11 +635,14 @@ Item {
keySequence(StandardKey.Paste)
compare(btnContinue.enabled, true)
mouseClick(btnContinue)
+ dynamicSpy.setup(page, "keycardAuthorized")
+ mockDriver.authorizationState = Onboarding.ProgressState.Success
+ tryCompare(dynamicSpy, "count", 1)
// PAGE 8: Adding key pair to Keycard
page = getCurrentPage(stack, KeycardAddKeyPairPage)
- tryCompare(page, "addKeyPairState", Onboarding.AddKeyPairState.InProgress)
- page.addKeyPairState = Onboarding.AddKeyPairState.Success // SIMULATION
+ tryCompare(page, "addKeyPairState", Onboarding.ProgressState.InProgress)
+ page.addKeyPairState = Onboarding.ProgressState.Success // SIMULATION
const btnContinue2 = findChild(page, "btnContinue")
verify(!!btnContinue2)
compare(btnContinue2.enabled, true)
@@ -801,8 +826,8 @@ Item {
// PAGE 5: Profile sync in progress
page = getCurrentPage(stack, SyncProgressPage)
- tryCompare(page, "syncState", Onboarding.SyncState.InProgress)
- page.syncState = Onboarding.SyncState.Success // SIMULATION
+ tryCompare(page, "syncState", Onboarding.ProgressState.InProgress)
+ page.syncState = Onboarding.ProgressState.Success // SIMULATION
const btnLogin2 = findChild(page, "btnLogin") // TODO test other flows/buttons here as well
verify(!!btnLogin2)
compare(btnLogin2.enabled, true)
@@ -868,11 +893,19 @@ Item {
// PAGE 5: Enter Keycard PIN
page = getCurrentPage(stack, KeycardEnterPinPage)
- dynamicSpy.setup(page, "keycardPinEntered")
+ dynamicSpy.setup(page, "authorizationRequested")
keyClickSequence(mockDriver.existingPin)
tryCompare(dynamicSpy, "count", 1)
compare(dynamicSpy.signalArguments[0][0], mockDriver.existingPin)
+ dynamicSpy.setup(page, "exportKeysRequested")
+ mockDriver.authorizationState = Onboarding.ProgressState.Success
+ tryCompare(dynamicSpy, "count", 1)
+
+ dynamicSpy.setup(page, "exportKeysDone")
+ mockDriver.restoreKeysExportState = Onboarding.ProgressState.Success
+ tryCompare(dynamicSpy, "count", 1)
+
// PAGE 6: Enable Biometrics
if (data.biometrics) {
page = getCurrentPage(stack, EnableBiometricsPage)
@@ -891,7 +924,7 @@ Item {
verify(!!resultData)
compare(resultData.password, "")
compare(resultData.enableBiometrics, data.biometrics && data.bioEnabled)
- compare(resultData.keycardPin, mockDriver.existingPin)
+ compare(resultData.keycardPin, "")
compare(resultData.seedphrase, "")
}
@@ -1206,11 +1239,14 @@ Item {
keyClickSequence(newPin)
tryCompare(dynamicSpy, "count", 1)
compare(dynamicSpy.signalArguments[0][0], newPin)
+ dynamicSpy.setup(page, "keycardAuthorized")
+ mockDriver.authorizationState = Onboarding.ProgressState.Success
+ tryCompare(dynamicSpy, "count", 1)
// PAGE 6: Adding key pair to Keycard
page = getCurrentPage(stack, KeycardAddKeyPairPage)
- tryCompare(page, "addKeyPairState", Onboarding.AddKeyPairState.InProgress)
- page.addKeyPairState = Onboarding.AddKeyPairState.Success // SIMULATION
+ tryCompare(page, "addKeyPairState", Onboarding.ProgressState.InProgress)
+ page.addKeyPairState = Onboarding.ProgressState.Success // SIMULATION
btnContinue = findChild(page, "btnContinue")
verify(!!btnContinue)
diff --git a/ui/StatusQ/src/onboarding/enums.h b/ui/StatusQ/src/onboarding/enums.h
index 0a594e95ad4..964eeb264f8 100644
--- a/ui/StatusQ/src/onboarding/enums.h
+++ b/ui/StatusQ/src/onboarding/enums.h
@@ -38,7 +38,6 @@ class OnboardingEnums: public QObject
CreateProfileWithPassword,
CreateProfileWithSeedphrase,
- CreateProfileWithKeycard,
CreateProfileWithKeycardNewSeedphrase,
CreateProfileWithKeycardExistingSeedphrase,
@@ -68,27 +67,21 @@ class OnboardingEnums: public QObject
BlockedPUK, // PUK remaining attempts == 0
// exit states
NotEmpty,
- Empty
+ Empty,
+ Authorized
};
- enum class AddKeyPairState {
+ enum class ProgressState {
+ Idle,
InProgress,
Success,
Failed
};
- enum class SyncState {
- Idle,
- InProgress,
- Failed,
- Success
- };
-
private:
Q_ENUM(PrimaryFlow)
Q_ENUM(SecondaryFlow)
Q_ENUM(LoginMethod)
Q_ENUM(KeycardState)
- Q_ENUM(AddKeyPairState)
- Q_ENUM(SyncState)
+ Q_ENUM(ProgressState)
};
diff --git a/ui/app/AppLayouts/Onboarding2/KeycardCreateProfileFlow.qml b/ui/app/AppLayouts/Onboarding2/KeycardCreateProfileFlow.qml
index 161e72513ce..c0499da2e2a 100644
--- a/ui/app/AppLayouts/Onboarding2/KeycardCreateProfileFlow.qml
+++ b/ui/app/AppLayouts/Onboarding2/KeycardCreateProfileFlow.qml
@@ -13,26 +13,27 @@ SQUtils.QObject {
required property StackView stackView
required property int keycardState
+ required property int pinSettingState
+ required property int authorizationState
required property int addKeyPairState
required property int keycardPinInfoPageDelay
- required property var seedWords
+ required property var getSeedWords
required property var isSeedPhraseValid
property bool displayKeycardPromoBanner
signal loginWithKeycardRequested
signal keycardFactoryResetRequested
- signal keyPairTransferRequested
+ signal loadMnemonicRequested
signal keycardPinCreated(string pin)
signal seedphraseSubmitted(string seedphrase)
signal keypairAddTryAgainRequested
signal reloadKeycardRequested
signal createProfileWithoutKeycardRequested
+ signal authorizationRequested
- signal mnemonicWasShown()
- signal mnemonicRemovalRequested()
signal finished(bool withNewSeedphrase)
function init() {
@@ -43,6 +44,7 @@ SQUtils.QObject {
id: d
property bool withNewSeedphrase
+ property var seedWords
function initialComponent() {
if (root.keycardState === Onboarding.KeycardState.Empty)
@@ -124,9 +126,17 @@ SQUtils.QObject {
Component {
id: backupSeedRevealPage
BackupSeedphraseReveal {
- seedWords: root.seedWords
+ Component.onCompleted: {
+ try {
+ const seedwords = root.getSeedWords()
+ d.seedWords = JSON.parse(seedwords)
+ root.seedphraseSubmitted(d.seedWords)
+ } catch (e) {
+ console.error('Failed to get seedwords', e)
+ }
+ }
+ seedWords: d.seedWords
- onMnemonicWasShown: root.mnemonicWasShown()
onBackupSeedphraseConfirmed: root.stackView.push(backupSeedVerifyPage)
}
}
@@ -135,9 +145,9 @@ SQUtils.QObject {
id: backupSeedVerifyPage
BackupSeedphraseVerify {
seedWordsToVerify: {
- const randomIndexes = SQUtils.Utils.nSamples(4, root.seedWords.length)
+ const randomIndexes = SQUtils.Utils.nSamples(4, d.seedWords.length)
return randomIndexes.map(i => ({ seedWordNumber: i+1,
- seedWord: root.seedWords[i]
+ seedWord: d.seedWords[i]
}))
}
@@ -150,8 +160,7 @@ SQUtils.QObject {
BackupSeedphraseOutro {
onBackupSeedphraseRemovalConfirmed: {
- root.mnemonicRemovalRequested()
- root.keyPairTransferRequested()
+ root.loadMnemonicRequested()
root.stackView.push(addKeypairPage)
}
}
@@ -161,13 +170,20 @@ SQUtils.QObject {
id: seedphrasePage
SeedphrasePage {
+ id: seedphrasePage
title: qsTr("Create profile on empty Keycard using a recovery phrase")
+ authorizationState: root.authorizationState
isSeedPhraseValid: root.isSeedPhraseValid
onSeedphraseSubmitted: (seedphrase) => {
root.seedphraseSubmitted(seedphrase)
- root.keyPairTransferRequested()
- root.stackView.push(addKeypairPage)
+ root.authorizationRequested()
+ }
+ onKeycardAuthorized: {
+ if (!d.withNewSeedphrase) {
+ root.loadMnemonicRequested()
+ root.stackView.push(addKeypairPage)
+ }
}
}
}
@@ -176,14 +192,26 @@ SQUtils.QObject {
id: keycardCreatePinPage
KeycardCreatePinPage {
+ id: createPinPage
+
+ keycardPinInfoPageDelay: root.keycardPinInfoPageDelay
+ pinSettingState: root.pinSettingState
+ authorizationState: root.authorizationState
onKeycardPinCreated: (pin) => {
- Backpressure.debounce(root, root.keycardPinInfoPageDelay, () => {
- root.keycardPinCreated(pin)
- if (d.withNewSeedphrase)
- root.stackView.push(backupSeedIntroPage)
- else
- root.stackView.push(seedphrasePage)
- })()
+ root.keycardPinCreated(pin)
+ }
+ onKeycardPinSuccessfullySet: {
+ if (d.withNewSeedphrase) {
+ // Need to authorize before getting a seedphrase
+ root.authorizationRequested()
+ } else {
+ root.stackView.push(seedphrasePage)
+ }
+ }
+ onKeycardAuthorized: {
+ if (d.withNewSeedphrase) {
+ root.stackView.push(backupSeedIntroPage)
+ }
}
}
}
diff --git a/ui/app/AppLayouts/Onboarding2/KeycardCreateReplacementFlow.qml b/ui/app/AppLayouts/Onboarding2/KeycardCreateReplacementFlow.qml
index 99109b16a25..a27fd8a4cb9 100644
--- a/ui/app/AppLayouts/Onboarding2/KeycardCreateReplacementFlow.qml
+++ b/ui/app/AppLayouts/Onboarding2/KeycardCreateReplacementFlow.qml
@@ -14,6 +14,8 @@ SQUtils.QObject {
required property int keycardState
required property int addKeyPairState
+ required property int authorizationState
+ required property int pinSettingState
required property int keycardPinInfoPageDelay
required property var isSeedPhraseValid
@@ -22,8 +24,8 @@ SQUtils.QObject {
signal loginWithKeycardRequested
signal keycardFactoryResetRequested
- signal keyPairTransferRequested
signal keycardPinCreated(string pin)
+ signal authorizationRequested
signal seedphraseSubmitted(string seedphrase)
signal keypairAddTryAgainRequested
@@ -90,6 +92,7 @@ SQUtils.QObject {
SeedphrasePage {
title: qsTr("Enter recovery phrase of lost Keycard")
+ authorizationState: root.authorizationState
isSeedPhraseValid: root.isSeedPhraseValid
onSeedphraseSubmitted: (seedphrase) => {
root.seedphraseSubmitted(seedphrase)
@@ -102,12 +105,14 @@ SQUtils.QObject {
id: keycardCreatePinPage
KeycardCreatePinPage {
+ pinSettingState: root.pinSettingState
+ authorizationState: root.authorizationState
onKeycardPinCreated: (pin) => {
- Backpressure.debounce(root, root.keycardPinInfoPageDelay, () => {
root.keycardPinCreated(pin)
- root.keyPairTransferRequested()
- root.stackView.push(addKeypairPage)
- })()
+ root.authorizationRequested()
+ }
+ onKeycardAuthorized: {
+ root.stackView.push(addKeypairPage)
}
}
}
diff --git a/ui/app/AppLayouts/Onboarding2/LoginBySyncingFlow.qml b/ui/app/AppLayouts/Onboarding2/LoginBySyncingFlow.qml
index c8067c1b181..9ffbf17b133 100644
--- a/ui/app/AppLayouts/Onboarding2/LoginBySyncingFlow.qml
+++ b/ui/app/AppLayouts/Onboarding2/LoginBySyncingFlow.qml
@@ -39,7 +39,7 @@ SQUtils.QObject {
SyncProgressPage {
readonly property bool backAvailableHint:
- root.syncState === Onboarding.SyncState.Failed
+ root.syncState === Onboarding.ProgressState.Failed
syncState: root.syncState
diff --git a/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml b/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml
index 5814659be88..8954529798c 100644
--- a/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml
+++ b/ui/app/AppLayouts/Onboarding2/LoginWithKeycardFlow.qml
@@ -13,7 +13,8 @@ SQUtils.QObject {
required property StackView stackView
required property int keycardState
- required property var tryToSetPinFunction
+ required property int authorizationState
+ required property int restoreKeysExportState
required property int remainingPinAttempts
required property int remainingPukAttempts
required property var isSeedPhraseValid
@@ -22,13 +23,14 @@ SQUtils.QObject {
property bool displayKeycardPromoBanner
- signal keycardPinEntered(string pin)
signal keycardPinCreated(string pin)
signal seedphraseSubmitted(string seedphrase)
+ signal authorizationRequested(string pin)
signal reloadKeycardRequested
signal keycardFactoryResetRequested
signal unblockWithPukRequested
signal createProfileWithEmptyKeycardRequested
+ signal exportKeysRequested
signal finished
function init() {
@@ -88,16 +90,15 @@ SQUtils.QObject {
id: keycardEnterPinPage
KeycardEnterPinPage {
- tryToSetPinFunction: root.tryToSetPinFunction
+ authorizationState: root.authorizationState
+ restoreKeysExportState: root.restoreKeysExportState
+ onAuthorizationRequested: root.authorizationRequested(pin)
remainingAttempts: root.remainingPinAttempts
unblockWithPukAvailable: root.remainingPukAttempts > 0
+ keycardPinInfoPageDelay: root.keycardPinInfoPageDelay
- onKeycardPinEntered: (pin) => {
- Backpressure.debounce(root, root.keycardPinInfoPageDelay, () => {
- root.keycardPinEntered(pin)
- root.finished()
- })()
- }
+ onExportKeysRequested: root.exportKeysRequested()
+ onExportKeysDone: root.finished()
onReloadKeycardRequested: d.reload()
onKeycardFactoryResetRequested: root.keycardFactoryResetRequested()
@@ -111,6 +112,7 @@ SQUtils.QObject {
SeedphrasePage {
title: qsTr("Unblock Keycard using the recovery phrase")
btnContinueText: qsTr("Unblock Keycard")
+ authorizationState: root.authorizationState
isSeedPhraseValid: root.isSeedPhraseValid
onSeedphraseSubmitted: (seedphrase) => {
root.seedphraseSubmitted(seedphrase)
@@ -123,7 +125,10 @@ SQUtils.QObject {
id: keycardCreatePinPage
KeycardCreatePinPage {
+ id: createPinPage
+
onKeycardPinCreated: (pin) => {
+ createPinPage.loading = true
Backpressure.debounce(root, root.keycardPinInfoPageDelay, () => {
root.keycardPinCreated(pin)
root.finished()
diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml
index 3e725c786ef..d04f23211d9 100644
--- a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml
+++ b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml
@@ -13,9 +13,12 @@ SQUtils.QObject {
required property StackView stackView
required property int keycardState
+ required property int pinSettingState
+ required property int authorizationState
+ required property int restoreKeysExportState
required property int addKeyPairState
required property int syncState
- required property var seedWords
+ required property var getSeedWords
required property int remainingPinAttempts
required property int remainingPukAttempts
@@ -33,7 +36,6 @@ SQUtils.QObject {
required property var tryToSetPukFunction
signal keycardPinCreated(string pin)
- signal keycardPinEntered(string pin)
signal enableBiometricsRequested(bool enable)
signal shareUsageDataRequested(bool enabled)
signal syncProceedWithConnectionString(string connectionString)
@@ -41,10 +43,9 @@ SQUtils.QObject {
signal setPasswordRequested(string password)
signal reloadKeycardRequested
signal keycardFactoryResetRequested
- signal keyPairTransferRequested
-
- signal mnemonicWasShown()
- signal mnemonicRemovalRequested()
+ signal exportKeysRequested
+ signal loadMnemonicRequested
+ signal authorizationRequested(string pin)
signal linkActivated(string link)
@@ -193,8 +194,10 @@ SQUtils.QObject {
stackView: root.stackView
keycardState: root.keycardState
+ pinSettingState: root.pinSettingState
+ authorizationState: root.authorizationState
addKeyPairState: root.addKeyPairState
- seedWords: root.seedWords
+ getSeedWords: root.getSeedWords
displayKeycardPromoBanner: root.displayKeycardPromoBanner
isSeedPhraseValid: root.isSeedPhraseValid
@@ -202,10 +205,11 @@ SQUtils.QObject {
onReloadKeycardRequested: root.reloadKeycardRequested()
onKeycardFactoryResetRequested: root.keycardFactoryResetRequested()
- onKeyPairTransferRequested: root.keyPairTransferRequested()
+ onLoadMnemonicRequested: root.loadMnemonicRequested()
onKeycardPinCreated: (pin) => root.keycardPinCreated(pin)
onLoginWithKeycardRequested: loginWithKeycardFlow.init()
- onKeypairAddTryAgainRequested: root.keyPairTransferRequested() // FIXME?
+ // onKeypairAddTryAgainRequested: root.keyPairTransferRequested() // FIXME?
+ onAuthorizationRequested: root.authorizationRequested("") // Pin was saved locally already
onCreateProfileWithoutKeycardRequested: {
const page = stackView.find(
@@ -214,9 +218,6 @@ SQUtils.QObject {
stackView.replace(page, createProfilePage, StackView.PopTransition)
}
- onMnemonicWasShown: root.mnemonicWasShown()
- onMnemonicRemovalRequested: root.mnemonicRemovalRequested()
-
onSeedphraseSubmitted: (seedphrase) => root.seedphraseSubmitted(seedphrase)
onFinished: (withNewSeedphrase) => {
@@ -254,20 +255,22 @@ SQUtils.QObject {
stackView: root.stackView
keycardState: root.keycardState
+ authorizationState: root.authorizationState
+ restoreKeysExportState: root.restoreKeysExportState
remainingPinAttempts: root.remainingPinAttempts
remainingPukAttempts: root.remainingPukAttempts
displayKeycardPromoBanner: root.displayKeycardPromoBanner
- tryToSetPinFunction: root.tryToSetPinFunction
+ onAuthorizationRequested: root.authorizationRequested(pin)
isSeedPhraseValid: root.isSeedPhraseValid
keycardPinInfoPageDelay: root.keycardPinInfoPageDelay
- onKeycardPinEntered: (pin) => root.keycardPinEntered(pin)
onKeycardPinCreated: (pin) => root.keycardPinCreated(pin)
onSeedphraseSubmitted: (seedphrase) => root.seedphraseSubmitted(seedphrase)
onReloadKeycardRequested: root.reloadKeycardRequested()
onCreateProfileWithEmptyKeycardRequested: keycardCreateProfileFlow.init()
onKeycardFactoryResetRequested: root.keycardFactoryResetRequested()
+ onExportKeysRequested: root.exportKeysRequested()
onUnblockWithPukRequested: unblockWithPukFlow.init()
onFinished: {
@@ -302,6 +305,8 @@ SQUtils.QObject {
stackView: root.stackView
keycardState: root.keycardState
+ pinSettingState: root.pinSettingState
+ authorizationState: root.authorizationState
addKeyPairState: root.addKeyPairState
displayKeycardPromoBanner: root.displayKeycardPromoBanner
@@ -311,10 +316,10 @@ SQUtils.QObject {
onReloadKeycardRequested: root.reloadKeycardRequested()
onKeycardFactoryResetRequested: root.keycardFactoryResetRequested()
- onKeyPairTransferRequested: root.keyPairTransferRequested()
onKeycardPinCreated: (pin) => root.keycardPinCreated(pin)
onLoginWithKeycardRequested: loginWithKeycardFlow.init()
- onKeypairAddTryAgainRequested: root.keyPairTransferRequested() // FIXME?
+ onAuthorizationRequested: root.authorizationRequested("") // Pin was saved locally already
+ // onKeypairAddTryAgainRequested: root.keyPairTransferRequested() // FIXME?
onCreateProfileWithoutKeycardRequested: {
const page = stackView.find(
diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml b/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml
index e4dc3c0918d..e003ebd6f72 100644
--- a/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml
+++ b/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml
@@ -97,6 +97,20 @@ Page {
root.finished(flow, data)
}
+
+ function loadMnemonic() {
+ root.onboardingStore.loadMnemonic(d.seedphrase)
+ }
+
+ function authorize(pin) {
+ if (!pin && !d.keycardPin) {
+ return
+ }
+ if (!pin) {
+ pin = d.keycardPin
+ }
+ root.onboardingStore.authorize(pin)
+ }
}
background: Rectangle {
@@ -144,10 +158,15 @@ Page {
stackView: stack
keycardState: root.onboardingStore.keycardState
+ pinSettingState: root.onboardingStore.pinSettingState
+ authorizationState: root.onboardingStore.authorizationState
+ restoreKeysExportState: root.onboardingStore.restoreKeysExportState
syncState: root.onboardingStore.syncState
addKeyPairState: root.onboardingStore.addKeyPairState
- seedWords: root.onboardingStore.getMnemonic().split(" ")
+ getSeedWords: function () {
+ return root.onboardingStore.getMnemonic().split(" ")
+ }
displayKeycardPromoBanner: !d.settings.keycardPromoShown
biometricsAvailable: root.biometricsAvailable
@@ -166,16 +185,10 @@ Page {
root.onboardingStore.setPin(pin)
}
- onKeycardPinEntered: (pin) => {
- d.keycardPin = pin
- root.onboardingStore.setPin(pin)
- }
-
- onKeyPairTransferRequested: root.onboardingStore.startKeypairTransfer()
+ onLoadMnemonicRequested: d.loadMnemonic()
+ onAuthorizationRequested: d.authorize(pin)
onShareUsageDataRequested: (enabled) => root.shareUsageDataRequested(enabled)
onReloadKeycardRequested: root.reloadKeycardRequested()
- onMnemonicWasShown: root.onboardingStore.mnemonicWasShown()
- onMnemonicRemovalRequested: root.onboardingStore.removeMnemonic()
onSyncProceedWithConnectionString: (connectionString) =>
root.onboardingStore.inputConnectionStringForBootstrapping(connectionString)
@@ -183,6 +196,7 @@ Page {
onSetPasswordRequested: (password) => d.password = password
onEnableBiometricsRequested: (enabled) => d.enableBiometrics = enabled
onLinkActivated: (link) => Qt.openUrlExternally(link)
+ onExportKeysRequested: root.onboardingStore.exportRecoverKeys()
onFinished: (flow) => d.finishFlow(flow)
onKeycardFactoryResetRequested: console.warn("!!! FIXME OnboardingLayout::onKeycardFactoryResetRequested")
}
diff --git a/ui/app/AppLayouts/Onboarding2/UseRecoveryPhraseFlow.qml b/ui/app/AppLayouts/Onboarding2/UseRecoveryPhraseFlow.qml
index bbbb465a568..a1845d26bd2 100644
--- a/ui/app/AppLayouts/Onboarding2/UseRecoveryPhraseFlow.qml
+++ b/ui/app/AppLayouts/Onboarding2/UseRecoveryPhraseFlow.qml
@@ -4,6 +4,7 @@ import QtQuick.Controls 2.15
import StatusQ.Core.Utils 0.1 as SQUtils
import AppLayouts.Onboarding2.pages 1.0
+import AppLayouts.Onboarding.enums 1.0
SQUtils.QObject {
id: root
@@ -41,6 +42,7 @@ SQUtils.QObject {
SeedphrasePage {
isSeedPhraseValid: root.isSeedPhraseValid
+ authorizationState: Onboarding.ProgressState.Idle
onSeedphraseSubmitted: (seedphrase) => {
root.seedphraseSubmitted(seedphrase)
diff --git a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml
index 175ffbeac20..6f43f46fdac 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/BackupSeedphraseReveal.qml
@@ -15,7 +15,6 @@ OnboardingPage {
required property var seedWords
- signal mnemonicWasShown()
signal backupSeedphraseConfirmed()
QtObject {
@@ -103,7 +102,6 @@ OnboardingPage {
visible: !d.seedphraseRevealed
onClicked: {
d.seedphraseRevealed = true
- root.mnemonicWasShown()
}
}
}
diff --git a/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml b/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml
index dc900da57b8..e2118cca4dd 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/CreateKeycardProfilePage.qml
@@ -77,7 +77,7 @@ OnboardingPage {
StatusButton {
objectName: "btnCreateWithEmptySeedphrase"
Layout.fillWidth: true
- text: qsTr("Let’s go!")
+ text: qsTr("Let's go!")
font.pixelSize: Theme.additionalTextSize
onClicked: root.createKeycardProfileWithNewSeedphrase()
}
diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardAddKeyPairPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardAddKeyPairPage.qml
index 83a9a8269b1..8d354aa257d 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/KeycardAddKeyPairPage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardAddKeyPairPage.qml
@@ -13,7 +13,7 @@ import AppLayouts.Onboarding.enums 1.0
OnboardingPage {
id: root
- required property int addKeyPairState // Onboarding.AddKeyPairState.xxx
+ required property int addKeyPairState // Onboarding.ProgressState.xxx
signal keypairAddContinueRequested()
signal keypairAddTryAgainRequested()
@@ -23,7 +23,7 @@ OnboardingPage {
states: [
State {
name: "inprogress"
- when: root.addKeyPairState === Onboarding.AddKeyPairState.InProgress
+ when: root.addKeyPairState === Onboarding.ProgressState.InProgress
PropertyChanges {
target: root
title: qsTr("Adding key pair to Keycard")
@@ -44,7 +44,7 @@ OnboardingPage {
},
State {
name: "success"
- when: root.addKeyPairState === Onboarding.AddKeyPairState.Success
+ when: root.addKeyPairState === Onboarding.ProgressState.Success
PropertyChanges {
target: root
title: qsTr("Key pair added to Keycard")
@@ -68,7 +68,7 @@ OnboardingPage {
},
State {
name: "failed"
- when: root.addKeyPairState === Onboarding.AddKeyPairState.Failed
+ when: root.addKeyPairState === Onboarding.ProgressState.Failed
PropertyChanges {
target: root
title: "".arg(Theme.palette.dangerColor1) + qsTr("Failed to add key pair to Keycard") + ""
diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml
index 028f518e73a..60433dcd483 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardCreatePinPage.qml
@@ -7,15 +7,23 @@ import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Core.Theme 0.1
+import StatusQ.Core.Backpressure 0.1
import AppLayouts.Onboarding2.controls 1.0
+import AppLayouts.Onboarding.enums 1.0
import utils 1.0
KeycardBasePage {
id: root
+ property int keycardPinInfoPageDelay
+ required property int pinSettingState
+ required property int authorizationState
+
signal keycardPinCreated(string pin)
+ signal keycardPinSuccessfullySet()
+ signal keycardAuthorized()
image.source: Theme.png("onboarding/keycard/reading")
@@ -53,10 +61,16 @@ KeycardBasePage {
StatusBaseText {
id: errorText
anchors.horizontalCenter: parent.horizontalCenter
- text: qsTr("PINs don’t match")
+ text: qsTr("PINs don't match")
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.dangerColor1
visible: false
+ },
+ StatusLoadingIndicator {
+ id: loadingIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: Theme.halfPadding
+ visible: false
}
]
@@ -83,26 +97,89 @@ KeycardBasePage {
image.source: Theme.png("onboarding/keycard/error")
}
},
+ State {
+ name: "error"
+ when: root.pinSettingState === Onboarding.ProgressState.Failed || root.authorizationState === Onboarding.ProgressState.Failed
+ PropertyChanges {
+ target: errorText
+ visible: true
+ text: qsTr("Error setting pin")
+ }
+ PropertyChanges {
+ target: root
+ image.source: Theme.png("onboarding/keycard/error")
+ }
+ },
+ State {
+ name: "authorized"
+ when: root.authorizationState === Onboarding.ProgressState.Success
+ PropertyChanges {
+ target: root
+ title: qsTr("PIN set")
+ }
+ PropertyChanges {
+ target: pinInput
+ enabled: false
+ }
+ PropertyChanges {
+ target: root
+ image.source: Theme.png("onboarding/keycard/success")
+ }
+ StateChangeScript {
+ script: {
+ Backpressure.debounce(root, keycardPinInfoPageDelay, function() {
+ root.keycardAuthorized()
+ })()
+ }
+ }
+ },
State {
name: "success"
+ when: root.pinSettingState === Onboarding.ProgressState.Success
+ PropertyChanges {
+ target: root
+ title: qsTr("PIN set")
+ }
+ PropertyChanges {
+ target: pinInput
+ enabled: false
+ }
+ PropertyChanges {
+ target: root
+ image.source: Theme.png("onboarding/keycard/success")
+ }
+ StateChangeScript {
+ script: {
+ root.keycardPinSuccessfullySet()
+ }
+ }
+ },
+ State {
+ name: "settingPin"
extend: "repeating"
- when: !!d.pin && !!d.pin2 && d.pin === d.pin2
+ when: !!d.pin && !!d.pin2 && d.pin === d.pin2 && (root.pinSettingState === Onboarding.ProgressState.Idle || root.pinSettingState === Onboarding.ProgressState.InProgress)
PropertyChanges {
target: root
- title: qsTr("Keycard PIN set")
+ title: qsTr("Setting Keycard PIN")
}
PropertyChanges {
target: pinInput
enabled: false
}
+ PropertyChanges {
+ target: loadingIndicator
+ visible: true
+ }
PropertyChanges {
target: root
image.source: Theme.png("onboarding/keycard/success")
}
StateChangeScript {
script: {
- pinInput.setPin(d.pin)
- root.keycardPinCreated(d.pin)
+ Backpressure.debounce(root, keycardPinInfoPageDelay, function() {
+ pinInput.setPin(d.pin)
+ root.keycardPinCreated(d.pin)
+ })()
}
}
},
diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml
index c84b4adedb3..7980815679b 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardEnterPinPage.qml
@@ -9,28 +9,33 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Core.Backpressure 0.1
import AppLayouts.Onboarding2.controls 1.0
+import AppLayouts.Onboarding.enums 1.0
import utils 1.0
KeycardBasePage {
id: root
- property var tryToSetPinFunction: (pin) => { console.error("tryToSetPinFunction: IMPLEMENT ME"); return false }
+ required property int authorizationState
+ required property int restoreKeysExportState
required property int remainingAttempts
property bool unblockWithPukAvailable
+ property int keycardPinInfoPageDelay
signal keycardPinEntered(string pin)
signal reloadKeycardRequested
signal unblockWithSeedphraseRequested
signal unblockWithPukRequested
signal keycardFactoryResetRequested
+ signal authorizationRequested(string pin)
+ signal exportKeysRequested()
+ signal exportKeysDone()
image.source: Theme.png("onboarding/keycard/reading")
QtObject {
id: d
property string tempPin
- property bool pinValid
}
buttons: [
@@ -42,10 +47,7 @@ KeycardBasePage {
onPinInputChanged: {
if (pinInput.pinInput.length === pinInput.pinLen) { // we have the full length PIN now
d.tempPin = pinInput.pinInput
- d.pinValid = root.tryToSetPinFunction(d.tempPin)
- if (!d.pinValid) {
- pinInput.statesInitialization()
- }
+ root.authorizationRequested(d.tempPin)
}
}
},
@@ -57,6 +59,20 @@ KeycardBasePage {
color: Theme.palette.dangerColor1
visible: false
},
+ StatusBaseText {
+ id: errorExportingText
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: qsTr("Error exporting the keys, please try again")
+ font.pixelSize: Theme.tertiaryTextFontSize
+ color: Theme.palette.dangerColor1
+ visible: false
+ },
+ StatusLoadingIndicator {
+ id: loadingIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: Theme.halfPadding
+ visible: false
+ },
MaybeOutlineButton {
id: btnUnblockWithPuk
visible: false
@@ -125,7 +141,7 @@ KeycardBasePage {
},
State {
name: "incorrect"
- when: !!d.tempPin && !d.pinValid
+ when: root.authorizationState === Onboarding.ProgressState.Failed
PropertyChanges {
target: root
title: qsTr("PIN incorrect")
@@ -134,20 +150,82 @@ KeycardBasePage {
target: errorText
visible: true
}
+ StateChangeScript {
+ script: {
+ Backpressure.debounce(root, 100, function() {
+ pinInput.clearPin()
+ })()
+ }
+ }
+ },
+ State {
+ name: "error"
+ when: root.restoreKeysExportState === Onboarding.ProgressState.Failed
+ PropertyChanges {
+ target: root
+ title: qsTr("Keys export failed")
+ }
+ PropertyChanges {
+ target: errorExportingText
+ visible: true
+ }
+ },
+ State {
+ name: "authorizing"
+ when: root.authorizationState === Onboarding.ProgressState.InProgress
+ PropertyChanges {
+ target: root
+ title: qsTr("Authorizing")
+ }
+ PropertyChanges {
+ target: pinInput
+ enabled: false
+ }
+ PropertyChanges {
+ target: loadingIndicator
+ visible: true
+ }
+ },
+ State {
+ name: "exportSuccess"
+ when: root.restoreKeysExportState === Onboarding.ProgressState.Success
+ PropertyChanges {
+ target: root
+ title: qsTr("Keys exported successfully")
+ }
+ PropertyChanges {
+ target: pinInput
+ enabled: false
+ }
+ StateChangeScript {
+ script: {
+ Backpressure.debounce(root, keycardPinInfoPageDelay, function() {
+ root.exportKeysDone()
+ })()
+ }
+ }
},
State {
- name: "success"
- when: d.pinValid
+ name: "pinSuccess"
+ when: root.authorizationState === Onboarding.ProgressState.Success
PropertyChanges {
target: root
- title: qsTr("PIN correct")
+ title: qsTr("PIN correct. Exporting keys.")
}
PropertyChanges {
target: pinInput
enabled: false
}
+ PropertyChanges {
+ target: loadingIndicator
+ visible: true
+ }
StateChangeScript {
- script: root.keycardPinEntered(pinInput.pinInput)
+ script: {
+ Backpressure.debounce(root, keycardPinInfoPageDelay, function() {
+ root.exportKeysRequested()
+ })()
+ }
}
},
State {
@@ -161,7 +239,6 @@ KeycardBasePage {
pinInput.statesInitialization()
pinInput.forceFocus()
d.tempPin = ""
- d.pinValid = false
}
}
}
diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml
index c155b228c8a..5d853d47ec4 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardIntroPage.qml
@@ -108,7 +108,7 @@ KeycardBasePage {
MaybeOutlineButton {
id: btnReload
visible: false
- text: qsTr("I’ve inserted a different Keycard")
+ text: qsTr("I've inserted a different Keycard")
anchors.horizontalCenter: parent.horizontalCenter
onClicked: root.reloadKeycardRequested()
}
diff --git a/ui/app/AppLayouts/Onboarding2/pages/KeycardLostPage.qml b/ui/app/AppLayouts/Onboarding2/pages/KeycardLostPage.qml
index d947774c963..28da23881af 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/KeycardLostPage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/KeycardLostPage.qml
@@ -12,7 +12,7 @@ KeycardBasePage {
signal useProfileWithoutKeycardRequested()
title: qsTr("Lost Keycard")
- subtitle: qsTr("Sorry you’ve lost your Keycard")
+ subtitle: qsTr("Sorry you've lost your Keycard")
image.source: Theme.png("onboarding/keycard/empty")
buttons: [
diff --git a/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml b/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml
index 507be21f784..2512ec4f1fd 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/SeedphrasePage.qml
@@ -7,6 +7,8 @@ import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
+import AppLayouts.Onboarding.enums 1.0
+
import shared.panels 1.0
OnboardingPage {
@@ -16,9 +18,12 @@ OnboardingPage {
property string subtitle: qsTr("Enter your 12, 18 or 24 word recovery phrase")
property alias btnContinueText: btnContinue.text
+ required property int authorizationState
+
property var isSeedPhraseValid: (mnemonic) => { console.error("isSeedPhraseValid IMPLEMENT ME"); return false }
signal seedphraseSubmitted(string seedphrase)
+ signal keycardAuthorized()
contentItem: Item {
ColumnLayout {
@@ -62,4 +67,30 @@ OnboardingPage {
}
}
}
+
+ state: "creating"
+
+ states: [
+ State {
+ name: "creating"
+ },
+ State {
+ name: "authorized"
+ when: root.authorizationState === Onboarding.ProgressState.Success
+ StateChangeScript {
+ script: {
+ root.keycardAuthorized()
+ }
+ }
+ },
+ State {
+ name: "loadingMnemonic"
+ when: root.authorizationState === Onboarding.ProgressState.InProgress
+
+ PropertyChanges {
+ target: btnContinue
+ loading: true
+ }
+ }
+ ]
}
diff --git a/ui/app/AppLayouts/Onboarding2/pages/SyncProgressPage.qml b/ui/app/AppLayouts/Onboarding2/pages/SyncProgressPage.qml
index 529742b98b1..c937b9b754a 100644
--- a/ui/app/AppLayouts/Onboarding2/pages/SyncProgressPage.qml
+++ b/ui/app/AppLayouts/Onboarding2/pages/SyncProgressPage.qml
@@ -12,7 +12,7 @@ import AppLayouts.Onboarding.enums 1.0
OnboardingPage {
id: root
- required property int syncState // Onboarding.SyncState.xxx
+ required property int syncState // Onboarding.ProgressState.xxx
signal loginToAppRequested()
signal restartSyncRequested()
@@ -21,7 +21,7 @@ OnboardingPage {
states: [
State {
name: "inprogress"
- when: root.syncState === Onboarding.SyncState.InProgress || root.syncState === Onboarding.SyncState.Idle
+ when: root.syncState === Onboarding.ProgressState.InProgress || root.syncState === Onboarding.ProgressState.Idle
PropertyChanges {
target: root
title: qsTr("Profile sync in progress...")
@@ -46,7 +46,7 @@ OnboardingPage {
},
State {
name: "success"
- when: root.syncState === Onboarding.SyncState.Success
+ when: root.syncState === Onboarding.ProgressState.Success
PropertyChanges {
target: root
title: qsTr("Profile synced")
@@ -70,7 +70,7 @@ OnboardingPage {
},
State {
name: "failed"
- when: root.syncState === Onboarding.SyncState.Failed
+ when: root.syncState === Onboarding.ProgressState.Failed
PropertyChanges {
target: root
title: "".arg(Theme.palette.dangerColor1) + qsTr("Profile syncing failed") + ""
diff --git a/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml b/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml
index 5af022324c5..a49439f3eb1 100644
--- a/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml
+++ b/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml
@@ -7,22 +7,25 @@ import AppLayouts.Onboarding.enums 1.0
QtObject {
id: root
- signal appLoaded()
-
+ signal appLoaded
readonly property QtObject d: StatusQUtils.QObject {
id: d
readonly property var onboardingModuleInst: onboardingModule
Component.onCompleted: {
- onboardingModuleInst.appLoaded.connect(root.appLoaded)
- onboardingModuleInst.accountLoginError.connect(root.accountLoginError)
- onboardingModuleInst.obtainingPasswordSuccess.connect(root.obtainingPasswordSuccess)
- onboardingModuleInst.obtainingPasswordError.connect(root.obtainingPasswordError)
+ d.onboardingModuleInst.appLoaded.connect(root.appLoaded)
+ // TODO implement the following signals
+ // d.onboardingModuleInst.accountLoginError.connect(root.accountLoginError)
+ // d.onboardingModuleInst.obtainingPasswordSuccess.connect(root.obtainingPasswordSuccess)
+ // d.onboardingModuleInst.obtainingPasswordError.connect(root.obtainingPasswordError)
}
}
// keycard
readonly property int keycardState: d.onboardingModuleInst.keycardState // cf. enum Onboarding.KeycardState
+ readonly property int pinSettingState: d.onboardingModuleInst.pinSettingState // cf. enum Onboarding.ProgressState
+ readonly property int authorizationState: d.onboardingModuleInst.authorizationState // cf. enum Onboarding.ProgressState
+ readonly property int restoreKeysExportState: d.onboardingModuleInst.restoreKeysExportState // cf. enum Onboarding.ProgressState
readonly property int keycardRemainingPinAttempts: d.onboardingModuleInst.keycardRemainingPinAttempts
readonly property int keycardRemainingPukAttempts: d.onboardingModuleInst.keycardRemainingPukAttempts
@@ -30,17 +33,24 @@ QtObject {
return d.onboardingModuleInst.finishOnboardingFlow(flow, JSON.stringify(data))
}
- function setPin(pin: string) { // -> bool
- return d.onboardingModuleInst.setPin(pin)
+ function setPin(pin: string) {
+ d.onboardingModuleInst.setPin(pin)
}
function setPuk(puk: string) { // -> bool
return d.onboardingModuleInst.setPuk(puk)
}
- readonly property int addKeyPairState: d.onboardingModuleInst.addKeyPairState // cf. enum Onboarding.AddKeyPairState
- function startKeypairTransfer() { // -> void
- d.onboardingModuleInst.startKeypairTransfer()
+ function authorize(pin: string) {
+ d.onboardingModuleInst.authorize(pin)
+ }
+
+ readonly property int addKeyPairState: d.onboardingModuleInst.addKeyPairState // cf. enum Onboarding.ProgressState
+ function loadMnemonic(mnemonic) { // -> void
+ d.onboardingModuleInst.loadMnemonic(mnemonic)
+ }
+ function exportRecoverKeys() { // -> void
+ d.onboardingModuleInst.exportRecoverKeys()
}
// password
@@ -59,17 +69,11 @@ QtObject {
return d.onboardingModuleInst.validMnemonic(mnemonic)
}
function getMnemonic() { // -> string
- return d.onboardingModuleInst.mnemonic()
- }
- function mnemonicWasShown() { // -> void
- d.onboardingModuleInst.mnemonicWasShown()
- }
- function removeMnemonic() { // -> void
- d.onboardingModuleInst.removeMnemonic()
+ return d.onboardingModuleInst.getMnemonic()
}
// sync
- readonly property int syncState: d.onboardingModuleInst.syncState // cf. enum Onboarding.SyncState
+ readonly property int syncState: d.onboardingModuleInst.syncState // cf. enum Onboarding.ProgressState
function validateLocalPairingConnectionString(connectionString: string) { // -> bool
return d.onboardingModuleInst.validateLocalPairingConnectionString(connectionString)
}
diff --git a/vendor/nim-keycard-go b/vendor/nim-keycard-go
index aa05fe490b5..d9f628f7d59 160000
--- a/vendor/nim-keycard-go
+++ b/vendor/nim-keycard-go
@@ -1 +1 @@
-Subproject commit aa05fe490b57023028e242567f52bd124ed87d30
+Subproject commit d9f628f7d59269a29c223efd95542683696959dd
diff --git a/vendor/status-keycard-go b/vendor/status-keycard-go
index 8122bdc0f30..bcf094518e5 160000
--- a/vendor/status-keycard-go
+++ b/vendor/status-keycard-go
@@ -1 +1 @@
-Subproject commit 8122bdc0f30c4957a0f72fb423c6972d3c2e8ed5
+Subproject commit bcf094518e5d3a8144b831669d833d70e7aed112