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

     1  package miner
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/filecoin-project/go-state-types/abi"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  )
    10  
    11  func TestDeadlineAssignment(t *testing.T) {
    12  	const partitionSize = 4
    13  	const maxPartitions = 100
    14  
    15  	type deadline struct {
    16  		liveSectors, deadSectors uint64
    17  		expectSectors            []uint64
    18  	}
    19  
    20  	type testCase struct {
    21  		sectors   uint64
    22  		deadlines [WPoStPeriodDeadlines]*deadline
    23  	}
    24  
    25  	testCases := []testCase{{
    26  		// Even assignment and striping.
    27  		sectors: 10,
    28  		deadlines: [WPoStPeriodDeadlines]*deadline{
    29  			0: {
    30  				expectSectors: []uint64{
    31  					0, 1, 2, 3,
    32  					8, 9,
    33  				},
    34  			},
    35  			1: {
    36  				expectSectors: []uint64{
    37  					4, 5, 6, 7,
    38  				},
    39  			},
    40  		},
    41  	}, {
    42  		// Fill non-full first
    43  		sectors: 5,
    44  		deadlines: [WPoStPeriodDeadlines]*deadline{
    45  			0: {
    46  				expectSectors: []uint64{3, 4},
    47  			},
    48  			1: {}, // expect nothing.
    49  			3: {
    50  				liveSectors:   1,
    51  				expectSectors: []uint64{0, 1, 2},
    52  			},
    53  		},
    54  	}, {
    55  		// Assign to deadline with least number of live partitions.
    56  		sectors: 1,
    57  		deadlines: [WPoStPeriodDeadlines]*deadline{
    58  			0: {
    59  				// 2 live partitions. +1 would add another.
    60  				liveSectors: 8,
    61  			},
    62  			1: {
    63  				// 2 live partitions. +1 wouldn't add another.
    64  				// 1 dead partition.
    65  				liveSectors:   7,
    66  				deadSectors:   5,
    67  				expectSectors: []uint64{0},
    68  			},
    69  		},
    70  	}, {
    71  		// Avoid increasing max partitions. Both deadlines have the same
    72  		// number of partitions post-compaction, but deadline 1 has
    73  		// fewer pre-compaction.
    74  		sectors: 1,
    75  		deadlines: [WPoStPeriodDeadlines]*deadline{
    76  			0: {
    77  				// one live, one dead.
    78  				liveSectors: 4,
    79  				deadSectors: 4,
    80  			},
    81  			1: {
    82  				// 1 live partitions. +1 would add another.
    83  				liveSectors:   4,
    84  				expectSectors: []uint64{0},
    85  			},
    86  		},
    87  	}, {
    88  		// With multiple open partitions, assign to most full first.
    89  		sectors: 1,
    90  		deadlines: [WPoStPeriodDeadlines]*deadline{
    91  			0: {
    92  				liveSectors: 1,
    93  			},
    94  			1: {
    95  				liveSectors:   2,
    96  				expectSectors: []uint64{0},
    97  			},
    98  		},
    99  	}, {
   100  		// dead sectors also count
   101  		sectors: 1,
   102  		deadlines: [WPoStPeriodDeadlines]*deadline{
   103  			0: {
   104  				liveSectors: 1,
   105  			},
   106  			1: {
   107  				deadSectors:   2,
   108  				expectSectors: []uint64{0},
   109  			},
   110  		},
   111  	}, {
   112  		// dead sectors really do count.
   113  		sectors: 1,
   114  		deadlines: [WPoStPeriodDeadlines]*deadline{
   115  			0: {
   116  				deadSectors: 1,
   117  			},
   118  			1: {
   119  				deadSectors:   2,
   120  				expectSectors: []uint64{0},
   121  			},
   122  		},
   123  	}, {
   124  		// when partitions are equally full, assign based on live sectors.
   125  		sectors: 1,
   126  		deadlines: [WPoStPeriodDeadlines]*deadline{
   127  			0: {
   128  				liveSectors: 1,
   129  				deadSectors: 1,
   130  			},
   131  			1: {
   132  				deadSectors:   2,
   133  				expectSectors: []uint64{0},
   134  			},
   135  		},
   136  	}}
   137  
   138  	for _, tc := range testCases {
   139  		var deadlines [WPoStPeriodDeadlines]*Deadline
   140  		for i := range deadlines {
   141  			dl := tc.deadlines[i]
   142  			if dl == nil {
   143  				// blackout
   144  				continue
   145  			}
   146  			deadlines[i] = &Deadline{
   147  				LiveSectors:  dl.liveSectors,
   148  				TotalSectors: dl.liveSectors + dl.deadSectors,
   149  			}
   150  		}
   151  		sectors := make([]*SectorOnChainInfo, tc.sectors)
   152  		for i := range sectors {
   153  			sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)}
   154  		}
   155  		assignment, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors)
   156  		require.NoError(t, err)
   157  		for i, sectors := range assignment {
   158  			dl := tc.deadlines[i]
   159  			// blackout?
   160  			if dl == nil {
   161  				assert.Empty(t, sectors, "expected no sectors to have been assigned to blacked out deadline")
   162  				continue
   163  			}
   164  			require.Equal(t, len(dl.expectSectors), len(sectors), "for deadline %d", i)
   165  
   166  			for i, expectedSectorNo := range dl.expectSectors {
   167  				assert.Equal(t, uint64(sectors[i].SectorNumber), expectedSectorNo)
   168  			}
   169  		}
   170  	}
   171  }
   172  
   173  func TestMaxPartitionsPerDeadline(t *testing.T) {
   174  	const maxPartitions = 5
   175  	const partitionSize = 5
   176  
   177  	t.Run("fails if all deadlines hit their max partitions limit before assigning all sectors to deadlines", func(T *testing.T) {
   178  		// one deadline can take 5 * 5 = 25 sectors
   179  		// so 48 deadlines can take 48 * 25 = 1200 sectors.
   180  		// Hence, we should fail if we try to assign 1201 sectors.
   181  
   182  		var deadlines [WPoStPeriodDeadlines]*Deadline
   183  		for i := range deadlines {
   184  			deadlines[i] = &Deadline{
   185  				LiveSectors:  0,
   186  				TotalSectors: 0,
   187  			}
   188  		}
   189  
   190  		sectors := make([]*SectorOnChainInfo, 1201)
   191  		for i := range sectors {
   192  			sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)}
   193  		}
   194  
   195  		_, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors)
   196  		require.Error(t, err)
   197  	})
   198  
   199  	t.Run("succeeds if all all deadlines hit their max partitions limit but assignment is complete", func(t *testing.T) {
   200  		// one deadline can take 5 * 5 = 25 sectors
   201  		// so 48 deadlines that can take 48 * 25 = 1200 sectors.
   202  		var deadlines [WPoStPeriodDeadlines]*Deadline
   203  		for i := range deadlines {
   204  			deadlines[i] = &Deadline{
   205  				LiveSectors:  0,
   206  				TotalSectors: 0,
   207  			}
   208  		}
   209  
   210  		sectors := make([]*SectorOnChainInfo, 1200)
   211  		for i := range sectors {
   212  			sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)}
   213  		}
   214  
   215  		deadlineToSectors, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors)
   216  		require.NoError(t, err)
   217  
   218  		for _, sectors := range deadlineToSectors {
   219  			require.Len(t, sectors, 25) // there should be (1200/48) = 25 sectors per deadline
   220  		}
   221  	})
   222  
   223  	t.Run("fails if some deadlines have sectors beforehand and all deadlines hit their max partition limit", func(t *testing.T) {
   224  		var deadlines [WPoStPeriodDeadlines]*Deadline
   225  		for i := range deadlines {
   226  			deadlines[i] = &Deadline{
   227  				LiveSectors:  1,
   228  				TotalSectors: 2,
   229  			}
   230  		}
   231  
   232  		// can only take 1200 - (2 * 48) = 1104 sectors
   233  
   234  		sectors := make([]*SectorOnChainInfo, 1105)
   235  		for i := range sectors {
   236  			sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)}
   237  		}
   238  
   239  		_, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors)
   240  		require.Error(t, err)
   241  	})
   242  }