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 }