code.vegaprotocol.io/vega@v0.79.0/core/validators/topology_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/base64" 21 "encoding/hex" 22 "encoding/json" 23 "fmt" 24 "testing" 25 26 bmocks "code.vegaprotocol.io/vega/core/broker/mocks" 27 "code.vegaprotocol.io/vega/core/events" 28 "code.vegaprotocol.io/vega/core/nodewallets" 29 "code.vegaprotocol.io/vega/core/validators" 30 "code.vegaprotocol.io/vega/core/validators/mocks" 31 "code.vegaprotocol.io/vega/libs/crypto" 32 vgcrypto "code.vegaprotocol.io/vega/libs/crypto" 33 vgtesting "code.vegaprotocol.io/vega/libs/testing" 34 "code.vegaprotocol.io/vega/logging" 35 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 36 37 "github.com/golang/mock/gomock" 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/mock" 40 "github.com/stretchr/testify/require" 41 ) 42 43 var tmPubKey = "tm-pub-key" 44 45 type NodeWallets struct { 46 vega validators.Wallet 47 tendermintPubkey string 48 ethereumAddress string 49 ethereum validators.Signer 50 } 51 52 func (n *NodeWallets) GetVega() validators.Wallet { 53 return n.vega 54 } 55 56 func (n *NodeWallets) GetTendermintPubkey() string { 57 return n.tendermintPubkey 58 } 59 60 func (n *NodeWallets) GetEthereumAddress() string { 61 return n.ethereumAddress 62 } 63 64 func (n *NodeWallets) GetEthereum() validators.Signer { 65 return n.ethereum 66 } 67 68 type DummyMultiSigTopology struct{} 69 70 func (*DummyMultiSigTopology) ChainID() string { 71 return "12" 72 } 73 74 func (*DummyMultiSigTopology) IsSigner(address string) bool { 75 return true 76 } 77 78 func (*DummyMultiSigTopology) ExcessSigners(addresses []string) bool { 79 return false 80 } 81 82 func (*DummyMultiSigTopology) GetThreshold() uint32 { 83 return 666 84 } 85 86 func (*DummyMultiSigTopology) GetSigners() []string { 87 return []string{} 88 } 89 90 type testTop struct { 91 *validators.Topology 92 ctrl *gomock.Controller 93 wallet *mocks.MockWallet 94 broker *bmocks.MockBroker 95 timeService *mocks.MockTimeService 96 multisigTop *mocks.MockMultiSigTopology 97 multisigTop2 *mocks.MockMultiSigTopology 98 } 99 100 func getTestTopologyWithNodeWallet( 101 t *testing.T, wallet *mocks.MockWallet, nw *NodeWallets, ctrl *gomock.Controller, 102 ) *testTop { 103 t.Helper() 104 105 broker := bmocks.NewMockBroker(ctrl) 106 timeService := mocks.NewMockTimeService(ctrl) 107 broker.EXPECT().Send(gomock.Any()).AnyTimes() 108 mtop1 := mocks.NewMockMultiSigTopology(ctrl) 109 mtop2 := mocks.NewMockMultiSigTopology(ctrl) 110 111 commander := mocks.NewMockCommander(gomock.NewController(t)) 112 113 top := validators.NewTopology(logging.NewTestLogger(), validators.NewDefaultConfig(), nw, broker, true, commander, mtop1, mtop2, timeService) 114 return &testTop{ 115 Topology: top, 116 ctrl: ctrl, 117 wallet: wallet, 118 broker: broker, 119 timeService: timeService, 120 multisigTop: mtop1, 121 multisigTop2: mtop2, 122 } 123 } 124 125 func getTestTopology(t *testing.T) *testTop { 126 t.Helper() 127 ctrl := gomock.NewController(t) 128 dummyPubKey := "iamapubkey" 129 pubKey := crypto.NewPublicKey(dummyPubKey, []byte(dummyPubKey)) 130 131 wallet := mocks.NewMockWallet(ctrl) 132 wallet.EXPECT().PubKey().Return(pubKey).AnyTimes() 133 wallet.EXPECT().ID().Return(pubKey).AnyTimes() 134 135 nw := &NodeWallets{ 136 vega: wallet, 137 tendermintPubkey: "rlg/jtPcVSdV23oFX8828sYFD84d7QsPt12YpiQH3Zw=", 138 ethereumAddress: "0x5cd0ec63687588817044794bf15d4e37991efab3", 139 } 140 141 return getTestTopologyWithNodeWallet(t, wallet, nw, ctrl) 142 } 143 144 func getTestTopWithDefaultValidator(t *testing.T) *testTop { 145 t.Helper() 146 147 top := getTestTopology(t) 148 149 // Add Tendermint public key to validator set 150 151 defaultTmPubKey := "default-tm-public-key" 152 defaultTmPubKeyBase64 := base64.StdEncoding.EncodeToString([]byte(defaultTmPubKey)) 153 154 state := struct { 155 Validators map[string]validators.ValidatorData 156 }{ 157 Validators: map[string]validators.ValidatorData{ 158 defaultTmPubKeyBase64: { 159 ID: top.wallet.PubKey().Hex(), 160 VegaPubKey: top.wallet.PubKey().Hex(), 161 TmPubKey: "asdasd", 162 EthereumAddress: "0x123456", 163 InfoURL: "n0.xyz.vega/node/123", 164 Country: "GB", 165 }, 166 }, 167 } 168 169 buf, err := json.Marshal(state) 170 if err != nil { 171 t.Fatalf("error marshalling state %v", err) 172 } 173 174 if err := top.LoadValidatorsOnGenesis(context.Background(), buf); err != nil { 175 t.Fatalf("error loading validators on genesis: %v", err) 176 } 177 178 return top 179 } 180 181 func getTestTopologyWithSelfValidatorData( 182 t *testing.T, self validators.ValidatorData, 183 ) *testTop { 184 t.Helper() 185 186 ctrl := gomock.NewController(t) 187 pubKey := crypto.NewPublicKey(self.VegaPubKey, []byte(self.VegaPubKey)) 188 id := crypto.NewPublicKey(self.ID, []byte(self.VegaPubKey)) 189 190 wallet := mocks.NewMockWallet(ctrl) 191 wallet.EXPECT().PubKey().Return(pubKey).AnyTimes() 192 wallet.EXPECT().ID().Return(id).AnyTimes() 193 nw := &NodeWallets{ 194 vega: wallet, 195 tendermintPubkey: self.TmPubKey, 196 ethereumAddress: self.EthereumAddress, 197 } 198 199 return getTestTopologyWithNodeWallet(t, wallet, nw, ctrl) 200 } 201 202 func loadGenesisValidators( 203 t *testing.T, top *testTop, data ...validators.ValidatorData, 204 ) error { 205 t.Helper() 206 state := struct { 207 Validators map[string]validators.ValidatorData 208 }{ 209 Validators: map[string]validators.ValidatorData{}, 210 } 211 212 for _, v := range data { 213 state.Validators[v.TmPubKey] = v 214 } 215 216 buf, err := json.Marshal(state) 217 if err != nil { 218 t.Fatalf("error marshalling state %v", err) 219 } 220 221 return top.LoadValidatorsOnGenesis(context.Background(), buf) 222 } 223 224 func TestValidatorTopology(t *testing.T) { 225 t.Run("add node registration - success", testAddNewNodeSuccess) 226 t.Run("add node registration - failure", testAddNewNodeFailure) 227 t.Run("test add node registration send event to broker", testAddNewNodeSendsValidatorUpdateEventToBroker) 228 t.Run("topology validators length is equal to number of added validators", testGetLen) 229 t.Run("added validator exists in topology", testExists) 230 t.Run("test get by key", testGetByKey) 231 t.Run("test validators validations", testValidatorsValidation) 232 } 233 234 func testValidatorsValidation(t *testing.T) { 235 self := validators.ValidatorData{ 236 ID: "f42b834d75f9ecb7b8167277fdae6ff664085d69588c508ada655d7876961558", 237 VegaPubKey: "6a8325087e5bdf57b60cf06c3764e3c6a32840079fdc432a437ce32cd99316b5", 238 TmPubKey: "rlg/jtPcVSdV23oFX8828sYFD84d7QsPt12YpiQH3Zw=", 239 EthereumAddress: "0x5cd0ec63687588817044794bf15d4e37991efab3", 240 } 241 otherValidators := []validators.ValidatorData{ 242 { 243 ID: "4f69b1784656174e89eb094513b7136e88670b42517ed0e48cb6fd3062eb8478", 244 VegaPubKey: "f4686749895bf51c6df4092ef6be4279c384a3c380c24ea7a2fd20afc602a35d", 245 TmPubKey: "uBr9FP/M/QyVtOa3j18+hjksXra7qxCa7e25/FVW5c0=", 246 EthereumAddress: "0xF3920d9Ab483177C99846503A118fa84A557bB27", 247 }, 248 { 249 ID: "74023df02b8afc9eaf3e3e2e8b07eab1d2122ac3e74b1b0222daf4af565ad3dd", 250 VegaPubKey: "10b06fec6398d9e9d542d7b7d36933a1e6f0bb0631b0e532681c05123d4bd5aa", 251 TmPubKey: "hz528OlxLZoV+476oJP2lzrhAZwZNjjLAfvpd2wLvcg=", 252 EthereumAddress: "0x1b79814f66773df25ba126E8d1A557ab2676246f", 253 }, 254 } 255 256 testSuite := []struct { 257 name string 258 self validators.ValidatorData 259 others []validators.ValidatorData 260 expectFailure bool 261 }{ 262 { 263 name: "node setup correct", 264 self: self, 265 others: append(otherValidators, self), 266 expectFailure: false, 267 }, 268 { 269 name: "node setup incorrect, invalid ID", 270 self: validators.ValidatorData{ 271 TmPubKey: "rlg/jtPcVSdV23oFX8828sYFD84d7QsPt12YpiQH3Zw=", 272 273 ID: "INVALID-ID", 274 VegaPubKey: "6a8325087e5bdf57b60cf06c3764e3c6a32840079fdc432a437ce32cd99316b5", 275 EthereumAddress: "0x5cd0ec63687588817044794bf15d4e37991efab3", 276 }, 277 others: append(otherValidators, self), 278 expectFailure: true, 279 }, 280 { 281 name: "node setup correct, invalid pubkey", 282 self: validators.ValidatorData{ 283 ID: "f42b834d75f9ecb7b8167277fdae6ff664085d69588c508ada655d7876961558", 284 VegaPubKey: "INVALID-PUBKEY", 285 TmPubKey: "rlg/jtPcVSdV23oFX8828sYFD84d7QsPt12YpiQH3Zw=", 286 EthereumAddress: "0x5cd0ec63687588817044794bf15d4e37991efab3", 287 }, 288 others: append(otherValidators, self), 289 expectFailure: true, 290 }, 291 { 292 name: "node setup incorrect, invalid ethereum address", 293 self: validators.ValidatorData{ 294 ID: "f42b834d75f9ecb7b8167277fdae6ff664085d69588c508ada655d7876961558", 295 VegaPubKey: "6a8325087e5bdf57b60cf06c3764e3c6a32840079fdc432a437ce32cd99316b5", 296 TmPubKey: "rlg/jtPcVSdV23oFX8828sYFD84d7QsPt12YpiQH3Zw=", 297 EthereumAddress: "0xNOPE", 298 }, 299 others: append(otherValidators, self), 300 expectFailure: true, 301 }, 302 { 303 name: "node setup inccorrect, all invalid", 304 self: validators.ValidatorData{ 305 ID: "WRONG", 306 VegaPubKey: "BAD", 307 TmPubKey: "rlg/jtPcVSdV23oFX8828sYFD84d7QsPt12YpiQH3Zw=", 308 EthereumAddress: "0xNOPE", 309 }, 310 others: append(otherValidators, self), 311 expectFailure: true, 312 }, 313 } 314 for _, set := range testSuite { 315 t.Run(set.name, func(t *testing.T) { 316 // one validator -> self, 2 non validators 317 top := getTestTopologyWithSelfValidatorData(t, set.self) 318 if set.expectFailure { 319 assert.Panics(t, func() { 320 loadGenesisValidators(t, top, set.others...) 321 }) 322 } else { 323 assert.NotPanics(t, func() { 324 err := loadGenesisValidators(t, top, set.others...) 325 assert.NoError(t, err) 326 }) 327 } 328 top.ctrl.Finish() 329 }) 330 } 331 } 332 333 func testAddNewNodeSuccess(t *testing.T) { 334 top := getTestTopWithDefaultValidator(t) 335 defer top.ctrl.Finish() 336 337 nr := commandspb.AnnounceNode{ 338 Id: "vega-master-pubkey", 339 ChainPubKey: tmPubKey, 340 VegaPubKey: "vega-key", 341 EthereumAddress: "eth-address", 342 } 343 ctx := context.Background() 344 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 345 assert.NoError(t, err) 346 } 347 348 func testAddNewNodeFailure(t *testing.T) { 349 top := getTestTopWithDefaultValidator(t) 350 defer top.ctrl.Finish() 351 352 nr := commandspb.AnnounceNode{ 353 Id: "vega-master-pubkey", 354 ChainPubKey: "tm-pub-key-1", 355 VegaPubKey: "vega-key", 356 EthereumAddress: "eth-address", 357 } 358 ctx := context.Background() 359 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 360 assert.NoError(t, err) 361 362 // Add node with existing VegaPubKey 363 nr = commandspb.AnnounceNode{ 364 Id: "vega-master-pubkey", 365 ChainPubKey: "tm-pub-key-2", 366 VegaPubKey: "vega-key", 367 EthereumAddress: "eth-address-2", 368 } 369 err = top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 370 assert.Error(t, err) 371 } 372 373 func testGetLen(t *testing.T) { 374 top := getTestTopWithDefaultValidator(t) 375 defer top.ctrl.Finish() 376 377 // first the len is 1 since the default validator loaded from genenesis 378 assert.Equal(t, 1, top.Len()) 379 380 nr := commandspb.AnnounceNode{ 381 Id: "vega-master-pubkey", 382 ChainPubKey: tmPubKey, 383 VegaPubKey: "vega-key", 384 EthereumAddress: "eth-address", 385 } 386 ctx := context.Background() 387 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 388 assert.NoError(t, err) 389 390 assert.Equal(t, 2, top.Len()) 391 } 392 393 func testExists(t *testing.T) { 394 top := getTestTopWithDefaultValidator(t) 395 defer top.ctrl.Finish() 396 397 assert.False(t, top.IsValidatorVegaPubKey("vega-key")) 398 assert.False(t, top.IsValidatorNodeID("vega-master-pubkey")) 399 400 nr := commandspb.AnnounceNode{ 401 Id: "vega-master-pubkey", 402 ChainPubKey: tmPubKey, 403 VegaPubKey: "vega-key", 404 EthereumAddress: "eth-address", 405 } 406 ctx := context.Background() 407 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 408 assert.NoError(t, err) 409 410 assert.True(t, top.IsValidatorVegaPubKey("vega-key")) 411 assert.True(t, top.IsValidatorNodeID("vega-master-pubkey")) 412 } 413 414 func testGetByKey(t *testing.T) { 415 top := getTestTopWithDefaultValidator(t) 416 defer top.ctrl.Finish() 417 418 assert.False(t, top.IsValidatorVegaPubKey("vega-key")) 419 assert.False(t, top.IsValidatorNodeID("vega-master-pubkey")) 420 421 nr := commandspb.AnnounceNode{ 422 Id: "vega-master-pubkey", 423 ChainPubKey: tmPubKey, 424 VegaPubKey: "vega-key", 425 EthereumAddress: "eth-address", 426 InfoUrl: "n0.xyz.vega/node/url/random", 427 Country: "CZ", 428 } 429 ctx := context.Background() 430 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 431 assert.NoError(t, err) 432 433 expectedData := &validators.ValidatorData{ 434 ID: "vega-master-pubkey", 435 VegaPubKey: nr.VegaPubKey, 436 EthereumAddress: "eth-address", 437 TmPubKey: nr.ChainPubKey, 438 InfoURL: nr.InfoUrl, 439 Country: nr.Country, 440 } 441 442 actualData := top.Get(nr.Id) 443 assert.NotNil(t, actualData) 444 445 assert.Equal(t, expectedData, actualData) 446 } 447 448 func testAddNewNodeSendsValidatorUpdateEventToBroker(t *testing.T) { 449 ctrl := gomock.NewController(t) 450 defer ctrl.Finish() 451 452 vegaPaths, cleanupFn := vgtesting.NewVegaPaths() 453 defer cleanupFn() 454 _, err := nodewallets.GenerateVegaWallet(vegaPaths, "pass", "pass", false) 455 require.NoError(t, err) 456 wallet, err := nodewallets.GetVegaWallet(vegaPaths, "pass") 457 require.NoError(t, err) 458 459 nw := &NodeWallets{ 460 vega: wallet, 461 } 462 463 broker := bmocks.NewMockBroker(ctrl) 464 timeService := mocks.NewMockTimeService(ctrl) 465 commander := mocks.NewMockCommander(gomock.NewController(t)) 466 top := validators.NewTopology(logging.NewTestLogger(), validators.NewDefaultConfig(), nw, broker, true, commander, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, timeService) 467 468 ctx := context.Background() 469 nr := commandspb.AnnounceNode{ 470 Id: "vega-master-pubkey", 471 ChainPubKey: tmPubKey, 472 VegaPubKey: "vega-key", 473 EthereumAddress: "eth-address", 474 InfoUrl: "n0.xyz.vega/node/url/random", 475 Country: "CZ", 476 Name: "validator", 477 AvatarUrl: "http://n0.xyz/avatar", 478 } 479 480 updateEvent := events.NewValidatorUpdateEvent( 481 ctx, 482 nr.Id, 483 nr.VegaPubKey, 484 nr.VegaPubKeyIndex, 485 nr.EthereumAddress, 486 nr.ChainPubKey, 487 nr.InfoUrl, 488 nr.Country, 489 nr.Name, 490 nr.AvatarUrl, 491 nr.FromEpoch, 492 true, 493 0, 494 ) 495 496 rankingEvent := events.NewValidatorRanking(ctx, "0", nr.Id, "0", "0", "0", "pending", "tendermint", 10) 497 498 // one for the validator update, one for the ranking score 499 broker.EXPECT().Send(updateEvent).Times(1) 500 broker.EXPECT().Send(rankingEvent).Times(1) 501 502 assert.NoError(t, top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint)) 503 } 504 505 func TestValidatorTopologyKeyRotate(t *testing.T) { 506 t.Run("add key rotate - success", testAddKeyRotateSuccess) 507 t.Run("add key rotate - fails when node does not exists", testAddKeyRotateSuccessFailsOnNonExistingNode) 508 t.Run("add key rotate - fails when target block height is less then current block height", testAddKeyRotateSuccessFailsWhenTargetBlockHeightIsLessThenCurrentBlockHeight) 509 t.Run("add key rotate - fails when new key index is less then current current key index", testAddKeyRotateSuccessFailsWhenNewKeyIndexIsLessThenCurrentKeyIndex) 510 t.Run("add key rotate - fails when key rotation for node already exists", testAddKeyRotateSuccessFailsWhenKeyRotationForNodeAlreadyExists) 511 t.Run("add key rotate - fails when current pub key hash does not match", testAddKeyRotateSuccessFailsWhenCurrentPubKeyHashDoesNotMatch) 512 t.Run("beginning of block - success", testBeginBlockSuccess) 513 t.Run("beginning of block - notify key change", testBeginBlockNotifyKeyChange) 514 } 515 516 func testAddKeyRotateSuccess(t *testing.T) { 517 top := getTestTopWithDefaultValidator(t) 518 defer top.ctrl.Finish() 519 top.timeService.EXPECT().GetTimeNow().AnyTimes() 520 521 id := "vega-master-pubkey" 522 vegaPubKey := "vega-key" 523 newVegaPubKey := fmt.Sprintf("new-%s", vegaPubKey) 524 525 nr := commandspb.AnnounceNode{ 526 Id: id, 527 ChainPubKey: tmPubKey, 528 VegaPubKey: hex.EncodeToString([]byte(vegaPubKey)), 529 EthereumAddress: "eth-address", 530 } 531 ctx := context.TODO() 532 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 533 assert.NoError(t, err) 534 535 kr := &commandspb.KeyRotateSubmission{ 536 NewPubKeyIndex: 1, 537 TargetBlock: 15, 538 NewPubKey: newVegaPubKey, 539 CurrentPubKeyHash: hashKey(vegaPubKey), 540 } 541 542 err = top.AddKeyRotate(ctx, id, 10, kr) 543 assert.NoError(t, err) 544 } 545 546 func testAddKeyRotateSuccessFailsOnNonExistingNode(t *testing.T) { 547 top := getTestTopWithDefaultValidator(t) 548 defer top.ctrl.Finish() 549 top.timeService.EXPECT().GetTimeNow().AnyTimes() 550 551 id := "vega-master-pubkey" 552 newVegaPubKey := "new-ega-key" 553 554 ctx := context.TODO() 555 556 err := top.AddKeyRotate(ctx, id, 10, newKeyRotationSubmission("", newVegaPubKey, 1, 10)) 557 assert.Error(t, err) 558 assert.EqualError(t, err, "failed to add key rotate for non existing node \"vega-master-pubkey\"") 559 } 560 561 func testAddKeyRotateSuccessFailsWhenTargetBlockHeightIsLessThenCurrentBlockHeight(t *testing.T) { 562 top := getTestTopWithDefaultValidator(t) 563 defer top.ctrl.Finish() 564 top.timeService.EXPECT().GetTimeNow().AnyTimes() 565 566 id := "vega-master-pubkey" 567 vegaPubKey := "vega-key" 568 newVegaPubKey := fmt.Sprintf("new-%s", vegaPubKey) 569 570 nr := commandspb.AnnounceNode{ 571 Id: id, 572 ChainPubKey: tmPubKey, 573 VegaPubKey: hex.EncodeToString([]byte(vegaPubKey)), 574 EthereumAddress: "eth-address", 575 } 576 ctx := context.TODO() 577 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 578 assert.NoError(t, err) 579 580 err = top.AddKeyRotate(ctx, id, 15, newKeyRotationSubmission(vegaPubKey, newVegaPubKey, 1, 10)) 581 assert.ErrorIs(t, err, validators.ErrTargetBlockHeightMustBeGreaterThanCurrentHeight) 582 } 583 584 func testAddKeyRotateSuccessFailsWhenNewKeyIndexIsLessThenCurrentKeyIndex(t *testing.T) { 585 top := getTestTopWithDefaultValidator(t) 586 defer top.ctrl.Finish() 587 top.timeService.EXPECT().GetTimeNow().AnyTimes() 588 589 id := "vega-master-pubkey" 590 vegaPubKey := "vega-key" 591 newVegaPubKey := fmt.Sprintf("new-%s", vegaPubKey) 592 593 nr := commandspb.AnnounceNode{ 594 Id: id, 595 ChainPubKey: tmPubKey, 596 VegaPubKey: hex.EncodeToString([]byte(vegaPubKey)), 597 EthereumAddress: "eth-address", 598 VegaPubKeyIndex: 2, 599 } 600 ctx := context.TODO() 601 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 602 assert.NoError(t, err) 603 604 // test less then 605 err = top.AddKeyRotate(ctx, id, 10, newKeyRotationSubmission(vegaPubKey, newVegaPubKey, 1, 15)) 606 assert.ErrorIs(t, err, validators.ErrNewVegaPubKeyIndexMustBeGreaterThenCurrentPubKeyIndex) 607 } 608 609 func testAddKeyRotateSuccessFailsWhenKeyRotationForNodeAlreadyExists(t *testing.T) { 610 top := getTestTopWithDefaultValidator(t) 611 defer top.ctrl.Finish() 612 top.timeService.EXPECT().GetTimeNow().AnyTimes() 613 614 id := "vega-master-pubkey" 615 vegaPubKey := "vega-key" 616 newVegaPubKey := fmt.Sprintf("new-%s", vegaPubKey) 617 618 nr := commandspb.AnnounceNode{ 619 Id: id, 620 ChainPubKey: tmPubKey, 621 VegaPubKey: hex.EncodeToString([]byte(vegaPubKey)), 622 EthereumAddress: "eth-address", 623 VegaPubKeyIndex: 1, 624 } 625 ctx := context.TODO() 626 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 627 assert.NoError(t, err) 628 629 // add first 630 err = top.AddKeyRotate(ctx, id, 10, newKeyRotationSubmission(vegaPubKey, newVegaPubKey, 2, 12)) 631 assert.NoError(t, err) 632 633 // add second 634 err = top.AddKeyRotate(ctx, id, 10, newKeyRotationSubmission(vegaPubKey, newVegaPubKey, 2, 13)) 635 assert.ErrorIs(t, err, validators.ErrNodeAlreadyHasPendingKeyRotation) 636 } 637 638 func testAddKeyRotateSuccessFailsWhenCurrentPubKeyHashDoesNotMatch(t *testing.T) { 639 top := getTestTopWithDefaultValidator(t) 640 defer top.ctrl.Finish() 641 top.timeService.EXPECT().GetTimeNow().AnyTimes() 642 643 id := "vega-master-pubkey" 644 vegaPubKey := "vega-key" 645 newVegaPubKey := fmt.Sprintf("new-%s", vegaPubKey) 646 647 nr := commandspb.AnnounceNode{ 648 Id: id, 649 ChainPubKey: tmPubKey, 650 VegaPubKey: hex.EncodeToString([]byte(vegaPubKey)), 651 EthereumAddress: "eth-address", 652 VegaPubKeyIndex: 1, 653 } 654 ctx := context.TODO() 655 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 656 assert.NoError(t, err) 657 658 err = top.AddKeyRotate(ctx, id, 10, newKeyRotationSubmission("random-key", newVegaPubKey, 2, 12)) 659 assert.ErrorIs(t, err, validators.ErrCurrentPubKeyHashDoesNotMatch) 660 } 661 662 func hashKey(key string) string { 663 return hex.EncodeToString(vgcrypto.Hash([]byte(key))) 664 } 665 666 func newKeyRotationSubmission(currentPubKey, newVegaPubKey string, keyIndex uint32, targetBlock uint64) *commandspb.KeyRotateSubmission { 667 return &commandspb.KeyRotateSubmission{ 668 NewPubKeyIndex: keyIndex, 669 TargetBlock: targetBlock, 670 NewPubKey: newVegaPubKey, 671 CurrentPubKeyHash: hashKey(currentPubKey), 672 } 673 } 674 675 func testBeginBlockSuccess(t *testing.T) { 676 top := getTestTopWithDefaultValidator(t) 677 defer top.ctrl.Finish() 678 top.timeService.EXPECT().GetTimeNow().AnyTimes() 679 680 chainValidators := []string{"tm-pubkey-1", "tm-pubkey-2", "tm-pubkey-3", "tm-pubkey-4"} 681 682 ctx := context.TODO() 683 for i := 0; i < len(chainValidators); i++ { 684 j := i + 1 685 id := fmt.Sprintf("vega-master-pubkey-%d", j) 686 nr := commandspb.AnnounceNode{ 687 Id: id, 688 ChainPubKey: chainValidators[i], 689 VegaPubKey: hex.EncodeToString([]byte(fmt.Sprintf("vega-key-%d", j))), 690 EthereumAddress: fmt.Sprintf("eth-address-%d", j), 691 } 692 693 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 694 assert.NoErrorf(t, err, "failed to add node registation %s", id) 695 } 696 697 // add key rotations 698 err := top.AddKeyRotate(ctx, "vega-master-pubkey-1", 10, newKeyRotationSubmission("vega-key-1", "new-vega-key-1", 1, 11)) 699 assert.NoError(t, err) 700 err = top.AddKeyRotate(ctx, "vega-master-pubkey-2", 10, newKeyRotationSubmission("vega-key-2", "new-vega-key-2", 1, 11)) 701 assert.NoError(t, err) 702 err = top.AddKeyRotate(ctx, "vega-master-pubkey-3", 10, newKeyRotationSubmission("vega-key-3", "new-vega-key-3", 1, 13)) 703 assert.NoError(t, err) 704 err = top.AddKeyRotate(ctx, "vega-master-pubkey-4", 10, newKeyRotationSubmission("vega-key-4", "new-vega-key-4", 1, 13)) 705 assert.NoError(t, err) 706 707 // when 708 top.BeginBlock(ctx, 11, "") 709 // then 710 data1 := top.Get("vega-master-pubkey-1") 711 assert.NotNil(t, data1) 712 assert.Equal(t, "new-vega-key-1", data1.VegaPubKey) 713 data2 := top.Get("vega-master-pubkey-2") 714 assert.NotNil(t, data2) 715 assert.Equal(t, "new-vega-key-2", data2.VegaPubKey) 716 data3 := top.Get("vega-master-pubkey-3") 717 assert.NotNil(t, data3) 718 assert.Equal(t, hex.EncodeToString([]byte("vega-key-3")), data3.VegaPubKey) 719 data4 := top.Get("vega-master-pubkey-4") 720 assert.NotNil(t, data4) 721 assert.Equal(t, hex.EncodeToString([]byte("vega-key-4")), data4.VegaPubKey) 722 723 // when 724 top.BeginBlock(ctx, 13, "") 725 // then 726 data3 = top.Get("vega-master-pubkey-3") 727 assert.NotNil(t, data3) 728 assert.Equal(t, "new-vega-key-3", data3.VegaPubKey) 729 data4 = top.Get("vega-master-pubkey-4") 730 assert.NotNil(t, data4) 731 assert.Equal(t, "new-vega-key-4", data4.VegaPubKey) 732 } 733 734 type Callback struct { 735 mock.Mock 736 } 737 738 func (m *Callback) Call(ctx context.Context, a, b string) { 739 m.Called(ctx, a, b) 740 } 741 742 func newCallback(times int) *Callback { 743 c := Callback{} 744 c.On("Call", mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("string")).Times(times) 745 return &c 746 } 747 748 func testBeginBlockNotifyKeyChange(t *testing.T) { 749 top := getTestTopWithDefaultValidator(t) 750 defer top.ctrl.Finish() 751 top.timeService.EXPECT().GetTimeNow().AnyTimes() 752 753 chainValidators := []string{"tm-pubkey-1", "tm-pubkey-2"} 754 755 ctx := context.TODO() 756 for i := 0; i < len(chainValidators); i++ { 757 j := i + 1 758 id := fmt.Sprintf("vega-master-pubkey-%d", j) 759 nr := commandspb.AnnounceNode{ 760 Id: id, 761 ChainPubKey: chainValidators[i], 762 VegaPubKey: hex.EncodeToString([]byte(fmt.Sprintf("vega-key-%d", j))), 763 EthereumAddress: fmt.Sprintf("eth-address-%d", j), 764 } 765 766 err := top.AddNewNode(ctx, &nr, validators.ValidatorStatusTendermint) 767 assert.NoErrorf(t, err, "failed to add node registation %s", id) 768 } 769 770 // add key rotations 771 err := top.AddKeyRotate(ctx, "vega-master-pubkey-1", 10, newKeyRotationSubmission("vega-key-1", "new-vega-key-1", 1, 11)) 772 assert.NoError(t, err) 773 err = top.AddKeyRotate(ctx, "vega-master-pubkey-2", 10, newKeyRotationSubmission("vega-key-2", "new-vega-key-2", 1, 11)) 774 assert.NoError(t, err) 775 776 // register callbacks 777 c1 := newCallback(2) 778 c2 := newCallback(2) 779 top.NotifyOnKeyChange(c1.Call, c2.Call) 780 781 // when 782 top.BeginBlock(ctx, 11, "") 783 784 // then 785 c1.AssertExpectations(t) 786 c2.AssertExpectations(t) 787 }