github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/whisper.go (about)

     1  // Copyright 2014 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package whisperv2
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/SmartMeshFoundation/Spectrum/common"
    26  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    27  	"github.com/SmartMeshFoundation/Spectrum/crypto/ecies"
    28  	"github.com/SmartMeshFoundation/Spectrum/event/filter"
    29  	"github.com/SmartMeshFoundation/Spectrum/log"
    30  	"github.com/SmartMeshFoundation/Spectrum/p2p"
    31  	"github.com/SmartMeshFoundation/Spectrum/rpc"
    32  
    33  	"gopkg.in/fatih/set.v0"
    34  )
    35  
    36  const (
    37  	statusCode   = 0x00
    38  	messagesCode = 0x01
    39  
    40  	protocolVersion uint64 = 0x02
    41  	protocolName           = "shh"
    42  
    43  	signatureFlag   = byte(1 << 7)
    44  	signatureLength = 65
    45  
    46  	expirationCycle   = 800 * time.Millisecond
    47  	transmissionCycle = 300 * time.Millisecond
    48  )
    49  
    50  const (
    51  	DefaultTTL = 50 * time.Second
    52  	DefaultPoW = 50 * time.Millisecond
    53  )
    54  
    55  type MessageEvent struct {
    56  	To      *ecdsa.PrivateKey
    57  	From    *ecdsa.PublicKey
    58  	Message *Message
    59  }
    60  
    61  // Whisper represents a dark communication interface through the Ethereum
    62  // network, using its very own P2P communication layer.
    63  type Whisper struct {
    64  	protocol p2p.Protocol
    65  	filters  *filter.Filters
    66  
    67  	keys map[string]*ecdsa.PrivateKey
    68  
    69  	messages    map[common.Hash]*Envelope // Pool of messages currently tracked by this node
    70  	expirations map[uint32]*set.SetNonTS  // Message expiration pool (TODO: something lighter)
    71  	poolMu      sync.RWMutex              // Mutex to sync the message and expiration pools
    72  
    73  	peers  map[*peer]struct{} // Set of currently active peers
    74  	peerMu sync.RWMutex       // Mutex to sync the active peer set
    75  
    76  	quit chan struct{}
    77  }
    78  
    79  // New creates a Whisper client ready to communicate through the Ethereum P2P
    80  // network.
    81  func New() *Whisper {
    82  	whisper := &Whisper{
    83  		filters:     filter.New(),
    84  		keys:        make(map[string]*ecdsa.PrivateKey),
    85  		messages:    make(map[common.Hash]*Envelope),
    86  		expirations: make(map[uint32]*set.SetNonTS),
    87  		peers:       make(map[*peer]struct{}),
    88  		quit:        make(chan struct{}),
    89  	}
    90  	whisper.filters.Start()
    91  
    92  	// p2p whisper sub protocol handler
    93  	whisper.protocol = p2p.Protocol{
    94  		Name:    protocolName,
    95  		Version: uint(protocolVersion),
    96  		Length:  2,
    97  		Run:     whisper.handlePeer,
    98  	}
    99  
   100  	return whisper
   101  }
   102  
   103  // APIs returns the RPC descriptors the Whisper implementation offers
   104  func (s *Whisper) APIs() []rpc.API {
   105  	return []rpc.API{
   106  		{
   107  			Namespace: "shh",
   108  			Version:   "1.0",
   109  			Service:   NewPublicWhisperAPI(s),
   110  			Public:    true,
   111  		},
   112  	}
   113  }
   114  
   115  // Protocols returns the whisper sub-protocols ran by this particular client.
   116  func (self *Whisper) Protocols() []p2p.Protocol {
   117  	return []p2p.Protocol{self.protocol}
   118  }
   119  
   120  // Version returns the whisper sub-protocols version number.
   121  func (self *Whisper) Version() uint {
   122  	return self.protocol.Version
   123  }
   124  
   125  // NewIdentity generates a new cryptographic identity for the client, and injects
   126  // it into the known identities for message decryption.
   127  func (self *Whisper) NewIdentity() *ecdsa.PrivateKey {
   128  	key, err := crypto.GenerateKey()
   129  	if err != nil {
   130  		panic(err)
   131  	}
   132  	self.keys[string(crypto.FromECDSAPub(&key.PublicKey))] = key
   133  
   134  	return key
   135  }
   136  
   137  // HasIdentity checks if the the whisper node is configured with the private key
   138  // of the specified public pair.
   139  func (self *Whisper) HasIdentity(key *ecdsa.PublicKey) bool {
   140  	return self.keys[string(crypto.FromECDSAPub(key))] != nil
   141  }
   142  
   143  // GetIdentity retrieves the private key of the specified public identity.
   144  func (self *Whisper) GetIdentity(key *ecdsa.PublicKey) *ecdsa.PrivateKey {
   145  	return self.keys[string(crypto.FromECDSAPub(key))]
   146  }
   147  
   148  // Watch installs a new message handler to run in case a matching packet arrives
   149  // from the whisper network.
   150  func (self *Whisper) Watch(options Filter) int {
   151  	filter := filterer{
   152  		to:      string(crypto.FromECDSAPub(options.To)),
   153  		from:    string(crypto.FromECDSAPub(options.From)),
   154  		matcher: newTopicMatcher(options.Topics...),
   155  		fn: func(data interface{}) {
   156  			options.Fn(data.(*Message))
   157  		},
   158  	}
   159  	return self.filters.Install(filter)
   160  }
   161  
   162  // Unwatch removes an installed message handler.
   163  func (self *Whisper) Unwatch(id int) {
   164  	self.filters.Uninstall(id)
   165  }
   166  
   167  // Send injects a message into the whisper send queue, to be distributed in the
   168  // network in the coming cycles.
   169  func (self *Whisper) Send(envelope *Envelope) error {
   170  	return self.add(envelope)
   171  }
   172  
   173  // Start implements node.Service, starting the background data propagation thread
   174  // of the Whisper protocol.
   175  func (self *Whisper) Start(*p2p.Server) error {
   176  	log.Info("Whisper started")
   177  	go self.update()
   178  	return nil
   179  }
   180  
   181  // Stop implements node.Service, stopping the background data propagation thread
   182  // of the Whisper protocol.
   183  func (self *Whisper) Stop() error {
   184  	close(self.quit)
   185  	log.Info("Whisper stopped")
   186  	return nil
   187  }
   188  
   189  // Messages retrieves all the currently pooled messages matching a filter id.
   190  func (self *Whisper) Messages(id int) []*Message {
   191  	messages := make([]*Message, 0)
   192  	if filter := self.filters.Get(id); filter != nil {
   193  		for _, envelope := range self.messages {
   194  			if message := self.open(envelope); message != nil {
   195  				if self.filters.Match(filter, createFilter(message, envelope.Topics)) {
   196  					messages = append(messages, message)
   197  				}
   198  			}
   199  		}
   200  	}
   201  	return messages
   202  }
   203  
   204  // handlePeer is called by the underlying P2P layer when the whisper sub-protocol
   205  // connection is negotiated.
   206  func (self *Whisper) handlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
   207  	// Create the new peer and start tracking it
   208  	whisperPeer := newPeer(self, peer, rw)
   209  
   210  	self.peerMu.Lock()
   211  	self.peers[whisperPeer] = struct{}{}
   212  	self.peerMu.Unlock()
   213  
   214  	defer func() {
   215  		self.peerMu.Lock()
   216  		delete(self.peers, whisperPeer)
   217  		self.peerMu.Unlock()
   218  	}()
   219  
   220  	// Run the peer handshake and state updates
   221  	if err := whisperPeer.handshake(); err != nil {
   222  		return err
   223  	}
   224  	whisperPeer.start()
   225  	defer whisperPeer.stop()
   226  
   227  	// Read and process inbound messages directly to merge into client-global state
   228  	for {
   229  		// Fetch the next packet and decode the contained envelopes
   230  		packet, err := rw.ReadMsg()
   231  		if err != nil {
   232  			return err
   233  		}
   234  		var envelopes []*Envelope
   235  		if err := packet.Decode(&envelopes); err != nil {
   236  			log.Info(fmt.Sprintf("%v: failed to decode envelope: %v", peer, err))
   237  			continue
   238  		}
   239  		// Inject all envelopes into the internal pool
   240  		for _, envelope := range envelopes {
   241  			if err := self.add(envelope); err != nil {
   242  				// TODO Punish peer here. Invalid envelope.
   243  				log.Debug(fmt.Sprintf("%v: failed to pool envelope: %v", peer, err))
   244  			}
   245  			whisperPeer.mark(envelope)
   246  		}
   247  	}
   248  }
   249  
   250  // add inserts a new envelope into the message pool to be distributed within the
   251  // whisper network. It also inserts the envelope into the expiration pool at the
   252  // appropriate time-stamp.
   253  func (self *Whisper) add(envelope *Envelope) error {
   254  	self.poolMu.Lock()
   255  	defer self.poolMu.Unlock()
   256  
   257  	// short circuit when a received envelope has already expired
   258  	if envelope.Expiry < uint32(time.Now().Unix()) {
   259  		return nil
   260  	}
   261  
   262  	// Insert the message into the tracked pool
   263  	hash := envelope.Hash()
   264  	if _, ok := self.messages[hash]; ok {
   265  		log.Trace(fmt.Sprintf("whisper envelope already cached: %x\n", envelope))
   266  		return nil
   267  	}
   268  	self.messages[hash] = envelope
   269  
   270  	// Insert the message into the expiration pool for later removal
   271  	if self.expirations[envelope.Expiry] == nil {
   272  		self.expirations[envelope.Expiry] = set.NewNonTS()
   273  	}
   274  	if !self.expirations[envelope.Expiry].Has(hash) {
   275  		self.expirations[envelope.Expiry].Add(hash)
   276  
   277  		// Notify the local node of a message arrival
   278  		go self.postEvent(envelope)
   279  	}
   280  	log.Trace(fmt.Sprintf("cached whisper envelope %x\n", envelope))
   281  	return nil
   282  }
   283  
   284  // postEvent opens an envelope with the configured identities and delivers the
   285  // message upstream from application processing.
   286  func (self *Whisper) postEvent(envelope *Envelope) {
   287  	if message := self.open(envelope); message != nil {
   288  		self.filters.Notify(createFilter(message, envelope.Topics), message)
   289  	}
   290  }
   291  
   292  // open tries to decrypt a whisper envelope with all the configured identities,
   293  // returning the decrypted message and the key used to achieve it. If not keys
   294  // are configured, open will return the payload as if non encrypted.
   295  func (self *Whisper) open(envelope *Envelope) *Message {
   296  	// Short circuit if no identity is set, and assume clear-text
   297  	if len(self.keys) == 0 {
   298  		if message, err := envelope.Open(nil); err == nil {
   299  			return message
   300  		}
   301  	}
   302  	// Iterate over the keys and try to decrypt the message
   303  	for _, key := range self.keys {
   304  		message, err := envelope.Open(key)
   305  		if err == nil {
   306  			message.To = &key.PublicKey
   307  			return message
   308  		} else if err == ecies.ErrInvalidPublicKey {
   309  			return message
   310  		}
   311  	}
   312  	// Failed to decrypt, don't return anything
   313  	return nil
   314  }
   315  
   316  // createFilter creates a message filter to check against installed handlers.
   317  func createFilter(message *Message, topics []Topic) filter.Filter {
   318  	matcher := make([][]Topic, len(topics))
   319  	for i, topic := range topics {
   320  		matcher[i] = []Topic{topic}
   321  	}
   322  	return filterer{
   323  		to:      string(crypto.FromECDSAPub(message.To)),
   324  		from:    string(crypto.FromECDSAPub(message.Recover())),
   325  		matcher: newTopicMatcher(matcher...),
   326  	}
   327  }
   328  
   329  // update loops until the lifetime of the whisper node, updating its internal
   330  // state by expiring stale messages from the pool.
   331  func (self *Whisper) update() {
   332  	// Start a ticker to check for expirations
   333  	expire := time.NewTicker(expirationCycle)
   334  
   335  	// Repeat updates until termination is requested
   336  	for {
   337  		select {
   338  		case <-expire.C:
   339  			self.expire()
   340  
   341  		case <-self.quit:
   342  			return
   343  		}
   344  	}
   345  }
   346  
   347  // expire iterates over all the expiration timestamps, removing all stale
   348  // messages from the pools.
   349  func (self *Whisper) expire() {
   350  	self.poolMu.Lock()
   351  	defer self.poolMu.Unlock()
   352  
   353  	now := uint32(time.Now().Unix())
   354  	for then, hashSet := range self.expirations {
   355  		// Short circuit if a future time
   356  		if then > now {
   357  			continue
   358  		}
   359  		// Dump all expired messages and remove timestamp
   360  		hashSet.Each(func(v interface{}) bool {
   361  			delete(self.messages, v.(common.Hash))
   362  			return true
   363  		})
   364  		self.expirations[then].Clear()
   365  	}
   366  }
   367  
   368  // envelopes retrieves all the messages currently pooled by the node.
   369  func (self *Whisper) envelopes() []*Envelope {
   370  	self.poolMu.RLock()
   371  	defer self.poolMu.RUnlock()
   372  
   373  	envelopes := make([]*Envelope, 0, len(self.messages))
   374  	for _, envelope := range self.messages {
   375  		envelopes = append(envelopes, envelope)
   376  	}
   377  	return envelopes
   378  }