github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/neatptc/filters/filter.go (about) 1 package filters 2 3 import ( 4 "context" 5 "math/big" 6 7 "github.com/neatlab/neatio/chain/core" 8 "github.com/neatlab/neatio/chain/core/bloombits" 9 "github.com/neatlab/neatio/chain/core/types" 10 "github.com/neatlab/neatio/neatdb" 11 "github.com/neatlab/neatio/network/rpc" 12 "github.com/neatlab/neatio/utilities/common" 13 "github.com/neatlab/neatio/utilities/event" 14 ) 15 16 type Backend interface { 17 ChainDb() neatdb.Database 18 EventMux() *event.TypeMux 19 HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) 20 GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) 21 GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) 22 23 SubscribeTxPreEvent(chan<- core.TxPreEvent) event.Subscription 24 SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription 25 SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription 26 SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription 27 28 BloomStatus() (uint64, uint64) 29 ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) 30 } 31 32 type Filter struct { 33 backend Backend 34 35 db neatdb.Database 36 begin, end int64 37 addresses []common.Address 38 topics [][]common.Hash 39 40 matcher *bloombits.Matcher 41 } 42 43 func New(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { 44 45 var filters [][][]byte 46 if len(addresses) > 0 { 47 filter := make([][]byte, len(addresses)) 48 for i, address := range addresses { 49 filter[i] = address.Bytes() 50 } 51 filters = append(filters, filter) 52 } 53 for _, topicList := range topics { 54 filter := make([][]byte, len(topicList)) 55 for i, topic := range topicList { 56 filter[i] = topic.Bytes() 57 } 58 filters = append(filters, filter) 59 } 60 61 size, _ := backend.BloomStatus() 62 63 return &Filter{ 64 backend: backend, 65 begin: begin, 66 end: end, 67 addresses: addresses, 68 topics: topics, 69 db: backend.ChainDb(), 70 matcher: bloombits.NewMatcher(size, filters), 71 } 72 } 73 74 func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { 75 76 header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 77 if header == nil { 78 return nil, nil 79 } 80 head := header.Number.Uint64() 81 82 if f.begin == -1 { 83 f.begin = int64(head) 84 } 85 end := uint64(f.end) 86 if f.end == -1 { 87 end = head 88 } 89 90 var ( 91 logs []*types.Log 92 err error 93 ) 94 size, sections := f.backend.BloomStatus() 95 if indexed := sections * size; indexed > uint64(f.begin) { 96 if indexed > end { 97 logs, err = f.indexedLogs(ctx, end) 98 } else { 99 logs, err = f.indexedLogs(ctx, indexed-1) 100 } 101 if err != nil { 102 return logs, err 103 } 104 } 105 rest, err := f.unindexedLogs(ctx, end) 106 logs = append(logs, rest...) 107 return logs, err 108 } 109 110 func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 111 112 matches := make(chan uint64, 64) 113 114 session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) 115 if err != nil { 116 return nil, err 117 } 118 defer session.Close() 119 120 f.backend.ServiceFilter(ctx, session) 121 122 var logs []*types.Log 123 124 for { 125 select { 126 case number, ok := <-matches: 127 128 if !ok { 129 err := session.Error() 130 if err == nil { 131 f.begin = int64(end) + 1 132 } 133 return logs, err 134 } 135 f.begin = int64(number) + 1 136 137 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) 138 if header == nil || err != nil { 139 return logs, err 140 } 141 found, err := f.checkMatches(ctx, header) 142 if err != nil { 143 return logs, err 144 } 145 logs = append(logs, found...) 146 147 case <-ctx.Done(): 148 return logs, ctx.Err() 149 } 150 } 151 } 152 153 func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 154 var logs []*types.Log 155 156 for ; f.begin <= int64(end); f.begin++ { 157 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) 158 if header == nil || err != nil { 159 return logs, err 160 } 161 if bloomFilter(header.Bloom, f.addresses, f.topics) { 162 found, err := f.checkMatches(ctx, header) 163 if err != nil { 164 return logs, err 165 } 166 logs = append(logs, found...) 167 } 168 } 169 return logs, nil 170 } 171 172 func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { 173 174 logsList, err := f.backend.GetLogs(ctx, header.Hash()) 175 if err != nil { 176 return nil, err 177 } 178 var unfiltered []*types.Log 179 for _, logs := range logsList { 180 unfiltered = append(unfiltered, logs...) 181 } 182 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 183 if len(logs) > 0 { 184 185 if logs[0].TxHash == (common.Hash{}) { 186 receipts, err := f.backend.GetReceipts(ctx, header.Hash()) 187 if err != nil { 188 return nil, err 189 } 190 unfiltered = unfiltered[:0] 191 for _, receipt := range receipts { 192 unfiltered = append(unfiltered, receipt.Logs...) 193 } 194 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 195 } 196 return logs, nil 197 } 198 return nil, nil 199 } 200 201 func includes(addresses []common.Address, a common.Address) bool { 202 for _, addr := range addresses { 203 if addr == a { 204 return true 205 } 206 } 207 208 return false 209 } 210 211 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 212 var ret []*types.Log 213 Logs: 214 for _, log := range logs { 215 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 216 continue 217 } 218 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 219 continue 220 } 221 222 if len(addresses) > 0 && !includes(addresses, log.Address) { 223 continue 224 } 225 226 if len(topics) > len(log.Topics) { 227 continue Logs 228 } 229 for i, topics := range topics { 230 match := len(topics) == 0 231 for _, topic := range topics { 232 if log.Topics[i] == topic { 233 match = true 234 break 235 } 236 } 237 if !match { 238 continue Logs 239 } 240 } 241 ret = append(ret, log) 242 } 243 return ret 244 } 245 246 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 247 if len(addresses) > 0 { 248 var included bool 249 for _, addr := range addresses { 250 if types.BloomLookup(bloom, addr) { 251 included = true 252 break 253 } 254 } 255 if !included { 256 return false 257 } 258 } 259 260 for _, sub := range topics { 261 included := len(sub) == 0 262 for _, topic := range sub { 263 if types.BloomLookup(bloom, topic) { 264 included = true 265 break 266 } 267 } 268 if !included { 269 return false 270 } 271 } 272 return true 273 }