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  }