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