github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/reward/reward_test.go (about)

     1  package reward_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	address "github.com/filecoin-project/go-address"
     7  	"github.com/filecoin-project/go-state-types/abi"
     8  	"github.com/filecoin-project/go-state-types/big"
     9  	"github.com/filecoin-project/go-state-types/exitcode"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    14  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/reward"
    15  	"github.com/filecoin-project/specs-actors/v4/support/mock"
    16  	tutil "github.com/filecoin-project/specs-actors/v4/support/testing"
    17  )
    18  
    19  func TestExports(t *testing.T) {
    20  	mock.CheckActorExports(t, reward.Actor{})
    21  }
    22  
    23  const EpochZeroReward = "36266264293777134739"
    24  
    25  func TestConstructor(t *testing.T) {
    26  	actor := rewardHarness{reward.Actor{}, t}
    27  
    28  	t.Run("construct with 0 power", func(t *testing.T) {
    29  		rt := mock.NewBuilder(builtin.RewardActorAddr).
    30  			WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID).
    31  			Build(t)
    32  		startRealizedPower := abi.NewStoragePower(0)
    33  		actor.constructAndVerify(rt, &startRealizedPower)
    34  		st := getState(rt)
    35  		assert.Equal(t, abi.ChainEpoch(0), st.Epoch)
    36  		assert.Equal(t, abi.NewStoragePower(0), st.CumsumRealized)
    37  		assert.Equal(t, big.MustFromString(EpochZeroReward), st.ThisEpochReward)
    38  		epochZeroBaseline := big.Sub(reward.BaselineInitialValue, big.NewInt(1)) // account for rounding error of one byte during construction
    39  		assert.Equal(t, epochZeroBaseline, st.ThisEpochBaselinePower)
    40  		assert.Equal(t, reward.BaselineInitialValue, st.EffectiveBaselinePower)
    41  	})
    42  	t.Run("construct with less power than baseline", func(t *testing.T) {
    43  		rt := mock.NewBuilder(builtin.RewardActorAddr).
    44  			WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID).
    45  			Build(t)
    46  		startRealizedPower := big.Lsh(abi.NewStoragePower(1), 39)
    47  		actor.constructAndVerify(rt, &startRealizedPower)
    48  		st := getState(rt)
    49  		assert.Equal(t, abi.ChainEpoch(0), st.Epoch)
    50  		assert.Equal(t, startRealizedPower, st.CumsumRealized)
    51  
    52  		assert.NotEqual(t, big.Zero(), st.ThisEpochReward)
    53  	})
    54  	t.Run("construct with more power than baseline", func(t *testing.T) {
    55  		rt := mock.NewBuilder(builtin.RewardActorAddr).
    56  			WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID).
    57  			Build(t)
    58  		startRealizedPower := reward.BaselineInitialValue
    59  		actor.constructAndVerify(rt, &startRealizedPower)
    60  		st := getState(rt)
    61  		rwrd := st.ThisEpochReward
    62  
    63  		// start with 2x power
    64  		rt = mock.NewBuilder(builtin.RewardActorAddr).
    65  			WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID).
    66  			Build(t)
    67  		startRealizedPower = big.Mul(reward.BaselineInitialValue, big.NewInt(2))
    68  		actor.constructAndVerify(rt, &startRealizedPower)
    69  		newSt := getState(rt)
    70  		// Reward value is the same; realized power impact on reward is capped at baseline
    71  		assert.Equal(t, rwrd, newSt.ThisEpochReward)
    72  	})
    73  
    74  }
    75  
    76  func TestAwardBlockReward(t *testing.T) {
    77  	actor := rewardHarness{reward.Actor{}, t}
    78  	winner := tutil.NewIDAddr(t, 1000)
    79  	builder := mock.NewBuilder(builtin.RewardActorAddr).
    80  		WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID)
    81  
    82  	t.Run("rejects gas reward exceeding balance", func(t *testing.T) {
    83  		rt := builder.Build(t)
    84  		startRealizedPower := abi.NewStoragePower(0)
    85  		actor.constructAndVerify(rt, &startRealizedPower)
    86  
    87  		rt.SetBalance(abi.NewTokenAmount(9))
    88  		rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
    89  		rt.ExpectAbort(exitcode.ErrIllegalState, func() {
    90  			gasReward := big.NewInt(10)
    91  			actor.awardBlockReward(rt, winner, big.Zero(), gasReward, 1, big.Zero())
    92  		})
    93  	})
    94  
    95  	t.Run("rejects negative penalty or reward", func(t *testing.T) {
    96  		rt := builder.Build(t)
    97  		startRealizedPower := abi.NewStoragePower(0)
    98  		actor.constructAndVerify(rt, &startRealizedPower)
    99  
   100  		rt.SetBalance(abi.NewTokenAmount(1e18))
   101  		rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   102  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   103  			penalty := big.NewInt(-1)
   104  			actor.awardBlockReward(rt, winner, penalty, big.Zero(), 1, big.Zero())
   105  		})
   106  		rt.Reset()
   107  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   108  			gasReward := big.NewInt(-1)
   109  			actor.awardBlockReward(rt, winner, big.Zero(), gasReward, 1, big.Zero())
   110  		})
   111  	})
   112  
   113  	t.Run("rejects zero wincount", func(t *testing.T) {
   114  		rt := builder.Build(t)
   115  		startRealizedPower := abi.NewStoragePower(0)
   116  		actor.constructAndVerify(rt, &startRealizedPower)
   117  
   118  		rt.SetBalance(abi.NewTokenAmount(1e18))
   119  		rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   120  		rt.ExpectAbort(exitcode.ErrIllegalArgument, func() {
   121  			actor.awardBlockReward(rt, winner, big.Zero(), big.Zero(), 0, big.Zero())
   122  		})
   123  		rt.Reset()
   124  	})
   125  
   126  	t.Run("pays reward and tracks penalty", func(t *testing.T) {
   127  		rt := builder.Build(t)
   128  		startRealizedPower := abi.NewStoragePower(0)
   129  		actor.constructAndVerify(rt, &startRealizedPower)
   130  
   131  		rt.SetBalance(big.Mul(big.NewInt(1e9), abi.NewTokenAmount(1e18)))
   132  		rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   133  		penalty := big.NewInt(100)
   134  		gasReward := big.NewInt(200)
   135  		expectedReward := big.Sum(big.Div(big.MustFromString(EpochZeroReward), big.NewInt(5)), gasReward)
   136  		actor.awardBlockReward(rt, winner, penalty, gasReward, 1, expectedReward)
   137  		rt.Reset()
   138  	})
   139  
   140  	t.Run("pays out current balance when reward exceeds total balance", func(t *testing.T) {
   141  		rt := builder.Build(t)
   142  		startRealizedPower := abi.NewStoragePower(1)
   143  		actor.constructAndVerify(rt, &startRealizedPower)
   144  
   145  		// Total reward is a huge number, upon writing ~1e18, so 300 should be way less
   146  		smallReward := abi.NewTokenAmount(300)
   147  		penalty := abi.NewTokenAmount(100)
   148  		rt.SetBalance(smallReward)
   149  		rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   150  
   151  		minerPenalty := big.Mul(big.NewInt(reward.PenaltyMultiplier), penalty)
   152  		expectedParams := builtin.ApplyRewardParams{Reward: smallReward, Penalty: minerPenalty}
   153  		rt.ExpectSend(winner, builtin.MethodsMiner.ApplyRewards, &expectedParams, smallReward, nil, 0)
   154  		rt.Call(actor.AwardBlockReward, &reward.AwardBlockRewardParams{
   155  			Miner:     winner,
   156  			Penalty:   penalty,
   157  			GasReward: big.Zero(),
   158  			WinCount:  1,
   159  		})
   160  		rt.Verify()
   161  	})
   162  
   163  	t.Run("TotalStoragePowerReward tracks correctly", func(t *testing.T) {
   164  		rt := builder.Build(t)
   165  		startRealizedPower := abi.NewStoragePower(1)
   166  		actor.constructAndVerify(rt, &startRealizedPower)
   167  		miner := tutil.NewIDAddr(t, 1000)
   168  
   169  		st := getState(rt)
   170  		assert.Equal(t, big.Zero(), st.TotalStoragePowerReward)
   171  		st.ThisEpochReward = abi.NewTokenAmount(5000)
   172  		rt.ReplaceState(st)
   173  		// enough balance to pay 3 full rewards and one partial
   174  		totalPayout := abi.NewTokenAmount(3500)
   175  		rt.SetBalance(totalPayout)
   176  
   177  		// award normalized by expected leaders is 1000
   178  		actor.awardBlockReward(rt, miner, big.Zero(), big.Zero(), 1, big.NewInt(1000))
   179  		actor.awardBlockReward(rt, miner, big.Zero(), big.Zero(), 1, big.NewInt(1000))
   180  		actor.awardBlockReward(rt, miner, big.Zero(), big.Zero(), 1, big.NewInt(1000))
   181  		actor.awardBlockReward(rt, miner, big.Zero(), big.Zero(), 1, big.NewInt(500)) // partial payout when balance below reward
   182  
   183  		newState := getState(rt)
   184  		assert.Equal(t, totalPayout, newState.TotalStoragePowerReward)
   185  
   186  	})
   187  
   188  	t.Run("funds are sent to the burnt funds actor if sending locked funds to miner fails", func(t *testing.T) {
   189  		rt := builder.Build(t)
   190  		startRealizedPower := abi.NewStoragePower(1)
   191  		actor.constructAndVerify(rt, &startRealizedPower)
   192  		miner := tutil.NewIDAddr(t, 1000)
   193  		st := getState(rt)
   194  		assert.Equal(t, big.Zero(), st.TotalStoragePowerReward)
   195  		st.ThisEpochReward = abi.NewTokenAmount(5000)
   196  		rt.ReplaceState(st)
   197  		// enough balance to pay 3 full rewards and one partial
   198  		totalPayout := abi.NewTokenAmount(3500)
   199  		rt.SetBalance(totalPayout)
   200  
   201  		rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   202  		expectedReward := big.NewInt(1000)
   203  		penalty := big.Zero()
   204  		expectedParams := builtin.ApplyRewardParams{Reward: expectedReward, Penalty: penalty}
   205  		rt.ExpectSend(miner, builtin.MethodsMiner.ApplyRewards, &expectedParams, expectedReward, nil, exitcode.ErrForbidden)
   206  		rt.ExpectSend(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, expectedReward, nil, exitcode.Ok)
   207  
   208  		rt.Call(actor.AwardBlockReward, &reward.AwardBlockRewardParams{
   209  			Miner:     miner,
   210  			Penalty:   big.Zero(),
   211  			GasReward: big.Zero(),
   212  			WinCount:  1,
   213  		})
   214  
   215  		rt.Verify()
   216  	})
   217  }
   218  
   219  func TestThisEpochReward(t *testing.T) {
   220  	t.Run("successfully fetch reward for this epoch", func(t *testing.T) {
   221  		actor := rewardHarness{reward.Actor{}, t}
   222  		builder := mock.NewBuilder(builtin.RewardActorAddr).
   223  			WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID)
   224  		rt := builder.Build(t)
   225  		power := abi.NewStoragePower(1 << 50)
   226  		actor.constructAndVerify(rt, &power)
   227  
   228  		resp := actor.thisEpochReward(rt)
   229  		st := getState(rt)
   230  
   231  		require.EqualValues(t, st.ThisEpochBaselinePower, resp.ThisEpochBaselinePower)
   232  		require.EqualValues(t, st.ThisEpochRewardSmoothed, resp.ThisEpochRewardSmoothed)
   233  	})
   234  }
   235  
   236  func TestSuccessiveKPIUpdates(t *testing.T) {
   237  	actor := rewardHarness{reward.Actor{}, t}
   238  	builder := mock.NewBuilder(builtin.RewardActorAddr).
   239  		WithCaller(builtin.SystemActorAddr, builtin.SystemActorCodeID)
   240  	rt := builder.Build(t)
   241  	power := abi.NewStoragePower(1 << 50)
   242  	actor.constructAndVerify(rt, &power)
   243  
   244  	rt.SetEpoch(abi.ChainEpoch(1))
   245  	actor.updateNetworkKPI(rt, &power)
   246  
   247  	rt.SetEpoch(abi.ChainEpoch(2))
   248  	actor.updateNetworkKPI(rt, &power)
   249  
   250  	rt.SetEpoch(abi.ChainEpoch(3))
   251  	actor.updateNetworkKPI(rt, &power)
   252  
   253  }
   254  
   255  type rewardHarness struct {
   256  	reward.Actor
   257  	t testing.TB
   258  }
   259  
   260  func (h *rewardHarness) constructAndVerify(rt *mock.Runtime, currRawPower *abi.StoragePower) {
   261  	rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   262  	ret := rt.Call(h.Constructor, currRawPower)
   263  	assert.Nil(h.t, ret)
   264  	rt.Verify()
   265  
   266  }
   267  
   268  func (h *rewardHarness) updateNetworkKPI(rt *mock.Runtime, currRawPower *abi.StoragePower) {
   269  	rt.SetCaller(builtin.StoragePowerActorAddr, builtin.StoragePowerActorCodeID)
   270  	rt.ExpectValidateCallerAddr(builtin.StoragePowerActorAddr)
   271  	ret := rt.Call(h.UpdateNetworkKPI, currRawPower)
   272  	assert.Nil(h.t, ret)
   273  	rt.Verify()
   274  }
   275  
   276  func (h *rewardHarness) awardBlockReward(rt *mock.Runtime, miner address.Address, penalty, gasReward abi.TokenAmount, winCount int64, expectedPayment abi.TokenAmount) {
   277  	rt.ExpectValidateCallerAddr(builtin.SystemActorAddr)
   278  	// expect penalty multiplier
   279  	minerPenalty := big.Mul(big.NewInt(reward.PenaltyMultiplier), penalty)
   280  	expectedParams := builtin.ApplyRewardParams{Reward: expectedPayment, Penalty: minerPenalty}
   281  	rt.ExpectSend(miner, builtin.MethodsMiner.ApplyRewards, &expectedParams, expectedPayment, nil, 0)
   282  
   283  	rt.Call(h.AwardBlockReward, &reward.AwardBlockRewardParams{
   284  		Miner:     miner,
   285  		Penalty:   penalty,
   286  		GasReward: gasReward,
   287  		WinCount:  winCount,
   288  	})
   289  	rt.Verify()
   290  }
   291  
   292  func (h *rewardHarness) thisEpochReward(rt *mock.Runtime) *reward.ThisEpochRewardReturn {
   293  	rt.ExpectValidateCallerAny()
   294  
   295  	ret := rt.Call(h.ThisEpochReward, nil)
   296  	rt.Verify()
   297  
   298  	resp, ok := ret.(*reward.ThisEpochRewardReturn)
   299  	require.True(h.t, ok)
   300  	return resp
   301  }
   302  
   303  func getState(rt *mock.Runtime) *reward.State {
   304  	var st reward.State
   305  	rt.GetState(&st)
   306  	return &st
   307  }