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 < policy.ChainFinality { return fmt.Errorf("\"recent-stateroots\" has to be greater than %d", policy.ChainFinality) } 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(buildconstants.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, buildconstants.BlockGasLimit, 100*float64(limitSum)/float64(buildconstants.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(buildconstants.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 invocation 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 ( F3Cmd = &cli.Command{ Name: "f3", Usage: "Manages Filecoin Fast Finality (F3) interactions", Subcommands: []*cli.Command{ { Name: "list-miners", Aliases: []string{"lm"}, Usage: "Lists the miners that currently participate in F3 via this node.", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() miners, err := api.F3ListParticipants(cctx.Context) if err != nil { return fmt.Errorf("listing participants: %w", err) } if len(miners) == 0 { _, err = fmt.Fprintln(cctx.App.Writer, "No miners.") return err } const ( miner = "Miner" from = "From" to = "To" ) tw := tablewriter.New( tablewriter.Col(miner), tablewriter.Col(from), tablewriter.Col(to), ) for _, participant := range miners { addr, err := address.NewIDAddress(participant.MinerID) if err != nil { return fmt.Errorf("converting miner ID to address: %w", err) } tw.Write(map[string]interface{}{ miner: addr, from: participant.FromInstance, to: participant.FromInstance + participant.ValidityTerm, }) } return tw.Flush(cctx.App.Writer) }, }, { Name: "certs", Aliases: []string{"c"}, Usage: "Manages interactions with F3 finality certificates.", Subcommands: []*cli.Command{ { Name: "get", Usage: "Gets an F3 finality certificate to a given instance ID, " + "or the latest certificate if no instance is specified.", ArgsUsage: "[instance]", Flags: []cli.Flag{ f3FlagOutput, }, Before: func(cctx *cli.Context) error { if count := cctx.NArg(); count > 1 { return fmt.Errorf("too many arguments: expected at most 1 but got %d", count) } return nil }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() // Get the certificate, either for the given instance or the latest if no // instance is specified. var cert *certs.FinalityCertificate if cctx.Args().Present() { var instance uint64 instance, err = strconv.ParseUint(cctx.Args().First(), 10, 64) if err != nil { return fmt.Errorf("parsing instance: %w", err) } cert, err = api.F3GetCertificate(cctx.Context, instance) } else { cert, err = api.F3GetLatestCertificate(cctx.Context) } if err != nil { return fmt.Errorf("getting finality certificate: %w", err) } if cert == nil { _, _ = fmt.Fprintln(cctx.App.ErrWriter, "No certificate.") return nil } return outputFinalityCertificate(cctx, cert) }, }, { Name: "list", Usage: `Lists a range of F3 finality certificates. By default the certificates are listed in newest to oldest order, i.e. descending instance IDs. The order may be reversed using the '--reverse' flag. A range may optionally be specified as the first argument to indicate inclusive range of 'from' and 'to' instances in following notation: '<from>..<to>'. Either <from> or <to> may be omitted, but not both. An omitted <from> value is always interpreted as 0, and an omitted <to> value indicates the latest instance. If both are specified, <from> must never exceed <to>. If no range is specified, the latest 10 certificates are listed, i.e. the range of '0..' with limit of 10. Otherwise, all certificates in the specified range are listed unless limit is explicitly specified. Examples: * All certificates from newest to oldest: $ lotus f3 certs list 0.. * Three newest certificates: $ lotus f3 certs list --limit 3 0.. * Three oldest certificates: $ lotus f3 certs list --limit 3 --reverse 0.. * Up to three certificates starting from instance 1413 to the oldest: $ lotus f3 certs list --limit 3 ..1413 * Up to 3 certificates starting from instance 1413 to the newest: $ lotus f3 certs list --limit 3 --reverse 1413.. * All certificates from instance 3 to 1413 in order of newest to oldest: $ lotus f3 certs list 3..1413 `, ArgsUsage: "[range]", Flags: []cli.Flag{ f3FlagOutput, f3FlagInstanceLimit, f3FlagReverseOrder, }, Before: func(cctx *cli.Context) error { if count := cctx.NArg(); count > 1 { return fmt.Errorf("too many arguments: expected at most 1 but got %d", count) } return nil }, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() limit := cctx.Int(f3FlagInstanceLimit.Name) reverse := cctx.Bool(f3FlagReverseOrder.Name) fromTo := cctx.Args().First() if fromTo == "" { fromTo = "0.." if !cctx.IsSet(f3FlagInstanceLimit.Name) { limit = 10 } } r, err := newRanger(fromTo, limit, reverse, func() (uint64, error) { latest, err := api.F3GetLatestCertificate(cctx.Context) if err != nil { return 0, fmt.Errorf("getting latest finality certificate: %w", err) } if latest == nil { return 0, errors.New("no latest finality certificate") } return latest.GPBFTInstance, nil }) if err != nil { return err } var cert *certs.FinalityCertificate for cctx.Context.Err() == nil { next, proceed := r.next() if !proceed { return nil } cert, err = api.F3GetCertificate(cctx.Context, next) if err != nil { return fmt.Errorf("getting finality certificate for instance %d: %w", next, err) } if cert == nil { return fmt.Errorf("nil finality certificate for instance %d", next) } if err := outputFinalityCertificate(cctx, cert); err != nil { return err } _, _ = fmt.Fprintln(cctx.App.Writer) } return nil }, }, }, }, { Name: "manifest", Usage: "Gets the current manifest used by F3.", Flags: []cli.Flag{f3FlagOutput}, Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() manifest, err := api.F3GetManifest(cctx.Context) if err != nil { return fmt.Errorf("getting manifest: %w", err) } switch output := cctx.String(f3FlagOutput.Name); strings.ToLower(output) { case "text": return prettyPrintManifest(cctx.App.Writer, manifest) case "json": encoder := json.NewEncoder(cctx.App.Writer) encoder.SetIndent("", " ") return encoder.Encode(manifest) default: return fmt.Errorf("unknown output format: %s", output) } }, }, { Name: "status", Usage: "Checks the F3 status.", Action: func(cctx *cli.Context) error { api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } defer closer() running, err := api.F3IsRunning(cctx.Context) if err != nil { return fmt.Errorf("getting running state: %w", err) } _, _ = fmt.Fprintf(cctx.App.Writer, "Running: %t\n", running) if !running { return nil } progress, err := api.F3GetProgress(cctx.Context) if err != nil { return fmt.Errorf("getting progress: %w", err) } _, _ = fmt.Fprintln(cctx.App.Writer, "Progress:") _, _ = fmt.Fprintf(cctx.App.Writer, " Instance: %d\n", progress.ID) _, _ = fmt.Fprintf(cctx.App.Writer, " Round: %d\n", progress.Round) _, _ = fmt.Fprintf(cctx.App.Writer, " Phase: %s\n", progress.Phase) manifest, err := api.F3GetManifest(cctx.Context) if err != nil { return fmt.Errorf("getting manifest: %w", err) } return prettyPrintManifest(cctx.App.Writer, manifest) }, }, }, } )
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(buildconstants.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(buildconstants.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 is0xRecipient := false params.To, err = address.NewFromString(cctx.Args().Get(0)) if err != nil { ea, err := ethtypes.ParseEthAddress(cctx.Args().Get(0)) if err != nil { return ShowHelp(cctx, fmt.Errorf("failed to parse target address; address must be a valid FIL address or an ETH address: %w", err)) } is0xRecipient = true params.To, err = ea.ToFilecoinAddress() if err != nil { return ShowHelp(cctx, fmt.Errorf("failed to convert ETH address to FIL address: %w", err)) } if !(params.To.Protocol() == address.ID || params.To.Protocol() == address.Delegated) { return ShowHelp(cctx, fmt.Errorf("ETH addresses can only map to a FIL addresses starting with f410f or f0")) } } 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 } else { defaddr, err := srv.FullNodeAPI().WalletDefaultAddress(ctx) if err != nil { return fmt.Errorf("failed to get default address: %w", err) } params.From = defaddr } 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) || is0xRecipient { 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")) } _, _ = fmt.Fprintf(cctx.App.Writer, "Sending message from: %s\nSending message to: %s\nUsing Method: %d\n", params.From.String(), params.To.String(), params.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, buildconstants.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(policy.ChainFinality) { 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. |