2857f6c0ed
It's very easy to write an incorrect command that operates over different heads by using the "empty" tipset. This change makes the `LoadTipSet` command helper get the latest head from the lotus daemon if its unset. The cost is an extra call to get the head. That should be trivial in most cases.
80 lines
2.3 KiB
Go
80 lines
2.3 KiB
Go
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
|
|
}
|
|
|
|
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
|
|
},
|
|
}
|