mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-22 14:44:30 +00:00
feat: allow custom bloom production (#261)
## Why this should be merged See ava-labs/strevm#120. To get bloom filters for a block when the header doesn't contain the correct bloom because of SAE's delayed settlement, you need a custom accessor to provide arbitrary bloom. ## How this works Introduces an optional interface which, if satisfied by `filters.Backend` implementations, is used to override the Bloom filter instead of retrieving it from the `types.Header`. ## How this was tested Existing tests unchanged. Integration test demonstrates calling of the override method when present. --------- Co-authored-by: Arran Schlosberg <me@arranschlosberg.com>
This commit is contained in:
parent
e673c70970
commit
62502c6712
7 changed files with 272 additions and 3 deletions
46
core/bloom_indexer.libevm.go
Normal file
46
core/bloom_indexer.libevm.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2026 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/ava-labs/libevm/core/types"
|
||||
"github.com/ava-labs/libevm/ethdb"
|
||||
)
|
||||
|
||||
// BloomThrottling is the time to wait between processing two consecutive index sections.
|
||||
const BloomThrottling = bloomThrottling
|
||||
|
||||
// NewBloomIndexerBackend creates a [BloomIndexer] instance for the given database and section size,
|
||||
// allowing users to provide custom functionality to the bloom indexer.
|
||||
func NewBloomIndexerBackend(db ethdb.Database, size uint64) *BloomIndexer {
|
||||
return &BloomIndexer{
|
||||
db: db,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessWithBloomOverride is the same as [BloomIndexer.Process], but takes the header and bloom separately.
|
||||
// This must obey the same invariates as [BloomIndexer.Process], including calling [BloomIndexer.Reset]
|
||||
// to start a new section prior to this call, otherwise this function will panic.
|
||||
func (b *BloomIndexer) ProcessWithBloomOverride(header *types.Header, bloom types.Bloom) error {
|
||||
index := uint(header.Number.Uint64() - b.section*b.size)
|
||||
if err := b.gen.AddBloom(index, bloom); err != nil {
|
||||
return err
|
||||
}
|
||||
b.head = header.Hash()
|
||||
return nil
|
||||
}
|
||||
67
eth/bloombits.libevm.go
Normal file
67
eth/bloombits.libevm.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2026 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package eth
|
||||
|
||||
import (
|
||||
"github.com/ava-labs/libevm/core/bloombits"
|
||||
"github.com/ava-labs/libevm/ethdb"
|
||||
)
|
||||
|
||||
const (
|
||||
// BloomFilterThreads is the number of goroutines used locally per filter to
|
||||
// multiplex requests onto the global servicing goroutines.
|
||||
BloomFilterThreads = bloomFilterThreads
|
||||
|
||||
// BloomRetrievalBatch is the maximum number of bloom bit retrievals to
|
||||
// service in a single batch.
|
||||
BloomRetrievalBatch = bloomRetrievalBatch
|
||||
|
||||
// BloomRetrievalWait is the maximum time to wait for enough bloom bit
|
||||
// requests to accumulate request an entire batch (avoiding hysteresis).
|
||||
BloomRetrievalWait = bloomRetrievalWait
|
||||
)
|
||||
|
||||
// StartBloomHandlers starts a batch of goroutines to serve data for
|
||||
// [bloombits.Retrieval] requests from any number of filters. This is identical
|
||||
// to [Ethereum.startBloomHandlers], but exposed for independent use.
|
||||
func StartBloomHandlers(db ethdb.Database, sectionSize uint64) *BloomHandlers {
|
||||
bh := &BloomHandlers{
|
||||
Requests: make(chan chan *bloombits.Retrieval),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
eth := &Ethereum{
|
||||
bloomRequests: bh.Requests,
|
||||
closeBloomHandler: bh.quit,
|
||||
chainDb: db,
|
||||
}
|
||||
eth.startBloomHandlers(sectionSize)
|
||||
return bh
|
||||
}
|
||||
|
||||
// BloomHandlers serve data for [bloombits.Retrieval] requests from any number
|
||||
// of filters. [BloomHandlers.Close] MUST be called to release goroutines, after
|
||||
// which a send on the requests channel will block indefinitely.
|
||||
type BloomHandlers struct {
|
||||
Requests chan chan *bloombits.Retrieval
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
// Close releases resources in use by the [BloomHandlers]; repeated calls will
|
||||
// panic.
|
||||
func (bh *BloomHandlers) Close() {
|
||||
close(bh.quit)
|
||||
}
|
||||
30
eth/bloombits.libevm_test.go
Normal file
30
eth/bloombits.libevm_test.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2026 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package eth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/goleak"
|
||||
|
||||
"github.com/ava-labs/libevm/core/rawdb"
|
||||
)
|
||||
|
||||
func TestStartBloomHandlersNoLeaks(t *testing.T) {
|
||||
defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
|
||||
StartBloomHandlers(rawdb.NewMemoryDatabase(), 42).Close()
|
||||
}
|
||||
|
|
@ -290,7 +290,7 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *ty
|
|||
|
||||
// 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 bloomFilter(maybeOverrideBloom(header, f.sys.backend), f.addresses, f.topics) {
|
||||
return f.checkMatches(ctx, header)
|
||||
}
|
||||
return nil, nil
|
||||
|
|
@ -337,7 +337,7 @@ func (f *Filter) pendingLogs() []*types.Log {
|
|||
if block == nil || receipts == nil {
|
||||
return nil
|
||||
}
|
||||
if bloomFilter(block.Bloom(), f.addresses, f.topics) {
|
||||
if bloomFilter(maybeOverrideBloom(block.Header(), f.sys.backend), f.addresses, f.topics) {
|
||||
var unfiltered []*types.Log
|
||||
for _, r := range receipts {
|
||||
unfiltered = append(unfiltered, r.Logs...)
|
||||
|
|
|
|||
|
|
@ -508,7 +508,7 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func
|
|||
|
||||
// filter logs of a single header in light client mode
|
||||
func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log {
|
||||
if !bloomFilter(header.Bloom, addresses, topics) {
|
||||
if !bloomFilter(maybeOverrideBloom(header, es.backend), addresses, topics) {
|
||||
return nil
|
||||
}
|
||||
// Get the logs of the block
|
||||
|
|
|
|||
35
eth/filters/filter_system.libevm.go
Normal file
35
eth/filters/filter_system.libevm.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2026 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"github.com/ava-labs/libevm/core/types"
|
||||
)
|
||||
|
||||
// BloomOverrider is an optional extension to [Backend], allowing arbitrary
|
||||
// bloom filters to be returned for a header. If not implemented,
|
||||
// [types.Header.Bloom] is used instead.
|
||||
type BloomOverrider interface {
|
||||
OverrideHeaderBloom(*types.Header) types.Bloom
|
||||
}
|
||||
|
||||
func maybeOverrideBloom(header *types.Header, backend Backend) types.Bloom {
|
||||
if bo, ok := backend.(BloomOverrider); ok {
|
||||
return bo.OverrideHeaderBloom(header)
|
||||
}
|
||||
return header.Bloom
|
||||
}
|
||||
91
eth/filters/filter_system.libevm_test.go
Normal file
91
eth/filters/filter_system.libevm_test.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2026 the libevm authors.
|
||||
//
|
||||
// The libevm additions to go-ethereum are free software: you can redistribute
|
||||
// them and/or modify them 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 libevm additions are distributed in the hope that they 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
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ava-labs/libevm/core"
|
||||
"github.com/ava-labs/libevm/core/rawdb"
|
||||
"github.com/ava-labs/libevm/core/types"
|
||||
"github.com/ava-labs/libevm/rpc"
|
||||
)
|
||||
|
||||
type bloomOverriderBackend struct {
|
||||
*testBackend
|
||||
overridden chan struct{}
|
||||
}
|
||||
|
||||
var _ BloomOverrider = (*bloomOverriderBackend)(nil)
|
||||
|
||||
func (b *bloomOverriderBackend) OverrideHeaderBloom(header *types.Header) types.Bloom {
|
||||
b.overridden <- struct{}{}
|
||||
return header.Bloom
|
||||
}
|
||||
|
||||
func TestBloomOverride(t *testing.T) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
backend, sys := newTestFilterSystem(t, db, Config{})
|
||||
sut := &bloomOverriderBackend{
|
||||
testBackend: backend,
|
||||
overridden: make(chan struct{}),
|
||||
}
|
||||
sys.backend = sut
|
||||
|
||||
t.Run("lightFilterLogs", func(t *testing.T) {
|
||||
api := NewFilterAPI(sys, true /*lightMode*/)
|
||||
defer CloseAPI(api)
|
||||
|
||||
id, err := api.NewFilter(FilterCriteria{})
|
||||
require.NoErrorf(t, err, "%T.NewFilter()", api)
|
||||
defer api.UninstallFilter(id)
|
||||
|
||||
// If there is no historical header then the filter system returns early.
|
||||
for i := range int64(2) {
|
||||
sut.chainFeed.Send(core.ChainEvent{
|
||||
Block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(i),
|
||||
}),
|
||||
})
|
||||
}
|
||||
<-sut.overridden
|
||||
})
|
||||
|
||||
t.Run("blockLogs", func(t *testing.T) {
|
||||
hdr := &types.Header{Number: big.NewInt(0)}
|
||||
h := hdr.Hash()
|
||||
rawdb.WriteHeader(db, hdr)
|
||||
rawdb.WriteCanonicalHash(db, h, 0)
|
||||
rawdb.WriteHeaderNumber(db, h, 0)
|
||||
|
||||
go sys.NewBlockFilter(h, nil, nil).Logs(t.Context()) //nolint:errcheck // Known but irrelevant error
|
||||
<-sut.overridden
|
||||
})
|
||||
|
||||
t.Run("pendingLogs", func(t *testing.T) {
|
||||
hdr := &types.Header{Number: big.NewInt(1)}
|
||||
sut.pendingBlock = types.NewBlockWithHeader(hdr)
|
||||
sut.pendingReceipts = types.Receipts{}
|
||||
|
||||
n := rpc.PendingBlockNumber.Int64()
|
||||
go sys.NewRangeFilter(n, n, nil, nil).Logs(t.Context()) //nolint:errcheck // Known but irrelevant error
|
||||
<-sut.overridden
|
||||
})
|
||||
}
|
||||
Loading…
Reference in a new issue