github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/api.go (about) 1 // Copyright 2015 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Spectrum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package whisperv2 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/SmartMeshFoundation/Spectrum/common" 26 "github.com/SmartMeshFoundation/Spectrum/common/hexutil" 27 "github.com/SmartMeshFoundation/Spectrum/crypto" 28 ) 29 30 // PublicWhisperAPI provides the whisper RPC service. 31 type PublicWhisperAPI struct { 32 w *Whisper 33 34 messagesMu sync.RWMutex 35 messages map[hexutil.Uint]*whisperFilter 36 } 37 38 type whisperOfflineError struct{} 39 40 func (e *whisperOfflineError) Error() string { 41 return "whisper is offline" 42 } 43 44 // whisperOffLineErr is returned when the node doesn't offer the shh service. 45 var whisperOffLineErr = new(whisperOfflineError) 46 47 // NewPublicWhisperAPI create a new RPC whisper service. 48 func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { 49 return &PublicWhisperAPI{w: w, messages: make(map[hexutil.Uint]*whisperFilter)} 50 } 51 52 // Version returns the Whisper version this node offers. 53 func (s *PublicWhisperAPI) Version() (hexutil.Uint, error) { 54 if s.w == nil { 55 return 0, whisperOffLineErr 56 } 57 return hexutil.Uint(s.w.Version()), nil 58 } 59 60 // HasIdentity checks if the the whisper node is configured with the private key 61 // of the specified public pair. 62 func (s *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { 63 if s.w == nil { 64 return false, whisperOffLineErr 65 } 66 return s.w.HasIdentity(crypto.ToECDSAPub(common.FromHex(identity))), nil 67 } 68 69 // NewIdentity generates a new cryptographic identity for the client, and injects 70 // it into the known identities for message decryption. 71 func (s *PublicWhisperAPI) NewIdentity() (string, error) { 72 if s.w == nil { 73 return "", whisperOffLineErr 74 } 75 76 identity := s.w.NewIdentity() 77 return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil 78 } 79 80 type NewFilterArgs struct { 81 To string 82 From string 83 Topics [][][]byte 84 } 85 86 // NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. 87 func (s *PublicWhisperAPI) NewFilter(args NewFilterArgs) (hexutil.Uint, error) { 88 if s.w == nil { 89 return 0, whisperOffLineErr 90 } 91 92 var id hexutil.Uint 93 filter := Filter{ 94 To: crypto.ToECDSAPub(common.FromHex(args.To)), 95 From: crypto.ToECDSAPub(common.FromHex(args.From)), 96 Topics: NewFilterTopics(args.Topics...), 97 Fn: func(message *Message) { 98 wmsg := NewWhisperMessage(message) 99 s.messagesMu.RLock() // Only read lock to the filter pool 100 defer s.messagesMu.RUnlock() 101 if s.messages[id] != nil { 102 s.messages[id].insert(wmsg) 103 } 104 }, 105 } 106 id = hexutil.Uint(s.w.Watch(filter)) 107 108 s.messagesMu.Lock() 109 s.messages[id] = newWhisperFilter(id, s.w) 110 s.messagesMu.Unlock() 111 112 return id, nil 113 } 114 115 // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. 116 func (s *PublicWhisperAPI) GetFilterChanges(filterId hexutil.Uint) []WhisperMessage { 117 s.messagesMu.RLock() 118 defer s.messagesMu.RUnlock() 119 120 if s.messages[filterId] != nil { 121 if changes := s.messages[filterId].retrieve(); changes != nil { 122 return changes 123 } 124 } 125 return returnWhisperMessages(nil) 126 } 127 128 // UninstallFilter disables and removes an existing filter. 129 func (s *PublicWhisperAPI) UninstallFilter(filterId hexutil.Uint) bool { 130 s.messagesMu.Lock() 131 defer s.messagesMu.Unlock() 132 133 if _, ok := s.messages[filterId]; ok { 134 delete(s.messages, filterId) 135 return true 136 } 137 return false 138 } 139 140 // GetMessages retrieves all the known messages that match a specific filter. 141 func (s *PublicWhisperAPI) GetMessages(filterId hexutil.Uint) []WhisperMessage { 142 // Retrieve all the cached messages matching a specific, existing filter 143 s.messagesMu.RLock() 144 defer s.messagesMu.RUnlock() 145 146 var messages []*Message 147 if s.messages[filterId] != nil { 148 messages = s.messages[filterId].messages() 149 } 150 151 return returnWhisperMessages(messages) 152 } 153 154 // returnWhisperMessages converts aNhisper message to a RPC whisper message. 155 func returnWhisperMessages(messages []*Message) []WhisperMessage { 156 msgs := make([]WhisperMessage, len(messages)) 157 for i, msg := range messages { 158 msgs[i] = NewWhisperMessage(msg) 159 } 160 return msgs 161 } 162 163 type PostArgs struct { 164 From string `json:"from"` 165 To string `json:"to"` 166 Topics [][]byte `json:"topics"` 167 Payload string `json:"payload"` 168 Priority int64 `json:"priority"` 169 TTL int64 `json:"ttl"` 170 } 171 172 // Post injects a message into the whisper network for distribution. 173 func (s *PublicWhisperAPI) Post(args PostArgs) (bool, error) { 174 if s.w == nil { 175 return false, whisperOffLineErr 176 } 177 178 // construct whisper message with transmission options 179 message := NewMessage(common.FromHex(args.Payload)) 180 options := Options{ 181 To: crypto.ToECDSAPub(common.FromHex(args.To)), 182 TTL: time.Duration(args.TTL) * time.Second, 183 Topics: NewTopics(args.Topics...), 184 } 185 186 // set sender identity 187 if len(args.From) > 0 { 188 if key := s.w.GetIdentity(crypto.ToECDSAPub(common.FromHex(args.From))); key != nil { 189 options.From = key 190 } else { 191 return false, fmt.Errorf("unknown identity to send from: %s", args.From) 192 } 193 } 194 195 // Wrap and send the message 196 pow := time.Duration(args.Priority) * time.Millisecond 197 envelope, err := message.Wrap(pow, options) 198 if err != nil { 199 return false, err 200 } 201 202 return true, s.w.Send(envelope) 203 } 204 205 // WhisperMessage is the RPC representation of a whisper message. 206 type WhisperMessage struct { 207 ref *Message 208 209 Payload string `json:"payload"` 210 To string `json:"to"` 211 From string `json:"from"` 212 Sent int64 `json:"sent"` 213 TTL int64 `json:"ttl"` 214 Hash string `json:"hash"` 215 } 216 217 func (args *PostArgs) UnmarshalJSON(data []byte) (err error) { 218 var obj struct { 219 From string `json:"from"` 220 To string `json:"to"` 221 Topics []string `json:"topics"` 222 Payload string `json:"payload"` 223 Priority hexutil.Uint64 `json:"priority"` 224 TTL hexutil.Uint64 `json:"ttl"` 225 } 226 227 if err := json.Unmarshal(data, &obj); err != nil { 228 return err 229 } 230 231 args.From = obj.From 232 args.To = obj.To 233 args.Payload = obj.Payload 234 args.Priority = int64(obj.Priority) // TODO(gluk256): handle overflow 235 args.TTL = int64(obj.TTL) // ... here too ... 236 237 // decode topic strings 238 args.Topics = make([][]byte, len(obj.Topics)) 239 for i, topic := range obj.Topics { 240 args.Topics[i] = common.FromHex(topic) 241 } 242 243 return nil 244 } 245 246 // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a 247 // JSON message blob into a WhisperFilterArgs structure. 248 func (args *NewFilterArgs) UnmarshalJSON(b []byte) (err error) { 249 // Unmarshal the JSON message and sanity check 250 var obj struct { 251 To interface{} `json:"to"` 252 From interface{} `json:"from"` 253 Topics interface{} `json:"topics"` 254 } 255 if err := json.Unmarshal(b, &obj); err != nil { 256 return err 257 } 258 259 // Retrieve the simple data contents of the filter arguments 260 if obj.To == nil { 261 args.To = "" 262 } else { 263 argstr, ok := obj.To.(string) 264 if !ok { 265 return fmt.Errorf("to is not a string") 266 } 267 args.To = argstr 268 } 269 if obj.From == nil { 270 args.From = "" 271 } else { 272 argstr, ok := obj.From.(string) 273 if !ok { 274 return fmt.Errorf("from is not a string") 275 } 276 args.From = argstr 277 } 278 // Construct the nested topic array 279 if obj.Topics != nil { 280 // Make sure we have an actual topic array 281 list, ok := obj.Topics.([]interface{}) 282 if !ok { 283 return fmt.Errorf("topics is not an array") 284 } 285 // Iterate over each topic and handle nil, string or array 286 topics := make([][]string, len(list)) 287 for idx, field := range list { 288 switch value := field.(type) { 289 case nil: 290 topics[idx] = []string{} 291 292 case string: 293 topics[idx] = []string{value} 294 295 case []interface{}: 296 topics[idx] = make([]string, len(value)) 297 for i, nested := range value { 298 switch value := nested.(type) { 299 case nil: 300 topics[idx][i] = "" 301 302 case string: 303 topics[idx][i] = value 304 305 default: 306 return fmt.Errorf("topic[%d][%d] is not a string", idx, i) 307 } 308 } 309 default: 310 return fmt.Errorf("topic[%d] not a string or array", idx) 311 } 312 } 313 314 topicsDecoded := make([][][]byte, len(topics)) 315 for i, condition := range topics { 316 topicsDecoded[i] = make([][]byte, len(condition)) 317 for j, topic := range condition { 318 topicsDecoded[i][j] = common.FromHex(topic) 319 } 320 } 321 322 args.Topics = topicsDecoded 323 } 324 return nil 325 } 326 327 // whisperFilter is the message cache matching a specific filter, accumulating 328 // inbound messages until the are requested by the client. 329 type whisperFilter struct { 330 id hexutil.Uint // Filter identifier for old message retrieval 331 ref *Whisper // Whisper reference for old message retrieval 332 333 cache []WhisperMessage // Cache of messages not yet polled 334 skip map[common.Hash]struct{} // List of retrieved messages to avoid duplication 335 update time.Time // Time of the last message query 336 337 lock sync.RWMutex // Lock protecting the filter internals 338 } 339 340 // messages retrieves all the cached messages from the entire pool matching the 341 // filter, resetting the filter's change buffer. 342 func (w *whisperFilter) messages() []*Message { 343 w.lock.Lock() 344 defer w.lock.Unlock() 345 346 w.cache = nil 347 w.update = time.Now() 348 349 w.skip = make(map[common.Hash]struct{}) 350 messages := w.ref.Messages(int(w.id)) 351 for _, message := range messages { 352 w.skip[message.Hash] = struct{}{} 353 } 354 return messages 355 } 356 357 // insert injects a new batch of messages into the filter cache. 358 func (w *whisperFilter) insert(messages ...WhisperMessage) { 359 w.lock.Lock() 360 defer w.lock.Unlock() 361 362 for _, message := range messages { 363 if _, ok := w.skip[message.ref.Hash]; !ok { 364 w.cache = append(w.cache, messages...) 365 } 366 } 367 } 368 369 // retrieve fetches all the cached messages from the filter. 370 func (w *whisperFilter) retrieve() (messages []WhisperMessage) { 371 w.lock.Lock() 372 defer w.lock.Unlock() 373 374 messages, w.cache = w.cache, nil 375 w.update = time.Now() 376 377 return 378 } 379 380 // newWhisperFilter creates a new serialized, poll based whisper topic filter. 381 func newWhisperFilter(id hexutil.Uint, ref *Whisper) *whisperFilter { 382 return &whisperFilter{ 383 id: id, 384 ref: ref, 385 update: time.Now(), 386 skip: make(map[common.Hash]struct{}), 387 } 388 } 389 390 // NewWhisperMessage converts an internal message into an API version. 391 func NewWhisperMessage(message *Message) WhisperMessage { 392 return WhisperMessage{ 393 ref: message, 394 395 Payload: common.ToHex(message.Payload), 396 From: common.ToHex(crypto.FromECDSAPub(message.Recover())), 397 To: common.ToHex(crypto.FromECDSAPub(message.To)), 398 Sent: message.Sent.Unix(), 399 TTL: int64(message.TTL / time.Second), 400 Hash: common.ToHex(message.Hash.Bytes()), 401 } 402 }