mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
208 lines
6.8 KiB
Go
208 lines
6.8 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package validators
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/luxfi/database/memdb"
|
|
"github.com/luxfi/evmgpu/plugin/evm/validators/state"
|
|
"github.com/luxfi/evmgpu/plugin/evm/validators/state/interfaces"
|
|
"github.com/luxfi/ids"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
luxdvalidators "github.com/luxfi/validators"
|
|
)
|
|
|
|
func TestLoadNewValidators(t *testing.T) {
|
|
testNodeIDs := []ids.NodeID{
|
|
ids.GenerateTestNodeID(),
|
|
ids.GenerateTestNodeID(),
|
|
ids.GenerateTestNodeID(),
|
|
}
|
|
testValidationIDs := []ids.ID{
|
|
ids.GenerateTestID(),
|
|
ids.GenerateTestID(),
|
|
ids.GenerateTestID(),
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
initialValidators map[ids.ID]*luxdvalidators.GetValidatorOutput
|
|
newValidators map[ids.ID]*luxdvalidators.GetValidatorOutput
|
|
registerMockListenerCalls func(*interfaces.MockStateCallbackListener)
|
|
expectedLoadErr error
|
|
}{
|
|
{
|
|
name: "before empty/after empty",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
|
|
registerMockListenerCalls: func(*interfaces.MockStateCallbackListener) {},
|
|
},
|
|
{
|
|
name: "before empty/after one",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
|
|
},
|
|
},
|
|
{
|
|
name: "before one/after empty",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{},
|
|
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
|
|
// initial validator will trigger first
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
|
|
// then it will be removed
|
|
mock.EXPECT().OnValidatorRemoved(testValidationIDs[0], testNodeIDs[0]).Times(1)
|
|
},
|
|
},
|
|
{
|
|
name: "no change",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
|
|
},
|
|
},
|
|
{
|
|
name: "status and weight change and new one",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 2,
|
|
},
|
|
testValidationIDs[1]: {
|
|
NodeID: testNodeIDs[1],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
|
|
// initial validator will trigger first
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
|
|
// Weight change happens but status stays the same (true) since we default to active
|
|
// new validator will be added
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[1], testNodeIDs[1], uint64(0), true).Times(1)
|
|
},
|
|
},
|
|
{
|
|
name: "renew validation ID",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[1]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
|
|
// initial validator will trigger first
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
|
|
// then it will be removed
|
|
mock.EXPECT().OnValidatorRemoved(testValidationIDs[0], testNodeIDs[0]).Times(1)
|
|
// new validator will be added
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[1], testNodeIDs[0], uint64(0), true).Times(1)
|
|
},
|
|
},
|
|
{
|
|
name: "renew node ID",
|
|
initialValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[0],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
newValidators: map[ids.ID]*luxdvalidators.GetValidatorOutput{
|
|
testValidationIDs[0]: {
|
|
NodeID: testNodeIDs[1],
|
|
Weight: 1,
|
|
},
|
|
},
|
|
expectedLoadErr: state.ErrImmutableField,
|
|
registerMockListenerCalls: func(mock *interfaces.MockStateCallbackListener) {
|
|
// initial validator will trigger first
|
|
mock.EXPECT().OnValidatorAdded(testValidationIDs[0], testNodeIDs[0], uint64(0), true).Times(1)
|
|
// then it won't be called since we don't track the node ID changes
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(tt *testing.T) {
|
|
require := require.New(tt)
|
|
db := memdb.New()
|
|
validatorState, err := state.NewState(db)
|
|
require.NoError(err)
|
|
|
|
// set initial validators
|
|
for vID, validator := range test.initialValidators {
|
|
require.NoError(validatorState.AddValidator(interfaces.Validator{
|
|
ValidationID: vID,
|
|
NodeID: validator.NodeID,
|
|
Weight: validator.Weight,
|
|
StartTimestamp: 0, // Default value since GetValidatorOutput doesn't have StartTime
|
|
IsActive: true, // Default to active
|
|
IsL1Validator: false, // Default value
|
|
}))
|
|
}
|
|
// enable mock listener
|
|
ctrl := gomock.NewController(tt)
|
|
mockListener := interfaces.NewMockStateCallbackListener(ctrl)
|
|
test.registerMockListenerCalls(mockListener)
|
|
|
|
validatorState.RegisterListener(mockListener)
|
|
// load new validators
|
|
err = loadValidators(validatorState, test.newValidators)
|
|
if test.expectedLoadErr != nil {
|
|
require.Error(err)
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
// check if the state is as expected
|
|
require.Equal(len(test.newValidators), validatorState.GetValidationIDs().Len())
|
|
for vID, validator := range test.newValidators {
|
|
v, err := validatorState.GetValidator(vID)
|
|
require.NoError(err)
|
|
require.Equal(validator.NodeID, v.NodeID)
|
|
require.Equal(validator.Weight, v.Weight)
|
|
// Default values are used for fields not in GetValidatorOutput
|
|
require.Equal(uint64(0), v.StartTimestamp)
|
|
require.Equal(true, v.IsActive)
|
|
require.Equal(false, v.IsL1Validator)
|
|
}
|
|
})
|
|
}
|
|
}
|