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