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