From bd0eb5775f6198f0a4268a7c9abfe12815156d70 Mon Sep 17 00:00:00 2001 From: ucwong Date: Mon, 17 Feb 2025 23:32:09 +0800 Subject: [PATCH] create block's bloom by merging receipts' bloom and filter test fix --- core/block_validator.go | 6 +++- core/rawdb/accessors_chain_test.go | 4 +-- core/state_processor.go | 2 +- core/types/block.go | 5 ++- core/types/bloom9.go | 39 +++++++++++----------- core/types/bloom9_test.go | 52 +++++++++++++++++++++++++++--- core/types/receipt.go | 4 +-- core/types/receipt_test.go | 2 +- ctxc/filters/api.go | 9 +++--- ctxc/filters/filter.go | 52 +++++++++--------------------- ctxc/filters/filter_test.go | 10 +++++- 11 files changed, 113 insertions(+), 72 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 8ca990f02b..d7a55e4f40 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -92,7 +92,11 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. - rbloom := types.CreateBloom(receipts) + // + // Receipts must go through MakeReceipt to calculate the receipt's bloom + // already. Merge the receipt's bloom together instead of recalculating + // everything. + rbloom := types.MergeBloom(receipts) if rbloom != header.Bloom { return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 96338877f0..83cc6c1e3f 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -290,7 +290,7 @@ func TestBlockReceiptStorage(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), GasUsed: 111111, } - receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1}) + receipt1.Bloom = types.CreateBloom(receipt1) receipt2 := &types.Receipt{ PostState: common.Hash{2}.Bytes(), @@ -303,7 +303,7 @@ func TestBlockReceiptStorage(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), GasUsed: 222222, } - receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2}) + receipt2.Bloom = types.CreateBloom(receipt2) receipts := []*types.Receipt{receipt1, receipt2} // Check that no receipt entries are in a pristine database diff --git a/core/state_processor.go b/core/state_processor.go index 94bcf8fc67..41786f7891 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -153,7 +153,7 @@ func MakeReceipt(cvm *vm.CVM, result *ExecutionResult, statedb *state.StateDB, b } // Set the receipt logs and create a bloom for filtering receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + receipt.Bloom = types.CreateBloom(receipt) receipt.BlockHash = blockHash receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) diff --git a/core/types/block.go b/core/types/block.go index 5178f404e9..6697135ef9 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -281,7 +281,10 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher Hasher) *B b.header.ReceiptHash = EmptyRootHash } else { b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher) - b.header.Bloom = CreateBloom(receipts) + // Receipts must go through MakeReceipt to calculate the receipt's bloom + // already. Merge the receipt's bloom together instead of recalculating + // everything. + b.header.Bloom = MergeBloom(receipts) } if len(uncles) == 0 { diff --git a/core/types/bloom9.go b/core/types/bloom9.go index 7f449e9e30..6170a80d15 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -100,32 +100,35 @@ func (b *Bloom) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("Bloom", input, b[:]) } -// CreateBloom creates a bloom filter out of the give Receipts (+Logs) -func CreateBloom(receipts Receipts) Bloom { - buf := make([]byte, 6) - var bin Bloom - for _, receipt := range receipts { - for _, log := range receipt.Logs { - bin.add(log.Address.Bytes(), buf) - for _, b := range log.Topics { - bin.add(b[:], buf) - } +// CreateBloom creates a bloom filter out of the give Receipt (+Logs) +func CreateBloom(receipt *Receipt) Bloom { + var ( + bin Bloom + buf = make([]byte, 6) + ) + for _, log := range receipt.Logs { + bin.add(log.Address.Bytes(), buf) + for _, b := range log.Topics { + bin.add(b[:], buf) } } return bin } -// LogsBloom returns the bloom bytes for the given logs -func LogsBloom(logs []*Log) []byte { - buf := make([]byte, 6) +// MergeBloom merges the precomputed bloom filters in the Receipts without +// recalculating them. It assumes that each receipt’s Bloom field is already +// correctly populated. +func MergeBloom(receipts Receipts) Bloom { var bin Bloom - for _, log := range logs { - bin.add(log.Address.Bytes(), buf) - for _, b := range log.Topics { - bin.add(b[:], buf) + for _, receipt := range receipts { + if len(receipt.Logs) != 0 { + bl := receipt.Bloom.Bytes() + for i := range bin { + bin[i] |= bl[i] + } } } - return bin[:] + return bin } // Bloom9 returns the bloom filter for the given data diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go index feafa36404..e0ef8125fc 100644 --- a/core/types/bloom9_test.go +++ b/core/types/bloom9_test.go @@ -127,26 +127,70 @@ func BenchmarkCreateBloom(b *testing.B) { for i := 0; i < 200; i += 2 { copy(rLarge[i:], rSmall) } - b.Run("small", func(b *testing.B) { + b.Run("small-createbloom", func(b *testing.B) { b.ReportAllocs() + for i := 0; i < b.N; i++ { + for _, receipt := range rSmall { + receipt.Bloom = CreateBloom(receipt) + } + } + b.StopTimer() + + bl := MergeBloom(rSmall) + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) + b.Run("large-createbloom", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for _, receipt := range rLarge { + receipt.Bloom = CreateBloom(receipt) + } + } + b.StopTimer() + + bl := MergeBloom(rLarge) + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) + b.Run("small-mergebloom", func(b *testing.B) { + for _, receipt := range rSmall { + receipt.Bloom = CreateBloom(receipt) + } + b.ReportAllocs() + b.ResetTimer() + var bl Bloom for i := 0; i < b.N; i++ { - bl = CreateBloom(rSmall) + bl = MergeBloom(rSmall) } b.StopTimer() + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") got := crypto.Keccak256Hash(bl.Bytes()) if got != exp { b.Errorf("Got %x, exp %x", got, exp) } }) - b.Run("large", func(b *testing.B) { + b.Run("large-mergebloom", func(b *testing.B) { + for _, receipt := range rLarge { + receipt.Bloom = CreateBloom(receipt) + } b.ReportAllocs() + b.ResetTimer() + var bl Bloom for i := 0; i < b.N; i++ { - bl = CreateBloom(rLarge) + bl = MergeBloom(rLarge) } b.StopTimer() + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") got := crypto.Keccak256Hash(bl.Bytes()) if got != exp { diff --git a/core/types/receipt.go b/core/types/receipt.go index 0ba88a988c..198969531d 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -232,7 +232,7 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { for i, log := range stored.Logs { r.Logs[i] = (*Log)(log) } - r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + r.Bloom = CreateBloom((*Receipt)(r)) return nil } @@ -253,7 +253,7 @@ func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { for i, log := range stored.Logs { r.Logs[i] = (*Log)(log) } - r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + r.Bloom = CreateBloom((*Receipt)(r)) return nil } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 120ce68097..fb9f2cad17 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -52,7 +52,7 @@ func TestLegacyReceiptDecoding(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), GasUsed: 111111, } - receipt.Bloom = CreateBloom(Receipts{receipt}) + receipt.Bloom = CreateBloom(receipt) for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { diff --git a/ctxc/filters/api.go b/ctxc/filters/api.go index c2e0243dfa..272674379a 100644 --- a/ctxc/filters/api.go +++ b/ctxc/filters/api.go @@ -33,10 +33,11 @@ import ( ) var ( - errInvalidTopic = errors.New("invalid topic(s)") - errFilterNotFound = errors.New("filter not found") - errInvalidBlockRange = errors.New("invalid block range params") - errExceedMaxTopics = errors.New("exceed max topics") + errInvalidTopic = errors.New("invalid topic(s)") + errFilterNotFound = errors.New("filter not found") + errInvalidBlockRange = errors.New("invalid block range params") + errExceedMaxTopics = errors.New("exceed max topics") + errPendingLogsUnsupported = errors.New("pending logs are not supported") ) // The maximum number of topic criteria allowed, vm.LOG4 - vm.LOG0 diff --git a/ctxc/filters/filter.go b/ctxc/filters/filter.go index 28a9750237..d683527584 100644 --- a/ctxc/filters/filter.go +++ b/ctxc/filters/filter.go @@ -20,6 +20,7 @@ import ( "context" "errors" "math/big" + "slices" "github.com/CortexFoundation/CortexTheseus/common" "github.com/CortexFoundation/CortexTheseus/core/bloombits" @@ -104,22 +105,12 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { if header == nil { return nil, errors.New("unknown block") } - return f.blockLogs(ctx, header, false) + return f.blockLogs(ctx, header) } - var ( - beginPending = f.begin == rpc.PendingBlockNumber.Int64() - endPending = f.end == rpc.PendingBlockNumber.Int64() - ) - - // special case for pending logs - if beginPending && !endPending { - return nil, errInvalidBlockRange - } - - // Short-cut if all we care about is pending logs - if beginPending && endPending { - return f.pendingLogs(), nil + // Disallow pending logs. + if f.begin == rpc.PendingBlockNumber.Int64() || f.end == rpc.PendingBlockNumber.Int64() { + return nil, errPendingLogsUnsupported } resolveSpecial := func(number int64) (int64, error) { @@ -164,16 +155,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { case log := <-logChan: logs = append(logs, log) case err := <-errChan: - if err != nil { - // if an error occurs during extraction, we do return the extracted data - return logs, err - } - // Append the pending ones - if endPending { - pendingLogs := f.pendingLogs() - logs = append(logs, pendingLogs...) - } - return logs, nil + return logs, err } } } @@ -251,7 +233,7 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64, logChan chan *type if header == nil || err != nil { return err } - found, err := f.blockLogs(ctx, header, true) + found, err := f.checkMatches(ctx, header) if err != nil { return err } @@ -273,10 +255,13 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *ty if header == nil || err != nil { return err } - found, err := f.blockLogs(ctx, header, false) + found, err := f.blockLogs(ctx, header) if err != nil { return err } + if len(found) > 0 { + } + for _, log := range found { select { case logChan <- log: @@ -289,15 +274,8 @@ 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, skipBloom bool) ([]*types.Log, error) { - // Fast track: no filtering criteria - if len(f.addresses) == 0 && len(f.topics) == 0 { - list, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64()) - if err != nil { - return nil, err - } - return flatten(list), nil - } else if skipBloom || bloomFilter(header.Bloom, f.addresses, f.topics) { +func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) { + if bloomFilter(header.Bloom, f.addresses, f.topics) { return f.checkMatches(ctx, header) } return nil, nil @@ -365,7 +343,7 @@ func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []comm if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { return false } - if len(addresses) > 0 && !includes(addresses, log.Address) { + if len(addresses) > 0 && !slices.Contains(addresses, log.Address) { return false } // If the to filtered topics is greater than the amount of topics in logs, skip. @@ -376,7 +354,7 @@ func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []comm if len(sub) == 0 { continue // empty rule set == wildcard } - if !includes(sub, log.Topics[i]) { + if !slices.Contains(sub, log.Topics[i]) { return false } } diff --git a/ctxc/filters/filter_test.go b/ctxc/filters/filter_test.go index c7a3e223ca..55faf700d1 100644 --- a/ctxc/filters/filter_test.go +++ b/ctxc/filters/filter_test.go @@ -36,7 +36,7 @@ func makeReceipt(addr common.Address) *types.Receipt { receipt.Logs = []*types.Log{ {Address: addr}, } - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + receipt.Bloom = types.CreateBloom(receipt) return receipt } @@ -64,15 +64,19 @@ func BenchmarkFilters(b *testing.B) { case 2403: receipt := makeReceipt(addr1) gen.AddUncheckedReceipt(receipt) + gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, nil, nil)) case 1034: receipt := makeReceipt(addr2) gen.AddUncheckedReceipt(receipt) + gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, nil, nil)) case 34: receipt := makeReceipt(addr3) gen.AddUncheckedReceipt(receipt) + gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, nil, nil)) case 99999: receipt := makeReceipt(addr4) gen.AddUncheckedReceipt(receipt) + gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, nil, nil)) } }) @@ -127,6 +131,7 @@ func TestFilters(t *testing.T) { Topics: []common.Hash{hash1}, }, } + receipt.Bloom = types.CreateBloom(receipt) gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)) case 2: @@ -137,6 +142,7 @@ func TestFilters(t *testing.T) { Topics: []common.Hash{hash2}, }, } + receipt.Bloom = types.CreateBloom(receipt) gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)) @@ -148,6 +154,7 @@ func TestFilters(t *testing.T) { Topics: []common.Hash{hash3}, }, } + receipt.Bloom = types.CreateBloom(receipt) gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(998, common.HexToAddress("0x998"), big.NewInt(998), 998, big.NewInt(998), nil)) case 999: @@ -158,6 +165,7 @@ func TestFilters(t *testing.T) { Topics: []common.Hash{hash4}, }, } + receipt.Bloom = types.CreateBloom(receipt) gen.AddUncheckedReceipt(receipt) gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, big.NewInt(999), nil)) }