github.com/MetalBlockchain/metalgo@v1.11.9/snow/validators/gvalidators/validator_state_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package gvalidators 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "testing" 11 12 "github.com/stretchr/testify/require" 13 "go.uber.org/mock/gomock" 14 15 "github.com/MetalBlockchain/metalgo/ids" 16 "github.com/MetalBlockchain/metalgo/snow/validators" 17 "github.com/MetalBlockchain/metalgo/utils/crypto/bls" 18 "github.com/MetalBlockchain/metalgo/vms/rpcchainvm/grpcutils" 19 20 pb "github.com/MetalBlockchain/metalgo/proto/pb/validatorstate" 21 ) 22 23 var errCustom = errors.New("custom") 24 25 type testState struct { 26 client *Client 27 server *validators.MockState 28 } 29 30 func setupState(t testing.TB, ctrl *gomock.Controller) *testState { 31 require := require.New(t) 32 33 t.Helper() 34 35 state := &testState{ 36 server: validators.NewMockState(ctrl), 37 } 38 39 listener, err := grpcutils.NewListener() 40 require.NoError(err) 41 serverCloser := grpcutils.ServerCloser{} 42 43 server := grpcutils.NewServer() 44 pb.RegisterValidatorStateServer(server, NewServer(state.server)) 45 serverCloser.Add(server) 46 47 go grpcutils.Serve(listener, server) 48 49 conn, err := grpcutils.Dial(listener.Addr().String()) 50 require.NoError(err) 51 52 state.client = NewClient(pb.NewValidatorStateClient(conn)) 53 54 t.Cleanup(func() { 55 serverCloser.Stop() 56 _ = conn.Close() 57 _ = listener.Close() 58 }) 59 60 return state 61 } 62 63 func TestGetMinimumHeight(t *testing.T) { 64 require := require.New(t) 65 ctrl := gomock.NewController(t) 66 67 state := setupState(t, ctrl) 68 69 // Happy path 70 expectedHeight := uint64(1337) 71 state.server.EXPECT().GetMinimumHeight(gomock.Any()).Return(expectedHeight, nil) 72 73 height, err := state.client.GetMinimumHeight(context.Background()) 74 require.NoError(err) 75 require.Equal(expectedHeight, height) 76 77 // Error path 78 state.server.EXPECT().GetMinimumHeight(gomock.Any()).Return(expectedHeight, errCustom) 79 80 _, err = state.client.GetMinimumHeight(context.Background()) 81 // TODO: require specific error 82 require.Error(err) //nolint:forbidigo // currently returns grpc error 83 } 84 85 func TestGetCurrentHeight(t *testing.T) { 86 require := require.New(t) 87 ctrl := gomock.NewController(t) 88 89 state := setupState(t, ctrl) 90 91 // Happy path 92 expectedHeight := uint64(1337) 93 state.server.EXPECT().GetCurrentHeight(gomock.Any()).Return(expectedHeight, nil) 94 95 height, err := state.client.GetCurrentHeight(context.Background()) 96 require.NoError(err) 97 require.Equal(expectedHeight, height) 98 99 // Error path 100 state.server.EXPECT().GetCurrentHeight(gomock.Any()).Return(expectedHeight, errCustom) 101 102 _, err = state.client.GetCurrentHeight(context.Background()) 103 // TODO: require specific error 104 require.Error(err) //nolint:forbidigo // currently returns grpc error 105 } 106 107 func TestGetSubnetID(t *testing.T) { 108 require := require.New(t) 109 ctrl := gomock.NewController(t) 110 111 state := setupState(t, ctrl) 112 113 // Happy path 114 chainID := ids.GenerateTestID() 115 expectedSubnetID := ids.GenerateTestID() 116 state.server.EXPECT().GetSubnetID(gomock.Any(), chainID).Return(expectedSubnetID, nil) 117 118 subnetID, err := state.client.GetSubnetID(context.Background(), chainID) 119 require.NoError(err) 120 require.Equal(expectedSubnetID, subnetID) 121 122 // Error path 123 state.server.EXPECT().GetSubnetID(gomock.Any(), chainID).Return(expectedSubnetID, errCustom) 124 125 _, err = state.client.GetSubnetID(context.Background(), chainID) 126 // TODO: require specific error 127 require.Error(err) //nolint:forbidigo // currently returns grpc error 128 } 129 130 func TestGetValidatorSet(t *testing.T) { 131 require := require.New(t) 132 ctrl := gomock.NewController(t) 133 134 state := setupState(t, ctrl) 135 136 // Happy path 137 sk0, err := bls.NewSecretKey() 138 require.NoError(err) 139 vdr0 := &validators.GetValidatorOutput{ 140 NodeID: ids.GenerateTestNodeID(), 141 PublicKey: bls.PublicFromSecretKey(sk0), 142 Weight: 1, 143 } 144 145 sk1, err := bls.NewSecretKey() 146 require.NoError(err) 147 vdr1 := &validators.GetValidatorOutput{ 148 NodeID: ids.GenerateTestNodeID(), 149 PublicKey: bls.PublicFromSecretKey(sk1), 150 Weight: 2, 151 } 152 153 vdr2 := &validators.GetValidatorOutput{ 154 NodeID: ids.GenerateTestNodeID(), 155 PublicKey: nil, 156 Weight: 3, 157 } 158 159 expectedVdrs := map[ids.NodeID]*validators.GetValidatorOutput{ 160 vdr0.NodeID: vdr0, 161 vdr1.NodeID: vdr1, 162 vdr2.NodeID: vdr2, 163 } 164 height := uint64(1337) 165 subnetID := ids.GenerateTestID() 166 state.server.EXPECT().GetValidatorSet(gomock.Any(), height, subnetID).Return(expectedVdrs, nil) 167 168 vdrs, err := state.client.GetValidatorSet(context.Background(), height, subnetID) 169 require.NoError(err) 170 require.Equal(expectedVdrs, vdrs) 171 172 // Error path 173 state.server.EXPECT().GetValidatorSet(gomock.Any(), height, subnetID).Return(expectedVdrs, errCustom) 174 175 _, err = state.client.GetValidatorSet(context.Background(), height, subnetID) 176 // TODO: require specific error 177 require.Error(err) //nolint:forbidigo // currently returns grpc error 178 } 179 180 func TestPublicKeyDeserialize(t *testing.T) { 181 require := require.New(t) 182 183 sk, err := bls.NewSecretKey() 184 require.NoError(err) 185 pk := bls.PublicFromSecretKey(sk) 186 187 pkBytes := bls.PublicKeyToUncompressedBytes(pk) 188 pkDe := bls.PublicKeyFromValidUncompressedBytes(pkBytes) 189 require.NotNil(pkDe) 190 require.Equal(pk, pkDe) 191 } 192 193 // BenchmarkGetValidatorSet measures the time it takes complete a gRPC client 194 // request based on a mocked validator set. 195 func BenchmarkGetValidatorSet(b *testing.B) { 196 for _, size := range []int{1, 16, 32, 1024, 2048} { 197 vs := setupValidatorSet(b, size) 198 b.Run(fmt.Sprintf("get_validator_set_%d_validators", size), func(b *testing.B) { 199 benchmarkGetValidatorSet(b, vs) 200 }) 201 } 202 } 203 204 func benchmarkGetValidatorSet(b *testing.B, vs map[ids.NodeID]*validators.GetValidatorOutput) { 205 require := require.New(b) 206 ctrl := gomock.NewController(b) 207 state := setupState(b, ctrl) 208 209 height := uint64(1337) 210 subnetID := ids.GenerateTestID() 211 state.server.EXPECT().GetValidatorSet(gomock.Any(), height, subnetID).Return(vs, nil).AnyTimes() 212 b.ResetTimer() 213 for i := 0; i < b.N; i++ { 214 _, err := state.client.GetValidatorSet(context.Background(), height, subnetID) 215 require.NoError(err) 216 } 217 b.StopTimer() 218 } 219 220 func setupValidatorSet(b *testing.B, size int) map[ids.NodeID]*validators.GetValidatorOutput { 221 b.Helper() 222 223 set := make(map[ids.NodeID]*validators.GetValidatorOutput, size) 224 sk, err := bls.NewSecretKey() 225 require.NoError(b, err) 226 pk := bls.PublicFromSecretKey(sk) 227 for i := 0; i < size; i++ { 228 id := ids.GenerateTestNodeID() 229 set[id] = &validators.GetValidatorOutput{ 230 NodeID: id, 231 PublicKey: pk, 232 Weight: uint64(i), 233 } 234 } 235 return set 236 }