Skip to content

Commit

Permalink
EVM-665 No Log Bloom (#1550)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorcrevar authored May 25, 2023
1 parent 9040ca9 commit 7b1f128
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 20 deletions.
1 change: 1 addition & 0 deletions consensus/polybft/block_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func (b *BlockBuilder) Build(handler func(h *types.Header)) (*types.FullBlock, e

_, b.header.StateRoot = b.state.Commit()
b.header.GasUsed = b.state.TotalGas()
b.header.LogsBloom = types.CreateBloom(b.Receipts())

// build the block
b.block = consensus.BuildBlock(consensus.BuildBlockParams{
Expand Down
160 changes: 160 additions & 0 deletions consensus/polybft/block_builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package polybft

import (
"math/big"
"testing"
"time"

"github.com/0xPolygon/polygon-edge/chain"
"github.com/0xPolygon/polygon-edge/consensus/polybft/wallet"
"github.com/0xPolygon/polygon-edge/crypto"
"github.com/0xPolygon/polygon-edge/helper/common"
"github.com/0xPolygon/polygon-edge/state"
itrie "github.com/0xPolygon/polygon-edge/state/immutable-trie"
"github.com/0xPolygon/polygon-edge/types"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/umbracle/ethgo"
)

func TestBlockBuilder_BuildBlockTxOneFailedTxAndOneTakesTooMuchGas(t *testing.T) {
t.Parallel()

const (
amount = 1_000
gasPrice = 1_000
gasLimit = 21000
blockGasLimit = 21000 * 3
chainID = 100
)

accounts := [6]*wallet.Account{}

for i := range accounts {
accounts[i] = generateTestAccount(t)
}

forks := &chain.Forks{}
logger := hclog.NewNullLogger()
signer := crypto.NewSigner(forks.At(0), chainID)

mchain := &chain.Chain{
Params: &chain.Params{
ChainID: chainID,
Forks: forks,
},
}

mstate := itrie.NewState(itrie.NewMemoryStorage())
executor := state.NewExecutor(mchain.Params, mstate, logger)

executor.GetHash = func(header *types.Header) func(i uint64) types.Hash {
return func(i uint64) (res types.Hash) {
return types.BytesToHash(common.EncodeUint64ToBytes(i))
}
}

balanceMap := map[types.Address]*chain.GenesisAccount{}

for i, acc := range accounts {
// the third tx will fail because of insufficient balance
if i != 2 {
balanceMap[types.Address(acc.Ecdsa.Address())] = &chain.GenesisAccount{
Balance: ethgo.Ether(1),
}
}
}

hash, err := executor.WriteGenesis(balanceMap, types.ZeroHash)

require.NoError(t, err)
require.NotEqual(t, types.ZeroHash, hash)

// Gas Limit is important to be high for tx pool
parentHeader := &types.Header{StateRoot: hash, GasLimit: 1_000_000_000_000_000}

txPool := &txPoolMock{}
txPool.On("Prepare", uint64(0)).Once()

for i, acc := range accounts {
receiver := types.Address(acc.Ecdsa.Address())
privateKey, err := acc.GetEcdsaPrivateKey()

require.NoError(t, err)

tx := &types.Transaction{
Value: big.NewInt(amount),
GasPrice: big.NewInt(gasPrice),
Gas: gasLimit,
Nonce: 0,
To: &receiver,
}

// fifth tx will cause filling to stop
if i == 4 {
tx.Gas = blockGasLimit - 1
}

tx, err = signer.SignTx(tx, privateKey)
require.NoError(t, err)

// all tx until the fifth will be retrieved from the pool
if i <= 4 {
txPool.On("Peek").Return(tx).Once()
}

// first two and fourth will be added to the block, third will be demoted
if i == 2 {
txPool.On("Demote", tx)
} else if i < 4 {
txPool.On("Pop", tx)
}
}

bb := NewBlockBuilder(&BlockBuilderParams{
BlockTime: time.Millisecond * 100,
Parent: parentHeader,
Coinbase: types.ZeroAddress,
Executor: executor,
GasLimit: blockGasLimit,
TxPool: txPool,
Logger: logger,
})

require.NoError(t, bb.Reset())

bb.Fill()

fb, err := bb.Build(func(h *types.Header) {
// fake the logs for bloom
rs := bb.Receipts()

if len(rs) == 3 {
rs[0].Logs = []*types.Log{
{Address: types.StringToAddress("ff7783")},
}
rs[1].Logs = []*types.Log{
{Address: types.StringToAddress("03bbcc")},
{Address: types.StringToAddress("112233")},
}
}
})
require.NoError(t, err)

txPool.AssertExpectations(t)
require.Len(t, bb.txns, 3, "Should have 3 transactions but has %d", len(bb.txns))
require.Len(t, bb.Receipts(), 3)

// assert logs bloom
for _, r := range bb.Receipts() {
for _, l := range r.Logs {
assert.True(t, fb.Block.Header.LogsBloom.IsLogInBloom(l))
}
}

assert.False(t, fb.Block.Header.LogsBloom.IsLogInBloom(
&types.Log{Address: types.StringToAddress("999911117777")}))
assert.False(t, fb.Block.Header.LogsBloom.IsLogInBloom(
&types.Log{Address: types.StringToAddress("111177779999")}))
}
32 changes: 12 additions & 20 deletions types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func (b Bloom) MarshalText() ([]byte, error) {
// CreateBloom creates a new bloom filter from a set of receipts
func CreateBloom(receipts []*Receipt) (b Bloom) {
h := keccak.DefaultKeccakPool.Get()
defer keccak.DefaultKeccakPool.Put(h)

for _, receipt := range receipts {
for _, log := range receipt.Logs {
Expand All @@ -87,8 +88,6 @@ func CreateBloom(receipts []*Receipt) (b Bloom) {
}
}

keccak.DefaultKeccakPool.Put(h)

return
}

Expand All @@ -99,36 +98,32 @@ func (b *Bloom) setEncode(hasher *keccak.Keccak, h []byte) {

for i := 0; i < 6; i += 2 {
// Find the global bit location
bit := (uint(buf[i+1]) + (uint(buf[i]) << 8)) & 2047
bit := (uint(buf[i+1]) + (uint(buf[i]) << 8)) & (BloomByteLength*8 - 1)

// Find where the bit maps in the [0..255] byte array
byteLocation := 256 - 1 - bit/8
// Find where the bit maps in the [0..BloomByteLength-1] byte array
byteLocation := BloomByteLength - 1 - bit/8
bitLocation := bit % 8
b[byteLocation] = b[byteLocation] | (1 << bitLocation)
b[byteLocation] |= 1 << bitLocation
}
}

// IsLogInBloom checks if the log has a possible presence in the bloom filter
func (b *Bloom) IsLogInBloom(log *Log) bool {
hasher := keccak.DefaultKeccakPool.Get()
defer keccak.DefaultKeccakPool.Put(hasher)

// Check if the log address is present
addressPresent := b.isByteArrPresent(hasher, log.Address.Bytes())
if !addressPresent {
if !b.isByteArrPresent(hasher, log.Address.Bytes()) {
return false
}

// Check if all the topics are present
for _, topic := range log.Topics {
topicsPresent := b.isByteArrPresent(hasher, topic.Bytes())

if !topicsPresent {
if !b.isByteArrPresent(hasher, topic.Bytes()) {
return false
}
}

keccak.DefaultKeccakPool.Put(hasher)

return true
}

Expand All @@ -140,15 +135,12 @@ func (b *Bloom) isByteArrPresent(hasher *keccak.Keccak, data []byte) bool {

for i := 0; i < 6; i += 2 {
// Find the global bit location
bit := (uint(buf[i+1]) + (uint(buf[i]) << 8)) & 2047
bit := (uint(buf[i+1]) + (uint(buf[i]) << 8)) & (BloomByteLength*8 - 1)

// Find where the bit maps in the [0..255] byte array
byteLocation := 256 - 1 - bit/8
// Find where the bit maps in the [0..BloomByteLength-1] byte array
byteLocation := BloomByteLength - 1 - bit/8
bitLocation := bit % 8

referenceByte := b[byteLocation]

isSet := uint(referenceByte & (1 << (bitLocation - 1)))
isSet := b[byteLocation] & (1 << bitLocation)

if isSet == 0 {
return false
Expand Down

0 comments on commit 7b1f128

Please sign in to comment.