github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/whisper/whisperv5/api.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:51</date> 10 //</624342688220581888> 11 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 // 25 // 26 // 27 28 package whisperv5 29 30 import ( 31 "context" 32 "crypto/ecdsa" 33 "errors" 34 "fmt" 35 "sync" 36 "time" 37 38 "github.com/ethereum/go-ethereum/common" 39 "github.com/ethereum/go-ethereum/common/hexutil" 40 "github.com/ethereum/go-ethereum/crypto" 41 "github.com/ethereum/go-ethereum/log" 42 "github.com/ethereum/go-ethereum/p2p/discover" 43 "github.com/ethereum/go-ethereum/rpc" 44 ) 45 46 var ( 47 ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") 48 ErrInvalidSymmetricKey = errors.New("invalid symmetric key") 49 ErrInvalidPublicKey = errors.New("invalid public key") 50 ErrInvalidSigningPubKey = errors.New("invalid signing public key") 51 ErrTooLowPoW = errors.New("message rejected, PoW too low") 52 ErrNoTopics = errors.New("missing topic(s)") 53 ) 54 55 // 56 // 57 type PublicWhisperAPI struct { 58 w *Whisper 59 60 mu sync.Mutex 61 lastUsed map[string]time.Time // 62 } 63 64 // 65 func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { 66 api := &PublicWhisperAPI{ 67 w: w, 68 lastUsed: make(map[string]time.Time), 69 } 70 return api 71 } 72 73 // 74 func (api *PublicWhisperAPI) Version(ctx context.Context) string { 75 return ProtocolVersionStr 76 } 77 78 // 79 type Info struct { 80 Memory int `json:"memory"` // 81 Messages int `json:"messages"` // 82 MinPow float64 `json:"minPow"` // 83 MaxMessageSize uint32 `json:"maxMessageSize"` // 84 } 85 86 // 87 func (api *PublicWhisperAPI) Info(ctx context.Context) Info { 88 stats := api.w.Stats() 89 return Info{ 90 Memory: stats.memoryUsed, 91 Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), 92 MinPow: api.w.MinPow(), 93 MaxMessageSize: api.w.MaxMessageSize(), 94 } 95 } 96 97 // 98 // 99 func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { 100 return true, api.w.SetMaxMessageSize(size) 101 } 102 103 // 104 func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { 105 return true, api.w.SetMinimumPoW(pow) 106 } 107 108 // 109 // 110 func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) { 111 n, err := discover.ParseNode(enode) 112 if err != nil { 113 return false, err 114 } 115 return true, api.w.AllowP2PMessagesFromPeer(n.ID[:]) 116 } 117 118 // 119 // 120 func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { 121 return api.w.NewKeyPair() 122 } 123 124 // 125 func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { 126 key, err := crypto.ToECDSA(privateKey) 127 if err != nil { 128 return "", err 129 } 130 return api.w.AddKeyPair(key) 131 } 132 133 // 134 func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { 135 if ok := api.w.DeleteKeyPair(key); ok { 136 return true, nil 137 } 138 return false, fmt.Errorf("key pair %s not found", key) 139 } 140 141 // 142 func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { 143 return api.w.HasKeyPair(id) 144 } 145 146 // 147 // 148 func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { 149 key, err := api.w.GetPrivateKey(id) 150 if err != nil { 151 return hexutil.Bytes{}, err 152 } 153 return crypto.FromECDSAPub(&key.PublicKey), nil 154 } 155 156 // 157 // 158 func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { 159 key, err := api.w.GetPrivateKey(id) 160 if err != nil { 161 return hexutil.Bytes{}, err 162 } 163 return crypto.FromECDSA(key), nil 164 } 165 166 // 167 // 168 // 169 func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { 170 return api.w.GenerateSymKey() 171 } 172 173 // 174 // 175 // 176 func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { 177 return api.w.AddSymKeyDirect([]byte(key)) 178 } 179 180 // 181 func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { 182 return api.w.AddSymKeyFromPassword(passwd) 183 } 184 185 // 186 func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { 187 return api.w.HasSymKey(id) 188 } 189 190 // 191 func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { 192 return api.w.GetSymKey(id) 193 } 194 195 // 196 func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { 197 return api.w.DeleteSymKey(id) 198 } 199 200 // 201 202 // 203 type NewMessage struct { 204 SymKeyID string `json:"symKeyID"` 205 PublicKey []byte `json:"pubKey"` 206 Sig string `json:"sig"` 207 TTL uint32 `json:"ttl"` 208 Topic TopicType `json:"topic"` 209 Payload []byte `json:"payload"` 210 Padding []byte `json:"padding"` 211 PowTime uint32 `json:"powTime"` 212 PowTarget float64 `json:"powTarget"` 213 TargetPeer string `json:"targetPeer"` 214 } 215 216 type newMessageOverride struct { 217 PublicKey hexutil.Bytes 218 Payload hexutil.Bytes 219 Padding hexutil.Bytes 220 } 221 222 // 223 func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) { 224 var ( 225 symKeyGiven = len(req.SymKeyID) > 0 226 pubKeyGiven = len(req.PublicKey) > 0 227 err error 228 ) 229 230 // 231 if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { 232 return false, ErrSymAsym 233 } 234 235 params := &MessageParams{ 236 TTL: req.TTL, 237 Payload: req.Payload, 238 Padding: req.Padding, 239 WorkTime: req.PowTime, 240 PoW: req.PowTarget, 241 Topic: req.Topic, 242 } 243 244 // 245 if len(req.Sig) > 0 { 246 if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { 247 return false, err 248 } 249 } 250 251 // 252 if symKeyGiven { 253 if params.Topic == (TopicType{}) { // 254 return false, ErrNoTopics 255 } 256 if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { 257 return false, err 258 } 259 if !validateSymmetricKey(params.KeySym) { 260 return false, ErrInvalidSymmetricKey 261 } 262 } 263 264 // 265 if pubKeyGiven { 266 if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil { 267 return false, ErrInvalidPublicKey 268 } 269 } 270 271 // 272 whisperMsg, err := NewSentMessage(params) 273 if err != nil { 274 return false, err 275 } 276 277 env, err := whisperMsg.Wrap(params) 278 if err != nil { 279 return false, err 280 } 281 282 // 283 if len(req.TargetPeer) > 0 { 284 n, err := discover.ParseNode(req.TargetPeer) 285 if err != nil { 286 return false, fmt.Errorf("failed to parse target peer: %s", err) 287 } 288 return true, api.w.SendP2PMessage(n.ID[:], env) 289 } 290 291 // 292 if req.PowTarget < api.w.MinPow() { 293 return false, ErrTooLowPoW 294 } 295 296 return true, api.w.Send(env) 297 } 298 299 // 300 301 // 302 type Criteria struct { 303 SymKeyID string `json:"symKeyID"` 304 PrivateKeyID string `json:"privateKeyID"` 305 Sig []byte `json:"sig"` 306 MinPow float64 `json:"minPow"` 307 Topics []TopicType `json:"topics"` 308 AllowP2P bool `json:"allowP2P"` 309 } 310 311 type criteriaOverride struct { 312 Sig hexutil.Bytes 313 } 314 315 // 316 // 317 func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { 318 var ( 319 symKeyGiven = len(crit.SymKeyID) > 0 320 pubKeyGiven = len(crit.PrivateKeyID) > 0 321 err error 322 ) 323 324 // 325 notifier, supported := rpc.NotifierFromContext(ctx) 326 if !supported { 327 return nil, rpc.ErrNotificationsUnsupported 328 } 329 330 // 331 if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { 332 return nil, ErrSymAsym 333 } 334 335 filter := Filter{ 336 PoW: crit.MinPow, 337 Messages: make(map[common.Hash]*ReceivedMessage), 338 AllowP2P: crit.AllowP2P, 339 } 340 341 if len(crit.Sig) > 0 { 342 if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil { 343 return nil, ErrInvalidSigningPubKey 344 } 345 } 346 347 for i, bt := range crit.Topics { 348 if len(bt) == 0 || len(bt) > 4 { 349 return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) 350 } 351 filter.Topics = append(filter.Topics, bt[:]) 352 } 353 354 // 355 if symKeyGiven { 356 if len(filter.Topics) == 0 { 357 return nil, ErrNoTopics 358 } 359 key, err := api.w.GetSymKey(crit.SymKeyID) 360 if err != nil { 361 return nil, err 362 } 363 if !validateSymmetricKey(key) { 364 return nil, ErrInvalidSymmetricKey 365 } 366 filter.KeySym = key 367 filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) 368 } 369 370 // 371 if pubKeyGiven { 372 filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) 373 if err != nil || filter.KeyAsym == nil { 374 return nil, ErrInvalidPublicKey 375 } 376 } 377 378 id, err := api.w.Subscribe(&filter) 379 if err != nil { 380 return nil, err 381 } 382 383 // 384 rpcSub := notifier.CreateSubscription() 385 go func() { 386 // 387 ticker := time.NewTicker(250 * time.Millisecond) 388 defer ticker.Stop() 389 390 for { 391 select { 392 case <-ticker.C: 393 if filter := api.w.GetFilter(id); filter != nil { 394 for _, rpcMessage := range toMessage(filter.Retrieve()) { 395 if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { 396 log.Error("Failed to send notification", "err", err) 397 } 398 } 399 } 400 case <-rpcSub.Err(): 401 api.w.Unsubscribe(id) 402 return 403 case <-notifier.Closed(): 404 api.w.Unsubscribe(id) 405 return 406 } 407 } 408 }() 409 410 return rpcSub, nil 411 } 412 413 // 414 415 // 416 type Message struct { 417 Sig []byte `json:"sig,omitempty"` 418 TTL uint32 `json:"ttl"` 419 Timestamp uint32 `json:"timestamp"` 420 Topic TopicType `json:"topic"` 421 Payload []byte `json:"payload"` 422 Padding []byte `json:"padding"` 423 PoW float64 `json:"pow"` 424 Hash []byte `json:"hash"` 425 Dst []byte `json:"recipientPublicKey,omitempty"` 426 } 427 428 type messageOverride struct { 429 Sig hexutil.Bytes 430 Payload hexutil.Bytes 431 Padding hexutil.Bytes 432 Hash hexutil.Bytes 433 Dst hexutil.Bytes 434 } 435 436 // 437 func ToWhisperMessage(message *ReceivedMessage) *Message { 438 msg := Message{ 439 Payload: message.Payload, 440 Padding: message.Padding, 441 Timestamp: message.Sent, 442 TTL: message.TTL, 443 PoW: message.PoW, 444 Hash: message.EnvelopeHash.Bytes(), 445 Topic: message.Topic, 446 } 447 448 if message.Dst != nil { 449 b := crypto.FromECDSAPub(message.Dst) 450 if b != nil { 451 msg.Dst = b 452 } 453 } 454 455 if isMessageSigned(message.Raw[0]) { 456 b := crypto.FromECDSAPub(message.SigToPubKey()) 457 if b != nil { 458 msg.Sig = b 459 } 460 } 461 462 return &msg 463 } 464 465 // 466 func toMessage(messages []*ReceivedMessage) []*Message { 467 msgs := make([]*Message, len(messages)) 468 for i, msg := range messages { 469 msgs[i] = ToWhisperMessage(msg) 470 } 471 return msgs 472 } 473 474 // 475 // 476 func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { 477 api.mu.Lock() 478 f := api.w.GetFilter(id) 479 if f == nil { 480 api.mu.Unlock() 481 return nil, fmt.Errorf("filter not found") 482 } 483 api.lastUsed[id] = time.Now() 484 api.mu.Unlock() 485 486 receivedMessages := f.Retrieve() 487 messages := make([]*Message, 0, len(receivedMessages)) 488 for _, msg := range receivedMessages { 489 messages = append(messages, ToWhisperMessage(msg)) 490 } 491 492 return messages, nil 493 } 494 495 // 496 func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { 497 api.mu.Lock() 498 defer api.mu.Unlock() 499 500 delete(api.lastUsed, id) 501 return true, api.w.Unsubscribe(id) 502 } 503 504 // 505 // 506 func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { 507 var ( 508 src *ecdsa.PublicKey 509 keySym []byte 510 keyAsym *ecdsa.PrivateKey 511 topics [][]byte 512 513 symKeyGiven = len(req.SymKeyID) > 0 514 asymKeyGiven = len(req.PrivateKeyID) > 0 515 516 err error 517 ) 518 519 // 520 if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { 521 return "", ErrSymAsym 522 } 523 524 if len(req.Sig) > 0 { 525 if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil { 526 return "", ErrInvalidSigningPubKey 527 } 528 } 529 530 if symKeyGiven { 531 if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { 532 return "", err 533 } 534 if !validateSymmetricKey(keySym) { 535 return "", ErrInvalidSymmetricKey 536 } 537 } 538 539 if asymKeyGiven { 540 if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { 541 return "", err 542 } 543 } 544 545 if len(req.Topics) > 0 { 546 topics = make([][]byte, 0, len(req.Topics)) 547 for _, topic := range req.Topics { 548 topics = append(topics, topic[:]) 549 } 550 } 551 552 f := &Filter{ 553 Src: src, 554 KeySym: keySym, 555 KeyAsym: keyAsym, 556 PoW: req.MinPow, 557 AllowP2P: req.AllowP2P, 558 Topics: topics, 559 Messages: make(map[common.Hash]*ReceivedMessage), 560 } 561 562 id, err := api.w.Subscribe(f) 563 if err != nil { 564 return "", err 565 } 566 567 api.mu.Lock() 568 api.lastUsed[id] = time.Now() 569 api.mu.Unlock() 570 571 return id, nil 572 } 573