feat(@desktop/wallet): account Interaction - move keypair to a keycard

Part 1 of: #11737
This commit is contained in:
Sale Djenic 2023-08-25 11:05:05 +02:00 committed by saledjenic
parent 789a01cf09
commit c81dfdc7c7
16 changed files with 170 additions and 64 deletions

View File

@ -26,7 +26,7 @@ method onDisplayKeycardSharedModuleFlow*(self: AccessInterface) {.base.} =
method onSharedKeycarModuleFlowTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method runSetupKeycardPopup*(self: AccessInterface) {.base.} =
method runSetupKeycardPopup*(self: AccessInterface, keyUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method runCreateNewKeycardWithNewSeedPhrasePopup*(self: AccessInterface) {.base.} =

View File

@ -114,11 +114,11 @@ method onSharedKeycarModuleFlowTerminated*(self: Module, lastStepInTheCurrentFlo
method onDisplayKeycardSharedModuleFlow*(self: Module) =
self.view.emitDisplayKeycardSharedModuleFlow()
method runSetupKeycardPopup*(self: Module) =
method runSetupKeycardPopup*(self: Module, keyUid: string) =
self.createSharedKeycardModule()
if self.keycardSharedModule.isNil:
return
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.SetupNewKeycard)
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.SetupNewKeycard, keyUid)
method runCreateNewKeycardWithNewSeedPhrasePopup*(self: Module) =
self.createSharedKeycardModule()

View File

@ -56,8 +56,8 @@ QtObject:
proc emitDestroyKeycardSharedModuleFlow*(self: View) =
self.destroyKeycardSharedModuleFlow()
proc runSetupKeycardPopup*(self: View) {.slot.} =
self.delegate.runSetupKeycardPopup()
proc runSetupKeycardPopup*(self: View, keyUid: string) {.slot.} =
self.delegate.runSetupKeycardPopup(keyUid)
proc runCreateNewKeycardWithNewSeedPhrasePopup*(self: View) {.slot.} =
self.delegate.runCreateNewKeycardWithNewSeedPhrasePopup()

View File

@ -450,9 +450,19 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Paths
if flowToRun == FlowType.SetupNewKeycard:
let items = keypairs.buildKeyPairsList(self.controller.getKeypairs(), excludeAlreadyMigratedPairs = true,
excludePrivateKeyKeypairs = false)
if keyUid.len == 0:
self.view.createKeyPairModel(items)
self.view.setCurrentState(newSelectExistingKeyPairState(flowToRun, nil))
self.controller.readyToDisplayPopup()
else:
let filteredItems = items.filter(x => x.getKeyUid() == keyUid)
if filteredItems.len != 1:
error "sm_cannot resolve a keypair being migrated"
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
return
self.setSelectedKeyPair(filteredItems[0])
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
self.controller.runLoadAccountFlow()
return
if flowToRun == FlowType.Authentication:
self.controller.connectKeychainSignals()

View File

@ -13,6 +13,33 @@ proc storeKeypair(self: Service, keypair: KeypairDto) =
return
self.keypairs[keypair.keyUid] = keypair
# replaces only keypair/accounts fields that could be changed
proc replaceKeypair(self: Service, keypair: KeypairDto) =
if keypair.keyUid.len == 0:
error "trying to replace a keypair with empty keyUid"
return
if not self.keypairs.hasKey(keypair.keyUid):
error "trying to replace a non existing keypair"
return
var localKp = self.keypairs[keypair.keyUid]
localKp.name = keypair.name
localKp.lastUsedDerivationIndex = keypair.lastUsedDerivationIndex
localKp.syncedFrom = keypair.syncedFrom
localKp.removed = keypair.removed
localKp.keycards = keypair.keycards
for locAcc in localKp.accounts:
for acc in keypair.accounts:
if cmpIgnoreCase(locAcc.address, acc.address) != 0:
continue
locAcc.name = acc.name
locAcc.colorId = acc.colorId
locAcc.emoji = acc.emoji
locAcc.operable = acc.operable
locAcc.removed = acc.removed
locAcc.prodPreferredChainIds = acc.prodPreferredChainIds
locAcc.testPreferredChainIds = acc.testPreferredChainIds
break
proc storeAccountToKeypair(self: Service, account: WalletAccountDto) =
if account.keyUid.len == 0:
error "trying to store a keypair related account with empty keyUid"
@ -644,6 +671,7 @@ proc handleKeypair(self: Service, keypair: KeypairDto) =
localKp.name = keypair.name
localKp.lastUsedDerivationIndex = keypair.lastUsedDerivationIndex
localKp.syncedFrom = keypair.syncedFrom
localKp.keycards = keypair.keycards
# - first remove removed accounts from the UI
let addresses = localKp.accounts.map(a => a.address)
for address in addresses:

View File

@ -8,7 +8,16 @@
)
self.threadpool.start(arg)
proc updateLocalKeypairOnKeycardChange(self: Service, keyUid: string) =
var kp: KeypairDto
if keyUid.len > 0:
kp = getKeypairByKeyUidFromDb(keyUid)
if kp.isNil:
return
self.replaceKeypair(kp)
proc emitAddKeycardAddAccountsChange(self: Service, success: bool, keycard: KeycardDto) =
self.updateLocalKeypairOnKeycardChange(keycard.keyUid)
let data = KeycardArgs(
success: success,
keycard: keycard
@ -69,6 +78,7 @@ proc onMigratedAccountsForKeycardRemoved*(self: Service, response: string) {.slo
data.keycard = kpJson.toKeycardDto()
except Exception as e:
error "error handilng migrated keycard response", errDesription=e.msg
self.updateLocalKeypairOnKeycardChange(data.keycard.keyUid)
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data)
proc getAllKnownKeycards*(self: Service): seq[KeycardDto] =
@ -105,6 +115,11 @@ proc isKeycardAccount*(self: Service, account: WalletAccountDto): bool =
return keycards.len > 0
proc updateKeycardName*(self: Service, keycardUid: string, name: string): bool =
let kc = self.getKeycardByKeycardUid(keycardUid)
let kp = self.getKeypairByKeyUid(kc.keyUid)
if kp.isNil:
error "there is no known keypair", keyUid=kc.keyUid, procName="updateKeycardName"
return
var data = KeycardArgs(
success: false,
keycard: KeycardDto(keycardUid: keycardUid, keycardName: name)
@ -112,12 +127,20 @@ proc updateKeycardName*(self: Service, keycardUid: string, name: string): bool =
try:
let response = backend.setKeycardName(keycardUid, name)
data.success = responseHasNoErrors("updateKeycardName", response)
for kc in kp.keycards.mitems:
if kc.keycardUid == keycardUid:
kc.keycardName = name
break
except Exception as e:
error "error: ", procName="updateKeycardName", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
return data.success
proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool =
let kp = self.getKeypairByKeyUid(keyUid)
if kp.isNil:
error "there is no known keypair", keyUid=keyUid, procName="setKeycardLocked"
return
var data = KeycardArgs(
success: false,
keycard: KeycardDto(keyUid: keyUid, keycardUid: keycardUid)
@ -125,12 +148,20 @@ proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool
try:
let response = backend.keycardLocked(keycardUid)
data.success = responseHasNoErrors("setKeycardLocked", response)
for kc in kp.keycards.mitems:
if kc.keycardUid == keycardUid:
kc.keycardLocked = true
break
except Exception as e:
error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
return data.success
proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): bool =
let kp = self.getKeypairByKeyUid(keyUid)
if kp.isNil:
error "there is no known keypair", keyUid=keyUid, procName="setKeycardUnlocked"
return
var data = KeycardArgs(
success: false,
keycard: KeycardDto(keyUid: keyUid, keycardUid: keycardUid)
@ -138,12 +169,21 @@ proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): boo
try:
let response = backend.keycardUnlocked(keycardUid)
data.success = responseHasNoErrors("setKeycardUnlocked", response)
for kc in kp.keycards.mitems:
if kc.keycardUid == keycardUid:
kc.keycardLocked = false
break
except Exception as e:
error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
return data.success
proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool =
let kc = self.getKeycardByKeycardUid(oldKeycardUid)
let kp = self.getKeypairByKeyUid(kc.keyUid)
if kp.isNil:
error "there is no known keypair", keyUid=kc.keyUid, procName="updateKeycardUid"
return
var data = KeycardArgs(
success: false,
oldKeycardUid: oldKeycardUid,
@ -152,6 +192,10 @@ proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: stri
try:
let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid)
data.success = responseHasNoErrors("updateKeycardUid", response)
for kc in kp.keycards.mitems:
if kc.keycardUid == oldKeycardUid:
kc.keycardUid = newKeycardUid
break
except Exception as e:
error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
@ -165,12 +209,18 @@ proc deleteKeycard*(self: Service, keycardUid: string): bool =
try:
let response = backend.deleteKeycard(keycardUid)
data.success = responseHasNoErrors("deleteKeycard", response)
let kc = self.getKeycardByKeycardUid(keycardUid)
self.updateLocalKeypairOnKeycardChange(kc.keyUid)
except Exception as e:
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_KEYCARD_DELETED, data)
return data.success
proc deleteAllKeycardsWithKeyUid*(self: Service, keyUid: string): bool =
let kp = self.getKeypairByKeyUid(keyUid)
if kp.isNil:
error "there is no known keypair", keyUid=keyUid, procName="deleteAllKeycardsWithKeyUid"
return
var data = KeycardArgs(
success: false,
keycard: KeycardDto(keyUid: keyUid)
@ -178,6 +228,7 @@ proc deleteAllKeycardsWithKeyUid*(self: Service, keyUid: string): bool =
try:
let response = backend.deleteAllKeycardsWithKeyUID(keyUid)
data.success = responseHasNoErrors("deleteAllKeycardsWithKeyUid", response)
kp.keycards = @[]
except Exception as e:
error "error: ", procName="deleteAllKeycardsWithKeyUid", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_ALL_KEYCARDS_DELETED, data)

View File

@ -6,6 +6,7 @@ import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.stores 1.0 as SharedStores
import shared.popups.keycard 1.0
import AppLayouts.Wallet.controls 1.0
@ -13,8 +14,10 @@ import "stores"
import "popups"
import "views"
import StatusQ.Core 0.1
import StatusQ.Layout 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
StatusSectionLayout {
id: root
@ -334,4 +337,50 @@ StatusSectionLayout {
}
}
}
Connections {
target: root.store.keycardStore.keycardModule
enabled: profileContainer.currentIndex === Constants.settingsSubsection.wallet ||
profileContainer.currentIndex === Constants.settingsSubsection.keycard
function onDisplayKeycardSharedModuleFlow() {
keycardPopup.active = true
}
function onDestroyKeycardSharedModuleFlow() {
keycardPopup.active = false
}
function onSharedModuleBusy() {
Global.openPopup(sharedModuleBusyPopupComponent)
}
}
Loader {
id: keycardPopup
active: false
sourceComponent: KeycardPopup {
sharedKeycardModule: root.store.keycardStore.keycardModule.keycardSharedModule
emojiPopup: root.emojiPopup
}
onLoaded: {
keycardPopup.item.open()
}
}
Component {
id: sharedModuleBusyPopupComponent
StatusDialog {
id: titleContentDialog
title: qsTr("Status Keycard")
StatusBaseText {
anchors.fill: parent
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.directColor1
text: qsTr("The Keycard module is still busy, please try again")
}
standardButtons: Dialog.Ok
}
}
}

View File

@ -28,6 +28,7 @@ Rectangle {
signal runImportViaPrivateKeyFlow()
signal runRenameKeypairFlow()
signal runRemoveKeypairFlow()
signal runMoveKeypairToKeycardFlow()
QtObject {
id: d
@ -92,6 +93,7 @@ Rectangle {
onRunImportViaPrivateKeyFlow: root.runImportViaPrivateKeyFlow()
onRunRenameKeypairFlow: root.runRenameKeypairFlow()
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow()
onRunMoveKeypairToKeycardFlow: root.runMoveKeypairToKeycardFlow()
}
}
},

View File

@ -17,6 +17,7 @@ StatusMenu {
signal runImportViaPrivateKeyFlow()
signal runRenameKeypairFlow()
signal runRemoveKeypairFlow()
signal runMoveKeypairToKeycardFlow()
StatusAction {
text: enabled? qsTr("Show encrypted QR on device") : ""
@ -33,16 +34,15 @@ StatusMenu {
}
StatusAction {
text: enabled? root.keyPair.migratedToKeycard? qsTr("Stop using Keycard") : qsTr("Move keys to a Keycard") : ""
enabled: !!root.keyPair &&
root.keyPair.operability !== Constants.keypair.operability.nonOperable
text: enabled? root.keyPair.migratedToKeycard? qsTr("Stop using Keycard") : qsTr("Move keypair to a Keycard") : ""
enabled: !!root.keyPair
icon.name: !!root.keyPair && root.keyPair.migratedToKeycard? "keycard-crossed" : "keycard"
icon.color: Theme.palette.primaryColor1
onTriggered: {
if (root.keyPair.migratedToKeycard)
console.warn("TODO: stop using Keycard")
else
console.warn("TODO: move keys to a Keycard")
root.runMoveKeypairToKeycardFlow()
}
}

View File

@ -6,8 +6,8 @@ QtObject {
property var keycardModule
function runSetupKeycardPopup() {
root.keycardModule.runSetupKeycardPopup()
function runSetupKeycardPopup(keyUid) {
root.keycardModule.runSetupKeycardPopup(keyUid)
}
function runCreateNewKeycardWithNewSeedPhrasePopup() {

View File

@ -5,11 +5,9 @@ import QtQml.Models 2.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
import shared.popups.keycard 1.0
import "../stores"
import "./keycard"
@ -50,23 +48,6 @@ SettingsContentBase {
property string observedKeyUid: ""
}
Component {
id: sharedModuleBusyPopupComponent
StatusDialog {
id: titleContentDialog
title: qsTr("Status Keycard")
StatusBaseText {
anchors.fill: parent
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.directColor1
text: qsTr("The Keycard module is still busy, please try again")
}
standardButtons: Dialog.Ok
}
}
MainView {
Layout.preferredWidth: root.contentWidth
keycardStore: root.keycardStore
@ -94,32 +75,5 @@ SettingsContentBase {
root.handleBackAction()
}
}
Connections {
target: root.keycardStore.keycardModule
function onDisplayKeycardSharedModuleFlow() {
keycardPopup.active = true
}
function onDestroyKeycardSharedModuleFlow() {
keycardPopup.active = false
}
function onSharedModuleBusy() {
Global.openPopup(sharedModuleBusyPopupComponent)
}
}
Loader {
id: keycardPopup
active: false
sourceComponent: KeycardPopup {
sharedKeycardModule: root.keycardStore.keycardModule.keycardSharedModule
emojiPopup: root.emojiPopup
}
onLoaded: {
keycardPopup.item.open()
}
}
}
}

View File

@ -122,6 +122,9 @@ SettingsContentBase {
removeKeypairPopup.accounts= model.keyPair.accounts
removeKeypairPopup.active = true
}
onRunMoveKeypairToKeycardFlow: {
root.rootStore.keycardStore.runSetupKeycardPopup(model.keyPair.keyUid)
}
}
NetworksView {
@ -187,6 +190,9 @@ SettingsContentBase {
onRunImportMissingKeypairFlow: {
root.walletStore.runKeypairImportPopup(keyPair.keyUid, Constants.keypairImportPopup.mode.selectImportMethod)
}
onRunMoveKeypairToKeycardFlow: {
root.rootStore.keycardStore.runSetupKeycardPopup(keyPair.keyUid)
}
}
DappPermissionsView {

View File

@ -105,7 +105,7 @@ ColumnLayout {
}
]
onClicked: {
root.keycardStore.runSetupKeycardPopup()
root.keycardStore.runSetupKeycardPopup("")
}
}

View File

@ -27,6 +27,7 @@ ColumnLayout {
signal runRenameKeypairFlow()
signal runRemoveKeypairFlow()
signal runImportMissingKeypairFlow()
signal runMoveKeypairToKeycardFlow()
property var account
property var keyPair
@ -307,5 +308,6 @@ ColumnLayout {
hasPairedDevices: root.walletStore.walletModule.hasPairedDevices
onRunRenameKeypairFlow: root.runRenameKeypairFlow()
onRunRemoveKeypairFlow: root.runRemoveKeypairFlow()
onRunMoveKeypairToKeycardFlow: root.runMoveKeypairToKeycardFlow()
}
}

View File

@ -28,6 +28,7 @@ Column {
signal goToDappPermissionsView()
signal runRenameKeypairFlow(var model)
signal runRemoveKeypairFlow(var model)
signal runMoveKeypairToKeycardFlow(var model)
spacing: 8
@ -238,6 +239,9 @@ Column {
onRunImportViaQrFlow: {
root.walletStore.runKeypairImportPopup(model.keyPair.keyUid, Constants.keypairImportPopup.mode.importViaQr)
}
onRunMoveKeypairToKeycardFlow: {
root.runMoveKeypairToKeycardFlow(model)
}
}
}
}

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 8425e6d238551431d5a75b7a6e5787e85c3b9d11
Subproject commit 3ab312f6d17ab509cd233647bab1b0dcb46435af