github.com/klaytn/klaytn@v1.12.1/reward/reward_distributor.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  	"errors"
    21  	"math/big"
    22  	"strconv"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/klaytn/klaytn/blockchain/types"
    27  	"github.com/klaytn/klaytn/common"
    28  	"github.com/klaytn/klaytn/consensus/istanbul"
    29  	"github.com/klaytn/klaytn/crypto/sha3"
    30  	"github.com/klaytn/klaytn/log"
    31  	"github.com/klaytn/klaytn/params"
    32  	"github.com/klaytn/klaytn/rlp"
    33  )
    34  
    35  var CalcDeferredRewardTimer time.Duration
    36  
    37  var logger = log.NewModuleLogger(log.Reward)
    38  
    39  var (
    40  	errInvalidFormat = errors.New("invalid ratio format")
    41  	errParsingRatio  = errors.New("parsing ratio fail")
    42  )
    43  
    44  type BalanceAdder interface {
    45  	AddBalance(addr common.Address, v *big.Int)
    46  }
    47  
    48  // Cannot use governance.Engine because of cyclic dependency.
    49  // Instead declare only the methods used by this package.
    50  type governanceHelper interface {
    51  	CurrentParams() *params.GovParamSet
    52  	EffectiveParams(num uint64) (*params.GovParamSet, error)
    53  }
    54  
    55  type rewardConfig struct {
    56  	// hardfork rules
    57  	rules params.Rules
    58  
    59  	// values calculated from block header
    60  	totalFee *big.Int
    61  
    62  	// values from GovParamSet
    63  	mintingAmount *big.Int
    64  	minimumStake  *big.Int
    65  	deferredTxFee bool
    66  
    67  	// parsed ratio
    68  	cnRatio    *big.Int
    69  	kffRatio   *big.Int
    70  	kcfRatio   *big.Int
    71  	totalRatio *big.Int
    72  
    73  	// parsed KIP82 ratio
    74  	cnProposerRatio *big.Int
    75  	cnStakingRatio  *big.Int
    76  	cnTotalRatio    *big.Int
    77  }
    78  
    79  type RewardSpec struct {
    80  	Minted   *big.Int                    `json:"minted"`   // the amount newly minted
    81  	TotalFee *big.Int                    `json:"totalFee"` // total tx fee spent
    82  	BurntFee *big.Int                    `json:"burntFee"` // the amount burnt
    83  	Proposer *big.Int                    `json:"proposer"` // the amount allocated to the block proposer
    84  	Stakers  *big.Int                    `json:"stakers"`  // total amount allocated to stakers
    85  	KFF      *big.Int                    `json:"kff"`      // the amount allocated to KFF
    86  	KCF      *big.Int                    `json:"kcf"`      // the amount allocated to KCF
    87  	Rewards  map[common.Address]*big.Int `json:"rewards"`  // mapping from reward recipient to amounts
    88  }
    89  
    90  func NewRewardSpec() *RewardSpec {
    91  	return &RewardSpec{
    92  		Minted:   big.NewInt(0),
    93  		TotalFee: big.NewInt(0),
    94  		BurntFee: big.NewInt(0),
    95  		Proposer: big.NewInt(0),
    96  		Stakers:  big.NewInt(0),
    97  		KFF:      big.NewInt(0),
    98  		KCF:      big.NewInt(0),
    99  		Rewards:  make(map[common.Address]*big.Int),
   100  	}
   101  }
   102  
   103  func (spec *RewardSpec) Add(delta *RewardSpec) {
   104  	spec.Minted.Add(spec.Minted, delta.Minted)
   105  	spec.TotalFee.Add(spec.TotalFee, delta.TotalFee)
   106  	spec.BurntFee.Add(spec.BurntFee, delta.BurntFee)
   107  	spec.Proposer.Add(spec.Proposer, delta.Proposer)
   108  	spec.Stakers.Add(spec.Stakers, delta.Stakers)
   109  	spec.KFF.Add(spec.KFF, delta.KFF)
   110  	spec.KCF.Add(spec.KCF, delta.KCF)
   111  
   112  	for addr, amount := range delta.Rewards {
   113  		incrementRewardsMap(spec.Rewards, addr, amount)
   114  	}
   115  }
   116  
   117  // TODO: this is for legacy, will be removed
   118  type RewardDistributor struct{}
   119  
   120  func NewRewardDistributor(gh governanceHelper) *RewardDistributor {
   121  	return &RewardDistributor{}
   122  }
   123  
   124  // DistributeBlockReward distributes a given block's reward at the end of block processing
   125  func DistributeBlockReward(b BalanceAdder, rewards map[common.Address]*big.Int) {
   126  	for addr, amount := range rewards {
   127  		b.AddBalance(addr, amount)
   128  	}
   129  }
   130  
   131  func NewRewardConfig(header *types.Header, rules params.Rules, pset *params.GovParamSet) (*rewardConfig, error) {
   132  	cnRatio, kffRatio, kcfRatio, totalRatio, err := parseRewardRatio(pset.Ratio())
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	var cnProposerRatio, cnStakingRatio, cnTotalRatio int64
   138  	if rules.IsKore {
   139  		cnProposerRatio, cnStakingRatio, cnTotalRatio, err = parseRewardKip82Ratio(pset.Kip82Ratio())
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  	}
   144  
   145  	return &rewardConfig{
   146  		// hardfork rules
   147  		rules: rules,
   148  
   149  		// values calculated from block header
   150  		totalFee: GetTotalTxFee(header, rules, pset),
   151  
   152  		// values from GovParamSet
   153  		mintingAmount: new(big.Int).Set(pset.MintingAmountBig()),
   154  		minimumStake:  new(big.Int).Set(pset.MinimumStakeBig()),
   155  		deferredTxFee: pset.DeferredTxFee(),
   156  
   157  		// parsed ratio
   158  		cnRatio:    big.NewInt(cnRatio),
   159  		kffRatio:   big.NewInt(kffRatio),
   160  		kcfRatio:   big.NewInt(kcfRatio),
   161  		totalRatio: big.NewInt(totalRatio),
   162  
   163  		// parsed KIP82 ratio
   164  		cnProposerRatio: big.NewInt(cnProposerRatio),
   165  		cnStakingRatio:  big.NewInt(cnStakingRatio),
   166  		cnTotalRatio:    big.NewInt(cnTotalRatio),
   167  	}, nil
   168  }
   169  
   170  func GetTotalTxFee(header *types.Header, rules params.Rules, pset *params.GovParamSet) *big.Int {
   171  	totalFee := new(big.Int).SetUint64(header.GasUsed)
   172  	if rules.IsMagma {
   173  		totalFee = totalFee.Mul(totalFee, header.BaseFee)
   174  	} else {
   175  		totalFee = totalFee.Mul(totalFee, new(big.Int).SetUint64(pset.UnitPrice()))
   176  	}
   177  	return totalFee
   178  }
   179  
   180  // config.Istanbul must have been set
   181  func IsRewardSimple(pset *params.GovParamSet) bool {
   182  	return pset.Policy() != uint64(istanbul.WeightedRandom)
   183  }
   184  
   185  // CalcRewardParamBlock returns the block number with which governance parameters must be fetched
   186  // This mimics the legacy reward config cache before Kore
   187  func CalcRewardParamBlock(num, epoch uint64, rules params.Rules) uint64 {
   188  	if !rules.IsKore && num%epoch == 0 {
   189  		return num - epoch
   190  	}
   191  	return num
   192  }
   193  
   194  // GetBlockReward returns the actual reward amounts paid in this block
   195  // Used in klay_getReward RPC API
   196  func GetBlockReward(header *types.Header, rules params.Rules, pset *params.GovParamSet) (*RewardSpec, error) {
   197  	var spec *RewardSpec
   198  	var err error
   199  
   200  	if IsRewardSimple(pset) {
   201  		spec, err = CalcDeferredRewardSimple(header, rules, pset)
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  	} else {
   206  		spec, err = CalcDeferredReward(header, rules, pset)
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  	}
   211  
   212  	// Compensate the difference between CalcDeferredReward() and actual payment.
   213  	// If not DeferredTxFee, CalcDeferredReward() assumes 0 total_fee, but
   214  	// some non-zero fee already has been paid to the proposer.
   215  	if !pset.DeferredTxFee() {
   216  		if rules.IsMagma {
   217  			txFee := GetTotalTxFee(header, rules, pset)
   218  			txFeeBurn := getBurnAmountMagma(txFee)
   219  			txFeeRemained := new(big.Int).Sub(txFee, txFeeBurn)
   220  			spec.BurntFee = txFeeBurn
   221  
   222  			spec.Proposer = spec.Proposer.Add(spec.Proposer, txFeeRemained)
   223  			spec.TotalFee = spec.TotalFee.Add(spec.TotalFee, txFee)
   224  			incrementRewardsMap(spec.Rewards, header.Rewardbase, txFeeRemained)
   225  		} else {
   226  			txFee := GetTotalTxFee(header, rules, pset)
   227  			spec.Proposer = spec.Proposer.Add(spec.Proposer, txFee)
   228  			spec.TotalFee = spec.TotalFee.Add(spec.TotalFee, txFee)
   229  			// get the proposer of this block.
   230  			proposer, err := ecrecover(header)
   231  			if err != nil {
   232  				return nil, err
   233  			}
   234  			incrementRewardsMap(spec.Rewards, proposer, txFee)
   235  
   236  		}
   237  	}
   238  
   239  	return spec, nil
   240  }
   241  
   242  // CalcDeferredRewardSimple distributes rewards to proposer after optional fee burning
   243  // this behaves similar to the previous MintKLAY
   244  // MintKLAY has been superseded because we need to split reward distribution
   245  // logic into (1) calculation, and (2) actual distribution.
   246  // CalcDeferredRewardSimple does the former and DistributeBlockReward does the latter
   247  func CalcDeferredRewardSimple(header *types.Header, rules params.Rules, pset *params.GovParamSet) (*RewardSpec, error) {
   248  	rc, err := NewRewardConfig(header, rules, pset)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	minted := rc.mintingAmount
   254  
   255  	// If not DeferredTxFee, fees are already added to the proposer during TX execution.
   256  	// Therefore, there are no fees to distribute here at the end of block processing.
   257  	// However, before Kore, there was a bug that distributed tx fee regardless
   258  	// of `deferredTxFee` flag. See https://github.com/klaytn/klaytn/issues/1692.
   259  	// To maintain backward compatibility, we only fix the buggy logic after Magma
   260  	// and leave the buggy logic before Magma.
   261  	// However, the fees must be compensated to calculate actual rewards paid.
   262  
   263  	// bug-fixed logic after Magma
   264  	if !rc.deferredTxFee && rc.rules.IsMagma {
   265  		proposer := new(big.Int).Set(minted)
   266  		logger.Debug("CalcDeferredRewardSimple after Kore when deferredTxFee=false returns",
   267  			"proposer", proposer)
   268  		spec := NewRewardSpec()
   269  		spec.Minted = minted
   270  		spec.TotalFee = big.NewInt(0)
   271  		spec.BurntFee = big.NewInt(0)
   272  		spec.Proposer = proposer
   273  		incrementRewardsMap(spec.Rewards, header.Rewardbase, proposer)
   274  		return spec, nil
   275  	}
   276  
   277  	totalFee := rc.totalFee
   278  	rewardFee := new(big.Int).Set(totalFee)
   279  	burntFee := big.NewInt(0)
   280  
   281  	if rc.rules.IsMagma {
   282  		burnt := getBurnAmountMagma(rewardFee)
   283  		rewardFee = rewardFee.Sub(rewardFee, burnt)
   284  		burntFee = burntFee.Add(burntFee, burnt)
   285  	}
   286  
   287  	proposer := big.NewInt(0).Add(minted, rewardFee)
   288  
   289  	logger.Debug("CalcDeferredRewardSimple returns",
   290  		"proposer", proposer.Uint64(),
   291  		"totalFee", totalFee.Uint64(),
   292  		"burntFee", burntFee.Uint64(),
   293  	)
   294  
   295  	spec := NewRewardSpec()
   296  	spec.Minted = minted
   297  	spec.TotalFee = totalFee
   298  	spec.BurntFee = burntFee
   299  	spec.Proposer = proposer
   300  	incrementRewardsMap(spec.Rewards, header.Rewardbase, proposer)
   301  	return spec, nil
   302  }
   303  
   304  // CalcDeferredReward calculates the deferred rewards,
   305  // which are determined at the end of block processing.
   306  func CalcDeferredReward(header *types.Header, rules params.Rules, pset *params.GovParamSet) (*RewardSpec, error) {
   307  	defer func(start time.Time) {
   308  		CalcDeferredRewardTimer = time.Since(start)
   309  	}(time.Now())
   310  
   311  	rc, err := NewRewardConfig(header, rules, pset)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	var (
   317  		minted      = rc.mintingAmount
   318  		stakingInfo = GetStakingInfo(header.Number.Uint64())
   319  	)
   320  
   321  	totalFee, rewardFee, burntFee := calcDeferredFee(rc)
   322  	proposer, stakers, kff, kcf, splitRem := calcSplit(rc, minted, rewardFee)
   323  	shares, shareRem := calcShares(stakingInfo, stakers, rc.minimumStake.Uint64())
   324  
   325  	// Remainder from (CN, KFF, KCF) split goes to KFF
   326  	kff = kff.Add(kff, splitRem)
   327  	// Remainder from staker shares goes to Proposer
   328  	// Then, deduct it from stakers so that `minted + totalFee - burntFee = proposer + stakers + kff + kcf`
   329  	proposer = proposer.Add(proposer, shareRem)
   330  	stakers = stakers.Sub(stakers, shareRem)
   331  
   332  	// if KFF or KCF is not set, proposer gets the portion
   333  	if stakingInfo == nil || common.EmptyAddress(stakingInfo.KFFAddr) {
   334  		logger.Debug("KFF empty, proposer gets its portion", "kff", kff)
   335  		proposer = proposer.Add(proposer, kff)
   336  		kff = big.NewInt(0)
   337  	}
   338  	if stakingInfo == nil || common.EmptyAddress(stakingInfo.KCFAddr) {
   339  		logger.Debug("KCF empty, proposer gets its portion", "kcf", kcf)
   340  		proposer = proposer.Add(proposer, kcf)
   341  		kcf = big.NewInt(0)
   342  	}
   343  
   344  	spec := NewRewardSpec()
   345  	spec.Minted = minted
   346  	spec.TotalFee = totalFee
   347  	spec.BurntFee = burntFee
   348  	spec.Proposer = proposer
   349  	spec.Stakers = stakers
   350  	spec.KFF = kff
   351  	spec.KCF = kcf
   352  
   353  	incrementRewardsMap(spec.Rewards, header.Rewardbase, proposer)
   354  
   355  	if stakingInfo != nil && !common.EmptyAddress(stakingInfo.KFFAddr) {
   356  		incrementRewardsMap(spec.Rewards, stakingInfo.KFFAddr, kff)
   357  	}
   358  	if stakingInfo != nil && !common.EmptyAddress(stakingInfo.KCFAddr) {
   359  		incrementRewardsMap(spec.Rewards, stakingInfo.KCFAddr, kcf)
   360  	}
   361  
   362  	for rewardAddr, rewardAmount := range shares {
   363  		incrementRewardsMap(spec.Rewards, rewardAddr, rewardAmount)
   364  	}
   365  	logger.Debug("CalcDeferredReward() returns", "spec", spec)
   366  
   367  	return spec, nil
   368  }
   369  
   370  // calcDeferredFee splits fee into (total, reward, burnt)
   371  func calcDeferredFee(rc *rewardConfig) (*big.Int, *big.Int, *big.Int) {
   372  	// If not DeferredTxFee, fees are already added to the proposer during TX execution.
   373  	// Therefore, there are no fees to distribute here at the end of block processing.
   374  	// However, the fees must be compensated to calculate actual rewards paid.
   375  	if !rc.deferredTxFee {
   376  		return big.NewInt(0), big.NewInt(0), big.NewInt(0)
   377  	}
   378  
   379  	totalFee := rc.totalFee
   380  	rewardFee := new(big.Int).Set(totalFee)
   381  	burntFee := big.NewInt(0)
   382  
   383  	// after magma, burn half of gas
   384  	if rc.rules.IsMagma {
   385  		burnt := getBurnAmountMagma(rewardFee)
   386  		rewardFee = rewardFee.Sub(rewardFee, burnt)
   387  		burntFee = burntFee.Add(burntFee, burnt)
   388  	}
   389  
   390  	// after kore, burn fees up to proposer's minted reward
   391  	if rc.rules.IsKore {
   392  		burnt := getBurnAmountKore(rc, rewardFee)
   393  		rewardFee = rewardFee.Sub(rewardFee, burnt)
   394  		burntFee = burntFee.Add(burntFee, burnt)
   395  	}
   396  
   397  	logger.Debug("calcDeferredFee()",
   398  		"totalFee", totalFee.Uint64(),
   399  		"rewardFee", rewardFee.Uint64(),
   400  		"burntFee", burntFee.Uint64(),
   401  	)
   402  	return totalFee, rewardFee, burntFee
   403  }
   404  
   405  func getBurnAmountMagma(fee *big.Int) *big.Int {
   406  	return new(big.Int).Div(fee, big.NewInt(2))
   407  }
   408  
   409  func getBurnAmountKore(rc *rewardConfig, fee *big.Int) *big.Int {
   410  	cn, _, _ := splitByRatio(rc, rc.mintingAmount)
   411  	proposer, _ := splitByKip82Ratio(rc, cn)
   412  
   413  	logger.Debug("getBurnAmountKore()",
   414  		"fee", fee.Uint64(),
   415  		"proposer", proposer.Uint64(),
   416  	)
   417  
   418  	if fee.Cmp(proposer) >= 0 {
   419  		return proposer
   420  	} else {
   421  		return new(big.Int).Set(fee) // return copy of the parameter
   422  	}
   423  }
   424  
   425  // calcSplit splits fee into (proposer, stakers, kff, kcf, remaining)
   426  // the sum of the output must be equal to (minted + fee)
   427  func calcSplit(rc *rewardConfig, minted, fee *big.Int) (*big.Int, *big.Int, *big.Int, *big.Int, *big.Int) {
   428  	totalResource := big.NewInt(0)
   429  	totalResource = totalResource.Add(minted, fee)
   430  
   431  	if rc.rules.IsKore {
   432  		cn, kff, kcf := splitByRatio(rc, minted)
   433  		proposer, stakers := splitByKip82Ratio(rc, cn)
   434  
   435  		proposer = proposer.Add(proposer, fee)
   436  
   437  		remaining := new(big.Int).Set(totalResource)
   438  		remaining = remaining.Sub(remaining, kff)
   439  		remaining = remaining.Sub(remaining, kcf)
   440  		remaining = remaining.Sub(remaining, proposer)
   441  		remaining = remaining.Sub(remaining, stakers)
   442  
   443  		logger.Debug("calcSplit() after kore",
   444  			"[in] minted", minted.Uint64(),
   445  			"[in] fee", fee.Uint64(),
   446  			"[out] proposer", proposer.Uint64(),
   447  			"[out] stakers", stakers.Uint64(),
   448  			"[out] kff", kff.Uint64(),
   449  			"[out] kcf", kcf.Uint64(),
   450  			"[out] remaining", remaining.Uint64(),
   451  		)
   452  		return proposer, stakers, kff, kcf, remaining
   453  	} else {
   454  		cn, kff, kcf := splitByRatio(rc, totalResource)
   455  
   456  		remaining := new(big.Int).Set(totalResource)
   457  		remaining = remaining.Sub(remaining, kff)
   458  		remaining = remaining.Sub(remaining, kcf)
   459  		remaining = remaining.Sub(remaining, cn)
   460  
   461  		logger.Debug("calcSplit() before kore",
   462  			"[in] minted", minted.Uint64(),
   463  			"[in] fee", fee.Uint64(),
   464  			"[out] cn", cn.Uint64(),
   465  			"[out] kff", kff.Uint64(),
   466  			"[out] kcf", kcf.Uint64(),
   467  			"[out] remaining", remaining.Uint64(),
   468  		)
   469  		return cn, big.NewInt(0), kff, kcf, remaining
   470  	}
   471  }
   472  
   473  // splitByRatio splits by `ratio`. It ignores any remaining amounts.
   474  func splitByRatio(rc *rewardConfig, source *big.Int) (*big.Int, *big.Int, *big.Int) {
   475  	cn := new(big.Int).Mul(source, rc.cnRatio)
   476  	cn = cn.Div(cn, rc.totalRatio)
   477  
   478  	kff := new(big.Int).Mul(source, rc.kffRatio)
   479  	kff = kff.Div(kff, rc.totalRatio)
   480  
   481  	kcf := new(big.Int).Mul(source, rc.kcfRatio)
   482  	kcf = kcf.Div(kcf, rc.totalRatio)
   483  
   484  	return cn, kff, kcf
   485  }
   486  
   487  // splitByKip82Ratio splits by `kip82ratio`. It ignores any remaining amounts.
   488  func splitByKip82Ratio(rc *rewardConfig, source *big.Int) (*big.Int, *big.Int) {
   489  	proposer := new(big.Int).Mul(source, rc.cnProposerRatio)
   490  	proposer = proposer.Div(proposer, rc.cnTotalRatio)
   491  
   492  	stakers := new(big.Int).Mul(source, rc.cnStakingRatio)
   493  	stakers = stakers.Div(stakers, rc.cnTotalRatio)
   494  
   495  	return proposer, stakers
   496  }
   497  
   498  // calcShares distributes stake reward among staked CNs
   499  func calcShares(stakingInfo *StakingInfo, stakeReward *big.Int, minStake uint64) (map[common.Address]*big.Int, *big.Int) {
   500  	// if stakingInfo is nil, stakeReward goes to proposer
   501  	if stakingInfo == nil {
   502  		return make(map[common.Address]*big.Int), stakeReward
   503  	}
   504  
   505  	cns := stakingInfo.GetConsolidatedStakingInfo()
   506  
   507  	totalStakesInt := uint64(0)
   508  
   509  	for _, node := range cns.GetAllNodes() {
   510  		if node.StakingAmount > minStake { // comparison in Klay
   511  			totalStakesInt += (node.StakingAmount - minStake)
   512  		}
   513  	}
   514  
   515  	totalStakes := new(big.Int).SetUint64(totalStakesInt)
   516  	remaining := new(big.Int).Set(stakeReward)
   517  	shares := make(map[common.Address]*big.Int)
   518  
   519  	for _, node := range cns.GetAllNodes() {
   520  		if node.StakingAmount > minStake {
   521  			effectiveStake := new(big.Int).SetUint64(node.StakingAmount - minStake)
   522  			// The KLAY unit will cancel out:
   523  			// rewardAmount (peb) = stakeReward (peb) * effectiveStake (KLAY) / totalStakes (KLAY)
   524  			rewardAmount := new(big.Int).Mul(stakeReward, effectiveStake)
   525  			rewardAmount = rewardAmount.Div(rewardAmount, totalStakes)
   526  			remaining = remaining.Sub(remaining, rewardAmount)
   527  			if rewardAmount.Cmp(big.NewInt(0)) > 0 {
   528  				shares[node.RewardAddr] = rewardAmount
   529  			}
   530  		}
   531  	}
   532  	logger.Debug("calcShares()",
   533  		"[in] stakeReward", stakeReward.Uint64(),
   534  		"[out] remaining", remaining.Uint64(),
   535  		"[out] shares", shares,
   536  	)
   537  
   538  	return shares, remaining
   539  }
   540  
   541  // parseRewardRatio parses string `ratio` into ints
   542  func parseRewardRatio(ratio string) (int64, int64, int64, int64, error) {
   543  	s := strings.Split(ratio, "/")
   544  	if len(s) != params.RewardSliceCount {
   545  		logger.Error("Invalid ratio format", "ratio", ratio)
   546  		return 0, 0, 0, 0, errInvalidFormat
   547  	}
   548  	cn, err1 := strconv.ParseInt(s[0], 10, 64)
   549  	kff, err2 := strconv.ParseInt(s[1], 10, 64)
   550  	kcf, err3 := strconv.ParseInt(s[2], 10, 64)
   551  
   552  	if err1 != nil || err2 != nil || err3 != nil {
   553  		logger.Error("Could not parse ratio", "ratio", ratio)
   554  		return 0, 0, 0, 0, errParsingRatio
   555  	}
   556  	return cn, kff, kcf, cn + kff + kcf, nil
   557  }
   558  
   559  // parseRewardKip82Ratio parses string `kip82ratio` into ints
   560  func parseRewardKip82Ratio(ratio string) (int64, int64, int64, error) {
   561  	s := strings.Split(ratio, "/")
   562  	if len(s) != params.RewardKip82SliceCount {
   563  		logger.Error("Invalid kip82ratio format", "ratio", ratio)
   564  		return 0, 0, 0, errInvalidFormat
   565  	}
   566  	proposer, err1 := strconv.ParseInt(s[0], 10, 64)
   567  	stakers, err2 := strconv.ParseInt(s[1], 10, 64)
   568  
   569  	if err1 != nil || err2 != nil {
   570  		logger.Error("Could not parse kip82ratio", "ratio", ratio)
   571  		return 0, 0, 0, errParsingRatio
   572  	}
   573  	return proposer, stakers, proposer + stakers, nil
   574  }
   575  
   576  func incrementRewardsMap(m map[common.Address]*big.Int, addr common.Address, amount *big.Int) {
   577  	_, ok := m[addr]
   578  	if !ok {
   579  		m[addr] = big.NewInt(0)
   580  	}
   581  
   582  	m[addr] = m[addr].Add(m[addr], amount)
   583  }
   584  
   585  // ecrecover extracts the Klaytn account address from a signed header.
   586  func ecrecover(header *types.Header) (common.Address, error) {
   587  	// Retrieve the signature from the header extra-data
   588  	istanbulExtra, err := types.ExtractIstanbulExtra(header)
   589  	if err != nil {
   590  		return common.Address{}, nil
   591  	}
   592  
   593  	sigHash, err := sigHash(header)
   594  	if err != nil {
   595  		return common.Address{}, err
   596  	}
   597  	addr, err := istanbul.GetSignatureAddress(sigHash.Bytes(), istanbulExtra.Seal)
   598  	if err != nil {
   599  		return addr, err
   600  	}
   601  	return addr, nil
   602  }
   603  
   604  func sigHash(header *types.Header) (hash common.Hash, err error) {
   605  	hasher := sha3.NewKeccak256()
   606  
   607  	// Clean seal is required for calculating proposer seal.
   608  	if err := rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false)); err != nil {
   609  		logger.Error("fail to encode", "err", err)
   610  		return common.Hash{}, err
   611  	}
   612  	hasher.Sum(hash[:0])
   613  	return hash, nil
   614  }