From 38684307aa4c07273a482c087de6704c46db7b7b Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Tue, 13 Apr 2021 22:23:09 -0400 Subject: [PATCH 01/18] Cron defer and recycle logic --- actors/builtin/miner/miner_actor.go | 31 +++++++++++++++++++++++++---- actors/builtin/miner/miner_state.go | 11 ++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index ca927b959..872b33be6 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -711,6 +711,7 @@ func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.E var err error newlyVested := big.Zero() feeToBurn := abi.NewTokenAmount(0) + var needsCron bool rt.StateTransaction(&st, func() { // available balance already accounts for fee debt so it is correct to call // this before RepayDebts. We would have to @@ -791,11 +792,23 @@ func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.E err = st.AddPreCommitExpiry(store, expiryBound, params.SectorNumber) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to add pre-commit expiry to queue") + + // activate miner cron + needsCron = !st.DeadlineCronActive + if needsCron { + st.DeadlineCronActive = true + } }) burnFunds(rt, feeToBurn) rt.StateReadonly(&st) err = st.CheckBalanceInvariants(rt.CurrentBalance()) + if needsCron { + newDlInfo := st.DeadlineInfo(rt.CurrEpoch()) + enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ + EventType: CronEventProvingDeadline, + }) + } builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") notifyPledgeChanged(rt, newlyVested.Neg()) @@ -2025,6 +2038,7 @@ func handleProvingDeadline(rt Runtime) { penaltyTotal := abi.NewTokenAmount(0) pledgeDeltaTotal := abi.NewTokenAmount(0) + var continueCron bool var st State rt.StateTransaction(&st, func() { { @@ -2077,6 +2091,11 @@ func handleProvingDeadline(rt Runtime) { penaltyTotal = big.Add(penaltyFromVesting, penaltyFromBalance) pledgeDeltaTotal = big.Sub(pledgeDeltaTotal, penaltyFromVesting) } + + continueCron = st.ContinueDeadlineCron() + if !continueCron { + st.DeadlineCronActive = false + } }) // Remove power for new faults, and burn penalties. @@ -2085,10 +2104,14 @@ func handleProvingDeadline(rt Runtime) { notifyPledgeChanged(rt, pledgeDeltaTotal) // Schedule cron callback for next deadline's last epoch. - newDlInfo := st.DeadlineInfo(currEpoch) - enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ - EventType: CronEventProvingDeadline, - }) + if continueCron { + newDlInfo := st.DeadlineInfo(currEpoch) + enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ + EventType: CronEventProvingDeadline, + }) + } else { + rt.Log(rtt.INFO, "miner %s going inactive, deadline cron discontinued", rt.Receiver()) + } // Record whether or not we _have_ early terminations now. hasEarlyTerminations := havePendingEarlyTerminations(rt, &st) diff --git a/actors/builtin/miner/miner_state.go b/actors/builtin/miner/miner_state.go index d8c438e30..4e2a35fe4 100644 --- a/actors/builtin/miner/miner_state.go +++ b/actors/builtin/miner/miner_state.go @@ -75,6 +75,9 @@ type State struct { // Deadlines with outstanding fees for early sector termination. EarlyTerminations bitfield.BitField + + // True when miner cron is active, false otherwise + DeadlineCronActive bool } // Bitwidth of AMTs determined empirically from mutation patterns and projections of mainnet data. @@ -226,6 +229,7 @@ func ConstructState(store adt.Store, infoCid cid.Cid, periodStart abi.ChainEpoch CurrentDeadline: deadlineIndex, Deadlines: emptyDeadlinesCid, EarlyTerminations: bitfield.New(), + DeadlineCronActive: false, }, nil } @@ -752,6 +756,13 @@ func (st *State) SaveVestingFunds(store adt.Store, funds *VestingFunds) error { return nil } +// Return true when the miner actor needs to continue scheduling deadline crons +func (st *State) ContinueDeadlineCron() bool { + return !st.PreCommitDeposits.IsZero() || + !st.InitialPledge.IsZero() || + !st.LockedFunds.IsZero() +} + // // Funds and vesting // From a582571cbb551203a90205bbbbc23c2cd299a0d8 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Tue, 13 Apr 2021 23:24:15 -0400 Subject: [PATCH 02/18] Remove assumption that deadline state is current --- actors/builtin/miner/deadlines.go | 11 ++++++++++ actors/builtin/miner/miner_actor.go | 17 ++++++++------- actors/builtin/miner/miner_state.go | 30 ++++++++++++++++++++------- actors/builtin/miner/quantize.go | 9 ++++++++ actors/builtin/miner/quantize_test.go | 21 +++++++++++++++++++ 5 files changed, 74 insertions(+), 14 deletions(-) diff --git a/actors/builtin/miner/deadlines.go b/actors/builtin/miner/deadlines.go index 819a0e9fe..104e5c05c 100644 --- a/actors/builtin/miner/deadlines.go +++ b/actors/builtin/miner/deadlines.go @@ -94,3 +94,14 @@ func deadlineAvailableForCompaction(provingPeriodStart abi.ChainEpoch, dlIdx uin return deadlineIsMutable(provingPeriodStart, dlIdx, currentEpoch) && !deadlineAvailableForOptimisticPoStDispute(provingPeriodStart, dlIdx, currentEpoch) } + +// Determine current period start and deadline index directly from current epoch and +// the offset implied by the proving period. This works correctly even for the state +// of a miner actor without an active deadline cron +func NewDeadlineInfoFromOffsetAndEpoch(periodStartSeed abi.ChainEpoch, currEpoch abi.ChainEpoch) *dline.Info { + q := NewQuantSpec(WPoStProvingPeriod, periodStartSeed) + currentPeriodStart := q.QuantizeDown(currEpoch) + currentDeadlineIdx := uint64((currEpoch-currentPeriodStart)/WPoStChallengeWindow) % WPoStPeriodDeadlines + + return NewDeadlineInfo(currentPeriodStart, currentDeadlineIdx, currEpoch) +} diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index 872b33be6..9b091768a 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -508,7 +508,8 @@ func (a Actor) DisputeWindowedPoSt(rt Runtime, params *DisputeWindowedPoStParams powerDelta := NewPowerPairZero() var st State rt.StateTransaction(&st, func() { - if !deadlineAvailableForOptimisticPoStDispute(st.ProvingPeriodStart, params.Deadline, currEpoch) { + dlInfo := st.DeadlineInfo(currEpoch) + if !deadlineAvailableForOptimisticPoStDispute(dlInfo.PeriodStart, params.Deadline, currEpoch) { rt.Abortf(exitcode.ErrForbidden, "can only dispute window posts during the dispute window (%d epochs after the challenge window closes)", WPoStDisputeWindow) } @@ -519,8 +520,8 @@ func (a Actor) DisputeWindowedPoSt(rt Runtime, params *DisputeWindowedPoStParams // Check proof { // Find the proving period start for the deadline in question. - ppStart := st.ProvingPeriodStart - if st.CurrentDeadline < params.Deadline { + ppStart := dlInfo.PeriodStart + if dlInfo.Index < params.Deadline { ppStart -= WPoStProvingPeriod } targetDeadline := NewDeadlineInfo(ppStart, params.Deadline, currEpoch) @@ -1344,7 +1345,7 @@ func (a Actor) TerminateSectors(rt Runtime, params *TerminateSectorsParams) *Ter err = toProcess.ForEach(func(dlIdx uint64, partitionSectors PartitionSectorMap) error { // If the deadline the current or next deadline to prove, don't allow terminating sectors. // We assume that deadlines are immutable when being proven. - if !deadlineIsMutable(st.ProvingPeriodStart, dlIdx, currEpoch) { + if !deadlineIsMutable(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch) { rt.Abortf(exitcode.ErrIllegalArgument, "cannot terminate sectors in immutable deadline %d", dlIdx) } @@ -1440,8 +1441,9 @@ func (a Actor) DeclareFaults(rt Runtime, params *DeclareFaultsParams) *abi.Empty sectors, err := LoadSectors(store, st.Sectors) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") + currEpoch := rt.CurrEpoch() err = toProcess.ForEach(func(dlIdx uint64, pm PartitionSectorMap) error { - targetDeadline, err := declarationDeadlineInfo(st.ProvingPeriodStart, dlIdx, rt.CurrEpoch()) + targetDeadline, err := declarationDeadlineInfo(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "invalid fault declaration deadline %d", dlIdx) err = validateFRDeclarationDeadline(targetDeadline) @@ -1529,8 +1531,9 @@ func (a Actor) DeclareFaultsRecovered(rt Runtime, params *DeclareFaultsRecovered sectors, err := LoadSectors(store, st.Sectors) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to load sectors array") + currEpoch := rt.CurrEpoch() err = toProcess.ForEach(func(dlIdx uint64, pm PartitionSectorMap) error { - targetDeadline, err := declarationDeadlineInfo(st.ProvingPeriodStart, dlIdx, rt.CurrEpoch()) + targetDeadline, err := declarationDeadlineInfo(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "invalid recovery declaration deadline %d", dlIdx) err = validateFRDeclarationDeadline(targetDeadline) builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "failed recovery declaration at deadline %d", dlIdx) @@ -1590,7 +1593,7 @@ func (a Actor) CompactPartitions(rt Runtime, params *CompactPartitionsParams) *a info := getMinerInfo(rt, &st) rt.ValidateImmediateCallerIs(append(info.ControlAddresses, info.Owner, info.Worker)...) - if !deadlineAvailableForCompaction(st.ProvingPeriodStart, params.Deadline, rt.CurrEpoch()) { + if !deadlineAvailableForCompaction(st.CurrentProvingPeriodStart(rt.CurrEpoch()), params.Deadline, rt.CurrEpoch()) { rt.Abortf(exitcode.ErrForbidden, "cannot compact deadline %d during its challenge window, or the prior challenge window, or before %d epochs have passed since its last challenge window ended", params.Deadline, WPoStDisputeWindow) } diff --git a/actors/builtin/miner/miner_state.go b/actors/builtin/miner/miner_state.go index 4e2a35fe4..aaec516ef 100644 --- a/actors/builtin/miner/miner_state.go +++ b/actors/builtin/miner/miner_state.go @@ -277,12 +277,27 @@ func (st *State) SaveInfo(store adt.Store, info *MinerInfo) error { return nil } -// Returns deadline calculations for the current (according to state) proving period. +// Returns deadline calculations for the current proving period, according to the current epoch and constant state offset func (st *State) DeadlineInfo(currEpoch abi.ChainEpoch) *dline.Info { - return NewDeadlineInfo(st.ProvingPeriodStart, st.CurrentDeadline, currEpoch) + return NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, currEpoch) } -// Returns deadline calculations for the current (according to state) proving period. +// Returns deadline calculations for the state recorded proving period and deadline. This is out of date if the a +// miner does not have an active miner cron +func (st *State) RecordedDeadlineInfo(currEpoch abi.ChainEpoch) (*dline.Info, error) { + if !st.DeadlineCronActive { + return nil, xerrors.New("attempting to use recorded dline info while miner cron inactive") + } + return NewDeadlineInfo(st.ProvingPeriodStart, st.CurrentDeadline, currEpoch), nil +} + +// Returns current proving period start for the current epoch according to the current epoch and constant state offset +func (st *State) CurrentProvingPeriodStart(currEpoch abi.ChainEpoch) abi.ChainEpoch { + dlInfo := st.DeadlineInfo(currEpoch) + return dlInfo.PeriodStart +} + +// Returns deadline calculations for the current (according to state) proving period func (st *State) QuantSpecForDeadline(dlIdx uint64) QuantSpec { return QuantSpecForDeadline(NewDeadlineInfo(st.ProvingPeriodStart, dlIdx, 0)) } @@ -518,7 +533,7 @@ func (st *State) RescheduleSectorExpirations( var allReplaced []*SectorOnChainInfo if err = deadlineSectors.ForEach(func(dlIdx uint64, pm PartitionSectorMap) error { - dlInfo := NewDeadlineInfo(st.ProvingPeriodStart, dlIdx, currEpoch).NextNotElapsed() + dlInfo := NewDeadlineInfo(st.CurrentProvingPeriodStart(currEpoch), dlIdx, currEpoch).NextNotElapsed() newExpiration := dlInfo.Last() dl, err := deadlines.LoadDeadline(store, dlIdx) @@ -560,7 +575,7 @@ func (st *State) AssignSectorsToDeadlines( var deadlineArr [WPoStPeriodDeadlines]*Deadline if err = deadlines.ForEach(store, func(idx uint64, dl *Deadline) error { // Skip deadlines that aren't currently mutable. - if deadlineIsMutable(st.ProvingPeriodStart, idx, currentEpoch) { + if deadlineIsMutable(st.CurrentProvingPeriodStart(currentEpoch), idx, currentEpoch) { deadlineArr[int(idx)] = dl } return nil @@ -1125,9 +1140,10 @@ func (st *State) AdvanceDeadline(store adt.Store, currEpoch abi.ChainEpoch) (*Ad } // Advance to the next deadline (in case we short-circuit below). - st.CurrentDeadline = (st.CurrentDeadline + 1) % WPoStPeriodDeadlines + // Maintaining this state info is a legacy operation no longer required for code correctness + st.CurrentDeadline = (dlInfo.Index + 1) % WPoStPeriodDeadlines if st.CurrentDeadline == 0 { - st.ProvingPeriodStart = st.ProvingPeriodStart + WPoStProvingPeriod + st.ProvingPeriodStart = dlInfo.PeriodStart + WPoStProvingPeriod } deadlines, err := st.LoadDeadlines(store) diff --git a/actors/builtin/miner/quantize.go b/actors/builtin/miner/quantize.go index 6e511af8d..4ed230418 100644 --- a/actors/builtin/miner/quantize.go +++ b/actors/builtin/miner/quantize.go @@ -16,6 +16,15 @@ func (q QuantSpec) QuantizeUp(e abi.ChainEpoch) abi.ChainEpoch { return quantizeUp(e, q.unit, q.offset) } +func (q QuantSpec) QuantizeDown(e abi.ChainEpoch) abi.ChainEpoch { + next := q.QuantizeUp(e) + // QuantizeDown == QuantizeUp iff e is a fixed point of QuantizeUp + if e == next { + return next + } + return next - q.unit +} + var NoQuantization = NewQuantSpec(1, 0) // Rounds e to the nearest exact multiple of the quantization unit offset by diff --git a/actors/builtin/miner/quantize_test.go b/actors/builtin/miner/quantize_test.go index d71d5fe6b..f97e901a1 100644 --- a/actors/builtin/miner/quantize_test.go +++ b/actors/builtin/miner/quantize_test.go @@ -35,3 +35,24 @@ func TestQuantizeUp(t *testing.T) { assert.Equal(t, abi.ChainEpoch(10000), quantizeUp(10000, 100, 2000000)) }) } + +func TestQuantizeDown(t *testing.T) { + t.Run("no quantization", func(t *testing.T) { + q := NoQuantization + assert.Equal(t, abi.ChainEpoch(0), q.QuantizeDown(0)) + assert.Equal(t, abi.ChainEpoch(1), q.QuantizeDown(1)) + assert.Equal(t, abi.ChainEpoch(1337), q.QuantizeDown(1337)) + }) + t.Run("zero offset", func(t *testing.T) { + q := NewQuantSpec(abi.ChainEpoch(10), abi.ChainEpoch(0)) + assert.Equal(t, abi.ChainEpoch(6660), q.QuantizeDown(6666)) + assert.Equal(t, abi.ChainEpoch(50), q.QuantizeDown(50)) + assert.Equal(t, abi.ChainEpoch(50), q.QuantizeDown(59)) + }) + + t.Run("non zero offset", func(t *testing.T) { + q := NewQuantSpec(abi.ChainEpoch(10), abi.ChainEpoch(1)) + assert.Equal(t, abi.ChainEpoch(11), q.QuantizeDown(20)) + assert.Equal(t, abi.ChainEpoch(11), q.QuantizeDown(11)) + }) +} From dc62c91dcf194474cf0ee2151f3d4096a02f8163 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 10:22:17 -0400 Subject: [PATCH 03/18] Remove enrollment from construction --- actors/builtin/miner/miner_actor.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index 9b091768a..f56274337 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -126,11 +126,6 @@ func (a Actor) Constructor(rt Runtime, params *ConstructorParams) *abi.EmptyValu builtin.RequireNoErr(rt, err, exitcode.ErrIllegalState, "failed to construct state") rt.StateCreate(state) - // Register first cron callback for epoch before the next deadline starts. - deadlineClose := periodStart + WPoStChallengeWindow*abi.ChainEpoch(1+deadlineIndex) - enrollCronEvent(rt, deadlineClose-1, &CronEventPayload{ - EventType: CronEventProvingDeadline, - }) return nil } From c2f4b0023b69501112f90f18a403a6bd5cc192d5 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 13:33:27 -0400 Subject: [PATCH 04/18] Tests fixed --- actors/builtin/miner/cbor_gen.go | 26 +- actors/builtin/miner/miner_actor.go | 6 +- actors/builtin/miner/miner_state.go | 7 +- actors/builtin/miner/miner_test.go | 297 +++++++++--------- .../test/modify_pending_proposals_test.go | 16 +- actors/test/commit_post_test.go | 13 +- actors/test/power_scenario_test.go | 47 +-- support/vm/testing.go | 12 +- 8 files changed, 236 insertions(+), 188 deletions(-) diff --git a/actors/builtin/miner/cbor_gen.go b/actors/builtin/miner/cbor_gen.go index 49d52ecf9..1331bc224 100644 --- a/actors/builtin/miner/cbor_gen.go +++ b/actors/builtin/miner/cbor_gen.go @@ -16,7 +16,7 @@ import ( var _ = xerrors.Errorf -var lengthBufState = []byte{142} +var lengthBufState = []byte{143} func (t *State) MarshalCBOR(w io.Writer) error { if t == nil { @@ -112,6 +112,11 @@ func (t *State) MarshalCBOR(w io.Writer) error { if err := t.EarlyTerminations.MarshalCBOR(w); err != nil { return err } + + // t.DeadlineCronActive (bool) (bool) + if err := cbg.WriteBool(w, t.DeadlineCronActive); err != nil { + return err + } return nil } @@ -129,7 +134,7 @@ func (t *State) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 14 { + if extra != 15 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -301,6 +306,23 @@ func (t *State) UnmarshalCBOR(r io.Reader) error { } } + // t.DeadlineCronActive (bool) (bool) + + maj, extra, err = cbg.CborReadHeaderBuf(br, scratch) + if err != nil { + return err + } + if maj != cbg.MajOther { + return fmt.Errorf("booleans must be major type 7") + } + switch extra { + case 20: + t.DeadlineCronActive = false + case 21: + t.DeadlineCronActive = true + default: + return fmt.Errorf("booleans are either major type 7, value 20 or 21 (got %d)", extra) + } return nil } diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index f56274337..aa34ab32e 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -795,11 +795,11 @@ func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.E st.DeadlineCronActive = true } }) - burnFunds(rt, feeToBurn) rt.StateReadonly(&st) err = st.CheckBalanceInvariants(rt.CurrentBalance()) if needsCron { + // fmt.Printf("[PRECOMMIT] miner actor enrolling cron!\n") newDlInfo := st.DeadlineInfo(rt.CurrEpoch()) enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ EventType: CronEventProvingDeadline, @@ -2091,8 +2091,10 @@ func handleProvingDeadline(rt Runtime) { } continueCron = st.ContinueDeadlineCron() + // fmt.Printf("[CRON] continue cron: %t\n", continueCron) if !continueCron { st.DeadlineCronActive = false + // fmt.Printf("resetting cron active to false") } }) @@ -2103,7 +2105,7 @@ func handleProvingDeadline(rt Runtime) { // Schedule cron callback for next deadline's last epoch. if continueCron { - newDlInfo := st.DeadlineInfo(currEpoch) + newDlInfo := st.DeadlineInfo(currEpoch + 1) enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ EventType: CronEventProvingDeadline, }) diff --git a/actors/builtin/miner/miner_state.go b/actors/builtin/miner/miner_state.go index aaec516ef..f2f57d112 100644 --- a/actors/builtin/miner/miner_state.go +++ b/actors/builtin/miner/miner_state.go @@ -284,11 +284,8 @@ func (st *State) DeadlineInfo(currEpoch abi.ChainEpoch) *dline.Info { // Returns deadline calculations for the state recorded proving period and deadline. This is out of date if the a // miner does not have an active miner cron -func (st *State) RecordedDeadlineInfo(currEpoch abi.ChainEpoch) (*dline.Info, error) { - if !st.DeadlineCronActive { - return nil, xerrors.New("attempting to use recorded dline info while miner cron inactive") - } - return NewDeadlineInfo(st.ProvingPeriodStart, st.CurrentDeadline, currEpoch), nil +func (st *State) RecordedDeadlineInfo(currEpoch abi.ChainEpoch) *dline.Info { + return NewDeadlineInfo(st.ProvingPeriodStart, st.CurrentDeadline, currEpoch) } // Returns current proving period start for the current epoch according to the current epoch and constant state offset diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index fddbbfd20..558fdbf29 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -97,9 +97,6 @@ func TestConstruction(t *testing.T) { rt.ExpectSend(worker, builtin.MethodsAccount.PubkeyAddress, nil, big.Zero(), &workerKey, exitcode.Ok) // Register proving period cron. dlIdx := (rt.Epoch() - provingPeriodStart) / miner.WPoStChallengeWindow - firstDeadlineClose := provingPeriodStart + (1+dlIdx)*miner.WPoStChallengeWindow - rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, - makeDeadlineCronEventParams(t, firstDeadlineClose-1), big.Zero(), nil, exitcode.Ok) ret := rt.Call(actor.Constructor, ¶ms) assert.Nil(t, ret) @@ -163,13 +160,8 @@ func TestConstruction(t *testing.T) { WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, } - provingPeriodStart := abi.ChainEpoch(-2222) // This is just set from running the code. rt.ExpectValidateCallerAddr(builtin.InitActorAddr) rt.ExpectSend(worker, builtin.MethodsAccount.PubkeyAddress, nil, big.Zero(), &workerKey, exitcode.Ok) - dlIdx := (rt.Epoch() - provingPeriodStart) / miner.WPoStChallengeWindow - firstDeadlineClose := provingPeriodStart + (1+dlIdx)*miner.WPoStChallengeWindow - rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, - makeDeadlineCronEventParams(t, firstDeadlineClose-1), big.Zero(), nil, exitcode.Ok) ret := rt.Call(actor.Constructor, ¶ms) assert.Nil(t, ret) @@ -417,7 +409,7 @@ func TestCommitments(t *testing.T) { precommit := actor.preCommitSector(rt, precommitParams, preCommitConf{ dealWeight: dealWeight, verifiedDealWeight: verifiedDealWeight, - }) + }, true) // assert precommit exists and meets expectations onChainPrecommit := actor.getPreCommit(rt, sectorNo) @@ -551,7 +543,7 @@ func TestCommitments(t *testing.T) { precommitParams := actor.makePreCommit(sectorNo, precommitEpoch-1, expiration, []abi.DealID{1}) actor.preCommitSector(rt, precommitParams, preCommitConf{ pledgeDelta: &test.expectedPledgeDelta, - }) + }, true) }) } @@ -569,7 +561,7 @@ func TestCommitments(t *testing.T) { expiration := deadline.PeriodEnd() + defaultSectorExpiration*miner.WPoStProvingPeriod rt.ExpectAbort(exitcode.ErrInsufficientFunds, func() { - actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}, true) }) actor.checkState(rt) }) @@ -590,7 +582,7 @@ func TestCommitments(t *testing.T) { rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "deals too large to fit in sector", func() { actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, []abi.DealID{1}), preCommitConf{ dealSpace: actor.sectorSize + 1, - }) + }, true) }) actor.checkState(rt) }) @@ -612,7 +604,7 @@ func TestCommitments(t *testing.T) { st.FeeDebt = abi.NewTokenAmount(9999) rt.ReplaceState(st) - actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}, true) st = getState(rt) assert.Equal(t, big.Zero(), st.FeeDebt) actor.checkState(rt) @@ -628,22 +620,25 @@ func TestCommitments(t *testing.T) { actor.constructAndVerify(rt) deadline := actor.deadline(rt) challengeEpoch := precommitEpoch - 1 + //fmt.Printf("precommit epoch: %d, current epoch %d\n", precommitEpoch, rt.Epoch()) - oldSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil)[0] - + oldSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true)[0] + //fmt.Printf("finished first one\n") + st := getState(rt) + assert.True(t, st.DeadlineCronActive) // Good commitment. expiration := deadline.PeriodEnd() + defaultSectorExpiration*miner.WPoStProvingPeriod - actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}) - + actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}, false) + //fmt.Printf("finished second one\n") // Duplicate pre-commit sector ID rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "already been allocated", func() { - actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}, false) }) rt.Reset() // Sector ID already committed rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "already been allocated", func() { - actor.preCommitSector(rt, actor.makePreCommit(oldSector.SectorNumber, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(oldSector.SectorNumber, challengeEpoch, expiration, nil), preCommitConf{}, false) }) rt.Reset() @@ -651,7 +646,7 @@ func TestCommitments(t *testing.T) { rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "sealed CID had wrong prefix", func() { pc := actor.makePreCommit(102, challengeEpoch, deadline.PeriodEnd(), nil) pc.SealedCID = tutil.MakeCID("Random Data", nil) - actor.preCommitSector(rt, pc, preCommitConf{}) + actor.preCommitSector(rt, pc, preCommitConf{}, false) }) rt.Reset() @@ -659,32 +654,32 @@ func TestCommitments(t *testing.T) { rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "unsupported seal proof type", func() { pc := actor.makePreCommit(102, challengeEpoch, deadline.PeriodEnd(), nil) pc.SealProof = abi.RegisteredSealProof_StackedDrg8MiBV1_1 - actor.preCommitSector(rt, pc, preCommitConf{}) + actor.preCommitSector(rt, pc, preCommitConf{}, false) }) rt.Reset() // Expires at current epoch rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "must be after activation", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, rt.Epoch(), nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, rt.Epoch(), nil), preCommitConf{}, false) }) rt.Reset() // Expires before current epoch rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "must be after activation", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, rt.Epoch()-1, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, rt.Epoch()-1, nil), preCommitConf{}, false) }) rt.Reset() // Expires too early rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "must exceed", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration-20*builtin.EpochsInDay, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration-20*builtin.EpochsInDay, nil), preCommitConf{}, false) }) rt.Reset() // Expires before min duration + max seal duration rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "must exceed", func() { expiration := rt.Epoch() + miner.MinSectorExpiration + miner.MaxProveCommitDuration[actor.sealProofType] - 1 - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}, false) }) rt.Reset() @@ -692,29 +687,29 @@ func TestCommitments(t *testing.T) { rt.SetEpoch(precommitEpoch) rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "invalid expiration", func() { expiration := deadline.PeriodEnd() + miner.WPoStProvingPeriod*(miner.MaxSectorExpirationExtension/miner.WPoStProvingPeriod+1) - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}, false) }) rt.Reset() // Sector ID out of range rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "out of range", func() { - actor.preCommitSector(rt, actor.makePreCommit(abi.MaxSectorNumber+1, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(abi.MaxSectorNumber+1, challengeEpoch, expiration, nil), preCommitConf{}, false) }) rt.Reset() // Seal randomness challenge too far in past tooOldChallengeEpoch := precommitEpoch - miner.ChainFinality - miner.MaxProveCommitDuration[actor.sealProofType] - 1 rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "too old", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, tooOldChallengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, tooOldChallengeEpoch, expiration, nil), preCommitConf{}, false) }) rt.Reset() // Try to precommit while in fee debt with insufficient balance - st := getState(rt) + st = getState(rt) st.FeeDebt = big.Add(rt.Balance(), abi.NewTokenAmount(1e18)) rt.ReplaceState(st) rt.ExpectAbortContainsMessage(exitcode.ErrInsufficientFunds, "unlocked balance can not repay fee debt", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}, false) }) // reset state back to normal st.FeeDebt = big.Zero() @@ -730,7 +725,7 @@ func TestCommitments(t *testing.T) { Type: runtime.ConsensusFaultDoubleForkMining, }) rt.ExpectAbortContainsMessage(exitcode.ErrForbidden, "precommit not allowed during active consensus fault", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration, nil), preCommitConf{}, false) }) // reset state back to normal rt.ReplaceState(st) @@ -771,7 +766,7 @@ func TestCommitments(t *testing.T) { // Make a good commitment for the proof to target. sectorNo := abi.SectorNumber(100) params := actor.makePreCommit(sectorNo, precommitEpoch-1, deadline.PeriodEnd()+defaultSectorExpiration*miner.WPoStProvingPeriod, []abi.DealID{1}) - precommit := actor.preCommitSector(rt, params, preCommitConf{}) + precommit := actor.preCommitSector(rt, params, preCommitConf{}, true) // Sector pre-commitment missing. rt.SetEpoch(precommitEpoch + miner.PreCommitChallengeDelay + 1) @@ -857,7 +852,7 @@ func TestCommitments(t *testing.T) { // Make a good commitment for the proof to target. sectorNo := abi.SectorNumber(100) params := actor.makePreCommit(sectorNo, precommitEpoch-1, deadline.PeriodEnd()+defaultSectorExpiration*miner.WPoStProvingPeriod, []abi.DealID{1}) - precommit := actor.preCommitSector(rt, params, preCommitConf{}) + precommit := actor.preCommitSector(rt, params, preCommitConf{}, true) // add 1000 tokens that vest immediately st := getState(rt) @@ -896,7 +891,7 @@ func TestCommitments(t *testing.T) { sectorNo := abi.SectorNumber(100) params := actor.makePreCommit(sectorNo, precommitEpoch-1, deadline.PeriodEnd()+defaultSectorExpiration*miner.WPoStProvingPeriod, nil) - precommit := actor.preCommitSector(rt, params, preCommitConf{}) + precommit := actor.preCommitSector(rt, params, preCommitConf{}, true) // precommit at correct epoch rt.SetEpoch(rt.Epoch() + miner.PreCommitChallengeDelay + 1) @@ -958,13 +953,13 @@ func TestCommitments(t *testing.T) { expiration := deadline.PeriodEnd() + defaultSectorExpiration*miner.WPoStProvingPeriod precommit := actor.makePreCommit(sectorNo, rt.Epoch()-1, expiration, makeDealIDs(limit+1)) rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "too many deals for sector", func() { - actor.preCommitSector(rt, precommit, preCommitConf{}) + actor.preCommitSector(rt, precommit, preCommitConf{}, true) }) // sector at or below limit succeeds rt, actor, _ = setup(proof) precommit = actor.makePreCommit(sectorNo, rt.Epoch()-1, expiration, makeDealIDs(limit)) - actor.preCommitSector(rt, precommit, preCommitConf{}) + actor.preCommitSector(rt, precommit, preCommitConf{}, true) actor.checkState(rt) } }) @@ -990,11 +985,11 @@ func TestCommitments(t *testing.T) { pc := actor.makePreCommit(104, challengeEpoch, expiration, nil) pc.SealProof = abi.RegisteredSealProof_StackedDrg32GiBV1 rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, pc, preCommitConf{}) + actor.preCommitSector(rt, pc, preCommitConf{}, true) }) rt.Reset() pc.SealProof = abi.RegisteredSealProof_StackedDrg32GiBV1_1 - actor.preCommitSector(rt, pc, preCommitConf{}) + actor.preCommitSector(rt, pc, preCommitConf{}, true) } actor.checkState(rt) @@ -1035,7 +1030,7 @@ func TestCCUpgrade(t *testing.T) { upgradeParams.ReplaceSectorDeadline = dlIdx upgradeParams.ReplaceSectorPartition = partIdx upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}) + upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) // Check new pre-commit in state assert.True(t, upgrade.Info.ReplaceCapacity) @@ -1134,7 +1129,7 @@ func TestCCUpgrade(t *testing.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}}) + oldSectors := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, [][]abi.DealID{nil, {10}}, true) st := getState(rt) dlIdx, partIdx, err := st.FindSector(rt.AdtStore(), oldSectors[0].SectorNumber) @@ -1151,7 +1146,7 @@ func TestCCUpgrade(t *testing.T) { params := *upgradeParams params.DealIDs = nil rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.Reset() } @@ -1159,7 +1154,7 @@ func TestCCUpgrade(t *testing.T) { params := *upgradeParams params.ReplaceSectorNumber = oldSectors[1].SectorNumber rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.Reset() } @@ -1167,7 +1162,7 @@ func TestCCUpgrade(t *testing.T) { params := *upgradeParams params.ReplaceSectorNumber = 999 rt.ExpectAbort(exitcode.ErrNotFound, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.Reset() } @@ -1175,7 +1170,7 @@ func TestCCUpgrade(t *testing.T) { params := *upgradeParams params.ReplaceSectorPartition = 999 rt.ExpectAbortContainsMessage(exitcode.ErrNotFound, "no partition 999", func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.Reset() } @@ -1183,7 +1178,7 @@ func TestCCUpgrade(t *testing.T) { params := *upgradeParams params.Expiration = params.Expiration - miner.WPoStProvingPeriod rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.Reset() } @@ -1217,7 +1212,7 @@ func TestCCUpgrade(t *testing.T) { rt.ReplaceState(st) rt.ExpectAbort(exitcode.ErrForbidden, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.ReplaceState(&prevState) rt.Reset() @@ -1253,14 +1248,14 @@ func TestCCUpgrade(t *testing.T) { rt.ReplaceState(st) rt.ExpectAbort(exitcode.ErrNotFound, func() { - actor.preCommitSector(rt, ¶ms, preCommitConf{}) + actor.preCommitSector(rt, ¶ms, preCommitConf{}, false) }) rt.ReplaceState(&prevState) rt.Reset() } // Demonstrate that the params are otherwise ok - actor.preCommitSector(rt, upgradeParams, preCommitConf{}) + actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) rt.Verify() actor.checkState(rt) }) @@ -1293,7 +1288,7 @@ func TestCCUpgrade(t *testing.T) { upgradeParams.ReplaceSectorDeadline = dlIdx upgradeParams.ReplaceSectorPartition = partIdx upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}) + upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) // Check new pre-commit in state assert.True(t, upgrade.Info.ReplaceCapacity) @@ -1387,6 +1382,7 @@ func TestCCUpgrade(t *testing.T) { 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) @@ -1643,6 +1639,8 @@ func TestCCUpgrade(t *testing.T) { // 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) @@ -1721,7 +1719,7 @@ func TestCCUpgrade(t *testing.T) { upgradeParams1.ReplaceSectorDeadline = dlIdx upgradeParams1.ReplaceSectorPartition = partIdx upgradeParams1.ReplaceSectorNumber = oldSector.SectorNumber - upgrade1 := actor.preCommitSector(rt, upgradeParams1, preCommitConf{}) + upgrade1 := actor.preCommitSector(rt, upgradeParams1, preCommitConf{}, false) // Check new pre-commit in state assert.True(t, upgrade1.Info.ReplaceCapacity) @@ -1733,7 +1731,7 @@ func TestCCUpgrade(t *testing.T) { upgradeParams2.ReplaceSectorDeadline = dlIdx upgradeParams2.ReplaceSectorPartition = partIdx upgradeParams2.ReplaceSectorNumber = oldSector.SectorNumber - upgrade2 := actor.preCommitSector(rt, upgradeParams2, preCommitConf{}) + upgrade2 := actor.preCommitSector(rt, upgradeParams2, preCommitConf{}, false) // Check new pre-commit in state assert.True(t, upgrade2.Info.ReplaceCapacity) @@ -1862,7 +1860,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) store := rt.AdtStore() - sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil)[0] + sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true)[0] pwr := miner.PowerForSector(actor.sectorSize, sector) st := getState(rt) @@ -1932,7 +1930,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) store := rt.AdtStore() - sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil)[0] + sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true)[0] pwr := miner.PowerForSector(actor.sectorSize, sector) st := getState(rt) @@ -1993,7 +1991,7 @@ func TestWindowPost(t *testing.T) { store := rt.AdtStore() // Commit more sectors than fit in one partition in every eligible deadline, overflowing to a second partition. sectorsToCommit := ((miner.WPoStPeriodDeadlines - 2) * actor.partitionSize) + 1 - sectors := actor.commitAndProveSectors(rt, int(sectorsToCommit), defaultSectorExpiration, nil) + sectors := actor.commitAndProveSectors(rt, int(sectorsToCommit), defaultSectorExpiration, nil, true) lastSector := sectors[len(sectors)-1] st := getState(rt) @@ -2063,7 +2061,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - infos := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + infos := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) pwr := miner.PowerForSectors(actor.sectorSize, infos) actor.applyRewards(rt, bigRewards, big.Zero()) @@ -2129,7 +2127,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - infos := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil) + infos := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil, true) actor.applyRewards(rt, bigRewards, big.Zero()) @@ -2196,7 +2194,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - infos := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil) + infos := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil, true) actor.applyRewards(rt, bigRewards, big.Zero()) @@ -2232,7 +2230,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - infos := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil) + infos := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil, true) actor.applyRewards(rt, bigRewards, big.Zero()) @@ -2281,7 +2279,7 @@ func TestWindowPost(t *testing.T) { // create enough sectors that one will be in a different partition n := 95 - infos := actor.commitAndProveSectors(rt, n, defaultSectorExpiration, nil) + infos := actor.commitAndProveSectors(rt, n, defaultSectorExpiration, nil, true) // add lots of funds so we can pay penalties without going into debt st := getState(rt) @@ -2319,7 +2317,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) store := rt.AdtStore() - sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil)[0] + sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true)[0] pwr := miner.PowerForSector(actor.sectorSize, sector) st := getState(rt) @@ -2360,7 +2358,7 @@ func TestWindowPost(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) store := rt.AdtStore() - sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil)[0] + sector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true)[0] st := getState(rt) dlIdx, _, err := st.FindSector(store, sector.SectorNumber) @@ -2443,7 +2441,7 @@ func TestWindowPost(t *testing.T) { numSectors := int(actor.partitionSize * (miner.WPoStPeriodDeadlines - 2)) // creates a partition in every deadline except 0 and 47 - sectors := actor.commitAndProveSectors(rt, numSectors, defaultSectorExpiration, nil) + sectors := actor.commitAndProveSectors(rt, numSectors, defaultSectorExpiration, nil, true) actor.t.Log("here") // prove every sector once to activate power. This @@ -2504,14 +2502,14 @@ func TestProveCommit(t *testing.T) { actor.epochRewardSmooth = smoothing.TestingConstantEstimate(big.NewInt(1e15)) // prove one sector to establish collateral and locked funds - sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // preecommit another sector so we may prove it expiration := defaultSectorExpiration*miner.WPoStProvingPeriod + periodOffset - 1 precommitEpoch := rt.Epoch() + 1 rt.SetEpoch(precommitEpoch) params := actor.makePreCommit(actor.nextSectorNo, rt.Epoch()-1, expiration, nil) - precommit := actor.preCommitSector(rt, params, preCommitConf{}) + precommit := actor.preCommitSector(rt, params, preCommitConf{}, false) // Confirm the unlocked PCD will not cover the new IP assert.True(t, sectors[0].InitialPledge.GreaterThan(precommit.PreCommitDeposit)) @@ -2541,11 +2539,11 @@ func TestProveCommit(t *testing.T) { precommitEpoch := rt.Epoch() + 1 rt.SetEpoch(precommitEpoch) paramsA := actor.makePreCommit(actor.nextSectorNo, rt.Epoch()-1, expiration, []abi.DealID{1}) - preCommitA := actor.preCommitSector(rt, paramsA, preCommitConf{}) + preCommitA := actor.preCommitSector(rt, paramsA, preCommitConf{}, true) sectorNoA := actor.nextSectorNo actor.nextSectorNo++ paramsB := actor.makePreCommit(actor.nextSectorNo, rt.Epoch()-1, expiration, []abi.DealID{2}) - preCommitB := actor.preCommitSector(rt, paramsB, preCommitConf{}) + preCommitB := actor.preCommitSector(rt, paramsB, preCommitConf{}, false) sectorNoB := actor.nextSectorNo // handle both prove commits in the same epoch @@ -2570,30 +2568,18 @@ func TestDeadlineCron(t *testing.T) { builder := builderForHarness(actor). WithBalance(bigBalance, big.Zero()) - t.Run("empty periods", func(t *testing.T) { + t.Run("cron on inactive state", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) st := getState(rt) assert.Equal(t, periodOffset-miner.WPoStProvingPeriod, st.ProvingPeriodStart) + assert.False(t, st.ContinueDeadlineCron()) - // crons before proving period do nothing - secondCronEpoch := periodOffset + miner.WPoStProvingPeriod - 1 - dlinfo := actor.deadline(rt) - for dlinfo.Close < secondCronEpoch { - dlinfo = advanceDeadline(rt, actor, &cronConfig{}) - } - - // The proving period start isn't changed, because the period hadn't started yet. - st = getState(rt) - assert.Equal(t, periodOffset, st.ProvingPeriodStart) + // cron does nothing and does not enroll another cron + deadline := actor.deadline(rt) + rt.SetEpoch(deadline.Last()) + actor.onDeadlineCron(rt, &cronConfig{noEnrollment: true}) - // next cron moves proving period forward and enrolls for next cron - rt.SetEpoch(dlinfo.Last()) - actor.onDeadlineCron(rt, &cronConfig{ - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, - }) - st = getState(rt) - assert.Equal(t, periodOffset+miner.WPoStProvingPeriod, st.ProvingPeriodStart) actor.checkState(rt) }) @@ -2601,7 +2587,7 @@ func TestDeadlineCron(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // advance cron to activate power. advanceAndSubmitPoSts(rt, actor, sectors...) activePower := miner.PowerForSectors(actor.sectorSize, sectors) @@ -2609,6 +2595,7 @@ func TestDeadlineCron(t *testing.T) { st := getState(rt) initialPledge := st.InitialPledge expiration := sectors[0].Expiration + assert.True(t, st.DeadlineCronActive) // setup state to simulate moving forward all the way to expiry dlIdx, _, err := st.FindSector(rt.AdtStore(), sectors[0].SectorNumber) @@ -2624,10 +2611,12 @@ func TestDeadlineCron(t *testing.T) { powerDelta := activePower.Neg() // because we skip forward in state the sector is detected faulty, no penalty advanceDeadline(rt, actor, &cronConfig{ - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, + noEnrollment: true, // the last power has expired so we expect cron to go inactive expiredSectorsPowerDelta: &powerDelta, expiredSectorsPledgeDelta: initialPledge.Neg(), }) + st = getState(rt) + assert.False(t, st.DeadlineCronActive) actor.checkState(rt) }) @@ -2635,7 +2624,7 @@ func TestDeadlineCron(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // advance cron to activate power. advanceAndSubmitPoSts(rt, actor, sectors...) activePower := miner.PowerForSectors(actor.sectorSize, sectors) @@ -2643,6 +2632,7 @@ func TestDeadlineCron(t *testing.T) { st := getState(rt) initialPledge := st.InitialPledge expiration := sectors[0].Expiration + assert.True(t, st.DeadlineCronActive) // setup state to simulate moving forward all the way to expiry dlIdx, _, err := st.FindSector(rt.AdtStore(), sectors[0].SectorNumber) @@ -2668,11 +2658,13 @@ func TestDeadlineCron(t *testing.T) { // because we skip forward in state and don't check post, there's no penalty. // this is the first time the sector is detected faulty advanceDeadline(rt, actor, &cronConfig{ - expectedEnrollment: rt.Epoch() + miner.WPoStChallengeWindow, + noEnrollment: true, expiredSectorsPowerDelta: &powerDelta, expiredSectorsPledgeDelta: initialPledge.Neg(), repaidFeeDebt: initialPledge, // We repay unlocked IP as fees }) + st = getState(rt) + assert.False(t, st.DeadlineCronActive) actor.checkState(rt) }) @@ -2680,12 +2672,12 @@ func TestDeadlineCron(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - activeSectors := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil) + activeSectors := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, nil, true) // advance cron to activate power. advanceAndSubmitPoSts(rt, actor, activeSectors...) activePower := miner.PowerForSectors(actor.sectorSize, activeSectors) - unprovenSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + unprovenSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, false) unprovenPower := miner.PowerForSectors(actor.sectorSize, unprovenSectors) totalPower := unprovenPower.Add(activePower) @@ -2741,7 +2733,7 @@ func TestDeadlineCron(t *testing.T) { actor.checkState(rt) }) - t.Run("test cron run late", func(t *testing.T) { + t.Run("test cron run trigger faults", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) @@ -2749,7 +2741,7 @@ func TestDeadlineCron(t *testing.T) { actor.applyRewards(rt, bigRewards, big.Zero()) // create enough sectors that one will be in a different partition - allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // advance cron to activate power. advanceAndSubmitPoSts(rt, actor, allSectors...) @@ -2764,8 +2756,7 @@ func TestDeadlineCron(t *testing.T) { dlinfo = advanceDeadline(rt, actor, &cronConfig{}) } - // advance clock well past the end of next period (into next deadline period) without calling cron - rt.SetEpoch(dlinfo.Last() + miner.WPoStChallengeWindow + 5) + rt.SetEpoch(dlinfo.Last()) // run cron and expect all sectors to be detected as faults (no penalty) pwr := miner.PowerForSectors(actor.sectorSize, allSectors) @@ -2774,7 +2765,7 @@ func TestDeadlineCron(t *testing.T) { powerDeltaClaim := miner.NewPowerPair(pwr.Raw.Neg(), pwr.QA.Neg()) // expect next cron to be one deadline period after expected cron for this deadline - nextCron := dlinfo.Last() + +miner.WPoStChallengeWindow + nextCron := dlinfo.Last() + miner.WPoStChallengeWindow actor.onDeadlineCron(rt, &cronConfig{ expectedEnrollment: nextCron, @@ -2794,7 +2785,7 @@ func TestDeclareFaults(t *testing.T) { // Get sector into proving state rt := builder.Build(t) actor.constructAndVerify(rt) - allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) pwr := miner.PowerForSectors(actor.sectorSize, allSectors) // add lots of funds so penalties come from vesting funds @@ -2841,7 +2832,7 @@ func TestDeclareRecoveries(t *testing.T) { t.Run("recovery happy path", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - oneSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + oneSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // advance to first proving period and submit so we'll have time to declare the fault next cycle advanceAndSubmitPoSts(rt, actor, oneSector...) @@ -2865,7 +2856,7 @@ func TestDeclareRecoveries(t *testing.T) { t.Run("recovery must pay back fee debt", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - oneSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + oneSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // advance to first proving period and submit so we'll have time to declare the fault next cycle advanceAndSubmitPoSts(rt, actor, oneSector...) @@ -2917,7 +2908,7 @@ func TestDeclareRecoveries(t *testing.T) { t.Run("recovery fails during active consensus fault", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) - oneSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + oneSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) // consensus fault actor.reportConsensusFault(rt, addr.TestAddress, &runtime.ConsensusFault{ @@ -2952,7 +2943,7 @@ func TestExtendSectorExpiration(t *testing.T) { commitSector := func(t *testing.T, rt *mock.Runtime) *miner.SectorOnChainInfo { actor.constructAndVerify(rt) - sectorInfo := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + sectorInfo := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) return sectorInfo[0] } @@ -3114,7 +3105,7 @@ func TestExtendSectorExpiration(t *testing.T) { const sectorCount = 4000 // commit a bunch of sectors to ensure that we get multiple partitions. - sectorInfos := actor.commitAndProveSectors(rt, sectorCount, defaultSectorExpiration, nil) + sectorInfos := actor.commitAndProveSectors(rt, sectorCount, defaultSectorExpiration, nil, true) advanceAndSubmitPoSts(rt, actor, sectorInfos...) newExpiration := sectorInfos[0].Expiration + 42*miner.WPoStProvingPeriod @@ -3244,9 +3235,12 @@ func TestExtendSectorExpiration(t *testing.T) { // advance one more time. No missed PoSt fees are charged. Total Power and pledge are lowered. pwr := miner.PowerForSectors(actor.sectorSize, []*miner.SectorOnChainInfo{newSector}).Neg() advanceDeadline(rt, actor, &cronConfig{ + noEnrollment: true, expiredSectorsPowerDelta: &pwr, expiredSectorsPledgeDelta: newSector.InitialPledge.Neg(), }) + st = getState(rt) + assert.False(t, st.DeadlineCronActive) actor.checkState(rt) }) @@ -3295,7 +3289,7 @@ func TestTerminateSectors(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) rt.SetEpoch(abi.ChainEpoch(1)) - sectorInfo := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + sectorInfo := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) sector := sectorInfo[0] advanceAndSubmitPoSts(rt, actor, sector) @@ -3363,7 +3357,7 @@ func TestTerminateSectors(t *testing.T) { upgradeParams.ReplaceSectorDeadline = dlIdx upgradeParams.ReplaceSectorPartition = partIdx upgradeParams.ReplaceSectorNumber = oldSector.SectorNumber - upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}) + upgrade := actor.preCommitSector(rt, upgradeParams, preCommitConf{}, false) // Prove new sector rt.SetEpoch(upgrade.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) @@ -3398,7 +3392,7 @@ func TestTerminateSectors(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) rt.SetEpoch(abi.ChainEpoch(1)) - sectorInfo := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + sectorInfo := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) sector := sectorInfo[0] st := getState(rt) @@ -3608,7 +3602,7 @@ func TestCompactPartitions(t *testing.T) { rt.SetEpoch(200) // create 4 sectors in partition 0 - info := actor.commitAndProveSectors(rt, 4, defaultSectorExpiration, [][]abi.DealID{{10}, {20}, {30}, {40}}) + info := actor.commitAndProveSectors(rt, 4, defaultSectorExpiration, [][]abi.DealID{{10}, {20}, {30}, {40}}, true) advanceAndSubmitPoSts(rt, actor, info...) // prove and activate power. @@ -3657,7 +3651,7 @@ func TestCompactPartitions(t *testing.T) { rt.SetEpoch(200) // create 2 sectors in partition 0 - info := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, [][]abi.DealID{{10}, {20}}) + info := actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, [][]abi.DealID{{10}, {20}}, true) advanceAndSubmitPoSts(rt, actor, info...) // prove and activate power. // fault sector1 @@ -3687,7 +3681,7 @@ func TestCompactPartitions(t *testing.T) { rt.SetEpoch(deadlineEpoch) // create 2 sectors in partition 0 - actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, [][]abi.DealID{{10}, {20}}) + actor.commitAndProveSectors(rt, 2, defaultSectorExpiration, [][]abi.DealID{{10}, {20}}, true) // Wait WPoStProofChallengePeriod epochs so we can compact the sector. advanceToEpochWithCron(rt, actor, rt.Epoch()+miner.WPoStDisputeWindow) @@ -3798,7 +3792,7 @@ func TestCheckSectorProven(t *testing.T) { Build(t) actor.constructAndVerify(rt) - sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, [][]abi.DealID{{10}}) + sectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, [][]abi.DealID{{10}}, true) actor.checkSectorProven(rt, sectors[0].SectorNumber) actor.checkState(rt) @@ -3879,17 +3873,19 @@ func TestChangeWorkerAddress(t *testing.T) { assert.Equal(t, info.PendingWorkerKey.EffectiveAt, effectiveEpoch) // no change if current epoch is less than effective epoch - actor.advancePastDeadlineEndWithCron(rt) + st := getState(rt) + deadline := st.DeadlineInfo(rt.Epoch()) + rt.SetEpoch(deadline.PeriodEnd()) info = actor.getInfo(rt) require.NotNil(t, info.PendingWorkerKey) require.EqualValues(t, actor.worker, info.Worker) // move to deadline containing effectiveEpoch - advanceToEpochWithCron(rt, actor, effectiveEpoch) + rt.SetEpoch(effectiveEpoch) - // run cron to enact worker change - actor.advancePastDeadlineEndWithCron(rt) + // enact worker change + actor.confirmUpdateWorkerKey(rt) // assert address has changed info = actor.getInfo(rt) @@ -3917,7 +3913,9 @@ func TestChangeWorkerAddress(t *testing.T) { actor.changeWorkerAddress(rt, newWorker1, effectiveEpoch, originalControlAddrs) // no change if current epoch is less than effective epoch - actor.advancePastDeadlineEndWithCron(rt) + st := getState(rt) + deadline := st.DeadlineInfo(rt.Epoch()) + rt.SetEpoch(deadline.PeriodEnd()) // attempt to change address again actor.changeWorkerAddress(rt, newWorker2, rt.Epoch()+miner.WorkerKeyChangeDelay, originalControlAddrs) @@ -3927,8 +3925,8 @@ func TestChangeWorkerAddress(t *testing.T) { assert.Equal(t, info.PendingWorkerKey.NewWorker, newWorker1) assert.Equal(t, info.PendingWorkerKey.EffectiveAt, effectiveEpoch) - advanceToEpochWithCron(rt, actor, effectiveEpoch) - actor.advancePastDeadlineEndWithCron(rt) + rt.SetEpoch(effectiveEpoch) + actor.confirmUpdateWorkerKey(rt) // assert original change is effected info = actor.getInfo(rt) @@ -3976,10 +3974,9 @@ func TestChangeWorkerAddress(t *testing.T) { effectiveEpoch := currentEpoch + miner.WorkerKeyChangeDelay actor.changeWorkerAddress(rt, newWorker, effectiveEpoch, []addr.Address{c1, c2}) - // set current epoch the run deadline cron + // set current epoch and update worker key rt.SetEpoch(effectiveEpoch) - advanceToEpochWithCron(rt, actor, effectiveEpoch) - actor.advancePastDeadlineEndWithCron(rt) + actor.confirmUpdateWorkerKey(rt) // assert both worker and control addresses have changed st := getState(rt) @@ -4655,7 +4652,7 @@ func TestCompactSectorNumbers(t *testing.T) { // Create a sector. rt := builder.Build(t) actor.constructAndVerify(rt) - allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) targetSno := allSectors[0].SectorNumber actor.compactSectorNumbers(rt, bf(uint64(targetSno), uint64(targetSno)+1)) @@ -4668,13 +4665,13 @@ func TestCompactSectorNumbers(t *testing.T) { { precommit := actor.makePreCommit(targetSno+1, precommitEpoch-1, expiration, nil) rt.ExpectAbort(exitcode.ErrIllegalArgument, func() { - actor.preCommitSector(rt, precommit, preCommitConf{}) + actor.preCommitSector(rt, precommit, preCommitConf{}, false) }) } { precommit := actor.makePreCommit(targetSno+2, precommitEpoch-1, expiration, nil) - actor.preCommitSector(rt, precommit, preCommitConf{}) + actor.preCommitSector(rt, precommit, preCommitConf{}, false) } actor.checkState(rt) }) @@ -4683,7 +4680,7 @@ func TestCompactSectorNumbers(t *testing.T) { // Create a sector. rt := builder.Build(t) actor.constructAndVerify(rt) - allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) targetSno := allSectors[0].SectorNumber rt.SetCaller(actor.owner, builtin.AccountActorCodeID) @@ -4700,7 +4697,7 @@ func TestCompactSectorNumbers(t *testing.T) { // Create a sector. rt := builder.Build(t) actor.constructAndVerify(rt) - allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) targetSno := allSectors[0].SectorNumber rt.SetCaller(actor.controlAddrs[0], builtin.AccountActorCodeID) @@ -4717,7 +4714,7 @@ func TestCompactSectorNumbers(t *testing.T) { // Create a sector. rt := builder.Build(t) actor.constructAndVerify(rt) - allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil) + allSectors := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true) targetSno := allSectors[0].SectorNumber rAddr := tutil.NewIDAddr(t, 1005) @@ -4832,12 +4829,6 @@ func (h *actorHarness) constructAndVerify(rt *mock.Runtime) { // Fetch worker pubkey. rt.ExpectSend(h.worker, builtin.MethodsAccount.PubkeyAddress, nil, big.Zero(), &h.key, exitcode.Ok) // Register proving period cron. - deadlineEnd := h.periodOffset + ((rt.Epoch()-h.periodOffset)/miner.WPoStChallengeWindow)*miner.WPoStChallengeWindow - if rt.Epoch() >= h.periodOffset { - deadlineEnd += miner.WPoStChallengeWindow - } - rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, - makeDeadlineCronEventParams(h.t, deadlineEnd-1), big.Zero(), nil, exitcode.Ok) rt.SetCaller(builtin.InitActorAddr, builtin.InitActorCodeID) ret := rt.Call(h.a.Constructor, ¶ms) assert.Nil(h.t, ret) @@ -4850,7 +4841,7 @@ func (h *actorHarness) constructAndVerify(rt *mock.Runtime) { func (h *actorHarness) deadline(rt *mock.Runtime) *dline.Info { st := getState(rt) - return st.DeadlineInfo(rt.Epoch()) + return st.RecordedDeadlineInfo(rt.Epoch()) } func (h *actorHarness) getPreCommit(rt *mock.Runtime, sno abi.SectorNumber) *miner.SectorPreCommitOnChainInfo { @@ -5089,7 +5080,7 @@ type preCommitConf struct { pledgeDelta *abi.TokenAmount } -func (h *actorHarness) preCommitSector(rt *mock.Runtime, params *miner.PreCommitSectorParams, conf preCommitConf) *miner.SectorPreCommitOnChainInfo { +func (h *actorHarness) preCommitSector(rt *mock.Runtime, params *miner.PreCommitSectorParams, conf preCommitConf, first bool) *miner.SectorPreCommitOnChainInfo { rt.SetCaller(h.worker, builtin.AccountActorCodeID) rt.ExpectValidateCallerAddr(append(h.controlAddrs, h.owner, h.worker)...) @@ -5121,6 +5112,17 @@ func (h *actorHarness) preCommitSector(rt *mock.Runtime, params *miner.PreCommit rt.ExpectSend(builtin.StorageMarketActorAddr, builtin.MethodsMarket.VerifyDealsForActivation, &vdParams, big.Zero(), &vdReturn, exitcode.Ok) } st := getState(rt) + if st.FeeDebt.GreaterThan(big.Zero()) { + rt.ExpectSend(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, st.FeeDebt, nil, exitcode.Ok) + } + + if first { + //fmt.Printf("internal precommit epoch: %d\n", rt.Epoch()) + dlInfo := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, rt.Epoch()) + cronParams := makeDeadlineCronEventParams(h.t, dlInfo.Last()) + //fmt.Printf("internal expected cron params: %v\n", cronParams) + rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, cronParams, big.Zero(), nil, exitcode.Ok) + } if conf.pledgeDelta != nil { if !conf.pledgeDelta.IsZero() { @@ -5133,10 +5135,7 @@ func (h *actorHarness) preCommitSector(rt *mock.Runtime, params *miner.PreCommit } } - if st.FeeDebt.GreaterThan(big.Zero()) { - rt.ExpectSend(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, st.FeeDebt, nil, exitcode.Ok) - } - + //fmt.Printf("call precommit\n") rt.Call(h.a.PreCommitSector, params) rt.Verify() return h.getPreCommit(rt, params.SectorNumber) @@ -5274,7 +5273,7 @@ func (h *actorHarness) proveCommitSectorAndConfirm(rt *mock.Runtime, precommit * // Pre-commits and then proves a number of sectors. // The sectors will expire at the end of lifetimePeriods proving periods after now. // The runtime epoch will be moved forward to the epoch of commitment proofs. -func (h *actorHarness) commitAndProveSectors(rt *mock.Runtime, n int, lifetimePeriods uint64, dealIDs [][]abi.DealID) []*miner.SectorOnChainInfo { +func (h *actorHarness) commitAndProveSectors(rt *mock.Runtime, n int, lifetimePeriods uint64, dealIDs [][]abi.DealID, first bool) []*miner.SectorOnChainInfo { precommitEpoch := rt.Epoch() deadline := h.deadline(rt) expiration := deadline.PeriodEnd() + abi.ChainEpoch(lifetimePeriods)*miner.WPoStProvingPeriod @@ -5288,13 +5287,13 @@ func (h *actorHarness) commitAndProveSectors(rt *mock.Runtime, n int, lifetimePe sectorDealIDs = dealIDs[i] } params := h.makePreCommit(sectorNo, precommitEpoch-1, expiration, sectorDealIDs) - precommit := h.preCommitSector(rt, params, preCommitConf{}) + precommit := h.preCommitSector(rt, params, preCommitConf{}, first && i == 0) precommits[i] = precommit h.nextSectorNo++ } - + //fmt.Printf("advancing to epoch with cron, current epoch %d, advancing to: %d\n", rt.Epoch(), precommitEpoch+miner.PreCommitChallengeDelay+1) advanceToEpochWithCron(rt, h, precommitEpoch+miner.PreCommitChallengeDelay+1) - + //fmt.Printf("finished advancing, current epoch: %d\n", rt.Epoch()) info := []*miner.SectorOnChainInfo{} for _, pc := range precommits { sector := h.proveCommitSectorAndConfirm(rt, pc, makeProveCommit(pc.Info.SectorNumber), proveCommitConf{}) @@ -5321,7 +5320,7 @@ func (h *actorHarness) commitAndProveSector(rt *mock.Runtime, sectorNo abi.Secto // Precommit preCommitParams := h.makePreCommit(sectorNo, precommitEpoch-1, expiration, dealIDs) - precommit := h.preCommitSector(rt, preCommitParams, preCommitConf{}) + precommit := h.preCommitSector(rt, preCommitParams, preCommitConf{}, true) advanceToEpochWithCron(rt, h, precommitEpoch+miner.PreCommitChallengeDelay+1) @@ -5361,7 +5360,7 @@ func (h *actorHarness) commitProveAndUpgradeSector(rt *mock.Runtime, sectorNo, u dealWeight: big.Zero(), verifiedDealWeight: big.NewInt(int64(h.sectorSize)), dealSpace: h.sectorSize, - }) + }, false) // Prove new sector rt.SetEpoch(upgrade.PreCommitEpoch + miner.PreCommitChallengeDelay + 1) @@ -5855,6 +5854,7 @@ func (h *actorHarness) applyRewards(rt *mock.Runtime, amt, penalty abi.TokenAmou } type cronConfig struct { + noEnrollment bool // true if expect not to continue enrollment false otherwise expectedEnrollment abi.ChainEpoch detectedFaultsPowerDelta *miner.PowerPair expiredSectorsPowerDelta *miner.PowerPair @@ -5934,8 +5934,10 @@ func (h *actorHarness) onDeadlineCron(rt *mock.Runtime, config *cronConfig) { } // Re-enrollment for next period. - rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, - makeDeadlineCronEventParams(h.t, config.expectedEnrollment), big.Zero(), nil, exitcode.Ok) + if !config.noEnrollment { + rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, + makeDeadlineCronEventParams(h.t, config.expectedEnrollment), big.Zero(), nil, exitcode.Ok) + } rt.SetCaller(builtin.StoragePowerActorAddr, builtin.StoragePowerActorCodeID) rt.Call(h.a.OnDeferredCronEvent, &miner.CronEventPayload{ @@ -6054,8 +6056,10 @@ func (h *actorHarness) setMultiaddrs(rt *mock.Runtime, newMultiaddrs ...abi.Mult // and then advancing to the first epoch in the new deadline. func advanceDeadline(rt *mock.Runtime, h *actorHarness, config *cronConfig) *dline.Info { deadline := h.deadline(rt) + rt.SetEpoch(deadline.Last()) config.expectedEnrollment = deadline.Last() + miner.WPoStChallengeWindow + //fmt.Printf("advancing to last epoch of current deadline: %d, expect enrollment to: %d\n", deadline.Last(), config.expectedEnrollment) h.onDeadlineCron(rt, config) rt.SetEpoch(deadline.NextOpen()) return h.deadline(rt) @@ -6199,10 +6203,13 @@ func makeDeadlineCronEventParams(t testing.TB, epoch abi.ChainEpoch) *power.Enro buf := bytes.Buffer{} err := eventPayload.MarshalCBOR(&buf) require.NoError(t, err) - return &power.EnrollCronEventParams{ + + params := &power.EnrollCronEventParams{ EventEpoch: epoch, Payload: buf.Bytes(), } + //fmt.Printf("internally expected cron params: %v\n", params) + return params } func makeProveCommit(sectorNo abi.SectorNumber) *miner.ProveCommitSectorParams { diff --git a/actors/migration/nv10/test/modify_pending_proposals_test.go b/actors/migration/nv10/test/modify_pending_proposals_test.go index 5f8f81a44..e10bd7416 100644 --- a/actors/migration/nv10/test/modify_pending_proposals_test.go +++ b/actors/migration/nv10/test/modify_pending_proposals_test.go @@ -22,14 +22,14 @@ import ( ipld2 "github.com/filecoin-project/specs-actors/v2/support/ipld" vm2 "github.com/filecoin-project/specs-actors/v2/support/vm" - builtin3 "github.com/filecoin-project/specs-actors/v4/actors/builtin" - exported3 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported" - market3 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market" - miner3 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" - "github.com/filecoin-project/specs-actors/v4/actors/migration/nv10" - states3 "github.com/filecoin-project/specs-actors/v4/actors/states" - tutil "github.com/filecoin-project/specs-actors/v4/support/testing" - vm3 "github.com/filecoin-project/specs-actors/v4/support/vm" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported" + market3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/market" + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" + "github.com/filecoin-project/specs-actors/v3/actors/migration/nv10" + states3 "github.com/filecoin-project/specs-actors/v3/actors/states" + tutil "github.com/filecoin-project/specs-actors/v3/support/testing" + vm3 "github.com/filecoin-project/specs-actors/v3/support/vm" ) func TestUpdatePendingDealsMigration(t *testing.T) { diff --git a/actors/test/commit_post_test.go b/actors/test/commit_post_test.go index 59c66317a..65cf806e0 100644 --- a/actors/test/commit_post_test.go +++ b/actors/test/commit_post_test.go @@ -32,10 +32,10 @@ func TestCommitPoStFlow(t *testing.T) { // create miner params := power.CreateMinerParams{ - Owner: addrs[0], - Worker: addrs[0], - WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - Peer: abi.PeerID("not really a peer id"), + Owner: addrs[0], + Worker: addrs[0], + 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) @@ -72,7 +72,8 @@ func TestCommitPoStFlow(t *testing.T) { Params: vm.ExpectObject(&preCommitParams), SubInvocations: []vm.ExpectInvocation{ {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}}, + {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, + {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}}, }.Matches(t, v.Invocations()[0]) balances := vm.GetMinerBalances(t, v, minerAddrs.IDAddress) @@ -114,7 +115,7 @@ func TestCommitPoStFlow(t *testing.T) { // The call to burnt funds indicates the overdue precommit has been penalized {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend, Value: vm.ExpectAttoFil(precommit.PreCommitDeposit)}, - {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.EnrollCronEvent}, + // No re-enrollment of cron because burning of PCD discontinues miner cron scheduling }}, //{To: minerAddrs.IDAddress, Method: builtin.MethodsMiner.ConfirmSectorProofsValid}, {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.UpdateNetworkKPI}, diff --git a/actors/test/power_scenario_test.go b/actors/test/power_scenario_test.go index 81f40ca9b..6fb62a9bc 100644 --- a/actors/test/power_scenario_test.go +++ b/actors/test/power_scenario_test.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" "github.com/filecoin-project/specs-actors/v4/support/ipld" + tutil "github.com/filecoin-project/specs-actors/v4/support/testing" vm "github.com/filecoin-project/specs-actors/v4/support/vm" ) @@ -21,10 +22,10 @@ func TestCreateMiner(t *testing.T) { addrs := vm.CreateAccounts(ctx, t, v, 1, big.Mul(big.NewInt(10_000), big.NewInt(1e18)), 93837778) params := power.CreateMinerParams{ - Owner: addrs[0], - Worker: addrs[0], - WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - Peer: abi.PeerID("not really a peer id"), + Owner: addrs[0], + Worker: addrs[0], + WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, + Peer: abi.PeerID("not really a peer id"), } ret := vm.ApplyOk(t, v, addrs[0], builtin.StoragePowerActorAddr, big.NewInt(1e10), builtin.MethodsPower.CreateMiner, ¶ms) @@ -49,17 +50,12 @@ func TestCreateMiner(t *testing.T) { To: minerAddrs.IDAddress, Method: builtin.MethodConstructor, Params: vm.ExpectObject(&miner.ConstructorParams{ - OwnerAddr: params.Owner, - WorkerAddr: params.Worker, - WindowPoStProofType: params.WindowPoStProofType, - PeerId: params.Peer, + OwnerAddr: params.Owner, + WorkerAddr: params.Worker, + WindowPoStProofType: params.WindowPoStProofType, + PeerId: params.Peer, }), - SubInvocations: []vm.ExpectInvocation{{ - // Miner calls back to power actor to enroll its cron event - To: builtin.StoragePowerActorAddr, - Method: builtin.MethodsPower.EnrollCronEvent, - SubInvocations: []vm.ExpectInvocation{}, - }}, + SubInvocations: []vm.ExpectInvocation{}, }}, }}, }.Matches(t, v.Invocations()[0]) @@ -71,10 +67,11 @@ func TestOnEpochTickEnd(t *testing.T) { addrs := vm.CreateAccounts(ctx, t, v, 1, big.Mul(big.NewInt(10_000), big.NewInt(1e18)), 93837778) // create a miner + minerBalance := big.Mul(big.NewInt(10_000), vm.FIL) params := power.CreateMinerParams{Owner: addrs[0], Worker: addrs[0], WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, - Peer: abi.PeerID("pid")} - ret := vm.ApplyOk(t, v, addrs[0], builtin.StoragePowerActorAddr, big.NewInt(1e10), builtin.MethodsPower.CreateMiner, ¶ms) + Peer: abi.PeerID("pid")} + ret := vm.ApplyOk(t, v, addrs[0], builtin.StoragePowerActorAddr, minerBalance, builtin.MethodsPower.CreateMiner, ¶ms) ret, ok := ret.(*power.CreateMinerReturn) require.True(t, ok) @@ -82,8 +79,22 @@ func TestOnEpochTickEnd(t *testing.T) { minerAddrs, ok := ret.(*power.CreateMinerReturn) require.True(t, ok) - // find epoch of miner's next cron task (4 levels deep, first message each level) - cronParams := vm.ParamsForInvocation(t, v, 0, 0, 0, 0) + // create precommit to schedule cron + sealProof := abi.RegisteredSealProof_StackedDrg32GiBV1_1 + sectorNumber := abi.SectorNumber(100) + sealedCid := tutil.MakeCID("100", &miner.SealedCIDPrefix) + preCommitParams := miner.PreCommitSectorParams{ + SealProof: sealProof, + SectorNumber: sectorNumber, + SealedCID: sealedCid, + SealRandEpoch: v.GetEpoch() - 1, + DealIDs: nil, + Expiration: v.GetEpoch() + miner.MinSectorExpiration + miner.MaxProveCommitDuration[sealProof] + 100, + } + vm.ApplyOk(t, v, addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.PreCommitSector, &preCommitParams) + + // find epoch of miner's next cron task (precommit:1, enrollCron:2) + cronParams := vm.ParamsForInvocation(t, v, 1, 2) cronConfig, ok := cronParams.(*power.EnrollCronEventParams) require.True(t, ok) diff --git a/support/vm/testing.go b/support/vm/testing.go index 743c0dfa2..b85e92981 100644 --- a/support/vm/testing.go +++ b/support/vm/testing.go @@ -284,7 +284,15 @@ func MinerDLInfo(t *testing.T, v *VM, minerIDAddr address.Address) *dline.Info { err := v.GetState(minerIDAddr, &minerState) require.NoError(t, err) - return minerState.DeadlineInfo(v.GetEpoch()) + return miner.NewDeadlineInfoFromOffsetAndEpoch(minerState.ProvingPeriodStart, v.GetEpoch()) +} + +func NextMinerDLInfo(t *testing.T, v *VM, minerIDAddr address.Address) *dline.Info { + var minerState miner.State + err := v.GetState(minerIDAddr, &minerState) + require.NoError(t, err) + + return miner.NewDeadlineInfoFromOffsetAndEpoch(minerState.ProvingPeriodStart, v.GetEpoch()+1) } // AdvanceByDeadline creates a new VM advanced to an epoch specified by the predicate while keeping the @@ -299,7 +307,7 @@ func AdvanceByDeadline(t *testing.T, v *VM, minerIDAddr address.Address, predica _, code := v.ApplyMessage(builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) require.Equal(t, exitcode.Ok, code) - dlInfo = MinerDLInfo(t, v, minerIDAddr) + dlInfo = NextMinerDLInfo(t, v, minerIDAddr) } return v, dlInfo } From 320e8666f66094894dff0f5dc583c7cf4cd1bee7 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 16:15:47 -0400 Subject: [PATCH 05/18] Migration --- Makefile | 4 +- actors/builtin/codes.go | 22 +- actors/migration/nv12/miner.go | 49 +++ .../nv12/test/parallel_migration_test.go | 49 +++ actors/migration/nv12/top.go | 323 ++++++++++++++++++ actors/migration/nv12/util.go | 73 ++++ go.mod | 1 + go.sum | 3 + 8 files changed, 511 insertions(+), 13 deletions(-) create mode 100644 actors/migration/nv12/miner.go create mode 100644 actors/migration/nv12/test/parallel_migration_test.go create mode 100644 actors/migration/nv12/top.go create mode 100644 actors/migration/nv12/util.go diff --git a/Makefile b/Makefile index 7ad31fa0b..911abc141 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,11 @@ build: test: $(GO_BIN) test ./... - $(GO_BIN) test -race ./actors/migration/nv10/test + $(GO_BIN) test -race ./actors/migration/nv12/test .PHONY: test test-migration: - $(GO_BIN) test -race ./actors/migration/nv10/test + $(GO_BIN) test -race ./actors/migration/nv12/test .PHONY: test-migration test-coverage: diff --git a/actors/builtin/codes.go b/actors/builtin/codes.go index 8071af748..c48aa2998 100644 --- a/actors/builtin/codes.go +++ b/actors/builtin/codes.go @@ -35,17 +35,17 @@ func init() { builtinActors = make(map[cid.Cid]*actorInfo) for id, info := range map[*cid.Cid]*actorInfo{ //nolint:nomaprange - &SystemActorCodeID: {name: "fil/3/system"}, - &InitActorCodeID: {name: "fil/3/init"}, - &CronActorCodeID: {name: "fil/3/cron"}, - &StoragePowerActorCodeID: {name: "fil/3/storagepower"}, - &StorageMinerActorCodeID: {name: "fil/3/storageminer"}, - &StorageMarketActorCodeID: {name: "fil/3/storagemarket"}, - &PaymentChannelActorCodeID: {name: "fil/3/paymentchannel"}, - &RewardActorCodeID: {name: "fil/3/reward"}, - &VerifiedRegistryActorCodeID: {name: "fil/3/verifiedregistry"}, - &AccountActorCodeID: {name: "fil/3/account", signer: true}, - &MultisigActorCodeID: {name: "fil/3/multisig", signer: true}, + &SystemActorCodeID: {name: "fil/4/system"}, + &InitActorCodeID: {name: "fil/4/init"}, + &CronActorCodeID: {name: "fil/4/cron"}, + &StoragePowerActorCodeID: {name: "fil/4/storagepower"}, + &StorageMinerActorCodeID: {name: "fil/4/storageminer"}, + &StorageMarketActorCodeID: {name: "fil/4/storagemarket"}, + &PaymentChannelActorCodeID: {name: "fil/4/paymentchannel"}, + &RewardActorCodeID: {name: "fil/4/reward"}, + &VerifiedRegistryActorCodeID: {name: "fil/4/verifiedregistry"}, + &AccountActorCodeID: {name: "fil/4/account", signer: true}, + &MultisigActorCodeID: {name: "fil/4/multisig", signer: true}, } { c, err := builder.Sum([]byte(info.name)) if err != nil { diff --git a/actors/migration/nv12/miner.go b/actors/migration/nv12/miner.go new file mode 100644 index 000000000..b5ef86383 --- /dev/null +++ b/actors/migration/nv12/miner.go @@ -0,0 +1,49 @@ +package nv12 + +import ( + "context" + + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" + "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" +) + +type minerMigrator struct{} + +func (m minerMigrator) migrateState(ctx context.Context, store cbor.IpldStore, in actorMigrationInput) (*actorMigrationResult, error) { + var inState miner3.State + if err := store.Get(ctx, in.head, &inState); err != nil { + return nil, err + } + + outState := miner4.State{ + // No change + Info: inState.Info, + PreCommitDeposits: inState.PreCommitDeposits, + LockedFunds: inState.LockedFunds, + VestingFunds: inState.VestingFunds, + FeeDebt: inState.FeeDebt, + InitialPledge: inState.InitialPledge, + PreCommittedSectors: inState.PreCommittedSectors, + PreCommittedSectorsExpiry: inState.PreCommittedSectorsExpiry, + AllocatedSectors: inState.AllocatedSectors, + Sectors: inState.Sectors, + ProvingPeriodStart: inState.ProvingPeriodStart, + CurrentDeadline: inState.CurrentDeadline, + Deadlines: inState.Deadlines, + EarlyTerminations: inState.EarlyTerminations, + // Changed field + DeadlineCronActive: true, + } + newHead, err := store.Put(ctx, &outState) + return &actorMigrationResult{ + newCodeCID: m.migratedCodeCID(), + newHead: newHead, + }, err +} + +func (m minerMigrator) migratedCodeCID() cid.Cid { + return builtin4.StorageMinerActorCodeID +} diff --git a/actors/migration/nv12/test/parallel_migration_test.go b/actors/migration/nv12/test/parallel_migration_test.go new file mode 100644 index 000000000..07bc0aab6 --- /dev/null +++ b/actors/migration/nv12/test/parallel_migration_test.go @@ -0,0 +1,49 @@ +package test_test + +import ( + "context" + "testing" + + "github.com/filecoin-project/go-state-types/abi" + ipld2 "github.com/filecoin-project/specs-actors/v2/support/ipld" + vm3 "github.com/filecoin-project/specs-actors/v3/support/vm" + "github.com/filecoin-project/specs-actors/v4/actors/migration/nv12" + adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" + cbor "github.com/ipfs/go-ipld-cbor" + + "github.com/ipfs/go-cid" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" + "gotest.tools/assert" +) + +func TestParallelMigrationCalls(t *testing.T) { + // Construct simple prior state tree over a synchronized store + ctx := context.Background() + log := nv12.TestLogger{TB: t} + bs := ipld2.NewSyncBlockStoreInMemory() + vm := vm3.NewVMWithSingletons(ctx, t, bs) + + // Run migration + adtStore := adt4.WrapStore(ctx, cbor.NewCborStore(bs)) + startRoot := vm.StateRoot() + endRootSerial, err := nv12.MigrateStateTree(ctx, adtStore, startRoot, abi.ChainEpoch(0), nv12.Config{MaxWorkers: 1}, log, nv12.NewMemMigrationCache()) + require.NoError(t, err) + + // Migrate in parallel + var endRootParallel1, endRootParallel2 cid.Cid + grp, ctx := errgroup.WithContext(ctx) + grp.Go(func() error { + var err1 error + endRootParallel1, err1 = nv12.MigrateStateTree(ctx, adtStore, startRoot, abi.ChainEpoch(0), nv12.Config{MaxWorkers: 2}, log, nv12.NewMemMigrationCache()) + return err1 + }) + grp.Go(func() error { + var err2 error + endRootParallel2, err2 = nv12.MigrateStateTree(ctx, adtStore, startRoot, abi.ChainEpoch(0), nv12.Config{MaxWorkers: 2}, log, nv12.NewMemMigrationCache()) + return err2 + }) + require.NoError(t, grp.Wait()) + assert.Equal(t, endRootSerial, endRootParallel1) + assert.Equal(t, endRootParallel1, endRootParallel2) +} diff --git a/actors/migration/nv12/top.go b/actors/migration/nv12/top.go new file mode 100644 index 000000000..2540fd9c4 --- /dev/null +++ b/actors/migration/nv12/top.go @@ -0,0 +1,323 @@ +package nv12 + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/rt" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + states3 "github.com/filecoin-project/specs-actors/v3/actors/states" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + states4 "github.com/filecoin-project/specs-actors/v4/actors/states" + adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" + + "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + "golang.org/x/sync/errgroup" + "golang.org/x/xerrors" +) + +// Config parameterizes a state tree migration +type Config struct { + // Number of migration worker goroutines to run. + // More workers enables higher CPU utilization doing migration computations (including state encoding) + MaxWorkers uint + // Capacity of the queue of jobs available to workers (zero for unbuffered). + // A queue length of hundreds to thousands improves throughput at the cost of memory. + JobQueueSize uint + // Capacity of the queue receiving migration results from workers, for persisting (zero for unbuffered). + // A queue length of tens to hundreds improves throughput at the cost of memory. + ResultQueueSize uint + // Time between progress logs to emit. + // Zero (the default) results in no progress logs. + ProgressLogPeriod time.Duration +} + +type Logger interface { + // This is the same logging interface provided by the Runtime + Log(level rt.LogLevel, msg string, args ...interface{}) +} + +func ActorHeadKey(addr address.Address, head cid.Cid) string { + return addr.String() + "-h-" + head.String() +} + +// Migrates from v11 to v12 +// +// This migration updates all miner actor states to record their deadline cron status. +// In v12 miner actors defer scheduling deadline cron until beginning to prove storage +// and stop running deadline cron when they stop proving storage so this value can be false. +// All miner actors in v11 have active deadline crons so this field is set to true in migration. +// MigrationCache stores and loads cached data. Its implementation must be threadsafe +type MigrationCache interface { + Write(key string, newCid cid.Cid) error + Read(key string) (bool, cid.Cid, error) + Load(key string, loadFunc func() (cid.Cid, error)) (cid.Cid, error) +} + +// Migrates the filecoin state tree starting from the global state tree and upgrading all actor state. +// The store must support concurrent writes (even if the configured worker count is 1). +func MigrateStateTree(ctx context.Context, store cbor.IpldStore, actorsRootIn cid.Cid, priorEpoch abi.ChainEpoch, cfg Config, log Logger, cache MigrationCache) (cid.Cid, error) { + if cfg.MaxWorkers <= 0 { + return cid.Undef, xerrors.Errorf("invalid migration config with %d workers", cfg.MaxWorkers) + } + + // Maps prior version code CIDs to migration functions. + var migrations = map[cid.Cid]actorMigration{ + builtin3.AccountActorCodeID: nilMigrator{builtin4.AccountActorCodeID}, + builtin3.CronActorCodeID: nilMigrator{builtin4.CronActorCodeID}, + builtin3.InitActorCodeID: nilMigrator{builtin4.InitActorCodeID}, + builtin3.MultisigActorCodeID: nilMigrator{builtin4.MultisigActorCodeID}, + builtin3.PaymentChannelActorCodeID: nilMigrator{builtin4.PaymentChannelActorCodeID}, + builtin3.RewardActorCodeID: nilMigrator{builtin4.RewardActorCodeID}, + builtin3.StorageMarketActorCodeID: nilMigrator{builtin4.StorageMarketActorCodeID}, + builtin3.StorageMinerActorCodeID: cachedMigration(cache, minerMigrator{}), + builtin3.StoragePowerActorCodeID: nilMigrator{builtin4.StoragePowerActorCodeID}, + builtin3.SystemActorCodeID: nilMigrator{builtin4.SystemActorCodeID}, + builtin3.VerifiedRegistryActorCodeID: nilMigrator{builtin4.VerifiedRegistryActorCodeID}, + } + + // Set of prior version code CIDs for actors to defer during iteration, for explicit migration afterwards. + var deferredCodeIDs = map[cid.Cid]struct{}{ + // None + } + + if len(migrations)+len(deferredCodeIDs) != 11 { + panic(fmt.Sprintf("incomplete migration specification with %d code CIDs", len(migrations))) + } + startTime := time.Now() + + // Load input and output state trees + adtStore := adt4.WrapStore(ctx, store) + actorsIn, err := states3.LoadTree(adtStore, actorsRootIn) + if err != nil { + return cid.Undef, err + } + actorsOut, err := states4.NewTree(adtStore) + if err != nil { + return cid.Undef, err + } + + // Setup synchronization + grp, ctx := errgroup.WithContext(ctx) + // Input and output queues for workers. + jobCh := make(chan *migrationJob, cfg.JobQueueSize) + jobResultCh := make(chan *migrationJobResult, cfg.ResultQueueSize) + // Atomically-modified counters for logging progress + var jobCount uint32 + var doneCount uint32 + + // Iterate all actors in old state root to create migration jobs for each non-deferred actor. + grp.Go(func() error { + defer close(jobCh) + log.Log(rt.INFO, "Creating migration jobs for tree %s", actorsRootIn) + if err = actorsIn.ForEach(func(addr address.Address, actorIn *states3.Actor) error { + if _, ok := deferredCodeIDs[actorIn.Code]; ok { + return nil // Deferred for explicit migration later. + } + nextInput := &migrationJob{ + Address: addr, + Actor: *actorIn, // Must take a copy, the pointer is not stable. + cache: cache, + actorMigration: migrations[actorIn.Code], + } + select { + case jobCh <- nextInput: + case <-ctx.Done(): + return ctx.Err() + } + atomic.AddUint32(&jobCount, 1) + return nil + }); err != nil { + return err + } + log.Log(rt.INFO, "Done creating %d migration jobs for tree %s after %v", jobCount, actorsRootIn, time.Since(startTime)) + return nil + }) + + // Worker threads run jobs. + var workerWg sync.WaitGroup + for i := uint(0); i < cfg.MaxWorkers; i++ { + workerWg.Add(1) + workerId := i + grp.Go(func() error { + defer workerWg.Done() + for job := range jobCh { + result, err := job.run(ctx, store, priorEpoch) + if err != nil { + return err + } + select { + case jobResultCh <- result: + case <-ctx.Done(): + return ctx.Err() + } + atomic.AddUint32(&doneCount, 1) + } + log.Log(rt.INFO, "Worker %d done", workerId) + return nil + }) + } + log.Log(rt.INFO, "Started %d workers", cfg.MaxWorkers) + + // Monitor the job queue. This non-critical goroutine is outside the errgroup and exits when + // workersFinished is closed, or the context done. + workersFinished := make(chan struct{}) // Closed when waitgroup is emptied. + if cfg.ProgressLogPeriod > 0 { + go func() { + defer log.Log(rt.DEBUG, "Job queue monitor done") + for { + select { + case <-time.After(cfg.ProgressLogPeriod): + jobsNow := jobCount // Snapshot values to avoid incorrect-looking arithmetic if they change. + doneNow := doneCount + pendingNow := jobsNow - doneNow + elapsed := time.Since(startTime) + rate := float64(doneNow) / elapsed.Seconds() + log.Log(rt.INFO, "%d jobs created, %d done, %d pending after %v (%.0f/s)", + jobsNow, doneNow, pendingNow, elapsed, rate) + case <-workersFinished: + return + case <-ctx.Done(): + return + } + } + }() + } + + // Close result channel when workers are done sending to it. + grp.Go(func() error { + workerWg.Wait() + close(jobResultCh) + close(workersFinished) + log.Log(rt.INFO, "All workers done after %v", time.Since(startTime)) + return nil + }) + + // Insert migrated records in output state tree and accumulators. + grp.Go(func() error { + log.Log(rt.INFO, "Result writer started") + resultCount := 0 + for result := range jobResultCh { + if err := actorsOut.SetActor(result.Address, &result.Actor); err != nil { + return err + } + resultCount++ + } + log.Log(rt.INFO, "Result writer wrote %d results to state tree after %v", resultCount, time.Since(startTime)) + return nil + }) + + if err := grp.Wait(); err != nil { + return cid.Undef, err + } + + return cid.Undef, nil +} + +type actorMigrationInput struct { + address address.Address // actor's address + balance abi.TokenAmount // actor's balance + head cid.Cid // actor's state head CID + priorEpoch abi.ChainEpoch // epoch of last state transition prior to migration + cache MigrationCache // cache of existing cid -> cid migrations for this actor +} + +type actorMigrationResult struct { + newCodeCID cid.Cid + newHead cid.Cid +} + +type actorMigration interface { + // Loads an actor's state from an input store and writes new state to an output store. + // Returns the new state head CID. + migrateState(ctx context.Context, store cbor.IpldStore, input actorMigrationInput) (result *actorMigrationResult, err error) + migratedCodeCID() cid.Cid +} + +type migrationJob struct { + address.Address + states3.Actor + actorMigration + cache MigrationCache +} +type migrationJobResult struct { + address.Address + states4.Actor +} + +func (job *migrationJob) run(ctx context.Context, store cbor.IpldStore, priorEpoch abi.ChainEpoch) (*migrationJobResult, error) { + result, err := job.migrateState(ctx, store, actorMigrationInput{ + address: job.Address, + balance: job.Actor.Balance, + head: job.Actor.Head, + priorEpoch: priorEpoch, + cache: job.cache, + }) + if err != nil { + return nil, xerrors.Errorf("state migration failed for %s actor, addr %s: %w", + builtin3.ActorNameByCode(job.Actor.Code), job.Address, err) + } + + // Set up new actor record with the migrated state. + return &migrationJobResult{ + job.Address, // Unchanged + states4.Actor{ + Code: result.newCodeCID, + Head: result.newHead, + CallSeqNum: job.Actor.CallSeqNum, // Unchanged + Balance: job.Actor.Balance, // Unchanged + }, + }, nil +} + +// Migrator which preserves the head CID and provides a fixed result code CID. +type nilMigrator struct { + OutCodeCID cid.Cid +} + +func (n nilMigrator) migrateState(_ context.Context, _ cbor.IpldStore, in actorMigrationInput) (*actorMigrationResult, error) { + return &actorMigrationResult{ + newCodeCID: n.OutCodeCID, + newHead: in.head, + }, nil +} + +func (n nilMigrator) migratedCodeCID() cid.Cid { + return n.OutCodeCID +} + +// Migrator that uses cached transformation if it exists +type cachedMigrator struct { + cache MigrationCache + actorMigration +} + +func (c cachedMigrator) migrateState(ctx context.Context, store cbor.IpldStore, in actorMigrationInput) (*actorMigrationResult, error) { + newHead, err := c.cache.Load(ActorHeadKey(in.address, in.head), func() (cid.Cid, error) { + result, err := c.actorMigration.migrateState(ctx, store, in) + if err != nil { + return cid.Undef, err + } + return result.newHead, nil + }) + if err != nil { + return nil, err + } + return &actorMigrationResult{ + newCodeCID: c.migratedCodeCID(), + newHead: newHead, + }, nil +} + +func cachedMigration(cache MigrationCache, m actorMigration) actorMigration { + return cachedMigrator{ + actorMigration: m, + cache: cache, + } +} diff --git a/actors/migration/nv12/util.go b/actors/migration/nv12/util.go new file mode 100644 index 000000000..91268dcd0 --- /dev/null +++ b/actors/migration/nv12/util.go @@ -0,0 +1,73 @@ +package nv12 + +import ( + "sync" + "testing" + + "github.com/filecoin-project/go-state-types/rt" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" +) + +type MemMigrationCache struct { + MigrationMap sync.Map +} + +func NewMemMigrationCache() *MemMigrationCache { + return new(MemMigrationCache) +} + +func (m *MemMigrationCache) Write(key string, c cid.Cid) error { + m.MigrationMap.Store(key, c) + return nil +} + +func (m *MemMigrationCache) Read(key string) (bool, cid.Cid, error) { + val, found := m.MigrationMap.Load(key) + if !found { + return false, cid.Undef, nil + } + c, ok := val.(cid.Cid) + if !ok { + return false, cid.Undef, xerrors.Errorf("non cid value in cache") + } + + return true, c, nil +} + +func (m *MemMigrationCache) Load(key string, loadFunc func() (cid.Cid, error)) (cid.Cid, error) { + found, c, err := m.Read(key) + if err != nil { + return cid.Undef, err + } + if found { + return c, nil + } + c, err = loadFunc() + if err != nil { + return cid.Undef, err + } + m.MigrationMap.Store(key, c) + return c, nil +} + +func (m *MemMigrationCache) Clone() *MemMigrationCache { + newCache := NewMemMigrationCache() + newCache.Update(m) + return newCache +} + +func (m *MemMigrationCache) Update(other *MemMigrationCache) { + other.MigrationMap.Range(func(key, value interface{}) bool { + m.MigrationMap.Store(key, value) + return true + }) +} + +type TestLogger struct { + TB testing.TB +} + +func (t TestLogger) Log(_ rt.LogLevel, msg string, args ...interface{}) { + t.TB.Logf(msg, args...) +} diff --git a/go.mod b/go.mod index a5553dd20..6dfa28e35 100644 --- a/go.mod +++ b/go.mod @@ -25,4 +25,5 @@ require ( github.com/xorcare/golden v0.6.0 golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 + gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index ed5f4d874..7a432af6b 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,7 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7 github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -192,5 +193,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= From c71da4d03d620f32d51c771c5b446080c159c4e8 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 16:35:10 -0400 Subject: [PATCH 06/18] Fix migration to flush root --- actors/migration/nv12/top.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/actors/migration/nv12/top.go b/actors/migration/nv12/top.go index 2540fd9c4..044214fcb 100644 --- a/actors/migration/nv12/top.go +++ b/actors/migration/nv12/top.go @@ -217,7 +217,10 @@ func MigrateStateTree(ctx context.Context, store cbor.IpldStore, actorsRootIn ci return cid.Undef, err } - return cid.Undef, nil + elapsed := time.Since(startTime) + rate := float64(doneCount) / elapsed.Seconds() + log.Log(rt.INFO, "All %d done after %v (%.0f/s). Flushing state tree root.", doneCount, elapsed, rate) + return actorsOut.Flush() } type actorMigrationInput struct { From 78e5162b9e40f030dab50dcb31b7145dfe4feaa2 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 17:24:32 -0400 Subject: [PATCH 07/18] Add invariants for new state --- actors/builtin/miner/miner_test.go | 18 +++++++++++++++--- actors/builtin/miner/testing.go | 7 +++++++ actors/builtin/power/testing.go | 2 +- actors/states/check.go | 8 ++++++-- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index 558fdbf29..afca457bf 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -4521,9 +4521,13 @@ func TestApplyRewards(t *testing.T) { require.EqualValues(t, expectedOffset, int64(vf.Epoch)%int64(miner.RewardVestingSpec.Quantization)) } + st = getState(rt) lockedAmt, _ := miner.LockedRewardFromReward(amt) assert.Equal(t, lockedAmt, st.LockedFunds) - actor.checkState(rt) + // technically applying rewards without first activating cron is an impossible state but convenient for testing + _, msgs := miner.CheckStateInvariants(st, rt.AdtStore(), rt.Balance()) + assert.Equal(t, 1, len(msgs.Messages())) + assert.Contains(t, msgs.Messages()[0], "DeadlineCronActive == false") }) t.Run("penalty is burnt", func(t *testing.T) { @@ -4539,7 +4543,11 @@ func TestApplyRewards(t *testing.T) { expectedLockAmt = big.Sub(expectedLockAmt, penalty) assert.Equal(t, expectedLockAmt, actor.getLockedFunds(rt)) - actor.checkState(rt) + // technically applying rewards without first activating cron is an impossible state but convenient for testing + st := getState(rt) + _, msgs := miner.CheckStateInvariants(st, rt.AdtStore(), rt.Balance()) + assert.Equal(t, 1, len(msgs.Messages())) + assert.Contains(t, msgs.Messages()[0], "DeadlineCronActive == false") }) t.Run("penalty is partially burnt and stored as fee debt", func(t *testing.T) { @@ -4573,6 +4581,7 @@ func TestApplyRewards(t *testing.T) { st = getState(rt) // fee debt = penalty - reward - initial balance = 3*amt - 2*amt = amt assert.Equal(t, amt, st.FeeDebt) + // technically applying rewards without first activating cron is an impossible state but convenient for testing actor.checkState(rt) }) @@ -4638,7 +4647,10 @@ func TestApplyRewards(t *testing.T) { assert.True(t, st.IsDebtFree()) // remaining funds locked in vesting table assert.Equal(t, remainingLocked, st.LockedFunds) - actor.checkState(rt) + // technically applying rewards without first activating cron is an impossible state but convenient for testing + _, msgs := miner.CheckStateInvariants(st, rt.AdtStore(), rt.Balance()) + assert.Equal(t, 1, len(msgs.Messages())) + assert.Contains(t, msgs.Messages()[0], "DeadlineCronActive == false") }) } diff --git a/actors/builtin/miner/testing.go b/actors/builtin/miner/testing.go index 336b7c4f9..8572bc786 100644 --- a/actors/builtin/miner/testing.go +++ b/actors/builtin/miner/testing.go @@ -22,6 +22,7 @@ type StateSummary struct { FaultyPower PowerPair Deals map[abi.DealID]DealSummary WindowPoStProofType abi.RegisteredPoStProof + DeadlineCronActive bool } // Checks internal invariants of init state. @@ -33,6 +34,7 @@ func CheckStateInvariants(st *State, store adt.Store, balance abi.TokenAmount) ( ActivePower: NewPowerPairZero(), FaultyPower: NewPowerPairZero(), WindowPoStProofType: 0, + DeadlineCronActive: st.DeadlineCronActive, } // Load data from linked structures. @@ -745,6 +747,11 @@ func CheckMinerBalances(st *State, store adt.Store, balance abi.TokenAmount, acc acc.Require(st.LockedFunds.Equals(vestingSum), "locked funds %d is not sum of vesting table entries %d", st.LockedFunds, vestingSum) + + // Non zero funds implies that DeadlineCronActive is true. + if !st.LockedFunds.IsZero() || !st.PreCommitDeposits.IsZero() || !st.InitialPledge.IsZero() { + acc.Require(st.DeadlineCronActive, "DeadlineCronActive == false when IP+PCD+LF > 0") + } } func CheckPreCommits(st *State, store adt.Store, allocatedSectors map[uint64]bool, acc *builtin.MessageAccumulator) { diff --git a/actors/builtin/power/testing.go b/actors/builtin/power/testing.go index 9d1edd032..6ccb07aa9 100644 --- a/actors/builtin/power/testing.go +++ b/actors/builtin/power/testing.go @@ -42,7 +42,7 @@ func CheckStateInvariants(st *State, store adt.Store) (*StateSummary, *builtin.M acc.Require(st.TotalRawBytePower.LessThanEqual(st.TotalBytesCommitted), "total raw power %v is greater than raw power committed %v", st.TotalRawBytePower, st.TotalBytesCommitted) acc.Require(st.TotalQualityAdjPower.LessThanEqual(st.TotalQABytesCommitted), - "total qua power %v is greater than qa power committed %v", st.TotalQualityAdjPower, st.TotalQABytesCommitted) + "total qa power %v is greater than qa power committed %v", st.TotalQualityAdjPower, st.TotalQABytesCommitted) crons := CheckCronInvariants(st, store, acc) claims := CheckClaimInvariants(st, store, acc) diff --git a/actors/states/check.go b/actors/states/check.go index 3a51099ab..2399d5eaf 100644 --- a/actors/states/check.go +++ b/actors/states/check.go @@ -172,8 +172,7 @@ func CheckMinersAgainstPower(acc *builtin.MessageAccumulator, minerSummaries map // check crons crons, ok := powerSummary.Crons[addr] - if !ok { - acc.Addf("miner %s has no cron events, at least one proving period cron expected", addr) + if !ok { // with deferred and discontinued crons it is normal for a miner actor to have no cron events continue } @@ -183,6 +182,8 @@ func CheckMinersAgainstPower(acc *builtin.MessageAccumulator, minerSummaries map err := payload.UnmarshalCBOR(bytes.NewReader(event.Payload)) acc.Require(err == nil, "miner %v registered cron at epoch %d with wrong or corrupt payload", addr, event.Epoch) + acc.Require(payload.EventType == miner.CronEventProcessEarlyTerminations || payload.EventType == miner.CronEventProvingDeadline, + "miner %v has unexpected cron event type %v", addr, payload.EventType) if payload.EventType == miner.CronEventProvingDeadline { if provingPeriodCron != nil { @@ -192,6 +193,9 @@ func CheckMinersAgainstPower(acc *builtin.MessageAccumulator, minerSummaries map provingPeriodCron = &event } } + hasProvingPeriodCron := provingPeriodCron != nil + acc.Require(hasProvingPeriodCron == minerSummary.DeadlineCronActive, "miner %v has invalid DeadlineCronActive (%t) for hasProvingPeriodCron status (%t)", + addr, minerSummary.DeadlineCronActive, hasProvingPeriodCron) acc.Require(provingPeriodCron != nil, "miner %v has no proving period cron", addr) } From abb90e0a9a294b1233877ca5b5b7d91e26ad1261 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 17:42:51 -0400 Subject: [PATCH 08/18] Remove debug prints --- actors/builtin/miner/miner_actor.go | 3 --- actors/builtin/miner/miner_test.go | 10 ---------- 2 files changed, 13 deletions(-) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index aa34ab32e..7afb347a3 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -799,7 +799,6 @@ func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.E rt.StateReadonly(&st) err = st.CheckBalanceInvariants(rt.CurrentBalance()) if needsCron { - // fmt.Printf("[PRECOMMIT] miner actor enrolling cron!\n") newDlInfo := st.DeadlineInfo(rt.CurrEpoch()) enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ EventType: CronEventProvingDeadline, @@ -2091,10 +2090,8 @@ func handleProvingDeadline(rt Runtime) { } continueCron = st.ContinueDeadlineCron() - // fmt.Printf("[CRON] continue cron: %t\n", continueCron) if !continueCron { st.DeadlineCronActive = false - // fmt.Printf("resetting cron active to false") } }) diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index afca457bf..ef5a1295a 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -620,16 +620,13 @@ func TestCommitments(t *testing.T) { actor.constructAndVerify(rt) deadline := actor.deadline(rt) challengeEpoch := precommitEpoch - 1 - //fmt.Printf("precommit epoch: %d, current epoch %d\n", precommitEpoch, rt.Epoch()) oldSector := actor.commitAndProveSectors(rt, 1, defaultSectorExpiration, nil, true)[0] - //fmt.Printf("finished first one\n") st := getState(rt) assert.True(t, st.DeadlineCronActive) // Good commitment. expiration := deadline.PeriodEnd() + defaultSectorExpiration*miner.WPoStProvingPeriod actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}, false) - //fmt.Printf("finished second one\n") // Duplicate pre-commit sector ID rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "already been allocated", func() { actor.preCommitSector(rt, actor.makePreCommit(101, challengeEpoch, expiration, nil), preCommitConf{}, false) @@ -5129,10 +5126,8 @@ func (h *actorHarness) preCommitSector(rt *mock.Runtime, params *miner.PreCommit } if first { - //fmt.Printf("internal precommit epoch: %d\n", rt.Epoch()) dlInfo := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, rt.Epoch()) cronParams := makeDeadlineCronEventParams(h.t, dlInfo.Last()) - //fmt.Printf("internal expected cron params: %v\n", cronParams) rt.ExpectSend(builtin.StoragePowerActorAddr, builtin.MethodsPower.EnrollCronEvent, cronParams, big.Zero(), nil, exitcode.Ok) } @@ -5147,7 +5142,6 @@ func (h *actorHarness) preCommitSector(rt *mock.Runtime, params *miner.PreCommit } } - //fmt.Printf("call precommit\n") rt.Call(h.a.PreCommitSector, params) rt.Verify() return h.getPreCommit(rt, params.SectorNumber) @@ -5303,9 +5297,7 @@ func (h *actorHarness) commitAndProveSectors(rt *mock.Runtime, n int, lifetimePe precommits[i] = precommit h.nextSectorNo++ } - //fmt.Printf("advancing to epoch with cron, current epoch %d, advancing to: %d\n", rt.Epoch(), precommitEpoch+miner.PreCommitChallengeDelay+1) advanceToEpochWithCron(rt, h, precommitEpoch+miner.PreCommitChallengeDelay+1) - //fmt.Printf("finished advancing, current epoch: %d\n", rt.Epoch()) info := []*miner.SectorOnChainInfo{} for _, pc := range precommits { sector := h.proveCommitSectorAndConfirm(rt, pc, makeProveCommit(pc.Info.SectorNumber), proveCommitConf{}) @@ -6071,7 +6063,6 @@ func advanceDeadline(rt *mock.Runtime, h *actorHarness, config *cronConfig) *dli rt.SetEpoch(deadline.Last()) config.expectedEnrollment = deadline.Last() + miner.WPoStChallengeWindow - //fmt.Printf("advancing to last epoch of current deadline: %d, expect enrollment to: %d\n", deadline.Last(), config.expectedEnrollment) h.onDeadlineCron(rt, config) rt.SetEpoch(deadline.NextOpen()) return h.deadline(rt) @@ -6220,7 +6211,6 @@ func makeDeadlineCronEventParams(t testing.TB, epoch abi.ChainEpoch) *power.Enro EventEpoch: epoch, Payload: buf.Bytes(), } - //fmt.Printf("internally expected cron params: %v\n", params) return params } From 697e2d5130172d0e83303f8855e191f4d6442028 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 18:02:47 -0400 Subject: [PATCH 09/18] Small deadline invariant test --- actors/builtin/miner/deadlines_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/actors/builtin/miner/deadlines_test.go b/actors/builtin/miner/deadlines_test.go index 7b0c2f678..a603d4e6b 100644 --- a/actors/builtin/miner/deadlines_test.go +++ b/actors/builtin/miner/deadlines_test.go @@ -19,3 +19,24 @@ func TestProvingPeriodDeadlines(t *testing.T) { assert.Equal(t, d.NextNotElapsed().Last(), quant.QuantizeUp(curr)) }) } + +func TestDeadlineInfoFromOffsetAndEpoch(t *testing.T) { + // All proving periods equivalent mod WPoStProving period should give equivalent + // dlines for a given epoch. Only the offset property should matter + + pp := abi.ChainEpoch(1972) + ppThree := abi.ChainEpoch(1972 + 2880*3) + ppMillion := abi.ChainEpoch(1972 + 2880*10e6) + + epochs := []abi.ChainEpoch{4, 2000, 400000, 5000000} + for _, epoch := range epochs { + dlineA := miner.NewDeadlineInfoFromOffsetAndEpoch(pp, epoch) + dlineB := miner.NewDeadlineInfoFromOffsetAndEpoch(ppThree, epoch) + dlineC := miner.NewDeadlineInfoFromOffsetAndEpoch(ppMillion, epoch) + + assert.Equal(t, *dlineA, *dlineB) + assert.Equal(t, *dlineB, *dlineC) + + } + +} From df1253270160ac16d466ebe47fceabb212995c35 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Wed, 14 Apr 2021 18:34:43 -0400 Subject: [PATCH 10/18] Clamp BR at epsilon and small review --- actors/builtin/miner/miner_actor.go | 4 +--- actors/builtin/miner/monies.go | 4 +++- actors/builtin/miner/testing.go | 2 +- actors/migration/nv12/test/parallel_migration_test.go | 2 +- go.mod | 1 - go.sum | 2 -- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index 7afb347a3..9f0ca95ad 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -791,9 +791,7 @@ func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.E // activate miner cron needsCron = !st.DeadlineCronActive - if needsCron { - st.DeadlineCronActive = true - } + st.DeadlineCronActive = true }) burnFunds(rt, feeToBurn) rt.StateReadonly(&st) diff --git a/actors/builtin/miner/monies.go b/actors/builtin/miner/monies.go index 0267ae956..e358f3b62 100644 --- a/actors/builtin/miner/monies.go +++ b/actors/builtin/miner/monies.go @@ -80,7 +80,9 @@ func ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate smoothing.Fil expectedRewardForProvingPeriod := smoothing.ExtrapolatedCumSumOfRatio(projectionDuration, 0, rewardEstimate, networkQAPowerEstimate) br128 := big.Mul(qaSectorPower, expectedRewardForProvingPeriod) // Q.0 * Q.128 => Q.128 br := big.Rsh(br128, math.Precision128) - return big.Max(br, big.Zero()) // negative BR is clamped at 0 + // negative BR is clamped at 1 attoFIL. This is not zero because zero valued BR derived values + // (PCD, IP) are used as succinct indicators of state activity. + return big.Max(br, big.NewInt(1)) } // The penalty for a sector continuing faulty for another proving period. diff --git a/actors/builtin/miner/testing.go b/actors/builtin/miner/testing.go index 8572bc786..c14def750 100644 --- a/actors/builtin/miner/testing.go +++ b/actors/builtin/miner/testing.go @@ -749,7 +749,7 @@ func CheckMinerBalances(st *State, store adt.Store, balance abi.TokenAmount, acc "locked funds %d is not sum of vesting table entries %d", st.LockedFunds, vestingSum) // Non zero funds implies that DeadlineCronActive is true. - if !st.LockedFunds.IsZero() || !st.PreCommitDeposits.IsZero() || !st.InitialPledge.IsZero() { + if st.ContinueDeadlineCron() { acc.Require(st.DeadlineCronActive, "DeadlineCronActive == false when IP+PCD+LF > 0") } } diff --git a/actors/migration/nv12/test/parallel_migration_test.go b/actors/migration/nv12/test/parallel_migration_test.go index 07bc0aab6..3d0d73d52 100644 --- a/actors/migration/nv12/test/parallel_migration_test.go +++ b/actors/migration/nv12/test/parallel_migration_test.go @@ -12,9 +12,9 @@ import ( cbor "github.com/ipfs/go-ipld-cbor" "github.com/ipfs/go-cid" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" - "gotest.tools/assert" ) func TestParallelMigrationCalls(t *testing.T) { diff --git a/go.mod b/go.mod index 6dfa28e35..a5553dd20 100644 --- a/go.mod +++ b/go.mod @@ -25,5 +25,4 @@ require ( github.com/xorcare/golden v0.6.0 golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 - gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index 7a432af6b..f427d8c02 100644 --- a/go.sum +++ b/go.sum @@ -193,7 +193,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= From ae4e263e321d2b212dc0baf6ee93020c383633f2 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 15 Apr 2021 10:48:40 -0400 Subject: [PATCH 11/18] Fix BR clamping to only impact PCD and IP terms --- actors/builtin/miner/monies.go | 19 +++++++++---- actors/builtin/miner/monies_test.go | 43 ++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/actors/builtin/miner/monies.go b/actors/builtin/miner/monies.go index e358f3b62..860c829dd 100644 --- a/actors/builtin/miner/monies.go +++ b/actors/builtin/miner/monies.go @@ -80,9 +80,18 @@ func ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate smoothing.Fil expectedRewardForProvingPeriod := smoothing.ExtrapolatedCumSumOfRatio(projectionDuration, 0, rewardEstimate, networkQAPowerEstimate) br128 := big.Mul(qaSectorPower, expectedRewardForProvingPeriod) // Q.0 * Q.128 => Q.128 br := big.Rsh(br128, math.Precision128) - // negative BR is clamped at 1 attoFIL. This is not zero because zero valued BR derived values - // (PCD, IP) are used as succinct indicators of state activity. - return big.Max(br, big.NewInt(1)) + + return big.Max(br, big.Zero()) +} + +// Some uses of BR (PCD, IP) require a strictly positive value for BR derived values so +// accounting variables can be used as succinct indicators of miner activity. +func ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, qaSectorPower abi.StoragePower, projectionDuration abi.ChainEpoch) abi.TokenAmount { + br := ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate, qaSectorPower, projectionDuration) + if br.LessThanEqual(big.Zero()) { + br = abi.NewTokenAmount(1) + } + return br } // The penalty for a sector continuing faulty for another proving period. @@ -139,7 +148,7 @@ func PledgePenaltyForInvalidWindowPoSt(rewardEstimate, networkQAPowerEstimate sm // Computes the PreCommit deposit given sector qa weight and current network conditions. // PreCommit Deposit = BR(PreCommitDepositProjectionPeriod) func PreCommitDepositForPower(rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, qaSectorPower abi.StoragePower) abi.TokenAmount { - return ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate, qaSectorPower, PreCommitDepositProjectionPeriod) + return ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate, qaSectorPower, PreCommitDepositProjectionPeriod) } // Computes the pledge requirement for committing new quality-adjusted power to the network, given the current @@ -154,7 +163,7 @@ func PreCommitDepositForPower(rewardEstimate, networkQAPowerEstimate smoothing.F // LockTarget = (LockTargetFactorNum / LockTargetFactorDenom) * FILCirculatingSupply(t) // PledgeShare(t) = sectorQAPower / max(BaselinePower(t), NetworkQAPower(t)) func InitialPledgeForPower(qaPower, baselinePower abi.StoragePower, rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, circulatingSupply abi.TokenAmount) abi.TokenAmount { - ipBase := ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate, qaPower, InitialPledgeProjectionPeriod) + ipBase := ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate, qaPower, InitialPledgeProjectionPeriod) lockTargetNum := big.Mul(InitialPledgeLockTarget.Numerator, circulatingSupply) lockTargetDenom := InitialPledgeLockTarget.Denominator diff --git a/actors/builtin/miner/monies_test.go b/actors/builtin/miner/monies_test.go index 4ee06a87a..f2717713e 100644 --- a/actors/builtin/miner/monies_test.go +++ b/actors/builtin/miner/monies_test.go @@ -99,7 +99,7 @@ func TestPledgePenaltyForTermination(t *testing.T) { dayReward := big.Div(initialPledge, bigInitialPledgeFactor) twentyDayReward := big.Mul(dayReward, bigInitialPledgeFactor) sectorAge := abi.ChainEpoch(20 * builtin.EpochsInDay) - replacementAge := abi.ChainEpoch(miner.TerminationLifetimeCap+ 1) * builtin.EpochsInDay + replacementAge := abi.ChainEpoch(miner.TerminationLifetimeCap+1) * builtin.EpochsInDay // use low power, so we don't test SP=SP power := big.NewInt(1) @@ -154,3 +154,44 @@ func TestNegativeBRClamp(t *testing.T) { fourBR := miner.ExpectedRewardForPower(rewardEstimate, powerEstimate, qaSectorPower, abi.ChainEpoch(4)) assert.Equal(t, big.Zero(), fourBR) } + +func TestContinuedFault(t *testing.T) { + t.Run("zero power means zero fault penalty", func(t *testing.T) { + epochTargetReward := abi.NewTokenAmount(1 << 50) + zeroQAPower := abi.NewStoragePower(0) + networkQAPower := abi.NewStoragePower(1 << 10) + powerRateOfChange := abi.NewStoragePower(1 << 10) + rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero()) + powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange) + + penaltyForZeroPowerFaulted := miner.PledgePenaltyForContinuedFault(rewardEstimate, powerEstimate, zeroQAPower) + assert.Equal(t, big.Zero(), penaltyForZeroPowerFaulted) + }) +} + +func TestExpectedRewardForPowerClamptedAtAttoFIL(t *testing.T) { + t.Run("expected zero valued BR clamped at 1 attofil", func(t *testing.T) { + epochTargetReward := abi.NewTokenAmount(1 << 50) + zeroQAPower := abi.NewStoragePower(0) + networkQAPower := abi.NewStoragePower(1 << 10) + powerRateOfChange := abi.NewStoragePower(1 << 10) + rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero()) + powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange) + + brClamped := miner.ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, powerEstimate, zeroQAPower, abi.ChainEpoch(1)) + assert.Equal(t, big.NewInt(1), brClamped) + }) + + t.Run("expected negative valued BR clamped at 1 atto FIL", func(t *testing.T) { + epochTargetReward := abi.NewTokenAmount(1 << 50) + qaSectorPower := abi.NewStoragePower(1 << 36) + networkQAPower := abi.NewStoragePower(1 << 10) + powerRateOfChange := abi.NewStoragePower(1 << 10).Neg() + rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero()) + powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange) + + fourBRClamped := miner.ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, powerEstimate, qaSectorPower, abi.ChainEpoch(4)) + assert.Equal(t, big.NewInt(1), fourBRClamped) + }) + +} From d13ff30a53eacf8f7c6ac2f7731dd2aba91a45ec Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 15 Apr 2021 12:51:24 -0400 Subject: [PATCH 12/18] More review response with tests --- actors/builtin/miner/deadlines_test.go | 66 +++++++++++++++++++++----- actors/builtin/miner/miner_state.go | 2 + actors/builtin/miner/monies.go | 1 + actors/builtin/miner/monies_test.go | 19 ++++++++ 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/actors/builtin/miner/deadlines_test.go b/actors/builtin/miner/deadlines_test.go index a603d4e6b..fa15c72db 100644 --- a/actors/builtin/miner/deadlines_test.go +++ b/actors/builtin/miner/deadlines_test.go @@ -21,22 +21,66 @@ func TestProvingPeriodDeadlines(t *testing.T) { } func TestDeadlineInfoFromOffsetAndEpoch(t *testing.T) { + // All proving periods equivalent mod WPoStProving period should give equivalent // dlines for a given epoch. Only the offset property should matter + t.Run("Offset and epoch invariant checking", func(t *testing.T) { + pp := abi.ChainEpoch(1972) + ppThree := abi.ChainEpoch(1972 + 2880*3) + ppMillion := abi.ChainEpoch(1972 + 2880*10e6) + + epochs := []abi.ChainEpoch{4, 2000, 400000, 5000000} + for _, epoch := range epochs { + dlineA := miner.NewDeadlineInfoFromOffsetAndEpoch(pp, epoch) + dlineB := miner.NewDeadlineInfoFromOffsetAndEpoch(ppThree, epoch) + dlineC := miner.NewDeadlineInfoFromOffsetAndEpoch(ppMillion, epoch) + + assert.Equal(t, *dlineA, *dlineB) + assert.Equal(t, *dlineB, *dlineC) + } + }) + t.Run("sanity checks", func(t *testing.T) { + offset := abi.ChainEpoch(7) + start := abi.ChainEpoch(2880*103) + offset + // epoch 2880*103 + offset we are in deadline 0, pp start = 2880*103 + offset + dline := miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start) + assert.Equal(t, uint64(0), dline.Index) + assert.Equal(t, start, dline.PeriodStart) - pp := abi.ChainEpoch(1972) - ppThree := abi.ChainEpoch(1972 + 2880*3) - ppMillion := abi.ChainEpoch(1972 + 2880*10e6) + // epoch 2880*103 + offset + WPoStChallengeWindow - 1 we are in deadline 0 + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+miner.WPoStChallengeWindow-1) + assert.Equal(t, uint64(0), dline.Index) + assert.Equal(t, start, dline.PeriodStart) - epochs := []abi.ChainEpoch{4, 2000, 400000, 5000000} - for _, epoch := range epochs { - dlineA := miner.NewDeadlineInfoFromOffsetAndEpoch(pp, epoch) - dlineB := miner.NewDeadlineInfoFromOffsetAndEpoch(ppThree, epoch) - dlineC := miner.NewDeadlineInfoFromOffsetAndEpoch(ppMillion, epoch) + // epoch 2880*103 + offset + WPoStChallengeWindow we are in deadline 1 + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+miner.WPoStChallengeWindow) + assert.Equal(t, uint64(1), dline.Index) + assert.Equal(t, start, dline.PeriodStart) - assert.Equal(t, *dlineA, *dlineB) - assert.Equal(t, *dlineB, *dlineC) + // epoch 2880*103 + offset + 40*WPoStChallengeWindow we are in deadline 40 + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+40*miner.WPoStChallengeWindow) + assert.Equal(t, uint64(40), dline.Index) + assert.Equal(t, start, dline.PeriodStart) - } + // epoch 2880*103 + offset + 40*WPoStChallengeWindow - 1 we are in deadline 39 + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+40*miner.WPoStChallengeWindow-1) + assert.Equal(t, uint64(39), dline.Index) + assert.Equal(t, start, dline.PeriodStart) + // epoch 2880*103 + offset + 40*WPoStChallengeWindow + 1 we are in deadline 40 + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+40*miner.WPoStChallengeWindow+1) + assert.Equal(t, uint64(40), dline.Index) + assert.Equal(t, start, dline.PeriodStart) + + // epoch 2880*103 + offset + WPoStPeriodDeadlines*WPoStChallengeWindow -1 we are in deadline WPoStPeriodDeadlines - 1 + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+abi.ChainEpoch(miner.WPoStPeriodDeadlines)*miner.WPoStChallengeWindow-abi.ChainEpoch(1)) + assert.Equal(t, uint64(miner.WPoStPeriodDeadlines-1), dline.Index) + assert.Equal(t, start, dline.PeriodStart) + + // epoch 2880*103 + offset + WPoStPeriodDeadlines*WPoStChallengeWindow + 1 we are in deadline 0, pp start = 2880*104 + offset + dline = miner.NewDeadlineInfoFromOffsetAndEpoch(offset, start+abi.ChainEpoch(miner.WPoStPeriodDeadlines)*miner.WPoStChallengeWindow) + assert.Equal(t, uint64(0), dline.Index) + assert.Equal(t, start+miner.WPoStProvingPeriod, dline.PeriodStart) + + }) } diff --git a/actors/builtin/miner/miner_state.go b/actors/builtin/miner/miner_state.go index f2f57d112..87866263f 100644 --- a/actors/builtin/miner/miner_state.go +++ b/actors/builtin/miner/miner_state.go @@ -54,6 +54,7 @@ type State struct { // sector belongs is compacted. Sectors cid.Cid // Array, AMT[SectorNumber]SectorOnChainInfo (sparse) + // DEPRECATED. This field will change names and no longer be updated every proving period in a future upgrade // The first epoch in this miner's current proving period. This is the first epoch in which a PoSt for a // partition at the miner's first deadline may arrive. Alternatively, it is after the last epoch at which // a PoSt for the previous window is valid. @@ -63,6 +64,7 @@ type State struct { // Updated at the end of every period by a cron callback. ProvingPeriodStart abi.ChainEpoch + // DEPRECATED. This field will be removed from state in a future upgrade. // Index of the deadline within the proving period beginning at ProvingPeriodStart that has not yet been // finalized. // Updated at the end of each deadline window by a cron callback. diff --git a/actors/builtin/miner/monies.go b/actors/builtin/miner/monies.go index 860c829dd..4f546ff56 100644 --- a/actors/builtin/miner/monies.go +++ b/actors/builtin/miner/monies.go @@ -84,6 +84,7 @@ func ExpectedRewardForPower(rewardEstimate, networkQAPowerEstimate smoothing.Fil return big.Max(br, big.Zero()) } +// BR but zero values are clamped at 1 attofil // Some uses of BR (PCD, IP) require a strictly positive value for BR derived values so // accounting variables can be used as succinct indicators of miner activity. func ExpectedRewardForPowerClampedAtAttoFIL(rewardEstimate, networkQAPowerEstimate smoothing.FilterEstimate, qaSectorPower abi.StoragePower, projectionDuration abi.ChainEpoch) abi.TokenAmount { diff --git a/actors/builtin/miner/monies_test.go b/actors/builtin/miner/monies_test.go index f2717713e..a694ed2ad 100644 --- a/actors/builtin/miner/monies_test.go +++ b/actors/builtin/miner/monies_test.go @@ -195,3 +195,22 @@ func TestExpectedRewardForPowerClamptedAtAttoFIL(t *testing.T) { }) } + +func TestPrecommitDepositAndInitialPledgePostiive(t *testing.T) { + epochTargetReward := abi.NewTokenAmount(0) // zero reward so IP Base unclamped is 0 + qaSectorPower := abi.NewStoragePower(1 << 36) + networkQAPower := abi.NewStoragePower(1 << 10) + baselinePower := networkQAPower + powerRateOfChange := abi.NewStoragePower(1 << 10) + rewardEstimate := smoothing.NewEstimate(epochTargetReward, big.Zero()) + powerEstimate := smoothing.NewEstimate(networkQAPower, powerRateOfChange) + circulatingSupply := abi.NewTokenAmount(0) + t.Run("IP is clamped at 1 attofil", func(t *testing.T) { + ip := miner.InitialPledgeForPower(qaSectorPower, baselinePower, rewardEstimate, powerEstimate, circulatingSupply) + assert.Equal(t, abi.NewTokenAmount(1), ip) + }) + t.Run("PCD is clamped at 1 attoFIL", func(t *testing.T) { + pcd := miner.PreCommitDepositForPower(rewardEstimate, powerEstimate, qaSectorPower) + assert.Equal(t, abi.NewTokenAmount(1), pcd) + }) +} From d4d03cbe8adeb07fbc2431360c2341797d1ae4d7 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 15 Apr 2021 16:54:14 -0400 Subject: [PATCH 13/18] Unit tests w/ cronControl abstraction --- actors/builtin/miner/miner_test.go | 151 +++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index ef5a1295a..ab460abd8 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -2772,6 +2772,143 @@ func TestDeadlineCron(t *testing.T) { }) } +// cronControl is a convenience harness on top of the actor harness giving the caller access to common +// sequences of miner actor actions useful for checking that cron starts, runs, stops and continues +type cronControl struct { + rt *mock.Runtime + actor *actorHarness + preCommitNum int +} + +func newCronControl(rt *mock.Runtime, actor *actorHarness) *cronControl { + return &cronControl{ + rt: rt, + actor: actor, + preCommitNum: 0, + } +} + +// Start cron by precommitting at preCommitEpoch, return expiry epoch. +// Verifies that cron is not started, precommit is run and cron is enrolled. +// Returns expiration of precommit. +func (h *cronControl) preCommitToStartCron(t *testing.T, preCommitEpoch abi.ChainEpoch) abi.ChainEpoch { + h.rt.SetEpoch(preCommitEpoch) + st := getState(h.rt) + require.False(t, st.DeadlineCronActive) // No cron running yet + require.False(t, st.ContinueDeadlineCron()) // State inactive + + dlinfo := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, preCommitEpoch) // actor.deadline might be out of date + sectorNo := abi.SectorNumber(h.preCommitNum) + h.preCommitNum++ + expiration := dlinfo.PeriodEnd() + defaultSectorExpiration*miner.WPoStProvingPeriod // something on deadline boundary but > 180 days + precommitParams := h.actor.makePreCommit(sectorNo, preCommitEpoch-1, expiration, nil) + h.actor.preCommitSector(h.rt, precommitParams, preCommitConf{}, true) + + st = getState(h.rt) + assert.True(t, st.DeadlineCronActive) + assert.True(t, st.ContinueDeadlineCron()) // PCD !=0, state active + + expiryEpoch := preCommitEpoch + builtin.EpochsInDay + miner.PreCommitChallengeDelay + abi.ChainEpoch(1) + return expiryEpoch +} + +// Stop cron by advancing to the preCommit expiry epoch. +// Assumes no proved sectors, no vesting funds. +// Verifies cron runs until expiry, PCD burnt and cron discontinued during last deadline +// Return open of first deadline after expiration. +func (h *cronControl) expirePreCommitStopCron(t *testing.T, startEpoch, expiryEpoch abi.ChainEpoch) abi.ChainEpoch { + st := getState(h.rt) + require.True(t, st.DeadlineCronActive) + require.True(t, st.ContinueDeadlineCron()) // PCD !=0, state active + + dlinfo := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, startEpoch) // actor.deadline might be out of date + + for dlinfo.Open <= expiryEpoch { // PCDs are quantized to expire on the *next* new deadline after the one they expire in + // asserts cron is rescheduled + dlinfo = advanceDeadline(h.rt, h.actor, &cronConfig{}) + } + // We expect PCD burnt and cron not rescheduled here. + h.rt.SetEpoch(dlinfo.Last()) + h.actor.onDeadlineCron(h.rt, &cronConfig{ + noEnrollment: true, + expiredPrecommitPenalty: st.PreCommitDeposits, + }) + h.rt.SetEpoch(dlinfo.NextOpen()) + + st = getState(h.rt) + assert.False(t, st.DeadlineCronActive) // No cron running now + assert.False(t, st.ContinueDeadlineCron()) // No reason to cron now, state inactive + return h.rt.Epoch() +} + +func (h *cronControl) preCommitStartCronExpireStopCron(t *testing.T, startEpoch abi.ChainEpoch) abi.ChainEpoch { + expiryEpoch := h.preCommitToStartCron(t, startEpoch) + return h.expirePreCommitStopCron(t, startEpoch, expiryEpoch) +} + +func TestDeadlineCronDefersStopsRestarts(t *testing.T) { + periodOffset := abi.ChainEpoch(100) + actor := newHarness(t, periodOffset) + builder := builderForHarness(actor). + WithBalance(bigBalance, big.Zero()) + + t.Run("cron enrolls on precommit, prove commits and continues enrolling", func(t *testing.T) { + rt := builder.Build(t) + actor.constructAndVerify(rt) + longExpiration := uint64(500) + sectors := actor.commitAndProveSectors(rt, 1, longExpiration, nil, true) + // advance cron to activate power. + advanceAndSubmitPoSts(rt, actor, sectors...) + // advance 499 days of deadline (1 before expiration occurrs) + // this asserts that cron continues to enroll within advanceAndSubmitPoSt + for i := 0; i < 499; i++ { + advanceAndSubmitPoSts(rt, actor, sectors...) + } + actor.checkState(rt) + st := getState(rt) + assert.True(t, st.DeadlineCronActive) + }) + + t.Run("cron enrolls on precommit, expires on pcd expiration, re-enrolls on new precommit immediately", func(t *testing.T) { + rt := builder.Build(t) + epoch := periodOffset + 1 + rt.SetEpoch(epoch) + actor.constructAndVerify(rt) + cronCtrl := newCronControl(rt, actor) + + epoch = cronCtrl.preCommitStartCronExpireStopCron(t, epoch) + cronCtrl.preCommitToStartCron(t, epoch) + }) + + t.Run("cron enrolls on precommit, expires on pcd expiration, re-enrolls on new precommit after falling out of date", func(t *testing.T) { + rt := builder.Build(t) + epoch := periodOffset + 1 + rt.SetEpoch(epoch) + actor.constructAndVerify(rt) + cronCtrl := newCronControl(rt, actor) + + epoch = cronCtrl.preCommitStartCronExpireStopCron(t, epoch) + // Advance some epochs to fall several pp out of date, then precommit again reenrolling cron + epoch = epoch + 200*miner.WPoStProvingPeriod + epoch = cronCtrl.preCommitStartCronExpireStopCron(t, epoch) + // Stay within the same deadline but advance an epoch + epoch = epoch + 1 + cronCtrl.preCommitToStartCron(t, epoch) + }) + + t.Run("enroll, pcd expire, re-enroll x 1000", func(t *testing.T) { + rt := builder.Build(t) + epoch := periodOffset + 1 + rt.SetEpoch(epoch) + actor.constructAndVerify(rt) + cronCtrl := newCronControl(rt, actor) + for i := 0; i < 1000; i++ { + epoch = cronCtrl.preCommitStartCronExpireStopCron(t, epoch) + 42 + } + }) + +} + func TestDeclareFaults(t *testing.T) { periodOffset := abi.ChainEpoch(100) actor := newHarness(t, periodOffset) @@ -5864,6 +6001,7 @@ type cronConfig struct { expiredSectorsPowerDelta *miner.PowerPair expiredSectorsPledgeDelta abi.TokenAmount continuedFaultsPenalty abi.TokenAmount // Expected amount burnt to pay continued fault penalties. + expiredPrecommitPenalty abi.TokenAmount // Expected amount burnt to pay for expired precommits repaidFeeDebt abi.TokenAmount // Expected amount burnt to repay fee debt. penaltyFromUnlocked abi.TokenAmount // Expected reduction in unlocked balance from penalties exceeding vesting funds. } @@ -5913,6 +6051,9 @@ func (h *actorHarness) onDeadlineCron(rt *mock.Runtime, config *cronConfig) { if !config.repaidFeeDebt.NilOrZero() { penaltyTotal = big.Add(penaltyTotal, config.repaidFeeDebt) } + if !config.expiredPrecommitPenalty.NilOrZero() { + penaltyTotal = big.Add(penaltyTotal, config.expiredPrecommitPenalty) + } if !penaltyTotal.IsZero() { rt.ExpectSend(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, penaltyTotal, nil, exitcode.Ok) penaltyFromVesting := penaltyTotal @@ -5920,6 +6061,10 @@ func (h *actorHarness) onDeadlineCron(rt *mock.Runtime, config *cronConfig) { if !config.repaidFeeDebt.NilOrZero() { penaltyFromVesting = big.Sub(penaltyFromVesting, config.repaidFeeDebt) } + // Precommit deposit burns are repaid from PCD account + if !config.expiredPrecommitPenalty.NilOrZero() { + penaltyFromVesting = big.Sub(penaltyFromVesting, config.expiredPrecommitPenalty) + } // New penalties are paid first from vesting funds but, if exhausted, overflow to unlocked balance. if !config.penaltyFromUnlocked.NilOrZero() { penaltyFromVesting = big.Sub(penaltyFromVesting, config.penaltyFromUnlocked) @@ -6058,6 +6203,7 @@ func (h *actorHarness) setMultiaddrs(rt *mock.Runtime, newMultiaddrs ...abi.Mult // Completes a deadline by moving the epoch forward to the penultimate one, calling the deadline cron handler, // and then advancing to the first epoch in the new deadline. +// Asserts that the deadline schedules a new cron on the next deadline func advanceDeadline(rt *mock.Runtime, h *actorHarness, config *cronConfig) *dline.Info { deadline := h.deadline(rt) @@ -6077,6 +6223,11 @@ func advanceToEpochWithCron(rt *mock.Runtime, h *actorHarness, e abi.ChainEpoch) rt.SetEpoch(e) } +// Advance between 0 and 48 deadlines submitting window posts where necessary to keep +// sectors proven. If sectors is empty this is a noop. If sectors is a singleton this +// will advance to that sector's proving deadline running deadline crons up to and +// including this deadline. If sectors includes a sector assigned to the furthest +// away deadline this will process a whole proving period. func advanceAndSubmitPoSts(rt *mock.Runtime, h *actorHarness, sectors ...*miner.SectorOnChainInfo) { st := getState(rt) From 06a95748c41109c92054e0ea4fe0cb40caa3b22c Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 15 Apr 2021 23:26:00 -0400 Subject: [PATCH 14/18] Use new cron model for advancing deadlines throughout unit tests --- actors/builtin/miner/deadlines.go | 1 - actors/builtin/miner/miner_test.go | 77 ++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/actors/builtin/miner/deadlines.go b/actors/builtin/miner/deadlines.go index 104e5c05c..9ec98a545 100644 --- a/actors/builtin/miner/deadlines.go +++ b/actors/builtin/miner/deadlines.go @@ -102,6 +102,5 @@ func NewDeadlineInfoFromOffsetAndEpoch(periodStartSeed abi.ChainEpoch, currEpoch q := NewQuantSpec(WPoStProvingPeriod, periodStartSeed) currentPeriodStart := q.QuantizeDown(currEpoch) currentDeadlineIdx := uint64((currEpoch-currentPeriodStart)/WPoStChallengeWindow) % WPoStPeriodDeadlines - return NewDeadlineInfo(currentPeriodStart, currentDeadlineIdx, currEpoch) } diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index ab460abd8..1c122dd4f 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -2591,12 +2591,15 @@ func TestDeadlineCron(t *testing.T) { st := getState(rt) initialPledge := st.InitialPledge - expiration := sectors[0].Expiration + expirationRaw := sectors[0].Expiration assert.True(t, st.DeadlineCronActive) // setup state to simulate moving forward all the way to expiry + dlIdx, _, err := st.FindSector(rt.AdtStore(), sectors[0].SectorNumber) require.NoError(t, err) + expQuantSpec := st.QuantSpecForDeadline(dlIdx) + expiration := expQuantSpec.QuantizeUp(expirationRaw) remainingEpochs := expiration - st.ProvingPeriodStart remainingPeriods := remainingEpochs/miner.WPoStProvingPeriod + 1 st.ProvingPeriodStart += remainingPeriods * miner.WPoStProvingPeriod @@ -2628,12 +2631,14 @@ func TestDeadlineCron(t *testing.T) { st := getState(rt) initialPledge := st.InitialPledge - expiration := sectors[0].Expiration + expirationRaw := sectors[0].Expiration assert.True(t, st.DeadlineCronActive) // setup state to simulate moving forward all the way to expiry dlIdx, _, err := st.FindSector(rt.AdtStore(), sectors[0].SectorNumber) require.NoError(t, err) + expQuantSpec := st.QuantSpecForDeadline(dlIdx) + expiration := expQuantSpec.QuantizeUp(expirationRaw) remainingEpochs := expiration - st.ProvingPeriodStart remainingPeriods := remainingEpochs/miner.WPoStProvingPeriod + 1 st.ProvingPeriodStart += remainingPeriods * miner.WPoStProvingPeriod @@ -2794,8 +2799,7 @@ func newCronControl(rt *mock.Runtime, actor *actorHarness) *cronControl { func (h *cronControl) preCommitToStartCron(t *testing.T, preCommitEpoch abi.ChainEpoch) abi.ChainEpoch { h.rt.SetEpoch(preCommitEpoch) st := getState(h.rt) - require.False(t, st.DeadlineCronActive) // No cron running yet - require.False(t, st.ContinueDeadlineCron()) // State inactive + h.requireCronInactive(t) dlinfo := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, preCommitEpoch) // actor.deadline might be out of date sectorNo := abi.SectorNumber(h.preCommitNum) @@ -2804,10 +2808,8 @@ func (h *cronControl) preCommitToStartCron(t *testing.T, preCommitEpoch abi.Chai precommitParams := h.actor.makePreCommit(sectorNo, preCommitEpoch-1, expiration, nil) h.actor.preCommitSector(h.rt, precommitParams, preCommitConf{}, true) - st = getState(h.rt) - assert.True(t, st.DeadlineCronActive) - assert.True(t, st.ContinueDeadlineCron()) // PCD !=0, state active - + // PCD != 0 so cron must be active + h.requireCronActive(t) expiryEpoch := preCommitEpoch + builtin.EpochsInDay + miner.PreCommitChallengeDelay + abi.ChainEpoch(1) return expiryEpoch } @@ -2817,10 +2819,8 @@ func (h *cronControl) preCommitToStartCron(t *testing.T, preCommitEpoch abi.Chai // Verifies cron runs until expiry, PCD burnt and cron discontinued during last deadline // Return open of first deadline after expiration. func (h *cronControl) expirePreCommitStopCron(t *testing.T, startEpoch, expiryEpoch abi.ChainEpoch) abi.ChainEpoch { + h.requireCronActive(t) st := getState(h.rt) - require.True(t, st.DeadlineCronActive) - require.True(t, st.ContinueDeadlineCron()) // PCD !=0, state active - dlinfo := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, startEpoch) // actor.deadline might be out of date for dlinfo.Open <= expiryEpoch { // PCDs are quantized to expire on the *next* new deadline after the one they expire in @@ -2835,10 +2835,20 @@ func (h *cronControl) expirePreCommitStopCron(t *testing.T, startEpoch, expiryEp }) h.rt.SetEpoch(dlinfo.NextOpen()) - st = getState(h.rt) + h.requireCronInactive(t) + return h.rt.Epoch() +} + +func (h *cronControl) requireCronInactive(t *testing.T) { + st := getState(h.rt) assert.False(t, st.DeadlineCronActive) // No cron running now assert.False(t, st.ContinueDeadlineCron()) // No reason to cron now, state inactive - return h.rt.Epoch() +} + +func (h *cronControl) requireCronActive(t *testing.T) { + st := getState(h.rt) + require.True(t, st.DeadlineCronActive) + require.True(t, st.ContinueDeadlineCron()) } func (h *cronControl) preCommitStartCronExpireStopCron(t *testing.T, startEpoch abi.ChainEpoch) abi.ChainEpoch { @@ -2855,8 +2865,13 @@ func TestDeadlineCronDefersStopsRestarts(t *testing.T) { t.Run("cron enrolls on precommit, prove commits and continues enrolling", func(t *testing.T) { rt := builder.Build(t) actor.constructAndVerify(rt) + cronCtrl := newCronControl(rt, actor) longExpiration := uint64(500) + + cronCtrl.requireCronInactive(t) sectors := actor.commitAndProveSectors(rt, 1, longExpiration, nil, true) + cronCtrl.requireCronActive(t) + // advance cron to activate power. advanceAndSubmitPoSts(rt, actor, sectors...) // advance 499 days of deadline (1 before expiration occurrs) @@ -2906,7 +2921,6 @@ func TestDeadlineCronDefersStopsRestarts(t *testing.T) { epoch = cronCtrl.preCommitStartCronExpireStopCron(t, epoch) + 42 } }) - } func TestDeclareFaults(t *testing.T) { @@ -3483,7 +3497,9 @@ func TestTerminateSectors(t *testing.T) { require.NoError(t, err) // advance clock so upgrade happens later - rt.SetEpoch(rt.Epoch() + 10_000) + for i := 0; i < 4; i++ { // 4 * 2880 = 11,520 + advanceAndSubmitPoSts(rt, actor, oldSector) + } challengeEpoch := rt.Epoch() - 1 upgradeParams := actor.makePreCommit(200, challengeEpoch, oldSector.Expiration, []abi.DealID{1}) @@ -4990,6 +5006,11 @@ func (h *actorHarness) deadline(rt *mock.Runtime) *dline.Info { return st.RecordedDeadlineInfo(rt.Epoch()) } +func (h *actorHarness) currentDeadline(rt *mock.Runtime) *dline.Info { + st := getState(rt) + return st.DeadlineInfo(rt.Epoch()) +} + func (h *actorHarness) getPreCommit(rt *mock.Runtime, sno abi.SectorNumber) *miner.SectorPreCommitOnChainInfo { st := getState(rt) pc, found, err := st.GetPrecommittedSector(rt.AdtStore(), sno) @@ -5435,6 +5456,7 @@ func (h *actorHarness) commitAndProveSectors(rt *mock.Runtime, n int, lifetimePe h.nextSectorNo++ } advanceToEpochWithCron(rt, h, precommitEpoch+miner.PreCommitChallengeDelay+1) + info := []*miner.SectorOnChainInfo{} for _, pc := range precommits { sector := h.proveCommitSectorAndConfirm(rt, pc, makeProveCommit(pc.Info.SectorNumber), proveCommitConf{}) @@ -6201,17 +6223,24 @@ func (h *actorHarness) setMultiaddrs(rt *mock.Runtime, newMultiaddrs ...abi.Mult // Higher-level orchestration // -// Completes a deadline by moving the epoch forward to the penultimate one, calling the deadline cron handler, +// Completes a deadline by moving the epoch forward to the penultimate one, +// if cron is active calling the deadline cron handler, // and then advancing to the first epoch in the new deadline. -// Asserts that the deadline schedules a new cron on the next deadline +// If cron is run asserts that the deadline schedules a new cron on the next deadline func advanceDeadline(rt *mock.Runtime, h *actorHarness, config *cronConfig) *dline.Info { - deadline := h.deadline(rt) + st := getState(rt) + deadline := miner.NewDeadlineInfoFromOffsetAndEpoch(st.ProvingPeriodStart, rt.Epoch()) - rt.SetEpoch(deadline.Last()) - config.expectedEnrollment = deadline.Last() + miner.WPoStChallengeWindow - h.onDeadlineCron(rt, config) + if st.DeadlineCronActive { + rt.SetEpoch(deadline.Last()) + + config.expectedEnrollment = deadline.Last() + miner.WPoStChallengeWindow + h.onDeadlineCron(rt, config) + } rt.SetEpoch(deadline.NextOpen()) - return h.deadline(rt) + st = getState(rt) + + return st.DeadlineInfo(rt.Epoch()) } func advanceToEpochWithCron(rt *mock.Runtime, h *actorHarness, e abi.ChainEpoch) { @@ -6241,7 +6270,7 @@ func advanceAndSubmitPoSts(rt *mock.Runtime, h *actorHarness, sectors ...*miner. deadlines[dlIdx] = append(deadlines[dlIdx], sector) } - dlinfo := h.deadline(rt) + dlinfo := h.currentDeadline(rt) for len(deadlines) > 0 { dlSectors, ok := deadlines[dlinfo.Index] if ok { @@ -6310,7 +6339,7 @@ func advanceAndSubmitPoSts(rt *mock.Runtime, h *actorHarness, sectors ...*miner. } advanceDeadline(rt, h, &cronConfig{}) - dlinfo = h.deadline(rt) + dlinfo = h.currentDeadline(rt) } } From 38fa52ff36a6b13a643ba2f43b4d7efd67b96996 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Fri, 16 Apr 2021 11:49:40 -0400 Subject: [PATCH 15/18] Fix existing tests --- actors/builtin/miner/miner_test.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index 1c122dd4f..0ee319ea0 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -3517,8 +3517,31 @@ func TestTerminateSectors(t *testing.T) { assert.Equal(t, oldSector.ExpectedDayReward, newSector.ReplacedDayReward) assert.Equal(t, rt.Epoch()-oldSector.Activation, newSector.ReplacedSectorAge) - // post new sector to activate power - advanceAndSubmitPoSts(rt, actor, oldSector, newSector) + // 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) From 6e62ed1b740244d528639395a99324fd9765d3d1 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Fri, 16 Apr 2021 13:50:31 -0400 Subject: [PATCH 16/18] Integration test of migration --- actors/migration/nv12/test/miner_cron_test.go | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 actors/migration/nv12/test/miner_cron_test.go diff --git a/actors/migration/nv12/test/miner_cron_test.go b/actors/migration/nv12/test/miner_cron_test.go new file mode 100644 index 000000000..d3f183b98 --- /dev/null +++ b/actors/migration/nv12/test/miner_cron_test.go @@ -0,0 +1,132 @@ +package test_test + +import ( + "context" + "strings" + "testing" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/go-state-types/rt" + ipld2 "github.com/filecoin-project/specs-actors/v2/support/ipld" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + power3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/power" + vm3 "github.com/filecoin-project/specs-actors/v3/support/vm" + "github.com/ipfs/go-cid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + builtin "github.com/filecoin-project/specs-actors/v4/actors/builtin" + exported "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported" + "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" + "github.com/filecoin-project/specs-actors/v4/actors/migration/nv12" + "github.com/filecoin-project/specs-actors/v4/actors/states" + vm "github.com/filecoin-project/specs-actors/v4/support/vm" +) + +func TestEmptyMinersStopCronAfterMigration(t *testing.T) { + ctx := context.Background() + log := nv12.TestLogger{TB: t} + v := vm3.NewVMWithSingletons(ctx, t, ipld2.NewSyncBlockStoreInMemory()) + addrs := vm3.CreateAccounts(ctx, t, v, 110, big.Mul(big.NewInt(100_000), vm3.FIL), 93837779) + + // create empty miners + minerAddrs := make([]address.Address, 100) + for i := 0; i < 100; i++ { + worker := addrs[i] + minerBalance := big.Mul(big.NewInt(10_000), vm3.FIL) + + params := power3.CreateMinerParams{ + Owner: worker, + Worker: worker, + WindowPoStProofType: abi.RegisteredPoStProof_StackedDrgWindow32GiBV1, + Peer: abi.PeerID("fake peer id"), + } + ret := vm3.ApplyOk(t, v, worker, builtin3.StoragePowerActorAddr, minerBalance, builtin3.MethodsPower.CreateMiner, ¶ms) + createRet, ok := ret.(*power3.CreateMinerReturn) + require.True(t, ok) + minerAddrs[i] = createRet.IDAddress + } + // run network for a few proving periods + stop := v.GetEpoch() + abi.ChainEpoch(10_000) + v = AdvanceToEpochWithCronV3(t, v, stop) + + // migrate + nextRoot, err := nv12.MigrateStateTree(ctx, v.Store(), v.StateRoot(), v.GetEpoch(), nv12.Config{MaxWorkers: 1}, log, nv12.NewMemMigrationCache()) + require.NoError(t, err) + + lookup := map[cid.Cid]rt.VMActor{} + for _, ba := range exported.BuiltinActors() { + lookup[ba.Code()] = ba + } + v4, err := vm.NewVMAtEpoch(ctx, lookup, v.Store(), nextRoot, v.GetEpoch()) + require.NoError(t, err) + + // check that all miners are cronning + stateTree, err := v4.GetStateTree() + require.NoError(t, err) + stateTree.ForEach(func(addr address.Address, act *states.Actor) error { + if act.Code.Equals(builtin.StorageMinerActorCodeID) { + var mSt miner.State + err := v4.GetState(addr, &mSt) + require.NoError(t, err) + assert.True(t, mSt.DeadlineCronActive) + } + return nil + }) + + // empty miners stop cronning within 1 proving period + v4 = AdvanceToEpochWithCron(t, v4, v4.GetEpoch()+miner.WPoStProvingPeriod) + + // check invariants + stateTree, err = v4.GetStateTree() + require.NoError(t, err) + totalBalance, err := v4.GetTotalActorBalance() + require.NoError(t, err) + msgs, err := states.CheckStateInvariants(stateTree, totalBalance, v4.GetEpoch()-1) + require.NoError(t, err) + + assert.Equal(t, 0, len(msgs.Messages()), strings.Join(msgs.Messages(), "\n")) + + // check that no miners are cronning + stateTree.ForEach(func(addr address.Address, act *states.Actor) error { + if act.Code.Equals(builtin.StorageMinerActorCodeID) { + var mSt miner.State + err := v4.GetState(addr, &mSt) + require.NoError(t, err) + assert.False(t, mSt.DeadlineCronActive) + } + return nil + }) +} + +// Advances to given epoch running cron for all epochs up to but not including this epoch. +// This utility didn't exist in v3 vm utils so copying here after the fact to handle migrations. +func AdvanceToEpochWithCronV3(t *testing.T, v *vm3.VM, stop abi.ChainEpoch) *vm3.VM { + currEpoch := v.GetEpoch() + var err error + for currEpoch < stop { + _, code := v.ApplyMessage(builtin3.SystemActorAddr, builtin3.CronActorAddr, big.Zero(), builtin3.MethodsCron.EpochTick, nil) + require.Equal(t, exitcode.Ok, code) + currEpoch += 1 + v, err = v.WithEpoch(currEpoch) + require.NoError(t, err) + } + return v +} + +// Advances to given epoch running cron for all epochs up to but not including this epoch. +func AdvanceToEpochWithCron(t *testing.T, v *vm.VM, stop abi.ChainEpoch) *vm.VM { + currEpoch := v.GetEpoch() + var err error + for currEpoch < stop { + _, code := v.ApplyMessage(builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) + require.Equal(t, exitcode.Ok, code) + currEpoch += 1 + v, err = v.WithEpoch(currEpoch) + require.NoError(t, err) + } + return v +} From 7f17f624146c0c57f0c6d7be86a6b9274d188230 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Tue, 20 Apr 2021 09:42:44 -0400 Subject: [PATCH 17/18] More cleanup --- actors/builtin/miner/miner_actor.go | 2 +- actors/migration/nv12/test/miner_cron_test.go | 6 ++++-- go.sum | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index 9f0ca95ad..0ebf4ce62 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -796,13 +796,13 @@ func (a Actor) PreCommitSector(rt Runtime, params *PreCommitSectorParams) *abi.E burnFunds(rt, feeToBurn) rt.StateReadonly(&st) err = st.CheckBalanceInvariants(rt.CurrentBalance()) + builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") if needsCron { newDlInfo := st.DeadlineInfo(rt.CurrEpoch()) enrollCronEvent(rt, newDlInfo.Last(), &CronEventPayload{ EventType: CronEventProvingDeadline, }) } - builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") notifyPledgeChanged(rt, newlyVested.Neg()) diff --git a/actors/migration/nv12/test/miner_cron_test.go b/actors/migration/nv12/test/miner_cron_test.go index d3f183b98..bc6b67e90 100644 --- a/actors/migration/nv12/test/miner_cron_test.go +++ b/actors/migration/nv12/test/miner_cron_test.go @@ -67,7 +67,7 @@ func TestEmptyMinersStopCronAfterMigration(t *testing.T) { // check that all miners are cronning stateTree, err := v4.GetStateTree() require.NoError(t, err) - stateTree.ForEach(func(addr address.Address, act *states.Actor) error { + err = stateTree.ForEach(func(addr address.Address, act *states.Actor) error { if act.Code.Equals(builtin.StorageMinerActorCodeID) { var mSt miner.State err := v4.GetState(addr, &mSt) @@ -76,6 +76,7 @@ func TestEmptyMinersStopCronAfterMigration(t *testing.T) { } return nil }) + require.NoError(t, err) // empty miners stop cronning within 1 proving period v4 = AdvanceToEpochWithCron(t, v4, v4.GetEpoch()+miner.WPoStProvingPeriod) @@ -91,7 +92,7 @@ func TestEmptyMinersStopCronAfterMigration(t *testing.T) { assert.Equal(t, 0, len(msgs.Messages()), strings.Join(msgs.Messages(), "\n")) // check that no miners are cronning - stateTree.ForEach(func(addr address.Address, act *states.Actor) error { + err = stateTree.ForEach(func(addr address.Address, act *states.Actor) error { if act.Code.Equals(builtin.StorageMinerActorCodeID) { var mSt miner.State err := v4.GetState(addr, &mSt) @@ -100,6 +101,7 @@ func TestEmptyMinersStopCronAfterMigration(t *testing.T) { } return nil }) + require.NoError(t, err) } // Advances to given epoch running cron for all epochs up to but not including this epoch. diff --git a/go.sum b/go.sum index f427d8c02..ed5f4d874 100644 --- a/go.sum +++ b/go.sum @@ -35,7 +35,6 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7 github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= From c0ccc81846fc3954419dac615e9954cf46431f86 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Tue, 27 Apr 2021 00:28:24 -0400 Subject: [PATCH 18/18] Remove race detection from CI to stop OOMing box --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fc94bb81b..ea585f05b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -76,7 +76,6 @@ jobs: make test-coverage mkdir -p /tmp/artifacts mv coverage.out /tmp/artifacts/coverage.out - make test-migration - codecov/upload: file: /tmp/artifacts/coverage.out - store_artifacts: