github.com/status-im/status-go@v1.1.0/protocol/messenger_peersyncing.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "encoding/hex" 7 "time" 8 9 "github.com/golang/protobuf/proto" 10 datasyncproto "github.com/status-im/mvds/protobuf" 11 "github.com/status-im/mvds/state" 12 13 "github.com/pkg/errors" 14 "go.uber.org/zap" 15 16 "github.com/status-im/status-go/eth-node/types" 17 "github.com/status-im/status-go/protocol/common" 18 "github.com/status-im/status-go/protocol/communities" 19 datasyncpeer "github.com/status-im/status-go/protocol/datasync/peer" 20 "github.com/status-im/status-go/protocol/encryption/sharedsecret" 21 "github.com/status-im/status-go/protocol/peersyncing" 22 v1protocol "github.com/status-im/status-go/protocol/v1" 23 ) 24 25 var peerSyncingLoopInterval time.Duration = 60 * time.Second 26 var maxAdvertiseMessages = 40 27 28 func (m *Messenger) markDeliveredMessages(acks [][]byte) { 29 for _, ack := range acks { 30 //get message ID from database by datasync ID, with at-least-one 31 // semantic 32 messageIDBytes, err := m.persistence.MarkAsConfirmed(ack, true) 33 if err != nil { 34 m.logger.Info("got datasync acknowledge for message we don't have in db", zap.String("ack", hex.EncodeToString(ack))) 35 continue 36 } 37 38 messageID := messageIDBytes.String() 39 //mark messages as delivered 40 41 m.logger.Debug("got datasync acknowledge for message", zap.String("ack", hex.EncodeToString(ack)), zap.String("messageID", messageID)) 42 43 err = m.UpdateMessageOutgoingStatus(messageID, common.OutgoingStatusDelivered) 44 if err != nil { 45 m.logger.Debug("Can't set message status as delivered", zap.Error(err)) 46 } 47 48 err = m.UpdateRawMessageSent(messageID, true) 49 if err != nil { 50 m.logger.Debug("can't set raw message as sent", zap.Error(err)) 51 } 52 53 m.transport.ConfirmMessageDelivered(messageID) 54 55 //send signal to client that message status updated 56 if m.config.messengerSignalsHandler != nil { 57 message, err := m.persistence.MessageByID(messageID) 58 if err != nil { 59 m.logger.Debug("Can't get message from database", zap.Error(err)) 60 continue 61 } 62 m.config.messengerSignalsHandler.MessageDelivered(message.LocalChatID, messageID) 63 } 64 } 65 } 66 67 func (m *Messenger) handleDatasyncMetadata(response *common.HandleMessageResponse) error { 68 m.OnDatasyncAcks(response.DatasyncSender, response.DatasyncAcks) 69 70 if !m.featureFlags.Peersyncing { 71 return nil 72 } 73 74 isPeerSyncingEnabled, err := m.settings.GetPeerSyncingEnabled() 75 if err != nil { 76 return err 77 } 78 if !isPeerSyncingEnabled { 79 return nil 80 } 81 82 err = m.OnDatasyncOffer(response) 83 if err != nil { 84 return err 85 } 86 87 err = m.OnDatasyncRequests(response.DatasyncSender, response.DatasyncRequests) 88 if err != nil { 89 return err 90 } 91 92 return nil 93 } 94 95 func (m *Messenger) startPeerSyncingLoop() { 96 logger := m.logger.Named("PeerSyncingLoop") 97 98 ticker := time.NewTicker(peerSyncingLoopInterval) 99 go func() { 100 for { 101 select { 102 case <-ticker.C: 103 err := m.sendDatasyncOffers() 104 if err != nil { 105 m.logger.Warn("failed to send datasync offers", zap.Error(err)) 106 } 107 108 case <-m.quit: 109 ticker.Stop() 110 logger.Debug("peersyncing loop stopped") 111 return 112 } 113 } 114 }() 115 } 116 117 func (m *Messenger) sendDatasyncOffers() error { 118 if !m.featureFlags.Peersyncing { 119 return nil 120 } 121 122 isPeerSyncingEnabled, err := m.settings.GetPeerSyncingEnabled() 123 if err != nil { 124 return err 125 } 126 if !isPeerSyncingEnabled { 127 return nil 128 } 129 130 err = m.sendDatasyncOffersForCommunities() 131 if err != nil { 132 return err 133 } 134 135 err = m.sendDatasyncOffersForChats() 136 if err != nil { 137 return err 138 } 139 140 // Check all the group ids that need to be on offer 141 // Get all the messages that need to be offered 142 // Prepare datasync messages 143 // Dispatch them to the right group 144 return nil 145 } 146 147 func (m *Messenger) sendDatasyncOffersForCommunities() error { 148 joinedCommunities, err := m.communitiesManager.Joined() 149 if err != nil { 150 return err 151 } 152 153 for _, community := range joinedCommunities { 154 var chatIDs [][]byte 155 for id := range community.Chats() { 156 chatIDs = append(chatIDs, []byte(community.IDString()+id)) 157 } 158 if len(chatIDs) == 0 { 159 continue 160 } 161 availableMessagesMap, err := m.peersyncing.AvailableMessagesMapByChatIDs(chatIDs, maxAdvertiseMessages) 162 if err != nil { 163 return err 164 } 165 datasyncMessage := &datasyncproto.Payload{} 166 if len(availableMessagesMap) == 0 { 167 continue 168 } 169 for chatID, m := range availableMessagesMap { 170 datasyncMessage.GroupOffers = append(datasyncMessage.GroupOffers, &datasyncproto.Offer{GroupId: types.Hex2Bytes(chatID), MessageIds: m}) 171 } 172 payload, err := proto.Marshal(datasyncMessage) 173 if err != nil { 174 return err 175 } 176 rawMessage := common.RawMessage{ 177 Payload: payload, 178 Ephemeral: true, 179 SkipApplicationWrap: true, 180 PubsubTopic: community.PubsubTopic(), 181 Priority: &common.LowPriority, 182 } 183 _, err = m.sender.SendPublic(context.Background(), community.IDString(), rawMessage) 184 if err != nil { 185 return err 186 } 187 } 188 return nil 189 } 190 191 func (m *Messenger) sendDatasyncOffersForChats() error { 192 for _, chat := range m.Chats() { 193 chatIDBytes := []byte(chat.ID) 194 availableMessagesMap, err := m.peersyncing.AvailableMessagesMapByChatIDs([][]byte{chatIDBytes}, maxAdvertiseMessages) 195 if err != nil { 196 return err 197 } 198 datasyncMessage := &datasyncproto.Payload{} 199 if len(availableMessagesMap) == 0 { 200 continue 201 } 202 for _, message := range availableMessagesMap { 203 datasyncMessage.GroupOffers = append(datasyncMessage.GroupOffers, &datasyncproto.Offer{GroupId: chatIDBytes, MessageIds: message}) 204 } 205 payload, err := proto.Marshal(datasyncMessage) 206 if err != nil { 207 return err 208 } 209 210 publicKey, err := chat.PublicKey() 211 if err != nil { 212 return err 213 } 214 rawMessage := common.RawMessage{ 215 Payload: payload, 216 Ephemeral: true, 217 SkipApplicationWrap: true, 218 } 219 _, err = m.sender.SendPrivate(context.Background(), publicKey, &rawMessage) 220 if err != nil { 221 return err 222 } 223 } 224 return nil 225 } 226 227 func (m *Messenger) OnDatasyncOffer(response *common.HandleMessageResponse) error { 228 sender := response.DatasyncSender 229 offers := response.DatasyncOffers 230 231 if len(offers) == 0 { 232 return nil 233 } 234 235 if common.PubkeyToHex(sender) == m.myHexIdentity() { 236 return nil 237 } 238 239 var offeredMessages []peersyncing.SyncMessage 240 241 for _, o := range offers { 242 offeredMessages = append(offeredMessages, peersyncing.SyncMessage{ChatID: o.GroupID, ID: o.MessageID}) 243 } 244 245 messagesToFetch, err := m.peersyncing.OnOffer(offeredMessages) 246 if err != nil { 247 return err 248 } 249 250 if len(messagesToFetch) == 0 { 251 return nil 252 } 253 254 datasyncMessage := &datasyncproto.Payload{} 255 for _, msg := range messagesToFetch { 256 idString := types.Bytes2Hex(msg.ID) 257 lastOffered := m.peersyncingOffers[idString] 258 timeNow := m.GetCurrentTimeInMillis() / 1000 259 if lastOffered+30 < timeNow { 260 m.peersyncingOffers[idString] = timeNow 261 datasyncMessage.Requests = append(datasyncMessage.Requests, msg.ID) 262 } 263 } 264 payload, err := proto.Marshal(datasyncMessage) 265 if err != nil { 266 return err 267 } 268 rawMessage := common.RawMessage{ 269 LocalChatID: common.PubkeyToHex(sender), 270 Payload: payload, 271 Ephemeral: true, 272 SkipApplicationWrap: true, 273 } 274 _, err = m.sender.SendPrivate(context.Background(), sender, &rawMessage) 275 if err != nil { 276 return err 277 } 278 279 // Check if any of the things need to be added 280 // Reply if anything needs adding 281 // Ack any message that is out 282 return nil 283 } 284 285 // canSyncMessageWith checks the permission of a message 286 func (m *Messenger) canSyncMessageWith(message peersyncing.SyncMessage, peer *ecdsa.PublicKey) (bool, error) { 287 switch message.Type { 288 case peersyncing.SyncMessageCommunityType: 289 chat, ok := m.allChats.Load(string(message.ChatID)) 290 if !ok { 291 return false, nil 292 } 293 community, err := m.communitiesManager.GetByIDString(chat.CommunityID) 294 if err != nil { 295 return false, err 296 } 297 298 return m.canSyncCommunityMessageWith(chat, community, peer) 299 case peersyncing.SyncMessageOneToOneType: 300 chat, ok := m.allChats.Load(string(message.ChatID)) 301 if !ok { 302 return false, nil 303 } 304 return m.canSyncOneToOneMessageWith(chat, peer) 305 default: 306 return false, nil 307 } 308 } 309 310 // NOTE: This is not stricly correct. It's possible that you sync a message that has been 311 // posted after the banning of a user from a community, but before we realized that. 312 // As an approximation it should be ok, but worth thinking about how to address this. 313 func (m *Messenger) canSyncCommunityMessageWith(chat *Chat, community *communities.Community, peer *ecdsa.PublicKey) (bool, error) { 314 return community.IsMemberInChat(peer, chat.CommunityChatID()), nil 315 } 316 317 func (m *Messenger) canSyncOneToOneMessageWith(chat *Chat, peer *ecdsa.PublicKey) (bool, error) { 318 return chat.HasMember(common.PubkeyToHex(peer)), nil 319 } 320 321 func (m *Messenger) OnDatasyncRequests(requester *ecdsa.PublicKey, messageIDs [][]byte) error { 322 if len(messageIDs) == 0 { 323 return nil 324 } 325 326 messages, err := m.peersyncing.MessagesByIDs(messageIDs) 327 if err != nil { 328 return err 329 } 330 for _, msg := range messages { 331 canSync, err := m.canSyncMessageWith(msg, requester) 332 if err != nil { 333 return err 334 } 335 if !canSync { 336 continue 337 } 338 idString := common.PubkeyToHex(requester) + types.Bytes2Hex(msg.ID) 339 lastRequested := m.peersyncingRequests[idString] 340 timeNow := m.GetCurrentTimeInMillis() / 1000 341 if lastRequested+30 < timeNow { 342 m.peersyncingRequests[idString] = timeNow 343 344 // Check permissions 345 rawMessage := common.RawMessage{ 346 LocalChatID: common.PubkeyToHex(requester), 347 Payload: msg.Payload, 348 Ephemeral: true, 349 SkipApplicationWrap: true, 350 } 351 _, err = m.sender.SendPrivate(context.Background(), requester, &rawMessage) 352 if err != nil { 353 return err 354 } 355 } 356 357 } 358 // no need of group id, since we can derive from message 359 return nil 360 } 361 362 func (m *Messenger) OnDatasyncAcks(sender *ecdsa.PublicKey, acks [][]byte) { 363 // we should make sure the sender can acknowledge those messages 364 m.markDeliveredMessages(acks) 365 } 366 367 // sendDataSync sends a message scheduled by the data sync layer. 368 // Data Sync layer calls this method "dispatch" function. 369 func (m *Messenger) sendDataSync(receiver state.PeerID, payload *datasyncproto.Payload) error { 370 ctx := context.Background() 371 if !payload.IsValid() { 372 m.logger.Error("payload is invalid") 373 return errors.New("payload is invalid") 374 } 375 376 marshalledPayload, err := proto.Marshal(payload) 377 if err != nil { 378 m.logger.Error("failed to marshal payload") 379 return err 380 } 381 382 publicKey, err := datasyncpeer.IDToPublicKey(receiver) 383 if err != nil { 384 m.logger.Error("failed to convert id to public key", zap.Error(err)) 385 return err 386 } 387 388 // Calculate the messageIDs 389 messageIDs := make([][]byte, 0, len(payload.Messages)) 390 hexMessageIDs := make([]string, 0, len(payload.Messages)) 391 for _, payload := range payload.Messages { 392 mid := v1protocol.MessageID(&m.identity.PublicKey, payload.Body) 393 messageIDs = append(messageIDs, mid) 394 hexMessageIDs = append(hexMessageIDs, mid.String()) 395 } 396 397 messageSpec, err := m.encryptor.BuildEncryptedMessage(m.identity, publicKey, marshalledPayload) 398 if err != nil { 399 return errors.Wrap(err, "failed to encrypt message") 400 } 401 402 // The shared secret needs to be handle before we send a message 403 // otherwise the topic might not be set up before we receive a message 404 err = m.handleSharedSecrets([]*sharedsecret.Secret{messageSpec.SharedSecret}) 405 if err != nil { 406 return err 407 } 408 409 hashes, newMessages, err := m.sender.SendMessageSpec(ctx, publicKey, messageSpec, messageIDs) 410 if err != nil { 411 m.logger.Error("failed to send a datasync message", zap.Error(err)) 412 return err 413 } 414 415 m.logger.Debug("sent private messages", zap.Any("messageIDs", hexMessageIDs), zap.Strings("hashes", types.EncodeHexes(hashes))) 416 m.transport.TrackMany(messageIDs, hashes, newMessages) 417 418 return nil 419 }