Added ChatEntity interface and made required changes for its use

This commit is contained in:
Samuel Hawksby-Robinson 2020-07-25 15:16:00 +01:00 committed by Andrea Maria Piana
parent ceed618102
commit 6ffe67deec
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
6 changed files with 79 additions and 48 deletions

13
protocol/chat_entity.go Normal file
View File

@ -0,0 +1,13 @@
package protocol
import (
"crypto/ecdsa"
"github.com/status-im/status-go/protocol/protobuf"
)
type ChatEntity interface {
GetChatId() string
GetMessageType() protobuf.MessageType
GetSigPubKey() *ecdsa.PublicKey
}

View File

@ -1,29 +1,31 @@
package protocol
import "github.com/status-im/status-go/protocol/protobuf"
import (
"crypto/ecdsa"
"github.com/status-im/status-go/protocol/protobuf"
)
// EmojiReaction represents an emoji reaction from a user in the application layer, used for persistence, querying and
// signaling
type EmojiReaction struct {
protobuf.EmojiReaction
// ID calculated as keccak256(compressedAuthorPubKey, data) where data is unencrypted payload.
ID string
// Clock Lamport timestamp of the chat message
Clock uint64
// MessageID the ID of the target message that the user wishes to react to
MessageID string
// ChatID the ID of the chat the message belongs to, for query efficiency the ChatID is stored in the db even though the
// target message also stores the ChatID
ChatID string
// EmojiID the ID of the emoji the user wishes to react with
EmojiID protobuf.EmojiReaction_Type
// From is a public key of the author of the emoji reaction.
From string
// Retracted represents whether the user has chosen to remove a previously given reaction
Retracted bool
// SigPubKey is the ecdsa encoded public key of the emoji reaction author
SigPubKey *ecdsa.PublicKey `json:"-"`
}
// GetSigPubKey returns an ecdsa encoded public key
// this function is also required to implement the ChatEntity interface
func (e EmojiReaction) GetSigPubKey() *ecdsa.PublicKey {
return e.SigPubKey
}

View File

@ -332,3 +332,9 @@ func getAudioMessageMIME(i *protobuf.AudioMessage) (string, error) {
return "", errors.New("audio format not supported")
}
// GetSigPubKey returns an ecdsa encoded public key
// this function is also required to implement the ChatEntity interface
func (m Message) GetSigPubKey() *ecdsa.PublicKey {
return m.SigPubKey
}

View File

@ -126,7 +126,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
if err := message.PrepareContent(); err != nil {
return fmt.Errorf("failed to prepare content: %v", err)
}
chat, err := m.matchMessage(message, state.AllChats, state.Timesource)
chat, err := m.matchChatEntity(message, state.AllChats, state.Timesource)
if err != nil {
return err
}
@ -312,9 +312,9 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
if err != nil {
return fmt.Errorf("failed to prepare message content: %v", err)
}
chat, err := m.matchMessage(receivedMessage, state.AllChats, state.Timesource)
chat, err := m.matchChatEntity(receivedMessage, state.AllChats, state.Timesource)
if err != nil {
return err // matchMessage returns a descriptive error message
return err // matchChatEntity returns a descriptive error message
}
// If deleted-at is greater, ignore message
@ -359,9 +359,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
}
// Add to response
if receivedMessage != nil {
state.Response.Messages = append(state.Response.Messages, receivedMessage)
}
state.Response.Messages = append(state.Response.Messages, receivedMessage)
return nil
}
@ -567,26 +565,26 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM
return m.handleCommandMessage(messageState, oldMessage)
}
func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat, timesource TimeSource) (*Chat, error) {
if message.SigPubKey == nil {
func (m *MessageHandler) matchChatEntity(chatEntity ChatEntity, chats map[string]*Chat, timesource TimeSource) (*Chat, error) {
if chatEntity.GetSigPubKey() == nil {
m.logger.Error("public key can't be empty")
return nil, errors.New("received a message with empty public key")
return nil, errors.New("received a chatEntity with empty public key")
}
switch {
case message.MessageType == protobuf.MessageType_PUBLIC_GROUP:
case chatEntity.GetMessageType() == protobuf.MessageType_PUBLIC_GROUP:
// For public messages, all outgoing and incoming messages have the same chatID
// equal to a public chat name.
chatID := message.ChatId
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
return nil, errors.New("received a public message from non-existing chat")
return nil, errors.New("received a public chatEntity from non-existing chat")
}
return chat, nil
case message.MessageType == protobuf.MessageType_ONE_TO_ONE && common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey):
case chatEntity.GetMessageType() == protobuf.MessageType_ONE_TO_ONE && common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey):
// It's a private message coming from us so we rely on Message.ChatID
// If chat does not exist, it should be created to support multidevice synchronization.
chatID := message.ChatId
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
if len(chatID) != PubKeyStringLength {
@ -606,27 +604,27 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat,
chat = &newChat
}
return chat, nil
case message.MessageType == protobuf.MessageType_ONE_TO_ONE:
// It's an incoming private message. ChatID is calculated from the signature.
case chatEntity.GetMessageType() == protobuf.MessageType_ONE_TO_ONE:
// It's an incoming private chatEntity. ChatID is calculated from the signature.
// If a chat does not exist, a new one is created and saved.
chatID := contactIDFromPublicKey(message.SigPubKey)
chatID := contactIDFromPublicKey(chatEntity.GetSigPubKey())
chat := chats[chatID]
if chat == nil {
// TODO: this should be a three-word name used in the mobile client
newChat := CreateOneToOneChat(chatID[:8], message.SigPubKey, timesource)
newChat := CreateOneToOneChat(chatID[:8], chatEntity.GetSigPubKey(), timesource)
chat = &newChat
}
return chat, nil
case message.MessageType == protobuf.MessageType_PRIVATE_GROUP:
// In the case of a group message, ChatID is the same for all messages belonging to a group.
case chatEntity.GetMessageType() == protobuf.MessageType_PRIVATE_GROUP:
// In the case of a group chatEntity, ChatID is the same for all messages belonging to a group.
// It needs to be verified if the signature public key belongs to the chat.
chatID := message.ChatId
chatID := chatEntity.GetChatId()
chat := chats[chatID]
if chat == nil {
return nil, errors.New("received group chat message for non-existing chat")
return nil, errors.New("received group chat chatEntity for non-existing chat")
}
theirKeyHex := contactIDFromPublicKey(message.SigPubKey)
theirKeyHex := contactIDFromPublicKey(chatEntity.GetSigPubKey())
myKeyHex := contactIDFromPublicKey(&m.identity.PublicKey)
var theyJoined bool
var iJoined bool
@ -670,21 +668,23 @@ func (m *MessageHandler) messageExists(messageID string, existingMessagesMap map
return false, nil
}
func (m *MessageHandler) HandleEmojiReaction(state *ReceivedMessageState, message protobuf.EmojiReaction) error {
func (m *MessageHandler) HandleEmojiReaction(state *ReceivedMessageState, pbEmojiR protobuf.EmojiReaction) error {
logger := m.logger.With(zap.String("site", "HandleEmojiReaction"))
// TODO change this to chat id directly from the protobuf once it is updated
contact := state.CurrentMessageState.Contact
chat, ok := state.AllChats[contact.ID]
if !ok {
chat = OneToOneFromPublicKey(state.CurrentMessageState.PublicKey, state.Timesource)
// We don't want to show the chat to the user
chat.Active = false
emojiReaction := &EmojiReaction{
EmojiReaction: pbEmojiR,
ID: state.CurrentMessageState.MessageID,
From: state.CurrentMessageState.Contact.ID,
SigPubKey: state.CurrentMessageState.PublicKey,
}
chat, err := m.matchChatEntity(emojiReaction, state.AllChats, state.Timesource)
if err != nil {
return err // matchChatEntity returns a descriptive error message
}
logger.Info("Handling emoji reaction")
if chat.LastClockValue < message.Clock {
chat.LastClockValue = message.Clock
if chat.LastClockValue < pbEmojiR.Clock {
chat.LastClockValue = pbEmojiR.Clock
}
state.ModifiedChats[chat.ID] = true

View File

@ -2254,7 +2254,7 @@ func (s *MessageHandlerSuite) TestRun() {
s.Empty(message.LocalChatID)
message.ID = strconv.Itoa(idx) // manually set the ID because messages does not go through messageProcessor
chat, err := s.messageHandler.matchMessage(&message, chatsMap, &testTimeSource{})
chat, err := s.messageHandler.matchChatEntity(&message, chatsMap, &testTimeSource{})
if tc.Error {
s.Require().Error(err)
} else {

View File

@ -5,10 +5,20 @@ package protobuf;
import "enums.proto";
message EmojiReaction {
// clock Lamport timestamp of the chat message
uint64 clock = 1;
// chat_id the ID of the chat the message belongs to, for query efficiency the chat_id is stored in the db even though the
// target message also stores the chat_id
string chat_id = 2;
// message_id the ID of the target message that the user wishes to react to
string message_id = 3;
// message_type is (somewhat confusingly) the ID of the type of chat the message belongs to
MessageType message_type = 4;
// type the ID of the emoji the user wishes to react with
Type type = 5;
enum Type {