github.com/status-im/status-go@v1.1.0/wakuv2/common/message.go (about)

     1  package common
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"github.com/waku-org/go-waku/waku/v2/payload"
    10  	"github.com/waku-org/go-waku/waku/v2/protocol"
    11  
    12  	"github.com/ethereum/go-ethereum/common"
    13  	"github.com/ethereum/go-ethereum/crypto"
    14  	"github.com/ethereum/go-ethereum/log"
    15  )
    16  
    17  // MessageType represents where this message comes from
    18  type MessageType = string
    19  
    20  const (
    21  	RelayedMessageType MessageType = "relay"
    22  	StoreMessageType   MessageType = "store"
    23  	SendMessageType    MessageType = "send"
    24  	MissingMessageType MessageType = "missing"
    25  )
    26  
    27  // MessageParams specifies the exact way a message should be wrapped
    28  // into an Envelope.
    29  type MessageParams struct {
    30  	Src     *ecdsa.PrivateKey
    31  	Dst     *ecdsa.PublicKey
    32  	KeySym  []byte
    33  	Topic   TopicType
    34  	Payload []byte
    35  	Padding []byte
    36  }
    37  
    38  // ReceivedMessage represents a data packet to be received through the
    39  // WakuV2 protocol and successfully decrypted.
    40  type ReceivedMessage struct {
    41  	Envelope *protocol.Envelope // Wrapped Waku Message
    42  
    43  	MsgType MessageType
    44  
    45  	Data      []byte
    46  	Padding   []byte
    47  	Signature []byte
    48  
    49  	Sent uint32           // Time when the message was posted into the network in seconds
    50  	Src  *ecdsa.PublicKey // Message recipient (identity used to decode the message)
    51  	Dst  *ecdsa.PublicKey // Message recipient (identity used to decode the message)
    52  
    53  	PubsubTopic  string
    54  	ContentTopic TopicType
    55  
    56  	SymKeyHash common.Hash // The Keccak256Hash of the key
    57  
    58  	hash common.Hash
    59  
    60  	Processed atomic.Bool
    61  }
    62  
    63  // EnvelopeError code and optional description of the error.
    64  type EnvelopeError struct {
    65  	Hash        common.Hash
    66  	Code        uint
    67  	Description string
    68  }
    69  
    70  // MessagesResponse sent as a response after processing batch of envelopes.
    71  type MessagesResponse struct {
    72  	// Hash is a hash of all envelopes sent in the single batch.
    73  	Hash common.Hash
    74  	// Per envelope error.
    75  	Errors []EnvelopeError
    76  }
    77  
    78  func (msg *ReceivedMessage) isSymmetricEncryption() bool {
    79  	return msg.SymKeyHash != common.Hash{}
    80  }
    81  
    82  func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
    83  	return msg.Dst != nil
    84  }
    85  
    86  // MessageStore defines interface for temporary message store.
    87  type MessageStore interface {
    88  	Add(*ReceivedMessage) error
    89  	Pop() ([]*ReceivedMessage, error)
    90  }
    91  
    92  // NewMemoryMessageStore returns pointer to an instance of the MemoryMessageStore.
    93  func NewMemoryMessageStore() *MemoryMessageStore {
    94  	return &MemoryMessageStore{
    95  		messages: map[common.Hash]*ReceivedMessage{},
    96  	}
    97  }
    98  
    99  // MemoryMessageStore represents messages stored in a memory hash table.
   100  type MemoryMessageStore struct {
   101  	mu       sync.Mutex
   102  	messages map[common.Hash]*ReceivedMessage
   103  }
   104  
   105  func NewReceivedMessage(env *protocol.Envelope, msgType MessageType) *ReceivedMessage {
   106  	ct, err := ExtractTopicFromContentTopic(env.Message().ContentTopic)
   107  	if err != nil {
   108  		log.Debug("failed to extract content topic from message", "topic", env.Message().ContentTopic, "err", err)
   109  		return nil
   110  	}
   111  
   112  	return &ReceivedMessage{
   113  		Envelope:     env,
   114  		MsgType:      msgType,
   115  		Sent:         uint32(env.Message().GetTimestamp() / int64(time.Second)),
   116  		ContentTopic: ct,
   117  		PubsubTopic:  env.PubsubTopic(),
   118  	}
   119  }
   120  
   121  // Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
   122  func (msg *ReceivedMessage) Hash() common.Hash {
   123  	if (msg.hash == common.Hash{}) {
   124  		msg.hash = common.BytesToHash(msg.Envelope.Hash().Bytes())
   125  	}
   126  	return msg.hash
   127  }
   128  
   129  // Add adds message to store.
   130  func (store *MemoryMessageStore) Add(msg *ReceivedMessage) error {
   131  	store.mu.Lock()
   132  	defer store.mu.Unlock()
   133  	if _, exist := store.messages[msg.Hash()]; !exist {
   134  		store.messages[msg.Hash()] = msg
   135  	}
   136  	return nil
   137  }
   138  
   139  // Pop returns all available messages and cleans the store.
   140  func (store *MemoryMessageStore) Pop() ([]*ReceivedMessage, error) {
   141  	store.mu.Lock()
   142  	defer store.mu.Unlock()
   143  	all := make([]*ReceivedMessage, 0, len(store.messages))
   144  	for hash, msg := range store.messages {
   145  		delete(store.messages, hash)
   146  		all = append(all, msg)
   147  	}
   148  	return all, nil
   149  }
   150  
   151  // Open tries to decrypt an message, and populates the message fields in case of success.
   152  func (msg *ReceivedMessage) Open(watcher *Filter) (result *ReceivedMessage) {
   153  	if watcher == nil {
   154  		return nil
   155  	}
   156  
   157  	// The API interface forbids filters doing both symmetric and asymmetric encryption.
   158  	if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
   159  		return nil
   160  	}
   161  
   162  	// TODO: should we update msg instead of creating a new received message?
   163  	result = new(ReceivedMessage)
   164  
   165  	keyInfo := new(payload.KeyInfo)
   166  	if watcher.expectsAsymmetricEncryption() {
   167  		keyInfo.Kind = payload.Asymmetric
   168  		keyInfo.PrivKey = watcher.KeyAsym
   169  		msg.Dst = &watcher.KeyAsym.PublicKey
   170  	} else if watcher.expectsSymmetricEncryption() {
   171  		keyInfo.Kind = payload.Symmetric
   172  		keyInfo.SymKey = watcher.KeySym
   173  		msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
   174  	}
   175  
   176  	raw, err := payload.DecodePayload(msg.Envelope.Message(), keyInfo)
   177  
   178  	if err != nil {
   179  		log.Error("failed to decode message", "err", err)
   180  		return nil
   181  	}
   182  
   183  	result.Envelope = msg.Envelope
   184  	result.Data = raw.Data
   185  	result.Padding = raw.Padding
   186  	result.Signature = raw.Signature
   187  	result.Src = raw.PubKey
   188  	result.SymKeyHash = msg.SymKeyHash
   189  	result.Dst = msg.Dst
   190  	result.Sent = uint32(msg.Envelope.Message().GetTimestamp() / int64(time.Second))
   191  
   192  	ct, err := ExtractTopicFromContentTopic(msg.Envelope.Message().ContentTopic)
   193  	if err != nil {
   194  		log.Error("failed to decode message", "err", err)
   195  		return nil
   196  	}
   197  
   198  	result.PubsubTopic = watcher.PubsubTopic
   199  	result.ContentTopic = ct
   200  
   201  	return result
   202  }