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