github.com/beyonderyue/gochain@v2.2.26+incompatible/eth/filters/filter.go (about) 1 // Copyright 2014 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 filters 18 19 import ( 20 "context" 21 "errors" 22 "math/big" 23 24 "github.com/gochain-io/gochain/common" 25 "github.com/gochain-io/gochain/core" 26 "github.com/gochain-io/gochain/core/bloombits" 27 "github.com/gochain-io/gochain/core/types" 28 "github.com/gochain-io/gochain/event" 29 "github.com/gochain-io/gochain/rpc" 30 ) 31 32 type Backend interface { 33 ChainDb() common.Database 34 EventMux() *event.TypeMux 35 HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) 36 HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) 37 GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) 38 GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) 39 40 SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent, name string) 41 UnsubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) 42 SubscribeChainEvent(ch chan<- core.ChainEvent, name string) 43 UnsubscribeChainEvent(ch chan<- core.ChainEvent) 44 SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent, name string) 45 UnsubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) 46 SubscribeLogsEvent(ch chan<- []*types.Log, name string) 47 UnsubscribeLogsEvent(ch chan<- []*types.Log) 48 49 BloomStatus() (uint64, uint64) 50 ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) 51 } 52 53 // Filter can be used to retrieve and filter logs. 54 type Filter struct { 55 backend Backend 56 57 db common.Database 58 addresses []common.Address 59 topics [][]common.Hash 60 61 block common.Hash // Block hash if filtering a single block 62 begin, end int64 // Range interval if filtering multiple blocks 63 64 matcher *bloombits.Matcher 65 } 66 67 // NewRangeFilter creates a new filter which uses a bloom filter on blocks to 68 // figure out whether a particular block is interesting or not. 69 func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { 70 // Flatten the address and topic filter clauses into a single bloombits filter 71 // system. Since the bloombits are not positional, nil topics are permitted, 72 // which get flattened into a nil byte slice. 73 var filters [][][]byte 74 if len(addresses) > 0 { 75 filter := make([][]byte, len(addresses)) 76 for i, address := range addresses { 77 filter[i] = address.Bytes() 78 } 79 filters = append(filters, filter) 80 } 81 for _, topicList := range topics { 82 filter := make([][]byte, len(topicList)) 83 for i, topic := range topicList { 84 filter[i] = topic.Bytes() 85 } 86 filters = append(filters, filter) 87 } 88 size, _ := backend.BloomStatus() 89 90 // Create a generic filter and convert it into a range filter 91 filter := newFilter(backend, addresses, topics) 92 93 filter.matcher = bloombits.NewMatcher(size, filters) 94 filter.begin = begin 95 filter.end = end 96 97 return filter 98 } 99 100 // NewBlockFilter creates a new filter which directly inspects the contents of 101 // a block to figure out whether it is interesting or not. 102 func NewBlockFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { 103 // Create a generic filter and convert it into a block filter 104 filter := newFilter(backend, addresses, topics) 105 filter.block = block 106 return filter 107 } 108 109 // newFilter creates a generic filter that can either filter based on a block hash, 110 // or based on range queries. The search criteria needs to be explicitly set. 111 func newFilter(backend Backend, addresses []common.Address, topics [][]common.Hash) *Filter { 112 return &Filter{ 113 backend: backend, 114 addresses: addresses, 115 topics: topics, 116 db: backend.ChainDb(), 117 } 118 } 119 120 // Logs searches the blockchain for matching log entries, returning all from the 121 // first block that contains matches, updating the start of the filter accordingly. 122 func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { 123 // If we're doing singleton block filtering, execute and return 124 if f.block != (common.Hash{}) { 125 header, err := f.backend.HeaderByHash(ctx, f.block) 126 if err != nil { 127 return nil, err 128 } 129 if header == nil { 130 return nil, errors.New("unknown block") 131 } 132 return f.blockLogs(ctx, header) 133 } 134 // Figure out the limits of the filter range 135 header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 136 if header == nil { 137 return nil, nil 138 } 139 head := header.Number.Uint64() 140 141 if f.begin == -1 { 142 f.begin = int64(head) 143 } 144 end := uint64(f.end) 145 if f.end == -1 { 146 end = head 147 } 148 // Gather all indexed logs, and finish with non indexed ones 149 var ( 150 logs []*types.Log 151 err error 152 ) 153 size, sections := f.backend.BloomStatus() 154 if indexed := sections * size; indexed > uint64(f.begin) { 155 if indexed > end { 156 logs, err = f.indexedLogs(ctx, end) 157 } else { 158 logs, err = f.indexedLogs(ctx, indexed-1) 159 } 160 if err != nil { 161 return logs, err 162 } 163 } 164 rest, err := f.unindexedLogs(ctx, end) 165 logs = append(logs, rest...) 166 return logs, err 167 } 168 169 // indexedLogs returns the logs matching the filter criteria based on the bloom 170 // bits indexed available locally or via the network. 171 func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 172 // Create a matcher session and request servicing from the backend 173 matches := make(chan uint64, 64) 174 175 session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) 176 if err != nil { 177 return nil, err 178 } 179 defer session.Close() 180 181 f.backend.ServiceFilter(ctx, session) 182 183 // Iterate over the matches until exhausted or context closed 184 var logs []*types.Log 185 186 for { 187 select { 188 case number, ok := <-matches: 189 // Abort if all matches have been fulfilled 190 if !ok { 191 err := session.Error() 192 if err == nil { 193 f.begin = int64(end) + 1 194 } 195 return logs, err 196 } 197 f.begin = int64(number) + 1 198 199 // Retrieve the suggested block and pull any truly matching logs 200 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) 201 if header == nil || err != nil { 202 return logs, err 203 } 204 found, err := f.checkMatches(ctx, header) 205 if err != nil { 206 return logs, err 207 } 208 logs = append(logs, found...) 209 210 case <-ctx.Done(): 211 return logs, ctx.Err() 212 } 213 } 214 } 215 216 // indexedLogs returns the logs matching the filter criteria based on raw block 217 // iteration and bloom matching. 218 func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 219 var logs []*types.Log 220 221 for ; f.begin <= int64(end); f.begin++ { 222 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) 223 if header == nil || err != nil { 224 return logs, err 225 } 226 found, err := f.blockLogs(ctx, header) 227 if err != nil { 228 return logs, err 229 } 230 logs = append(logs, found...) 231 } 232 return logs, nil 233 } 234 235 // blockLogs returns the logs matching the filter criteria within a single block. 236 func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { 237 if bloomFilter(header.Bloom, f.addresses, f.topics) { 238 found, err := f.checkMatches(ctx, header) 239 if err != nil { 240 return logs, err 241 } 242 logs = append(logs, found...) 243 } 244 return logs, nil 245 } 246 247 // checkMatches checks if the receipts belonging to the given header contain any log events that 248 // match the filter criteria. This function is called when the bloom filter signals a potential match. 249 func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { 250 // Get the logs of the block 251 logsList, err := f.backend.GetLogs(ctx, header.Hash()) 252 if err != nil { 253 return nil, err 254 } 255 var unfiltered []*types.Log 256 for _, logs := range logsList { 257 unfiltered = append(unfiltered, logs...) 258 } 259 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 260 if len(logs) > 0 { 261 // We have matching logs, check if we need to resolve full logs via the light client 262 if logs[0].TxHash == (common.Hash{}) { 263 receipts, err := f.backend.GetReceipts(ctx, header.Hash()) 264 if err != nil { 265 return nil, err 266 } 267 unfiltered = unfiltered[:0] 268 for _, receipt := range receipts { 269 unfiltered = append(unfiltered, receipt.Logs...) 270 } 271 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 272 } 273 return logs, nil 274 } 275 return nil, nil 276 } 277 278 func includes(addresses []common.Address, a common.Address) bool { 279 for _, addr := range addresses { 280 if addr == a { 281 return true 282 } 283 } 284 285 return false 286 } 287 288 // filterLogs creates a slice of logs matching the given criteria. 289 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 290 var ret []*types.Log 291 Logs: 292 for _, log := range logs { 293 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 294 continue 295 } 296 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 297 continue 298 } 299 300 if len(addresses) > 0 && !includes(addresses, log.Address) { 301 continue 302 } 303 // If the to filtered topics is greater than the amount of topics in logs, skip. 304 if len(topics) > len(log.Topics) { 305 continue Logs 306 } 307 for i, sub := range topics { 308 match := len(sub) == 0 // empty rule set == wildcard 309 for _, topic := range sub { 310 if log.Topics[i] == topic { 311 match = true 312 break 313 } 314 } 315 if !match { 316 continue Logs 317 } 318 } 319 ret = append(ret, log) 320 } 321 return ret 322 } 323 324 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 325 if len(addresses) > 0 { 326 var included bool 327 for _, addr := range addresses { 328 if types.BloomLookup(bloom, addr) { 329 included = true 330 break 331 } 332 } 333 if !included { 334 return false 335 } 336 } 337 338 for _, sub := range topics { 339 included := len(sub) == 0 // empty rule set == wildcard 340 for _, topic := range sub { 341 if types.BloomLookup(bloom, topic) { 342 included = true 343 break 344 } 345 } 346 if !included { 347 return false 348 } 349 } 350 return true 351 }