github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/whisper/api.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package whisper 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/ethereumproject/go-ethereum/common" 26 "github.com/ethereumproject/go-ethereum/crypto" 27 "github.com/ethereumproject/go-ethereum/rpc" 28 ) 29 30 // PublicWhisperAPI provides the whisper RPC service. 31 type PublicWhisperAPI struct { 32 w *Whisper 33 34 messagesMu sync.RWMutex 35 messages map[int]*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[int]*whisperFilter)} 50 } 51 52 // Version returns the Whisper version this node offers. 53 func (s *PublicWhisperAPI) Version() (*rpc.HexNumber, error) { 54 if s.w == nil { 55 return rpc.NewHexNumber(0), whisperOffLineErr 56 } 57 return rpc.NewHexNumber(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) (*rpc.HexNumber, error) { 88 if s.w == nil { 89 return nil, whisperOffLineErr 90 } 91 92 var id int 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 107 id = s.w.Watch(filter) 108 109 s.messagesMu.Lock() 110 s.messages[id] = newWhisperFilter(id, s.w) 111 s.messagesMu.Unlock() 112 113 return rpc.NewHexNumber(id), nil 114 } 115 116 // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. 117 func (s *PublicWhisperAPI) GetFilterChanges(filterId rpc.HexNumber) []WhisperMessage { 118 s.messagesMu.RLock() 119 defer s.messagesMu.RUnlock() 120 121 if s.messages[filterId.Int()] != nil { 122 if changes := s.messages[filterId.Int()].retrieve(); changes != nil { 123 return changes 124 } 125 } 126 return returnWhisperMessages(nil) 127 } 128 129 // UninstallFilter disables and removes an existing filter. 130 func (s *PublicWhisperAPI) UninstallFilter(filterId rpc.HexNumber) bool { 131 s.messagesMu.Lock() 132 defer s.messagesMu.Unlock() 133 134 if _, ok := s.messages[filterId.Int()]; ok { 135 delete(s.messages, filterId.Int()) 136 return true 137 } 138 return false 139 } 140 141 // GetMessages retrieves all the known messages that match a specific filter. 142 func (s *PublicWhisperAPI) GetMessages(filterId rpc.HexNumber) []WhisperMessage { 143 // Retrieve all the cached messages matching a specific, existing filter 144 s.messagesMu.RLock() 145 defer s.messagesMu.RUnlock() 146 147 var messages []*Message 148 if s.messages[filterId.Int()] != nil { 149 messages = s.messages[filterId.Int()].messages() 150 } 151 152 return returnWhisperMessages(messages) 153 } 154 155 // returnWhisperMessages converts aNhisper message to a RPC whisper message. 156 func returnWhisperMessages(messages []*Message) []WhisperMessage { 157 msgs := make([]WhisperMessage, len(messages)) 158 for i, msg := range messages { 159 msgs[i] = NewWhisperMessage(msg) 160 } 161 return msgs 162 } 163 164 type PostArgs struct { 165 From string `json:"from"` 166 To string `json:"to"` 167 Topics [][]byte `json:"topics"` 168 Payload string `json:"payload"` 169 Priority int64 `json:"priority"` 170 TTL int64 `json:"ttl"` 171 } 172 173 // Post injects a message into the whisper network for distribution. 174 func (s *PublicWhisperAPI) Post(args PostArgs) (bool, error) { 175 if s.w == nil { 176 return false, whisperOffLineErr 177 } 178 179 // construct whisper message with transmission options 180 message := NewMessage(common.FromHex(args.Payload)) 181 options := Options{ 182 To: crypto.ToECDSAPub(common.FromHex(args.To)), 183 TTL: time.Duration(args.TTL) * time.Second, 184 Topics: NewTopics(args.Topics...), 185 } 186 187 // set sender identity 188 if len(args.From) > 0 { 189 if key := s.w.GetIdentity(crypto.ToECDSAPub(common.FromHex(args.From))); key != nil { 190 options.From = key 191 } else { 192 return false, fmt.Errorf("unknown identity to send from: %s", args.From) 193 } 194 } 195 196 // Wrap and send the message 197 pow := time.Duration(args.Priority) * time.Millisecond 198 envelope, err := message.Wrap(pow, options) 199 if err != nil { 200 return false, err 201 } 202 203 return true, s.w.Send(envelope) 204 } 205 206 // WhisperMessage is the RPC representation of a whisper message. 207 type WhisperMessage struct { 208 ref *Message 209 210 Payload string `json:"payload"` 211 To string `json:"to"` 212 From string `json:"from"` 213 Sent int64 `json:"sent"` 214 TTL int64 `json:"ttl"` 215 Hash string `json:"hash"` 216 } 217 218 func (args *PostArgs) UnmarshalJSON(data []byte) (err error) { 219 var obj struct { 220 From string `json:"from"` 221 To string `json:"to"` 222 Topics []string `json:"topics"` 223 Payload string `json:"payload"` 224 Priority rpc.HexNumber `json:"priority"` 225 TTL rpc.HexNumber `json:"ttl"` 226 } 227 228 if err := json.Unmarshal(data, &obj); err != nil { 229 return err 230 } 231 232 args.From = obj.From 233 args.To = obj.To 234 args.Payload = obj.Payload 235 args.Priority = obj.Priority.Int64() 236 args.TTL = obj.TTL.Int64() 237 238 // decode topic strings 239 args.Topics = make([][]byte, len(obj.Topics)) 240 for i, topic := range obj.Topics { 241 args.Topics[i] = common.FromHex(topic) 242 } 243 244 return nil 245 } 246 247 // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a 248 // JSON message blob into a WhisperFilterArgs structure. 249 func (args *NewFilterArgs) UnmarshalJSON(b []byte) (err error) { 250 // Unmarshal the JSON message and sanity check 251 var obj struct { 252 To interface{} `json:"to"` 253 From interface{} `json:"from"` 254 Topics interface{} `json:"topics"` 255 } 256 if err := json.Unmarshal(b, &obj); err != nil { 257 return err 258 } 259 260 // Retrieve the simple data contents of the filter arguments 261 if obj.To == nil { 262 args.To = "" 263 } else { 264 argstr, ok := obj.To.(string) 265 if !ok { 266 return fmt.Errorf("to is not a string") 267 } 268 args.To = argstr 269 } 270 if obj.From == nil { 271 args.From = "" 272 } else { 273 argstr, ok := obj.From.(string) 274 if !ok { 275 return fmt.Errorf("from is not a string") 276 } 277 args.From = argstr 278 } 279 // Construct the nested topic array 280 if obj.Topics != nil { 281 // Make sure we have an actual topic array 282 list, ok := obj.Topics.([]interface{}) 283 if !ok { 284 return fmt.Errorf("topics is not an array") 285 } 286 // Iterate over each topic and handle nil, string or array 287 topics := make([][]string, len(list)) 288 for idx, field := range list { 289 switch value := field.(type) { 290 case nil: 291 topics[idx] = []string{} 292 293 case string: 294 topics[idx] = []string{value} 295 296 case []interface{}: 297 topics[idx] = make([]string, len(value)) 298 for i, nested := range value { 299 switch value := nested.(type) { 300 case nil: 301 topics[idx][i] = "" 302 303 case string: 304 topics[idx][i] = value 305 306 default: 307 return fmt.Errorf("topic[%d][%d] is not a string", idx, i) 308 } 309 } 310 default: 311 return fmt.Errorf("topic[%d] not a string or array", idx) 312 } 313 } 314 315 topicsDecoded := make([][][]byte, len(topics)) 316 for i, condition := range topics { 317 topicsDecoded[i] = make([][]byte, len(condition)) 318 for j, topic := range condition { 319 topicsDecoded[i][j] = common.FromHex(topic) 320 } 321 } 322 323 args.Topics = topicsDecoded 324 } 325 return nil 326 } 327 328 // whisperFilter is the message cache matching a specific filter, accumulating 329 // inbound messages until the are requested by the client. 330 type whisperFilter struct { 331 id int // Filter identifier for old message retrieval 332 ref *Whisper // Whisper reference for old message retrieval 333 334 cache []WhisperMessage // Cache of messages not yet polled 335 skip map[common.Hash]struct{} // List of retrieved messages to avoid duplication 336 update time.Time // Time of the last message query 337 338 lock sync.RWMutex // Lock protecting the filter internals 339 } 340 341 // messages retrieves all the cached messages from the entire pool matching the 342 // filter, resetting the filter's change buffer. 343 func (w *whisperFilter) messages() []*Message { 344 w.lock.Lock() 345 defer w.lock.Unlock() 346 347 w.cache = nil 348 w.update = time.Now() 349 350 w.skip = make(map[common.Hash]struct{}) 351 messages := w.ref.Messages(w.id) 352 for _, message := range messages { 353 w.skip[message.Hash] = struct{}{} 354 } 355 return messages 356 } 357 358 // insert injects a new batch of messages into the filter cache. 359 func (w *whisperFilter) insert(messages ...WhisperMessage) { 360 w.lock.Lock() 361 defer w.lock.Unlock() 362 363 for _, message := range messages { 364 if _, ok := w.skip[message.ref.Hash]; !ok { 365 w.cache = append(w.cache, messages...) 366 } 367 } 368 } 369 370 // retrieve fetches all the cached messages from the filter. 371 func (w *whisperFilter) retrieve() (messages []WhisperMessage) { 372 w.lock.Lock() 373 defer w.lock.Unlock() 374 375 messages, w.cache = w.cache, nil 376 w.update = time.Now() 377 378 return 379 } 380 381 // newWhisperFilter creates a new serialized, poll based whisper topic filter. 382 func newWhisperFilter(id int, ref *Whisper) *whisperFilter { 383 return &whisperFilter{ 384 id: id, 385 ref: ref, 386 387 update: time.Now(), 388 skip: make(map[common.Hash]struct{}), 389 } 390 } 391 392 // NewWhisperMessage converts an internal message into an API version. 393 func NewWhisperMessage(message *Message) WhisperMessage { 394 return WhisperMessage{ 395 ref: message, 396 397 Payload: common.ToHex(message.Payload), 398 From: common.ToHex(crypto.FromECDSAPub(message.Recover())), 399 To: common.ToHex(crypto.FromECDSAPub(message.To)), 400 Sent: message.Sent.Unix(), 401 TTL: int64(message.TTL / time.Second), 402 Hash: common.ToHex(message.Hash.Bytes()), 403 } 404 }