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  }