github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/eth/filters/filter_system.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 12:09:38</date> 10 //</624342635535929344> 11 12 13 //包过滤器为块实现以太坊过滤系统, 14 //事务和日志事件。 15 package filters 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "sync" 22 "time" 23 24 ethereum "github.com/ethereum/go-ethereum" 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/core" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/event" 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/rpc" 32 ) 33 34 //类型确定筛选器的类型,并用于将筛选器放入 35 //添加正确的桶。 36 type Type byte 37 38 const ( 39 //UnknownSubscription表示未知的订阅类型 40 UnknownSubscription Type = iota 41 //新日志或已删除日志的日志订阅查询(chain reorg) 42 LogsSubscription 43 //PendingLogs对挂起块中的日志的订阅查询 44 PendingLogsSubscription 45 //MinedAndPendingLogs对挖掘和挂起块中的日志的订阅查询。 46 MinedAndPendingLogsSubscription 47 //PendingtTransactionsSubscription查询待处理的Tx哈希 48 //进入挂起状态的事务 49 PendingTransactionsSubscription 50 //块订阅查询导入块的哈希 51 BlocksSubscription 52 //LastSubscription跟踪最后一个索引 53 LastIndexSubscription 54 ) 55 56 const ( 57 58 //txchanSize是侦听newtxSevent的频道的大小。 59 //该数字是根据Tx池的大小引用的。 60 txChanSize = 4096 61 //rmlogschansize是侦听removedlogsevent的通道的大小。 62 rmLogsChanSize = 10 63 //logschansize是侦听logsevent的通道的大小。 64 logsChanSize = 10 65 //ChainevChansize是侦听ChainEvent的通道的大小。 66 chainEvChanSize = 10 67 ) 68 69 var ( 70 ErrInvalidSubscriptionID = errors.New("invalid id") 71 ) 72 73 type subscription struct { 74 id rpc.ID 75 typ Type 76 created time.Time 77 logsCrit ethereum.FilterQuery 78 logs chan []*types.Log 79 hashes chan []common.Hash 80 headers chan *types.Header 81 installed chan struct{} //安装过滤器时关闭 82 err chan error //卸载筛选器时关闭 83 } 84 85 //EventSystem创建订阅、处理事件并将其广播到 86 //符合订阅条件的订阅。 87 type EventSystem struct { 88 mux *event.TypeMux 89 backend Backend 90 lightMode bool 91 lastHead *types.Header 92 93 //订阅 94 txsSub event.Subscription //订阅新事务事件 95 logsSub event.Subscription //订阅新日志事件 96 rmLogsSub event.Subscription //已删除日志事件的订阅 97 chainSub event.Subscription //订阅新的链事件 98 pendingLogSub *event.TypeMuxSubscription //订阅挂起日志事件 99 100 //渠道 101 install chan *subscription //安装事件通知筛选器 102 uninstall chan *subscription //删除事件通知筛选器 103 txsCh chan core.NewTxsEvent //接收新事务事件的通道 104 logsCh chan []*types.Log //接收新日志事件的通道 105 rmLogsCh chan core.RemovedLogsEvent //接收已删除日志事件的通道 106 chainCh chan core.ChainEvent //接收新链事件的通道 107 } 108 109 //newEventSystem创建一个新的管理器,用于侦听给定mux上的事件, 110 //分析并过滤它们。它使用全部映射来检索过滤器更改。这个 111 //工作循环保存自己的索引,用于将事件转发到筛选器。 112 // 113 //返回的管理器有一个需要用stop函数停止的循环 114 //或者停止给定的多路复用器。 115 func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { 116 m := &EventSystem{ 117 mux: mux, 118 backend: backend, 119 lightMode: lightMode, 120 install: make(chan *subscription), 121 uninstall: make(chan *subscription), 122 txsCh: make(chan core.NewTxsEvent, txChanSize), 123 logsCh: make(chan []*types.Log, logsChanSize), 124 rmLogsCh: make(chan core.RemovedLogsEvent, rmLogsChanSize), 125 chainCh: make(chan core.ChainEvent, chainEvChanSize), 126 } 127 128 //订阅事件 129 m.txsSub = m.backend.SubscribeNewTxsEvent(m.txsCh) 130 m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh) 131 m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh) 132 m.chainSub = m.backend.SubscribeChainEvent(m.chainCh) 133 //TODO(RJL493456442):使用feed订阅挂起的日志事件 134 m.pendingLogSub = m.mux.Subscribe(core.PendingLogsEvent{}) 135 136 //确保所有订阅都不为空 137 if m.txsSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil || 138 m.pendingLogSub.Closed() { 139 log.Crit("Subscribe for event system failed") 140 } 141 142 go m.eventLoop() 143 return m 144 } 145 146 //订阅是在客户端为特定事件注册自身时创建的。 147 type Subscription struct { 148 ID rpc.ID 149 f *subscription 150 es *EventSystem 151 unsubOnce sync.Once 152 } 153 154 //err返回在取消订阅时关闭的通道。 155 func (sub *Subscription) Err() <-chan error { 156 return sub.f.err 157 } 158 159 //取消订阅从事件广播循环中卸载订阅。 160 func (sub *Subscription) Unsubscribe() { 161 sub.unsubOnce.Do(func() { 162 uninstallLoop: 163 for { 164 //编写卸载请求并使用日志/哈希。这防止 165 //写入时死锁的EventLoop广播方法 166 //订阅循环等待时筛选事件通道 167 //此方法返回(因此不读取这些事件)。 168 select { 169 case sub.es.uninstall <- sub.f: 170 break uninstallLoop 171 case <-sub.f.logs: 172 case <-sub.f.hashes: 173 case <-sub.f.headers: 174 } 175 } 176 177 //在返回之前,请等待在工作循环中卸载筛选器 178 //这样可以确保管理器不会使用 179 //在该方法返回后,客户端可能会尽快关闭。 180 <-sub.Err() 181 }) 182 } 183 184 //订阅在事件广播循环中安装订阅。 185 func (es *EventSystem) subscribe(sub *subscription) *Subscription { 186 es.install <- sub 187 <-sub.installed 188 return &Subscription{ID: sub.id, f: sub, es: es} 189 } 190 191 //subscriptLogs创建一个订阅,该订阅将写入与 192 //给定日志通道的给定条件。从和到的默认值 193 //块为“最新”。如果FromBlock>ToBlock,则返回错误。 194 func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) (*Subscription, error) { 195 var from, to rpc.BlockNumber 196 if crit.FromBlock == nil { 197 from = rpc.LatestBlockNumber 198 } else { 199 from = rpc.BlockNumber(crit.FromBlock.Int64()) 200 } 201 if crit.ToBlock == nil { 202 to = rpc.LatestBlockNumber 203 } else { 204 to = rpc.BlockNumber(crit.ToBlock.Int64()) 205 } 206 207 //只对挂起的日志感兴趣 208 if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber { 209 return es.subscribePendingLogs(crit, logs), nil 210 } 211 //只对新开采的原木感兴趣 212 if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { 213 return es.subscribeLogs(crit, logs), nil 214 } 215 //仅对特定区块范围内的开采原木感兴趣 216 if from >= 0 && to >= 0 && to >= from { 217 return es.subscribeLogs(crit, logs), nil 218 } 219 //对特定块号、新日志和挂起日志中的挖掘日志感兴趣 220 if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber { 221 return es.subscribeMinedPendingLogs(crit, logs), nil 222 } 223 //对从特定区块编号到新开采区块的原木感兴趣 224 if from >= 0 && to == rpc.LatestBlockNumber { 225 return es.subscribeLogs(crit, logs), nil 226 } 227 return nil, fmt.Errorf("invalid from and to block combination: from > to") 228 } 229 230 //subscripbeMinedPendingLogs创建一个返回已挖掘和 231 //与给定条件匹配的挂起日志。 232 func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription { 233 sub := &subscription{ 234 id: rpc.NewID(), 235 typ: MinedAndPendingLogsSubscription, 236 logsCrit: crit, 237 created: time.Now(), 238 logs: logs, 239 hashes: make(chan []common.Hash), 240 headers: make(chan *types.Header), 241 installed: make(chan struct{}), 242 err: make(chan error), 243 } 244 return es.subscribe(sub) 245 } 246 247 //subscriptLogs创建一个订阅,该订阅将写入与 248 //给定日志通道的给定条件。 249 func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription { 250 sub := &subscription{ 251 id: rpc.NewID(), 252 typ: LogsSubscription, 253 logsCrit: crit, 254 created: time.Now(), 255 logs: logs, 256 hashes: make(chan []common.Hash), 257 headers: make(chan *types.Header), 258 installed: make(chan struct{}), 259 err: make(chan error), 260 } 261 return es.subscribe(sub) 262 } 263 264 //subscribePendingLogs创建一个订阅,该订阅为 265 //进入事务池的事务。 266 func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription { 267 sub := &subscription{ 268 id: rpc.NewID(), 269 typ: PendingLogsSubscription, 270 logsCrit: crit, 271 created: time.Now(), 272 logs: logs, 273 hashes: make(chan []common.Hash), 274 headers: make(chan *types.Header), 275 installed: make(chan struct{}), 276 err: make(chan error), 277 } 278 return es.subscribe(sub) 279 } 280 281 //SUBSCRIBENEWEADS创建一个订阅,该订阅写入的块头为 282 //进口链。 283 func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription { 284 sub := &subscription{ 285 id: rpc.NewID(), 286 typ: BlocksSubscription, 287 created: time.Now(), 288 logs: make(chan []*types.Log), 289 hashes: make(chan []common.Hash), 290 headers: headers, 291 installed: make(chan struct{}), 292 err: make(chan error), 293 } 294 return es.subscribe(sub) 295 } 296 297 //订阅BuffixTxs创建一个订阅事务哈希的订阅。 298 //进入事务池的事务。 299 func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription { 300 sub := &subscription{ 301 id: rpc.NewID(), 302 typ: PendingTransactionsSubscription, 303 created: time.Now(), 304 logs: make(chan []*types.Log), 305 hashes: hashes, 306 headers: make(chan *types.Header), 307 installed: make(chan struct{}), 308 err: make(chan error), 309 } 310 return es.subscribe(sub) 311 } 312 313 type filterIndex map[Type]map[rpc.ID]*subscription 314 315 //将事件广播到符合条件的筛选器。 316 func (es *EventSystem) broadcast(filters filterIndex, ev interface{}) { 317 if ev == nil { 318 return 319 } 320 321 switch e := ev.(type) { 322 case []*types.Log: 323 if len(e) > 0 { 324 for _, f := range filters[LogsSubscription] { 325 if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 326 f.logs <- matchedLogs 327 } 328 } 329 } 330 case core.RemovedLogsEvent: 331 for _, f := range filters[LogsSubscription] { 332 if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 333 f.logs <- matchedLogs 334 } 335 } 336 case *event.TypeMuxEvent: 337 if muxe, ok := e.Data.(core.PendingLogsEvent); ok { 338 for _, f := range filters[PendingLogsSubscription] { 339 if e.Time.After(f.created) { 340 if matchedLogs := filterLogs(muxe.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 341 f.logs <- matchedLogs 342 } 343 } 344 } 345 } 346 case core.NewTxsEvent: 347 hashes := make([]common.Hash, 0, len(e.Txs)) 348 for _, tx := range e.Txs { 349 hashes = append(hashes, tx.Hash()) 350 } 351 for _, f := range filters[PendingTransactionsSubscription] { 352 f.hashes <- hashes 353 } 354 case core.ChainEvent: 355 for _, f := range filters[BlocksSubscription] { 356 f.headers <- e.Block.Header() 357 } 358 if es.lightMode && len(filters[LogsSubscription]) > 0 { 359 es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) { 360 for _, f := range filters[LogsSubscription] { 361 if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { 362 f.logs <- matchedLogs 363 } 364 } 365 }) 366 } 367 } 368 } 369 370 func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { 371 oldh := es.lastHead 372 es.lastHead = newHeader 373 if oldh == nil { 374 return 375 } 376 newh := newHeader 377 //查找公共祖先,创建回滚和新块哈希的列表 378 var oldHeaders, newHeaders []*types.Header 379 for oldh.Hash() != newh.Hash() { 380 if oldh.Number.Uint64() >= newh.Number.Uint64() { 381 oldHeaders = append(oldHeaders, oldh) 382 oldh = rawdb.ReadHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) 383 } 384 if oldh.Number.Uint64() < newh.Number.Uint64() { 385 newHeaders = append(newHeaders, newh) 386 newh = rawdb.ReadHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) 387 if newh == nil { 388 //当CHT同步时发生,无需执行任何操作 389 newh = oldh 390 } 391 } 392 } 393 //回滚旧块 394 for _, h := range oldHeaders { 395 callBack(h, true) 396 } 397 //检查新块(数组顺序相反) 398 for i := len(newHeaders) - 1; i >= 0; i-- { 399 callBack(newHeaders[i], false) 400 } 401 } 402 403 //在轻型客户端模式下筛选单个头的日志 404 func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log { 405 if bloomFilter(header.Bloom, addresses, topics) { 406 //获取块的日志 407 ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 408 defer cancel() 409 logsList, err := es.backend.GetLogs(ctx, header.Hash()) 410 if err != nil { 411 return nil 412 } 413 var unfiltered []*types.Log 414 for _, logs := range logsList { 415 for _, log := range logs { 416 logcopy := *log 417 logcopy.Removed = remove 418 unfiltered = append(unfiltered, &logcopy) 419 } 420 } 421 logs := filterLogs(unfiltered, nil, nil, addresses, topics) 422 if len(logs) > 0 && logs[0].TxHash == (common.Hash{}) { 423 //我们有匹配但非派生的日志 424 receipts, err := es.backend.GetReceipts(ctx, header.Hash()) 425 if err != nil { 426 return nil 427 } 428 unfiltered = unfiltered[:0] 429 for _, receipt := range receipts { 430 for _, log := range receipt.Logs { 431 logcopy := *log 432 logcopy.Removed = remove 433 unfiltered = append(unfiltered, &logcopy) 434 } 435 } 436 logs = filterLogs(unfiltered, nil, nil, addresses, topics) 437 } 438 return logs 439 } 440 return nil 441 } 442 443 //EventLoop(un)安装过滤器并处理mux事件。 444 func (es *EventSystem) eventLoop() { 445 //确保清除所有订阅 446 defer func() { 447 es.pendingLogSub.Unsubscribe() 448 es.txsSub.Unsubscribe() 449 es.logsSub.Unsubscribe() 450 es.rmLogsSub.Unsubscribe() 451 es.chainSub.Unsubscribe() 452 }() 453 454 index := make(filterIndex) 455 for i := UnknownSubscription; i < LastIndexSubscription; i++ { 456 index[i] = make(map[rpc.ID]*subscription) 457 } 458 459 for { 460 select { 461 //处理订阅的事件 462 case ev := <-es.txsCh: 463 es.broadcast(index, ev) 464 case ev := <-es.logsCh: 465 es.broadcast(index, ev) 466 case ev := <-es.rmLogsCh: 467 es.broadcast(index, ev) 468 case ev := <-es.chainCh: 469 es.broadcast(index, ev) 470 case ev, active := <-es.pendingLogSub.Chan(): 471 if !active { //系统停止 472 return 473 } 474 es.broadcast(index, ev) 475 476 case f := <-es.install: 477 if f.typ == MinedAndPendingLogsSubscription { 478 //类型为日志和挂起的日志订阅 479 index[LogsSubscription][f.id] = f 480 index[PendingLogsSubscription][f.id] = f 481 } else { 482 index[f.typ][f.id] = f 483 } 484 close(f.installed) 485 486 case f := <-es.uninstall: 487 if f.typ == MinedAndPendingLogsSubscription { 488 //类型为日志和挂起的日志订阅 489 delete(index[LogsSubscription], f.id) 490 delete(index[PendingLogsSubscription], f.id) 491 } else { 492 delete(index[f.typ], f.id) 493 } 494 close(f.err) 495 496 //系统停止 497 case <-es.txsSub.Err(): 498 return 499 case <-es.logsSub.Err(): 500 return 501 case <-es.rmLogsSub.Err(): 502 return 503 case <-es.chainSub.Err(): 504 return 505 } 506 } 507 } 508