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 }