fix(@desktop/wallet): issues with deleting wallet accounts fixed

In case an account being deleted is migrated to a Keycard keystore file it was not removed from
`keycards_accounts` table and in case it was the last account for that keypair derived from keystore
file was not removed as well as other keycards containing the same keypair. That's all sorted out.
This commit is contained in:
Sale Djenic 2023-04-25 14:02:10 +02:00 committed by saledjenic
parent 61bd370b20
commit 1a919f586f
12 changed files with 66 additions and 40 deletions

View File

@ -80,7 +80,7 @@ method load*(self: Module) =
singletonInstance.engine.setRootContextProperty("browserSectionCurrentAccount", newQVariant(self.view))
self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args):
if(self.view.isAddressCurrentAccount(AccountDeleted(e).account.address)):
if(self.view.isAddressCurrentAccount(AccountDeleted(e).address)):
self.switchAccount(0)
self.view.connectedAccountDeleted()

View File

@ -31,7 +31,7 @@ proc init*(self: Controller) =
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_PROFILE_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid)
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid, args.keycardUid)
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
return self.walletAccountService.getWalletAccounts()
@ -39,8 +39,8 @@ proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAcco
proc updateAccount*(self: Controller, address: string, accountName: string, color: string, emoji: string) =
discard self.walletAccountService.updateWalletAccount(address, accountName, color, emoji)
proc deleteAccount*(self: Controller, address: string, password = "", doPasswordHashing = false) =
self.walletAccountService.deleteAccount(address, password, doPasswordHashing)
proc deleteAccount*(self: Controller, address: string, password = "", keyUid = "", keycardUid = "") =
self.walletAccountService.deleteAccount(address, password, keyUid, keycardUid)
proc authenticateKeyPair*(self: Controller, keyUid = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_PROFILE_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER,

View File

@ -34,7 +34,7 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
method authenticateUser*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string) {.base.} =
method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =

View File

@ -124,12 +124,11 @@ method deleteAccount*(self: Module, keyUid: string, address: string) =
self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, address: address)
self.authenticateActivityForKeyUid(keyUid, AuthenticationReason.DeleteAccountAuthentication)
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string) =
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string, keycardUid: string) =
if self.authentiactionReason == AuthenticationReason.DeleteAccountAuthentication:
if self.processingWalletAccount.keyUid != keyUid:
error "cannot resolve key uid of an account being deleted", keyUid=keyUid
return
if password.len == 0:
return
let doPasswordHashing = pin.len != PINLengthForStatusApp
self.controller.deleteAccount(self.processingWalletAccount.address, password, doPasswordHashing)
self.controller.deleteAccount(self.processingWalletAccount.address, password, keyUid, keycardUid)

View File

@ -42,13 +42,13 @@ proc init*(self: Controller) =
let args = SharedKeycarModuleArgs(e)
if args.uniqueIdentifier != UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER:
return
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid)
self.delegate.onUserAuthenticated(args.pin, args.password, args.keyUid, args.keycardUid)
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
return self.walletAccountService.getWalletAccounts()
proc deleteAccount*(self: Controller, address: string, password = "", doPasswordHashing = false) =
self.walletAccountService.deleteAccount(address, password, doPasswordHashing)
proc deleteAccount*(self: Controller, address: string, password = "", keyUid = "", keycardUid = "") =
self.walletAccountService.deleteAccount(address, password, keyUid, keycardUid)
proc authenticateKeyPair*(self: Controller, keyUid = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_WALLET_SECTION_ACCOUNTS_MODULE_AUTH_IDENTIFIER,

View File

@ -31,5 +31,5 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
method authenticateUser*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string) {.base.} =
method onUserAuthenticated*(self: AccessInterface, pin: string, password: string, keyUid: string, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -141,15 +141,14 @@ method deleteAccount*(self: Module, keyUid: string, address: string) =
self.processingWalletAccount = WalletAccountDetails(keyUid: keyUid, address: address)
self.authenticateActivityForKeyUid(keyUid, AuthenticationReason.DeleteAccountAuthentication)
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string) =
method onUserAuthenticated*(self: Module, pin: string, password: string, keyUid: string, keycardUid: string) =
if self.authentiactionReason == AuthenticationReason.DeleteAccountAuthentication:
if self.processingWalletAccount.keyUid != keyUid:
error "cannot resolve key uid of an account being deleted", keyUid=keyUid
return
if password.len == 0:
return
let doPasswordHashing = pin.len != PINLengthForStatusApp
self.controller.deleteAccount(self.processingWalletAccount.address, password, doPasswordHashing)
self.controller.deleteAccount(self.processingWalletAccount.address, password, keyUid, keycardUid)
method updateAccount*(self: Module, address: string, accountName: string, color: string, emoji: string) =
self.controller.updateAccount(address, accountName, color, emoji)

View File

@ -32,7 +32,7 @@ type
proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]], hasBalanceCache: bool, hasMarketValuesCache: bool)
proc onCurrencyFormatsUpdated(self: Module)
proc onAccountAdded(self: Module, account: WalletAccountDto)
proc onAccountRemoved(self: Module, account: WalletAccountDto)
proc onAccountRemoved(self: Module, address: string)
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -69,7 +69,7 @@ method load*(self: Module) =
self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args):
let args = AccountDeleted(e)
self.onAccountRemoved(args.account)
self.onAccountRemoved(args.address)
self.events.on(SIGNAL_WALLET_ACCOUNT_UPDATED) do(e:Args):
self.switchAccount(self.currentAccountIndex)
@ -153,5 +153,5 @@ proc onCurrencyFormatsUpdated(self: Module) =
proc onAccountAdded(self: Module, account: WalletAccountDto) =
self.switchAccount(self.currentAccountIndex)
proc onAccountRemoved(self: Module, account: WalletAccountDto) =
self.switchAccount(self.currentAccountIndex)
proc onAccountRemoved(self: Module, address: string) =
self.switchAccount(self.currentAccountIndex)

View File

@ -26,7 +26,7 @@ type
proc onTokensRebuilt(self: Module, accountsTokens: OrderedTable[string, seq[WalletTokenDto]])
proc onCurrencyFormatsUpdated(self: Module)
proc onAccountAdded(self: Module, account: WalletAccountDto)
proc onAccountRemoved(self: Module, account: WalletAccountDto)
proc onAccountRemoved(self: Module, address: string)
proc newModule*(
delegate: delegate_interface.AccessInterface,
@ -56,7 +56,7 @@ method load*(self: Module) =
self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args):
let args = AccountDeleted(e)
self.onAccountRemoved(args.account)
self.onAccountRemoved(args.address)
self.events.on(SIGNAL_WALLET_ACCOUNT_UPDATED) do(e:Args):
self.switchAccount(self.currentAccountIndex)
@ -132,6 +132,5 @@ proc onCurrencyFormatsUpdated(self: Module) =
proc onAccountAdded(self: Module, account: WalletAccountDto) =
self.switchAccount(self.currentAccountIndex)
proc onAccountRemoved(self: Module, account: WalletAccountDto) =
self.switchAccount(self.currentAccountIndex)
proc onAccountRemoved(self: Module, address: string) =
self.switchAccount(self.currentAccountIndex)

View File

@ -190,7 +190,7 @@ QtObject:
self.refetchAllOwnedCollectibles()
self.events.on(SIGNAL_WALLET_ACCOUNT_DELETED) do(e:Args):
self.removeAddress(AccountDeleted(e).account.address)
self.removeAddress(AccountDeleted(e).address)
# needs to be re-written once cache for colletibles works
proc areCollectionsLoaded*(self: Service, address: string): bool =

View File

@ -74,7 +74,7 @@ type AccountSaved* = ref object of Args
account*: WalletAccountDto
type AccountDeleted* = ref object of Args
account*: WalletAccountDto
address*: string
type CurrencyUpdated = ref object of Args
@ -130,6 +130,8 @@ QtObject:
proc startWallet(self: Service)
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto])
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto])
proc getAllKnownKeycards*(self: Service): seq[KeyPairDto]
proc removeMigratedAccountsForKeycard*(self: Service, keyUid: string, keycardUid: string, accountsToRemove: seq[string])
proc delete*(self: Service) =
self.closingApp = true
@ -163,6 +165,18 @@ QtObject:
except Exception as e:
error "error: ", procName="fetchAccounts", errName = e.name, errDesription = e.msg
proc getAccountsByKeyUID*(self: Service, keyUid: string): seq[WalletAccountDto] =
if keyUid.len == 0:
error "error: cannot fetch accounts for an empty keyUid", procName="getAccountsByKeyUID"
return
try:
let response = status_go_accounts.getAccountsByKeyUID(keyUid)
return response.result.getElems().map(
x => x.toWalletAccountDto()
).filter(a => not a.isChat)
except Exception as e:
error "error: ", procName="getAccountsByKeyUID", errName = e.name, errDesription = e.msg
proc setEnsName(self: Service, account: WalletAccountDto) =
let chainId = self.networkService.getNetworkForEns().chainId
try:
@ -241,15 +255,6 @@ QtObject:
proc walletAccountsContainsAddress*(self: Service, address: string): bool =
return self.walletAccounts.hasKey(address)
proc removeAccount*(self: Service, address: string): WalletAccountDto =
result = WalletAccountDto()
if not self.walletAccountsContainsAddress(address):
return
result = self.walletAccounts[address]
self.walletAccounts.del(address)
# updating related accounts for other accounts
self.setRelatedAccountsForAllAccounts(result.derivedFrom)
proc getAccountByAddress*(self: Service, address: string): WalletAccountDto =
result = WalletAccountDto()
if not self.walletAccountsContainsAddress(address):
@ -353,6 +358,15 @@ QtObject:
self.buildAllTokens(@[newAccount.address], store = true)
self.events.emit(SIGNAL_WALLET_ACCOUNT_SAVED, AccountSaved(account: newAccount))
proc removeAccountFromLocalStoreAndNotify(self: Service, address: string) =
if not self.walletAccountsContainsAddress(address):
return
let removedAcc = self.walletAccounts[address]
self.walletAccounts.del(address)
# updating related accounts for other accounts
self.setRelatedAccountsForAllAccounts(removedAcc.derivedFrom)
self.events.emit(SIGNAL_WALLET_ACCOUNT_DELETED, AccountDeleted(address: address))
## if password is not provided local keystore file won't be created
proc addWalletAccount*(self: Service, password: string, doPasswordHashing: bool, name, keyPairName, address, path: string,
lastUsedDerivationIndex: int, rootWalletMasterKey, publicKey, keyUid, accountType, color, emoji: string): string =
@ -425,14 +439,25 @@ QtObject:
error "error: ", procName="getRandomMnemonic", errName=e.name, errDesription=e.msg
return ""
proc deleteAccount*(self: Service, address: string, password: string, doPasswordHashing: bool) =
proc deleteAccount*(self: Service, address: string, password: string, keyUid: string, keycardUid: string) =
try:
let isKeycardAccount = keycardUid.len > 0
var finalPassword = password
if doPasswordHashing:
if not isKeycardAccount:
finalPassword = utils.hashPassword(password)
discard status_go_accounts.deleteAccount(address, finalPassword)
let accountDeleted = self.removeAccount(address)
self.events.emit(SIGNAL_WALLET_ACCOUNT_DELETED, AccountDeleted(account: accountDeleted))
let response = status_go_accounts.deleteAccount(address, finalPassword)
if not response.error.isNil:
error "status-go error", procName="deleteAccount", errCode=response.error.code, errDesription=response.error.message
return
self.removeAccountFromLocalStoreAndNotify(address)
if isKeycardAccount:
self.removeMigratedAccountsForKeycard(keyUid, keycardUid, @[address])
let accounts = self.getAccountsByKeyUID(keyUID)
if accounts.len == 0:
let allKnownKeycards = self.getAllKnownKeycards()
for kc in allKnownKeycards:
if kc.keyUid == keyUid:
self.removeMigratedAccountsForKeycard(kc.keyUid, kc.keycardUid, kc.accountsAddresses)
except Exception as e:
error "error: ", procName="deleteAccount", errName = e.name, errDesription = e.msg

View File

@ -21,6 +21,10 @@ const WATCH* = "watch"
proc getAccounts*(): RpcResponse[JsonNode] {.raises: [Exception].} =
return core.callPrivateRPC("accounts_getAccounts")
proc getAccountsByKeyUID*(keyUid: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [keyUid]
return core.callPrivateRPC("accounts_getAccountsByKeyUID", payload)
proc deleteAccount*(address: string, password: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [address, password]
return core.callPrivateRPC("accounts_deleteAccount", payload)