github.com/ylsgit/go-ethereum@v1.6.5/whisper/whisperv5/filter.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  	"crypto/ecdsa"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/log"
    26  )
    27  
    28  type Filter struct {
    29  	Src        *ecdsa.PublicKey  // Sender of the message
    30  	KeyAsym    *ecdsa.PrivateKey // Private Key of recipient
    31  	KeySym     []byte            // Key associated with the Topic
    32  	Topics     [][]byte          // Topics to filter messages with
    33  	PoW        float64           // Proof of work as described in the Whisper spec
    34  	AllowP2P   bool              // Indicates whether this filter is interested in direct peer-to-peer messages
    35  	SymKeyHash common.Hash       // The Keccak256Hash of the symmetric key, needed for optimization
    36  
    37  	Messages map[common.Hash]*ReceivedMessage
    38  	mutex    sync.RWMutex
    39  }
    40  
    41  type Filters struct {
    42  	watchers map[string]*Filter
    43  	whisper  *Whisper
    44  	mutex    sync.RWMutex
    45  }
    46  
    47  func NewFilters(w *Whisper) *Filters {
    48  	return &Filters{
    49  		watchers: make(map[string]*Filter),
    50  		whisper:  w,
    51  	}
    52  }
    53  
    54  func (fs *Filters) Install(watcher *Filter) (string, error) {
    55  	if watcher.Messages == nil {
    56  		watcher.Messages = make(map[common.Hash]*ReceivedMessage)
    57  	}
    58  
    59  	id, err := GenerateRandomID()
    60  	if err != nil {
    61  		return "", err
    62  	}
    63  
    64  	fs.mutex.Lock()
    65  	defer fs.mutex.Unlock()
    66  
    67  	if fs.watchers[id] != nil {
    68  		return "", fmt.Errorf("failed to generate unique ID")
    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  			watcher.Trigger(msg)
   123  		}
   124  	}
   125  }
   126  
   127  func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
   128  	if f.MatchEnvelope(env) {
   129  		msg := env.Open(f)
   130  		if msg != nil {
   131  			return msg
   132  		} else {
   133  			log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
   134  		}
   135  	} else {
   136  		log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
   137  	}
   138  	return nil
   139  }
   140  
   141  func (f *Filter) expectsAsymmetricEncryption() bool {
   142  	return f.KeyAsym != nil
   143  }
   144  
   145  func (f *Filter) expectsSymmetricEncryption() bool {
   146  	return f.KeySym != nil
   147  }
   148  
   149  func (f *Filter) Trigger(msg *ReceivedMessage) {
   150  	f.mutex.Lock()
   151  	defer f.mutex.Unlock()
   152  
   153  	if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
   154  		f.Messages[msg.EnvelopeHash] = msg
   155  	}
   156  }
   157  
   158  func (f *Filter) Retrieve() (all []*ReceivedMessage) {
   159  	f.mutex.Lock()
   160  	defer f.mutex.Unlock()
   161  
   162  	all = make([]*ReceivedMessage, 0, len(f.Messages))
   163  	for _, msg := range f.Messages {
   164  		all = append(all, msg)
   165  	}
   166  	f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
   167  	return all
   168  }
   169  
   170  func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
   171  	if f.PoW > 0 && msg.PoW < f.PoW {
   172  		return false
   173  	}
   174  	if f.Src != nil && !IsPubKeyEqual(msg.Src, f.Src) {
   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) > 4 {
   215  		bt = bt[:4]
   216  	}
   217  
   218  	for j, b := range bt {
   219  		if topic[j] != b {
   220  			return false
   221  		}
   222  	}
   223  	return true
   224  }
   225  
   226  func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
   227  	if !ValidatePublicKey(a) {
   228  		return false
   229  	} else if !ValidatePublicKey(b) {
   230  		return false
   231  	}
   232  	// the curve is always the same, just compare the points
   233  	return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
   234  }