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