diff --git a/cmd/lotus-shed/frozen-miners.go b/cmd/lotus-shed/frozen-miners.go new file mode 100644 index 00000000000..6b843f0d6ba --- /dev/null +++ b/cmd/lotus-shed/frozen-miners.go @@ -0,0 +1,85 @@ +package main + +import ( + "fmt" + + "github.com/filecoin-project/go-state-types/abi" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var frozenMinersCmd = &cli.Command{ + Name: "frozen-miners", + Description: "information about miner actors with late or frozen deadline crons", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "tipset", + Usage: "specify tipset state to search on (pass comma separated array of cids)", + }, + &cli.BoolFlag{ + Name: "future", + Usage: "print info of miners with last deadline cron in the future (normal for v0 and early v2 actors)", + }, + }, + Action: func(c *cli.Context) error { + api, acloser, err := lcli.GetFullNodeAPI(c) + if err != nil { + return err + } + defer acloser() + ctx := lcli.ReqContext(c) + + ts, err := lcli.LoadTipSet(ctx, c, api) + if err != nil { + return err + } + if ts == nil { + ts, err = api.ChainHead(ctx) + if err != nil { + return err + } + } + + queryEpoch := ts.Height() + + mAddrs, err := api.StateListMiners(ctx, ts.Key()) + if err != nil { + return err + } + + for _, mAddr := range mAddrs { + st, err := api.StateReadState(ctx, mAddr, ts.Key()) + if err != nil { + return err + } + minerState, ok := st.State.(map[string]interface{}) + if !ok { + return xerrors.Errorf("internal error: failed to cast miner state to expected map type") + } + + ppsIface := minerState["ProvingPeriodStart"] + pps := int64(ppsIface.(float64)) + dlIdxIface := minerState["CurrentDeadline"] + dlIdx := uint64(dlIdxIface.(float64)) + latestDeadline := abi.ChainEpoch(pps) + abi.ChainEpoch(int64(dlIdx))*miner.WPoStChallengeWindow + nextDeadline := latestDeadline + miner.WPoStChallengeWindow + + // Need +1 because last epoch of the deadline queryEpoch = x + 59 cron gets run and + // state is left with latestDeadline = x + 60 + if c.Bool("future") && latestDeadline > queryEpoch+1 { + fmt.Printf("%s -- last deadline start in future epoch %d > query epoch %d + 1\n", mAddr, latestDeadline, queryEpoch) + } + + // Equality is an error because last epoch of the deadline queryEpoch = x + 59. Cron + // should get run and bump latestDeadline = x + 60 so nextDeadline = x + 120 + if queryEpoch >= nextDeadline { + fmt.Printf("%s -- next deadline start in non-future epoch %d <= query epoch %d\n", mAddr, nextDeadline, queryEpoch) + } + + } + + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 5acaa321813..488e2a6ae25 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -19,6 +19,7 @@ func main() { base32Cmd, base16Cmd, bitFieldCmd, + frozenMinersCmd, keyinfoCmd, jwtCmd, noncefix,