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