|
|
@ -18,6 +18,7 @@ package filters |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"context" |
|
|
|
|
|
|
|
"errors" |
|
|
|
"math/big" |
|
|
|
"math/big" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
@ -33,6 +34,7 @@ type Backend interface { |
|
|
|
ChainDb() ethdb.Database |
|
|
|
ChainDb() ethdb.Database |
|
|
|
EventMux() *event.TypeMux |
|
|
|
EventMux() *event.TypeMux |
|
|
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) |
|
|
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) |
|
|
|
|
|
|
|
HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) |
|
|
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
|
|
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
|
|
|
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) |
|
|
|
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) |
|
|
|
|
|
|
|
|
|
|
@ -49,17 +51,19 @@ type Backend interface { |
|
|
|
type Filter struct { |
|
|
|
type Filter struct { |
|
|
|
backend Backend |
|
|
|
backend Backend |
|
|
|
|
|
|
|
|
|
|
|
db ethdb.Database |
|
|
|
db ethdb.Database |
|
|
|
begin, end int64 |
|
|
|
addresses []common.Address |
|
|
|
addresses []common.Address |
|
|
|
topics [][]common.Hash |
|
|
|
topics [][]common.Hash |
|
|
|
|
|
|
|
|
|
|
|
block common.Hash // Block hash if filtering a single block
|
|
|
|
|
|
|
|
begin, end int64 // Range interval if filtering multiple blocks
|
|
|
|
|
|
|
|
|
|
|
|
matcher *bloombits.Matcher |
|
|
|
matcher *bloombits.Matcher |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// New creates a new filter which uses a bloom filter on blocks to figure out whether
|
|
|
|
// NewRangeFilter creates a new filter which uses a bloom filter on blocks to
|
|
|
|
// a particular block is interesting or not.
|
|
|
|
// figure out whether a particular block is interesting or not.
|
|
|
|
func New(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { |
|
|
|
func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { |
|
|
|
// Flatten the address and topic filter clauses into a single bloombits filter
|
|
|
|
// Flatten the address and topic filter clauses into a single bloombits filter
|
|
|
|
// system. Since the bloombits are not positional, nil topics are permitted,
|
|
|
|
// system. Since the bloombits are not positional, nil topics are permitted,
|
|
|
|
// which get flattened into a nil byte slice.
|
|
|
|
// which get flattened into a nil byte slice.
|
|
|
@ -78,23 +82,52 @@ func New(backend Backend, begin, end int64, addresses []common.Address, topics [ |
|
|
|
} |
|
|
|
} |
|
|
|
filters = append(filters, filter) |
|
|
|
filters = append(filters, filter) |
|
|
|
} |
|
|
|
} |
|
|
|
// Assemble and return the filter
|
|
|
|
|
|
|
|
size, _ := backend.BloomStatus() |
|
|
|
size, _ := backend.BloomStatus() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a generic filter and convert it into a range filter
|
|
|
|
|
|
|
|
filter := newFilter(backend, addresses, topics) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
filter.matcher = bloombits.NewMatcher(size, filters) |
|
|
|
|
|
|
|
filter.begin = begin |
|
|
|
|
|
|
|
filter.end = end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return filter |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NewBlockFilter creates a new filter which directly inspects the contents of
|
|
|
|
|
|
|
|
// a block to figure out whether it is interesting or not.
|
|
|
|
|
|
|
|
func NewBlockFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { |
|
|
|
|
|
|
|
// Create a generic filter and convert it into a block filter
|
|
|
|
|
|
|
|
filter := newFilter(backend, addresses, topics) |
|
|
|
|
|
|
|
filter.block = block |
|
|
|
|
|
|
|
return filter |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// newFilter creates a generic filter that can either filter based on a block hash,
|
|
|
|
|
|
|
|
// or based on range queries. The search criteria needs to be explicitly set.
|
|
|
|
|
|
|
|
func newFilter(backend Backend, addresses []common.Address, topics [][]common.Hash) *Filter { |
|
|
|
return &Filter{ |
|
|
|
return &Filter{ |
|
|
|
backend: backend, |
|
|
|
backend: backend, |
|
|
|
begin: begin, |
|
|
|
|
|
|
|
end: end, |
|
|
|
|
|
|
|
addresses: addresses, |
|
|
|
addresses: addresses, |
|
|
|
topics: topics, |
|
|
|
topics: topics, |
|
|
|
db: backend.ChainDb(), |
|
|
|
db: backend.ChainDb(), |
|
|
|
matcher: bloombits.NewMatcher(size, filters), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Logs searches the blockchain for matching log entries, returning all from the
|
|
|
|
// Logs searches the blockchain for matching log entries, returning all from the
|
|
|
|
// first block that contains matches, updating the start of the filter accordingly.
|
|
|
|
// first block that contains matches, updating the start of the filter accordingly.
|
|
|
|
func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { |
|
|
|
func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { |
|
|
|
|
|
|
|
// If we're doing singleton block filtering, execute and return
|
|
|
|
|
|
|
|
if f.block != (common.Hash{}) { |
|
|
|
|
|
|
|
header, err := f.backend.HeaderByHash(ctx, f.block) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if header == nil { |
|
|
|
|
|
|
|
return nil, errors.New("unknown block") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return f.blockLogs(ctx, header) |
|
|
|
|
|
|
|
} |
|
|
|
// Figure out the limits of the filter range
|
|
|
|
// Figure out the limits of the filter range
|
|
|
|
header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) |
|
|
|
header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) |
|
|
|
if header == nil { |
|
|
|
if header == nil { |
|
|
@ -187,13 +220,23 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e |
|
|
|
if header == nil || err != nil { |
|
|
|
if header == nil || err != nil { |
|
|
|
return logs, err |
|
|
|
return logs, err |
|
|
|
} |
|
|
|
} |
|
|
|
if bloomFilter(header.Bloom, f.addresses, f.topics) { |
|
|
|
found, err := f.blockLogs(ctx, header) |
|
|
|
found, err := f.checkMatches(ctx, header) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return logs, err |
|
|
|
return logs, err |
|
|
|
} |
|
|
|
} |
|
|
|
logs = append(logs, found...) |
|
|
|
logs = append(logs, found...) |
|
|
|
} |
|
|
|
|
|
|
|
return logs, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// blockLogs returns the logs matching the filter criteria within a single block.
|
|
|
|
|
|
|
|
func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { |
|
|
|
|
|
|
|
if bloomFilter(header.Bloom, f.addresses, f.topics) { |
|
|
|
|
|
|
|
found, err := f.checkMatches(ctx, header) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return logs, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
logs = append(logs, found...) |
|
|
|
} |
|
|
|
} |
|
|
|
return logs, nil |
|
|
|
return logs, nil |
|
|
|
} |
|
|
|
} |
|
|
|