chore(keycard): sync keycard with the current app state updated

This commit resolves a crash happened due to connection to
`SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT` when keycard sync
flow was run in the background.

Also updated the keycard synchronization process with the current state of
the application and is the first step of many which leads towards completion
of entire syncing feature.
This commit is contained in:
Sale Djenic 2023-02-21 09:25:02 +01:00 committed by saledjenic
parent cf5271cfea
commit c83794470b
21 changed files with 285 additions and 152 deletions

View File

@ -1,4 +1,4 @@
import NimQml, sequtils, sugar, chronicles, os, uuids
import NimQml, sequtils, chronicles, os, uuids
import ../../app_service/service/general/service as general_service
import ../../app_service/service/keychain/service as keychain_service
@ -32,12 +32,11 @@ import ../../app_service/service/mailservers/service as mailservers_service
import ../../app_service/service/gif/service as gif_service
import ../../app_service/service/ens/service as ens_service
import ../../app_service/service/community_tokens/service as tokens_service
import ../../app_service/common/account_constants
import ../modules/shared_modules/keycard_popup/module as keycard_shared_module
import ../modules/startup/module as startup_module
import ../modules/main/module as main_module
import ../core/notifications/notifications_manager
import ../../constants as main_constants
import ../global/global_singleton
@ -107,6 +106,7 @@ type
proc load(self: AppController)
proc buildAndRegisterLocalAccountSensitiveSettings(self: AppController)
proc buildAndRegisterUserProfile(self: AppController)
proc tryKeycardSyncWithTheAppState(self: AppController)
# Startup Module Delegate Interface
proc startupDidLoad*(self: AppController)
@ -373,6 +373,7 @@ proc startupDidLoad*(self: AppController) =
self.startupModule.startUpUIRaised()
proc mainDidLoad*(self: AppController) =
self.tryKeycardSyncWithTheAppState()
self.startupModule.moveToAppState()
self.checkForStoringPasswordToKeychain()
@ -492,6 +493,7 @@ proc buildAndRegisterUserProfile(self: AppController) =
singletonInstance.engine.setRootContextProperty("userProfile", self.userProfileVariant)
proc tryKeycardSyncWithTheAppState(self: AppController) =
############################################################################## store def kc sync with app kc uid
## Onboarding flows sync keycard state after login keypair | (inc. kp store) | update
## `Im new to Status` -> `Generate new keys` -> na | na | na
@ -513,56 +515,19 @@ proc buildAndRegisterUserProfile(self: AppController) =
## `Login` -> if card was unlocked via seed phrase -> no | no | yes
## `Login` -> `Create replacement Keycard with seed phrase` -> no | yes | no (we don't know which kc is replaced if user has more kc for the same kp)
##############################################################################
if singletonInstance.userProfile.getIsKeycardUser():
if self.storeDefaultKeyPair:
let allAccounts = self.walletAccountService.fetchAccounts()
let defaultWalletAccounts = allAccounts.filter(a =>
a.walletType == WalletTypeDefaultStatusAccount and
a.path == account_constants.PATH_DEFAULT_WALLET and
not a.isChat and
a.isWallet
if singletonInstance.userProfile.getIsKeycardUser() or
self.syncKeycardBasedOnAppWalletState:
let data = SharedKeycarModuleArgs(
pin: self.startupModule.getPin(),
keyUid: singletonInstance.userProfile.getKeyUid()
)
if defaultWalletAccounts.len == 0:
error "default wallet account was not generated"
return
let defaultWalletAddress = defaultWalletAccounts[0].address
let keyPair = KeyPairDto(keycardUid: self.keycardService.getLastReceivedKeycardData().flowEvent.instanceUID,
keycardName: displayName,
keycardLocked: false,
accountsAddresses: @[defaultWalletAddress],
keyUid: loggedInAccount.keyUid)
discard self.walletAccountService.addMigratedKeyPair(keyPair)
if self.syncKeycardBasedOnAppWalletState:
let allAccounts = self.walletAccountService.fetchAccounts()
let accountsForLoggedInUser = allAccounts.filter(a => a.keyUid == loggedInAccount.keyUid)
var keyPair = KeyPairDto(keycardUid: "",
keycardName: displayName,
keycardLocked: false,
accountsAddresses: @[],
keyUid: loggedInAccount.keyUid)
var activeValidPathsToStoreToAKeycard: seq[string]
for acc in accountsForLoggedInUser:
activeValidPathsToStoreToAKeycard.add(acc.path)
keyPair.accountsAddresses.add(acc.address)
self.keycardService.startStoreMetadataFlow(displayName, self.startupModule.getPin(), activeValidPathsToStoreToAKeycard)
## sleep for 3 seconds, since that is more than enough to store metadata to a keycard, if the reader is still plugged in
## and the card is still inserted, otherwise we just skip that.
## At the moment we're not able to sync later keycard without metadata, cause such card doesn't return instance uid for
## loaded seed phrase, that's in the notes I am taking for discussion with keycard team. If they are able to provide
## instance uid for GetMetadata flow we will be able to use SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC signal for syncing
## otherwise we need to handle that way separatelly in `handleKeycardSyncing` of shared module
sleep(3000)
self.keycardService.cancelCurrentFlow()
let (_, kcEvent) = self.keycardService.getLastReceivedKeycardData()
if kcEvent.instanceUID.len > 0:
keyPair.keycardUid = kcEvent.instanceUID
discard self.walletAccountService.addMigratedKeyPair(keyPair)
self.statusFoundation.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_TRY_KEYCARD_SYNC, data)
if self.changedKeycardUids.len > 0:
let oldUid = self.changedKeycardUids[0].oldKcUid
let newUid = self.changedKeycardUids[^1].newKcUid
discard self.walletAccountService.updateKeycardUid(oldUid, newUid)
discard self.walletAccountService.setKeycardUnlocked(loggedInAccount.keyUid, newUid)
if self.changedKeycardUids.len > 0:
let oldUid = self.changedKeycardUids[0].oldKcUid
let newUid = self.changedKeycardUids[^1].newKcUid
discard self.walletAccountService.updateKeycardUid(oldUid, newUid)
discard self.walletAccountService.setKeycardUnlocked(singletonInstance.userProfile.getKeyUid(), newUid)
proc storeDefaultKeyPairForNewKeycardUser*(self: AppController) =
self.storeDefaultKeyPair = true

View File

@ -11,6 +11,7 @@ import ../../../../app_service/service/contacts/dto/[contacts, status_update]
import ../../../../app_service/service/devices/dto/[device]
import ../../../../app_service/service/settings/dto/[settings]
import ../../../../app_service/service/saved_address/[dto]
import ../../../../app_service/service/wallet_account/[key_pair_dto]
type MessageSignal* = ref object of Signal
bookmarks*: seq[BookmarkDto]
@ -32,6 +33,8 @@ type MessageSignal* = ref object of Signal
clearedHistories*: seq[ClearedHistoryDto]
verificationRequests*: seq[VerificationRequest]
savedAddresses*: seq[SavedAddressDto]
keycards*: seq[KeyPairDto]
keycardActions*: seq[KeycardActionDto]
type MessageDeliveredSignal* = ref object of Signal
chatId*: string
@ -130,5 +133,13 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal =
for jsonSavedAddress in event["event"]["savedAddresses"]:
signal.savedAddresses.add(jsonSavedAddress.toSavedAddressDto())
if event["event"]{"keycards"} != nil:
for jsonKc in event["event"]["keycards"]:
signal.keycards.add(jsonKc.toKeyPairDto())
if event["event"]{"keycardActions"} != nil:
for jsonKc in event["event"]["keycardActions"]:
signal.keycardActions.add(jsonKc.toKeycardActionDto())
result = signal

View File

@ -53,6 +53,12 @@ proc init*(self: Controller) =
return
self.delegate.onNewKeycardSet(args.keyPair)
self.events.on(SIGNAL_KEYCARDS_SYNCHRONIZED) do(e: Args):
let args = KeycardActivityArgs(e)
if not args.success:
return
self.delegate.onKeycardsSynchronized()
self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardLocked(args.keyPair.keyUid, args.keyPair.keycardUid)

View File

@ -66,6 +66,9 @@ method runCreateNewPairingCodePopup*(self: AccessInterface, keyUid: string) {.ba
method onLoggedInUserImageChanged*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardsSynchronized*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onNewKeycardSet*(self: AccessInterface, keyPair: KeyPairDto) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -276,6 +276,9 @@ method onLoggedInUserImageChanged*(self: Module) =
return
self.view.keycardDetailsModel().setImage(singletonInstance.userProfile.getPubKey(), singletonInstance.userProfile.getIcon())
method onKeycardsSynchronized*(self: Module) =
self.buildKeycardList()
method onNewKeycardSet*(self: Module, keyPair: KeyPairDto) =
let walletAccounts = self.controller.getWalletAccounts()
var mainViewItem = self.view.keycardModel().getItemForKeyUid(keyPair.keyUid)

View File

@ -150,6 +150,12 @@ method load*(self: Module) =
return
self.refreshWalletAccounts()
self.events.on(SIGNAL_KEYCARDS_SYNCHRONIZED) do(e: Args):
let args = KeycardActivityArgs(e)
if not args.success:
return
self.refreshWalletAccounts()
self.controller.init()
self.view.load()

View File

@ -165,7 +165,7 @@ proc disconnectAll*(self: Controller) =
proc delete*(self: Controller) =
self.disconnectAll()
proc init*(self: Controller) =
proc init*(self: Controller, fullConnect = true) =
self.connectKeycardReponseSignal()
var handlerId = self.events.onWithUUID(SIGNAL_SHARED_KEYCARD_MODULE_USER_AUTHENTICATED) do(e: Args):
@ -176,22 +176,23 @@ proc init*(self: Controller) =
self.delegate.onUserAuthenticated(args.password, args.pin)
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
let args = KeycardActivityArgs(e)
self.tmpAddingMigratedKeypairSuccess = args.success
self.delegate.onSecondaryActionClicked()
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_CONVERTING_PROFILE_KEYPAIR) do(e: Args):
let args = ResultArgs(e)
self.tmpConvertingProfileSuccess = args.success
self.delegate.onSecondaryActionClicked()
self.connectionIds.add(handlerId)
if fullConnect:
handlerId = self.events.onWithUUID(SIGNAL_NEW_KEYCARD_SET) do(e: Args):
let args = KeycardActivityArgs(e)
self.tmpAddingMigratedKeypairSuccess = args.success
self.delegate.onSecondaryActionClicked()
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_CONVERTING_PROFILE_KEYPAIR) do(e: Args):
let args = ResultArgs(e)
self.tmpConvertingProfileSuccess = args.success
self.delegate.onSecondaryActionClicked()
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
let arg = TokensPerAccountArgs(e)
self.delegate.onTokensRebuilt(arg.accountsTokens)
self.connectionIds.add(handlerId)
handlerId = self.events.onWithUUID(SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT) do(e:Args):
let arg = TokensPerAccountArgs(e)
self.delegate.onTokensRebuilt(arg.accountsTokens)
self.connectionIds.add(handlerId)
proc switchToWalletSection*(self: Controller) =
let data = ActiveSectionChatArgs(sectionId: conf.WALLET_SECTION_ID)
@ -661,11 +662,11 @@ proc setCurrentKeycardStateToUnlocked*(self: Controller, keyUid: string, keycard
if not self.walletAccountService.setKeycardUnlocked(keyUid, keycardUid):
info "updating keycard unlocked state failed", keyUid=keyUid, keycardUid=keycardUid
proc updateKeycardName*(self: Controller, keyUid: string, keycardUid: string, keycardName: string): bool =
proc updateKeycardName*(self: Controller, keycardUid: string, keycardName: string): bool =
if not serviceApplicable(self.walletAccountService):
return false
if not self.walletAccountService.updateKeycardName(keyUid, keycardUid, keycardName):
info "updating keycard name failed", keyUid=keyUid, keycardUid=keycardUid, keycardName=keycardName
if not self.walletAccountService.updateKeycardName(keycardUid, keycardName):
info "updating keycard name failed", keycardUid=keycardUid, keycardName=keycardName
return false
return 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.getKeyPairForProcessing().getKeyUid(), controller.getKeycardUid(), name)
self.success = controller.updateKeycardName(controller.getKeycardUid(), name)
if self.success:
controller.runStoreMetadataFlow(name, controller.getPin(), paths)
else:

View File

@ -1,5 +1,3 @@
import os
type
WrongSeedPhraseState* = ref object of State
verifiedSeedPhrase: bool

View File

@ -1,7 +1,7 @@
import NimQml, Tables, strformat, strutils
import key_pair_account_item
import ../../../../../app_service/common/account_constants
import ../../../../../app_service/common/utils
export key_pair_account_item
@ -82,10 +82,8 @@ QtObject:
proc containsPathOutOfTheDefaultStatusDerivationTree*(self: KeyPairAccountModel): bool =
for it in self.items:
if not it.getPath().startsWith(account_constants.PATH_WALLET_ROOT&"/") or
it.getPath().count("'") != 3 or
it.getPath().count("/") != 5:
return true
if utils.isPathOutOfTheDefaultStatusDerivationTree(it.getPath()):
return true
return false
proc getItemAtIndex*(self: KeyPairAccountModel, index: int): KeyPairAccountItem =

View File

@ -1,4 +1,4 @@
import NimQml, tables, random, strutils, marshal, sequtils, sugar, chronicles
import NimQml, tables, random, strutils, sequtils, sugar, chronicles
import io_interface
import view, controller
@ -66,10 +66,10 @@ method delete*[T](self: Module[T]) =
self.viewVariant.delete
self.controller.delete
proc init[T](self: Module[T]) =
proc init[T](self: Module[T], fullConnect = true) =
if not self.initialized:
self.initialized = true
self.controller.init()
self.controller.init(fullConnect)
method getModuleAsVariant*[T](self: Module[T]): QVariant =
return self.viewVariant
@ -241,13 +241,20 @@ proc handleKeycardSyncing[T](self: Module[T]) =
accountsAddresses: @[],
keyUid: flowEvent.keyUid)
let alreadySetKeycards = self.controller.getAllKnownKeycards().filter(kp => kp.keycardUid == flowEvent.instanceUID)
if alreadySetKeycards.len == 1:
var accountsToRemove = alreadySetKeycards[0].accountsAddresses
if alreadySetKeycards.len <= 1:
var accountsToRemove: seq[string]
if alreadySetKeycards.len == 1:
accountsToRemove = alreadySetKeycards[0].accountsAddresses
let appAccounts = self.controller.getWalletAccounts()
var activeValidPathsToStoreToAKeycard: seq[string]
var containsPathOutOfTheDefaultStatusDerivationTree = false
for appAcc in appAccounts:
if appAcc.keyUid != flowEvent.keyUid:
continue
# do not sync if any wallet's account has path out of the default Status derivation tree
if utils.isPathOutOfTheDefaultStatusDerivationTree(appAcc.path):
containsPathOutOfTheDefaultStatusDerivationTree = true
break
activeValidPathsToStoreToAKeycard.add(appAcc.path)
var index = -1
var found = false
@ -264,24 +271,25 @@ proc handleKeycardSyncing[T](self: Module[T]) =
# 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
if not containsPathOutOfTheDefaultStatusDerivationTree:
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)
@ -295,7 +303,7 @@ method syncKeycardBasedOnAppState*[T](self: Module[T], keyUid: string, pin: stri
if keyUid.len == 0:
debug "cannot sync with the empty keyUid"
return
self.init()
self.init(fullConnect = false)
self.controller.setKeyUidWhichIsBeingSyncing(keyUid)
self.controller.setPin(pin)
self.controller.setKeycardSyncingInProgress(true)

View File

@ -342,7 +342,7 @@ proc delayStartingApp[T](self: Module[T]) =
## - FlowType.FirstRunOldUserImportSeedPhrase
## - FlowType.FirstRunOldUserKeycardImport
## we want to delay app start just to be sure that messages from waku will be received
self.controller.connectToTimeoutEventAndStratTimer(timeoutInMilliseconds = 10000) # delay for 30 seconds
self.controller.connectToTimeoutEventAndStratTimer(timeoutInMilliseconds = 30000) # delay for 30 seconds
method startAppAfterDelay*[T](self: Module[T]) =
if not self.view.fetchingDataModel().allMessagesLoaded():

View File

@ -1,6 +1,6 @@
import json, random, times, strutils, sugar, os, re, chronicles
import nimcrypto
import signing_phrases
import signing_phrases, account_constants
import ../../constants as main_constants
@ -74,3 +74,10 @@ proc validateLink*(link: string): bool =
link, re"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)", 0):
error "Invalid social link", errDescription = link
result = false
proc isPathOutOfTheDefaultStatusDerivationTree*(path: string): bool =
if not path.startsWith(account_constants.PATH_WALLET_ROOT&"/") or
path.count("'") != 3 or
path.count("/") != 5:
return true
return false

View File

@ -140,14 +140,14 @@ type
const addMigratedKeyPairTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AddMigratedKeyPairTaskArg](argEncoded)
try:
let response = backend.addMigratedKeyPair(
let response = backend.addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(
arg.keyPair.keycardUid,
arg.keyPair.keycardName,
arg.keyPair.keyUid,
arg.keyPair.accountsAddresses,
arg.password
)
let success = responseHasNoErrors("addMigratedKeyPair", response)
let success = responseHasNoErrors("addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded", response)
let responseJson = %*{
"success": success,
"keyPair": arg.keyPair.toJsonNode()

View File

@ -2,12 +2,23 @@ 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"
const ParamKeycardUid = "keycard-uid"
const ParamKeycardName = "keycard-name"
const ParamKeycardLocked = "keycard-locked"
const ParamKeyUid = "key-uid"
const ParamAccountAddresses = "accounts-addresses"
const ParamAction = "action"
const ParamOldKeycardUid = "old-keycard-uid"
const ParamKeycard = "keycard"
const KeycardActionKeycardAdded* = "KEYCARD_ADDED"
const KeycardActionAccountsAdded* = "ACCOUNTS_ADDED"
const KeycardActionKeycardDeleted* = "KEYCARD_DELETED"
const KeycardActionAccountsRemoved* = "ACCOUNTS_REMOVED"
const KeycardActionLocked* = "LOCKED"
const KeycardActionUnlocked* = "UNLOCKED"
const KeycardActionUidUpdated* = "UID_UPDATED"
const KeycardActionNameChanged* = "NAME_CHANGED"
type KeyPairDto* = object
keycardUid*: string
@ -16,23 +27,37 @@ type KeyPairDto* = object
accountsAddresses*: seq[string]
keyUid*: string
type KeycardActionDto* = object
action*: string
oldKeycardUid*: string
keycard*: KeyPairDto
proc toKeyPairDto*(jsonObj: JsonNode): KeyPairDto =
result = KeyPairDto()
discard jsonObj.getProp(KeycardUid, result.keycardUid)
discard jsonObj.getProp(KeycardName, result.keycardName)
discard jsonObj.getProp(KeycardLocked, result.keycardLocked)
discard jsonObj.getProp(KeyUid, result.keyUid)
discard jsonObj.getProp(ParamKeycardUid, result.keycardUid)
discard jsonObj.getProp(ParamKeycardName, result.keycardName)
discard jsonObj.getProp(ParamKeycardLocked, result.keycardLocked)
discard jsonObj.getProp(ParamKeyUid, result.keyUid)
var jArr: JsonNode
if(jsonObj.getProp(AccountAddresses, jArr) and jArr.kind == JArray):
if(jsonObj.getProp(ParamAccountAddresses, jArr) and jArr.kind == JArray):
for addrObj in jArr:
result.accountsAddresses.add(addrObj.getStr)
proc toKeycardActionDto*(jsonObj: JsonNode): KeycardActionDto =
result = KeycardActionDto()
discard jsonObj.getProp(ParamAction, result.action)
discard jsonObj.getProp(ParamOldKeycardUid, result.oldKeycardUid)
var keycardObj: JsonNode
if(jsonObj.getProp("keycard", keycardObj)):
result.keycard = toKeyPairDto(keycardObj)
proc toJsonNode*(self: KeyPairDto): JsonNode =
result = %* {
KeycardUid: self.keycardUid,
KeycardName: self.keycardName,
KeycardLocked: self.keycardLocked,
KeyUid: self.keyUid,
AccountAddresses: self.accountsAddresses
ParamKeycardUid: self.keycardUid,
ParamKeycardName: self.keycardName,
ParamKeycardLocked: self.keycardLocked,
ParamKeyUid: self.keyUid,
ParamAccountAddresses: self.accountsAddresses
}

View File

@ -34,7 +34,9 @@ const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_READY* = "walletAccount/derivedAddre
const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt"
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_DETAILS_FETCHED* = "walletAccount/derivedAddressDetailsFetched"
const SIGNAL_KEYCARDS_SYNCHRONIZED* = "keycardsSynchronized"
const SIGNAL_NEW_KEYCARD_SET* = "newKeycardSet"
const SIGNAL_KEYCARD_DELETED* = "keycardDeleted"
const SIGNAL_KEYCARD_ACCOUNTS_REMOVED* = "keycardAccountsRemoved"
const SIGNAL_KEYCARD_LOCKED* = "keycardLocked"
const SIGNAL_KEYCARD_UNLOCKED* = "keycardUnlocked"
@ -124,6 +126,8 @@ QtObject:
proc checkRecentHistory*(self: Service)
proc checkConnected(self: Service)
proc startWallet(self: Service)
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto])
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto])
proc delete*(self: Service) =
self.closingApp = true
@ -227,6 +231,9 @@ QtObject:
if settingsField.name == KEY_CURRENCY:
self.events.emit(SIGNAL_WALLET_ACCOUNT_CURRENCY_UPDATED, CurrencyUpdated())
self.handleKeycardsState(receivedData.keycards)
self.handleKeycardActions(receivedData.keycardActions)
self.events.on(SignalType.Wallet.event) do(e:Args):
var data = WalletSignal(e)
case data.eventType:
@ -614,24 +621,33 @@ QtObject:
)
self.threadpool.start(arg)
proc emitAddKeycardAddAccountsChange(self: Service, success: bool, keyPair: KeyPairDto) =
let data = KeycardActivityArgs(
success: success,
keyPair: keyPair
)
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data)
proc onMigratedKeyPairAdded*(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()
var keyPair: KeyPairDto
var success = false
discard responseObj.getProp("success", success)
if success:
var kpJson: JsonNode
if responseObj.getProp("keyPair", kpJson):
keyPair = kpJson.toKeyPairDto()
self.emitAddKeycardAddAccountsChange(success, keyPair)
except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_NEW_KEYCARD_SET, data)
self.emitAddKeycardAddAccountsChange(success = false, KeyPairDto())
proc addMigratedKeyPair*(self: Service, keyPair: KeyPairDto, password = ""): bool =
# Providing a password corresponding local keystore file will be removed as well, though
# in some contexts we just need to add keypair to the db, so password is not needed.
try:
let response = backend.addMigratedKeyPair(
let response = backend.addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(
keyPair.keycardUid,
keyPair.keycardName,
keyPair.keyUid,
@ -640,7 +656,7 @@ QtObject:
)
result = responseHasNoErrors("addMigratedKeyPair", response)
if result:
self.events.emit(SIGNAL_NEW_KEYCARD_SET, KeycardActivityArgs(success: true, keyPair: keyPair))
self.emitAddKeycardAddAccountsChange(success = true, keyPair)
except Exception as e:
error "error: ", procName="addMigratedKeyPair", errName = e.name, errDesription = e.msg
@ -653,18 +669,28 @@ QtObject:
)
self.threadpool.start(arg)
proc emitKeycardRemovedAccountsChange(self: Service, success: bool, keyUid: string, keycardUid: string,
removedAccounts: seq[string]) =
let data = KeycardActivityArgs(
success: success,
keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid, accountsAddresses: removedAccounts)
)
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data)
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()
var keyPair: KeyPairDto
var success = false
discard responseObj.getProp("success", success)
if success:
var kpJson: JsonNode
if responseObj.getProp("keyPair", kpJson):
keyPair = kpJson.toKeyPairDto()
self.emitKeycardRemovedAccountsChange(success, keyPair.keyUid, keyPair.keycardUid, keyPair.accountsAddresses)
except Exception as e:
error "error handilng migrated keypair response", errDesription=e.msg
self.events.emit(SIGNAL_KEYCARD_ACCOUNTS_REMOVED, data)
self.emitKeycardRemovedAccountsChange(success = false, keyUid = "", keycardUid = "", removedAccounts = @[])
proc getAllKnownKeycards*(self: Service): seq[KeyPairDto] =
try:
@ -674,6 +700,16 @@ QtObject:
except Exception as e:
error "error: ", procName="getAllKnownKeycards", errName = e.name, errDesription = e.msg
proc getKeycardWithKeycardUid*(self: Service, keycardUid: string): KeyPairDto =
let allKnownKeycards = self.getAllKnownKeycards()
let keycardsWithKeycardUid = allKnownKeycards.filter(kp => kp.keycardUid == keycardUid)
if keycardsWithKeycardUid.len == 0:
return
if keycardsWithKeycardUid.len > 1:
error "there are more than one keycard with the same uid", keycardUid=keycardUid
return
return keycardsWithKeycardUid[0]
proc getAllMigratedKeyPairs*(self: Service): seq[KeyPairDto] =
try:
let response = backend.getAllMigratedKeyPairs()
@ -690,50 +726,68 @@ QtObject:
except Exception as e:
error "error: ", procName="getMigratedKeyPairByKeyUid", errName = e.name, errDesription = e.msg
proc updateKeycardName*(self: Service, keyUid: string, keycardUid: string, name: string): bool =
proc emitKeycardNameChange(self: Service, keycardUid: string, name: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keycardUid: keycardUid, keycardName: name))
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
proc updateKeycardName*(self: Service, keycardUid: string, name: string): bool =
try:
let response = backend.setKeycardName(keycardUid, name)
result = responseHasNoErrors("updateKeycardName", response)
if result:
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid, keycardName: name))
self.events.emit(SIGNAL_KEYCARD_NAME_CHANGED, data)
self.emitKeycardNameChange(keycardUid, name)
except Exception as e:
error "error: ", procName="updateKeycardName", errName = e.name, errDesription = e.msg
proc emitKeycardLockedChange(self: Service, keyUid: string, keycardUid: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
proc setKeycardLocked*(self: Service, keyUid: string, keycardUid: string): bool =
try:
let response = backend.keycardLocked(keycardUid)
result = responseHasNoErrors("setKeycardLocked", response)
if result:
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_LOCKED, data)
self.emitKeycardLockedChange(keyUid, keycardUid)
except Exception as e:
error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg
proc emitKeycardUnlockedChange(self: Service, keyUid: string, keycardUid: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
proc setKeycardUnlocked*(self: Service, keyUid: string, keycardUid: string): bool =
try:
let response = backend.keycardUnlocked(keycardUid)
result = responseHasNoErrors("setKeycardUnlocked", response)
if result:
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keyUid: keyUid, keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, data)
self.emitKeycardUnlockedChange(keyUid, keycardUid)
except Exception as e:
error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg
proc emitUpdateKeycardUidChange(self: Service, oldKeycardUid: string, newKeycardUid: string) =
let data = KeycardActivityArgs(success: true, oldKeycardUid: oldKeycardUid, keyPair: KeyPairDto(keycardUid: newKeycardUid))
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool =
try:
let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid)
result = responseHasNoErrors("updateKeycardUid", response)
if result:
let data = KeycardActivityArgs(success: true, oldKeycardUid: oldKeycardUid, keyPair: KeyPairDto(keycardUid: newKeycardUid))
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, data)
self.emitUpdateKeycardUidChange(oldKeycardUid, newKeycardUid)
except Exception as e:
error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg
proc emitDeleteKeycardChange(self: Service, keycardUid: string) =
let data = KeycardActivityArgs(success: true, keyPair: KeyPairDto(keycardUid: keycardUid))
self.events.emit(SIGNAL_KEYCARD_DELETED, data)
proc deleteKeycard*(self: Service, keycardUid: string): bool =
try:
let response = backend.deleteKeycard(keycardUid)
return responseHasNoErrors("deleteKeycard", response)
result = responseHasNoErrors("deleteKeycard", response)
if result:
self.emitDeleteKeycardChange(keycardUid)
except Exception as e:
error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg
return false
@ -743,4 +797,35 @@ QtObject:
result = self.addOrReplaceWalletAccount(name, address, path, addressAccountIsDerivedFrom, publicKey, keyUid,
accountType, color, emoji)
if result.len == 0:
self.addNewAccountToLocalStore()
self.addNewAccountToLocalStore()
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto]) =
if keycardActions.len == 0:
return
for kcAction in keycardActions:
if kcAction.action == KeycardActionKeycardAdded or
kcAction.action == KeycardActionAccountsAdded:
self.emitAddKeycardAddAccountsChange(success = true, kcAction.keycard)
elif kcAction.action == KeycardActionKeycardDeleted:
self.emitDeleteKeycardChange(kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionAccountsRemoved:
let keycard = self.getKeycardWithKeycardUid(kcAction.keycard.keycardUid)
self.emitKeycardRemovedAccountsChange(success = true, keycard.keyUid, kcAction.keycard.keycardUid, kcAction.keycard.accountsAddresses)
elif kcAction.action == KeycardActionLocked:
let keycard = self.getKeycardWithKeycardUid(kcAction.keycard.keycardUid)
self.emitKeycardLockedChange(keycard.keyUid, kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionUnlocked:
let keycard = self.getKeycardWithKeycardUid(kcAction.keycard.keycardUid)
self.emitKeycardUnlockedChange(keycard.keyUid, kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionUidUpdated:
self.emitUpdateKeycardUidChange(kcAction.oldKeycardUid, kcAction.keycard.keycardUid)
elif kcAction.action == KeycardActionNameChanged:
self.emitKeycardNameChange(kcAction.keycard.keycardUid, kcAction.keycard.keycardName)
else:
error "unsupported action received", action=kcAction.action
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto]) =
if keycardsState.len == 0:
return
let data = KeycardActivityArgs(success: true)
self.events.emit(SIGNAL_KEYCARDS_SYNCHRONIZED, data)

View File

@ -239,12 +239,12 @@ rpc(fetchMarketValues, "wallet"):
rpc(fetchTokenDetails, "wallet"):
symbols: seq[string]
rpc(addMigratedKeyPair, "accounts"):
rpc(addMigratedKeyPairOrAddAccountsIfKeyPairIsAdded, "accounts"):
keycardUid: string
keyPairName: string
keyUid: string
accountAddresses: seq[string]
keyStoreDir: string
password: string
rpc(removeMigratedAccountsForKeycard, "accounts"):
keycardUid: string

View File

@ -84,6 +84,10 @@ SettingsContentBase {
Layout.preferredWidth: root.contentWidth
keycardStore: root.keycardStore
keyUid: d.observedKeyUid
onChangeSectionTitle: {
root.sectionTitle = title
}
}
Connections {

View File

@ -18,11 +18,20 @@ ColumnLayout {
property KeycardStore keycardStore
property string keyUid: ""
signal changeSectionTitle(string title)
spacing: Constants.settingsSection.itemSpacing
QtObject {
id: d
property bool collapsed: true
function checkAndCheckTitleIfNeeded(newKeycardName) {
// We change title if there is only a single keycard for a keypair in keycard details view
if (root.keycardStore.keycardModule.keycardDetailsModel.count === 1) {
root.changeSectionTitle(newKeycardName)
}
}
}
StatusListView {
@ -42,6 +51,10 @@ ColumnLayout {
keyPairIcon: model.keycard.icon
keyPairImage: model.keycard.image
keyPairAccounts: model.keycard.accounts
onKeycardNameChanged: {
d.checkAndCheckTitleIfNeeded(keycardName)
}
}
}

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit d0cc036d486092262382d45967db11f19d66641e
Subproject commit 2d16e7b8910f40070086f3966ce8f9eb55ee8223

@ -1 +1 @@
Subproject commit b50cfe22ac3802d508e34b0561095c7100f6efa8
Subproject commit 5bfafd14e6361c551cfb55b527448de8e4646e83