github.com/klaytn/klaytn@v1.12.1/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 func TestRewardAddressLookup(t *testing.T) { 115 testCases := []struct { 116 validators []common.Address 117 stakingInfo *reward.StakingInfo 118 expectedStakingAmounts []float64 119 }{ 120 { 121 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 122 &reward.StakingInfo{ 123 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 124 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, 125 CouncilStakingAmounts: []uint64{10000000, 5000000, 5000000}, 126 }, 127 []float64{10000000, 5000000, 5000000}, 128 }, 129 } 130 for _, testCase := range testCases { 131 council := newTestWeightedCouncil(testCase.validators) 132 weightedValidators, _, err := getStakingAmountsOfValidators(council.validators, testCase.stakingInfo) 133 134 assert.NoError(t, err) 135 for _, val := range weightedValidators { 136 assert.NotEqual(t, val.RewardAddress(), common.Address{}) 137 } 138 } 139 } 140 141 // TestCalcTotalAmount tests calcTotalAmount that calculates totalAmount of stakingAmounts and gini coefficient if UseGini is true. 142 // if UseGini is true, gini is calculated and reflected to stakingAmounts. 143 func TestCalcTotalAmount(t *testing.T) { 144 testCases := []struct { 145 weightedValidators []*weightedValidator // Produced by getStakingAmountsOfValidators() 146 stakingInfo *reward.StakingInfo // Produced by GetStakingInfo() 147 stakingAmounts []float64 // Produced by getStakingAmountsOfValidators() 148 expectedGini float64 // Gini among the []weightedValidators which is a subset of CouncilNodeAddrs 149 expectedTotalAmount float64 // Sum of Gini-adjusted amounts 150 expectedStakingAmounts []float64 // Gini-adjusted amounts 151 }{ 152 { // Gini disabled 153 []*weightedValidator{ 154 {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, 155 }, 156 &reward.StakingInfo{ 157 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 158 UseGini: false, 159 Gini: reward.DefaultGiniCoefficient, // To see if calcTotalAmount modifies Gini field, set it to -1. 160 }, 161 []float64{5000000, 5000000, 5000000}, 162 reward.DefaultGiniCoefficient, 163 15000000, 164 []float64{5000000, 5000000, 5000000}, 165 }, 166 { // Gini enabled but equal amounts. 167 []*weightedValidator{ 168 {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, 169 }, 170 &reward.StakingInfo{ 171 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 172 UseGini: true, 173 Gini: reward.DefaultGiniCoefficient, 174 }, 175 []float64{5000000, 5000000, 5000000}, 176 0, 177 15000000, 178 []float64{5000000, 5000000, 5000000}, 179 }, 180 { // Gini enabled and unequal amounts. 181 []*weightedValidator{ 182 {address: common.StringToAddress("101")}, {address: common.StringToAddress("102")}, {address: common.StringToAddress("103")}, {address: common.StringToAddress("104")}, {address: common.StringToAddress("105")}, 183 }, 184 &reward.StakingInfo{ 185 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 186 UseGini: true, 187 Gini: reward.DefaultGiniCoefficient, 188 }, 189 []float64{10000000, 20000000, 30000000, 40000000, 50000000}, 190 0.27, 191 3779508, 192 []float64{324946, 560845, 771786, 967997, 1153934}, 193 }, 194 } 195 for _, testCase := range testCases { 196 stakingAmounts := testCase.stakingAmounts 197 totalAmount, giniUsed := calcTotalAmount(testCase.weightedValidators, testCase.stakingInfo, stakingAmounts) 198 199 assert.Equal(t, testCase.expectedGini, giniUsed) // calcTotalAmount computes gini among validators on its own 200 assert.Equal(t, reward.DefaultGiniCoefficient, testCase.stakingInfo.Gini) // stakingInfo.Gini left untouched 201 assert.Equal(t, testCase.expectedTotalAmount, totalAmount) 202 assert.Equal(t, testCase.expectedStakingAmounts, stakingAmounts) 203 } 204 } 205 206 // TestCalcWeight tests calcWeight that calculates weights and saves them to validators. 207 // weights are the ratio of each stakingAmount to totalStaking 208 func TestCalcWeight(t *testing.T) { 209 testCases := []struct { 210 weightedValidators []*weightedValidator 211 stakingAmounts []float64 212 totalStaking float64 213 expectedWeights []uint64 214 }{ 215 { 216 []*weightedValidator{ 217 {}, {}, {}, 218 }, 219 []float64{0, 0, 0}, 220 0, 221 []uint64{0, 0, 0}, 222 }, 223 { 224 []*weightedValidator{ 225 {}, {}, {}, 226 }, 227 []float64{5000000, 5000000, 5000000}, 228 15000000, 229 []uint64{33, 33, 33}, 230 }, 231 { 232 []*weightedValidator{ 233 {}, {}, {}, {}, 234 }, 235 []float64{5000000, 10000000, 5000000, 5000000}, 236 25000000, 237 []uint64{20, 40, 20, 20}, 238 }, 239 { 240 []*weightedValidator{ 241 {}, {}, {}, {}, {}, 242 }, 243 []float64{324946, 560845, 771786, 967997, 1153934}, 244 3779508, 245 []uint64{9, 15, 20, 26, 31}, 246 }, 247 } 248 for _, testCase := range testCases { 249 calcWeight(testCase.weightedValidators, testCase.stakingAmounts, testCase.totalStaking) 250 for i, weight := range testCase.expectedWeights { 251 assert.Equal(t, weight, testCase.weightedValidators[i].Weight()) 252 } 253 } 254 } 255 256 // TestWeightedCouncil_validatorWeightWithStakingInfo is union of above tests. 257 // Weight should be calculated exactly by a validator list and a stakingInfo given 258 func TestWeightedCouncil_validatorWeightWithStakingInfo(t *testing.T) { 259 testCases := []struct { 260 validators []common.Address 261 stakingInfo *reward.StakingInfo 262 expectedWeights []uint64 263 }{ 264 { 265 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 266 &reward.StakingInfo{ 267 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103")}, 268 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203")}, 269 UseGini: false, 270 CouncilStakingAmounts: []uint64{0, 0, 0}, 271 }, 272 []uint64{0, 0, 0}, 273 }, 274 { 275 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 276 &reward.StakingInfo{ 277 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 278 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204")}, 279 UseGini: true, 280 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000}, 281 }, 282 []uint64{25, 25, 25, 25}, 283 }, 284 { 285 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 286 &reward.StakingInfo{ 287 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 288 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("205")}, 289 UseGini: true, 290 CouncilStakingAmounts: []uint64{10000000, 20000000, 30000000, 40000000, 50000000}, 291 }, 292 []uint64{9, 15, 20, 26, 31}, 293 }, 294 { 295 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 296 &reward.StakingInfo{ 297 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, 298 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, 299 UseGini: false, 300 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, 301 }, 302 []uint64{40, 20, 20, 20}, 303 }, 304 { 305 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104")}, 306 &reward.StakingInfo{ 307 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901")}, 308 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201")}, 309 UseGini: true, 310 CouncilStakingAmounts: []uint64{5000000, 5000000, 5000000, 5000000, 5000000}, 311 }, 312 []uint64{38, 21, 21, 21}, 313 }, 314 { 315 []common.Address{common.StringToAddress("104"), common.StringToAddress("103"), common.StringToAddress("102"), common.StringToAddress("101")}, 316 &reward.StakingInfo{ 317 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, 318 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, 319 UseGini: true, 320 CouncilStakingAmounts: []uint64{10000000, 5000000, 20000000, 5000000, 5000000, 5000000}, 321 }, 322 []uint64{29, 21, 37, 12}, 323 }, 324 { 325 []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("105")}, 326 &reward.StakingInfo{ 327 CouncilNodeAddrs: []common.Address{common.StringToAddress("101"), common.StringToAddress("102"), common.StringToAddress("103"), common.StringToAddress("104"), common.StringToAddress("901"), common.StringToAddress("902")}, 328 CouncilRewardAddrs: []common.Address{common.StringToAddress("201"), common.StringToAddress("202"), common.StringToAddress("203"), common.StringToAddress("204"), common.StringToAddress("201"), common.StringToAddress("202")}, 329 UseGini: true, 330 CouncilStakingAmounts: []uint64{10000000, 5000000, 20000000, 5000000, 5000000, 5000000}, 331 }, 332 []uint64{29, 21, 37, 12, 1}, 333 }, 334 } 335 for _, testCase := range testCases { 336 council := newTestWeightedCouncil(testCase.validators) 337 candidates := append(council.validators, council.demotedValidators...) 338 weightedValidators, stakingAmounts, err := getStakingAmountsOfValidators(candidates, testCase.stakingInfo) 339 assert.NoError(t, err) 340 totalStaking, _ := calcTotalAmount(weightedValidators, testCase.stakingInfo, stakingAmounts) 341 calcWeight(weightedValidators, stakingAmounts, totalStaking) 342 343 for i, weight := range testCase.expectedWeights { 344 assert.Equal(t, weight, weightedValidators[i].Weight()) 345 } 346 } 347 }