From 14f977085976fffe8ee95a3d286f06f5f35f4753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 26 May 2023 12:21:06 +0200 Subject: [PATCH] feat: miner cli: sectors list upgrade-bounds tool --- chain/actors/policy/policy.go | 4 + chain/actors/policy/policy.go.template | 4 + cmd/lotus-miner/sectors.go | 167 +++++++++++++++++++++++++ documentation/en/cli-lotus-miner.md | 28 ++++- 4 files changed, 199 insertions(+), 4 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 4b90c46a0..bf982af89 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -61,6 +61,10 @@ const ( MaxPreCommitRandomnessLookback = builtin11.EpochsInDay + SealRandomnessLookback ) +var ( + MarketDefaultAllocationTermBuffer = market11.MarketDefaultAllocationTermBuffer +) + // SetSupportedProofTypes sets supported proof types, across all actor versions. // This should only be used for testing. func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template index f5178500a..3eb39836a 100644 --- a/chain/actors/policy/policy.go.template +++ b/chain/actors/policy/policy.go.template @@ -39,6 +39,10 @@ const ( MaxPreCommitRandomnessLookback = builtin{{.latestVersion}}.EpochsInDay + SealRandomnessLookback ) +var ( + MarketDefaultAllocationTermBuffer = market{{.latestVersion}}.MarketDefaultAllocationTermBuffer +) + // SetSupportedProofTypes sets supported proof types, across all actor versions. // This should only be used for testing. func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index 4d241b282..c3f0a6965 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "encoding/csv" "encoding/json" "errors" "fmt" @@ -318,6 +319,9 @@ var sectorsListCmd = &cli.Command{ Value: parallelSectorChecks, }, }, + Subcommands: []*cli.Command{ + sectorsListUpgradeBoundsCmd, + }, Action: func(cctx *cli.Context) error { // http mode allows for parallel json decoding/encoding, which was a bottleneck here minerApi, closer, err := lcli.GetStorageMinerAPI(cctx, cliutil.StorageMinerUseHttp) @@ -585,6 +589,169 @@ var sectorsListCmd = &cli.Command{ }, } +var sectorsListUpgradeBoundsCmd = &cli.Command{ + Name: "upgrade-bounds", + Usage: "Output upgrade bounds for available sectors", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "buckets", + Value: 25, + }, + &cli.BoolFlag{ + Name: "csv", + Usage: "output machine-readable values", + }, + &cli.BoolFlag{ + Name: "deal-terms", + Usage: "bucket by how many deal-sectors can start at a given expiration", + }, + }, + Action: func(cctx *cli.Context) error { + minerApi, closer, err := lcli.GetStorageMinerAPI(cctx, cliutil.StorageMinerUseHttp) + if err != nil { + return err + } + defer closer() + + fullApi, closer2, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer2() + + ctx := lcli.ReqContext(cctx) + + list, err := minerApi.SectorsListInStates(ctx, []api.SectorState{ + api.SectorState(sealing.Available), + }) + if err != nil { + return xerrors.Errorf("getting sector list: %w", err) + } + + head, err := fullApi.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + filter := bitfield.New() + + for _, s := range list { + filter.Set(uint64(s)) + } + + maddr, err := minerApi.ActorAddress(ctx) + if err != nil { + return err + } + + sset, err := fullApi.StateMinerSectors(ctx, maddr, &filter, head.Key()) + if err != nil { + return err + } + + if len(sset) == 0 { + return nil + } + + var minExpiration, maxExpiration abi.ChainEpoch + + for _, s := range sset { + if s.Expiration < minExpiration || minExpiration == 0 { + minExpiration = s.Expiration + } + if s.Expiration > maxExpiration { + maxExpiration = s.Expiration + } + } + + buckets := cctx.Int("buckets") + bucketSize := (maxExpiration - minExpiration) / abi.ChainEpoch(buckets) + bucketCounts := make([]int, buckets+1) + + for b := range bucketCounts { + bucketMin := minExpiration + abi.ChainEpoch(b)*bucketSize + bucketMax := minExpiration + abi.ChainEpoch(b+1)*bucketSize + + if cctx.Bool("deal-terms") { + bucketMax = bucketMax + policy.MarketDefaultAllocationTermBuffer + } + + for _, s := range sset { + isInBucket := s.Expiration >= bucketMin && s.Expiration < bucketMax + + if isInBucket { + bucketCounts[b]++ + } + } + + } + + // Creating CSV writer + writer := csv.NewWriter(os.Stdout) + + // Writing CSV headers + err = writer.Write([]string{"Max Expiration in Bucket", "Sector Count"}) + if err != nil { + return xerrors.Errorf("writing csv headers: %w", err) + } + + // Writing bucket details + + if cctx.Bool("csv") { + for i := 0; i < buckets; i++ { + maxExp := minExpiration + abi.ChainEpoch(i+1)*bucketSize + + timeStr := strconv.FormatInt(int64(maxExp), 10) + + err = writer.Write([]string{ + timeStr, + strconv.Itoa(bucketCounts[i]), + }) + if err != nil { + return xerrors.Errorf("writing csv row: %w", err) + } + } + + // Flush to make sure all data is written to the underlying writer + writer.Flush() + + if err := writer.Error(); err != nil { + return xerrors.Errorf("flushing csv writer: %w", err) + } + + return nil + } + + tw := tablewriter.New( + tablewriter.Col("Bucket Expiration"), + tablewriter.Col("Sector Count"), + tablewriter.Col("Bar"), + ) + + var barCols = 40 + var maxCount int + + for _, c := range bucketCounts { + if c > maxCount { + maxCount = c + } + } + + for i := 0; i < buckets; i++ { + maxExp := minExpiration + abi.ChainEpoch(i+1)*bucketSize + timeStr := cliutil.EpochTime(head.Height(), maxExp) + + tw.Write(map[string]interface{}{ + "Bucket Expiration": timeStr, + "Sector Count": color.YellowString("%d", bucketCounts[i]), + "Bar": "[" + color.GreenString(strings.Repeat("|", bucketCounts[i]*barCols/maxCount)) + strings.Repeat(" ", barCols-bucketCounts[i]*barCols/maxCount) + "]", + }) + } + + return tw.Flush(os.Stdout) + }, +} + var sectorsRefsCmd = &cli.Command{ Name: "refs", Usage: "List References to sectors", diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 1446a36a9..1e319eb33 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -654,17 +654,37 @@ NAME: lotus-miner sectors list - List sectors USAGE: - lotus-miner sectors list [command options] [arguments...] + lotus-miner sectors list command [command options] [arguments...] + +COMMANDS: + upgrade-bounds Output upgrade bounds for available sectors + help, h Shows a list of commands or help for one command OPTIONS: - --check-parallelism value number of parallel requests to make for checking sector states (default: 300) - --events, -e display number of events the sector has received (default: false) + --show-removed, -r show removed sectors (default: false) --fast, -f don't show on-chain info for better performance (default: false) + --events, -e display number of events the sector has received (default: false) --initial-pledge, -p display initial pledge (default: false) --seal-time, -t display how long it took for the sector to be sealed (default: false) - --show-removed, -r show removed sectors (default: false) --states value filter sectors by a comma-separated list of states --unproven, -u only show sectors which aren't in the 'Proving' state (default: false) + --check-parallelism value number of parallel requests to make for checking sector states (default: 300) + --help, -h show help (default: false) + +``` + +#### lotus-miner sectors list upgrade-bounds +``` +NAME: + lotus-miner sectors list upgrade-bounds - Output upgrade bounds for available sectors + +USAGE: + lotus-miner sectors list upgrade-bounds [command options] [arguments...] + +OPTIONS: + --buckets value (default: 25) + --csv output machine-readable values (default: false) + --deal-terms bucket by how many deal-sectors can start at a given expiration (default: false) ```