github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/validator/multi_staking_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 /* 18 Multiple Staking Contracts 19 20 Validators can deploy multiple staking contracts. 21 If a validator wants to deploy additional staking contracts, those staking contracts should have same rewardAddress. 22 StakingAmounts of staking contracts with a same rewardAddress will be added and it is reflected to a probability of becoming a block proposer. 23 24 Testing 25 26 StakingInfos are data from addressBook. 27 A StakingInfo has lists of addresses and stakingAmount. 28 They are matched by an index. Values of the lists with a same index are from a same staking contract. 29 30 All addresses used in tests are made by 3 digits number. 31 NodeAddress : begin with 1 32 rewardAddress : begin with 2 33 NodeAddress of additional staking contract : begin with 9 34 */ 35 package validator 36 37 import ( 38 "testing" 39 40 "github.com/klaytn/klaytn/common" 41 "github.com/klaytn/klaytn/consensus/istanbul" 42 "github.com/klaytn/klaytn/reward" 43 "github.com/stretchr/testify/assert" 44 ) 45 46 func newTestWeightedCouncil(nodeAddrs []common.Address) *weightedCouncil { 47 return NewWeightedCouncil(nodeAddrs, nil, nil, make([]uint64, len(nodeAddrs)), nil, istanbul.WeightedRandom, 0, 0, 0, nil) 48 } 49 50 // TestWeightedCouncil_getStakingAmountsOfValidators checks if validators and stakingAmounts from a stakingInfo are matched well. 51 // stakingAmounts of additional staking contracts will be added to stakingAmounts of validators which have the same reward address. 52 // input 53 // - validator and stakingInfo is matched by a nodeAddress. 54 // output 55 // - weightedValidators are sorted by nodeAddress 56 // - stakingAmounts should be same as expectedStakingAmounts 57 func TestWeightedCouncil_getStakingAmountsOfValidators(t *testing.T) { 58 testCases := []struct { 59 validators []common.Address 60 stakingInfo *reward.StakingInfo 61 expectedStakingAmounts []float64 62 }{ 63 { 64 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 65 &reward.StakingInfo{ 66 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 67 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, 68 CouncilStakingAmounts: []uint64{10000000, 5000000, 5000000}, 69 }, 70 []float64{10000000, 5000000, 5000000}, 71 }, 72 { 73 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 74 &reward.StakingInfo{ 75 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 76 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, 77 CouncilStakingAmounts: []uint64{7000000, 5000000, 10000000}, 78 }, 79 []float64{7000000, 5000000, 10000000}, 80 }, 81 { 82 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 83 &reward.StakingInfo{ 84 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, 85 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, 86 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, 87 }, 88 []float64{10000000, 5000000, 5000000, 5000000}, 89 }, 90 { 91 []common.Address{common.StringToAddress("104"), common.StringToAddress("103"), common.StringToAddress("102"), common.StringToAddress("101")}, 92 &reward.StakingInfo{ 93 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, 94 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, 95 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000, 5000000}, 96 }, 97 []float64{10000000, 10000000, 5000000, 5000000}, 98 }, 99 } 100 for _, testCase := range testCases { 101 council := newTestWeightedCouncil(testCase.validators) 102 candidates := append(council.validators, council.demotedValidators...) 103 weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, testCase.stakingInfo) 104 105 assert.NoError(t, err) 106 assert.Equal(t, len(testCase.validators), len(weightedValidators)) 107 for _, validator := range weightedValidators { 108 assert.Contains(t, testCase.validators, validator.address) 109 } 110 assert.Equal(t, testCase.expectedStakingAmounts, stakingAmounts) 111 } 112 } 113 114 // TestCalcTotalAmount tests calcTotalAmount that calculates totalAmount of stakingAmounts and gini coefficient if UseGini is true. 115 // if UseGini is true, gini is calculated and reflected to stakingAmounts. 116 func TestCalcTotalAmount(t *testing.T) { 117 testCases := []struct { 118 weightedValidators []*weightedValidator // Produced by getStakingAmountsOfValidators() 119 stakingInfo *reward.StakingInfo // Produced by GetStakingInfo() 120 stakingAmounts []float64 // Produced by getStakingAmountsOfValidators() 121 expectedGini float64 // Gini among the []weightedValidators which is a subset of CouncilNodeAddrs 122 expectedTotalAmount float64 // Sum of Gini-adjusted amounts 123 expectedStakingAmounts []float64 // Gini-adjusted amounts 124 }{ 125 { // Gini disabled 126 []*weightedValidator{ 127 {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, 128 }, 129 &reward.StakingInfo{ 130 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 131 UseGini: false, 132 Gini: reward.DefaultGiniCoefficient, // To see if calcTotalAmount modifies Gini field, set it to -1. 133 }, 134 []float64{5000000, 5000000, 5000000}, 135 reward.DefaultGiniCoefficient, 136 15000000, 137 []float64{5000000, 5000000, 5000000}, 138 }, 139 { // Gini enabled but equal amounts. 140 []*weightedValidator{ 141 {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, 142 }, 143 &reward.StakingInfo{ 144 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 145 UseGini: true, 146 Gini: reward.DefaultGiniCoefficient, 147 }, 148 []float64{5000000, 5000000, 5000000}, 149 0, 150 15000000, 151 []float64{5000000, 5000000, 5000000}, 152 }, 153 { // Gini enabled and unequal amounts. 154 []*weightedValidator{ 155 {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, {address: common.StringToAddress("104")}, {address: common.StringToAddress("105")}, 156 }, 157 &reward.StakingInfo{ 158 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 159 UseGini: true, 160 Gini: reward.DefaultGiniCoefficient, 161 }, 162 []float64{10000000, 20000000, 30000000, 40000000, 50000000}, 163 0.27, 164 3779508, 165 []float64{324946, 560845, 771786, 967997, 1153934}, 166 }, 167 } 168 for _, testCase := range testCases { 169 stakingAmounts := testCase.stakingAmounts 170 totalAmount, giniUsed := calcTotalAmount(testCase.weightedValidators, testCase.stakingInfo, stakingAmounts) 171 172 assert.Equal(t, testCase.expectedGini, giniUsed) // calcTotalAmount computes gini among validators on its own 173 assert.Equal(t, reward.DefaultGiniCoefficient, testCase.stakingInfo.Gini) // stakingInfo.Gini left untouched 174 assert.Equal(t, testCase.expectedTotalAmount, totalAmount) 175 assert.Equal(t, testCase.expectedStakingAmounts, stakingAmounts) 176 } 177 } 178 179 // TestCalcWeight tests calcWeight that calculates weights and saves them to validators. 180 // weights are the ratio of each stakingAmount to totalStaking 181 func TestCalcWeight(t *testing.T) { 182 testCases := []struct { 183 weightedValidators []*weightedValidator 184 stakingAmounts []float64 185 totalStaking float64 186 expectedWeights []uint64 187 }{ 188 { 189 []*weightedValidator{ 190 {}, {}, {}, 191 }, 192 []float64{0, 0, 0}, 193 0, 194 []uint64{0, 0, 0}, 195 }, 196 { 197 []*weightedValidator{ 198 {}, {}, {}, 199 }, 200 []float64{5000000, 5000000, 5000000}, 201 15000000, 202 []uint64{33, 33, 33}, 203 }, 204 { 205 []*weightedValidator{ 206 {}, {}, {}, {}, 207 }, 208 []float64{5000000, 10000000, 5000000, 5000000}, 209 25000000, 210 []uint64{20, 40, 20, 20}, 211 }, 212 { 213 []*weightedValidator{ 214 {}, {}, {}, {}, {}, 215 }, 216 []float64{324946, 560845, 771786, 967997, 1153934}, 217 3779508, 218 []uint64{9, 15, 20, 26, 31}, 219 }, 220 } 221 for _, testCase := range testCases { 222 calcWeight(testCase.weightedValidators, testCase.stakingAmounts, testCase.totalStaking) 223 for i, weight := range testCase.expectedWeights { 224 assert.Equal(t, weight, testCase.weightedValidators[i].Weight()) 225 } 226 } 227 } 228 229 // TestWeightedCouncil_validatorWeightWithStakingInfo is union of above tests. 230 // Weight should be calculated exactly by a validator list and a stakingInfo given 231 func TestWeightedCouncil_validatorWeightWithStakingInfo(t *testing.T) { 232 testCases := []struct { 233 validators []common.Address 234 stakingInfo *reward.StakingInfo 235 expectedWeights []uint64 236 }{ 237 { 238 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 239 &reward.StakingInfo{ 240 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 241 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, 242 UseGini: false, 243 CouncilStakingAmounts: []uint64{0, 0, 0}, 244 }, 245 []uint64{0, 0, 0}, 246 }, 247 { 248 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 249 &reward.StakingInfo{ 250 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 251 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204")}, 252 UseGini: true, 253 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000}, 254 }, 255 []uint64{25, 25, 25, 25}, 256 }, 257 { 258 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 259 &reward.StakingInfo{ 260 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 261 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("205")}, 262 UseGini: true, 263 CouncilStakingAmounts: []uint64{10000000, 20000000, 30000000, 40000000, 50000000}, 264 }, 265 []uint64{9, 15, 20, 26, 31}, 266 }, 267 { 268 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 269 &reward.StakingInfo{ 270 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, 271 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, 272 UseGini: false, 273 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, 274 }, 275 []uint64{40, 20, 20, 20}, 276 }, 277 { 278 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 279 &reward.StakingInfo{ 280 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, 281 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, 282 UseGini: true, 283 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, 284 }, 285 []uint64{38, 21, 21, 21}, 286 }, 287 { 288 []common.Address{common.StringToAddress("104"), common.StringToAddress("103"), common.StringToAddress("102"), common.StringToAddress("101")}, 289 &reward.StakingInfo{ 290 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, 291 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, 292 UseGini: true, 293 CouncilStakingAmounts: []uint64{10000000, 5000000, 20000000, 5000000, 5000000, 5000000}, 294 }, 295 []uint64{29, 21, 37, 12}, 296 }, 297 { 298 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 299 &reward.StakingInfo{ 300 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, 301 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, 302 UseGini: true, 303 CouncilStakingAmounts: []uint64{10000000, 5000000, 20000000, 5000000, 5000000, 5000000}, 304 }, 305 []uint64{29, 21, 37, 12, 1}, 306 }, 307 } 308 for _, testCase := range testCases { 309 council := newTestWeightedCouncil(testCase.validators) 310 candidates := append(council.validators, council.demotedValidators...) 311 weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, testCase.stakingInfo) 312 assert.NoError(t, err) 313 totalStaking, _ := calcTotalAmount(weightedValidators, testCase.stakingInfo, stakingAmounts) 314 calcWeight(weightedValidators, stakingAmounts, totalStaking) 315 316 for i, weight := range testCase.expectedWeights { 317 assert.Equal(t, weight, weightedValidators[i].Weight()) 318 } 319 } 320 }