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