github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/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  	mathrand "math/rand"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/hexutil"
    27  	"github.com/ethereum/go-ethereum/crypto"
    28  	"github.com/ethereum/go-ethereum/log"
    29  )
    30  
    31  var whisperOffLineErr = errors.New("whisper is offline")
    32  
    33  // PublicWhisperAPI provides the whisper RPC service.
    34  type PublicWhisperAPI struct {
    35  	whisper *Whisper
    36  }
    37  
    38  // NewPublicWhisperAPI create a new RPC whisper service.
    39  func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
    40  	return &PublicWhisperAPI{whisper: w}
    41  }
    42  
    43  // Start starts the Whisper worker threads.
    44  func (api *PublicWhisperAPI) Start() error {
    45  	if api.whisper == nil {
    46  		return whisperOffLineErr
    47  	}
    48  	return api.whisper.Start(nil)
    49  }
    50  
    51  // Stop stops the Whisper worker threads.
    52  func (api *PublicWhisperAPI) Stop() error {
    53  	if api.whisper == nil {
    54  		return whisperOffLineErr
    55  	}
    56  	return api.whisper.Stop()
    57  }
    58  
    59  // Version returns the Whisper version this node offers.
    60  func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) {
    61  	if api.whisper == nil {
    62  		return 0, whisperOffLineErr
    63  	}
    64  	return hexutil.Uint(api.whisper.Version()), nil
    65  }
    66  
    67  // Stats returns the Whisper statistics for diagnostics.
    68  func (api *PublicWhisperAPI) Stats() (string, error) {
    69  	if api.whisper == nil {
    70  		return "", whisperOffLineErr
    71  	}
    72  	return api.whisper.Stats(), nil
    73  }
    74  
    75  // MarkPeerTrusted marks specific peer trusted, which will allow it
    76  // to send historic (expired) messages.
    77  func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error {
    78  	if api.whisper == nil {
    79  		return whisperOffLineErr
    80  	}
    81  	return api.whisper.MarkPeerTrusted(peerID)
    82  }
    83  
    84  // RequestHistoricMessages requests the peer to deliver the old (expired) messages.
    85  // data contains parameters (time frame, payment details, etc.), required
    86  // by the remote email-like server. Whisper is not aware about the data format,
    87  // it will just forward the raw data to the server.
    88  //func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error {
    89  //	if api.whisper == nil {
    90  //		return whisperOffLineErr
    91  //	}
    92  //	return api.whisper.RequestHistoricMessages(peerID, data)
    93  //}
    94  
    95  // HasIdentity checks if the whisper node is configured with the private key
    96  // of the specified public pair.
    97  func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) {
    98  	if api.whisper == nil {
    99  		return false, whisperOffLineErr
   100  	}
   101  	return api.whisper.HasIdentity(identity), nil
   102  }
   103  
   104  // DeleteIdentity deletes the specifies key if it exists.
   105  func (api *PublicWhisperAPI) DeleteIdentity(identity string) error {
   106  	if api.whisper == nil {
   107  		return whisperOffLineErr
   108  	}
   109  	api.whisper.DeleteIdentity(identity)
   110  	return nil
   111  }
   112  
   113  // NewIdentity generates a new cryptographic identity for the client, and injects
   114  // it into the known identities for message decryption.
   115  func (api *PublicWhisperAPI) NewIdentity() (string, error) {
   116  	if api.whisper == nil {
   117  		return "", whisperOffLineErr
   118  	}
   119  	identity := api.whisper.NewIdentity()
   120  	return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil
   121  }
   122  
   123  // GenerateSymKey generates a random symmetric key and stores it under
   124  // the 'name' id. Will be used in the future for session key exchange.
   125  func (api *PublicWhisperAPI) GenerateSymKey(name string) error {
   126  	if api.whisper == nil {
   127  		return whisperOffLineErr
   128  	}
   129  	return api.whisper.GenerateSymKey(name)
   130  }
   131  
   132  // AddSymKey stores the key under the 'name' id.
   133  func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error {
   134  	if api.whisper == nil {
   135  		return whisperOffLineErr
   136  	}
   137  	return api.whisper.AddSymKey(name, key)
   138  }
   139  
   140  // HasSymKey returns true if there is a key associated with the name string.
   141  // Otherwise returns false.
   142  func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) {
   143  	if api.whisper == nil {
   144  		return false, whisperOffLineErr
   145  	}
   146  	res := api.whisper.HasSymKey(name)
   147  	return res, nil
   148  }
   149  
   150  // DeleteSymKey deletes the key associated with the name string if it exists.
   151  func (api *PublicWhisperAPI) DeleteSymKey(name string) error {
   152  	if api.whisper == nil {
   153  		return whisperOffLineErr
   154  	}
   155  	api.whisper.DeleteSymKey(name)
   156  	return nil
   157  }
   158  
   159  // NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages.
   160  // Returns the ID of the newly created Filter.
   161  func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) {
   162  	if api.whisper == nil {
   163  		return "", whisperOffLineErr
   164  	}
   165  
   166  	filter := Filter{
   167  		Src:       crypto.ToECDSAPub(common.FromHex(args.From)),
   168  		KeySym:    api.whisper.GetSymKey(args.KeyName),
   169  		PoW:       args.PoW,
   170  		Messages:  make(map[common.Hash]*ReceivedMessage),
   171  		AcceptP2P: args.AcceptP2P,
   172  	}
   173  	if len(filter.KeySym) > 0 {
   174  		filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
   175  	}
   176  	filter.Topics = append(filter.Topics, args.Topics...)
   177  
   178  	if len(args.Topics) == 0 && len(args.KeyName) != 0 {
   179  		info := "NewFilter: at least one topic must be specified"
   180  		log.Error(fmt.Sprintf(info))
   181  		return "", errors.New(info)
   182  	}
   183  
   184  	if len(args.KeyName) != 0 && len(filter.KeySym) == 0 {
   185  		info := "NewFilter: key was not found by name: " + args.KeyName
   186  		log.Error(fmt.Sprintf(info))
   187  		return "", errors.New(info)
   188  	}
   189  
   190  	if len(args.To) == 0 && len(filter.KeySym) == 0 {
   191  		info := "NewFilter: filter must contain either symmetric or asymmetric key"
   192  		log.Error(fmt.Sprintf(info))
   193  		return "", errors.New(info)
   194  	}
   195  
   196  	if len(args.To) != 0 && len(filter.KeySym) != 0 {
   197  		info := "NewFilter: filter must not contain both symmetric and asymmetric key"
   198  		log.Error(fmt.Sprintf(info))
   199  		return "", errors.New(info)
   200  	}
   201  
   202  	if len(args.To) > 0 {
   203  		dst := crypto.ToECDSAPub(common.FromHex(args.To))
   204  		if !ValidatePublicKey(dst) {
   205  			info := "NewFilter: Invalid 'To' address"
   206  			log.Error(fmt.Sprintf(info))
   207  			return "", errors.New(info)
   208  		}
   209  		filter.KeyAsym = api.whisper.GetIdentity(string(args.To))
   210  		if filter.KeyAsym == nil {
   211  			info := "NewFilter: non-existent identity provided"
   212  			log.Error(fmt.Sprintf(info))
   213  			return "", errors.New(info)
   214  		}
   215  	}
   216  
   217  	if len(args.From) > 0 {
   218  		if !ValidatePublicKey(filter.Src) {
   219  			info := "NewFilter: Invalid 'From' address"
   220  			log.Error(fmt.Sprintf(info))
   221  			return "", errors.New(info)
   222  		}
   223  	}
   224  
   225  	return api.whisper.Watch(&filter)
   226  }
   227  
   228  // UninstallFilter disables and removes an existing filter.
   229  func (api *PublicWhisperAPI) UninstallFilter(filterId string) {
   230  	api.whisper.Unwatch(filterId)
   231  }
   232  
   233  // GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval.
   234  func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage {
   235  	f := api.whisper.GetFilter(filterId)
   236  	if f != nil {
   237  		newMail := f.Retrieve()
   238  		return toWhisperMessages(newMail)
   239  	}
   240  	return toWhisperMessages(nil)
   241  }
   242  
   243  // GetMessages retrieves all the known messages that match a specific filter.
   244  func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
   245  	all := api.whisper.Messages(filterId)
   246  	return toWhisperMessages(all)
   247  }
   248  
   249  // toWhisperMessages converts a Whisper message to a RPC whisper message.
   250  func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage {
   251  	msgs := make([]*WhisperMessage, len(messages))
   252  	for i, msg := range messages {
   253  		msgs[i] = NewWhisperMessage(msg)
   254  	}
   255  	return msgs
   256  }
   257  
   258  // Post creates a whisper message and injects it into the network for distribution.
   259  func (api *PublicWhisperAPI) Post(args PostArgs) error {
   260  	if api.whisper == nil {
   261  		return whisperOffLineErr
   262  	}
   263  
   264  	params := MessageParams{
   265  		TTL:      args.TTL,
   266  		Dst:      crypto.ToECDSAPub(common.FromHex(args.To)),
   267  		KeySym:   api.whisper.GetSymKey(args.KeyName),
   268  		Topic:    args.Topic,
   269  		Payload:  args.Payload,
   270  		Padding:  args.Padding,
   271  		WorkTime: args.WorkTime,
   272  		PoW:      args.PoW,
   273  	}
   274  
   275  	if len(args.From) > 0 {
   276  		pub := crypto.ToECDSAPub(common.FromHex(args.From))
   277  		if !ValidatePublicKey(pub) {
   278  			info := "Post: Invalid 'From' address"
   279  			log.Error(fmt.Sprintf(info))
   280  			return errors.New(info)
   281  		}
   282  		params.Src = api.whisper.GetIdentity(string(args.From))
   283  		if params.Src == nil {
   284  			info := "Post: non-existent identity provided"
   285  			log.Error(fmt.Sprintf(info))
   286  			return errors.New(info)
   287  		}
   288  	}
   289  
   290  	filter := api.whisper.GetFilter(args.FilterID)
   291  	if filter == nil && len(args.FilterID) > 0 {
   292  		info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID)
   293  		log.Error(fmt.Sprintf(info))
   294  		return errors.New(info)
   295  	}
   296  
   297  	if filter != nil {
   298  		// get the missing fields from the filter
   299  		if params.KeySym == nil && filter.KeySym != nil {
   300  			params.KeySym = filter.KeySym
   301  		}
   302  		if params.Src == nil && filter.Src != nil {
   303  			params.Src = filter.KeyAsym
   304  		}
   305  		if (params.Topic == TopicType{}) {
   306  			sz := len(filter.Topics)
   307  			if sz < 1 {
   308  				info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID)
   309  				log.Error(fmt.Sprintf(info))
   310  				return errors.New(info)
   311  			} else if sz == 1 {
   312  				params.Topic = filter.Topics[0]
   313  			} else {
   314  				// choose randomly
   315  				rnd := mathrand.Intn(sz)
   316  				params.Topic = filter.Topics[rnd]
   317  			}
   318  		}
   319  	}
   320  
   321  	// validate
   322  	if len(args.KeyName) != 0 && len(params.KeySym) == 0 {
   323  		info := "Post: key was not found by name: " + args.KeyName
   324  		log.Error(fmt.Sprintf(info))
   325  		return errors.New(info)
   326  	}
   327  
   328  	if len(args.To) == 0 && len(params.KeySym) == 0 {
   329  		info := "Post: message must be encrypted either symmetrically or asymmetrically"
   330  		log.Error(fmt.Sprintf(info))
   331  		return errors.New(info)
   332  	}
   333  
   334  	if len(args.To) != 0 && len(params.KeySym) != 0 {
   335  		info := "Post: ambigous encryption method requested"
   336  		log.Error(fmt.Sprintf(info))
   337  		return errors.New(info)
   338  	}
   339  
   340  	if len(args.To) > 0 {
   341  		if !ValidatePublicKey(params.Dst) {
   342  			info := "Post: Invalid 'To' address"
   343  			log.Error(fmt.Sprintf(info))
   344  			return errors.New(info)
   345  		}
   346  	}
   347  
   348  	// encrypt and send
   349  	message := NewSentMessage(&params)
   350  	envelope, err := message.Wrap(&params)
   351  	if err != nil {
   352  		log.Error(fmt.Sprintf(err.Error()))
   353  		return err
   354  	}
   355  	if len(envelope.Data) > MaxMessageLength {
   356  		info := "Post: message is too big"
   357  		log.Error(fmt.Sprintf(info))
   358  		return errors.New(info)
   359  	}
   360  	if (envelope.Topic == TopicType{} && envelope.IsSymmetric()) {
   361  		info := "Post: topic is missing for symmetric encryption"
   362  		log.Error(fmt.Sprintf(info))
   363  		return errors.New(info)
   364  	}
   365  
   366  	if args.PeerID != nil {
   367  		return api.whisper.SendP2PMessage(args.PeerID, envelope)
   368  	}
   369  
   370  	return api.whisper.Send(envelope)
   371  }
   372  
   373  type PostArgs struct {
   374  	TTL      uint32        `json:"ttl"`
   375  	From     string        `json:"from"`
   376  	To       string        `json:"to"`
   377  	KeyName  string        `json:"keyname"`
   378  	Topic    TopicType     `json:"topic"`
   379  	Padding  hexutil.Bytes `json:"padding"`
   380  	Payload  hexutil.Bytes `json:"payload"`
   381  	WorkTime uint32        `json:"worktime"`
   382  	PoW      float64       `json:"pow"`
   383  	FilterID string        `json:"filterID"`
   384  	PeerID   hexutil.Bytes `json:"peerID"`
   385  }
   386  
   387  type WhisperFilterArgs struct {
   388  	To        string      `json:"to"`
   389  	From      string      `json:"from"`
   390  	KeyName   string      `json:"keyname"`
   391  	PoW       float64     `json:"pow"`
   392  	Topics    []TopicType `json:"topics"`
   393  	AcceptP2P bool        `json:"p2p"`
   394  }
   395  
   396  // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
   397  // JSON message blob into a WhisperFilterArgs structure.
   398  func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
   399  	// Unmarshal the JSON message and sanity check
   400  	var obj struct {
   401  		To        string        `json:"to"`
   402  		From      string        `json:"from"`
   403  		KeyName   string        `json:"keyname"`
   404  		PoW       float64       `json:"pow"`
   405  		Topics    []interface{} `json:"topics"`
   406  		AcceptP2P bool          `json:"p2p"`
   407  	}
   408  	if err := json.Unmarshal(b, &obj); err != nil {
   409  		return err
   410  	}
   411  
   412  	args.To = obj.To
   413  	args.From = obj.From
   414  	args.KeyName = obj.KeyName
   415  	args.PoW = obj.PoW
   416  	args.AcceptP2P = obj.AcceptP2P
   417  
   418  	// Construct the topic array
   419  	if obj.Topics != nil {
   420  		topics := make([]string, len(obj.Topics))
   421  		for i, field := range obj.Topics {
   422  			switch value := field.(type) {
   423  			case string:
   424  				topics[i] = value
   425  			case nil:
   426  				return fmt.Errorf("topic[%d] is empty", i)
   427  			default:
   428  				return fmt.Errorf("topic[%d] is not a string", i)
   429  			}
   430  		}
   431  		topicsDecoded := make([]TopicType, len(topics))
   432  		for j, s := range topics {
   433  			x := common.FromHex(s)
   434  			if x == nil || len(x) != TopicLength {
   435  				return fmt.Errorf("topic[%d] is invalid", j)
   436  			}
   437  			topicsDecoded[j] = BytesToTopic(x)
   438  		}
   439  		args.Topics = topicsDecoded
   440  	}
   441  
   442  	return nil
   443  }
   444  
   445  // WhisperMessage is the RPC representation of a whisper message.
   446  type WhisperMessage struct {
   447  	Topic   string  `json:"topic"`
   448  	Payload string  `json:"payload"`
   449  	Padding string  `json:"padding"`
   450  	From    string  `json:"from"`
   451  	To      string  `json:"to"`
   452  	Sent    uint32  `json:"sent"`
   453  	TTL     uint32  `json:"ttl"`
   454  	PoW     float64 `json:"pow"`
   455  	Hash    string  `json:"hash"`
   456  }
   457  
   458  // NewWhisperMessage converts an internal message into an API version.
   459  func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
   460  	msg := WhisperMessage{
   461  		Topic:   common.ToHex(message.Topic[:]),
   462  		Payload: common.ToHex(message.Payload),
   463  		Padding: common.ToHex(message.Padding),
   464  		Sent:    message.Sent,
   465  		TTL:     message.TTL,
   466  		PoW:     message.PoW,
   467  		Hash:    common.ToHex(message.EnvelopeHash.Bytes()),
   468  	}
   469  
   470  	if message.Dst != nil {
   471  		msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst))
   472  	}
   473  	if isMessageSigned(message.Raw[0]) {
   474  		msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey()))
   475  	}
   476  	return &msg
   477  }