// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . // package filters implements an ethereum filtering system for block, // transactions and log events. package filters import ( "fmt" "sync" "time" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" ) // FilterType determines the type of filter and is used to put the filter in to // the correct bucket when added. type FilterType byte const ( ChainFilter FilterType = iota // new block events filter PendingTxFilter // pending transaction filter LogFilter // new or removed log filter PendingLogFilter // pending log filter ) // FilterSystem manages filters that filter specific events such as // block, transaction and log events. The Filtering system can be used to listen // for specific LOG events fired by the EVM (Ethereum Virtual Machine). type FilterSystem struct { filterMu sync.RWMutex filterId int chainFilters map[int]*Filter pendingTxFilters map[int]*Filter logFilters map[int]*Filter pendingLogFilters map[int]*Filter // generic is an ugly hack for Get generic map[int]*Filter sub event.Subscription } // NewFilterSystem returns a newly allocated filter manager func NewFilterSystem(mux *event.TypeMux) *FilterSystem { fs := &FilterSystem{ chainFilters: make(map[int]*Filter), pendingTxFilters: make(map[int]*Filter), logFilters: make(map[int]*Filter), pendingLogFilters: make(map[int]*Filter), generic: make(map[int]*Filter), } fs.sub = mux.Subscribe( core.PendingLogsEvent{}, core.RemovedLogsEvent{}, core.ChainEvent{}, core.TxPreEvent{}, vm.Logs(nil), ) go fs.filterLoop() return fs } // Stop quits the filter loop required for polling events func (fs *FilterSystem) Stop() { fs.sub.Unsubscribe() } // Acquire filter system maps lock, required to force lock acquisition // sequence with filterMu acquired first to avoid deadlocks by callbacks func (fs *FilterSystem) Lock() { fs.filterMu.Lock() } // Release filter system maps lock func (fs *FilterSystem) Unlock() { fs.filterMu.Unlock() } // Add adds a filter to the filter manager // Expects filterMu to be locked. func (fs *FilterSystem) Add(filter *Filter, filterType FilterType) (int, error) { id := fs.filterId filter.created = time.Now() switch filterType { case ChainFilter: fs.chainFilters[id] = filter case PendingTxFilter: fs.pendingTxFilters[id] = filter case LogFilter: fs.logFilters[id] = filter case PendingLogFilter: fs.pendingLogFilters[id] = filter default: return 0, fmt.Errorf("unknown filter type %v", filterType) } fs.generic[id] = filter fs.filterId++ return id, nil } // Remove removes a filter by filter id // Expects filterMu to be locked. func (fs *FilterSystem) Remove(id int) { delete(fs.chainFilters, id) delete(fs.pendingTxFilters, id) delete(fs.logFilters, id) delete(fs.pendingLogFilters, id) delete(fs.generic, id) } func (fs *FilterSystem) Get(id int) *Filter { fs.filterMu.RLock() defer fs.filterMu.RUnlock() return fs.generic[id] } // filterLoop waits for specific events from ethereum and fires their handlers // when the filter matches the requirements. func (fs *FilterSystem) filterLoop() { for event := range fs.sub.Chan() { switch ev := event.Data.(type) { case core.ChainEvent: fs.filterMu.RLock() for _, filter := range fs.chainFilters { if filter.BlockCallback != nil && !filter.created.After(event.Time) { filter.BlockCallback(ev.Block, ev.Logs) } } fs.filterMu.RUnlock() case core.TxPreEvent: fs.filterMu.RLock() for _, filter := range fs.pendingTxFilters { if filter.TransactionCallback != nil && !filter.created.After(event.Time) { filter.TransactionCallback(ev.Tx) } } fs.filterMu.RUnlock() case vm.Logs: fs.filterMu.RLock() for _, filter := range fs.logFilters { if filter.LogCallback != nil && !filter.created.After(event.Time) { for _, log := range filter.FilterLogs(ev) { filter.LogCallback(log, false) } } } fs.filterMu.RUnlock() case core.RemovedLogsEvent: fs.filterMu.RLock() for _, filter := range fs.logFilters { if filter.LogCallback != nil && !filter.created.After(event.Time) { for _, removedLog := range filter.FilterLogs(ev.Logs) { filter.LogCallback(removedLog, true) } } } fs.filterMu.RUnlock() case core.PendingLogsEvent: fs.filterMu.RLock() for _, filter := range fs.pendingLogFilters { if filter.LogCallback != nil && !filter.created.After(event.Time) { for _, pendingLog := range ev.Logs { filter.LogCallback(pendingLog, false) } } } fs.filterMu.RUnlock() } } }