From c8874e5fa6431a9900e42ed26bc7e27c1f994930 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 27 Aug 2020 23:10:50 -0700 Subject: [PATCH] Randomize deadline assignment This commit randomizes deadline assignment by starting to open new partitions fixes #432 --- actors/builtin/miner/deadline_assignment.go | 9 +++++---- .../builtin/miner/deadline_assignment_test.go | 19 +++++++++++-------- actors/builtin/miner/miner_actor.go | 10 ++++++++-- actors/builtin/miner/miner_state.go | 3 ++- actors/builtin/miner/miner_state_test.go | 2 +- actors/builtin/miner/miner_test.go | 4 ++++ 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/actors/builtin/miner/deadline_assignment.go b/actors/builtin/miner/deadline_assignment.go index f96f3e0e19..b03369e6e0 100644 --- a/actors/builtin/miner/deadline_assignment.go +++ b/actors/builtin/miner/deadline_assignment.go @@ -40,6 +40,7 @@ func (dai *deadlineAssignmentInfo) maxPartitionsReached(partitionSize, maxPartit } type deadlineAssignmentHeap struct { + offset uint64 maxPartitions uint64 partitionSize uint64 deadlines []*deadlineAssignmentInfo @@ -155,10 +156,8 @@ func (dah *deadlineAssignmentHeap) Less(i, j int) bool { return a.liveSectors < b.liveSectors } - // Finally, fallback on the deadline index. - // TODO: Randomize by index instead of simply sorting. - // https://github.com/filecoin-project/specs-actors/issues/432 - return a.index < b.index + // Finally, fallback on the deadline index, starting at an offset. + return (uint64(a.index)-dah.offset)%WPoStPeriodDeadlines < (uint64(b.index)-dah.offset)%WPoStPeriodDeadlines } func (dah *deadlineAssignmentHeap) Push(x interface{}) { @@ -175,6 +174,7 @@ func (dah *deadlineAssignmentHeap) Pop() interface{} { // Assigns partitions to deadlines, first filling partial partitions, then // adding new partitions to deadlines with the fewest live sectors. func assignDeadlines( + offset uint64, maxPartitions uint64, partitionSize uint64, deadlines *[WPoStPeriodDeadlines]*Deadline, @@ -182,6 +182,7 @@ func assignDeadlines( ) (changes [WPoStPeriodDeadlines][]*SectorOnChainInfo, err error) { // Build a heap dlHeap := deadlineAssignmentHeap{ + offset: offset, maxPartitions: maxPartitions, partitionSize: partitionSize, deadlines: make([]*deadlineAssignmentInfo, 0, len(deadlines)), diff --git a/actors/builtin/miner/deadline_assignment_test.go b/actors/builtin/miner/deadline_assignment_test.go index 758d31e891..4c1ecdc8f9 100644 --- a/actors/builtin/miner/deadline_assignment_test.go +++ b/actors/builtin/miner/deadline_assignment_test.go @@ -19,20 +19,22 @@ func TestDeadlineAssignment(t *testing.T) { type testCase struct { sectors uint64 + offset uint64 deadlines [WPoStPeriodDeadlines]*deadline } testCases := []testCase{{ // Even assignment and striping. sectors: 10, + offset: 1, deadlines: [WPoStPeriodDeadlines]*deadline{ - 0: { + 1: { expectSectors: []uint64{ 0, 1, 2, 3, 8, 9, }, }, - 1: { + 2: { expectSectors: []uint64{ 4, 5, 6, 7, }, @@ -54,12 +56,13 @@ func TestDeadlineAssignment(t *testing.T) { }, { // Assign to deadline with least number of live partitions. sectors: 1, + offset: 6, deadlines: [WPoStPeriodDeadlines]*deadline{ - 0: { + 6: { // 2 live partitions. +1 would add another. liveSectors: 8, }, - 1: { + 7: { // 2 live partitions. +1 wouldn't add another. // 1 dead partition. liveSectors: 7, @@ -152,7 +155,7 @@ func TestDeadlineAssignment(t *testing.T) { for i := range sectors { sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)} } - assignment, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors) + assignment, err := assignDeadlines(tc.offset, maxPartitions, partitionSize, &deadlines, sectors) require.NoError(t, err) for i, sectors := range assignment { dl := tc.deadlines[i] @@ -192,7 +195,7 @@ func TestMaxPartitionsPerDeadline(t *testing.T) { sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)} } - _, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors) + _, err := assignDeadlines(0, maxPartitions, partitionSize, &deadlines, sectors) require.Error(t, err) }) @@ -212,7 +215,7 @@ func TestMaxPartitionsPerDeadline(t *testing.T) { sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)} } - deadlineToSectors, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors) + deadlineToSectors, err := assignDeadlines(0, maxPartitions, partitionSize, &deadlines, sectors) require.NoError(t, err) for _, sectors := range deadlineToSectors { @@ -236,7 +239,7 @@ func TestMaxPartitionsPerDeadline(t *testing.T) { sectors[i] = &SectorOnChainInfo{SectorNumber: abi.SectorNumber(i)} } - _, err := assignDeadlines(maxPartitions, partitionSize, &deadlines, sectors) + _, err := assignDeadlines(0, maxPartitions, partitionSize, &deadlines, sectors) require.Error(t, err) }) } diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index bc20e7f177..b98f64c2a3 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -742,6 +742,13 @@ func (a Actor) ConfirmSectorProofsValid(rt Runtime, params *builtin.ConfirmSecto rt.Abortf(exitcode.ErrIllegalArgument, "all prove commits failed to validate") } + buf := new(bytes.Buffer) + receiver := rt.Message().Receiver() + err = receiver.MarshalCBOR(buf) + assignmentRandomness := binary.BigEndian.Uint64(rt.GetRandomnessFromBeacon( + crypto.DomainSeparationTag_WindowedPoStDeadlineAssignment, rt.CurrEpoch(), buf.Bytes(), + )) + var newPower PowerPair totalPledge := big.Zero() depositToUnlock := big.Zero() @@ -804,8 +811,7 @@ func (a Actor) ConfirmSectorProofsValid(rt Runtime, params *builtin.ConfirmSecto err = st.DeletePrecommittedSectors(store, newSectorNos...) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to delete precommited sectors") - - newPower, err = st.AssignSectorsToDeadlines(store, rt.CurrEpoch(), newSectors, info.WindowPoStPartitionSectors, info.SectorSize) + newPower, err = st.AssignSectorsToDeadlines(store, rt.CurrEpoch(), assignmentRandomness, newSectors, info.WindowPoStPartitionSectors, info.SectorSize) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to assign new sectors to deadlines") // Add sector and pledge lock-up to miner state diff --git a/actors/builtin/miner/miner_state.go b/actors/builtin/miner/miner_state.go index 663a3f7d0a..05754f23ac 100644 --- a/actors/builtin/miner/miner_state.go +++ b/actors/builtin/miner/miner_state.go @@ -496,6 +496,7 @@ func (st *State) RescheduleSectorExpirations( func (st *State) AssignSectorsToDeadlines( store adt.Store, currentEpoch abi.ChainEpoch, + randomness uint64, sectors []*SectorOnChainInfo, partitionSize uint64, sectorSize abi.SectorSize, @@ -523,7 +524,7 @@ func (st *State) AssignSectorsToDeadlines( } activatedPower := NewPowerPairZero() - deadlineToSectors, err := assignDeadlines(MaxPartitionsPerDeadline, partitionSize, &deadlineArr, sectors) + deadlineToSectors, err := assignDeadlines(randomness, MaxPartitionsPerDeadline, partitionSize, &deadlineArr, sectors) if err != nil { return NewPowerPairZero(), xerrors.Errorf("failed to assign sectors to deadlines: %w", err) } diff --git a/actors/builtin/miner/miner_state_test.go b/actors/builtin/miner/miner_state_test.go index 740f31ebf5..6abc115b2f 100644 --- a/actors/builtin/miner/miner_state_test.go +++ b/actors/builtin/miner/miner_state_test.go @@ -646,7 +646,7 @@ func TestSectorAssignment(t *testing.T) { t.Run("assign sectors to deadlines", func(t *testing.T) { harness := constructStateHarness(t, abi.ChainEpoch(0)) - newPower, err := harness.s.AssignSectorsToDeadlines(harness.store, 0, sectorInfos, + newPower, err := harness.s.AssignSectorsToDeadlines(harness.store, 0, 0, sectorInfos, partitionSectors, sectorSize) sectorArr := sectorsArr(t, harness.store, sectorInfos) diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index f48a0359e9..ce733d03b6 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -3526,6 +3526,10 @@ func (h *actorHarness) confirmSectorProofsValid(rt *mock.Runtime, conf proveComm // expected pledge is the sum of initial pledges if len(validPrecommits) > 0 { + randBuf := bytes.NewBuffer(nil) + require.NoError(h.t, h.receiver.MarshalCBOR(randBuf)) + rt.ExpectGetRandomnessBeacon(crypto.DomainSeparationTag_WindowedPoStDeadlineAssignment, rt.Epoch(), randBuf.Bytes(), make([]byte, 32)) + expectPledge := big.Zero() expectQAPower := big.Zero()