From 1715f60b389a645b86dca2bc3cb5b878904468cf Mon Sep 17 00:00:00 2001 From: Bui Quang Minh Date: Wed, 5 Feb 2025 11:00:53 +0700 Subject: [PATCH] core/types: create block's bloom by merging receipts' bloom Currently, when calculating block's bloom, we loop through all the receipt logs to calculate the hash value. However, normally, after going through applyTransaction, the receipt's bloom is already calculated based on the receipt log, so the block's bloom can be calculated by just ORing these receipt's blooms. goos: darwin goarch: arm64 pkg: github.com/ethereum/go-ethereum/core/types cpu: Apple M1 Pro BenchmarkCreateBloom BenchmarkCreateBloom/small BenchmarkCreateBloom/small-10 810922 1481 ns/op 104 B/op 5 allocs/op BenchmarkCreateBloom/large BenchmarkCreateBloom/large-10 8173 143764 ns/op 9614 B/op 401 allocs/op BenchmarkCreateBloom/small-mergebloom BenchmarkCreateBloom/small-mergebloom-10 5178918 232.0 ns/op 0 B/op 0 allocs/op BenchmarkCreateBloom/large-mergebloom BenchmarkCreateBloom/large-mergebloom-10 54110 22207 ns/op 0 B/op 0 allocs/op --- core/block_validator.go | 6 +++++- core/types/block.go | 8 +++++++- core/types/bloom9.go | 17 +++++++++++++++++ core/types/bloom9_test.go | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index 5885df9ee2..591e472bc1 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -129,7 +129,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(res.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(res.Receipts) if rbloom != header.Bloom { return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) } diff --git a/core/types/block.go b/core/types/block.go index c3db4d89e8..b284fb3b16 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -237,6 +237,9 @@ type extblock struct { // // The body elements and the receipts are used to recompute and overwrite the // relevant portions of the header. +// +// The receipt's bloom must already calculated for the block's bloom to be +// correctly calculated. func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block { if body == nil { body = &Body{} @@ -260,7 +263,10 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher b.header.ReceiptHash = EmptyReceiptsHash } 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 a560a20724..73ca26e58b 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -115,6 +115,23 @@ func CreateBloom(receipts Receipts) Bloom { return bin } +// MergeBloom merges the already calculated bloom in Receipts +// without recalculating. So it assumes receipt.Bloom is populated +// with the correct bloom already. +func MergeBloom(receipts Receipts) Bloom { + var bin Bloom + for _, receipt := range receipts { + if len(receipt.Logs) != 0 { + bl := receipt.Bloom.Bytes() + for i := range bin { + bin[i] |= bl[i] + } + } + } + + return bin +} + // LogsBloom returns the bloom bytes for the given logs func LogsBloom(logs []*Log) []byte { buf := make([]byte, 6) diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go index d3178d112e..a441641340 100644 --- a/core/types/bloom9_test.go +++ b/core/types/bloom9_test.go @@ -152,4 +152,38 @@ func BenchmarkCreateBloom(b *testing.B) { b.Errorf("Got %x, exp %x", got, exp) } }) + b.Run("small-mergebloom", func(b *testing.B) { + for _, receipt := range rSmall { + receipt.Bloom = CreateBloom(Receipts{receipt}) + } + + b.ReportAllocs() + var bl Bloom + for i := 0; i < b.N; i++ { + 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-mergebloom", func(b *testing.B) { + for _, receipt := range rLarge { + receipt.Bloom = CreateBloom(Receipts{receipt}) + } + + b.ReportAllocs() + var bl Bloom + for i := 0; i < b.N; i++ { + bl = MergeBloom(rLarge) + } + b.StopTimer() + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) }