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 }