github.com/ylsgit/go-ethereum@v1.6.5/whisper/whisperv5/api.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package whisperv5
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/common/hexutil"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/p2p/discover"
    28  )
    29  
    30  var whisperOfflineErr = errors.New("whisper is offline")
    31  
    32  // PublicWhisperAPI provides the whisper RPC service.
    33  type PublicWhisperAPI struct {
    34  	whisper *Whisper
    35  }
    36  
    37  // NewPublicWhisperAPI create a new RPC whisper service.
    38  func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
    39  	return &PublicWhisperAPI{whisper: w}
    40  }
    41  
    42  // Start starts the Whisper worker threads.
    43  func (api *PublicWhisperAPI) Start() error {
    44  	if api.whisper == nil {
    45  		return whisperOfflineErr
    46  	}
    47  	return api.whisper.Start(nil)
    48  }
    49  
    50  // Stop stops the Whisper worker threads.
    51  func (api *PublicWhisperAPI) Stop() error {
    52  	if api.whisper == nil {
    53  		return whisperOfflineErr
    54  	}
    55  	return api.whisper.Stop()
    56  }
    57  
    58  // Version returns the Whisper version this node offers.
    59  func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) {
    60  	if api.whisper == nil {
    61  		return 0, whisperOfflineErr
    62  	}
    63  	return hexutil.Uint(api.whisper.Version()), nil
    64  }
    65  
    66  // Info returns the Whisper statistics for diagnostics.
    67  func (api *PublicWhisperAPI) Info() (string, error) {
    68  	if api.whisper == nil {
    69  		return "", whisperOfflineErr
    70  	}
    71  	return api.whisper.Stats(), nil
    72  }
    73  
    74  // SetMaxMessageLength sets the maximal message length allowed by this node
    75  func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error {
    76  	if api.whisper == nil {
    77  		return whisperOfflineErr
    78  	}
    79  	return api.whisper.SetMaxMessageLength(val)
    80  }
    81  
    82  // SetMinimumPoW sets the minimal PoW required by this node
    83  func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error {
    84  	if api.whisper == nil {
    85  		return whisperOfflineErr
    86  	}
    87  	return api.whisper.SetMinimumPoW(val)
    88  }
    89  
    90  // AllowP2PMessagesFromPeer marks specific peer trusted, which will allow it
    91  // to send historic (expired) messages.
    92  func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error {
    93  	if api.whisper == nil {
    94  		return whisperOfflineErr
    95  	}
    96  	n, err := discover.ParseNode(enode)
    97  	if err != nil {
    98  		return errors.New("failed to parse enode of trusted peer: " + err.Error())
    99  	}
   100  	return api.whisper.AllowP2PMessagesFromPeer(n.ID[:])
   101  }
   102  
   103  // HasKeyPair checks if the whisper node is configured with the private key
   104  // of the specified public pair.
   105  func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) {
   106  	if api.whisper == nil {
   107  		return false, whisperOfflineErr
   108  	}
   109  	return api.whisper.HasKeyPair(id), nil
   110  }
   111  
   112  // DeleteKeyPair deletes the specifies key if it exists.
   113  func (api *PublicWhisperAPI) DeleteKeyPair(id string) (bool, error) {
   114  	if api.whisper == nil {
   115  		return false, whisperOfflineErr
   116  	}
   117  	return api.whisper.DeleteKeyPair(id), nil
   118  }
   119  
   120  // NewKeyPair generates a new cryptographic identity for the client, and injects
   121  // it into the known identities for message decryption.
   122  func (api *PublicWhisperAPI) NewKeyPair() (string, error) {
   123  	if api.whisper == nil {
   124  		return "", whisperOfflineErr
   125  	}
   126  	return api.whisper.NewKeyPair()
   127  }
   128  
   129  // GetPublicKey returns the public key for identity id
   130  func (api *PublicWhisperAPI) GetPublicKey(id string) (hexutil.Bytes, error) {
   131  	if api.whisper == nil {
   132  		return nil, whisperOfflineErr
   133  	}
   134  	key, err := api.whisper.GetPrivateKey(id)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	return crypto.FromECDSAPub(&key.PublicKey), nil
   139  }
   140  
   141  // GetPrivateKey returns the private key for identity id
   142  func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) {
   143  	if api.whisper == nil {
   144  		return "", whisperOfflineErr
   145  	}
   146  	key, err := api.whisper.GetPrivateKey(id)
   147  	if err != nil {
   148  		return "", err
   149  	}
   150  	return common.ToHex(crypto.FromECDSA(key)), nil
   151  }
   152  
   153  // GenerateSymmetricKey generates a random symmetric key and stores it under id,
   154  // which is then returned. Will be used in the future for session key exchange.
   155  func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) {
   156  	if api.whisper == nil {
   157  		return "", whisperOfflineErr
   158  	}
   159  	return api.whisper.GenerateSymKey()
   160  }
   161  
   162  // AddSymmetricKeyDirect stores the key, and returns its id.
   163  func (api *PublicWhisperAPI) AddSymmetricKeyDirect(key hexutil.Bytes) (string, error) {
   164  	if api.whisper == nil {
   165  		return "", whisperOfflineErr
   166  	}
   167  	return api.whisper.AddSymKeyDirect(key)
   168  }
   169  
   170  // AddSymmetricKeyFromPassword generates the key from password, stores it, and returns its id.
   171  func (api *PublicWhisperAPI) AddSymmetricKeyFromPassword(password string) (string, error) {
   172  	if api.whisper == nil {
   173  		return "", whisperOfflineErr
   174  	}
   175  	return api.whisper.AddSymKeyFromPassword(password)
   176  }
   177  
   178  // HasSymmetricKey returns true if there is a key associated with the given id.
   179  // Otherwise returns false.
   180  func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) {
   181  	if api.whisper == nil {
   182  		return false, whisperOfflineErr
   183  	}
   184  	res := api.whisper.HasSymKey(id)
   185  	return res, nil
   186  }
   187  
   188  // GetSymmetricKey returns the symmetric key associated with the given id.
   189  func (api *PublicWhisperAPI) GetSymmetricKey(name string) (hexutil.Bytes, error) {
   190  	if api.whisper == nil {
   191  		return nil, whisperOfflineErr
   192  	}
   193  	b, err := api.whisper.GetSymKey(name)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	return b, nil
   198  }
   199  
   200  // DeleteSymmetricKey deletes the key associated with the name string if it exists.
   201  func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) {
   202  	if api.whisper == nil {
   203  		return false, whisperOfflineErr
   204  	}
   205  	res := api.whisper.DeleteSymKey(name)
   206  	return res, nil
   207  }
   208  
   209  // Subscribe creates and registers a new filter to watch for inbound whisper messages.
   210  // Returns the ID of the newly created filter.
   211  func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
   212  	if api.whisper == nil {
   213  		return "", whisperOfflineErr
   214  	}
   215  
   216  	filter := Filter{
   217  		PoW:      args.MinPoW,
   218  		Messages: make(map[common.Hash]*ReceivedMessage),
   219  		AllowP2P: args.AllowP2P,
   220  	}
   221  
   222  	var err error
   223  	for i, bt := range args.Topics {
   224  		if len(bt) == 0 || len(bt) > 4 {
   225  			return "", errors.New(fmt.Sprintf("subscribe: topic %d has wrong size: %d", i, len(bt)))
   226  		}
   227  		filter.Topics = append(filter.Topics, bt)
   228  	}
   229  
   230  	if err = ValidateKeyID(args.Key); err != nil {
   231  		return "", errors.New("subscribe: " + err.Error())
   232  	}
   233  
   234  	if len(args.Sig) > 0 {
   235  		sb := common.FromHex(args.Sig)
   236  		if sb == nil {
   237  			return "", errors.New("subscribe: sig parameter is invalid")
   238  		}
   239  		filter.Src = crypto.ToECDSAPub(sb)
   240  		if !ValidatePublicKey(filter.Src) {
   241  			return "", errors.New("subscribe: invalid 'sig' field")
   242  		}
   243  	}
   244  
   245  	if args.Symmetric {
   246  		if len(args.Topics) == 0 {
   247  			return "", errors.New("subscribe: at least one topic must be specified with symmetric encryption")
   248  		}
   249  		symKey, err := api.whisper.GetSymKey(args.Key)
   250  		if err != nil {
   251  			return "", errors.New("subscribe: invalid key ID")
   252  		}
   253  		if !validateSymmetricKey(symKey) {
   254  			return "", errors.New("subscribe: retrieved key is invalid")
   255  		}
   256  		filter.KeySym = symKey
   257  		filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
   258  	} else {
   259  		filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key)
   260  		if err != nil {
   261  			return "", errors.New("subscribe: invalid key ID")
   262  		}
   263  		if filter.KeyAsym == nil {
   264  			return "", errors.New("subscribe: non-existent identity provided")
   265  		}
   266  	}
   267  
   268  	return api.whisper.Subscribe(&filter)
   269  }
   270  
   271  // Unsubscribe disables and removes an existing filter.
   272  func (api *PublicWhisperAPI) Unsubscribe(id string) {
   273  	api.whisper.Unsubscribe(id)
   274  }
   275  
   276  // GetSubscriptionMessages retrieves all the new messages matched by the corresponding
   277  // subscription filter since the last retrieval.
   278  func (api *PublicWhisperAPI) GetNewSubscriptionMessages(id string) []*WhisperMessage {
   279  	f := api.whisper.GetFilter(id)
   280  	if f != nil {
   281  		newMail := f.Retrieve()
   282  		return toWhisperMessages(newMail)
   283  	}
   284  	return toWhisperMessages(nil)
   285  }
   286  
   287  // GetMessages retrieves all the floating messages that match a specific subscription filter.
   288  // It is likely to be called once per session, right after Subscribe call.
   289  func (api *PublicWhisperAPI) GetFloatingMessages(id string) []*WhisperMessage {
   290  	all := api.whisper.Messages(id)
   291  	return toWhisperMessages(all)
   292  }
   293  
   294  // toWhisperMessages converts a Whisper message to a RPC whisper message.
   295  func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage {
   296  	msgs := make([]*WhisperMessage, len(messages))
   297  	for i, msg := range messages {
   298  		msgs[i] = NewWhisperMessage(msg)
   299  	}
   300  	return msgs
   301  }
   302  
   303  // Post creates a whisper message and injects it into the network for distribution.
   304  func (api *PublicWhisperAPI) Post(args PostArgs) error {
   305  	if api.whisper == nil {
   306  		return whisperOfflineErr
   307  	}
   308  
   309  	var err error
   310  	params := MessageParams{
   311  		TTL:      args.TTL,
   312  		WorkTime: args.PowTime,
   313  		PoW:      args.PowTarget,
   314  		Payload:  args.Payload,
   315  		Padding:  args.Padding,
   316  	}
   317  
   318  	if len(args.Key) == 0 {
   319  		return errors.New("post: key is missing")
   320  	}
   321  
   322  	if len(args.Sig) > 0 {
   323  		params.Src, err = api.whisper.GetPrivateKey(args.Sig)
   324  		if err != nil {
   325  			return err
   326  		}
   327  		if params.Src == nil {
   328  			return errors.New("post: empty identity")
   329  		}
   330  	}
   331  
   332  	if len(args.Topic) == TopicLength {
   333  		params.Topic = BytesToTopic(args.Topic)
   334  	} else if len(args.Topic) != 0 {
   335  		return errors.New(fmt.Sprintf("post: wrong topic size %d", len(args.Topic)))
   336  	}
   337  
   338  	if args.Type == "sym" {
   339  		if err = ValidateKeyID(args.Key); err != nil {
   340  			return err
   341  		}
   342  		params.KeySym, err = api.whisper.GetSymKey(args.Key)
   343  		if err != nil {
   344  			return err
   345  		}
   346  		if !validateSymmetricKey(params.KeySym) {
   347  			return errors.New("post: key for symmetric encryption is invalid")
   348  		}
   349  		if len(params.Topic) == 0 {
   350  			return errors.New("post: topic is missing for symmetric encryption")
   351  		}
   352  	} else if args.Type == "asym" {
   353  		kb := common.FromHex(args.Key)
   354  		if kb == nil {
   355  			return errors.New("post: public key for asymmetric encryption is invalid")
   356  		}
   357  		params.Dst = crypto.ToECDSAPub(kb)
   358  		if !ValidatePublicKey(params.Dst) {
   359  			return errors.New("post: public key for asymmetric encryption is invalid")
   360  		}
   361  	} else {
   362  		return errors.New("post: wrong type (sym/asym)")
   363  	}
   364  
   365  	// encrypt and send
   366  	message, err := NewSentMessage(&params)
   367  	if err != nil {
   368  		return err
   369  	}
   370  	envelope, err := message.Wrap(&params)
   371  	if err != nil {
   372  		return err
   373  	}
   374  	if envelope.size() > api.whisper.maxMsgLength {
   375  		return errors.New("post: message is too big")
   376  	}
   377  
   378  	if len(args.TargetPeer) != 0 {
   379  		n, err := discover.ParseNode(args.TargetPeer)
   380  		if err != nil {
   381  			return errors.New("post: failed to parse enode of target peer: " + err.Error())
   382  		}
   383  		return api.whisper.SendP2PMessage(n.ID[:], envelope)
   384  	} else if args.PowTarget < api.whisper.minPoW {
   385  		return errors.New("post: target PoW is less than minimum PoW, the message can not be sent")
   386  	}
   387  
   388  	return api.whisper.Send(envelope)
   389  }
   390  
   391  type PostArgs struct {
   392  	Type       string        `json:"type"`       // "sym"/"asym" (symmetric or asymmetric)
   393  	TTL        uint32        `json:"ttl"`        // time-to-live in seconds
   394  	Sig        string        `json:"sig"`        // id of the signing key
   395  	Key        string        `json:"key"`        // key id (in case of sym) or public key (in case of asym)
   396  	Topic      hexutil.Bytes `json:"topic"`      // topic (4 bytes)
   397  	Padding    hexutil.Bytes `json:"padding"`    // optional padding bytes
   398  	Payload    hexutil.Bytes `json:"payload"`    // payload to be encrypted
   399  	PowTime    uint32        `json:"powTime"`    // maximal time in seconds to be spent on PoW
   400  	PowTarget  float64       `json:"powTarget"`  // minimal PoW required for this message
   401  	TargetPeer string        `json:"targetPeer"` // peer id (for p2p message only)
   402  }
   403  
   404  type WhisperFilterArgs struct {
   405  	Symmetric bool     // encryption type
   406  	Key       string   // id of the key to be used for decryption
   407  	Sig       string   // public key of the sender to be verified
   408  	MinPoW    float64  // minimal PoW requirement
   409  	Topics    [][]byte // list of topics (up to 4 bytes each) to match
   410  	AllowP2P  bool     // indicates wheather direct p2p messages are allowed for this filter
   411  }
   412  
   413  // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
   414  // JSON message blob into a WhisperFilterArgs structure.
   415  func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
   416  	// Unmarshal the JSON message and sanity check
   417  	var obj struct {
   418  		Type     string        `json:"type"`
   419  		Key      string        `json:"key"`
   420  		Sig      string        `json:"sig"`
   421  		MinPoW   float64       `json:"minPoW"`
   422  		Topics   []interface{} `json:"topics"`
   423  		AllowP2P bool          `json:"allowP2P"`
   424  	}
   425  	if err := json.Unmarshal(b, &obj); err != nil {
   426  		return err
   427  	}
   428  
   429  	switch obj.Type {
   430  	case "sym":
   431  		args.Symmetric = true
   432  	case "asym":
   433  		args.Symmetric = false
   434  	default:
   435  		return errors.New("wrong type (sym/asym)")
   436  	}
   437  
   438  	args.Key = obj.Key
   439  	args.Sig = obj.Sig
   440  	args.MinPoW = obj.MinPoW
   441  	args.AllowP2P = obj.AllowP2P
   442  
   443  	// Construct the topic array
   444  	if obj.Topics != nil {
   445  		topics := make([]string, len(obj.Topics))
   446  		for i, field := range obj.Topics {
   447  			switch value := field.(type) {
   448  			case string:
   449  				topics[i] = value
   450  			case nil:
   451  				return fmt.Errorf("topic[%d] is empty", i)
   452  			default:
   453  				return fmt.Errorf("topic[%d] is not a string", i)
   454  			}
   455  		}
   456  		topicsDecoded := make([][]byte, len(topics))
   457  		for j, s := range topics {
   458  			x := common.FromHex(s)
   459  			if x == nil || len(x) > TopicLength {
   460  				return fmt.Errorf("topic[%d] is invalid", j)
   461  			}
   462  			topicsDecoded[j] = x
   463  		}
   464  		args.Topics = topicsDecoded
   465  	}
   466  
   467  	return nil
   468  }
   469  
   470  // WhisperMessage is the RPC representation of a whisper message.
   471  type WhisperMessage struct {
   472  	Topic     string  `json:"topic"`
   473  	Payload   string  `json:"payload"`
   474  	Padding   string  `json:"padding"`
   475  	Src       string  `json:"sig"`
   476  	Dst       string  `json:"recipientPublicKey"`
   477  	Timestamp uint32  `json:"timestamp"`
   478  	TTL       uint32  `json:"ttl"`
   479  	PoW       float64 `json:"pow"`
   480  	Hash      string  `json:"hash"`
   481  }
   482  
   483  // NewWhisperMessage converts an internal message into an API version.
   484  func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
   485  	msg := WhisperMessage{
   486  		Payload:   common.ToHex(message.Payload),
   487  		Padding:   common.ToHex(message.Padding),
   488  		Timestamp: message.Sent,
   489  		TTL:       message.TTL,
   490  		PoW:       message.PoW,
   491  		Hash:      common.ToHex(message.EnvelopeHash.Bytes()),
   492  	}
   493  
   494  	if len(message.Topic) == TopicLength {
   495  		msg.Topic = common.ToHex(message.Topic[:])
   496  	}
   497  	if message.Dst != nil {
   498  		b := crypto.FromECDSAPub(message.Dst)
   499  		if b != nil {
   500  			msg.Dst = common.ToHex(b)
   501  		}
   502  	}
   503  	if isMessageSigned(message.Raw[0]) {
   504  		b := crypto.FromECDSAPub(message.SigToPubKey())
   505  		if b != nil {
   506  			msg.Src = common.ToHex(b)
   507  		}
   508  	}
   509  	return &msg
   510  }