github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/eth/filters/filter.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:37</date> 10 //</624450088852262912> 11 12 13 package filters 14 15 import ( 16 "context" 17 "errors" 18 "math/big" 19 20 "github.com/ethereum/go-ethereum/common" 21 "github.com/ethereum/go-ethereum/core" 22 "github.com/ethereum/go-ethereum/core/bloombits" 23 "github.com/ethereum/go-ethereum/core/types" 24 "github.com/ethereum/go-ethereum/ethdb" 25 "github.com/ethereum/go-ethereum/event" 26 "github.com/ethereum/go-ethereum/rpc" 27 ) 28 29 type Backend interface { 30 ChainDb() ethdb.Database 31 EventMux() *event.TypeMux 32 HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) 33 HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) 34 GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) 35 GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) 36 37 SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription 38 SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription 39 SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription 40 SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription 41 42 BloomStatus() (uint64, uint64) 43 ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) 44 } 45 46 //筛选器可用于检索和筛选日志。 47 type Filter struct { 48 backend Backend 49 50 db ethdb.Database 51 addresses []common.Address 52 topics [][]common.Hash 53 54 block common.Hash //如果筛选单个块,则阻止哈希 55 begin, end int64 //过滤多个块时的范围间隔 56 57 matcher *bloombits.Matcher 58 } 59 60 //newrangefilter创建一个新的过滤器,它在块上使用bloom过滤器来 61 //找出一个特定的块是否有趣。 62 func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { 63 //将地址和主题筛选子句展平为单个bloombits筛选器 64 //系统。因为bloombits不是位置的,所以不允许使用任何主题, 65 //它被压扁成一个零字节的片。 66 var filters [][][]byte 67 if len(addresses) > 0 { 68 filter := make([][]byte, len(addresses)) 69 for i, address := range addresses { 70 filter[i] = address.Bytes() 71 } 72 filters = append(filters, filter) 73 } 74 for _, topicList := range topics { 75 filter := make([][]byte, len(topicList)) 76 for i, topic := range topicList { 77 filter[i] = topic.Bytes() 78 } 79 filters = append(filters, filter) 80 } 81 size, _ := backend.BloomStatus() 82 83 //创建通用筛选器并将其转换为范围筛选器 84 filter := newFilter(backend, addresses, topics) 85 86 filter.matcher = bloombits.NewMatcher(size, filters) 87 filter.begin = begin 88 filter.end = end 89 90 return filter 91 } 92 93 //newblockfilter创建一个新的过滤器,它直接检查 94 //用来判断它是否有趣的块。 95 func NewBlockFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { 96 //创建通用筛选器并将其转换为块筛选器 97 filter := newFilter(backend, addresses, topics) 98 filter.block = block 99 return filter 100 } 101 102 //newfilter创建一个通用筛选器,该筛选器可以基于块哈希进行筛选, 103 //或者基于范围查询。需要显式设置搜索条件。 104 func newFilter(backend Backend, addresses []common.Address, topics [][]common.Hash) *Filter { 105 return &Filter{ 106 backend: backend, 107 addresses: addresses, 108 topics: topics, 109 db: backend.ChainDb(), 110 } 111 } 112 113 //日志在区块链中搜索匹配的日志条目,从 114 //包含匹配项的第一个块,相应地更新筛选器的开头。 115 func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { 116 //如果我们进行单例块过滤,执行并返回 117 if f.block != (common.Hash{}) { 118 header, err := f.backend.HeaderByHash(ctx, f.block) 119 if err != nil { 120 return nil, err 121 } 122 if header == nil { 123 return nil, errors.New("unknown block") 124 } 125 return f.blockLogs(ctx, header) 126 } 127 //找出过滤范围的限制 128 header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 129 if header == nil { 130 return nil, nil 131 } 132 head := header.Number.Uint64() 133 134 if f.begin == -1 { 135 f.begin = int64(head) 136 } 137 end := uint64(f.end) 138 if f.end == -1 { 139 end = head 140 } 141 //收集所有索引日志,并使用非索引日志完成 142 var ( 143 logs []*types.Log 144 err error 145 ) 146 size, sections := f.backend.BloomStatus() 147 if indexed := sections * size; indexed > uint64(f.begin) { 148 if indexed > end { 149 logs, err = f.indexedLogs(ctx, end) 150 } else { 151 logs, err = f.indexedLogs(ctx, indexed-1) 152 } 153 if err != nil { 154 return logs, err 155 } 156 } 157 rest, err := f.unindexedLogs(ctx, end) 158 logs = append(logs, rest...) 159 return logs, err 160 } 161 162 //indexedlogs返回与基于bloom的筛选条件匹配的日志 163 //在本地或通过网络可用的索引位。 164 func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 165 //创建Matcher会话并从后端请求服务 166 matches := make(chan uint64, 64) 167 168 session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) 169 if err != nil { 170 return nil, err 171 } 172 defer session.Close() 173 174 f.backend.ServiceFilter(ctx, session) 175 176 //迭代匹配项,直到耗尽或上下文关闭 177 var logs []*types.Log 178 179 for { 180 select { 181 case number, ok := <-matches: 182 //如果满足所有匹配,则中止 183 if !ok { 184 err := session.Error() 185 if err == nil { 186 f.begin = int64(end) + 1 187 } 188 return logs, err 189 } 190 f.begin = int64(number) + 1 191 192 //检索建议的块并提取任何真正匹配的日志 193 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) 194 if header == nil || err != nil { 195 return logs, err 196 } 197 found, err := f.checkMatches(ctx, header) 198 if err != nil { 199 return logs, err 200 } 201 logs = append(logs, found...) 202 203 case <-ctx.Done(): 204 return logs, ctx.Err() 205 } 206 } 207 } 208 209 //indexedlogs返回与基于原始块的筛选条件匹配的日志 210 //迭代和开花匹配。 211 func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { 212 var logs []*types.Log 213 214 for ; f.begin <= int64(end); f.begin++ { 215 header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) 216 if header == nil || err != nil { 217 return logs, err 218 } 219 found, err := f.blockLogs(ctx, header) 220 if err != nil { 221 return logs, err 222 } 223 logs = append(logs, found...) 224 } 225 return logs, nil 226 } 227 228 //block logs返回与单个块中的筛选条件匹配的日志。 229 func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { 230 if bloomFilter(header.Bloom, f.addresses, f.topics) { 231 found, err := f.checkMatches(ctx, header) 232 if err != nil { 233 return logs, err 234 } 235 logs = append(logs, found...) 236 } 237 return logs, nil 238 } 239 240 //checkmatches检查属于给定头的收据是否包含 241 //匹配筛选条件。当布卢姆滤波器发出潜在匹配信号时,调用此函数。 242 func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { 243 //获取块的日志 244 logsList, err := f.backend.GetLogs(ctx, header.Hash()) 245 if err != nil { 246 return nil, err 247 } 248 var unfiltered []*types.Log 249 for _, logs := range logsList { 250 unfiltered = append(unfiltered, logs...) 251 } 252 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 253 if len(logs) > 0 { 254 //我们有匹配的日志,检查是否需要通过Light客户端解析完整的日志 255 if logs[0].TxHash == (common.Hash{}) { 256 receipts, err := f.backend.GetReceipts(ctx, header.Hash()) 257 if err != nil { 258 return nil, err 259 } 260 unfiltered = unfiltered[:0] 261 for _, receipt := range receipts { 262 unfiltered = append(unfiltered, receipt.Logs...) 263 } 264 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 265 } 266 return logs, nil 267 } 268 return nil, nil 269 } 270 271 func includes(addresses []common.Address, a common.Address) bool { 272 for _, addr := range addresses { 273 if addr == a { 274 return true 275 } 276 } 277 278 return false 279 } 280 281 //FieldLtG创建一个与给定标准匹配的日志片段。 282 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 283 var ret []*types.Log 284 Logs: 285 for _, log := range logs { 286 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 287 continue 288 } 289 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 290 continue 291 } 292 293 if len(addresses) > 0 && !includes(addresses, log.Address) { 294 continue 295 } 296 //如果到筛选的主题大于日志中的主题数量,则跳过。 297 if len(topics) > len(log.Topics) { 298 continue Logs 299 } 300 for i, sub := range topics { 301 match := len(sub) == 0 //empty rule set == wildcard 302 for _, topic := range sub { 303 if log.Topics[i] == topic { 304 match = true 305 break 306 } 307 } 308 if !match { 309 continue Logs 310 } 311 } 312 ret = append(ret, log) 313 } 314 return ret 315 } 316 317 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 318 if len(addresses) > 0 { 319 var included bool 320 for _, addr := range addresses { 321 if types.BloomLookup(bloom, addr) { 322 included = true 323 break 324 } 325 } 326 if !included { 327 return false 328 } 329 } 330 331 for _, sub := range topics { 332 included := len(sub) == 0 //空规则集==通配符 333 for _, topic := range sub { 334 if types.BloomLookup(bloom, topic) { 335 included = true 336 break 337 } 338 } 339 if !included { 340 return false 341 } 342 } 343 return true 344 } 345