github.com/zhiqiangxu/go-ethereum@v1.9.16-0.20210824055606-be91cfdebc48/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/zhiqiangxu/go-ethereum/common" 25 "github.com/zhiqiangxu/go-ethereum/crypto" 26 "github.com/zhiqiangxu/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) expectsAsymmetricEncryption() bool { 195 return f.KeyAsym != nil 196 } 197 198 func (f *Filter) expectsSymmetricEncryption() bool { 199 return f.KeySym != nil 200 } 201 202 // Trigger adds a yet-unknown message to the filter's list of 203 // received messages. 204 func (f *Filter) Trigger(msg *ReceivedMessage) { 205 f.mutex.Lock() 206 defer f.mutex.Unlock() 207 208 if _, exist := f.Messages[msg.EnvelopeHash]; !exist { 209 f.Messages[msg.EnvelopeHash] = msg 210 } 211 } 212 213 // Retrieve will return the list of all received messages associated 214 // to a filter. 215 func (f *Filter) Retrieve() (all []*ReceivedMessage) { 216 f.mutex.Lock() 217 defer f.mutex.Unlock() 218 219 all = make([]*ReceivedMessage, 0, len(f.Messages)) 220 for _, msg := range f.Messages { 221 all = append(all, msg) 222 } 223 224 f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages 225 return all 226 } 227 228 // MatchMessage checks if the filter matches an already decrypted 229 // message (i.e. a Message that has already been handled by 230 // MatchEnvelope when checked by a previous filter). 231 // Topics are not checked here, since this is done by topic matchers. 232 func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { 233 if f.PoW > 0 && msg.PoW < f.PoW { 234 return false 235 } 236 237 if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { 238 return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) 239 } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { 240 return f.SymKeyHash == msg.SymKeyHash 241 } 242 return false 243 } 244 245 // MatchEnvelope checks if it's worth decrypting the message. If 246 // it returns `true`, client code is expected to attempt decrypting 247 // the message and subsequently call MatchMessage. 248 // Topics are not checked here, since this is done by topic matchers. 249 func (f *Filter) MatchEnvelope(envelope *Envelope) bool { 250 return f.PoW <= 0 || envelope.pow >= f.PoW 251 } 252 253 // IsPubKeyEqual checks that two public keys are equal 254 func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { 255 if !ValidatePublicKey(a) { 256 return false 257 } else if !ValidatePublicKey(b) { 258 return false 259 } 260 // the curve is always the same, just compare the points 261 return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 262 }