github.com/theQRL/go-zond@v0.1.1/zond/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/theQRL/go-zond/common" 25 "github.com/theQRL/go-zond/core/bloombits" 26 "github.com/theQRL/go-zond/core/types" 27 "github.com/theQRL/go-zond/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) 108 } 109 110 var ( 111 beginPending = f.begin == rpc.PendingBlockNumber.Int64() 112 endPending = f.end == rpc.PendingBlockNumber.Int64() 113 ) 114 115 // special case for pending logs 116 if beginPending && !endPending { 117 return nil, errors.New("invalid block range") 118 } 119 120 // Short-cut if all we care about is pending logs 121 if beginPending && endPending { 122 return f.pendingLogs(), nil 123 } 124 125 resolveSpecial := func(number int64) (int64, error) { 126 var hdr *types.Header 127 switch number { 128 case rpc.LatestBlockNumber.Int64(), rpc.PendingBlockNumber.Int64(): 129 // we should return head here since we've already captured 130 // that we need to get the pending logs in the pending boolean above 131 hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 132 if hdr == nil { 133 return 0, errors.New("latest header not found") 134 } 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 151 var err error 152 // range query need to resolve the special begin/end block number 153 if f.begin, err = resolveSpecial(f.begin); err != nil { 154 return nil, err 155 } 156 if f.end, err = resolveSpecial(f.end); err != nil { 157 return nil, err 158 } 159 160 logChan, errChan := f.rangeLogsAsync(ctx) 161 var logs []*types.Log 162 for { 163 select { 164 case log := <-logChan: 165 logs = append(logs, log) 166 case err := <-errChan: 167 if err != nil { 168 // if an error occurs during extraction, we do return the extracted data 169 return logs, err 170 } 171 // Append the pending ones 172 if endPending { 173 pendingLogs := f.pendingLogs() 174 logs = append(logs, pendingLogs...) 175 } 176 return logs, nil 177 } 178 } 179 } 180 181 // rangeLogsAsync retrieves block-range logs that match the filter criteria asynchronously, 182 // it creates and returns two channels: one for delivering log data, and one for reporting errors. 183 func (f *Filter) rangeLogsAsync(ctx context.Context) (chan *types.Log, chan error) { 184 var ( 185 logChan = make(chan *types.Log) 186 errChan = make(chan error) 187 ) 188 189 go func() { 190 defer func() { 191 close(errChan) 192 close(logChan) 193 }() 194 195 // Gather all indexed logs, and finish with non indexed ones 196 var ( 197 end = uint64(f.end) 198 size, sections = f.sys.backend.BloomStatus() 199 err error 200 ) 201 if indexed := sections * size; indexed > uint64(f.begin) { 202 if indexed > end { 203 indexed = end + 1 204 } 205 if err = f.indexedLogs(ctx, indexed-1, logChan); err != nil { 206 errChan <- err 207 return 208 } 209 } 210 211 if err := f.unindexedLogs(ctx, end, logChan); err != nil { 212 errChan <- err 213 return 214 } 215 216 errChan <- nil 217 }() 218 219 return logChan, errChan 220 } 221 222 // indexedLogs returns the logs matching the filter criteria based on the bloom 223 // bits indexed available locally or via the network. 224 func (f *Filter) indexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error { 225 // Create a matcher session and request servicing from the backend 226 matches := make(chan uint64, 64) 227 228 session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) 229 if err != nil { 230 return err 231 } 232 defer session.Close() 233 234 f.sys.backend.ServiceFilter(ctx, session) 235 236 for { 237 select { 238 case number, ok := <-matches: 239 // Abort if all matches have been fulfilled 240 if !ok { 241 err := session.Error() 242 if err == nil { 243 f.begin = int64(end) + 1 244 } 245 return err 246 } 247 f.begin = int64(number) + 1 248 249 // Retrieve the suggested block and pull any truly matching logs 250 header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) 251 if header == nil || err != nil { 252 return err 253 } 254 found, err := f.checkMatches(ctx, header) 255 if err != nil { 256 return err 257 } 258 for _, log := range found { 259 logChan <- log 260 } 261 262 case <-ctx.Done(): 263 return ctx.Err() 264 } 265 } 266 } 267 268 // unindexedLogs returns the logs matching the filter criteria based on raw block 269 // iteration and bloom matching. 270 func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error { 271 for ; f.begin <= int64(end); f.begin++ { 272 header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) 273 if header == nil || err != nil { 274 return err 275 } 276 found, err := f.blockLogs(ctx, header) 277 if err != nil { 278 return err 279 } 280 for _, log := range found { 281 select { 282 case logChan <- log: 283 case <-ctx.Done(): 284 return ctx.Err() 285 } 286 } 287 } 288 return nil 289 } 290 291 // blockLogs returns the logs matching the filter criteria within a single block. 292 func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) { 293 if bloomFilter(header.Bloom, f.addresses, f.topics) { 294 return f.checkMatches(ctx, header) 295 } 296 return nil, nil 297 } 298 299 // checkMatches checks if the receipts belonging to the given header contain any log events that 300 // match the filter criteria. This function is called when the bloom filter signals a potential match. 301 // skipFilter signals all logs of the given block are requested. 302 func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) { 303 hash := header.Hash() 304 // Logs in cache are partially filled with context data 305 // such as tx index, block hash, etc. 306 // Notably tx hash is NOT filled in because it needs 307 // access to block body data. 308 cached, err := f.sys.cachedLogElem(ctx, hash, header.Number.Uint64()) 309 if err != nil { 310 return nil, err 311 } 312 logs := filterLogs(cached.logs, nil, nil, f.addresses, f.topics) 313 if len(logs) == 0 { 314 return nil, nil 315 } 316 // Most backends will deliver un-derived logs, but check nevertheless. 317 if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) { 318 return logs, nil 319 } 320 321 body, err := f.sys.cachedGetBody(ctx, cached, hash, header.Number.Uint64()) 322 if err != nil { 323 return nil, err 324 } 325 for i, log := range logs { 326 // Copy log not to modify cache elements 327 logcopy := *log 328 logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash() 329 logs[i] = &logcopy 330 } 331 return logs, nil 332 } 333 334 // pendingLogs returns the logs matching the filter criteria within the pending block. 335 func (f *Filter) pendingLogs() []*types.Log { 336 block, receipts := f.sys.backend.PendingBlockAndReceipts() 337 if block == nil || receipts == nil { 338 return nil 339 } 340 if bloomFilter(block.Bloom(), f.addresses, f.topics) { 341 var unfiltered []*types.Log 342 for _, r := range receipts { 343 unfiltered = append(unfiltered, r.Logs...) 344 } 345 return filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 346 } 347 return nil 348 } 349 350 // includes returns true if the element is present in the list. 351 func includes[T comparable](things []T, element T) bool { 352 for _, thing := range things { 353 if thing == element { 354 return true 355 } 356 } 357 return false 358 } 359 360 // filterLogs creates a slice of logs matching the given criteria. 361 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 362 var check = func(log *types.Log) bool { 363 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 364 return false 365 } 366 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 367 return false 368 } 369 if len(addresses) > 0 && !includes(addresses, log.Address) { 370 return false 371 } 372 // If the to filtered topics is greater than the amount of topics in logs, skip. 373 if len(topics) > len(log.Topics) { 374 return false 375 } 376 for i, sub := range topics { 377 if len(sub) == 0 { 378 continue // empty rule set == wildcard 379 } 380 if !includes(sub, log.Topics[i]) { 381 return false 382 } 383 } 384 return true 385 } 386 var ret []*types.Log 387 for _, log := range logs { 388 if check(log) { 389 ret = append(ret, log) 390 } 391 } 392 return ret 393 } 394 395 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 396 if len(addresses) > 0 { 397 var included bool 398 for _, addr := range addresses { 399 if types.BloomLookup(bloom, addr) { 400 included = true 401 break 402 } 403 } 404 if !included { 405 return false 406 } 407 } 408 409 for _, sub := range topics { 410 included := len(sub) == 0 // empty rule set == wildcard 411 for _, topic := range sub { 412 if types.BloomLookup(bloom, topic) { 413 included = true 414 break 415 } 416 } 417 if !included { 418 return false 419 } 420 } 421 return true 422 }