github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/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 19:16:46</date> 10 //</624450125040717824> 11 12 13 package whisperv6 14 15 import ( 16 "context" 17 "crypto/ecdsa" 18 "errors" 19 "fmt" 20 "sync" 21 "time" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/common/hexutil" 25 "github.com/ethereum/go-ethereum/crypto" 26 "github.com/ethereum/go-ethereum/log" 27 "github.com/ethereum/go-ethereum/p2p/enode" 28 "github.com/ethereum/go-ethereum/rpc" 29 ) 30 31 //错误列表 32 var ( 33 ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") 34 ErrInvalidSymmetricKey = errors.New("invalid symmetric key") 35 ErrInvalidPublicKey = errors.New("invalid public key") 36 ErrInvalidSigningPubKey = errors.New("invalid signing public key") 37 ErrTooLowPoW = errors.New("message rejected, PoW too low") 38 ErrNoTopics = errors.New("missing topic(s)") 39 ) 40 41 //publicWhisperAPI提供可以 42 //公开使用,不涉及安全问题。 43 type PublicWhisperAPI struct { 44 w *Whisper 45 46 mu sync.Mutex 47 lastUsed map[string]time.Time //跟踪上次轮询筛选器的时间。 48 } 49 50 //NewPublicWhisperAPI创建新的RPC Whisper服务。 51 func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { 52 api := &PublicWhisperAPI{ 53 w: w, 54 lastUsed: make(map[string]time.Time), 55 } 56 return api 57 } 58 59 //version返回耳语子协议版本。 60 func (api *PublicWhisperAPI) Version(ctx context.Context) string { 61 return ProtocolVersionStr 62 } 63 64 //信息包含诊断信息。 65 type Info struct { 66 Memory int `json:"memory"` //浮动消息的内存大小(字节)。 67 Messages int `json:"messages"` //浮动消息数。 68 MinPow float64 `json:"minPow"` //最小可接受功率 69 MaxMessageSize uint32 `json:"maxMessageSize"` //最大接受邮件大小 70 } 71 72 //信息返回关于耳语节点的诊断信息。 73 func (api *PublicWhisperAPI) Info(ctx context.Context) Info { 74 stats := api.w.Stats() 75 return Info{ 76 Memory: stats.memoryUsed, 77 Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), 78 MinPow: api.w.MinPow(), 79 MaxMessageSize: api.w.MaxMessageSize(), 80 } 81 } 82 83 //setmaxmessagesize设置可接受的最大消息大小。 84 //上限由MaxMessageSize定义。 85 func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { 86 return true, api.w.SetMaxMessageSize(size) 87 } 88 89 //setminpow设置最小的pow,并通知对等方。 90 func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { 91 return true, api.w.SetMinimumPoW(pow) 92 } 93 94 //setbloomfilter设置bloom filter的新值,并通知对等方。 95 func (api *PublicWhisperAPI) SetBloomFilter(ctx context.Context, bloom hexutil.Bytes) (bool, error) { 96 return true, api.w.SetBloomFilter(bloom) 97 } 98 99 //marktrustedpeer标记一个受信任的对等方,这将允许它发送历史(过期)消息。 100 //注意:此功能不添加新节点,节点需要作为对等节点存在。 101 func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, url string) (bool, error) { 102 n, err := enode.ParseV4(url) 103 if err != nil { 104 return false, err 105 } 106 return true, api.w.AllowP2PMessagesFromPeer(n.ID().Bytes()) 107 } 108 109 //new key pair为消息解密和加密生成一个新的公钥和私钥对。 110 //它返回一个可用于引用密钥对的ID。 111 func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { 112 return api.w.NewKeyPair() 113 } 114 115 //addprivatekey导入给定的私钥。 116 func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { 117 key, err := crypto.ToECDSA(privateKey) 118 if err != nil { 119 return "", err 120 } 121 return api.w.AddKeyPair(key) 122 } 123 124 //DeleteKeyPair删除具有给定密钥的密钥(如果存在)。 125 func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { 126 if ok := api.w.DeleteKeyPair(key); ok { 127 return true, nil 128 } 129 return false, fmt.Errorf("key pair %s not found", key) 130 } 131 132 //hasKeyPair返回节点是否有与给定ID关联的密钥对的指示。 133 func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { 134 return api.w.HasKeyPair(id) 135 } 136 137 //GetPublicKey返回与给定键关联的公钥。关键是十六进制 138 //以ANSI X9.62第4.3.6节规定的格式对键进行编码表示。 139 func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { 140 key, err := api.w.GetPrivateKey(id) 141 if err != nil { 142 return hexutil.Bytes{}, err 143 } 144 return crypto.FromECDSAPub(&key.PublicKey), nil 145 } 146 147 //getprivatekey返回与给定密钥关联的私钥。关键是十六进制 148 //以ANSI X9.62第4.3.6节规定的格式对键进行编码表示。 149 func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { 150 key, err := api.w.GetPrivateKey(id) 151 if err != nil { 152 return hexutil.Bytes{}, err 153 } 154 return crypto.FromECDSA(key), nil 155 } 156 157 //newsymkey生成随机对称密钥。 158 //它返回一个可用于引用该键的ID。 159 //可用于加密和解密双方都知道密钥的消息。 160 func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { 161 return api.w.GenerateSymKey() 162 } 163 164 //addsymkey导入对称密钥。 165 //它返回一个可用于引用该键的ID。 166 //可用于加密和解密双方都知道密钥的消息。 167 func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { 168 return api.w.AddSymKeyDirect([]byte(key)) 169 } 170 171 //generatesymkeyfrompassword从给定的密码派生一个密钥,存储并返回其ID。 172 func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { 173 return api.w.AddSymKeyFromPassword(passwd) 174 } 175 176 //hassymkey返回节点是否具有与给定密钥关联的对称密钥的指示。 177 func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { 178 return api.w.HasSymKey(id) 179 } 180 181 //GetSymkey返回与给定ID关联的对称密钥。 182 func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { 183 return api.w.GetSymKey(id) 184 } 185 186 //DeleteSymkey删除与给定ID关联的对称密钥。 187 func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { 188 return api.w.DeleteSymKey(id) 189 } 190 191 //makelightclient将节点转换为light client,但不会转发 192 //任何传入消息,并且只发送源自此节点的消息。 193 func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool { 194 api.w.SetLightClientMode(true) 195 return api.w.LightClientMode() 196 } 197 198 //cancellightclient取消light client模式。 199 func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool { 200 api.w.SetLightClientMode(false) 201 return !api.w.LightClientMode() 202 } 203 204 //go:生成gencodec-键入newmessage-field override newmessage override-out gen_newmessage_json.go 205 206 //new message表示通过rpc发布的新低语消息。 207 type NewMessage struct { 208 SymKeyID string `json:"symKeyID"` 209 PublicKey []byte `json:"pubKey"` 210 Sig string `json:"sig"` 211 TTL uint32 `json:"ttl"` 212 Topic TopicType `json:"topic"` 213 Payload []byte `json:"payload"` 214 Padding []byte `json:"padding"` 215 PowTime uint32 `json:"powTime"` 216 PowTarget float64 `json:"powTarget"` 217 TargetPeer string `json:"targetPeer"` 218 } 219 220 type newMessageOverride struct { 221 PublicKey hexutil.Bytes 222 Payload hexutil.Bytes 223 Padding hexutil.Bytes 224 } 225 226 //在低语网络上发布消息。 227 //如果成功,返回消息的哈希值。 228 func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (hexutil.Bytes, error) { 229 var ( 230 symKeyGiven = len(req.SymKeyID) > 0 231 pubKeyGiven = len(req.PublicKey) > 0 232 err error 233 ) 234 235 //用户必须指定对称密钥或非对称密钥 236 if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { 237 return nil, ErrSymAsym 238 } 239 240 params := &MessageParams{ 241 TTL: req.TTL, 242 Payload: req.Payload, 243 Padding: req.Padding, 244 WorkTime: req.PowTime, 245 PoW: req.PowTarget, 246 Topic: req.Topic, 247 } 248 249 //设置用于对消息签名的键 250 if len(req.Sig) > 0 { 251 if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { 252 return nil, err 253 } 254 } 255 256 //设置用于加密消息的对称密钥 257 if symKeyGiven { 258 if params.Topic == (TopicType{}) { //主题对于对称加密是必需的 259 return nil, ErrNoTopics 260 } 261 if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { 262 return nil, err 263 } 264 if !validateDataIntegrity(params.KeySym, aesKeyLength) { 265 return nil, ErrInvalidSymmetricKey 266 } 267 } 268 269 //设置用于加密消息的非对称密钥 270 if pubKeyGiven { 271 if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil { 272 return nil, ErrInvalidPublicKey 273 } 274 } 275 276 //加密并发送消息 277 whisperMsg, err := NewSentMessage(params) 278 if err != nil { 279 return nil, err 280 } 281 282 var result []byte 283 env, err := whisperMsg.Wrap(params) 284 if err != nil { 285 return nil, err 286 } 287 288 //发送到特定节点(跳过电源检查) 289 if len(req.TargetPeer) > 0 { 290 n, err := enode.ParseV4(req.TargetPeer) 291 if err != nil { 292 return nil, fmt.Errorf("failed to parse target peer: %s", err) 293 } 294 err = api.w.SendP2PMessage(n.ID().Bytes(), env) 295 if err == nil { 296 hash := env.Hash() 297 result = hash[:] 298 } 299 return result, err 300 } 301 302 //确保消息POW满足节点的最小可接受POW 303 if req.PowTarget < api.w.MinPow() { 304 return nil, ErrTooLowPoW 305 } 306 307 err = api.w.Send(env) 308 if err == nil { 309 hash := env.Hash() 310 result = hash[:] 311 } 312 return result, err 313 } 314 315 //go:生成gencodec-类型标准-字段覆盖标准覆盖-out gen_标准\json.go 316 317 //条件保存入站消息的各种筛选选项。 318 type Criteria struct { 319 SymKeyID string `json:"symKeyID"` 320 PrivateKeyID string `json:"privateKeyID"` 321 Sig []byte `json:"sig"` 322 MinPow float64 `json:"minPow"` 323 Topics []TopicType `json:"topics"` 324 AllowP2P bool `json:"allowP2P"` 325 } 326 327 type criteriaOverride struct { 328 Sig hexutil.Bytes 329 } 330 331 //消息设置了一个订阅,该订阅在消息到达时触发匹配的事件 332 //给定的一组标准。 333 func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { 334 var ( 335 symKeyGiven = len(crit.SymKeyID) > 0 336 pubKeyGiven = len(crit.PrivateKeyID) > 0 337 err error 338 ) 339 340 //确保RPC连接支持订阅 341 notifier, supported := rpc.NotifierFromContext(ctx) 342 if !supported { 343 return nil, rpc.ErrNotificationsUnsupported 344 } 345 346 //用户必须指定对称密钥或非对称密钥 347 if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { 348 return nil, ErrSymAsym 349 } 350 351 filter := Filter{ 352 PoW: crit.MinPow, 353 Messages: make(map[common.Hash]*ReceivedMessage), 354 AllowP2P: crit.AllowP2P, 355 } 356 357 if len(crit.Sig) > 0 { 358 if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil { 359 return nil, ErrInvalidSigningPubKey 360 } 361 } 362 363 for i, bt := range crit.Topics { 364 if len(bt) == 0 || len(bt) > 4 { 365 return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) 366 } 367 filter.Topics = append(filter.Topics, bt[:]) 368 } 369 370 //侦听使用给定对称密钥加密的消息 371 if symKeyGiven { 372 if len(filter.Topics) == 0 { 373 return nil, ErrNoTopics 374 } 375 key, err := api.w.GetSymKey(crit.SymKeyID) 376 if err != nil { 377 return nil, err 378 } 379 if !validateDataIntegrity(key, aesKeyLength) { 380 return nil, ErrInvalidSymmetricKey 381 } 382 filter.KeySym = key 383 filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) 384 } 385 386 //侦听使用给定公钥加密的消息 387 if pubKeyGiven { 388 filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) 389 if err != nil || filter.KeyAsym == nil { 390 return nil, ErrInvalidPublicKey 391 } 392 } 393 394 id, err := api.w.Subscribe(&filter) 395 if err != nil { 396 return nil, err 397 } 398 399 //创建订阅并开始等待消息事件 400 rpcSub := notifier.CreateSubscription() 401 go func() { 402 //现在在内部进行投票,重构内部以获得通道支持 403 ticker := time.NewTicker(250 * time.Millisecond) 404 defer ticker.Stop() 405 406 for { 407 select { 408 case <-ticker.C: 409 if filter := api.w.GetFilter(id); filter != nil { 410 for _, rpcMessage := range toMessage(filter.Retrieve()) { 411 if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { 412 log.Error("Failed to send notification", "err", err) 413 } 414 } 415 } 416 case <-rpcSub.Err(): 417 api.w.Unsubscribe(id) 418 return 419 case <-notifier.Closed(): 420 api.w.Unsubscribe(id) 421 return 422 } 423 } 424 }() 425 426 return rpcSub, nil 427 } 428 429 //go:生成gencodec-类型message-字段覆盖message override-out gen_message_json.go 430 431 //message是whisper消息的RPC表示。 432 type Message struct { 433 Sig []byte `json:"sig,omitempty"` 434 TTL uint32 `json:"ttl"` 435 Timestamp uint32 `json:"timestamp"` 436 Topic TopicType `json:"topic"` 437 Payload []byte `json:"payload"` 438 Padding []byte `json:"padding"` 439 PoW float64 `json:"pow"` 440 Hash []byte `json:"hash"` 441 Dst []byte `json:"recipientPublicKey,omitempty"` 442 } 443 444 type messageOverride struct { 445 Sig hexutil.Bytes 446 Payload hexutil.Bytes 447 Padding hexutil.Bytes 448 Hash hexutil.Bytes 449 Dst hexutil.Bytes 450 } 451 452 //TowHisPermessage将内部消息转换为API版本。 453 func ToWhisperMessage(message *ReceivedMessage) *Message { 454 msg := Message{ 455 Payload: message.Payload, 456 Padding: message.Padding, 457 Timestamp: message.Sent, 458 TTL: message.TTL, 459 PoW: message.PoW, 460 Hash: message.EnvelopeHash.Bytes(), 461 Topic: message.Topic, 462 } 463 464 if message.Dst != nil { 465 b := crypto.FromECDSAPub(message.Dst) 466 if b != nil { 467 msg.Dst = b 468 } 469 } 470 471 if isMessageSigned(message.Raw[0]) { 472 b := crypto.FromECDSAPub(message.SigToPubKey()) 473 if b != nil { 474 msg.Sig = b 475 } 476 } 477 478 return &msg 479 } 480 481 //ToMessage将一组消息转换为其RPC表示形式。 482 func toMessage(messages []*ReceivedMessage) []*Message { 483 msgs := make([]*Message, len(messages)) 484 for i, msg := range messages { 485 msgs[i] = ToWhisperMessage(msg) 486 } 487 return msgs 488 } 489 490 //getfiltermessages返回符合筛选条件和 491 //从上一次投票到现在。 492 func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { 493 api.mu.Lock() 494 f := api.w.GetFilter(id) 495 if f == nil { 496 api.mu.Unlock() 497 return nil, fmt.Errorf("filter not found") 498 } 499 api.lastUsed[id] = time.Now() 500 api.mu.Unlock() 501 502 receivedMessages := f.Retrieve() 503 messages := make([]*Message, 0, len(receivedMessages)) 504 for _, msg := range receivedMessages { 505 messages = append(messages, ToWhisperMessage(msg)) 506 } 507 508 return messages, nil 509 } 510 511 //DeleteMessageFilter删除一个筛选器。 512 func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { 513 api.mu.Lock() 514 defer api.mu.Unlock() 515 516 delete(api.lastUsed, id) 517 return true, api.w.Unsubscribe(id) 518 } 519 520 //NewMessageFilter创建一个可用于轮询的新筛选器 521 //(新)满足给定条件的消息。 522 func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { 523 var ( 524 src *ecdsa.PublicKey 525 keySym []byte 526 keyAsym *ecdsa.PrivateKey 527 topics [][]byte 528 529 symKeyGiven = len(req.SymKeyID) > 0 530 asymKeyGiven = len(req.PrivateKeyID) > 0 531 532 err error 533 ) 534 535 //用户必须指定对称密钥或非对称密钥 536 if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { 537 return "", ErrSymAsym 538 } 539 540 if len(req.Sig) > 0 { 541 if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil { 542 return "", ErrInvalidSigningPubKey 543 } 544 } 545 546 if symKeyGiven { 547 if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { 548 return "", err 549 } 550 if !validateDataIntegrity(keySym, aesKeyLength) { 551 return "", ErrInvalidSymmetricKey 552 } 553 } 554 555 if asymKeyGiven { 556 if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { 557 return "", err 558 } 559 } 560 561 if len(req.Topics) > 0 { 562 topics = make([][]byte, len(req.Topics)) 563 for i, topic := range req.Topics { 564 topics[i] = make([]byte, TopicLength) 565 copy(topics[i], topic[:]) 566 } 567 } 568 569 f := &Filter{ 570 Src: src, 571 KeySym: keySym, 572 KeyAsym: keyAsym, 573 PoW: req.MinPow, 574 AllowP2P: req.AllowP2P, 575 Topics: topics, 576 Messages: make(map[common.Hash]*ReceivedMessage), 577 } 578 579 id, err := api.w.Subscribe(f) 580 if err != nil { 581 return "", err 582 } 583 584 api.mu.Lock() 585 api.lastUsed[id] = time.Now() 586 api.mu.Unlock() 587 588 return id, nil 589 } 590