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  }