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

     1  package miner_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/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/miner"
    15  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    16  	"github.com/filecoin-project/specs-actors/v4/support/ipld"
    17  )
    18  
    19  func TestDeadlines(t *testing.T) {
    20  	sectors := []*miner.SectorOnChainInfo{
    21  		testSector(2, 1, 50, 60, 1000),
    22  		testSector(3, 2, 51, 61, 1001),
    23  		testSector(7, 3, 52, 62, 1002),
    24  		testSector(8, 4, 53, 63, 1003),
    25  
    26  		testSector(8, 5, 54, 64, 1004),
    27  		testSector(11, 6, 55, 65, 1005),
    28  		testSector(13, 7, 56, 66, 1006),
    29  		testSector(8, 8, 57, 67, 1007),
    30  
    31  		testSector(8, 9, 58, 68, 1008),
    32  	}
    33  	extraSectors := []*miner.SectorOnChainInfo{
    34  		testSector(8, 10, 58, 68, 1008),
    35  	}
    36  	allSectors := append(sectors, extraSectors...)
    37  
    38  	sectorSize := abi.SectorSize(32 << 30)
    39  	quantSpec := miner.NewQuantSpec(4, 1)
    40  	partitionSize := uint64(4)
    41  
    42  	dlState := expectedDeadlineState{
    43  		quant:         quantSpec,
    44  		partitionSize: partitionSize,
    45  		sectorSize:    sectorSize,
    46  		sectors:       allSectors,
    47  	}
    48  
    49  	sectorPower := func(t *testing.T, sectorNos ...uint64) miner.PowerPair {
    50  		return miner.PowerForSectors(sectorSize, selectSectors(t, allSectors, bf(sectorNos...)))
    51  	}
    52  
    53  	//
    54  	// Define some basic test scenarios that build one each other.
    55  	//
    56  
    57  	// Adds sectors, and proves them if requested.
    58  	//
    59  	// Partition 1: sectors 1, 2, 3, 4
    60  	// Partition 2: sectors 5, 6, 7, 8
    61  	// Partition 3: sectors 9
    62  	addSectors := func(t *testing.T, store adt.Store, dl *miner.Deadline, prove bool) {
    63  		power := miner.PowerForSectors(sectorSize, sectors)
    64  		activatedPower, err := dl.AddSectors(store, partitionSize, false, sectors, sectorSize, quantSpec)
    65  		require.NoError(t, err)
    66  		assert.True(t, activatedPower.Equals(power))
    67  
    68  		dlState.withUnproven(1, 2, 3, 4, 5, 6, 7, 8, 9).
    69  			withPartitions(
    70  				bf(1, 2, 3, 4),
    71  				bf(5, 6, 7, 8),
    72  				bf(9),
    73  			).assert(t, store, dl)
    74  
    75  		if !prove {
    76  			return
    77  		}
    78  
    79  		sectorArr := sectorsArr(t, store, sectors)
    80  
    81  		// Prove everything
    82  		result, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 0, []miner.PoStPartition{{Index: 0}, {Index: 1}, {Index: 2}})
    83  		require.NoError(t, err)
    84  		require.True(t, result.PowerDelta.Equals(power))
    85  
    86  		faultyPower, recoveryPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 0)
    87  		require.NoError(t, err)
    88  		require.True(t, faultyPower.IsZero())
    89  		require.True(t, recoveryPower.IsZero())
    90  
    91  		dlState.withPartitions(
    92  			bf(1, 2, 3, 4),
    93  			bf(5, 6, 7, 8),
    94  			bf(9),
    95  		).assert(t, store, dl)
    96  	}
    97  
    98  	// Adds sectors according to addSectors, then terminates them:
    99  	//
   100  	// From partition 0: sectors 1 & 3
   101  	// From partition 1: sectors 6
   102  	addThenTerminate := func(t *testing.T, store adt.Store, dl *miner.Deadline, proveFirst bool) {
   103  		addSectors(t, store, dl, proveFirst)
   104  
   105  		removedPower, err := dl.TerminateSectors(store, sectorsArr(t, store, sectors), 15, miner.PartitionSectorMap{
   106  			0: bf(1, 3),
   107  			1: bf(6),
   108  		}, sectorSize, quantSpec)
   109  		require.NoError(t, err)
   110  
   111  		expectedPower := miner.NewPowerPairZero()
   112  		unproven := []uint64{2, 4, 5, 7, 8, 9} // not 1, 3, 6
   113  		if proveFirst {
   114  			unproven = nil
   115  			expectedPower = sectorPower(t, 1, 3, 6)
   116  		}
   117  		require.True(t, expectedPower.Equals(removedPower), "dlState to remove power for terminated sectors")
   118  
   119  		dlState.withTerminations(1, 3, 6).
   120  			withUnproven(unproven...).
   121  			withPartitions(
   122  				bf(1, 2, 3, 4),
   123  				bf(5, 6, 7, 8),
   124  				bf(9),
   125  			).assert(t, store, dl)
   126  	}
   127  
   128  	// Adds and terminates sectors according to the previous two functions,
   129  	// then pops early terminations.
   130  	addThenTerminateThenPopEarly := func(t *testing.T, store adt.Store, dl *miner.Deadline) {
   131  		addThenTerminate(t, store, dl, true)
   132  
   133  		earlyTerminations, more, err := dl.PopEarlyTerminations(store, 100, 100)
   134  		require.NoError(t, err)
   135  		assert.False(t, more)
   136  		assert.Equal(t, uint64(2), earlyTerminations.PartitionsProcessed)
   137  		assert.Equal(t, uint64(3), earlyTerminations.SectorsProcessed)
   138  		assert.Len(t, earlyTerminations.Sectors, 1)
   139  		assertBitfieldEquals(t, earlyTerminations.Sectors[15], 1, 3, 6)
   140  
   141  		// Popping early terminations doesn't affect the terminations bitfield.
   142  		dlState.withTerminations(1, 3, 6).
   143  			withPartitions(
   144  				bf(1, 2, 3, 4),
   145  				bf(5, 6, 7, 8),
   146  				bf(9),
   147  			).assert(t, store, dl)
   148  	}
   149  
   150  	// Runs the above scenarios, then removes partition 0.
   151  	addThenTerminateThenRemovePartition := func(t *testing.T, store adt.Store, dl *miner.Deadline) {
   152  		addThenTerminateThenPopEarly(t, store, dl)
   153  
   154  		live, dead, removedPower, err := dl.RemovePartitions(store, bf(0), quantSpec)
   155  		require.NoError(t, err, "should have removed partitions")
   156  		assertBitfieldEquals(t, live, 2, 4)
   157  		assertBitfieldEquals(t, dead, 1, 3)
   158  		livePower := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, live))
   159  		require.True(t, livePower.Equals(removedPower))
   160  
   161  		dlState.withTerminations(6).
   162  			withPartitions(
   163  				bf(5, 6, 7, 8),
   164  				bf(9),
   165  			).assert(t, store, dl)
   166  	}
   167  
   168  	// Adds sectors according to addSectors, then marks sectors 1, 5, 6
   169  	// faulty, expiring at epoch 9.
   170  	//
   171  	// Sector 5 will expire on-time at epoch 9 while 6 will expire early at epoch 9.
   172  	addThenMarkFaulty := func(t *testing.T, store adt.Store, dl *miner.Deadline, proveFirst bool) {
   173  		addSectors(t, store, dl, proveFirst)
   174  
   175  		// Mark faulty.
   176  		powerDelta, err := dl.RecordFaults(
   177  			store, sectorsArr(t, store, sectors), sectorSize, quantSpec, 9,
   178  			map[uint64]bitfield.BitField{
   179  				0: bf(1),
   180  				1: bf(5, 6),
   181  			},
   182  		)
   183  		require.NoError(t, err)
   184  
   185  		expectedPower := miner.NewPowerPairZero()
   186  		unproven := []uint64{2, 3, 4, 7, 8, 9} // not 1, 5, 6
   187  		if proveFirst {
   188  			unproven = nil
   189  			expectedPower = sectorPower(t, 1, 5, 6)
   190  		}
   191  		assert.True(t, powerDelta.Equals(expectedPower.Neg()))
   192  
   193  		dlState.withFaults(1, 5, 6).
   194  			withUnproven(unproven...).
   195  			withPartitions(
   196  				bf(1, 2, 3, 4),
   197  				bf(5, 6, 7, 8),
   198  				bf(9),
   199  			).assert(t, store, dl)
   200  	}
   201  
   202  	// Test the basic scenarios (technically, we could just run the final one).
   203  
   204  	t.Run("adds sectors", func(t *testing.T) {
   205  		store := ipld.NewADTStore(context.Background())
   206  		dl := emptyDeadline(t, store)
   207  		addSectors(t, store, dl, false)
   208  	})
   209  
   210  	t.Run("adds sectors and proves", func(t *testing.T) {
   211  		store := ipld.NewADTStore(context.Background())
   212  		dl := emptyDeadline(t, store)
   213  		addSectors(t, store, dl, true)
   214  	})
   215  
   216  	t.Run("terminates sectors", func(t *testing.T) {
   217  		store := ipld.NewADTStore(context.Background())
   218  		dl := emptyDeadline(t, store)
   219  		addThenTerminate(t, store, dl, true)
   220  	})
   221  
   222  	t.Run("terminates unproven sectors", func(t *testing.T) {
   223  		store := ipld.NewADTStore(context.Background())
   224  		dl := emptyDeadline(t, store)
   225  		addThenTerminate(t, store, dl, false)
   226  	})
   227  
   228  	t.Run("pops early terminations", func(t *testing.T) {
   229  		store := ipld.NewADTStore(context.Background())
   230  		dl := emptyDeadline(t, store)
   231  
   232  		addThenTerminateThenPopEarly(t, store, dl)
   233  	})
   234  
   235  	t.Run("removes partitions", func(t *testing.T) {
   236  		store := ipld.NewADTStore(context.Background())
   237  		dl := emptyDeadline(t, store)
   238  
   239  		addThenTerminateThenRemovePartition(t, store, dl)
   240  	})
   241  
   242  	t.Run("marks faulty", func(t *testing.T) {
   243  		store := ipld.NewADTStore(context.Background())
   244  		dl := emptyDeadline(t, store)
   245  
   246  		addThenMarkFaulty(t, store, dl, true)
   247  	})
   248  
   249  	t.Run("marks unproven sectors faulty", func(t *testing.T) {
   250  		store := ipld.NewADTStore(context.Background())
   251  		dl := emptyDeadline(t, store)
   252  
   253  		addThenMarkFaulty(t, store, dl, false)
   254  	})
   255  
   256  	//
   257  	// Now, build on these basic scenarios with some "what ifs".
   258  	//
   259  
   260  	t.Run("cannot remove partitions with early terminations", func(t *testing.T) {
   261  		store := ipld.NewADTStore(context.Background())
   262  		dl := emptyDeadline(t, store)
   263  		addThenTerminate(t, store, dl, false)
   264  
   265  		_, _, _, err := dl.RemovePartitions(store, bf(0), quantSpec)
   266  		require.Error(t, err, "should have failed to remove a partition with early terminations")
   267  	})
   268  
   269  	t.Run("can pop early terminations in multiple steps", func(t *testing.T) {
   270  		store := ipld.NewADTStore(context.Background())
   271  		dl := emptyDeadline(t, store)
   272  		addThenTerminate(t, store, dl, true)
   273  
   274  		var result miner.TerminationResult
   275  
   276  		// process 1 sector, 2 partitions (should pop 1 sector)
   277  		result1, hasMore, err := dl.PopEarlyTerminations(store, 2, 1)
   278  		require.NoError(t, err)
   279  		require.True(t, hasMore)
   280  		require.NoError(t, result.Add(result1))
   281  
   282  		// process 2 sectors, 1 partitions (should pop 1 sector)
   283  		result2, hasMore, err := dl.PopEarlyTerminations(store, 2, 1)
   284  		require.NoError(t, err)
   285  		require.True(t, hasMore)
   286  		require.NoError(t, result.Add(result2))
   287  
   288  		// process 1 sectors, 1 partitions (should pop 1 sector)
   289  		result3, hasMore, err := dl.PopEarlyTerminations(store, 1, 1)
   290  		require.NoError(t, err)
   291  		require.False(t, hasMore)
   292  		require.NoError(t, result.Add(result3))
   293  
   294  		assert.Equal(t, uint64(3), result.PartitionsProcessed)
   295  		assert.Equal(t, uint64(3), result.SectorsProcessed)
   296  		assert.Len(t, result.Sectors, 1)
   297  		assertBitfieldEquals(t, result.Sectors[15], 1, 3, 6)
   298  
   299  		// Popping early terminations doesn't affect the terminations bitfield.
   300  		dlState.withTerminations(1, 3, 6).
   301  			withPartitions(
   302  				bf(1, 2, 3, 4),
   303  				bf(5, 6, 7, 8),
   304  				bf(9),
   305  			).assert(t, store, dl)
   306  	})
   307  
   308  	t.Run("cannot remove missing partition", func(t *testing.T) {
   309  		store := ipld.NewADTStore(context.Background())
   310  		dl := emptyDeadline(t, store)
   311  
   312  		addThenTerminateThenRemovePartition(t, store, dl)
   313  
   314  		_, _, _, err := dl.RemovePartitions(store, bf(2), quantSpec)
   315  		require.Error(t, err, "should have failed to remove missing partition")
   316  	})
   317  
   318  	t.Run("removing no partitions does nothing", func(t *testing.T) {
   319  		store := ipld.NewADTStore(context.Background())
   320  		dl := emptyDeadline(t, store)
   321  
   322  		addThenTerminateThenPopEarly(t, store, dl)
   323  
   324  		live, dead, removedPower, err := dl.RemovePartitions(store, bf(), quantSpec)
   325  		require.NoError(t, err, "should not have failed to remove no partitions")
   326  		require.True(t, removedPower.IsZero())
   327  		assertBitfieldEquals(t, live)
   328  		assertBitfieldEquals(t, dead)
   329  
   330  		// Popping early terminations doesn't affect the terminations bitfield.
   331  		dlState.withTerminations(1, 3, 6).
   332  			withPartitions(
   333  				bf(1, 2, 3, 4),
   334  				bf(5, 6, 7, 8),
   335  				bf(9),
   336  			).assert(t, store, dl)
   337  	})
   338  
   339  	t.Run("fails to remove partitions with faulty sectors", func(t *testing.T) {
   340  		store := ipld.NewADTStore(context.Background())
   341  
   342  		dl := emptyDeadline(t, store)
   343  		addThenMarkFaulty(t, store, dl, false)
   344  
   345  		// Try to remove a partition with faulty sectors.
   346  		_, _, _, err := dl.RemovePartitions(store, bf(1), quantSpec)
   347  		require.Error(t, err, "should have failed to remove a partition with faults")
   348  	})
   349  
   350  	t.Run("terminate proven & faulty", func(t *testing.T) {
   351  		store := ipld.NewADTStore(context.Background())
   352  		dl := emptyDeadline(t, store)
   353  
   354  		addThenMarkFaulty(t, store, dl, true) // 1, 5, 6 faulty
   355  
   356  		sectorArr := sectorsArr(t, store, sectors)
   357  		removedPower, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{
   358  			0: bf(1, 3),
   359  			1: bf(6),
   360  		}, sectorSize, quantSpec)
   361  		require.NoError(t, err)
   362  
   363  		// Sector 3 active, 1, 6 faulty
   364  		expectedPowerLoss := miner.PowerForSectors(sectorSize, selectSectors(t, sectors, bf(3)))
   365  		require.True(t, expectedPowerLoss.Equals(removedPower), "dlState to remove power for terminated sectors")
   366  
   367  		dlState.withTerminations(1, 3, 6).
   368  			withFaults(5).
   369  			withPartitions(
   370  				bf(1, 2, 3, 4),
   371  				bf(5, 6, 7, 8),
   372  				bf(9),
   373  			).assert(t, store, dl)
   374  	})
   375  
   376  	t.Run("terminate unproven & faulty", func(t *testing.T) {
   377  		store := ipld.NewADTStore(context.Background())
   378  		dl := emptyDeadline(t, store)
   379  
   380  		addThenMarkFaulty(t, store, dl, false) // 1, 5, 6 faulty
   381  
   382  		sectorArr := sectorsArr(t, store, sectors)
   383  		removedPower, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{
   384  			0: bf(1, 3),
   385  			1: bf(6),
   386  		}, sectorSize, quantSpec)
   387  		require.NoError(t, err)
   388  
   389  		// Sector 3 unproven, 1, 6 faulty
   390  		require.True(t, removedPower.Equals(miner.NewPowerPairZero()), "should remove no power")
   391  
   392  		dlState.withTerminations(1, 3, 6).
   393  			withUnproven(2, 4, 7, 8, 9). // not 1, 3, 5, & 6
   394  			withFaults(5).
   395  			withPartitions(
   396  				bf(1, 2, 3, 4),
   397  				bf(5, 6, 7, 8),
   398  				bf(9),
   399  			).assert(t, store, dl)
   400  	})
   401  
   402  	t.Run("fails to terminate missing sector", func(t *testing.T) {
   403  		store := ipld.NewADTStore(context.Background())
   404  		dl := emptyDeadline(t, store)
   405  
   406  		addThenMarkFaulty(t, store, dl, false) // 1, 5, 6 faulty
   407  
   408  		sectorArr := sectorsArr(t, store, sectors)
   409  		_, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{
   410  			0: bf(6),
   411  		}, sectorSize, quantSpec)
   412  		require.Error(t, err)
   413  		require.Contains(t, err.Error(), "can only terminate live sectors")
   414  	})
   415  
   416  	t.Run("fails to terminate missing partition", func(t *testing.T) {
   417  		store := ipld.NewADTStore(context.Background())
   418  		dl := emptyDeadline(t, store)
   419  
   420  		addThenMarkFaulty(t, store, dl, false) // 1, 5, 6 faulty
   421  
   422  		sectorArr := sectorsArr(t, store, sectors)
   423  		_, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{
   424  			4: bf(6),
   425  		}, sectorSize, quantSpec)
   426  		require.Error(t, err)
   427  		require.Contains(t, err.Error(), "failed to find partition 4")
   428  	})
   429  
   430  	t.Run("fails to terminate already terminated sector", func(t *testing.T) {
   431  		store := ipld.NewADTStore(context.Background())
   432  		dl := emptyDeadline(t, store)
   433  
   434  		addThenTerminate(t, store, dl, false) // terminates 1, 3, & 6
   435  
   436  		sectorArr := sectorsArr(t, store, sectors)
   437  		_, err := dl.TerminateSectors(store, sectorArr, 15, miner.PartitionSectorMap{
   438  			0: bf(1, 2),
   439  		}, sectorSize, quantSpec)
   440  		require.Error(t, err)
   441  		require.Contains(t, err.Error(), "can only terminate live sectors")
   442  	})
   443  
   444  	t.Run("faulty sectors expire", func(t *testing.T) {
   445  		store := ipld.NewADTStore(context.Background())
   446  
   447  		dl := emptyDeadline(t, store)
   448  		// Mark sectors 5 & 6 faulty, expiring at epoch 9.
   449  		addThenMarkFaulty(t, store, dl, true)
   450  
   451  		// We expect all sectors but 7 to have expired at this point.
   452  		exp, err := dl.PopExpiredSectors(store, 9, quantSpec)
   453  		require.NoError(t, err)
   454  
   455  		onTimeExpected := bf(1, 2, 3, 4, 5, 8, 9)
   456  		earlyExpected := bf(6)
   457  
   458  		assertBitfieldsEqual(t, onTimeExpected, exp.OnTimeSectors)
   459  		assertBitfieldsEqual(t, earlyExpected, exp.EarlySectors)
   460  
   461  		dlState.withTerminations(1, 2, 3, 4, 5, 6, 8, 9).
   462  			withPartitions(
   463  				bf(1, 2, 3, 4),
   464  				bf(5, 6, 7, 8),
   465  				bf(9),
   466  			).assert(t, store, dl)
   467  
   468  		// Check early terminations.
   469  		earlyTerminations, more, err := dl.PopEarlyTerminations(store, 100, 100)
   470  		require.NoError(t, err)
   471  		assert.False(t, more)
   472  		assert.Equal(t, uint64(1), earlyTerminations.PartitionsProcessed)
   473  		assert.Equal(t, uint64(1), earlyTerminations.SectorsProcessed)
   474  		assert.Len(t, earlyTerminations.Sectors, 1)
   475  		assertBitfieldEquals(t, earlyTerminations.Sectors[9], 6)
   476  
   477  		// Popping early terminations doesn't affect the terminations bitfield.
   478  		dlState.withTerminations(1, 2, 3, 4, 5, 6, 8, 9).
   479  			withPartitions(
   480  				bf(1, 2, 3, 4),
   481  				bf(5, 6, 7, 8),
   482  				bf(9),
   483  			).assert(t, store, dl)
   484  	})
   485  
   486  	t.Run("cannot pop expired sectors before proving", func(t *testing.T) {
   487  		store := ipld.NewADTStore(context.Background())
   488  
   489  		dl := emptyDeadline(t, store)
   490  		// Add sectors, but don't prove.
   491  		addSectors(t, store, dl, false)
   492  
   493  		// Try to pop some expirations.
   494  		_, err := dl.PopExpiredSectors(store, 9, quantSpec)
   495  		require.Error(t, err)
   496  		require.Contains(t, err.Error(), "cannot pop expired sectors from a partition with unproven sectors")
   497  	})
   498  
   499  	t.Run("post all the things", func(t *testing.T) {
   500  		store := ipld.NewADTStore(context.Background())
   501  
   502  		dl := emptyDeadline(t, store)
   503  		addSectors(t, store, dl, true)
   504  
   505  		// add an inactive sector
   506  		power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec)
   507  		require.NoError(t, err)
   508  		expectedPower := miner.PowerForSectors(sectorSize, extraSectors)
   509  		assert.True(t, expectedPower.Equals(power))
   510  
   511  		sectorArr := sectorsArr(t, store, allSectors)
   512  
   513  		postResult1, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   514  			{Index: 0, Skipped: bf()},
   515  			{Index: 1, Skipped: bf()},
   516  		})
   517  		require.NoError(t, err)
   518  		assertBitfieldEquals(t, postResult1.Sectors, 1, 2, 3, 4, 5, 6, 7, 8)
   519  		assertEmptyBitfield(t, postResult1.IgnoredSectors)
   520  		require.True(t, postResult1.NewFaultyPower.Equals(miner.NewPowerPairZero()))
   521  		require.True(t, postResult1.RetractedRecoveryPower.Equals(miner.NewPowerPairZero()))
   522  		require.True(t, postResult1.RecoveredPower.Equals(miner.NewPowerPairZero()))
   523  
   524  		// First two partitions posted
   525  		dlState.withPosts(0, 1).
   526  			withUnproven(10).
   527  			withPartitions(
   528  				bf(1, 2, 3, 4),
   529  				bf(5, 6, 7, 8),
   530  				bf(9, 10),
   531  			).assert(t, store, dl)
   532  
   533  		postResult2, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   534  			{Index: 2, Skipped: bf()},
   535  		})
   536  		require.NoError(t, err)
   537  		assertBitfieldEquals(t, postResult2.Sectors, 9, 10)
   538  		assertEmptyBitfield(t, postResult2.IgnoredSectors)
   539  		require.True(t, postResult2.NewFaultyPower.Equals(miner.NewPowerPairZero()))
   540  		require.True(t, postResult2.RetractedRecoveryPower.Equals(miner.NewPowerPairZero()))
   541  		require.True(t, postResult2.RecoveredPower.Equals(miner.NewPowerPairZero()))
   542  		// activate sector 10
   543  		require.True(t, postResult2.PowerDelta.Equals(sectorPower(t, 10)))
   544  
   545  		// All 3 partitions posted, unproven sector 10 proven and power activated.
   546  		dlState.withPosts(0, 1, 2).
   547  			withPartitions(
   548  				bf(1, 2, 3, 4),
   549  				bf(5, 6, 7, 8),
   550  				bf(9, 10),
   551  			).assert(t, store, dl)
   552  
   553  		powerDelta, penalizedPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13)
   554  		require.NoError(t, err)
   555  
   556  		// No power delta for successful post.
   557  		require.True(t, powerDelta.IsZero())
   558  		require.True(t, penalizedPower.IsZero())
   559  
   560  		// Everything back to normal.
   561  		dlState.withPartitions(
   562  			bf(1, 2, 3, 4),
   563  			bf(5, 6, 7, 8),
   564  			bf(9, 10),
   565  		).assert(t, store, dl)
   566  	})
   567  
   568  	t.Run("post with unproven, faults, recoveries, and retracted recoveries", func(t *testing.T) {
   569  		store := ipld.NewADTStore(context.Background())
   570  		dl := emptyDeadline(t, store)
   571  
   572  		// Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty.
   573  		addThenMarkFaulty(t, store, dl, true)
   574  
   575  		// add an inactive sector
   576  		power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec)
   577  		require.NoError(t, err)
   578  		expectedPower := miner.PowerForSectors(sectorSize, extraSectors)
   579  		assert.True(t, expectedPower.Equals(power))
   580  
   581  		sectorArr := sectorsArr(t, store, allSectors)
   582  
   583  		// Declare sectors 1 & 6 recovered.
   584  		require.NoError(t, dl.DeclareFaultsRecovered(store, sectorArr, sectorSize, map[uint64]bitfield.BitField{
   585  			0: bf(1),
   586  			1: bf(6),
   587  		}))
   588  
   589  		// We're now recovering 1 & 6.
   590  		dlState.withRecovering(1, 6).
   591  			withFaults(1, 5, 6).
   592  			withUnproven(10).
   593  			withPartitions(
   594  				bf(1, 2, 3, 4),
   595  				bf(5, 6, 7, 8),
   596  				bf(9, 10),
   597  			).assert(t, store, dl)
   598  
   599  		// Prove partitions 0 & 1, skipping sectors 1 & 7.
   600  		postResult, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   601  			{Index: 0, Skipped: bf(1)},
   602  			{Index: 1, Skipped: bf(7)},
   603  		})
   604  
   605  		require.NoError(t, err)
   606  		// 1, 5, and 7 are expected to be faulty.
   607  		// - 1 should have recovered but didn't (retracted)
   608  		// - 5 was already marked faulty.
   609  		// - 7 is newly faulty.
   610  		// - 6 has recovered.
   611  		assertBitfieldEquals(t, postResult.Sectors, 1, 2, 3, 4, 5, 6, 7, 8)
   612  		assertBitfieldEquals(t, postResult.IgnoredSectors, 1, 5, 7)
   613  		// sector 7 is newly faulty
   614  		require.True(t, postResult.NewFaultyPower.Equals(sectorPower(t, 7)))
   615  		// we failed to recover 1 (retracted)
   616  		require.True(t, postResult.RetractedRecoveryPower.Equals(sectorPower(t, 1)))
   617  		// we recovered 6
   618  		require.True(t, postResult.RecoveredPower.Equals(sectorPower(t, 6)))
   619  		// no power delta from these deadlines.
   620  		require.True(t, postResult.PowerDelta.IsZero())
   621  
   622  		// First two partitions should be posted.
   623  		dlState.withPosts(0, 1).
   624  			withFaults(1, 5, 7).
   625  			withUnproven(10).
   626  			withPartitions(
   627  				bf(1, 2, 3, 4),
   628  				bf(5, 6, 7, 8),
   629  				bf(9, 10),
   630  			).assert(t, store, dl)
   631  
   632  		powerDelta, penalizedPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13)
   633  		require.NoError(t, err)
   634  
   635  		expFaultPower := sectorPower(t, 9, 10)
   636  		expPowerDelta := sectorPower(t, 9).Neg()
   637  
   638  		// Sector 9 wasn't proven.
   639  		require.True(t, powerDelta.Equals(expPowerDelta))
   640  		// No new changes to recovering power.
   641  		require.True(t, penalizedPower.Equals(expFaultPower))
   642  
   643  		// Posts taken care of.
   644  		// Unproven now faulty.
   645  		dlState.withFaults(1, 5, 7, 9, 10).
   646  			withPartitions(
   647  				bf(1, 2, 3, 4),
   648  				bf(5, 6, 7, 8),
   649  				bf(9, 10),
   650  			).assert(t, store, dl)
   651  	})
   652  
   653  	t.Run("post with skipped unproven", func(t *testing.T) {
   654  		store := ipld.NewADTStore(context.Background())
   655  
   656  		dl := emptyDeadline(t, store)
   657  		addSectors(t, store, dl, true)
   658  
   659  		// add an inactive sector
   660  		power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec)
   661  		require.NoError(t, err)
   662  		expectedPower := miner.PowerForSectors(sectorSize, extraSectors)
   663  		assert.True(t, expectedPower.Equals(power))
   664  
   665  		sectorArr := sectorsArr(t, store, allSectors)
   666  
   667  		postResult1, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   668  			{Index: 0, Skipped: bf()},
   669  			{Index: 1, Skipped: bf()},
   670  			{Index: 2, Skipped: bf(10)},
   671  		})
   672  
   673  		require.NoError(t, err)
   674  		assertBitfieldEquals(t, postResult1.Sectors, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
   675  		assertBitfieldEquals(t, postResult1.IgnoredSectors, 10)
   676  		require.True(t, postResult1.NewFaultyPower.Equals(sectorPower(t, 10)))
   677  		require.True(t, postResult1.PowerDelta.IsZero()) // not proven yet.
   678  		require.True(t, postResult1.RetractedRecoveryPower.IsZero())
   679  		require.True(t, postResult1.RecoveredPower.IsZero())
   680  
   681  		// All posted
   682  		dlState.withPosts(0, 1, 2).
   683  			withFaults(10).
   684  			withPartitions(
   685  				bf(1, 2, 3, 4),
   686  				bf(5, 6, 7, 8),
   687  				bf(9, 10),
   688  			).assert(t, store, dl)
   689  
   690  		powerDelta, penalizedPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13)
   691  		require.NoError(t, err)
   692  
   693  		// All posts submitted, no power delta, no extra penalties.
   694  		require.True(t, powerDelta.IsZero())
   695  		// Penalize for skipped sector 10.
   696  		require.True(t, penalizedPower.IsZero())
   697  
   698  		// Everything back to normal, except that we have a fault.
   699  		dlState.withFaults(10).
   700  			withPartitions(
   701  				bf(1, 2, 3, 4),
   702  				bf(5, 6, 7, 8),
   703  				bf(9, 10),
   704  			).assert(t, store, dl)
   705  	})
   706  
   707  	t.Run("post missing partition", func(t *testing.T) {
   708  		store := ipld.NewADTStore(context.Background())
   709  
   710  		dl := emptyDeadline(t, store)
   711  		addSectors(t, store, dl, true)
   712  
   713  		// add an inactive sector
   714  		power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec)
   715  		require.NoError(t, err)
   716  		expectedPower := miner.PowerForSectors(sectorSize, extraSectors)
   717  		assert.True(t, expectedPower.Equals(power))
   718  
   719  		sectorArr := sectorsArr(t, store, allSectors)
   720  
   721  		_, err = dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   722  			{Index: 0, Skipped: bf()},
   723  			{Index: 3, Skipped: bf()},
   724  		})
   725  		require.Error(t, err)
   726  		require.Contains(t, err.Error(), "no such partition")
   727  	})
   728  
   729  	t.Run("post partition twice", func(t *testing.T) {
   730  		store := ipld.NewADTStore(context.Background())
   731  
   732  		dl := emptyDeadline(t, store)
   733  		addSectors(t, store, dl, true)
   734  
   735  		// add an inactive sector
   736  		power, err := dl.AddSectors(store, partitionSize, false, extraSectors, sectorSize, quantSpec)
   737  		require.NoError(t, err)
   738  		expectedPower := miner.PowerForSectors(sectorSize, extraSectors)
   739  		assert.True(t, expectedPower.Equals(power))
   740  
   741  		sectorArr := sectorsArr(t, store, allSectors)
   742  
   743  		_, err = dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   744  			{Index: 0, Skipped: bf()},
   745  			{Index: 0, Skipped: bf()},
   746  		})
   747  		require.Error(t, err)
   748  		require.Contains(t, err.Error(), "duplicate partitions proven")
   749  	})
   750  
   751  	t.Run("retract recoveries", func(t *testing.T) {
   752  		store := ipld.NewADTStore(context.Background())
   753  		dl := emptyDeadline(t, store)
   754  
   755  		// Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty.
   756  		addThenMarkFaulty(t, store, dl, true)
   757  
   758  		sectorArr := sectorsArr(t, store, sectors)
   759  
   760  		// Declare sectors 1 & 6 recovered.
   761  		require.NoError(t, dl.DeclareFaultsRecovered(store, sectorArr, sectorSize, map[uint64]bitfield.BitField{
   762  			0: bf(1),
   763  			1: bf(6),
   764  		}))
   765  
   766  		// Retract recovery for sector 1.
   767  		powerDelta, err := dl.RecordFaults(store, sectorArr, sectorSize, quantSpec, 13, map[uint64]bitfield.BitField{
   768  			0: bf(1),
   769  		})
   770  
   771  		// We're just retracting a recovery, this doesn't count as a new fault.
   772  		require.NoError(t, err)
   773  		require.True(t, powerDelta.Equals(miner.NewPowerPairZero()))
   774  
   775  		// We're now recovering 6.
   776  		dlState.withRecovering(6).
   777  			withFaults(1, 5, 6).
   778  			withPartitions(
   779  				bf(1, 2, 3, 4),
   780  				bf(5, 6, 7, 8),
   781  				bf(9),
   782  			).assert(t, store, dl)
   783  
   784  		// Prove all partitions.
   785  		postResult, err := dl.RecordProvenSectors(store, sectorArr, sectorSize, quantSpec, 13, []miner.PoStPartition{
   786  			{Index: 0, Skipped: bf()},
   787  			{Index: 1, Skipped: bf()},
   788  			{Index: 2, Skipped: bf()},
   789  		})
   790  
   791  		require.NoError(t, err)
   792  		// 1 & 5 are still faulty
   793  		assertBitfieldEquals(t, postResult.Sectors, 1, 2, 3, 4, 5, 6, 7, 8, 9)
   794  		assertBitfieldEquals(t, postResult.IgnoredSectors, 1, 5)
   795  		// All faults were declared.
   796  		require.True(t, postResult.NewFaultyPower.Equals(miner.NewPowerPairZero()))
   797  		// we didn't fail to recover anything.
   798  		require.True(t, postResult.RetractedRecoveryPower.Equals(miner.NewPowerPairZero()))
   799  		// we recovered 6.
   800  		require.True(t, postResult.RecoveredPower.Equals(sectorPower(t, 6)))
   801  
   802  		// First two partitions should be posted.
   803  		dlState.withPosts(0, 1, 2).
   804  			withFaults(1, 5).
   805  			withPartitions(
   806  				bf(1, 2, 3, 4),
   807  				bf(5, 6, 7, 8),
   808  				bf(9),
   809  			).assert(t, store, dl)
   810  
   811  		newFaultyPower, failedRecoveryPower, err := dl.ProcessDeadlineEnd(store, quantSpec, 13)
   812  		require.NoError(t, err)
   813  
   814  		// No power changes.
   815  		require.True(t, newFaultyPower.Equals(miner.NewPowerPairZero()))
   816  		require.True(t, failedRecoveryPower.Equals(miner.NewPowerPairZero()))
   817  
   818  		// Posts taken care of.
   819  		dlState.withFaults(1, 5).
   820  			withPartitions(
   821  				bf(1, 2, 3, 4),
   822  				bf(5, 6, 7, 8),
   823  				bf(9),
   824  			).assert(t, store, dl)
   825  	})
   826  
   827  	t.Run("reschedule expirations", func(t *testing.T) {
   828  		store := ipld.NewADTStore(context.Background())
   829  		dl := emptyDeadline(t, store)
   830  
   831  		sectorArr := sectorsArr(t, store, sectors)
   832  
   833  		// Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty.
   834  		addThenMarkFaulty(t, store, dl, true)
   835  
   836  		// Try to reschedule two sectors, only the 7 (non faulty) should succeed.
   837  		replaced, err := dl.RescheduleSectorExpirations(store, sectorArr, 1, miner.PartitionSectorMap{
   838  			1: bf(6, 7, 99), // 99 should be skipped, it doesn't exist.
   839  			5: bf(100),      // partition 5 doesn't exist.
   840  			2: bf(),         // empty bitfield should be fine.
   841  		}, sectorSize, quantSpec)
   842  		require.NoError(t, err)
   843  
   844  		assert.Len(t, replaced, 1)
   845  
   846  		exp, err := dl.PopExpiredSectors(store, 1, quantSpec)
   847  		require.NoError(t, err)
   848  
   849  		sector7 := selectSectors(t, sectors, bf(7))[0]
   850  
   851  		dlState.withFaults(1, 5, 6).
   852  			withTerminations(7).
   853  			withPartitions(
   854  				bf(1, 2, 3, 4),
   855  				bf(5, 6, 7, 8),
   856  				bf(9),
   857  			).assert(t, store, dl)
   858  		assertBitfieldEmpty(t, exp.EarlySectors)
   859  		assertBitfieldEquals(t, exp.OnTimeSectors, 7)
   860  		assert.True(t, exp.ActivePower.Equals(miner.PowerForSector(sectorSize, sector7)))
   861  		assert.True(t, exp.FaultyPower.IsZero())
   862  		assert.True(t, exp.OnTimePledge.Equals(sector7.InitialPledge))
   863  	})
   864  
   865  	t.Run("cannot declare faults in missing partitions", func(t *testing.T) {
   866  		store := ipld.NewADTStore(context.Background())
   867  		dl := emptyDeadline(t, store)
   868  
   869  		addSectors(t, store, dl, true)
   870  		sectorArr := sectorsArr(t, store, allSectors)
   871  
   872  		// Declare sectors 1 & 6 faulty.
   873  		_, err := dl.RecordFaults(store, sectorArr, sectorSize, quantSpec, 17, map[uint64]bitfield.BitField{
   874  			0: bf(1),
   875  			4: bf(6),
   876  		})
   877  		require.Error(t, err)
   878  		require.Contains(t, err.Error(), "no such partition 4")
   879  	})
   880  
   881  	t.Run("cannot declare faults recovered in missing partitions", func(t *testing.T) {
   882  		store := ipld.NewADTStore(context.Background())
   883  		dl := emptyDeadline(t, store)
   884  
   885  		// Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty.
   886  		addThenMarkFaulty(t, store, dl, true)
   887  		sectorArr := sectorsArr(t, store, allSectors)
   888  
   889  		// Declare sectors 1 & 6 faulty.
   890  		err := dl.DeclareFaultsRecovered(store, sectorArr, sectorSize, map[uint64]bitfield.BitField{
   891  			0: bf(1),
   892  			4: bf(6),
   893  		})
   894  		require.Error(t, err)
   895  		require.Contains(t, err.Error(), "no such partition 4")
   896  	})
   897  }
   898  
   899  func emptyDeadline(t *testing.T, store adt.Store) *miner.Deadline {
   900  	dl, err := miner.ConstructDeadline(store)
   901  	require.NoError(t, err)
   902  	return dl
   903  }
   904  
   905  // Helper type for validating deadline state.
   906  //
   907  // All methods take and the state by _value_ so one can (and should) construct a
   908  // sane base-state.
   909  type expectedDeadlineState struct {
   910  	quant         miner.QuantSpec
   911  	sectorSize    abi.SectorSize
   912  	partitionSize uint64
   913  	sectors       []*miner.SectorOnChainInfo
   914  
   915  	faults       bitfield.BitField
   916  	recovering   bitfield.BitField
   917  	terminations bitfield.BitField
   918  	unproven     bitfield.BitField
   919  	posts        bitfield.BitField
   920  
   921  	partitionSectors []bitfield.BitField
   922  }
   923  
   924  //nolint:unused
   925  func (s expectedDeadlineState) withQuantSpec(quant miner.QuantSpec) expectedDeadlineState {
   926  	s.quant = quant
   927  	return s
   928  }
   929  
   930  //nolint:unused
   931  func (s expectedDeadlineState) withFaults(faults ...uint64) expectedDeadlineState {
   932  	s.faults = bf(faults...)
   933  	return s
   934  }
   935  
   936  //nolint:unused
   937  func (s expectedDeadlineState) withRecovering(recovering ...uint64) expectedDeadlineState {
   938  	s.recovering = bf(recovering...)
   939  	return s
   940  }
   941  
   942  //nolint:unused
   943  func (s expectedDeadlineState) withTerminations(terminations ...uint64) expectedDeadlineState {
   944  	s.terminations = bf(terminations...)
   945  	return s
   946  }
   947  
   948  //nolint:unused
   949  func (s expectedDeadlineState) withUnproven(unproven ...uint64) expectedDeadlineState {
   950  	s.unproven = bf(unproven...)
   951  	return s
   952  }
   953  
   954  //nolint:unused
   955  func (s expectedDeadlineState) withPosts(posts ...uint64) expectedDeadlineState {
   956  	s.posts = bf(posts...)
   957  	return s
   958  }
   959  
   960  //nolint:unused
   961  func (s expectedDeadlineState) withPartitions(partitions ...bitfield.BitField) expectedDeadlineState {
   962  	s.partitionSectors = partitions
   963  	return s
   964  }
   965  
   966  // Assert that the deadline's state matches the expected state.
   967  func (s expectedDeadlineState) assert(t *testing.T, store adt.Store, dl *miner.Deadline) {
   968  	_, faults, recoveries, terminations, unproven := checkDeadlineInvariants(
   969  		t, store, dl, s.quant, s.sectorSize, s.sectors,
   970  	)
   971  
   972  	assertBitfieldsEqual(t, s.faults, faults)
   973  	assertBitfieldsEqual(t, s.recovering, recoveries)
   974  	assertBitfieldsEqual(t, s.terminations, terminations)
   975  	assertBitfieldsEqual(t, s.unproven, unproven)
   976  	assertBitfieldsEqual(t, s.posts, dl.PartitionsPoSted)
   977  
   978  	partitions, err := dl.PartitionsArray(store)
   979  	require.NoError(t, err)
   980  	require.Equal(t, uint64(len(s.partitionSectors)), partitions.Length(), "unexpected number of partitions")
   981  	for i, partSectors := range s.partitionSectors {
   982  		var partition miner.Partition
   983  		found, err := partitions.Get(uint64(i), &partition)
   984  		require.NoError(t, err)
   985  		require.True(t, found)
   986  		assertBitfieldsEqual(t, partSectors, partition.Sectors)
   987  	}
   988  }
   989  
   990  // check the deadline's invariants, returning all contained sectors, faults,
   991  // recoveries, terminations, and partition/sector assignments.
   992  func checkDeadlineInvariants(
   993  	t *testing.T, store adt.Store, dl *miner.Deadline,
   994  	quant miner.QuantSpec, ssize abi.SectorSize,
   995  	sectors []*miner.SectorOnChainInfo,
   996  ) (
   997  	allSectors bitfield.BitField,
   998  	allFaults bitfield.BitField,
   999  	allRecoveries bitfield.BitField,
  1000  	allTerminations bitfield.BitField,
  1001  	allUnproven bitfield.BitField,
  1002  ) {
  1003  	msgs := &builtin.MessageAccumulator{}
  1004  	summary := miner.CheckDeadlineStateInvariants(dl, store, quant, ssize, sectorsAsMap(sectors), msgs)
  1005  	assert.True(t, msgs.IsEmpty(), strings.Join(msgs.Messages(), "\n"))
  1006  
  1007  	allSectors = summary.AllSectors
  1008  	allFaults = summary.FaultySectors
  1009  	allRecoveries = summary.RecoveringSectors
  1010  	allTerminations = summary.TerminatedSectors
  1011  	allUnproven = summary.UnprovenSectors
  1012  	return
  1013  }