code.vegaprotocol.io/vega@v0.79.0/core/validators/witness_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package validators_test 17 18 import ( 19 "context" 20 "encoding/hex" 21 "sync" 22 "testing" 23 "time" 24 25 "code.vegaprotocol.io/vega/core/txn" 26 "code.vegaprotocol.io/vega/core/validators" 27 "code.vegaprotocol.io/vega/core/validators/mocks" 28 "code.vegaprotocol.io/vega/libs/crypto" 29 "code.vegaprotocol.io/vega/libs/num" 30 "code.vegaprotocol.io/vega/logging" 31 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 32 33 "github.com/cenkalti/backoff" 34 "github.com/golang/mock/gomock" 35 "github.com/golang/protobuf/proto" 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/require" 38 ) 39 40 type testWitness struct { 41 *validators.Witness 42 ctrl *gomock.Controller 43 top *mocks.MockValidatorTopology 44 cmd *mocks.MockCommander 45 tsvc *mocks.MockTimeService 46 startTime time.Time 47 } 48 49 func getTestWitness(t *testing.T) *testWitness { 50 t.Helper() 51 ctrl := gomock.NewController(t) 52 top := mocks.NewMockValidatorTopology(ctrl) 53 cmd := mocks.NewMockCommander(ctrl) 54 tsvc := mocks.NewMockTimeService(ctrl) 55 56 now := time.Now() 57 tsvc.EXPECT().GetTimeNow().Times(1).Return(now) 58 w := validators.NewWitness(context.Background(), 59 logging.NewTestLogger(), validators.NewDefaultConfig(), top, cmd, tsvc) 60 assert.NotNil(t, w) 61 62 return &testWitness{ 63 Witness: w, 64 ctrl: ctrl, 65 top: top, 66 cmd: cmd, 67 tsvc: tsvc, 68 startTime: now, 69 } 70 } 71 72 func TestExtResCheck(t *testing.T) { 73 t.Run("start - error duplicate", testStartErrorDuplicate) 74 t.Run("start - error check failed", testStartErrorCheckFailed) 75 t.Run("start - OK", testStartOK) 76 t.Run("add node vote - error invalid id", testNodeVoteInvalidProposalReference) 77 t.Run("add node vote - error note a validator", testNodeVoteNotAValidator) 78 t.Run("add node vote - error duplicate vote", testNodeVoteDuplicateVote) 79 t.Run("add node vote - OK", testNodeVoteOK) 80 t.Run("on chain time update validated asset", testOnTick) 81 t.Run("on chain time update validated asset - non validator", testOnTickNonValidator) 82 } 83 84 func testStartErrorDuplicate(t *testing.T) { 85 erc := getTestWitness(t) 86 defer erc.ctrl.Finish() 87 defer erc.Stop() 88 89 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 90 res := testRes{"resource-id-1", func() error { 91 return nil 92 }} 93 checkUntil := erc.startTime.Add(700 * time.Second) 94 cb := func(interface{}, bool) {} 95 96 err := erc.StartCheck(res, cb, checkUntil) 97 assert.NoError(t, err) 98 err = erc.StartCheck(res, cb, checkUntil) 99 assert.EqualError(t, err, validators.ErrResourceDuplicate.Error()) 100 } 101 102 func testStartErrorCheckFailed(t *testing.T) { 103 erc := getTestWitness(t) 104 defer erc.ctrl.Finish() 105 defer erc.Stop() 106 107 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 108 res := testRes{"resource-id-1", func() error { 109 return nil 110 }} 111 checkUntil := erc.startTime.Add(31 * 24 * time.Hour) 112 cb := func(interface{}, bool) {} 113 114 err := erc.StartCheck(res, cb, checkUntil) 115 assert.EqualError(t, err, validators.ErrCheckUntilInvalid.Error()) 116 } 117 118 func testStartOK(t *testing.T) { 119 erc := getTestWitness(t) 120 defer erc.ctrl.Finish() 121 defer erc.Stop() 122 123 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 124 res := testRes{"resource-id-1", func() error { 125 return nil 126 }} 127 checkUntil := erc.startTime.Add(700 * time.Second) 128 cb := func(interface{}, bool) {} 129 130 err := erc.StartCheck(res, cb, checkUntil) 131 assert.NoError(t, err) 132 } 133 134 func testNodeVoteInvalidProposalReference(t *testing.T) { 135 erc := getTestWitness(t) 136 defer erc.ctrl.Finish() 137 defer erc.Stop() 138 139 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 140 res := testRes{"resource-id-1", func() error { 141 return nil 142 }} 143 checkUntil := erc.startTime.Add(700 * time.Second) 144 cb := func(interface{}, bool) {} 145 146 err := erc.StartCheck(res, cb, checkUntil) 147 assert.NoError(t, err) 148 149 pubKey := newPublicKey("somepubkey") 150 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: "bad-id"}, pubKey) 151 assert.EqualError(t, err, validators.ErrInvalidResourceIDForNodeVote.Error()) 152 } 153 154 func testNodeVoteNotAValidator(t *testing.T) { 155 erc := getTestWitness(t) 156 defer erc.ctrl.Finish() 157 defer erc.Stop() 158 159 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 160 res := testRes{"resource-id-1", func() error { 161 return nil 162 }} 163 checkUntil := erc.startTime.Add(700 * time.Second) 164 cb := func(interface{}, bool) {} 165 166 err := erc.StartCheck(res, cb, checkUntil) 167 assert.NoError(t, err) 168 169 pubKey := newPublicKey("somepubkey") 170 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(false) 171 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 172 assert.EqualError(t, err, validators.ErrVoteFromNonValidator.Error()) 173 } 174 175 func testNodeVoteOK(t *testing.T) { 176 erc := getTestWitness(t) 177 defer erc.ctrl.Finish() 178 defer erc.Stop() 179 180 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 181 182 res := testRes{"resource-id-1", func() error { 183 return nil 184 }} 185 checkUntil := erc.startTime.Add(700 * time.Second) 186 cb := func(interface{}, bool) {} 187 188 err := erc.StartCheck(res, cb, checkUntil) 189 assert.NoError(t, err) 190 191 pubKey := newPublicKey("somepubkey") 192 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true) 193 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 194 assert.NoError(t, err) 195 } 196 197 func testNodeVoteDuplicateVote(t *testing.T) { 198 erc := getTestWitness(t) 199 defer erc.ctrl.Finish() 200 defer erc.Stop() 201 202 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 203 res := testRes{"resource-id-1", func() error { 204 return nil 205 }} 206 checkUntil := erc.startTime.Add(700 * time.Second) 207 cb := func(interface{}, bool) {} 208 209 err := erc.StartCheck(res, cb, checkUntil) 210 assert.NoError(t, err) 211 212 // first vote, all good 213 pubKey := newPublicKey("somepubkey") 214 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true) 215 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 216 require.NoError(t, err) 217 218 // second vote, bad 219 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true) 220 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 221 require.EqualError(t, err, validators.ErrDuplicateVoteFromNode.Error()) 222 } 223 224 func TestVoteMajorityCalculation(t *testing.T) { 225 erc := getTestWitness(t) 226 defer erc.ctrl.Finish() 227 defer erc.Stop() 228 erc.Witness.OnDefaultValidatorsVoteRequiredUpdate(context.Background(), num.DecimalFromFloat(0.67)) 229 selfPubKey := "b7ee437dc100d642" 230 231 erc.top.EXPECT().GetTotalVotingPower().AnyTimes().Return(int64(500)) 232 erc.top.EXPECT().GetVotingPower(gomock.Any()).AnyTimes().Return(int64(100)) 233 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 234 235 ch := make(chan struct{}, 1) 236 defer close(ch) 237 res := testRes{"resource-id-1", func() error { 238 ch <- struct{}{} 239 return nil 240 }} 241 checkUntil := erc.startTime.Add(700 * time.Second) 242 cb := func(interface{}, bool) { 243 // unblock chanel listen to finish test 244 ch <- struct{}{} 245 } 246 247 err := erc.StartCheck(res, cb, checkUntil) 248 assert.NoError(t, err) 249 250 // first wait once for the asset to be validated 251 <-ch 252 253 wg := sync.WaitGroup{} 254 wg.Add(2) 255 // first on chain time update, we send our own vote 256 erc.cmd.EXPECT().Command(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Do(func(_ context.Context, _ txn.Command, _ proto.Message, _ func(string, error), _ *backoff.ExponentialBackOff) { 257 wg.Done() 258 }) 259 newNow := erc.startTime.Add(1 * time.Second) 260 erc.OnTick(context.Background(), newNow) 261 262 // then we propagate our own vote 263 pubKey := newPublicKey(selfPubKey) 264 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true).Do(func(_ string) { 265 wg.Done() 266 }) 267 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 268 assert.NoError(t, err) 269 270 for i := 0; i < 3; i++ { 271 // second vote from another validator 272 othPubKey := newPublicKey(crypto.RandomHash()) 273 wg.Add(1) 274 erc.top.EXPECT().IsValidatorVegaPubKey(othPubKey.Hex()).Times(1).Return(true).Do(func(_ string) { 275 wg.Done() 276 }) 277 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, othPubKey) 278 assert.NoError(t, err) 279 } 280 281 // we have 4 votes, that should suffice to pass 282 // call onTick again to get the callback called 283 newNow = newNow.Add(1 * time.Second) 284 erc.top.EXPECT().IsTendermintValidator(gomock.Any()).Times(4).Return(true) 285 erc.OnTick(context.Background(), newNow) 286 287 // block to wait for the result 288 <-ch 289 wg.Wait() 290 } 291 292 func testOnTick(t *testing.T) { 293 erc := getTestWitness(t) 294 defer erc.ctrl.Finish() 295 defer erc.Stop() 296 297 selfPubKey := "b7ee437dc100d642" 298 299 erc.Witness.OnDefaultValidatorsVoteRequiredUpdate(context.Background(), num.DecimalFromFloat(0.67)) 300 erc.top.EXPECT().GetTotalVotingPower().AnyTimes().Return(int64(298)) 301 erc.top.EXPECT().GetVotingPower(gomock.Any()).AnyTimes().Return(int64(100)) 302 erc.top.EXPECT().IsValidator().AnyTimes().Return(true) 303 304 wg := sync.WaitGroup{} 305 ch := make(chan struct{}, 1) 306 defer close(ch) 307 res := testRes{"resource-id-1", func() error { 308 ch <- struct{}{} 309 return nil 310 }} 311 checkUntil := erc.startTime.Add(700 * time.Second) 312 cb := func(interface{}, bool) { 313 // unblock chanel listen to finish test 314 ch <- struct{}{} 315 } 316 317 err := erc.StartCheck(res, cb, checkUntil) 318 assert.NoError(t, err) 319 320 // first wait once for the asset to be validated 321 <-ch 322 323 // first on chain time update, we send our own vote 324 wg.Add(1) 325 erc.cmd.EXPECT().Command(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Do(func(_ context.Context, _ txn.Command, _ proto.Message, _ func(string, error), _ *backoff.ExponentialBackOff) { 326 wg.Done() 327 }) 328 newNow := erc.startTime.Add(1 * time.Second) 329 erc.OnTick(context.Background(), newNow) 330 331 // then we propagate our own vote 332 pubKey := newPublicKey(selfPubKey) 333 wg.Add(1) 334 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true).Do(func(_ string) { 335 wg.Done() 336 }) 337 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 338 assert.NoError(t, err) 339 340 // second vote from another validator 341 othPubKey := newPublicKey("somepubkey") 342 wg.Add(1) 343 erc.top.EXPECT().IsValidatorVegaPubKey(othPubKey.Hex()).Times(1).Return(true).Do(func(_ string) { 344 wg.Done() 345 }) 346 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, othPubKey) 347 assert.NoError(t, err) 348 349 // call onTick again to get the callback called 350 newNow = newNow.Add(1 * time.Second) 351 erc.top.EXPECT().IsTendermintValidator(gomock.Any()).Times(2).Return(true) 352 erc.OnTick(context.Background(), newNow) 353 354 // block to wait for the result 355 <-ch 356 wg.Wait() 357 } 358 359 func testOnTickNonValidator(t *testing.T) { 360 erc := getTestWitness(t) 361 defer erc.ctrl.Finish() 362 defer erc.Stop() 363 364 selfPubKey := "b7ee437dc100d642" 365 366 erc.top.EXPECT().GetTotalVotingPower().AnyTimes().Return(int64(298)) 367 erc.top.EXPECT().GetVotingPower(gomock.Any()).AnyTimes().Return(int64(100)) 368 erc.top.EXPECT().IsValidator().AnyTimes().Return(false) 369 370 res := testRes{"resource-id-1", func() error { 371 return nil 372 }} 373 374 checkUntil := erc.startTime.Add(700 * time.Second) 375 cb := func(interface{}, bool) {} 376 377 err := erc.StartCheck(res, cb, checkUntil) 378 assert.NoError(t, err) 379 380 // first on chain time update, we send our own vote 381 erc.cmd.EXPECT().Command(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) 382 newNow := erc.startTime.Add(1 * time.Second) 383 erc.OnTick(context.Background(), newNow) 384 385 // then we propagate our own vote 386 pubKey := newPublicKey(selfPubKey) 387 erc.top.EXPECT().IsValidatorVegaPubKey(pubKey.Hex()).Times(1).Return(true) 388 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, pubKey) 389 assert.NoError(t, err) 390 391 // second vote from another validator 392 othPubKey := newPublicKey("somepubkey") 393 erc.top.EXPECT().IsValidatorVegaPubKey(othPubKey.Hex()).Times(1).Return(true) 394 err = erc.AddNodeCheck(context.Background(), &commandspb.NodeVote{Reference: res.id}, othPubKey) 395 assert.NoError(t, err) 396 397 // call onTick again to get the callback called 398 newNow = newNow.Add(1 * time.Second) 399 erc.top.EXPECT().IsTendermintValidator(gomock.Any()).Times(2).Return(true) 400 erc.OnTick(context.Background(), newNow) 401 } 402 403 type testRes struct { 404 id string 405 check func() error 406 } 407 408 func (t testRes) GetChainID() string { return "" } 409 func (t testRes) GetID() string { return t.id } 410 func (t testRes) GetType() commandspb.NodeVote_Type { 411 return commandspb.NodeVote_TYPE_FUNDS_DEPOSITED 412 } 413 func (t testRes) Check(ctx context.Context) error { return t.check() } 414 415 func newPublicKey(k string) crypto.PublicKey { 416 pubKeyB := []byte(k) 417 return crypto.NewPublicKey(hex.EncodeToString(pubKeyB), pubKeyB) 418 }