feat: check password before saving a new account

Also shows the error if there is one when adding.
Should show a loading state too, but it doesn't work because the Nim function freezes the QML
This commit is contained in:
Jonathan Rainville 2020-06-25 15:31:30 -04:00 committed by Iuri Matias
parent 8e21a1b8b3
commit 8cb8395ceb
9 changed files with 140 additions and 47 deletions

View File

@ -121,17 +121,17 @@ QtObject:
self.setCurrentAccountByIndex(0) self.setCurrentAccountByIndex(0)
self.accountListChanged() self.accountListChanged()
proc generateNewAccount*(self: WalletView, password: string, accountName: string, color: string) {.slot.} = proc generateNewAccount*(self: WalletView, password: string, accountName: string, color: string): string {.slot.} =
self.status.wallet.generateNewAccount(password, accountName, color) result = self.status.wallet.generateNewAccount(password, accountName, color)
proc addAccountsFromSeed*(self: WalletView, seed: string, password: string, accountName: string, color: string) {.slot.} = proc addAccountsFromSeed*(self: WalletView, seed: string, password: string, accountName: string, color: string): string {.slot.} =
self.status.wallet.addAccountsFromSeed(seed, password, accountName, color) result = self.status.wallet.addAccountsFromSeed(seed, password, accountName, color)
proc addAccountsFromPrivateKey*(self: WalletView, privateKey: string, password: string, accountName: string, color: string) {.slot.} = proc addAccountsFromPrivateKey*(self: WalletView, privateKey: string, password: string, accountName: string, color: string): string {.slot.} =
self.status.wallet.addAccountsFromPrivateKey(privateKey, password, accountName, color) result = self.status.wallet.addAccountsFromPrivateKey(privateKey, password, accountName, color)
proc addWatchOnlyAccount*(self: WalletView, address: string, accountName: string, color: string) {.slot.} = proc addWatchOnlyAccount*(self: WalletView, address: string, accountName: string, color: string): string {.slot.} =
self.status.wallet.addWatchOnlyAccount(address, accountName, color) result = self.status.wallet.addWatchOnlyAccount(address, accountName, color)
proc changeAccountSettings*(self: WalletView, address: string, accountName: string, color: string): string {.slot.} = proc changeAccountSettings*(self: WalletView, address: string, accountName: string, color: string): string {.slot.} =
result = self.status.wallet.changeAccountSettings(address, accountName, color) result = self.status.wallet.changeAccountSettings(address, accountName, color)

View File

@ -7,7 +7,10 @@ import accounts/constants
import ../../signals/types as signal_types import ../../signals/types as signal_types
import ../wallet/account import ../wallet/account
proc queryAccounts*(): string = proc hashPassword(password: string): string =
result = "0x" & $keccak_256.digest(password)
proc getDefaultAccount*(): string =
var response = callPrivateRPC("eth_accounts") var response = callPrivateRPC("eth_accounts")
result = parseJson(response)["result"][0].getStr() result = parseJson(response)["result"][0].getStr()
@ -49,7 +52,7 @@ proc saveAccountAndLogin*(
password: string, password: string,
configJSON: string, configJSON: string,
settingsJSON: string): types.Account = settingsJSON: string): types.Account =
let hashedPassword = "0x" & $keccak_256.digest(password) let hashedPassword = hashPassword(password)
let subaccountData = %* [ let subaccountData = %* [
{ {
"public-key": account.derived.defaultWallet.publicKey, "public-key": account.derived.defaultWallet.publicKey,
@ -81,7 +84,7 @@ proc saveAccountAndLogin*(
raise newException(StatusGoException, "Error saving account and logging in: " & error) raise newException(StatusGoException, "Error saving account and logging in: " & error)
proc storeDerivedAccounts*(account: GeneratedAccount, password: string): MultiAccounts = proc storeDerivedAccounts*(account: GeneratedAccount, password: string): MultiAccounts =
let hashedPassword = "0x" & $keccak_256.digest(password) let hashedPassword = hashPassword(password)
let multiAccount = %* { let multiAccount = %* {
"accountID": account.id, "accountID": account.id,
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET], "paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET],
@ -150,7 +153,7 @@ proc setupAccount*(account: GeneratedAccount, password: string): types.Account =
discard libstatus.addPeer(peer) discard libstatus.addPeer(peer)
proc login*(nodeAccount: NodeAccount, password: string): NodeAccount = proc login*(nodeAccount: NodeAccount, password: string): NodeAccount =
let hashedPassword = "0x" & $keccak_256.digest(password) let hashedPassword = hashPassword(password)
let account = nodeAccount.toAccount let account = nodeAccount.toAccount
let loginResult = $libstatus.login($toJson(account), hashedPassword) let loginResult = $libstatus.login($toJson(account), hashedPassword)
let error = parseJson(loginResult)["error"].getStr let error = parseJson(loginResult)["error"].getStr
@ -162,6 +165,16 @@ proc login*(nodeAccount: NodeAccount, password: string): NodeAccount =
raise newException(StatusGoException, "Error logging in: " & error) raise newException(StatusGoException, "Error logging in: " & error)
proc verifyAccountPassword*(address: string, password: string): bool =
let hashedPassword = hashPassword(password)
let verifyResult = $libstatus.verifyAccountPassword(KEYSTOREDIR, address, hashedPassword)
let error = parseJson(verifyResult)["error"].getStr
if error == "":
return true
return false
proc multiAccountImportMnemonic*(mnemonic: string): GeneratedAccount = proc multiAccountImportMnemonic*(mnemonic: string): GeneratedAccount =
let mnemonicJson = %* { let mnemonicJson = %* {
"mnemonicPhrase": mnemonic, "mnemonicPhrase": mnemonic,

View File

@ -36,4 +36,6 @@ proc login*(acctData: cstring, password: cstring): cstring {.importc: "Login".}
proc logout*(): cstring {.importc: "Logout".} proc logout*(): cstring {.importc: "Logout".}
proc verifyAccountPassword*(keyStoreDir: cstring, address: cstring, password: cstring): cstring {.importc: "VerifyAccountPassword".}
proc validateMnemonic*(mnemonic: cstring): cstring {.importc: "ValidateMnemonic".} proc validateMnemonic*(mnemonic: cstring): cstring {.importc: "ValidateMnemonic".}

View File

@ -97,30 +97,44 @@ proc calculateTotalFiatBalance*(self: WalletModel) =
for account in self.accounts: for account in self.accounts:
self.totalBalance += account.realFiatBalance self.totalBalance += account.realFiatBalance
proc addNewGeneratedAccount(self: WalletModel, generatedAccount: GeneratedAccount, password: string, accountName: string, color: string, accountType: string, isADerivedAccount = true) = proc addNewGeneratedAccount(self: WalletModel, generatedAccount: GeneratedAccount, password: string, accountName: string, color: string, accountType: string, isADerivedAccount = true): string =
generatedAccount.name = accountName try:
var derivedAccount: DerivedAccount = status_accounts.saveAccount(generatedAccount, password, color, accountType, isADerivedAccount) generatedAccount.name = accountName
var account = self.newAccount(accountName, derivedAccount.address, color, fmt"0.00 {self.defaultCurrency}", derivedAccount.publicKey) var derivedAccount: DerivedAccount = status_accounts.saveAccount(generatedAccount, password, color, accountType, isADerivedAccount)
self.accounts.add(account) var account = self.newAccount(accountName, derivedAccount.address, color, fmt"0.00 {self.defaultCurrency}", derivedAccount.publicKey)
self.events.emit("newAccountAdded", AccountArgs(account: account)) self.accounts.add(account)
self.events.emit("newAccountAdded", AccountArgs(account: account))
except Exception as e:
return fmt"Error adding new account: {e.msg}"
proc generateNewAccount*(self: WalletModel, password: string, accountName: string, color: string) = return ""
proc addNewGeneratedAccountWithPassword(self: WalletModel, generatedAccount: GeneratedAccount, password: string, accountName: string, color: string, accountType: string, isADerivedAccount = true): string =
let defaultAccount = status_accounts.getDefaultAccount()
let isPasswordOk = status_accounts.verifyAccountPassword(defaultAccount, password)
if (not isPasswordOk):
return "Wrong password"
result = self.addNewGeneratedAccount(generatedAccount, password, accountName, color, accountType, isADerivedAccount)
proc generateNewAccount*(self: WalletModel, password: string, accountName: string, color: string): string =
let accounts = status_accounts.generateAddresses(1) let accounts = status_accounts.generateAddresses(1)
let generatedAccount = accounts[0] let generatedAccount = accounts[0]
self.addNewGeneratedAccount(generatedAccount, password, accountName, color, constants.GENERATED) return self.addNewGeneratedAccountWithPassword(generatedAccount, password, accountName, color, constants.GENERATED)
proc addAccountsFromSeed*(self: WalletModel, seed: string, password: string, accountName: string, color: string) = proc addAccountsFromSeed*(self: WalletModel, seed: string, password: string, accountName: string, color: string): string =
let mnemonic = replace(seed, ',', ' ') let mnemonic = replace(seed, ',', ' ')
let generatedAccount = status_accounts.multiAccountImportMnemonic(mnemonic) let generatedAccount = status_accounts.multiAccountImportMnemonic(mnemonic)
self.addNewGeneratedAccount(generatedAccount, password, accountName, color, constants.SEED) return self.addNewGeneratedAccountWithPassword(generatedAccount, password, accountName, color, constants.SEED)
proc addAccountsFromPrivateKey*(self: WalletModel, privateKey: string, password: string, accountName: string, color: string) = proc addAccountsFromPrivateKey*(self: WalletModel, privateKey: string, password: string, accountName: string, color: string): string =
let generatedAccount = status_accounts.MultiAccountImportPrivateKey(privateKey) let generatedAccount = status_accounts.MultiAccountImportPrivateKey(privateKey)
self.addNewGeneratedAccount(generatedAccount, password, accountName, color, constants.KEY, false) return self.addNewGeneratedAccountWithPassword(generatedAccount, password, accountName, color, constants.KEY, false)
proc addWatchOnlyAccount*(self: WalletModel, address: string, accountName: string, color: string) = proc addWatchOnlyAccount*(self: WalletModel, address: string, accountName: string, color: string): string =
let account = GeneratedAccount(address: address) let account = GeneratedAccount(address: address)
self.addNewGeneratedAccount(account, "", accountName, color, constants.WATCH, false) return self.addNewGeneratedAccount(account, "", accountName, color, constants.WATCH, false)
proc hasAsset*(self: WalletModel, account: string, symbol: string): bool = proc hasAsset*(self: WalletModel, account: string, symbol: string): bool =
self.tokens.anyIt(it["symbol"].getStr == symbol) self.tokens.anyIt(it["symbol"].getStr == symbol)

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -12,6 +13,7 @@ ModalPopup {
property string passwordValidationError: "" property string passwordValidationError: ""
property string privateKeyValidationError: "" property string privateKeyValidationError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
property bool loading: false
function validate() { function validate() {
if (passwordInput.text === "") { if (passwordInput.text === "") {
@ -94,17 +96,31 @@ ModalPopup {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.padding anchors.rightMargin: Theme.padding
label: qsTr("Add account >") label: loading ? qsTr("Loading...") : qsTr("Add account >")
disabled: passwordInput.text === "" || accountNameInput.text === "" || accountPKeyInput.text === "" disabled: loading || passwordInput.text === "" || accountNameInput.text === "" || accountPKeyInput.text === ""
MessageDialog {
id: accountError
title: "Adding the account failed"
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
onClicked : { onClicked : {
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
loading = true
if (!validate()) { if (!validate()) {
return return loading = false
}
const error = walletModel.addAccountsFromPrivateKey(accountPKeyInput.text, passwordInput.text, accountNameInput.text, selectedColor)
loading = false
if (error) {
accountError.text = error
return accountError.open()
} }
walletModel.addAccountsFromPrivateKey(accountPKeyInput.text, passwordInput.text, accountNameInput.text, selectedColor)
// TODO manage errors adding account
popup.close(); popup.close();
} }
} }

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -11,6 +12,7 @@ ModalPopup {
property string passwordValidationError: "" property string passwordValidationError: ""
property string seedValidationError: "" property string seedValidationError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
property bool loading: false
function validate() { function validate() {
if (passwordInput.text === "") { if (passwordInput.text === "") {
@ -95,17 +97,31 @@ ModalPopup {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.padding anchors.rightMargin: Theme.padding
label: "Add account >" label: loading ? qsTr("Loading...") : qsTr("Add account >")
disabled: passwordInput.text === "" || accountNameInput.text === "" || accountSeedInput.text === "" disabled: loading || passwordInput.text === "" || accountNameInput.text === "" || accountSeedInput.text === ""
MessageDialog {
id: accountError
title: "Adding the account failed"
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
onClicked : { onClicked : {
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
loading = true
if (!validate()) { if (!validate()) {
return return loading = false
}
const error = walletModel.addAccountsFromSeed(accountSeedInput.text, passwordInput.text, accountNameInput.text, selectedColor)
loading = false
if (error) {
accountError.text = error
return accountError.open()
} }
walletModel.addAccountsFromSeed(accountSeedInput.text, passwordInput.text, accountNameInput.text, selectedColor)
// TODO manage errors adding account
popup.close(); popup.close();
} }
} }

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -10,6 +11,7 @@ ModalPopup {
property string selectedColor: Constants.accountColors[0] property string selectedColor: Constants.accountColors[0]
property string addressError: "" property string addressError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
property bool loading: false
function validate() { function validate() {
if (addressInput.text === "") { if (addressInput.text === "") {
@ -73,17 +75,31 @@ ModalPopup {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.padding anchors.rightMargin: Theme.padding
label: "Add account >" label: loading ? qsTr("Loading...") : qsTr("Add account >")
disabled: addressInput.text === "" || accountNameInput.text === "" disabled: loading || addressInput.text === "" || accountNameInput.text === ""
MessageDialog {
id: accountError
title: "Adding the account failed"
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
onClicked : { onClicked : {
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
loading = true
if (!validate()) { if (!validate()) {
return return loading = false
}
const error = walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, selectedColor);
loading = false
if (error) {
accountError.text = error
return accountError.open()
} }
walletModel.addWatchOnlyAccount(addressInput.text, accountNameInput.text, selectedColor);
// TODO manage errors adding account
popup.close(); popup.close();
} }
} }

View File

@ -1,4 +1,5 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
@ -10,6 +11,7 @@ ModalPopup {
property string selectedColor: Constants.accountColors[0] property string selectedColor: Constants.accountColors[0]
property string passwordValidationError: "" property string passwordValidationError: ""
property string accountNameValidationError: "" property string accountNameValidationError: ""
property bool loading: false
function validate() { function validate() {
if (passwordInput.text === "") { if (passwordInput.text === "") {
@ -73,17 +75,31 @@ ModalPopup {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.padding anchors.rightMargin: Theme.padding
label: "Add account >" label: loading ? qsTr("Loading...") : qsTr("Add account >")
disabled: passwordInput.text === "" || accountNameInput.text === "" disabled: loading || passwordInput.text === "" || accountNameInput.text === ""
MessageDialog {
id: accountError
title: "Adding the account failed"
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
onClicked : { onClicked : {
// TODO the loaidng doesn't work because the function freezes th eview. Might need to use threads
loading = true
if (!validate()) { if (!validate()) {
return return loading = false
}
const error = walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, selectedColor)
loading = false
if (error) {
accountError.text = error
return accountError.open()
} }
walletModel.generateNewAccount(passwordInput.text, accountNameInput.text, selectedColor);
// TODO manage errors adding account
popup.close(); popup.close();
} }
} }

View File

@ -149,7 +149,6 @@ Item {
SVGImage { SVGImage {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// TODO replace by a real loading image
source: "../app/img/arrowUp.svg" source: "../app/img/arrowUp.svg"
width: 13.5 width: 13.5
height: 17.5 height: 17.5
@ -176,6 +175,7 @@ Item {
anchors.left: txtPassword.right anchors.left: txtPassword.right
anchors.leftMargin: Theme.padding anchors.leftMargin: Theme.padding
anchors.verticalCenter: txtPassword.verticalCenter anchors.verticalCenter: txtPassword.verticalCenter
// TODO replace by a real loading image
source: "../app/img/settings.svg" source: "../app/img/settings.svg"
width: 30 width: 30
height: 30 height: 30