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