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