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