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