feature(@desktop/keycard): sync a Keycard state on every usage

Closes: #8759
This commit is contained in:
Sale Djenic 2023-01-04 16:01:10 +01:00 committed by saledjenic
parent 93c90b8436
commit cae2a5bea3
43 changed files with 794 additions and 280 deletions

View File

@ -26,6 +26,7 @@ logScope:
topics = "main-module-controller"
const UNIQUE_MAIN_MODULE_IDENTIFIER* = "MainModule"
const UNIQUE_MAIN_MODULE_KEYCARD_SYNC_IDENTIFIER* = "MainModule-KeycardSyncPurpose"
type
Controller* = ref object of RootObj
@ -270,6 +271,10 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED) do(e: Args):
let args = SharedKeycarModuleFlowTerminatedArgs(e)
if args.uniqueIdentifier == UNIQUE_MAIN_MODULE_KEYCARD_SYNC_IDENTIFIER:
self.delegate.onSharedKeycarModuleKeycardSyncPurposeTerminated(args.lastStepInTheCurrentFlow)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_KEYCARD_SYNC_TERMINATED, Args())
return
if args.uniqueIdentifier != UNIQUE_MAIN_MODULE_IDENTIFIER or
self.authenticateUserFlowRequestedBy.len == 0:
return
@ -293,6 +298,10 @@ proc init*(self: Controller) =
self.authenticateUserFlowRequestedBy = args.uniqueIdentifier
self.delegate.runAuthenticationPopup(args.keyUid)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC) do(e: Args):
let args = SharedKeycarModuleArgs(e)
self.delegate.tryKeycardSync(args.keyUid, args.pin)
proc isConnected*(self: Controller): bool =
return self.nodeService.isConnected()

View File

@ -232,6 +232,12 @@ method activateStatusDeepLink*(self: AccessInterface, statusDeepLink: string) {.
method setCommunityIdToSpectate*(self: AccessInterface, commnityId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method tryKeycardSync*(self: AccessInterface, keyUid: string, pin: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onSharedKeycarModuleKeycardSyncPurposeTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} =
raise newException(ValueError, "No implementation available")
# This way (using concepts) is used only for the modules managed by AppController
type
DelegateInterface* = concept c

View File

@ -94,6 +94,7 @@ type
nodeSectionModule: node_section_module.AccessInterface
networksModule: networks_module.AccessInterface
keycardSharedModule: keycard_shared_module.AccessInterface
keycardSharedModuleKeycardSyncPurpose: keycard_shared_module.AccessInterface
moduleLoaded: bool
statusUrlCommunityToSpectate: string
@ -209,6 +210,8 @@ method delete*[T](self: Module[T]) =
self.networksModule.delete
if not self.keycardSharedModule.isNil:
self.keycardSharedModule.delete
if not self.keycardSharedModuleKeycardSyncPurpose.isNil:
self.keycardSharedModuleKeycardSyncPurpose.delete
self.view.delete
self.viewVariant.delete
self.controller.delete
@ -983,6 +986,19 @@ method runAuthenticationPopup*[T](self: Module[T], keyUid: string) =
return
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.Authentication, keyUid)
method onSharedKeycarModuleKeycardSyncPurposeTerminated*[T](self: Module[T], lastStepInTheCurrentFlow: bool) =
if not self.keycardSharedModuleKeycardSyncPurpose.isNil:
self.keycardSharedModuleKeycardSyncPurpose.delete
self.keycardSharedModuleKeycardSyncPurpose = nil
method tryKeycardSync*[T](self: Module[T], keyUid: string, pin: string) =
self.keycardSharedModuleKeycardSyncPurpose = keycard_shared_module.newModule[Module[T]](self, UNIQUE_MAIN_MODULE_KEYCARD_SYNC_IDENTIFIER,
self.events, self.keycardService, self.settingsService, self.privacyService, self.accountsService,
self.walletAccountService, self.keychainService)
if self.keycardSharedModuleKeycardSyncPurpose.isNil:
return
self.keycardSharedModuleKeycardSyncPurpose.syncKeycardBasedOnAppState(keyUid, pin)
method onDisplayKeycardSharedModuleFlow*[T](self: Module[T]) =
self.view.emitDisplayKeycardSharedModuleFlow()

View File

@ -55,19 +55,29 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardLocked(args.keycardUid)
self.delegate.onKeycardLocked(args.keyPair.keyUid, args.keyPair.keycardUid)
self.events.on(SIGNAL_KEYCARD_UNLOCKED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardUnlocked(args.keycardUid)
self.delegate.onKeycardUnlocked(args.keyPair.keyUid, args.keyPair.keycardUid)
self.events.on(SIGNAL_KEYCARD_NAME_CHANGED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardNameChanged(args.keycardUid, args.keycardNewName)
self.delegate.onKeycardNameChanged(args.keyPair.keycardUid, args.keyPair.keycardName)
self.events.on(SIGNAL_KEYCARD_UID_UPDATED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardUidUpdated(args.keycardUid, args.keycardNewUid)
self.delegate.onKeycardUidUpdated(args.oldKeycardUid, args.keyPair.keycardUid)
self.events.on(SIGNAL_KEYCARD_ACCOUNTS_REMOVED) do(e: Args):
let args = KeycardActivityArgs(e)
if not args.success:
return
self.delegate.onKeycardAccountsRemoved(args.keyPair.keyUid, args.keyPair.keycardUid, args.keyPair.accountsAddresses)
self.events.on(SIGNAL_WALLET_ACCOUNT_UPDATED) do(e: Args):
let args = WalletAccountUpdated(e)
self.delegate.onWalletAccountUpdated(args.account)
proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] =
return self.walletAccountService.getAllMigratedKeyPairs()

View File

@ -1,5 +1,6 @@
import NimQml
from ../../../../../app_service/service/wallet_account/service import KeyPairDto
from ../../../../../app_service/service/wallet_account/service import WalletAccountDto
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -68,10 +69,10 @@ method onLoggedInUserImageChanged*(self: AccessInterface) {.base.} =
method onNewKeycardSet*(self: AccessInterface, keyPair: KeyPairDto) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardLocked*(self: AccessInterface, keycardUid: string) {.base.} =
method onKeycardLocked*(self: AccessInterface, keyUid: string, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardUnlocked*(self: AccessInterface, keycardUid: string) {.base.} =
method onKeycardUnlocked*(self: AccessInterface, keyUid: string, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardNameChanged*(self: AccessInterface, keycardUid: string, keycardNewName: string) {.base.} =
@ -80,6 +81,12 @@ method onKeycardNameChanged*(self: AccessInterface, keycardUid: string, keycardN
method onKeycardUidUpdated*(self: AccessInterface, keycardUid: string, keycardNewUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardAccountsRemoved*(self: AccessInterface, keyUid: string, keycardUid: string, accountsToRemove: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available")
method onWalletAccountUpdated*(self: AccessInterface, account: WalletAccountDto) {.base.} =
raise newException(ValueError, "No implementation available")
method prepareKeycardDetailsModel*(self: AccessInterface, keyUid: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -89,7 +89,11 @@ method viewDidLoad*(self: Module) =
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
proc isSharedKeycardModuleFlowRunning(self: Module): bool =
return not self.keycardSharedModule.isNil
method getKeycardSharedModule*(self: Module): QVariant =
if self.isSharedKeycardModuleFlowRunning():
return self.keycardSharedModule.getModuleAsVariant()
proc createSharedKeycardModule(self: Module) =
@ -97,9 +101,6 @@ proc createSharedKeycardModule(self: Module) =
self.events, self.keycardService, self.settingsService, self.privacyService, self.accountsService,
self.walletAccountService, self.keychainService)
proc isSharedKeycardModuleFlowRunning(self: Module): bool =
return not self.keycardSharedModule.isNil
method onSharedKeycarModuleFlowTerminated*(self: Module, lastStepInTheCurrentFlow: bool) =
if self.isSharedKeycardModuleFlowRunning():
self.view.emitDestroyKeycardSharedModuleFlow()
@ -181,14 +182,14 @@ method runCreateNewPairingCodePopup*(self: Module, keyUid: string) =
return
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.ChangePairingCode, keyUid)
proc buildKeycardItem(self: Module, walletAccounts: seq[WalletAccountDto], keyPair: KeyPairDto, reason: BuildItemReason):
KeycardItem =
let findAccountByAccountAddress = proc(accounts: seq[WalletAccountDto], address: string): WalletAccountDto =
proc findAccountByAccountAddress(accounts: seq[WalletAccountDto], address: string): WalletAccountDto =
for i in 0 ..< accounts.len:
if cmpIgnoreCase(accounts[i].address, address) == 0:
return accounts[i]
return nil
proc buildKeycardItem(self: Module, walletAccounts: seq[WalletAccountDto], keyPair: KeyPairDto, reason: BuildItemReason):
KeycardItem =
let isAccountInKnownAccounts = proc(knownAccounts: seq[WalletAccountDto], address: string): bool =
for i in 0 ..< knownAccounts.len:
if cmpIgnoreCase(knownAccounts[i].address, address) == 0:
@ -196,11 +197,14 @@ proc buildKeycardItem(self: Module, walletAccounts: seq[WalletAccountDto], keyPa
return false
var knownAccounts: seq[WalletAccountDto]
var unknownAccountsAddresses: seq[string]
for accAddr in keyPair.accountsAddresses:
let account = findAccountByAccountAddress(walletAccounts, accAddr)
if account.isNil:
## we should never be here cause we need to remove deleted accounts from the `keypairs` table and sync
## that state accross different app instances
## We are here if the keycard is not sync yet with the app's state. That may happen if there are more copies of the
## same keycard, then deleting an account for a keypair syncs the inserted keycard, but other copies of the card
## remain with that account till the moment they are synced.
unknownAccountsAddresses.add(accAddr)
continue
if reason == BuildItemReason.MainView and
(isAccountInKnownAccounts(knownAccounts, accAddr) or
@ -230,6 +234,12 @@ proc buildKeycardItem(self: Module, walletAccounts: seq[WalletAccountDto], keyPa
item.setPairType(KeyPairType.PrivateKeyImport.int)
item.setIcon("keycard")
item.addAccount(newKeyPairAccountItem(ka.name, ka.path, ka.address, ka.publicKey, ka.emoji, ka.color, icon = icon, balance = 0.0))
if reason == BuildItemReason.DetailsView:
var i = 0
for ua in unknownAccountsAddresses:
i.inc
let name = "acc" & $i
item.addAccount(newKeyPairAccountItem(name, path = "", ua, pubKey = "", emoji = "", color = "#939BA1", icon = "wallet", balance = 0.0))
return item
proc areAllKnownKeycardsLockedForKeypair(self: Module, keyUid: string): bool =
@ -260,39 +270,76 @@ method onLoggedInUserImageChanged*(self: Module) =
method onNewKeycardSet*(self: Module, keyPair: KeyPairDto) =
let walletAccounts = self.controller.getWalletAccounts()
let mainViewItem = self.buildKeycardItem(walletAccounts, keyPair, BuildItemReason.MainView)
var mainViewItem = self.view.keycardModel().getItemForKeyUid(keyPair.keyUid)
if mainViewItem.isNil:
mainViewItem = self.buildKeycardItem(walletAccounts, keyPair, BuildItemReason.MainView)
if not mainViewItem.isNil:
self.view.keycardModel().addItem(mainViewItem)
else:
for accAddr in keyPair.accountsAddresses:
if mainViewItem.containsAccountAddress(accAddr):
continue
let account = findAccountByAccountAddress(walletAccounts, accAddr)
if account.isNil:
## we should never be here cause all keypairs are firstly added to wallet
continue
mainViewItem.addAccount(newKeyPairAccountItem(account.name, account.path, account.address, account.publicKey,
account.emoji, account.color, icon = "", balance = 0.0))
if self.view.keycardDetailsModel().isNil:
return
let detailsViewItem = self.buildKeycardItem(walletAccounts, keyPair, BuildItemReason.DetailsView)
var detailsViewItem = self.view.keycardDetailsModel().getItemForKeycardUid(keyPair.keycardUid)
if detailsViewItem.isNil:
detailsViewItem = self.buildKeycardItem(walletAccounts, keyPair, BuildItemReason.DetailsView)
if not detailsViewItem.isNil:
self.view.keycardDetailsModel().addItem(detailsViewItem)
else:
for accAddr in keyPair.accountsAddresses:
if detailsViewItem.containsAccountAddress(accAddr):
continue
let account = findAccountByAccountAddress(walletAccounts, accAddr)
if account.isNil:
## we should never be here cause all keypairs are firstly added to wallet
continue
detailsViewItem.addAccount(newKeyPairAccountItem(account.name, account.path, account.address, account.publicKey,
account.emoji, account.color, icon = "", balance = 0.0))
method onKeycardLocked*(self: Module, keycardUid: string) =
self.view.keycardModel().setLocked(keycardUid, true)
method onKeycardLocked*(self: Module, keyUid: string, keycardUid: string) =
self.view.keycardModel().setLockedForKeycardsWithKeyUid(keyUid, true)
if self.view.keycardDetailsModel().isNil:
return
self.view.keycardDetailsModel().setLocked(keycardUid, true)
self.view.keycardDetailsModel().setLockedForKeycardWithKeycardUid(keycardUid, true)
method onKeycardUnlocked*(self: Module, keycardUid: string) =
self.view.keycardModel().setLocked(keycardUid, false)
method onKeycardUnlocked*(self: Module, keyUid: string, keycardUid: string) =
self.view.keycardModel().setLockedForKeycardsWithKeyUid(keyUid, false)
if self.view.keycardDetailsModel().isNil:
return
self.view.keycardDetailsModel().setLocked(keycardUid, false)
self.view.keycardDetailsModel().setLockedForKeycardWithKeycardUid(keycardUid, false)
method onKeycardNameChanged*(self: Module, keycardUid: string, keycardNewName: string) =
self.view.keycardModel().setName(keycardUid, keycardNewName)
self.view.keycardModel().setNameForKeycardWithKeycardUid(keycardUid, keycardNewName)
if self.view.keycardDetailsModel().isNil:
return
self.view.keycardDetailsModel().setName(keycardUid, keycardNewName)
self.view.keycardDetailsModel().setNameForKeycardWithKeycardUid(keycardUid, keycardNewName)
method onKeycardUidUpdated*(self: Module, keycardUid: string, keycardNewUid: string) =
self.view.keycardModel().setKeycardUid(keycardUid, keycardNewUid)
if self.view.keycardDetailsModel().isNil:
return
self.view.keycardDetailsModel().setKeycardUid(keycardUid, keycardNewUid)
method onKeycardAccountsRemoved*(self: Module, keyUid: string, keycardUid: string, accountsToRemove: seq[string]) =
self.view.keycardModel().removeAccountsFromKeycardsWithKeyUid(keyUid, accountsToRemove, removeKeycardItemIfHasNoAccounts = true)
if self.view.keycardDetailsModel().isNil:
return
self.view.keycardDetailsModel().removeAccountsFromKeycardWithKeycardUid(keycardUid, accountsToRemove, removeKeycardItemIfHasNoAccounts = true)
method onWalletAccountUpdated*(self: Module, account: WalletAccountDto) =
self.view.keycardModel().updateDetailsForAddressForKeyPairsWithKeyUid(account.keyUid, account.address, account.name,
account.color, account.emoji)
if self.view.keycardDetailsModel().isNil:
return
self.view.keycardDetailsModel().updateDetailsForAddressForKeyPairsWithKeyUid(account.keyUid, account.address, account.name,
account.color, account.emoji)
method prepareKeycardDetailsModel*(self: Module, keyUid: string) =
let walletAccounts = self.controller.getWalletAccounts()
var items: seq[KeycardItem]

View File

@ -37,7 +37,10 @@ QtObject:
self.delegate.viewDidLoad()
proc getKeycardSharedModule(self: View): QVariant {.slot.} =
return self.delegate.getKeycardSharedModule()
let module = self.delegate.getKeycardSharedModule()
if not module.isNil:
return module
return newQVariant()
QtProperty[QVariant] keycardSharedModule:
read = getKeycardSharedModule

View File

@ -222,7 +222,10 @@ QtObject:
self.displayUserProfile(publicKey)
proc getKeycardSharedModule(self: View): QVariant {.slot.} =
return self.delegate.getKeycardSharedModule()
let module = self.delegate.getKeycardSharedModule()
if not module.isNil:
return module
return newQVariant()
QtProperty[QVariant] keycardSharedModule:
read = getKeycardSharedModule

View File

@ -51,7 +51,7 @@ proc init*(self: Controller) =
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.password)
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED_AND_WALLET_ADDRESS_GENERATED) do(e: Args):
let args = SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs(e)
@ -66,6 +66,12 @@ proc init*(self: Controller) =
derivedAddress = args.derivedAddresses[0]
self.delegate.addressDetailsFetched(derivedAddress, args.error)
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED) do(e: Args):
let args = SharedKeycarModuleFlowTerminatedArgs(e)
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER:
return
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
return self.walletAccountService.getWalletAccounts()
@ -84,8 +90,8 @@ proc addAccountsFromSeed*(self: Controller, seedPhrase: string, password: string
proc addWatchOnlyAccount*(self: Controller, address: string, accountName: string, color: string, emoji: string): string =
return self.walletAccountService.addWatchOnlyAccount(address, accountName, color, emoji)
proc deleteAccount*(self: Controller, address: string) =
self.walletAccountService.deleteAccount(address)
proc deleteAccount*(self: Controller, address: string, keyPairMigratedToKeycard: bool) =
self.walletAccountService.deleteAccount(address, keyPairMigratedToKeycard)
proc fetchDerivedAddressDetails*(self: Controller, address: string) =
self.walletAccountService.fetchDerivedAddressDetails(address)
@ -127,3 +133,7 @@ proc getCurrentCurrency*(self: Controller): string =
proc getCurrencyFormat*(self: Controller, symbol: string): CurrencyFormatDto =
return self.currencyService.getCurrencyFormat(symbol)
proc getMigratedKeyPairByKeyUid*(self: Controller, keyUid: string): seq[KeyPairDto] =
return self.walletAccountService.getMigratedKeyPairByKeyUid(keyUid)

View File

@ -13,6 +13,9 @@ method load*(self: AccessInterface) {.base.} =
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method syncKeycard*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method generateNewAccount*(self: AccessInterface, password: string, accountName: string, color: string, emoji: string, path: string, derivedFrom: string): string {.base.} =
raise newException(ValueError, "No implementation available")
@ -29,7 +32,7 @@ method addAccountsFromSeed*(self: AccessInterface, seedPhrase: string, password:
method addWatchOnlyAccount*(self: AccessInterface, address: string, accountName: string, color: string, emoji: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method deleteAccount*(self: AccessInterface, address: string) {.base.} =
method deleteAccount*(self: AccessInterface, keyUid: string, address: string) {.base.} =
raise newException(ValueError, "No implementation available")
method refreshWalletAccounts*(self: AccessInterface) {.base.} =
@ -56,7 +59,7 @@ method validSeedPhrase*(self: AccessInterface, value: string): bool {.base.} =
method authenticateUser*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, password: string) {.base.} =
method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method authenticateUserAndDeriveAddressOnKeycardForPath*(self: AccessInterface, keyUid: string, derivationPath: string) {.base.} =
@ -74,3 +77,6 @@ method onUserAuthenticatedAndWalletAddressGenerated*(self: AccessInterface, addr
method addressDetailsFetched*(self: AccessInterface, derivedAddress: DerivedAddressDto, error: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onSharedKeycarModuleFlowTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -13,12 +13,19 @@ import ../../../../../app_service/service/token/service as token_service
import ../../../../../app_service/service/currency/service as currency_service
import ../../../shared_models/token_model as token_model
import ../../../shared_models/token_item as token_item
import ../../../shared_modules/keycard_popup/models/key_pair_item as keycard_key_pair_item
import ../../../shared_modules/keycard_popup/module as keycard_shared_module
import ./compact_item as compact_item
import ./compact_model as compact_model
export io_interface
type
AuthenticationReason {.pure.} = enum
LoggedInUserAuthentication = 0
DeriveAccountForKeyPairAuthentication
DeleteAccountAuthentication
type WalletAccountDetails = object
address: string
path: string
@ -37,7 +44,8 @@ type
accountsService: accounts_service.Service
walletAccountService: wallet_account_service.Service
keycardSharedModule: keycard_shared_module.AccessInterface
walletAccountWhichIsAboutToBeAdded: WalletAccountDetails
processingWalletAccount: WalletAccountDetails
authentiactionReason: AuthenticationReason
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -58,6 +66,7 @@ proc newModule*(
result.view = newView(result)
result.controller = controller.newController(result, events, walletAccountService, accountsService, networkService, tokenService, currencyService)
result.moduleLoaded = false
result.authentiactionReason = AuthenticationReason.LoggedInUserAuthentication
method delete*(self: Module) =
self.view.delete
@ -65,6 +74,11 @@ method delete*(self: Module) =
if not self.keycardSharedModule.isNil:
self.keycardSharedModule.delete
method onSharedKeycarModuleFlowTerminated*(self: Module, lastStepInTheCurrentFlow: bool) =
if not self.keycardSharedModule.isNil:
self.keycardSharedModule.delete
self.keycardSharedModule = nil
method refreshWalletAccounts*(self: Module) =
let keyPairMigrated = proc(migratedKeyPairs: seq[KeyPairDto], keyUid: string): bool =
for kp in migratedKeyPairs:
@ -144,6 +158,10 @@ method viewDidLoad*(self: Module) =
self.moduleLoaded = true
self.delegate.accountsModuleDidLoad()
proc tryKeycardSync(self: Module, keyUid, pin: string) =
let dataForKeycardToSync = SharedKeycarModuleArgs(pin: pin, keyUid: keyUid)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC, dataForKeycardToSync)
method generateNewAccount*(self: Module, password: string, accountName: string, color: string, emoji: string,
path: string, derivedFrom: string): string =
let skipPasswordVerification = singletonInstance.userProfile.getIsKeycardUser()
@ -162,8 +180,15 @@ method addAccountsFromSeed*(self: Module, seedPhrase: string, password: string,
method addWatchOnlyAccount*(self: Module, address: string, accountName: string, color: string, emoji: string): string =
return self.controller.addWatchOnlyAccount(address, accountName, color, emoji)
method deleteAccount*(self: Module, address: string) =
self.controller.deleteAccount(address)
method deleteAccount*(self: Module, keyUid: string, address: string) =
let keyPair = self.controller.getMigratedKeyPairByKeyUid(keyUid)
let keyPairMigratedToKeycard = keyPair.len > 0
if not keyPairMigratedToKeycard:
self.controller.deleteAccount(address, keyPairMigratedToKeycard)
else:
self.authentiactionReason = AuthenticationReason.DeleteAccountAuthentication
self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, address: address)
self.controller.authenticateUser(keyUid)
method getDerivedAddressList*(self: Module, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int, hashPassword: bool) =
self.controller.getDerivedAddressList(password, derivedFrom, path, pageSize, pageNumber, hashPassword)
@ -178,24 +203,36 @@ method validSeedPhrase*(self: Module, value: string): bool =
return self.controller.validSeedPhrase(value)
method authenticateUser*(self: Module) =
self.authentiactionReason = AuthenticationReason.LoggedInUserAuthentication
if singletonInstance.userProfile.getIsKeycardUser():
let keyUid = singletonInstance.userProfile.getKeyUid()
self.controller.authenticateUser(keyUid)
else:
self.controller.authenticateUser()
method onUserAuthenticated*(self: Module, password: string) =
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string) =
if self.authentiactionReason == AuthenticationReason.LoggedInUserAuthentication or
self.authentiactionReason == AuthenticationReason.DeriveAccountForKeyPairAuthentication:
if password.len > 0:
self.view.userAuthenticationSuccess(password)
else:
self.view.userAuthentiactionFail()
elif self.authentiactionReason == AuthenticationReason.DeleteAccountAuthentication:
if self.processingWalletAccount.keyUid == keyUid:
self.controller.deleteAccount(self.processingWalletAccount.address, true)
self.tryKeycardSync(keyUid, pin)
method createSharedKeycardModule*(self: Module) =
if self.keycardSharedModule.isNil:
self.keycardSharedModule = keycard_shared_module.newModule[Module](self, UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_IDENTIFIER,
self.events, self.keycardService, settingsService = nil, privacyService = nil, self.accountsService,
self.walletAccountService, keychainService = nil)
method destroySharedKeycarModule*(self: Module) =
if not self.keycardSharedModule.isNil:
let kpForProcessing = self.keycardSharedModule.getKeyPairForProcessing()
if not kpForProcessing.isNil:
self.tryKeycardSync(kpForProcessing.getKeyUid(), self.keycardSharedModule.getPin())
self.keycardSharedModule.delete
self.keycardSharedModule = nil
@ -221,12 +258,13 @@ proc findFirstAvaliablePathForWallet(self: Module, keyUid: string): string =
error "we couldn't find available wallet account path"
method authenticateUserAndDeriveAddressOnKeycardForPath*(self: Module, keyUid: string, derivationPath: string) =
self.authentiactionReason = AuthenticationReason.DeriveAccountForKeyPairAuthentication
var finalPath = derivationPath
if self.checkIfWalletAccountIsAlreadyCreated(keyUid, finalPath):
finalPath = self.findFirstAvaliablePathForWallet(keyUid)
if self.keycardSharedModule.isNil:
self.createSharedKeycardModule()
self.walletAccountWhichIsAboutToBeAdded = WalletAccountDetails(keyUid: keyUid, path: finalPath)
self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, path: finalPath)
self.view.setDerivedAddressesLoading(true)
self.view.setDerivedAddressesError("")
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.AuthenticateAndDeriveAccountAddress, keyUid, finalPath)
@ -238,27 +276,27 @@ method onUserAuthenticatedAndWalletAddressGenerated*(self: Module, address: stri
if password.len == 0:
self.view.setDerivedAddressesLoading(false)
return
self.onUserAuthenticated(password)
self.walletAccountWhichIsAboutToBeAdded.address = address
self.walletAccountWhichIsAboutToBeAdded.addressAccountIsDerivedFrom = derivedFrom
self.walletAccountWhichIsAboutToBeAdded.publicKey = publicKey
self.onUserAuthenticated(pin = "", password, keyUid = "")
self.processingWalletAccount.address = address
self.processingWalletAccount.addressAccountIsDerivedFrom = derivedFrom
self.processingWalletAccount.publicKey = publicKey
self.controller.fetchDerivedAddressDetails(address)
method addressDetailsFetched*(self: Module, derivedAddress: DerivedAddressDto, error: string) =
var derivedAddressDto = derivedAddress
if error.len > 0:
derivedAddressDto.address = self.walletAccountWhichIsAboutToBeAdded.address
derivedAddressDto.address = self.processingWalletAccount.address
derivedAddressDto.alreadyCreated = true
self.view.setDerivedAddresses(@[derivedAddressDto], error)
method addNewWalletAccountGeneratedFromKeycard*(self: Module, accountType: string, accountName: string, color: string,
emoji: string): string =
return self.controller.addWalletAccount(accountName,
self.walletAccountWhichIsAboutToBeAdded.address,
self.walletAccountWhichIsAboutToBeAdded.path,
self.walletAccountWhichIsAboutToBeAdded.addressAccountIsDerivedFrom,
self.walletAccountWhichIsAboutToBeAdded.publicKey,
self.walletAccountWhichIsAboutToBeAdded.keyUid,
self.processingWalletAccount.address,
self.processingWalletAccount.path,
self.processingWalletAccount.addressAccountIsDerivedFrom,
self.processingWalletAccount.publicKey,
self.processingWalletAccount.keyUid,
accountType,
color,
emoji)

View File

@ -222,8 +222,8 @@ QtObject:
proc addWatchOnlyAccount*(self: View, address: string, accountName: string, color: string, emoji: string): string {.slot.} =
return self.delegate.addWatchOnlyAccount(address, accountName, color, emoji)
proc deleteAccount*(self: View, address: string) {.slot.} =
self.delegate.deleteAccount(address)
proc deleteAccount*(self: View, keyUid: string, address: string) {.slot.} =
self.delegate.deleteAccount(keyUid, address)
proc getAccountNameByAddress*(self: View, address: string): string {.slot.} =
return self.model.getAccountNameByAddress(address)

View File

@ -18,6 +18,7 @@ QtObject:
delegate: io_interface.AccessInterface
defaultAccount: account_item.Item
name: string
keyUid: string
address: string
mixedcaseAddress: string
path: string
@ -57,6 +58,13 @@ QtObject:
read = getName
notify = nameChanged
proc getKeyUid(self: View): QVariant {.slot.} =
return newQVariant(self.keyUid)
proc keyUidChanged(self: View) {.signal.}
QtProperty[QVariant] keyUid:
read = getKeyUid
notify = keyUidChanged
proc getAddress(self: View): QVariant {.slot.} =
return newQVariant(self.address)
proc addressChanged(self: View) {.signal.}
@ -182,6 +190,9 @@ QtObject:
if(self.name != item.getName()):
self.name = item.getName()
self.nameChanged()
if(self.keyUid != item.getKeyUid()):
self.keyUid = item.getKeyUid()
self.keyUidChanged()
if(self.address != item.getAddress()):
self.address = item.getAddress()
self.addressChanged()

View File

@ -7,6 +7,8 @@ import ../../../global/app_signals
import ../../../global/global_singleton
import ../../../core/signals/types
import ../../../core/eventemitter
import ../../startup/io_interface as startup_io
import ../../../../app_service/common/utils
import ../../../../app_service/common/account_constants
import ../../../../app_service/service/keycard/service as keycard_service
import ../../../../app_service/service/settings/service as settings_service
@ -34,6 +36,7 @@ type
connectionIds: seq[UUID]
keychainConnectionIds: seq[UUID]
connectionKeycardResponse: UUID
connectionKeycardSyncTerminatedSignal: UUID
tmpKeycardContainsMetadata: bool
tmpCardMetadata: CardMetadata
tmpPin: string
@ -49,6 +52,7 @@ type
tmpSeedPhrase: string
tmpSeedPhraseLength: int
tmpKeyUidWhichIsBeingAuthenticating: string
tmpKeyUidWhichIsBeingSyncing: string
tmpUsePinFromBiometrics: bool
tmpOfferToStoreUpdatedPinToKeychain: bool
tmpKeycardUid: string
@ -58,6 +62,8 @@ type
tmpKeycardCopyCardMetadata: CardMetadata
tmpKeycardCopyPin: string
tmpKeycardCopyDestinationKeycardUid: string
tmpKeycardSyncingInProgress: bool
tmpFlowData: SharedKeycarModuleFlowTerminatedArgs
proc newController*(delegate: io_interface.AccessInterface,
uniqueIdentifier: string,
@ -87,6 +93,10 @@ proc newController*(delegate: io_interface.AccessInterface,
result.tmpUsePinFromBiometrics = false
result.tmpAddingMigratedKeypairSuccess = false
result.tmpConvertingProfileSuccess = false
result.tmpKeycardSyncingInProgress = false
## Forward declaration:
proc finishFlowTermination(self: Controller)
proc serviceApplicable[T](service: T): bool =
if not service.isNil:
@ -115,6 +125,15 @@ proc connectKeycardReponseSignal(self: Controller) =
let args = KeycardArgs(e)
self.delegate.onKeycardResponse(args.flowType, args.flowEvent)
proc disconnectKeycardSyncSignal(self: Controller) =
self.events.disconnect(self.connectionKeycardSyncTerminatedSignal)
proc connectKeycardSyncSignal(self: Controller) =
self.connectionKeycardSyncTerminatedSignal = self.events.onWithUUID(SIGNAL_SHARED_KEYCARD_MODULE_KEYCARD_SYNC_TERMINATED) do(e: Args):
self.disconnectKeycardSyncSignal()
self.connectKeycardReponseSignal()
self.finishFlowTermination()
proc connectKeychainSignals*(self: Controller) =
var handlerId = self.events.onWithUUID(SIGNAL_KEYCHAIN_SERVICE_SUCCESS) do(e:Args):
let args = KeyChainServiceArg(e)
@ -248,7 +267,19 @@ proc getPairingCode*(self: Controller): string =
return self.tmpPairingCode
proc getKeyUidWhichIsBeingAuthenticating*(self: Controller): string =
self.tmpKeyUidWhichIsBeingAuthenticating
return self.tmpKeyUidWhichIsBeingAuthenticating
proc getKeyUidWhichIsBeingSyncing*(self: Controller): string =
return self.tmpKeyUidWhichIsBeingSyncing
proc setKeyUidWhichIsBeingSyncing*(self: Controller, value: string) =
self.tmpKeyUidWhichIsBeingSyncing = value
proc keycardSyncingInProgress*(self: Controller): bool =
return self.tmpKeycardSyncingInProgress
proc setKeycardSyncingInProgress*(self: Controller, value: bool) =
self.tmpKeycardSyncingInProgress = value
proc setSelectedKeyPair*(self: Controller, isProfile: bool, paths: seq[string], keyPairDto: KeyPairDto) =
if paths.len != keyPairDto.accountsAddresses.len:
@ -410,11 +441,11 @@ proc runGetAppInfoFlow*(self: Controller, factoryReset = false) =
self.cancelCurrentFlow()
self.keycardService.startGetAppInfoFlow(factoryReset)
proc runGetMetadataFlow*(self: Controller, resolveAddress = false, exportMasterAddr = false) =
proc runGetMetadataFlow*(self: Controller, resolveAddress = false, exportMasterAddr = false, pin = "") =
if not serviceApplicable(self.keycardService):
return
self.cancelCurrentFlow()
self.keycardService.startGetMetadataFlow(resolveAddress, exportMasterAddr)
self.keycardService.startGetMetadataFlow(resolveAddress, exportMasterAddr, pin)
proc runChangePinFlow*(self: Controller) =
if not serviceApplicable(self.keycardService):
@ -491,7 +522,9 @@ proc readyToDisplayPopup*(self: Controller) =
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP, data)
proc cleanTmpData(self: Controller) =
# we should not reset here `tmpKeycardSyncingInProgress` property
self.tmpKeyUidWhichIsBeingAuthenticating = ""
self.tmpKeyUidWhichIsBeingSyncing = ""
self.tmpAddingMigratedKeypairSuccess = false
self.tmpConvertingProfileSuccess = false
self.tmpCardMetadata = CardMetadata()
@ -512,18 +545,40 @@ proc cleanTmpData(self: Controller) =
self.setPinForKeycardCopy("")
self.setDestinationKeycardUid("")
proc finishFlowTermination(self: Controller) =
let data = self.tmpFlowData
self.tmpFlowData = nil
self.cleanTmpData()
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED, data)
proc terminateCurrentFlow*(self: Controller, lastStepInTheCurrentFlow: bool) =
let flowType = self.delegate.getCurrentFlowType()
self.cancelCurrentFlow()
let (_, flowEvent) = self.getLastReceivedKeycardData()
var data = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
self.tmpFlowData = SharedKeycarModuleFlowTerminatedArgs(uniqueIdentifier: self.uniqueIdentifier,
lastStepInTheCurrentFlow: lastStepInTheCurrentFlow)
if lastStepInTheCurrentFlow:
let exportedEncryptionPubKey = flowEvent.generatedWalletAccount.publicKey
data.password = if exportedEncryptionPubKey.len > 0: exportedEncryptionPubKey else: self.getPassword()
data.pin = self.getPin()
data.keyUid = flowEvent.keyUid
self.cleanTmpData()
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED, data)
self.tmpFlowData.password = if exportedEncryptionPubKey.len > 0: exportedEncryptionPubKey else: self.getPassword()
self.tmpFlowData.pin = self.getPin()
self.tmpFlowData.keyUid = flowEvent.keyUid
## we're trying to sync a keycard state on popup close if:
## - shared module is not run from the onboarding flow
## - the keycard syncing is not already in progress
## - the flow which is terminating is one of the flows which we need to perform a sync process for
## - the pin is known
if self.uniqueIdentifier != startup_io.UNIQUE_STARTUP_MODULE_IDENTIFIER and
not self.keycardSyncingInProgress() and
not utils.arrayContains(FlowsWeShouldNotTryAKeycardSyncFor, flowType) and
self.getPin().len == PINLengthForStatusApp and
flowEvent.keyUid.len > 0:
let dataForKeycardToSync = SharedKeycarModuleArgs(pin: self.getPin(), keyUid: flowEvent.keyUid)
self.disconnectKeycardReponseSignal()
self.connectKeycardSyncSignal()
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC, dataForKeycardToSync)
return
self.finishFlowTermination()
proc authenticateUser*(self: Controller, keyUid = "") =
self.disconnectKeycardReponseSignal()
@ -554,6 +609,11 @@ proc addMigratedKeyPair*(self: Controller, keyPair: KeyPairDto) =
let keystoreDir = self.accountsService.getKeyStoreDir()
self.walletAccountService.addMigratedKeyPairAsync(keyPair, keystoreDir)
proc removeMigratedAccountsForKeycard*(self: Controller, keyUid: string, keycardUid: string, accountsToRemove: seq[string]) =
if not serviceApplicable(self.walletAccountService):
return
self.walletAccountService.removeMigratedAccountsForKeycard(keyUid, keycardUid, accountsToRemove)
proc getAddingMigratedKeypairSuccess*(self: Controller): bool =
return self.tmpAddingMigratedKeypairSuccess
@ -572,30 +632,30 @@ proc getAllKnownKeycards*(self: Controller): seq[KeyPairDto] =
return
return self.walletAccountService.getAllKnownKeycards()
proc setCurrentKeycardStateToLocked*(self: Controller, keycardUid: string) =
proc setCurrentKeycardStateToLocked*(self: Controller, keyUid: string, keycardUid: string) =
if not serviceApplicable(self.walletAccountService):
return
if not self.walletAccountService.setKeycardLocked(keycardUid):
info "updating keycard locked state failed", keycardUid=keycardUid
if not self.walletAccountService.setKeycardLocked(keyUid, keycardUid):
info "updating keycard locked state failed", keyUid=keyUid, keycardUid=keycardUid
proc setCurrentKeycardStateToUnlocked*(self: Controller, keycardUid: string) =
proc setCurrentKeycardStateToUnlocked*(self: Controller, keyUid: string, keycardUid: string) =
if not serviceApplicable(self.walletAccountService):
return
if not self.walletAccountService.setKeycardUnlocked(keycardUid):
info "updating keycard unlocked state failed", keycardUid=keycardUid
if not self.walletAccountService.setKeycardUnlocked(keyUid, keycardUid):
info "updating keycard unlocked state failed", keyUid=keyUid, keycardUid=keycardUid
proc updateKeycardName*(self: Controller, keycardUid: string, keycardName: string): bool =
proc updateKeycardName*(self: Controller, keyUid: string, keycardUid: string, keycardName: string): bool =
if not serviceApplicable(self.walletAccountService):
return false
if not self.walletAccountService.updateKeycardName(keycardUid, keycardName):
info "updating keycard name failed", keycardUid=keycardUid
if not self.walletAccountService.updateKeycardName(keyUid, keycardUid, keycardName):
info "updating keycard name failed", keyUid=keyUid, keycardUid=keycardUid, keycardName=keycardName
return false
return true
proc updateKeycardUid*(self: Controller, keycardUid: string) =
proc updateKeycardUid*(self: Controller, keyUid: string, keycardUid: string) =
if not serviceApplicable(self.walletAccountService):
return
self.setCurrentKeycardStateToUnlocked(self.tmpKeycardUid)
self.setCurrentKeycardStateToUnlocked(keyUid, self.tmpKeycardUid)
if self.tmpKeycardUid != keycardUid:
if not self.walletAccountService.updateKeycardUid(self.tmpKeycardUid, keycardUid):
self.tmpKeycardUid = keycardUid

View File

@ -14,13 +14,15 @@ method executeCancelCommand*(self: KeycardEmptyMetadataState, controller: Contro
self.flowType == FlowType.SetupNewKeycardNewSeedPhrase or
self.flowType == FlowType.SetupNewKeycardOldSeedPhrase or
self.flowType == FlowType.ImportFromKeycard or
self.flowType == FlowType.UnlockKeycard or
self.flowType == FlowType.DisplayKeycardContent or
self.flowType == FlowType.RenameKeycard or
self.flowType == FlowType.CreateCopyOfAKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executePrePrimaryStateCommand*(self: KeycardEmptyMetadataState, controller: Controller) =
if self.flowType == FlowType.DisplayKeycardContent or
if self.flowType == FlowType.UnlockKeycard or
self.flowType == FlowType.DisplayKeycardContent or
self.flowType == FlowType.ImportFromKeycard or
self.flowType == FlowType.RenameKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)

View File

@ -15,7 +15,7 @@ method executePrePrimaryStateCommand*(self: RenamingKeycardState, controller: Co
let md = controller.getMetadataFromKeycard()
let paths = md.walletAccounts.map(a => a.path)
let name = controller.getKeyPairForProcessing().getName()
self.success = controller.updateKeycardName(controller.getKeycardUid(), name)
self.success = controller.updateKeycardName(controller.getKeyPairForProcessing().getKeyUid(), controller.getKeycardUid(), name)
if self.success:
controller.runStoreMetadataFlow(name, controller.getPin(), paths)
else:

View File

@ -75,7 +75,7 @@ method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, k
return createState(StateType.MaxPukRetriesReached, self.flowType, nil)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setPukValid(true)
controller.updateKeycardUid(keycardEvent.instanceUID)
controller.updateKeycardUid(keycardEvent.keyUid, keycardEvent.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.LoadAccount:
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
@ -89,5 +89,5 @@ method resolveKeycardNextState*(self: RepeatPinState, keycardFlowType: string, k
if controller.getCurrentKeycardServiceFlow() == KCSFlowType.StoreMetadata:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.instanceUID.len > 0:
controller.updateKeycardUid(keycardEvent.instanceUID)
controller.updateKeycardUid(keycardEvent.keyUid, keycardEvent.instanceUID)
return createState(StateType.PinSet, self.flowType, nil)

View File

@ -307,6 +307,8 @@ proc ensureReaderAndCardPresenceAndResolveNextState*(state: State, keycardFlowTy
keycardEvent.error.len > 0:
if keycardEvent.error == ErrorNoKeys:
return createState(StateType.KeycardEmpty, state.flowType, nil)
if keycardEvent.error == ErrorNoData:
return createState(StateType.KeycardEmptyMetadata, state.flowType, nil)
if state.flowType == FlowType.DisplayKeycardContent:
controller.setKeyPairForProcessing(newKeyPairItem(keyUid = keycardEvent.keyUid)) # must set keypair in case of running some other flow which needs e.g. keyuid. like unlock flow

View File

@ -38,5 +38,5 @@ method resolveKeycardNextState*(self: WrongPukState, keycardFlowType: string, ke
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
if keycardEvent.error.len == 0:
controller.setPukValid(true)
controller.updateKeycardUid(keycardEvent.instanceUID)
controller.updateKeycardUid(keycardEvent.keyUid, keycardEvent.instanceUID)
return createState(StateType.UnlockKeycardSuccess, self.flowType, nil)

View File

@ -15,31 +15,30 @@ proc delete*(self: WrongSeedPhraseState) =
method executePrePrimaryStateCommand*(self: WrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
sleep(500) # just to shortly remove text on the UI side
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getSelectedKeyPairDto().keyUid
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.CreateCopyOfAKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
sleep(500) # just to shortly remove text on the UI side
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyPairForProcessing().getKeyUid()
if self.verifiedSeedPhrase:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.UnlockKeycard:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
sleep(500) # just to shortly remove text on the UI side
self.verifiedSeedPhrase = controller.validSeedPhrase(controller.getSeedPhrase()) and
controller.getKeyUidForSeedPhrase(controller.getSeedPhrase()) == controller.getKeyPairForProcessing().getKeyUid()
if self.verifiedSeedPhrase:
controller.runGetMetadataFlow()
else:
method getNextPrimaryState*(self: WrongSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.SetupNewKeycard or
self.flowType == FlowType.CreateCopyOfAKeycard or
self.flowType == FlowType.UnlockKeycard:
if not self.verifiedSeedPhrase:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
return self
method executeCancelCommand*(self: WrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.SetupNewKeycard or

View File

@ -9,6 +9,8 @@ const SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP* = "sharedKeycarModuleDisplayPo
const SIGNAL_SHARED_KEYCARD_MODULE_FLOW_TERMINATED* = "sharedKeycarModuleFlowTerminated"
const SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER* = "sharedKeycarModuleAuthenticateUser"
const SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED* = "sharedKeycarModuleUserAuthenticated"
const SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC* = "sharedKeycarModuleTryKeycardSync"
const SIGNAL_SHARED_KEYCARD_MODULE_KEYCARD_SYNC_TERMINATED* = "sharedKeycarModuleKeycardSyncTerminated"
## Authentication in the app is a global thing and may be used from any part of the app. How to achieve that... it's enough just to send
## `SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER` signal with properly set `SharedKeycarModuleAuthenticationArgs` and props there:
@ -45,6 +47,14 @@ type
SharedKeycarModuleAuthenticationArgs* = ref object of SharedKeycarModuleBaseArgs
keyUid*: string
type
SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs* = ref object of Args
uniqueIdentifier*: string
address*: string
publicKey*: string
derivedFrom*: string
password*: string
type FlowType* {.pure.} = enum
General = "General"
FactoryReset = "FactoryReset"
@ -62,13 +72,17 @@ type FlowType* {.pure.} = enum
AuthenticateAndDeriveAccountAddress = "AuthenticateAndDeriveAccountAddress"
CreateCopyOfAKeycard = "CreateCopyOfAKeycard"
type
SharedKeycarModuleUserAuthenticatedAndWalletAddressGeneratedArgs* = ref object of Args
uniqueIdentifier*: string
address*: string
publicKey*: string
derivedFrom*: string
password*: string
# For the following flows we don't run card syncing.
const FlowsWeShouldNotTryAKeycardSyncFor* = @[
FlowType.General,
FlowType.FactoryReset,
FlowType.SetupNewKeycard,
FlowType.SetupNewKeycardNewSeedPhrase,
FlowType.SetupNewKeycardOldSeedPhrase,
FlowType.ImportFromKeycard,
FlowType.Authentication,
FlowType.AuthenticateAndDeriveAccountAddress
]
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -79,6 +93,9 @@ method delete*(self: AccessInterface) {.base.} =
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method getCurrentFlowType*(self: AccessInterface): FlowType {.base.} =
raise newException(ValueError, "No implementation available")
method getKeycardData*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
@ -178,5 +195,11 @@ method keychainObtainedDataFailure*(self: AccessInterface, errorDescription: str
method keychainObtainedDataSuccess*(self: AccessInterface, data: string) {.base.} =
raise newException(ValueError, "No implementation available")
method syncKeycardBasedOnAppState*(self: AccessInterface, keyUid: string, pin: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getPin*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
type
DelegateInterface* = concept c

View File

@ -1,4 +1,4 @@
import NimQml, Tables, strformat
import NimQml, Tables, strformat, strutils
import key_pair_account_item
export key_pair_account_item
@ -72,6 +72,12 @@ QtObject:
self.endInsertRows()
self.countChanged()
proc containsAccountAddress*(self: KeyPairAccountModel, address: string): bool =
for it in self.items:
if cmpIgnoreCase(it.getAddress(), address) == 0:
return true
return false
proc getItemAtIndex*(self: KeyPairAccountModel, index: int): KeyPairAccountItem =
if index < 0 or index >= self.items.len:
return newKeyPairAccountItem()
@ -86,3 +92,20 @@ QtObject:
self.items.delete(index)
self.endRemoveRows()
self.countChanged()
proc removeItemByAddress*(self: KeyPairAccountModel, address: string) =
for i in 0 ..< self.items.len:
if cmpIgnoreCase(self.items[i].getAddress(), address) == 0:
self.removeItemAtIndex(i)
return
proc updateDetailsForAddressIfTheyAreSet*(self: KeyPairAccountModel, address, name, color, emoji: string) =
for i in 0 ..< self.items.len:
if cmpIgnoreCase(self.items[i].getAddress(), address) == 0:
if name.len > 0:
self.items[i].setName(name)
if color.len > 0:
self.items[i].setColor(color)
if emoji.len > 0:
self.items[i].setEmoji(emoji)
return

View File

@ -181,12 +181,19 @@ QtObject:
proc removeAccountAtIndex*(self: KeyPairItem, index: int) {.slot.} =
self.accounts.removeItemAtIndex(index)
self.setLastAccountAsObservedAccount()
proc removeAccountByAddress*(self: KeyPairItem, address: string) {.slot.} =
self.accounts.removeItemByAddress(address)
self.setLastAccountAsObservedAccount()
proc addAccount*(self: KeyPairItem, item: KeyPairAccountItem) =
self.accounts.addItem(item)
self.setLastAccountAsObservedAccount()
proc setAccounts*(self: KeyPairItem, items: seq[KeyPairAccountItem]) =
self.accounts.setItems(items)
self.setLastAccountAsObservedAccount()
proc containsAccountAddress*(self: KeyPairItem, address: string): bool =
return self.accounts.containsAccountAddress(address)
proc updateDetailsForAccountWithAddressIfTheyAreSet*(self: KeyPairItem, address, name, color, emoji: string) =
self.accounts.updateDetailsForAddressIfTheyAreSet(address, name, color, emoji)
proc setItem*(self: KeyPairItem, item: KeyPairItem) =
self.setKeyUid(item.getKeyUid())

View File

@ -53,6 +53,16 @@ QtObject:
self.countChanged()
self.lockedItemsCountChanged()
proc removeItem*(self: KeycardModel, index: int) =
if (index < 0 or index >= self.items.len):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, index, index)
self.items.delete(index)
self.endRemoveRows()
self.countChanged()
proc `$`*(self: KeycardModel): string =
for i in 0 ..< self.items.len:
result &= fmt"""KeycardModel:
@ -84,6 +94,12 @@ QtObject:
return self.items[i]
return nil
proc getItemForKeycardUid*(self: KeycardModel, keycardUid: string): KeycardItem =
for i in 0 ..< self.items.len:
if(self.items[i].getKeycardUid() == keycardUid):
return self.items[i]
return nil
proc findIndexForMember(self: KeycardModel, pubKey: string): int =
for i in 0 ..< self.items.len:
if(self.items[i].getPubKey() == pubKey):
@ -96,12 +112,19 @@ QtObject:
return
self.items[ind].setImage(image)
proc setLocked*(self: KeycardModel, keycardUid: string, locked: bool) =
proc setLockedForKeycardWithKeycardUid*(self: KeycardModel, keycardUid: string, locked: bool) =
for i in 0 ..< self.items.len:
if(self.items[i].getKeycardUid() == keycardUid):
self.items[i].setLocked(locked)
self.lockedItemsCountChanged()
proc setName*(self: KeycardModel, keycardUid: string, name: string) =
proc setLockedForKeycardsWithKeyUid*(self: KeycardModel, keyUid: string, locked: bool) =
for i in 0 ..< self.items.len:
if(self.items[i].getKeyUid() == keyUid):
self.items[i].setLocked(locked)
self.lockedItemsCountChanged()
proc setNameForKeycardWithKeycardUid*(self: KeycardModel, keycardUid: string, name: string) =
for i in 0 ..< self.items.len:
if(self.items[i].getKeycardUid() == keycardUid):
self.items[i].setName(name)
@ -110,3 +133,27 @@ QtObject:
for i in 0 ..< self.items.len:
if(self.items[i].getKeycardUid() == keycardUid):
self.items[i].setKeycardUid(keycardNewUid)
proc removeAccountsFromKeycardWithKeycardUid*(self: KeycardModel, keycardUid: string, accountsToRemove: seq[string],
removeKeycardItemIfHasNoAccounts: bool) =
for i in 0 ..< self.items.len:
if(self.items[i].getKeycardUid() == keycardUid):
for acc in accountsToRemove:
self.items[i].removeAccountByAddress(acc)
if removeKeycardItemIfHasNoAccounts and self.items[i].getAccountsModel().getCount() == 0:
self.removeItem(i)
proc removeAccountsFromKeycardsWithKeyUid*(self: KeycardModel, keyUid: string, accountsToRemove: seq[string],
removeKeycardItemIfHasNoAccounts: bool) =
for i in 0 ..< self.items.len:
if(self.items[i].getKeyUid() == keyUid):
for acc in accountsToRemove:
self.items[i].removeAccountByAddress(acc)
if removeKeycardItemIfHasNoAccounts and self.items[i].getAccountsModel().getCount() == 0:
self.removeItem(i)
proc updateDetailsForAddressForKeyPairsWithKeyUid*(self: KeycardModel, keyUid: string, accAddress: string, accName: string,
accColor: string, accEmoji: string) =
for i in 0 ..< self.items.len:
if(self.items[i].getKeyUid() == keyUid):
self.items[i].updateDetailsForAccountWithAddressIfTheyAreSet(accAddress, accName, accColor, accEmoji)

View File

@ -1,4 +1,4 @@
import NimQml, random, strutils, marshal, chronicles
import NimQml, random, strutils, marshal, sequtils, sugar, chronicles
import io_interface
import view, controller
@ -7,6 +7,8 @@ import models/[key_pair_model, key_pair_item]
import ../../../global/global_singleton
import ../../../core/eventemitter
import ../../../../app_service/common/utils
import ../../../../app_service/service/keycard/constants
import ../../../../app_service/service/keycard/service as keycard_service
import ../../../../app_service/service/settings/service as settings_service
import ../../../../app_service/service/privacy/service as privacy_service
@ -62,9 +64,17 @@ method delete*[T](self: Module[T]) =
self.viewVariant.delete
self.controller.delete
proc init[T](self: Module[T]) =
if not self.initialized:
self.initialized = true
self.controller.init()
method getModuleAsVariant*[T](self: Module[T]): QVariant =
return self.viewVariant
method getPin*[T](self: Module[T]): string =
return self.controller.getPin()
method getKeycardData*[T](self: Module[T]): string =
return self.view.getKeycardData()
@ -125,15 +135,19 @@ method checkRepeatedKeycardPukWhileTyping*[T](self: Module[T], puk: string): boo
self.controller.setPukMatch(match)
return match
method getMnemonic*[T](self: Module[T]): string =
method getCurrentFlowType*[T](self: Module[T]): FlowType =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
error "sm_cannot resolve current state in order to determine mnemonic"
return
if currStateObj.flowType() == FlowType.SetupNewKeycard:
return FlowType.General
return currStateObj.flowType()
method getMnemonic*[T](self: Module[T]): string =
let flowType = self.getCurrentFlowType()
if flowType == FlowType.SetupNewKeycard:
return self.controller.getProfileMnemonic()
if currStateObj.flowType() == FlowType.SetupNewKeycardNewSeedPhrase or
currStateObj.flowType() == FlowType.SetupNewKeycardOldSeedPhrase:
if flowType == FlowType.SetupNewKeycardNewSeedPhrase or
flowType == FlowType.SetupNewKeycardOldSeedPhrase:
return self.controller.getSeedPhrase()
method setSeedPhrase*[T](self: Module[T], value: string) =
@ -151,6 +165,30 @@ method migratingProfileKeyPair*[T](self: Module[T]): bool =
method getSigningPhrase*[T](self: Module[T]): string =
return self.controller.getSigningPhrase()
proc preActionActivities[T](self: Module[T], currFlowType: FlowType, currStateType: StateType) =
if currStateType == StateType.ManageKeycardAccounts or
currStateType == StateType.CreatePin or
currStateType == StateType.RepeatPin or
currStateType == StateType.CreatePuk or
currStateType == StateType.RepeatPuk or
currStateType == StateType.EnterPuk or
currStateType == StateType.WrongPuk:
self.view.setDisablePopup(false)
return
if currStateType == StateType.EnterPin or
currStateType == StateType.CreatePin or
currStateType == StateType.RepeatPin or
currStateType == StateType.WrongPin:
let disable = self.controller.getPin().len == PINLengthForStatusApp
self.view.setDisablePopup(disable)
return
if currStateType == StateType.CreatePuk or
currStateType == StateType.RepeatPuk:
let disable = self.controller.getPUK().len == PUKLengthForStatusApp
self.view.setDisablePopup(disable)
return
self.view.setDisablePopup(true)
proc preStateActivities[T](self: Module[T], currFlowType: FlowType, nextStateType: StateType) =
if nextStateType == StateType.MaxPinRetriesReached or
nextStateType == StateType.MaxPukRetriesReached or
@ -158,7 +196,7 @@ proc preStateActivities[T](self: Module[T], currFlowType: FlowType, nextStateTyp
nextStateType == StateType.UnlockKeycardOptions:
## in case the card is locked on another device, we're updating its state in the DB
let (_, flowEvent) = self.controller.getLastReceivedKeycardData()
self.controller.setCurrentKeycardStateToLocked(flowEvent.instanceUID)
self.controller.setCurrentKeycardStateToLocked(flowEvent.keyUid, flowEvent.instanceUID)
if currFlowType == FlowType.Authentication:
self.view.setLockedPropForKeyPairForProcessing(nextStateType == StateType.MaxPinRetriesReached)
@ -185,12 +223,89 @@ proc reEvaluateKeyPairForProcessing[T](self: Module[T], currFlowType: FlowType,
keycardUid = flowEvent.instanceUID
self.prepareKeyPairForProcessing(self.getKeyPairForProcessing().getKeyUid(), keycardUid)
proc handleKeycardSyncing[T](self: Module[T]) =
if not self.controller.keycardSyncingInProgress():
return
let kcsFlowType = self.controller.getCurrentKeycardServiceFlow()
if kcsFlowType == KCSFlowType.GetMetadata:
let (eventType, flowEvent) = self.controller.getLastReceivedKeycardData()
if flowEvent.keyUid != self.controller.getKeyUidWhichIsBeingSyncing():
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
return
if eventType == ResponseTypeValueKeycardFlowResult and flowEvent.error.len == 0:
var kpDto = KeyPairDto(keycardUid: flowEvent.instanceUID,
keycardName: flowEvent.cardMetadata.name,
keycardLocked: false,
accountsAddresses: @[],
keyUid: flowEvent.keyUid)
let alreadySetKeycards = self.controller.getAllKnownKeycards().filter(kp => kp.keycardUid == flowEvent.instanceUID)
if alreadySetKeycards.len == 1:
var accountsToRemove = alreadySetKeycards[0].accountsAddresses
let appAccounts = self.controller.getWalletAccounts()
var activeValidPathsToStoreToAKeycard: seq[string]
for appAcc in appAccounts:
if appAcc.keyUid != flowEvent.keyUid:
continue
activeValidPathsToStoreToAKeycard.add(appAcc.path)
var index = -1
var found = false
for acc in accountsToRemove:
index.inc
if cmpIgnoreCase(acc, appAcc.address) == 0:
found = true
break
if found and index > -1:
# if account address which is present in the wallet is still present in accounts addresses of a keycard,
# then it needs to be present in `keypairs` table in db, so remove it from the list
accountsToRemove.delete(index)
else:
# we store to db only accounts we haven't stored before, accounts which are already on a keycard (in metadata)
# we assume they are already in the db
kpDto.accountsAddresses.add(appAcc.address)
if accountsToRemove.len > 0:
self.controller.removeMigratedAccountsForKeycard(kpDto.keyUid, kpDto.keycardUid, accountsToRemove)
if kpDto.accountsAddresses.len > 0:
self.controller.addMigratedKeyPair(kpDto)
# if all accounts are removed from the app, there is no point in storing empty accounts list to a keycard, cause in that case
# keypair which is on that keycard won't be known to the app, that means keypair was removed from the app
if activeValidPathsToStoreToAKeycard.len > 0:
## we need to store paths to a keycard if the num of paths in the app and on a keycard is diffrent
## or if the paths are different
var storeToKeycard = activeValidPathsToStoreToAKeycard.len != flowEvent.cardMetadata.walletAccounts.len
if not storeToKeycard:
for wa in flowEvent.cardMetadata.walletAccounts:
if not utils.arrayContains(activeValidPathsToStoreToAKeycard, wa.path):
storeToKeycard = true
break
if storeToKeycard:
self.controller.runStoreMetadataFlow(flowEvent.cardMetadata.name, self.controller.getPin(), activeValidPathsToStoreToAKeycard)
return
elif alreadySetKeycards.len > 1:
error "it's impossible to have more then one keycard with the same uid", keycarUid=flowEvent.instanceUID
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
method syncKeycardBasedOnAppState*[T](self: Module[T], keyUid: string, pin: string) =
## This method must not be called directly. If you want to initiate keycard syncing please emit
## `SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC` signal
if pin.len != PINLengthForStatusApp:
debug "cannot sync with the pin which doesn't meet app expectations"
return
if keyUid.len == 0:
debug "cannot sync with the empty keyUid"
return
self.init()
self.controller.setKeyUidWhichIsBeingSyncing(keyUid)
self.controller.setPin(pin)
self.controller.setKeycardSyncingInProgress(true)
self.controller.runGetMetadataFlow(resolveAddress = true, exportMasterAddr = true, pin)
method onBackActionClicked*[T](self: Module[T]) =
let currStateObj = self.view.currentStateObj()
if currStateObj.isNil:
error "sm_cannot resolve current state"
return
debug "sm_back_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
self.preActionActivities(currStateObj.flowType(), currStateObj.stateType())
currStateObj.executePreBackStateCommand(self.controller)
let backState = currStateObj.getBackState()
self.preStateActivities(backState.flowType(), backState.stateType())
@ -205,6 +320,7 @@ method onCancelActionClicked*[T](self: Module[T]) =
error "sm_cannot resolve current state"
return
debug "sm_cancel_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
self.preActionActivities(currStateObj.flowType(), currStateObj.stateType())
currStateObj.executeCancelCommand(self.controller)
method onPrimaryActionClicked*[T](self: Module[T]) =
@ -213,6 +329,7 @@ method onPrimaryActionClicked*[T](self: Module[T]) =
error "sm_cannot resolve current state"
return
debug "sm_primary_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
self.preActionActivities(currStateObj.flowType(), currStateObj.stateType())
currStateObj.executePrePrimaryStateCommand(self.controller)
let nextState = currStateObj.getNextPrimaryState(self.controller)
if nextState.isNil:
@ -229,6 +346,7 @@ method onSecondaryActionClicked*[T](self: Module[T]) =
error "sm_cannot resolve current state"
return
debug "sm_secondary_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
self.preActionActivities(currStateObj.flowType(), currStateObj.stateType())
currStateObj.executePreSecondaryStateCommand(self.controller)
let nextState = currStateObj.getNextSecondaryState(self.controller)
if nextState.isNil:
@ -240,6 +358,9 @@ method onSecondaryActionClicked*[T](self: Module[T]) =
debug "sm_secondary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) =
if self.controller.keycardSyncingInProgress():
self.handleKeycardSyncing()
return
if self.derivingAccountDetails.deriveAddressAfterAuthentication and
self.derivingAccountDetails.addressRequested:
# clearing...
@ -390,9 +511,7 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
error "sm_cannot run an general flow"
return
if not self.initialized:
self.initialized = true
self.controller.init()
self.init()
if flowToRun == FlowType.FactoryReset:
self.tmpLocalState = newReadingKeycardState(flowToRun, nil)
self.controller.runGetMetadataFlow(resolveAddress = true)
@ -449,6 +568,7 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path
self.controller.runChangePairingFlow()
return
if flowToRun == FlowType.AuthenticateAndDeriveAccountAddress:
self.prepareKeyPairForProcessing(keyUid) ## needed for keycard sync
self.derivingAccountDetails = DerivingAccountDetails(
keyUid: keyUid,
path: bip44Path,
@ -517,6 +637,10 @@ proc buildKeyPairItemBasedOnCardMetadata[T](self: Module[T], cardMetadata: CardM
icon = "keycard",
pairType = KeyPairType.Unknown,
derivedFrom = "")
let currKp = self.getKeyPairForProcessing()
if not currKp.isNil:
result.item.setKeyUid(currKp.getKeyUid())
result.item.setPubKey(currKp.getPubKey())
result.knownKeyPair = true
for wa in cardMetadata.walletAccounts:
if self.updateKeyPairItemIfDataAreKnown(wa.address, result.item):
@ -539,10 +663,11 @@ method onUserAuthenticated*[T](self: Module[T], password: string, pin: string) =
if self.derivingAccountDetails.deriveAddressAfterAuthentication:
self.derivingAccountDetails.addressRequested = true
self.controller.setPassword(password)
self.controller.setPin(pin) # we need to keep it in case new acc is added we need to sync accounts on the Keycard
self.controller.runDeriveAccountFlow(self.derivingAccountDetails.path, pin)
return
let currStateObj = self.view.currentStateObj()
if not currStateObj.isNil and currStateObj.flowType() == FlowType.SetupNewKeycard:
let flowType = self.getCurrentFlowType()
if flowType == FlowType.SetupNewKeycard:
self.controller.setPassword(password)
self.onSecondaryActionClicked()
@ -580,4 +705,3 @@ method keychainObtainedDataSuccess*[T](self: Module[T], data: string) =
self.controller.enterKeycardPin(data)
else:
self.view.setCurrentState(newBiometricsPinInvalidState(FlowType.Authentication, nil))

View File

@ -7,6 +7,7 @@ QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
disablePopup: bool # used to disable popup after each action, to block users do multiple clikcs which the action is ongoing
currentState: StateWrapper
currentStateVariant: QVariant
keyPairModel: KeyPairModel
@ -43,16 +44,28 @@ QtObject:
result.currentState = newStateWrapper()
result.currentStateVariant = newQVariant(result.currentState)
result.remainingAttempts = -1
result.disablePopup = false
signalConnect(result.currentState, "backActionClicked()", result, "onBackActionClicked()", 2)
signalConnect(result.currentState, "cancelActionClicked()", result, "onCancelActionClicked()", 2)
signalConnect(result.currentState, "primaryActionClicked()", result, "onPrimaryActionClicked()", 2)
signalConnect(result.currentState, "secondaryActionClicked()", result, "onSecondaryActionClicked()", 2)
proc diablePopupChanged*(self: View) {.signal.}
proc getDisablePopup*(self: View): bool {.slot.} =
return self.disablePopup
QtProperty[bool] disablePopup:
read = getDisablePopup
notify = diablePopupChanged
proc setDisablePopup*(self: View, value: bool) =
self.disablePopup = value
self.diablePopupChanged()
proc currentStateObj*(self: View): State =
return self.currentState.getStateObj()
proc setCurrentState*(self: View, state: State) =
self.setDisablePopup(false)
self.currentState.setStateObj(state)
proc getCurrentState(self: View): QVariant {.slot.} =
return self.currentStateVariant

View File

@ -17,8 +17,6 @@ import ../shared_modules/keycard_popup/io_interface as keycard_shared_module
logScope:
topics = "startup-controller"
const UNIQUE_STARTUP_MODULE_IDENTIFIER* = "SartupModule"
type ProfileImageDetails = object
url*: string
croppedImage*: string

View File

@ -3,6 +3,7 @@ import ../../../app_service/service/accounts/service as accounts_service
import models/login_account_item as login_acc_item
from ../../../app_service/service/keycard/service import KeycardEvent, KeyDetails
const UNIQUE_STARTUP_MODULE_IDENTIFIER* = "SartupModule"
type
AccessInterface* {.pure inheritable.} = ref object of RootObj

View File

@ -111,7 +111,11 @@ method load*[T](self: Module[T]) =
self.setSelectedLoginAccount(items[0])
self.delegate.startupDidLoad()
proc isSharedKeycardModuleFlowRunning[T](self: Module[T]): bool =
return not self.keycardSharedModule.isNil
method getKeycardSharedModule*[T](self: Module[T]): QVariant =
if self.isSharedKeycardModuleFlowRunning():
return self.keycardSharedModule.getModuleAsVariant()
proc createSharedKeycardModule[T](self: Module[T]) =
@ -119,9 +123,6 @@ proc createSharedKeycardModule[T](self: Module[T]) =
self.events, self.keycardService, settingsService = nil, privacyService = nil, self.accountsService,
walletAccountService = nil, self.keychainService)
proc isSharedKeycardModuleFlowRunning[T](self: Module[T]): bool =
return not self.keycardSharedModule.isNil
method moveToLoadingAppState*[T](self: Module[T]) =
self.view.setAppState(AppState.AppLoadingState)

View File

@ -82,7 +82,10 @@ QtObject:
read = getCurrentStartupState
proc getKeycardSharedModule(self: View): QVariant {.slot.} =
return self.delegate.getKeycardSharedModule()
let module = self.delegate.getKeycardSharedModule()
if not module.isNil:
return module
return newQVariant()
QtProperty[QVariant] keycardSharedModule:
read = getKeycardSharedModule

View File

@ -1,10 +1,13 @@
import json, random, times, strutils, os, re, chronicles
import json, random, times, strutils, sugar, os, re, chronicles
import nimcrypto
import signing_phrases
const STATUS_DOMAIN* = ".stateofus.eth"
const ETH_DOMAIN* = ".eth"
proc arrayContains*[T](arr: seq[T], value: T): bool =
return arr.any(x => x == value)
proc hashPassword*(password: string): string =
result = "0x" & $keccak_256.digest(password)

View File

@ -219,12 +219,14 @@ QtObject:
self.currentFlow = KCSFlowType.GetAppInfo
self.startFlow(payload)
proc startGetMetadataFlow*(self: Service, resolveAddress: bool, exportMasterAddr = false) =
proc startGetMetadataFlow*(self: Service, resolveAddress: bool, exportMasterAddr = false, pin = "") =
var payload = %* { }
if resolveAddress:
payload[RequestParamResolveAddr] = %* resolveAddress
if exportMasterAddr:
payload[RequestParamExportMasterAddress] = %* exportMasterAddr
if pin.len > 0:
payload[RequestParamPIN] = %* pin
self.currentFlow = KCSFlowType.GetMetadata
self.startFlow(payload)

View File

@ -122,7 +122,37 @@ const addMigratedKeyPairTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall
arg.keyPair.accountsAddresses,
arg.keyStoreDir
)
arg.finish(response)
let success = responseHasNoErrors("addMigratedKeyPair", response)
let responseJson = %*{
"success": success,
"keyPair": arg.keyPair.toJsonNode()
}
arg.finish(responseJson)
except Exception as e:
error "error adding new keypair: ", message = e.msg
arg.finish("")
#################################################
# Async add migrated keypair
#################################################
type
RemoveMigratedAccountsForKeycardTaskArg* = ref object of QObjectTaskArg
keyPair: KeyPairDto
const removeMigratedAccountsForKeycardTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[RemoveMigratedAccountsForKeycardTaskArg](argEncoded)
try:
let response = backend.removeMigratedAccountsForKeycard(
arg.keyPair.keycardUid,
arg.keyPair.accountsAddresses
)
let success = responseHasNoErrors("removeMigratedAccountsForKeycard", response)
let responseJson = %*{
"success": success,
"keyPair": arg.keyPair.toJsonNode()
}
arg.finish(responseJson)
except Exception as e:
error "error remove accounts from keycard: ", message = e.msg
arg.finish("")

View File

@ -2,6 +2,13 @@ import json
include ../../common/json_utils
const KeycardUid = "keycard-uid"
const KeycardName = "keycard-name"
const KeycardLocked = "keycard-locked"
const KeyUid = "key-uid"
const AccountAddresses = "accounts-addresses"
type KeyPairDto* = object
keycardUid*: string
keycardName*: string
@ -11,12 +18,21 @@ type KeyPairDto* = object
proc toKeyPairDto*(jsonObj: JsonNode): KeyPairDto =
result = KeyPairDto()
discard jsonObj.getProp("keycard-uid", result.keycardUid)
discard jsonObj.getProp("keycard-name", result.keycardName)
discard jsonObj.getProp("keycard-locked", result.keycardLocked)
discard jsonObj.getProp("key-uid", result.keyUid)
discard jsonObj.getProp(KeycardUid, result.keycardUid)
discard jsonObj.getProp(KeycardName, result.keycardName)
discard jsonObj.getProp(KeycardLocked, result.keycardLocked)
discard jsonObj.getProp(KeyUid, result.keyUid)
var jArr: JsonNode
if(jsonObj.getProp("accounts-addresses", jArr) and jArr.kind == JArray):
if(jsonObj.getProp(AccountAddresses, jArr) and jArr.kind == JArray):
for addrObj in jArr:
result.accountsAddresses.add(addrObj.getStr)
proc toJsonNode*(self: KeyPairDto): JsonNode =
result = %* {
KeycardUid: self.keycardUid,
KeycardName: self.keycardName,
KeycardLocked: self.keycardLocked,
KeyUid: self.keyUid,
AccountAddresses: self.accountsAddresses
}

View File

@ -34,6 +34,7 @@ const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt"
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED* = "walletAccount/derivedAddressDetailsFetched"
const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet"
const SIGNAL_KEYCARD_ACCOUNTS_REMOVED* = "keycardAccountsRemoved"
const SIGNAL_KEYCARD_LOCKED* = "keycardLocked"
const SIGNAL_KEYCARD_UNLOCKED* = "keycardUnlocked"
const SIGNAL_KEYCARD_UID_UPDATED* = "keycardUidUpdated"
@ -77,8 +78,8 @@ type TokenVisibilityToggled = ref object of Args
type NetwordkEnabledToggled = ref object of Args
type WalletAccountUpdated = ref object of Args
account: WalletAccountDto
type WalletAccountUpdated* = ref object of Args
account*: WalletAccountDto
type DerivedAddressesArgs* = ref object of Args
derivedAddresses*: seq[DerivedAddressDto]
@ -89,11 +90,20 @@ type TokensPerAccountArgs* = ref object of Args
type KeycardActivityArgs* = ref object of Args
success*: bool
keycardUid*: string
keycardNewUid*: string
keycardNewName*: string
oldKeycardUid*: string
keyPair*: KeyPairDto
proc responseHasNoErrors(procName: string, response: RpcResponse[JsonNode]): bool =
var errMsg = ""
if not response.error.isNil:
errMsg = "(" & $response.error.code & ") " & response.error.message
elif response.result.kind == JObject and response.result.contains("error"):
errMsg = response.result["error"].getStr
if(errMsg.len == 0):
return true
error "error: ", procName=procName, errDesription = errMsg
return false
include async_tasks
include ../../common/json_utils
@ -240,6 +250,17 @@ QtObject:
self.buildAllTokens(@[newAccount.address])
self.events.emit(SIGNAL_WALLET_ACCOUNT_SAVED, AccountSaved(account: newAccount))
proc addOrReplaceWalletAccount(self: Service, name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType,
color, emoji: string, walletDefaultAccount = false, chatDefaultAccount = false): string =
try:
let response = status_go_accounts.saveAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
accountType, color, emoji, walletDefaultAccount, chatDefaultAccount)
if not response.error.isNil:
return "(" & $response.error.code & ") " & response.error.message
except Exception as e:
error "error: ", procName="addWalletAccount", errName = e.name, errDesription = e.msg
return "error: " & e.msg
proc generateNewAccount*(self: Service, password: string, accountName: string, color: string, emoji: string,
path: string, derivedFrom: string, skipPasswordVerification: bool): string =
try:
@ -325,12 +346,17 @@ QtObject:
self.addNewAccountToLocalStore()
proc deleteAccount*(self: Service, address: string) =
proc deleteAccount*(self: Service, address: string, keyPairMigrated: bool) =
try:
if keyPairMigrated:
discard status_go_accounts.deleteAccountForMigratedKeypair(address)
else:
discard status_go_accounts.deleteAccount(address)
let accountDeleted = self.walletAccounts[address]
self.walletAccounts.del(address)
self.events.emit(SIGNAL_WALLET_ACCOUNT_DELETED, AccountDeleted(account: accountDeleted))
except Exception as e:
error "error: ", procName="deleteAccount", errName = e.name, errDesription = e.msg
proc getCurrency*(self: Service): string =
return self.settingsService.getCurrency()
@ -351,19 +377,16 @@ QtObject:
self.events.emit(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED, NetwordkEnabledToggled())
proc updateWalletAccount*(self: Service, address: string, accountName: string, color: string, emoji: string) =
let account = self.walletAccounts[address]
status_go_accounts.updateAccount(
accountName,
account.address,
account.publicKey,
account.walletType,
color,
emoji
)
if not self.walletAccounts.hasKey(address):
error "account's address is not among known addresses: ", address=address
return
var account = self.walletAccounts[address]
let res = self.addOrReplaceWalletAccount(accountName, account.address, account.path, account.derivedfrom,
account.publicKey, account.keyUid, account.walletType, color, emoji, account.isWallet, account.isChat)
if res.len == 0:
account.name = accountName
account.color = color
account.emoji = emoji
self.events.emit(SIGNAL_WALLET_ACCOUNT_UPDATED, WalletAccountUpdated(account: account))
proc getDerivedAddressList*(self: Service, password: string, derivedFrom: string, path: string, pageSize: int, pageNumber: int, hashPassword: bool)=
@ -453,7 +476,8 @@ QtObject:
error "error: ", procName="onAllTokensBuilt", errName = e.name, errDesription = e.msg
proc buildAllTokens(self: Service, accounts: seq[string]) =
if(not singletonInstance.localAccountSensitiveSettings.getIsWalletEnabled()):
if not singletonInstance.localAccountSensitiveSettings.getIsWalletEnabled() or
accounts.len == 0:
return
let arg = BuildTokensTaskArg(
@ -492,17 +516,6 @@ QtObject:
let chainIds = self.networkService.getNetworks().filter(a => a.enabled).map(a => a.chainId)
return self.getWalletAccounts().map(a => a.getCurrencyBalance(chainIds, self.getCurrentCurrencyIfEmpty(currency))).foldl(a + b, 0.0)
proc responseHasNoErrors(self: Service, procName: string, response: RpcResponse[JsonNode]): bool =
var errMsg = ""
if not response.error.isNil:
errMsg = "(" & $response.error.code & ") " & response.error.message
elif response.result.kind == JObject and response.result.contains("error"):
errMsg = response.result["error"].getStr
if(errMsg.len == 0):
return true
error "error: ", procName=procName, errDesription = errMsg
return false
proc addMigratedKeyPairAsync*(self: Service, keyPair: KeyPairDto, keyStoreDir: string) =
let arg = AddMigratedKeyPairTaskArg(
tptr: cast[ByteAddress](addMigratedKeyPairTask),
@ -511,18 +524,19 @@ QtObject:
keyPair: keyPair,
keyStoreDir: keyStoreDir
)
self.processedKeyPair = keyPair
self.threadpool.start(arg)
proc onMigratedKeyPairAdded*(self: Service, response: string) {.slot.} =
var result = false
var data = KeycardActivityArgs()
data.success = false
try:
let rpcResponse = Json.decode(response, RpcResponse[JsonNode])
result = self.responseHasNoErrors("addMigratedKeyPair", rpcResponse)
let responseObj = response.parseJson
discard responseObj.getProp("success", data.success)
var kpJson: JsonNode
if responseObj.getProp("keyPair", kpJson):
data.keyPair = kpJson.toKeyPairDto()
except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg
let data = KeycardActivityArgs(success: result, keyPair: self.processedKeyPair)
self.processedKeyPair = KeyPairDto()
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data)
proc addMigratedKeyPair*(self: Service, keyPair: KeyPairDto, keyStoreDir: string): bool =
@ -534,16 +548,38 @@ QtObject:
keyPair.accountsAddresses,
keyStoreDir
)
result = self.responseHasNoErrors("addMigratedKeyPair", response)
result = responseHasNoErrors("addMigratedKeyPair", response)
if result:
self.events.emit(SIGNAL_NEW_KEYCARD_SET, KeycardActivityArgs(success: true, keyPair: keyPair))
except Exception as e:
error "error: ", procName="addMigratedKeyPair", errName = e.name, errDesription = e.msg
proc removeMigratedAccountsForKeycard*(self: Service, keyUid: string, keycardUid: string, accountsToRemove: seq[string]) =
let arg = RemoveMigratedAccountsForKeycardTaskArg(
tptr: cast[ByteAddress](removeMigratedAccountsForKeycardTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onMigratedAccountsForKeycardRemoved",
keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid, accountsAddresses: accountsToRemove)
)
self.threadpool.start(arg)
proc onMigratedAccountsForKeycardRemoved*(self: Service, response: string) {.slot.} =
var data = KeycardActivityArgs()
data.success = false
try:
let responseObj = response.parseJson
discard responseObj.getProp("success", data.success)
var kpJson: JsonNode
if responseObj.getProp("keyPair", kpJson):
data.keyPair = kpJson.toKeyPairDto()
except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data)
proc getAllKnownKeycards*(self: Service): seq[KeyPairDto] =
try:
let response = backend.getAllKnownKeycards()
if self.responseHasNoErrors("getAllKnownKeycards", response):
if responseHasNoErrors("getAllKnownKeycards", response):
return map(response.result.getElems(), proc(x: JsonNode): KeyPairDto = toKeyPairDto(x))
except Exception as e:
error "error: ", procName="getAllKnownKeycards", errName = e.name, errDesription = e.msg
@ -551,7 +587,7 @@ QtObject:
proc getAllMigratedKeyPairs*(self: Service): seq[KeyPairDto] =
try:
let response = backend.getAllMigratedKeyPairs()
if self.responseHasNoErrors("getAllMigratedKeyPairs", response):
if responseHasNoErrors("getAllMigratedKeyPairs", response):
return map(response.result.getElems(), proc(x: JsonNode): KeyPairDto = toKeyPairDto(x))
except Exception as e:
error "error: ", procName="getAllMigratedKeyPairs", errName = e.name, errDesription = e.msg
@ -559,63 +595,62 @@ QtObject:
proc getMigratedKeyPairByKeyUid*(self: Service, keyUid: string): seq[KeyPairDto] =
try:
let response = backend.getMigratedKeyPairByKeyUID(keyUid)
if self.responseHasNoErrors("getMigratedKeyPairByKeyUid", response):
if responseHasNoErrors("getMigratedKeyPairByKeyUid", response):
return map(response.result.getElems(), proc(x: JsonNode): KeyPairDto = toKeyPairDto(x))
except Exception as e:
error "error: ", procName="getMigratedKeyPairByKeyUid", errName = e.name, errDesription = e.msg
proc updateKeycardName*(self: Service, keycardUid: string, name: string): bool =
proc updateKeycardName*(self: Service, keyUid: string, keycardUid: string, name: string): bool =
try:
let response = backend.setKeycardName(keycardUid, name)
result = self.responseHasNoErrors("updateKeycardName", response)
result = responseHasNoErrors("updateKeycardName", response)
if result:
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, KeycardActivityArgs(keycardUid: keycardUid, keycardNewName: name))
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid, keycardName: name))
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
except Exception as e:
error "error: ", procName="updateKeycardName", errName = e.name, errDesription = e.msg
proc setKeycardLocked*(self: Service, keycardUid: string): bool =
proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool =
try:
let response = backend.keycardLocked(keycardUid)
result = self.responseHasNoErrors("setKeycardLocked", response)
result = responseHasNoErrors("setKeycardLocked", response)
if result:
self.events.emit(SIGNAL_KEYCARD_LOCKED, KeycardActivityArgs(keycardUid: keycardUid))
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
except Exception as e:
error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg
proc setKeycardUnlocked*(self: Service, keycardUid: string): bool =
proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): bool =
try:
let response = backend.keycardUnlocked(keycardUid)
result = self.responseHasNoErrors("setKeycardUnlocked", response)
result = responseHasNoErrors("setKeycardUnlocked", response)
if result:
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, KeycardActivityArgs(keycardUid: keycardUid))
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
except Exception as e:
error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg
proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool =
try:
let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid)
result = self.responseHasNoErrors("updateKeycardUid", response)
result = responseHasNoErrors("updateKeycardUid", response)
if result:
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, KeycardActivityArgs(keycardUid: oldKeycardUid, keycardNewUid: newKeycardUid))
let data = KeycardActivityArgs(success: true, oldKeycardUid: oldKeycardUid, keyPair: KeyPairDto(keycardUid: newKeycardUid))
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
except Exception as e:
error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg
proc deleteKeycard*(self: Service, keycardUid: string): bool =
try:
let response = backend.deleteKeycard(keycardUid)
return self.responseHasNoErrors("deleteKeycard", response)
return responseHasNoErrors("deleteKeycard", response)
except Exception as e:
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
return false
proc addWalletAccount*(self: Service, name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType,
color, emoji: string): string =
try:
let response = status_go_accounts.saveAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
accountType, color, emoji, walletDefaultAccount = false, chatDefaultAccount = false)
if not response.error.isNil:
return "(" & $response.error.code & ") " & response.error.message
result = self.addOrReplaceWalletAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
accountType, color, emoji)
if result.len == 0:
self.addNewAccountToLocalStore()
except Exception as e:
error "error: ", procName="addWalletAccount", errName = e.name, errDesription = e.msg
return "error: " & e.msg

View File

@ -24,6 +24,9 @@ proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} =
proc deleteAccount*(address: string): RpcResponse[JsonNode] {.raises: [Exception].} =
return core.callPrivateRPC("accounts_deleteAccount", %* [address])
proc deleteAccountForMigratedKeypair*(address: string): RpcResponse[JsonNode] {.raises: [Exception].} =
return core.callPrivateRPC("accounts_deleteAccountForMigratedKeypair", %* [address])
proc saveAccount*(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid, accountType, color, emoji: string,
walletDefaultAccount: bool, chatDefaultAccount: bool):
RpcResponse[JsonNode] {.raises: [Exception].} =
@ -44,19 +47,6 @@ proc saveAccount*(name, address, path, addressAccountIsDerivedFrom, publicKey, k
]
return core.callPrivateRPC("accounts_saveAccounts", payload)
proc updateAccount*(name, address, publicKey, walletType, color, emoji: string) {.raises: [Exception].} =
discard core.callPrivateRPC("accounts_saveAccounts", %* [
[{
"emoji": emoji,
"color": color,
"name": name,
"address": address,
"public-key": publicKey,
"type": walletType,
"path": "m/44'/60'/0'/0/1" # <--- TODO: fix this. Derivation path is not supposed to change
}]
])
proc generateAddresses*(paths: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* {
"n": NUMBER_OF_ADDRESSES_TO_GENERATE,

View File

@ -231,6 +231,10 @@ rpc(addMigratedKeyPair, "accounts"):
accountAddresses: seq[string]
keyStoreDir: string
rpc(removeMigratedAccountsForKeycard, "accounts"):
keycardUid: string
accountsToRemove: seq[string]
rpc(getAllKnownKeycards, "accounts"):
discard

View File

@ -30,8 +30,8 @@ QtObject {
walletSection.switchAccountByAddress(address)
}
function deleteAccount(address) {
return walletSectionAccounts.deleteAccount(address)
function deleteAccount(keyUid, address) {
return walletSectionAccounts.deleteAccount(keyUid, address)
}
function updateCurrentAccount(address, accountName, color, emoji) {

View File

@ -157,7 +157,7 @@ Item {
onConfirmButtonClicked: {
confirmationPopup.close();
root.goBack();
root.walletStore.deleteAccount(walletStore.currentAccount.address);
root.walletStore.deleteAccount(walletStore.currentAccount.keyUid, walletStore.currentAccount.address);
}
}

View File

@ -30,7 +30,7 @@ ColumnLayout {
}
property bool pathError: Utils.isInvalidPath(RootStore.derivedAddressesError)
property bool derivationAddressLoading: RootStore.derivedAddressesLoading
property string defaultDerivationPath: "m/44'/60'/0'/0"
property string defaultDerivationPath: "m/44'/60'/0'/0/0"
}
spacing: 7

View File

@ -11,7 +11,15 @@ QtObject {
property var sharedKeycardModule
property bool primaryButtonEnabled: false
readonly property bool disablePopupClose: {
// disables action buttons (back, cancel, primary, secondary) and close button (upper right "X" button) as well
readonly property bool disableActionPopupButtons: root.sharedKeycardModule.disablePopup
readonly property bool disablePopupClose: { // disables popup close button (upper right "X" button)
if (root.disableActionPopupButtons) {
return true
}
switch (root.sharedKeycardModule.currentState.stateType) {
case Constants.keycardSharedState.readingKeycard:
@ -38,6 +46,7 @@ QtObject {
StatusBackButton {
id: backButton
visible: root.sharedKeycardModule.currentState.displayBackButton
enabled: !root.disableActionPopupButtons
height: Constants.keycard.general.footerButtonsHeight
width: height
onClicked: {
@ -347,26 +356,7 @@ QtObject {
return false
}
enabled: {
switch (root.sharedKeycardModule.currentState.stateType) {
case Constants.keycardSharedState.readingKeycard:
case Constants.keycardSharedState.migratingKeyPair:
case Constants.keycardSharedState.creatingAccountNewSeedPhrase:
case Constants.keycardSharedState.creatingAccountOldSeedPhrase:
case Constants.keycardSharedState.importingFromKeycard:
case Constants.keycardSharedState.renamingKeycard:
case Constants.keycardSharedState.changingKeycardPin:
case Constants.keycardSharedState.changingKeycardPuk:
case Constants.keycardSharedState.changingKeycardPairingCode:
case Constants.keycardSharedState.copyingKeycard:
if (root.disablePopupClose) {
return false
}
}
return true
}
enabled: !root.disableActionPopupButtons
onClicked: {
root.sharedKeycardModule.currentState.doCancelAction()
@ -462,22 +452,9 @@ QtObject {
visible: text !== ""
enabled: {
switch (root.sharedKeycardModule.currentState.stateType) {
case Constants.keycardSharedState.readingKeycard:
case Constants.keycardSharedState.migratingKeyPair:
case Constants.keycardSharedState.creatingAccountNewSeedPhrase:
case Constants.keycardSharedState.creatingAccountOldSeedPhrase:
case Constants.keycardSharedState.importingFromKeycard:
case Constants.keycardSharedState.renamingKeycard:
case Constants.keycardSharedState.changingKeycardPin:
case Constants.keycardSharedState.changingKeycardPuk:
case Constants.keycardSharedState.changingKeycardPairingCode:
case Constants.keycardSharedState.copyingKeycard:
if (root.disablePopupClose) {
if (root.disableActionPopupButtons) {
return false
}
}
switch (root.sharedKeycardModule.currentState.flowType) {
@ -784,6 +761,7 @@ QtObject {
switch (root.sharedKeycardModule.currentState.stateType) {
case Constants.keycardSharedState.keycardEmpty:
case Constants.keycardSharedState.keycardEmptyMetadata:
case Constants.keycardSharedState.keycardAlreadyUnlocked:
case Constants.keycardSharedState.wrongKeycard:
case Constants.keycardSharedState.unlockKeycardSuccess:
@ -1000,22 +978,9 @@ QtObject {
}
visible: text !== ""
enabled: {
switch (root.sharedKeycardModule.currentState.stateType) {
case Constants.keycardSharedState.readingKeycard:
case Constants.keycardSharedState.migratingKeyPair:
case Constants.keycardSharedState.creatingAccountNewSeedPhrase:
case Constants.keycardSharedState.creatingAccountOldSeedPhrase:
case Constants.keycardSharedState.importingFromKeycard:
case Constants.keycardSharedState.renamingKeycard:
case Constants.keycardSharedState.changingKeycardPin:
case Constants.keycardSharedState.changingKeycardPuk:
case Constants.keycardSharedState.changingKeycardPairingCode:
case Constants.keycardSharedState.copyingKeycard:
if (root.disablePopupClose) {
if (root.disableActionPopupButtons) {
return false
}
}
switch (root.sharedKeycardModule.currentState.flowType) {
case Constants.keycardSharedFlow.setupNewKeycard:

View File

@ -280,7 +280,7 @@ Item {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
event.accepted = true
if (d.allEntriesValid) {
d.sharedKeycardModule.currentState.doPrimaryAction()
root.sharedKeycardModule.currentState.doPrimaryAction()
return
}
}

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit d95b2597773cc3678cb1594d960df8c4203b811f
Subproject commit ccbd2866fe8ee3223cf1d39d1cb3bedb2222149b