| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // Copyright 2014 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 10:04:59 +02:00
										 |  |  | package filters | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 	"math" | 
					
						
							| 
									
										
										
										
											2016-02-13 01:40:44 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 11:19:23 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2015-08-30 10:04:59 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core" | 
					
						
							| 
									
										
										
										
											2015-03-26 12:06:14 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2015-08-31 17:09:50 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/event" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rpc" | 
					
						
							|  |  |  | 	"golang.org/x/net/context" | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | type Backend interface { | 
					
						
							|  |  |  | 	ChainDb() ethdb.Database | 
					
						
							|  |  |  | 	EventMux() *event.TypeMux | 
					
						
							|  |  |  | 	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) | 
					
						
							|  |  |  | 	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // Filter can be used to retrieve and filter logs | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | type Filter struct { | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	backend   Backend | 
					
						
							|  |  |  | 	useMipMap bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-13 01:40:44 +01:00
										 |  |  | 	created time.Time | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 	db         ethdb.Database | 
					
						
							|  |  |  | 	begin, end int64 | 
					
						
							|  |  |  | 	addresses  []common.Address | 
					
						
							|  |  |  | 	topics     [][]common.Hash | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // New creates a new filter which uses a bloom filter on blocks to figure out whether | 
					
						
							|  |  |  | // a particular block is interesting or not. | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | func New(backend Backend, useMipMap bool) *Filter { | 
					
						
							|  |  |  | 	return &Filter{ | 
					
						
							|  |  |  | 		backend:   backend, | 
					
						
							|  |  |  | 		useMipMap: useMipMap, | 
					
						
							|  |  |  | 		db:        backend.ChainDb(), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // SetBeginBlock sets the earliest block for filtering. | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | // -1 = latest block (i.e., the current block) | 
					
						
							|  |  |  | // hash = particular hash from-to | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | func (f *Filter) SetBeginBlock(begin int64) { | 
					
						
							|  |  |  | 	f.begin = begin | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // SetEndBlock sets the latest block for filtering. | 
					
						
							|  |  |  | func (f *Filter) SetEndBlock(end int64) { | 
					
						
							|  |  |  | 	f.end = end | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // SetAddresses matches only logs that are generated from addresses that are included | 
					
						
							|  |  |  | // in the given addresses. | 
					
						
							|  |  |  | func (f *Filter) SetAddresses(addr []common.Address) { | 
					
						
							|  |  |  | 	f.addresses = addr | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // SetTopics matches only logs that have topics matching the given topics. | 
					
						
							|  |  |  | func (f *Filter) SetTopics(topics [][]common.Hash) { | 
					
						
							|  |  |  | 	f.topics = topics | 
					
						
							| 
									
										
										
										
											2014-08-14 17:02:39 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | // Run filters logs with the current parameters set | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | func (f *Filter) Find(ctx context.Context) ([]Log, error) { | 
					
						
							|  |  |  | 	head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) | 
					
						
							|  |  |  | 	if head == nil { | 
					
						
							|  |  |  | 		return nil, nil | 
					
						
							| 
									
										
										
										
											2016-07-26 16:37:04 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	headBlockNumber := head.Number.Uint64() | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var beginBlockNo uint64 = uint64(f.begin) | 
					
						
							|  |  |  | 	if f.begin == -1 { | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 		beginBlockNo = headBlockNumber | 
					
						
							| 
									
										
										
										
											2014-09-26 13:32:54 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	var endBlockNo uint64 = uint64(f.end) | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	if f.end == -1 { | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 		endBlockNo = headBlockNumber | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 	// if no addresses are present we can't make use of fast search which | 
					
						
							|  |  |  | 	// uses the mipmap bloom filters to check for fast inclusion and uses | 
					
						
							|  |  |  | 	// higher range probability in order to ensure at least a false positive | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	if !f.useMipMap || len(f.addresses) == 0 { | 
					
						
							|  |  |  | 		return f.getLogs(ctx, beginBlockNo, endBlockNo) | 
					
						
							| 
									
										
										
										
											2015-08-31 17:09:50 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	return f.mipFind(beginBlockNo, endBlockNo, 0), nil | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-06-09 13:22:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 	level := core.MIPMapLevels[depth] | 
					
						
							|  |  |  | 	// normalise numerator so we can work in level specific batches and | 
					
						
							|  |  |  | 	// work with the proper range checks | 
					
						
							|  |  |  | 	for num := start / level * level; num <= end; num += level { | 
					
						
							|  |  |  | 		// find addresses in bloom filters | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		bloom := core.GetMipmapBloom(f.db, num, level) | 
					
						
							|  |  |  | 		for _, addr := range f.addresses { | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 			if bloom.TestBytes(addr[:]) { | 
					
						
							|  |  |  | 				// range check normalised values and make sure that | 
					
						
							|  |  |  | 				// we're resolving the correct range instead of the | 
					
						
							|  |  |  | 				// normalised values. | 
					
						
							|  |  |  | 				start := uint64(math.Max(float64(num), float64(start))) | 
					
						
							|  |  |  | 				end := uint64(math.Min(float64(num+level-1), float64(end))) | 
					
						
							|  |  |  | 				if depth+1 == len(core.MIPMapLevels) { | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 					l, _ := f.getLogs(context.Background(), start, end) | 
					
						
							|  |  |  | 					logs = append(logs, l...) | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 					logs = append(logs, f.mipFind(start, end, depth+1)...) | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				// break so we don't check the same range for each | 
					
						
							|  |  |  | 				// possible address. Checks on multiple addresses | 
					
						
							|  |  |  | 				// are handled further down the stack. | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return logs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { | 
					
						
							| 
									
										
										
										
											2015-10-12 17:58:51 +02:00
										 |  |  | 	for i := start; i <= end; i++ { | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 		header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) | 
					
						
							|  |  |  | 		if header == nil || err != nil { | 
					
						
							|  |  |  | 			return logs, err | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Use bloom filtering to see if this block is interesting given the | 
					
						
							|  |  |  | 		// current parameters | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 		if f.bloomFilter(header.Bloom) { | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 			// Get the logs of the block | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 			receipts, err := f.backend.GetReceipts(ctx, header.Hash()) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var unfiltered []Log | 
					
						
							| 
									
										
										
										
											2015-08-30 10:04:59 +02:00
										 |  |  | 			for _, receipt := range receipts { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 				rl := make([]Log, len(receipt.Logs)) | 
					
						
							|  |  |  | 				for i, l := range receipt.Logs { | 
					
						
							|  |  |  | 					rl[i] = Log{l, false} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				unfiltered = append(unfiltered, rl...) | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 			logs = append(logs, filterLogs(unfiltered, f.addresses, f.topics)...) | 
					
						
							| 
									
										
										
										
											2014-08-15 00:24:37 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	return logs, nil | 
					
						
							| 
									
										
										
										
											2014-08-15 00:24:37 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-17 11:19:23 +01:00
										 |  |  | func includes(addresses []common.Address, a common.Address) bool { | 
					
						
							| 
									
										
										
										
											2014-08-15 16:19:10 +02:00
										 |  |  | 	for _, addr := range addresses { | 
					
						
							| 
									
										
										
										
											2015-09-01 09:19:45 +02:00
										 |  |  | 		if addr == a { | 
					
						
							|  |  |  | 			return true | 
					
						
							| 
									
										
										
										
											2014-08-15 16:19:10 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-01 09:19:45 +02:00
										 |  |  | 	return false | 
					
						
							| 
									
										
										
										
											2014-08-15 16:19:10 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | func filterLogs(logs []Log, addresses []common.Address, topics [][]common.Hash) []Log { | 
					
						
							|  |  |  | 	var ret []Log | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 	// Filter the logs for interesting stuff | 
					
						
							| 
									
										
										
										
											2015-02-04 17:28:54 -08:00
										 |  |  | Logs: | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 	for _, log := range logs { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		if len(addresses) > 0 && !includes(addresses, log.Address) { | 
					
						
							| 
									
										
										
										
											2014-08-15 00:24:37 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		logTopics := make([]common.Hash, len(topics)) | 
					
						
							| 
									
										
										
										
											2015-04-08 17:14:58 +02:00
										 |  |  | 		copy(logTopics, log.Topics) | 
					
						
							| 
									
										
										
										
											2015-03-01 19:08:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		// If the to filtered topics is greater than the amount of topics in logs, skip. | 
					
						
							|  |  |  | 		if len(topics) > len(log.Topics) { | 
					
						
							| 
									
										
										
										
											2015-04-24 13:36:34 +02:00
										 |  |  | 			continue Logs | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		for i, topics := range topics { | 
					
						
							| 
									
										
										
										
											2015-04-24 13:36:34 +02:00
										 |  |  | 			var match bool | 
					
						
							| 
									
										
										
										
											2015-03-01 19:08:26 +01:00
										 |  |  | 			for _, topic := range topics { | 
					
						
							| 
									
										
										
										
											2015-04-21 12:00:57 +02:00
										 |  |  | 				// common.Hash{} is a match all (wildcard) | 
					
						
							|  |  |  | 				if (topic == common.Hash{}) || log.Topics[i] == topic { | 
					
						
							| 
									
										
										
										
											2015-03-01 19:08:26 +01:00
										 |  |  | 					match = true | 
					
						
							| 
									
										
										
										
											2015-04-24 13:36:34 +02:00
										 |  |  | 					break | 
					
						
							| 
									
										
										
										
											2015-03-01 19:08:26 +01:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-08-15 16:19:10 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-04-24 13:36:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if !match { | 
					
						
							|  |  |  | 				continue Logs | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-08-15 16:19:10 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 		ret = append(ret, log) | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 	return ret | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 05:51:29 +02:00
										 |  |  | func (f *Filter) bloomFilter(bloom types.Bloom) bool { | 
					
						
							|  |  |  | 	return bloomFilter(bloom, f.addresses, f.topics) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { | 
					
						
							|  |  |  | 	if len(addresses) > 0 { | 
					
						
							| 
									
										
										
										
											2015-02-17 16:12:55 +01:00
										 |  |  | 		var included bool | 
					
						
							| 
									
										
										
										
											2016-10-14 05:51:29 +02:00
										 |  |  | 		for _, addr := range addresses { | 
					
						
							|  |  |  | 			if types.BloomLookup(bloom, addr) { | 
					
						
							| 
									
										
										
										
											2015-02-17 16:12:55 +01:00
										 |  |  | 				included = true | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !included { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 05:51:29 +02:00
										 |  |  | 	for _, sub := range topics { | 
					
						
							| 
									
										
										
										
											2015-03-01 19:08:26 +01:00
										 |  |  | 		var included bool | 
					
						
							|  |  |  | 		for _, topic := range sub { | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 			if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) { | 
					
						
							| 
									
										
										
										
											2015-03-01 19:08:26 +01:00
										 |  |  | 				included = true | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !included { | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 			return false | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-28 10:23:18 +01:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2014-08-11 16:23:17 +02:00
										 |  |  | } |