266 lines
6.5 KiB
Go
266 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-bitfield"
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
"github.com/filecoin-project/go-state-types/big"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
|
|
|
|
"github.com/filecoin-project/lotus/chain/actors"
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
lcli "github.com/filecoin-project/lotus/cli"
|
|
)
|
|
|
|
var sectorsCmd = &cli.Command{
|
|
Name: "sectors",
|
|
Usage: "Tools for interacting with sectors",
|
|
Flags: []cli.Flag{},
|
|
Subcommands: []*cli.Command{
|
|
terminateSectorCmd,
|
|
terminateSectorPenaltyEstimationCmd,
|
|
},
|
|
}
|
|
|
|
var terminateSectorCmd = &cli.Command{
|
|
Name: "terminate",
|
|
Usage: "Forcefully terminate a sector (WARNING: This means losing power and pay a one-time termination penalty(including collateral) for the terminated sector)",
|
|
ArgsUsage: "[sectorNum1 sectorNum2 ...]",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "actor",
|
|
Usage: "specify the address of miner actor",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "really-do-it",
|
|
Usage: "pass this flag if you know what you are doing",
|
|
},
|
|
},
|
|
Action: func(cctx *cli.Context) error {
|
|
if cctx.Args().Len() < 1 {
|
|
return fmt.Errorf("at least one sector must be specified")
|
|
}
|
|
|
|
var maddr address.Address
|
|
if act := cctx.String("actor"); act != "" {
|
|
var err error
|
|
maddr, err = address.NewFromString(act)
|
|
if err != nil {
|
|
return fmt.Errorf("parsing address %s: %w", act, err)
|
|
}
|
|
}
|
|
|
|
if !cctx.Bool("really-do-it") {
|
|
return fmt.Errorf("this is a command for advanced users, only use it if you are sure of what you are doing")
|
|
}
|
|
|
|
nodeApi, closer, err := lcli.GetFullNodeAPI(cctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer closer()
|
|
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
if maddr.Empty() {
|
|
api, acloser, err := lcli.GetStorageMinerAPI(cctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer acloser()
|
|
|
|
maddr, err = api.ActorAddress(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
mi, err := nodeApi.StateMinerInfo(ctx, maddr, types.EmptyTSK)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
terminationDeclarationParams := []miner2.TerminationDeclaration{}
|
|
|
|
for _, sn := range cctx.Args().Slice() {
|
|
sectorNum, err := strconv.ParseUint(sn, 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse sector number: %w", err)
|
|
}
|
|
|
|
sectorbit := bitfield.New()
|
|
sectorbit.Set(sectorNum)
|
|
|
|
loca, err := nodeApi.StateSectorPartition(ctx, maddr, abi.SectorNumber(sectorNum), types.EmptyTSK)
|
|
if err != nil {
|
|
return fmt.Errorf("get state sector partition %s", err)
|
|
}
|
|
|
|
para := miner2.TerminationDeclaration{
|
|
Deadline: loca.Deadline,
|
|
Partition: loca.Partition,
|
|
Sectors: sectorbit,
|
|
}
|
|
|
|
terminationDeclarationParams = append(terminationDeclarationParams, para)
|
|
}
|
|
|
|
terminateSectorParams := &miner2.TerminateSectorsParams{
|
|
Terminations: terminationDeclarationParams,
|
|
}
|
|
|
|
sp, err := actors.SerializeParams(terminateSectorParams)
|
|
if err != nil {
|
|
return xerrors.Errorf("serializing params: %w", err)
|
|
}
|
|
|
|
smsg, err := nodeApi.MpoolPushMessage(ctx, &types.Message{
|
|
From: mi.Owner,
|
|
To: maddr,
|
|
Method: miner.Methods.TerminateSectors,
|
|
|
|
Value: big.Zero(),
|
|
Params: sp,
|
|
}, nil)
|
|
if err != nil {
|
|
return xerrors.Errorf("mpool push message: %w", err)
|
|
}
|
|
|
|
fmt.Println("sent termination message:", smsg.Cid())
|
|
|
|
wait, err := nodeApi.StateWaitMsg(ctx, smsg.Cid(), uint64(cctx.Int("confidence")))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if wait.Receipt.ExitCode != 0 {
|
|
return fmt.Errorf("terminate sectors message returned exit %d", wait.Receipt.ExitCode)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func findPenaltyInInternalExecutions(prefix string, trace []types.ExecutionTrace) {
|
|
for _, im := range trace {
|
|
if im.Msg.To.String() == "f099" /*Burn actor*/ {
|
|
fmt.Printf("Estimated termination penalty: %s attoFIL\n", im.Msg.Value)
|
|
return
|
|
}
|
|
findPenaltyInInternalExecutions(prefix+"\t", im.Subcalls)
|
|
}
|
|
}
|
|
|
|
var terminateSectorPenaltyEstimationCmd = &cli.Command{
|
|
Name: "termination-estimate",
|
|
Usage: "Estimate the termination penalty",
|
|
ArgsUsage: "[sectorNum1 sectorNum2 ...]",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "actor",
|
|
Usage: "specify the address of miner actor",
|
|
},
|
|
},
|
|
Action: func(cctx *cli.Context) error {
|
|
if cctx.Args().Len() < 1 {
|
|
return fmt.Errorf("at least one sector must be specified")
|
|
}
|
|
|
|
var maddr address.Address
|
|
if act := cctx.String("actor"); act != "" {
|
|
var err error
|
|
maddr, err = address.NewFromString(act)
|
|
if err != nil {
|
|
return fmt.Errorf("parsing address %s: %w", act, err)
|
|
}
|
|
}
|
|
|
|
nodeApi, closer, err := lcli.GetFullNodeAPI(cctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer closer()
|
|
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
if maddr.Empty() {
|
|
api, acloser, err := lcli.GetStorageMinerAPI(cctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer acloser()
|
|
|
|
maddr, err = api.ActorAddress(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
mi, err := nodeApi.StateMinerInfo(ctx, maddr, types.EmptyTSK)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
terminationDeclarationParams := []miner2.TerminationDeclaration{}
|
|
|
|
for _, sn := range cctx.Args().Slice() {
|
|
sectorNum, err := strconv.ParseUint(sn, 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse sector number: %w", err)
|
|
}
|
|
|
|
sectorbit := bitfield.New()
|
|
sectorbit.Set(sectorNum)
|
|
|
|
loca, err := nodeApi.StateSectorPartition(ctx, maddr, abi.SectorNumber(sectorNum), types.EmptyTSK)
|
|
if err != nil {
|
|
return fmt.Errorf("get state sector partition %s", err)
|
|
}
|
|
|
|
para := miner2.TerminationDeclaration{
|
|
Deadline: loca.Deadline,
|
|
Partition: loca.Partition,
|
|
Sectors: sectorbit,
|
|
}
|
|
|
|
terminationDeclarationParams = append(terminationDeclarationParams, para)
|
|
}
|
|
|
|
terminateSectorParams := &miner2.TerminateSectorsParams{
|
|
Terminations: terminationDeclarationParams,
|
|
}
|
|
|
|
sp, err := actors.SerializeParams(terminateSectorParams)
|
|
if err != nil {
|
|
return xerrors.Errorf("serializing params: %w", err)
|
|
}
|
|
|
|
msg := &types.Message{
|
|
From: mi.Owner,
|
|
To: maddr,
|
|
Method: miner.Methods.TerminateSectors,
|
|
|
|
Value: big.Zero(),
|
|
Params: sp,
|
|
}
|
|
|
|
//TODO: 4667 add an option to give a more precise estimation with pending termination penalty excluded
|
|
|
|
invocResult, err := nodeApi.StateCall(ctx, msg, types.EmptyTSK)
|
|
if err != nil {
|
|
return xerrors.Errorf("fail to state call: %w", err)
|
|
}
|
|
|
|
findPenaltyInInternalExecutions("\t", invocResult.ExecutionTrace.Subcalls)
|
|
return nil
|
|
},
|
|
}
|