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