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 }