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