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

     1  package miner_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/filecoin-project/go-state-types/abi"
     7  	"github.com/filecoin-project/go-state-types/big"
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    11  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/miner"
    12  	"github.com/filecoin-project/specs-actors/v4/actors/util/smoothing"
    13  )
    14  
    15  // Test termination fee
    16  func TestPledgePenaltyForTermination(t *testing.T) {
    17  	epochTargetReward := abi.NewTokenAmount(1 << 50)
    18  	qaSectorPower := abi.NewStoragePower(1 << 36)
    19  	networkQAPower := abi.NewStoragePower(1 << 50)
    20  
    21  	rewardEstimate := smoothing.TestingConstantEstimate(epochTargetReward)
    22  	powerEstimate := smoothing.TestingConstantEstimate(networkQAPower)
    23  
    24  	undeclaredPenalty := miner.PledgePenaltyForTerminationLowerBound(rewardEstimate, powerEstimate, qaSectorPower)
    25  	bigInitialPledgeFactor := big.NewInt(int64(miner.InitialPledgeFactor))
    26  	bigLifetimeCap := big.NewInt(int64(miner.TerminationLifetimeCap))
    27  
    28  	t.Run("when undeclared fault fee exceeds expected reward, returns undeclaraed fault fee", func(t *testing.T) {
    29  		// small pledge and means undeclared penalty will be bigger
    30  		initialPledge := abi.NewTokenAmount(1 << 10)
    31  		dayReward := big.Div(initialPledge, bigInitialPledgeFactor)
    32  		twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor)
    33  		sectorAge := 20 * abi.ChainEpoch(builtin.EpochsInDay)
    34  
    35  		fee := miner.PledgePenaltyForTermination(dayReward, sectorAge, twentyDayReward, powerEstimate, qaSectorPower, rewardEstimate, big.Zero(), 0)
    36  
    37  		assert.Equal(t, undeclaredPenalty, fee)
    38  	})
    39  
    40  	t.Run("when expected reward exceeds undeclared fault fee, returns expected reward", func(t *testing.T) {
    41  		// initialPledge equal to undeclaredPenalty guarantees expected reward is greater
    42  		initialPledge := undeclaredPenalty
    43  		dayReward := big.Div(initialPledge, bigInitialPledgeFactor)
    44  		twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor)
    45  		sectorAgeInDays := int64(20)
    46  		sectorAge := abi.ChainEpoch(sectorAgeInDays * builtin.EpochsInDay)
    47  
    48  		fee := miner.PledgePenaltyForTermination(dayReward, sectorAge, twentyDayReward, powerEstimate, qaSectorPower, rewardEstimate, big.Zero(), 0)
    49  
    50  		// expect fee to be pledge + br * age * factor where br = pledge/initialPledgeFactor
    51  		expectedFee := big.Add(
    52  			initialPledge,
    53  			big.Div(
    54  				big.Product(initialPledge, big.NewInt(sectorAgeInDays), miner.TerminationRewardFactor.Numerator),
    55  				big.Product(bigInitialPledgeFactor, miner.TerminationRewardFactor.Denominator)))
    56  		assert.Equal(t, expectedFee, fee)
    57  	})
    58  
    59  	t.Run("sector age is capped", func(t *testing.T) {
    60  		initialPledge := undeclaredPenalty
    61  		dayReward := big.Div(initialPledge, bigInitialPledgeFactor)
    62  		twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor)
    63  		sectorAge := abi.ChainEpoch(500 * builtin.EpochsInDay)
    64  
    65  		fee := miner.PledgePenaltyForTermination(dayReward, sectorAge, twentyDayReward, powerEstimate, qaSectorPower, rewardEstimate, big.Zero(), 0)
    66  
    67  		// expect fee to be pledge * br * age-cap * factor where br = pledge/initialPledgeFactor
    68  		expectedFee := big.Add(
    69  			initialPledge,
    70  			big.Div(
    71  				big.Product(initialPledge, bigLifetimeCap, miner.TerminationRewardFactor.Numerator),
    72  				big.Product(bigInitialPledgeFactor, miner.TerminationRewardFactor.Denominator)))
    73  		assert.Equal(t, expectedFee, fee)
    74  	})
    75  
    76  	t.Run("fee for replacement = fee for original sector when power, BR are unchanged", func(t *testing.T) {
    77  		// initialPledge equal to undeclaredPenalty guarantees expected reward is greater
    78  		initialPledge := undeclaredPenalty
    79  		dayReward := big.Div(initialPledge, bigInitialPledgeFactor)
    80  		twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor)
    81  		sectorAge := abi.ChainEpoch(20 * builtin.EpochsInDay)
    82  		replacementAge := abi.ChainEpoch(2 * builtin.EpochsInDay)
    83  
    84  		// use low power, so we don't test SP=SP
    85  		power := big.NewInt(1)
    86  
    87  		// fee for old sector if had terminated when it was replaced
    88  		unreplacedFee := miner.PledgePenaltyForTermination(dayReward, sectorAge, twentyDayReward, powerEstimate, power, rewardEstimate, big.Zero(), 0)
    89  
    90  		// actual fee including replacement parameters
    91  		actualFee := miner.PledgePenaltyForTermination(dayReward, replacementAge, twentyDayReward, powerEstimate, power, rewardEstimate, dayReward, sectorAge-replacementAge)
    92  
    93  		assert.Equal(t, unreplacedFee, actualFee)
    94  	})
    95  
    96  	t.Run("fee for replacement = fee for same sector without replacement after lifetime cap", func(t *testing.T) {
    97  		// initialPledge equal to undeclaredPenalty guarantees expected reward is greater
    98  		initialPledge := undeclaredPenalty
    99  		dayReward := big.Div(initialPledge, bigInitialPledgeFactor)
   100  		twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor)
   101  		sectorAge := abi.ChainEpoch(20 * builtin.EpochsInDay)
   102  		replacementAge := abi.ChainEpoch(miner.TerminationLifetimeCap+1) * builtin.EpochsInDay
   103  
   104  		// use low power, so we don't test SP=SP
   105  		power := big.NewInt(1)
   106  
   107  		// fee for new sector with no replacement
   108  		noReplace := miner.PledgePenaltyForTermination(dayReward, replacementAge, twentyDayReward, powerEstimate, power, rewardEstimate, big.Zero(), 0)
   109  
   110  		// actual fee including replacement parameters
   111  		withReplace := miner.PledgePenaltyForTermination(dayReward, replacementAge, twentyDayReward, powerEstimate, power, rewardEstimate, dayReward, sectorAge)
   112  
   113  		assert.Equal(t, noReplace, withReplace)
   114  	})
   115  
   116  	t.Run("charges for replaced sector at replaced sector day rate", func(t *testing.T) {
   117  		// initialPledge equal to undeclaredPenalty guarantees expected reward is greater
   118  		initialPledge := undeclaredPenalty
   119  		dayReward := big.Div(initialPledge, bigInitialPledgeFactor)
   120  		oldDayReward := big.Mul(big.NewInt(2), dayReward)
   121  		twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor)
   122  		oldSectorAgeInDays := int64(20)
   123  		oldSectorAge := abi.ChainEpoch(oldSectorAgeInDays * builtin.EpochsInDay)
   124  		replacementAgeInDays := int64(15)
   125  		replacementAge := abi.ChainEpoch(replacementAgeInDays * builtin.EpochsInDay)
   126  
   127  		// use low power, so termination fee exceeds SP
   128  		power := big.NewInt(1)
   129  
   130  		oldPenalty := big.Div(
   131  			big.Product(oldDayReward, big.NewInt(oldSectorAgeInDays), miner.TerminationRewardFactor.Numerator),
   132  			miner.TerminationRewardFactor.Denominator,
   133  		)
   134  		newPenalty := big.Div(
   135  			big.Product(dayReward, big.NewInt(replacementAgeInDays), miner.TerminationRewardFactor.Numerator),
   136  			miner.TerminationRewardFactor.Denominator,
   137  		)
   138  		expectedFee := big.Sum(twentyDayReward, oldPenalty, newPenalty)
   139  
   140  		fee := miner.PledgePenaltyForTermination(dayReward, replacementAge, twentyDayReward, powerEstimate, power, rewardEstimate, oldDayReward, oldSectorAge)
   141  
   142  		assert.Equal(t, expectedFee, fee)
   143  	})
   144  }
   145  
   146  func TestNegativeBRClamp(t *testing.T) {
   147  	epochTargetReward := abi.NewTokenAmount(1 << 50)
   148  	qaSectorPower := abi.NewStoragePower(1 << 36)
   149  	networkQAPower := abi.NewStoragePower(1 << 10)
   150  	powerRateOfChange := abi.NewStoragePower(1 << 10).Neg()
   151  	rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero())
   152  	powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange)
   153  
   154  	fourBR := miner.ExpectedRewardForPower(rewardEstimate, powerEstimate, qaSectorPower, abi.ChainEpoch(4))
   155  	assert.Equal(t, big.Zero(), fourBR)
   156  }
   157  
   158  func TestContinuedFault(t *testing.T) {
   159  	t.Run("zero power means zero fault penalty", func(t *testing.T) {
   160  		epochTargetReward := abi.NewTokenAmount(1 << 50)
   161  		zeroQAPower := abi.NewStoragePower(0)
   162  		networkQAPower := abi.NewStoragePower(1 << 10)
   163  		powerRateOfChange := abi.NewStoragePower(1 << 10)
   164  		rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero())
   165  		powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange)
   166  
   167  		penaltyForZeroPowerFaulted := miner.PledgePenaltyForContinuedFault(rewardEstimate, powerEstimate, zeroQAPower)
   168  		assert.Equal(t, big.Zero(), penaltyForZeroPowerFaulted)
   169  	})
   170  }
   171  
   172  func TestExpectedRewardForPowerClamptedAtAttoFIL(t *testing.T) {
   173  	t.Run("expected zero valued BR clamped at 1 attofil", func(t *testing.T) {
   174  		epochTargetReward := abi.NewTokenAmount(1 << 50)
   175  		zeroQAPower := abi.NewStoragePower(0)
   176  		networkQAPower := abi.NewStoragePower(1 << 10)
   177  		powerRateOfChange := abi.NewStoragePower(1 << 10)
   178  		rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero())
   179  		powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange)
   180  
   181  		brClamped := miner.ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, powerEstimate, zeroQAPower, abi.ChainEpoch(1))
   182  		assert.Equal(t, big.NewInt(1), brClamped)
   183  	})
   184  
   185  	t.Run("expected negative valued BR clamped at 1 atto FIL", func(t *testing.T) {
   186  		epochTargetReward := abi.NewTokenAmount(1 << 50)
   187  		qaSectorPower := abi.NewStoragePower(1 << 36)
   188  		networkQAPower := abi.NewStoragePower(1 << 10)
   189  		powerRateOfChange := abi.NewStoragePower(1 << 10).Neg()
   190  		rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero())
   191  		powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange)
   192  
   193  		fourBRClamped := miner.ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, powerEstimate, qaSectorPower, abi.ChainEpoch(4))
   194  		assert.Equal(t, big.NewInt(1), fourBRClamped)
   195  	})
   196  
   197  }
   198  
   199  func TestPrecommitDepositAndInitialPledgePostiive(t *testing.T) {
   200  	epochTargetReward := abi.NewTokenAmount(0) // zero reward so IP Base unclamped is 0
   201  	qaSectorPower := abi.NewStoragePower(1 << 36)
   202  	networkQAPower := abi.NewStoragePower(1 << 10)
   203  	baselinePower := networkQAPower
   204  	powerRateOfChange := abi.NewStoragePower(1 << 10)
   205  	rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero())
   206  	powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange)
   207  	circulatingSupply := abi.NewTokenAmount(0)
   208  	t.Run("IP is clamped at 1 attofil", func(t *testing.T) {
   209  		ip := miner.InitialPledgeForPower(qaSectorPower, baselinePower, rewardEstimate, powerEstimate, circulatingSupply)
   210  		assert.Equal(t, abi.NewTokenAmount(1), ip)
   211  	})
   212  	t.Run("PCD is clamped at 1 attoFIL", func(t *testing.T) {
   213  		pcd := miner.PreCommitDepositForPower(rewardEstimate, powerEstimate, qaSectorPower)
   214  		assert.Equal(t, abi.NewTokenAmount(1), pcd)
   215  	})
   216  }