Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix validator id on bor_getSnapshot, bor_getSnapshotAtHash and bor_getCurrentValidators #1415

Merged
merged 6 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
unit tests on new functions
  • Loading branch information
lucca30 committed Jan 27, 2025
commit 8255ce1e1f5184a534392bc185813448c750607a
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package abi

//go:generate mockgen -destination=./abi_mock.go -package=api . ABI
type ABI interface {
Pack(name string, args ...interface{}) ([]byte, error)
UnpackIntoInterface(v interface{}, name string, data []byte) error
Expand Down
68 changes: 68 additions & 0 deletions consensus/bor/abi/abi_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions consensus/bor/heimdall/span/spanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (c *ChainSpanner) tryGetBorValidatorsWithId(ctx context.Context, blockNrOrH
valz := make([]*valset.Validator, producersCount)

for i := 0; i < producersCount; i++ {
p, err := c.getProducersBySpanAndIndexMethod(ctx, blockNrOrHash, blockNumber, toAddress, gas, spanNumber, i)
p, err := c.getProducersBySpanAndIndexMethod(ctx, blockNrOrHash, toAddress, gas, spanNumber, i)
// if fails, return validators without id
if err != nil {
return borValidatorsWithoutId, nil
Expand Down Expand Up @@ -180,7 +180,7 @@ func (c *ChainSpanner) getSpanByBlock(ctx context.Context, blockNrOrHash rpc.Blo
return spanNumber, nil
}

func (c *ChainSpanner) getProducersBySpanAndIndexMethod(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64, toAddress common.Address, gas hexutil.Uint64, spanNumber *big.Int, index int) (*contractValidator, error) {
func (c *ChainSpanner) getProducersBySpanAndIndexMethod(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, toAddress common.Address, gas hexutil.Uint64, spanNumber *big.Int, index int) (*contractValidator, error) {
const getProducersBySpanAndIndexMethod = "producers"
producerData, err := c.validatorSet.Pack(getProducersBySpanAndIndexMethod, spanNumber, big.NewInt(int64(index)))
if err != nil {
Expand Down
206 changes: 206 additions & 0 deletions consensus/bor/heimdall/span/spanner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package span

import (
"context"
"fmt"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/abi"
"github.com/ethereum/go-ethereum/consensus/bor/api"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func TestGetCurrentValidatorsByBlockNrOrHash(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

chainConfig := &params.ChainConfig{}
validatorContractAddress := common.HexToAddress("0x1234567890123456789012345678901234567890")

testCases := []struct {
name string
blockNumber uint64
mockEthAPIExpected func(*api.MockCaller)
mockAbiExpected func(*abi.MockABI)
expectedValidators []*valset.Validator
expectError bool
}{
{
name: "Successful retrieval of validators",
blockNumber: 1000,
mockEthAPIExpected: func(mockCaller *api.MockCaller) {
mockCaller.EXPECT().Call(
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Return(common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000"), nil).AnyTimes()
},
mockAbiExpected: func(mockAbi *abi.MockABI) {
basicMocks(mockAbi)

callCount := 0
mockAbi.EXPECT().UnpackIntoInterface(
gomock.Any(),
gomock.Eq("producers"),
gomock.Any(),
).DoAndReturn(func(v interface{}, name string, data []byte) error {
defer func() { callCount++ }()

resp, _ := v.(*contractValidator)

if callCount == 0 {
*resp = contractValidator{
Id: big.NewInt(1),
Signer: common.HexToAddress("0x1111111111111111111111111111111111111111"),
Power: big.NewInt(10),
}
}
if callCount == 1 {
*resp = contractValidator{
Id: big.NewInt(2),
Signer: common.HexToAddress("0x2222222222222222222222222222222222222222"),
Power: big.NewInt(15),
}
}
return nil
}).AnyTimes()

},

Check failure on line 76 in consensus/bor/heimdall/span/spanner_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-20.04)

unnecessary trailing newline (whitespace)
expectedValidators: []*valset.Validator{
{
ID: 1,
Address: common.HexToAddress("0x1111111111111111111111111111111111111111"),
VotingPower: 10,
},
{
ID: 2,
Address: common.HexToAddress("0x2222222222222222222222222222222222222222"),
VotingPower: 15,
},
},
expectError: false,
},
{
name: "Successful retrieval of validators without id",
blockNumber: 1000,
mockEthAPIExpected: func(mockCaller *api.MockCaller) {
mockCaller.EXPECT().Call(
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Return(common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000"), nil).AnyTimes()
},
mockAbiExpected: func(mockAbi *abi.MockABI) {
basicMocks(mockAbi)

mockAbi.EXPECT().UnpackIntoInterface(
gomock.Any(),
gomock.Eq("producers"),
gomock.Any(),
).DoAndReturn(func(v interface{}, name string, data []byte) error {
return fmt.Errorf("failed")
}).AnyTimes()

},

Check failure on line 114 in consensus/bor/heimdall/span/spanner_test.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-20.04)

unnecessary trailing newline (whitespace)
expectedValidators: []*valset.Validator{
{
ID: 0,
Address: common.HexToAddress("0x1111111111111111111111111111111111111111"),
VotingPower: 10,
},
{
ID: 0,
Address: common.HexToAddress("0x2222222222222222222222222222222222222222"),
VotingPower: 15,
},
},
expectError: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockEthAPI := api.NewMockCaller(ctrl)
mockValidatorSetABI := abi.NewMockABI(ctrl)

// Setup
chainSpanner := NewChainSpanner(
mockEthAPI,
mockValidatorSetABI,
chainConfig,
validatorContractAddress,
)

// Set up mock expectations
tc.mockEthAPIExpected(mockEthAPI)
tc.mockAbiExpected(mockValidatorSetABI)

blockNumber := rpc.BlockNumber(tc.blockNumber)
blockNrOrHash := rpc.BlockNumberOrHashWithNumber(blockNumber)

// Execute method
validators, err := chainSpanner.GetCurrentValidatorsByBlockNrOrHash(context.Background(), blockNrOrHash, tc.blockNumber)

// Assertions
if tc.expectError {
assert.Error(t, err)
assert.Nil(t, validators)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.expectedValidators, validators)
}
})
}
}

func basicMocks(mockAbi *abi.MockABI) {
mockAbi.EXPECT().Pack(
gomock.Any(),
gomock.Any(),
).Return(common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000"), nil).AnyTimes()

mockAbi.EXPECT().UnpackIntoInterface(
gomock.Any(),
gomock.Eq("FIRST_END_BLOCK"),
gomock.Any(),
).DoAndReturn(func(v interface{}, name string, data []byte) error {
resp, _ := v.(**big.Int)
*resp = big.NewInt(999)
return nil
}).AnyTimes()

mockAbi.EXPECT().UnpackIntoInterface(
gomock.Any(),
gomock.Eq("getSpanByBlock"),
gomock.Any(),
).DoAndReturn(func(v interface{}, name string, data []byte) error {
resp, _ := v.(**big.Int)
*resp = big.NewInt(1)
return nil
}).AnyTimes()

mockAbi.EXPECT().UnpackIntoInterface(
gomock.Any(),
gomock.Eq("getBorValidators"),
gomock.Any(),
).DoAndReturn(func(v interface{}, name string, data []byte) error {
resp, _ := v.(*[]interface{})
ret0, _ := (*resp)[0].(*[]common.Address)
ret1, _ := (*resp)[1].(*[]*big.Int)

*ret0 = []common.Address{common.HexToAddress("0x1111111111111111111111111111111111111111"), common.HexToAddress("0x2222222222222222222222222222222222222222")}
*ret1 = []*big.Int{big.NewInt(10), big.NewInt(15)}

return nil
}).AnyTimes()
}
2 changes: 1 addition & 1 deletion consensus/bor/valset/validator_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (vals *ValidatorSet) IncrementProposerPriority(times int) {
// validator queried on ValidatorSet contract
func (vals *ValidatorSet) IncludeIds(valsWithId []*Validator) {
if vals.IsNilOrEmpty() {
panic("empty validator set")
log.Warn("Empty validator set")
}

addressToId := make(map[common.Address]uint64)
Expand Down
60 changes: 60 additions & 0 deletions consensus/bor/valset/validator_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,63 @@ func TestUpdateWithChangeSet(t *testing.T) {
_, updatedTempVal := valSet.GetByAddress(tempVal.Address)
require.Equal(t, int64(250), updatedTempVal.VotingPower)
}

func TestValidatorSet_IncludeIds(t *testing.T) {
v1 := &Validator{
Address: common.HexToAddress("0x1111111111111111111111111111111111111111"),
VotingPower: 100,
ProposerPriority: 0,
ID: 0,
}
v2 := &Validator{
Address: common.HexToAddress("0x2222222222222222222222222222222222222222"),
VotingPower: 200,
ProposerPriority: 0,
ID: 0,
}

valSet := NewValidatorSet([]*Validator{v1, v2})

valsWithId := []*Validator{
{
Address: v1.Address,
ID: 10, // new ID for v1
VotingPower: 999,
ProposerPriority: 999,
},
{
Address: v2.Address,
ID: 20, // new ID for v2
VotingPower: 999,
ProposerPriority: 999,
},
{
Address: common.HexToAddress("0x3333333333333333333333333333333333333333"),
ID: 30,
VotingPower: 300,
},
}

valSet.IncludeIds(valsWithId)

assert.Equal(t, uint64(10), valSet.Validators[0].ID, "v1 ID should be updated to 10")
assert.Equal(t, uint64(20), valSet.Validators[1].ID, "v2 ID should be updated to 20")

assert.Equal(t, 2, len(valSet.Validators), "No extra validators should be added")

assert.Equal(t, int64(100), valSet.Validators[0].VotingPower)
assert.Equal(t, int64(200), valSet.Validators[1].VotingPower)
}

func TestValidatorSet_IncludeIds_EmptySet(t *testing.T) {
valSet := NewValidatorSet(nil) // empty set

valSet.IncludeIds([]*Validator{
{
Address: common.HexToAddress("0xabcdef"),
ID: 42,
},
})

assert.Equal(t, 0, valSet.Size(), "ValidatorSet remains empty")
}
Loading