mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-18 21:01:38 +00:00
core/types,eth/filters: pre-calculate blooms for matching
This commit is contained in:
parent
ac406c2fe7
commit
70731fb458
2 changed files with 97 additions and 34 deletions
|
|
@ -161,3 +161,11 @@ func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, b
|
||||||
func BloomLookup(bin Bloom, topic bytesBacked) bool {
|
func BloomLookup(bin Bloom, topic bytesBacked) bool {
|
||||||
return bin.Test(topic.Bytes())
|
return bin.Test(topic.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BloomValues returns the (index, value) byte pairs that a bloom filter would
|
||||||
|
// set when adding the given data. Callers can cache the result to avoid
|
||||||
|
// re-hashing the same input on repeated bloom membership tests.
|
||||||
|
func BloomValues(data []byte) (uint, byte, uint, byte, uint, byte) {
|
||||||
|
var buf [6]byte
|
||||||
|
return bloomValues(data, &buf)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,21 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// bloomProbe holds the precomputed bloom bit positions and bit masks for a
|
||||||
|
// single address or topic, so that bloom membership can be tested with plain
|
||||||
|
// bit-ANDs instead of re-hashing on every block.
|
||||||
|
type bloomProbe struct {
|
||||||
|
i1, i2, i3 uint
|
||||||
|
v1, v2, v3 byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// test reports whether all three bits set by this probe are present in bloom.
|
||||||
|
func (p *bloomProbe) test(bloom *types.Bloom) bool {
|
||||||
|
return bloom[p.i1]&p.v1 != 0 &&
|
||||||
|
bloom[p.i2]&p.v2 != 0 &&
|
||||||
|
bloom[p.i3]&p.v3 != 0
|
||||||
|
}
|
||||||
|
|
||||||
// Filter can be used to retrieve and filter logs.
|
// Filter can be used to retrieve and filter logs.
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
sys *FilterSystem
|
sys *FilterSystem
|
||||||
|
|
@ -40,6 +55,12 @@ type Filter struct {
|
||||||
addresses []common.Address
|
addresses []common.Address
|
||||||
topics [][]common.Hash
|
topics [][]common.Hash
|
||||||
|
|
||||||
|
// Precomputed bloom probes for addresses and topics. These mirror the
|
||||||
|
// structure of addresses/topics so repeated block-bloom tests avoid
|
||||||
|
// re-hashing the same inputs.
|
||||||
|
addrProbes []bloomProbe
|
||||||
|
topicProbes [][]bloomProbe
|
||||||
|
|
||||||
block *common.Hash // Block hash if filtering a single block
|
block *common.Hash // Block hash if filtering a single block
|
||||||
begin, end int64 // Range interval if filtering multiple blocks
|
begin, end int64 // Range interval if filtering multiple blocks
|
||||||
|
|
||||||
|
|
@ -71,11 +92,41 @@ func (sys *FilterSystem) NewBlockFilter(block common.Hash, addresses []common.Ad
|
||||||
// newFilter creates a generic filter that can either filter based on a block hash,
|
// 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.
|
// or based on range queries. The search criteria needs to be explicitly set.
|
||||||
func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter {
|
func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter {
|
||||||
return &Filter{
|
var addrProbes []bloomProbe
|
||||||
sys: sys,
|
if len(addresses) > 0 {
|
||||||
addresses: addresses,
|
addrProbes = make([]bloomProbe, len(addresses))
|
||||||
topics: topics,
|
for i, addr := range addresses {
|
||||||
|
addrProbes[i] = newBloomProbe(addr.Bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
var topicProbes [][]bloomProbe
|
||||||
|
if len(topics) > 0 {
|
||||||
|
topicProbes = make([][]bloomProbe, len(topics))
|
||||||
|
for i, sub := range topics {
|
||||||
|
if len(sub) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
probes := make([]bloomProbe, len(sub))
|
||||||
|
for j, t := range sub {
|
||||||
|
probes[j] = newBloomProbe(t.Bytes())
|
||||||
|
}
|
||||||
|
topicProbes[i] = probes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Filter{
|
||||||
|
sys: sys,
|
||||||
|
addresses: addresses,
|
||||||
|
topics: topics,
|
||||||
|
addrProbes: addrProbes,
|
||||||
|
topicProbes: topicProbes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBloomProbe precomputes the bloom bit-triplet for data.
|
||||||
|
func newBloomProbe(data []byte) bloomProbe {
|
||||||
|
var p bloomProbe
|
||||||
|
p.i1, p.v1, p.i2, p.v2, p.i3, p.v3 = types.BloomValues(data)
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs searches the blockchain for matching log entries, returning all from the
|
// Logs searches the blockchain for matching log entries, returning all from the
|
||||||
|
|
@ -457,12 +508,45 @@ func (f *Filter) unindexedLogs(ctx context.Context, chainView *filtermaps.ChainV
|
||||||
|
|
||||||
// blockLogs returns the logs matching the filter criteria within a single block.
|
// blockLogs returns the logs matching the filter criteria within a single block.
|
||||||
func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) {
|
func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) {
|
||||||
if bloomFilter(header.Bloom, f.addresses, f.topics) {
|
if f.bloomMatch(&header.Bloom) {
|
||||||
return f.checkMatches(ctx, header)
|
return f.checkMatches(ctx, header)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bloomMatch tests the filter's addresses and topics against the given bloom
|
||||||
|
// using the precomputed probes.
|
||||||
|
func (f *Filter) bloomMatch(bloom *types.Bloom) bool {
|
||||||
|
if len(f.addrProbes) > 0 {
|
||||||
|
matched := false
|
||||||
|
for i := range f.addrProbes {
|
||||||
|
if f.addrProbes[i].test(bloom) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, sub := range f.topicProbes {
|
||||||
|
if len(sub) == 0 {
|
||||||
|
continue // wildcard
|
||||||
|
}
|
||||||
|
matched := false
|
||||||
|
for i := range sub {
|
||||||
|
if sub[i].test(bloom) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// checkMatches checks if the receipts belonging to the given header contain any log events that
|
// checkMatches checks if the receipts belonging to the given header contain any log events that
|
||||||
// match the filter criteria. This function is called when the bloom filter signals a potential match.
|
// match the filter criteria. This function is called when the bloom filter signals a potential match.
|
||||||
func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
|
func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
|
||||||
|
|
@ -532,35 +616,6 @@ func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []comm
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool {
|
|
||||||
if len(addresses) > 0 {
|
|
||||||
var included bool
|
|
||||||
for _, addr := range addresses {
|
|
||||||
if types.BloomLookup(bloom, addr) {
|
|
||||||
included = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !included {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sub := range topics {
|
|
||||||
included := len(sub) == 0 // empty rule set == wildcard
|
|
||||||
for _, topic := range sub {
|
|
||||||
if types.BloomLookup(bloom, topic) {
|
|
||||||
included = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !included {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReceiptWithTx contains a receipt and its corresponding transaction
|
// ReceiptWithTx contains a receipt and its corresponding transaction
|
||||||
type ReceiptWithTx struct {
|
type ReceiptWithTx struct {
|
||||||
Receipt *types.Receipt
|
Receipt *types.Receipt
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue