@@ -35,6 +35,7 @@ type Filter struct {
35
35
PoW float64 // Proof of work as described in the Whisper spec
36
36
AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
37
37
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
38
+ id string // unique identifier
38
39
39
40
Messages map [common.Hash ]* ReceivedMessage
40
41
mutex sync.RWMutex
@@ -43,15 +44,21 @@ type Filter struct {
43
44
// Filters represents a collection of filters
44
45
type Filters struct {
45
46
watchers map [string ]* Filter
46
- whisper * Whisper
47
- mutex sync.RWMutex
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
48
53
}
49
54
50
55
// NewFilters returns a newly created filter collection
51
56
func NewFilters (w * Whisper ) * Filters {
52
57
return & Filters {
53
- watchers : make (map [string ]* Filter ),
54
- whisper : w ,
58
+ watchers : make (map [string ]* Filter ),
59
+ topicMatcher : make (map [TopicType ]map [* Filter ]struct {}),
60
+ allTopicsMatcher : make (map [* Filter ]struct {}),
61
+ whisper : w ,
55
62
}
56
63
}
57
64
@@ -81,7 +88,9 @@ func (fs *Filters) Install(watcher *Filter) (string, error) {
81
88
watcher .SymKeyHash = crypto .Keccak256Hash (watcher .KeySym )
82
89
}
83
90
91
+ watcher .id = id
84
92
fs .watchers [id ] = watcher
93
+ fs .addTopicMatcher (watcher )
85
94
return id , err
86
95
}
87
96
@@ -91,12 +100,51 @@ func (fs *Filters) Uninstall(id string) bool {
91
100
fs .mutex .Lock ()
92
101
defer fs .mutex .Unlock ()
93
102
if fs .watchers [id ] != nil {
103
+ fs .removeFromTopicMatchers (fs .watchers [id ])
94
104
delete (fs .watchers , id )
95
105
return true
96
106
}
97
107
return false
98
108
}
99
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
+
100
148
// Get returns a filter from the collection with a specific ID
101
149
func (fs * Filters ) Get (id string ) * Filter {
102
150
fs .mutex .RLock ()
@@ -112,11 +160,10 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
112
160
fs .mutex .RLock ()
113
161
defer fs .mutex .RUnlock ()
114
162
115
- i := - 1 // only used for logging info
116
- for _ , watcher := range fs .watchers {
117
- i ++
163
+ candidates := fs .getWatchersByTopic (env .Topic )
164
+ for _ , watcher := range candidates {
118
165
if p2pMessage && ! watcher .AllowP2P {
119
- log .Trace (fmt .Sprintf ("msg [%x], filter [%d ]: p2p messages are not allowed" , env .Hash (), i ))
166
+ log .Trace (fmt .Sprintf ("msg [%x], filter [%s ]: p2p messages are not allowed" , env .Hash (), watcher . id ))
120
167
continue
121
168
}
122
169
@@ -128,10 +175,10 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
128
175
if match {
129
176
msg = env .Open (watcher )
130
177
if msg == nil {
131
- log .Trace ("processing message: failed to open" , "message" , env .Hash ().Hex (), "filter" , i )
178
+ log .Trace ("processing message: failed to open" , "message" , env .Hash ().Hex (), "filter" , watcher . id )
132
179
}
133
180
} else {
134
- log .Trace ("processing message: does not match" , "message" , env .Hash ().Hex (), "filter" , i )
181
+ log .Trace ("processing message: does not match" , "message" , env .Hash ().Hex (), "filter" , watcher . id )
135
182
}
136
183
}
137
184
@@ -194,44 +241,27 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) {
194
241
195
242
// MatchMessage checks if the filter matches an already decrypted
196
243
// message (i.e. a Message that has already been handled by
197
- // MatchEnvelope when checked by a previous filter)
244
+ // MatchEnvelope when checked by a previous filter).
245
+ // Topics are not checked here, since this is done by topic matchers.
198
246
func (f * Filter ) MatchMessage (msg * ReceivedMessage ) bool {
199
247
if f .PoW > 0 && msg .PoW < f .PoW {
200
248
return false
201
249
}
202
250
203
251
if f .expectsAsymmetricEncryption () && msg .isAsymmetricEncryption () {
204
- return IsPubKeyEqual (& f .KeyAsym .PublicKey , msg .Dst ) && f . MatchTopic ( msg . Topic )
252
+ return IsPubKeyEqual (& f .KeyAsym .PublicKey , msg .Dst )
205
253
} else if f .expectsSymmetricEncryption () && msg .isSymmetricEncryption () {
206
- return f .SymKeyHash == msg .SymKeyHash && f . MatchTopic ( msg . Topic )
254
+ return f .SymKeyHash == msg .SymKeyHash
207
255
}
208
256
return false
209
257
}
210
258
211
259
// MatchEnvelope checks if it's worth decrypting the message. If
212
260
// it returns `true`, client code is expected to attempt decrypting
213
261
// the message and subsequently call MatchMessage.
262
+ // Topics are not checked here, since this is done by topic matchers.
214
263
func (f * Filter ) MatchEnvelope (envelope * Envelope ) bool {
215
- if f .PoW > 0 && envelope .pow < f .PoW {
216
- return false
217
- }
218
-
219
- return f .MatchTopic (envelope .Topic )
220
- }
221
-
222
- // MatchTopic checks that the filter captures a given topic.
223
- func (f * Filter ) MatchTopic (topic TopicType ) bool {
224
- if len (f .Topics ) == 0 {
225
- // any topic matches
226
- return true
227
- }
228
-
229
- for _ , bt := range f .Topics {
230
- if matchSingleTopic (topic , bt ) {
231
- return true
232
- }
233
- }
234
- return false
264
+ return f .PoW <= 0 || envelope .pow >= f .PoW
235
265
}
236
266
237
267
func matchSingleTopic (topic TopicType , bt []byte ) bool {
0 commit comments