core/types: create block's bloom by merging receipts' bloom (#31129)

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
```

---------

Co-authored-by: Gary Rong <garyrong0905@gmail.com>
Co-authored-by: Zsolt Felfoldi <zsfelfoldi@gmail.com>
pull/31178/head
minh-bq 1 week ago committed by GitHub
parent 77762820c9
commit 68de26e346
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      cmd/evm/internal/t8ntool/execution.go
  2. 6
      core/block_validator.go
  3. 8
      core/rawdb/accessors_chain_test.go
  4. 2
      core/state_processor.go
  5. 8
      core/types/block.go
  6. 39
      core/types/bloom9.go
  7. 52
      core/types/bloom9_test.go
  8. 2
      core/types/receipt.go
  9. 12
      core/types/receipt_test.go
  10. 2
      eth/filters/filter_test.go

@ -304,7 +304,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
// Set the receipt logs and create the bloom filter.
receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
receipt.Bloom = types.CreateBloom(receipt)
// These three are non-consensus fields:
//receipt.BlockHash
//receipt.BlockNumber
@ -376,7 +377,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
StateRoot: root,
TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)),
ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)),
Bloom: types.CreateBloom(receipts),
Bloom: types.MergeBloom(receipts),
LogsHash: rlpHash(statedb.Logs()),
Receipts: receipts,
Rejected: rejectedTxs,

@ -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)
}

@ -338,7 +338,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(),
@ -351,7 +351,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
@ -679,7 +679,7 @@ func TestReadLogs(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(),
@ -692,7 +692,7 @@ func TestReadLogs(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}
hash := common.BytesToHash([]byte{0x03, 0x14})

@ -189,7 +189,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// Set the receipt logs and create the bloom filter.
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())

@ -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 {

@ -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

@ -126,26 +126,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 {

@ -291,7 +291,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
}
r.CumulativeGasUsed = stored.CumulativeGasUsed
r.Logs = stored.Logs
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
r.Bloom = CreateBloom((*Receipt)(r))
return nil
}

@ -394,7 +394,7 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
func TestReceiptMarshalBinary(t *testing.T) {
// Legacy Receipt
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
legacyReceipt.Bloom = CreateBloom(legacyReceipt)
have, err := legacyReceipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
@ -421,7 +421,7 @@ func TestReceiptMarshalBinary(t *testing.T) {
// 2930 Receipt
buf.Reset()
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
accessListReceipt.Bloom = CreateBloom(accessListReceipt)
have, err = accessListReceipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
@ -439,7 +439,7 @@ func TestReceiptMarshalBinary(t *testing.T) {
// 1559 Receipt
buf.Reset()
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
eip1559Receipt.Bloom = CreateBloom(eip1559Receipt)
have, err = eip1559Receipt.MarshalBinary()
if err != nil {
t.Fatalf("marshal binary error: %v", err)
@ -463,7 +463,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) {
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
legacyReceipt.Bloom = CreateBloom(legacyReceipt)
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
}
@ -474,7 +474,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) {
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
accessListReceipt.Bloom = CreateBloom(accessListReceipt)
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
}
@ -485,7 +485,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) {
if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
t.Fatalf("unmarshal binary error: %v", err)
}
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
eip1559Receipt.Bloom = CreateBloom(eip1559Receipt)
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt)
}

@ -42,7 +42,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
}

Loading…
Cancel
Save