Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

Revert "Remove cc upgrade" #1475

Merged
merged 1 commit into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions actors/builtin/miner/deadline_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,70 @@ 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 {
Expand Down
38 changes: 38 additions & 0 deletions actors/builtin/miner/deadline_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,44 @@ 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)
Expand Down
19 changes: 19 additions & 0 deletions actors/builtin/miner/expiration_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,25 @@ 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.
Expand Down
86 changes: 86 additions & 0 deletions actors/builtin/miner/expiration_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,48 @@ 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)
Expand Down Expand Up @@ -420,6 +462,43 @@ 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)
Expand Down Expand Up @@ -609,6 +688,13 @@ 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)

Expand Down
57 changes: 50 additions & 7 deletions actors/builtin/miner/miner_actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,8 +720,14 @@ func (a Actor) PreCommitSectorBatch(rt Runtime, params *PreCommitSectorBatchPara
maxActivation := currEpoch + MaxProveCommitDuration[precommit.SealProof]
validateExpiration(rt, maxActivation, precommit.Expiration, precommit.SealProof)

if precommit.ReplaceCapacity {
rt.Abortf(exitcode.SysErrForbidden, "cc upgrade through precommit discontinued, use lightweight cc upgrade instead")
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)
}

sectorsDeals[i] = market.SectorDeals{
Expand Down Expand Up @@ -1078,6 +1084,8 @@ 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
Expand All @@ -1104,6 +1112,15 @@ 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
Expand All @@ -1119,6 +1136,11 @@ 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 {
Expand All @@ -1140,7 +1162,8 @@ 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.
_, replacedAge, replacedDayReward := zeroReplacedSectorParameters()
replacedPledge, replacedAge, replacedDayReward := replacedSectorParameters(rt, precommit, replacedBySectorNumber)
initialPledge = big.Max(initialPledge, replacedPledge)

newSectorInfo := SectorOnChainInfo{
SectorNumber: precommit.Info.SectorNumber,
Expand All @@ -1164,7 +1187,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...)
Expand Down Expand Up @@ -2660,8 +2683,28 @@ func currentDeadlineIndex(currEpoch abi.ChainEpoch, periodStart abi.ChainEpoch)
return uint64((currEpoch - periodStart) / WPoStChallengeWindow)
}

func zeroReplacedSectorParameters() (pledge abi.TokenAmount, age abi.ChainEpoch, dayReward big.Int) {
return big.Zero(), abi.ChainEpoch(0), big.Zero()
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
}

// Update worker address with pending worker key if exists and delay has passed
Expand Down Expand Up @@ -2775,7 +2818,7 @@ func minEpoch(a, b abi.ChainEpoch) abi.ChainEpoch {
return b
}

func maxEpoch(a, b abi.ChainEpoch) abi.ChainEpoch { //nolint:deadcode,unused
func maxEpoch(a, b abi.ChainEpoch) abi.ChainEpoch {
if a > b {
return a
}
Expand Down
Loading