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>
This commit is contained in:
parent
77762820c9
commit
68de26e346
|
@ -304,7 +304,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash)
|
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:
|
// These three are non-consensus fields:
|
||||||
//receipt.BlockHash
|
//receipt.BlockHash
|
||||||
//receipt.BlockNumber
|
//receipt.BlockNumber
|
||||||
|
@ -376,7 +377,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
StateRoot: root,
|
StateRoot: root,
|
||||||
TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)),
|
TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)),
|
||||||
ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)),
|
ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)),
|
||||||
Bloom: types.CreateBloom(receipts),
|
Bloom: types.MergeBloom(receipts),
|
||||||
LogsHash: rlpHash(statedb.Logs()),
|
LogsHash: rlpHash(statedb.Logs()),
|
||||||
Receipts: receipts,
|
Receipts: receipts,
|
||||||
Rejected: rejectedTxs,
|
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.
|
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||||
// For valid blocks this should always validate to true.
|
// 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 {
|
if rbloom != header.Bloom {
|
||||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
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}),
|
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||||
GasUsed: 111111,
|
GasUsed: 111111,
|
||||||
}
|
}
|
||||||
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
|
receipt1.Bloom = types.CreateBloom(receipt1)
|
||||||
|
|
||||||
receipt2 := &types.Receipt{
|
receipt2 := &types.Receipt{
|
||||||
PostState: common.Hash{2}.Bytes(),
|
PostState: common.Hash{2}.Bytes(),
|
||||||
|
@ -351,7 +351,7 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||||
GasUsed: 222222,
|
GasUsed: 222222,
|
||||||
}
|
}
|
||||||
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
|
receipt2.Bloom = types.CreateBloom(receipt2)
|
||||||
receipts := []*types.Receipt{receipt1, receipt2}
|
receipts := []*types.Receipt{receipt1, receipt2}
|
||||||
|
|
||||||
// Check that no receipt entries are in a pristine database
|
// 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}),
|
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||||
GasUsed: 111111,
|
GasUsed: 111111,
|
||||||
}
|
}
|
||||||
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
|
receipt1.Bloom = types.CreateBloom(receipt1)
|
||||||
|
|
||||||
receipt2 := &types.Receipt{
|
receipt2 := &types.Receipt{
|
||||||
PostState: common.Hash{2}.Bytes(),
|
PostState: common.Hash{2}.Bytes(),
|
||||||
|
@ -692,7 +692,7 @@ func TestReadLogs(t *testing.T) {
|
||||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||||
GasUsed: 222222,
|
GasUsed: 222222,
|
||||||
}
|
}
|
||||||
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
|
receipt2.Bloom = types.CreateBloom(receipt2)
|
||||||
receipts := []*types.Receipt{receipt1, receipt2}
|
receipts := []*types.Receipt{receipt1, receipt2}
|
||||||
|
|
||||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
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.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
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.BlockHash = blockHash
|
||||||
receipt.BlockNumber = blockNumber
|
receipt.BlockNumber = blockNumber
|
||||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
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
|
// The body elements and the receipts are used to recompute and overwrite the
|
||||||
// relevant portions of the header.
|
// 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 {
|
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block {
|
||||||
if body == nil {
|
if body == nil {
|
||||||
body = &Body{}
|
body = &Body{}
|
||||||
|
@ -260,7 +263,10 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher
|
||||||
b.header.ReceiptHash = EmptyReceiptsHash
|
b.header.ReceiptHash = EmptyReceiptsHash
|
||||||
} else {
|
} else {
|
||||||
b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
|
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 {
|
if len(uncles) == 0 {
|
||||||
|
|
|
@ -100,32 +100,35 @@ func (b *Bloom) UnmarshalText(input []byte) error {
|
||||||
return hexutil.UnmarshalFixedText("Bloom", input, b[:])
|
return hexutil.UnmarshalFixedText("Bloom", input, b[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBloom creates a bloom filter out of the give Receipts (+Logs)
|
// CreateBloom creates a bloom filter out of the give Receipt (+Logs)
|
||||||
func CreateBloom(receipts Receipts) Bloom {
|
func CreateBloom(receipt *Receipt) Bloom {
|
||||||
buf := make([]byte, 6)
|
var (
|
||||||
var bin Bloom
|
bin Bloom
|
||||||
for _, receipt := range receipts {
|
buf = make([]byte, 6)
|
||||||
for _, log := range receipt.Logs {
|
)
|
||||||
bin.add(log.Address.Bytes(), buf)
|
for _, log := range receipt.Logs {
|
||||||
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)
|
|
||||||
var bin Bloom
|
|
||||||
for _, log := range logs {
|
|
||||||
bin.add(log.Address.Bytes(), buf)
|
bin.add(log.Address.Bytes(), buf)
|
||||||
for _, b := range log.Topics {
|
for _, b := range log.Topics {
|
||||||
bin.add(b[:], buf)
|
bin.add(b[:], buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bin[:]
|
return bin
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 _, receipt := range receipts {
|
||||||
|
if len(receipt.Logs) != 0 {
|
||||||
|
bl := receipt.Bloom.Bytes()
|
||||||
|
for i := range bin {
|
||||||
|
bin[i] |= bl[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bloom9 returns the bloom filter for the given data
|
// 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 {
|
for i := 0; i < 200; i += 2 {
|
||||||
copy(rLarge[i:], rSmall)
|
copy(rLarge[i:], rSmall)
|
||||||
}
|
}
|
||||||
b.Run("small", func(b *testing.B) {
|
b.Run("small-createbloom", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
var bl Bloom
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bl = CreateBloom(rSmall)
|
for _, receipt := range rSmall {
|
||||||
|
receipt.Bloom = CreateBloom(receipt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
|
|
||||||
|
bl := MergeBloom(rSmall)
|
||||||
var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
||||||
got := crypto.Keccak256Hash(bl.Bytes())
|
got := crypto.Keccak256Hash(bl.Bytes())
|
||||||
if got != exp {
|
if got != exp {
|
||||||
b.Errorf("Got %x, exp %x", got, exp)
|
b.Errorf("Got %x, exp %x", got, exp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
b.Run("large", func(b *testing.B) {
|
b.Run("large-createbloom", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
var bl Bloom
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bl = CreateBloom(rLarge)
|
for _, receipt := range rLarge {
|
||||||
|
receipt.Bloom = CreateBloom(receipt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
b.StopTimer()
|
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 = 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(receipt)
|
||||||
|
}
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var bl Bloom
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bl = MergeBloom(rLarge)
|
||||||
|
}
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
||||||
got := crypto.Keccak256Hash(bl.Bytes())
|
got := crypto.Keccak256Hash(bl.Bytes())
|
||||||
if got != exp {
|
if got != exp {
|
||||||
|
|
|
@ -291,7 +291,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||||
r.Logs = stored.Logs
|
r.Logs = stored.Logs
|
||||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
r.Bloom = CreateBloom((*Receipt)(r))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,7 +394,7 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
||||||
|
|
||||||
func TestReceiptMarshalBinary(t *testing.T) {
|
func TestReceiptMarshalBinary(t *testing.T) {
|
||||||
// Legacy Receipt
|
// Legacy Receipt
|
||||||
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
|
legacyReceipt.Bloom = CreateBloom(legacyReceipt)
|
||||||
have, err := legacyReceipt.MarshalBinary()
|
have, err := legacyReceipt.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("marshal binary error: %v", err)
|
t.Fatalf("marshal binary error: %v", err)
|
||||||
|
@ -421,7 +421,7 @@ func TestReceiptMarshalBinary(t *testing.T) {
|
||||||
|
|
||||||
// 2930 Receipt
|
// 2930 Receipt
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
|
accessListReceipt.Bloom = CreateBloom(accessListReceipt)
|
||||||
have, err = accessListReceipt.MarshalBinary()
|
have, err = accessListReceipt.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("marshal binary error: %v", err)
|
t.Fatalf("marshal binary error: %v", err)
|
||||||
|
@ -439,7 +439,7 @@ func TestReceiptMarshalBinary(t *testing.T) {
|
||||||
|
|
||||||
// 1559 Receipt
|
// 1559 Receipt
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
|
eip1559Receipt.Bloom = CreateBloom(eip1559Receipt)
|
||||||
have, err = eip1559Receipt.MarshalBinary()
|
have, err = eip1559Receipt.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("marshal binary error: %v", err)
|
t.Fatalf("marshal binary error: %v", err)
|
||||||
|
@ -463,7 +463,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) {
|
||||||
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
|
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
|
||||||
t.Fatalf("unmarshal binary error: %v", err)
|
t.Fatalf("unmarshal binary error: %v", err)
|
||||||
}
|
}
|
||||||
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
|
legacyReceipt.Bloom = CreateBloom(legacyReceipt)
|
||||||
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
|
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
|
||||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", 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 {
|
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
|
||||||
t.Fatalf("unmarshal binary error: %v", err)
|
t.Fatalf("unmarshal binary error: %v", err)
|
||||||
}
|
}
|
||||||
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
|
accessListReceipt.Bloom = CreateBloom(accessListReceipt)
|
||||||
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
|
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
|
||||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", 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 {
|
if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
|
||||||
t.Fatalf("unmarshal binary error: %v", err)
|
t.Fatalf("unmarshal binary error: %v", err)
|
||||||
}
|
}
|
||||||
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
|
eip1559Receipt.Bloom = CreateBloom(eip1559Receipt)
|
||||||
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
|
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
|
||||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", 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{
|
receipt.Logs = []*types.Log{
|
||||||
{Address: addr},
|
{Address: addr},
|
||||||
}
|
}
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(receipt)
|
||||||
return receipt
|
return receipt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue