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  }