diff --git a/actors/builtin/miner/deadline_state.go b/actors/builtin/miner/deadline_state.go index 543b7ba83..4f5411969 100644 --- a/actors/builtin/miner/deadline_state.go +++ b/actors/builtin/miner/deadline_state.go @@ -1159,70 +1159,6 @@ func (dl *Deadline) TakePoStProofs(store adt.Store, idx uint64) (partitions bitf return post.Partitions, post.Proofs, nil } -// RescheduleSectorExpirations reschedules the expirations of the given sectors -// to the target epoch, skipping any sectors it can't find. -// -// The power of the rescheduled sectors is assumed to have not changed since -// initial scheduling. -// -// Note: see the docs on State.RescheduleSectorExpirations for details on why we -// skip sectors/partitions we can't find. -func (dl *Deadline) RescheduleSectorExpirations( - store adt.Store, sectors Sectors, - expiration abi.ChainEpoch, partitionSectors PartitionSectorMap, - ssize abi.SectorSize, quant builtin.QuantSpec, -) ([]*SectorOnChainInfo, error) { - partitions, err := dl.PartitionsArray(store) - if err != nil { - return nil, err - } - - var rescheduledPartitions []uint64 // track partitions with moved expirations. - var allReplaced []*SectorOnChainInfo - if err := partitionSectors.ForEach(func(partIdx uint64, sectorNos bitfield.BitField) error { - var partition Partition - if found, err := partitions.Get(partIdx, &partition); err != nil { - return xerrors.Errorf("failed to load partition %d: %w", partIdx, err) - } else if !found { - // We failed to find the partition, it could have moved - // due to compaction. This function is only reschedules - // sectors it can find so we'll just skip it. - return nil - } - - replaced, err := partition.RescheduleExpirations(store, sectors, expiration, sectorNos, ssize, quant) - if err != nil { - return xerrors.Errorf("failed to reschedule expirations in partition %d: %w", partIdx, err) - } - if len(replaced) == 0 { - // nothing moved. - return nil - } - allReplaced = append(allReplaced, replaced...) - - rescheduledPartitions = append(rescheduledPartitions, partIdx) - if err = partitions.Set(partIdx, &partition); err != nil { - return xerrors.Errorf("failed to store partition %d: %w", partIdx, err) - } - return nil - }); err != nil { - return nil, err - } - - if len(rescheduledPartitions) > 0 { - dl.Partitions, err = partitions.Root() - if err != nil { - return nil, xerrors.Errorf("failed to save partitions: %w", err) - } - err := dl.AddExpirationPartitions(store, expiration, rescheduledPartitions, quant) - if err != nil { - return nil, xerrors.Errorf("failed to reschedule partition expirations: %w", err) - } - } - - return allReplaced, nil -} - // DisputeInfo includes all the information necessary to dispute a post to the // given partitions. type DisputeInfo struct { diff --git a/actors/builtin/miner/deadline_state_test.go b/actors/builtin/miner/deadline_state_test.go index 5267854f8..3296604fe 100644 --- a/actors/builtin/miner/deadline_state_test.go +++ b/actors/builtin/miner/deadline_state_test.go @@ -824,44 +824,6 @@ func TestDeadlines(t *testing.T) { ).assert(t, store, dl) }) - t.Run("reschedule expirations", func(t *testing.T) { - store := ipld.NewADTStore(context.Background()) - dl := emptyDeadline(t, store) - - sectorArr := sectorsArr(t, store, sectors) - - // Marks sectors 1 (partition 0), 5 & 6 (partition 1) as faulty. - addThenMarkFaulty(t, store, dl, true) - - // Try to reschedule two sectors, only the 7 (non faulty) should succeed. - replaced, err := dl.RescheduleSectorExpirations(store, sectorArr, 1, miner.PartitionSectorMap{ - 1: bf(6, 7, 99), // 99 should be skipped, it doesn't exist. - 5: bf(100), // partition 5 doesn't exist. - 2: bf(), // empty bitfield should be fine. - }, sectorSize, quantSpec) - require.NoError(t, err) - - assert.Len(t, replaced, 1) - - exp, err := dl.PopExpiredSectors(store, 1, quantSpec) - require.NoError(t, err) - - sector7 := selectSectors(t, sectors, bf(7))[0] - - dlState.withFaults(1, 5, 6). - withTerminations(7). - withPartitions( - bf(1, 2, 3, 4), - bf(5, 6, 7, 8), - bf(9), - ).assert(t, store, dl) - assertBitfieldEmpty(t, exp.EarlySectors) - assertBitfieldEquals(t, exp.OnTimeSectors, 7) - assert.True(t, exp.ActivePower.Equals(miner.PowerForSector(sectorSize, sector7))) - assert.True(t, exp.FaultyPower.IsZero()) - assert.True(t, exp.OnTimePledge.Equals(sector7.InitialPledge)) - }) - t.Run("cannot declare faults in missing partitions", func(t *testing.T) { store := ipld.NewADTStore(context.Background()) dl := emptyDeadline(t, store) diff --git a/actors/builtin/miner/expiration_queue.go b/actors/builtin/miner/expiration_queue.go index 1617729f6..15633d76f 100644 --- a/actors/builtin/miner/expiration_queue.go +++ b/actors/builtin/miner/expiration_queue.go @@ -196,25 +196,6 @@ func (q ExpirationQueue) AddActiveSectors(sectors []*SectorOnChainInfo, ssize ab return snos, totalPower, totalPledge, nil } -// Reschedules some sectors to a new (quantized) expiration epoch. -// The sectors being rescheduled are assumed to be not faulty, and hence are removed from and re-scheduled for on-time -// rather than early expiration. -// The sectors' power and pledge are assumed not to change, despite the new expiration. -func (q ExpirationQueue) RescheduleExpirations(newExpiration abi.ChainEpoch, sectors []*SectorOnChainInfo, ssize abi.SectorSize) error { - if len(sectors) == 0 { - return nil - } - - snos, power, pledge, err := q.removeActiveSectors(sectors, ssize) - if err != nil { - return xerrors.Errorf("failed to remove sector expirations: %w", err) - } - if err = q.add(newExpiration, snos, bitfield.New(), power, NewPowerPairZero(), pledge); err != nil { - return xerrors.Errorf("failed to record new sector expirations: %w", err) - } - return nil -} - // Re-schedules sectors to expire at an early expiration epoch (quantized), if they wouldn't expire before then anyway. // The sectors must not be currently faulty, so must be registered as expiring on-time rather than early. // The pledge for the now-early sectors is removed from the queue. diff --git a/actors/builtin/miner/expiration_queue_test.go b/actors/builtin/miner/expiration_queue_test.go index 3ed90308a..a6796a324 100644 --- a/actors/builtin/miner/expiration_queue_test.go +++ b/actors/builtin/miner/expiration_queue_test.go @@ -298,48 +298,6 @@ func TestExpirationQueue(t *testing.T) { assert.Equal(t, 0, int(queue.Length())) }) - t.Run("reschedules sectors to expire later", func(t *testing.T) { - queue := emptyExpirationQueue(t) - _, _, _, err := queue.AddActiveSectors(sectors, sectorSize) - require.NoError(t, err) - - _, err = queue.Root() - require.NoError(t, err) - - err = queue.RescheduleExpirations(abi.ChainEpoch(20), sectors[:3], sectorSize) - require.NoError(t, err) - - _, err = queue.Root() - require.NoError(t, err) - - // expect 3 rescheduled sectors to be bundled into 1 set - assert.Equal(t, 4, int(queue.Length())) - - // rescheduled sectors are no longer scheduled before epoch 8 - set, err := queue.PopUntil(7) - require.NoError(t, err) - assertBitfieldEmpty(t, set.OnTimeSectors) - assert.Equal(t, 4, int(queue.Length())) - - // pop off sectors before new expiration and expect only the rescheduled set to remain - _, err = queue.PopUntil(19) - require.NoError(t, err) - assert.Equal(t, 1, int(queue.Length())) - - // pop off rescheduled sectors - set, err = queue.PopUntil(20) - require.NoError(t, err) - assert.Equal(t, 0, int(queue.Length())) - - // expect all sector stats from first 3 sectors to belong to new expiration set - assertBitfieldEquals(t, set.OnTimeSectors, 1, 2, 3) - assertBitfieldEmpty(t, set.EarlySectors) - - assert.Equal(t, big.NewInt(3003), set.OnTimePledge) - assert.True(t, set.ActivePower.Equals(miner.PowerForSectors(sectorSize, sectors[:3]))) - assert.True(t, set.FaultyPower.Equals(miner.NewPowerPairZero())) - }) - t.Run("reschedules sectors as faults", func(t *testing.T) { // Create 3 expiration sets with 2 sectors apiece queue := emptyExpirationQueueWithQuantizing(t, builtin.NewQuantSpec(4, 1), testAmtBitwidth) @@ -462,43 +420,6 @@ func TestExpirationQueue(t *testing.T) { assert.True(t, set.FaultyPower.Equals(miner.NewPowerPairZero())) }) - t.Run("reschedule expirations then reschedule as fault", func(t *testing.T) { - // Create expiration 3 sets with 2 sectors apiece - queue := emptyExpirationQueueWithQuantizing(t, builtin.NewQuantSpec(4, 1), testAmtBitwidth) - _, _, _, err := queue.AddActiveSectors(sectors, sectorSize) - require.NoError(t, err) - - _, err = queue.Root() - require.NoError(t, err) - - // reschedule 2 from second group to first - toReschedule := []*miner.SectorOnChainInfo{sectors[2]} - err = queue.RescheduleExpirations(2, toReschedule, sectorSize) - require.NoError(t, err) - - // now reschedule one sector in first group and another in second group as faults to expire in first set - faults := []*miner.SectorOnChainInfo{sectors[1], sectors[2]} - power, err := queue.RescheduleAsFaults(4, faults, sectorSize) - require.NoError(t, err) - - expectedPower := miner.PowerForSectors(sectorSize, faults) - assert.Equal(t, expectedPower, power) - - // expect 0, 1, 2, 3 in first group - set, err := queue.PopUntil(5) - require.NoError(t, err) - assertBitfieldEquals(t, set.OnTimeSectors, 1, 2, 3) - assert.Equal(t, miner.PowerForSectors(sectorSize, []*miner.SectorOnChainInfo{sectors[0]}), set.ActivePower) - assert.Equal(t, expectedPower, set.FaultyPower) - - // expect rest to come later - set, err = queue.PopUntil(20) - require.NoError(t, err) - assertBitfieldEquals(t, set.OnTimeSectors, 4, 5, 6) - assert.Equal(t, miner.PowerForSectors(sectorSize, []*miner.SectorOnChainInfo{sectors[3], sectors[4], sectors[5]}), set.ActivePower) - assert.Equal(t, miner.NewPowerPairZero(), set.FaultyPower) - }) - t.Run("reschedule recover restores all sector stats", func(t *testing.T) { // Create expiration 3 sets with 2 sectors apiece queue := emptyExpirationQueueWithQuantizing(t, builtin.NewQuantSpec(4, 1), testAmtBitwidth) @@ -688,13 +609,6 @@ func TestExpirationQueue(t *testing.T) { assert.Zero(t, queue.Length()) }) - t.Run("rescheduling no expirations leaves the queue empty", func(t *testing.T) { - queue := emptyExpirationQueueWithQuantizing(t, builtin.NewQuantSpec(4, 1), testAmtBitwidth) - err := queue.RescheduleExpirations(10, nil, sectorSize) - require.NoError(t, err) - assert.Zero(t, queue.Length()) - }) - t.Run("rescheduling no expirations as faults leaves the queue empty", func(t *testing.T) { queue := emptyExpirationQueueWithQuantizing(t, builtin.NewQuantSpec(4, 1), testAmtBitwidth) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index 866238629..3535bd9c4 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -720,14 +720,8 @@ func (a Actor) PreCommitSectorBatch(rt Runtime, params *PreCommitSectorBatchPara maxActivation := currEpoch + MaxProveCommitDuration[precommit.SealProof] validateExpiration(rt, maxActivation, precommit.Expiration, precommit.SealProof) - if precommit.ReplaceCapacity && len(precommit.DealIDs) == 0 { - rt.Abortf(exitcode.ErrIllegalArgument, "cannot replace sector without committing deals") - } - if precommit.ReplaceSectorDeadline >= WPoStPeriodDeadlines { - rt.Abortf(exitcode.ErrIllegalArgument, "invalid deadline %d", precommit.ReplaceSectorDeadline) - } - if precommit.ReplaceSectorNumber > abi.MaxSectorNumber { - rt.Abortf(exitcode.ErrIllegalArgument, "invalid sector number %d", precommit.ReplaceSectorNumber) + if precommit.ReplaceCapacity { + rt.Abortf(exitcode.SysErrForbidden, "cc upgrade through precommit discontinued, use lightweight cc upgrade instead") } sectorsDeals[i] = market.SectorDeals{ @@ -1084,8 +1078,6 @@ func confirmSectorProofsValid(rt Runtime, preCommits []*SectorPreCommitOnChainIn // Ideally, we'd combine some of these operations, but at least we have // a constant number of them. - // Committed-capacity sectors licensed for early removal by new sectors being proven. - replaceSectors := make(DeadlineSectorMap) activation := rt.CurrEpoch() // Pre-commits for new sectors. var validPreCommits []*SectorPreCommitOnChainInfo @@ -1112,15 +1104,6 @@ func confirmSectorProofsValid(rt Runtime, preCommits []*SectorPreCommitOnChainIn } validPreCommits = append(validPreCommits, precommit) - - if precommit.Info.ReplaceCapacity { - err := replaceSectors.AddValues( - precommit.Info.ReplaceSectorDeadline, - precommit.Info.ReplaceSectorPartition, - uint64(precommit.Info.ReplaceSectorNumber), - ) - builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed to record sectors for replacement") - } } // When all prove commits have failed abort early @@ -1136,11 +1119,6 @@ func confirmSectorProofsValid(rt Runtime, preCommits []*SectorPreCommitOnChainIn store := adt.AsStore(rt) rt.StateTransaction(&st, func() { info := getMinerInfo(rt, &st) - // Schedule expiration for replaced sectors to the end of their next deadline window. - // They can't be removed right now because we want to challenge them immediately before termination. - replaced, err := st.RescheduleSectorExpirations(store, rt.CurrEpoch(), info.SectorSize, replaceSectors) - builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to replace sector expirations") - replacedBySectorNumber := asMapBySectorNumber(replaced) newSectorNos := make([]abi.SectorNumber, 0, len(validPreCommits)) for _, precommit := range validPreCommits { @@ -1162,8 +1140,7 @@ func confirmSectorProofsValid(rt Runtime, preCommits []*SectorPreCommitOnChainIn // Lower-bound the pledge by that of the sector being replaced. // Record the replaced age and reward rate for termination fee calculations. - replacedPledge, replacedAge, replacedDayReward := replacedSectorParameters(rt, precommit, replacedBySectorNumber) - initialPledge = big.Max(initialPledge, replacedPledge) + _, replacedAge, replacedDayReward := zeroReplacedSectorParameters() newSectorInfo := SectorOnChainInfo{ SectorNumber: precommit.Info.SectorNumber, @@ -1187,7 +1164,7 @@ func confirmSectorProofsValid(rt Runtime, preCommits []*SectorPreCommitOnChainIn totalPledge = big.Add(totalPledge, initialPledge) } - err = st.PutSectors(store, newSectors...) + err := st.PutSectors(store, newSectors...) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to put new sectors") err = st.DeletePrecommittedSectors(store, newSectorNos...) @@ -2683,28 +2660,8 @@ func currentDeadlineIndex(currEpoch abi.ChainEpoch, periodStart abi.ChainEpoch) return uint64((currEpoch - periodStart) / WPoStChallengeWindow) } -func asMapBySectorNumber(sectors []*SectorOnChainInfo) map[abi.SectorNumber]*SectorOnChainInfo { - m := make(map[abi.SectorNumber]*SectorOnChainInfo, len(sectors)) - for _, s := range sectors { - m[s.SectorNumber] = s - } - return m -} - -func replacedSectorParameters(rt Runtime, precommit *SectorPreCommitOnChainInfo, - replacedByNum map[abi.SectorNumber]*SectorOnChainInfo) (pledge abi.TokenAmount, age abi.ChainEpoch, dayReward big.Int) { - if !precommit.Info.ReplaceCapacity { - return big.Zero(), abi.ChainEpoch(0), big.Zero() - } - replaced, ok := replacedByNum[precommit.Info.ReplaceSectorNumber] - if !ok { - rt.Abortf(exitcode.ErrNotFound, "no such sector %v to replace", precommit.Info.ReplaceSectorNumber) - } - // The sector will actually be active for the period between activation and its next proving deadline, - // but this covers the period for which we will be looking to the old sector for termination fees. - return replaced.InitialPledge, - maxEpoch(0, rt.CurrEpoch()-replaced.Activation), - replaced.ExpectedDayReward +func zeroReplacedSectorParameters() (pledge abi.TokenAmount, age abi.ChainEpoch, dayReward big.Int) { + return big.Zero(), abi.ChainEpoch(0), big.Zero() } // Update worker address with pending worker key if exists and delay has passed @@ -2818,7 +2775,7 @@ func minEpoch(a, b abi.ChainEpoch) abi.ChainEpoch { return b } -func maxEpoch(a, b abi.ChainEpoch) abi.ChainEpoch { +func maxEpoch(a, b abi.ChainEpoch) abi.ChainEpoch { //nolint:deadcode,unused if a > b { return a } diff --git a/actors/builtin/miner/miner_state.go b/actors/builtin/miner/miner_state.go index 9a3712f4b..bf81e4478 100644 --- a/actors/builtin/miner/miner_state.go +++ b/actors/builtin/miner/miner_state.go @@ -527,55 +527,6 @@ func (st *State) FindSector(store adt.Store, sno abi.SectorNumber) (uint64, uint return FindSector(store, deadlines, sno) } -// Schedules each sector to expire at its next deadline end. If it can't find -// any given sector, it skips it. -// -// This method assumes that each sector's power has not changed, despite the rescheduling. -// -// Note: this method is used to "upgrade" sectors, rescheduling the now-replaced -// sectors to expire at the end of the next deadline. Given the expense of -// sealing a sector, this function skips missing/faulty/terminated "upgraded" -// sectors instead of failing. That way, the new sectors can still be proved. -func (st *State) RescheduleSectorExpirations( - store adt.Store, currEpoch abi.ChainEpoch, ssize abi.SectorSize, - deadlineSectors DeadlineSectorMap, -) ([]*SectorOnChainInfo, error) { - deadlines, err := st.LoadDeadlines(store) - if err != nil { - return nil, err - } - sectors, err := LoadSectors(store, st.Sectors) - if err != nil { - return nil, err - } - - var allReplaced []*SectorOnChainInfo - if err = deadlineSectors.ForEach(func(dlIdx uint64, pm PartitionSectorMap) error { - dlInfo := NewDeadlineInfo(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch).NextNotElapsed() - newExpiration := dlInfo.Last() - - dl, err := deadlines.LoadDeadline(store, dlIdx) - if err != nil { - return err - } - - replaced, err := dl.RescheduleSectorExpirations(store, sectors, newExpiration, pm, ssize, QuantSpecForDeadline(dlInfo)) - if err != nil { - return err - } - allReplaced = append(allReplaced, replaced...) - - if err := deadlines.UpdateDeadline(store, dlIdx, dl); err != nil { - return err - } - - return nil - }); err != nil { - return nil, err - } - return allReplaced, st.SaveDeadlines(store, deadlines) -} - // Assign new sectors to deadlines. func (st *State) AssignSectorsToDeadlines( store adt.Store, currentEpoch abi.ChainEpoch, sectors []*SectorOnChainInfo, partitionSize uint64, sectorSize abi.SectorSize, diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index 1215c37c0..4f0785a2f 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -380,855 +380,6 @@ func TestControlAddresses(t *testing.T) { } -// Test sector lifecycle when a sector is upgraded -func TestCCUpgrade(t *testing.T) { - periodOffset := abi.ChainEpoch(100) - t.Run("valid committed capacity upgrade", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // Move the current epoch forward so that the first deadline is a stable candidate for both sectors - rt.SetEpoch(periodOffset + miner.WPoStChallengeWindow) - - // Commit a sector to upgrade - // Use the max sector number to make sure everything works. - oldSector := actor.commitAndProveSector(rt, abi.MaxSectorNumber, defaultSectorExpiration, nil) - - // advance cron to activate power. - advanceAndSubmitPoSts(rt, actor, oldSector) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // Reduce the epoch reward so that a new sector's initial pledge would otherwise be lesser. - // It has to be reduced quite a lot to overcome the new sector having more power due to verified deal weight. - actor.epochRewardSmooth = smoothing.TestingConstantEstimate(big.Div(actor.epochRewardSmooth.Estimate(), big.NewInt(20))) - - challengeEpoch := rt.Epoch() - 1 - upgradeParams := actor.makePreCommit(200, challengeEpoch, oldSector.Expiration, []abi.DealID{1}) - upgradeParams.ReplaceCapacity = true - upgradeParams.ReplaceSectorDeadline = dlIdx - upgradeParams.ReplaceSectorPartition = partIdx - upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) - - // Check new pre-commit in state - assert.True(t, upgrade.Info.ReplaceCapacity) - assert.Equal(t, upgradeParams.ReplaceSectorNumber, upgrade.Info.ReplaceSectorNumber) - - // Old sector is unchanged - oldSectorAgain := actor.getSector(rt, oldSector.SectorNumber) - assert.Equal(t, oldSector, oldSectorAgain) - - // Deposit and pledge as expected - st = getState(rt) - assert.Equal(t, st.PreCommitDeposits, upgrade.PreCommitDeposit) - assert.Equal(t, st.InitialPledge, oldSector.InitialPledge) - - // Prove new sector - rt.SetEpoch(upgrade.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) - newSector := actor.proveCommitSectorAndConfirm(rt, upgrade, makeProveCommit(upgrade.Info.SectorNumber), proveCommitConf{}) - - // Both sectors' deposits are returned, and pledge is committed - st = getState(rt) - assert.Equal(t, big.Zero(), st.PreCommitDeposits) - assert.Equal(t, st.InitialPledge, big.Add(oldSector.InitialPledge, newSector.InitialPledge)) - // new sector pledge is max of computed pledge and pledge from old sector - assert.Equal(t, oldSector.InitialPledge, newSector.InitialPledge) - - // Both sectors are present (in the same deadline/partition). - deadline, partition := actor.getDeadlineAndPartition(rt, dlIdx, partIdx) - assert.Equal(t, uint64(2), deadline.TotalSectors) - assert.Equal(t, uint64(2), deadline.LiveSectors) - assertEmptyBitfield(t, deadline.EarlyTerminations) - - assertBitfieldEquals(t, partition.Sectors, uint64(newSector.SectorNumber), uint64(oldSector.SectorNumber)) - assertEmptyBitfield(t, partition.Faults) - assertEmptyBitfield(t, partition.Recoveries) - assertEmptyBitfield(t, partition.Terminated) - - // The old sector's expiration has changed to the end of this proving deadline. - // The new one expires when the old one used to. - // The partition is registered with an expiry at both epochs. - dQueue := actor.collectDeadlineExpirations(rt, deadline) - dlInfo := miner.NewDeadlineInfo(st.ProvingPeriodStart, dlIdx, rt.Epoch()) - quantizedExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(oldSector.Expiration) - assert.Equal(t, map[abi.ChainEpoch][]uint64{ - dlInfo.NextNotElapsed().Last(): {uint64(0)}, - quantizedExpiration: {uint64(0)}, - }, dQueue) - - pQueue := actor.collectPartitionExpirations(rt, partition) - assertBitfieldEquals(t, pQueue[dlInfo.NextNotElapsed().Last()].OnTimeSectors, uint64(oldSector.SectorNumber)) - assertBitfieldEquals(t, pQueue[quantizedExpiration].OnTimeSectors, uint64(newSector.SectorNumber)) - - // Roll forward to the beginning of the next iteration of this deadline - advanceToEpochWithCron(rt, actor, dlInfo.NextNotElapsed().Open) - - // Fail to submit PoSt. This means that both sectors will be detected faulty. - // Expect the old sector to be marked as terminated. - bothSectors := []*miner.SectorOnChainInfo{oldSector, newSector} - lostPower := actor.powerPairForSectors(bothSectors[:1]).Neg() // new sector not active yet. - faultExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(dlInfo.NextNotElapsed().Last() + miner.FaultMaxAge) - - advanceDeadline(rt, actor, &cronConfig{ - detectedFaultsPowerDelta: &lostPower, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - }) - - // The old sector is marked as terminated - st = getState(rt) - deadline, partition = actor.getDeadlineAndPartition(rt, dlIdx, partIdx) - assert.Equal(t, uint64(2), deadline.TotalSectors) - assert.Equal(t, uint64(1), deadline.LiveSectors) - assertBitfieldEquals(t, partition.Sectors, uint64(newSector.SectorNumber), uint64(oldSector.SectorNumber)) - assertBitfieldEquals(t, partition.Terminated, uint64(oldSector.SectorNumber)) - assertBitfieldEquals(t, partition.Faults, uint64(newSector.SectorNumber)) - newSectorPower := miner.PowerForSector(actor.sectorSize, newSector) - assert.True(t, newSectorPower.Equals(partition.LivePower)) - assert.True(t, newSectorPower.Equals(partition.FaultyPower)) - - // we expect the partition expiration to be scheduled twice, once early - // and once on-time (inside the partition, the sector is scheduled only once). - dQueue = actor.collectDeadlineExpirations(rt, deadline) - assert.Equal(t, map[abi.ChainEpoch][]uint64{ - miner.QuantSpecForDeadline(dlInfo).QuantizeUp(newSector.Expiration): {uint64(0)}, - faultExpiration: {uint64(0)}, - }, dQueue) - - // Old sector gone from pledge requirement and deposit - assert.Equal(t, st.InitialPledge, newSector.InitialPledge) - actor.checkState(rt) - }) - - t.Run("invalid committed capacity upgrade rejected", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // Commit sectors to target upgrade. The first has no deals, the second has a deal. - oldSectors := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, [][]abi.DealID{nil, {10}}, true) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSectors[0].SectorNumber) - require.NoError(t, err) - - challengeEpoch := rt.Epoch() - 1 - upgradeParams := actor.makePreCommit(200, challengeEpoch, oldSectors[0].Expiration, []abi.DealID{20}) - upgradeParams.ReplaceCapacity = true - upgradeParams.ReplaceSectorDeadline = dlIdx - upgradeParams.ReplaceSectorPartition = partIdx - upgradeParams.ReplaceSectorNumber = oldSectors[0].SectorNumber - - { // Must have deals - params := *upgradeParams - params.DealIDs = nil - rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.Reset() - } - { // Old sector cannot have deals - params := *upgradeParams - params.ReplaceSectorNumber = oldSectors[1].SectorNumber - rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.Reset() - } - { // Target sector must exist - params := *upgradeParams - params.ReplaceSectorNumber = 999 - rt.ExpectAbort(exitcode.ErrNotFound, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.Reset() - } - { // Target partition must exist - params := *upgradeParams - params.ReplaceSectorPartition = 999 - rt.ExpectAbortContainsMessage(exitcode.ErrNotFound, "no partition 999", func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.Reset() - } - { // Expiration must not be sooner than target - params := *upgradeParams - params.Expiration = params.Expiration - miner.WPoStProvingPeriod - rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.Reset() - } - { // Target must not be faulty - params := *upgradeParams - st := getState(rt) - prevState := *st - quant := st.QuantSpecForDeadline(dlIdx) - deadlines, err := st.LoadDeadlines(rt.AdtStore()) - require.NoError(t, err) - deadline, err := deadlines.LoadDeadline(rt.AdtStore(), dlIdx) - require.NoError(t, err) - partitions, err := deadline.PartitionsArray(rt.AdtStore()) - require.NoError(t, err) - var partition miner.Partition - found, err := partitions.Get(partIdx, &partition) - require.True(t, found) - require.NoError(t, err) - sectorArr, err := miner.LoadSectors(rt.AdtStore(), st.Sectors) - require.NoError(t, err) - newFaults, _, _, err := partition.RecordFaults(rt.AdtStore(), sectorArr, bf(uint64(oldSectors[0].SectorNumber)), 100000, - actor.sectorSize, quant) - require.NoError(t, err) - assertBitfieldEquals(t, newFaults, uint64(oldSectors[0].SectorNumber)) - require.NoError(t, partitions.Set(partIdx, &partition)) - deadline.Partitions, err = partitions.Root() - require.NoError(t, err) - deadlines.Due[dlIdx] = rt.StorePut(deadline) - require.NoError(t, st.SaveDeadlines(rt.AdtStore(), deadlines)) - // Phew! - - rt.ReplaceState(st) - rt.ExpectAbort(exitcode.ErrForbidden, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.ReplaceState(&prevState) - rt.Reset() - } - - { // Target must not be terminated - params := *upgradeParams - st := getState(rt) - prevState := *st - quant := st.QuantSpecForDeadline(dlIdx) - deadlines, err := st.LoadDeadlines(rt.AdtStore()) - require.NoError(t, err) - deadline, err := deadlines.LoadDeadline(rt.AdtStore(), dlIdx) - require.NoError(t, err) - partitions, err := deadline.PartitionsArray(rt.AdtStore()) - require.NoError(t, err) - var partition miner.Partition - found, err := partitions.Get(partIdx, &partition) - require.True(t, found) - require.NoError(t, err) - sectorArr, err := miner.LoadSectors(rt.AdtStore(), st.Sectors) - require.NoError(t, err) - result, err := partition.TerminateSectors(rt.AdtStore(), sectorArr, rt.Epoch(), bf(uint64(oldSectors[0].SectorNumber)), - actor.sectorSize, quant) - require.NoError(t, err) - assertBitfieldEquals(t, result.OnTimeSectors, uint64(oldSectors[0].SectorNumber)) - require.NoError(t, partitions.Set(partIdx, &partition)) - deadline.Partitions, err = partitions.Root() - require.NoError(t, err) - deadlines.Due[dlIdx] = rt.StorePut(deadline) - require.NoError(t, st.SaveDeadlines(rt.AdtStore(), deadlines)) - // Phew! - - rt.ReplaceState(st) - rt.ExpectAbort(exitcode.ErrNotFound, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) - }) - rt.ReplaceState(&prevState) - rt.Reset() - } - - // Demonstrate that the params are otherwise ok - actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) - rt.Verify() - actor.checkState(rt) - }) - - t.Run("upgrade sector before it is proven", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // Move the current epoch forward so that the first deadline is a stable candidate for both sectors - rt.SetEpoch(periodOffset + miner.WPoStChallengeWindow) - - // Commit a sector to upgrade - // Use the max sector number to make sure everything works. - oldSector := actor.commitAndProveSector(rt, abi.MaxSectorNumber, defaultSectorExpiration, nil) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // Reduce the epoch reward so that a new sector's initial pledge would otherwise be lesser. - // It has to be reduced quite a lot to overcome the new sector having more power due to verified deal weight. - actor.epochRewardSmooth = smoothing.TestingConstantEstimate(big.Div(actor.epochRewardSmooth.Estimate(), big.NewInt(20))) - - challengeEpoch := rt.Epoch() - 1 - upgradeParams := actor.makePreCommit(200, challengeEpoch, oldSector.Expiration, []abi.DealID{1}) - upgradeParams.ReplaceCapacity = true - upgradeParams.ReplaceSectorDeadline = dlIdx - upgradeParams.ReplaceSectorPartition = partIdx - upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) - - // Check new pre-commit in state - assert.True(t, upgrade.Info.ReplaceCapacity) - assert.Equal(t, upgradeParams.ReplaceSectorNumber, upgrade.Info.ReplaceSectorNumber) - - // Old sector is unchanged - oldSectorAgain := actor.getSector(rt, oldSector.SectorNumber) - assert.Equal(t, oldSector, oldSectorAgain) - - // Deposit and pledge as expected - st = getState(rt) - assert.Equal(t, st.PreCommitDeposits, upgrade.PreCommitDeposit) - assert.Equal(t, st.InitialPledge, oldSector.InitialPledge) - - // Prove new sector - rt.SetEpoch(upgrade.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) - newSector := actor.proveCommitSectorAndConfirm(rt, upgrade, makeProveCommit(upgrade.Info.SectorNumber), proveCommitConf{}) - - // Both sectors' deposits are returned, and pledge is committed - st = getState(rt) - assert.Equal(t, big.Zero(), st.PreCommitDeposits) - assert.Equal(t, st.InitialPledge, big.Add(oldSector.InitialPledge, newSector.InitialPledge)) - // new sector pledge is max of computed pledge and pledge from old sector - assert.Equal(t, oldSector.InitialPledge, newSector.InitialPledge) - - // Both sectors are present (in the same deadline/partition). - deadline, partition := actor.getDeadlineAndPartition(rt, dlIdx, partIdx) - assert.Equal(t, uint64(2), deadline.TotalSectors) - assert.Equal(t, uint64(2), deadline.LiveSectors) - assertEmptyBitfield(t, deadline.EarlyTerminations) - - assertBitfieldEquals(t, partition.Sectors, uint64(newSector.SectorNumber), uint64(oldSector.SectorNumber)) - assertEmptyBitfield(t, partition.Faults) - assertEmptyBitfield(t, partition.Recoveries) - assertEmptyBitfield(t, partition.Terminated) - - // The old sector's expiration has changed to the end of this proving deadline. - // The new one expires when the old one used to. - // The partition is registered with an expiry at both epochs. - dQueue := actor.collectDeadlineExpirations(rt, deadline) - dlInfo := miner.NewDeadlineInfo(st.ProvingPeriodStart, dlIdx, rt.Epoch()) - quantizedExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(oldSector.Expiration) - assert.Equal(t, map[abi.ChainEpoch][]uint64{ - dlInfo.NextNotElapsed().Last(): {uint64(0)}, - quantizedExpiration: {uint64(0)}, - }, dQueue) - - pQueue := actor.collectPartitionExpirations(rt, partition) - assertBitfieldEquals(t, pQueue[dlInfo.NextNotElapsed().Last()].OnTimeSectors, uint64(oldSector.SectorNumber)) - assertBitfieldEquals(t, pQueue[quantizedExpiration].OnTimeSectors, uint64(newSector.SectorNumber)) - - // advance to sector proving deadline - dlInfo = actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - - // PoSt both sectors. They both gain power and no penalties are incurred. - rt.SetEpoch(dlInfo.Last()) - oldPower := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), miner.QAPowerForSector(actor.sectorSize, oldSector)) - newPower := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), miner.QAPowerForSector(actor.sectorSize, newSector)) - expectedPower := oldPower.Add(newPower) - partitions := []miner.PoStPartition{ - {Index: partIdx, Skipped: bitfield.New()}, - } - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{newSector, oldSector}, &poStConfig{ - expectedPowerDelta: expectedPower, - }) - - // replaced sector expires at cron, removing its power and pledge. - expectedPowerDelta := oldPower.Neg() - actor.onDeadlineCron(rt, &cronConfig{ - expiredSectorsPowerDelta: &expectedPowerDelta, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - }) - - t.Run("declare fault for replaced cc upgrade sector doesn't double subtract power", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - oldSector, newSector := actor.commitProveAndUpgradeSector(rt, 100, 200, defaultSectorExpiration, []abi.DealID{1}) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - advanceToEpochWithCron(rt, actor, rt.Epoch()) - // now declare old sector faulty - actor.declareFaults(rt, oldSector) - - pIdx := uint64(0) - deadline, partition := actor.getDeadlineAndPartition(rt, dlIdx, partIdx) - dQueue := actor.collectDeadlineExpirations(rt, deadline) - dlInfo := miner.NewDeadlineInfo(st.ProvingPeriodStart, dlIdx, rt.Epoch()) - expectedReplacedExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(rt.Epoch() + miner.FaultMaxAge) - quantizedExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(oldSector.Expiration) - - // deadling marks expirations for partition at expiration epoch - assert.Equal(t, map[abi.ChainEpoch][]uint64{ - dlInfo.NextNotElapsed().Last(): {pIdx}, - expectedReplacedExpiration: {pIdx}, - quantizedExpiration: {pIdx}, - }, dQueue) - - // but partitions expiration set at that epoch is empty - queue, err := miner.LoadExpirationQueue(rt.AdtStore(), partition.ExpirationsEpochs, miner.QuantSpecForDeadline(dlInfo), miner.PartitionExpirationAmtBitwidth) - require.NoError(t, err) - var es miner.ExpirationSet - expirationSetNotEmpty, err := queue.Get(uint64(expectedReplacedExpiration), &es) - require.NoError(t, err) - assert.False(t, expirationSetNotEmpty) - - // advance to sector proving deadline - dlInfo = actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - - // submit post for new sector. Power is added for new sector and no penalties are paid yet - rt.SetEpoch(dlInfo.Last()) - newPower := miner.QAPowerForSector(actor.sectorSize, newSector) - partitions := []miner.PoStPartition{ - {Index: pIdx, Skipped: bitfield.New()}, - } - // Old sector is faulty, so expect new sector twice in PoSt. - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{newSector, newSector}, &poStConfig{ - expectedPowerDelta: miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), newPower), - }) - - // At proving period cron expect to pay declared fee for old sector - // and to have its pledge requirement deducted indicating it has expired. - // Importantly, power is NOT removed, because it was taken when fault was declared. - oldPower := miner.QAPowerForSector(actor.sectorSize, oldSector) - expectedFee := miner.PledgePenaltyForContinuedFault(actor.epochRewardSmooth, actor.epochQAPowerSmooth, oldPower) - expectedPowerDelta := miner.NewPowerPairZero() - actor.applyRewards(rt, bigRewards, big.Zero()) - actor.onDeadlineCron(rt, &cronConfig{ - continuedFaultsPenalty: expectedFee, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - expiredSectorsPowerDelta: &expectedPowerDelta, - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - }) - - t.Run("skip replaced sector in its last PoSt", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - actor.applyRewards(rt, bigRewards, big.Zero()) - - oldSector, newSector := actor.commitProveAndUpgradeSector(rt, 100, 200, defaultSectorExpiration, []abi.DealID{1}) - - st := getState(rt) - dlIdx, pIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // advance to sector proving deadline - dlInfo := actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - - // skip old sector when submitting post. Expect newSector to fill place of old sector in proof. - // Miner should gain power for new sector, lose power for old sector (with no penalty). - rt.SetEpoch(dlInfo.Last()) - - oldQAPower := miner.QAPowerForSector(actor.sectorSize, oldSector) - newQAPower := miner.QAPowerForSector(actor.sectorSize, newSector) - expectedPowerDelta := miner.NewPowerPair(big.Zero(), big.Sub(newQAPower, oldQAPower)) - - partitions := []miner.PoStPartition{ - {Index: pIdx, Skipped: bf(uint64(oldSector.SectorNumber))}, - } - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{newSector, newSector}, &poStConfig{ - expectedPowerDelta: expectedPowerDelta, - }) - - // At proving period cron expect to pay continued fee for old (now faulty) sector - // and to have its pledge requirement deducted indicating it has expired. - // Importantly, power is NOT removed, because it was taken when sector was skipped in Windowe PoSt. - faultFee := miner.PledgePenaltyForContinuedFault(actor.epochRewardSmooth, actor.epochQAPowerSmooth, oldQAPower) - - actor.onDeadlineCron(rt, &cronConfig{ - continuedFaultsPenalty: faultFee, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - }) - - t.Run("skip PoSt altogether on replaced sector expiry", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - oldSector, _ := actor.commitProveAndUpgradeSector(rt, 100, 200, defaultSectorExpiration, []abi.DealID{1}) - - st := getState(rt) - dlIdx, _, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // advance to sector proving deadline - dlInfo := actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - rt.SetEpoch(dlInfo.Last()) - - // do not PoSt - // expect old sector to lose power (new sector hasn't added any yet) - // both sectors are detected faulty for the first time, with no penalty. - oldQAPower := miner.QAPowerForSector(actor.sectorSize, oldSector) - expectedPowerDelta := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), oldQAPower).Neg() - - // At cron, expect both sectors to be treated as undeclared faults. - // The replaced sector will expire anyway, so its pledge will be removed. - actor.onDeadlineCron(rt, &cronConfig{ - expiredSectorsPowerDelta: &expectedPowerDelta, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - }) - - t.Run("terminate replaced sector early", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // create sector and upgrade it - oldSector, newSector := actor.commitProveAndUpgradeSector(rt, 100, 200, defaultSectorExpiration, []abi.DealID{1}) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // now terminate replaced sector - sectorPower := miner.QAPowerForSector(actor.sectorSize, oldSector) - expectedFee := miner.PledgePenaltyForTermination(oldSector.ExpectedDayReward, rt.Epoch()-oldSector.Activation, - oldSector.ExpectedStoragePledge, actor.epochQAPowerSmooth, sectorPower, actor.epochRewardSmooth, - oldSector.ReplacedDayReward, oldSector.ReplacedSectorAge) - actor.applyRewards(rt, bigRewards, big.Zero()) - powerDelta, pledgeDelta := actor.terminateSectors(rt, bf(uint64(oldSector.SectorNumber)), expectedFee) - - // power and pledge should have been removed - assert.Equal(t, miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), sectorPower).Neg(), powerDelta) - assert.Equal(t, big.Sum(oldSector.InitialPledge.Neg(), expectedFee.Neg()), pledgeDelta) - - // advance to sector proving deadline - dlInfo := actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - - // oldSector is no longer active, so expect newSector twice in validation; once for its proof and once - // to replace oldSector. Power is added for new sector and no penalties are paid. - rt.SetEpoch(dlInfo.Last()) - newPower := miner.QAPowerForSector(actor.sectorSize, newSector) - partitions := []miner.PoStPartition{ - {Index: partIdx, Skipped: bitfield.New()}, - } - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{newSector, newSector}, &poStConfig{ - expectedPowerDelta: miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), newPower), - }) - - // Nothing interesting happens at cron. - // Importantly, power and pledge are NOT removed. This happened when sector was terminated - actor.onDeadlineCron(rt, &cronConfig{ - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - }) - - t.Run("extend a replaced sector's expiration", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // create sector and upgrade it - oldSector, newSector := actor.commitProveAndUpgradeSector(rt, 100, 200, defaultSectorExpiration, []abi.DealID{1}) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - params := &miner.ExtendSectorExpirationParams{ - Extensions: []miner.ExpirationExtension{{ - Deadline: dlIdx, - Partition: partIdx, - Sectors: bf(uint64(oldSector.SectorNumber)), - NewExpiration: rt.Epoch() + 250*builtin.EpochsInDay, - }}, - } - actor.extendSectors(rt, params) - - // advance to sector proving deadline - dlInfo := actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - - // both sectors are now active and not set to expire - rt.SetEpoch(dlInfo.Last()) - newPower := miner.QAPowerForSector(actor.sectorSize, newSector) - partitions := []miner.PoStPartition{ - {Index: partIdx, Skipped: bitfield.New()}, - } - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{oldSector, newSector}, &poStConfig{ - expectedPowerDelta: miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), newPower), - }) - - // Nothing interesting happens at cron because both sectors are active - actor.onDeadlineCron(rt, &cronConfig{ - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - - actor.checkState(rt) - }) - - t.Run("fault and recover a replaced sector", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // create sector and upgrade it - oldSector, newSector := actor.commitProveAndUpgradeSector(rt, 100, 200, defaultSectorExpiration, []abi.DealID{1}) - - advanceToEpochWithCron(rt, actor, rt.Epoch()) - - // declare replaced sector faulty - powerDelta := actor.declareFaults(rt, oldSector) - - // power for old sector should have been removed - oldQAPower := miner.QAPowerForSector(actor.sectorSize, oldSector) - oldSectorPower := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), oldQAPower) - assert.Equal(t, oldSectorPower.Neg(), powerDelta) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // recover replaced sector - actor.declareRecoveries(rt, dlIdx, partIdx, bf(uint64(oldSector.SectorNumber)), big.Zero()) - - // advance to sector proving deadline - dlInfo := actor.deadline(rt) - for dlIdx != dlInfo.Index { - advanceDeadline(rt, actor, &cronConfig{}) - dlInfo = actor.deadline(rt) - } - - // both sectors now need to be proven. - // Upon success, new sector will gain power and replaced sector will briefly regain power. - rt.SetEpoch(dlInfo.Last()) - newQAPower := miner.QAPowerForSector(actor.sectorSize, newSector) - newSectorPower := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), newQAPower) - expectedPowerDelta := oldSectorPower.Add(newSectorPower) - - partitions := []miner.PoStPartition{ - {Index: partIdx, Skipped: bitfield.New()}, - } - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{oldSector, newSector}, &poStConfig{ - expectedPowerDelta: expectedPowerDelta, - }) - - // At cron replaced sector's power is removed because it expires, and its initial pledge is removed - expectedPowerDelta = oldSectorPower.Neg() - actor.onDeadlineCron(rt, &cronConfig{ - expiredSectorsPowerDelta: &expectedPowerDelta, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - }) - - t.Run("try to upgrade committed capacity sector twice", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // Move the current epoch forward so that the first deadline is a stable candidate for both sectors - rt.SetEpoch(periodOffset + miner.WPoStChallengeWindow) - - // Commit a sector to upgrade - // Use the max sector number to make sure everything works. - oldSector := actor.commitAndProveSector(rt, abi.MaxSectorNumber, defaultSectorExpiration, nil) - - // advance cron to activate power. - advanceAndSubmitPoSts(rt, actor, oldSector) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // Reduce the epoch reward so that a new sector's initial pledge would otherwise be lesser. - actor.epochRewardSmooth = smoothing.TestingConstantEstimate(big.Div(actor.epochRewardSmooth.Estimate(), big.NewInt(20))) - - challengeEpoch := rt.Epoch() - 1 - - // Upgrade 1 - upgradeParams1 := actor.makePreCommit(200, challengeEpoch, oldSector.Expiration, []abi.DealID{1}) - upgradeParams1.ReplaceCapacity = true - upgradeParams1.ReplaceSectorDeadline = dlIdx - upgradeParams1.ReplaceSectorPartition = partIdx - upgradeParams1.ReplaceSectorNumber = oldSector.SectorNumber - upgrade1 := actor.preCommitSector(rt, upgradeParams1, preCommitConf{}, false) - - // Check new pre-commit in state - assert.True(t, upgrade1.Info.ReplaceCapacity) - assert.Equal(t, upgradeParams1.ReplaceSectorNumber, upgrade1.Info.ReplaceSectorNumber) - - // Upgrade 2 - upgradeParams2 := actor.makePreCommit(201, challengeEpoch, oldSector.Expiration, []abi.DealID{1}) - upgradeParams2.ReplaceCapacity = true - upgradeParams2.ReplaceSectorDeadline = dlIdx - upgradeParams2.ReplaceSectorPartition = partIdx - upgradeParams2.ReplaceSectorNumber = oldSector.SectorNumber - upgrade2 := actor.preCommitSector(rt, upgradeParams2, preCommitConf{}, false) - - // Check new pre-commit in state - assert.True(t, upgrade2.Info.ReplaceCapacity) - assert.Equal(t, upgradeParams2.ReplaceSectorNumber, upgrade2.Info.ReplaceSectorNumber) - - // Old sector is unchanged - oldSectorAgain := actor.getSector(rt, oldSector.SectorNumber) - assert.Equal(t, oldSector, oldSectorAgain) - - // Deposit and pledge as expected - st = getState(rt) - assert.Equal(t, st.PreCommitDeposits, big.Add(upgrade1.PreCommitDeposit, upgrade2.PreCommitDeposit)) - assert.Equal(t, st.InitialPledge, oldSector.InitialPledge) - - // Prove new sectors - rt.SetEpoch(upgrade1.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) - actor.proveCommitSector(rt, upgrade1, makeProveCommit(upgrade1.Info.SectorNumber)) - actor.proveCommitSector(rt, upgrade2, makeProveCommit(upgrade2.Info.SectorNumber)) - - // confirm both. - actor.confirmSectorProofsValid(rt, proveCommitConf{}, upgrade1, upgrade2) - - newSector1 := actor.getSector(rt, upgrade1.Info.SectorNumber) - newSector2 := actor.getSector(rt, upgrade2.Info.SectorNumber) - - // All three sectors pre-commit deposits are released, and have pledge committed. - st = getState(rt) - assert.Equal(t, big.Zero(), st.PreCommitDeposits) - assert.Equal(t, st.InitialPledge, big.Sum( - oldSector.InitialPledge, newSector1.InitialPledge, newSector2.InitialPledge, - )) - // Both new sectors' pledge are at least the old sector's pledge - assert.Equal(t, oldSector.InitialPledge, newSector1.InitialPledge) - assert.Equal(t, oldSector.InitialPledge, newSector2.InitialPledge) - - // All three sectors are present (in the same deadline/partition). - deadline, partition := actor.getDeadlineAndPartition(rt, dlIdx, partIdx) - assert.Equal(t, uint64(3), deadline.TotalSectors) - assert.Equal(t, uint64(3), deadline.LiveSectors) - assertEmptyBitfield(t, deadline.EarlyTerminations) - - assertBitfieldEquals(t, partition.Sectors, - uint64(newSector1.SectorNumber), - uint64(newSector2.SectorNumber), - uint64(oldSector.SectorNumber)) - assertEmptyBitfield(t, partition.Faults) - assertEmptyBitfield(t, partition.Recoveries) - assertEmptyBitfield(t, partition.Terminated) - - // The old sector's expiration has changed to the end of this proving deadline. - // The new one expires when the old one used to. - // The partition is registered with an expiry at both epochs. - dQueue := actor.collectDeadlineExpirations(rt, deadline) - dlInfo := miner.NewDeadlineInfo(st.ProvingPeriodStart, dlIdx, rt.Epoch()) - quantizedExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(oldSector.Expiration) - assert.Equal(t, map[abi.ChainEpoch][]uint64{ - dlInfo.NextNotElapsed().Last(): {uint64(0)}, - quantizedExpiration: {uint64(0)}, - }, dQueue) - - pQueue := actor.collectPartitionExpirations(rt, partition) - assertBitfieldEquals(t, pQueue[dlInfo.NextNotElapsed().Last()].OnTimeSectors, uint64(oldSector.SectorNumber)) - assertBitfieldEquals(t, pQueue[quantizedExpiration].OnTimeSectors, - uint64(newSector1.SectorNumber), uint64(newSector2.SectorNumber), - ) - - // Roll forward to the beginning of the next iteration of this deadline - advanceToEpochWithCron(rt, actor, dlInfo.NextNotElapsed().Open) - - // Fail to submit PoSt. This means that both sectors will be detected faulty (no penalty). - // Expect the old sector to be marked as terminated. - allSectors := []*miner.SectorOnChainInfo{oldSector, newSector1, newSector2} - lostPower := actor.powerPairForSectors(allSectors[:1]).Neg() // new sectors not active yet. - faultExpiration := miner.QuantSpecForDeadline(dlInfo).QuantizeUp(dlInfo.NextNotElapsed().Last() + miner.FaultMaxAge) - - advanceDeadline(rt, actor, &cronConfig{ - detectedFaultsPowerDelta: &lostPower, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - }) - - // The old sector is marked as terminated - st = getState(rt) - deadline, partition = actor.getDeadlineAndPartition(rt, dlIdx, partIdx) - assert.Equal(t, uint64(3), deadline.TotalSectors) - assert.Equal(t, uint64(2), deadline.LiveSectors) - assertBitfieldEquals(t, partition.Sectors, - uint64(newSector1.SectorNumber), - uint64(newSector2.SectorNumber), - uint64(oldSector.SectorNumber), - ) - assertBitfieldEquals(t, partition.Terminated, uint64(oldSector.SectorNumber)) - assertBitfieldEquals(t, partition.Faults, - uint64(newSector1.SectorNumber), - uint64(newSector2.SectorNumber), - ) - newPower := miner.PowerForSectors(actor.sectorSize, allSectors[1:]) - assert.True(t, newPower.Equals(partition.LivePower)) - assert.True(t, newPower.Equals(partition.FaultyPower)) - - // we expect the expiration to be scheduled twice, once early - // and once on-time. - dQueue = actor.collectDeadlineExpirations(rt, deadline) - assert.Equal(t, map[abi.ChainEpoch][]uint64{ - miner.QuantSpecForDeadline(dlInfo).QuantizeUp(newSector1.Expiration): {uint64(0)}, - faultExpiration: {uint64(0)}, - }, dQueue) - - // Old sector gone from pledge - assert.Equal(t, st.InitialPledge, big.Add(newSector1.InitialPledge, newSector2.InitialPledge)) - actor.checkState(rt) - }) -} - func TestWindowPost(t *testing.T) { // Remove this nasty static/global access when policy is encapsulated in a structure. // See https://github.com/filecoin-project/specs-actors/issues/353. @@ -3012,91 +2163,6 @@ func TestTerminateSectors(t *testing.T) { actor.checkState(rt) }) - t.Run("charges correct fee for young termination of committed capacity upgrade", func(t *testing.T) { - actor := newHarness(t, periodOffset) - rt := builderForHarness(actor). - WithBalance(bigBalance, big.Zero()). - Build(t) - actor.constructAndVerify(rt) - - // Move the current epoch forward so that the first deadline is a stable candidate for both sectors - rt.SetEpoch(periodOffset + miner.WPoStChallengeWindow) - - // Commit a sector to upgrade - daysBeforeUpgrade := 4 - // push expiration so we don't hit minimum lifetime limits when upgrading with the same expiration - oldExpiration := defaultSectorExpiration + daysBeforeUpgrade - oldSector := actor.commitAndProveSector(rt, 1, uint64(oldExpiration), nil) - advanceAndSubmitPoSts(rt, actor, oldSector) // activate power - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(t, err) - - // advance clock so upgrade happens later - for i := 0; i < daysBeforeUpgrade; i++ { // 4 * 2880 = 11,520 - advanceAndSubmitPoSts(rt, actor, oldSector) - } - - challengeEpoch := rt.Epoch() - 1 - upgradeParams := actor.makePreCommit(200, challengeEpoch, oldSector.Expiration, []abi.DealID{1}) - upgradeParams.ReplaceCapacity = true - upgradeParams.ReplaceSectorDeadline = dlIdx - upgradeParams.ReplaceSectorPartition = partIdx - upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) - - // Prove new sector - rt.SetEpoch(upgrade.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) - newSector := actor.proveCommitSectorAndConfirm(rt, upgrade, makeProveCommit(upgrade.Info.SectorNumber), proveCommitConf{}) - - // Expect replace parameters have been set - assert.Equal(t, oldSector.ExpectedDayReward, newSector.ReplacedDayReward) - assert.Equal(t, rt.Epoch()-oldSector.Activation, newSector.ReplacedSectorAge) - - // advance to deadline of new and old sectors - dlInfo := actor.currentDeadline(rt) - for dlInfo.Index != dlIdx { - dlInfo = advanceDeadline(rt, actor, &cronConfig{}) - } - /**/ - // PoSt both sectors. They both gain power and no penalties are incurred. - rt.SetEpoch(dlInfo.Last()) - oldPower := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), miner.QAPowerForSector(actor.sectorSize, oldSector)) - newPower := miner.NewPowerPair(big.NewInt(int64(actor.sectorSize)), miner.QAPowerForSector(actor.sectorSize, newSector)) - partitions := []miner.PoStPartition{ - {Index: partIdx, Skipped: bitfield.New()}, - } - actor.submitWindowPoSt(rt, dlInfo, partitions, []*miner.SectorOnChainInfo{newSector, oldSector}, &poStConfig{ - expectedPowerDelta: newPower, - }) - - // replaced sector expires at cron, removing its power and pledge. - expectedPowerDelta := oldPower.Neg() - actor.onDeadlineCron(rt, &cronConfig{ - expiredSectorsPowerDelta: &expectedPowerDelta, - expiredSectorsPledgeDelta: oldSector.InitialPledge.Neg(), - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - actor.checkState(rt) - - // advance clock a little and terminate new sector - rt.SetEpoch(rt.Epoch() + 5_000) - - // Add some locked funds to ensure full termination fee appears as pledge change. - actor.applyRewards(rt, bigRewards, big.Zero()) - - sectorPower := miner.QAPowerForSector(actor.sectorSize, newSector) - twentyDayReward := miner.ExpectedRewardForPower(actor.epochRewardSmooth, actor.epochQAPowerSmooth, sectorPower, miner.InitialPledgeProjectionPeriod) - newSectorAge := rt.Epoch() - newSector.Activation - oldSectorAge := newSector.Activation - oldSector.Activation - expectedFee := miner.PledgePenaltyForTermination(newSector.ExpectedDayReward, newSectorAge, twentyDayReward, - actor.epochQAPowerSmooth, sectorPower, actor.epochRewardSmooth, oldSector.ExpectedDayReward, oldSectorAge) - - sectors := bf(uint64(newSector.SectorNumber)) - actor.terminateSectors(rt, sectors, expectedFee) - actor.checkState(rt) - }) - t.Run("cannot terminate a sector when the challenge window is open", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) @@ -5223,46 +4289,6 @@ func (h *actorHarness) commitAndProveSector(rt *mock.Runtime, sectorNo abi.Secto return sectorInfo } -func (h *actorHarness) commitProveAndUpgradeSector(rt *mock.Runtime, sectorNo, upgradeSectorNo abi.SectorNumber, - lifetimePeriods uint64, dealIDs []abi.DealID, -) (oldSector *miner.SectorOnChainInfo, newSector *miner.SectorOnChainInfo) { - // Move the current epoch forward so that the first deadline is a stable candidate for both sectors - rt.SetEpoch(h.periodOffset + miner.WPoStChallengeWindow) - - // Commit a sector to upgrade - // Use the max sector number to make sure everything works. - oldSector = h.commitAndProveSector(rt, sectorNo, lifetimePeriods, nil) - - // advance cron to activate power. - advanceAndSubmitPoSts(rt, h, oldSector) - - st := getState(rt) - dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSector.SectorNumber) - require.NoError(h.t, err) - - // Reduce the epoch reward so that a new sector's initial pledge would otherwise be lesser. - // It has to be reduced quite a lot to overcome the new sector having more power due to verified deal weight. - h.epochRewardSmooth = smoothing.TestingConstantEstimate(big.Div(h.epochRewardSmooth.Estimate(), big.NewInt(20))) - - challengeEpoch := rt.Epoch() - 1 - upgradeParams := h.makePreCommit(upgradeSectorNo, challengeEpoch, oldSector.Expiration, dealIDs) - upgradeParams.ReplaceCapacity = true - upgradeParams.ReplaceSectorDeadline = dlIdx - upgradeParams.ReplaceSectorPartition = partIdx - upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := h.preCommitSector(rt, upgradeParams, preCommitConf{ - dealWeight: big.Zero(), - verifiedDealWeight: big.NewInt(int64(h.sectorSize)), - dealSpace: h.sectorSize, - }, false) - - // Prove new sector - rt.SetEpoch(upgrade.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) - newSector = h.proveCommitSectorAndConfirm(rt, upgrade, makeProveCommit(upgrade.Info.SectorNumber), proveCommitConf{}) - - return oldSector, newSector -} - // Deprecated // nolint:unused func (h *actorHarness) advancePastProvingPeriodWithCron(rt *mock.Runtime) { diff --git a/actors/builtin/miner/partition_state.go b/actors/builtin/miner/partition_state.go index e029c6263..3062c4d92 100644 --- a/actors/builtin/miner/partition_state.go +++ b/actors/builtin/miner/partition_state.go @@ -414,62 +414,6 @@ func (p *Partition) removeRecoveries(sectorNos bitfield.BitField, power PowerPai return nil } -// RescheduleExpirations moves expiring sectors to the target expiration, -// skipping any sectors it can't find. -// -// The power of the rescheduled sectors is assumed to have not changed since -// initial scheduling. -// -// Note: see the docs on State.RescheduleSectorExpirations for details on why we -// skip sectors/partitions we can't find. -func (p *Partition) RescheduleExpirations( - store adt.Store, sectors Sectors, - newExpiration abi.ChainEpoch, sectorNos bitfield.BitField, - ssize abi.SectorSize, quant builtin.QuantSpec, -) (replaced []*SectorOnChainInfo, err error) { - // Ensure these sectors actually belong to this partition. - present, err := bitfield.IntersectBitField(sectorNos, p.Sectors) - if err != nil { - return nil, err - } - - // Filter out terminated sectors. - live, err := bitfield.SubtractBitField(present, p.Terminated) - if err != nil { - return nil, err - } - - // Filter out faulty sectors. - active, err := bitfield.SubtractBitField(live, p.Faults) - if err != nil { - return nil, err - } - - sectorInfos, err := sectors.Load(active) - if err != nil { - return nil, err - } - - expirations, err := LoadExpirationQueue(store, p.ExpirationsEpochs, quant, PartitionExpirationAmtBitwidth) - if err != nil { - return nil, xerrors.Errorf("failed to load sector expirations: %w", err) - } - if err = expirations.RescheduleExpirations(newExpiration, sectorInfos, ssize); err != nil { - return nil, err - } - p.ExpirationsEpochs, err = expirations.Root() - if err != nil { - return nil, err - } - - // check invariants - if err := p.ValidateState(); err != nil { - return nil, err - } - - return sectorInfos, nil -} - // Replaces a number of "old" sectors with new ones. // The old sectors must not be faulty, terminated, or unproven. // If the same sector is both removed and added, this permits rescheduling *with a change in power*, diff --git a/actors/builtin/miner/partition_state_test.go b/actors/builtin/miner/partition_state_test.go index 465f2600d..5f8de1754 100644 --- a/actors/builtin/miner/partition_state_test.go +++ b/actors/builtin/miner/partition_state_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "sort" "strings" "testing" @@ -274,58 +273,6 @@ func TestPartitions(t *testing.T) { assert.Contains(t, err.Error(), "not all sectors are assigned to the partition") }) - t.Run("reschedules expirations", func(t *testing.T) { - store, partition := setup(t) - - unprovenSector := testSector(13, 7, 55, 65, 1006) - allSectors := append(sectors[:len(sectors):len(sectors)], unprovenSector) - sectorArr := sectorsArr(t, store, allSectors) - - // Mark sector 2 faulty, we should skip it when rescheduling - faultSet := bf(2) - _, _, _, err := partition.RecordFaults(store, sectorArr, faultSet, abi.ChainEpoch(7), sectorSize, quantSpec) - require.NoError(t, err) - - // Add an unproven sector. We _should_ reschedule the expiration. - // This is fine as we don't allow actually _expiring_ sectors - // while there are unproven sectors. - power, err := partition.AddSectors( - store, false, - []*miner.SectorOnChainInfo{unprovenSector}, - sectorSize, quantSpec, - ) - require.NoError(t, err) - expectedPower := miner.PowerForSectors(sectorSize, []*miner.SectorOnChainInfo{unprovenSector}) - assert.True(t, expectedPower.Equals(power)) - - // reschedule - replaced, err := partition.RescheduleExpirations(store, sectorArr, 18, bf(2, 4, 6, 7), sectorSize, quantSpec) - require.NoError(t, err) - - // Assert we returned the sector infos of the replaced sectors - assert.Len(t, replaced, 3) - sort.Slice(replaced, func(i, j int) bool { - return replaced[i].SectorNumber < replaced[j].SectorNumber - }) - assert.Equal(t, abi.SectorNumber(4), replaced[0].SectorNumber) - assert.Equal(t, abi.SectorNumber(6), replaced[1].SectorNumber) - assert.Equal(t, abi.SectorNumber(7), replaced[2].SectorNumber) - - // We need to change the actual sector infos so our queue validation works. - rescheduled := rescheduleSectors(t, 18, allSectors, bf(4, 6, 7)) - - // partition power and sector categorization should remain the same - assertPartitionState(t, store, partition, quantSpec, sectorSize, rescheduled, bf(1, 2, 3, 4, 5, 6, 7), bf(2), bf(), bf(), bf(7)) - - // sectors should move to new expiration group - assertPartitionExpirationQueue(t, store, partition, quantSpec, []expectExpirationGroup{ - {expiration: 5, sectors: bf(1, 2)}, - {expiration: 9, sectors: bf(3)}, - {expiration: 13, sectors: bf(5)}, - {expiration: 21, sectors: bf(4, 6, 7)}, - }) - }) - t.Run("replace sectors", func(t *testing.T) { store, partition := setup(t) @@ -928,20 +875,6 @@ func emptyPartition(t *testing.T, store adt.Store) *miner.Partition { return p } -func rescheduleSectors(t *testing.T, target abi.ChainEpoch, sectors []*miner.SectorOnChainInfo, filter bitfield.BitField) []*miner.SectorOnChainInfo { - toReschedule, err := filter.AllMap(miner.AddressedSectorsMax) - require.NoError(t, err) - output := make([]*miner.SectorOnChainInfo, len(sectors)) - for i, sector := range sectors { - cpy := *sector - if toReschedule[uint64(cpy.SectorNumber)] { - cpy.Expiration = target - } - output[i] = &cpy - } - return output -} - func sectorsAsMap(sectors []*miner.SectorOnChainInfo) map[abi.SectorNumber]*miner.SectorOnChainInfo { m := map[abi.SectorNumber]*miner.SectorOnChainInfo{} for _, s := range sectors { diff --git a/actors/test/committed_capacity_scenario_test.go b/actors/test/committed_capacity_scenario_test.go deleted file mode 100644 index 7f918b054..000000000 --- a/actors/test/committed_capacity_scenario_test.go +++ /dev/null @@ -1,424 +0,0 @@ -package test - -import ( - "context" - "testing" - - "github.com/filecoin-project/go-bitfield" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/specs-actors/v6/actors/builtin" - "github.com/filecoin-project/specs-actors/v6/actors/builtin/market" - "github.com/filecoin-project/specs-actors/v6/actors/builtin/miner" - "github.com/filecoin-project/specs-actors/v6/actors/builtin/power" - "github.com/filecoin-project/specs-actors/v6/actors/builtin/verifreg" - "github.com/filecoin-project/specs-actors/v6/actors/runtime/proof" - "github.com/filecoin-project/specs-actors/v6/support/ipld" - tutil "github.com/filecoin-project/specs-actors/v6/support/testing" - "github.com/filecoin-project/specs-actors/v6/support/vm" -) - -func TestReplaceCommittedCapacitySectorWithDealLadenSector(t *testing.T) { - ctx := context.Background() - v := vm.NewVMWithSingletons(ctx, t, ipld.NewBlockStoreInMemory()) - addrs := vm.CreateAccounts(ctx, t, v, 4, big.Mul(big.NewInt(10_000), vm.FIL), 93837778) - worker, verifier, unverifiedClient, verifiedClient := addrs[0], addrs[1], addrs[2], addrs[3] - - minerBalance := big.Mul(big.NewInt(1_000), vm.FIL) - sectorNumber := abi.SectorNumber(100) - sealedCid := tutil.MakeCID("100", &miner.SealedCIDPrefix) - sealProof := abi.RegisteredSealProof_StackedDrg32GiBV1_1 - - // create miner - params := power.CreateMinerParams{ - Owner: worker, - Worker: worker, - WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - Peer: abi.PeerID("not really a peer id"), - } - ret := vm.ApplyOk(t, v, addrs[0], builtin.StoragePowerActorAddr, minerBalance, builtin.MethodsPower.CreateMiner, ¶ms) - - minerAddrs, ok := ret.(*power.CreateMinerReturn) - require.True(t, ok) - - // - // Precommit, prove and PoSt empty sector (more fully tested in TestCommitPoStFlow) - // - - // precommit sector - preCommitParams := miner.PreCommitSectorParams{ - SealProof: sealProof, - SectorNumber: sectorNumber, - SealedCID: sealedCid, - SealRandEpoch: v.GetEpoch() - 1, - DealIDs: nil, - Expiration: v.GetEpoch() + 211*builtin.EpochsInDay, - } - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams) - - // advance time to max seal duration - proveTime := v.GetEpoch() + miner.MaxProveCommitDuration[sealProof] - v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime) - - // Prove commit sector after max seal duration - v, err := v.WithEpoch(proveTime) - require.NoError(t, err) - proveCommitParams := miner.ProveCommitSectorParams{ - SectorNumber: sectorNumber, - } - vm.ApplyOk(t, v, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitSector, &proveCommitParams) - - // In the same epoch, trigger cron to validate prove commit - vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - // advance to proving period and submit post - dlInfo, pIdx, v := vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, sectorNumber) - - submitParams := miner.SubmitWindowedPoStParams{ - Deadline: dlInfo.Index, - Partitions: []miner.PoStPartition{{ - Index: pIdx, - Skipped: bitfield.New(), - }}, - Proofs: []proof.PoStProof{{ - PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - }}, - ChainCommitEpoch: dlInfo.Challenge, - ChainCommitRand: []byte(vm.RandString), - } - - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) - - // check power table - sectorPower := vm.PowerForMinerSector(t, v, minerAddrs.IDAddress, sectorNumber) - minerPower := vm.MinerPower(t, v, minerAddrs.IDAddress) - networkStats := vm.GetNetworkStats(t, v) - assert.Equal(t, sectorPower.Raw, minerPower.Raw) - assert.Equal(t, sectorPower.QA, minerPower.Raw) - assert.Equal(t, sectorPower.Raw, networkStats.TotalBytesCommitted) - assert.Equal(t, sectorPower.QA, networkStats.TotalQABytesCommitted) - // miner does not meet consensus minimum so actual power is not added - assert.Equal(t, big.Zero(), networkStats.TotalRawBytePower) - assert.Equal(t, big.Zero(), networkStats.TotalQualityAdjPower) - - // - // publish verified and unverified deals - // - - // register verifier then verified client - addVerifierParams := verifreg.AddVerifierParams{ - Address: verifier, - Allowance: abi.NewStoragePower(32 << 40), - } - vm.ApplyOk(t, v, vm.VerifregRoot, builtin.VerifiedRegistryActorAddr, big.Zero(), builtin.MethodsVerifiedRegistry.AddVerifier, &addVerifierParams) - - addClientParams := verifreg.AddVerifiedClientParams{ - Address: verifiedClient, - Allowance: abi.NewStoragePower(32 << 40), - } - vm.ApplyOk(t, v, verifier, builtin.VerifiedRegistryActorAddr, big.Zero(), builtin.MethodsVerifiedRegistry.AddVerifiedClient, &addClientParams) - - // add market collateral for clients and miner - collateral := big.Mul(big.NewInt(3), vm.FIL) - vm.ApplyOk(t, v, unverifiedClient, builtin.StorageMarketActorAddr, collateral, builtin.MethodsMarket.AddBalance, &unverifiedClient) - vm.ApplyOk(t, v, verifiedClient, builtin.StorageMarketActorAddr, collateral, builtin.MethodsMarket.AddBalance, &verifiedClient) - collateral = big.Mul(big.NewInt(64), vm.FIL) - vm.ApplyOk(t, v, worker, builtin.StorageMarketActorAddr, collateral, builtin.MethodsMarket.AddBalance, &minerAddrs.IDAddress) - - // create 3 deals, some verified and some not - dealIDs := []abi.DealID{} - dealStart := v.GetEpoch() + miner.MaxProveCommitDuration[sealProof] - deals := publishDeal(t, v, worker, verifiedClient, minerAddrs.IDAddress, "deal1", 1<<30, true, dealStart, 181*builtin.EpochsInDay) - dealIDs = append(dealIDs, deals.IDs...) - deals = publishDeal(t, v, worker, verifiedClient, minerAddrs.IDAddress, "deal2", 1<<32, true, dealStart, 200*builtin.EpochsInDay) - dealIDs = append(dealIDs, deals.IDs...) - deals = publishDeal(t, v, worker, unverifiedClient, minerAddrs.IDAddress, "deal3", 1<<34, false, dealStart, 210*builtin.EpochsInDay) - dealIDs = append(dealIDs, deals.IDs...) - - // - // Precommit, Prove, Verify and PoSt committed capacity sector - // - - // precommit capacity upgrade sector with deals - upgradeSectorNumber := abi.SectorNumber(101) - upgradeSealedCid := tutil.MakeCID("101", &miner.SealedCIDPrefix) - preCommitParams = miner.PreCommitSectorParams{ - SealProof: sealProof, - SectorNumber: upgradeSectorNumber, - SealedCID: upgradeSealedCid, - SealRandEpoch: v.GetEpoch() - 1, - DealIDs: dealIDs, - Expiration: v.GetEpoch() + 241*builtin.EpochsInDay, - ReplaceCapacity: true, - ReplaceSectorDeadline: dlInfo.Index, - ReplaceSectorPartition: pIdx, - ReplaceSectorNumber: sectorNumber, - } - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams) - - // assert successful precommit invocation - none := []vm.ExpectInvocation{} - vm.ExpectInvocation{ - To: minerAddrs.IDAddress, - Method: builtin.MethodsMiner.PreCommitSector, - Params: vm.ExpectObject(&preCommitParams), - SubInvocations: []vm.ExpectInvocation{ - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward, SubInvocations: none}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower, SubInvocations: none}, - // addtion of deal ids prompts call to verify deals for activation - {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.VerifyDealsForActivation, SubInvocations: none}, - }, - }.Matches(t, v.LastInvocation()) - - t.Run("verified registry bytes are restored when verified deals are not proven", func(t *testing.T) { - tv, err := v.WithEpoch(dealStart + market.DealUpdatesInterval) - require.NoError(t, err) - - // run cron and check for deal expiry in market actor - vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - vm.ExpectInvocation{ - To: builtin.CronActorAddr, - Method: builtin.MethodsCron.EpochTick, - SubInvocations: []vm.ExpectInvocation{ - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{ - {To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.OnDeferredCronEvent, SubInvocations: []vm.ExpectInvocation{ - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, - // pre-commit deposit is burnt - {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}, - }}, - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, - }}, - {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick, SubInvocations: []vm.ExpectInvocation{ - // notify verified registry that used bytes are released - {To: builtin.VerifiedRegistryActorAddr, Method: builtin.MethodsVerifiedRegistry.RestoreBytes}, - {To: builtin.VerifiedRegistryActorAddr, Method: builtin.MethodsVerifiedRegistry.RestoreBytes}, - // slash funds - {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend}, - }}, - }, - }.Matches(t, tv.LastInvocation()) - }) - - // advance time to min seal duration - proveTime = v.GetEpoch() + miner.PreCommitChallengeDelay + 1 - v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime) - - // Prove commit sector after max seal duration - v, err = v.WithEpoch(proveTime) - require.NoError(t, err) - proveCommitParams = miner.ProveCommitSectorParams{ - SectorNumber: upgradeSectorNumber, - } - vm.ApplyOk(t, v, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitSector, &proveCommitParams) - - vm.ExpectInvocation{ - To: minerAddrs.IDAddress, - Method: builtin.MethodsMiner.ProveCommitSector, - Params: vm.ExpectObject(&proveCommitParams), - SubInvocations: []vm.ExpectInvocation{ - {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.ComputeDataCommitment, SubInvocations: none}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.SubmitPoRepForBulkVerify, SubInvocations: none}, - }, - }.Matches(t, v.LastInvocation()) - - // In the same epoch, trigger cron to validate prove commit - vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - vm.ExpectInvocation{ - To: builtin.CronActorAddr, - Method: builtin.MethodsCron.EpochTick, - SubInvocations: []vm.ExpectInvocation{ - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{ - {To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.ConfirmSectorProofsValid, SubInvocations: []vm.ExpectInvocation{ - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, - // deals are now activated - {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.ActivateDeals}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal}, - }}, - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, - }}, - {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick}, - }, - }.Matches(t, v.LastInvocation()) - - // miner still has power for old sector - minerPower = vm.MinerPower(t, v, minerAddrs.IDAddress) - networkStats = vm.GetNetworkStats(t, v) - assert.Equal(t, sectorPower.Raw, minerPower.Raw) - assert.Equal(t, sectorPower.QA, minerPower.Raw) - assert.Equal(t, sectorPower.Raw, networkStats.TotalBytesCommitted) - assert.Equal(t, sectorPower.QA, networkStats.TotalQABytesCommitted) - - // Assert that old sector and new sector have the same deadline. - // This is not generally true, but the current deadline assigment will always put these together when - // no other sectors have been assigned in-between. The following tests assume this fact, and must be - // modified if this no longer holds. - oldDlIdx, _ := vm.SectorDeadline(t, v, minerAddrs.IDAddress, sectorNumber) - newDlIdx, _ := vm.SectorDeadline(t, v, minerAddrs.IDAddress, upgradeSectorNumber) - require.Equal(t, oldDlIdx, newDlIdx) - - t.Run("miner misses first PoSt of replacement sector", func(t *testing.T) { - // advance to proving period end of new sector - dlInfo, _, tv := vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, sectorNumber) - tv, err = tv.WithEpoch(dlInfo.Last()) - require.NoError(t, err) - - // run cron to penalize missing PoSt - vm.ApplyOk(t, tv, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - vm.ExpectInvocation{ - To: builtin.CronActorAddr, - Method: builtin.MethodsCron.EpochTick, - SubInvocations: []vm.ExpectInvocation{ - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.OnEpochTickEnd, SubInvocations: []vm.ExpectInvocation{ - {To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.OnDeferredCronEvent, SubInvocations: []vm.ExpectInvocation{ - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, - // power is removed for old sector and pledge is burnt - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdateClaimedPower}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}, - }}, - {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, - }}, - {To: builtin.StorageMarketActorAddr, Method: builtin.MethodsMarket.CronTick, SubInvocations: []vm.ExpectInvocation{}}, - }, - }.Matches(t, tv.LastInvocation()) - - // miner's power is removed for old sector because it faulted and not added for the new sector. - minerPower = vm.MinerPower(t, tv, minerAddrs.IDAddress) - networkStats = vm.GetNetworkStats(t, tv) - assert.Equal(t, big.Zero(), minerPower.Raw) - assert.Equal(t, big.Zero(), minerPower.Raw) - assert.Equal(t, big.Zero(), networkStats.TotalBytesCommitted) - assert.Equal(t, big.Zero(), networkStats.TotalQABytesCommitted) - }) - - // advance to proving period and submit post - dlInfo, pIdx, v = vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, upgradeSectorNumber) - - t.Run("miner skips replacing sector in first PoSt", func(t *testing.T) { - tv, err := v.WithEpoch(v.GetEpoch()) // create vm copy - require.NoError(t, err) - - submitParams = miner.SubmitWindowedPoStParams{ - Deadline: dlInfo.Index, - Partitions: []miner.PoStPartition{{ - Index: pIdx, - // skip cc upgrade - Skipped: bitfield.NewFromSet([]uint64{uint64(upgradeSectorNumber)}), - }}, - Proofs: []proof.PoStProof{{ - PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - }}, - ChainCommitEpoch: dlInfo.Challenge, - ChainCommitRand: []byte(vm.RandString), - } - vm.ApplyOk(t, tv, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) - - vm.ExpectInvocation{ - To: minerAddrs.IDAddress, - Method: builtin.MethodsMiner.SubmitWindowedPoSt, - Params: vm.ExpectObject(&submitParams), - }.Matches(t, tv.LastInvocation()) - - // old sector power remains (until its proving deadline) - minerPower = vm.MinerPower(t, tv, minerAddrs.IDAddress) - networkStats = vm.GetNetworkStats(t, tv) - assert.Equal(t, sectorPower.Raw, minerPower.Raw) - assert.Equal(t, sectorPower.QA, minerPower.QA) - assert.Equal(t, sectorPower.Raw, networkStats.TotalBytesCommitted) - assert.Equal(t, sectorPower.QA, networkStats.TotalQABytesCommitted) - }) - - t.Run("miner skips replaced sector in its last PoSt", func(t *testing.T) { - tv, err := v.WithEpoch(v.GetEpoch()) // create vm copy - require.NoError(t, err) - - submitParams = miner.SubmitWindowedPoStParams{ - Deadline: dlInfo.Index, - Partitions: []miner.PoStPartition{{ - Index: pIdx, - // skip cc upgrade - Skipped: bitfield.NewFromSet([]uint64{uint64(sectorNumber)}), - }}, - Proofs: []proof.PoStProof{{ - PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - }}, - ChainCommitEpoch: dlInfo.Challenge, - ChainCommitRand: []byte(vm.RandString), - } - vm.ApplyOk(t, tv, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) - - vm.ExpectInvocation{ - To: minerAddrs.IDAddress, - Method: builtin.MethodsMiner.SubmitWindowedPoSt, - Params: vm.ExpectObject(&submitParams), - }.Matches(t, tv.LastInvocation()) - - // old sector power is immediately removed - upgradeSectorPower := vm.PowerForMinerSector(t, v, minerAddrs.IDAddress, upgradeSectorNumber) - minerPower = vm.MinerPower(t, tv, minerAddrs.IDAddress) - networkStats = vm.GetNetworkStats(t, tv) - assert.Equal(t, upgradeSectorPower.Raw, minerPower.Raw) - assert.Equal(t, upgradeSectorPower.QA, minerPower.QA) - assert.Equal(t, upgradeSectorPower.Raw, networkStats.TotalBytesCommitted) - assert.Equal(t, upgradeSectorPower.QA, networkStats.TotalQABytesCommitted) - }) - - submitParams = miner.SubmitWindowedPoStParams{ - Deadline: dlInfo.Index, - Partitions: []miner.PoStPartition{{ - Index: pIdx, - Skipped: bitfield.New(), - }}, - Proofs: []proof.PoStProof{{ - PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - }}, - ChainCommitEpoch: dlInfo.Challenge, - ChainCommitRand: []byte(vm.RandString), - } - vm.ApplyOk(t, v, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) - - vm.ExpectInvocation{ - To: minerAddrs.IDAddress, - Method: builtin.MethodsMiner.SubmitWindowedPoSt, - Params: vm.ExpectObject(&submitParams), - SubInvocations: []vm.ExpectInvocation{ - // This call to the power actor indicates power has been added for the replaced sector - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdateClaimedPower}, - }, - }.Matches(t, v.LastInvocation()) - - // power is upgraded for new sector - // Until the old sector is terminated at its proving period, miner gets combined power for new and old sectors - upgradeSectorPower := vm.PowerForMinerSector(t, v, minerAddrs.IDAddress, upgradeSectorNumber) - combinedPower := upgradeSectorPower.Add(sectorPower) - minerPower = vm.MinerPower(t, v, minerAddrs.IDAddress) - networkStats = vm.GetNetworkStats(t, v) - assert.Equal(t, combinedPower.Raw, minerPower.Raw) - assert.Equal(t, combinedPower.QA, minerPower.QA) - assert.Equal(t, combinedPower.Raw, networkStats.TotalBytesCommitted) - assert.Equal(t, combinedPower.QA, networkStats.TotalQABytesCommitted) - - // proving period cron removes sector reducing the miner's power to that of the new sector - v, err = v.WithEpoch(dlInfo.Last()) - require.NoError(t, err) - vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - // power is removed - // Until the old sector is terminated at its proving period, miner gets combined power for new and old sectors - minerPower = vm.MinerPower(t, v, minerAddrs.IDAddress) - networkStats = vm.GetNetworkStats(t, v) - assert.Equal(t, upgradeSectorPower.Raw, minerPower.Raw) - assert.Equal(t, upgradeSectorPower.QA, minerPower.QA) - assert.Equal(t, upgradeSectorPower.Raw, networkStats.TotalBytesCommitted) - assert.Equal(t, upgradeSectorPower.QA, networkStats.TotalQABytesCommitted) -} diff --git a/actors/test/cron_catches_expiries_scenario_test.go b/actors/test/cron_catches_expiries_scenario_test.go deleted file mode 100644 index 81d998d77..000000000 --- a/actors/test/cron_catches_expiries_scenario_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package test - -import ( - "context" - "testing" - - "github.com/filecoin-project/go-bitfield" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/filecoin-project/specs-actors/v6/actors/builtin" - "github.com/filecoin-project/specs-actors/v6/actors/builtin/miner" - "github.com/filecoin-project/specs-actors/v6/actors/builtin/power" - "github.com/filecoin-project/specs-actors/v6/actors/runtime/proof" - "github.com/filecoin-project/specs-actors/v6/support/ipld" - tutil "github.com/filecoin-project/specs-actors/v6/support/testing" - "github.com/filecoin-project/specs-actors/v6/support/vm" -) - -var fakeChainRandomness = []byte(vm.RandString) - -func TestCronCatchedCCExpirationsAtDeadlineBoundary(t *testing.T) { - ctx := context.Background() - v := vm.NewVMWithSingletons(ctx, t, ipld.NewBlockStoreInMemory()) - addrs := vm.CreateAccounts(ctx, t, v, 2, big.Mul(big.NewInt(10_000), vm.FIL), 93837778) - worker, unverifiedClient := addrs[0], addrs[1] - - minerBalance := big.Mul(big.NewInt(1_000), vm.FIL) - sectorNumber := abi.SectorNumber(100) - sealedCid := tutil.MakeCID("100", &miner.SealedCIDPrefix) - sealProof := abi.RegisteredSealProof_StackedDrg32GiBV1_1 - - // create miner - params := power.CreateMinerParams{ - Owner: worker, - Worker: worker, - WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - Peer: abi.PeerID("not really a peer id"), - } - ret := vm.ApplyOk(t, v, worker, builtin.StoragePowerActorAddr, minerBalance, builtin.MethodsPower.CreateMiner, ¶ms) - - minerAddrs, ok := ret.(*power.CreateMinerReturn) - require.True(t, ok) - - // precommit sector - preCommitParams := miner.PreCommitSectorParams{ - SealProof: sealProof, - SectorNumber: sectorNumber, - SealedCID: sealedCid, - SealRandEpoch: v.GetEpoch() - 1, - DealIDs: nil, - Expiration: v.GetEpoch() + 211*builtin.EpochsInDay, - } - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams) - - // advance time to max seal duration - proveTime := v.GetEpoch() + miner.PreCommitChallengeDelay + 1 - v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime) - - // Prove commit sector after max seal duration - v, err := v.WithEpoch(proveTime) - require.NoError(t, err) - proveCommitParams := miner.ProveCommitSectorParams{ - SectorNumber: sectorNumber, - } - vm.ApplyOk(t, v, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitSector, &proveCommitParams) - - // In the same epoch, trigger cron to validate prove commit - vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - // advance to proving period and submit post - dlInfo, pIdx, v := vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, sectorNumber) - - submitParams := miner.SubmitWindowedPoStParams{ - Deadline: dlInfo.Index, - Partitions: []miner.PoStPartition{{ - Index: pIdx, - Skipped: bitfield.New(), - }}, - Proofs: []proof.PoStProof{{ - PoStProof: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - }}, - ChainCommitEpoch: dlInfo.Challenge, - ChainCommitRand: fakeChainRandomness, - } - - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) - - // add market collateral for client and miner - collateral := big.Mul(big.NewInt(3), vm.FIL) - vm.ApplyOk(t, v, unverifiedClient, builtin.StorageMarketActorAddr, collateral, builtin.MethodsMarket.AddBalance, &unverifiedClient) - collateral = big.Mul(big.NewInt(64), vm.FIL) - vm.ApplyOk(t, v, worker, builtin.StorageMarketActorAddr, collateral, builtin.MethodsMarket.AddBalance, &minerAddrs.IDAddress) - - // create a deal required by upgrade sector - dealIDs := []abi.DealID{} - dealStart := v.GetEpoch() + miner.MaxProveCommitDuration[sealProof] - deals := publishDeal(t, v, worker, unverifiedClient, minerAddrs.IDAddress, "deal1", 1<<30, false, dealStart, 181*builtin.EpochsInDay) - dealIDs = append(dealIDs, deals.IDs...) - - // precommit capacity upgrade sector with deals - upgradeSectorNumber := abi.SectorNumber(101) - upgradeSealedCid := tutil.MakeCID("101", &miner.SealedCIDPrefix) - preCommitParams = miner.PreCommitSectorParams{ - SealProof: sealProof, - SectorNumber: upgradeSectorNumber, - SealedCID: upgradeSealedCid, - SealRandEpoch: v.GetEpoch() - 1, - DealIDs: dealIDs, - Expiration: v.GetEpoch() + 220*builtin.EpochsInDay, - ReplaceCapacity: true, - ReplaceSectorDeadline: dlInfo.Index, - ReplaceSectorPartition: pIdx, - ReplaceSectorNumber: sectorNumber, - } - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams) - - // Advance to beginning of the valid prove-commit window, then advance to proving deadline of original sector. - // This should allow us to prove commit the upgrade on the last epoch of the original sector's proving period. - proveTime = v.GetEpoch() + miner.PreCommitChallengeDelay + 1 - v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime) - dlInfo, _, v = vm.AdvanceTillProvingDeadline(t, v, minerAddrs.IDAddress, sectorNumber) - - // prove original sector so it won't be faulted - submitParams.ChainCommitEpoch = dlInfo.Challenge - vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.SubmitWindowedPoSt, &submitParams) - - // one epoch before deadline close (i.e. Last) is where we might see a problem with cron scheduling of expirations - v, err = v.WithEpoch(dlInfo.Last()) - require.NoError(t, err) - - // miner still has power for old sector - sectorPower := vm.PowerForMinerSector(t, v, minerAddrs.IDAddress, sectorNumber) - minerPower := vm.MinerPower(t, v, minerAddrs.IDAddress) - assert.Equal(t, sectorPower.Raw, minerPower.Raw) - assert.Equal(t, sectorPower.QA, minerPower.QA) - - // Prove commit sector after max seal duration - proveCommitParams = miner.ProveCommitSectorParams{ - SectorNumber: upgradeSectorNumber, - } - vm.ApplyOk(t, v, worker, minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitSector, &proveCommitParams) - - // In the same epoch, trigger cron to validate prove commit - // Replaced sector should be terminated at end of deadline it was replace in, so it should be terminated - // by this call. This requires the miner's proving period handling to be run after commit verification. - vm.ApplyOk(t, v, builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) - - // Loss of power indicates original sector has been terminated at correct time. - minerPower = vm.MinerPower(t, v, minerAddrs.IDAddress) - assert.Equal(t, big.Zero(), minerPower.Raw) - assert.Equal(t, big.Zero(), minerPower.QA) -} diff --git a/support/agent/cases_test.go b/support/agent/cases_test.go index f8e43bb62..4177763ca 100644 --- a/support/agent/cases_test.go +++ b/support/agent/cases_test.go @@ -89,7 +89,7 @@ func Test500Epochs(t *testing.T) { PrecommitRate: 2.0, FaultRate: 0.0001, RecoveryRate: 0.0001, - UpgradeSectors: true, + UpgradeSectors: false, ProofType: abi.RegisteredSealProof_StackedDrg32GiBV1_1, StartingBalance: big.Div(initialBalance, big.NewInt(2)), MinMarketBalance: big.NewInt(1e18), diff --git a/support/agent/miner_agent.go b/support/agent/miner_agent.go index d76cf0928..1299a3ce7 100644 --- a/support/agent/miner_agent.go +++ b/support/agent/miner_agent.go @@ -251,28 +251,29 @@ func (ma *MinerAgent) createPreCommit(s SimState, currentEpoch abi.ChainEpoch) ( // upgrade sector if upgrades are on, this sector has deals, and we have a cc sector isUpgrade := ma.Config.UpgradeSectors && len(dealIds) > 0 && len(ma.ccSectors) > 0 if isUpgrade { - var upgradeNumber uint64 - upgradeNumber, ma.ccSectors = PopRandom(ma.ccSectors, ma.rnd) - - // prevent sim from attempting to upgrade to sector with shorter duration - sinfo, err := ma.sectorInfo(s, upgradeNumber) - if err != nil { - return message{}, err - } - if sinfo.Expiration() > expiration { - params.Expiration = sinfo.Expiration() - } - - dlInfo, pIdx, err := ma.dlInfoForSector(s, upgradeNumber) - if err != nil { - return message{}, err - } - - params.ReplaceCapacity = true - params.ReplaceSectorNumber = abi.SectorNumber(upgradeNumber) - params.ReplaceSectorDeadline = dlInfo.Index - params.ReplaceSectorPartition = pIdx - ma.UpgradedSectors++ + // var upgradeNumber uint64 + // upgradeNumber, ma.ccSectors = PopRandom(ma.ccSectors, ma.rnd) + + // // prevent sim from attempting to upgrade to sector with shorter duration + // sinfo, err := ma.sectorInfo(s, upgradeNumber) + // if err != nil { + // return message{}, err + // } + // if sinfo.Expiration() > expiration { + // params.Expiration = sinfo.Expiration() + // } + + // dlInfo, pIdx, err := ma.dlInfoForSector(s, upgradeNumber) + // if err != nil { + // return message{}, err + // } + + // params.ReplaceCapacity = true + // params.ReplaceSectorNumber = abi.SectorNumber(upgradeNumber) + // params.ReplaceSectorDeadline = dlInfo.Index + // params.ReplaceSectorPartition = pIdx + // ma.UpgradedSectors++ + panic("agent test using old cc upgrade") } // assume PreCommit succeeds and schedule prove commit @@ -735,18 +736,18 @@ func filterSlice(ns []uint64, toRemove map[uint64]bool) []uint64 { return nextLive } -func (ma *MinerAgent) sectorInfo(v SimState, sectorNumber uint64) (SimSectorInfo, error) { - mSt, err := v.MinerState(ma.IDAddress) - if err != nil { - return nil, err - } +// func (ma *MinerAgent) sectorInfo(v SimState, sectorNumber uint64) (SimSectorInfo, error) { +// mSt, err := v.MinerState(ma.IDAddress) +// if err != nil { +// return nil, err +// } - sector, err := mSt.LoadSectorInfo(v.Store(), sectorNumber) - if err != nil { - return nil, err - } - return sector, nil -} +// sector, err := mSt.LoadSectorInfo(v.Store(), sectorNumber) +// if err != nil { +// return nil, err +// } +// return sector, nil +// } func (ma *MinerAgent) dlInfoForSector(v SimState, sectorNumber uint64) (*dline.Info, uint64, error) { mSt, err := v.MinerState(ma.IDAddress) diff --git a/test-vectors/determinism-check b/test-vectors/determinism-check index cf3ebaaa6..edf22bdcc 100644 --- a/test-vectors/determinism-check +++ b/test-vectors/determinism-check @@ -1 +1 @@ -- 42c779cb6aaad0a2891f432046f6673786ae8f6a21332936923ffa59073c9805 +- d5ac20486d88ccfe3e1b524b6b9b85c2a94fc3464175a017c7622b1821c70f03