github.com/klaytn/klaytn@v1.12.1/reward/staking_info.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 reward 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 "math" 25 "math/big" 26 "sort" 27 28 "github.com/klaytn/klaytn/common" 29 "github.com/klaytn/klaytn/params" 30 "github.com/klaytn/klaytn/rlp" 31 ) 32 33 const ( 34 AddrNotFoundInCouncilNodes = -1 35 maxStakingLimit = uint64(100000000000) 36 DefaultGiniCoefficient = -1.0 37 ) 38 39 var ( 40 maxStakingLimitBigInt = big.NewInt(0).SetUint64(maxStakingLimit) 41 42 ErrAddrNotInStakingInfo = errors.New("Address is not in stakingInfo") 43 ) 44 45 // StakingInfo contains staking information. 46 type StakingInfo struct { 47 BlockNum uint64 `json:"blockNum"` // Block number where staking information of Council is fetched 48 49 // Information retrieved from AddressBook smart contract 50 CouncilNodeAddrs []common.Address `json:"councilNodeAddrs"` // NodeIds of Council 51 CouncilStakingAddrs []common.Address `json:"councilStakingAddrs"` // Address of Staking account which holds staking balance 52 CouncilRewardAddrs []common.Address `json:"councilRewardAddrs"` // Address of Council account which will get block reward 53 54 KCFAddr common.Address `json:"kcfAddr"` // Address of KCF contract 55 KFFAddr common.Address `json:"kffAddr"` // Address of KFF contract 56 57 UseGini bool `json:"useGini"` 58 Gini float64 `json:"gini"` // gini coefficient 59 60 // Derived from CouncilStakingAddrs 61 CouncilStakingAmounts []uint64 `json:"councilStakingAmounts"` // Staking amounts of Council 62 } 63 64 // MarshalJSON supports json marshalling for both oldStakingInfo and StakingInfo 65 // TODO-klaytn-Mantle: remove this marshal function when backward-compatibility for KIR/PoC is not needed 66 func (st StakingInfo) MarshalJSON() ([]byte, error) { 67 type extendedSt struct { 68 BlockNum uint64 `json:"blockNum"` 69 CouncilNodeAddrs []common.Address `json:"councilNodeAddrs"` 70 CouncilStakingAddrs []common.Address `json:"councilStakingAddrs"` 71 CouncilRewardAddrs []common.Address `json:"councilRewardAddrs"` 72 KCFAddr common.Address `json:"kcfAddr"` 73 KFFAddr common.Address `json:"kffAddr"` 74 UseGini bool `json:"useGini"` 75 Gini float64 `json:"gini"` 76 CouncilStakingAmounts []uint64 `json:"councilStakingAmounts"` 77 78 // legacy fields of StakingInfo 79 KIRAddr common.Address `json:"KIRAddr"` // KIRAddr -> KCFAddr from v1.10.2 80 PoCAddr common.Address `json:"PoCAddr"` // PoCAddr -> KFFAddr from v1.10.2 81 } 82 83 var ext extendedSt 84 ext.BlockNum = st.BlockNum 85 ext.CouncilNodeAddrs = st.CouncilNodeAddrs 86 ext.CouncilStakingAddrs = st.CouncilStakingAddrs 87 ext.CouncilRewardAddrs = st.CouncilRewardAddrs 88 ext.KCFAddr = st.KCFAddr 89 ext.KFFAddr = st.KFFAddr 90 ext.UseGini = st.UseGini 91 ext.Gini = st.Gini 92 ext.CouncilStakingAmounts = st.CouncilStakingAmounts 93 94 // KIRAddr and PoCAddr are for backward-compatibility of database 95 ext.KIRAddr = st.KCFAddr 96 ext.PoCAddr = st.KFFAddr 97 98 return json.Marshal(&ext) 99 } 100 101 // UnmarshalJSON supports json unmarshalling for both oldStakingInfo and StakingInfo 102 func (st *StakingInfo) UnmarshalJSON(input []byte) error { 103 type extendedSt struct { 104 BlockNum uint64 `json:"blockNum"` 105 CouncilNodeAddrs []common.Address `json:"councilNodeAddrs"` 106 CouncilStakingAddrs []common.Address `json:"councilStakingAddrs"` 107 CouncilRewardAddrs []common.Address `json:"councilRewardAddrs"` 108 KCFAddr common.Address `json:"kcfAddr"` 109 KFFAddr common.Address `json:"kffAddr"` 110 UseGini bool `json:"useGini"` 111 Gini float64 `json:"gini"` 112 CouncilStakingAmounts []uint64 `json:"councilStakingAmounts"` 113 114 // legacy fields of StakingInfo 115 KIRAddr common.Address `json:"KIRAddr"` // KIRAddr -> KCFAddr from v1.10.2 116 PoCAddr common.Address `json:"PoCAddr"` // PoCAddr -> KFFAddr from v1.10.2 117 } 118 119 var ext extendedSt 120 emptyAddr := common.Address{} 121 122 if err := json.Unmarshal(input, &ext); err != nil { 123 return err 124 } 125 126 st.BlockNum = ext.BlockNum 127 st.CouncilNodeAddrs = ext.CouncilNodeAddrs 128 st.CouncilStakingAddrs = ext.CouncilStakingAddrs 129 st.CouncilRewardAddrs = ext.CouncilRewardAddrs 130 st.KCFAddr = ext.KCFAddr 131 st.KFFAddr = ext.KFFAddr 132 st.UseGini = ext.UseGini 133 st.Gini = ext.Gini 134 st.CouncilStakingAmounts = ext.CouncilStakingAmounts 135 136 if st.KCFAddr == emptyAddr { 137 st.KCFAddr = ext.KIRAddr 138 } 139 if st.KFFAddr == emptyAddr { 140 st.KFFAddr = ext.PoCAddr 141 } 142 143 return nil 144 } 145 146 // Refined staking information suitable for proposer selection. 147 // Sometimes a node would register multiple NodeAddrs 148 // in which each entry has different StakingAddr and same RewardAddr. 149 // We treat those entries with common RewardAddr as one node. 150 // 151 // For example, 152 // 153 // NodeAddrs = [N1, N2, N3] 154 // StakingAddrs = [S1, S2, S3] 155 // RewardAddrs = [R1, R1, R3] 156 // StakingAmounts = [A1, A2, A3] 157 // 158 // can be consolidated into 159 // 160 // CN1 = {[N1,N2], [S1,S2], R1, A1+A2} 161 // CN3 = {[N3], [S3], R3, A3} 162 type consolidatedNode struct { 163 NodeAddrs []common.Address 164 StakingAddrs []common.Address 165 RewardAddr common.Address // common reward address 166 StakingAmount uint64 // sum of staking amounts 167 } 168 169 type ConsolidatedStakingInfo struct { 170 nodes []consolidatedNode 171 nodeIndex map[common.Address]int // nodeAddr -> index in []nodes 172 } 173 174 type stakingInfoRLP struct { 175 BlockNum uint64 176 CouncilNodeAddrs []common.Address 177 CouncilStakingAddrs []common.Address 178 CouncilRewardAddrs []common.Address 179 KCFAddr common.Address 180 KFFAddr common.Address 181 UseGini bool 182 Gini uint64 183 CouncilStakingAmounts []uint64 184 } 185 186 func newEmptyStakingInfo(blockNum uint64) *StakingInfo { 187 stakingInfo := &StakingInfo{ 188 BlockNum: blockNum, 189 CouncilNodeAddrs: make([]common.Address, 0, 0), 190 CouncilStakingAddrs: make([]common.Address, 0, 0), 191 CouncilRewardAddrs: make([]common.Address, 0, 0), 192 KCFAddr: common.Address{}, 193 KFFAddr: common.Address{}, 194 CouncilStakingAmounts: make([]uint64, 0, 0), 195 Gini: DefaultGiniCoefficient, 196 UseGini: false, 197 } 198 return stakingInfo 199 } 200 201 func newStakingInfo(bc blockChain, helper governanceHelper, blockNum uint64, nodeAddrs []common.Address, stakingAddrs []common.Address, rewardAddrs []common.Address, KCFAddr common.Address, KFFAddr common.Address) (*StakingInfo, error) { 202 intervalBlock := bc.GetBlockByNumber(blockNum) 203 if intervalBlock == nil { 204 logger.Trace("Failed to get the block by the given number", "blockNum", blockNum) 205 return nil, errors.New(fmt.Sprintf("Failed to get the block by the given number. blockNum: %d", blockNum)) 206 } 207 statedb, err := bc.StateAt(intervalBlock.Root()) 208 if err != nil { 209 logger.Trace("Failed to make a state for interval block", "interval blockNum", blockNum, "err", err) 210 return nil, err 211 } 212 213 // Get balance of stakingAddrs 214 stakingAmounts := make([]uint64, len(stakingAddrs)) 215 for i, stakingAddr := range stakingAddrs { 216 tempStakingAmount := big.NewInt(0).Div(statedb.GetBalance(stakingAddr), big.NewInt(0).SetUint64(params.KLAY)) 217 if tempStakingAmount.Cmp(maxStakingLimitBigInt) > 0 { 218 tempStakingAmount.SetUint64(maxStakingLimit) 219 } 220 stakingAmounts[i] = tempStakingAmount.Uint64() 221 } 222 223 pset, err := helper.EffectiveParams(blockNum) 224 if err != nil { 225 return nil, err 226 } 227 useGini := pset.UseGiniCoeff() 228 gini := DefaultGiniCoefficient 229 230 stakingInfo := &StakingInfo{ 231 BlockNum: blockNum, 232 CouncilNodeAddrs: nodeAddrs, 233 CouncilStakingAddrs: stakingAddrs, 234 CouncilRewardAddrs: rewardAddrs, 235 KCFAddr: KCFAddr, 236 KFFAddr: KFFAddr, 237 CouncilStakingAmounts: stakingAmounts, 238 Gini: gini, 239 UseGini: useGini, 240 } 241 return stakingInfo, nil 242 } 243 244 func (s *StakingInfo) GetIndexByNodeAddress(nodeAddress common.Address) (int, error) { 245 for i, addr := range s.CouncilNodeAddrs { 246 if addr == nodeAddress { 247 return i, nil 248 } 249 } 250 return AddrNotFoundInCouncilNodes, ErrAddrNotInStakingInfo 251 } 252 253 func (s *StakingInfo) GetStakingAmountByNodeId(nodeAddress common.Address) (uint64, error) { 254 i, err := s.GetIndexByNodeAddress(nodeAddress) 255 if err != nil { 256 return 0, err 257 } 258 return s.CouncilStakingAmounts[i], nil 259 } 260 261 func (s *StakingInfo) String() string { 262 j, err := json.Marshal(s) 263 if err != nil { 264 return err.Error() 265 } 266 return string(j) 267 } 268 269 func (s *StakingInfo) EncodeRLP(w io.Writer) error { 270 // float64 is not rlp serializable, so it converts to bytes 271 return rlp.Encode(w, &stakingInfoRLP{s.BlockNum, s.CouncilNodeAddrs, s.CouncilStakingAddrs, s.CouncilRewardAddrs, s.KCFAddr, s.KFFAddr, s.UseGini, math.Float64bits(s.Gini), s.CouncilStakingAmounts}) 272 } 273 274 func (s *StakingInfo) DecodeRLP(st *rlp.Stream) error { 275 var dec stakingInfoRLP 276 if err := st.Decode(&dec); err != nil { 277 return err 278 } 279 s.BlockNum = dec.BlockNum 280 s.CouncilNodeAddrs, s.CouncilStakingAddrs, s.CouncilRewardAddrs = dec.CouncilNodeAddrs, dec.CouncilStakingAddrs, dec.CouncilRewardAddrs 281 s.KCFAddr, s.KFFAddr, s.UseGini, s.Gini = dec.KCFAddr, dec.KFFAddr, dec.UseGini, math.Float64frombits(dec.Gini) 282 s.CouncilStakingAmounts = dec.CouncilStakingAmounts 283 return nil 284 } 285 286 func (s *StakingInfo) GetConsolidatedStakingInfo() *ConsolidatedStakingInfo { 287 c := &ConsolidatedStakingInfo{ 288 nodes: make([]consolidatedNode, 0), 289 nodeIndex: make(map[common.Address]int), 290 } 291 292 rewardIndex := make(map[common.Address]int) // temporarily map rewardAddr -> index in []nodes 293 294 for j := 0; j < len(s.CouncilNodeAddrs); j++ { 295 var ( 296 nodeAddr = s.CouncilNodeAddrs[j] 297 stakingAddr = s.CouncilStakingAddrs[j] 298 rewardAddr = s.CouncilRewardAddrs[j] 299 stakingAmount = s.CouncilStakingAmounts[j] 300 ) 301 if idx, ok := rewardIndex[rewardAddr]; !ok { 302 c.nodes = append(c.nodes, consolidatedNode{ 303 NodeAddrs: []common.Address{nodeAddr}, 304 StakingAddrs: []common.Address{stakingAddr}, 305 RewardAddr: rewardAddr, 306 StakingAmount: stakingAmount, 307 }) 308 c.nodeIndex[nodeAddr] = len(c.nodes) - 1 // point to new element 309 rewardIndex[rewardAddr] = len(c.nodes) - 1 310 } else { 311 c.nodes[idx].NodeAddrs = append(c.nodes[idx].NodeAddrs, nodeAddr) 312 c.nodes[idx].StakingAddrs = append(c.nodes[idx].StakingAddrs, stakingAddr) 313 c.nodes[idx].StakingAmount += stakingAmount 314 c.nodeIndex[nodeAddr] = idx // point to existing element 315 } 316 } 317 return c 318 } 319 320 func (c *ConsolidatedStakingInfo) GetAllNodes() []consolidatedNode { 321 return c.nodes 322 } 323 324 func (c *ConsolidatedStakingInfo) GetConsolidatedNode(nodeAddr common.Address) *consolidatedNode { 325 if idx, ok := c.nodeIndex[nodeAddr]; ok { 326 return &c.nodes[idx] 327 } 328 return nil 329 } 330 331 // Calculate Gini coefficient of the StakingAmounts. 332 // Only amounts greater or equal to `minStake` are included in the calculation. 333 // Set `minStake` to 0 to calculate Gini coefficient of all amounts. 334 func (c *ConsolidatedStakingInfo) CalcGiniCoefficientMinStake(minStake uint64) float64 { 335 var amounts []float64 336 for _, node := range c.nodes { 337 if node.StakingAmount >= minStake { 338 amounts = append(amounts, float64(node.StakingAmount)) 339 } 340 } 341 342 if len(amounts) == 0 { 343 return DefaultGiniCoefficient 344 } 345 return CalcGiniCoefficient(amounts) 346 } 347 348 func (c *ConsolidatedStakingInfo) String() string { 349 j, err := json.Marshal(c.nodes) 350 if err != nil { 351 return err.Error() 352 } 353 return string(j) 354 } 355 356 type float64Slice []float64 357 358 func (p float64Slice) Len() int { return len(p) } 359 func (p float64Slice) Less(i, j int) bool { return p[i] < p[j] } 360 func (p float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 361 362 func CalcGiniCoefficient(stakingAmount float64Slice) float64 { 363 sort.Sort(stakingAmount) 364 365 // calculate gini coefficient 366 sumOfAbsoluteDifferences := float64(0) 367 subSum := float64(0) 368 369 for i, x := range stakingAmount { 370 temp := x*float64(i) - subSum 371 sumOfAbsoluteDifferences = sumOfAbsoluteDifferences + temp 372 subSum = subSum + x 373 } 374 375 result := sumOfAbsoluteDifferences / subSum / float64(len(stakingAmount)) 376 result = math.Round(result*100) / 100 377 378 return result 379 }