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