code.vegaprotocol.io/vega@v0.79.0/core/validators/validator_set_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
    17  
    18  import (
    19  	"context"
    20  	"math/rand"
    21  	"testing"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/crypto"
    26  	"code.vegaprotocol.io/vega/libs/num"
    27  	"code.vegaprotocol.io/vega/logging"
    28  
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  func TestValidatorSet(t *testing.T) {
    33  	t.Run("test update of multisig signers network parameter", testUpdateNumberEthMultisigSigners)
    34  	t.Run("test update of number of validators network parameter", testUpdateNumberOfTendermintValidators)
    35  	t.Run("test update of incumbent factor network parameter", testUpdateValidatorIncumbentBonusFactor)
    36  	t.Run("test update of min eth events for new validator network parameter", testUpdateMinimumEthereumEventsForNewValidator)
    37  	t.Run("test update of minimum required stake", testUpdateMinimumRequireSelfStake)
    38  	t.Run("test counting of forwarded events for pending validators", testAddForwarder)
    39  	t.Run("test the number of tendermint validators is reduced hence validators being demoted", testTendermintValidatorsNumberReduced)
    40  	t.Run("test the number of tendermint validators is greater than the number of current tm, promotion is available", testTendermintFreeSlotsPromotion)
    41  	t.Run("test swap of the best ersatz with the worst tendermint validators", testSwapBestErsatzWithWorstTendermint)
    42  	t.Run("test the number of ersatz validators is reduced hence validators being demoted", testErsatzValidatorsNumberReduced)
    43  	t.Run("test the number of ersatz validators is greater than the number of current tm, promotion is available", testErsatzFreeSlotsPromotion)
    44  	t.Run("test swap of the best pending with the worst ersatz validators", testSwapBestPendingWithWorstErsatz)
    45  	t.Run("test swap of from ez to tendermint with slot reduction in ersatz", testSwapAndErsatzSlotDecrease)
    46  	t.Run("test swap of from ez to tendermint with slot increase in tendermint", testSwapAndTendermintSlotIncrease)
    47  }
    48  
    49  func TestDecreaseNumberOfTendermintValidators(t *testing.T) {
    50  	tm := int64(1654747635)
    51  	rng := rand.New(rand.NewSource(tm))
    52  	byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock }
    53  	rankingScore := map[string]num.Decimal{
    54  		"70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908": num.NewDecimalFromFloat(0.6),
    55  		"db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117": num.NewDecimalFromFloat(0.3),
    56  		"20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3": num.NewDecimalFromFloat(0.7),
    57  		"4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066": num.NewDecimalFromFloat(0.2),
    58  		"4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd": num.DecimalOne(),
    59  	}
    60  
    61  	topology := &Topology{}
    62  	topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(3))
    63  
    64  	valStates := []*valState{
    65  		{
    66  			data: ValidatorData{
    67  				ID:              "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908",
    68  				EthereumAddress: crypto.RandomHash(),
    69  			},
    70  			statusChangeBlock: 1,
    71  			status:            ValidatorStatusTendermint,
    72  		},
    73  		{
    74  			data: ValidatorData{
    75  				ID:              "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117",
    76  				EthereumAddress: crypto.RandomHash(),
    77  			},
    78  			statusChangeBlock: 1,
    79  			status:            ValidatorStatusTendermint,
    80  		},
    81  		{
    82  			data: ValidatorData{
    83  				ID:              "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3",
    84  				EthereumAddress: crypto.RandomHash(),
    85  			},
    86  			statusChangeBlock: 1,
    87  			status:            ValidatorStatusTendermint,
    88  		},
    89  		{
    90  			data: ValidatorData{
    91  				ID:              "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066",
    92  				EthereumAddress: crypto.RandomHash(),
    93  			},
    94  			statusChangeBlock: 1,
    95  			status:            ValidatorStatusTendermint,
    96  		},
    97  		{
    98  			data: ValidatorData{
    99  				ID:              "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd",
   100  				EthereumAddress: crypto.RandomHash(),
   101  			},
   102  			statusChangeBlock: 1,
   103  			status:            ValidatorStatusTendermint,
   104  		},
   105  	}
   106  
   107  	// only 4 of the 5 validators are signers on the bridge
   108  	signers := map[string]struct{}{}
   109  	for _, vs := range valStates {
   110  		signers[vs.data.EthereumAddress] = struct{}{}
   111  	}
   112  	// 4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd currently having a ranking score of 1 is not a signer
   113  	delete(signers, valStates[len(valStates)-1].data.EthereumAddress)
   114  	sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng)
   115  
   116  	// effectively can't remove any signer from the bridge and all validators are currently signers
   117  	threshold := uint32(999)
   118  
   119  	tendermintValidators, remainingValidators, removedFromTM := handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   120  	require.Equal(t, 5, len(tendermintValidators))
   121  	require.Equal(t, 0, len(remainingValidators))
   122  	require.Equal(t, 0, len(removedFromTM))
   123  
   124  	count := 0
   125  	for _, valState := range valStates {
   126  		if valState.status == ValidatorStatusTendermint {
   127  			count++
   128  		}
   129  	}
   130  	require.Equal(t, 5, count)
   131  
   132  	// let change the ranking score of the validator who is not a signer to be lowest so that it can be removed regardless of the threshold
   133  	rankingScore["4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd"] = num.DecimalFromFloat(0.1)
   134  	sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng)
   135  
   136  	tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   137  	require.Equal(t, 4, len(tendermintValidators))
   138  	require.Equal(t, 1, len(remainingValidators))
   139  	require.Equal(t, 1, len(removedFromTM))
   140  
   141  	require.Equal(t, "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd", removedFromTM[0])
   142  	count = 0
   143  	for _, valState := range valStates {
   144  		if valState.status == ValidatorStatusTendermint {
   145  			count++
   146  		}
   147  	}
   148  	require.Equal(t, 4, count)
   149  
   150  	// run for another epoch - non can be decreased anymore with the current threshold
   151  	tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   152  	require.Equal(t, 4, len(tendermintValidators))
   153  	require.Equal(t, 1, len(remainingValidators))
   154  	require.Equal(t, 0, len(removedFromTM))
   155  
   156  	// change the threshold to 700 - expcect one validator to be removed
   157  	threshold = 700
   158  	tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   159  	require.Equal(t, 3, len(tendermintValidators))
   160  	require.Equal(t, 2, len(remainingValidators))
   161  	require.Equal(t, 1, len(removedFromTM))
   162  
   163  	require.Equal(t, "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066", removedFromTM[0])
   164  	count = 0
   165  	for _, valState := range valStates {
   166  		if valState.status == ValidatorStatusTendermint {
   167  			count++
   168  		}
   169  	}
   170  	require.Equal(t, 3, count)
   171  }
   172  
   173  func TestDecreaseNumberOfTendermintValidatorsNotUpdatingContract(t *testing.T) {
   174  	tm := int64(1654747635)
   175  	rng := rand.New(rand.NewSource(tm))
   176  	byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock }
   177  	rankingScore := map[string]num.Decimal{
   178  		"70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908": num.NewDecimalFromFloat(0.6),
   179  		"db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117": num.NewDecimalFromFloat(0.3),
   180  		"20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3": num.NewDecimalFromFloat(0.7),
   181  		"4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066": num.NewDecimalFromFloat(0.2),
   182  		"4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd": num.DecimalOne(),
   183  	}
   184  
   185  	topology := &Topology{}
   186  	topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(3))
   187  
   188  	valStates := []*valState{
   189  		{
   190  			data: ValidatorData{
   191  				ID:              "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908",
   192  				EthereumAddress: crypto.RandomHash(),
   193  			},
   194  			statusChangeBlock: 1,
   195  			status:            ValidatorStatusTendermint,
   196  		},
   197  		{
   198  			data: ValidatorData{
   199  				ID:              "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117",
   200  				EthereumAddress: crypto.RandomHash(),
   201  			},
   202  			statusChangeBlock: 1,
   203  			status:            ValidatorStatusTendermint,
   204  		},
   205  		{
   206  			data: ValidatorData{
   207  				ID:              "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3",
   208  				EthereumAddress: crypto.RandomHash(),
   209  			},
   210  			statusChangeBlock: 1,
   211  			status:            ValidatorStatusTendermint,
   212  		},
   213  		{
   214  			data: ValidatorData{
   215  				ID:              "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066",
   216  				EthereumAddress: crypto.RandomHash(),
   217  			},
   218  			statusChangeBlock: 1,
   219  			status:            ValidatorStatusTendermint,
   220  		},
   221  		{
   222  			data: ValidatorData{
   223  				ID:              "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd",
   224  				EthereumAddress: crypto.RandomHash(),
   225  			},
   226  			statusChangeBlock: 1,
   227  			status:            ValidatorStatusTendermint,
   228  		},
   229  	}
   230  
   231  	signer3Address := valStates[3].data.EthereumAddress
   232  
   233  	// starting with 5 signatures on the bridge
   234  	signers := map[string]struct{}{}
   235  	for _, vs := range valStates {
   236  		signers[vs.data.EthereumAddress] = struct{}{}
   237  	}
   238  	sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng)
   239  
   240  	threshold := uint32(666)
   241  
   242  	// one validator is removed
   243  	tendermintValidators, remainingValidators, removedFromTM := handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   244  	require.Equal(t, 4, len(tendermintValidators))
   245  	require.Equal(t, 1, len(remainingValidators))
   246  	require.Equal(t, 1, len(removedFromTM))
   247  
   248  	count := 0
   249  	for _, valState := range valStates {
   250  		if valState.status == ValidatorStatusTendermint {
   251  			count++
   252  		}
   253  	}
   254  	require.Equal(t, 4, count)
   255  
   256  	// we don't update the contract so it still has 5 signers - meaning it should not allow us to remove another validator now
   257  	rankingScore["4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd"] = num.DecimalFromFloat(0.1)
   258  	sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng)
   259  
   260  	tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   261  	require.Equal(t, 4, len(tendermintValidators))
   262  	require.Equal(t, 1, len(remainingValidators))
   263  	require.Equal(t, 0, len(removedFromTM))
   264  
   265  	// now update the contract to remove the signer
   266  	delete(signers, signer3Address)
   267  	tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, remainingValidators, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, threshold)
   268  	require.Equal(t, 3, len(tendermintValidators))
   269  	require.Equal(t, 2, len(remainingValidators))
   270  	require.Equal(t, 1, len(removedFromTM))
   271  }
   272  
   273  func TestApplyPromotionAllThingsEqual(t *testing.T) {
   274  	tm := int64(1654747635)
   275  	for i := 0; i < 100; i++ {
   276  		rng := rand.New(rand.NewSource(tm))
   277  		byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock }
   278  		rankingScore := map[string]num.Decimal{
   279  			"70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908": num.DecimalZero(),
   280  			"db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117": num.DecimalZero(),
   281  			"20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3": num.DecimalZero(),
   282  			"4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066": num.DecimalZero(),
   283  			"4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd": num.DecimalZero(),
   284  		}
   285  
   286  		topology := &Topology{}
   287  		topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(3))
   288  
   289  		valStates := []*valState{
   290  			{
   291  				data: ValidatorData{
   292  					ID:              "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908",
   293  					EthereumAddress: crypto.RandomHash(),
   294  				},
   295  				statusChangeBlock: 1,
   296  				status:            ValidatorStatusTendermint,
   297  			},
   298  			{
   299  				data: ValidatorData{
   300  					ID:              "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117",
   301  					EthereumAddress: crypto.RandomHash(),
   302  				},
   303  				statusChangeBlock: 1,
   304  				status:            ValidatorStatusTendermint,
   305  			},
   306  			{
   307  				data: ValidatorData{
   308  					ID:              "20a7d70939c3453613b6d0477650f8845a6dbc0e58d2416e0aa5c27500f563b3",
   309  					EthereumAddress: crypto.RandomHash(),
   310  				},
   311  				statusChangeBlock: 1,
   312  				status:            ValidatorStatusTendermint,
   313  			},
   314  			{
   315  				data: ValidatorData{
   316  					ID:              "4a329b356c4a875077eb5babcc5b7b91f27d75fe35c52a1dc85fe079b9e14066",
   317  					EthereumAddress: crypto.RandomHash(),
   318  				},
   319  				statusChangeBlock: 1,
   320  				status:            ValidatorStatusTendermint,
   321  			},
   322  			{
   323  				data: ValidatorData{
   324  					ID:              "4dd0e9f844b16777210d2815f81d8cc6f6ecc4f9bf7b895fcee3ab982e5c1ebd",
   325  					EthereumAddress: crypto.RandomHash(),
   326  				},
   327  				statusChangeBlock: 1,
   328  				status:            ValidatorStatusTendermint,
   329  			},
   330  		}
   331  
   332  		signer1Address := valStates[1].data.EthereumAddress
   333  
   334  		rand.Seed(time.Now().UnixNano())
   335  		rand.Shuffle(len(valStates), func(i, j int) { valStates[i], valStates[j] = valStates[j], valStates[i] })
   336  
   337  		sortValidatorDescRankingScoreAscBlockcompare(valStates, rankingScore, byStatusChangeBlock, rng)
   338  
   339  		signers := map[string]struct{}{}
   340  		for _, vs := range valStates {
   341  			signers[vs.data.EthereumAddress] = struct{}{}
   342  		}
   343  
   344  		tendermintValidators, remainingValidators, removedFromTM := handleSlotChanges(valStates, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, 666)
   345  		require.Equal(t, 4, len(tendermintValidators))
   346  		require.Equal(t, 1, len(remainingValidators))
   347  		require.Equal(t, 1, len(removedFromTM))
   348  
   349  		require.Equal(t, "db14f8d4e4beebd085b22c7332d8a12d3e3841319ba78542a418c02d7740d117", removedFromTM[0])
   350  		count := 0
   351  		for _, valState := range valStates {
   352  			if valState.status == ValidatorStatusTendermint {
   353  				count++
   354  			}
   355  		}
   356  		require.Equal(t, 4, count)
   357  
   358  		delete(signers, signer1Address)
   359  
   360  		tendermintValidators, remainingValidators, removedFromTM = handleSlotChanges(tendermintValidators, []*valState{}, ValidatorStatusTendermint, ValidatorStatusErsatz, 3, int64(3), rankingScore, signers, 666)
   361  		require.Equal(t, 3, len(tendermintValidators))
   362  		require.Equal(t, 1, len(remainingValidators))
   363  		require.Equal(t, 1, len(removedFromTM))
   364  
   365  		require.Equal(t, "70b29f15c7d3cc430283dfee07e17775f041427749f7f1f8b9979bdde15ae908", removedFromTM[0])
   366  		count = 0
   367  		for _, valState := range valStates {
   368  			if valState.status == ValidatorStatusTendermint {
   369  				count++
   370  			}
   371  		}
   372  		require.Equal(t, 3, count)
   373  	}
   374  }
   375  
   376  func TestSortValidatorDescRankingScoreAscBlockStatusChanged(t *testing.T) {
   377  	tm := int64(1654747635)
   378  	for i := 0; i < 100; i++ {
   379  		rng := rand.New(rand.NewSource(tm))
   380  		byStatusChangeBlock := func(val1, val2 *valState) bool { return val1.statusChangeBlock < val2.statusChangeBlock }
   381  		valStates1 := []*valState{
   382  			{
   383  				data: ValidatorData{
   384  					ID:         "node1",
   385  					VegaPubKey: "node1Key",
   386  				},
   387  				statusChangeBlock: 1,
   388  			},
   389  			{
   390  				data: ValidatorData{
   391  					ID:         "node2",
   392  					VegaPubKey: "node2Key",
   393  				},
   394  				statusChangeBlock: 2,
   395  			},
   396  			{
   397  				data: ValidatorData{
   398  					ID:         "node3",
   399  					VegaPubKey: "node3Key",
   400  				},
   401  				statusChangeBlock: 3,
   402  			},
   403  		}
   404  		rankingScore1 := map[string]num.Decimal{
   405  			"node1": num.DecimalFromFloat(0.9),
   406  			"node2": num.DecimalFromFloat(0.5),
   407  			"node3": num.DecimalFromFloat(0.7),
   408  		}
   409  
   410  		// can sort simply by ranking score descending
   411  		sortValidatorDescRankingScoreAscBlockcompare(valStates1, rankingScore1, byStatusChangeBlock, rng)
   412  		require.Equal(t, "node1", valStates1[0].data.ID)
   413  		require.Equal(t, "node3", valStates1[1].data.ID)
   414  		require.Equal(t, "node2", valStates1[2].data.ID)
   415  
   416  		valStates2 := []*valState{
   417  			{
   418  				data: ValidatorData{
   419  					ID:         "node1",
   420  					VegaPubKey: "node1Key",
   421  				},
   422  				statusChangeBlock: 1,
   423  			},
   424  			{
   425  				data: ValidatorData{
   426  					ID:         "node2",
   427  					VegaPubKey: "node2Key",
   428  				},
   429  				statusChangeBlock: 2,
   430  			},
   431  			{
   432  				data: ValidatorData{
   433  					ID:         "node3",
   434  					VegaPubKey: "node3Key",
   435  				},
   436  				statusChangeBlock: 3,
   437  			},
   438  		}
   439  		rankingScore2 := map[string]num.Decimal{
   440  			"node1": num.DecimalFromFloat(0.9),
   441  			"node2": num.DecimalFromFloat(0.5),
   442  			"node3": num.DecimalFromFloat(0.5),
   443  		}
   444  
   445  		// need to use last block change state sorted ascending as tie breaker - node 2 changed state before node 3 so it comes before it
   446  		sortValidatorDescRankingScoreAscBlockcompare(valStates2, rankingScore2, byStatusChangeBlock, rng)
   447  		require.Equal(t, "node1", valStates2[0].data.ID)
   448  		require.Equal(t, "node2", valStates2[1].data.ID)
   449  		require.Equal(t, "node3", valStates2[2].data.ID)
   450  
   451  		valStates3 := []*valState{
   452  			{
   453  				data: ValidatorData{
   454  					ID:         "node1",
   455  					VegaPubKey: "node1Key",
   456  				},
   457  				statusChangeBlock: 1,
   458  			},
   459  			{
   460  				data: ValidatorData{
   461  					ID:         "node2",
   462  					VegaPubKey: "node2Key",
   463  				},
   464  				statusChangeBlock: 1,
   465  			},
   466  			{
   467  				data: ValidatorData{
   468  					ID:         "node3",
   469  					VegaPubKey: "node3Key",
   470  				},
   471  				statusChangeBlock: 1,
   472  			},
   473  			{
   474  				data: ValidatorData{
   475  					ID:         "node4",
   476  					VegaPubKey: "node4Key",
   477  				},
   478  				statusChangeBlock: 1,
   479  			},
   480  		}
   481  		rankingScore3 := map[string]num.Decimal{
   482  			"node1": num.DecimalFromFloat(0.9),
   483  			"node2": num.DecimalFromFloat(0.5),
   484  			"node3": num.DecimalFromFloat(0.5),
   485  			"node4": num.DecimalFromFloat(0.9),
   486  		}
   487  
   488  		// need to use last block change state sorted ascending as tie breaker - node 2 changed state before node 3 so it comes before it
   489  		sortValidatorDescRankingScoreAscBlockcompare(valStates3, rankingScore3, byStatusChangeBlock, rng)
   490  		require.Equal(t, "node1", valStates3[0].data.ID)
   491  		require.Equal(t, "node4", valStates3[1].data.ID)
   492  		require.Equal(t, "node2", valStates3[2].data.ID)
   493  		require.Equal(t, "node3", valStates3[3].data.ID)
   494  
   495  		valStates4 := []*valState{
   496  			{
   497  				data: ValidatorData{
   498  					ID:         "node1",
   499  					VegaPubKey: "node1Key",
   500  				},
   501  				statusChangeBlock: 1,
   502  			},
   503  			{
   504  				data: ValidatorData{
   505  					ID:         "node2",
   506  					VegaPubKey: "node2Key",
   507  				},
   508  				statusChangeBlock: 1,
   509  			},
   510  			{
   511  				data: ValidatorData{
   512  					ID:         "node3",
   513  					VegaPubKey: "node3Key",
   514  				},
   515  				statusChangeBlock: 1,
   516  			},
   517  			{
   518  				data: ValidatorData{
   519  					ID:         "node4",
   520  					VegaPubKey: "node4Key",
   521  				},
   522  				statusChangeBlock: 1,
   523  			},
   524  		}
   525  		rankingScore4 := map[string]num.Decimal{
   526  			"node1": num.DecimalFromFloat(0.5),
   527  			"node2": num.DecimalFromFloat(0.5),
   528  			"node3": num.DecimalFromFloat(0.5),
   529  			"node4": num.DecimalFromFloat(0.9),
   530  		}
   531  		sortValidatorDescRankingScoreAscBlockcompare(valStates4, rankingScore4, byStatusChangeBlock, rng)
   532  		require.Equal(t, "node4", valStates4[0].data.ID)
   533  		require.Equal(t, "node1", valStates4[1].data.ID)
   534  		require.Equal(t, "node2", valStates4[2].data.ID)
   535  		require.Equal(t, "node3", valStates4[3].data.ID)
   536  	}
   537  }
   538  
   539  func testUpdateNumberEthMultisigSigners(t *testing.T) {
   540  	topology := &Topology{}
   541  	topology.UpdateNumberEthMultisigSigners(context.Background(), num.NewUint(10))
   542  	require.Equal(t, 10, topology.numberEthMultisigSigners)
   543  }
   544  
   545  func testUpdateNumberOfTendermintValidators(t *testing.T) {
   546  	topology := &Topology{}
   547  	topology.UpdateNumberOfTendermintValidators(context.Background(), num.NewUint(20))
   548  	topology.UpdateErsatzValidatorsFactor(context.Background(), num.DecimalFromFloat(0.5))
   549  	require.Equal(t, 20, topology.numberOfTendermintValidators)
   550  	require.Equal(t, 10, topology.numberOfErsatzValidators)
   551  }
   552  
   553  func testUpdateValidatorIncumbentBonusFactor(t *testing.T) {
   554  	topology := &Topology{}
   555  	topology.UpdateValidatorIncumbentBonusFactor(context.Background(), num.DecimalFromFloat(0.2))
   556  	require.Equal(t, "1.2", topology.validatorIncumbentBonusFactor.String())
   557  }
   558  
   559  func testUpdateMinimumEthereumEventsForNewValidator(t *testing.T) {
   560  	topology := &Topology{}
   561  	topology.UpdateMinimumEthereumEventsForNewValidator(context.Background(), num.NewUint(4))
   562  	require.Equal(t, uint64(4), topology.minimumEthereumEventsForNewValidator)
   563  }
   564  
   565  func testUpdateMinimumRequireSelfStake(t *testing.T) {
   566  	topology := &Topology{}
   567  	topology.UpdateMinimumRequireSelfStake(context.Background(), num.DecimalFromFloat(30000))
   568  	require.Equal(t, num.NewUint(30000), topology.minimumStake)
   569  }
   570  
   571  func testAddForwarder(t *testing.T) {
   572  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
   573  	// add unknown forwarder
   574  	topology.AddForwarder("node1")
   575  	require.Equal(t, 0, len(topology.validators))
   576  
   577  	topology.validators["node2"] = &valState{
   578  		data: ValidatorData{
   579  			ID:         "node2",
   580  			VegaPubKey: "node2Key",
   581  		},
   582  		heartbeatTracker: &validatorHeartbeatTracker{},
   583  	}
   584  	topology.validators["node3"] = &valState{
   585  		data: ValidatorData{
   586  			ID:         "node3",
   587  			VegaPubKey: "node3Key",
   588  		},
   589  		heartbeatTracker: &validatorHeartbeatTracker{},
   590  	}
   591  	require.Equal(t, uint64(0), topology.validators["node3"].numberOfEthereumEventsForwarded)
   592  	require.Equal(t, uint64(0), topology.validators["node2"].numberOfEthereumEventsForwarded)
   593  	topology.AddForwarder("node3Key")
   594  	require.Equal(t, uint64(1), topology.validators["node3"].numberOfEthereumEventsForwarded)
   595  	require.Equal(t, uint64(0), topology.validators["node2"].numberOfEthereumEventsForwarded)
   596  
   597  	topology.AddForwarder("node2Key")
   598  	require.Equal(t, uint64(1), topology.validators["node3"].numberOfEthereumEventsForwarded)
   599  	require.Equal(t, uint64(1), topology.validators["node2"].numberOfEthereumEventsForwarded)
   600  	topology.AddForwarder("node3Key")
   601  	require.Equal(t, uint64(2), topology.validators["node3"].numberOfEthereumEventsForwarded)
   602  	require.Equal(t, uint64(1), topology.validators["node2"].numberOfEthereumEventsForwarded)
   603  	topology.AddForwarder("node3Key")
   604  	require.Equal(t, uint64(3), topology.validators["node3"].numberOfEthereumEventsForwarded)
   605  	require.Equal(t, uint64(1), topology.validators["node2"].numberOfEthereumEventsForwarded)
   606  }
   607  
   608  func testTendermintValidatorsNumberReduced(t *testing.T) {
   609  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
   610  	topology.numberOfTendermintValidators = 5
   611  	topology.rng = rand.New(rand.NewSource(100000))
   612  	topology.validators["node1"] = &valState{
   613  		data: ValidatorData{
   614  			ID:       "node1",
   615  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
   616  		},
   617  		blockAdded:       1,
   618  		status:           ValidatorStatusTendermint,
   619  		heartbeatTracker: &validatorHeartbeatTracker{},
   620  	}
   621  	topology.validators["node2"] = &valState{
   622  		data: ValidatorData{
   623  			ID:       "node2",
   624  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
   625  		},
   626  		blockAdded:       1,
   627  		status:           ValidatorStatusTendermint,
   628  		heartbeatTracker: &validatorHeartbeatTracker{},
   629  	}
   630  	topology.validators["node3"] = &valState{
   631  		data: ValidatorData{
   632  			ID:       "node3",
   633  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
   634  		},
   635  		blockAdded:       2,
   636  		status:           ValidatorStatusTendermint,
   637  		heartbeatTracker: &validatorHeartbeatTracker{},
   638  	}
   639  	topology.validators["node4"] = &valState{
   640  		data: ValidatorData{
   641  			ID:       "node4",
   642  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
   643  		},
   644  		blockAdded:       3,
   645  		status:           ValidatorStatusTendermint,
   646  		heartbeatTracker: &validatorHeartbeatTracker{},
   647  	}
   648  	topology.validators["node5"] = &valState{
   649  		data: ValidatorData{
   650  			ID:       "node5",
   651  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
   652  		},
   653  		blockAdded:       4,
   654  		status:           ValidatorStatusTendermint,
   655  		heartbeatTracker: &validatorHeartbeatTracker{},
   656  	}
   657  
   658  	perf := map[string]num.Decimal{
   659  		"node1": decimalOne,
   660  		"node2": decimalOne,
   661  		"node3": decimalOne,
   662  		"node4": decimalOne,
   663  		"node5": decimalOne,
   664  	}
   665  
   666  	ranking := map[string]num.Decimal{
   667  		"node1": num.DecimalFromFloat(0.8),
   668  		"node2": num.DecimalFromFloat(0.8),
   669  		"node3": num.DecimalFromFloat(0.4),
   670  		"node4": num.DecimalFromFloat(0.5),
   671  		"node5": num.DecimalFromFloat(0.6),
   672  	}
   673  
   674  	delegations := []*types.ValidatorData{
   675  		{NodeID: "node1", SelfStake: num.NewUint(3000), StakeByDelegators: num.NewUint(7000)},
   676  		{NodeID: "node2", SelfStake: num.NewUint(3000), StakeByDelegators: num.NewUint(2000)},
   677  		{NodeID: "node5", SelfStake: num.NewUint(85000), StakeByDelegators: num.NewUint(0)},
   678  	}
   679  
   680  	// reduce the number of tendermint validators to 3 to that 2 must be removed, i.e. nodes 3 and 4 with the lowest scores
   681  	topology.currentBlockHeight = 1000
   682  	topology.numberOfTendermintValidators = 3
   683  	topology.numberOfErsatzValidators = 5
   684  	res, _ := topology.applyPromotion(perf, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(3), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(1)})
   685  
   686  	// node1 has 10000 / 100000 = 0.1 => 0.6666666667 => 6666
   687  	require.Equal(t, int64(6666), res[0].Power) // 10000 * 0.8/2.2
   688  	// node2 has 5000 / 100000 = 0.05 => 0.3333333333 => 3333
   689  	require.Equal(t, int64(3333), res[1].Power) // 10000 * 0.8/2.2
   690  	// node3 is remove => 0
   691  	require.Equal(t, int64(0), res[2].Power) // remove from rm
   692  	// node4 is remove => 0
   693  	require.Equal(t, int64(0), res[3].Power) // remove from rm
   694  	// node5 is anti-whaled => 0 => 0 => 10
   695  	require.Equal(t, int64(1), res[4].Power) // 10000 * 0.6/2.2
   696  
   697  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node3"].status])
   698  	require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock)
   699  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node4"].status])
   700  	require.Equal(t, int64(1001), topology.validators["node4"].statusChangeBlock)
   701  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status])
   702  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status])
   703  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status])
   704  }
   705  
   706  func testTendermintFreeSlotsPromotion(t *testing.T) {
   707  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
   708  	topology.numberOfTendermintValidators = 5
   709  	topology.numberOfErsatzValidators = 1
   710  	topology.validators["node1"] = &valState{
   711  		data: ValidatorData{
   712  			ID:       "node1",
   713  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
   714  		},
   715  		blockAdded:        1,
   716  		status:            ValidatorStatusTendermint,
   717  		statusChangeBlock: 900,
   718  		heartbeatTracker:  &validatorHeartbeatTracker{},
   719  	}
   720  	topology.validators["node2"] = &valState{
   721  		data: ValidatorData{
   722  			ID:       "node2",
   723  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
   724  		},
   725  		blockAdded:        1,
   726  		status:            ValidatorStatusTendermint,
   727  		statusChangeBlock: 901,
   728  		heartbeatTracker:  &validatorHeartbeatTracker{},
   729  	}
   730  	topology.validators["node3"] = &valState{
   731  		data: ValidatorData{
   732  			ID:       "node3",
   733  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
   734  		},
   735  		blockAdded:        2,
   736  		status:            ValidatorStatusTendermint,
   737  		statusChangeBlock: 902,
   738  		heartbeatTracker:  &validatorHeartbeatTracker{},
   739  	}
   740  	topology.validators["node4"] = &valState{
   741  		data: ValidatorData{
   742  			ID:       "node4",
   743  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
   744  		},
   745  		blockAdded:                      3,
   746  		status:                          ValidatorStatusPending,
   747  		statusChangeBlock:               903,
   748  		numberOfEthereumEventsForwarded: 2,
   749  		heartbeatTracker: &validatorHeartbeatTracker{
   750  			blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false},
   751  		},
   752  	}
   753  	topology.validators["node5"] = &valState{
   754  		data: ValidatorData{
   755  			ID:       "node5",
   756  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
   757  		},
   758  		blockAdded:                      4,
   759  		statusChangeBlock:               904,
   760  		status:                          ValidatorStatusErsatz,
   761  		numberOfEthereumEventsForwarded: 4,
   762  		heartbeatTracker: &validatorHeartbeatTracker{
   763  			blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false},
   764  		},
   765  	}
   766  
   767  	perfScore := map[string]num.Decimal{
   768  		"node1": decimalOne,
   769  		"node2": decimalOne,
   770  		"node3": decimalOne,
   771  		"node4": decimalOne,
   772  		"node5": decimalOne,
   773  	}
   774  
   775  	ranking := map[string]num.Decimal{
   776  		"node1": num.DecimalFromFloat(0.8),
   777  		"node2": num.DecimalFromFloat(0.8),
   778  		"node3": num.DecimalFromFloat(0.4),
   779  		"node4": num.DecimalFromFloat(0),
   780  		"node5": num.DecimalFromFloat(0.6),
   781  	}
   782  
   783  	delegations := []*types.ValidatorData{
   784  		{NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)},
   785  		{NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)},
   786  		{NodeID: "node3", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)},
   787  		{NodeID: "node4", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
   788  		{NodeID: "node5", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)},
   789  	}
   790  
   791  	// there are 5 slots for tm validators but only 3 currently
   792  	// there are 2 potential validators for promotion but one of them has not completed their prereq
   793  	topology.currentBlockHeight = 1000
   794  	res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(4), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(1)})
   795  	require.Equal(t, 4, len(res))
   796  	// node1 => 20000 / 100000 => 0.2 => 0.2857142857 => 3333
   797  	require.Equal(t, int64(2857), res[0].Power)
   798  	// node2 => 15000 / 100000 => 0.15 => 0.2142857143 => 2142
   799  	require.Equal(t, int64(2142), res[1].Power)
   800  	// node3 => 25000 / 100000 => 0.25 => 0.3571428571 = 3571
   801  	require.Equal(t, int64(3571), res[2].Power)
   802  	// node5 => 40000 / 100000 => 0.1 (0.4 - 0.15 - 0.15)=> 0.1428571429 => 1428
   803  	require.Equal(t, int64(1428), res[3].Power)
   804  
   805  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status])
   806  	require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock)
   807  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status])
   808  	require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock)
   809  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node3"].status])
   810  	require.Equal(t, int64(902), topology.validators["node3"].statusChangeBlock)
   811  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status])
   812  	require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock)
   813  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status])
   814  	require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock)
   815  }
   816  
   817  func testSwapBestErsatzWithWorstTendermint(t *testing.T) {
   818  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
   819  	topology.numberOfTendermintValidators = 4
   820  	topology.numberOfErsatzValidators = 1
   821  	topology.validators["node1"] = &valState{
   822  		data: ValidatorData{
   823  			ID:       "node1",
   824  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
   825  		},
   826  		blockAdded:        1,
   827  		status:            ValidatorStatusTendermint,
   828  		statusChangeBlock: 900,
   829  		heartbeatTracker:  &validatorHeartbeatTracker{},
   830  	}
   831  	topology.validators["node2"] = &valState{
   832  		data: ValidatorData{
   833  			ID:       "node2",
   834  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
   835  		},
   836  		blockAdded:        1,
   837  		status:            ValidatorStatusTendermint,
   838  		statusChangeBlock: 901,
   839  		heartbeatTracker:  &validatorHeartbeatTracker{},
   840  	}
   841  	topology.validators["node3"] = &valState{
   842  		data: ValidatorData{
   843  			ID:       "node3",
   844  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
   845  		},
   846  		blockAdded:        2,
   847  		status:            ValidatorStatusTendermint,
   848  		statusChangeBlock: 902,
   849  		heartbeatTracker:  &validatorHeartbeatTracker{},
   850  	}
   851  	topology.validators["node4"] = &valState{
   852  		data: ValidatorData{
   853  			ID:       "node4",
   854  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
   855  		},
   856  		blockAdded:        3,
   857  		status:            ValidatorStatusTendermint,
   858  		statusChangeBlock: 903,
   859  		heartbeatTracker:  &validatorHeartbeatTracker{},
   860  	}
   861  	topology.validators["node5"] = &valState{
   862  		data: ValidatorData{
   863  			ID:       "node5",
   864  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
   865  		},
   866  		blockAdded:        4,
   867  		statusChangeBlock: 904,
   868  		status:            ValidatorStatusErsatz,
   869  		heartbeatTracker:  &validatorHeartbeatTracker{},
   870  	}
   871  
   872  	perfScore := map[string]num.Decimal{
   873  		"node1": decimalOne,
   874  		"node2": decimalOne,
   875  		"node3": decimalOne,
   876  		"node4": decimalOne,
   877  		"node5": decimalOne,
   878  	}
   879  
   880  	ranking := map[string]num.Decimal{
   881  		"node1": num.DecimalFromFloat(0.8),
   882  		"node2": num.DecimalFromFloat(0.8),
   883  		"node3": num.DecimalFromFloat(0.4),
   884  		"node4": num.DecimalFromFloat(0.5),
   885  		"node5": num.DecimalFromFloat(0.6),
   886  	}
   887  
   888  	delegations := []*types.ValidatorData{
   889  		{NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)},
   890  		{NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)},
   891  		{NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)},
   892  		{NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)},
   893  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
   894  	}
   895  
   896  	// there are 4 slots for tm validators and the best ersatz (node5) has better performance than the worst tm (node3)
   897  	// therefore node3 is kicked out of tm and becomes ersatz and node5 is added to tm
   898  	topology.currentBlockHeight = 1000
   899  	res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(4), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(1)})
   900  	require.Equal(t, 5, len(res))
   901  	// node1 => 20000 / 100000 => 0.2 => 0.2857142857 => 3333
   902  	require.Equal(t, int64(2857), res[0].Power)
   903  	// node2 => 15000 / 100000 => 0.15 => 0.2142857143 => 2142
   904  	require.Equal(t, int64(2142), res[1].Power)
   905  	require.Equal(t, int64(0), res[2].Power) // node3 kicked out of tm
   906  	// node4 => 25000 / 100000 => 0.25 => 0.3571428571 = 3571
   907  	require.Equal(t, int64(3571), res[3].Power)
   908  	// node5 => 40000 / 100000 => 0.1 (0.4 - 0.15 - 0.15)=> 0.1428571429 => 1428
   909  	require.Equal(t, int64(1428), res[4].Power)
   910  
   911  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status])
   912  	require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock)
   913  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status])
   914  	require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock)
   915  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node3"].status])
   916  	require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock)
   917  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node4"].status])
   918  	require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock)
   919  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status])
   920  	require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock)
   921  }
   922  
   923  func testErsatzFreeSlotsPromotion(t *testing.T) {
   924  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
   925  	topology.numberOfTendermintValidators = 1
   926  	topology.numberOfErsatzValidators = 4
   927  
   928  	topology.validators["node1"] = &valState{
   929  		data: ValidatorData{
   930  			ID:       "node1",
   931  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
   932  		},
   933  		blockAdded:        1,
   934  		status:            ValidatorStatusTendermint,
   935  		statusChangeBlock: 900,
   936  		heartbeatTracker:  &validatorHeartbeatTracker{},
   937  	}
   938  	topology.validators["node2"] = &valState{
   939  		data: ValidatorData{
   940  			ID:       "node2",
   941  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
   942  		},
   943  		blockAdded:        1,
   944  		status:            ValidatorStatusErsatz,
   945  		statusChangeBlock: 901,
   946  		heartbeatTracker:  &validatorHeartbeatTracker{},
   947  	}
   948  	topology.validators["node3"] = &valState{
   949  		data: ValidatorData{
   950  			ID:       "node3",
   951  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
   952  		},
   953  		blockAdded:        2,
   954  		status:            ValidatorStatusErsatz,
   955  		statusChangeBlock: 902,
   956  		heartbeatTracker:  &validatorHeartbeatTracker{},
   957  	}
   958  	topology.validators["node4"] = &valState{
   959  		data: ValidatorData{
   960  			ID:       "node4",
   961  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
   962  		},
   963  		blockAdded:                      3,
   964  		status:                          ValidatorStatusPending,
   965  		statusChangeBlock:               903,
   966  		numberOfEthereumEventsForwarded: 2,
   967  		heartbeatTracker: &validatorHeartbeatTracker{
   968  			blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false},
   969  		},
   970  	}
   971  	topology.validators["node5"] = &valState{
   972  		data: ValidatorData{
   973  			ID:       "node5",
   974  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
   975  		},
   976  		blockAdded:                      4,
   977  		statusChangeBlock:               904,
   978  		status:                          ValidatorStatusPending,
   979  		numberOfEthereumEventsForwarded: 4,
   980  		heartbeatTracker: &validatorHeartbeatTracker{
   981  			blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false},
   982  		},
   983  	}
   984  
   985  	perfScore := map[string]num.Decimal{
   986  		"node1": decimalOne,
   987  		"node2": decimalOne,
   988  		"node3": decimalOne,
   989  		"node4": decimalOne,
   990  		"node5": decimalOne,
   991  	}
   992  
   993  	ranking := map[string]num.Decimal{
   994  		"node1": num.DecimalFromFloat(0.8),
   995  		"node2": num.DecimalFromFloat(0.8),
   996  		"node3": num.DecimalFromFloat(0.4),
   997  		"node4": num.DecimalFromFloat(0),
   998  		"node5": num.DecimalFromFloat(0.6),
   999  	}
  1000  
  1001  	delegations := []*types.ValidatorData{
  1002  		{NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)},
  1003  		{NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)},
  1004  		{NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)},
  1005  		{NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)},
  1006  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1007  	}
  1008  
  1009  	// there's only 1 slot for tendermint and it's taken by the node with the highest rank anyways.
  1010  	// there are 4 slots for ersatz validators and only 2 taken so the other two can be promoted
  1011  	// there are 2 potential validators for promotion but one of them has not completed their prerequisites
  1012  	topology.currentBlockHeight = 1000
  1013  	res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(3)})
  1014  	require.Equal(t, int64(10000), res[0].Power) // only node 1 is here
  1015  
  1016  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status])
  1017  	require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock)
  1018  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node2"].status])
  1019  	require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock)
  1020  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node3"].status])
  1021  	require.Equal(t, int64(902), topology.validators["node3"].statusChangeBlock)
  1022  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status])
  1023  	require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock)
  1024  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node5"].status])
  1025  	require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock)
  1026  }
  1027  
  1028  func testSwapBestPendingWithWorstErsatz(t *testing.T) {
  1029  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
  1030  	topology.numberOfTendermintValidators = 1
  1031  	topology.numberOfErsatzValidators = 2
  1032  
  1033  	topology.validators["node1"] = &valState{
  1034  		data: ValidatorData{
  1035  			ID:       "node1",
  1036  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
  1037  		},
  1038  		blockAdded:        1,
  1039  		status:            ValidatorStatusTendermint,
  1040  		statusChangeBlock: 900,
  1041  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1042  	}
  1043  	topology.validators["node2"] = &valState{
  1044  		data: ValidatorData{
  1045  			ID:       "node2",
  1046  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
  1047  		},
  1048  		blockAdded:        1,
  1049  		status:            ValidatorStatusErsatz,
  1050  		statusChangeBlock: 901,
  1051  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1052  	}
  1053  	topology.validators["node3"] = &valState{
  1054  		data: ValidatorData{
  1055  			ID:       "node3",
  1056  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
  1057  		},
  1058  		blockAdded:        2,
  1059  		status:            ValidatorStatusErsatz,
  1060  		statusChangeBlock: 902,
  1061  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1062  	}
  1063  	topology.validators["node4"] = &valState{
  1064  		data: ValidatorData{
  1065  			ID:       "node4",
  1066  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
  1067  		},
  1068  		blockAdded:                      3,
  1069  		status:                          ValidatorStatusPending,
  1070  		statusChangeBlock:               903,
  1071  		numberOfEthereumEventsForwarded: 2,
  1072  		heartbeatTracker: &validatorHeartbeatTracker{
  1073  			blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false},
  1074  		},
  1075  	}
  1076  	topology.validators["node5"] = &valState{
  1077  		data: ValidatorData{
  1078  			ID:       "node5",
  1079  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
  1080  		},
  1081  		blockAdded:                      4,
  1082  		statusChangeBlock:               904,
  1083  		status:                          ValidatorStatusPending,
  1084  		numberOfEthereumEventsForwarded: 4,
  1085  		heartbeatTracker: &validatorHeartbeatTracker{
  1086  			blockSigs: [10]bool{true, true, true, true, false, true, true, true, true, false},
  1087  		},
  1088  	}
  1089  
  1090  	perfScore := map[string]num.Decimal{
  1091  		"node1": decimalOne,
  1092  		"node2": decimalOne,
  1093  		"node3": decimalOne,
  1094  		"node4": decimalOne,
  1095  		"node5": decimalOne,
  1096  	}
  1097  
  1098  	ranking := map[string]num.Decimal{
  1099  		"node1": num.DecimalFromFloat(0.8),
  1100  		"node2": num.DecimalFromFloat(0.8),
  1101  		"node3": num.DecimalFromFloat(0.4),
  1102  		"node4": num.DecimalFromFloat(0),
  1103  		"node5": num.DecimalFromFloat(0.6),
  1104  	}
  1105  
  1106  	delegations := []*types.ValidatorData{
  1107  		{NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)},
  1108  		{NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)},
  1109  		{NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)},
  1110  		{NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)},
  1111  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1112  	}
  1113  
  1114  	// there's only 1 slot for tendermint and it's taken by the node with the highest rank anyways.
  1115  	// there are 2 slots for ersatz validators both taken
  1116  	// the score of node5 is higher than the lowest ersatz so they swap places
  1117  	topology.currentBlockHeight = 1000
  1118  	res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)})
  1119  	require.Equal(t, int64(10000), res[0].Power) // only node 1 is here
  1120  
  1121  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status])
  1122  	require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock)
  1123  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node2"].status])
  1124  	require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock)
  1125  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node3"].status])
  1126  	require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock)
  1127  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status])
  1128  	require.Equal(t, int64(903), topology.validators["node4"].statusChangeBlock)
  1129  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node5"].status])
  1130  	require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock)
  1131  }
  1132  
  1133  func testErsatzValidatorsNumberReduced(t *testing.T) {
  1134  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
  1135  	topology.numberOfTendermintValidators = 1
  1136  	topology.validators["node1"] = &valState{
  1137  		data: ValidatorData{
  1138  			ID:       "node1",
  1139  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
  1140  		},
  1141  		blockAdded:        1,
  1142  		statusChangeBlock: 900,
  1143  		status:            ValidatorStatusTendermint,
  1144  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1145  	}
  1146  	topology.validators["node2"] = &valState{
  1147  		data: ValidatorData{
  1148  			ID:       "node2",
  1149  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
  1150  		},
  1151  		blockAdded:        1,
  1152  		statusChangeBlock: 901,
  1153  		status:            ValidatorStatusErsatz,
  1154  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1155  	}
  1156  	topology.validators["node3"] = &valState{
  1157  		data: ValidatorData{
  1158  			ID:       "node3",
  1159  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
  1160  		},
  1161  		blockAdded:       2,
  1162  		status:           ValidatorStatusErsatz,
  1163  		heartbeatTracker: &validatorHeartbeatTracker{},
  1164  	}
  1165  	topology.validators["node4"] = &valState{
  1166  		data: ValidatorData{
  1167  			ID:       "node4",
  1168  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
  1169  		},
  1170  		blockAdded:       3,
  1171  		status:           ValidatorStatusErsatz,
  1172  		heartbeatTracker: &validatorHeartbeatTracker{},
  1173  	}
  1174  	topology.validators["node5"] = &valState{
  1175  		data: ValidatorData{
  1176  			ID:       "node5",
  1177  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
  1178  		},
  1179  		blockAdded:       4,
  1180  		status:           ValidatorStatusErsatz,
  1181  		heartbeatTracker: &validatorHeartbeatTracker{},
  1182  	}
  1183  
  1184  	perfScore := map[string]num.Decimal{
  1185  		"node1": decimalOne,
  1186  		"node2": decimalOne,
  1187  		"node3": decimalOne,
  1188  		"node4": decimalOne,
  1189  		"node5": decimalOne,
  1190  	}
  1191  
  1192  	ranking := map[string]num.Decimal{
  1193  		"node1": num.DecimalFromFloat(0.8),
  1194  		"node2": num.DecimalFromFloat(0.8),
  1195  		"node3": num.DecimalFromFloat(0.4),
  1196  		"node4": num.DecimalFromFloat(0.5),
  1197  		"node5": num.DecimalFromFloat(0.6),
  1198  	}
  1199  
  1200  	delegations := []*types.ValidatorData{
  1201  		{NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(8000)},
  1202  		{NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(5000)},
  1203  		{NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(0)},
  1204  		{NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(5000)},
  1205  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1206  	}
  1207  
  1208  	// reduce the number of ersatz validators from 4 to 1 so that 3 with the lower scores are demoted to pending
  1209  	topology.currentBlockHeight = 1000
  1210  	topology.numberOfErsatzValidators = 1
  1211  	res, _ := topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)})
  1212  	require.Equal(t, int64(10000), res[0].Power) // 10000 * 1
  1213  
  1214  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node1"].status])
  1215  	require.Equal(t, int64(900), topology.validators["node1"].statusChangeBlock)
  1216  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node2"].status])
  1217  	require.Equal(t, int64(901), topology.validators["node2"].statusChangeBlock)
  1218  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node3"].status])
  1219  	require.Equal(t, int64(1001), topology.validators["node3"].statusChangeBlock)
  1220  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node4"].status])
  1221  	require.Equal(t, int64(1001), topology.validators["node4"].statusChangeBlock)
  1222  	require.Equal(t, "pending", ValidatorStatusToName[topology.validators["node5"].status])
  1223  	require.Equal(t, int64(1001), topology.validators["node5"].statusChangeBlock)
  1224  }
  1225  
  1226  func testSwapAndErsatzSlotDecrease(t *testing.T) {
  1227  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
  1228  	topology.numberOfTendermintValidators = 4
  1229  	topology.numberOfErsatzValidators = 2
  1230  	topology.validators["node1"] = &valState{
  1231  		data: ValidatorData{
  1232  			ID:       "node1",
  1233  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
  1234  		},
  1235  		blockAdded:        1,
  1236  		statusChangeBlock: 900,
  1237  		status:            ValidatorStatusTendermint,
  1238  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1239  	}
  1240  	topology.validators["node2"] = &valState{
  1241  		data: ValidatorData{
  1242  			ID:       "node2",
  1243  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
  1244  		},
  1245  		blockAdded:        1,
  1246  		statusChangeBlock: 901,
  1247  		status:            ValidatorStatusTendermint,
  1248  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1249  	}
  1250  	topology.validators["node3"] = &valState{
  1251  		data: ValidatorData{
  1252  			ID:       "node3",
  1253  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
  1254  		},
  1255  		blockAdded:       2,
  1256  		status:           ValidatorStatusTendermint,
  1257  		heartbeatTracker: &validatorHeartbeatTracker{},
  1258  	}
  1259  	topology.validators["node4"] = &valState{
  1260  		data: ValidatorData{
  1261  			ID:       "node4",
  1262  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
  1263  		},
  1264  		blockAdded:       3,
  1265  		status:           ValidatorStatusTendermint,
  1266  		heartbeatTracker: &validatorHeartbeatTracker{},
  1267  	}
  1268  	topology.validators["node5"] = &valState{
  1269  		data: ValidatorData{
  1270  			ID:       "node5",
  1271  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
  1272  		},
  1273  		blockAdded:       4,
  1274  		status:           ValidatorStatusErsatz,
  1275  		heartbeatTracker: &validatorHeartbeatTracker{},
  1276  	}
  1277  	topology.validators["node6"] = &valState{
  1278  		data: ValidatorData{
  1279  			ID:       "node5",
  1280  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
  1281  		},
  1282  		blockAdded:       4,
  1283  		status:           ValidatorStatusErsatz,
  1284  		heartbeatTracker: &validatorHeartbeatTracker{},
  1285  	}
  1286  
  1287  	perfScore := map[string]num.Decimal{
  1288  		"node1": decimalOne,
  1289  		"node2": decimalOne,
  1290  		"node3": decimalOne,
  1291  		"node4": decimalOne,
  1292  		"node5": decimalOne,
  1293  		"node6": decimalOne,
  1294  	}
  1295  
  1296  	ranking := map[string]num.Decimal{
  1297  		"node1": num.DecimalFromFloat(0.1),
  1298  		"node2": num.DecimalFromFloat(0.8),
  1299  		"node3": num.DecimalFromFloat(0.8),
  1300  		"node4": num.DecimalFromFloat(0.8),
  1301  		"node5": num.DecimalFromFloat(0.8),
  1302  		"node6": num.DecimalFromFloat(0.5),
  1303  	}
  1304  
  1305  	delegations := []*types.ValidatorData{
  1306  		{NodeID: "node1", SelfStake: num.NewUint(12000), StakeByDelegators: num.NewUint(10000)},
  1307  		{NodeID: "node2", SelfStake: num.NewUint(10000), StakeByDelegators: num.NewUint(10000)},
  1308  		{NodeID: "node3", SelfStake: num.NewUint(40000), StakeByDelegators: num.NewUint(10000)},
  1309  		{NodeID: "node4", SelfStake: num.NewUint(20000), StakeByDelegators: num.NewUint(10000)},
  1310  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1311  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1312  		{NodeID: "node6", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1313  	}
  1314  	topology.rng = rand.New(rand.NewSource(1000))
  1315  	// reduce the number of ersatz validators from 2 to 1
  1316  	topology.currentBlockHeight = 1000
  1317  	topology.numberOfErsatzValidators = 1
  1318  	topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)})
  1319  
  1320  	ezCount := 0
  1321  	for _, v := range topology.validators {
  1322  		if ValidatorStatusToName[v.status] == "ersatz" {
  1323  			ezCount++
  1324  		}
  1325  	}
  1326  
  1327  	require.Equal(t, ezCount, topology.numberOfErsatzValidators)
  1328  }
  1329  
  1330  func testSwapAndTendermintSlotIncrease(t *testing.T) {
  1331  	topology := NewTopology(logging.NewLoggerFromConfig(logging.Config{}), NewDefaultConfig(), nil, nil, true, nil, &DummyMultiSigTopology{}, &DummyMultiSigTopology{}, &dummyTestTime{})
  1332  	topology.numberOfTendermintValidators = 4
  1333  	topology.numberOfErsatzValidators = 2
  1334  	topology.validators["node1"] = &valState{
  1335  		data: ValidatorData{
  1336  			ID:       "node1",
  1337  			TmPubKey: "67g7+123M0kfMR35U7LLq09eEU1dVr6jHBEgEtPzkrs=",
  1338  		},
  1339  		blockAdded:        1,
  1340  		statusChangeBlock: 900,
  1341  		status:            ValidatorStatusTendermint,
  1342  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1343  	}
  1344  	topology.validators["node2"] = &valState{
  1345  		data: ValidatorData{
  1346  			ID:       "node2",
  1347  			TmPubKey: "2w5hxsVqWFTV6/f0swyNVqOhY1vWI42MrfO0xkUqsiA=",
  1348  		},
  1349  		blockAdded:        1,
  1350  		statusChangeBlock: 901,
  1351  		status:            ValidatorStatusTendermint,
  1352  		heartbeatTracker:  &validatorHeartbeatTracker{},
  1353  	}
  1354  	topology.validators["node3"] = &valState{
  1355  		data: ValidatorData{
  1356  			ID:       "node3",
  1357  			TmPubKey: "QZNLWGlqoWv4J9lXqe0pkZQnCJuJbJfiJ50VOj/WsAs=",
  1358  		},
  1359  		blockAdded:       2,
  1360  		status:           ValidatorStatusTendermint,
  1361  		heartbeatTracker: &validatorHeartbeatTracker{},
  1362  	}
  1363  	topology.validators["node4"] = &valState{
  1364  		data: ValidatorData{
  1365  			ID:       "node4",
  1366  			TmPubKey: "Lor28j7E369gLsU6Q9dW64yKPMn9XiD/IcS1XDXbPSQ=",
  1367  		},
  1368  		blockAdded:       3,
  1369  		status:           ValidatorStatusTendermint,
  1370  		heartbeatTracker: &validatorHeartbeatTracker{},
  1371  	}
  1372  	topology.validators["node5"] = &valState{
  1373  		data: ValidatorData{
  1374  			ID:       "node5",
  1375  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
  1376  		},
  1377  		blockAdded:       4,
  1378  		status:           ValidatorStatusPending,
  1379  		heartbeatTracker: &validatorHeartbeatTracker{},
  1380  	}
  1381  	topology.validators["node6"] = &valState{
  1382  		data: ValidatorData{
  1383  			ID:       "node6",
  1384  			TmPubKey: "pobW1cLYgsbQGGwbwiwVMqp15WuRzaVp3mn7z+g3ByM=",
  1385  		},
  1386  		blockAdded:       4,
  1387  		status:           ValidatorStatusPending,
  1388  		heartbeatTracker: &validatorHeartbeatTracker{},
  1389  	}
  1390  
  1391  	perfScore := map[string]num.Decimal{
  1392  		"node1": decimalOne,
  1393  		"node2": decimalOne,
  1394  		"node3": decimalOne,
  1395  		"node4": decimalOne,
  1396  		"node5": decimalOne,
  1397  		"node6": decimalOne,
  1398  	}
  1399  
  1400  	ranking := map[string]num.Decimal{
  1401  		"node1": num.DecimalFromFloat(0.0), // this TM validator has zero score so will get demoted
  1402  		"node2": num.DecimalFromFloat(0.8), // everyone else should be end up being TM due to the slot space, and a promotion
  1403  		"node3": num.DecimalFromFloat(0.8),
  1404  		"node4": num.DecimalFromFloat(0.8),
  1405  		"node5": num.DecimalFromFloat(0.8),
  1406  		"node6": num.DecimalFromFloat(0.8),
  1407  	}
  1408  
  1409  	delegations := []*types.ValidatorData{
  1410  		{NodeID: "node1", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1411  		{NodeID: "node2", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1412  		{NodeID: "node3", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1413  		{NodeID: "node4", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1414  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1415  		{NodeID: "node5", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1416  		{NodeID: "node6", SelfStake: num.NewUint(30000), StakeByDelegators: num.NewUint(10000)},
  1417  	}
  1418  	topology.rng = rand.New(rand.NewSource(1000))
  1419  	// increase the number of tendermint validators to 5
  1420  	topology.currentBlockHeight = 1000
  1421  	topology.numberOfErsatzValidators = 1
  1422  	topology.numberOfTendermintValidators = 5
  1423  	topology.applyPromotion(perfScore, ranking, delegations, types.StakeScoreParams{MinVal: num.DecimalFromFloat(2), CompLevel: num.DecimalFromFloat(1), OptimalStakeMultiplier: num.DecimalFromFloat(5)})
  1424  
  1425  	// node5 should get promoted due to and increase in slots while node6 gets promoted and swapped with node1
  1426  	require.Equal(t, "ersatz", ValidatorStatusToName[topology.validators["node1"].status])
  1427  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node2"].status])
  1428  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node3"].status])
  1429  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node4"].status])
  1430  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node5"].status])
  1431  	require.Equal(t, "tendermint", ValidatorStatusToName[topology.validators["node6"].status])
  1432  }
  1433  
  1434  type DummyMultiSigTopology struct{}
  1435  
  1436  func (*DummyMultiSigTopology) ChainID() string {
  1437  	return "12"
  1438  }
  1439  
  1440  func (*DummyMultiSigTopology) IsSigner(address string) bool {
  1441  	return true
  1442  }
  1443  
  1444  func (*DummyMultiSigTopology) ExcessSigners(addresses []string) bool {
  1445  	return false
  1446  }
  1447  
  1448  func (*DummyMultiSigTopology) GetThreshold() uint32 {
  1449  	return 666
  1450  }
  1451  
  1452  func (*DummyMultiSigTopology) GetSigners() []string {
  1453  	return []string{}
  1454  }