fix(@desktop/chat): sign and send appears for both recipient and sender when sharing the address

The reason for this issue is a message where recipient accepted to share his address with sender.
In that message recipient's public key is set as a "from" property of a "Message" object and we
cannot determine which of two users has initiated transaction actually.

This is fixed checking if the "from" address from the "commandParameters" object of the "Message"
is contained as an address in the wallet of logged in user. If yes, means that currently logged in
user has initiated a transaction (he is a sender), otherwise currently logged in user is a
recipient.

We were just sending a transaction, without notifying message about that. Now we call
callPrivateRPC("acceptRequestTransaction".prefix, %* [transactionHash, messageId, signature])
and that notifies message about the change, but only on the sender side. Appropriate message
on the recipient side was not notified about the change. That need to be checked.
This commit is contained in:
Sale Djenic 2021-07-12 21:30:17 +02:00 committed by Iuri Matias
parent 88b990a21a
commit ecb2bac6e5
13 changed files with 115 additions and 73 deletions

View File

@ -36,7 +36,13 @@ proc handleChatEvents(self: ChatController) =
for message in evArgs.messages:
if (message.replace != ""):
# Delete the message that this message replaces
self.view.deleteMessage(message.chatId, message.replace)
if (not self.view.deleteMessage(message.chatId, message.replace)):
# In cases where new message need to replace a message which already replaced initial message
# "replace" property contains id of the initial message, but not the id of the message which
# replaced the initial one. That's why we call this proce here in case message with "message.replace"
# was not deleted.
discard self.view.deleteMessageWhichReplacedMessageWithId(message.chatId, message.replace)
self.view.reactions.push(evArgs.emojiReactions)
if (evArgs.communities.len > 0):
for community in evArgs.communities.mitems:
@ -66,7 +72,7 @@ proc handleChatEvents(self: ChatController) =
self.status.events.on("messageDeleted") do(e: Args):
var evArgs = MessageArgs(e)
self.view.deleteMessage(evArgs.channel, evArgs.id)
discard self.view.deleteMessage(evArgs.channel, evArgs.id)
self.status.events.on("chatHistoryCleared") do(e: Args):
var args = ChannelArgs(e)

View File

@ -430,8 +430,11 @@ QtObject:
proc hideLoadingIndicator*(self: ChatsView) {.slot.} =
self.messageView.hideLoadingIndicator()
proc deleteMessage*(self: ChatsView, channelId: string, messageId: string) =
self.messageView.deleteMessage(channelId, messageId)
proc deleteMessage*(self: ChatsView, channelId: string, messageId: string): bool =
result = self.messageView.deleteMessage(channelId, messageId)
proc deleteMessageWhichReplacedMessageWithId*(self: ChatsView, channelId: string, messageId: string): bool =
result = self.messageView.deleteMessageWhichReplacedMessageWithId(channelId, messageId)
proc addPinnedMessages*(self: ChatsView, pinnedMessages: seq[Message]) =
self.messageView.addPinnedMessages(pinnedMessages)

View File

@ -103,8 +103,10 @@ QtObject:
proc getMessage*(self: ChatMessageList, messageId: string): Message =
return self.messages[self.messageIndex[messageId]]
proc deleteMessage*(self: ChatMessageList, messageId: string) =
if not self.messageIndex.hasKey(messageId): return
proc deleteMessage*(self: ChatMessageList, messageId: string): bool =
if not self.messageIndex.hasKey(messageId):
return false
let messageIndex = self.messageIndex[messageId]
self.beginRemoveRows(newQModelIndex(), messageIndex, messageIndex)
self.messages.delete(messageIndex)
@ -115,10 +117,12 @@ QtObject:
self.messageIndex[self.messages[i].id] = i
self.endRemoveRows()
return true
proc deleteMessagesByChatId*(self: ChatMessageList, chatId: string) =
let messages = self.messages.filter(m => m.chatId == chatId)
for message in messages:
self.deleteMessage(message.id)
discard self.deleteMessage(message.id)
proc replaceMessage*(self: ChatMessageList, message: Message) =
let msgIdx = self.messageIndex[message.id]
@ -382,7 +386,7 @@ QtObject:
if m.fromAuthor == publicKey: # Can't delete on a loop
msgIdxToDelete.add(m.id)
for m in msgIdxToDelete:
self.deleteMessage(m)
discard self.deleteMessage(m)
proc getID*(self: ChatMessageList):string {.slot.} =
self.id

View File

@ -415,9 +415,24 @@ QtObject:
self.unreadDirectMessagesAndMentionsCount = currentUnreadDirectMessagesAndMentionsCount
self.unreadDirectMessagesAndMentionsCountChanged()
proc deleteMessage*(self: MessageView, channelId: string, messageId: string) =
self.messageList[channelId].deleteMessage(messageId)
self.hideMessage(messageId)
proc deleteMessage*(self: MessageView, channelId: string, messageId: string): bool =
result = self.messageList[channelId].deleteMessage(messageId)
if (result):
self.hideMessage(messageId)
proc deleteMessageWhichReplacedMessageWithId*(self: MessageView, channelId: string, messageId: string): bool =
var msgIdToBeDeleted: string
for message in self.messageList[channelId].messages:
if (message.replace == messageId):
msgIdToBeDeleted = message.id
break
if (msgIdToBeDeleted.len == 0):
return false
result = self.messageList[channelId].deleteMessage(msgIdToBeDeleted)
if (result):
self.hideMessage(msgIdToBeDeleted)
proc removeMessagesByUserId(self: MessageView, publicKey: string) {.slot.} =
for k in self.messageList.keys:

View File

@ -34,3 +34,6 @@ QtObject:
proc declineRequest*(self: TransactionsView, messageId: string) {.slot.} =
self.status.chat.declineRequestTransaction(messageId)
proc acceptRequestTransaction*(self: TransactionsView, transactionHash: string, messageId: string, signature: string) {.slot.} =
self.status.chat.acceptRequestTransaction(transactionHash, messageId, signature)

View File

@ -416,6 +416,10 @@ proc requestAddressForTransaction*(self: ChatModel, chatId: string, fromAddress:
let response = status_chat_commands.requestAddressForTransaction(chatId, fromAddress, amount, address)
discard self.processMessageUpdateAfterSend(response)
proc acceptRequestTransaction*(self: ChatModel, transactionHash: string, messageId: string, signature: string) =
let response = status_chat_commands.acceptRequestTransaction(transactionHash, messageId, signature)
discard self.processMessageUpdateAfterSend(response)
proc requestTransaction*(self: ChatModel, chatId: string, fromAddress: string, amount: string, tokenAddress: string) =
let address = if (tokenAddress == ZERO_ADDRESS): "" else: tokenAddress
let response = status_chat_commands.requestTransaction(chatId, fromAddress, amount, address)

View File

@ -32,6 +32,9 @@ type CommandParameters* = object
commandState*: int
signature*: string
proc `$`*(self: CommandParameters): string =
result = fmt"CommandParameters(id:{self.id}, fromAddr:{self.fromAddress}, addr:{self.address}, contract:{self.contract}, value:{self.value}, transactionHash:{self.transactionHash}, commandState:{self.commandState}, signature:{self.signature})"
type Message* = object
alias*: string
userName*: string

View File

@ -1,24 +1,20 @@
import ../libstatus/settings as status_settings
proc formatChatUpdate(response: JsonNode): (seq[Chat], seq[Message]) =
var chats: seq[Chat] = @[]
var messages: seq[Message] = @[]
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
if response["result"]{"messages"} != nil:
for jsonMsg in response["result"]["messages"]:
messages.add(jsonMsg.toMessage(pk))
messages.add(jsonMsg.toMessage())
if response["result"]{"chats"} != nil:
for jsonChat in response["result"]["chats"]:
chats.add(jsonChat.toChat)
result = (chats, messages)
proc processChatUpdate(self: ChatModel, response: JsonNode): (seq[Chat], seq[Message]) =
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
var chats: seq[Chat] = @[]
var messages: seq[Message] = @[]
if response{"result"}{"messages"} != nil:
for jsonMsg in response["result"]["messages"]:
messages.add(jsonMsg.toMessage(pk))
messages.add(jsonMsg.toMessage())
if response{"result"}{"chats"} != nil:
for jsonChat in response["result"]["chats"]:
let chat = jsonChat.toChat

View File

@ -77,18 +77,16 @@ proc parseChatMessagesResponse*(rpcResult: JsonNode): (string, seq[Message]) =
var messages: seq[Message] = @[]
let messagesObj = rpcResult{"messages"}
if(messagesObj != nil and messagesObj.kind != JNull):
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
for jsonMsg in messagesObj:
messages.add(jsonMsg.toMessage(pk))
messages.add(jsonMsg.toMessage())
return (rpcResult{"cursor"}.getStr, messages)
proc parseActivityCenterNotifications*(rpcResult: JsonNode): (string, seq[ActivityCenterNotification]) =
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
var notifs: seq[ActivityCenterNotification] = @[]
var msg: Message
if rpcResult{"notifications"}.kind != JNull:
for jsonMsg in rpcResult["notifications"]:
notifs.add(jsonMsg.toActivityCenterNotification(pk))
notifs.add(jsonMsg.toActivityCenterNotification())
return (rpcResult{"cursor"}.getStr, notifs)
proc rpcChatMessages*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string =
@ -542,10 +540,9 @@ proc parseChatPinnedMessagesResponse*(rpcResult: JsonNode): (string, seq[Message
var messages: seq[Message] = @[]
let messagesObj = rpcResult{"pinnedMessages"}
if(messagesObj != nil and messagesObj.kind != JNull):
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
var msg: Message
for jsonMsg in messagesObj:
msg = jsonMsg["message"].toMessage(pk)
msg = jsonMsg["message"].toMessage()
msg.pinnedBy = $jsonMsg{"pinnedBy"}.getStr
messages.add(msg)
return (rpcResult{"cursor"}.getStr, messages)

View File

@ -15,3 +15,6 @@ proc requestAddressForTransaction*(chatId: string, fromAddress: string, amount:
proc requestTransaction*(chatId: string, fromAddress: string, amount: string, tokenAddress: string): string =
result = callPrivateRPC("requestTransaction".prefix, %* [chatId, amount, tokenAddress, fromAddress])
proc acceptRequestTransaction*(transactionHash: string, messageId: string, signature: string): string =
result = callPrivateRPC("acceptRequestTransaction".prefix, %* [transactionHash, messageId, signature])

View File

@ -1,6 +1,8 @@
import json, random, strutils, sequtils, sugar, chronicles, tables
import json_serialization
import ../utils
import ../wallet/account
import ../libstatus/wallet as status_wallet
import ../libstatus/accounts as status_accounts
import ../libstatus/accounts/constants as constants
import ../libstatus/settings as status_settings
@ -13,7 +15,7 @@ import types
import web3/conversions
from ../utils import parseAddress, wei2Eth
proc toMessage*(jsonMsg: JsonNode, pk: string): Message
proc toMessage*(jsonMsg: JsonNode): Message
proc toChat*(jsonChat: JsonNode): Chat
@ -23,15 +25,13 @@ proc toCommunity*(jsonCommunity: JsonNode): Community
proc toCommunityMembershipRequest*(jsonCommunityMembershipRequest: JsonNode): CommunityMembershipRequest
proc toActivityCenterNotification*(jsonNotification: JsonNode, pk: string): ActivityCenterNotification
proc toActivityCenterNotification*(jsonNotification: JsonNode): ActivityCenterNotification
proc fromEvent*(event: JsonNode): Signal =
var signal:MessageSignal = MessageSignal()
signal.messages = @[]
signal.contacts = @[]
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
if event["event"]{"contacts"} != nil:
for jsonContact in event["event"]["contacts"]:
signal.contacts.add(jsonContact.toProfileModel())
@ -40,7 +40,7 @@ proc fromEvent*(event: JsonNode): Signal =
if event["event"]{"messages"} != nil:
for jsonMsg in event["event"]["messages"]:
var message = jsonMsg.toMessage(pk)
var message = jsonMsg.toMessage()
if message.hasMention:
chatsWithMentions.add(message.chatId)
signal.messages.add(message)
@ -70,7 +70,7 @@ proc fromEvent*(event: JsonNode): Signal =
if event["event"]{"activityCenterNotifications"} != nil:
for jsonNotification in event["event"]["activityCenterNotifications"]:
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification(pk))
signal.activityCenterNotification.add(jsonNotification.toActivityCenterNotification())
if event["event"]{"pinMessages"} != nil:
for jsonPinnedMessage in event["event"]["pinMessages"]:
@ -150,8 +150,6 @@ proc toChat*(jsonChat: JsonNode): Chat =
let chatTypeInt = jsonChat{"chatType"}.getInt
let chatType: ChatType = if chatTypeInt >= ord(low(ChatType)) or chatTypeInt <= ord(high(ChatType)): ChatType(chatTypeInt) else: ChatType.Unknown
let pk = status_settings.getSetting[string](Setting.PublicKey, "0x0")
result = Chat(
id: jsonChat{"id"}.getStr,
communityId: jsonChat{"communityId"}.getStr,
@ -176,7 +174,7 @@ proc toChat*(jsonChat: JsonNode): Chat =
result.muted = jsonChat["muted"].getBool
if jsonChat["lastMessage"].kind != JNull:
result.lastMessage = jsonChat{"lastMessage"}.toMessage(pk)
result.lastMessage = jsonChat{"lastMessage"}.toMessage()
if jsonChat.hasKey("joined") and jsonChat["joined"].kind != JNull:
result.joined = jsonChat{"joined"}.getInt
@ -279,8 +277,20 @@ proc toTextItem*(jsonText: JsonNode): TextItem =
for child in jsonText["children"]:
result.children.add(child.toTextItem)
proc currentUserWalletContainsAddress(address: string): bool =
if (address.len == 0):
return false
let accounts = status_wallet.getWalletAccounts()
for acc in accounts:
if (acc.address == address):
return true
return false
proc toMessage*(jsonMsg: JsonNode): Message =
let publicChatKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
proc toMessage*(jsonMsg: JsonNode, pk: string): Message =
var contentType: ContentType
try:
contentType = ContentType(jsonMsg{"contentType"}.getInt)
@ -311,7 +321,7 @@ proc toMessage*(jsonMsg: JsonNode, pk: string): Message =
timestamp: $jsonMsg{"timestamp"}.getInt,
whisperTimestamp: $jsonMsg{"whisperTimestamp"}.getInt,
outgoingStatus: $jsonMsg{"outgoingStatus"}.getStr,
isCurrentUser: pk == jsonMsg{"from"}.getStr,
isCurrentUser: publicChatKey == jsonMsg{"from"}.getStr,
stickerHash: "",
stickerPackId: -1,
parsedText: @[],
@ -362,7 +372,20 @@ proc toMessage*(jsonMsg: JsonNode, pk: string): Message =
signature: jsonMsg["commandParameters"]["signature"].getStr
)
message.hasMention = concat(message.parsedText.map(t => t.children.filter(c => c.textType == "mention" and c.literal == pk))).len > 0
# This is kind of a workaround in case we're processing a transaction message. The reason for
# that is a message where a recipient accepted to share his address with sender. In that message
# a recipient's public key is set as a "from" property of a "Message" object and we cannot
# determine which of two users has initiated transaction actually.
#
# To overcome this we're checking if the "from" address from the "commandParameters" object of
# the "Message" is contained as an address in the wallet of logged in user. If yes, means that
# currently logged in user has initiated a transaction (he is a sender), otherwise currently
# logged in user is a recipient.
message.isCurrentUser = currentUserWalletContainsAddress(message.commandParameters.fromAddress)
message.hasMention = concat(message.parsedText.map(
t => t.children.filter(
c => c.textType == "mention" and c.literal == publicChatKey))).len > 0
result = message
@ -376,7 +399,7 @@ proc toReaction*(jsonReaction: JsonNode): Reaction =
retracted: jsonReaction{"retracted"}.getBool
)
proc toActivityCenterNotification*(jsonNotification: JsonNode, pk: string): ActivityCenterNotification =
proc toActivityCenterNotification*(jsonNotification: JsonNode): ActivityCenterNotification =
var activityCenterNotificationType: ActivityCenterNotificationType
try:
activityCenterNotificationType = ActivityCenterNotificationType(jsonNotification{"type"}.getInt)
@ -396,4 +419,4 @@ proc toActivityCenterNotification*(jsonNotification: JsonNode, pk: string): Acti
)
if jsonNotification.contains("message") and jsonNotification{"message"}.kind != JNull:
result.message = jsonNotification{"message"}.toMessage(pk)
result.message = jsonNotification{"message"}.toMessage()

View File

@ -45,9 +45,6 @@ ModalPopup {
sendingError.text = qsTr("Invalid transaction parameters")
sendingError.open()
}
root.close()
}
id: root
@ -62,6 +59,7 @@ ModalPopup {
title: qsTrId("error-sending-the-transaction")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
onAccepted: root.close()
}
onClosed: {
@ -158,7 +156,7 @@ ModalPopup {
if (!gasEstimate.success) {
//% "Error estimating gas: %1"
let message = qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message)
console.warn(message)
//% ". The transaction will probably fail."
gasEstimateErrorPopup.confirmationText = message + qsTrId("--the-transaction-will-probably-fail-")
gasEstimateErrorPopup.open()
@ -296,28 +294,33 @@ ModalPopup {
onTransactionWasSent: {
try {
let response = JSON.parse(txResult)
if (response.uuid !== stack.uuid)
return
if (response.uuid !== stack.uuid) return
stack.currentGroup.isPending = false
let transactionId = response.result
if (!response.success) {
if (Utils.isInvalidPasswordMessage(response.result)){
if (Utils.isInvalidPasswordMessage(transactionId)){
//% "Wrong password"
transactionSigner.validationError = qsTrId("wrong-password")
return
}
sendingError.text = response.result
sendingError.text = transactionId
return sendingError.open()
}
chatsModel.transactions.acceptRequestTransaction(transactionId,
messageId,
profileModel.profile.pubKey + transactionId.substr(2))
//% "Transaction pending..."
toastMessage.title = qsTrId("ens-transaction-pending")
toastMessage.source = "../../../../img/loading.svg"
toastMessage.iconColor = Style.current.primary
toastMessage.iconRotates = true
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${response.result}`
toastMessage.link = `${walletModel.utilsView.etherscanLink}/${transactionId}`
toastMessage.open()
root.close()
} catch (e) {
console.error('Error parsing the response', e)
@ -327,9 +330,3 @@ ModalPopup {
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -24,6 +24,7 @@ Item {
}
}
}
property var token: JSON.parse(commandParametersObject.contract) // TODO: handle {}
property string tokenAmount: commandParametersObject.value
property string tokenSymbol: token.symbol || ""
@ -35,18 +36,10 @@ Item {
return walletModel.balanceView.getFiatValue(tokenAmount, token.symbol, defaultFiatSymbol) + " " + defaultFiatSymbol.toUpperCase()
}
property int state: commandParametersObject.commandState
property bool outgoing: {
switch (root.state) {
case Constants.pending:
case Constants.confirmed:
case Constants.transactionRequested:
case Constants.addressRequested: return isCurrentUser
case Constants.declined:
case Constants.transactionDeclined:
case Constants.addressReceived: return !isCurrentUser
default: return false
}
}
// Any transaction where isCurrentUser is true is actually outgoing transaction.
property bool outgoing: isCurrentUser
property int innerMargin: 12
property bool isError: commandParametersObject.contract === "{}"
onTokenSymbolChanged: {
@ -74,11 +67,12 @@ Item {
color: Style.current.secondaryText
text: {
if (root.state === Constants.transactionRequested) {
let prefix = root.outgoing ? "↓ ": "↑ "
let prefix = outgoing? "↑ " : "↓ "
//% "Transaction request"
return prefix + qsTrId("transaction-request")
}
return root.outgoing ?
return outgoing ?
//% " Outgoing transaction"
qsTrId("--outgoing-transaction") :
//% " Incoming transaction"
@ -208,9 +202,3 @@ Item {
}
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#4c4e50";formeditorZoom:1.25}
}
##^##*/