github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/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 "errors" 23 "fmt" 24 "sync" 25 "time" 26 27 "github.com/atheioschain/go-atheios/common" 28 "github.com/atheioschain/go-atheios/core" 29 "github.com/atheioschain/go-atheios/core/types" 30 "github.com/atheioschain/go-atheios/event" 31 "github.com/atheioschain/go-atheios/rpc" 32 "golang.org/x/net/context" 33 ) 34 35 // Type determines the kind of filter and is used to put the filter in to 36 // the correct bucket when added. 37 type Type byte 38 39 const ( 40 // UnknownSubscription indicates an unknown subscription type 41 UnknownSubscription Type = iota 42 // LogsSubscription queries for new or removed (chain reorg) logs 43 LogsSubscription 44 // PendingLogsSubscription queries for logs in pending blocks 45 PendingLogsSubscription 46 // MinedAndPendingLogsSubscription queries for logs in mined and pending blocks. 47 MinedAndPendingLogsSubscription 48 // PendingTransactionsSubscription queries tx hashes for pending 49 // transactions entering the pending state 50 PendingTransactionsSubscription 51 // BlocksSubscription queries hashes for blocks that are imported 52 BlocksSubscription 53 // LastSubscription keeps track of the last index 54 LastIndexSubscription 55 ) 56 57 var ( 58 ErrInvalidSubscriptionID = errors.New("invalid id") 59 ) 60 61 type subscription struct { 62 id rpc.ID 63 typ Type 64 created time.Time 65 logsCrit FilterCriteria 66 logs chan []*types.Log 67 hashes chan common.Hash 68 headers chan *types.Header 69 installed chan struct{} // closed when the filter is installed 70 err chan error // closed when the filter is uninstalled 71 } 72 73 // EventSystem creates subscriptions, processes events and broadcasts them to the 74 // subscription which match the subscription criteria. 75 type EventSystem struct { 76 mux *event.TypeMux 77 sub *event.TypeMuxSubscription 78 backend Backend 79 lightMode bool 80 lastHead *types.Header 81 install chan *subscription // install filter for event notification 82 uninstall chan *subscription // remove filter for event notification 83 } 84 85 // NewEventSystem creates a new manager that listens for event on the given mux, 86 // parses and filters them. It uses the all map to retrieve filter changes. The 87 // work loop holds its own index that is used to forward events to filters. 88 // 89 // The returned manager has a loop that needs to be stopped with the Stop function 90 // or by stopping the given mux. 91 func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { 92 m := &EventSystem{ 93 mux: mux, 94 backend: backend, 95 lightMode: lightMode, 96 install: make(chan *subscription), 97 uninstall: make(chan *subscription), 98 } 99 100 go m.eventLoop() 101 102 return m 103 } 104 105 // Subscription is created when the client registers itself for a particular event. 106 type Subscription struct { 107 ID rpc.ID 108 f *subscription 109 es *EventSystem 110 unsubOnce sync.Once 111 } 112 113 // Err returns a channel that is closed when unsubscribed. 114 func (sub *Subscription) Err() <-chan error { 115 return sub.f.err 116 } 117 118 // Unsubscribe uninstalls the subscription from the event broadcast loop. 119 func (sub *Subscription) Unsubscribe() { 120 sub.unsubOnce.Do(func() { 121 uninstallLoop: 122 for { 123 // write uninstall request and consume logs/hashes. This prevents 124 // the eventLoop broadcast method to deadlock when writing to the 125 // filter event channel while the subscription loop is waiting for 126 // this method to return (and thus not reading these events). 127 select { 128 case sub.es.uninstall <- sub.f: 129 break uninstallLoop 130 case <-sub.f.logs: 131 case <-sub.f.hashes: 132 case <-sub.f.headers: 133 } 134 } 135 136 // wait for filter to be uninstalled in work loop before returning 137 // this ensures that the manager won't use the event channel which 138 // will probably be closed by the client asap after this method returns. 139 <-sub.Err() 140 }) 141 } 142 143 // subscribe installs the subscription in the event broadcast loop. 144 func (es *EventSystem) subscribe(sub *subscription) *Subscription { 145 es.install <- sub 146 <-sub.installed 147 return &Subscription{ID: sub.id, f: sub, es: es} 148 } 149 150 // SubscribeLogs creates a subscription that will write all logs matching the 151 // given criteria to the given logs channel. Default value for the from and to 152 // block is "latest". If the fromBlock > toBlock an error is returned. 153 func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*types.Log) (*Subscription, error) { 154 var from, to rpc.BlockNumber 155 if crit.FromBlock == nil { 156 from = rpc.LatestBlockNumber 157 } else { 158 from = rpc.BlockNumber(crit.FromBlock.Int64()) 159 } 160 if crit.ToBlock == nil { 161 to = rpc.LatestBlockNumber 162 } else { 163 to = rpc.BlockNumber(crit.ToBlock.Int64()) 164 } 165 166 // only interested in pending logs 167 if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber { 168 return es.subscribePendingLogs(crit, logs), nil 169 } 170 // only interested in new mined logs 171 if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { 172 return es.subscribeLogs(crit, logs), nil 173 } 174 // only interested in mined logs within a specific block range 175 if from >= 0 && to >= 0 && to >= from { 176 return es.subscribeLogs(crit, logs), nil 177 } 178 // interested in mined logs from a specific block number, new logs and pending logs 179 if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber { 180 return es.subscribeMinedPendingLogs(crit, logs), nil 181 } 182 // interested in logs from a specific block number to new mined blocks 183 if from >= 0 && to == rpc.LatestBlockNumber { 184 return es.subscribeLogs(crit, logs), nil 185 } 186 return nil, fmt.Errorf("invalid from and to block combination: from > to") 187 } 188 189 // subscribeMinedPendingLogs creates a subscription that returned mined and 190 // pending logs that match the given criteria. 191 func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription { 192 sub := &subscription{ 193 id: rpc.NewID(), 194 typ: MinedAndPendingLogsSubscription, 195 logsCrit: crit, 196 created: time.Now(), 197 logs: logs, 198 hashes: make(chan common.Hash), 199 headers: make(chan *types.Header), 200 installed: make(chan struct{}), 201 err: make(chan error), 202 } 203 204 return es.subscribe(sub) 205 } 206 207 // subscribeLogs creates a subscription that will write all logs matching the 208 // given criteria to the given logs channel. 209 func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription { 210 sub := &subscription{ 211 id: rpc.NewID(), 212 typ: LogsSubscription, 213 logsCrit: crit, 214 created: time.Now(), 215 logs: logs, 216 hashes: make(chan common.Hash), 217 headers: make(chan *types.Header), 218 installed: make(chan struct{}), 219 err: make(chan error), 220 } 221 222 return es.subscribe(sub) 223 } 224 225 // subscribePendingLogs creates a subscription that writes transaction hashes for 226 // transactions that enter the transaction pool. 227 func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []*types.Log) *Subscription { 228 sub := &subscription{ 229 id: rpc.NewID(), 230 typ: PendingLogsSubscription, 231 logsCrit: crit, 232 created: time.Now(), 233 logs: logs, 234 hashes: make(chan common.Hash), 235 headers: make(chan *types.Header), 236 installed: make(chan struct{}), 237 err: make(chan error), 238 } 239 240 return es.subscribe(sub) 241 } 242 243 // SubscribeNewHeads creates a subscription that writes the header of a block that is 244 // imported in the chain. 245 func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription { 246 sub := &subscription{ 247 id: rpc.NewID(), 248 typ: BlocksSubscription, 249 created: time.Now(), 250 logs: make(chan []*types.Log), 251 hashes: make(chan common.Hash), 252 headers: headers, 253 installed: make(chan struct{}), 254 err: make(chan error), 255 } 256 257 return es.subscribe(sub) 258 } 259 260 // SubscribePendingTxEvents creates a subscription that writes transaction hashes for 261 // transactions that enter the transaction pool. 262 func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription { 263 sub := &subscription{ 264 id: rpc.NewID(), 265 typ: PendingTransactionsSubscription, 266 created: time.Now(), 267 logs: make(chan []*types.Log), 268 hashes: hashes, 269 headers: make(chan *types.Header), 270 installed: make(chan struct{}), 271 err: make(chan error), 272 } 273 274 return es.subscribe(sub) 275 } 276 277 type filterIndex map[Type]map[rpc.ID]*subscription 278 279 // broadcast event to filters that match criteria. 280 func (es *EventSystem) broadcast(filters filterIndex, ev *event.TypeMuxEvent) { 281 if ev == nil { 282 return 283 } 284 285 switch e := ev.Data.(type) { 286 case []*types.Log: 287 if len(e) > 0 { 288 for _, f := range filters[LogsSubscription] { 289 if ev.Time.After(f.created) { 290 if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 291 f.logs <- matchedLogs 292 } 293 } 294 } 295 } 296 case core.RemovedLogsEvent: 297 for _, f := range filters[LogsSubscription] { 298 if ev.Time.After(f.created) { 299 if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 300 f.logs <- matchedLogs 301 } 302 } 303 } 304 case core.PendingLogsEvent: 305 for _, f := range filters[PendingLogsSubscription] { 306 if ev.Time.After(f.created) { 307 if matchedLogs := filterLogs(e.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 308 f.logs <- matchedLogs 309 } 310 } 311 } 312 case core.TxPreEvent: 313 for _, f := range filters[PendingTransactionsSubscription] { 314 if ev.Time.After(f.created) { 315 f.hashes <- e.Tx.Hash() 316 } 317 } 318 case core.ChainEvent: 319 for _, f := range filters[BlocksSubscription] { 320 if ev.Time.After(f.created) { 321 f.headers <- e.Block.Header() 322 } 323 } 324 if es.lightMode && len(filters[LogsSubscription]) > 0 { 325 es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) { 326 for _, f := range filters[LogsSubscription] { 327 if ev.Time.After(f.created) { 328 if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { 329 f.logs <- matchedLogs 330 } 331 } 332 } 333 }) 334 } 335 } 336 } 337 338 func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { 339 oldh := es.lastHead 340 es.lastHead = newHeader 341 if oldh == nil { 342 return 343 } 344 newh := newHeader 345 // find common ancestor, create list of rolled back and new block hashes 346 var oldHeaders, newHeaders []*types.Header 347 for oldh.Hash() != newh.Hash() { 348 if oldh.Number.Uint64() >= newh.Number.Uint64() { 349 oldHeaders = append(oldHeaders, oldh) 350 oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) 351 } 352 if oldh.Number.Uint64() < newh.Number.Uint64() { 353 newHeaders = append(newHeaders, newh) 354 newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) 355 if newh == nil { 356 // happens when CHT syncing, nothing to do 357 newh = oldh 358 } 359 } 360 } 361 // roll back old blocks 362 for _, h := range oldHeaders { 363 callBack(h, true) 364 } 365 // check new blocks (array is in reverse order) 366 for i := len(newHeaders) - 1; i >= 0; i-- { 367 callBack(newHeaders[i], false) 368 } 369 } 370 371 // filter logs of a single header in light client mode 372 func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log { 373 if bloomFilter(header.Bloom, addresses, topics) { 374 // Get the logs of the block 375 ctx, _ := context.WithTimeout(context.Background(), time.Second*5) 376 receipts, err := es.backend.GetReceipts(ctx, header.Hash()) 377 if err != nil { 378 return nil 379 } 380 var unfiltered []*types.Log 381 for _, receipt := range receipts { 382 for _, log := range receipt.Logs { 383 logcopy := *log 384 logcopy.Removed = remove 385 unfiltered = append(unfiltered, &logcopy) 386 } 387 } 388 logs := filterLogs(unfiltered, nil, nil, addresses, topics) 389 return logs 390 } 391 return nil 392 } 393 394 // eventLoop (un)installs filters and processes mux events. 395 func (es *EventSystem) eventLoop() { 396 var ( 397 index = make(filterIndex) 398 sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, []*types.Log{}, core.TxPreEvent{}, core.ChainEvent{}) 399 ) 400 401 for i := UnknownSubscription; i < LastIndexSubscription; i++ { 402 index[i] = make(map[rpc.ID]*subscription) 403 } 404 405 for { 406 select { 407 case ev, active := <-sub.Chan(): 408 if !active { // system stopped 409 return 410 } 411 es.broadcast(index, ev) 412 case f := <-es.install: 413 if f.typ == MinedAndPendingLogsSubscription { 414 // the type are logs and pending logs subscriptions 415 index[LogsSubscription][f.id] = f 416 index[PendingLogsSubscription][f.id] = f 417 } else { 418 index[f.typ][f.id] = f 419 } 420 close(f.installed) 421 case f := <-es.uninstall: 422 if f.typ == MinedAndPendingLogsSubscription { 423 // the type are logs and pending logs subscriptions 424 delete(index[LogsSubscription], f.id) 425 delete(index[PendingLogsSubscription], f.id) 426 } else { 427 delete(index[f.typ], f.id) 428 } 429 close(f.err) 430 } 431 } 432 }