gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/aqua/filters/filter.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package filters 18 19 import ( 20 "context" 21 "math/big" 22 23 "gitlab.com/aquachain/aquachain/aqua/event" 24 "gitlab.com/aquachain/aquachain/aquadb" 25 "gitlab.com/aquachain/aquachain/common" 26 "gitlab.com/aquachain/aquachain/core" 27 "gitlab.com/aquachain/aquachain/core/bloombits" 28 "gitlab.com/aquachain/aquachain/core/types" 29 "gitlab.com/aquachain/aquachain/params" 30 "gitlab.com/aquachain/aquachain/rpc" 31 ) 32 33 type Backend interface { 34 ChainDb() aquadb.Database 35 GetHeaderVersion(*big.Int) params.HeaderVersion 36 EventMux() *event.TypeMux 37 HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) 38 GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) 39 GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) 40 41 SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription 42 SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription 43 SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription 44 SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription 45 46 BloomStatus() (uint64, uint64) 47 ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) 48 } 49 50 // Filter can be used to retrieve and filter logs. 51 type Filter struct { 52 backend Backend 53 54 db aquadb.Database 55 begin, end int64 56 addresses []common.Address 57 topics [][]common.Hash 58 59 matcher *bloombits.Matcher 60 } 61 62 // New creates a new filter which uses a bloom filter on blocks to figure out whether 63 // a particular block is interesting or not. 64 func New(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { 65 // Flatten the address and topic filter clauses into a single bloombits filter 66 // system. Since the bloombits are not positional, nil topics are permitted, 67 // which get flattened into a nil byte slice. 68 var filters [][][]byte 69 if len(addresses) > 0 { 70 filter := make([][]byte, len(addresses)) 71 for i, address := range addresses { 72 filter[i] = address.Bytes() 73 } 74 filters = append(filters, filter) 75 } 76 for _, topicList := range topics { 77 filter := make([][]byte, len(topicList)) 78 for i, topic := range topicList { 79 filter[i] = topic.Bytes() 80 } 81 filters = append(filters, filter) 82 } 83 // Assemble and return the filter 84 size, _ := backend.BloomStatus() 85 86 return &Filter{ 87 backend: backend, 88 begin: begin, 89 end: end, 90 addresses: addresses, 91 topics: topics, 92 db: backend.ChainDb(), 93 matcher: bloombits.NewMatcher(size, filters), 94 } 95 } 96 97 // Logs searches the blockchain for matching log entries, returning all from the 98 // first block that contains matches, updating the start of the filter accordingly. 99 func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { 100 // Figure out the limits of the filter range 101 header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 102 if header == nil { 103 return nil, nil 104 } 105 head := header.Number.Uint64() 106 107 if f.begin == -1 { 108 f.begin = int64(head) 109 } 110 end := uint64(f.end) 111 if f.end == -1 { 112 end = head 113 } 114 // Gather all indexed logs, and finish with non indexed ones 115 var ( 116 logs []*types.Log 117 err error 118 ) 119 size, sections := f.backend.BloomStatus() 120 if indexed := sections * size; indexed > uint64(f.begin) { 121 if indexed > end { 122 logs, err = f.indexedLogs(ctx, end) 123 } else { 124 logs, err = f.indexedLogs(ctx, indexed-1) 125 } 126 if err != nil { 127 return logs, err 128 } 129 } 130 rest, err := f.unindexedLogs(ctx, end) 131 logs = append(logs, rest...) 132 return logs, err 133 } 134 135 // indexedLogs returns the logs matching the filter criteria based on the bloom 136 // bits indexed available locally or via the network. 137 func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 138 // Create a matcher session and request servicing from the backend 139 matches := make(chan uint64, 64) 140 141 session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) 142 if err != nil { 143 return nil, err 144 } 145 defer session.Close() 146 147 f.backend.ServiceFilter(ctx, session) 148 149 // Iterate over the matches until exhausted or context closed 150 var logs []*types.Log 151 152 for { 153 select { 154 case number, ok := <-matches: 155 // Abort if all matches have been fulfilled 156 if !ok { 157 err := session.Error() 158 if err == nil { 159 f.begin = int64(end) + 1 160 } 161 return logs, err 162 } 163 f.begin = int64(number) + 1 164 165 // Retrieve the suggested block and pull any truly matching logs 166 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) 167 if header == nil || err != nil { 168 return logs, err 169 } 170 found, err := f.checkMatches(ctx, header) 171 if err != nil { 172 return logs, err 173 } 174 logs = append(logs, found...) 175 176 case <-ctx.Done(): 177 return logs, ctx.Err() 178 } 179 } 180 } 181 182 // indexedLogs returns the logs matching the filter criteria based on raw block 183 // iteration and bloom matching. 184 func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 185 var logs []*types.Log 186 187 for ; f.begin <= int64(end); f.begin++ { 188 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) 189 if header == nil || err != nil { 190 return logs, err 191 } 192 if bloomFilter(header.Bloom, f.addresses, f.topics) { 193 found, err := f.checkMatches(ctx, header) 194 if err != nil { 195 return logs, err 196 } 197 logs = append(logs, found...) 198 } 199 } 200 return logs, nil 201 } 202 203 // checkMatches checks if the receipts belonging to the given header contain any log events that 204 // match the filter criteria. This function is called when the bloom filter signals a potential match. 205 func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { 206 // Get the logs of the block 207 header.Version = f.backend.GetHeaderVersion(header.Number) 208 logsList, err := f.backend.GetLogs(ctx, header.Hash()) 209 if err != nil { 210 return nil, err 211 } 212 var unfiltered []*types.Log 213 for _, logs := range logsList { 214 unfiltered = append(unfiltered, logs...) 215 } 216 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 217 if len(logs) > 0 { 218 // We have matching logs, check if we need to resolve full logs via the light client 219 if logs[0].TxHash == (common.Hash{}) { 220 receipts, err := f.backend.GetReceipts(ctx, header.Hash()) 221 if err != nil { 222 return nil, err 223 } 224 unfiltered = unfiltered[:0] 225 for _, receipt := range receipts { 226 unfiltered = append(unfiltered, receipt.Logs...) 227 } 228 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 229 } 230 return logs, nil 231 } 232 return nil, nil 233 } 234 235 func includes(addresses []common.Address, a common.Address) bool { 236 for _, addr := range addresses { 237 if addr == a { 238 return true 239 } 240 } 241 242 return false 243 } 244 245 // filterLogs creates a slice of logs matching the given criteria. 246 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 247 var ret []*types.Log 248 Logs: 249 for _, log := range logs { 250 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 251 continue 252 } 253 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 254 continue 255 } 256 257 if len(addresses) > 0 && !includes(addresses, log.Address) { 258 continue 259 } 260 // If the to filtered topics is greater than the amount of topics in logs, skip. 261 if len(topics) > len(log.Topics) { 262 continue Logs 263 } 264 for i, topics := range topics { 265 match := len(topics) == 0 // empty rule set == wildcard 266 for _, topic := range topics { 267 if log.Topics[i] == topic { 268 match = true 269 break 270 } 271 } 272 if !match { 273 continue Logs 274 } 275 } 276 ret = append(ret, log) 277 } 278 return ret 279 } 280 281 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 282 if len(addresses) > 0 { 283 var included bool 284 for _, addr := range addresses { 285 if types.BloomLookup(bloom, addr) { 286 included = true 287 break 288 } 289 } 290 if !included { 291 return false 292 } 293 } 294 295 for _, sub := range topics { 296 included := len(sub) == 0 // empty rule set == wildcard 297 for _, topic := range sub { 298 if types.BloomLookup(bloom, topic) { 299 included = true 300 break 301 } 302 } 303 if !included { 304 return false 305 } 306 } 307 return true 308 }