diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 49a468efbf6..5bae8dea67c 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -50,6 +50,7 @@ var FaultDeclarationCutoff = miner0.FaultDeclarationCutoff const MinSectorExpiration = miner0.MinSectorExpiration // Not used / checked in v0 +// TODO: Abstract over network versions var DeclarationsMax = miner2.DeclarationsMax var AddressedSectorsMax = miner2.AddressedSectorsMax diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index e32b3674310..aff746df13e 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -132,7 +132,7 @@ func DealProviderCollateralBounds( case actors.Version3: return market3.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) default: - panic("unsupported network version") + panic("unsupported actors version") } } @@ -191,3 +191,38 @@ func GetDefaultSectorSize() abi.SectorSize { return szs[0] } + +func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { + if nwVer <= network.Version10 { + return builtin3.SealProofPoliciesV0[proof].SectorMaxLifetime + } + + return builtin3.SealProofPoliciesV11[proof].SectorMaxLifetime +} + +func GetAddressedSectorsMax(nwVer network.Version) int { + switch actors.VersionForNetwork(nwVer) { + case actors.Version0: + return miner0.AddressedSectorsMax + case actors.Version2: + return miner2.AddressedSectorsMax + case actors.Version3: + return miner3.AddressedSectorsMax + default: + panic("unsupported network version") + } +} + +func GetDeclarationsMax(nwVer network.Version) int { + switch actors.VersionForNetwork(nwVer) { + case actors.Version0: + // TODO: Should we instead panic here since the concept doesn't exist yet? + return miner0.AddressedPartitionsMax + case actors.Version2: + return miner2.DeclarationsMax + case actors.Version3: + return miner3.DeclarationsMax + default: + panic("unsupported network version") + } +} diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index cf39e5516da..7650de03582 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -90,7 +90,7 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Println() - maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor")) + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } diff --git a/cmd/lotus-storage-miner/main.go b/cmd/lotus-storage-miner/main.go index 84654d7899b..f5ff2517772 100644 --- a/cmd/lotus-storage-miner/main.go +++ b/cmd/lotus-storage-miner/main.go @@ -106,15 +106,21 @@ func main() { lcli.RunApp(app) } -func getActorAddress(ctx context.Context, nodeAPI api.StorageMiner, overrideMaddr string) (maddr address.Address, err error) { - if overrideMaddr != "" { - maddr, err = address.NewFromString(overrideMaddr) +func getActorAddress(ctx context.Context, cctx *cli.Context) (maddr address.Address, err error) { + if cctx.IsSet("actor") { + maddr, err = address.NewFromString(cctx.String("actor")) if err != nil { return maddr, err } return } + nodeAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return address.Undef, err + } + defer closer() + maddr, err = nodeAPI.ActorAddress(ctx) if err != nil { return maddr, xerrors.Errorf("getting actor address: %w", err) diff --git a/cmd/lotus-storage-miner/proving.go b/cmd/lotus-storage-miner/proving.go index f6bc74318e8..66007b63dc1 100644 --- a/cmd/lotus-storage-miner/proving.go +++ b/cmd/lotus-storage-miner/proving.go @@ -38,12 +38,6 @@ var provingFaultsCmd = &cli.Command{ Action: func(cctx *cli.Context) error { color.NoColor = !cctx.Bool("color") - nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - api, acloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -54,7 +48,7 @@ var provingFaultsCmd = &cli.Command{ stor := store.ActorStore(ctx, blockstore.NewAPIBlockstore(api)) - maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor")) + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } @@ -98,12 +92,6 @@ var provingInfoCmd = &cli.Command{ Action: func(cctx *cli.Context) error { color.NoColor = !cctx.Bool("color") - nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - api, acloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -112,7 +100,7 @@ var provingInfoCmd = &cli.Command{ ctx := lcli.ReqContext(cctx) - maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor")) + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } @@ -211,12 +199,6 @@ var provingDeadlinesCmd = &cli.Command{ Action: func(cctx *cli.Context) error { color.NoColor = !cctx.Bool("color") - nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - api, acloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -225,7 +207,7 @@ var provingDeadlinesCmd = &cli.Command{ ctx := lcli.ReqContext(cctx) - maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor")) + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } @@ -301,12 +283,6 @@ var provingDeadlineInfoCmd = &cli.Command{ return xerrors.Errorf("could not parse deadline index: %w", err) } - nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() - api, acloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -315,7 +291,7 @@ var provingDeadlineInfoCmd = &cli.Command{ ctx := lcli.ReqContext(cctx) - maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor")) + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err } diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index 8f6fd374f42..3791dbf0741 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -16,7 +16,7 @@ import ( "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" - miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors" @@ -422,16 +422,27 @@ var sectorsExtendCmd = &cli.Command{ &cli.Int64Flag{ Name: "new-expiration", Usage: "new expiration epoch", - Required: true, + Required: false, + }, + &cli.BoolFlag{ + Name: "v1-sectors", + Usage: "renews all v1 sectors up to the maximum possible lifetime", + Required: false, + }, + &cli.Int64Flag{ + Name: "tolerance", + Value: 20160, + Usage: "when extending v1 sectors, don't try to extend sectors by fewer than this number of epochs", + Required: false, + }, + &cli.Int64Flag{ + Name: "expiration-cutoff", + Usage: "when extending v1 sectors, skip sectors whose current expiration is more than epochs from now (infinity if unspecified)", + Required: false, }, &cli.StringFlag{}, }, Action: func(cctx *cli.Context) error { - nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer closer() api, nCloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { @@ -440,49 +451,160 @@ var sectorsExtendCmd = &cli.Command{ defer nCloser() ctx := lcli.ReqContext(cctx) - if !cctx.Args().Present() { - return xerrors.Errorf("must pass at least one sector number") - } - maddr, err := nodeApi.ActorAddress(ctx) + maddr, err := getActorAddress(ctx, cctx) if err != nil { - return xerrors.Errorf("getting miner actor address: %w", err) + return err } - sectors := map[miner.SectorLocation][]uint64{} + var params []miner3.ExtendSectorExpirationParams - for i, s := range cctx.Args().Slice() { - id, err := strconv.ParseUint(s, 10, 64) + if cctx.Bool("v1-sectors") { + + head, err := api.ChainHead(ctx) if err != nil { - return xerrors.Errorf("could not parse sector %d: %w", i, err) + return err } - p, err := api.StateSectorPartition(ctx, maddr, abi.SectorNumber(id), types.EmptyTSK) + nv, err := api.StateNetworkVersion(ctx, types.EmptyTSK) if err != nil { - return xerrors.Errorf("getting sector location for sector %d: %w", id, err) + return err + } + + extensions := map[miner.SectorLocation]map[abi.ChainEpoch][]uint64{} + + // are given durations within tolerance epochs + withinTolerance := func(a, b abi.ChainEpoch) bool { + diff := a - b + if diff < 0 { + diff = b - a + } + + return diff <= abi.ChainEpoch(cctx.Int64("tolerance")) } - if p == nil { - return xerrors.Errorf("sector %d not found in any partition", id) + sis, err := api.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner sector infos: %w", err) } - sectors[*p] = append(sectors[*p], id) - } + for _, si := range sis { + if si.SealProof >= abi.RegisteredSealProof_StackedDrg2KiBV1_1 { + continue + } + + if cctx.IsSet("expiration-cutoff") { + if si.Expiration > (head.Height() + abi.ChainEpoch(cctx.Int64("expiration-cutoff"))) { + continue + } + } + + ml := policy.GetSectorMaxLifetime(si.SealProof, nv) + // if the sector's missing less than "tolerance" of its maximum possible lifetime, don't bother extending it + if withinTolerance(si.Expiration-si.Activation, ml) { + continue + } + + // Set the new expiration to 48 hours less than the theoretical maximum lifetime + newExp := ml - (miner3.WPoStProvingPeriod * 2) + si.Activation + p, err := api.StateSectorPartition(ctx, maddr, si.SectorNumber, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting sector location for sector %d: %w", si.SectorNumber, err) + } + + if p == nil { + return xerrors.Errorf("sector %d not found in any partition", si.SectorNumber) + } - params := &miner0.ExtendSectorExpirationParams{} - for l, numbers := range sectors { + es, found := extensions[*p] + if !found { + ne := make(map[abi.ChainEpoch][]uint64) + ne[newExp] = []uint64{uint64(si.SectorNumber)} + extensions[*p] = ne + } else { + added := false + for exp := range es { + if withinTolerance(exp, newExp) { + es[exp] = append(es[exp], uint64(si.SectorNumber)) + added = true + break + } + } - params.Extensions = append(params.Extensions, miner0.ExpirationExtension{ - Deadline: l.Deadline, - Partition: l.Partition, - Sectors: bitfield.NewFromSet(numbers), - NewExpiration: abi.ChainEpoch(cctx.Int64("new-expiration")), - }) + if !added { + es[newExp] = []uint64{uint64(si.SectorNumber)} + } + } + } + + p := miner3.ExtendSectorExpirationParams{} + scount := 0 + + for l, exts := range extensions { + for newExp, numbers := range exts { + scount += len(numbers) + if scount > policy.GetAddressedSectorsMax(nv) || len(p.Extensions) == policy.GetDeclarationsMax(nv) { + params = append(params, p) + p = miner3.ExtendSectorExpirationParams{} + scount = len(numbers) + } + + p.Extensions = append(p.Extensions, miner3.ExpirationExtension{ + Deadline: l.Deadline, + Partition: l.Partition, + Sectors: bitfield.NewFromSet(numbers), + NewExpiration: newExp, + }) + } + } + + // if we have any sectors, then one last append is needed here + if scount != 0 { + params = append(params, p) + } + + } else { + if !cctx.Args().Present() || !cctx.IsSet("new-expiration") { + return xerrors.Errorf("must pass at least one sector number and new expiration") + } + sectors := map[miner.SectorLocation][]uint64{} + + for i, s := range cctx.Args().Slice() { + id, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return xerrors.Errorf("could not parse sector %d: %w", i, err) + } + + p, err := api.StateSectorPartition(ctx, maddr, abi.SectorNumber(id), types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting sector location for sector %d: %w", id, err) + } + + if p == nil { + return xerrors.Errorf("sector %d not found in any partition", id) + } + + sectors[*p] = append(sectors[*p], id) + } + + p := miner3.ExtendSectorExpirationParams{} + for l, numbers := range sectors { + + // TODO: Dedup with above loop + p.Extensions = append(p.Extensions, miner3.ExpirationExtension{ + Deadline: l.Deadline, + Partition: l.Partition, + Sectors: bitfield.NewFromSet(numbers), + NewExpiration: abi.ChainEpoch(cctx.Int64("new-expiration")), + }) + } + + params = append(params, p) } - sp, err := actors.SerializeParams(params) - if err != nil { - return xerrors.Errorf("serializing params: %w", err) + if len(params) == 0 { + fmt.Println("nothing to extend") + return nil } mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) @@ -490,19 +612,26 @@ var sectorsExtendCmd = &cli.Command{ return xerrors.Errorf("getting miner info: %w", err) } - smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Worker, - To: maddr, - Method: miner.Methods.ExtendSectorExpiration, + for i := range params { + sp, aerr := actors.SerializeParams(¶ms[i]) + if aerr != nil { + return xerrors.Errorf("serializing params: %w", err) + } - Value: big.Zero(), - Params: sp, - }, nil) - if err != nil { - return xerrors.Errorf("mpool push message: %w", err) - } + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Worker, + To: maddr, + Method: miner.Methods.ExtendSectorExpiration, - fmt.Println(smsg.Cid()) + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push message: %w", err) + } + + fmt.Println(smsg.Cid()) + } return nil }, @@ -741,12 +870,6 @@ var sectorsCapacityCollateralCmd = &cli.Command{ }, Action: func(cctx *cli.Context) error { - mApi, mCloser, err := lcli.GetStorageMinerAPI(cctx) - if err != nil { - return err - } - defer mCloser() - nApi, nCloser, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -755,7 +878,7 @@ var sectorsCapacityCollateralCmd = &cli.Command{ ctx := lcli.ReqContext(cctx) - maddr, err := mApi.ActorAddress(ctx) + maddr, err := getActorAddress(ctx, cctx) if err != nil { return err }