github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/reward/reward_actor.go (about) 1 package reward 2 3 import ( 4 "github.com/filecoin-project/go-state-types/abi" 5 "github.com/filecoin-project/go-state-types/big" 6 "github.com/filecoin-project/go-state-types/cbor" 7 "github.com/filecoin-project/go-state-types/exitcode" 8 rtt "github.com/filecoin-project/go-state-types/rt" 9 reward0 "github.com/filecoin-project/specs-actors/actors/builtin/reward" 10 "github.com/ipfs/go-cid" 11 12 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 13 "github.com/filecoin-project/specs-actors/v4/actors/runtime" 14 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" 15 ) 16 17 // PenaltyMultiplier is the factor miner penaltys are scaled up by 18 const PenaltyMultiplier = 3 19 20 type Actor struct{} 21 22 func (a Actor) Exports() []interface{} { 23 return []interface{}{ 24 builtin.MethodConstructor: a.Constructor, 25 2: a.AwardBlockReward, 26 3: a.ThisEpochReward, 27 4: a.UpdateNetworkKPI, 28 } 29 } 30 31 func (a Actor) Code() cid.Cid { 32 return builtin.RewardActorCodeID 33 } 34 35 func (a Actor) IsSingleton() bool { 36 return true 37 } 38 39 func (a Actor) State() cbor.Er { 40 return new(State) 41 } 42 43 var _ runtime.VMActor = Actor{} 44 45 func (a Actor) Constructor(rt runtime.Runtime, currRealizedPower *abi.StoragePower) *abi.EmptyValue { 46 rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 47 48 if currRealizedPower == nil { 49 rt.Abortf(exitcode.ErrIllegalArgument, "argument should not be nil") 50 return nil // linter does not understand abort exiting 51 } 52 st := ConstructState(*currRealizedPower) 53 rt.StateCreate(st) 54 return nil 55 } 56 57 //type AwardBlockRewardParams struct { 58 // Miner address.Address 59 // Penalty abi.TokenAmount // penalty for including bad messages in a block, >= 0 60 // GasReward abi.TokenAmount // gas reward from all gas fees in a block, >= 0 61 // WinCount int64 // number of reward units won, > 0 62 //} 63 type AwardBlockRewardParams = reward0.AwardBlockRewardParams 64 65 // Awards a reward to a block producer. 66 // This method is called only by the system actor, implicitly, as the last message in the evaluation of a block. 67 // The system actor thus computes the parameters and attached value. 68 // 69 // The reward includes two components: 70 // - the epoch block reward, computed and paid from the reward actor's balance, 71 // - the block gas reward, expected to be transferred to the reward actor with this invocation. 72 // 73 // The reward is reduced before the residual is credited to the block producer, by: 74 // - a penalty amount, provided as a parameter, which is burnt, 75 func (a Actor) AwardBlockReward(rt runtime.Runtime, params *AwardBlockRewardParams) *abi.EmptyValue { 76 rt.ValidateImmediateCallerIs(builtin.SystemActorAddr) 77 priorBalance := rt.CurrentBalance() 78 if params.Penalty.LessThan(big.Zero()) { 79 rt.Abortf(exitcode.ErrIllegalArgument, "negative penalty %v", params.Penalty) 80 } 81 if params.GasReward.LessThan(big.Zero()) { 82 rt.Abortf(exitcode.ErrIllegalArgument, "negative gas reward %v", params.GasReward) 83 } 84 if priorBalance.LessThan(params.GasReward) { 85 rt.Abortf(exitcode.ErrIllegalState, "actor current balance %v insufficient to pay gas reward %v", 86 priorBalance, params.GasReward) 87 } 88 if params.WinCount <= 0 { 89 rt.Abortf(exitcode.ErrIllegalArgument, "invalid win count %d", params.WinCount) 90 } 91 92 minerAddr, ok := rt.ResolveAddress(params.Miner) 93 if !ok { 94 rt.Abortf(exitcode.ErrNotFound, "failed to resolve given owner address") 95 } 96 // The miner penalty is scaled up by a factor of PenaltyMultiplier 97 penalty := big.Mul(big.NewInt(PenaltyMultiplier), params.Penalty) 98 totalReward := big.Zero() 99 var st State 100 rt.StateTransaction(&st, func() { 101 blockReward := big.Mul(st.ThisEpochReward, big.NewInt(params.WinCount)) 102 blockReward = big.Div(blockReward, big.NewInt(builtin.ExpectedLeadersPerEpoch)) 103 totalReward = big.Add(blockReward, params.GasReward) 104 currBalance := rt.CurrentBalance() 105 if totalReward.GreaterThan(currBalance) { 106 rt.Log(rtt.WARN, "reward actor balance %d below totalReward expected %d, paying out rest of balance", currBalance, totalReward) 107 totalReward = currBalance 108 109 blockReward = big.Sub(totalReward, params.GasReward) 110 // Since we have already asserted the balance is greater than gas reward blockReward is >= 0 111 builtin.RequireState(rt, blockReward.GreaterThanEqual(big.Zero()), "programming error, block reward %v below zero", blockReward) 112 } 113 st.TotalStoragePowerReward = big.Add(st.TotalStoragePowerReward, blockReward) 114 }) 115 116 builtin.RequireState(rt, totalReward.LessThanEqual(priorBalance), "reward %v exceeds balance %v", totalReward, priorBalance) 117 118 // if this fails, we can assume the miner is responsible and avoid failing here. 119 rewardParams := builtin.ApplyRewardParams{ 120 Reward: totalReward, 121 Penalty: penalty, 122 } 123 code := rt.Send(minerAddr, builtin.MethodsMiner.ApplyRewards, &rewardParams, totalReward, &builtin.Discard{}) 124 if !code.IsSuccess() { 125 rt.Log(rtt.ERROR, "failed to send ApplyRewards call to the miner actor with funds: %v, code: %v", totalReward, code) 126 code := rt.Send(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, totalReward, &builtin.Discard{}) 127 if !code.IsSuccess() { 128 rt.Log(rtt.ERROR, "failed to send unsent reward to the burnt funds actor, code: %v", code) 129 } 130 } 131 132 return nil 133 } 134 135 // Changed since v0: 136 // - removed ThisEpochReward (unsmoothed) 137 type ThisEpochRewardReturn struct { 138 ThisEpochRewardSmoothed smoothing.FilterEstimate 139 ThisEpochBaselinePower abi.StoragePower 140 } 141 142 // The award value used for the current epoch, updated at the end of an epoch 143 // through cron tick. In the case previous epochs were null blocks this 144 // is the reward value as calculated at the last non-null epoch. 145 func (a Actor) ThisEpochReward(rt runtime.Runtime, _ *abi.EmptyValue) *ThisEpochRewardReturn { 146 rt.ValidateImmediateCallerAcceptAny() 147 148 var st State 149 rt.StateReadonly(&st) 150 return &ThisEpochRewardReturn{ 151 ThisEpochRewardSmoothed: st.ThisEpochRewardSmoothed, 152 ThisEpochBaselinePower: st.ThisEpochBaselinePower, 153 } 154 } 155 156 // Called at the end of each epoch by the power actor (in turn by its cron hook). 157 // This is only invoked for non-empty tipsets, but catches up any number of null 158 // epochs to compute the next epoch reward. 159 func (a Actor) UpdateNetworkKPI(rt runtime.Runtime, currRealizedPower *abi.StoragePower) *abi.EmptyValue { 160 rt.ValidateImmediateCallerIs(builtin.StoragePowerActorAddr) 161 if currRealizedPower == nil { 162 rt.Abortf(exitcode.ErrIllegalArgument, "argument should not be nil") 163 } 164 165 var st State 166 rt.StateTransaction(&st, func() { 167 prev := st.Epoch 168 // if there were null runs catch up the computation until 169 // st.Epoch == rt.CurrEpoch() 170 for st.Epoch < rt.CurrEpoch() { 171 // Update to next epoch to process null rounds 172 st.updateToNextEpoch(*currRealizedPower) 173 } 174 175 st.updateToNextEpochWithReward(*currRealizedPower) 176 // only update smoothed estimates after updating reward and epoch 177 st.updateSmoothedEstimates(st.Epoch - prev) 178 }) 179 return nil 180 }