github.com/ava-labs/subnet-evm@v0.6.4/eth/filters/filter.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2014 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package filters 28 29 import ( 30 "context" 31 "errors" 32 "fmt" 33 "math/big" 34 35 "github.com/ava-labs/subnet-evm/core/bloombits" 36 "github.com/ava-labs/subnet-evm/core/types" 37 "github.com/ava-labs/subnet-evm/rpc" 38 "github.com/ethereum/go-ethereum/common" 39 ) 40 41 // Filter can be used to retrieve and filter logs. 42 type Filter struct { 43 sys *FilterSystem 44 45 addresses []common.Address 46 topics [][]common.Hash 47 48 block *common.Hash // Block hash if filtering a single block 49 begin, end int64 // Range interval if filtering multiple blocks 50 51 matcher *bloombits.Matcher 52 } 53 54 // NewRangeFilter creates a new filter which uses a bloom filter on blocks to 55 // figure out whether a particular block is interesting or not. 56 func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { 57 // Flatten the address and topic filter clauses into a single bloombits filter 58 // system. Since the bloombits are not positional, nil topics are permitted, 59 // which get flattened into a nil byte slice. 60 var filters [][][]byte 61 if len(addresses) > 0 { 62 filter := make([][]byte, len(addresses)) 63 for i, address := range addresses { 64 filter[i] = address.Bytes() 65 } 66 filters = append(filters, filter) 67 } 68 for _, topicList := range topics { 69 filter := make([][]byte, len(topicList)) 70 for i, topic := range topicList { 71 filter[i] = topic.Bytes() 72 } 73 filters = append(filters, filter) 74 } 75 size, _ := sys.backend.BloomStatus() 76 77 // Create a generic filter and convert it into a range filter 78 filter := newFilter(sys, addresses, topics) 79 80 filter.matcher = bloombits.NewMatcher(size, filters) 81 filter.begin = begin 82 filter.end = end 83 84 return filter 85 } 86 87 // NewBlockFilter creates a new filter which directly inspects the contents of 88 // a block to figure out whether it is interesting or not. 89 func (sys *FilterSystem) NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { 90 // Create a generic filter and convert it into a block filter 91 filter := newFilter(sys, addresses, topics) 92 filter.block = &block 93 return filter 94 } 95 96 // newFilter creates a generic filter that can either filter based on a block hash, 97 // or based on range queries. The search criteria needs to be explicitly set. 98 func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter { 99 return &Filter{ 100 sys: sys, 101 addresses: addresses, 102 topics: topics, 103 } 104 } 105 106 // Logs searches the blockchain for matching log entries, returning all from the 107 // first block that contains matches, updating the start of the filter accordingly. 108 func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { 109 // If we're doing singleton block filtering, execute and return 110 if f.block != nil { 111 header, err := f.sys.backend.HeaderByHash(ctx, *f.block) 112 if err != nil { 113 return nil, err 114 } 115 if header == nil { 116 return nil, errors.New("unknown block") 117 } 118 return f.blockLogs(ctx, header) 119 } 120 121 // Disallow blocks past the last accepted block if the backend does not 122 // allow unfinalized queries. 123 allowUnfinalizedQueries := f.sys.backend.IsAllowUnfinalizedQueries() 124 acceptedBlock := f.sys.backend.LastAcceptedBlock() 125 if !allowUnfinalizedQueries && acceptedBlock != nil { 126 lastAccepted := acceptedBlock.Number().Int64() 127 if f.begin >= 0 && f.begin > lastAccepted { 128 return nil, fmt.Errorf("requested from block %d after last accepted block %d", f.begin, lastAccepted) 129 } 130 if f.end >= 0 && f.end > lastAccepted { 131 return nil, fmt.Errorf("requested to block %d after last accepted block %d", f.end, lastAccepted) 132 } 133 } 134 135 var ( 136 beginPending = f.begin == rpc.PendingBlockNumber.Int64() 137 endPending = f.end == rpc.PendingBlockNumber.Int64() 138 endSet = f.end >= 0 139 ) 140 141 // special case for pending logs 142 if beginPending && !endPending { 143 return nil, errors.New("invalid block range") 144 } 145 146 // Short-cut if all we care about is pending logs 147 if beginPending && endPending { 148 return nil, nil 149 } 150 151 resolveSpecial := func(number int64) (int64, error) { 152 var hdr *types.Header 153 switch number { 154 case rpc.LatestBlockNumber.Int64(), rpc.PendingBlockNumber.Int64(): 155 // we should return head here since we've already captured 156 // that we need to get the pending logs in the pending boolean above 157 hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 158 if hdr == nil { 159 return 0, errors.New("latest header not found") 160 } 161 case rpc.FinalizedBlockNumber.Int64(): 162 hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber) 163 if hdr == nil { 164 return 0, errors.New("finalized header not found") 165 } 166 case rpc.SafeBlockNumber.Int64(): 167 hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber) 168 if hdr == nil { 169 return 0, errors.New("safe header not found") 170 } 171 default: 172 return number, nil 173 } 174 return hdr.Number.Int64(), nil 175 } 176 177 var err error 178 // range query need to resolve the special begin/end block number 179 if f.begin, err = resolveSpecial(f.begin); err != nil { 180 return nil, err 181 } 182 if f.end, err = resolveSpecial(f.end); err != nil { 183 return nil, err 184 } 185 186 // When querying unfinalized data without a populated end block, it is 187 // possible that the begin will be greater than the end. 188 // 189 // We error in this case to prevent a bad UX where the caller thinks there 190 // are no logs from the specified beginning to end (when in reality there may 191 // be some). 192 if endSet && f.end < f.begin { 193 return nil, fmt.Errorf("begin block %d is greater than end block %d", f.begin, f.end) 194 } 195 196 // If the requested range of blocks exceeds the maximum number of blocks allowed by the backend 197 // return an error instead of searching for the logs. 198 if maxBlocks := f.sys.backend.GetMaxBlocksPerRequest(); f.end-f.begin >= maxBlocks && maxBlocks > 0 { 199 return nil, fmt.Errorf("requested too many blocks from %d to %d, maximum is set to %d", f.begin, f.end, maxBlocks) 200 } 201 // Gather all indexed logs, and finish with non indexed ones 202 logChan, errChan := f.rangeLogsAsync(ctx) 203 var logs []*types.Log 204 for { 205 select { 206 case log := <-logChan: 207 logs = append(logs, log) 208 case err := <-errChan: 209 if err != nil { 210 // if an error occurs during extraction, we do return the extracted data 211 return logs, err 212 } 213 return logs, nil 214 } 215 } 216 } 217 218 // rangeLogsAsync retrieves block-range logs that match the filter criteria asynchronously, 219 // it creates and returns two channels: one for delivering log data, and one for reporting errors. 220 func (f *Filter) rangeLogsAsync(ctx context.Context) (chan *types.Log, chan error) { 221 var ( 222 logChan = make(chan *types.Log) 223 errChan = make(chan error) 224 ) 225 226 go func() { 227 defer func() { 228 close(errChan) 229 close(logChan) 230 }() 231 232 // Gather all indexed logs, and finish with non indexed ones 233 var ( 234 end = uint64(f.end) 235 size, sections = f.sys.backend.BloomStatus() 236 err error 237 ) 238 if indexed := sections * size; indexed > uint64(f.begin) { 239 if indexed > end { 240 indexed = end + 1 241 } 242 if err = f.indexedLogs(ctx, indexed-1, logChan); err != nil { 243 errChan <- err 244 return 245 } 246 } 247 248 if err := f.unindexedLogs(ctx, end, logChan); err != nil { 249 errChan <- err 250 return 251 } 252 253 errChan <- nil 254 }() 255 256 return logChan, errChan 257 } 258 259 // indexedLogs returns the logs matching the filter criteria based on the bloom 260 // bits indexed available locally or via the network. 261 func (f *Filter) indexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error { 262 // Create a matcher session and request servicing from the backend 263 matches := make(chan uint64, 64) 264 265 session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) 266 if err != nil { 267 return err 268 } 269 defer session.Close() 270 271 f.sys.backend.ServiceFilter(ctx, session) 272 273 for { 274 select { 275 case number, ok := <-matches: 276 // Abort if all matches have been fulfilled 277 if !ok { 278 err := session.Error() 279 if err == nil { 280 f.begin = int64(end) + 1 281 } 282 return err 283 } 284 f.begin = int64(number) + 1 285 286 // Retrieve the suggested block and pull any truly matching logs 287 header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) 288 if header == nil || err != nil { 289 return err 290 } 291 found, err := f.checkMatches(ctx, header) 292 if err != nil { 293 return err 294 } 295 for _, log := range found { 296 logChan <- log 297 } 298 299 case <-ctx.Done(): 300 return ctx.Err() 301 } 302 } 303 } 304 305 // unindexedLogs returns the logs matching the filter criteria based on raw block 306 // iteration and bloom matching. 307 func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error { 308 for ; f.begin <= int64(end); f.begin++ { 309 header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) 310 if header == nil || err != nil { 311 return err 312 } 313 found, err := f.blockLogs(ctx, header) 314 if err != nil { 315 return err 316 } 317 for _, log := range found { 318 select { 319 case logChan <- log: 320 case <-ctx.Done(): 321 return ctx.Err() 322 } 323 } 324 } 325 return nil 326 } 327 328 // blockLogs returns the logs matching the filter criteria within a single block. 329 func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) { 330 if bloomFilter(header.Bloom, f.addresses, f.topics) { 331 return f.checkMatches(ctx, header) 332 } 333 return nil, nil 334 } 335 336 // checkMatches checks if the receipts belonging to the given header contain any log events that 337 // match the filter criteria. This function is called when the bloom filter signals a potential match. 338 func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) { 339 logsList, err := f.sys.getLogs(ctx, header.Hash(), header.Number.Uint64()) 340 if err != nil { 341 return nil, err 342 } 343 344 unfiltered := types.FlattenLogs(logsList) 345 logs := filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 346 if len(logs) == 0 { 347 return nil, nil 348 } 349 // Most backends will deliver un-derived logs, but check nevertheless. 350 if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) { 351 return logs, nil 352 } 353 // We have matching logs, check if we need to resolve full logs via the light client 354 receipts, err := f.sys.backend.GetReceipts(ctx, header.Hash()) 355 if err != nil { 356 return nil, err 357 } 358 unfiltered = unfiltered[:0] 359 for _, receipt := range receipts { 360 unfiltered = append(unfiltered, receipt.Logs...) 361 } 362 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 363 364 return logs, nil 365 } 366 367 // includes returns true if the element is present in the list. 368 func includes[T comparable](things []T, element T) bool { 369 for _, thing := range things { 370 if thing == element { 371 return true 372 } 373 } 374 return false 375 } 376 377 // filterLogs creates a slice of logs matching the given criteria. 378 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 379 var check = func(log *types.Log) bool { 380 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 381 return false 382 } 383 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 384 return false 385 } 386 if len(addresses) > 0 && !includes(addresses, log.Address) { 387 return false 388 } 389 // If the to filtered topics is greater than the amount of topics in logs, skip. 390 if len(topics) > len(log.Topics) { 391 return false 392 } 393 for i, sub := range topics { 394 if len(sub) == 0 { 395 continue // empty rule set == wildcard 396 } 397 if !includes(sub, log.Topics[i]) { 398 return false 399 } 400 } 401 return true 402 } 403 var ret []*types.Log 404 for _, log := range logs { 405 if check(log) { 406 ret = append(ret, log) 407 } 408 } 409 return ret 410 } 411 412 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 413 if len(addresses) > 0 { 414 var included bool 415 for _, addr := range addresses { 416 if types.BloomLookup(bloom, addr) { 417 included = true 418 break 419 } 420 } 421 if !included { 422 return false 423 } 424 } 425 426 for _, sub := range topics { 427 included := len(sub) == 0 // empty rule set == wildcard 428 for _, topic := range sub { 429 if types.BloomLookup(bloom, topic) { 430 included = true 431 break 432 } 433 } 434 if !included { 435 return false 436 } 437 } 438 return true 439 }