diff --git a/src/app/core/signals/remote_signals/messages.nim b/src/app/core/signals/remote_signals/messages.nim index a86956720d..caa4375904 100644 --- a/src/app/core/signals/remote_signals/messages.nim +++ b/src/app/core/signals/remote_signals/messages.nim @@ -10,8 +10,8 @@ import ../../../../app_service/service/activity_center/dto/[notification] import ../../../../app_service/service/contacts/dto/[contacts, status_update] import ../../../../app_service/service/devices/dto/[installation] import ../../../../app_service/service/settings/dto/[settings] -import ../../../../app_service/service/saved_address/[dto] -import ../../../../app_service/service/wallet_account/[key_pair_dto] +import ../../../../app_service/service/saved_address/dto as saved_address_dto +import ../../../../app_service/service/wallet_account/[dto, key_pair_dto] type MessageSignal* = ref object of Signal bookmarks*: seq[BookmarkDto] @@ -35,6 +35,7 @@ type MessageSignal* = ref object of Signal savedAddresses*: seq[SavedAddressDto] keycards*: seq[KeyPairDto] keycardActions*: seq[KeycardActionDto] + walletAccounts*: seq[WalletAccountDto] type MessageDeliveredSignal* = ref object of Signal chatId*: string @@ -141,5 +142,9 @@ proc fromEvent*(T: type MessageSignal, event: JsonNode): MessageSignal = for jsonKc in event["event"]["keycardActions"]: signal.keycardActions.add(jsonKc.toKeycardActionDto()) + if event["event"]{"accounts"} != nil: + for jsonAcc in event["event"]["accounts"]: + signal.walletAccounts.add(jsonAcc.toWalletAccountDto()) + result = signal diff --git a/src/app/core/signals/remote_signals/signal_type.nim b/src/app/core/signals/remote_signals/signal_type.nim index 473c422dd3..62c740c8c0 100644 --- a/src/app/core/signals/remote_signals/signal_type.nim +++ b/src/app/core/signals/remote_signals/signal_type.nim @@ -49,6 +49,7 @@ type SignalType* {.pure.} = enum WakuFetchingBackupProgress = "waku.fetching.backup.progress" WakuBackedUpProfile = "waku.backedup.profile" WakuBackedUpSettings = "waku.backedup.settings" + WakuBackedUpWalletAccount = "waku.backedup.wallet-account" WakuBackedUpKeycards = "waku.backedup.keycards" LocalPairing = "localPairing" Unknown diff --git a/src/app/core/signals/remote_signals/waku_backed_up_wallet_accounts.nim b/src/app/core/signals/remote_signals/waku_backed_up_wallet_accounts.nim new file mode 100644 index 0000000000..1a7da03bae --- /dev/null +++ b/src/app/core/signals/remote_signals/waku_backed_up_wallet_accounts.nim @@ -0,0 +1,13 @@ +import json +import base + +import ../../../../app_service/service/wallet_account/[dto] + +type WakuBackedUpWalletAccountsSignal* = ref object of Signal + account*: WalletAccountDto + +proc fromEvent*(T: type WakuBackedUpWalletAccountsSignal, event: JsonNode): WakuBackedUpWalletAccountsSignal = + result = WakuBackedUpWalletAccountsSignal() + + if event["event"]{"backedUpWalletAccount"} != nil: + result.account = event["event"]["backedUpWalletAccount"].toWalletAccountDto() \ No newline at end of file diff --git a/src/app/core/signals/signals_manager.nim b/src/app/core/signals/signals_manager.nim index c2145109b1..50eba1fb67 100644 --- a/src/app/core/signals/signals_manager.nim +++ b/src/app/core/signals/signals_manager.nim @@ -116,6 +116,7 @@ QtObject: of SignalType.WakuFetchingBackupProgress: WakuFetchingBackupProgressSignal.fromEvent(jsonSignal) of SignalType.WakuBackedUpProfile: WakuBackedUpProfileSignal.fromEvent(jsonSignal) of SignalType.WakuBackedUpSettings: WakuBackedUpSettingsSignal.fromEvent(jsonSignal) + of SignalType.WakuBackedUpWalletAccount: WakuBackedUpWalletAccountsSignal.fromEvent(jsonSignal) of SignalType.WakuBackedUpKeycards: WakuBackedUpKeycardsSignal.fromEvent(jsonSignal) # pairing of SignalType.LocalPairing: LocalPairingSignal.fromEvent(jsonSignal) diff --git a/src/app/core/signals/types.nim b/src/app/core/signals/types.nim index d64f00ff61..609079d41a 100644 --- a/src/app/core/signals/types.nim +++ b/src/app/core/signals/types.nim @@ -2,8 +2,8 @@ import ./remote_signals/[base, chronicles_logs, community, discovery_summary, envelope, expired, mailserver, messages, peerstats, signal_type, stats, wallet, whisper_filter, update_available, status_updates, waku_backed_up_profile, - waku_backed_up_settings, waku_backed_up_keycards, waku_fetching_backup_progress, pairing] + waku_backed_up_settings, waku_backed_up_wallet_accounts, waku_backed_up_keycards, waku_fetching_backup_progress, pairing] export base, chronicles_logs, community, discovery_summary, envelope, expired, mailserver, messages, peerstats, signal_type, stats, wallet, whisper_filter, update_available, status_updates, waku_backed_up_profile, - waku_backed_up_settings, waku_backed_up_keycards, waku_fetching_backup_progress, pairing \ No newline at end of file + waku_backed_up_settings, waku_backed_up_wallet_accounts, waku_backed_up_keycards, waku_fetching_backup_progress, pairing \ No newline at end of file diff --git a/src/app/modules/main/profile_section/keycard/module.nim b/src/app/modules/main/profile_section/keycard/module.nim index 9c527ad29c..283bfeefdd 100644 --- a/src/app/modules/main/profile_section/keycard/module.nim +++ b/src/app/modules/main/profile_section/keycard/module.nim @@ -268,7 +268,8 @@ proc buildKeycardList(self: Module) = ## If all created keycards for certain keypair are locked, then we need to display that item as locked. item.setLocked(self.areAllKnownKeycardsLockedForKeypair(item.getKeyUid())) items.add(item) - self.view.setKeycardItems(items) + if items.len > 0: + self.view.setKeycardItems(items) method onLoggedInUserImageChanged*(self: Module) = self.view.keycardModel().setImage(singletonInstance.userProfile.getPubKey(), singletonInstance.userProfile.getIcon()) diff --git a/src/app_service/service/wallet_account/dto.nim b/src/app_service/service/wallet_account/dto.nim index d317648417..b429248216 100644 --- a/src/app_service/service/wallet_account/dto.nim +++ b/src/app_service/service/wallet_account/dto.nim @@ -1,4 +1,4 @@ -import tables, json, sequtils, sugar, strutils +import tables, json, strformat, sequtils, sugar, strutils include ../../common/json_utils @@ -104,6 +104,7 @@ type lastUsedDerivationIndex*: int hasBalanceCache*: bool hasMarketValuesCache*: bool + removed*: bool # needs for synchronization proc newDto*( name: string, @@ -149,10 +150,31 @@ proc toWalletAccountDto*(jsonObj: JsonNode): WalletAccountDto = discard jsonObj.getProp("derived-from", result.derivedfrom) discard jsonObj.getProp("keypair-name", result.keypairName) discard jsonObj.getProp("last-used-derivation-index", result.lastUsedDerivationIndex) + discard jsonObj.getProp("removed", result.removed) result.assetsLoading = true result.hasBalanceCache = false result.hasMarketValuesCache = false +proc `$`*(self: WalletAccountDto): string = + result = fmt"""WalletAccountDto[ + name: {self.name}, + address: {self.address}, + mixedcaseAddress: {self.mixedcaseAddress}, + keyUid: {self.keyUid}, + path: {self.path}, + color: {self.color}, + publicKey: {self.publicKey}, + walletType: {self.walletType}, + isChat: {self.isChat}, + emoji: {self.emoji}, + derivedfrom: {self.derivedfrom}, + keypairName: {self.keypairName}, + lastUsedDerivationIndex: {self.lastUsedDerivationIndex}, + hasBalanceCache: {self.hasBalanceCache}, + hasMarketValuesCache: {self.hasMarketValuesCache}, + removed: {self.removed} + ]""" + proc getCurrencyBalance*(self: BalanceDto, currencyPrice: float64): float64 = return self.balance * currencyPrice diff --git a/src/app_service/service/wallet_account/service.nim b/src/app_service/service/wallet_account/service.nim index 66157f9fc9..54f1b9dfd8 100644 --- a/src/app_service/service/wallet_account/service.nim +++ b/src/app_service/service/wallet_account/service.nim @@ -128,6 +128,7 @@ QtObject: proc buildAllTokens(self: Service, accounts: seq[string], store: bool) proc checkRecentHistory*(self: Service) proc startWallet(self: Service) + proc handleWalletAccount(self: Service, account: WalletAccountDto) proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto]) proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto]) proc getAllKnownKeycards*(self: Service): seq[KeyPairDto] @@ -177,6 +178,15 @@ QtObject: except Exception as e: error "error: ", procName="getAccountsByKeyUID", errName = e.name, errDesription = e.msg + proc verifyKeystoreFileForAccount*(self: Service, account, password: string): bool = + try: + let hashedPassword = utils.hashPassword(password) + let response = status_go_accounts.verifyKeystoreFileForAccount(account, hashedPassword) + return response.result.getBool + except Exception as e: + error "error: ", procName="verifyKeystoreFileForAccount", errName = e.name, errDesription = e.msg + return false + proc setEnsName(self: Service, account: WalletAccountDto) = let chainId = self.networkService.getNetworkForEns().chainId try: @@ -193,14 +203,17 @@ QtObject: (cmpIgnoreCase(x.derivedFrom, account.derivedFrom) == 0)) proc setRelatedAccountsForAllAccounts(self: Service, derivedFrom: string) = + if derivedFrom.len == 0: + return for wAcc in self.walletAccounts.mvalues: if not wAcc.derivedFrom.isEmptyOrWhitespace and cmpIgnoreCase(wAcc.derivedFrom, derivedFrom) == 0: self.setRelatedAccountsToAccount(wAcc) - proc storeAccount(self: Service, account: WalletAccountDto) = - # updating related accounts for already added accounts - self.setRelatedAccountsForAllAccounts(account.derivedFrom) + proc storeAccount(self: Service, account: WalletAccountDto, updateRelatedAccounts = true) = + if updateRelatedAccounts: + # updating related accounts for already added accounts + self.setRelatedAccountsForAllAccounts(account.derivedFrom) # add new account to store self.walletAccounts[account.address] = account @@ -291,6 +304,9 @@ QtObject: if settingsField.name == KEY_CURRENCY: self.events.emit(SIGNAL_WALLET_ACCOUNT_CURRENCY_UPDATED, CurrencyUpdated()) + if receivedData.walletAccounts.len > 0: + for acc in receivedData.walletAccounts: + self.handleWalletAccount(acc) self.handleKeycardsState(receivedData.keycards) self.handleKeycardActions(receivedData.keycardActions) @@ -336,7 +352,7 @@ QtObject: error "error: ", errDescription return - proc addNewAccountToLocalStore(self: Service) = + proc addNewAccountToLocalStoreAndNotify(self: Service) = let accounts = self.fetchAccounts() var newAccount: WalletAccountDto var found = false @@ -367,6 +383,16 @@ QtObject: self.setRelatedAccountsForAllAccounts(removedAcc.derivedFrom) self.events.emit(SIGNAL_WALLET_ACCOUNT_DELETED, AccountDeleted(address: address)) + proc updateAccountFromLocalStoreAndNotify(self: Service, address, name, color, emoji: string) = + if not self.walletAccountsContainsAddress(address): + return + var account = self.getAccountByAddress(address) + account.name = name + account.color = color + account.emoji = emoji + self.storeAccount(account, updateRelatedAccounts = false) + self.events.emit(SIGNAL_WALLET_ACCOUNT_UPDATED, WalletAccountUpdated(account: account)) + ## 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 = @@ -384,7 +410,7 @@ QtObject: if not response.error.isNil: error "status-go error", procName="addWalletAccount", errCode=response.error.code, errDesription=response.error.message return response.error.message - self.addNewAccountToLocalStore() + self.addNewAccountToLocalStoreAndNotify() return "" except Exception as e: error "error: ", procName="addWalletAccount", errName=e.name, errDesription=e.msg @@ -490,10 +516,7 @@ QtObject: if not response.error.isNil: error "status-go error", procName="updateWalletAccount", errCode=response.error.code, errDesription=response.error.message return false - account.name = accountName - account.color = color - account.emoji = emoji - self.events.emit(SIGNAL_WALLET_ACCOUNT_UPDATED, WalletAccountUpdated(account: account)) + self.updateAccountFromLocalStoreAndNotify(address, accountName, color, emoji) return true except Exception as e: error "error: ", procName="updateWalletAccount", errName=e.name, errDesription=e.msg @@ -854,6 +877,15 @@ QtObject: error "error: ", procName="deleteKeycard", errName = e.name, errDesription = e.msg return false + proc handleWalletAccount(self: Service, account: WalletAccountDto) = + if account.removed: + self.removeAccountFromLocalStoreAndNotify(account.address) + else: + if self.walletAccountsContainsAddress(account.address): + self.updateAccountFromLocalStoreAndNotify(account.address, account.name, account.color, account.emoji) + else: + self.addNewAccountToLocalStoreAndNotify() + proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto]) = if keycardActions.len == 0: return diff --git a/src/backend/accounts.nim b/src/backend/accounts.nim index 18a06afd53..0c07426d7b 100644 --- a/src/backend/accounts.nim +++ b/src/backend/accounts.nim @@ -29,7 +29,7 @@ proc deleteAccount*(address: string, password: string): RpcResponse[JsonNode] {. let payload = %* [address, password] return core.callPrivateRPC("accounts_deleteAccount", payload) -## Adds a new account and creates a Keystore file if password is provided, otherwise it only creates a new account +## Adds a new account and creates a Keystore file if password is provided, otherwise it only creates a new account. Notifies paired devices. proc addAccount*(password, name, keyPairName, address, path: string, lastUsedDerivationIndex: int, rootWalletMasterKey, publicKey, keyUid, accountType, color, emoji: string): RpcResponse[JsonNode] {.raises: [Exception].} = @@ -49,7 +49,7 @@ proc addAccount*(password, name, keyPairName, address, path: string, lastUsedDer "color": color, #"hidden" present on the status-go side, but we don't use it "derived-from": rootWalletMasterKey, - #"clock" we leave this empty, if needed should be set on the status-go side + #"clock" we leave this empty, set on the status-go side #"removed" present on the status-go side, used for synchronization, no need to set it here "keypair-name": keyPairName, "last-used-derivation-index": lastUsedDerivationIndex @@ -57,14 +57,14 @@ proc addAccount*(password, name, keyPairName, address, path: string, lastUsedDer ] return core.callPrivateRPC("accounts_addAccount", payload) -## Adds a new account without creating a Keystore file +## Adds a new account without creating a Keystore file and notifies paired devices proc addAccountWithoutKeystoreFileCreation*(name, keyPairName, address, path: string, lastUsedDerivationIndex: int, rootWalletMasterKey, publicKey, keyUid, accountType, color, emoji: string): RpcResponse[JsonNode] {.raises: [Exception].} = return addAccount(password = "", name, keyPairName, address, path, lastUsedDerivationIndex, rootWalletMasterKey, publicKey, keyUid, accountType, color, emoji) -## Updates either regular or keycard account, without interaction to a Keystore file +## Updates either regular or keycard account, without interaction to a Keystore file and notifies paired devices proc updateAccount*(name, keyPairName, address, path: string, lastUsedDerivationIndex: int, rootWalletMasterKey, publicKey, keyUid, accountType, color, emoji: string, walletDefaultAccount: bool, chatDefaultAccount: bool): RpcResponse[JsonNode] {.raises: [Exception].} = @@ -83,7 +83,7 @@ proc updateAccount*(name, keyPairName, address, path: string, lastUsedDerivation "color": color, #"hidden" present on the status-go side, but we don't use it "derived-from": rootWalletMasterKey, - #"clock" we leave this empty, if needed should be set on the status-go side + #"clock" we leave this empty, set on the status-go side #"removed" present on the status-go side, used for synchronization, no need to set it here "keypair-name": keyPairName, "last-used-derivation-index": lastUsedDerivationIndex @@ -408,4 +408,8 @@ proc getAddressDetails*(address: string,): RpcResponse[JsonNode] {.raises: [Exce proc verifyPassword*(password: string): RpcResponse[JsonNode] {.raises: [Exception].} = let payload = %* [password] - return core.callPrivateRPC("accounts_verifyPassword", payload) \ No newline at end of file + return core.callPrivateRPC("accounts_verifyPassword", payload) + +proc verifyKeystoreFileForAccount*(address, password: string): RpcResponse[JsonNode] {.raises: [Exception].} = + let payload = %* [address, password] + return core.callPrivateRPC("accounts_verifyKeystoreFileForAccount", payload) \ No newline at end of file