refactor: use create/restore/login status-go endpoitns for keycard (#15090)

* chore: remove unused storeDefaultKeyPair

* chore: added CreateAccount keycard parameters

* chore: remove unused newKeycard argument

* chore: create keycard account with ImportAccountAndLogin endpoint

* fix: remove keycardWhisperPrivateKey CreateAccount parameter

* feat: use LoginAccount endpoint for keycard

* fix: use CreateAccount RootDataDir instead of BackupDisabledDataDir

* fix: use loginAccountKeycard instead of loginAccountKeycardUsingSeedPhrase

* fix_: use `login` instead of `loginLocalPairingAccount` an `loginAccountKeycard`

* chore: refactor scheduling reencrpytion

* fix: FirstRunOldUserKeycardImport

* chore: improve code readability

* feat: use RestoreAccountAndLogin endpoint for keycard

* update status-go
This commit is contained in:
Igor Sirotin 2024-06-26 14:12:39 +02:00 committed by GitHub
parent 8cca2664e4
commit c8bc68b96c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 324 additions and 813 deletions

View File

@ -50,7 +50,6 @@ logScope:
type
AppController* = ref object of RootObj
storeDefaultKeyPair: bool
syncKeycardBasedOnAppWalletState: bool
applyKeycardReplacement: bool
changedKeycardUids: seq[tuple[oldKcUid: string, newKcUid: string]] # used in case user unlocked keycard during onboarding using seed phrase
@ -120,7 +119,6 @@ proc userLoggedIn*(self: AppController): string
proc appReady*(self: AppController)
proc logout*(self: AppController)
proc finishAppLoading*(self: AppController)
proc storeDefaultKeyPairForNewKeycardUser*(self: AppController)
proc syncKeycardBasedOnAppWalletStateAfterLogin*(self: AppController)
proc applyKeycardReplacementAfterLogin*(self: AppController)
proc addToKeycardUidPairsToCheckForAChangeAfterLogin*(self: AppController, oldKeycardUid: string, newKeycardUid: string)
@ -146,7 +144,6 @@ proc connect(self: AppController) =
proc newAppController*(statusFoundation: StatusFoundation): AppController =
result = AppController()
result.storeDefaultKeyPair = false
result.syncKeycardBasedOnAppWalletState = false
result.applyKeycardReplacement = false
result.statusFoundation = statusFoundation
@ -593,9 +590,6 @@ proc applyNecessaryActionsAfterLoggingIn(self: AppController) =
discard self.walletAccountService.updateKeycardUid(oldUid, newUid)
discard self.walletAccountService.setKeycardUnlocked(singletonInstance.userProfile.getKeyUid(), newUid)
proc storeDefaultKeyPairForNewKeycardUser*(self: AppController) =
self.storeDefaultKeyPair = true
proc syncKeycardBasedOnAppWalletStateAfterLogin*(self: AppController) =
self.syncKeycardBasedOnAppWalletState = true

View File

@ -75,9 +75,6 @@ QtObject:
return "0"
return $stint.fromHex(StUint[256], value)
proc generateAlias*(self: Utils, pk: string): string {.slot.} =
return generateAliasFromPk(pk)
proc readTextFile*(self: Utils, filepath: string): string {.slot.} =
try:
return readFile(filepath)

View File

@ -22,7 +22,7 @@ logScope:
type ProfileImageDetails = object
url*: string
croppedImage*: string # TODO: Remove after https://github.com/status-im/status-go/issues/4977
croppedImage*: string
cropRectangle*: ImageCropRectangle
type
@ -189,7 +189,9 @@ proc init*(self: Controller) =
proc shouldStartWithOnboardingScreen*(self: Controller): bool =
return self.accountsService.openedAccounts().len == 0
# This is used when fetching backup failed and we create a new displayName and profileImage.
# This is used in 2 flows, in case fetching backup failed and we create a new displayName and profileImage:
# 1. FirstRunOldUserImportSeedPhrase
# 2. FirstRunOldUserKeycardImport
# At this point the account is already created in the database. All that's left is to set the displayName and profileImage.
proc storeProfileDataAndProceedWithAppLoading*(self: Controller) =
self.delegate.removeAllKeycardUidPairsForCheckingForAChangeAfterLogin() # reason for this is in the table in AppController.nim file
@ -201,12 +203,6 @@ proc storeProfileDataAndProceedWithAppLoading*(self: Controller) =
proc checkFetchingStatusAndProceed*(self: Controller) =
self.delegate.checkFetchingStatusAndProceed()
proc getGeneratedAccounts*(self: Controller): seq[GeneratedAccountDto] =
return self.accountsService.generatedAccounts()
proc getImportedAccount*(self: Controller): GeneratedAccountDto =
return self.accountsService.getImportedAccount()
proc getPasswordStrengthScore*(self: Controller, password, userName: string): int =
return self.generalService.getPasswordStrengthScore(password, userName)
@ -348,7 +344,6 @@ proc tryToObtainDataFromKeychain*(self: Controller) =
let selectedAccount = self.getSelectedLoginAccount()
self.keychainService.tryToObtainData(selectedAccount.keyUid)
# TODO: Remove when implemented https://github.com/status-im/status-go/issues/4977
proc storeIdentityImage*(self: Controller): seq[Image] =
if self.tmpProfileImageDetails.url.len == 0:
return
@ -375,6 +370,7 @@ proc validMnemonic*(self: Controller, mnemonic: string): bool =
# validateMnemonicForImport checks if mnemonic is valid and not yet saved in local database
proc validateMnemonicForImport*(self: Controller, mnemonic: string): bool =
let (keyUID, err) = self.accountsService.validateMnemonic(mnemonic)
if err.len != 0:
self.delegate.emitStartupError(err, StartupErrorType.ImportAccError)
@ -387,16 +383,6 @@ proc validateMnemonicForImport*(self: Controller, mnemonic: string): bool =
self.setSeedPhrase(mnemonic)
return true
# TODO: Remove after https://github.com/status-im/status-go/issues/4977
proc importMnemonic*(self: Controller): bool =
let error = self.accountsService.importMnemonic(self.tmpSeedPhrase)
if(error.len == 0):
self.delegate.importAccountSuccess()
return true
else:
self.delegate.emitStartupError(error, StartupErrorType.ImportAccError)
return false
proc setupKeychain(self: Controller, store: bool) =
if store:
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
@ -425,6 +411,7 @@ proc importAccountAndLogin*(self: Controller, storeToKeychain: bool, recoverAcco
self.connectToFetchingFromWakuEvents()
else:
self.delegate.moveToLoadingAppState()
let error = self.accountsService.importAccountAndLogin(
self.tmpSeedPhrase,
self.tmpPassword,
@ -433,56 +420,48 @@ proc importAccountAndLogin*(self: Controller, storeToKeychain: bool, recoverAcco
self.tmpProfileImageDetails.url,
self.tmpProfileImageDetails.cropRectangle,
)
self.processCreateAccountResult(error, storeToKeychain)
proc storeKeycardAccountAndLogin*(self: Controller, storeToKeychain: bool, newKeycard: bool) =
if self.importMnemonic():
# NOTE: Called during FirstRunNewUserNewKeycardKeys and FirstRunNewUserImportSeedPhraseIntoKeycard
# WARNING: Reuse `importAccountAndLogin` with custom parameters
proc storeKeycardAccountAndLogin*(self: Controller, storeToKeychain: bool, newKeycard: bool = true) =
self.delegate.moveToLoadingAppState()
if newKeycard:
self.delegate.storeDefaultKeyPairForNewKeycardUser()
self.storeMetadataForNewKeycardUser()
else:
self.syncKeycardBasedOnAppWalletStateAfterLogin()
let (_, flowEvent) = self.keycardService.getLastReceivedKeycardData() # we need this to get the correct instanceUID
self.accountsService.setupAccountKeycard(flowEvent, self.tmpDisplayName, useImportedAcc = true)
self.setupKeychain(storeToKeychain)
else:
error "an error ocurred while importing mnemonic"
let (_, flowEvent) = self.keycardService.getLastReceivedKeycardData()
let error = self.accountsService.importAccountAndLogin(
self.tmpSeedPhrase,
password = "", # For keycard it will be substituted with`encryption.publicKey` in status-go
false,
self.tmpDisplayName,
self.tmpProfileImageDetails.url,
self.tmpProfileImageDetails.cropRectangle,
keycardInstanceUID = flowEvent.instanceUID,
)
self.processCreateAccountResult(error, storeToKeychain)
# NOTE: Called during FirstRunOldUserKeycardImport
proc setupKeycardAccount*(self: Controller, storeToKeychain: bool, recoverAccount: bool = false) =
if self.tmpKeycardEvent.keyUid.len == 0 or
self.accountsService.openedAccountsContainsKeyUid(self.tmpKeycardEvent.keyUid):
self.delegate.emitStartupError(ACCOUNT_ALREADY_EXISTS_ERROR, StartupErrorType.ImportAccError)
return
if recoverAccount:
self.delegate.prepareAndInitFetchingData()
self.connectToFetchingFromWakuEvents()
if self.tmpSeedPhrase.len > 0:
# if `tmpSeedPhrase` is not empty means user has recovered keycard via seed phrase
let accFromSeedPhrase = self.accountsService.createAccountFromMnemonic(self.tmpSeedPhrase, includeEncryption = true,
includeWhisper = true, includeRoot = true, includeDefaultWallet = true, includeEip1581 = true)
self.tmpKeycardEvent.masterKey.privateKey = accFromSeedPhrase.privateKey
self.tmpKeycardEvent.masterKey.publicKey = accFromSeedPhrase.publicKey
self.tmpKeycardEvent.masterKey.address = accFromSeedPhrase.address
self.tmpKeycardEvent.whisperKey.privateKey = accFromSeedPhrase.derivedAccounts.whisper.privateKey
self.tmpKeycardEvent.whisperKey.publicKey = accFromSeedPhrase.derivedAccounts.whisper.publicKey
self.tmpKeycardEvent.whisperKey.address = accFromSeedPhrase.derivedAccounts.whisper.address
self.tmpKeycardEvent.walletKey.privateKey = accFromSeedPhrase.derivedAccounts.defaultWallet.privateKey
self.tmpKeycardEvent.walletKey.publicKey = accFromSeedPhrase.derivedAccounts.defaultWallet.publicKey
self.tmpKeycardEvent.walletKey.address = accFromSeedPhrase.derivedAccounts.defaultWallet.address
self.tmpKeycardEvent.walletRootKey.privateKey = accFromSeedPhrase.derivedAccounts.walletRoot.privateKey
self.tmpKeycardEvent.walletRootKey.publicKey = accFromSeedPhrase.derivedAccounts.walletRoot.publicKey
self.tmpKeycardEvent.walletRootKey.address = accFromSeedPhrase.derivedAccounts.walletRoot.address
self.tmpKeycardEvent.eip1581Key.privateKey = accFromSeedPhrase.derivedAccounts.eip1581.privateKey
self.tmpKeycardEvent.eip1581Key.publicKey = accFromSeedPhrase.derivedAccounts.eip1581.publicKey
self.tmpKeycardEvent.eip1581Key.address = accFromSeedPhrase.derivedAccounts.eip1581.address
self.tmpKeycardEvent.encryptionKey.privateKey = accFromSeedPhrase.derivedAccounts.encryption.privateKey
self.tmpKeycardEvent.encryptionKey.publicKey = accFromSeedPhrase.derivedAccounts.encryption.publicKey
self.tmpKeycardEvent.encryptionKey.address = accFromSeedPhrase.derivedAccounts.encryption.address
self.syncKeycardBasedOnAppWalletStateAfterLogin()
self.accountsService.setupAccountKeycard(self.tmpKeycardEvent, self.tmpDisplayName, useImportedAcc = false, recoverAccount)
self.setupKeychain(storeToKeychain)
let error = self.accountsService.restoreKeycardAccountAndLogin(
self.tmpKeycardEvent,
recoverAccount,
self.tmpDisplayName,
self.tmpProfileImageDetails.url,
self.tmpProfileImageDetails.cropRectangle,
)
self.processCreateAccountResult(error, storeToKeychain)
proc getOpenedAccounts*(self: Controller): seq[AccountDto] =
return self.accountsService.openedAccounts()
@ -509,51 +488,41 @@ proc isSelectedAccountAKeycardAccount*(self: Controller): bool =
let selectedAccount = self.getSelectedLoginAccount()
return selectedAccount.keycardPairing.len > 0
proc login*(self: Controller) =
proc login*(self: Controller, keycard: bool = false, keycardReplacement: bool = false) =
self.delegate.moveToLoadingAppState()
let selectedAccount = self.getSelectedLoginAccount()
self.accountsService.login(selectedAccount, hashPassword(self.tmpPassword))
var passwordHash, chatPrivateKey, mnemonic = ""
if not keycard:
passwordHash = hashPassword(self.tmpPassword)
else:
passwordHash = self.tmpKeycardEvent.encryptionKey.publicKey
chatPrivateKey = self.tmpKeycardEvent.whisperKey.privateKey
mnemonic = self.tmpSeedPhrase
if keycard and keycardReplacement:
self.delegate.applyKeycardReplacementAfterLogin()
self.accountsService.login(
self.getSelectedLoginAccount(),
passwordHash,
chatPrivateKey,
mnemonic,
)
proc loginLocalPairingAccount*(self: Controller) =
self.delegate.moveToLoadingAppState()
if self.localPairingStatus.chatKey.len == 0:
self.accountsService.login(self.localPairingStatus.account, self.localPairingStatus.password)
else:
var kcEvent = KeycardEvent()
kcEvent.keyUid = self.localPairingStatus.account.keyUid
kcEvent.whisperKey.privateKey = self.localPairingStatus.chatKey
kcEvent.encryptionKey.publicKey = self.localPairingStatus.password
discard self.accountsService.loginAccountKeycard(self.localPairingStatus.account, kcEvent)
proc loginAccountKeycard*(self: Controller, storeToKeychainValue: string, keycardReplacement = false) =
if keycardReplacement:
self.delegate.applyKeycardReplacementAfterLogin()
singletonInstance.localAccountSettings.setStoreToKeychainValue(storeToKeychainValue)
self.delegate.moveToLoadingAppState()
let selAcc = self.getSelectedLoginAccount()
let error = self.accountsService.loginAccountKeycard(selAcc, self.tmpKeycardEvent)
if(error.len > 0):
self.delegate.emitAccountLoginError(error)
proc loginAccountKeycardUsingSeedPhrase*(self: Controller, storeToKeychain: bool) =
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true, includeWhisper = true)
let selAcc = self.getSelectedLoginAccount()
var kcData = KeycardEvent(
keyUid: acc.keyUid,
masterKey: KeyDetails(address: acc.address),
whisperKey: KeyDetails(privateKey: acc.derivedAccounts.whisper.privateKey),
encryptionKey: KeyDetails(publicKey: acc.derivedAccounts.encryption.publicKey)
self.accountsService.login(
self.localPairingStatus.account,
self.localPairingStatus.password,
chatPrivateKey = self.localPairingStatus.chatKey
)
if acc.derivedAccounts.whisper.privateKey.startsWith("0x"):
kcData.whisperKey.privateKey = acc.derivedAccounts.whisper.privateKey[2..^1]
self.setupKeychain(storeToKeychain)
self.delegate.moveToLoadingAppState()
let error = self.accountsService.loginAccountKeycard(selAcc, kcData)
if(error.len > 0):
self.delegate.emitAccountLoginError(error)
# FIXME: Why do we even have storeToKeychain during login? Makes no sense
# https://github.com/status-im/status-desktop/issues/15167
proc loginAccountKeycard*(self: Controller, storeToKeychain: bool, keycardReplacement = false) =
# singletonInstance.localAccountSettings.setStoreToKeychainValue(storeToKeychainValue)
self.login(keycard = true, keycardReplacement = keycardReplacement)
proc convertKeycardProfileKeypairToRegular*(self: Controller) =
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true)
@ -633,8 +602,8 @@ proc buildSeedPhrasesFromIndexes*(self: Controller, seedPhraseIndexes: seq[int])
proc generateRandomPUK*(self: Controller): string =
return self.keycardService.generateRandomPUK()
# Stores metadata, default Status account only, to the keycard for a newly created keycard user.
proc storeMetadataForNewKeycardUser(self: Controller) =
## Stores metadata, default Status account only, to the keycard for a newly created keycard user.
let paths = @[account_constants.PATH_DEFAULT_WALLET]
self.runStoreMetadataFlow(self.getDisplayName(), self.getPin(), paths)

View File

@ -29,16 +29,16 @@ proc command(self: BiometricsState, controller: Controller, storeToKeychain: boo
## but since current implementation is like that and this is not a bug fixing issue, left as it is.
controller.importAccountAndLogin(storeToKeychain, recoverAccount = true)
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
controller.storeKeycardAccountAndLogin(storeToKeychain)
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
controller.storeKeycardAccountAndLogin(storeToKeychain)
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
controller.setupKeycardAccount(storeToKeychain, recoverAccount = true)
elif self.flowType == FlowType.LostKeycardReplacement:
self.storeToKeychain = storeToKeychain
controller.startLoginFlowAutomatically(controller.getPin())
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
controller.loginAccountKeycard(storeToKeychain, keycardReplacement = false)
method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
self.command(controller, true)
@ -49,10 +49,6 @@ method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
if self.flowType == FlowType.LostKeycardReplacement:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and keycardEvent.error.len == 0:
controller.setKeycardEvent(keycardEvent)
var storeToKeychainValue = LS_VALUE_NEVER
if self.storeToKeychain:
storeToKeychainValue = LS_VALUE_NOT_NOW
controller.loginAccountKeycard(storeToKeychainValue, keycardReplacement = true)
controller.loginAccountKeycard(self.storeToKeychain, keycardReplacement = true)

View File

@ -32,7 +32,10 @@ method resolveKeycardNextState*(self: KeycardEnterPinState, keycardFlowType: str
let state = ensureReaderAndCardPresenceOnboarding(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
if self.flowType != FlowType.FirstRunOldUserKeycardImport:
return
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPIN:
@ -55,8 +58,8 @@ method resolveKeycardNextState*(self: KeycardEnterPinState, keycardFlowType: str
return createState(StateType.KeycardMaxPairingSlotsReached, self.flowType, self.getBackState)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
if not main_constants.IS_MACOS:
controller.setupKeycardAccount(storeToKeychain = false, recoverAccount = true)
return createState(StateType.ProfileFetching, self.flowType, nil)
if main_constants.SUPPORTS_FINGERPRINT:
let backState = findBackStateWithTargetedStateType(self, StateType.RecoverOldUser)
return createState(StateType.Biometrics, self.flowType, backState)
controller.setupKeycardAccount(storeToKeychain = false, recoverAccount = true)
return createState(StateType.ProfileFetching, self.flowType, nil)

View File

@ -36,11 +36,11 @@ method resolveKeycardNextState*(self: KeycardEnterPukState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
if not main_constants.IS_MACOS:
controller.setupKeycardAccount(storeToKeychain = false)
return nil
if main_constants.SUPPORTS_FINGERPRINT:
let backState = findBackStateWithTargetedStateType(self, StateType.RecoverOldUser)
return createState(StateType.Biometrics, self.flowType, backState)
controller.setupKeycardAccount(storeToKeychain = false)
return nil
if self.flowType == FlowType.AppLogin:
if keycardFlowType == ResponseTypeValueEnterNewPIN and
keycardEvent.error.len > 0 and
@ -57,5 +57,7 @@ method resolveKeycardNextState*(self: KeycardEnterPukState, keycardFlowType: str
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
# FIXME: Make sure storeToKeychain is correct here. The idea is not to pass it at all
# https://github.com/status-im/status-desktop/issues/15167
controller.loginAccountKeycard(storeToKeychain = false)
return nil

View File

@ -19,9 +19,9 @@ method getNextPrimaryState*(self: KeycardPinSetState, controller: Controller): S
return createState(StateType.UserProfileCreate, self.flowType, self.getBackState)
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
if controller.getValidPuk():
if not main_constants.IS_MACOS:
return createState(StateType.ProfileFetching, self.flowType, nil)
if main_constants.SUPPORTS_FINGERPRINT:
return createState(StateType.Biometrics, self.flowType, self.getBackState)
return createState(StateType.ProfileFetching, self.flowType, nil)
return createState(StateType.KeycardWrongPuk, self.flowType, self.getBackState)
if self.flowType == FlowType.AppLogin:
if controller.getRecoverKeycardUsingSeedPhraseWhileLoggingIn():
@ -35,7 +35,7 @@ method getNextPrimaryState*(self: KeycardPinSetState, controller: Controller): S
method executePrimaryCommand*(self: KeycardPinSetState, controller: Controller) =
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
if main_constants.IS_MACOS:
if main_constants.SUPPORTS_FINGERPRINT:
return
if controller.getValidPuk():
controller.setupKeycardAccount(storeToKeychain = false, recoverAccount = true)
@ -44,24 +44,28 @@ method executePrimaryCommand*(self: KeycardPinSetState, controller: Controller)
controller.startLoginFlowAutomatically(controller.getPin())
return
if controller.getValidPuk():
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
# FIXME: Make sure storeToKeychain is correct here. The idea is not to pass it at all
# https://github.com/status-im/status-desktop/issues/15167
# let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychain = false)
if self.flowType == FlowType.LostKeycardReplacement:
controller.startLoginFlowAutomatically(controller.getPin())
method resolveKeycardNextState*(self: KeycardPinSetState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
var storeToKeychainValue = LS_VALUE_NEVER
if self.flowType == FlowType.LostKeycardReplacement:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
if main_constants.IS_MACOS:
storeToKeychainValue = LS_VALUE_NOT_NOW
if keycardFlowType != ResponseTypeValueKeycardFlowResult:
return
if keycardEvent.error.len != 0:
return
let keycardReplacement = self.flowType == FlowType.LostKeycardReplacement
if not keycardReplacement and self.flowType != FlowType.AppLogin:
return
let storeToKeychain = keycardReplacement and main_constants.SUPPORTS_FINGERPRINT
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(storeToKeychainValue, keycardReplacement = true)
if self.flowType == FlowType.AppLogin:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
# we are here in case of recover account from the login flow using seed phrase
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(storeToKeychainValue, keycardReplacement = false)
controller.loginAccountKeycard(storeToKeychain, keycardReplacement)

View File

@ -23,7 +23,9 @@ method resolveKeycardNextState*(self: KeycardWrongPinState, keycardFlowType: str
let state = ensureReaderAndCardPresenceOnboarding(self, keycardFlowType, keycardEvent, controller)
if not state.isNil:
return state
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
if self.flowType != FlowType.FirstRunOldUserKeycardImport:
return
if keycardFlowType == ResponseTypeValueEnterPIN and
keycardEvent.error.len > 0 and
keycardEvent.error == RequestParamPIN:
@ -47,8 +49,8 @@ method resolveKeycardNextState*(self: KeycardWrongPinState, keycardFlowType: str
return createState(StateType.KeycardMaxPairingSlotsReached, self.flowType, self.getBackState)
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
if not main_constants.IS_MACOS:
controller.setupKeycardAccount(storeToKeychain = false)
return nil
if main_constants.SUPPORTS_FINGERPRINT:
let backState = findBackStateWithTargetedStateType(self, StateType.RecoverOldUser)
return createState(StateType.Biometrics, self.flowType, backState)
controller.setupKeycardAccount(storeToKeychain = false)
return nil

View File

@ -38,11 +38,11 @@ method resolveKeycardNextState*(self: KeycardWrongPukState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
if not main_constants.IS_MACOS:
controller.setupKeycardAccount(storeToKeychain = false, recoverAccount = true)
return createState(StateType.ProfileFetching, self.flowType, nil)
if main_constants.SUPPORTS_FINGERPRINT:
let backState = findBackStateWithTargetedStateType(self, StateType.RecoverOldUser)
return createState(StateType.Biometrics, self.flowType, backState)
controller.setupKeycardAccount(storeToKeychain = false, recoverAccount = true)
return createState(StateType.ProfileFetching, self.flowType, nil)
if self.flowType == FlowType.AppLogin:
if keycardFlowType == ResponseTypeValueEnterPUK and
keycardEvent.error.len > 0 and
@ -60,5 +60,7 @@ method resolveKeycardNextState*(self: KeycardWrongPukState, keycardFlowType: str
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
# FIXME: Make sure storeToKeychain is correct here. The idea is not to pass it at all
# https://github.com/status-im/status-desktop/issues/15167
controller.loginAccountKeycard(false)
return nil

View File

@ -11,4 +11,6 @@ proc delete*(self: LoginKeycardPinVerifiedState) =
method executePrimaryCommand*(self: LoginKeycardPinVerifiedState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
# FIXME: Make sure storeToKeychain is correct here. The idea is not to pass it at all
# https://github.com/status-im/status-desktop/issues/15167
controller.loginAccountKeycard(false)

View File

@ -25,8 +25,8 @@ method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller:
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
controller.importAccountAndLogin(storeToKeychain)
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
controller.storeKeycardAccountAndLogin(storeToKeychain)
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
controller.importAccountAndLogin(storeToKeychain, recoverAccount = true)
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)
controller.loginAccountKeycard(storeToKeychain, keycardReplacement = true)

View File

@ -68,9 +68,6 @@ method startUpUIRaised*(self: AccessInterface) {.base.} =
method emitLogOut*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.} =
raise newException(ValueError, "No implementation available")
method generateImage*(self: AccessInterface, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.base.} =
raise newException(ValueError, "No implementation available")
@ -110,9 +107,6 @@ method emitStartupError*(self: AccessInterface, error: string, errType: StartupE
method validMnemonic*(self: AccessInterface, mnemonic: string): bool {.base.} =
raise newException(ValueError, "No implementation available")
method importAccountSuccess*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method setSelectedLoginAccount*(self: AccessInterface, item: login_acc_item.Item) {.base.} =
raise newException(ValueError, "No implementation available")
@ -152,9 +146,6 @@ method setRemainingAttempts*(self: AccessInterface, value: int) {.base.} =
method runFactoryResetPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method storeDefaultKeyPairForNewKeycardUser*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method syncKeycardBasedOnAppWalletStateAfterLogin*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
@ -238,7 +229,6 @@ type
c.userLoggedIn()
c.finishAppLoading()
c.appReady()
c.storeDefaultKeyPairForNewKeycardUser()
c.syncKeycardBasedOnAppWalletStateAfterLogin()
c.applyKeycardReplacementAfterLogin()
c.addToKeycardUidPairsToCheckForAChangeAfterLogin(string, string)

View File

@ -1,29 +0,0 @@
type
Item* = object
id: string
alias: string
address: string
pubKey: string
keyUid: string
proc initItem*(id, alias, address, pubKey, keyUid: string): Item =
result.id = id
result.alias = alias
result.address = address
result.pubKey = pubKey
result.keyUid = keyUid
proc getId*(self: Item): string =
return self.id
proc getAlias*(self: Item): string =
return self.alias
proc getAddress*(self: Item): string =
return self.address
proc getPubKey*(self: Item): string =
return self.pubKey
proc getKeyUid*(self: Item): string =
return self.keyUid

View File

@ -1,66 +0,0 @@
import NimQml, Tables, strutils
import generated_account_item
type
ModelRole {.pure.} = enum
Id = UserRole + 1
Alias
Address
PubKey
KeyUid
QtObject:
type
Model* = ref object of QAbstractListModel
items: seq[Item]
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newModel*(): Model =
new(result, delete)
result.setup
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Id.int:"accountId",
ModelRole.Alias.int:"username",
ModelRole.Address.int:"address",
ModelRole.PubKey.int:"pubKey",
ModelRole.KeyUid.int:"keyUid"
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Id:
result = newQVariant(item.getId())
of ModelRole.Alias:
result = newQVariant(item.getAlias())
of ModelRole.Address:
result = newQVariant(item.getAddress())
of ModelRole.PubKey:
result = newQVariant(item.getPubKey())
of ModelRole.KeyUid:
result = newQVariant(item.getKeyUid())
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
self.items = items
self.endResetModel()

View File

@ -3,7 +3,6 @@ import NimQml, chronicles
import io_interface
import view, controller
import internal/[state, state_factory]
import models/generated_account_item as gen_acc_item
import models/login_account_item as login_acc_item
import models/fetching_data_model as fetch_model
import app/global/global_singleton
@ -119,12 +118,6 @@ method load*[T](self: Module[T]) =
singletonInstance.engine.setRootContextProperty("startupModule", self.viewVariant)
self.controller.init()
let generatedAccounts = self.controller.getGeneratedAccounts()
var accounts: seq[gen_acc_item.Item]
for acc in generatedAccounts:
accounts.add(gen_acc_item.initItem(acc.id, acc.alias, acc.address, acc.derivedAccounts.whisper.publicKey, acc.keyUid))
self.view.setGeneratedAccountList(accounts)
if self.controller.shouldStartWithOnboardingScreen():
self.view.setCurrentStartupState(newWelcomeState(state.FlowType.General, nil))
else:
@ -262,9 +255,6 @@ method onQuinaryActionClicked*[T](self: Module[T]) =
self.view.setCurrentStartupState(nextState)
debug "quinary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
method getImportedAccount*[T](self: Module[T]): GeneratedAccountDto =
return self.controller.getImportedAccount()
method generateImage*[T](self: Module[T], imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
return self.controller.generateImage(imageUrl, aX, aY, bX, bY)
@ -304,9 +294,6 @@ method emitStartupError*[T](self: Module[T], error: string, errType: StartupErro
method validMnemonic*[T](self: Module[T], mnemonic: string): bool =
return self.controller.validMnemonic(mnemonic)
method importAccountSuccess*[T](self: Module[T]) =
self.view.importAccountSuccess()
method setSelectedLoginAccount*[T](self: Module[T], item: login_acc_item.Item) =
self.controller.cancelCurrentFlow()
if item.getKeyUid().len == 0:
@ -535,9 +522,6 @@ method onSharedKeycarModuleFlowTerminated*[T](self: Module[T], lastStepInTheCurr
self.view.setCurrentStartupState(newState)
debug "new state for onboarding/login flow continuation after shared flow is terminated", setCurrFlow=newState.flowType(), newCurrState=newState.stateType()
method storeDefaultKeyPairForNewKeycardUser*[T](self: Module[T]) =
self.delegate.storeDefaultKeyPairForNewKeycardUser()
method syncKeycardBasedOnAppWalletStateAfterLogin*[T](self: Module[T]) =
self.delegate.syncKeycardBasedOnAppWalletStateAfterLogin()

View File

@ -2,8 +2,6 @@ import NimQml, chronicles
import io_interface
import selected_login_account
import internal/[state, state_wrapper]
import models/generated_account_model as gen_acc_model
import models/generated_account_item as gen_acc_item
import models/login_account_model as login_acc_model
import models/login_account_item as login_acc_item
import models/fetching_data_model as fetch_model
@ -25,8 +23,6 @@ QtObject:
showBeforeGetStartedPopup: bool
currentStartupState: StateWrapper
currentStartupStateVariant: QVariant
generatedAccountsModel: gen_acc_model.Model
generatedAccountsModelVariant: QVariant
selectedLoginAccount: SelectedLoginAccount
selectedLoginAccountVariant: QVariant
loginAccountsModel: login_acc_model.Model
@ -41,8 +37,6 @@ QtObject:
proc delete*(self: View) =
self.currentStartupStateVariant.delete
self.currentStartupState.delete
self.generatedAccountsModel.delete
self.generatedAccountsModelVariant.delete
self.selectedLoginAccount.delete
self.selectedLoginAccountVariant.delete
self.loginAccountsModel.delete
@ -62,8 +56,6 @@ QtObject:
result.appState = AppState.StartupState
result.currentStartupState = newStateWrapper()
result.currentStartupStateVariant = newQVariant(result.currentStartupState)
result.generatedAccountsModel = gen_acc_model.newModel()
result.generatedAccountsModelVariant = newQVariant(result.generatedAccountsModel)
result.selectedLoginAccount = newSelectedLoginAccount()
result.selectedLoginAccountVariant = newQVariant(result.selectedLoginAccount)
result.loginAccountsModel = login_acc_model.newModel()
@ -138,35 +130,6 @@ QtObject:
proc emitLogOut*(self: View) =
self.logOut()
proc generatedAccountsModelChanged*(self: View) {.signal.}
proc getGeneratedAccountsModel(self: View): QVariant {.slot.} =
return self.generatedAccountsModelVariant
proc setGeneratedAccountList*(self: View, accounts: seq[gen_acc_item.Item]) =
self.generatedAccountsModel.setItems(accounts)
self.generatedAccountsModelChanged()
QtProperty[QVariant] generatedAccountsModel:
read = getGeneratedAccountsModel
notify = generatedAccountsModelChanged
proc importedAccountChanged*(self: View) {.signal.}
proc getImportedAccountAlias*(self: View): string {.slot.} =
return self.delegate.getImportedAccount().alias
QtProperty[string] importedAccountAlias:
read = getImportedAccountAlias
notify = importedAccountChanged
proc getImportedAccountAddress*(self: View): string {.slot.} =
return self.delegate.getImportedAccount().address
QtProperty[string] importedAccountAddress:
read = getImportedAccountAddress
notify = importedAccountChanged
proc getImportedAccountPubKey*(self: View): string {.slot.} =
return self.delegate.getImportedAccount().derivedAccounts.whisper.publicKey
QtProperty[string] importedAccountPubKey:
read = getImportedAccountPubKey
notify = importedAccountChanged
proc generateImage*(self: View, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
return self.delegate.generateImage(imageUrl, aX, aY, bX, bY)
@ -207,9 +170,6 @@ QtObject:
proc validMnemonic*(self: View, mnemonic: string): bool {.slot.} =
return self.delegate.validMnemonic(mnemonic)
proc importAccountSuccess*(self: View) =
self.importedAccountChanged()
proc selectedLoginAccountChanged*(self: View) {.signal.}
proc getSelectedLoginAccount(self: View): QVariant {.slot.} =
return self.selectedLoginAccountVariant

View File

@ -1,6 +1,6 @@
import json, random, times, strutils, sugar, os, re, chronicles
import json, times, strutils, sugar, os, re, chronicles
import nimcrypto
import signing_phrases, account_constants
import account_constants
import ../../constants as main_constants
@ -26,16 +26,6 @@ proc prefix*(methodName: string, isExt:bool = true): string =
result = result & (if isExt: "ext_" else: "_")
result = result & methodName
proc generateSigningPhrase*(count: int): string =
let now = getTime()
var rng = initRand(now.toUnix * 1000000000 + now.nanosecond)
var phrases: seq[string] = @[]
for i in 1..count:
phrases.add(rng.sample(signing_phrases.phrases))
result = phrases.join(" ")
proc first*(jArray: JsonNode, fieldName, id: string): JsonNode =
if jArray == nil:
return nil

View File

@ -7,7 +7,7 @@ export image_crop_rectangle
type
CreateAccountRequest* = object
backupDisabledDataDir*: string
rootDataDir*: string
kdfIterations*: int
deviceName*: string
displayName*: string
@ -38,9 +38,12 @@ type
torrentConfigEnabled*: Option[bool]
torrentConfigPort*: Option[int]
keycardInstanceUID*: string
keycardPairingDataFile*: string
proc toJson*(self: CreateAccountRequest): JsonNode =
result = %*{
"backupDisabledDataDir": self.backupDisabledDataDir,
"rootDataDir": self.rootDataDir,
"kdfIterations": self.kdfIterations,
"deviceName": self.deviceName,
"displayName": self.displayName,
@ -54,6 +57,8 @@ proc toJson*(self: CreateAccountRequest): JsonNode =
"logEnabled": self.logEnabled,
"previewPrivacy": self.previewPrivacy,
"upstreamConfig": self.upstreamConfig,
"keycardInstanceUID": self.keycardInstanceUID,
"keycardPairingDataFile": self.keycardPairingDataFile,
}
if self.logLevel.isSome():

View File

@ -0,0 +1,28 @@
import json
type
KeycardData* = ref object
keyUID*: string
address*: string
whisperPrivateKey*: string
whisperPublicKey*: string
whisperAddress*: string
walletPublicKey*: string
walletAddress*: string
walletRootAddress*: string
eip1581Address*: string
encryptionPublicKey*: string
proc toJson*(self: KeycardData): JsonNode =
result = %*{
"keyUID": self.keyUID,
"address": self.address,
"whisperPrivateKey": self.whisperPrivateKey,
"whisperPublicKey": self.whisperPublicKey,
"whisperAddress": self.whisperAddress,
"walletPublicKey": self.walletPublicKey,
"walletAddress": self.walletAddress,
"walletRootAddress": self.walletRootAddress,
"eip1581Address": self.eip1581Address,
"encryptionPublicKey": self.encryptionPublicKey
}

View File

@ -1,5 +1,7 @@
import json
import ./wallet_secretes_config
import wallet_secretes_config
export wallet_secretes_config
type
LoginAccountRequest* = object
@ -9,6 +11,8 @@ type
runtimeLogLevel*: string
wakuV2Nameserver*: string
bandwidthStatsEnabled*: bool
keycardWhisperPrivateKey*: string
mnemonic*: string
walletSecretsConfig*: WalletSecretsConfig
proc toJson*(self: LoginAccountRequest): JsonNode =
@ -19,6 +23,8 @@ proc toJson*(self: LoginAccountRequest): JsonNode =
"runtimeLogLevel": self.runtimeLogLevel,
"wakuV2Nameserver": self.wakuV2Nameserver,
"bandwidthStatsEnabled": self.bandwidthStatsEnabled,
"keycardWhisperPrivateKey": self.keycardWhisperPrivateKey,
"mnemonic": self.mnemonic,
}
for key, value in self.walletSecretsConfig.toJson().pairs():
result[key] = value

View File

@ -1,18 +1,25 @@
import json
import create_account_request
import create_account_request, keycard_data
export create_account_request, keycard_data
type
RestoreAccountRequest* = object
mnemonic*: string
keycard*: KeycardData
fetchBackup*: bool
createAccountRequest*: CreateAccountRequest
proc toJson*(self: RestoreAccountRequest): JsonNode =
result = %*{
"mnemonic": self.mnemonic,
"fetchBackup": self.fetchBackup
"fetchBackup": self.fetchBackup,
}
if self.keycard != nil:
result["keycard"] = self.keycard.toJson()
for key, value in self.createAccountRequest.toJson().pairs():
result[key] = value

View File

@ -1,11 +1,10 @@
import NimQml, Tables, os, json, stew/shims/strformat, sequtils, strutils, uuids, times, std/options
import NimQml, Tables, os, json, stew/shims/strformat, sequtils, strutils, times, std/options
import json_serialization, chronicles
import ../../../app/global/global_singleton
import ./dto/accounts as dto_accounts
import ./dto/generated_accounts as dto_generated_accounts
import ./dto/login_request
import ./dto/create_account_request
import ./dto/restore_account_request
from ../keycard/service import KeycardEvent, KeyDetails
@ -20,8 +19,6 @@ import ../../../app/core/fleets/fleet_configuration
import ../../common/[account_constants, network_constants, utils]
import ../../../constants as main_constants
import ../settings/dto/settings as settings
export dto_accounts
export dto_generated_accounts
@ -29,8 +26,6 @@ export dto_generated_accounts
logScope:
topics = "accounts-service"
const DEFAULT_WALLET_ACCOUNT_NAME = "Account 1"
const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET, PATH_ENCRYPTION]
const ACCOUNT_ALREADY_EXISTS_ERROR* = "account already exists"
const KDF_ITERATIONS* {.intdefine.} = 256_000
const DEFAULT_CUSTOMIZATION_COLOR = "primary" # to match `CustomizationColor` on the go side
@ -62,15 +57,15 @@ QtObject:
events: EventEmitter
threadpool: ThreadPool
fleetConfiguration: FleetConfiguration
generatedAccounts: seq[GeneratedAccountDto]
accounts: seq[AccountDto]
loggedInAccount: AccountDto
importedAccount: GeneratedAccountDto
keyStoreDir: string
defaultWalletEmoji: string
tmpAccount: AccountDto
tmpHashedPassword: string
proc restoreAccountAndLogin(self: Service, request: RestoreAccountRequest): string
proc delete*(self: Service) =
self.QObject.delete
@ -83,6 +78,8 @@ QtObject:
result.keyStoreDir = main_constants.ROOTKEYSTOREDIR
result.defaultWalletEmoji = ""
proc scheduleReencrpytion(self: Service, account: AccountDto, hashedPassword: string, timeout: int = 1000)
proc setLocalAccountSettingsFile(self: Service) =
if self.loggedInAccount.isValid():
singletonInstance.localAccountSettings.setFileName(self.loggedInAccount.name)
@ -99,9 +96,6 @@ QtObject:
self.loggedInAccount.images = images
singletonInstance.localAccountSettings.setFileName(displayName)
proc getImportedAccount*(self: Service): GeneratedAccountDto =
return self.importedAccount
proc setKeyStoreDir(self: Service, key: string) =
self.keyStoreDir = joinPath(main_constants.ROOTKEYSTOREDIR, key) & main_constants.sep
discard status_general.initKeystore(self.keyStoreDir)
@ -118,22 +112,10 @@ QtObject:
self.updateLoggedInAccount(receivedData.backedUpProfile.displayName, receivedData.backedUpProfile.images)
proc init*(self: Service) =
try:
let response = status_account.generateAddresses(PATHS)
self.generatedAccounts = map(response.result.getElems(),
proc(x: JsonNode): GeneratedAccountDto = toGeneratedAccountDto(x))
for account in self.generatedAccounts.mitems:
account.alias = generateAliasFromPk(account.derivedAccounts.whisper.publicKey)
except Exception as e:
error "error: ", procName="init", errName = e.name, errDesription = e.msg
discard
proc clear*(self: Service) =
self.generatedAccounts = @[]
self.loggedInAccount = AccountDto()
self.importedAccount = GeneratedAccountDto()
proc validateMnemonic*(self: Service, mnemonic: string): (string, string) =
try:
@ -144,13 +126,6 @@ QtObject:
except Exception as e:
error "error: ", procName="validateMnemonic", errName = e.name, errDesription = e.msg
proc generatedAccounts*(self: Service): seq[GeneratedAccountDto] =
if(self.generatedAccounts.len == 0):
error "There was some issue initiating account service"
return
result = self.generatedAccounts
proc openedAccounts*(self: Service): seq[AccountDto] =
try:
let response = status_account.openedAccounts(main_constants.STATUSGODIR)
@ -165,106 +140,12 @@ QtObject:
proc openedAccountsContainsKeyUid*(self: Service, keyUid: string): bool =
return (keyUID in self.openedAccounts().mapIt(it.keyUid))
proc saveKeycardAccountAndLogin(self: Service, chatKey, password: string, account, subaccounts, settings,
config: JsonNode): AccountDto =
try:
let response = status_account.saveAccountAndLoginWithKeycard(chatKey, password, account, subaccounts, settings, config)
var error = "response doesn't contain \"error\""
if(response.result.contains("error")):
error = response.result["error"].getStr
if error == "":
debug "Account saved succesfully"
result = toAccountDto(account)
return
let err = "Error saving account and logging in via keycard : " & error
error "error: ", procName="saveKeycardAccountAndLogin", errDesription = err
except Exception as e:
error "error: ", procName="saveKeycardAccountAndLogin", errName = e.name, errDesription = e.msg
proc prepareSubaccountJsonObject(self: Service, account: GeneratedAccountDto, displayName: string):
JsonNode =
result = %* [
{
"public-key": account.derivedAccounts.defaultWallet.publicKey,
"address": account.derivedAccounts.defaultWallet.address,
"colorId": DEFAULT_CUSTOMIZATION_COLOR,
"wallet": true,
"path": PATH_DEFAULT_WALLET,
"name": DEFAULT_WALLET_ACCOUNT_NAME,
"derived-from": account.address,
"emoji": self.defaultWalletEmoji
},
{
"public-key": account.derivedAccounts.whisper.publicKey,
"address": account.derivedAccounts.whisper.address,
"name": if displayName == "": account.alias else: displayName,
"path": PATH_WHISPER,
"chat": true,
"derived-from": ""
}
]
proc getSubaccountDataForAccountId(self: Service, accountId: string, displayName: string): JsonNode =
for acc in self.generatedAccounts:
if(acc.id == accountId):
return self.prepareSubaccountJsonObject(acc, displayName)
if(self.importedAccount.isValid()):
if(self.importedAccount.id == accountId):
return self.prepareSubaccountJsonObject(self.importedAccount, displayName)
proc toStatusGoSupportedLogLevel*(logLevel: string): string =
if logLevel == "TRACE":
return "DEBUG"
return logLevel
proc prepareAccountSettingsJsonObject(self: Service, account: GeneratedAccountDto,
installationId: string, displayName: string, withoutMnemonic: bool): JsonNode =
result = %* {
"key-uid": account.keyUid,
"mnemonic": if withoutMnemonic: "" else: account.mnemonic,
"public-key": account.derivedAccounts.whisper.publicKey,
"name": account.alias,
"display-name": displayName,
"address": account.address,
"eip1581-address": account.derivedAccounts.eip1581.address,
"dapps-address": account.derivedAccounts.defaultWallet.address,
"wallet-root-address": account.derivedAccounts.walletRoot.address,
"preview-privacy?": true,
"signing-phrase": generateSigningPhrase(3),
"log-level": main_constants.LOG_LEVEL,
"latest-derived-path": 0,
"currency": "usd",
"networks/networks": @[],
"networks/current-network": "",
"wallet/visible-tokens": {},
"waku-enabled": true,
"appearance": 0,
"installation-id": installationId,
"current-user-status": %* {
"publicKey": account.derivedAccounts.whisper.publicKey,
"statusType": 1,
"clock": 0,
"text": ""
},
"profile-pictures-show-to": settings.PROFILE_PICTURES_SHOW_TO_EVERYONE,
"profile-pictures-visibility": settings.PROFILE_PICTURES_VISIBILITY_EVERYONE,
"url-unfurling-mode": int(settings.UrlUnfurlingMode.AlwaysAsk),
}
proc getAccountSettings(self: Service, accountId: string, installationId: string, displayName: string, withoutMnemonic: bool): JsonNode =
for acc in self.generatedAccounts:
if(acc.id == accountId):
return self.prepareAccountSettingsJsonObject(acc, installationId, displayName, withoutMnemonic)
if(self.importedAccount.isValid()):
if(self.importedAccount.id == accountId):
return self.prepareAccountSettingsJsonObject(self.importedAccount, installationId, displayName, withoutMnemonic)
# TODO: Remove after https://github.com/status-im/status-go/issues/4977
# TODO: Remove after https://github.com/status-im/status-desktop/issues/11435
proc getDefaultNodeConfig*(self: Service, installationId: string, recoverAccount: bool): JsonNode =
let fleet = Fleet.ShardsTest
let dnsDiscoveryURL = "enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.test.shards.nodes.status.im"
@ -305,49 +186,8 @@ QtObject:
result["KeycardPairingDataFile"] = newJString(main_constants.KEYCARDPAIRINGDATAFILE)
result["ProcessBackedupMessages"] = newJBool(recoverAccount)
# TODO: Remove after https://github.com/status-im/status-go/issues/4977
proc getLoginNodeConfig(self: Service): JsonNode =
# To create appropriate NodeConfig for Login we set only params that maybe be set via env variables or cli flags
result = %*{}
# mandatory params
result["NetworkId"] = NETWORKS[0]{"chainId"}
result["DataDir"] = %* "./ethereum/mainnet"
result["KeyStoreDir"] = %* self.keyStoreDir.replace(main_constants.STATUSGODIR, "")
result["KeycardPairingDataFile"] = %* main_constants.KEYCARDPAIRINGDATAFILE
# other params
result["Networks"] = NETWORKS
result["UpstreamConfig"] = %* {
"URL": NETWORKS[0]{"rpcUrl"},
"Enabled": true,
}
result["ShhextConfig"] = %* {
"VerifyENSURL": NETWORKS[0]{"fallbackUrl"},
"VerifyTransactionURL": NETWORKS[0]{"fallbackUrl"}
}
result["WakuV2Config"] = %* {
"Port": WAKU_V2_PORT,
"UDPPort": WAKU_V2_PORT
}
result["WalletConfig"] = NODE_CONFIG["WalletConfig"]
result["TorrentConfig"] = %* {
"Port": TORRENT_CONFIG_PORT,
"DataDir": DEFAULT_TORRENT_CONFIG_DATADIR,
"TorrentDir": DEFAULT_TORRENT_CONFIG_TORRENTDIR
}
if main_constants.runtimeLogLevelSet():
result["RuntimeLogLevel"] = newJString(toStatusGoSupportedLogLevel(main_constants.LOG_LEVEL))
if STATUS_PORT != 0:
result["ListenAddr"] = newJString("0.0.0.0:" & $main_constants.STATUS_PORT)
# FIXME: remove this method, settings should be processed in status-go
# https://github.com/status-im/status-go/issues/5359
proc addKeycardDetails(self: Service, kcInstance: string, settingsJson: var JsonNode, accountData: var JsonNode) =
let keycardPairingJsonString = readFile(main_constants.KEYCARDPAIRINGDATAFILE)
let keycardPairingJsonObj = keycardPairingJsonString.parseJSON
@ -383,7 +223,7 @@ QtObject:
proc buildCreateAccountRequest(self: Service, password: string, displayName: string, imagePath: string, imageCropRectangle: ImageCropRectangle): CreateAccountRequest =
return CreateAccountRequest(
backupDisabledDataDir: main_constants.STATUSGODIR,
rootDataDir: main_constants.STATUSGODIR,
kdfIterations: KDF_ITERATIONS,
password: hashPassword(password),
displayName: displayName,
@ -396,6 +236,7 @@ QtObject:
previewPrivacy: true,
torrentConfigEnabled: some(false),
torrentConfigPort: some(TORRENT_CONFIG_PORT),
keycardPairingDataFile: main_constants.KEYCARDPAIRINGDATAFILE,
walletSecretsConfig: self.buildWalletSecrets(),
)
@ -420,13 +261,57 @@ QtObject:
error "failed to create account or login", procName="createAccountAndLogin", errName = e.name, errDesription = e.msg
return e.msg
proc importAccountAndLogin*(self: Service, mnemonic: string, password: string, recoverAccount: bool, displayName: string, imagePath: string, imageCropRectangle: ImageCropRectangle): string =
try:
let request = RestoreAccountRequest(
proc importAccountAndLogin*(self: Service,
mnemonic: string,
password: string,
recoverAccount: bool,
displayName: string,
imagePath: string,
imageCropRectangle: ImageCropRectangle,
keycardInstanceUID: string = "",
): string =
var request = RestoreAccountRequest(
mnemonic: mnemonic,
fetchBackup: recoverAccount,
createAccountRequest: self.buildCreateAccountRequest(password, displayName, imagePath, imageCropRectangle),
)
request.createAccountRequest.keycardInstanceUID = keycardInstanceUID
self.restoreAccountAndLogin(request)
proc restoreKeycardAccountAndLogin*(self: Service,
keycardData: KeycardEvent,
recoverAccount: bool,
displayName: string,
imagePath: string,
imageCropRectangle: ImageCropRectangle,
): string =
let keycard = KeycardData(
keyUid: keycardData.keyUid,
address: keycardData.masterKey.address,
whisperPrivateKey: keycardData.whisperKey.privateKey,
whisperPublicKey: keycardData.whisperKey.publicKey,
whisperAddress: keycardData.whisperKey.address,
walletPublicKey: keycardData.walletKey.publicKey,
walletAddress: keycardData.walletKey.address,
walletRootAddress: keycardData.walletRootKey.address,
eip1581Address: keycardData.eip1581Key.address,
encryptionPublicKey: keycardData.encryptionKey.publicKey,
)
var request = RestoreAccountRequest(
keycard: keycard,
fetchBackup: recoverAccount,
createAccountRequest: self.buildCreateAccountRequest("", displayName, imagePath, imageCropRectangle),
)
request.createAccountRequest.keycardInstanceUID = keycardData.instanceUid
return self.restoreAccountAndLogin(request)
proc restoreAccountAndLogin(self: Service, request: RestoreAccountRequest): string =
try:
let response = status_account.restoreAccountAndLogin(request)
if not response.result.contains("error"):
@ -438,120 +323,11 @@ QtObject:
debug "Account saved succesfully"
return ""
error "importAccountAndLogin status-go error: ", error
return "importAccountAndLogin failed: " & error
error "restoreAccountAndLogin status-go error: ", error
return "restoreAccountAndLogin failed: " & error
except Exception as e:
error "failed to import account or login", procName="importAccountAndLogin", errName = e.name, errDesription = e.msg
return e.msg
proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent, displayName: string, useImportedAcc: bool,
recoverAccount: bool = false) =
try:
var keyUid = keycardData.keyUid
var address = keycardData.masterKey.address
var whisperPrivateKey = keycardData.whisperKey.privateKey
var whisperPublicKey = keycardData.whisperKey.publicKey
var whisperAddress = keycardData.whisperKey.address
var walletPublicKey = keycardData.walletKey.publicKey
var walletAddress = keycardData.walletKey.address
var walletRootAddress = keycardData.walletRootKey.address
var eip1581Address = keycardData.eip1581Key.address
var encryptionPublicKey = keycardData.encryptionKey.publicKey
if useImportedAcc:
keyUid = self.importedAccount.keyUid
address = self.importedAccount.address
whisperPublicKey = self.importedAccount.derivedAccounts.whisper.publicKey
whisperAddress = self.importedAccount.derivedAccounts.whisper.address
walletPublicKey = self.importedAccount.derivedAccounts.defaultWallet.publicKey
walletAddress = self.importedAccount.derivedAccounts.defaultWallet.address
walletRootAddress = self.importedAccount.derivedAccounts.walletRoot.address
eip1581Address = self.importedAccount.derivedAccounts.eip1581.address
encryptionPublicKey = self.importedAccount.derivedAccounts.encryption.publicKey
whisperPrivateKey = self.importedAccount.derivedAccounts.whisper.privateKey
if whisperPrivateKey.startsWith("0x"):
whisperPrivateKey = whisperPrivateKey[2 .. ^1]
let installationId = $genUUID()
let alias = generateAliasFromPk(whisperPublicKey)
var accountDataJson = %* {
"name": if displayName == "": alias else: displayName,
"display-name": displayName,
"address": address,
"key-uid": keyUid,
"kdfIterations": KDF_ITERATIONS,
}
self.setKeyStoreDir(keyUid)
let nodeConfigJson = self.getDefaultNodeConfig(installationId, recoverAccount)
let subaccountDataJson = %* [
{
"public-key": walletPublicKey,
"address": walletAddress,
"colorId": DEFAULT_CUSTOMIZATION_COLOR,
"wallet": true,
"path": PATH_DEFAULT_WALLET,
"name": DEFAULT_WALLET_ACCOUNT_NAME,
"derived-from": address,
"emoji": self.defaultWalletEmoji,
},
{
"public-key": whisperPublicKey,
"address": whisperAddress,
"name": if displayName == "": alias else: displayName,
"path": PATH_WHISPER,
"chat": true,
"derived-from": ""
}
]
var settingsJson = %* {
"key-uid": keyUid,
"public-key": whisperPublicKey,
"name": alias,
"display-name": displayName,
"address": address,
"eip1581-address": eip1581Address,
"dapps-address": walletAddress,
"wallet-root-address": walletRootAddress,
"preview-privacy?": true,
"signing-phrase": generateSigningPhrase(3),
"log-level": main_constants.LOG_LEVEL,
"latest-derived-path": 0,
"currency": "usd",
"networks/networks": @[],
"networks/current-network": "",
"wallet/visible-tokens": {},
"waku-enabled": true,
"appearance": 0,
"installation-id": installationId,
"current-user-status": {
"publicKey": whisperPublicKey,
"statusType": 1,
"clock": 0,
"text": ""
}
}
self.addKeycardDetails(keycardData.instanceUID, settingsJson, accountDataJson)
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
nodeConfigJson.isNil):
let description = "at least one json object is not prepared well"
error "error: ", procName="setupAccountKeycard", errDesription = description
return
self.loggedInAccount = self.saveKeycardAccountAndLogin(chatKey = whisperPrivateKey,
password = encryptionPublicKey,
accountDataJson,
subaccountDataJson,
settingsJson,
nodeConfigJson)
self.setLocalAccountSettingsFile()
except Exception as e:
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg
error "restore account failed", procName="restoreAccountAndLogin", errName = e.name, errDesription = e.msg
proc createAccountFromPrivateKey*(self: Service, privateKey: string): GeneratedAccountDto =
if privateKey.len == 0:
@ -610,27 +386,6 @@ QtObject:
data.error = e.msg
self.events.emit(SIGNAL_DERIVED_ADDRESSES_FROM_NOT_IMPORTED_MNEMONIC_FETCHED, data)
proc importMnemonic*(self: Service, mnemonic: string): string =
if mnemonic.len == 0:
return "empty mnemonic"
try:
let response = status_account.multiAccountImportMnemonic(mnemonic)
self.importedAccount = toGeneratedAccountDto(response.result)
if (self.accounts.contains(self.importedAccount.keyUid)):
return ACCOUNT_ALREADY_EXISTS_ERROR
let responseDerived = status_account.deriveAccounts(self.importedAccount.id, PATHS)
self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result)
self.importedAccount.alias= generateAliasFromPk(self.importedAccount.derivedAccounts.whisper.publicKey)
if (not self.importedAccount.isValid()):
return "imported account is not valid"
except Exception as e:
error "error: ", procName="importMnemonic", errName = e.name, errDesription = e.msg
return e.msg
proc verifyAccountPassword*(self: Service, account: string, password: string): bool =
try:
let response = status_account.verifyAccountPassword(account, utils.hashPassword(password), self.keyStoreDir)
@ -657,11 +412,13 @@ QtObject:
except Exception as e:
error "error: ", procName="verifyDatabasePassword", errName = e.name, errDesription = e.msg
proc doLogin(self: Service, account: AccountDto, passwordHash: string) =
proc doLogin(self: Service, account: AccountDto, passwordHash: string, chatPrivateKey: string = "", mnemonic: string = "") =
var request = LoginAccountRequest(
keyUid: account.keyUid,
kdfIterations: account.kdfIterations,
passwordHash: passwordHash,
keycardWhisperPrivateKey: chatPrivateKey,
mnemonic: mnemonic,
walletSecretsConfig: self.buildWalletSecrets(),
bandwidthStatsEnabled: true,
)
@ -678,8 +435,9 @@ QtObject:
debug "account logged in"
self.setLocalAccountSettingsFile()
proc login*(self: Service, account: AccountDto, hashedPassword: string) =
proc login*(self: Service, account: AccountDto, hashedPassword: string, chatPrivateKey: string = "", mnemonic: string = "") =
try:
# WARNING: Is this keystore migration still needed?
let keyStoreDir = joinPath(main_constants.ROOTKEYSTOREDIR, account.keyUid) & main_constants.sep
if not dirExists(keyStoreDir):
os.createDir(keyStoreDir)
@ -689,29 +447,32 @@ QtObject:
self.setKeyStoreDir(account.keyUid)
let isOldHashPassword = self.verifyDatabasePassword(account.keyUid, hashedPasswordToUpperCase(hashedPassword))
if isOldHashPassword:
if mnemonic == "":
let oldHashedPassword = hashedPasswordToUpperCase(hashedPassword)
if self.verifyDatabasePassword(account.keyUid, oldHashedPassword):
self.scheduleReencrpytion(account, hashedPassword, timeout = 1000)
return
self.doLogin(account, hashedPassword, chatPrivateKey, mnemonic)
except Exception as e:
error "login failed", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_LOGIN_ERROR, LoginErrorArgs(error: e.msg))
proc scheduleReencrpytion(self: Service, account: AccountDto, hashedPassword: string, timeout: int = 1000) =
debug "database reencryption scheduled"
# Save tmp properties so that we can login after the timer
self.tmpAccount = account
self.tmpHashedPassword = hashedPassword
# Start a 1 second timer for the loading screen to appear
let arg = TimerTaskArg(
tptr: timerTask,
vptr: cast[ByteAddress](self.vptr),
slot: "onWaitForReencryptionTimeout",
timeoutInMilliseconds: 1000
timeoutInMilliseconds: timeout
)
self.threadpool.start(arg)
return
self.doLogin(account, hashedPassword)
except Exception as e:
error "login failed", errName = e.name, errDesription = e.msg
self.events.emit(SIGNAL_LOGIN_ERROR, LoginErrorArgs(error: e.msg))
proc onWaitForReencryptionTimeout(self: Service, response: string) {.slot.} =
debug "starting database reencryption"
@ -727,34 +488,6 @@ QtObject:
self.tmpAccount = AccountDto()
self.tmpHashedPassword = ""
proc loginAccountKeycard*(self: Service, accToBeLoggedIn: AccountDto, keycardData: KeycardEvent): string =
try:
self.setKeyStoreDir(keycardData.keyUid)
var accountDataJson = %* {
"key-uid": accToBeLoggedIn.keyUid,
}
let nodeConfigJson = self.getLoginNodeConfig()
let response = status_account.loginWithKeycard(keycardData.whisperKey.privateKey,
keycardData.encryptionKey.publicKey,
accountDataJson,
nodeConfigJson)
var error = "response doesn't contain \"error\""
if(response.result.contains("error")):
error = response.result["error"].getStr
if error == "":
debug "Account logged in succesfully"
# this should be fetched later from waku
self.loggedInAccount = accToBeLoggedIn
self.setLocalAccountSettingsFile()
return
except Exception as e:
error "keycard login failed", procName="loginAccountKeycard", errName = e.name, errDesription = e.msg
return e.msg
proc convertRegularProfileKeypairToKeycard*(self: Service, keycardUid, currentPassword: string, newPassword: string) =
var accountDataJson = %* {
"key-uid": self.getLoggedInAccount().keyUid,
@ -816,7 +549,7 @@ QtObject:
if(errMsg.len == 0):
result = true
else:
error "error: ", procName="onConvertKeycardProfileKeypairToRegular", errDesription = errMsg
error "failed to convert keycard account", procName="onConvertKeycardProfileKeypairToRegular", errDesription = errMsg
except Exception as e:
error "error handilng migrated keypair response", procName="onConvertKeycardProfileKeypairToRegular", errDesription=e.msg
self.events.emit(SIGNAL_CONVERTING_PROFILE_KEYPAIR, ResultArgs(success: result))

View File

@ -1,4 +1,3 @@
import json
import ../../../backend/accounts as status_account
import ../../common/conversion
@ -42,9 +41,6 @@ proc compressCommunityKey*(publicKey: string): string =
except Exception as e:
echo "error: `compressCommunityKey` " & $e.name & " msg: " & $e.msg
proc generateAliasFromPk*(publicKey: string): string =
return status_account.generateAlias(publicKey).result.getStr
proc isAlias*(value: string): bool =
return status_account.isAlias(value)

View File

@ -13,8 +13,6 @@ export response_type
logScope:
topics = "rpc-accounts"
const NUMBER_OF_ADDRESSES_TO_GENERATE = 1
const MNEMONIC_PHRASE_LENGTH = 12
const PK_LENGTH_0X_INCLUDED = 132
const GENERATED* = "generated"
@ -132,22 +130,6 @@ proc updateAccount*(name, address, path: string, publicKey, keyUid, accountType,
]
return core.callPrivateRPC("accounts_saveAccount", payload)
proc generateAddresses*(paths: seq[string]): RpcResponse[JsonNode] =
let payload = %* {
"n": NUMBER_OF_ADDRESSES_TO_GENERATE,
"mnemonicPhraseLength": MNEMONIC_PHRASE_LENGTH,
"bip39Passphrase": "",
"paths": paths
}
try:
let response = status_go.multiAccountGenerateAndDeriveAddresses($payload)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "generateAddresses", exception=e.msg
raise newException(RpcException, e.msg)
proc decompressPk*(publicKey: string): RpcResponse[string] =
discard
if publicKey.startsWith("0x04") and publicKey.len == PK_LENGTH_0X_INCLUDED:
@ -221,20 +203,6 @@ proc getRandomMnemonic*(): RpcResponse[JsonNode] =
let payload = %* []
return core.callPrivateRPC("accounts_getRandomMnemonic", payload)
proc multiAccountImportMnemonic*(mnemonic: string): RpcResponse[JsonNode] =
let payload = %* {
"mnemonicPhrase": mnemonic,
"Bip39Passphrase": ""
}
try:
let response = status_go.multiAccountImportMnemonic($payload)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "multiAccountImportMnemonic", exception=e.msg
raise newException(RpcException, e.msg)
## Imports a new mnemonic and creates local keystore file.
proc importMnemonic*(mnemonic, password: string):
RpcResponse[JsonNode] =
@ -290,20 +258,6 @@ proc createAccountFromPrivateKey*(privateKey: string): RpcResponse[JsonNode] =
error "error doing rpc request", methodName = "createAccountFromPrivateKey", exception=e.msg
raise newException(RpcException, e.msg)
proc deriveAccounts*(accountId: string, paths: seq[string]): RpcResponse[JsonNode] =
let payload = %* {
"accountID": accountId,
"paths": paths
}
try:
let response = status_go.multiAccountDeriveAddresses($payload)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "deriveAccounts", exception=e.msg
raise newException(RpcException, e.msg)
proc openedAccounts*(path: string): RpcResponse[JsonNode] =
try:
let response = status_go.openAccounts(path)
@ -342,16 +296,6 @@ proc restoreAccountAndLogin*(request: RestoreAccountRequest): RpcResponse[JsonNo
error "error doing rpc request", methodName = "restoreAccountAndLogin", exception=e.msg
raise newException(RpcException, e.msg)
proc saveAccountAndLoginWithKeycard*(chatKey, password: string, account, subaccounts, settings, config: JsonNode):
RpcResponse[JsonNode] =
try:
let response = status_go.saveAccountAndLoginWithKeycard($account, password, $settings, $config, $subaccounts, chatKey)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg
raise newException(RpcException, e.msg)
proc convertRegularProfileKeypairToKeycard*(account: JsonNode, settings: JsonNode, keycardUid: string, password: string, newPassword: string):
RpcResponse[JsonNode] =
try:
@ -380,14 +324,6 @@ proc loginAccount*(request: LoginAccountRequest): RpcResponse[JsonNode] =
error "loginAccount failed", exception=e.msg
raise newException(RpcException, e.msg)
proc loginWithKeycard*(chatKey, password: string, account, confNode: JsonNode): RpcResponse[JsonNode] =
try:
let response = status_go.loginWithKeycard($account, password, chatKey, $confNode)
result.result = Json.decode(response, JsonNode)
except RpcException as e:
error "error doing rpc request", methodName = "loginWithKeycard", exception=e.msg
raise newException(RpcException, e.msg)
proc verifyAccountPassword*(address: string, hashedPassword: string, keystoreDir: string):
RpcResponse[JsonNode] =
try:

@ -1 +1 @@
Subproject commit d38b1147f8b4d0c4abdb1dbf353aff36454e02ed
Subproject commit 0fdd7d8def22e64018fe2a47b9c0d98da387c6bc

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit ee2330fe5d4233a7d8b105420c2b93284ad161f3
Subproject commit 49eaabaca5100368c5b39fb8c107aad2535371e5