github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/eth/filters/filter_system.go (about) 1 // Copyright 2015 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 implements an ethereum filtering system for block, 18 // transactions and log events. 19 package filters 20 21 import ( 22 "context" 23 "fmt" 24 "sync" 25 "time" 26 27 "github.com/ethereum/go-ethereum" 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/core" 30 "github.com/ethereum/go-ethereum/core/rawdb" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/event" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/rpc" 35 ) 36 37 // Type determines the kind of filter and is used to put the filter in to 38 // the correct bucket when added. 39 type Type byte 40 41 const ( 42 // UnknownSubscription indicates an unknown subscription type 43 UnknownSubscription Type = iota 44 // LogsSubscription queries for new or removed (chain reorg) logs 45 LogsSubscription 46 // PendingLogsSubscription queries for logs in pending blocks 47 PendingLogsSubscription 48 // MinedAndPendingLogsSubscription queries for logs in mined and pending blocks. 49 MinedAndPendingLogsSubscription 50 // PendingTransactionsSubscription queries tx hashes for pending 51 // transactions entering the pending state 52 PendingTransactionsSubscription 53 // BlocksSubscription queries hashes for blocks that are imported 54 BlocksSubscription 55 // StateSyncSubscription to listen main chain state 56 StateSyncSubscription 57 // LastIndexSubscription keeps track of the last index 58 LastIndexSubscription 59 ) 60 61 const ( 62 // txChanSize is the size of channel listening to NewTxsEvent. 63 // The number is referenced from the size of tx pool. 64 txChanSize = 4096 65 // rmLogsChanSize is the size of channel listening to RemovedLogsEvent. 66 rmLogsChanSize = 10 67 // logsChanSize is the size of channel listening to LogsEvent. 68 logsChanSize = 10 69 // chainEvChanSize is the size of channel listening to ChainEvent. 70 chainEvChanSize = 10 71 // stateEvChanSize is the size of channel listening to StateSyncEvent. 72 stateEvChanSize = 10 73 ) 74 75 type subscription struct { 76 id rpc.ID 77 typ Type 78 created time.Time 79 logsCrit ethereum.FilterQuery 80 logs chan []*types.Log 81 hashes chan []common.Hash 82 headers chan *types.Header 83 installed chan struct{} // closed when the filter is installed 84 err chan error // closed when the filter is uninstalled 85 86 stateSyncData chan *types.StateSyncData 87 } 88 89 // EventSystem creates subscriptions, processes events and broadcasts them to the 90 // subscription which match the subscription criteria. 91 type EventSystem struct { 92 backend Backend 93 lightMode bool 94 lastHead *types.Header 95 96 // Subscriptions 97 txsSub event.Subscription // Subscription for new transaction event 98 logsSub event.Subscription // Subscription for new log event 99 rmLogsSub event.Subscription // Subscription for removed log event 100 pendingLogsSub event.Subscription // Subscription for pending log event 101 chainSub event.Subscription // Subscription for new chain event 102 103 // Channels 104 install chan *subscription // install filter for event notification 105 uninstall chan *subscription // remove filter for event notification 106 txsCh chan core.NewTxsEvent // Channel to receive new transactions event 107 logsCh chan []*types.Log // Channel to receive new log event 108 pendingLogsCh chan []*types.Log // Channel to receive new log event 109 rmLogsCh chan core.RemovedLogsEvent // Channel to receive removed log event 110 chainCh chan core.ChainEvent // Channel to receive new chain event 111 112 // Bor related subscription and channels 113 stateSyncSub event.Subscription // Subscription for new state event 114 stateSyncCh chan core.StateSyncEvent // Channel to receive deposit state change event 115 } 116 117 // NewEventSystem creates a new manager that listens for event on the given mux, 118 // parses and filters them. It uses the all map to retrieve filter changes. The 119 // work loop holds its own index that is used to forward events to filters. 120 // 121 // The returned manager has a loop that needs to be stopped with the Stop function 122 // or by stopping the given mux. 123 func NewEventSystem(backend Backend, lightMode bool) *EventSystem { 124 m := &EventSystem{ 125 backend: backend, 126 lightMode: lightMode, 127 install: make(chan *subscription), 128 uninstall: make(chan *subscription), 129 txsCh: make(chan core.NewTxsEvent, txChanSize), 130 logsCh: make(chan []*types.Log, logsChanSize), 131 rmLogsCh: make(chan core.RemovedLogsEvent, rmLogsChanSize), 132 pendingLogsCh: make(chan []*types.Log, logsChanSize), 133 chainCh: make(chan core.ChainEvent, chainEvChanSize), 134 stateSyncCh: make(chan core.StateSyncEvent, stateEvChanSize), 135 } 136 137 // Subscribe events 138 m.txsSub = m.backend.SubscribeNewTxsEvent(m.txsCh) 139 m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh) 140 m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh) 141 m.chainSub = m.backend.SubscribeChainEvent(m.chainCh) 142 m.pendingLogsSub = m.backend.SubscribePendingLogsEvent(m.pendingLogsCh) 143 m.stateSyncSub = m.backend.SubscribeStateSyncEvent(m.stateSyncCh) 144 145 // Make sure none of the subscriptions are empty 146 if m.txsSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil || m.pendingLogsSub == nil { 147 log.Crit("Subscribe for event system failed") 148 } 149 150 go m.eventLoop() 151 return m 152 } 153 154 // Subscription is created when the client registers itself for a particular event. 155 type Subscription struct { 156 ID rpc.ID 157 f *subscription 158 es *EventSystem 159 unsubOnce sync.Once 160 } 161 162 // Err returns a channel that is closed when unsubscribed. 163 func (sub *Subscription) Err() <-chan error { 164 return sub.f.err 165 } 166 167 // Unsubscribe uninstalls the subscription from the event broadcast loop. 168 func (sub *Subscription) Unsubscribe() { 169 sub.unsubOnce.Do(func() { 170 uninstallLoop: 171 for { 172 // write uninstall request and consume logs/hashes. This prevents 173 // the eventLoop broadcast method to deadlock when writing to the 174 // filter event channel while the subscription loop is waiting for 175 // this method to return (and thus not reading these events). 176 select { 177 case sub.es.uninstall <- sub.f: 178 break uninstallLoop 179 case <-sub.f.logs: 180 case <-sub.f.hashes: 181 case <-sub.f.headers: 182 } 183 } 184 185 // wait for filter to be uninstalled in work loop before returning 186 // this ensures that the manager won't use the event channel which 187 // will probably be closed by the client asap after this method returns. 188 <-sub.Err() 189 }) 190 } 191 192 // subscribe installs the subscription in the event broadcast loop. 193 func (es *EventSystem) subscribe(sub *subscription) *Subscription { 194 es.install <- sub 195 <-sub.installed 196 return &Subscription{ID: sub.id, f: sub, es: es} 197 } 198 199 // SubscribeLogs creates a subscription that will write all logs matching the 200 // given criteria to the given logs channel. Default value for the from and to 201 // block is "latest". If the fromBlock > toBlock an error is returned. 202 func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) (*Subscription, error) { 203 var from, to rpc.BlockNumber 204 if crit.FromBlock == nil { 205 from = rpc.LatestBlockNumber 206 } else { 207 from = rpc.BlockNumber(crit.FromBlock.Int64()) 208 } 209 if crit.ToBlock == nil { 210 to = rpc.LatestBlockNumber 211 } else { 212 to = rpc.BlockNumber(crit.ToBlock.Int64()) 213 } 214 215 // only interested in pending logs 216 if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber { 217 return es.subscribePendingLogs(crit, logs), nil 218 } 219 // only interested in new mined logs 220 if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { 221 return es.subscribeLogs(crit, logs), nil 222 } 223 // only interested in mined logs within a specific block range 224 if from >= 0 && to >= 0 && to >= from { 225 return es.subscribeLogs(crit, logs), nil 226 } 227 // interested in mined logs from a specific block number, new logs and pending logs 228 if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber { 229 return es.subscribeMinedPendingLogs(crit, logs), nil 230 } 231 // interested in logs from a specific block number to new mined blocks 232 if from >= 0 && to == rpc.LatestBlockNumber { 233 return es.subscribeLogs(crit, logs), nil 234 } 235 return nil, fmt.Errorf("invalid from and to block combination: from > to") 236 } 237 238 // subscribeMinedPendingLogs creates a subscription that returned mined and 239 // pending logs that match the given criteria. 240 func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription { 241 sub := &subscription{ 242 id: rpc.NewID(), 243 typ: MinedAndPendingLogsSubscription, 244 logsCrit: crit, 245 created: time.Now(), 246 logs: logs, 247 hashes: make(chan []common.Hash), 248 headers: make(chan *types.Header), 249 installed: make(chan struct{}), 250 err: make(chan error), 251 } 252 return es.subscribe(sub) 253 } 254 255 // subscribeLogs creates a subscription that will write all logs matching the 256 // given criteria to the given logs channel. 257 func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription { 258 sub := &subscription{ 259 id: rpc.NewID(), 260 typ: LogsSubscription, 261 logsCrit: crit, 262 created: time.Now(), 263 logs: logs, 264 hashes: make(chan []common.Hash), 265 headers: make(chan *types.Header), 266 installed: make(chan struct{}), 267 err: make(chan error), 268 } 269 return es.subscribe(sub) 270 } 271 272 // subscribePendingLogs creates a subscription that writes transaction hashes for 273 // transactions that enter the transaction pool. 274 func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan []*types.Log) *Subscription { 275 sub := &subscription{ 276 id: rpc.NewID(), 277 typ: PendingLogsSubscription, 278 logsCrit: crit, 279 created: time.Now(), 280 logs: logs, 281 hashes: make(chan []common.Hash), 282 headers: make(chan *types.Header), 283 installed: make(chan struct{}), 284 err: make(chan error), 285 } 286 return es.subscribe(sub) 287 } 288 289 // SubscribeNewHeads creates a subscription that writes the header of a block that is 290 // imported in the chain. 291 func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription { 292 sub := &subscription{ 293 id: rpc.NewID(), 294 typ: BlocksSubscription, 295 created: time.Now(), 296 logs: make(chan []*types.Log), 297 hashes: make(chan []common.Hash), 298 headers: headers, 299 installed: make(chan struct{}), 300 err: make(chan error), 301 } 302 return es.subscribe(sub) 303 } 304 305 // SubscribePendingTxs creates a subscription that writes transaction hashes for 306 // transactions that enter the transaction pool. 307 func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription { 308 sub := &subscription{ 309 id: rpc.NewID(), 310 typ: PendingTransactionsSubscription, 311 created: time.Now(), 312 logs: make(chan []*types.Log), 313 hashes: hashes, 314 headers: make(chan *types.Header), 315 installed: make(chan struct{}), 316 err: make(chan error), 317 } 318 return es.subscribe(sub) 319 } 320 321 type filterIndex map[Type]map[rpc.ID]*subscription 322 323 func (es *EventSystem) handleLogs(filters filterIndex, ev []*types.Log) { 324 if len(ev) == 0 { 325 return 326 } 327 for _, f := range filters[LogsSubscription] { 328 matchedLogs := filterLogs(ev, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics) 329 if len(matchedLogs) > 0 { 330 f.logs <- matchedLogs 331 } 332 } 333 } 334 335 func (es *EventSystem) handlePendingLogs(filters filterIndex, ev []*types.Log) { 336 if len(ev) == 0 { 337 return 338 } 339 for _, f := range filters[PendingLogsSubscription] { 340 matchedLogs := filterLogs(ev, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics) 341 if len(matchedLogs) > 0 { 342 f.logs <- matchedLogs 343 } 344 } 345 } 346 347 func (es *EventSystem) handleRemovedLogs(filters filterIndex, ev core.RemovedLogsEvent) { 348 for _, f := range filters[LogsSubscription] { 349 matchedLogs := filterLogs(ev.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics) 350 if len(matchedLogs) > 0 { 351 f.logs <- matchedLogs 352 } 353 } 354 } 355 356 func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent) { 357 hashes := make([]common.Hash, 0, len(ev.Txs)) 358 for _, tx := range ev.Txs { 359 hashes = append(hashes, tx.Hash()) 360 } 361 for _, f := range filters[PendingTransactionsSubscription] { 362 f.hashes <- hashes 363 } 364 } 365 366 func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) { 367 for _, f := range filters[BlocksSubscription] { 368 f.headers <- ev.Block.Header() 369 } 370 if es.lightMode && len(filters[LogsSubscription]) > 0 { 371 es.lightFilterNewHead(ev.Block.Header(), func(header *types.Header, remove bool) { 372 for _, f := range filters[LogsSubscription] { 373 if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { 374 f.logs <- matchedLogs 375 } 376 } 377 }) 378 } 379 } 380 381 func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { 382 oldh := es.lastHead 383 es.lastHead = newHeader 384 if oldh == nil { 385 return 386 } 387 newh := newHeader 388 // find common ancestor, create list of rolled back and new block hashes 389 var oldHeaders, newHeaders []*types.Header 390 for oldh.Hash() != newh.Hash() { 391 if oldh.Number.Uint64() >= newh.Number.Uint64() { 392 oldHeaders = append(oldHeaders, oldh) 393 oldh = rawdb.ReadHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) 394 } 395 if oldh.Number.Uint64() < newh.Number.Uint64() { 396 newHeaders = append(newHeaders, newh) 397 newh = rawdb.ReadHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) 398 if newh == nil { 399 // happens when CHT syncing, nothing to do 400 newh = oldh 401 } 402 } 403 } 404 // roll back old blocks 405 for _, h := range oldHeaders { 406 callBack(h, true) 407 } 408 // check new blocks (array is in reverse order) 409 for i := len(newHeaders) - 1; i >= 0; i-- { 410 callBack(newHeaders[i], false) 411 } 412 } 413 414 // filter logs of a single header in light client mode 415 func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log { 416 if bloomFilter(header.Bloom, addresses, topics) { 417 // Get the logs of the block 418 ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 419 defer cancel() 420 logsList, err := es.backend.GetLogs(ctx, header.Hash()) 421 if err != nil { 422 return nil 423 } 424 var unfiltered []*types.Log 425 for _, logs := range logsList { 426 for _, log := range logs { 427 logcopy := *log 428 logcopy.Removed = remove 429 unfiltered = append(unfiltered, &logcopy) 430 } 431 } 432 logs := filterLogs(unfiltered, nil, nil, addresses, topics) 433 if len(logs) > 0 && logs[0].TxHash == (common.Hash{}) { 434 // We have matching but non-derived logs 435 receipts, err := es.backend.GetReceipts(ctx, header.Hash()) 436 if err != nil { 437 return nil 438 } 439 unfiltered = unfiltered[:0] 440 for _, receipt := range receipts { 441 for _, log := range receipt.Logs { 442 logcopy := *log 443 logcopy.Removed = remove 444 unfiltered = append(unfiltered, &logcopy) 445 } 446 } 447 logs = filterLogs(unfiltered, nil, nil, addresses, topics) 448 } 449 return logs 450 } 451 return nil 452 } 453 454 // eventLoop (un)installs filters and processes mux events. 455 func (es *EventSystem) eventLoop() { 456 // Ensure all subscriptions get cleaned up 457 defer func() { 458 es.txsSub.Unsubscribe() 459 es.logsSub.Unsubscribe() 460 es.rmLogsSub.Unsubscribe() 461 es.pendingLogsSub.Unsubscribe() 462 es.chainSub.Unsubscribe() 463 es.stateSyncSub.Unsubscribe() 464 }() 465 466 index := make(filterIndex) 467 for i := UnknownSubscription; i < LastIndexSubscription; i++ { 468 index[i] = make(map[rpc.ID]*subscription) 469 } 470 471 for { 472 select { 473 case ev := <-es.txsCh: 474 es.handleTxsEvent(index, ev) 475 case ev := <-es.logsCh: 476 es.handleLogs(index, ev) 477 case ev := <-es.rmLogsCh: 478 es.handleRemovedLogs(index, ev) 479 case ev := <-es.pendingLogsCh: 480 es.handlePendingLogs(index, ev) 481 case ev := <-es.chainCh: 482 es.handleChainEvent(index, ev) 483 case ev := <-es.stateSyncCh: 484 es.handleStateSyncEvent(index, ev) 485 486 case f := <-es.install: 487 if f.typ == MinedAndPendingLogsSubscription { 488 // the type are logs and pending logs subscriptions 489 index[LogsSubscription][f.id] = f 490 index[PendingLogsSubscription][f.id] = f 491 } else { 492 index[f.typ][f.id] = f 493 } 494 close(f.installed) 495 496 case f := <-es.uninstall: 497 if f.typ == MinedAndPendingLogsSubscription { 498 // the type are logs and pending logs subscriptions 499 delete(index[LogsSubscription], f.id) 500 delete(index[PendingLogsSubscription], f.id) 501 } else { 502 delete(index[f.typ], f.id) 503 } 504 close(f.err) 505 506 // System stopped 507 case <-es.txsSub.Err(): 508 return 509 case <-es.logsSub.Err(): 510 return 511 case <-es.rmLogsSub.Err(): 512 return 513 case <-es.chainSub.Err(): 514 return 515 } 516 } 517 }