fix: checksum validation (#15915)

* feat: checksum validation added to adding saved addresses

Fixes: #15772

* feat: checksum validation added to adding watch only address

Fixes: #15779
This commit is contained in:
saledjenic 2024-08-12 19:56:54 +02:00 committed by GitHub
parent 63076e6ecc
commit 7dc08731f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 131 additions and 14 deletions

View File

@ -71,3 +71,6 @@ proc hasPairedDevices*(self: Controller): bool =
proc reloadAccountTokens*(self: Controller) = proc reloadAccountTokens*(self: Controller) =
self.walletAccountService.reloadAccountTokens() self.walletAccountService.reloadAccountTokens()
proc isChecksumValidForAddress*(self: Controller, address: string): bool =
return self.walletAccountService.isChecksumValidForAddress(address)

View File

@ -123,3 +123,6 @@ method canProfileProveOwnershipOfProvidedAddresses*(self: AccessInterface, addre
method reloadAccountTokens*(self: AccessInterface) {.base.} = method reloadAccountTokens*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method isChecksumValidForAddress*(self: AccessInterface, address: string): bool {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -539,3 +539,6 @@ method canProfileProveOwnershipOfProvidedAddresses*(self: Module, addresses: str
method reloadAccountTokens*(self: Module) = method reloadAccountTokens*(self: Module) =
self.view.setIsAccountTokensReloading(true) self.view.setIsAccountTokensReloading(true)
self.controller.reloadAccountTokens() self.controller.reloadAccountTokens()
method isChecksumValidForAddress*(self: Module, address: string): bool =
return self.controller.isChecksumValidForAddress(address)

View File

@ -316,3 +316,5 @@ QtObject:
read = getIsAccountTokensReloading read = getIsAccountTokensReloading
notify = isAccountTokensReloadingChanged notify = isAccountTokensReloadingChanged
proc isChecksumValidForAddress*(self: View, address: string): bool {.slot.} =
return self.delegate.isChecksumValidForAddress(address)

View File

@ -252,6 +252,9 @@ proc getNumOfAddressesToGenerateForKeypair*(self: Controller, keyUid: string): i
proc resolveSuggestedPathForKeypair*(self: Controller, keyUid: string): string = proc resolveSuggestedPathForKeypair*(self: Controller, keyUid: string): string =
return self.walletAccountService.resolveSuggestedPathForKeypair(keyUid) return self.walletAccountService.resolveSuggestedPathForKeypair(keyUid)
proc isChecksumValidForAddress*(self: Controller, address: string): bool =
return self.walletAccountService.isChecksumValidForAddress(address)
proc remainingAccountCapacity*(self: Controller): int = proc remainingAccountCapacity*(self: Controller): int =
return self.walletAccountService.remainingAccountCapacity() return self.walletAccountService.remainingAccountCapacity()

View File

@ -107,6 +107,9 @@ method removingSavedAddressConfirmed*(self: AccessInterface, address: string) {.
method savedAddressDeleted*(self: AccessInterface, address: string, errorMsg: string) {.base.} = method savedAddressDeleted*(self: AccessInterface, address: string, errorMsg: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method isChecksumValidForAddress*(self: AccessInterface, address: string): bool {.base.} =
raise newException(ValueError, "No implementation available")
method remainingAccountCapacity*(self: AccessInterface): int {.base.} = method remainingAccountCapacity*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -736,6 +736,9 @@ method buildNewSeedPhraseKeypairAndAddItToOrigin*[T](self: Module[T]) =
derivedFrom = genAcc.address) derivedFrom = genAcc.address)
self.setItemForSelectedOrigin(item) self.setItemForSelectedOrigin(item)
method isChecksumValidForAddress*[T](self: Module[T], address: string): bool =
return self.controller.isChecksumValidForAddress(address)
method remainingAccountCapacity*[T](self: Module[T]): int = method remainingAccountCapacity*[T](self: Module[T]): int =
return self.controller.remainingAccountCapacity() return self.controller.remainingAccountCapacity()

View File

@ -364,6 +364,9 @@ QtObject:
proc removingSavedAddressRejected*(self: View) {.slot.} = proc removingSavedAddressRejected*(self: View) {.slot.} =
self.setDisablePopup(false) self.setDisablePopup(false)
proc isChecksumValidForAddress*(self: View, address: string): bool {.slot.} =
return self.delegate.isChecksumValidForAddress(address)
proc remainingAccountCapacity*(self: View): int {.slot.} = proc remainingAccountCapacity*(self: View): int {.slot.} =
return self.delegate.remainingAccountCapacity() return self.delegate.remainingAccountCapacity()

View File

@ -92,6 +92,17 @@ QtObject:
result.networkService = networkService result.networkService = networkService
result.currencyService = currencyService result.currencyService = currencyService
proc isChecksumValidForAddress*(self: Service, address: string): bool =
var updated = false
try:
let response = backend.isChecksumValidForAddress(address)
if not response.error.isNil:
error "status-go error", procName="isChecksumValidForAddress", errCode=response.error.code, errDesription=response.error.message
return response.result.getBool
except Exception as e:
error "error: ", procName="isChecksumValidForAddress", errName=e.name, errDesription=e.msg
include service_account include service_account
include service_token include service_token
include service_keycard include service_keycard

View File

@ -332,3 +332,6 @@ rpc(getBalancesByChain, "wallet"):
rpc(restartWalletReloadTimer, "wallet"): rpc(restartWalletReloadTimer, "wallet"):
discard discard
rpc(isChecksumValidForAddress, "wallet"):
address: string

View File

@ -0,0 +1,22 @@
import QtQuick 2.13
import QtQuick.Controls 2.15
import StatusQ.Controls 0.1
StatusIcon {
id: root
property string tooltipText
MouseArea {
id: tooltipSensor
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
}
StatusToolTip {
visible: tooltipSensor.containsMouse && !!text
text: root.tooltipText
}
}

View File

@ -7,6 +7,7 @@ StatusCenteredFlow 0.1 StatusCenteredFlow.qml
StatusFontSettings 0.1 StatusFontSettings.qml StatusFontSettings 0.1 StatusFontSettings.qml
StatusGridView 0.1 StatusGridView.qml StatusGridView 0.1 StatusGridView.qml
StatusIcon 0.1 StatusIcon.qml StatusIcon 0.1 StatusIcon.qml
StatusIconWithTooltip 0.1 StatusIconWithTooltip.qml
StatusIdenticonRingSettings 0.1 StatusIdenticonRingSettings.qml StatusIdenticonRingSettings 0.1 StatusIdenticonRingSettings.qml
StatusListView 0.1 StatusListView.qml StatusListView 0.1 StatusListView.qml
StatusModalHeaderSettings 0.1 StatusModalHeaderSettings.qml StatusModalHeaderSettings 0.1 StatusModalHeaderSettings.qml

View File

@ -176,6 +176,7 @@
<file>StatusQ/Core/StatusFontSettings.qml</file> <file>StatusQ/Core/StatusFontSettings.qml</file>
<file>StatusQ/Core/StatusGridView.qml</file> <file>StatusQ/Core/StatusGridView.qml</file>
<file>StatusQ/Core/StatusIcon.qml</file> <file>StatusQ/Core/StatusIcon.qml</file>
<file>StatusQ/Core/StatusIconWithTooltip.qml</file>
<file>StatusQ/Core/StatusIdenticonRingSettings.qml</file> <file>StatusQ/Core/StatusIdenticonRingSettings.qml</file>
<file>StatusQ/Core/StatusListView.qml</file> <file>StatusQ/Core/StatusListView.qml</file>
<file>StatusQ/Core/StatusModalHeaderSettings.qml</file> <file>StatusQ/Core/StatusModalHeaderSettings.qml</file>

View File

@ -119,6 +119,8 @@ StatusModal {
|| chainShortNamesDirty && (!d.editMode || d.storedChainShortNames !== d.chainShortNames) || chainShortNamesDirty && (!d.editMode || d.storedChainShortNames !== d.chainShortNames)
|| d.colorId.toUpperCase() !== d.storedColorId.toUpperCase() || d.colorId.toUpperCase() !== d.storedColorId.toUpperCase()
property bool incorrectChecksum: false
readonly property var chainPrefixRegexPattern: /[^:]+\:?|:/g readonly property var chainPrefixRegexPattern: /[^:]+\:?|:/g
readonly property bool addressInputIsENS: !!d.ens && readonly property bool addressInputIsENS: !!d.ens &&
@ -231,6 +233,13 @@ StatusModal {
addressInput.errorMessageCmp.visible = true addressInput.errorMessageCmp.visible = true
} }
function checkIfAddressChecksumIsValid() {
d.incorrectChecksum = false
if (d.addressInputIsAddress) {
d.incorrectChecksum = !root.store.isChecksumValidForAddress(d.address)
}
}
function checkForAddressInputErrorsWarnings() { function checkForAddressInputErrorsWarnings() {
addressInput.errorMessageCmp.visible = false addressInput.errorMessageCmp.visible = false
addressInput.errorMessageCmp.color = Theme.palette.dangerColor1 addressInput.errorMessageCmp.color = Theme.palette.dangerColor1
@ -261,6 +270,7 @@ StatusModal {
networkSelector.state = "" networkSelector.state = ""
if (d.addressInputIsAddress) { if (d.addressInputIsAddress) {
d.checkForAddressInputOwningErrorsWarnings() d.checkForAddressInputOwningErrorsWarnings()
d.checkIfAddressChecksumIsValid()
return return
} }
@ -453,7 +463,7 @@ StatusModal {
enabled: !(d.editMode || d.addAddress) enabled: !(d.editMode || d.addAddress)
input.edit.textFormat: TextEdit.RichText input.edit.textFormat: TextEdit.RichText
input.rightComponent: (d.resolvingEnsNameInProgress || d.checkingContactsAddressInProgress) ? input.rightComponent: (d.resolvingEnsNameInProgress || d.checkingContactsAddressInProgress) ?
loadingIndicator : null loadingIndicator : d.incorrectChecksum? incorrectChecksumComponent : null
input.asset.name: d.addressInputValid && !d.editMode ? "checkbox" : "" input.asset.name: d.addressInputValid && !d.editMode ? "checkbox" : ""
input.asset.color: enabled ? Theme.palette.primaryColor1 : Theme.palette.baseColor1 input.asset.color: enabled ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
input.asset.width: 17 input.asset.width: 17
@ -472,6 +482,18 @@ StatusModal {
StatusLoadingIndicator {} StatusLoadingIndicator {}
} }
Component {
id: incorrectChecksumComponent
StatusIconWithTooltip {
icon: "warning"
width: 20
height: 20
color: Theme.palette.warningColor1
tooltipText: qsTr("Checksum of the entered address is incorrect")
}
}
onTextChanged: { onTextChanged: {
if (skipTextUpdate || !d.initialized) if (skipTextUpdate || !d.initialized)
return return

View File

@ -304,6 +304,10 @@ QtObject {
} }
} }
function isChecksumValidForAddress(address) {
return root.walletSectionInst.isChecksumValidForAddress(address)
}
function getNameForAddress(address) { function getNameForAddress(address) {
var name = getNameForWalletAddress(address) var name = getNameForWalletAddress(address)
if (name.length === 0) { if (name.length === 0) {

View File

@ -20,6 +20,16 @@ Column {
addressInput.reset() addressInput.reset()
} }
QtObject {
id: d
property bool incorrectChecksum: false
function checkIfAddressChecksumIsValid(address) {
d.incorrectChecksum = !root.store.isChecksumValidForAddress(address)
}
}
StatusInput { StatusInput {
id: addressInput id: addressInput
objectName: "AddAccountPopup-WatchOnlyAddress" objectName: "AddAccountPopup-WatchOnlyAddress"
@ -29,14 +39,27 @@ Column {
label: qsTr("Ethereum address or ENS name") label: qsTr("Ethereum address or ENS name")
placeholderText: qsTr("Type or paste ETH address") placeholderText: qsTr("Type or paste ETH address")
input.multiline: true input.multiline: true
input.rightComponent: StatusButton { input.rightComponent: Row {
anchors.verticalCenter: parent.verticalCenter spacing: 8
borderColor: Theme.palette.primaryColor1
size: StatusBaseButton.Size.Tiny StatusIconWithTooltip {
text: qsTr("Paste") visible: d.incorrectChecksum
onClicked: { icon: "warning"
addressInput.text = "" width: 20
addressInput.input.edit.paste() height: 20
color: Theme.palette.warningColor1
tooltipText: qsTr("Checksum of the entered address is incorrect")
}
StatusButton {
anchors.verticalCenter: parent.verticalCenter
borderColor: Theme.palette.primaryColor1
size: StatusBaseButton.Size.Tiny
text: qsTr("Paste")
onClicked: {
addressInput.text = ""
addressInput.input.edit.paste()
}
} }
} }
validators: [ validators: [
@ -46,8 +69,11 @@ Column {
] ]
onTextChanged: { onTextChanged: {
d.incorrectChecksum = false
if (addressInput.valid) { if (addressInput.valid) {
root.store.changeWatchOnlyAccountAddressPostponed(text.trim()) const trimmedText = text.trim()
root.store.changeWatchOnlyAccountAddressPostponed(trimmedText)
d.checkIfAddressChecksumIsValid(trimmedText)
return return
} }
root.store.cleanWatchOnlyAccountAddress() root.store.cleanWatchOnlyAccountAddress()

View File

@ -167,6 +167,10 @@ BasePopupStore {
root.addAccountModule.startScanningForActivity() root.addAccountModule.startScanningForActivity()
} }
function isChecksumValidForAddress(address) {
return root.addAccountModule.isChecksumValidForAddress(address)
}
function remainingAccountCapacity() { function remainingAccountCapacity() {
return root.addAccountModule.remainingAccountCapacity() return root.addAccountModule.remainingAccountCapacity()
} }