github.com/aquanetwork/aquachain@v1.7.8/opt/whisper/whisperv6/filter.go (about)

     1  // Copyright 2016 The aquachain Authors
     2  // This file is part of the aquachain library.
     3  //
     4  // The aquachain 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 aquachain 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 aquachain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package whisperv6
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"gitlab.com/aquachain/aquachain/common"
    25  	"gitlab.com/aquachain/aquachain/common/log"
    26  	"gitlab.com/aquachain/aquachain/crypto"
    27  )
    28  
    29  // Filter represents a Whisper message filter
    30  type Filter struct {
    31  	Src        *ecdsa.PublicKey  // Sender of the message
    32  	KeyAsym    *ecdsa.PrivateKey // Private Key of recipient
    33  	KeySym     []byte            // Key associated with the Topic
    34  	Topics     [][]byte          // Topics to filter messages with
    35  	PoW        float64           // Proof of work as described in the Whisper spec
    36  	AllowP2P   bool              // Indicates whether this filter is interested in direct peer-to-peer messages
    37  	SymKeyHash common.Hash       // The Keccak256Hash of the symmetric key, needed for optimization
    38  
    39  	Messages map[common.Hash]*ReceivedMessage
    40  	mutex    sync.RWMutex
    41  }
    42  
    43  // Filters represents a collection of filters
    44  type Filters struct {
    45  	watchers map[string]*Filter
    46  	whisper  *Whisper
    47  	mutex    sync.RWMutex
    48  }
    49  
    50  // NewFilters returns a newly created filter collection
    51  func NewFilters(w *Whisper) *Filters {
    52  	return &Filters{
    53  		watchers: make(map[string]*Filter),
    54  		whisper:  w,
    55  	}
    56  }
    57  
    58  // Install will add a new filter to the filter collection
    59  func (fs *Filters) Install(watcher *Filter) (string, error) {
    60  	if watcher.KeySym != nil && watcher.KeyAsym != nil {
    61  		return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys")
    62  	}
    63  
    64  	if watcher.Messages == nil {
    65  		watcher.Messages = make(map[common.Hash]*ReceivedMessage)
    66  	}
    67  
    68  	id, err := GenerateRandomID()
    69  	if err != nil {
    70  		return "", err
    71  	}
    72  
    73  	fs.mutex.Lock()
    74  	defer fs.mutex.Unlock()
    75  
    76  	if fs.watchers[id] != nil {
    77  		return "", fmt.Errorf("failed to generate unique ID")
    78  	}
    79  
    80  	if watcher.expectsSymmetricEncryption() {
    81  		watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
    82  	}
    83  
    84  	fs.watchers[id] = watcher
    85  	return id, err
    86  }
    87  
    88  // Uninstall will remove a filter whose id has been specified from
    89  // the filter collection
    90  func (fs *Filters) Uninstall(id string) bool {
    91  	fs.mutex.Lock()
    92  	defer fs.mutex.Unlock()
    93  	if fs.watchers[id] != nil {
    94  		delete(fs.watchers, id)
    95  		return true
    96  	}
    97  	return false
    98  }
    99  
   100  // Get returns a filter from the collection with a specific ID
   101  func (fs *Filters) Get(id string) *Filter {
   102  	fs.mutex.RLock()
   103  	defer fs.mutex.RUnlock()
   104  	return fs.watchers[id]
   105  }
   106  
   107  // NotifyWatchers notifies any filter that has declared interest
   108  // for the envelope's topic.
   109  func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
   110  	var msg *ReceivedMessage
   111  
   112  	fs.mutex.RLock()
   113  	defer fs.mutex.RUnlock()
   114  
   115  	i := -1 // only used for logging info
   116  	for _, watcher := range fs.watchers {
   117  		i++
   118  		if p2pMessage && !watcher.AllowP2P {
   119  			log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
   120  			continue
   121  		}
   122  
   123  		var match bool
   124  		if msg != nil {
   125  			match = watcher.MatchMessage(msg)
   126  		} else {
   127  			match = watcher.MatchEnvelope(env)
   128  			if match {
   129  				msg = env.Open(watcher)
   130  				if msg == nil {
   131  					log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
   132  				}
   133  			} else {
   134  				log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
   135  			}
   136  		}
   137  
   138  		if match && msg != nil {
   139  			log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
   140  			if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
   141  				watcher.Trigger(msg)
   142  			}
   143  		}
   144  	}
   145  }
   146  
   147  func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
   148  	if f.MatchEnvelope(env) {
   149  		msg := env.Open(f)
   150  		if msg != nil {
   151  			return msg
   152  		}
   153  
   154  		log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
   155  	} else {
   156  		log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
   157  	}
   158  	return nil
   159  }
   160  
   161  func (f *Filter) expectsAsymmetricEncryption() bool {
   162  	return f.KeyAsym != nil
   163  }
   164  
   165  func (f *Filter) expectsSymmetricEncryption() bool {
   166  	return f.KeySym != nil
   167  }
   168  
   169  // Trigger adds a yet-unknown message to the filter's list of
   170  // received messages.
   171  func (f *Filter) Trigger(msg *ReceivedMessage) {
   172  	f.mutex.Lock()
   173  	defer f.mutex.Unlock()
   174  
   175  	if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
   176  		f.Messages[msg.EnvelopeHash] = msg
   177  	}
   178  }
   179  
   180  // Retrieve will return the list of all received messages associated
   181  // to a filter.
   182  func (f *Filter) Retrieve() (all []*ReceivedMessage) {
   183  	f.mutex.Lock()
   184  	defer f.mutex.Unlock()
   185  
   186  	all = make([]*ReceivedMessage, 0, len(f.Messages))
   187  	for _, msg := range f.Messages {
   188  		all = append(all, msg)
   189  	}
   190  
   191  	f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
   192  	return all
   193  }
   194  
   195  // MatchMessage checks if the filter matches an already decrypted
   196  // message (i.e. a Message that has already been handled by
   197  // MatchEnvelope when checked by a previous filter)
   198  func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
   199  	if f.PoW > 0 && msg.PoW < f.PoW {
   200  		return false
   201  	}
   202  
   203  	if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
   204  		return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic)
   205  	} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
   206  		return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic)
   207  	}
   208  	return false
   209  }
   210  
   211  // MatchEnvelope checks if it's worth decrypting the message. If
   212  // it returns `true`, client code is expected to attempt decrypting
   213  // the message and subsequently call MatchMessage.
   214  func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
   215  	if f.PoW > 0 && envelope.pow < f.PoW {
   216  		return false
   217  	}
   218  
   219  	return f.MatchTopic(envelope.Topic)
   220  }
   221  
   222  // MatchTopic checks that the filter captures a given topic.
   223  func (f *Filter) MatchTopic(topic TopicType) bool {
   224  	if len(f.Topics) == 0 {
   225  		// any topic matches
   226  		return true
   227  	}
   228  
   229  	for _, bt := range f.Topics {
   230  		if matchSingleTopic(topic, bt) {
   231  			return true
   232  		}
   233  	}
   234  	return false
   235  }
   236  
   237  func matchSingleTopic(topic TopicType, bt []byte) bool {
   238  	if len(bt) > TopicLength {
   239  		bt = bt[:TopicLength]
   240  	}
   241  
   242  	if len(bt) < TopicLength {
   243  		return false
   244  	}
   245  
   246  	for j, b := range bt {
   247  		if topic[j] != b {
   248  			return false
   249  		}
   250  	}
   251  	return true
   252  }
   253  
   254  // IsPubKeyEqual checks that two public keys are equal
   255  func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
   256  	if !ValidatePublicKey(a) {
   257  		return false
   258  	} else if !ValidatePublicKey(b) {
   259  		return false
   260  	}
   261  	// the curve is always the same, just compare the points
   262  	return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
   263  }