Documentation ¶
Index ¶
- Constants
- Variables
- func AddrInfoFromArg(ctx context.Context, cctx *cli.Context) ([]peer.AddrInfo, error)
- func BackupCmd(repoFlag string, rt repo.RepoType, getApi BackupApiFn) *cli.Command
- func BarString(total, y, g float64) string
- func ComputeStateHTMLTempl(w io.Writer, ts *types.TipSet, o *api.ComputeStateOutput, printTiming bool, ...) error
- func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, ...) ([]*types.Message, error)
- func EncodedString(sv *paych.SignedVoucher) (string, error)
- func IncorrectNumArgs(cctx *ufcli.Context) error
- func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI, ...) (*types.SignedMessage, error)
- func IsSyncDone(ctx context.Context, napi v0api.FullNode) (bool, error)
- func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error)
- func JsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error)
- func LoadTipSet(ctx context.Context, cctx *cli.Context, api TipSetResolver) (*types.TipSet, error)
- func NewCliError(s string) error
- func ParseTipSetRef(ctx context.Context, api TipSetResolver, tss string) (*types.TipSet, error)
- func ParseTipSetRefOffline(ctx context.Context, cs *store.ChainStore, tss string) (*types.TipSet, error)
- func ParseTipSetString(ts string) ([]cid.Cid, error)
- func PrintJson(obj interface{}) error
- func RunApp(app *ufcli.App)
- func ShowHelp(cctx *ufcli.Context, err error) error
- func SyncBasefeeCheck(ctx context.Context, fullapi v1api.FullNode) error
- func SyncWait(ctx context.Context, napi v0api.FullNode, watch bool) error
- func WithCategory(cat string, cmd *cli.Command) *cli.Command
- type ApiConnector
- type AppFmt
- type BackupAPI
- type BackupApiFn
- type CheckInfo
- type ErrCmdFailed
- type PrintHelpErr
- type ProvInfo
- type SendParams
- type ServicesAPI
- type ServicesImpl
- func (s *ServicesImpl) Close() error
- func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error)
- func (s *ServicesImpl) FullNodeAPI() api.FullNode
- func (s *ServicesImpl) GetBaseFee(ctx context.Context) (abi.TokenAmount, error)
- func (s *ServicesImpl) LocalAddresses(ctx context.Context) (address.Address, []address.Address, error)
- func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error)
- func (s *ServicesImpl) MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error)
- func (s *ServicesImpl) MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool, ...) ([]*types.SignedMessage, error)
- func (s *ServicesImpl) PublishMessage(ctx context.Context, prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error)
- func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error)
- type TipSetResolver
Constants ¶
const Confidence = 10
Variables ¶
var AuthApiInfoToken = &cli.Command{ Name: "api-info", Usage: "Get token with API info required to connect to this node", Flags: []cli.Flag{ &cli.StringFlag{ Name: "perm", Usage: "permission to assign to the token, one of: read, write, sign, admin", }, }, Action: func(cctx *cli.Context) error { napi, closer, err := GetAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if !cctx.IsSet("perm") { return xerrors.New("--perm flag not set, use with one of: read, write, sign, admin") } perm := cctx.String("perm") idx := 0 for i, p := range api.AllPermissions { if auth.Permission(perm) == p { idx = i + 1 } } if idx == 0 { return fmt.Errorf("--perm flag has to be one of: %s", api.AllPermissions) } token, err := napi.AuthNew(ctx, api.AllPermissions[:idx]) if err != nil { return err } ti, ok := cctx.App.Metadata["repoType"] if !ok { log.Errorf("unknown repo type, are you sure you want to use GetCommonAPI?") ti = repo.FullNode } t, ok := ti.(repo.RepoType) if !ok { log.Errorf("repoType type does not match the type of repo.RepoType") } ainfo, err := GetAPIInfo(cctx, t) if err != nil { return xerrors.Errorf("could not get API info for %s: %w", t, err) } currentEnv, _, _ := t.APIInfoEnvVars() fmt.Printf("%s=%s:%s\n", currentEnv, string(token), ainfo.Addr) return nil }, }
var AuthCmd = &cli.Command{ Name: "auth", Usage: "Manage RPC permissions", Subcommands: []*cli.Command{ AuthCreateAdminToken, AuthApiInfoToken, }, }
var AuthCreateAdminToken = &cli.Command{ Name: "create-token", Usage: "Create token", Flags: []cli.Flag{ &cli.StringFlag{ Name: "perm", Usage: "permission to assign to the token, one of: read, write, sign, admin", }, }, Action: func(cctx *cli.Context) error { napi, closer, err := GetAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if !cctx.IsSet("perm") { return xerrors.New("--perm flag not set") } perm := cctx.String("perm") idx := 0 for i, p := range api.AllPermissions { if auth.Permission(perm) == p { idx = i + 1 } } if idx == 0 { return fmt.Errorf("--perm flag has to be one of: %s", api.AllPermissions) } token, err := napi.AuthNew(ctx, api.AllPermissions[:idx]) if err != nil { return err } fmt.Println(string(token)) return nil }, }
var BarCols = float64(64)
var ChainBisectCmd = &cli.Command{ Name: "bisect", Usage: "bisect chain for an event", ArgsUsage: "[minHeight maxHeight path shellCommand <shellCommandArgs (if any)>]", Description: `Bisect the chain state tree: lotus chain bisect [min height] [max height] '1/2/3/state/path' 'shell command' 'args' Returns the first tipset in which condition is true v [start] FFFFFFFTTT [end] Example: find height at which deal ID 100 000 appeared - lotus chain bisect 1 32000 '@Ha:t03/1' jq -e '.[2] > 100000' For special path elements see 'chain get' help `, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() < 4 { return IncorrectNumArgs(cctx) } start, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64) if err != nil { return err } end, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) if err != nil { return err } subPath := cctx.Args().Get(2) highest, err := api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(end), types.EmptyTSK) if err != nil { return xerrors.Errorf("getting end tipset: %w", err) } prev := highest.Height() for { mid := (start + end) / 2 if end-start == 1 { mid = end start = end } midTs, err := api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(mid), highest.Key()) if err != nil { return err } path := "/ipld/" + midTs.ParentState().String() + "/" + subPath afmt.Printf("* Testing %d (%d - %d) (%s): ", mid, start, end, path) nd, err := api.ChainGetNode(ctx, path) if err != nil { return err } b, err := json.MarshalIndent(nd.Obj, "", "\t") if err != nil { return err } cmd := exec.CommandContext(ctx, cctx.Args().Get(3), cctx.Args().Slice()[4:]...) cmd.Stdin = bytes.NewReader(b) var out bytes.Buffer var serr bytes.Buffer cmd.Stdout = &out cmd.Stderr = &serr switch cmd.Run().(type) { case nil: if strings.TrimSpace(out.String()) != "false" { end = mid highest = midTs afmt.Println("true") } else { start = mid afmt.Printf("false (cli)\n") } case *exec.ExitError: if len(serr.String()) > 0 { afmt.Println("error") afmt.Printf("> Command: %s\n---->\n", strings.Join(cctx.Args().Slice()[3:], " ")) afmt.Println(string(b)) afmt.Println("<----") return xerrors.Errorf("error running bisect check: %s", serr.String()) } start = mid afmt.Println("false") default: return err } if start == end { if strings.TrimSpace(out.String()) == "true" { afmt.Println(midTs.Height()) } else { afmt.Println(prev) } return nil } prev = abi.ChainEpoch(mid) } }, }
var ChainCmd = &cli.Command{ Name: "chain", Usage: "Interact with filecoin blockchain", Subcommands: []*cli.Command{ ChainHeadCmd, ChainGetBlock, ChainReadObjCmd, ChainDeleteObjCmd, ChainStatObjCmd, ChainGetMsgCmd, ChainSetHeadCmd, ChainListCmd, ChainGetCmd, ChainBisectCmd, ChainExportCmd, ChainExportRangeCmd, SlashConsensusFault, ChainGasPriceCmd, ChainInspectUsage, ChainDecodeCmd, ChainEncodeCmd, ChainDisputeSetCmd, ChainPruneCmd, }, }
var ChainDecodeCmd = &cli.Command{
Name: "decode",
Usage: "decode various types",
Subcommands: []*cli.Command{
chainDecodeParamsCmd,
},
}
var ChainDeleteObjCmd = &cli.Command{ Name: "delete-obj", Usage: "Delete an object from the chain blockstore", Description: "WARNING: Removing wrong objects from the chain blockstore may lead to sync issues", ArgsUsage: "[objectCid]", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "really-do-it", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } c, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to parse cid input: %s", err) } if !cctx.Bool("really-do-it") { return xerrors.Errorf("pass the --really-do-it flag to proceed") } err = api.ChainDeleteObj(ctx, c) if err != nil { return err } afmt.Printf("Obj %s deleted\n", c.String()) return nil }, }
var ChainDisputeSetCmd = &cli.Command{
Name: "disputer",
Usage: "interact with the window post disputer",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "max-fee",
Usage: "Spend up to X FIL per DisputeWindowedPoSt message",
},
&cli.StringFlag{
Name: "from",
Usage: "optionally specify the account to send messages from",
},
},
Subcommands: []*cli.Command{
disputerStartCmd,
disputerMsgCmd,
},
}
var ChainEncodeCmd = &cli.Command{
Name: "encode",
Usage: "encode various types",
Subcommands: []*cli.Command{
chainEncodeParamsCmd,
},
}
var ChainExportCmd = &cli.Command{ Name: "export", Usage: "export chain to a car file", ArgsUsage: "[outputPath]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "tipset", Usage: "specify tipset to start the export from", Value: "@head", }, &cli.Int64Flag{ Name: "recent-stateroots", Usage: "specify the number of recent state roots to include in the export", }, &cli.BoolFlag{ Name: "skip-old-msgs", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } rsrs := abi.ChainEpoch(cctx.Int64("recent-stateroots")) if cctx.IsSet("recent-stateroots") && rsrs < build.Finality { return fmt.Errorf("\"recent-stateroots\" has to be greater than %d", build.Finality) } fi, err := createExportFile(cctx.App, cctx.Args().First()) if err != nil { return err } defer func() { err := fi.Close() if err != nil { fmt.Printf("error closing output file: %+v", err) } }() ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } skipold := cctx.Bool("skip-old-msgs") if rsrs == 0 && skipold { return fmt.Errorf("must pass recent stateroots along with skip-old-msgs") } stream, err := api.ChainExport(ctx, rsrs, skipold, ts.Key()) if err != nil { return err } var last bool for b := range stream { last = len(b) == 0 _, err := fi.Write(b) if err != nil { return err } } if !last { return xerrors.Errorf("incomplete export (remote connection lost?)") } return nil }, }
var ChainExportRangeCmd = &cli.Command{ Name: "export-range", Usage: "export chain to a car file", ArgsUsage: "", Flags: []cli.Flag{ &cli.StringFlag{ Name: "head", Usage: "specify tipset to start the export from (higher epoch)", Value: "@head", }, &cli.StringFlag{ Name: "tail", Usage: "specify tipset to end the export at (lower epoch)", Value: "@tail", }, &cli.BoolFlag{ Name: "messages", Usage: "specify if messages should be include", Value: false, }, &cli.BoolFlag{ Name: "receipts", Usage: "specify if receipts should be include", Value: false, }, &cli.BoolFlag{ Name: "stateroots", Usage: "specify if stateroots should be include", Value: false, }, &cli.IntFlag{ Name: "workers", Usage: "specify the number of workers", Value: 1, }, &cli.IntFlag{ Name: "write-buffer", Usage: "specify write buffer size", Value: 1 << 20, }, &cli.BoolFlag{ Name: "internal", Usage: "write the file locally to disk", Value: true, Hidden: true, }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var head, tail *types.TipSet headstr := cctx.String("head") if headstr == "@head" { head, err = api.ChainHead(ctx) if err != nil { return err } } else { head, err = ParseTipSetRef(ctx, api, headstr) if err != nil { return fmt.Errorf("parsing head: %w", err) } } tailstr := cctx.String("tail") if tailstr == "@tail" { tail, err = api.ChainGetGenesis(ctx) if err != nil { return err } } else { tail, err = ParseTipSetRef(ctx, api, tailstr) if err != nil { return fmt.Errorf("parsing tail: %w", err) } } if head.Height() < tail.Height() { return errors.New("Height of --head tipset must be greater or equal to the height of the --tail tipset") } if !cctx.Bool("internal") { return errors.New("Non-internal exports are not implemented") } err = api.ChainExportRangeInternal(ctx, head.Key(), tail.Key(), lapi.ChainExportConfig{ WriteBufferSize: cctx.Int("write-buffer"), NumWorkers: cctx.Int("workers"), IncludeMessages: cctx.Bool("messages"), IncludeReceipts: cctx.Bool("receipts"), IncludeStateRoots: cctx.Bool("stateroots"), }) if err != nil { return err } return nil }, }
var ChainGasPriceCmd = &cli.Command{ Name: "gas-price", Usage: "Estimate gas prices", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) nb := []int{1, 2, 3, 5, 10, 20, 50, 100, 300} for _, nblocks := range nb { addr := builtin.SystemActorAddr est, err := api.GasEstimateGasPremium(ctx, uint64(nblocks), addr, 10000, types.EmptyTSK) if err != nil { return err } afmt.Printf("%d blocks: %s (%s)\n", nblocks, est, types.FIL(est)) } return nil }, }
var ChainGetBlock = &cli.Command{ Name: "get-block", Aliases: []string{"getblock"}, Usage: "Get a block and print its details", ArgsUsage: "[blockCid]", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "raw", Usage: "print just the raw block header", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } bcid, err := cid.Decode(cctx.Args().First()) if err != nil { return err } blk, err := api.ChainGetBlock(ctx, bcid) if err != nil { return xerrors.Errorf("get block failed: %w", err) } if cctx.Bool("raw") { out, err := json.MarshalIndent(blk, "", " ") if err != nil { return err } afmt.Println(string(out)) return nil } msgs, err := api.ChainGetBlockMessages(ctx, bcid) if err != nil { return xerrors.Errorf("failed to get messages: %w", err) } pmsgs, err := api.ChainGetParentMessages(ctx, bcid) if err != nil { return xerrors.Errorf("failed to get parent messages: %w", err) } recpts, err := api.ChainGetParentReceipts(ctx, bcid) if err != nil { log.Warn(err) } cblock := struct { types.BlockHeader BlsMessages []*types.Message SecpkMessages []*types.SignedMessage ParentReceipts []*types.MessageReceipt ParentMessages []cid.Cid }{} cblock.BlockHeader = *blk cblock.BlsMessages = msgs.BlsMessages cblock.SecpkMessages = msgs.SecpkMessages cblock.ParentReceipts = recpts cblock.ParentMessages = apiMsgCids(pmsgs) out, err := json.MarshalIndent(cblock, "", " ") if err != nil { return err } afmt.Println(string(out)) return nil }, }
var ChainGetCmd = &cli.Command{ Name: "get", Usage: "Get chain DAG node by path", ArgsUsage: "[path]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "as-type", Usage: "specify type to interpret output as", }, &cli.BoolFlag{ Name: "verbose", Value: false, }, &cli.StringFlag{ Name: "tipset", Usage: "specify tipset for /pstate (pass comma separated array of cids)", }, }, Description: `Get ipld node under a specified path: lotus chain get /ipfs/[cid]/some/path Path prefixes: - /ipfs/[cid], /ipld/[cid] - traverse IPLD path - /pstate - traverse from head.ParentStateRoot Note: You can use special path elements to traverse through some data structures: - /ipfs/[cid]/@H:elem - get 'elem' from hamt - /ipfs/[cid]/@Hi:123 - get varint elem 123 from hamt - /ipfs/[cid]/@Hu:123 - get uvarint elem 123 from hamt - /ipfs/[cid]/@Ha:t01 - get element under Addr(t01).Bytes - /ipfs/[cid]/@A:10 - get 10th amt element - .../@Ha:t01/@state - get pretty map-based actor state List of --as-type types: - raw - block - message - smessage, signedmessage - actor - amt - hamt-epoch - hamt-address - cronevent - account-state `, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } p := path.Clean(cctx.Args().First()) if strings.HasPrefix(p, "/pstate") { p = p[len("/pstate"):] ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } p = "/ipfs/" + ts.ParentState().String() + p if cctx.Bool("verbose") { afmt.Println(p) } } obj, err := api.ChainGetNode(ctx, p) if err != nil { return err } t := strings.ToLower(cctx.String("as-type")) if t == "" { b, err := json.MarshalIndent(obj.Obj, "", "\t") if err != nil { return err } afmt.Println(string(b)) return nil } var cbu cbg.CBORUnmarshaler switch t { case "raw": cbu = nil case "block": cbu = new(types.BlockHeader) case "message": cbu = new(types.Message) case "smessage", "signedmessage": cbu = new(types.SignedMessage) case "actor": cbu = new(types.Actor) case "amt": return handleAmt(ctx, api, obj.Cid) case "hamt-epoch": return handleHamtEpoch(ctx, api, obj.Cid) case "hamt-address": return handleHamtAddress(ctx, api, obj.Cid) case "cronevent": cbu = new(power.CronEvent) case "account-state": cbu = new(account.State) case "miner-state": cbu = new(miner.State) case "power-state": cbu = new(power.State) case "market-state": cbu = new(market.State) default: return fmt.Errorf("unknown type: %q", t) } raw, err := api.ChainReadObj(ctx, obj.Cid) if err != nil { return err } if cbu == nil { afmt.Printf("%x", raw) return nil } if err := cbu.UnmarshalCBOR(bytes.NewReader(raw)); err != nil { return fmt.Errorf("failed to unmarshal as %q", t) } b, err := json.MarshalIndent(cbu, "", "\t") if err != nil { return err } afmt.Println(string(b)) return nil }, }
var ChainGetMsgCmd = &cli.Command{ Name: "getmessage", Aliases: []string{"get-message", "get-msg"}, Usage: "Get and print a message by its cid", ArgsUsage: "[messageCid]", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) c, err := cid.Decode(cctx.Args().First()) if err != nil { return xerrors.Errorf("failed to parse cid input: %w", err) } mb, err := api.ChainReadObj(ctx, c) if err != nil { return xerrors.Errorf("failed to read object: %w", err) } var i interface{} m, err := types.DecodeMessage(mb) if err != nil { sm, err := types.DecodeSignedMessage(mb) if err != nil { return xerrors.Errorf("failed to decode object as a message: %w", err) } i = sm } else { i = m } enc, err := json.MarshalIndent(i, "", " ") if err != nil { return err } afmt.Println(string(enc)) return nil }, }
var ChainHeadCmd = &cli.Command{ Name: "head", Usage: "Print chain head", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) head, err := api.ChainHead(ctx) if err != nil { return err } for _, c := range head.Cids() { afmt.Println(c) } return nil }, }
var ChainInspectUsage = &cli.Command{ Name: "inspect-usage", Usage: "Inspect block space usage of a given tipset", Flags: []cli.Flag{ &cli.StringFlag{ Name: "tipset", Usage: "specify tipset to view block space usage of", Value: "@head", }, &cli.IntFlag{ Name: "length", Usage: "length of chain to inspect block space usage for", Value: 1, }, &cli.IntFlag{ Name: "num-results", Usage: "number of results to print per category", Value: 10, }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } cur := ts var msgs []lapi.Message for i := 0; i < cctx.Int("length"); i++ { pmsgs, err := api.ChainGetParentMessages(ctx, cur.Blocks()[0].Cid()) if err != nil { return err } msgs = append(msgs, pmsgs...) next, err := api.ChainGetTipSet(ctx, cur.Parents()) if err != nil { return err } cur = next } codeCache := make(map[address.Address]cid.Cid) lookupActorCode := func(a address.Address) (cid.Cid, error) { c, ok := codeCache[a] if ok { return c, nil } act, err := api.StateGetActor(ctx, a, ts.Key()) if err != nil { return cid.Undef, err } codeCache[a] = act.Code return act.Code, nil } bySender := make(map[string]int64) byDest := make(map[string]int64) byMethod := make(map[string]int64) bySenderC := make(map[string]int64) byDestC := make(map[string]int64) byMethodC := make(map[string]int64) var sum int64 for _, m := range msgs { bySender[m.Message.From.String()] += m.Message.GasLimit bySenderC[m.Message.From.String()]++ byDest[m.Message.To.String()] += m.Message.GasLimit byDestC[m.Message.To.String()]++ sum += m.Message.GasLimit code, err := lookupActorCode(m.Message.To) if err != nil { if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) { continue } return err } mm := consensus.NewActorRegistry().Methods[code][m.Message.Method] byMethod[mm.Name] += m.Message.GasLimit byMethodC[mm.Name]++ } type keyGasPair struct { Key string Gas int64 } mapToSortedKvs := func(m map[string]int64) []keyGasPair { var vals []keyGasPair for k, v := range m { vals = append(vals, keyGasPair{ Key: k, Gas: v, }) } sort.Slice(vals, func(i, j int) bool { return vals[i].Gas > vals[j].Gas }) return vals } senderVals := mapToSortedKvs(bySender) destVals := mapToSortedKvs(byDest) methodVals := mapToSortedKvs(byMethod) numRes := cctx.Int("num-results") afmt.Printf("Total Gas Limit: %d\n", sum) afmt.Printf("By Sender:\n") for i := 0; i < numRes && i < len(senderVals); i++ { sv := senderVals[i] afmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, bySenderC[sv.Key]) } afmt.Println() afmt.Printf("By Receiver:\n") for i := 0; i < numRes && i < len(destVals); i++ { sv := destVals[i] afmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byDestC[sv.Key]) } afmt.Println() afmt.Printf("By Method:\n") for i := 0; i < numRes && i < len(methodVals); i++ { sv := methodVals[i] afmt.Printf("%s\t%0.2f%%\t(total: %d, count: %d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas, byMethodC[sv.Key]) } return nil }, }
var ChainListCmd = &cli.Command{ Name: "list", Aliases: []string{"love"}, Usage: "View a segment of the chain", Flags: []cli.Flag{ &cli.Uint64Flag{Name: "height", DefaultText: "current head"}, &cli.IntFlag{Name: "count", Value: 30}, &cli.StringFlag{ Name: "format", Usage: "specify the format to print out tipsets", Value: "<height>: (<time>) <blocks>", }, &cli.BoolFlag{ Name: "gas-stats", Usage: "view gas statistics for the chain", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var head *types.TipSet if cctx.IsSet("height") { head, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Uint64("height")), types.EmptyTSK) } else { head, err = api.ChainHead(ctx) } if err != nil { return err } count := cctx.Int("count") if count < 1 { return nil } tss := make([]*types.TipSet, 0, count) tss = append(tss, head) for i := 1; i < count; i++ { if head.Height() == 0 { break } head, err = api.ChainGetTipSet(ctx, head.Parents()) if err != nil { return err } tss = append(tss, head) } if cctx.Bool("gas-stats") { otss := make([]*types.TipSet, 0, len(tss)) for i := len(tss) - 1; i >= 0; i-- { otss = append(otss, tss[i]) } tss = otss for i, ts := range tss { pbf := ts.Blocks()[0].ParentBaseFee afmt.Printf("%d: %d blocks (baseFee: %s -> maxFee: %s)\n", ts.Height(), len(ts.Blocks()), ts.Blocks()[0].ParentBaseFee, types.FIL(types.BigMul(pbf, types.NewInt(uint64(build.BlockGasLimit))))) for _, b := range ts.Blocks() { msgs, err := api.ChainGetBlockMessages(ctx, b.Cid()) if err != nil { return err } var limitSum int64 psum := big.NewInt(0) for _, m := range msgs.BlsMessages { limitSum += m.GasLimit psum = big.Add(psum, m.GasPremium) } for _, m := range msgs.SecpkMessages { limitSum += m.Message.GasLimit psum = big.Add(psum, m.Message.GasPremium) } lenmsgs := len(msgs.BlsMessages) + len(msgs.SecpkMessages) avgpremium := big.Zero() if lenmsgs > 0 { avgpremium = big.Div(psum, big.NewInt(int64(lenmsgs))) } afmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPremium: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, build.BlockGasLimit, 100*float64(limitSum)/float64(build.BlockGasLimit), avgpremium) } if i < len(tss)-1 { msgs, err := api.ChainGetParentMessages(ctx, tss[i+1].Blocks()[0].Cid()) if err != nil { return err } var limitSum int64 for _, m := range msgs { limitSum += m.Message.GasLimit } recpts, err := api.ChainGetParentReceipts(ctx, tss[i+1].Blocks()[0].Cid()) if err != nil { return err } var gasUsed int64 for _, r := range recpts { gasUsed += r.GasUsed } gasEfficiency := 100 * float64(gasUsed) / float64(limitSum) gasCapacity := 100 * float64(limitSum) / float64(build.BlockGasLimit) afmt.Printf("\ttipset: \t%d msgs, %d (%0.2f%%) / %d (%0.2f%%)\n", len(msgs), gasUsed, gasEfficiency, limitSum, gasCapacity) } afmt.Println() } } else { for i := len(tss) - 1; i >= 0; i-- { printTipSet(cctx.String("format"), tss[i], afmt) } } return nil }, }
var ChainPruneCmd = &cli.Command{
Name: "prune",
Usage: "splitstore gc",
Subcommands: []*cli.Command{
chainPruneColdCmd,
chainPruneHotGCCmd,
chainPruneHotMovingGCCmd,
},
}
var ChainReadObjCmd = &cli.Command{ Name: "read-obj", Usage: "Read the raw bytes of an object", ArgsUsage: "[objectCid]", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } c, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to parse cid input: %s", err) } obj, err := api.ChainReadObj(ctx, c) if err != nil { return err } afmt.Printf("%x\n", obj) return nil }, }
var ChainSetHeadCmd = &cli.Command{ Name: "sethead", Aliases: []string{"set-head"}, Usage: "manually set the local nodes head tipset (Caution: normally only used for recovery)", ArgsUsage: "[tipsetkey]", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "genesis", Usage: "reset head to genesis", }, &cli.Uint64Flag{ Name: "epoch", Usage: "reset head to given epoch", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if !cctx.Bool("genesis") && !cctx.IsSet("epoch") && cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } var ts *types.TipSet if cctx.Bool("genesis") { ts, err = api.ChainGetGenesis(ctx) } if ts == nil && cctx.IsSet("epoch") { ts, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Uint64("epoch")), types.EmptyTSK) } if ts == nil { ts, err = parseTipSet(ctx, api, cctx.Args().Slice()) } if err != nil { return err } if ts == nil { return fmt.Errorf("must pass cids for tipset to set as head") } if err := api.ChainSetHead(ctx, ts.Key()); err != nil { return err } return nil }, }
var ChainStatObjCmd = &cli.Command{ Name: "stat-obj", Usage: "Collect size and ipld link counts for objs", ArgsUsage: "[cid]", Description: `Collect object size and ipld link count for an object. When a base is provided it will be walked first, and all links visisted will be ignored when the passed in object is walked. `, Flags: []cli.Flag{ &cli.StringFlag{ Name: "base", Usage: "ignore links found in this obj", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } obj, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to parse cid input: %s", err) } base := cid.Undef if cctx.IsSet("base") { base, err = cid.Decode(cctx.String("base")) if err != nil { return err } } stats, err := api.ChainStatObj(ctx, obj, base) if err != nil { return err } afmt.Printf("Links: %d\n", stats.Links) afmt.Printf("Size: %s (%d)\n", types.SizeStr(types.NewInt(stats.Size)), stats.Size) return nil }, }
var CommonCommands = []*cli.Command{ AuthCmd, LogCmd, WaitApiCmd, FetchParamCmd, PprofCmd, VersionCmd, }
var DaemonContext = cliutil.DaemonContext
var ErrAbortedByUser = errors.New("aborted by user")
var ErrCheckFailed = fmt.Errorf("check has failed")
var EvmCallSimulateCmd = &cli.Command{ Name: "call", Usage: "Simulate an eth contract call", ArgsUsage: "[from] [to] [params]", Action: func(cctx *cli.Context) error { if cctx.NArg() != 3 { return IncorrectNumArgs(cctx) } fromEthAddr, err := ethtypes.ParseEthAddress(cctx.Args().Get(0)) if err != nil { return err } toEthAddr, err := ethtypes.ParseEthAddress(cctx.Args().Get(1)) if err != nil { return err } params, err := ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(2)) if err != nil { return err } api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) res, err := api.EthCall(ctx, ethtypes.EthCall{ From: &fromEthAddr, To: &toEthAddr, Data: params, }, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) if err != nil { fmt.Println("Eth call fails, return val: ", res) return err } fmt.Println("Result: ", res) return nil }, }
var EvmCmd = &cli.Command{ Name: "evm", Usage: "Commands related to the Filecoin EVM runtime", Subcommands: []*cli.Command{ EvmDeployCmd, EvmInvokeCmd, EvmGetInfoCmd, EvmCallSimulateCmd, EvmGetContractAddress, EvmGetBytecode, }, }
var EvmDeployCmd = &cli.Command{ Name: "deploy", Usage: "Deploy an EVM smart contract and return its address", ArgsUsage: "contract", Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", Usage: "optionally specify the account to use for sending the creation message", }, &cli.BoolFlag{ Name: "hex", Usage: "use when input contract is in hex", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if argc := cctx.Args().Len(); argc != 1 { return xerrors.Errorf("must pass the contract init code") } contract, err := os.ReadFile(cctx.Args().First()) if err != nil { return xerrors.Errorf("failed to read contract: %w", err) } if cctx.Bool("hex") { contract, err = ethtypes.DecodeHexStringTrimSpace(string(contract)) if err != nil { return xerrors.Errorf("failed to decode contract: %w", err) } } var fromAddr address.Address if from := cctx.String("from"); from == "" { fromAddr, err = api.WalletDefaultAddress(ctx) } else { fromAddr, err = address.NewFromString(from) } if err != nil { return err } initcode := abi.CborBytes(contract) params, err := actors.SerializeParams(&initcode) if err != nil { return fmt.Errorf("failed to serialize Create params: %w", err) } msg := &types.Message{ To: builtintypes.EthereumAddressManagerActorAddr, From: fromAddr, Value: big.Zero(), Method: builtintypes.MethodsEAM.CreateExternal, Params: params, } afmt.Println("sending message...") smsg, err := api.MpoolPushMessage(ctx, msg, nil) if err != nil { return xerrors.Errorf("failed to push message: %w", err) } afmt.Println("waiting for message to execute...") wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0) if err != nil { return xerrors.Errorf("error waiting for message: %w", err) } if wait.Receipt.ExitCode != 0 { return xerrors.Errorf("actor execution failed") } var result eam.CreateReturn r := bytes.NewReader(wait.Receipt.Return) if err := result.UnmarshalCBOR(r); err != nil { return xerrors.Errorf("error unmarshaling return value: %w", err) } addr, err := address.NewIDAddress(result.ActorID) if err != nil { return err } afmt.Printf("Actor ID: %d\n", result.ActorID) afmt.Printf("ID Address: %s\n", addr) afmt.Printf("Robust Address: %s\n", result.RobustAddress) afmt.Printf("Eth Address: %s\n", "0x"+hex.EncodeToString(result.EthAddress[:])) ea, err := ethtypes.CastEthAddress(result.EthAddress[:]) if err != nil { return fmt.Errorf("failed to create ethereum address: %w", err) } delegated, err := ea.ToFilecoinAddress() if err != nil { return fmt.Errorf("failed to calculate f4 address: %w", err) } afmt.Printf("f4 Address: %s\n", delegated) if len(wait.Receipt.Return) > 0 { result := base64.StdEncoding.EncodeToString(wait.Receipt.Return) afmt.Printf("Return: %s\n", result) } return nil }, }
var EvmGetBytecode = &cli.Command{ Name: "bytecode", Usage: "Write the bytecode of a smart contract to a file", ArgsUsage: "[contract-address] [file-name]", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "bin", Usage: "write the bytecode as raw binary and don't hex-encode", }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() != 2 { return IncorrectNumArgs(cctx) } contractAddr, err := ethtypes.ParseEthAddress(cctx.Args().Get(0)) if err != nil { return err } fileName := cctx.Args().Get(1) api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) code, err := api.EthGetCode(ctx, contractAddr, ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")) if err != nil { return err } if !cctx.Bool("bin") { newCode := make([]byte, hex.EncodedLen(len(code))) hex.Encode(newCode, code) code = newCode } if err := os.WriteFile(fileName, code, 0o666); err != nil { return xerrors.Errorf("failed to write bytecode to file %s: %w", fileName, err) } fmt.Printf("Code for %s written to %s\n", contractAddr, fileName) return nil }, }
var EvmGetContractAddress = &cli.Command{ Name: "contract-address", Usage: "Generate contract address from smart contract code", ArgsUsage: "[senderEthAddr] [salt] [contractHexPath]", Action: func(cctx *cli.Context) error { if cctx.NArg() != 3 { return IncorrectNumArgs(cctx) } sender, err := ethtypes.ParseEthAddress(cctx.Args().Get(0)) if err != nil { return err } salt, err := ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(1)) if err != nil { return xerrors.Errorf("Could not decode salt: %w", err) } if len(salt) > 32 { return xerrors.Errorf("Len of salt bytes greater than 32") } var fsalt [32]byte copy(fsalt[:], salt[:]) contractBin := cctx.Args().Get(2) if err != nil { return err } contractHex, err := os.ReadFile(contractBin) if err != nil { return err } contract, err := ethtypes.DecodeHexStringTrimSpace(string(contractHex)) if err != nil { return xerrors.Errorf("Could not decode contract file: %w", err) } contractAddr, err := ethtypes.GetContractEthAddressFromCode(sender, fsalt, contract) if err != nil { return err } fmt.Println("Contract Eth address: ", contractAddr) return nil }, }
var EvmGetInfoCmd = &cli.Command{ Name: "stat", Usage: "Print eth/filecoin addrs and code cid", ArgsUsage: "address", Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) addrString := cctx.Args().Get(0) var faddr address.Address var eaddr ethtypes.EthAddress addr, err := address.NewFromString(addrString) if err != nil { eaddr, err = ethtypes.ParseEthAddress(addrString) if err != nil { return xerrors.Errorf("address is not a filecoin or eth address") } faddr, err = eaddr.ToFilecoinAddress() if err != nil { return err } } else { eaddr, faddr, err = ethAddrFromFilecoinAddress(ctx, addr, api) if err != nil { return err } } actor, err := api.StateGetActor(ctx, faddr, types.EmptyTSK) fmt.Println("Filecoin address: ", faddr) fmt.Println("Eth address: ", eaddr) if err != nil { fmt.Printf("Actor lookup failed for faddr %s with error: %s\n", faddr, err) } else { idAddr, err := api.StateLookupID(ctx, faddr, types.EmptyTSK) if err == nil { fmt.Println("ID address: ", idAddr) fmt.Println("Code cid: ", actor.Code.String()) fmt.Println("Actor Type: ", builtin.ActorNameByCode(actor.Code)) } } return nil }, }
var EvmInvokeCmd = &cli.Command{ Name: "invoke", Usage: "Invoke an EVM smart contract using the specified CALLDATA", ArgsUsage: "address calldata", Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", Usage: "optionally specify the account to use for sending the exec message", }, &cli.IntFlag{ Name: "value", Usage: "optionally specify the value to be sent with the invokation message", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if argc := cctx.Args().Len(); argc != 2 { return xerrors.Errorf("must pass the address and calldata") } addr, err := address.NewFromString(cctx.Args().Get(0)) if err != nil { return xerrors.Errorf("failed to decode address: %w", err) } var calldata []byte calldata, err = ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(1)) if err != nil { return xerrors.Errorf("decoding hex input data: %w", err) } var buffer bytes.Buffer if err := cbg.WriteByteArray(&buffer, calldata); err != nil { return xerrors.Errorf("failed to encode evm params as cbor: %w", err) } calldata = buffer.Bytes() var fromAddr address.Address if from := cctx.String("from"); from == "" { defaddr, err := api.WalletDefaultAddress(ctx) if err != nil { return err } fromAddr = defaddr } else { addr, err := address.NewFromString(from) if err != nil { return err } fromAddr = addr } val := abi.NewTokenAmount(cctx.Int64("value")) msg := &types.Message{ To: addr, From: fromAddr, Value: val, Method: builtintypes.MethodsEVM.InvokeContract, Params: calldata, } afmt.Println("sending message...") smsg, err := api.MpoolPushMessage(ctx, msg, nil) if err != nil { return xerrors.Errorf("failed to push message: %w", err) } afmt.Println("waiting for message to execute...") wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0) if err != nil { return xerrors.Errorf("error waiting for message: %w", err) } if wait.Receipt.ExitCode != 0 { return xerrors.Errorf("actor execution failed") } afmt.Println("Gas used: ", wait.Receipt.GasUsed) result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return))) if err != nil { return xerrors.Errorf("evm result not correctly encoded: %w", err) } if len(result) > 0 { afmt.Println(hex.EncodeToString(result)) } else { afmt.Println("OK") } if eventsRoot := wait.Receipt.EventsRoot; eventsRoot != nil { afmt.Println("Events emitted:") s := &apiIpldStore{ctx, api} amt, err := amt4.LoadAMT(ctx, s, *eventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth)) if err != nil { return err } var evt types.Event err = amt.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error { fmt.Printf("%x\n", deferred.Raw) if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil { return err } if err != nil { return err } fmt.Printf("\tEmitter ID: %s\n", evt.Emitter) for _, e := range evt.Entries { value, err := cbg.ReadByteArray(bytes.NewBuffer(e.Value), uint64(len(e.Value))) if err != nil { return err } fmt.Printf("\t\tKey: %s, Value: 0x%x, Flags: b%b\n", e.Key, value, e.Flags) } return nil }) } if err != nil { return err } return nil }, }
var FetchParamCmd = &cli.Command{ Name: "fetch-params", Usage: "Fetch proving parameters", ArgsUsage: "[sectorSize]", Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } sectorSizeInt, err := units.RAMInBytes(cctx.Args().First()) if err != nil { return xerrors.Errorf("error parsing sector size (specify as \"32GiB\", for instance): %w", err) } sectorSize := uint64(sectorSizeInt) err = paramfetch.GetParams(ReqContext(cctx), build.ParametersJSON(), build.SrsJSON(), sectorSize) if err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } return nil }, }
var FilplusCmd = &cli.Command{
Name: "filplus",
Usage: "Interact with the verified registry actor used by Filplus",
Flags: []cli.Flag{},
Subcommands: []*cli.Command{
filplusVerifyClientCmd,
filplusListNotariesCmd,
filplusListClientsCmd,
filplusCheckClientCmd,
filplusCheckNotaryCmd,
filplusSignRemoveDataCapProposal,
filplusListAllocationsCmd,
filplusListClaimsCmd,
filplusRemoveExpiredAllocationsCmd,
filplusRemoveExpiredClaimsCmd,
filplusExtendClaimCmd,
},
}
var GetAPI = cliutil.GetCommonAPI
var GetAPIInfo = cliutil.GetAPIInfo
var GetFullNodeAPI = cliutil.GetFullNodeAPI
var GetFullNodeAPIV1 = cliutil.GetFullNodeAPIV1
var GetGatewayAPI = cliutil.GetGatewayAPI
var GetRawAPI = cliutil.GetRawAPI
var GetStorageMinerAPI = cliutil.GetStorageMinerAPI
var GetWorkerAPI = cliutil.GetWorkerAPI
var InfoCmd = &cli.Command{
Name: "info",
Usage: "Print node info",
Action: infoCmdAct,
}
var LogAlerts = &cli.Command{ Name: "alerts", Usage: "Get alert states", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "all", Usage: "get all (active and inactive) alerts", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) alerts, err := api.LogAlerts(ctx) if err != nil { return xerrors.Errorf("getting alerts: %w", err) } all := cctx.Bool("all") for _, alert := range alerts { if !all && !alert.Active { continue } active := color.RedString("active ") if !alert.Active { active = color.GreenString("inactive") } fmt.Printf("%s %s:%s\n", active, alert.Type.System, alert.Type.Subsystem) if alert.LastResolved != nil { fmt.Printf(" last resolved at %s; reason: %s\n", alert.LastResolved.Time.Truncate(time.Millisecond), alert.LastResolved.Message) } if alert.LastActive != nil { fmt.Printf(" %s %s; reason: %s\n", color.YellowString("last raised at"), alert.LastActive.Time.Truncate(time.Millisecond), alert.LastActive.Message) } } return nil }, }
var LogCmd = &cli.Command{ Name: "log", Usage: "Manage logging", Subcommands: []*cli.Command{ LogList, LogSetLevel, LogAlerts, }, }
var LogList = &cli.Command{ Name: "list", Usage: "List log systems", Action: func(cctx *cli.Context) error { api, closer, err := GetAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) systems, err := api.LogList(ctx) if err != nil { return err } for _, system := range systems { fmt.Println(system) } return nil }, }
var LogSetLevel = &cli.Command{ Name: "set-level", Usage: "Set log level", ArgsUsage: "[level]", Description: `Set the log level for logging systems: The system flag can be specified multiple times. eg) log set-level --system chain --system chainxchg debug Available Levels: debug info warn error Environment Variables: GOLOG_LOG_LEVEL - Default log level for all log systems GOLOG_LOG_FMT - Change output log format (json, nocolor) GOLOG_FILE - Write logs to file GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr `, Flags: []cli.Flag{ &cli.StringSliceFlag{ Name: "system", Usage: "limit to log system", Value: &cli.StringSlice{}, }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if !cctx.Args().Present() { return fmt.Errorf("level is required") } systems := cctx.StringSlice("system") if len(systems) == 0 { var err error systems, err = api.LogList(ctx) if err != nil { return err } } for _, system := range systems { if err := api.LogSetLevel(ctx, system, cctx.Args().First()); err != nil { return xerrors.Errorf("setting log level on %s: %v", system, err) } } return nil }, }
var MpoolClear = &cli.Command{ Name: "clear", Usage: "Clear all pending messages from the mpool (USE WITH CARE) (DEPRECATED)", Hidden: true, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "local", Usage: "also clear local messages", }, &cli.BoolFlag{ Name: "really-do-it", Usage: "must be specified for the action to take effect", }, }, Action: func(cctx *cli.Context) error { fmt.Println("DEPRECATED: This behavior is being moved to `lotus-shed mpool clear`") api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() really := cctx.Bool("really-do-it") if !really { return fmt.Errorf("--really-do-it must be specified for this action to have an effect; you have been warned") } local := cctx.Bool("local") ctx := ReqContext(cctx) return api.MpoolClear(ctx, local) }, }
Deprecated: MpoolClear is now available at `lotus-shed mpool clear`
var MpoolCmd = &cli.Command{ Name: "mpool", Usage: "Manage message pool", Subcommands: []*cli.Command{ MpoolPending, MpoolClear, MpoolSub, MpoolStat, MpoolReplaceCmd, MpoolFindCmd, MpoolConfig, MpoolGasPerfCmd, mpoolManage, }, }
var MpoolConfig = &cli.Command{ Name: "config", Usage: "get or set current mpool configuration", ArgsUsage: "[new-config]", Action: func(cctx *cli.Context) error { if cctx.NArg() > 1 { return IncorrectNumArgs(cctx) } afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() == 0 { cfg, err := api.MpoolGetConfig(ctx) if err != nil { return err } bytes, err := json.Marshal(cfg) if err != nil { return err } afmt.Println(string(bytes)) } else { cfg := new(types.MpoolConfig) bytes := []byte(cctx.Args().Get(0)) err := json.Unmarshal(bytes, cfg) if err != nil { return err } return api.MpoolSetConfig(ctx, cfg) } return nil }, }
var MpoolFindCmd = &cli.Command{ Name: "find", Usage: "find a message in the mempool", Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", Usage: "search for messages with given 'from' address", }, &cli.StringFlag{ Name: "to", Usage: "search for messages with given 'to' address", }, &cli.Int64Flag{ Name: "method", Usage: "search for messages with given method", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pending, err := api.MpoolPending(ctx, types.EmptyTSK) if err != nil { return err } var toFilter, fromFilter address.Address if cctx.IsSet("to") { a, err := address.NewFromString(cctx.String("to")) if err != nil { return xerrors.Errorf("'to' address was invalid: %w", err) } toFilter = a } if cctx.IsSet("from") { a, err := address.NewFromString(cctx.String("from")) if err != nil { return xerrors.Errorf("'from' address was invalid: %w", err) } fromFilter = a } var methodFilter *abi.MethodNum if cctx.IsSet("method") { m := abi.MethodNum(cctx.Int64("method")) methodFilter = &m } var out []*types.SignedMessage for _, m := range pending { if toFilter != address.Undef && m.Message.To != toFilter { continue } if fromFilter != address.Undef && m.Message.From != fromFilter { continue } if methodFilter != nil && *methodFilter != m.Message.Method { continue } out = append(out, m) } b, err := json.MarshalIndent(out, "", " ") if err != nil { return err } afmt.Println(string(b)) return nil }, }
var MpoolGasPerfCmd = &cli.Command{ Name: "gas-perf", Usage: "Check gas performance of messages in mempool", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "all", Usage: "print gas performance for all mempool messages (default only prints for local)", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) msgs, err := api.MpoolPending(ctx, types.EmptyTSK) if err != nil { return err } var filter map[address.Address]struct{} if !cctx.Bool("all") { filter = map[address.Address]struct{}{} addrss, err := api.WalletList(ctx) if err != nil { return xerrors.Errorf("getting local addresses: %w", err) } for _, a := range addrss { filter[a] = struct{}{} } var filtered []*types.SignedMessage for _, msg := range msgs { if _, has := filter[msg.Message.From]; !has { continue } filtered = append(filtered, msg) } msgs = filtered } ts, err := api.ChainHead(ctx) if err != nil { return xerrors.Errorf("failed to get chain head: %w", err) } baseFee := ts.Blocks()[0].ParentBaseFee bigBlockGasLimit := big.NewInt(build.BlockGasLimit) getGasReward := func(msg *types.SignedMessage) big.Int { maxPremium := types.BigSub(msg.Message.GasFeeCap, baseFee) if types.BigCmp(maxPremium, msg.Message.GasPremium) < 0 { maxPremium = msg.Message.GasPremium } return types.BigMul(maxPremium, types.NewInt(uint64(msg.Message.GasLimit))) } getGasPerf := func(gasReward big.Int, gasLimit int64) float64 { a := new(stdbig.Rat).SetInt(new(stdbig.Int).Mul(gasReward.Int, bigBlockGasLimit.Int)) b := stdbig.NewRat(1, gasLimit) c := new(stdbig.Rat).Mul(a, b) r, _ := c.Float64() return r } for _, m := range msgs { gasReward := getGasReward(m) gasPerf := getGasPerf(gasReward, m.Message.GasLimit) afmt.Printf("%s\t%d\t%s\t%f\n", m.Message.From, m.Message.Nonce, gasReward, gasPerf) } return nil }, }
var MpoolPending = &cli.Command{ Name: "pending", Usage: "Get pending messages", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "local", Usage: "print pending messages for addresses in local wallet only", }, &cli.BoolFlag{ Name: "cids", Usage: "only print cids of messages in output", }, &cli.StringFlag{ Name: "to", Usage: "return messages to a given address", }, &cli.StringFlag{ Name: "from", Usage: "return messages from a given address", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var toa, froma address.Address if tos := cctx.String("to"); tos != "" { a, err := address.NewFromString(tos) if err != nil { return xerrors.Errorf("given 'to' address %q was invalid: %w", tos, err) } toa = a } if froms := cctx.String("from"); froms != "" { a, err := address.NewFromString(froms) if err != nil { return xerrors.Errorf("given 'from' address %q was invalid: %w", froms, err) } froma = a } var filter map[address.Address]struct{} if cctx.Bool("local") { filter = map[address.Address]struct{}{} addrss, err := api.WalletList(ctx) if err != nil { return xerrors.Errorf("getting local addresses: %w", err) } for _, a := range addrss { filter[a] = struct{}{} } } msgs, err := api.MpoolPending(ctx, types.EmptyTSK) if err != nil { return err } for _, msg := range msgs { if filter != nil { if _, has := filter[msg.Message.From]; !has { continue } } if toa != address.Undef && msg.Message.To != toa { continue } if froma != address.Undef && msg.Message.From != froma { continue } if cctx.Bool("cids") { afmt.Println(msg.Cid()) } else { out, err := json.MarshalIndent(msg, "", " ") if err != nil { return err } afmt.Println(string(out)) } } return nil }, }
var MpoolReplaceCmd = &cli.Command{ Name: "replace", Usage: "replace a message in the mempool", Flags: []cli.Flag{ &cli.StringFlag{ Name: "gas-feecap", Usage: "gas feecap for new message (burn and pay to miner, attoFIL/GasUnit)", }, &cli.StringFlag{ Name: "gas-premium", Usage: "gas price for new message (pay to miner, attoFIL/GasUnit)", }, &cli.Int64Flag{ Name: "gas-limit", Usage: "gas limit for new message (GasUnit)", }, &cli.BoolFlag{ Name: "auto", Usage: "automatically reprice the specified message", }, &cli.StringFlag{ Name: "fee-limit", Usage: "Spend up to X FIL for this message in units of FIL. Previously when flag was `max-fee` units were in attoFIL. Applicable for auto mode", }, }, ArgsUsage: "<from> <nonce> | <message-cid>", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var from address.Address var nonce uint64 switch cctx.NArg() { case 1: mcid, err := cid.Decode(cctx.Args().First()) if err != nil { return err } msg, err := api.ChainGetMessage(ctx, mcid) if err != nil { return xerrors.Errorf("could not find referenced message: %w", err) } from = msg.From nonce = msg.Nonce case 2: arg0 := cctx.Args().Get(0) f, err := address.NewFromString(arg0) if err != nil { return err } n, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) if err != nil { return err } from = f nonce = n default: return cli.ShowCommandHelp(cctx, cctx.Command.Name) } ts, err := api.ChainHead(ctx) if err != nil { return xerrors.Errorf("getting chain head: %w", err) } pending, err := api.MpoolPending(ctx, ts.Key()) if err != nil { return err } var found *types.SignedMessage for _, p := range pending { if p.Message.From == from && p.Message.Nonce == nonce { found = p break } } if found == nil { return xerrors.Errorf("no pending message found from %s with nonce %d", from, nonce) } msg := found.Message if cctx.Bool("auto") { cfg, err := api.MpoolGetConfig(ctx) if err != nil { return xerrors.Errorf("failed to lookup the message pool config: %w", err) } defaultRBF := messagepool.ComputeRBF(msg.GasPremium, cfg.ReplaceByFeeRatio) var mss *lapi.MessageSendSpec if cctx.IsSet("fee-limit") { maxFee, err := types.ParseFIL(cctx.String("fee-limit")) if err != nil { return xerrors.Errorf("parsing max-spend: %w", err) } mss = &lapi.MessageSendSpec{ MaxFee: abi.TokenAmount(maxFee), } } msg.GasFeeCap = abi.NewTokenAmount(0) msg.GasPremium = abi.NewTokenAmount(0) retm, err := api.GasEstimateMessageGas(ctx, &msg, mss, types.EmptyTSK) if err != nil { return xerrors.Errorf("failed to estimate gas values: %w", err) } msg.GasPremium = big.Max(retm.GasPremium, defaultRBF) msg.GasFeeCap = big.Max(retm.GasFeeCap, msg.GasPremium) mff := func() (abi.TokenAmount, error) { return abi.TokenAmount(config.DefaultDefaultMaxFee()), nil } messagepool.CapGasFee(mff, &msg, mss) } else { if cctx.IsSet("gas-limit") { msg.GasLimit = cctx.Int64("gas-limit") } msg.GasPremium, err = types.BigFromString(cctx.String("gas-premium")) if err != nil { return xerrors.Errorf("parsing gas-premium: %w", err) } msg.GasFeeCap, err = types.BigFromString(cctx.String("gas-feecap")) if err != nil { return xerrors.Errorf("parsing gas-feecap: %w", err) } } smsg, err := api.WalletSignMessage(ctx, msg.From, &msg) if err != nil { return xerrors.Errorf("failed to sign message: %w", err) } cid, err := api.MpoolPush(ctx, smsg) if err != nil { return xerrors.Errorf("failed to push new message to mempool: %w", err) } afmt.Println("new message cid: ", cid) return nil }, }
var MpoolStat = &cli.Command{ Name: "stat", Usage: "print mempool stats", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "local", Usage: "print stats for addresses in local wallet only", }, &cli.IntFlag{ Name: "basefee-lookback", Usage: "number of blocks to look back for minimum basefee", Value: 60, }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := api.ChainHead(ctx) if err != nil { return xerrors.Errorf("getting chain head: %w", err) } currBF := ts.Blocks()[0].ParentBaseFee minBF := currBF { currTs := ts for i := 0; i < cctx.Int("basefee-lookback"); i++ { currTs, err = api.ChainGetTipSet(ctx, currTs.Parents()) if err != nil { return xerrors.Errorf("walking chain: %w", err) } if newBF := currTs.Blocks()[0].ParentBaseFee; newBF.LessThan(minBF) { minBF = newBF } } } var filter map[address.Address]struct{} if cctx.Bool("local") { filter = map[address.Address]struct{}{} addrss, err := api.WalletList(ctx) if err != nil { return xerrors.Errorf("getting local addresses: %w", err) } for _, a := range addrss { filter[a] = struct{}{} } } msgs, err := api.MpoolPending(ctx, types.EmptyTSK) if err != nil { return err } type statBucket struct { msgs map[uint64]*types.SignedMessage } type mpStat struct { addr string past, cur, future uint64 belowCurr, belowPast uint64 gasLimit big.Int } buckets := map[address.Address]*statBucket{} for _, v := range msgs { if filter != nil { if _, has := filter[v.Message.From]; !has { continue } } bkt, ok := buckets[v.Message.From] if !ok { bkt = &statBucket{ msgs: map[uint64]*types.SignedMessage{}, } buckets[v.Message.From] = bkt } bkt.msgs[v.Message.Nonce] = v } var out []mpStat for a, bkt := range buckets { act, err := api.StateGetActor(ctx, a, ts.Key()) if err != nil { afmt.Printf("%s, err: %s\n", a, err) continue } cur := act.Nonce for { _, ok := bkt.msgs[cur] if !ok { break } cur++ } var s mpStat s.addr = a.String() s.gasLimit = big.Zero() for _, m := range bkt.msgs { if m.Message.Nonce < act.Nonce { s.past++ } else if m.Message.Nonce > cur { s.future++ } else { s.cur++ } if m.Message.GasFeeCap.LessThan(currBF) { s.belowCurr++ } if m.Message.GasFeeCap.LessThan(minBF) { s.belowPast++ } s.gasLimit = big.Add(s.gasLimit, types.NewInt(uint64(m.Message.GasLimit))) } out = append(out, s) } sort.Slice(out, func(i, j int) bool { return out[i].addr < out[j].addr }) var total mpStat total.gasLimit = big.Zero() for _, stat := range out { total.past += stat.past total.cur += stat.cur total.future += stat.future total.belowCurr += stat.belowCurr total.belowPast += stat.belowPast total.gasLimit = big.Add(total.gasLimit, stat.gasLimit) afmt.Printf("%s: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", stat.addr, stat.past, stat.cur, stat.future, stat.belowCurr, cctx.Int("basefee-lookback"), stat.belowPast, stat.gasLimit) } afmt.Println("-----") afmt.Printf("total: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d, gasLimit: %s\n", total.past, total.cur, total.future, total.belowCurr, cctx.Int("basefee-lookback"), total.belowPast, total.gasLimit) return nil }, }
var MpoolSub = &cli.Command{ Name: "sub", Usage: "Subscribe to mpool changes", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) sub, err := api.MpoolSub(ctx) if err != nil { return err } for { select { case update := <-sub: out, err := json.MarshalIndent(update, "", " ") if err != nil { return err } fmt.Println(string(out)) case <-ctx.Done(): return nil } } }, }
var MultisigCmd = &cli.Command{ Name: "msig", Usage: "Interact with a multisig wallet", Flags: []cli.Flag{ &cli.IntFlag{ Name: "confidence", Usage: "number of block confirmations to wait for", Value: int(build.MessageConfidence), }, }, Subcommands: []*cli.Command{ msigCreateCmd, msigInspectCmd, msigProposeCmd, msigRemoveProposeCmd, msigApproveCmd, msigCancelCmd, msigAddProposeCmd, msigAddApproveCmd, msigAddCancelCmd, msigSwapProposeCmd, msigSwapApproveCmd, msigSwapCancelCmd, msigLockProposeCmd, msigLockApproveCmd, msigLockCancelCmd, msigVestedCmd, msigProposeThresholdCmd, }, }
var NetBandwidthCmd = &cli.Command{ Name: "bandwidth", Usage: "Print bandwidth usage information", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "by-peer", Usage: "list bandwidth usage by peer", }, &cli.BoolFlag{ Name: "by-protocol", Usage: "list bandwidth usage by protocol", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) bypeer := cctx.Bool("by-peer") byproto := cctx.Bool("by-protocol") tw := tabwriter.NewWriter(os.Stdout, 4, 4, 2, ' ', 0) _, _ = fmt.Fprintf(tw, "Segment\tTotalIn\tTotalOut\tRateIn\tRateOut\n") if bypeer { bw, err := api.NetBandwidthStatsByPeer(ctx) if err != nil { return err } var peers []string for p := range bw { peers = append(peers, p) } sort.Slice(peers, func(i, j int) bool { return peers[i] < peers[j] }) for _, p := range peers { s := bw[p] _, _ = fmt.Fprintf( tw, "%s\t%s\t%s\t%s/s\t%s/s\n", p, humanize.Bytes(uint64(s.TotalIn)), humanize.Bytes(uint64(s.TotalOut)), humanize.Bytes(uint64(s.RateIn)), humanize.Bytes(uint64(s.RateOut)), ) } } else if byproto { bw, err := api.NetBandwidthStatsByProtocol(ctx) if err != nil { return err } var protos []protocol.ID for p := range bw { protos = append(protos, p) } sort.Slice(protos, func(i, j int) bool { return protos[i] < protos[j] }) for _, p := range protos { s := bw[p] if p == "" { p = "<unknown>" } _, _ = fmt.Fprintf( tw, "%s\t%s\t%s\t%s/s\t%s/s\n", p, humanize.Bytes(uint64(s.TotalIn)), humanize.Bytes(uint64(s.TotalOut)), humanize.Bytes(uint64(s.RateIn)), humanize.Bytes(uint64(s.RateOut)), ) } } else { s, err := api.NetBandwidthStats(ctx) if err != nil { return err } _, _ = fmt.Fprintf( tw, "Total\t%s\t%s\t%s/s\t%s/s\n", humanize.Bytes(uint64(s.TotalIn)), humanize.Bytes(uint64(s.TotalOut)), humanize.Bytes(uint64(s.RateIn)), humanize.Bytes(uint64(s.RateOut)), ) } return tw.Flush() }, }
var NetBlockAddCmd = &cli.Command{ Name: "add", Usage: "Add connection gating rules", Subcommands: []*cli.Command{ NetBlockAddPeer, NetBlockAddIP, NetBlockAddSubnet, }, }
var NetBlockAddIP = &cli.Command{ Name: "ip", Usage: "Block an IP address", ArgsUsage: "<IP> ...", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) return api.NetBlockAdd(ctx, atypes.NetBlockList{IPAddrs: cctx.Args().Slice()}) }, }
var NetBlockAddPeer = &cli.Command{ Name: "peer", Usage: "Block a peer", ArgsUsage: "<Peer> ...", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var peers []peer.ID for _, s := range cctx.Args().Slice() { p, err := peer.Decode(s) if err != nil { return err } peers = append(peers, p) } return api.NetBlockAdd(ctx, atypes.NetBlockList{Peers: peers}) }, }
var NetBlockAddSubnet = &cli.Command{ Name: "subnet", Usage: "Block an IP subnet", ArgsUsage: "<CIDR> ...", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) return api.NetBlockAdd(ctx, atypes.NetBlockList{IPSubnets: cctx.Args().Slice()}) }, }
var NetBlockCmd = &cli.Command{ Name: "block", Usage: "Manage network connection gating rules", Subcommands: []*cli.Command{ NetBlockAddCmd, NetBlockRemoveCmd, NetBlockListCmd, }, }
var NetBlockListCmd = &cli.Command{ Name: "list", Usage: "list connection gating rules", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) acl, err := api.NetBlockList(ctx) if err != nil { return err } if len(acl.Peers) != 0 { sort.Slice(acl.Peers, func(i, j int) bool { return strings.Compare(string(acl.Peers[i]), string(acl.Peers[j])) > 0 }) fmt.Println("Blocked Peers:") for _, p := range acl.Peers { fmt.Printf("\t%s\n", p) } } if len(acl.IPAddrs) != 0 { sort.Slice(acl.IPAddrs, func(i, j int) bool { return strings.Compare(acl.IPAddrs[i], acl.IPAddrs[j]) < 0 }) fmt.Println("Blocked IPs:") for _, a := range acl.IPAddrs { fmt.Printf("\t%s\n", a) } } if len(acl.IPSubnets) != 0 { sort.Slice(acl.IPSubnets, func(i, j int) bool { return strings.Compare(acl.IPSubnets[i], acl.IPSubnets[j]) < 0 }) fmt.Println("Blocked Subnets:") for _, n := range acl.IPSubnets { fmt.Printf("\t%s\n", n) } } return nil }, }
var NetBlockRemoveCmd = &cli.Command{ Name: "remove", Usage: "Remove connection gating rules", Subcommands: []*cli.Command{ NetBlockRemovePeer, NetBlockRemoveIP, NetBlockRemoveSubnet, }, }
var NetBlockRemoveIP = &cli.Command{ Name: "ip", Usage: "Unblock an IP address", ArgsUsage: "<IP> ...", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) return api.NetBlockRemove(ctx, atypes.NetBlockList{IPAddrs: cctx.Args().Slice()}) }, }
var NetBlockRemovePeer = &cli.Command{ Name: "peer", Usage: "Unblock a peer", ArgsUsage: "<Peer> ...", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var peers []peer.ID for _, s := range cctx.Args().Slice() { p, err := peer.Decode(s) if err != nil { return err } peers = append(peers, p) } return api.NetBlockRemove(ctx, atypes.NetBlockList{Peers: peers}) }, }
var NetBlockRemoveSubnet = &cli.Command{ Name: "subnet", Usage: "Unblock an IP subnet", ArgsUsage: "<CIDR> ...", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) return api.NetBlockRemove(ctx, atypes.NetBlockList{IPSubnets: cctx.Args().Slice()}) }, }
var NetCmd = &cli.Command{ Name: "net", Usage: "Manage P2P Network", Subcommands: []*cli.Command{ NetPeers, NetPing, NetConnect, NetDisconnect, NetListen, NetId, NetFindPeer, NetScores, NetReachability, NetBandwidthCmd, NetBlockCmd, NetStatCmd, NetLimitCmd, NetProtectAdd, NetProtectRemove, NetProtectList, }, }
var NetConnect = &cli.Command{ Name: "connect", Usage: "Connect to a peer", ArgsUsage: "[peerMultiaddr|minerActorAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pis, err := AddrInfoFromArg(ctx, cctx) if err != nil { return err } for _, pi := range pis { fmt.Printf("connect %s: ", pi.ID) err := api.NetConnect(ctx, pi) if err != nil { fmt.Println("failure") return err } fmt.Println("success") } return nil }, }
var NetDisconnect = &cli.Command{ Name: "disconnect", Usage: "Disconnect from a peer", ArgsUsage: "[peerID]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ids := cctx.Args().Slice() for _, id := range ids { pid, err := peer.Decode(id) if err != nil { fmt.Println("failure") return err } fmt.Printf("disconnect %s: ", pid) err = api.NetDisconnect(ctx, pid) if err != nil { fmt.Println("failure") return err } fmt.Println("success") } return nil }, }
var NetFindPeer = &cli.Command{ Name: "find-peer", Aliases: []string{"findpeer"}, Usage: "Find the addresses of a given peerID", ArgsUsage: "[peerId]", Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { fmt.Println("Usage: findpeer [peer ID]") return nil } pid, err := peer.Decode(cctx.Args().First()) if err != nil { return err } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) addrs, err := api.NetFindPeer(ctx, pid) if err != nil { return err } fmt.Println(addrs) return nil }, }
var NetId = &cli.Command{ Name: "id", Usage: "Get node identity", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pid, err := api.ID(ctx) if err != nil { return err } fmt.Println(pid) return nil }, }
var NetLimitCmd = &cli.Command{ Name: "limit", Usage: "Get or set resource limits for a scope", ArgsUsage: "scope [limit]", Description: `Get or set resource limits for a scope. The scope can be one of the following: - system -- reports the system aggregate resource usage. - transient -- reports the transient resource usage. - svc:<service> -- reports the resource usage of a specific service. - proto:<proto> -- reports the resource usage of a specific protocol. - peer:<peer> -- reports the resource usage of a specific peer. The limit is json-formatted, with the same structure as the limits file. `, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "set", Usage: "set the limit for a scope", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) args := cctx.Args().Slice() if cctx.Bool("set") { if len(args) != 2 { return xerrors.Errorf("must specify exactly a scope and a limit") } scope := args[0] limitStr := args[1] var limit atypes.NetLimit err := json.Unmarshal([]byte(limitStr), &limit) if err != nil { return xerrors.Errorf("error decoding limit: %w", err) } return api.NetSetLimit(ctx, scope, limit) } if len(args) != 1 { return xerrors.Errorf("must specify exactly one scope") } scope := args[0] result, err := api.NetLimit(ctx, scope) if err != nil { return err } enc := json.NewEncoder(os.Stdout) return enc.Encode(result) }, }
var NetListen = &cli.Command{ Name: "listen", Usage: "List listen addresses", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) addrs, err := api.NetAddrsListen(ctx) if err != nil { return err } for _, peer := range addrs.Addrs { fmt.Printf("%s/p2p/%s\n", peer, addrs.ID) } return nil }, }
var NetPeers = &cli.Command{ Name: "peers", Usage: "Print peers", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "agent", Aliases: []string{"a"}, Usage: "Print agent name", }, &cli.BoolFlag{ Name: "extended", Aliases: []string{"x"}, Usage: "Print extended peer information in json", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) peers, err := api.NetPeers(ctx) if err != nil { return err } sort.Slice(peers, func(i, j int) bool { return strings.Compare(string(peers[i].ID), string(peers[j].ID)) > 0 }) if cctx.Bool("extended") { seen := make(map[peer.ID]struct{}) for _, peer := range peers { _, dup := seen[peer.ID] if dup { continue } seen[peer.ID] = struct{}{} info, err := api.NetPeerInfo(ctx, peer.ID) if err != nil { log.Warnf("error getting extended peer info: %s", err) } else { bytes, err := json.Marshal(&info) if err != nil { log.Warnf("error marshalling extended peer info: %s", err) } else { fmt.Println(string(bytes)) } } } } else { for _, peer := range peers { var agent string if cctx.Bool("agent") { agent, err = api.NetAgentVersion(ctx, peer.ID) if err != nil { log.Warnf("getting agent version: %s", err) } else { agent = ", " + agent } } fmt.Printf("%s, %s%s\n", peer.ID, peer.Addrs, agent) } } return nil }, }
var NetPing = &cli.Command{ Name: "ping", Usage: "Ping peers", ArgsUsage: "[peerMultiaddr]", Flags: []cli.Flag{ &cli.IntFlag{ Name: "count", Value: 10, Aliases: []string{"c"}, Usage: "specify the number of times it should ping", }, &cli.DurationFlag{ Name: "interval", Value: time.Second, Aliases: []string{"i"}, Usage: "minimum time between pings", }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pis, err := AddrInfoFromArg(ctx, cctx) if err != nil { return err } count := cctx.Int("count") interval := cctx.Duration("interval") for _, pi := range pis { err := api.NetConnect(ctx, pi) if err != nil { return xerrors.Errorf("connect: %w", err) } fmt.Printf("PING %s\n", pi.ID) var avg time.Duration var successful int for i := 0; i < count && ctx.Err() == nil; i++ { start := time.Now() rtt, err := api.NetPing(ctx, pi.ID) if err != nil { if ctx.Err() != nil { break } log.Errorf("Ping failed: error=%v", err) continue } fmt.Printf("Pong received: time=%v\n", rtt) avg = avg + rtt successful++ wctx, cancel := context.WithTimeout(ctx, time.Until(start.Add(interval))) <-wctx.Done() cancel() } if successful > 0 { fmt.Printf("Average latency: %v\n", avg/time.Duration(successful)) } } return nil }, }
var NetProtectAdd = &cli.Command{ Name: "protect", Usage: "Add one or more peer IDs to the list of protected peer connections", ArgsUsage: "<peer-id> [<peer-id>...]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pids, err := decodePeerIDsFromArgs(cctx) if err != nil { return err } err = api.NetProtectAdd(ctx, pids) if err != nil { return err } fmt.Println("added to protected peers:") for _, pid := range pids { fmt.Printf(" %s\n", pid) } return nil }, }
var NetProtectList = &cli.Command{ Name: "list-protected", Usage: "List the peer IDs with protected connection.", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pids, err := api.NetProtectList(ctx) if err != nil { return err } for _, pid := range pids { fmt.Printf("%s\n", pid) } return nil }, }
var NetProtectRemove = &cli.Command{ Name: "unprotect", Usage: "Remove one or more peer IDs from the list of protected peer connections.", ArgsUsage: "<peer-id> [<peer-id>...]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) pids, err := decodePeerIDsFromArgs(cctx) if err != nil { return err } err = api.NetProtectRemove(ctx, pids) if err != nil { return err } fmt.Printf("removed from protected peers:") for _, pid := range pids { fmt.Printf(" %s\n", pid) } return nil }, }
var NetReachability = &cli.Command{ Name: "reachability", Usage: "Print information about reachability from the internet", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) i, err := api.NetAutoNatStatus(ctx) if err != nil { return err } fmt.Println("AutoNAT status: ", i.Reachability.String()) if len(i.PublicAddrs) > 0 { fmt.Println("Public address:", i.PublicAddrs) } return nil }, }
var NetScores = &cli.Command{ Name: "scores", Usage: "Print peers' pubsub scores", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "extended", Aliases: []string{"x"}, Usage: "print extended peer scores in json", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) scores, err := api.NetPubsubScores(ctx) if err != nil { return err } if cctx.Bool("extended") { enc := json.NewEncoder(os.Stdout) for _, peer := range scores { err := enc.Encode(peer) if err != nil { return err } } } else { for _, peer := range scores { fmt.Printf("%s, %f\n", peer.ID, peer.Score.Score) } } return nil }, }
var NetStatCmd = &cli.Command{ Name: "stat", Usage: "Report resource usage for a scope", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "json", }, }, ArgsUsage: "scope", Description: `Report resource usage for a scope. The scope can be one of the following: - system -- reports the system aggregate resource usage. - transient -- reports the transient resource usage. - svc:<service> -- reports the resource usage of a specific service. - proto:<proto> -- reports the resource usage of a specific protocol. - peer:<peer> -- reports the resource usage of a specific peer. - all -- reports the resource usage for all currently active scopes. `, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) args := cctx.Args().Slice() if len(args) != 1 { return xerrors.Errorf("must specify exactly one scope") } scope := args[0] result, err := api.NetStat(ctx, scope) if err != nil { return xerrors.Errorf("get stat: %w", err) } if cctx.Bool("json") { enc := json.NewEncoder(os.Stdout) return enc.Encode(result) } printScope := func(stat *network.ScopeStat, scope string) { if stat == nil { return } limit, err := api.NetLimit(ctx, scope) if err != nil { fmt.Printf("error: %s\n", color.RedString("%s", err)) } fmt.Printf("%s\n", scope) fmt.Printf("\tmemory: [%s] %s/%s\n", BarString(float64(limit.Memory), 0, float64(stat.Memory)), types.SizeStr(types.NewInt(uint64(stat.Memory))), types.SizeStr(types.NewInt(uint64(limit.Memory)))) fmt.Printf("\tstreams in: [%s] %d/%d\n", BarString(float64(limit.StreamsInbound), 0, float64(stat.NumStreamsInbound)), stat.NumStreamsInbound, limit.StreamsInbound) fmt.Printf("\tstreams out: [%s] %d/%d\n", BarString(float64(limit.StreamsOutbound), 0, float64(stat.NumStreamsOutbound)), stat.NumStreamsOutbound, limit.StreamsOutbound) fmt.Printf("\tconn in: [%s] %d/%d\n", BarString(float64(limit.ConnsInbound), 0, float64(stat.NumConnsInbound)), stat.NumConnsInbound, limit.ConnsInbound) fmt.Printf("\tconn out: [%s] %d/%d\n", BarString(float64(limit.ConnsOutbound), 0, float64(stat.NumConnsOutbound)), stat.NumConnsOutbound, limit.ConnsOutbound) fmt.Printf("\tfile desc: [%s] %d/%d\n", BarString(float64(limit.FD), 0, float64(stat.NumFD)), stat.NumFD, limit.FD) fmt.Println() } printScope(result.System, "system") printScope(result.Transient, "transient") printScopes := func(name string, st map[string]network.ScopeStat) { type namedStat struct { name string stat network.ScopeStat } stats := make([]namedStat, 0, len(st)) for n, stat := range st { stats = append(stats, namedStat{ name: n, stat: stat, }) } sort.Slice(stats, func(i, j int) bool { if stats[i].stat.Memory == stats[j].stat.Memory { return stats[i].name < stats[j].name } return stats[i].stat.Memory > stats[j].stat.Memory }) for _, stat := range stats { tmp := stat.stat printScope(&tmp, name+stat.name) } } printScopes("svc:", result.Services) printScopes("proto:", result.Protocols) printScopes("peer:", result.Peers) return nil }, }
var PaychCmd = &cli.Command{
Name: "paych",
Usage: "Manage payment channels",
Subcommands: []*cli.Command{
paychAddFundsCmd,
paychListCmd,
paychVoucherCmd,
paychSettleCmd,
paychStatusCmd,
paychStatusByFromToCmd,
paychCloseCmd,
},
}
var PprofCmd = &cli.Command{ Name: "pprof", Hidden: true, Subcommands: []*cli.Command{ PprofGoroutines, }, }
var PprofGoroutines = &cli.Command{ Name: "goroutines", Usage: "Get goroutine stacks", Action: func(cctx *cli.Context) error { ti, ok := cctx.App.Metadata["repoType"] if !ok { log.Errorf("unknown repo type, are you sure you want to use GetAPI?") ti = repo.FullNode } t, ok := ti.(repo.RepoType) if !ok { log.Errorf("repoType type does not match the type of repo.RepoType") } ainfo, err := GetAPIInfo(cctx, t) if err != nil { return xerrors.Errorf("could not get API info for %s: %w", t, err) } addr, err := ainfo.Host() if err != nil { return err } addr = "http://" + addr + "/debug/pprof/goroutine?debug=2" r, err := http.Get(addr) if err != nil { return err } if _, err := io.Copy(os.Stdout, r.Body); err != nil { return err } return r.Body.Close() }, }
var ReqContext = cliutil.ReqContext
var SendCmd = &cli.Command{ Name: "send", Usage: "Send funds between accounts", ArgsUsage: "[targetAddress] [amount]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", Usage: "optionally specify the account to send funds from", }, &cli.StringFlag{ Name: "from-eth-addr", Usage: "optionally specify the eth addr to send funds from", }, &cli.StringFlag{ Name: "gas-premium", Usage: "specify gas price to use in AttoFIL", Value: "0", }, &cli.StringFlag{ Name: "gas-feecap", Usage: "specify gas fee cap to use in AttoFIL", Value: "0", }, &cli.Int64Flag{ Name: "gas-limit", Usage: "specify gas limit", Value: 0, }, &cli.Uint64Flag{ Name: "nonce", Usage: "specify the nonce to use", Value: 0, }, &cli.Uint64Flag{ Name: "method", Usage: "specify method to invoke", Value: uint64(builtin.MethodSend), }, &cli.StringFlag{ Name: "params-json", Usage: "specify invocation parameters in json", }, &cli.StringFlag{ Name: "params-hex", Usage: "specify invocation parameters in hex", }, &cli.BoolFlag{ Name: "force", Usage: "Deprecated: use global 'force-send'", }, }, Action: func(cctx *cli.Context) error { if cctx.IsSet("force") { fmt.Println("'force' flag is deprecated, use global flag 'force-send'") } if cctx.NArg() != 2 { return IncorrectNumArgs(cctx) } srv, err := GetFullNodeServices(cctx) if err != nil { return err } defer srv.Close() ctx := ReqContext(cctx) var params SendParams params.To, err = address.NewFromString(cctx.Args().Get(0)) if err != nil { return ShowHelp(cctx, fmt.Errorf("failed to parse target address: %w", err)) } val, err := types.ParseFIL(cctx.Args().Get(1)) if err != nil { return ShowHelp(cctx, fmt.Errorf("failed to parse amount: %w", err)) } params.Val = abi.TokenAmount(val) if from := cctx.String("from"); from != "" { addr, err := address.NewFromString(from) if err != nil { return err } params.From = addr } else if from := cctx.String("from-eth-addr"); from != "" { eaddr, err := ethtypes.ParseEthAddress(from) if err != nil { return err } faddr, err := eaddr.ToFilecoinAddress() if err != nil { fmt.Println("error on conversion to faddr") return err } fmt.Println("f4 addr: ", faddr) params.From = faddr } if cctx.IsSet("params-hex") { decparams, err := hex.DecodeString(cctx.String("params-hex")) if err != nil { return fmt.Errorf("failed to decode hex params: %w", err) } params.Params = decparams } if ethtypes.IsEthAddress(params.From) { if cctx.IsSet("method") { return xerrors.Errorf("messages from f410f addresses may not specify a method number") } if params.To == builtintypes.EthereumAddressManagerActorAddr { params.Method = builtintypes.MethodsEAM.CreateExternal } else { params.Method = builtintypes.MethodsEVM.InvokeContract } if cctx.IsSet("params-json") { return xerrors.Errorf("may not call with json parameters from an eth account") } if len(params.Params) > 0 { var buf bytes.Buffer if err := cbg.WriteByteArray(&buf, params.Params); err != nil { return xerrors.Errorf("failed to marshal EVM parameters") } params.Params = buf.Bytes() } if !(params.To.Protocol() == address.ID || params.To.Protocol() == address.Delegated) { api := srv.FullNodeAPI() params.To, err = api.StateLookupID(ctx, params.To, types.EmptyTSK) if err != nil { return xerrors.Errorf("addresses starting with f410f can only send to other addresses starting with f410f, or id addresses. could not find id address for %s", params.To.String()) } } } else { params.Method = abi.MethodNum(cctx.Uint64("method")) } if cctx.IsSet("gas-premium") { gp, err := types.BigFromString(cctx.String("gas-premium")) if err != nil { return err } params.GasPremium = &gp } if cctx.IsSet("gas-feecap") { gfc, err := types.BigFromString(cctx.String("gas-feecap")) if err != nil { return err } params.GasFeeCap = &gfc } if cctx.IsSet("gas-limit") { limit := cctx.Int64("gas-limit") params.GasLimit = &limit } if cctx.IsSet("params-json") { if params.Params != nil { return fmt.Errorf("can only specify one of 'params-json' and 'params-hex'") } decparams, err := srv.DecodeTypedParamsFromJSON(ctx, params.To, params.Method, cctx.String("params-json")) if err != nil { return fmt.Errorf("failed to decode json params: %w", err) } params.Params = decparams } if cctx.IsSet("nonce") { n := cctx.Uint64("nonce") params.Nonce = &n } proto, err := srv.MessageForSend(ctx, params) if err != nil { return xerrors.Errorf("creating message prototype: %w", err) } sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { if strings.Contains(err.Error(), "no current EF") { return xerrors.Errorf("transaction rejected on ledger: %w", err) } return err } _, _ = fmt.Fprintf(cctx.App.Writer, "%s\n", sm.Cid()) return nil }, }
var SlashConsensusFault = &cli.Command{ Name: "slash-consensus", Usage: "Report consensus fault", ArgsUsage: "[blockCid1 blockCid2]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", Usage: "optionally specify the account to report consensus from", }, &cli.StringFlag{ Name: "extra", Usage: "Extra block cid", }, }, Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) srv, err := GetFullNodeServices(cctx) if err != nil { return err } defer srv.Close() a := srv.FullNodeAPI() ctx := ReqContext(cctx) c1, err := cid.Parse(cctx.Args().Get(0)) if err != nil { return xerrors.Errorf("parsing cid 1: %w", err) } b1, err := a.ChainGetBlock(ctx, c1) if err != nil { return xerrors.Errorf("getting block 1: %w", err) } c2, err := cid.Parse(cctx.Args().Get(1)) if err != nil { return xerrors.Errorf("parsing cid 2: %w", err) } b2, err := a.ChainGetBlock(ctx, c2) if err != nil { return xerrors.Errorf("getting block 2: %w", err) } if b1.Miner != b2.Miner { return xerrors.Errorf("block1.miner:%s block2.miner:%s", b1.Miner, b2.Miner) } var fromAddr address.Address if from := cctx.String("from"); from == "" { defaddr, err := a.WalletDefaultAddress(ctx) if err != nil { return err } fromAddr = defaddr } else { addr, err := address.NewFromString(from) if err != nil { return err } fromAddr = addr } bh1, err := cborutil.Dump(b1) if err != nil { return err } bh2, err := cborutil.Dump(b2) if err != nil { return err } params := miner.ReportConsensusFaultParams{ BlockHeader1: bh1, BlockHeader2: bh2, } if cctx.String("extra") != "" { cExtra, err := cid.Parse(cctx.String("extra")) if err != nil { return xerrors.Errorf("parsing cid extra: %w", err) } bExtra, err := a.ChainGetBlock(ctx, cExtra) if err != nil { return xerrors.Errorf("getting block extra: %w", err) } be, err := cborutil.Dump(bExtra) if err != nil { return err } params.BlockHeaderExtra = be } enc, err := actors.SerializeParams(¶ms) if err != nil { return err } proto := &api.MessagePrototype{ Message: types.Message{ To: b2.Miner, From: fromAddr, Value: types.NewInt(0), Method: builtin.MethodsMiner.ReportConsensusFault, Params: enc, }, } smsg, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } afmt.Println(smsg.Cid()) return nil }, }
var StateActiveSectorsCmd = &cli.Command{ Name: "active-sectors", Usage: "Query the active sector set of a miner", ArgsUsage: "[minerAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } maddr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } sectors, err := api.StateMinerActiveSectors(ctx, maddr, ts.Key()) if err != nil { return err } for _, s := range sectors { fmt.Printf("%d: %s\n", s.SectorNumber, s.SealedCID) } return nil }, }
var StateCallCmd = &cli.Command{ Name: "call", Usage: "Invoke a method on an actor locally", ArgsUsage: "[toAddress methodId params (optional)]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", Usage: "", Value: builtin.SystemActorAddr.String(), }, &cli.StringFlag{ Name: "value", Usage: "specify value field for invocation", Value: "0", }, &cli.StringFlag{ Name: "ret", Usage: "specify how to parse output (raw, decoded, base64, hex)", Value: "decoded", }, &cli.StringFlag{ Name: "encoding", Value: "base64", Usage: "specify params encoding to parse (base64, hex)", }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() < 2 { return ShowHelp(cctx, fmt.Errorf("must specify at least actor and method to invoke")) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) toa, err := address.NewFromString(cctx.Args().First()) if err != nil { return fmt.Errorf("given 'to' address %q was invalid: %w", cctx.Args().First(), err) } froma, err := address.NewFromString(cctx.String("from")) if err != nil { return fmt.Errorf("given 'from' address %q was invalid: %w", cctx.String("from"), err) } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } method, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) if err != nil { return fmt.Errorf("must pass method as a number") } value, err := types.ParseFIL(cctx.String("value")) if err != nil { return fmt.Errorf("failed to parse 'value': %s", err) } var params []byte if cctx.NArg() > 2 { switch cctx.String("encoding") { case "base64": params, err = base64.StdEncoding.DecodeString(cctx.Args().Get(2)) if err != nil { return xerrors.Errorf("decoding base64 value: %w", err) } case "hex": params, err = hex.DecodeString(cctx.Args().Get(2)) if err != nil { return xerrors.Errorf("decoding hex value: %w", err) } default: return xerrors.Errorf("unrecognized encoding: %s", cctx.String("encoding")) } } ret, err := api.StateCall(ctx, &types.Message{ From: froma, To: toa, Value: types.BigInt(value), Method: abi.MethodNum(method), Params: params, }, ts.Key()) if err != nil { return fmt.Errorf("state call failed: %w", err) } if ret.MsgRct.ExitCode != 0 { return fmt.Errorf("invocation failed (exit: %d, gasUsed: %d): %s", ret.MsgRct.ExitCode, ret.MsgRct.GasUsed, ret.Error) } fmt.Println("Call receipt:") fmt.Printf("Exit code: %d\n", ret.MsgRct.ExitCode) fmt.Printf("Gas Used: %d\n", ret.MsgRct.GasUsed) switch cctx.String("ret") { case "decoded": act, err := api.StateGetActor(ctx, toa, ts.Key()) if err != nil { return xerrors.Errorf("getting actor: %w", err) } retStr, err := JsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return) if err != nil { return xerrors.Errorf("decoding return: %w", err) } fmt.Printf("Return:\n%s\n", retStr) case "raw": fmt.Printf("Return: \n%s\n", ret.MsgRct.Return) case "hex": fmt.Printf("Return: \n%x\n", ret.MsgRct.Return) case "base64": fmt.Printf("Return: \n%s\n", base64.StdEncoding.EncodeToString(ret.MsgRct.Return)) } return nil }, }
var StateCircSupplyCmd = &cli.Command{ Name: "circulating-supply", Usage: "Get the exact current circulating supply of Filecoin", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "vm-supply", Usage: "calculates the approximation of the circulating supply used internally by the VM (instead of the exact amount)", Value: false, }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } if cctx.IsSet("vm-supply") { circ, err := api.StateVMCirculatingSupplyInternal(ctx, ts.Key()) if err != nil { return err } fmt.Println("Circulating supply: ", types.FIL(circ.FilCirculating)) fmt.Println("Mined: ", types.FIL(circ.FilMined)) fmt.Println("Vested: ", types.FIL(circ.FilVested)) fmt.Println("Burnt: ", types.FIL(circ.FilBurnt)) fmt.Println("Locked: ", types.FIL(circ.FilLocked)) } else { circ, err := api.StateCirculatingSupply(ctx, ts.Key()) if err != nil { return err } fmt.Println("Exact circulating supply: ", types.FIL(circ)) return nil } return nil }, }
var StateComputeStateCmd = &cli.Command{ Name: "compute-state", Usage: "Perform state computations", Flags: []cli.Flag{ &cli.Uint64Flag{ Name: "vm-height", Usage: "set the height that the vm will see", }, &cli.BoolFlag{ Name: "apply-mpool-messages", Usage: "apply messages from the mempool to the computed state", }, &cli.BoolFlag{ Name: "show-trace", Usage: "print out full execution trace for given tipset", }, &cli.BoolFlag{ Name: "html", Usage: "generate html report", }, &cli.BoolFlag{ Name: "json", Usage: "generate json output", }, &cli.StringFlag{ Name: "compute-state-output", Usage: "a json file containing pre-existing compute-state output, to generate html reports without rerunning state changes", }, &cli.BoolFlag{ Name: "no-timing", Usage: "don't show timing information in html traces", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) h := abi.ChainEpoch(cctx.Uint64("vm-height")) var ts *types.TipSet if tss := cctx.String("tipset"); tss != "" { ts, err = ParseTipSetRef(ctx, api, tss) } else if h > 0 { ts, err = api.ChainGetTipSetByHeight(ctx, h, types.EmptyTSK) } else { ts, err = api.ChainHead(ctx) } if err != nil { return err } if h == 0 { h = ts.Height() } var msgs []*types.Message if cctx.Bool("apply-mpool-messages") { pmsgs, err := api.MpoolSelect(ctx, ts.Key(), 1) if err != nil { return err } for _, sm := range pmsgs { msgs = append(msgs, &sm.Message) } } var stout *lapi.ComputeStateOutput if csofile := cctx.String("compute-state-output"); csofile != "" { data, err := os.ReadFile(csofile) if err != nil { return err } var o lapi.ComputeStateOutput if err := json.Unmarshal(data, &o); err != nil { return err } stout = &o } else { o, err := api.StateCompute(ctx, h, msgs, ts.Key()) if err != nil { return err } stout = o } if cctx.Bool("json") { out, err := json.Marshal(stout) if err != nil { return err } fmt.Println(string(out)) return nil } if cctx.Bool("html") { st, err := state.LoadStateTree(cbor.NewCborStore(blockstore.NewAPIBlockstore(api)), stout.Root) if err != nil { return xerrors.Errorf("loading state tree: %w", err) } codeCache := map[address.Address]cid.Cid{} getCode := func(addr address.Address) (cid.Cid, error) { if c, found := codeCache[addr]; found { return c, nil } c, err := st.GetActor(addr) if err != nil { return cid.Cid{}, err } codeCache[addr] = c.Code return c.Code, nil } _, _ = fmt.Fprintln(os.Stderr, "computed state cid: ", stout.Root) return ComputeStateHTMLTempl(os.Stdout, ts, stout, !cctx.Bool("no-timing"), getCode) } fmt.Println("computed state cid: ", stout.Root) if cctx.Bool("show-trace") { for _, ir := range stout.Trace { fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", ir.Msg.From, ir.Msg.To, ir.Msg.Value, ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.Return) printInternalExecutions("\t", ir.ExecutionTrace.Subcalls) } } return nil }, }
var StateExecTraceCmd = &cli.Command{ Name: "exec-trace", Usage: "Get the execution trace of a given message", ArgsUsage: "<messageCid>", Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } mcid, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("message cid was invalid: %s", err) } capi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) msg, err := capi.ChainGetMessage(ctx, mcid) if err != nil { return err } lookup, err := capi.StateSearchMsg(ctx, mcid) if err != nil { return err } if lookup == nil { return fmt.Errorf("failed to find message: %s", mcid) } ts, err := capi.ChainGetTipSet(ctx, lookup.TipSet) if err != nil { return err } pts, err := capi.ChainGetTipSet(ctx, ts.Parents()) if err != nil { return err } cso, err := capi.StateCompute(ctx, pts.Height(), nil, pts.Key()) if err != nil { return err } var trace *api.InvocResult for _, t := range cso.Trace { if t.Msg.From == msg.From && t.Msg.Nonce == msg.Nonce { trace = t break } } if trace == nil { return fmt.Errorf("failed to find message in tipset trace output") } out, err := json.MarshalIndent(trace, "", " ") if err != nil { return err } fmt.Println(string(out)) return nil }, }
var StateGetActorCmd = &cli.Command{ Name: "get-actor", Usage: "Print actor information", ArgsUsage: "[actorAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } addr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } a, err := api.StateGetActor(ctx, addr, ts.Key()) if err != nil { return err } strtype := builtin.ActorNameByCode(a.Code) fmt.Printf("Address:\t%s\n", addr) fmt.Printf("Balance:\t%s\n", types.FIL(a.Balance)) fmt.Printf("Nonce:\t\t%d\n", a.Nonce) fmt.Printf("Code:\t\t%s (%s)\n", a.Code, strtype) fmt.Printf("Head:\t\t%s\n", a.Head) if a.DelegatedAddress != nil { fmt.Printf("Delegated address:\t\t%s\n", a.DelegatedAddress) } return nil }, }
var StateGetDealSetCmd = &cli.Command{ Name: "get-deal", Usage: "View on-chain deal info", ArgsUsage: "[dealId]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } dealid, err := strconv.ParseUint(cctx.Args().First(), 10, 64) if err != nil { return xerrors.Errorf("parsing deal ID: %w", err) } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } deal, err := api.StateMarketStorageDeal(ctx, abi.DealID(dealid), ts.Key()) if err != nil { return err } data, err := json.MarshalIndent(deal, "", " ") if err != nil { return err } fmt.Println(string(data)) return nil }, }
var StateListActorsCmd = &cli.Command{ Name: "list-actors", Usage: "list all actors in the network", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } actors, err := api.StateListActors(ctx, ts.Key()) if err != nil { return err } for _, a := range actors { fmt.Println(a.String()) } return nil }, }
var StateListMessagesCmd = &cli.Command{ Name: "list-messages", Usage: "list messages on chain matching given criteria", Flags: []cli.Flag{ &cli.StringFlag{ Name: "to", Usage: "return messages to a given address", }, &cli.StringFlag{ Name: "from", Usage: "return messages from a given address", }, &cli.Uint64Flag{ Name: "toheight", Usage: "don't look before given block height", }, &cli.BoolFlag{ Name: "cids", Usage: "print message CIDs instead of messages", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var toa, froma address.Address if tos := cctx.String("to"); tos != "" { a, err := address.NewFromString(tos) if err != nil { return fmt.Errorf("given 'to' address %q was invalid: %w", tos, err) } toa = a } if froms := cctx.String("from"); froms != "" { a, err := address.NewFromString(froms) if err != nil { return fmt.Errorf("given 'from' address %q was invalid: %w", froms, err) } froma = a } toh := abi.ChainEpoch(cctx.Uint64("toheight")) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } windowSize := abi.ChainEpoch(100) cur := ts for cur.Height() > toh { if ctx.Err() != nil { return ctx.Err() } end := toh if cur.Height()-windowSize > end { end = cur.Height() - windowSize } msgs, err := api.StateListMessages(ctx, &lapi.MessageMatch{To: toa, From: froma}, cur.Key(), end) if err != nil { return err } for _, c := range msgs { if cctx.Bool("cids") { fmt.Println(c.String()) continue } m, err := api.ChainGetMessage(ctx, c) if err != nil { return err } b, err := json.MarshalIndent(m, "", " ") if err != nil { return err } fmt.Println(string(b)) } if end <= 0 { break } next, err := api.ChainGetTipSetByHeight(ctx, end-1, cur.Key()) if err != nil { return err } cur = next } return nil }, }
var StateListMinersCmd = &cli.Command{ Name: "list-miners", Usage: "list all miners in the network", Flags: []cli.Flag{ &cli.StringFlag{ Name: "sort-by", Usage: "criteria to sort miners by (none, num-deals)", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } miners, err := api.StateListMiners(ctx, ts.Key()) if err != nil { return err } switch cctx.String("sort-by") { case "num-deals": ndm, err := getDealsCounts(ctx, api) if err != nil { return err } sort.Slice(miners, func(i, j int) bool { return ndm[miners[i]] > ndm[miners[j]] }) for i := 0; i < 50 && i < len(miners); i++ { fmt.Printf("%s %d\n", miners[i], ndm[miners[i]]) } return nil default: return fmt.Errorf("unrecognized sorting order") case "", "none": } for _, m := range miners { fmt.Println(m.String()) } return nil }, }
var StateLookupIDCmd = &cli.Command{ Name: "lookup", Usage: "Find corresponding ID address", ArgsUsage: "[address]", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "reverse", Aliases: []string{"r"}, Usage: "Perform reverse lookup", }, }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } addr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } var a address.Address if !cctx.Bool("reverse") { a, err = api.StateLookupID(ctx, addr, ts.Key()) } else { a, err = api.StateAccountKey(ctx, addr, ts.Key()) } if err != nil { return err } fmt.Printf("%s\n", a) return nil }, }
var StateMarketCmd = &cli.Command{
Name: "market",
Usage: "Inspect the storage market actor",
Subcommands: []*cli.Command{
stateMarketBalanceCmd,
},
}
var StateMinerProvingDeadlineCmd = &cli.Command{ Name: "miner-proving-deadline", Usage: "Retrieve information about a given miner's proving deadline", ArgsUsage: "[minerAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } addr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } cd, err := api.StateMinerProvingDeadline(ctx, addr, ts.Key()) if err != nil { return xerrors.Errorf("getting miner info: %w", err) } fmt.Printf("Period Start:\t%s\n", cd.PeriodStart) fmt.Printf("Index:\t\t%d\n", cd.Index) fmt.Printf("Open:\t\t%s\n", cd.Open) fmt.Printf("Close:\t\t%s\n", cd.Close) fmt.Printf("Challenge:\t%s\n", cd.Challenge) fmt.Printf("FaultCutoff:\t%s\n", cd.FaultCutoff) return nil }, }
var StateNtwkVersionCmd = &cli.Command{ Name: "network-version", Usage: "Returns the network version", Action: func(cctx *cli.Context) error { if cctx.Args().Present() { return ShowHelp(cctx, fmt.Errorf("doesn't expect any arguments")) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } nv, err := api.StateNetworkVersion(ctx, ts.Key()) if err != nil { return err } fmt.Printf("Network Version: %d\n", nv) return nil }, }
var StatePowerCmd = &cli.Command{ Name: "power", Usage: "Query network or miner power", ArgsUsage: "[<minerAddress> (optional)]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } var maddr address.Address if cctx.Args().Present() { maddr, err = address.NewFromString(cctx.Args().First()) if err != nil { return err } ma, err := api.StateGetActor(ctx, maddr, ts.Key()) if err != nil { return err } if !builtin.IsStorageMinerActor(ma.Code) { return xerrors.New("provided address does not correspond to a miner actor") } } power, err := api.StateMinerPower(ctx, maddr, ts.Key()) if err != nil { return err } tp := power.TotalPower if cctx.Args().Present() { mp := power.MinerPower fmt.Printf( "%s(%s) / %s(%s) ~= %0.4f%%\n", mp.QualityAdjPower.String(), types.SizeStr(mp.QualityAdjPower), tp.QualityAdjPower.String(), types.SizeStr(tp.QualityAdjPower), types.BigDivFloat( types.BigMul(mp.QualityAdjPower, big.NewInt(100)), tp.QualityAdjPower, ), ) } else { fmt.Printf("%s(%s)\n", tp.QualityAdjPower.String(), types.SizeStr(tp.QualityAdjPower)) } return nil }, }
var StateReadStateCmd = &cli.Command{ Name: "read-state", Usage: "View a json representation of an actors state", ArgsUsage: "[actorAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } addr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } as, err := api.StateReadState(ctx, addr, ts.Key()) if err != nil { return err } data, err := json.MarshalIndent(as.State, "", " ") if err != nil { return err } fmt.Println(string(data)) return nil }, }
var StateReplayCmd = &cli.Command{ Name: "replay", Usage: "Replay a particular message", ArgsUsage: "<messageCid>", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "show-trace", Usage: "print out full execution trace for given message", }, &cli.BoolFlag{ Name: "detailed-gas", Usage: "print out detailed gas costs for given message", }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } mcid, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("message cid was invalid: %s", err) } fapi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) res, err := fapi.StateReplay(ctx, types.EmptyTSK, mcid) if err != nil { return xerrors.Errorf("replay call failed: %w", err) } fmt.Println("Replay receipt:") fmt.Printf("Exit code: %d\n", res.MsgRct.ExitCode) fmt.Printf("Return: %x\n", res.MsgRct.Return) fmt.Printf("Gas Used: %d\n", res.MsgRct.GasUsed) if cctx.Bool("detailed-gas") { fmt.Printf("Base Fee Burn: %d\n", res.GasCost.BaseFeeBurn) fmt.Printf("Overestimaton Burn: %d\n", res.GasCost.OverEstimationBurn) fmt.Printf("Miner Penalty: %d\n", res.GasCost.MinerPenalty) fmt.Printf("Miner Tip: %d\n", res.GasCost.MinerTip) fmt.Printf("Refund: %d\n", res.GasCost.Refund) } fmt.Printf("Total Message Cost: %d\n", res.GasCost.TotalCost) if res.MsgRct.ExitCode != 0 { fmt.Printf("Error message: %q\n", res.Error) } if cctx.Bool("show-trace") { fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", res.Msg.From, res.Msg.To, res.Msg.Value, res.Msg.Method, res.Msg.Params, res.MsgRct.ExitCode, res.MsgRct.Return) printInternalExecutions("\t", res.ExecutionTrace.Subcalls) } return nil }, }
var StateSearchMsgCmd = &cli.Command{ Name: "search-msg", Aliases: []string{"search-message"}, Usage: "Search to see whether a message has appeared on chain", ArgsUsage: "[messageCid]", Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) msg, err := cid.Decode(cctx.Args().First()) if err != nil { return err } mw, err := api.StateSearchMsg(ctx, msg) if err != nil { return err } if mw == nil { return fmt.Errorf("failed to find message: %s", msg) } m, err := api.ChainGetMessage(ctx, msg) if err != nil { return err } return printMsg(ctx, api, msg, mw, m) }, }
var StateSectorCmd = &cli.Command{ Name: "sector", Aliases: []string{"sector-info"}, Usage: "Get miner sector info", ArgsUsage: "[minerAddress] [sectorNumber]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 2 { return IncorrectNumArgs(cctx) } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } maddr, err := address.NewFromString(cctx.Args().Get(0)) if err != nil { return err } sid, err := strconv.ParseInt(cctx.Args().Get(1), 10, 64) if err != nil { return err } si, err := api.StateSectorGetInfo(ctx, maddr, abi.SectorNumber(sid), ts.Key()) if err != nil { return err } if si == nil { return xerrors.Errorf("sector %d for miner %s not found", sid, maddr) } fmt.Println("SectorNumber: ", si.SectorNumber) fmt.Println("SealProof: ", si.SealProof) fmt.Println("SealedCID: ", si.SealedCID) if si.SectorKeyCID != nil { fmt.Println("SectorKeyCID: ", si.SectorKeyCID) } fmt.Println("DealIDs: ", si.DealIDs) fmt.Println() fmt.Println("Activation: ", cliutil.EpochTimeTs(ts.Height(), si.Activation, ts)) fmt.Println("Expiration: ", cliutil.EpochTimeTs(ts.Height(), si.Expiration, ts)) fmt.Println() fmt.Println("DealWeight: ", si.DealWeight) fmt.Println("VerifiedDealWeight: ", si.VerifiedDealWeight) fmt.Println("InitialPledge: ", types.FIL(si.InitialPledge)) fmt.Println("ExpectedDayReward: ", types.FIL(si.ExpectedDayReward)) fmt.Println("ExpectedStoragePledge: ", types.FIL(si.ExpectedStoragePledge)) fmt.Println() sp, err := api.StateSectorPartition(ctx, maddr, abi.SectorNumber(sid), ts.Key()) if err != nil { return err } fmt.Println("Deadline: ", sp.Deadline) fmt.Println("Partition: ", sp.Partition) return nil }, }
var StateSectorSizeCmd = &cli.Command{ Name: "sector-size", Usage: "Look up miners sector size", ArgsUsage: "[minerAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } addr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } mi, err := api.StateMinerInfo(ctx, addr, ts.Key()) if err != nil { return err } fmt.Printf("%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize) return nil }, }
var StateSectorsCmd = &cli.Command{ Name: "sectors", Usage: "Query the sector set of a miner", ArgsUsage: "[minerAddress]", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } maddr, err := address.NewFromString(cctx.Args().First()) if err != nil { return err } ts, err := LoadTipSet(ctx, cctx, api) if err != nil { return err } sectors, err := api.StateMinerSectors(ctx, maddr, nil, ts.Key()) if err != nil { return err } for _, s := range sectors { fmt.Printf("%d: %s\n", s.SectorNumber, s.SealedCID) } return nil }, }
var StateSysActorCIDsCmd = &cli.Command{ Name: "actor-cids", Usage: "Returns the built-in actor bundle manifest ID & system actor cids", Flags: []cli.Flag{ &cli.UintFlag{ Name: "network-version", Usage: "specify network version", }, }, Action: func(cctx *cli.Context) error { if cctx.Args().Present() { return ShowHelp(cctx, fmt.Errorf("doesn't expect any arguments")) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var nv network.Version if cctx.IsSet("network-version") { nv = network.Version(cctx.Uint64("network-version")) } else { nv, err = api.StateNetworkVersion(ctx, types.EmptyTSK) if err != nil { return err } } fmt.Printf("Network Version: %d\n", nv) actorVersion, err := actorstypes.VersionForNetwork(nv) if err != nil { return err } fmt.Printf("Actor Version: %d\n", actorVersion) manifestCid, ok := actors.GetManifest(actorVersion) if ok { fmt.Printf("Manifest CID: %v\n", manifestCid) } tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0) _, _ = fmt.Fprintln(tw, "\nActor\tCID\t") actorsCids, err := api.StateActorCodeCIDs(ctx, nv) if err != nil { return err } var actorsCidTuples []struct { actorName string actorCid cid.Cid } for name, actorCid := range actorsCids { keyVal := struct { actorName string actorCid cid.Cid }{ actorName: name, actorCid: actorCid, } actorsCidTuples = append(actorsCidTuples, keyVal) } sort.Slice(actorsCidTuples, func(i, j int) bool { return actorsCidTuples[i].actorName < actorsCidTuples[j].actorName }) for _, keyVal := range actorsCidTuples { _, _ = fmt.Fprintf(tw, "%v\t%v\n", keyVal.actorName, keyVal.actorCid) } return tw.Flush() }, }
var StateWaitMsgCmd = &cli.Command{ Name: "wait-msg", Aliases: []string{"wait-message"}, Usage: "Wait for a message to appear on chain", ArgsUsage: "[messageCid]", Flags: []cli.Flag{ &cli.StringFlag{ Name: "timeout", Value: "10m", }, }, Action: func(cctx *cli.Context) error { if cctx.NArg() != 1 { return IncorrectNumArgs(cctx) } api, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) msg, err := cid.Decode(cctx.Args().First()) if err != nil { return err } mw, err := api.StateWaitMsg(ctx, msg, build.MessageConfidence) if err != nil { return err } m, err := api.ChainGetMessage(ctx, msg) if err != nil { return err } return printMsg(ctx, api, msg, mw, m) }, }
var StatusCmd = &cli.Command{ Name: "status", Usage: "Check node status", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "chain", Usage: "include chain health status", }, }, Action: func(cctx *cli.Context) error { apic, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) inclChainStatus := cctx.Bool("chain") status, err := apic.NodeStatus(ctx, inclChainStatus) if err != nil { return err } fmt.Printf("Sync Epoch: %d\n", status.SyncStatus.Epoch) fmt.Printf("Epochs Behind: %d\n", status.SyncStatus.Behind) fmt.Printf("Peers to Publish Messages: %d\n", status.PeerStatus.PeersToPublishMsgs) fmt.Printf("Peers to Publish Blocks: %d\n", status.PeerStatus.PeersToPublishBlocks) if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) { var ok100, okFin string if status.ChainStatus.BlocksPerTipsetLast100 >= 4.75 { ok100 = "[OK]" } else { ok100 = "[UNHEALTHY]" } if status.ChainStatus.BlocksPerTipsetLastFinality >= 4.75 { okFin = "[OK]" } else { okFin = "[UNHEALTHY]" } fmt.Printf("Blocks per TipSet in last 100 epochs: %f %s\n", status.ChainStatus.BlocksPerTipsetLast100, ok100) fmt.Printf("Blocks per TipSet in last finality: %f %s\n", status.ChainStatus.BlocksPerTipsetLastFinality, okFin) } return nil }, }
var SyncCheckBadCmd = &cli.Command{ Name: "check-bad", Usage: "check if the given block was marked bad, and for what reason", ArgsUsage: "[blockCid]", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) napi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if !cctx.Args().Present() { return fmt.Errorf("must specify block cid to check") } bcid, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to decode input as a cid: %s", err) } reason, err := napi.SyncCheckBad(ctx, bcid) if err != nil { return err } if reason == "" { afmt.Println("block was not marked as bad") return nil } afmt.Println(reason) return nil }, }
var SyncCheckpointCmd = &cli.Command{ Name: "checkpoint", Usage: "mark a certain tipset as checkpointed; the node will never fork away from this tipset", ArgsUsage: "[tipsetKey]", Flags: []cli.Flag{ &cli.Uint64Flag{ Name: "epoch", Usage: "checkpoint the tipset at the given epoch", }, }, Action: func(cctx *cli.Context) error { napi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) var ts *types.TipSet if cctx.IsSet("epoch") { ts, err = napi.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Uint64("epoch")), types.EmptyTSK) } if ts == nil { ts, err = parseTipSet(ctx, napi, cctx.Args().Slice()) } if err != nil { return err } if ts == nil { return fmt.Errorf("must pass cids for tipset to set as head, or specify epoch flag") } if err := napi.SyncCheckpoint(ctx, ts.Key()); err != nil { return err } return nil }, }
var SyncCmd = &cli.Command{ Name: "sync", Usage: "Inspect or interact with the chain syncer", Subcommands: []*cli.Command{ SyncStatusCmd, SyncWaitCmd, SyncMarkBadCmd, SyncUnmarkBadCmd, SyncCheckBadCmd, SyncCheckpointCmd, }, }
var SyncMarkBadCmd = &cli.Command{ Name: "mark-bad", Usage: "Mark the given block as bad, will prevent syncing to a chain that contains it", ArgsUsage: "[blockCid]", Action: func(cctx *cli.Context) error { napi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if !cctx.Args().Present() { return fmt.Errorf("must specify block cid to mark") } bcid, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to decode input as a cid: %s", err) } return napi.SyncMarkBad(ctx, bcid) }, }
var SyncStatusCmd = &cli.Command{ Name: "status", Usage: "check sync status", Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) apic, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) state, err := apic.SyncState(ctx) if err != nil { return err } afmt.Println("sync status:") for _, ss := range state.ActiveSyncs { afmt.Printf("worker %d:\n", ss.WorkerID) var base, target []cid.Cid var heightDiff int64 var theight abi.ChainEpoch if ss.Base != nil { base = ss.Base.Cids() heightDiff = int64(ss.Base.Height()) } if ss.Target != nil { target = ss.Target.Cids() heightDiff = int64(ss.Target.Height()) - heightDiff theight = ss.Target.Height() } else { heightDiff = 0 } afmt.Printf("\tBase:\t%s\n", base) afmt.Printf("\tTarget:\t%s (%d)\n", target, theight) afmt.Printf("\tHeight diff:\t%d\n", heightDiff) afmt.Printf("\tStage: %s\n", ss.Stage) afmt.Printf("\tHeight: %d\n", ss.Height) if ss.End.IsZero() { if !ss.Start.IsZero() { afmt.Printf("\tElapsed: %s\n", time.Since(ss.Start)) } } else { afmt.Printf("\tElapsed: %s\n", ss.End.Sub(ss.Start)) } if ss.Stage == api.StageSyncErrored { afmt.Printf("\tError: %s\n", ss.Message) } } return nil }, }
var SyncUnmarkBadCmd = &cli.Command{ Name: "unmark-bad", Usage: "Unmark the given block as bad, makes it possible to sync to a chain containing it", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "all", Usage: "drop the entire bad block cache", }, }, ArgsUsage: "[blockCid]", Action: func(cctx *cli.Context) error { napi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) if cctx.Bool("all") { return napi.SyncUnmarkAllBad(ctx) } if !cctx.Args().Present() { return fmt.Errorf("must specify block cid to unmark") } bcid, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to decode input as a cid: %s", err) } return napi.SyncUnmarkBad(ctx, bcid) }, }
var SyncWaitCmd = &cli.Command{ Name: "wait", Usage: "Wait for sync to be complete", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "watch", Usage: "don't exit after node is synced", }, }, Action: func(cctx *cli.Context) error { napi, closer, err := GetFullNodeAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) return SyncWait(ctx, napi, cctx.Bool("watch")) }, }
var VersionCmd = &cli.Command{ Name: "version", Usage: "Print version", Action: func(cctx *cli.Context) error { api, closer, err := GetAPI(cctx) if err != nil { return err } defer closer() ctx := ReqContext(cctx) v, err := api.Version(ctx) if err != nil { return err } fmt.Println("Daemon: ", v) fmt.Print("Local: ") cli.VersionPrinter(cctx) return nil }, }
var WaitApiCmd = &cli.Command{ Name: "wait-api", Usage: "Wait for lotus api to come online", Flags: []cli.Flag{ &cli.DurationFlag{ Name: "timeout", Usage: "duration to wait till fail", Value: time.Second * 30, }, }, Action: func(cctx *cli.Context) error { ctx := ReqContext(cctx) ctx, cancel := context.WithTimeout(ctx, cctx.Duration("timeout")) defer cancel() for { if ctx.Err() != nil { break } api, closer, err := GetAPI(cctx) if err != nil { fmt.Printf("Not online yet... (%s)\n", err) time.Sleep(time.Second) continue } defer closer() _, err = api.Version(ctx) if err != nil { return err } return nil } if ctx.Err() == context.DeadlineExceeded { return fmt.Errorf("timed out waiting for api to come online") } return ctx.Err() }, }
var WalletCmd = &cli.Command{
Name: "wallet",
Usage: "Manage wallet",
Subcommands: []*cli.Command{
walletNew,
walletList,
walletBalance,
walletExport,
walletImport,
walletGetDefault,
walletSetDefault,
walletSign,
walletVerify,
walletDelete,
walletMarket,
},
}
Functions ¶
func AddrInfoFromArg ¶ added in v1.17.0
func BackupCmd ¶ added in v0.9.0
func BackupCmd(repoFlag string, rt repo.RepoType, getApi BackupApiFn) *cli.Command
func ComputeStateHTMLTempl ¶ added in v0.5.0
func CreateExtendClaimMsg ¶ added in v1.27.0
func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifregtypes13.ClaimId]ProvInfo, miners []string, wallet address.Address, tmax abi.ChainEpoch, all, assumeYes bool, batchSize int) ([]*types.Message, error)
CreateExtendClaimMsg creates extend message[s] based on the following conditions 1. Extend all claims for a miner ID 2. Extend all claims for multiple miner IDs 3. Extend specified claims for a miner ID 4. Extend specific claims for specific miner ID 5. Extend all claims for a miner ID with different client address (2 messages) 6. Extend all claims for multiple miner IDs with different client address (2 messages) 7. Extend specified claims for a miner ID with different client address (2 messages) 8. Extend specific claims for specific miner ID with different client address (2 messages)
func EncodedString ¶ added in v0.3.0
func EncodedString(sv *paych.SignedVoucher) (string, error)
func IncorrectNumArgs ¶ added in v1.17.2
func InteractiveSend ¶ added in v1.11.0
func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI, proto *api.MessagePrototype) (*types.SignedMessage, error)
func IsSyncDone ¶ added in v1.25.0
func JsonParams ¶ added in v1.1.1
func JsonReturn ¶ added in v1.20.0
func LoadTipSet ¶ added in v0.3.0
LoadTipSet gets the tipset from the context, or the head from the API.
It always gets the head from the API so commands use a consistent tipset even if time pases.
func NewCliError ¶ added in v0.2.10
func ParseTipSetRef ¶ added in v0.5.0
func ParseTipSetRefOffline ¶ added in v1.19.0
func ParseTipSetString ¶ added in v0.7.1
func SyncBasefeeCheck ¶ added in v1.17.2
func WithCategory ¶ added in v0.5.0
func WithCategory(cat string, cmd *cli.Command) *cli.Command
Types ¶
type BackupApiFn ¶ added in v0.9.0
type CheckInfo ¶ added in v1.11.0
type CheckInfo struct { MessageTie cid.Cid CurrentMessageTie bool Check api.MessageCheckStatus }
type ErrCmdFailed ¶ added in v0.2.10
type ErrCmdFailed struct {
// contains filtered or unexported fields
}
func (*ErrCmdFailed) Error ¶ added in v0.2.10
func (e *ErrCmdFailed) Error() string
type PrintHelpErr ¶ added in v0.5.0
func (*PrintHelpErr) Error ¶ added in v0.5.0
func (e *PrintHelpErr) Error() string
func (*PrintHelpErr) Is ¶ added in v0.5.0
func (e *PrintHelpErr) Is(o error) bool
func (*PrintHelpErr) Unwrap ¶ added in v0.5.0
func (e *PrintHelpErr) Unwrap() error
type SendParams ¶ added in v1.5.1
type SendParams struct { To address.Address From address.Address Val abi.TokenAmount GasPremium *abi.TokenAmount GasFeeCap *abi.TokenAmount GasLimit *int64 Nonce *uint64 Method abi.MethodNum Params []byte }
type ServicesAPI ¶ added in v1.5.1
type ServicesAPI interface { FullNodeAPI() api.FullNode GetBaseFee(ctx context.Context) (abi.TokenAmount, error) // MessageForSend creates a prototype of a message based on SendParams MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error) // DecodeTypedParamsFromJSON takes in information needed to identify a method and converts JSON // parameters to bytes of their CBOR encoding DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error) // PublishMessage takes in a message prototype and publishes it // before publishing the message, it runs checks on the node, message and mpool to verify that // message is valid and won't be stuck. // if `force` is true, it skips the checks PublishMessage(ctx context.Context, prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) LocalAddresses(ctx context.Context) (address.Address, []address.Address, error) MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool, tsk types.TipSetKey) ([]*types.SignedMessage, error) MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error) // Close ends the session of services and disconnects from RPC, using Services after Close is called // most likely will result in an error // Should not be called concurrently Close() error }
func GetFullNodeServices ¶ added in v1.5.1
func GetFullNodeServices(ctx *cli.Context) (ServicesAPI, error)
type ServicesImpl ¶ added in v1.5.1
type ServicesImpl struct {
// contains filtered or unexported fields
}
func (*ServicesImpl) Close ¶ added in v1.5.1
func (s *ServicesImpl) Close() error
func (*ServicesImpl) DecodeTypedParamsFromJSON ¶ added in v1.5.1
func (*ServicesImpl) FullNodeAPI ¶ added in v1.11.0
func (s *ServicesImpl) FullNodeAPI() api.FullNode
func (*ServicesImpl) GetBaseFee ¶ added in v1.11.0
func (s *ServicesImpl) GetBaseFee(ctx context.Context) (abi.TokenAmount, error)
func (*ServicesImpl) LocalAddresses ¶ added in v1.11.0
func (s *ServicesImpl) LocalAddresses(ctx context.Context) (address.Address, []address.Address, error)
func (*ServicesImpl) MessageForSend ¶ added in v1.11.0
func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error)
func (*ServicesImpl) MpoolCheckPendingMessages ¶ added in v1.11.0
func (s *ServicesImpl) MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error)
func (*ServicesImpl) MpoolPendingFilter ¶ added in v1.11.0
func (s *ServicesImpl) MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool, tsk types.TipSetKey) ([]*types.SignedMessage, error)
func (*ServicesImpl) PublishMessage ¶ added in v1.11.0
func (s *ServicesImpl) PublishMessage(ctx context.Context, prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error)
PublishMessage modifies prototype to include gas estimation Errors with ErrCheckFailed if any of the checks fail First group of checks is related to the message prototype
func (*ServicesImpl) RunChecksForPrototype ¶ added in v1.11.0
func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error)
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package clicommands contains only the cli.Command definitions that are common to sptool and miner.
|
Package clicommands contains only the cli.Command definitions that are common to sptool and miner. |