github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/test/commit_post_test.go (about)

     1  package test_test
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/filecoin-project/go-bitfield"
     9  	"github.com/filecoin-project/go-state-types/abi"
    10  	"github.com/filecoin-project/go-state-types/big"
    11  	"github.com/filecoin-project/go-state-types/exitcode"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    16  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/miner"
    17  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/power"
    18  	"github.com/filecoin-project/specs-actors/v4/actors/runtime/proof"
    19  	"github.com/filecoin-project/specs-actors/v4/actors/states"
    20  	"github.com/filecoin-project/specs-actors/v4/support/ipld"
    21  	tutil "github.com/filecoin-project/specs-actors/v4/support/testing"
    22  	vm "github.com/filecoin-project/specs-actors/v4/support/vm"
    23  )
    24  
    25  func TestCommitPoStFlow(t *testing.T) {
    26  	ctx := context.Background()
    27  	v := vm.NewVMWithSingletons(ctx, t, ipld.NewBlockStoreInMemory())
    28  	addrs := vm.CreateAccounts(ctx, t, v, 1, big.Mul(big.NewInt(10_000), big.NewInt(1e18)), 93837778)
    29  
    30  	minerBalance := big.Mul(big.NewInt(10_000), vm.FIL)
    31  	sealProof := abi.RegisteredSealProof_StackedDrg32GiBV1_1
    32  
    33  	// create miner
    34  	params := power.CreateMinerParams{
    35  		Owner:               addrs[0],
    36  		Worker:              addrs[0],
    37  		WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1,
    38  		Peer:                abi.PeerID("not really a peer id"),
    39  	}
    40  	ret := vm.ApplyOk(t, v, addrs[0], builtin.StoragePowerActorAddr, minerBalance, builtin.MethodsPower.CreateMiner, &params)
    41  
    42  	minerAddrs, ok := ret.(*power.CreateMinerReturn)
    43  	require.True(t, ok)
    44  
    45  	// advance vm so we can have seal randomness epoch in the past
    46  	v, err := v.WithEpoch(200)
    47  	require.NoError(t, err)
    48  
    49  	//
    50  	// precommit sector
    51  	//
    52  
    53  	sectorNumber := abi.SectorNumber(100)
    54  	sealedCid := tutil.MakeCID("100", &miner.SealedCIDPrefix)
    55  	sectorSize, err := sealProof.SectorSize()
    56  	require.NoError(t, err)
    57  
    58  	preCommitParams := miner.PreCommitSectorParams{
    59  		SealProof:     sealProof,
    60  		SectorNumber:  sectorNumber,
    61  		SealedCID:     sealedCid,
    62  		SealRandEpoch: v.GetEpoch() - 1,
    63  		DealIDs:       nil,
    64  		Expiration:    v.GetEpoch() + miner.MinSectorExpiration + miner.MaxProveCommitDuration[sealProof] + 100,
    65  	}
    66  	vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams)
    67  
    68  	// assert successful precommit invocation
    69  	vm.ExpectInvocation{
    70  		To:     minerAddrs.IDAddress,
    71  		Method: builtin.MethodsMiner.PreCommitSector,
    72  		Params: vm.ExpectObject(&preCommitParams),
    73  		SubInvocations: []vm.ExpectInvocation{
    74  			{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward},
    75  			{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower},
    76  			{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}},
    77  	}.Matches(t, v.Invocations()[0])
    78  
    79  	balances := vm.GetMinerBalances(t, v, minerAddrs.IDAddress)
    80  	assert.True(t, balances.PreCommitDeposit.GreaterThan(big.Zero()))
    81  
    82  	// find information about precommited sector
    83  	var minerState miner.State
    84  	err = v.GetState(minerAddrs.IDAddress, &minerState)
    85  	require.NoError(t, err)
    86  
    87  	precommit, found, err := minerState.GetPrecommittedSector(v.Store(), sectorNumber)
    88  	require.NoError(t, err)
    89  	require.True(t, found)
    90  
    91  	// advance time to max seal duration
    92  	proveTime := v.GetEpoch() + miner.MaxProveCommitDuration[sealProof]
    93  	v, dlInfo := vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime)
    94  
    95  	//
    96  	// overdue precommit
    97  	//
    98  
    99  	t.Run("missed prove commit results in precommit expiry", func(t *testing.T) {
   100  		// advanced one more deadline so precommit is late
   101  		tv, err := v.WithEpoch(dlInfo.Close)
   102  		require.NoError(t, err)
   103  
   104  		// run cron which should expire precommit
   105  		vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil)
   106  
   107  		vm.ExpectInvocation{
   108  			To:     builtin.CronActorAddr,
   109  			Method: builtin.MethodsCron.EpochTick,
   110  			SubInvocations: []vm.ExpectInvocation{
   111  				{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{
   112  					{To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.OnDeferredCronEvent, SubInvocations: []vm.ExpectInvocation{
   113  						{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward},
   114  						{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower},
   115  
   116  						// The call to burnt funds indicates the overdue precommit has been penalized
   117  						{To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend, Value: vm.ExpectAttoFil(precommit.PreCommitDeposit)},
   118  						// No re-enrollment of cron because burning of PCD discontinues miner cron scheduling
   119  					}},
   120  					//{To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.ConfirmSectorProofsValid},
   121  					{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI},
   122  				}},
   123  				{To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick},
   124  			},
   125  		}.Matches(t, tv.Invocations()[0])
   126  
   127  		// precommit deposit has been reset
   128  		balances := vm.GetMinerBalances(t, tv, minerAddrs.IDAddress)
   129  		assert.Equal(t, big.Zero(), balances.InitialPledge)
   130  		assert.Equal(t, big.Zero(), balances.PreCommitDeposit)
   131  
   132  		// no power is added
   133  		networkStats := vm.GetNetworkStats(t, tv)
   134  		assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted)
   135  		assert.Equal(t, big.Zero(), networkStats.TotalPledgeCollateral)
   136  		assert.Equal(t, big.Zero(), networkStats.TotalRawBytePower)
   137  		assert.Equal(t, big.Zero(), networkStats.TotalQualityAdjPower)
   138  	})
   139  
   140  	//
   141  	// prove and verify
   142  	//
   143  
   144  	// Prove commit sector after max seal duration
   145  	v, err = v.WithEpoch(proveTime)
   146  	require.NoError(t, err)
   147  	proveCommitParams := miner.ProveCommitSectorParams{
   148  		SectorNumber: sectorNumber,
   149  	}
   150  	vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitSector, &proveCommitParams)
   151  
   152  	vm.ExpectInvocation{
   153  		To:     minerAddrs.IDAddress,
   154  		Method: builtin.MethodsMiner.ProveCommitSector,
   155  		Params: vm.ExpectObject(&proveCommitParams),
   156  		SubInvocations: []vm.ExpectInvocation{
   157  			{To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.ComputeDataCommitment},
   158  			{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.SubmitPoRepForBulkVerify},
   159  		},
   160  	}.Matches(t, v.Invocations()[0])
   161  
   162  	// In the same epoch, trigger cron to validate prove commit
   163  	vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil)
   164  
   165  	vm.ExpectInvocation{
   166  		To:     builtin.CronActorAddr,
   167  		Method: builtin.MethodsCron.EpochTick,
   168  		SubInvocations: []vm.ExpectInvocation{
   169  			{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{
   170  				// expect confirm sector proofs valid because we prove committed,
   171  				// but not an on deferred cron event because this is not a deadline boundary
   172  				{To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.ConfirmSectorProofsValid, SubInvocations: []vm.ExpectInvocation{
   173  					{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward},
   174  					{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower},
   175  					{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal},
   176  				}},
   177  				{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI},
   178  			}},
   179  			{To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick},
   180  		},
   181  	}.Matches(t, v.Invocations()[1])
   182  
   183  	// precommit deposit is released, ipr is added
   184  	balances = vm.GetMinerBalances(t, v, minerAddrs.IDAddress)
   185  	assert.True(t, balances.InitialPledge.GreaterThan(big.Zero()))
   186  	assert.Equal(t, big.Zero(), balances.PreCommitDeposit)
   187  
   188  	// power is unproven so network stats are unchanged
   189  	networkStats := vm.GetNetworkStats(t, v)
   190  	assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted)
   191  	assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero()))
   192  
   193  	//
   194  	// Submit PoSt
   195  	//
   196  
   197  	// advance to proving period
   198  	dlInfo, pIdx, v := vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, sectorNumber)
   199  	err = v.GetState(minerAddrs.IDAddress, &minerState)
   200  	require.NoError(t, err)
   201  
   202  	sector, found, err := minerState.GetSector(v.Store(), sectorNumber)
   203  	require.NoError(t, err)
   204  	require.True(t, found)
   205  
   206  	t.Run("submit PoSt succeeds", func(t *testing.T) {
   207  		tv, err := v.WithEpoch(v.GetEpoch())
   208  		require.NoError(t, err)
   209  
   210  		// Submit PoSt
   211  		submitParams := miner.SubmitWindowedPoStParams{
   212  			Deadline: dlInfo.Index,
   213  			Partitions: []miner.PoStPartition{{
   214  				Index:   pIdx,
   215  				Skipped: bitfield.New(),
   216  			}},
   217  			Proofs: []proof.PoStProof{{
   218  				PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1,
   219  			}},
   220  			ChainCommitEpoch: dlInfo.Challenge,
   221  			ChainCommitRand:  []byte("not really random"),
   222  		}
   223  		vm.ApplyOk(t, tv, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams)
   224  
   225  		sectorPower := miner.PowerForSector(sectorSize, sector)
   226  		updatePowerParams := &power.UpdateClaimedPowerParams{
   227  			RawByteDelta:         sectorPower.Raw,
   228  			QualityAdjustedDelta: sectorPower.QA,
   229  		}
   230  
   231  		vm.ExpectInvocation{
   232  			To:     minerAddrs.IDAddress,
   233  			Method: builtin.MethodsMiner.SubmitWindowedPoSt,
   234  			Params: vm.ExpectObject(&submitParams),
   235  			SubInvocations: []vm.ExpectInvocation{
   236  				// This call to the power actor indicates power has been added for the sector
   237  				{
   238  					To:     builtin.StoragePowerActorAddr,
   239  					Method: builtin.MethodsPower.UpdateClaimedPower,
   240  					Params: vm.ExpectObject(updatePowerParams),
   241  				},
   242  			},
   243  		}.Matches(t, tv.Invocations()[0])
   244  
   245  		// miner still has initial pledge
   246  		balances = vm.GetMinerBalances(t, tv, minerAddrs.IDAddress)
   247  		assert.True(t, balances.InitialPledge.GreaterThan(big.Zero()))
   248  
   249  		// committed bytes are added (miner would have gained power if minimum requirement were met)
   250  		networkStats := vm.GetNetworkStats(t, tv)
   251  		assert.Equal(t, big.NewInt(int64(sectorSize)), networkStats.TotalBytesCommitted)
   252  		assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero()))
   253  
   254  		// Trigger cron to keep reward accounting correct
   255  		vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil)
   256  
   257  		stateTree, err := tv.GetStateTree()
   258  		require.NoError(t, err)
   259  		totalBalance, err := tv.GetTotalActorBalance()
   260  		require.NoError(t, err)
   261  		acc, err := states.CheckStateInvariants(stateTree, totalBalance, tv.GetEpoch())
   262  		require.NoError(t, err)
   263  		assert.True(t, acc.IsEmpty(), strings.Join(acc.Messages(), "\n"))
   264  	})
   265  
   266  	t.Run("skip sector", func(t *testing.T) {
   267  		tv, err := v.WithEpoch(v.GetEpoch())
   268  		require.NoError(t, err)
   269  
   270  		// Submit PoSt
   271  		submitParams := miner.SubmitWindowedPoStParams{
   272  			Deadline: dlInfo.Index,
   273  			Partitions: []miner.PoStPartition{{
   274  				Index:   pIdx,
   275  				Skipped: bitfield.NewFromSet([]uint64{uint64(sectorNumber)}),
   276  			}},
   277  			Proofs: []proof.PoStProof{{
   278  				PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1,
   279  			}},
   280  			ChainCommitEpoch: dlInfo.Challenge,
   281  			ChainCommitRand:  []byte("not really random"),
   282  		}
   283  		// PoSt is rejected for skipping all sectors.
   284  		_, code := tv.ApplyMessage(addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams)
   285  		assert.Equal(t, exitcode.ErrIllegalArgument, code)
   286  
   287  		vm.ExpectInvocation{
   288  			To:       minerAddrs.IDAddress,
   289  			Method:   builtin.MethodsMiner.SubmitWindowedPoSt,
   290  			Params:   vm.ExpectObject(&submitParams),
   291  			Exitcode: exitcode.ErrIllegalArgument,
   292  		}.Matches(t, tv.Invocations()[0])
   293  
   294  		// miner still has initial pledge
   295  		balances = vm.GetMinerBalances(t, v, minerAddrs.IDAddress)
   296  		assert.True(t, balances.InitialPledge.GreaterThan(big.Zero()))
   297  
   298  		// network power is unchanged
   299  		networkStats := vm.GetNetworkStats(t, tv)
   300  		assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted)
   301  		assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero()))
   302  	})
   303  
   304  	t.Run("missed first PoSt deadline", func(t *testing.T) {
   305  		// move to proving period end
   306  		tv, err := v.WithEpoch(dlInfo.Last())
   307  		require.NoError(t, err)
   308  
   309  		// Run cron to detect missing PoSt
   310  		vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil)
   311  
   312  		vm.ExpectInvocation{
   313  			To:     builtin.CronActorAddr,
   314  			Method: builtin.MethodsCron.EpochTick,
   315  			SubInvocations: []vm.ExpectInvocation{
   316  				{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{
   317  					{To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.OnDeferredCronEvent, SubInvocations: []vm.ExpectInvocation{
   318  						{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward},
   319  						{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower},
   320  						{To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent},
   321  					}},
   322  					{To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI},
   323  				}},
   324  				{To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick},
   325  			},
   326  		}.Matches(t, tv.Invocations()[0])
   327  
   328  		// network power is unchanged
   329  		networkStats := vm.GetNetworkStats(t, tv)
   330  		assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted)
   331  		assert.True(t, networkStats.TotalPledgeCollateral.GreaterThan(big.Zero()))
   332  	})
   333  }