github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/whisper/whisperv5/filter.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package whisperv5
    13  
    14  import (
    15  	"crypto/ecdsa"
    16  	"fmt"
    17  	"sync"
    18  
    19  	"github.com/Sberex/go-sberex/common"
    20  	"github.com/Sberex/go-sberex/crypto"
    21  	"github.com/Sberex/go-sberex/log"
    22  )
    23  
    24  type Filter struct {
    25  	Src        *ecdsa.PublicKey  // Sender of the message
    26  	KeyAsym    *ecdsa.PrivateKey // Private Key of recipient
    27  	KeySym     []byte            // Key associated with the Topic
    28  	Topics     [][]byte          // Topics to filter messages with
    29  	PoW        float64           // Proof of work as described in the Whisper spec
    30  	AllowP2P   bool              // Indicates whether this filter is interested in direct peer-to-peer messages
    31  	SymKeyHash common.Hash       // The Keccak256Hash of the symmetric key, needed for optimization
    32  
    33  	Messages map[common.Hash]*ReceivedMessage
    34  	mutex    sync.RWMutex
    35  }
    36  
    37  type Filters struct {
    38  	watchers map[string]*Filter
    39  	whisper  *Whisper
    40  	mutex    sync.RWMutex
    41  }
    42  
    43  func NewFilters(w *Whisper) *Filters {
    44  	return &Filters{
    45  		watchers: make(map[string]*Filter),
    46  		whisper:  w,
    47  	}
    48  }
    49  
    50  func (fs *Filters) Install(watcher *Filter) (string, error) {
    51  	if watcher.Messages == nil {
    52  		watcher.Messages = make(map[common.Hash]*ReceivedMessage)
    53  	}
    54  
    55  	id, err := GenerateRandomID()
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  
    60  	fs.mutex.Lock()
    61  	defer fs.mutex.Unlock()
    62  
    63  	if fs.watchers[id] != nil {
    64  		return "", fmt.Errorf("failed to generate unique ID")
    65  	}
    66  
    67  	if watcher.expectsSymmetricEncryption() {
    68  		watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
    69  	}
    70  
    71  	fs.watchers[id] = watcher
    72  	return id, err
    73  }
    74  
    75  func (fs *Filters) Uninstall(id string) bool {
    76  	fs.mutex.Lock()
    77  	defer fs.mutex.Unlock()
    78  	if fs.watchers[id] != nil {
    79  		delete(fs.watchers, id)
    80  		return true
    81  	}
    82  	return false
    83  }
    84  
    85  func (fs *Filters) Get(id string) *Filter {
    86  	fs.mutex.RLock()
    87  	defer fs.mutex.RUnlock()
    88  	return fs.watchers[id]
    89  }
    90  
    91  func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
    92  	var msg *ReceivedMessage
    93  
    94  	fs.mutex.RLock()
    95  	defer fs.mutex.RUnlock()
    96  
    97  	i := -1 // only used for logging info
    98  	for _, watcher := range fs.watchers {
    99  		i++
   100  		if p2pMessage && !watcher.AllowP2P {
   101  			log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
   102  			continue
   103  		}
   104  
   105  		var match bool
   106  		if msg != nil {
   107  			match = watcher.MatchMessage(msg)
   108  		} else {
   109  			match = watcher.MatchEnvelope(env)
   110  			if match {
   111  				msg = env.Open(watcher)
   112  				if msg == nil {
   113  					log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
   114  				}
   115  			} else {
   116  				log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
   117  			}
   118  		}
   119  
   120  		if match && msg != nil {
   121  			log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
   122  			if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
   123  				watcher.Trigger(msg)
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
   130  	if f.MatchEnvelope(env) {
   131  		msg := env.Open(f)
   132  		if msg != nil {
   133  			return msg
   134  		} else {
   135  			log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
   136  		}
   137  	} else {
   138  		log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
   139  	}
   140  	return nil
   141  }
   142  
   143  func (f *Filter) expectsAsymmetricEncryption() bool {
   144  	return f.KeyAsym != nil
   145  }
   146  
   147  func (f *Filter) expectsSymmetricEncryption() bool {
   148  	return f.KeySym != nil
   149  }
   150  
   151  func (f *Filter) Trigger(msg *ReceivedMessage) {
   152  	f.mutex.Lock()
   153  	defer f.mutex.Unlock()
   154  
   155  	if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
   156  		f.Messages[msg.EnvelopeHash] = msg
   157  	}
   158  }
   159  
   160  func (f *Filter) Retrieve() (all []*ReceivedMessage) {
   161  	f.mutex.Lock()
   162  	defer f.mutex.Unlock()
   163  
   164  	all = make([]*ReceivedMessage, 0, len(f.Messages))
   165  	for _, msg := range f.Messages {
   166  		all = append(all, msg)
   167  	}
   168  
   169  	f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
   170  	return all
   171  }
   172  
   173  func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
   174  	if f.PoW > 0 && msg.PoW < f.PoW {
   175  		return false
   176  	}
   177  
   178  	if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
   179  		return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic)
   180  	} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
   181  		return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic)
   182  	}
   183  	return false
   184  }
   185  
   186  func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
   187  	if f.PoW > 0 && envelope.pow < f.PoW {
   188  		return false
   189  	}
   190  
   191  	if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() {
   192  		return f.MatchTopic(envelope.Topic)
   193  	} else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() {
   194  		return f.MatchTopic(envelope.Topic)
   195  	}
   196  	return false
   197  }
   198  
   199  func (f *Filter) MatchTopic(topic TopicType) bool {
   200  	if len(f.Topics) == 0 {
   201  		// any topic matches
   202  		return true
   203  	}
   204  
   205  	for _, bt := range f.Topics {
   206  		if matchSingleTopic(topic, bt) {
   207  			return true
   208  		}
   209  	}
   210  	return false
   211  }
   212  
   213  func matchSingleTopic(topic TopicType, bt []byte) bool {
   214  	if len(bt) > TopicLength {
   215  		bt = bt[:TopicLength]
   216  	}
   217  
   218  	if len(bt) < TopicLength {
   219  		return false
   220  	}
   221  
   222  	for j, b := range bt {
   223  		if topic[j] != b {
   224  			return false
   225  		}
   226  	}
   227  	return true
   228  }
   229  
   230  func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
   231  	if !ValidatePublicKey(a) {
   232  		return false
   233  	} else if !ValidatePublicKey(b) {
   234  		return false
   235  	}
   236  	// the curve is always the same, just compare the points
   237  	return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
   238  }