github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/neatptc/filters/filter_system.go (about) 1 package filters 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync" 8 "time" 9 10 "github.com/neatio-net/neatio" 11 "github.com/neatio-net/neatio/chain/core" 12 "github.com/neatio-net/neatio/chain/core/rawdb" 13 "github.com/neatio-net/neatio/chain/core/types" 14 "github.com/neatio-net/neatio/network/rpc" 15 "github.com/neatio-net/neatio/utilities/common" 16 "github.com/neatio-net/neatio/utilities/event" 17 ) 18 19 type Type byte 20 21 const ( 22 UnknownSubscription Type = iota 23 24 LogsSubscription 25 26 PendingLogsSubscription 27 28 MinedAndPendingLogsSubscription 29 30 PendingTransactionsSubscription 31 32 BlocksSubscription 33 34 LastIndexSubscription 35 ) 36 37 const ( 38 txChanSize = 4096 39 40 rmLogsChanSize = 10 41 42 logsChanSize = 10 43 44 chainEvChanSize = 10 45 ) 46 47 var ( 48 ErrInvalidSubscriptionID = errors.New("invalid id") 49 ) 50 51 type subscription struct { 52 id rpc.ID 53 typ Type 54 created time.Time 55 logsCrit neatio.FilterQuery 56 logs chan []*types.Log 57 hashes chan common.Hash 58 headers chan *types.Header 59 installed chan struct{} 60 err chan error 61 } 62 63 type EventSystem struct { 64 mux *event.TypeMux 65 backend Backend 66 lightMode bool 67 lastHead *types.Header 68 install chan *subscription 69 uninstall chan *subscription 70 } 71 72 func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { 73 m := &EventSystem{ 74 mux: mux, 75 backend: backend, 76 lightMode: lightMode, 77 install: make(chan *subscription), 78 uninstall: make(chan *subscription), 79 } 80 81 go m.eventLoop() 82 83 return m 84 } 85 86 type Subscription struct { 87 ID rpc.ID 88 f *subscription 89 es *EventSystem 90 unsubOnce sync.Once 91 } 92 93 func (sub *Subscription) Err() <-chan error { 94 return sub.f.err 95 } 96 97 func (sub *Subscription) Unsubscribe() { 98 sub.unsubOnce.Do(func() { 99 uninstallLoop: 100 for { 101 102 select { 103 case sub.es.uninstall <- sub.f: 104 break uninstallLoop 105 case <-sub.f.logs: 106 case <-sub.f.hashes: 107 case <-sub.f.headers: 108 } 109 } 110 111 <-sub.Err() 112 }) 113 } 114 115 func (es *EventSystem) subscribe(sub *subscription) *Subscription { 116 es.install <- sub 117 <-sub.installed 118 return &Subscription{ID: sub.id, f: sub, es: es} 119 } 120 121 func (es *EventSystem) SubscribeLogs(crit neatio.FilterQuery, logs chan []*types.Log) (*Subscription, error) { 122 var from, to rpc.BlockNumber 123 if crit.FromBlock == nil { 124 from = rpc.LatestBlockNumber 125 } else { 126 from = rpc.BlockNumber(crit.FromBlock.Int64()) 127 } 128 if crit.ToBlock == nil { 129 to = rpc.LatestBlockNumber 130 } else { 131 to = rpc.BlockNumber(crit.ToBlock.Int64()) 132 } 133 134 if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber { 135 return es.subscribePendingLogs(crit, logs), nil 136 } 137 138 if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { 139 return es.subscribeLogs(crit, logs), nil 140 } 141 142 if from >= 0 && to >= 0 && to >= from { 143 return es.subscribeLogs(crit, logs), nil 144 } 145 146 if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber { 147 return es.subscribeMinedPendingLogs(crit, logs), nil 148 } 149 150 if from >= 0 && to == rpc.LatestBlockNumber { 151 return es.subscribeLogs(crit, logs), nil 152 } 153 return nil, fmt.Errorf("invalid from and to block combination: from > to") 154 } 155 156 func (es *EventSystem) subscribeMinedPendingLogs(crit neatio.FilterQuery, logs chan []*types.Log) *Subscription { 157 sub := &subscription{ 158 id: rpc.NewID(), 159 typ: MinedAndPendingLogsSubscription, 160 logsCrit: crit, 161 created: time.Now(), 162 logs: logs, 163 hashes: make(chan common.Hash), 164 headers: make(chan *types.Header), 165 installed: make(chan struct{}), 166 err: make(chan error), 167 } 168 return es.subscribe(sub) 169 } 170 171 func (es *EventSystem) subscribeLogs(crit neatio.FilterQuery, logs chan []*types.Log) *Subscription { 172 sub := &subscription{ 173 id: rpc.NewID(), 174 typ: LogsSubscription, 175 logsCrit: crit, 176 created: time.Now(), 177 logs: logs, 178 hashes: make(chan common.Hash), 179 headers: make(chan *types.Header), 180 installed: make(chan struct{}), 181 err: make(chan error), 182 } 183 return es.subscribe(sub) 184 } 185 186 func (es *EventSystem) subscribePendingLogs(crit neatio.FilterQuery, logs chan []*types.Log) *Subscription { 187 sub := &subscription{ 188 id: rpc.NewID(), 189 typ: PendingLogsSubscription, 190 logsCrit: crit, 191 created: time.Now(), 192 logs: logs, 193 hashes: make(chan common.Hash), 194 headers: make(chan *types.Header), 195 installed: make(chan struct{}), 196 err: make(chan error), 197 } 198 return es.subscribe(sub) 199 } 200 201 func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription { 202 sub := &subscription{ 203 id: rpc.NewID(), 204 typ: BlocksSubscription, 205 created: time.Now(), 206 logs: make(chan []*types.Log), 207 hashes: make(chan common.Hash), 208 headers: headers, 209 installed: make(chan struct{}), 210 err: make(chan error), 211 } 212 return es.subscribe(sub) 213 } 214 215 func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription { 216 sub := &subscription{ 217 id: rpc.NewID(), 218 typ: PendingTransactionsSubscription, 219 created: time.Now(), 220 logs: make(chan []*types.Log), 221 hashes: hashes, 222 headers: make(chan *types.Header), 223 installed: make(chan struct{}), 224 err: make(chan error), 225 } 226 return es.subscribe(sub) 227 } 228 229 type filterIndex map[Type]map[rpc.ID]*subscription 230 231 func (es *EventSystem) broadcast(filters filterIndex, ev interface{}) { 232 if ev == nil { 233 return 234 } 235 236 switch e := ev.(type) { 237 case []*types.Log: 238 if len(e) > 0 { 239 for _, f := range filters[LogsSubscription] { 240 if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 241 f.logs <- matchedLogs 242 } 243 } 244 } 245 case core.RemovedLogsEvent: 246 for _, f := range filters[LogsSubscription] { 247 if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 248 f.logs <- matchedLogs 249 } 250 } 251 case *event.TypeMuxEvent: 252 switch muxe := e.Data.(type) { 253 case core.PendingLogsEvent: 254 for _, f := range filters[PendingLogsSubscription] { 255 if e.Time.After(f.created) { 256 if matchedLogs := filterLogs(muxe.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 { 257 f.logs <- matchedLogs 258 } 259 } 260 } 261 } 262 case core.TxPreEvent: 263 for _, f := range filters[PendingTransactionsSubscription] { 264 f.hashes <- e.Tx.Hash() 265 } 266 case core.ChainEvent: 267 for _, f := range filters[BlocksSubscription] { 268 f.headers <- e.Block.Header() 269 } 270 if es.lightMode && len(filters[LogsSubscription]) > 0 { 271 es.lightFilterNewHead(e.Block.Header(), func(header *types.Header, remove bool) { 272 for _, f := range filters[LogsSubscription] { 273 if matchedLogs := es.lightFilterLogs(header, f.logsCrit.Addresses, f.logsCrit.Topics, remove); len(matchedLogs) > 0 { 274 f.logs <- matchedLogs 275 } 276 } 277 }) 278 } 279 } 280 } 281 282 func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { 283 oldh := es.lastHead 284 es.lastHead = newHeader 285 if oldh == nil { 286 return 287 } 288 newh := newHeader 289 290 var oldHeaders, newHeaders []*types.Header 291 for oldh.Hash() != newh.Hash() { 292 if oldh.Number.Uint64() >= newh.Number.Uint64() { 293 oldHeaders = append(oldHeaders, oldh) 294 oldh = rawdb.ReadHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) 295 } 296 if oldh.Number.Uint64() < newh.Number.Uint64() { 297 newHeaders = append(newHeaders, newh) 298 newh = rawdb.ReadHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) 299 if newh == nil { 300 301 newh = oldh 302 } 303 } 304 } 305 306 for _, h := range oldHeaders { 307 callBack(h, true) 308 } 309 310 for i := len(newHeaders) - 1; i >= 0; i-- { 311 callBack(newHeaders[i], false) 312 } 313 } 314 315 func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log { 316 if bloomFilter(header.Bloom, addresses, topics) { 317 318 ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 319 defer cancel() 320 logsList, err := es.backend.GetLogs(ctx, header.Hash()) 321 if err != nil { 322 return nil 323 } 324 var unfiltered []*types.Log 325 for _, logs := range logsList { 326 for _, log := range logs { 327 logcopy := *log 328 logcopy.Removed = remove 329 unfiltered = append(unfiltered, &logcopy) 330 } 331 } 332 logs := filterLogs(unfiltered, nil, nil, addresses, topics) 333 if len(logs) > 0 && logs[0].TxHash == (common.Hash{}) { 334 335 receipts, err := es.backend.GetReceipts(ctx, header.Hash()) 336 if err != nil { 337 return nil 338 } 339 unfiltered = unfiltered[:0] 340 for _, receipt := range receipts { 341 for _, log := range receipt.Logs { 342 logcopy := *log 343 logcopy.Removed = remove 344 unfiltered = append(unfiltered, &logcopy) 345 } 346 } 347 logs = filterLogs(unfiltered, nil, nil, addresses, topics) 348 } 349 return logs 350 } 351 return nil 352 } 353 354 func (es *EventSystem) eventLoop() { 355 var ( 356 index = make(filterIndex) 357 sub = es.mux.Subscribe(core.PendingLogsEvent{}) 358 359 txCh = make(chan core.TxPreEvent, txChanSize) 360 txSub = es.backend.SubscribeTxPreEvent(txCh) 361 362 rmLogsCh = make(chan core.RemovedLogsEvent, rmLogsChanSize) 363 rmLogsSub = es.backend.SubscribeRemovedLogsEvent(rmLogsCh) 364 365 logsCh = make(chan []*types.Log, logsChanSize) 366 logsSub = es.backend.SubscribeLogsEvent(logsCh) 367 368 chainEvCh = make(chan core.ChainEvent, chainEvChanSize) 369 chainEvSub = es.backend.SubscribeChainEvent(chainEvCh) 370 ) 371 372 defer sub.Unsubscribe() 373 defer txSub.Unsubscribe() 374 defer rmLogsSub.Unsubscribe() 375 defer logsSub.Unsubscribe() 376 defer chainEvSub.Unsubscribe() 377 378 for i := UnknownSubscription; i < LastIndexSubscription; i++ { 379 index[i] = make(map[rpc.ID]*subscription) 380 } 381 382 for { 383 select { 384 case ev, active := <-sub.Chan(): 385 if !active { 386 return 387 } 388 es.broadcast(index, ev) 389 390 case ev := <-txCh: 391 es.broadcast(index, ev) 392 case ev := <-rmLogsCh: 393 es.broadcast(index, ev) 394 case ev := <-logsCh: 395 es.broadcast(index, ev) 396 case ev := <-chainEvCh: 397 es.broadcast(index, ev) 398 399 case f := <-es.install: 400 if f.typ == MinedAndPendingLogsSubscription { 401 402 index[LogsSubscription][f.id] = f 403 index[PendingLogsSubscription][f.id] = f 404 } else { 405 index[f.typ][f.id] = f 406 } 407 close(f.installed) 408 case f := <-es.uninstall: 409 if f.typ == MinedAndPendingLogsSubscription { 410 411 delete(index[LogsSubscription], f.id) 412 delete(index[PendingLogsSubscription], f.id) 413 } else { 414 delete(index[f.typ], f.id) 415 } 416 close(f.err) 417 418 case <-txSub.Err(): 419 return 420 case <-rmLogsSub.Err(): 421 return 422 case <-logsSub.Err(): 423 return 424 case <-chainEvSub.Err(): 425 return 426 } 427 } 428 }