github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/whisper/whisperv5/api.go (about) 1 // Copyright 2016 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 whisperv5 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 mathrand "math/rand" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/hexutil" 27 "github.com/ethereum/go-ethereum/crypto" 28 "github.com/ethereum/go-ethereum/log" 29 ) 30 31 var whisperOffLineErr = errors.New("whisper is offline") 32 33 // PublicWhisperAPI provides the whisper RPC service. 34 type PublicWhisperAPI struct { 35 whisper *Whisper 36 } 37 38 // NewPublicWhisperAPI create a new RPC whisper service. 39 func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { 40 return &PublicWhisperAPI{whisper: w} 41 } 42 43 // Start starts the Whisper worker threads. 44 func (api *PublicWhisperAPI) Start() error { 45 if api.whisper == nil { 46 return whisperOffLineErr 47 } 48 return api.whisper.Start(nil) 49 } 50 51 // Stop stops the Whisper worker threads. 52 func (api *PublicWhisperAPI) Stop() error { 53 if api.whisper == nil { 54 return whisperOffLineErr 55 } 56 return api.whisper.Stop() 57 } 58 59 // Version returns the Whisper version this node offers. 60 func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) { 61 if api.whisper == nil { 62 return 0, whisperOffLineErr 63 } 64 return hexutil.Uint(api.whisper.Version()), nil 65 } 66 67 // Stats returns the Whisper statistics for diagnostics. 68 func (api *PublicWhisperAPI) Stats() (string, error) { 69 if api.whisper == nil { 70 return "", whisperOffLineErr 71 } 72 return api.whisper.Stats(), nil 73 } 74 75 // MarkPeerTrusted marks specific peer trusted, which will allow it 76 // to send historic (expired) messages. 77 func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error { 78 if api.whisper == nil { 79 return whisperOffLineErr 80 } 81 return api.whisper.MarkPeerTrusted(peerID) 82 } 83 84 // RequestHistoricMessages requests the peer to deliver the old (expired) messages. 85 // data contains parameters (time frame, payment details, etc.), required 86 // by the remote email-like server. Whisper is not aware about the data format, 87 // it will just forward the raw data to the server. 88 //func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error { 89 // if api.whisper == nil { 90 // return whisperOffLineErr 91 // } 92 // return api.whisper.RequestHistoricMessages(peerID, data) 93 //} 94 95 // HasIdentity checks if the whisper node is configured with the private key 96 // of the specified public pair. 97 func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { 98 if api.whisper == nil { 99 return false, whisperOffLineErr 100 } 101 return api.whisper.HasIdentity(identity), nil 102 } 103 104 // DeleteIdentity deletes the specifies key if it exists. 105 func (api *PublicWhisperAPI) DeleteIdentity(identity string) error { 106 if api.whisper == nil { 107 return whisperOffLineErr 108 } 109 api.whisper.DeleteIdentity(identity) 110 return nil 111 } 112 113 // NewIdentity generates a new cryptographic identity for the client, and injects 114 // it into the known identities for message decryption. 115 func (api *PublicWhisperAPI) NewIdentity() (string, error) { 116 if api.whisper == nil { 117 return "", whisperOffLineErr 118 } 119 identity := api.whisper.NewIdentity() 120 return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil 121 } 122 123 // GenerateSymKey generates a random symmetric key and stores it under 124 // the 'name' id. Will be used in the future for session key exchange. 125 func (api *PublicWhisperAPI) GenerateSymKey(name string) error { 126 if api.whisper == nil { 127 return whisperOffLineErr 128 } 129 return api.whisper.GenerateSymKey(name) 130 } 131 132 // AddSymKey stores the key under the 'name' id. 133 func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error { 134 if api.whisper == nil { 135 return whisperOffLineErr 136 } 137 return api.whisper.AddSymKey(name, key) 138 } 139 140 // HasSymKey returns true if there is a key associated with the name string. 141 // Otherwise returns false. 142 func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) { 143 if api.whisper == nil { 144 return false, whisperOffLineErr 145 } 146 res := api.whisper.HasSymKey(name) 147 return res, nil 148 } 149 150 // DeleteSymKey deletes the key associated with the name string if it exists. 151 func (api *PublicWhisperAPI) DeleteSymKey(name string) error { 152 if api.whisper == nil { 153 return whisperOffLineErr 154 } 155 api.whisper.DeleteSymKey(name) 156 return nil 157 } 158 159 // NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. 160 // Returns the ID of the newly created Filter. 161 func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) { 162 if api.whisper == nil { 163 return "", whisperOffLineErr 164 } 165 166 filter := Filter{ 167 Src: crypto.ToECDSAPub(common.FromHex(args.From)), 168 KeySym: api.whisper.GetSymKey(args.KeyName), 169 PoW: args.PoW, 170 Messages: make(map[common.Hash]*ReceivedMessage), 171 AcceptP2P: args.AcceptP2P, 172 } 173 if len(filter.KeySym) > 0 { 174 filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) 175 } 176 filter.Topics = append(filter.Topics, args.Topics...) 177 178 if len(args.Topics) == 0 && len(args.KeyName) != 0 { 179 info := "NewFilter: at least one topic must be specified" 180 log.Error(fmt.Sprintf(info)) 181 return "", errors.New(info) 182 } 183 184 if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { 185 info := "NewFilter: key was not found by name: " + args.KeyName 186 log.Error(fmt.Sprintf(info)) 187 return "", errors.New(info) 188 } 189 190 if len(args.To) == 0 && len(filter.KeySym) == 0 { 191 info := "NewFilter: filter must contain either symmetric or asymmetric key" 192 log.Error(fmt.Sprintf(info)) 193 return "", errors.New(info) 194 } 195 196 if len(args.To) != 0 && len(filter.KeySym) != 0 { 197 info := "NewFilter: filter must not contain both symmetric and asymmetric key" 198 log.Error(fmt.Sprintf(info)) 199 return "", errors.New(info) 200 } 201 202 if len(args.To) > 0 { 203 dst := crypto.ToECDSAPub(common.FromHex(args.To)) 204 if !ValidatePublicKey(dst) { 205 info := "NewFilter: Invalid 'To' address" 206 log.Error(fmt.Sprintf(info)) 207 return "", errors.New(info) 208 } 209 filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) 210 if filter.KeyAsym == nil { 211 info := "NewFilter: non-existent identity provided" 212 log.Error(fmt.Sprintf(info)) 213 return "", errors.New(info) 214 } 215 } 216 217 if len(args.From) > 0 { 218 if !ValidatePublicKey(filter.Src) { 219 info := "NewFilter: Invalid 'From' address" 220 log.Error(fmt.Sprintf(info)) 221 return "", errors.New(info) 222 } 223 } 224 225 return api.whisper.Watch(&filter) 226 } 227 228 // UninstallFilter disables and removes an existing filter. 229 func (api *PublicWhisperAPI) UninstallFilter(filterId string) { 230 api.whisper.Unwatch(filterId) 231 } 232 233 // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. 234 func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage { 235 f := api.whisper.GetFilter(filterId) 236 if f != nil { 237 newMail := f.Retrieve() 238 return toWhisperMessages(newMail) 239 } 240 return toWhisperMessages(nil) 241 } 242 243 // GetMessages retrieves all the known messages that match a specific filter. 244 func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage { 245 all := api.whisper.Messages(filterId) 246 return toWhisperMessages(all) 247 } 248 249 // toWhisperMessages converts a Whisper message to a RPC whisper message. 250 func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage { 251 msgs := make([]*WhisperMessage, len(messages)) 252 for i, msg := range messages { 253 msgs[i] = NewWhisperMessage(msg) 254 } 255 return msgs 256 } 257 258 // Post creates a whisper message and injects it into the network for distribution. 259 func (api *PublicWhisperAPI) Post(args PostArgs) error { 260 if api.whisper == nil { 261 return whisperOffLineErr 262 } 263 264 params := MessageParams{ 265 TTL: args.TTL, 266 Dst: crypto.ToECDSAPub(common.FromHex(args.To)), 267 KeySym: api.whisper.GetSymKey(args.KeyName), 268 Topic: args.Topic, 269 Payload: args.Payload, 270 Padding: args.Padding, 271 WorkTime: args.WorkTime, 272 PoW: args.PoW, 273 } 274 275 if len(args.From) > 0 { 276 pub := crypto.ToECDSAPub(common.FromHex(args.From)) 277 if !ValidatePublicKey(pub) { 278 info := "Post: Invalid 'From' address" 279 log.Error(fmt.Sprintf(info)) 280 return errors.New(info) 281 } 282 params.Src = api.whisper.GetIdentity(string(args.From)) 283 if params.Src == nil { 284 info := "Post: non-existent identity provided" 285 log.Error(fmt.Sprintf(info)) 286 return errors.New(info) 287 } 288 } 289 290 filter := api.whisper.GetFilter(args.FilterID) 291 if filter == nil && len(args.FilterID) > 0 { 292 info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID) 293 log.Error(fmt.Sprintf(info)) 294 return errors.New(info) 295 } 296 297 if filter != nil { 298 // get the missing fields from the filter 299 if params.KeySym == nil && filter.KeySym != nil { 300 params.KeySym = filter.KeySym 301 } 302 if params.Src == nil && filter.Src != nil { 303 params.Src = filter.KeyAsym 304 } 305 if (params.Topic == TopicType{}) { 306 sz := len(filter.Topics) 307 if sz < 1 { 308 info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID) 309 log.Error(fmt.Sprintf(info)) 310 return errors.New(info) 311 } else if sz == 1 { 312 params.Topic = filter.Topics[0] 313 } else { 314 // choose randomly 315 rnd := mathrand.Intn(sz) 316 params.Topic = filter.Topics[rnd] 317 } 318 } 319 } 320 321 // validate 322 if len(args.KeyName) != 0 && len(params.KeySym) == 0 { 323 info := "Post: key was not found by name: " + args.KeyName 324 log.Error(fmt.Sprintf(info)) 325 return errors.New(info) 326 } 327 328 if len(args.To) == 0 && len(params.KeySym) == 0 { 329 info := "Post: message must be encrypted either symmetrically or asymmetrically" 330 log.Error(fmt.Sprintf(info)) 331 return errors.New(info) 332 } 333 334 if len(args.To) != 0 && len(params.KeySym) != 0 { 335 info := "Post: ambigous encryption method requested" 336 log.Error(fmt.Sprintf(info)) 337 return errors.New(info) 338 } 339 340 if len(args.To) > 0 { 341 if !ValidatePublicKey(params.Dst) { 342 info := "Post: Invalid 'To' address" 343 log.Error(fmt.Sprintf(info)) 344 return errors.New(info) 345 } 346 } 347 348 // encrypt and send 349 message := NewSentMessage(¶ms) 350 envelope, err := message.Wrap(¶ms) 351 if err != nil { 352 log.Error(fmt.Sprintf(err.Error())) 353 return err 354 } 355 if len(envelope.Data) > MaxMessageLength { 356 info := "Post: message is too big" 357 log.Error(fmt.Sprintf(info)) 358 return errors.New(info) 359 } 360 if (envelope.Topic == TopicType{} && envelope.IsSymmetric()) { 361 info := "Post: topic is missing for symmetric encryption" 362 log.Error(fmt.Sprintf(info)) 363 return errors.New(info) 364 } 365 366 if args.PeerID != nil { 367 return api.whisper.SendP2PMessage(args.PeerID, envelope) 368 } 369 370 return api.whisper.Send(envelope) 371 } 372 373 type PostArgs struct { 374 TTL uint32 `json:"ttl"` 375 From string `json:"from"` 376 To string `json:"to"` 377 KeyName string `json:"keyname"` 378 Topic TopicType `json:"topic"` 379 Padding hexutil.Bytes `json:"padding"` 380 Payload hexutil.Bytes `json:"payload"` 381 WorkTime uint32 `json:"worktime"` 382 PoW float64 `json:"pow"` 383 FilterID string `json:"filterID"` 384 PeerID hexutil.Bytes `json:"peerID"` 385 } 386 387 type WhisperFilterArgs struct { 388 To string `json:"to"` 389 From string `json:"from"` 390 KeyName string `json:"keyname"` 391 PoW float64 `json:"pow"` 392 Topics []TopicType `json:"topics"` 393 AcceptP2P bool `json:"p2p"` 394 } 395 396 // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a 397 // JSON message blob into a WhisperFilterArgs structure. 398 func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { 399 // Unmarshal the JSON message and sanity check 400 var obj struct { 401 To string `json:"to"` 402 From string `json:"from"` 403 KeyName string `json:"keyname"` 404 PoW float64 `json:"pow"` 405 Topics []interface{} `json:"topics"` 406 AcceptP2P bool `json:"p2p"` 407 } 408 if err := json.Unmarshal(b, &obj); err != nil { 409 return err 410 } 411 412 args.To = obj.To 413 args.From = obj.From 414 args.KeyName = obj.KeyName 415 args.PoW = obj.PoW 416 args.AcceptP2P = obj.AcceptP2P 417 418 // Construct the topic array 419 if obj.Topics != nil { 420 topics := make([]string, len(obj.Topics)) 421 for i, field := range obj.Topics { 422 switch value := field.(type) { 423 case string: 424 topics[i] = value 425 case nil: 426 return fmt.Errorf("topic[%d] is empty", i) 427 default: 428 return fmt.Errorf("topic[%d] is not a string", i) 429 } 430 } 431 topicsDecoded := make([]TopicType, len(topics)) 432 for j, s := range topics { 433 x := common.FromHex(s) 434 if x == nil || len(x) != TopicLength { 435 return fmt.Errorf("topic[%d] is invalid", j) 436 } 437 topicsDecoded[j] = BytesToTopic(x) 438 } 439 args.Topics = topicsDecoded 440 } 441 442 return nil 443 } 444 445 // WhisperMessage is the RPC representation of a whisper message. 446 type WhisperMessage struct { 447 Topic string `json:"topic"` 448 Payload string `json:"payload"` 449 Padding string `json:"padding"` 450 From string `json:"from"` 451 To string `json:"to"` 452 Sent uint32 `json:"sent"` 453 TTL uint32 `json:"ttl"` 454 PoW float64 `json:"pow"` 455 Hash string `json:"hash"` 456 } 457 458 // NewWhisperMessage converts an internal message into an API version. 459 func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage { 460 msg := WhisperMessage{ 461 Topic: common.ToHex(message.Topic[:]), 462 Payload: common.ToHex(message.Payload), 463 Padding: common.ToHex(message.Padding), 464 Sent: message.Sent, 465 TTL: message.TTL, 466 PoW: message.PoW, 467 Hash: common.ToHex(message.EnvelopeHash.Bytes()), 468 } 469 470 if message.Dst != nil { 471 msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst)) 472 } 473 if isMessageSigned(message.Raw[0]) { 474 msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())) 475 } 476 return &msg 477 }