github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/validator/weighted_random_test.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package validator
    18  
    19  import (
    20  	"math/big"
    21  	"math/rand"
    22  	"reflect"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/klaytn/klaytn/common"
    30  	"github.com/klaytn/klaytn/common/hexutil"
    31  	"github.com/klaytn/klaytn/consensus/istanbul"
    32  	"github.com/klaytn/klaytn/crypto"
    33  	"github.com/klaytn/klaytn/fork"
    34  	"github.com/klaytn/klaytn/params"
    35  	"github.com/stretchr/testify/assert"
    36  )
    37  
    38  var (
    39  	testAddrs = []common.Address{
    40  		common.HexToAddress("0x0adBC7b05Da383157200a9Fa192285898aB2CaAc"),
    41  		common.HexToAddress("0x371F315BeBe961776AC84B29e044b01074b93E69"),
    42  		common.HexToAddress("0x5845EAa7ac251542Dc96fBaD09E3CAd3ec105a7a"),
    43  		common.HexToAddress("0x63805D23fC86Aa16EFB157C036F226f3aa93099d"),
    44  		common.HexToAddress("0x68E0DEf1e6beb308eF5FdF2e19dB2884571c465c"),
    45  		common.HexToAddress("0x72E23aAe2Cc6eE54682bD67B6093F7b7971f3D2F"),
    46  		common.HexToAddress("0x78B898e37A45069518775972AB8155493e69A2F0"),
    47  		common.HexToAddress("0x8704Ffb473a16638ea42c7704995d6505102a4Ca"),
    48  		common.HexToAddress("0x93d3Ce8940c7907b0C1c3898dF7Aa797C457cD0f"),
    49  		common.HexToAddress("0x9a049EefC01aAE911F2B6F19d724dF9d3ca5cAe6"),
    50  		common.HexToAddress("0xC14124d61fc940c7aF29F62438D1B54fD7FFB65B"),
    51  		common.HexToAddress("0xc4cB0B3c2682C15D96739f9a13fE26f17c893f8f"),
    52  		common.HexToAddress("0xd4aB68fcEC8Fa23856188163B131F3E443e09EF8"),
    53  	}
    54  	testRewardAddrs = []common.Address{
    55  		common.HexToAddress("0x0a6e50a28f10CD9dba36DD9D3B95BaA32F9fe77a"),
    56  		common.HexToAddress("0x23FB6C77E069BD6456181f48a9c77f3a3812E7e7"),
    57  		common.HexToAddress("0x43d5e084D8A6c7FbCd0EbA9a517533fF384f0577"),
    58  		common.HexToAddress("0x4d180C12FB3B061f44E91D30d574F78D1DeCAD90"),
    59  		common.HexToAddress("0x53094cE69ea701bfb9D06239087d4CF09F127B78"),
    60  		common.HexToAddress("0x5F2152bf0C97f1d2c3Ffec8A98FEEB1e50798090"),
    61  		common.HexToAddress("0x653f42fb1F3474de222F7DDa2109250218989B19"),
    62  		common.HexToAddress("0x93eaEAa38D534B52E7DB3AB939330022330cD427"),
    63  		common.HexToAddress("0x96a0d7f6A82B860313FF8668b858aD4930d7B2d6"),
    64  		common.HexToAddress("0xB89ff800C21b3334f0e355A73242bB4363cf6e10"),
    65  		common.HexToAddress("0xDEeeF6fAC16f095Fa944E481F8e6c3b42ae3Cefa"),
    66  		common.HexToAddress("0xbDE3Ee8c01484dDBD59a425457Ab138cf3aa0E11"),
    67  		common.HexToAddress("0xdD5572A7aC7AB7407e8e4082dB442668C02924E3"),
    68  	}
    69  	testStakingAddrs = []common.Address{
    70  		common.HexToAddress("0x3776A66698babFA24F0316e4363B2E6C95B09ceF"),
    71  		common.HexToAddress("0x4d086A88329233E00158FEcbe7b38Dd8667Dd9f9"),
    72  		common.HexToAddress("0x5d7d13278AEF56263B7d25d51E1B2519Ac0D656B"),
    73  		common.HexToAddress("0x60fA2326f6C1A7a90Bd1B3c31Bd1A7f9Aed61443"),
    74  		common.HexToAddress("0x681C55B2CD831D262C785e213a70e277D0226c79"),
    75  		common.HexToAddress("0x6EeA09FF2bB16F1cD075c748E1684f1100085541"),
    76  		common.HexToAddress("0x817617C3f09d08a5d475bf72b4723A755CD9b8c7"),
    77  		common.HexToAddress("0x83F28D3512dC32701F375b112d0CB0810Cb736e4"),
    78  		common.HexToAddress("0x92D03E4998fB3F91A1E24496EDCf625136037f9e"),
    79  		common.HexToAddress("0xA0360cDC935A9f3bFe7Ad03D1C34989427ad239f"),
    80  		common.HexToAddress("0xE2946677DcEEDDF36F1f6EA00421635804872D49"),
    81  		common.HexToAddress("0xF246283a57A8018085AF39bdadFCC4aaC682e6dD"),
    82  		common.HexToAddress("0xF3c6f39e231C7363F9B5F4d71b5EE7Eb1fB265d7"),
    83  	}
    84  	testVotingPowers = []uint64{
    85  		1, 1, 1, 1, 1,
    86  		1, 1, 1, 1, 1,
    87  		1, 1, 1,
    88  	}
    89  	testZeroWeights = []uint64{
    90  		0, 0, 0, 0, 0,
    91  		0, 0, 0, 0, 0,
    92  		0, 0, 0,
    93  	}
    94  	testPrevHash = common.HexToHash("0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a")
    95  	testMixHash  = hexutil.MustDecode("0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a")
    96  
    97  	testExpectedProposers = []common.Address{
    98  		common.HexToAddress("0x8704Ffb473a16638ea42c7704995d6505102a4Ca"),
    99  		common.HexToAddress("0xC14124d61fc940c7aF29F62438D1B54fD7FFB65B"),
   100  		common.HexToAddress("0x72E23aAe2Cc6eE54682bD67B6093F7b7971f3D2F"),
   101  		common.HexToAddress("0xc4cB0B3c2682C15D96739f9a13fE26f17c893f8f"),
   102  		common.HexToAddress("0x68E0DEf1e6beb308eF5FdF2e19dB2884571c465c"),
   103  		common.HexToAddress("0x371F315BeBe961776AC84B29e044b01074b93E69"),
   104  		common.HexToAddress("0x63805D23fC86Aa16EFB157C036F226f3aa93099d"),
   105  		common.HexToAddress("0x93d3Ce8940c7907b0C1c3898dF7Aa797C457cD0f"),
   106  		common.HexToAddress("0x5845EAa7ac251542Dc96fBaD09E3CAd3ec105a7a"),
   107  		common.HexToAddress("0x78B898e37A45069518775972AB8155493e69A2F0"),
   108  		common.HexToAddress("0x0adBC7b05Da383157200a9Fa192285898aB2CaAc"),
   109  		common.HexToAddress("0xd4aB68fcEC8Fa23856188163B131F3E443e09EF8"),
   110  		common.HexToAddress("0x9a049EefC01aAE911F2B6F19d724dF9d3ca5cAe6"),
   111  	}
   112  
   113  	testNonZeroWeights = []uint64{
   114  		1, 1, 2, 1, 1,
   115  		1, 0, 3, 2, 1,
   116  		0, 1, 5,
   117  	}
   118  )
   119  
   120  func makeTestValidators(weights []uint64) (validators istanbul.Validators) {
   121  	validators = make([]istanbul.Validator, len(testAddrs))
   122  	for i := range testAddrs {
   123  		validators[i] = newWeightedValidator(testAddrs[i], testRewardAddrs[i], testVotingPowers[i], weights[i])
   124  	}
   125  	sort.Sort(validators)
   126  
   127  	return
   128  }
   129  
   130  func makeTestWeightedCouncil(weights []uint64) (valSet *weightedCouncil) {
   131  	// prepare weighted council
   132  	valSet = NewWeightedCouncil(testAddrs, nil, testRewardAddrs, testVotingPowers, weights, istanbul.WeightedRandom, 21, 0, 0, nil)
   133  	return
   134  }
   135  
   136  func TestWeightedCouncil_List(t *testing.T) {
   137  	validators := makeTestValidators(testZeroWeights)
   138  
   139  	valSet := makeTestWeightedCouncil(testZeroWeights)
   140  
   141  	validators_in_valset := valSet.List()
   142  
   143  	if len(validators_in_valset) != len(validators) {
   144  		t.Errorf("len of validators in valSet is different from len of given test set %v, validators %v", len(validators_in_valset), len(validators))
   145  	}
   146  
   147  	for i := 0; i < len(validators); i++ {
   148  		if validators[i].String() != validators_in_valset[i].String() {
   149  			t.Errorf("The element in validators in valset is different from given test set%v, validators %v", validators_in_valset[i], validators[i])
   150  		}
   151  	}
   152  }
   153  
   154  func TestWeightedCouncil_GetByIndex(t *testing.T) {
   155  	validators := makeTestValidators(testZeroWeights)
   156  	valSet := makeTestWeightedCouncil(testZeroWeights)
   157  
   158  	for i := 0; i < len(validators); i++ {
   159  		validatorToCheck := valSet.GetByIndex(uint64(i))
   160  
   161  		if validators[i].Address() != validatorToCheck.Address() {
   162  			t.Errorf("The validator with given index is different. index=%v, expected validator=%v, gotten validator %v", i, validators[i], valSet.GetByIndex(uint64(i)))
   163  		}
   164  	}
   165  
   166  	for errorIndex := len(validators) + 1; errorIndex < 100; errorIndex++ {
   167  		validatorToCheck := valSet.GetByIndex(uint64(errorIndex))
   168  
   169  		if validatorToCheck != nil {
   170  			t.Errorf("The result should be nil with given index. index=%v", errorIndex)
   171  		}
   172  	}
   173  
   174  	for errorIndex := -1; errorIndex > -100; errorIndex-- {
   175  		validatorToCheck := valSet.GetByIndex(uint64(errorIndex))
   176  
   177  		if validatorToCheck != nil {
   178  			t.Errorf("The result should be nil with given index. index=%v", errorIndex)
   179  		}
   180  	}
   181  }
   182  
   183  func TestWeightedCouncil_GetByAddress(t *testing.T) {
   184  	validators := makeTestValidators(testZeroWeights)
   185  	valSet := makeTestWeightedCouncil(testZeroWeights)
   186  
   187  	for i := 0; i < len(validators); i++ {
   188  		index, validatorToCheck := valSet.getByAddress(validators[i].Address())
   189  
   190  		if validators[index].Address() != validatorToCheck.Address() {
   191  			t.Errorf("The validator with given address is different index=%v, expected validator=%v, gotten validator %v", i, validators[i], valSet.GetByIndex(uint64(i)))
   192  		}
   193  	}
   194  
   195  	_, errorValidator := valSet.getByAddress(common.Address{})
   196  	if errorValidator != nil {
   197  		t.Errorf("The validator with given address should be nil.")
   198  	}
   199  }
   200  
   201  func TestWeightedCouncil_GetProposer(t *testing.T) {
   202  	validators := makeTestValidators(testZeroWeights)
   203  	valSet := makeTestWeightedCouncil(testZeroWeights)
   204  
   205  	// at the first, proposer is the first validator in the validator list
   206  	expectedProposer := validators[0]
   207  	proposerToCheck := valSet.GetProposer()
   208  
   209  	if expectedProposer.Address() != proposerToCheck.Address() {
   210  		t.Errorf("proposer should be same. Expected proposer: %v, gotten proposer %v", expectedProposer, proposerToCheck)
   211  	}
   212  
   213  	// random check. give random validator to valSet and check GetProposer() if it is same as the given validator
   214  	r := rand.New(rand.NewSource(time.Now().Unix()))
   215  	for i := 0; i < 100; i++ {
   216  		choosenIndex := r.Intn(len(validators))
   217  
   218  		valSet.proposer.Store(validators[choosenIndex])
   219  
   220  		expectedProposer := validators[choosenIndex]
   221  		proposerToCheck := valSet.GetProposer()
   222  
   223  		if expectedProposer.Address() != proposerToCheck.Address() {
   224  			t.Errorf("proposer should be same. Expected proposer: %v, gotten proposer %v", expectedProposer, proposerToCheck)
   225  		}
   226  	}
   227  }
   228  
   229  func TestDefaultSet_IsProposer(t *testing.T) {
   230  	validators := makeTestValidators(testZeroWeights)
   231  	valSet := makeTestWeightedCouncil(testZeroWeights)
   232  
   233  	currentProposer := valSet.GetProposer()
   234  
   235  	for i := 0; i < len(validators); i++ {
   236  		validatorToTest := validators[i]
   237  
   238  		expectedResult := validatorToTest.Address() == currentProposer.Address()
   239  		result := valSet.IsProposer(validatorToTest.Address())
   240  
   241  		if result != expectedResult {
   242  			t.Errorf("The result is different from the expected result. Expected Result : %v, Gotten Result : %v, CurrentProposer Address : %v, TestValidator Address : %v", expectedResult, result, currentProposer.Address(), validatorToTest.Address())
   243  		}
   244  	}
   245  }
   246  
   247  func TestWeightedCouncil_RefreshWithZeroWeight(t *testing.T) {
   248  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{
   249  		IstanbulCompatibleBlock: big.NewInt(5),
   250  	})
   251  	defer fork.ClearHardForkBlockNumberConfig()
   252  
   253  	validators := makeTestValidators(testZeroWeights)
   254  
   255  	valSet := makeTestWeightedCouncil(testZeroWeights)
   256  	runRefreshForTest(valSet)
   257  
   258  	// Run tests
   259  
   260  	// 1. check all validators are chosen for proposers
   261  	var sortedProposers istanbul.Validators
   262  	sortedProposers = make([]istanbul.Validator, len(testAddrs))
   263  	copy(sortedProposers, valSet.proposers)
   264  	sort.Sort(sortedProposers)
   265  	if !reflect.DeepEqual(sortedProposers, validators) {
   266  		t.Errorf("All validators are not in proposers: sorted proposers %v, validators %v", sortedProposers, validators)
   267  	}
   268  
   269  	// 2. check proposers
   270  	for i, val := range valSet.proposers {
   271  		if !reflect.DeepEqual(val.Address(), testExpectedProposers[i]) {
   272  			t.Errorf("proposer mismatch: have %v, want %v", val.Address().String(), testExpectedProposers[i].String())
   273  		}
   274  	}
   275  
   276  	// 3. test calculate proposer different round
   277  	checkCalcProposerWithRound(t, valSet, testAddrs[0], 0)
   278  	checkCalcProposerWithRound(t, valSet, testAddrs[0], 1)
   279  	checkCalcProposerWithRound(t, valSet, testAddrs[0], 5)
   280  	checkCalcProposerWithRound(t, valSet, testAddrs[0], 13)
   281  	checkCalcProposerWithRound(t, valSet, testAddrs[0], 1000)
   282  
   283  	// 4. test calculate proposer different block number
   284  	for i := 0; i < 100; i++ {
   285  		valSet.blockNum = uint64(i)
   286  		checkCalcProposerWithBlockNumber(t, valSet, testAddrs[0], 0)
   287  	}
   288  
   289  	// 5. test calculate proposer different block number and round
   290  	for i := 0; i < 100; i++ {
   291  		valSet.blockNum = uint64(i)
   292  		for j := 0; j < 100; j++ {
   293  			round := uint64(j)
   294  			checkCalcProposerWithBlockNumberAndRound(t, valSet, testAddrs[0], round)
   295  		}
   296  	}
   297  }
   298  
   299  func checkCalcProposerWithRound(t *testing.T, valSet *weightedCouncil, lastProposer common.Address, round uint64) {
   300  	valSet.CalcProposer(lastProposer, round)
   301  	_, expectedVal := valSet.GetByAddress(testExpectedProposers[round%uint64(len(valSet.proposers))])
   302  	if val := valSet.GetProposer(); !reflect.DeepEqual(val, expectedVal) {
   303  		t.Errorf("proposer mismatch: have %v, want %v", val.String(), expectedVal.Address().String())
   304  	}
   305  }
   306  
   307  func checkCalcProposerWithBlockNumber(t *testing.T, valSet *weightedCouncil, lastProposer common.Address, round uint64) {
   308  	valSet.CalcProposer(lastProposer, round)
   309  	_, expectedVal := valSet.GetByAddress(testExpectedProposers[valSet.blockNum%uint64(len(valSet.proposers))])
   310  	if val := valSet.GetProposer(); !reflect.DeepEqual(val, expectedVal) {
   311  		t.Errorf("proposer mismatch: have %v, want %v", val.String(), expectedVal.Address().String())
   312  	}
   313  }
   314  
   315  func checkCalcProposerWithBlockNumberAndRound(t *testing.T, valSet *weightedCouncil, lastProposer common.Address, round uint64) {
   316  	valSet.CalcProposer(lastProposer, round)
   317  	_, expectedVal := valSet.GetByAddress(testExpectedProposers[(valSet.blockNum+round)%uint64(len(valSet.proposers))])
   318  	if val := valSet.GetProposer(); !reflect.DeepEqual(val, expectedVal) {
   319  		t.Errorf("proposer mismatch: have %v, want %v", val.String(), expectedVal.Address().String())
   320  	}
   321  }
   322  
   323  func TestWeightedCouncil_RefreshWithNonZeroWeight(t *testing.T) {
   324  	validators := makeTestValidators(testNonZeroWeights)
   325  
   326  	valSet := makeTestWeightedCouncil(testNonZeroWeights)
   327  	runRefreshForTest(valSet)
   328  
   329  	// Run tests
   330  
   331  	// 1. number of proposers
   332  	totalWeights := uint64(0)
   333  	for _, v := range validators {
   334  		totalWeights += v.Weight()
   335  	}
   336  	assert.Equal(t, totalWeights, uint64(len(valSet.proposers)))
   337  
   338  	// 2. weight and appearance frequency
   339  	for _, v := range validators {
   340  		weight := v.Weight()
   341  		appearance := uint64(0)
   342  		for _, p := range valSet.proposers {
   343  			if v.Address() == p.Address() {
   344  				appearance++
   345  			}
   346  		}
   347  		assert.Equal(t, weight, appearance)
   348  	}
   349  }
   350  
   351  func TestWeightedCouncil_RemoveValidator(t *testing.T) {
   352  	validators := makeTestValidators(testNonZeroWeights)
   353  	valSet := makeTestWeightedCouncil(testNonZeroWeights)
   354  	runRefreshForTest(valSet)
   355  
   356  	for _, val := range validators {
   357  
   358  		_, removedVal := valSet.GetByAddress(val.Address())
   359  		if removedVal == nil {
   360  			t.Errorf("Fail to find validator with address %v", removedVal.Address().String())
   361  		}
   362  
   363  		if !valSet.RemoveValidator(removedVal.Address()) {
   364  			t.Errorf("Fail to remove validator %v", removedVal.String())
   365  		}
   366  
   367  		// check whether removedVal is really removed from validators
   368  		for _, v := range valSet.validators {
   369  			if removedVal.Address() == v.Address() {
   370  				t.Errorf("Validator(%v) does not removed from validators", removedVal.Address().String())
   371  			}
   372  		}
   373  
   374  		// check whether removedVal is also removed from proposers immediately
   375  		for _, p := range valSet.proposers {
   376  			if removedVal.Address() == p.Address() {
   377  				t.Errorf("Validator(%v) does not removed from proposers", removedVal.Address().String())
   378  			}
   379  		}
   380  	}
   381  
   382  	assert.Equal(t, uint64(0), valSet.Size())
   383  	assert.Equal(t, 0, len(valSet.Proposers()))
   384  }
   385  
   386  func TestWeightedCouncil_RefreshAfterRemoveValidator(t *testing.T) {
   387  	validators := makeTestValidators(testNonZeroWeights)
   388  	valSet := makeTestWeightedCouncil(testNonZeroWeights)
   389  	runRefreshForTest(valSet)
   390  
   391  	for _, val := range validators {
   392  
   393  		_, removedVal := valSet.GetByAddress(val.Address())
   394  		if removedVal == nil {
   395  			t.Errorf("Fail to find validator with address %v", removedVal.Address().String())
   396  		}
   397  
   398  		if !valSet.RemoveValidator(removedVal.Address()) {
   399  			t.Errorf("Fail to remove validator %v", removedVal.String())
   400  		}
   401  
   402  		// check whether removedVal is really removed from validators
   403  		for _, v := range valSet.validators {
   404  			if removedVal.Address() == v.Address() {
   405  				t.Errorf("Validator(%v) does not removed from validators", removedVal.Address().String())
   406  			}
   407  		}
   408  
   409  		runRefreshForTest(valSet)
   410  
   411  		// check whether removedVal is excluded as expected when refreshing proposers
   412  		for _, p := range valSet.proposers {
   413  			if removedVal.Address() == p.Address() {
   414  				t.Errorf("Validator(%v) does not removed from proposers", removedVal.Address().String())
   415  			}
   416  		}
   417  	}
   418  
   419  	assert.Equal(t, uint64(0), valSet.Size())
   420  	assert.Equal(t, 0, len(valSet.Proposers()))
   421  }
   422  
   423  func runRefreshForTest(valSet *weightedCouncil) {
   424  	hashString := strings.TrimPrefix(testPrevHash.Hex(), "0x")
   425  	if len(hashString) > 15 {
   426  		hashString = hashString[:15]
   427  	}
   428  	seed, _ := strconv.ParseInt(hashString, 16, 64)
   429  	valSet.refreshProposers(seed, 0)
   430  }
   431  
   432  func TestWeightedCouncil_SetSubGroupSize(t *testing.T) {
   433  	validators := makeTestValidators(testNonZeroWeights)
   434  	valSet := makeTestWeightedCouncil(testNonZeroWeights)
   435  
   436  	validatorsLen := len(validators)
   437  
   438  	for i := 1; i < validatorsLen; i++ {
   439  		valSet.SetSubGroupSize(uint64(i))
   440  
   441  		expectedSubGroupSize := uint64(i)
   442  		gottenSubGroupSize := valSet.SubGroupSize()
   443  
   444  		if expectedSubGroupSize != gottenSubGroupSize {
   445  			t.Errorf("SubGroupSize should be %v but gotten SubGroupSize is %v", expectedSubGroupSize, gottenSubGroupSize)
   446  		}
   447  	}
   448  }
   449  
   450  func TestWeightedCouncil_SubListWithProposer(t *testing.T) {
   451  	var (
   452  		validators = makeTestValidators(testNonZeroWeights)
   453  		prevHash   = crypto.Keccak256Hash([]byte("This is a test"))
   454  		valSet     = makeTestWeightedCouncil(testNonZeroWeights)
   455  
   456  		BlockBeforeHF = big.NewInt(4)
   457  		HFBlock       = big.NewInt(5)
   458  		BlockAfterHF  = big.NewInt(6)
   459  
   460  		expectIndexOfSubsetLenTest                     = []int{1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10}
   461  		expectIndexOfRoundTestBeforeIstanbulCompatible = [][]int{
   462  			{1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10},
   463  			{2, 3, 7, 1, 11, 6, 9, 4, 0, 8, 12, 10},
   464  			{3, 4, 7, 1, 11, 6, 9, 2, 0, 8, 12, 10},
   465  			{4, 5, 7, 1, 11, 6, 9, 2, 0, 8, 12, 10},
   466  			{5, 6, 7, 1, 11, 4, 9, 2, 0, 8, 12, 10},
   467  			{6, 7, 5, 1, 11, 4, 9, 2, 0, 8, 12, 10},
   468  			{7, 8, 5, 1, 11, 4, 9, 2, 0, 6, 12, 10},
   469  			{8, 9, 5, 1, 11, 4, 7, 2, 0, 6, 12, 10},
   470  			{9, 10, 5, 1, 11, 4, 7, 2, 0, 6, 12, 8},
   471  			{10, 11, 5, 1, 9, 4, 7, 2, 0, 6, 12, 8},
   472  			{11, 12, 5, 1, 9, 4, 7, 2, 0, 6, 10, 8},
   473  			{12, 0, 6, 2, 10, 5, 8, 3, 1, 7, 11, 9},
   474  			{0, 1, 7, 3, 11, 6, 9, 4, 2, 8, 12, 10},
   475  			{1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10},
   476  			{2, 3, 7, 1, 11, 6, 9, 4, 0, 8, 12, 10},
   477  		}
   478  		expectIndexOfRoundTestAfterIstanbulCompatible = [][]int{
   479  			{1, 2, 7, 3, 11, 6, 9, 4, 0, 8, 12, 10},
   480  			{2, 3, 6, 8, 10, 9, 1, 11, 5, 0, 4, 7},
   481  			{3, 4, 10, 5, 8, 0, 7, 9, 12, 6, 1, 11},
   482  			{4, 5, 6, 0, 7, 1, 3, 12, 2, 8, 9, 11},
   483  			{5, 6, 4, 9, 12, 7, 0, 3, 8, 2, 1, 11},
   484  			{6, 7, 2, 3, 8, 9, 11, 12, 5, 1, 4, 0},
   485  			{7, 8, 3, 11, 5, 10, 0, 1, 2, 6, 9, 12},
   486  			{8, 9, 5, 7, 11, 3, 1, 0, 10, 6, 4, 12},
   487  			{9, 10, 3, 7, 5, 6, 2, 0, 12, 8, 11, 1},
   488  			{10, 11, 7, 1, 0, 9, 8, 6, 12, 5, 2, 4},
   489  			{11, 12, 4, 8, 1, 6, 0, 3, 9, 10, 2, 7},
   490  			{12, 0, 7, 2, 4, 1, 6, 10, 9, 11, 8, 3},
   491  			{0, 1, 9, 8, 2, 3, 10, 5, 7, 12, 4, 6},
   492  			{1, 2, 11, 10, 6, 8, 7, 4, 9, 12, 0, 5},
   493  			{2, 3, 4, 8, 6, 5, 11, 1, 12, 0, 9, 10},
   494  		}
   495  	)
   496  
   497  	getExpectSubList := func(indices []int) []istanbul.Validator {
   498  		var expectSubList []istanbul.Validator
   499  		for _, index := range indices {
   500  			expectSubList = append(expectSubList, validators[index])
   501  		}
   502  		return expectSubList
   503  	}
   504  
   505  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{IstanbulCompatibleBlock: HFBlock})
   506  	defer fork.ClearHardForkBlockNumberConfig()
   507  
   508  	// SubsetLen test: various subset length test
   509  	valSet.SetBlockNum(1)
   510  	for testSubsetLen := 2; testSubsetLen < len(validators); testSubsetLen++ {
   511  		// set committee size and calculate proposer
   512  		valSet.SetSubGroupSize(uint64(testSubsetLen))
   513  		valSet.CalcProposer(valSet.GetProposer().Address(), uint64(0))
   514  
   515  		// get committee list
   516  		expectSubList := getExpectSubList(expectIndexOfSubsetLenTest[0:testSubsetLen])
   517  
   518  		// compare the subList of valSet with expected committee list
   519  		viewBeforeHF := &istanbul.View{Sequence: BlockBeforeHF, Round: big.NewInt(int64(0))}
   520  		viewAfterHF := &istanbul.View{Sequence: BlockAfterHF, Round: big.NewInt(int64(0))}
   521  		assert.Equal(t, expectSubList, valSet.SubList(prevHash, viewBeforeHF), "test Subset length: %d(before istanbulCompatible)", testSubsetLen)
   522  		assert.Equal(t, expectSubList, valSet.SubList(prevHash, viewAfterHF), "test subset length: %d(after istanbulCompatible)", testSubsetLen)
   523  	}
   524  
   525  	// Check: compare the size of the test data arrays
   526  	assert.Equal(t, len(expectIndexOfRoundTestBeforeIstanbulCompatible), len(expectIndexOfRoundTestAfterIstanbulCompatible))
   527  
   528  	// Round test: various round test
   529  	valSet.SetBlockNum(1)
   530  	valSet.SetSubGroupSize(uint64(len(validators) - 1))
   531  	for round := 0; round < len(expectIndexOfRoundTestBeforeIstanbulCompatible); round++ {
   532  		// calculate proposer and set view with test round value
   533  		valSet.CalcProposer(valSet.GetProposer().Address(), uint64(round))
   534  
   535  		// get committee list
   536  		expectSubListBeforeHF := getExpectSubList(expectIndexOfRoundTestBeforeIstanbulCompatible[round])
   537  		expectSubListAfterHF := getExpectSubList(expectIndexOfRoundTestAfterIstanbulCompatible[round])
   538  
   539  		// compare the subList of valSet with expected committee list
   540  		viewBeforeHF := &istanbul.View{Sequence: BlockBeforeHF, Round: big.NewInt(int64(round))}
   541  		viewAfterHF := &istanbul.View{Sequence: BlockAfterHF, Round: big.NewInt(int64(round))}
   542  		assert.Equal(t, expectSubListBeforeHF, valSet.SubList(prevHash, viewBeforeHF), "test round: %d(before istanbulCompatible)", round)
   543  		assert.Equal(t, expectSubListAfterHF, valSet.SubList(prevHash, viewAfterHF), "test round: %d(after istanbulCompatible)", round)
   544  	}
   545  }
   546  
   547  func TestWeightedCouncil_Randao(t *testing.T) {
   548  	var (
   549  		forkNum    = uint64(5)
   550  		validators = makeTestWeightedCouncil(nil).List()
   551  		valSize    = uint64(len(validators))
   552  	)
   553  	fork.SetHardForkBlockNumberConfig(&params.ChainConfig{
   554  		RandaoCompatibleBlock: big.NewInt(int64(forkNum)),
   555  	})
   556  	defer fork.ClearHardForkBlockNumberConfig()
   557  	valSet := makeTestWeightedCouncil(nil)
   558  
   559  	testcases := []struct {
   560  		blockNum          uint64
   561  		round             uint64
   562  		committeeSize     uint64
   563  		mixHash           []byte
   564  		expectedProposer  istanbul.Validator
   565  		expectedCommittee []istanbul.Validator
   566  	}{
   567  		{ // Before fork
   568  			blockNum:          forkNum - 2,
   569  			round:             0,
   570  			committeeSize:     valSize + 1,
   571  			mixHash:           testMixHash,
   572  			expectedProposer:  validators[forkNum-2],
   573  			expectedCommittee: validators,
   574  		},
   575  		{ // After fork
   576  			blockNum:          forkNum - 1,
   577  			round:             0,
   578  			committeeSize:     valSize + 1,
   579  			mixHash:           testMixHash,
   580  			expectedProposer:  SelectRandaoCommittee(validators, valSize+1, testMixHash)[0],
   581  			expectedCommittee: validators,
   582  		},
   583  		{ // nil MixHash
   584  			blockNum:          forkNum + 10, // expect log "no mixHash  number=(forkNum+10)"
   585  			round:             0,
   586  			committeeSize:     valSize - 1,
   587  			mixHash:           nil,
   588  			expectedProposer:  validators[0], // fall back to roundRobinProposer
   589  			expectedCommittee: nil,           // SubList fails.
   590  		},
   591  		{ // IsSubset() == true
   592  			blockNum:          forkNum,
   593  			round:             0,
   594  			committeeSize:     valSize - 1,
   595  			mixHash:           testMixHash,
   596  			expectedProposer:  SelectRandaoCommittee(validators, valSize-1, testMixHash)[0],
   597  			expectedCommittee: SelectRandaoCommittee(validators, valSize-1, testMixHash),
   598  		},
   599  		{ // IsSubset() == false
   600  			blockNum:          forkNum,
   601  			round:             0,
   602  			committeeSize:     valSize + 1,
   603  			mixHash:           testMixHash,
   604  			expectedProposer:  SelectRandaoCommittee(validators, valSize+1, testMixHash)[0],
   605  			expectedCommittee: validators,
   606  		},
   607  		{ // Nonzero round
   608  			blockNum:          forkNum,
   609  			round:             2,
   610  			committeeSize:     valSize - 1,
   611  			mixHash:           testMixHash,
   612  			expectedProposer:  SelectRandaoCommittee(validators, valSize-1, testMixHash)[2],
   613  			expectedCommittee: SelectRandaoCommittee(validators, valSize-1, testMixHash),
   614  		},
   615  	}
   616  
   617  	for i, tc := range testcases {
   618  		valSet.SetBlockNum(tc.blockNum)
   619  		valSet.SetSubGroupSize(tc.committeeSize)
   620  		valSet.SetMixHash(tc.mixHash)
   621  
   622  		view := &istanbul.View{
   623  			Sequence: big.NewInt(int64(tc.blockNum)),
   624  			Round:    big.NewInt(int64(tc.round)),
   625  		}
   626  
   627  		// The lastProposer is ignored by weightedRandomProposer
   628  		// but it is used by roundRobinProposer. If lastProposer = 0x0,
   629  		// then roundRobinProposer returns validators[round].
   630  		valSet.CalcProposer(common.Address{}, uint64(tc.round))
   631  		proposer := valSet.GetProposer()
   632  		assert.Equal(t, tc.expectedProposer, proposer, "tc[%d]", i)
   633  
   634  		committee := valSet.SubList(testPrevHash, view)
   635  		assert.Equal(t, tc.expectedCommittee, committee, "tc[%d]", i)
   636  	}
   637  }
   638  
   639  func TestWeightedCouncil_Copy(t *testing.T) {
   640  	a := makeTestWeightedCouncil(testNonZeroWeights)
   641  	a.SetSubGroupSize(21)
   642  	a.SetBlockNum(1234)
   643  	a.SetMixHash(testMixHash)
   644  
   645  	b := a.Copy().(*weightedCouncil)
   646  
   647  	assert.Equal(t, a.subSize, b.subSize)
   648  	assert.Equal(t, a.demotedValidators, b.demotedValidators)
   649  	assert.Equal(t, a.validators, b.validators)
   650  	assert.Equal(t, a.policy, b.policy)
   651  
   652  	assert.Equal(t, a.proposer, b.proposer)
   653  	assert.Equal(t, a.proposers, b.proposers)
   654  	assert.Equal(t, a.proposersBlockNum, b.proposersBlockNum)
   655  	assert.Equal(t, a.stakingInfo, b.stakingInfo)
   656  	assert.Equal(t, a.blockNum, b.blockNum)
   657  	assert.Equal(t, a.mixHash, b.mixHash)
   658  
   659  	assert.Equal(t, a.GetProposer(), b.GetProposer())
   660  }