github.com/status-im/status-go@v1.1.0/protocol/messenger_pin_messages.go (about) 1 package protocol 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/ecdsa" 7 "crypto/sha256" 8 "encoding/binary" 9 "errors" 10 "fmt" 11 12 gethcommon "github.com/ethereum/go-ethereum/common" 13 "github.com/status-im/status-go/eth-node/crypto" 14 "github.com/status-im/status-go/protocol/common" 15 "github.com/status-im/status-go/protocol/protobuf" 16 ) 17 18 // SendPinMessage sends the PinMessage to the corresponding chat 19 func (m *Messenger) SendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) { 20 m.mutex.Lock() 21 defer m.mutex.Unlock() 22 return m.sendPinMessage(ctx, message) 23 } 24 25 func (m *Messenger) sendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) { 26 var response MessengerResponse 27 28 // A valid added chat is required. 29 chat, ok := m.allChats.Load(message.ChatId) 30 if !ok { 31 return nil, errors.New("chat not found") 32 } 33 34 err := m.handleStandaloneChatIdentity(chat) 35 if err != nil { 36 return nil, err 37 } 38 39 err = extendPinMessageFromChat(message, chat, &m.identity.PublicKey, m.getTimesource()) 40 if err != nil { 41 return nil, err 42 } 43 44 message.ID, err = generatePinMessageID(&m.identity.PublicKey, message, chat) 45 if err != nil { 46 return nil, err 47 } 48 49 encodedMessage, err := m.encodeChatEntity(chat, message) 50 if err != nil { 51 return nil, err 52 } 53 54 rawMessage := common.RawMessage{ 55 LocalChatID: chat.ID, 56 Payload: encodedMessage, 57 MessageType: protobuf.ApplicationMetadataMessage_PIN_MESSAGE, 58 SkipGroupMessageWrap: true, 59 ResendType: chat.DefaultResendType(), 60 } 61 _, err = m.dispatchMessage(ctx, rawMessage) 62 if err != nil { 63 return nil, err 64 } 65 66 err = m.persistence.SavePinMessages([]*common.PinMessage{message}) 67 if err != nil { 68 return nil, err 69 } 70 71 if message.Pinned { 72 id, err := generatePinMessageNotificationID(&m.identity.PublicKey, message, chat) 73 if err != nil { 74 return nil, err 75 } 76 chatMessage := &common.Message{ 77 ChatMessage: &protobuf.ChatMessage{ 78 Clock: message.Clock, 79 Timestamp: m.getTimesource().GetCurrentTime(), 80 ChatId: chat.ID, 81 MessageType: message.MessageType, 82 ResponseTo: message.MessageId, 83 ContentType: protobuf.ChatMessage_SYSTEM_MESSAGE_PINNED_MESSAGE, 84 }, 85 WhisperTimestamp: m.getTimesource().GetCurrentTime(), 86 ID: id, 87 LocalChatID: chat.ID, 88 From: m.myHexIdentity(), 89 } 90 91 msg := []*common.Message{chatMessage} 92 err = m.persistence.SaveMessages(msg) 93 if err != nil { 94 return nil, err 95 } 96 97 msg, err = m.pullMessagesAndResponsesFromDB(msg) 98 if err != nil { 99 return nil, err 100 } 101 102 response.SetMessages(msg) 103 err = m.prepareMessages(response.messages) 104 if err != nil { 105 return nil, err 106 } 107 } 108 109 response.AddPinMessage(message) 110 response.AddChat(chat) 111 return &response, m.saveChat(chat) 112 } 113 114 func (m *Messenger) PinnedMessageByChatID(chatID, cursor string, limit int) ([]*common.PinnedMessage, string, error) { 115 pinnedMsgs, cursor, err := m.persistence.PinnedMessageByChatID(chatID, cursor, limit) 116 117 if err != nil { 118 return nil, "", err 119 } 120 121 if m.httpServer != nil { 122 for idx := range pinnedMsgs { 123 msg := pinnedMsgs[idx].Message 124 err = m.prepareMessage(msg, m.httpServer) 125 if err != nil { 126 return nil, "", err 127 } 128 pinnedMsgs[idx].Message = msg 129 } 130 } 131 return pinnedMsgs, cursor, nil 132 } 133 134 func (m *Messenger) SavePinMessages(messages []*common.PinMessage) error { 135 return m.persistence.SavePinMessages(messages) 136 } 137 138 func generatePinMessageID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) (string, error) { 139 data, err := pinMessageBaseID(pubKey, pm, chat) 140 if err != nil { 141 return "", err 142 } 143 144 id := sha256.Sum256(data) 145 idString := fmt.Sprintf("%x", id) 146 147 return idString, nil 148 } 149 150 func pinMessageBaseID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) ([]byte, error) { 151 data := gethcommon.FromHex(pm.MessageId) 152 153 switch { 154 case chat.ChatType == ChatTypeOneToOne: 155 ourPubKey := crypto.FromECDSAPub(pubKey) 156 tmpPubKey, err := chat.PublicKey() 157 if err != nil { 158 return nil, err 159 } 160 theirPubKey := crypto.FromECDSAPub(tmpPubKey) 161 162 if bytes.Compare(ourPubKey, theirPubKey) < 0 { 163 data = append(data, ourPubKey...) // our key 164 data = append(data, theirPubKey...) // their key 165 } else { 166 data = append(data, theirPubKey...) // their key 167 data = append(data, ourPubKey...) // our key 168 } 169 default: 170 data = append(data, []byte(chat.ID)...) 171 } 172 173 return data, nil 174 } 175 176 func generatePinMessageNotificationID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) (string, error) { 177 data, err := pinMessageBaseID(pubKey, pm, chat) 178 if err != nil { 179 return "", err 180 } 181 182 clockBytes := make([]byte, 8) 183 binary.LittleEndian.PutUint64(clockBytes, pm.Clock) 184 data = append(data, clockBytes...) 185 186 id := sha256.Sum256(data) 187 idString := fmt.Sprintf("%x", id) 188 189 return idString, nil 190 }