cli

package
v0.0.0-...-484cccf Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 16, 2023 License: Apache-2.0, MIT Imports: 122 Imported by: 0

Documentation

Index

Constants

View Source
const Confidence = 10
View Source
const DefaultMaxRetrievePrice = "0"

Variables

View Source
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
	},
}
View Source
var AuthCmd = &cli.Command{
	Name:  "auth",
	Usage: "Manage RPC permissions",
	Subcommands: []*cli.Command{
		AuthCreateAdminToken,
		AuthApiInfoToken,
	},
}
View Source
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
	},
}
View Source
var BarCols = float64(64)
View Source
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)
		}
	},
}
View Source
var ChainDecodeCmd = &cli.Command{
	Name:  "decode",
	Usage: "decode various types",
	Subcommands: []*cli.Command{
		chainDecodeParamsCmd,
	},
}
View Source
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
	},
}
View Source
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,
	},
}
View Source
var ChainEncodeCmd = &cli.Command{
	Name:  "encode",
	Usage: "encode various types",
	Subcommands: []*cli.Command{
		chainEncodeParamsCmd,
	},
}
View Source
var ChainExportCmd = &cli.Command{
	Name:      "export",
	Usage:     "export chain to a car file",
	ArgsUsage: "[outputPath]",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:  "tipset",
			Usage: "specify tipset to start the export from",
			Value: "@head",
		},
		&cli.Int64Flag{
			Name:  "recent-stateroots",
			Usage: "specify the number of recent state roots to include in the export",
		},
		&cli.BoolFlag{
			Name: "skip-old-msgs",
		},
	},
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		if cctx.NArg() != 1 {
			return IncorrectNumArgs(cctx)
		}

		rsrs := abi.ChainEpoch(cctx.Int64("recent-stateroots"))
		if cctx.IsSet("recent-stateroots") && rsrs < build.Finality {
			return fmt.Errorf("\"recent-stateroots\" has to be greater than %d", build.Finality)
		}

		fi, err := createExportFile(cctx.App, cctx.Args().First())
		if err != nil {
			return err
		}
		defer func() {
			err := fi.Close()
			if err != nil {
				fmt.Printf("error closing output file: %+v", err)
			}
		}()

		ts, err := LoadTipSet(ctx, cctx, api)
		if err != nil {
			return err
		}

		skipold := cctx.Bool("skip-old-msgs")

		if rsrs == 0 && skipold {
			return fmt.Errorf("must pass recent stateroots along with skip-old-msgs")
		}

		stream, err := api.ChainExport(ctx, rsrs, skipold, ts.Key())
		if err != nil {
			return err
		}

		var last bool
		for b := range stream {
			last = len(b) == 0

			_, err := fi.Write(b)
			if err != nil {
				return err
			}
		}

		if !last {
			return xerrors.Errorf("incomplete export (remote connection lost?)")
		}

		return nil
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
var ChainListCmd = &cli.Command{
	Name:    "list",
	Aliases: []string{"love"},
	Usage:   "View a segment of the chain",
	Flags: []cli.Flag{
		&cli.Uint64Flag{Name: "height", DefaultText: "current head"},
		&cli.IntFlag{Name: "count", Value: 30},
		&cli.StringFlag{
			Name:  "format",
			Usage: "specify the format to print out tipsets",
			Value: "<height>: (<time>) <blocks>",
		},
		&cli.BoolFlag{
			Name:  "gas-stats",
			Usage: "view gas statistics for the chain",
		},
	},
	Action: func(cctx *cli.Context) error {
		afmt := NewAppFmt(cctx.App)
		api, closer, err := GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		var head *types.TipSet

		if cctx.IsSet("height") {
			head, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Uint64("height")), types.EmptyTSK)
		} else {
			head, err = api.ChainHead(ctx)
		}
		if err != nil {
			return err
		}

		count := cctx.Int("count")
		if count < 1 {
			return nil
		}

		tss := make([]*types.TipSet, 0, count)
		tss = append(tss, head)

		for i := 1; i < count; i++ {
			if head.Height() == 0 {
				break
			}

			head, err = api.ChainGetTipSet(ctx, head.Parents())
			if err != nil {
				return err
			}

			tss = append(tss, head)
		}

		if cctx.Bool("gas-stats") {
			otss := make([]*types.TipSet, 0, len(tss))
			for i := len(tss) - 1; i >= 0; i-- {
				otss = append(otss, tss[i])
			}
			tss = otss
			for i, ts := range tss {
				pbf := ts.Blocks()[0].ParentBaseFee
				afmt.Printf("%d: %d blocks (baseFee: %s -> maxFee: %s)\n", ts.Height(), len(ts.Blocks()), ts.Blocks()[0].ParentBaseFee, types.FIL(types.BigMul(pbf, types.NewInt(uint64(build.BlockGasLimit)))))

				for _, b := range ts.Blocks() {
					msgs, err := api.ChainGetBlockMessages(ctx, b.Cid())
					if err != nil {
						return err
					}
					var limitSum int64
					psum := big.NewInt(0)
					for _, m := range msgs.BlsMessages {
						limitSum += m.GasLimit
						psum = big.Add(psum, m.GasPremium)
					}

					for _, m := range msgs.SecpkMessages {
						limitSum += m.Message.GasLimit
						psum = big.Add(psum, m.Message.GasPremium)
					}

					lenmsgs := len(msgs.BlsMessages) + len(msgs.SecpkMessages)

					avgpremium := big.Zero()
					if lenmsgs > 0 {
						avgpremium = big.Div(psum, big.NewInt(int64(lenmsgs)))
					}

					afmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPremium: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, build.BlockGasLimit, 100*float64(limitSum)/float64(build.BlockGasLimit), avgpremium)
				}
				if i < len(tss)-1 {
					msgs, err := api.ChainGetParentMessages(ctx, tss[i+1].Blocks()[0].Cid())
					if err != nil {
						return err
					}
					var limitSum int64
					for _, m := range msgs {
						limitSum += m.Message.GasLimit
					}

					recpts, err := api.ChainGetParentReceipts(ctx, tss[i+1].Blocks()[0].Cid())
					if err != nil {
						return err
					}

					var gasUsed int64
					for _, r := range recpts {
						gasUsed += r.GasUsed
					}

					gasEfficiency := 100 * float64(gasUsed) / float64(limitSum)
					gasCapacity := 100 * float64(limitSum) / float64(build.BlockGasLimit)

					afmt.Printf("\ttipset: \t%d msgs, %d (%0.2f%%) / %d (%0.2f%%)\n", len(msgs), gasUsed, gasEfficiency, limitSum, gasCapacity)
				}
				afmt.Println()
			}
		} else {
			for i := len(tss) - 1; i >= 0; i-- {
				printTipSet(cctx.String("format"), tss[i], afmt)
			}
		}
		return nil
	},
}
View Source
var ChainPruneCmd = &cli.Command{
	Name:  "prune",
	Usage: "splitstore gc",
	Subcommands: []*cli.Command{
		chainPruneColdCmd,
		chainPruneHotGCCmd,
		chainPruneHotMovingGCCmd,
	},
}
View Source
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
	},
}
View Source
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.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
	},
}
View Source
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
	},
}
View Source
var CidBaseFlag = cli.StringFlag{
	Name:        "cid-base",
	Hidden:      true,
	Value:       "base32",
	Usage:       "Multibase encoding used for version 1 CIDs in output.",
	DefaultText: "base32",
}
View Source
var Commands = []*cli.Command{
	WithCategory("basic", sendCmd),
	WithCategory("basic", walletCmd),
	WithCategory("basic", infoCmd),
	WithCategory("basic", clientCmd),
	WithCategory("basic", multisigCmd),
	WithCategory("basic", filplusCmd),
	WithCategory("basic", paychCmd),
	WithCategory("developer", AuthCmd),
	WithCategory("developer", MpoolCmd),
	WithCategory("developer", StateCmd),
	WithCategory("developer", ChainCmd),
	WithCategory("developer", LogCmd),
	WithCategory("developer", WaitApiCmd),
	WithCategory("developer", FetchParamCmd),
	WithCategory("developer", EvmCmd),
	WithCategory("network", NetCmd),
	WithCategory("network", SyncCmd),
	WithCategory("status", StatusCmd),
	PprofCmd,
	VersionCmd,
}
View Source
var CommonCommands = []*cli.Command{
	AuthCmd,
	LogCmd,
	WaitApiCmd,
	FetchParamCmd,
	PprofCmd,
	VersionCmd,
}
View Source
var DaemonContext = cliutil.DaemonContext
View Source
var ErrAbortedByUser = errors.New("aborted by user")
View Source
var ErrCheckFailed = fmt.Errorf("check has failed")
View Source
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,
		}, "")
		if err != nil {
			fmt.Println("Eth call fails, return val: ", res)
			return err
		}

		fmt.Println("Result: ", res)

		return nil

	},
}
View Source
var EvmCmd = &cli.Command{
	Name:  "evm",
	Usage: "Commands related to the Filecoin EVM runtime",
	Subcommands: []*cli.Command{
		EvmDeployCmd,
		EvmInvokeCmd,
		EvmGetInfoCmd,
		EvmCallSimulateCmd,
		EvmGetContractAddress,
		EvmGetBytecode,
	},
}
View Source
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
	},
}
View Source
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, "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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
var EvmInvokeCmd = &cli.Command{
	Name:      "invoke",
	Usage:     "Invoke an EVM smart contract using the specified CALLDATA",
	ArgsUsage: "address calldata",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:  "from",
			Usage: "optionally specify the account to use for sending the exec message",
		}, &cli.IntFlag{
			Name:  "value",
			Usage: "optionally specify the value to be sent with the invokation message",
		},
	},
	Action: func(cctx *cli.Context) error {
		afmt := NewAppFmt(cctx.App)

		api, closer, err := GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		if argc := cctx.Args().Len(); argc != 2 {
			return xerrors.Errorf("must pass the address and calldata")
		}

		addr, err := address.NewFromString(cctx.Args().Get(0))
		if err != nil {
			return xerrors.Errorf("failed to decode address: %w", err)
		}

		var calldata []byte
		calldata, err = ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(1))
		if err != nil {
			return xerrors.Errorf("decoding hex input data: %w", err)
		}

		var buffer bytes.Buffer
		if err := cbg.WriteByteArray(&buffer, calldata); err != nil {
			return xerrors.Errorf("failed to encode evm params as cbor: %w", err)
		}
		calldata = buffer.Bytes()

		var fromAddr address.Address
		if from := cctx.String("from"); from == "" {
			defaddr, err := api.WalletDefaultAddress(ctx)
			if err != nil {
				return err
			}

			fromAddr = defaddr
		} else {
			addr, err := address.NewFromString(from)
			if err != nil {
				return err
			}

			fromAddr = addr
		}

		val := abi.NewTokenAmount(cctx.Int64("value"))
		msg := &types.Message{
			To:     addr,
			From:   fromAddr,
			Value:  val,
			Method: builtintypes.MethodsEVM.InvokeContract,
			Params: calldata,
		}

		afmt.Println("sending message...")
		smsg, err := api.MpoolPushMessage(ctx, msg, nil)
		if err != nil {
			return xerrors.Errorf("failed to push message: %w", err)
		}

		afmt.Println("waiting for message to execute...")
		wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0)
		if err != nil {
			return xerrors.Errorf("error waiting for message: %w", err)
		}

		if wait.Receipt.ExitCode != 0 {
			return xerrors.Errorf("actor execution failed")
		}

		afmt.Println("Gas used: ", wait.Receipt.GasUsed)
		result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
		if err != nil {
			return xerrors.Errorf("evm result not correctly encoded: %w", err)
		}

		if len(result) > 0 {
			afmt.Println(hex.EncodeToString(result))
		} else {
			afmt.Println("OK")
		}

		if eventsRoot := wait.Receipt.EventsRoot; eventsRoot != nil {
			afmt.Println("Events emitted:")

			s := &apiIpldStore{ctx, api}
			amt, err := amt4.LoadAMT(ctx, s, *eventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth))
			if err != nil {
				return err
			}

			var evt types.Event
			err = amt.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error {
				fmt.Printf("%x\n", deferred.Raw)
				if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil {
					return err
				}
				if err != nil {
					return err
				}
				fmt.Printf("\tEmitter ID: %s\n", evt.Emitter)
				for _, e := range evt.Entries {
					value, err := cbg.ReadByteArray(bytes.NewBuffer(e.Value), uint64(len(e.Value)))
					if err != nil {
						return err
					}
					fmt.Printf("\t\tKey: %s, Value: 0x%x, Flags: b%b\n", e.Key, value, e.Flags)
				}
				return nil

			})
		}
		if err != nil {
			return err
		}

		return nil
	},
}
View Source
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
	},
}
View Source
var GetAPIInfo = cliutil.GetAPIInfo
View Source
var GetFullNodeAPI = cliutil.GetFullNodeAPI
View Source
var GetFullNodeAPIV1 = cliutil.GetFullNodeAPIV1
View Source
var GetGatewayAPI = cliutil.GetGatewayAPI
View Source
var GetMarketsAPI = cliutil.GetMarketsAPI
View Source
var GetRawAPI = cliutil.GetRawAPI
View Source
var GetStorageMinerAPI = cliutil.GetStorageMinerAPI
View Source
var GetWorkerAPI = cliutil.GetWorkerAPI
View Source
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
	},
}
View Source
var LogCmd = &cli.Command{
	Name:  "log",
	Usage: "Manage logging",
	Subcommands: []*cli.Command{
		LogList,
		LogSetLevel,
		LogAlerts,
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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`

View Source
var MpoolCmd = &cli.Command{
	Name:  "mpool",
	Usage: "Manage message pool",
	Subcommands: []*cli.Command{
		MpoolPending,
		MpoolClear,
		MpoolSub,
		MpoolStat,
		MpoolReplaceCmd,
		MpoolFindCmd,
		MpoolConfig,
		MpoolGasPerfCmd,
		mpoolManage,
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
var MpoolGasPerfCmd = &cli.Command{
	Name:  "gas-perf",
	Usage: "Check gas performance of messages in mempool",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:  "all",
			Usage: "print gas performance for all mempool messages (default only prints for local)",
		},
	},
	Action: func(cctx *cli.Context) error {
		afmt := NewAppFmt(cctx.App)

		api, closer, err := GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()

		ctx := ReqContext(cctx)

		msgs, err := api.MpoolPending(ctx, types.EmptyTSK)
		if err != nil {
			return err
		}

		var filter map[address.Address]struct{}
		if !cctx.Bool("all") {
			filter = map[address.Address]struct{}{}

			addrss, err := api.WalletList(ctx)
			if err != nil {
				return xerrors.Errorf("getting local addresses: %w", err)
			}

			for _, a := range addrss {
				filter[a] = struct{}{}
			}

			var filtered []*types.SignedMessage
			for _, msg := range msgs {
				if _, has := filter[msg.Message.From]; !has {
					continue
				}
				filtered = append(filtered, msg)
			}
			msgs = filtered
		}

		ts, err := api.ChainHead(ctx)
		if err != nil {
			return xerrors.Errorf("failed to get chain head: %w", err)
		}

		baseFee := ts.Blocks()[0].ParentBaseFee

		bigBlockGasLimit := big.NewInt(build.BlockGasLimit)

		getGasReward := func(msg *types.SignedMessage) big.Int {
			maxPremium := types.BigSub(msg.Message.GasFeeCap, baseFee)
			if types.BigCmp(maxPremium, msg.Message.GasPremium) < 0 {
				maxPremium = msg.Message.GasPremium
			}
			return types.BigMul(maxPremium, types.NewInt(uint64(msg.Message.GasLimit)))
		}

		getGasPerf := func(gasReward big.Int, gasLimit int64) float64 {

			a := new(stdbig.Rat).SetInt(new(stdbig.Int).Mul(gasReward.Int, bigBlockGasLimit.Int))
			b := stdbig.NewRat(1, gasLimit)
			c := new(stdbig.Rat).Mul(a, b)
			r, _ := c.Float64()
			return r
		}

		for _, m := range msgs {
			gasReward := getGasReward(m)
			gasPerf := getGasPerf(gasReward, m.Message.GasLimit)

			afmt.Printf("%s\t%d\t%s\t%f\n", m.Message.From, m.Message.Nonce, gasReward, gasPerf)
		}

		return nil
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
			}
		}
	},
}
View Source
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 := GetAPI(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()

	},
}
View Source
var NetBlockAddCmd = &cli.Command{
	Name:  "add",
	Usage: "Add connection gating rules",
	Subcommands: []*cli.Command{
		NetBlockAddPeer,
		NetBlockAddIP,
		NetBlockAddSubnet,
	},
}
View Source
var NetBlockAddIP = &cli.Command{
	Name:      "ip",
	Usage:     "Block an IP address",
	ArgsUsage: "<IP> ...",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		return api.NetBlockAdd(ctx, atypes.NetBlockList{IPAddrs: cctx.Args().Slice()})
	},
}
View Source
var NetBlockAddPeer = &cli.Command{
	Name:      "peer",
	Usage:     "Block a peer",
	ArgsUsage: "<Peer> ...",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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})
	},
}
View Source
var NetBlockAddSubnet = &cli.Command{
	Name:      "subnet",
	Usage:     "Block an IP subnet",
	ArgsUsage: "<CIDR> ...",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		return api.NetBlockAdd(ctx, atypes.NetBlockList{IPSubnets: cctx.Args().Slice()})
	},
}
View Source
var NetBlockCmd = &cli.Command{
	Name:  "block",
	Usage: "Manage network connection gating rules",
	Subcommands: []*cli.Command{
		NetBlockAddCmd,
		NetBlockRemoveCmd,
		NetBlockListCmd,
	},
}
View Source
var NetBlockListCmd = &cli.Command{
	Name:  "list",
	Usage: "list connection gating rules",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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
	},
}
View Source
var NetBlockRemoveCmd = &cli.Command{
	Name:  "remove",
	Usage: "Remove connection gating rules",
	Subcommands: []*cli.Command{
		NetBlockRemovePeer,
		NetBlockRemoveIP,
		NetBlockRemoveSubnet,
	},
}
View Source
var NetBlockRemoveIP = &cli.Command{
	Name:      "ip",
	Usage:     "Unblock an IP address",
	ArgsUsage: "<IP> ...",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		return api.NetBlockRemove(ctx, atypes.NetBlockList{IPAddrs: cctx.Args().Slice()})
	},
}
View Source
var NetBlockRemovePeer = &cli.Command{
	Name:      "peer",
	Usage:     "Unblock a peer",
	ArgsUsage: "<Peer> ...",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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})
	},
}
View Source
var NetBlockRemoveSubnet = &cli.Command{
	Name:      "subnet",
	Usage:     "Unblock an IP subnet",
	ArgsUsage: "<CIDR> ...",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		return api.NetBlockRemove(ctx, atypes.NetBlockList{IPSubnets: cctx.Args().Slice()})
	},
}
View Source
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,
	},
}
View Source
var NetConnect = &cli.Command{
	Name:      "connect",
	Usage:     "Connect to a peer",
	ArgsUsage: "[peerMultiaddr|minerActorAddress]",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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.Pretty())
			err := api.NetConnect(ctx, pi)
			if err != nil {
				fmt.Println("failure")
				return err
			}
			fmt.Println("success")
		}

		return nil
	},
}
View Source
var NetDisconnect = &cli.Command{
	Name:      "disconnect",
	Usage:     "Disconnect from a peer",
	ArgsUsage: "[peerID]",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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.Pretty())
			err = api.NetDisconnect(ctx, pid)
			if err != nil {
				fmt.Println("failure")
				return err
			}
			fmt.Println("success")
		}
		return nil
	},
}
View Source
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 := GetAPI(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
	},
}
View Source
var NetId = &cli.Command{
	Name:  "id",
	Usage: "Get node identity",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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
	},
}
View Source
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 := GetAPI(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)
	},
}
View Source
var NetListen = &cli.Command{
	Name:  "listen",
	Usage: "List listen addresses",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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
	},
}
View Source
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 := GetAPI(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
	},
}
View Source
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 := GetAPI(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
	},
}
View Source
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 := GetAPI(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
	},
}
View Source
var NetProtectList = &cli.Command{
	Name:  "list-protected",
	Usage: "List the peer IDs with protected connection.",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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
	},
}
View Source
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 := GetAPI(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
	},
}
View Source
var NetReachability = &cli.Command{
	Name:  "reachability",
	Usage: "Print information about reachability from the internet",
	Action: func(cctx *cli.Context) error {
		api, closer, err := GetAPI(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 i.PublicAddr != "" {
			fmt.Println("Public address: ", i.PublicAddr)
		}
		return nil
	},
}
View Source
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 := GetAPI(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
	},
}
View Source
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 := GetAPI(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 {
				printScope(&stat.stat, name+stat.name)
			}

		}

		printScopes("svc:", result.Services)
		printScopes("proto:", result.Protocols)
		printScopes("peer:", result.Peers)

		return nil
	},
}
View Source
var PprofCmd = &cli.Command{
	Name:   "pprof",
	Hidden: true,
	Subcommands: []*cli.Command{
		PprofGoroutines,
	},
}
View Source
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()
	},
}
View Source
var ReqContext = cliutil.ReqContext
View Source
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(&params)
		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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
var StateCmd = &cli.Command{
	Name:  "state",
	Usage: "Interact with and query filecoin chain state",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:  "tipset",
			Usage: "specify tipset to call method on (pass comma separated array of cids)",
		},
	},
	Subcommands: []*cli.Command{
		StatePowerCmd,
		StateSectorsCmd,
		StateActiveSectorsCmd,
		StateListActorsCmd,
		StateListMinersCmd,
		StateCircSupplyCmd,
		StateSectorCmd,
		StateGetActorCmd,
		StateLookupIDCmd,
		StateReplayCmd,
		StateSectorSizeCmd,
		StateReadStateCmd,
		StateListMessagesCmd,
		StateComputeStateCmd,
		StateCallCmd,
		StateGetDealSetCmd,
		StateWaitMsgCmd,
		StateSearchMsgCmd,
		StateMinerInfo,
		StateMarketCmd,
		StateExecTraceCmd,
		StateNtwkVersionCmd,
		StateMinerProvingDeadlineCmd,
		StateSysActorCIDsCmd,
	},
}
View Source
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)

		ts, err := LoadTipSet(ctx, cctx, api)
		if err != nil {
			return err
		}

		h := abi.ChainEpoch(cctx.Uint64("vm-height"))
		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 := ioutil.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
	},
}
View Source
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
	},
}
View Source
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.Address != nil {
			fmt.Printf("Delegated address:\t\t%s\n", a.Address)
		}

		return nil
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
var StateMarketCmd = &cli.Command{
	Name:  "market",
	Usage: "Inspect the storage market actor",
	Subcommands: []*cli.Command{
		stateMarketBalanceCmd,
	},
}
View Source
var StateMinerInfo = &cli.Command{
	Name:      "miner-info",
	Usage:     "Retrieve miner information",
	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
		}

		availableBalance, err := api.StateMinerAvailableBalance(ctx, addr, ts.Key())
		if err != nil {
			return xerrors.Errorf("getting miner available balance: %w", err)
		}
		fmt.Printf("Available Balance: %s\n", types.FIL(availableBalance))
		fmt.Printf("Owner:\t%s\n", mi.Owner)
		fmt.Printf("Worker:\t%s\n", mi.Worker)
		for i, controlAddress := range mi.ControlAddresses {
			fmt.Printf("Control %d: \t%s\n", i, controlAddress)
		}
		if mi.Beneficiary != address.Undef {
			fmt.Printf("Beneficiary:\t%s\n", mi.Beneficiary)
			if mi.Beneficiary != mi.Owner {
				fmt.Printf("Beneficiary Quota:\t%s\n", mi.BeneficiaryTerm.Quota)
				fmt.Printf("Beneficiary Used Quota:\t%s\n", mi.BeneficiaryTerm.UsedQuota)
				fmt.Printf("Beneficiary Expiration:\t%s\n", mi.BeneficiaryTerm.Expiration)
			}
		}
		if mi.PendingBeneficiaryTerm != nil {
			fmt.Printf("Pending Beneficiary Term:\n")
			fmt.Printf("New Beneficiary:\t%s\n", mi.PendingBeneficiaryTerm.NewBeneficiary)
			fmt.Printf("New Quota:\t%s\n", mi.PendingBeneficiaryTerm.NewQuota)
			fmt.Printf("New Expiration:\t%s\n", mi.PendingBeneficiaryTerm.NewExpiration)
			fmt.Printf("Approved By Beneficiary:\t%t\n", mi.PendingBeneficiaryTerm.ApprovedByBeneficiary)
			fmt.Printf("Approved By Nominee:\t%t\n", mi.PendingBeneficiaryTerm.ApprovedByNominee)
		}

		fmt.Printf("PeerID:\t%s\n", mi.PeerId)
		fmt.Printf("Multiaddrs:\t")
		for _, addr := range mi.Multiaddrs {
			a, err := multiaddr.NewMultiaddrBytes(addr)
			if err != nil {
				return xerrors.Errorf("undecodable listen address: %w", err)
			}
			fmt.Printf("%s ", a)
		}
		fmt.Println()
		fmt.Printf("Consensus Fault End:\t%d\n", mi.ConsensusFaultElapsed)

		fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize)
		pow, err := api.StateMinerPower(ctx, addr, ts.Key())
		if err != nil {
			return err
		}

		fmt.Printf("Byte Power:   %s / %s (%0.4f%%)\n",
			color.BlueString(types.SizeStr(pow.MinerPower.RawBytePower)),
			types.SizeStr(pow.TotalPower.RawBytePower),
			types.BigDivFloat(
				types.BigMul(pow.MinerPower.RawBytePower, big.NewInt(100)),
				pow.TotalPower.RawBytePower,
			),
		)

		fmt.Printf("Actual Power: %s / %s (%0.4f%%)\n",
			color.GreenString(types.DeciStr(pow.MinerPower.QualityAdjPower)),
			types.DeciStr(pow.TotalPower.QualityAdjPower),
			types.BigDivFloat(
				types.BigMul(pow.MinerPower.QualityAdjPower, big.NewInt(100)),
				pow.TotalPower.QualityAdjPower,
			),
		)

		fmt.Println()

		cd, err := api.StateMinerProvingDeadline(ctx, addr, ts.Key())
		if err != nil {
			return xerrors.Errorf("getting miner info: %w", err)
		}

		fmt.Printf("Proving Period Start:\t%s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.PeriodStart))

		return nil
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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)
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
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
		}
		for name, cid := range actorsCids {
			_, _ = fmt.Fprintf(tw, "%v\t%v\n", name, cid)
		}
		return tw.Flush()
	},
}
View Source
var StateWaitMsgCmd = &cli.Command{
	Name:      "wait-msg",
	Aliases:   []string{"wait-message"},
	Usage:     "Wait for a message to appear on chain",
	ArgsUsage: "[messageCid]",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:  "timeout",
			Value: "10m",
		},
	},
	Action: func(cctx *cli.Context) error {
		if cctx.NArg() != 1 {
			return IncorrectNumArgs(cctx)
		}

		api, closer, err := GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}
		defer closer()

		ctx := ReqContext(cctx)

		msg, err := cid.Decode(cctx.Args().First())
		if err != nil {
			return err
		}

		mw, err := api.StateWaitMsg(ctx, msg, build.MessageConfidence)
		if err != nil {
			return err
		}

		m, err := api.ChainGetMessage(ctx, msg)
		if err != nil {
			return err
		}

		return printMsg(ctx, api, msg, mw, m)
	},
}
View Source
var StatusCmd = &cli.Command{
	Name:  "status",
	Usage: "Check node status",
	Flags: []cli.Flag{
		&cli.BoolFlag{
			Name:  "chain",
			Usage: "include chain health status",
		},
	},

	Action: func(cctx *cli.Context) error {
		apic, closer, err := GetFullNodeAPIV1(cctx)
		if err != nil {
			return err
		}
		defer closer()
		ctx := ReqContext(cctx)

		inclChainStatus := cctx.Bool("chain")

		status, err := apic.NodeStatus(ctx, inclChainStatus)
		if err != nil {
			return err
		}

		fmt.Printf("Sync Epoch: %d\n", status.SyncStatus.Epoch)
		fmt.Printf("Epochs Behind: %d\n", status.SyncStatus.Behind)
		fmt.Printf("Peers to Publish Messages: %d\n", status.PeerStatus.PeersToPublishMsgs)
		fmt.Printf("Peers to Publish Blocks: %d\n", status.PeerStatus.PeersToPublishBlocks)

		if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) {
			var ok100, okFin string
			if status.ChainStatus.BlocksPerTipsetLast100 >= 4.75 {
				ok100 = "[OK]"
			} else {
				ok100 = "[UNHEALTHY]"
			}
			if status.ChainStatus.BlocksPerTipsetLastFinality >= 4.75 {
				okFin = "[OK]"
			} else {
				okFin = "[UNHEALTHY]"
			}

			fmt.Printf("Blocks per TipSet in last 100 epochs: %f %s\n", status.ChainStatus.BlocksPerTipsetLast100, ok100)
			fmt.Printf("Blocks per TipSet in last finality: %f %s\n", status.ChainStatus.BlocksPerTipsetLastFinality, okFin)
		}

		return nil
	},
}
View Source
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
	},
}
View Source
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
	},
}
View Source
var SyncCmd = &cli.Command{
	Name:  "sync",
	Usage: "Inspect or interact with the chain syncer",
	Subcommands: []*cli.Command{
		SyncStatusCmd,
		SyncWaitCmd,
		SyncMarkBadCmd,
		SyncUnmarkBadCmd,
		SyncCheckBadCmd,
		SyncCheckpointCmd,
	},
}
View Source
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)
	},
}
View Source
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
	},
}
View Source
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)
	},
}
View Source
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"))
	},
}
View Source
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
	},
}
View Source
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()
	},
}

Functions

func AddrInfoFromArg

func AddrInfoFromArg(ctx context.Context, cctx *cli.Context) ([]peer.AddrInfo, error)

func BackupCmd

func BackupCmd(repoFlag string, rt repo.RepoType, getApi BackupApiFn) *cli.Command

func BarString

func BarString(total, y, g float64) string

func ComputeStateHTMLTempl

func ComputeStateHTMLTempl(w io.Writer, ts *types.TipSet, o *api.ComputeStateOutput, printTiming bool, getCode func(addr address.Address) (cid.Cid, error)) error

func EncodedString

func EncodedString(sv *paych.SignedVoucher) (string, error)

func GetCidEncoder

func GetCidEncoder(cctx *cli.Context) (cidenc.Encoder, error)

GetCidEncoder returns an encoder using the `cid-base` flag if provided, or the default (Base32) encoder if not.

func IncorrectNumArgs

func IncorrectNumArgs(cctx *ufcli.Context) error

func InteractiveSend

func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI,
	proto *api.MessagePrototype) (*types.SignedMessage, error)

func JsonParams

func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error)

func JsonReturn

func JsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error)

func LoadTipSet

func LoadTipSet(ctx context.Context, cctx *cli.Context, api TipSetResolver) (*types.TipSet, error)

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

func NewCliError(s string) error

func OutputDataTransferChannels

func OutputDataTransferChannels(out io.Writer, channels []lapi.DataTransferChannel, verbose, completed, showFailed bool)

OutputDataTransferChannels generates table output for a list of channels

func ParseTipSetRef

func ParseTipSetRef(ctx context.Context, api TipSetResolver, tss string) (*types.TipSet, error)

func ParseTipSetRefOffline

func ParseTipSetRefOffline(ctx context.Context, cs *store.ChainStore, tss string) (*types.TipSet, error)

func ParseTipSetString

func ParseTipSetString(ts string) ([]cid.Cid, error)

func RunApp

func RunApp(app *ufcli.App)

func ShowHelp

func ShowHelp(cctx *ufcli.Context, err error) error

func SyncBasefeeCheck

func SyncBasefeeCheck(ctx context.Context, fullapi v1api.FullNode) error

func SyncWait

func SyncWait(ctx context.Context, napi v0api.FullNode, watch bool) error

func WithCategory

func WithCategory(cat string, cmd *cli.Command) *cli.Command

Types

type ApiConnector

type ApiConnector func() api.FullNode

ApiConnector returns API instance

type AppFmt

type AppFmt struct {
	Stdin io.Reader
	// contains filtered or unexported fields
}

func NewAppFmt

func NewAppFmt(a *ufcli.App) *AppFmt

func (*AppFmt) Print

func (a *AppFmt) Print(args ...interface{})

func (*AppFmt) Printf

func (a *AppFmt) Printf(fmtstr string, args ...interface{})

func (*AppFmt) Println

func (a *AppFmt) Println(args ...interface{})

func (*AppFmt) Scan

func (a *AppFmt) Scan(args ...interface{}) (int, error)

type BackupAPI

type BackupAPI interface {
	CreateBackup(ctx context.Context, fpath string) error
}

type BackupApiFn

type BackupApiFn func(ctx *cli.Context) (BackupAPI, jsonrpc.ClientCloser, error)

type CheckInfo

type CheckInfo struct {
	MessageTie        cid.Cid
	CurrentMessageTie bool

	Check api.MessageCheckStatus
}

type ErrCmdFailed

type ErrCmdFailed struct {
	// contains filtered or unexported fields
}

func (*ErrCmdFailed) Error

func (e *ErrCmdFailed) Error() string

type PrintHelpErr

type PrintHelpErr struct {
	Err error
	Ctx *ufcli.Context
}

func (*PrintHelpErr) Error

func (e *PrintHelpErr) Error() string

func (*PrintHelpErr) Is

func (e *PrintHelpErr) Is(o error) bool

func (*PrintHelpErr) Unwrap

func (e *PrintHelpErr) Unwrap() error

type QueriedAsk

type QueriedAsk struct {
	Ask           *storagemarket.StorageAsk
	DealProtocols []string

	Ping time.Duration
}

func GetAsks

func GetAsks(ctx context.Context, api lapi.FullNode) ([]QueriedAsk, error)

type SendParams

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

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

func GetFullNodeServices(ctx *cli.Context) (ServicesAPI, error)

type ServicesImpl

type ServicesImpl struct {
	// contains filtered or unexported fields
}

func (*ServicesImpl) Close

func (s *ServicesImpl) Close() error

func (*ServicesImpl) DecodeTypedParamsFromJSON

func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error)

func (*ServicesImpl) FullNodeAPI

func (s *ServicesImpl) FullNodeAPI() api.FullNode

func (*ServicesImpl) GetBaseFee

func (s *ServicesImpl) GetBaseFee(ctx context.Context) (abi.TokenAmount, error)

func (*ServicesImpl) LocalAddresses

func (s *ServicesImpl) LocalAddresses(ctx context.Context) (address.Address, []address.Address, error)

func (*ServicesImpl) MessageForSend

func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error)

func (*ServicesImpl) MpoolCheckPendingMessages

func (s *ServicesImpl) MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error)

func (*ServicesImpl) MpoolPendingFilter

func (s *ServicesImpl) MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool,
	tsk types.TipSetKey) ([]*types.SignedMessage, error)

func (*ServicesImpl) PublishMessage

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

func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error)

type TipSetResolver

type TipSetResolver interface {
	ChainHead(context.Context) (*types.TipSet, error)
	ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
	ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL