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  }