core/types,eth/filters: pre-calculate blooms for matching

This commit is contained in:
Weixie Cui 2026-04-21 20:10:44 +08:00
parent ac406c2fe7
commit 70731fb458
2 changed files with 97 additions and 34 deletions

View file

@ -161,3 +161,11 @@ func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, b
func BloomLookup(bin Bloom, topic bytesBacked) bool {
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)
}

View file

@ -33,6 +33,21 @@ import (
"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.
type Filter struct {
sys *FilterSystem
@ -40,6 +55,12 @@ type Filter struct {
addresses []common.Address
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
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,
// or based on range queries. The search criteria needs to be explicitly set.
func newFilter(sys *FilterSystem, addresses []common.Address, topics [][]common.Hash) *Filter {
return &Filter{
sys: sys,
addresses: addresses,
topics: topics,
var addrProbes []bloomProbe
if len(addresses) > 0 {
addrProbes = make([]bloomProbe, len(addresses))
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
@ -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.
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 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
// 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) {
@ -532,35 +616,6 @@ func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []comm
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
type ReceiptWithTx struct {
Receipt *types.Receipt