github.com/samgwo/go-ethereum@v1.8.2-0.20180302101319-49bcb5fbd55e/whisper/whisperv6/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 whisperv6 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "sync" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/crypto" 26 "github.com/ethereum/go-ethereum/log" 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 id string // unique identifier 39 40 Messages map[common.Hash]*ReceivedMessage 41 mutex sync.RWMutex 42 } 43 44 // Filters represents a collection of filters 45 type Filters struct { 46 watchers map[string]*Filter 47 48 topicMatcher map[TopicType]map[*Filter]struct{} // map a topic to the filters that are interested in being notified when a message matches that topic 49 allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is 50 51 whisper *Whisper 52 mutex sync.RWMutex 53 } 54 55 // NewFilters returns a newly created filter collection 56 func NewFilters(w *Whisper) *Filters { 57 return &Filters{ 58 watchers: make(map[string]*Filter), 59 topicMatcher: make(map[TopicType]map[*Filter]struct{}), 60 allTopicsMatcher: make(map[*Filter]struct{}), 61 whisper: w, 62 } 63 } 64 65 // Install will add a new filter to the filter collection 66 func (fs *Filters) Install(watcher *Filter) (string, error) { 67 if watcher.KeySym != nil && watcher.KeyAsym != nil { 68 return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys") 69 } 70 71 if watcher.Messages == nil { 72 watcher.Messages = make(map[common.Hash]*ReceivedMessage) 73 } 74 75 id, err := GenerateRandomID() 76 if err != nil { 77 return "", err 78 } 79 80 fs.mutex.Lock() 81 defer fs.mutex.Unlock() 82 83 if fs.watchers[id] != nil { 84 return "", fmt.Errorf("failed to generate unique ID") 85 } 86 87 if watcher.expectsSymmetricEncryption() { 88 watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) 89 } 90 91 watcher.id = id 92 fs.watchers[id] = watcher 93 fs.addTopicMatcher(watcher) 94 return id, err 95 } 96 97 // Uninstall will remove a filter whose id has been specified from 98 // the filter collection 99 func (fs *Filters) Uninstall(id string) bool { 100 fs.mutex.Lock() 101 defer fs.mutex.Unlock() 102 if fs.watchers[id] != nil { 103 fs.removeFromTopicMatchers(fs.watchers[id]) 104 delete(fs.watchers, id) 105 return true 106 } 107 return false 108 } 109 110 // addTopicMatcher adds a filter to the topic matchers. 111 // If the filter's Topics array is empty, it will be tried on every topic. 112 // Otherwise, it will be tried on the topics specified. 113 func (fs *Filters) addTopicMatcher(watcher *Filter) { 114 if len(watcher.Topics) == 0 { 115 fs.allTopicsMatcher[watcher] = struct{}{} 116 } else { 117 for _, t := range watcher.Topics { 118 topic := BytesToTopic(t) 119 if fs.topicMatcher[topic] == nil { 120 fs.topicMatcher[topic] = make(map[*Filter]struct{}) 121 } 122 fs.topicMatcher[topic][watcher] = struct{}{} 123 } 124 } 125 } 126 127 // removeFromTopicMatchers removes a filter from the topic matchers 128 func (fs *Filters) removeFromTopicMatchers(watcher *Filter) { 129 delete(fs.allTopicsMatcher, watcher) 130 for _, topic := range watcher.Topics { 131 delete(fs.topicMatcher[BytesToTopic(topic)], watcher) 132 } 133 } 134 135 // getWatchersByTopic returns a slice containing the filters that 136 // match a specific topic 137 func (fs *Filters) getWatchersByTopic(topic TopicType) []*Filter { 138 res := make([]*Filter, 0, len(fs.allTopicsMatcher)) 139 for watcher := range fs.allTopicsMatcher { 140 res = append(res, watcher) 141 } 142 for watcher := range fs.topicMatcher[topic] { 143 res = append(res, watcher) 144 } 145 return res 146 } 147 148 // Get returns a filter from the collection with a specific ID 149 func (fs *Filters) Get(id string) *Filter { 150 fs.mutex.RLock() 151 defer fs.mutex.RUnlock() 152 return fs.watchers[id] 153 } 154 155 // NotifyWatchers notifies any filter that has declared interest 156 // for the envelope's topic. 157 func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { 158 var msg *ReceivedMessage 159 160 fs.mutex.RLock() 161 defer fs.mutex.RUnlock() 162 163 candidates := fs.getWatchersByTopic(env.Topic) 164 for _, watcher := range candidates { 165 if p2pMessage && !watcher.AllowP2P { 166 log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), watcher.id)) 167 continue 168 } 169 170 var match bool 171 if msg != nil { 172 match = watcher.MatchMessage(msg) 173 } else { 174 match = watcher.MatchEnvelope(env) 175 if match { 176 msg = env.Open(watcher) 177 if msg == nil { 178 log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", watcher.id) 179 } 180 } else { 181 log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", watcher.id) 182 } 183 } 184 185 if match && msg != nil { 186 log.Trace("processing message: decrypted", "hash", env.Hash().Hex()) 187 if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) { 188 watcher.Trigger(msg) 189 } 190 } 191 } 192 } 193 194 func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage { 195 if f.MatchEnvelope(env) { 196 msg := env.Open(f) 197 if msg != nil { 198 return msg 199 } 200 201 log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex()) 202 } else { 203 log.Trace("processing envelope: does not match", "hash", env.Hash().Hex()) 204 } 205 return nil 206 } 207 208 func (f *Filter) expectsAsymmetricEncryption() bool { 209 return f.KeyAsym != nil 210 } 211 212 func (f *Filter) expectsSymmetricEncryption() bool { 213 return f.KeySym != nil 214 } 215 216 // Trigger adds a yet-unknown message to the filter's list of 217 // received messages. 218 func (f *Filter) Trigger(msg *ReceivedMessage) { 219 f.mutex.Lock() 220 defer f.mutex.Unlock() 221 222 if _, exist := f.Messages[msg.EnvelopeHash]; !exist { 223 f.Messages[msg.EnvelopeHash] = msg 224 } 225 } 226 227 // Retrieve will return the list of all received messages associated 228 // to a filter. 229 func (f *Filter) Retrieve() (all []*ReceivedMessage) { 230 f.mutex.Lock() 231 defer f.mutex.Unlock() 232 233 all = make([]*ReceivedMessage, 0, len(f.Messages)) 234 for _, msg := range f.Messages { 235 all = append(all, msg) 236 } 237 238 f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages 239 return all 240 } 241 242 // MatchMessage checks if the filter matches an already decrypted 243 // message (i.e. a Message that has already been handled by 244 // MatchEnvelope when checked by a previous filter). 245 // Topics are not checked here, since this is done by topic matchers. 246 func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { 247 if f.PoW > 0 && msg.PoW < f.PoW { 248 return false 249 } 250 251 if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { 252 return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) 253 } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { 254 return f.SymKeyHash == msg.SymKeyHash 255 } 256 return false 257 } 258 259 // MatchEnvelope checks if it's worth decrypting the message. If 260 // it returns `true`, client code is expected to attempt decrypting 261 // the message and subsequently call MatchMessage. 262 // Topics are not checked here, since this is done by topic matchers. 263 func (f *Filter) MatchEnvelope(envelope *Envelope) bool { 264 return f.PoW <= 0 || envelope.pow >= f.PoW 265 } 266 267 func matchSingleTopic(topic TopicType, bt []byte) bool { 268 if len(bt) > TopicLength { 269 bt = bt[:TopicLength] 270 } 271 272 if len(bt) < TopicLength { 273 return false 274 } 275 276 for j, b := range bt { 277 if topic[j] != b { 278 return false 279 } 280 } 281 return true 282 } 283 284 // IsPubKeyEqual checks that two public keys are equal 285 func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { 286 if !ValidatePublicKey(a) { 287 return false 288 } else if !ValidatePublicKey(b) { 289 return false 290 } 291 // the curve is always the same, just compare the points 292 return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 293 }