Documentation
¶
Index ¶
- Variables
- func GetAPI(ctx context.Context, addrStr string, token string) (lily.LilyAPI, jsonrpc.ClientCloser, error)
- func LilyNodeAPIOption(out *lily.LilyAPI, fopts ...node.Option) node.Option
- func Metadata() map[string]interface{}
- func NewSentinelNodeRPC(ctx context.Context, addr string, requestHeader http.Header) (lily.LilyAPI, jsonrpc.ClientCloser, error)
- func ShowCommandHelp(ctx *cli.Context, command string) error
- func SyncWait(ctx context.Context, lapi lily.LilyAPI, watch bool) error
- type VisorDBOpts
- type VisorLogOpts
- type VisorMetricOpts
- type VisorTracingOpts
Constants ¶
This section is empty.
Variables ¶
View Source
var AppHelpTemplate = `` /* 600-byte string literal not displayed */
View Source
var ChainCmd = &cli.Command{ Name: "chain", Usage: "Interact with filecoin blockchain", Subcommands: []*cli.Command{ ChainHeadCmd, ChainGetBlock, ChainReadObjCmd, ChainStatObjCmd, ChainGetMsgCmd, ChainListCmd, }, }
View Source
var ChainGetBlock = &cli.Command{ Name: "getblock", Usage: "Get a block and print its details", ArgsUsage: "[blockCid]", Flags: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.BoolFlag{ Name: "raw", Usage: "print just the raw block header", }, }), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() if !cctx.Args().Present() { return fmt.Errorf("must pass cid of block to print") } bcid, err := cid.Decode(cctx.Args().First()) if err != nil { return err } blk, err := lapi.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 } fmt.Println(string(out)) return nil } msgs, err := lapi.ChainGetBlockMessages(ctx, bcid) if err != nil { return xerrors.Errorf("failed to get messages: %w", err) } pmsgs, err := lapi.ChainGetParentMessages(ctx, bcid) if err != nil { return xerrors.Errorf("failed to get parent messages: %w", err) } recpts, err := lapi.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 } fmt.Println(string(out)) return nil }, }
View Source
var ChainGetMsgCmd = &cli.Command{ Name: "getmessage", Usage: "Get and print a message by its cid", ArgsUsage: "[messageCid]", Action: func(cctx *cli.Context) error { if !cctx.Args().Present() { return fmt.Errorf("must pass a cid of a message to get") } ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() c, err := cid.Decode(cctx.Args().First()) if err != nil { return xerrors.Errorf("failed to parse cid input: %w", err) } mb, err := lapi.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 } fmt.Println(string(enc)) return nil }, }
View Source
var ChainHeadCmd = &cli.Command{ Name: "head", Usage: "Print chain head", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() head, err := lapi.ChainHead(ctx) if err != nil { return err } for _, c := range head.Cids() { fmt.Println(c) } return nil }, }
View Source
var ChainListCmd = &cli.Command{ Name: "list", Aliases: []string{"love"}, Usage: "View a segment of the chain", Flags: flagSet( clientAPIFlagSet, []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 { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() var head *types.TipSet if cctx.IsSet("height") { head, err = lapi.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Uint64("height")), types.EmptyTSK) } else { head, err = lapi.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 = lapi.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 fmt.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(lotusbuild.BlockGasLimit))))) for _, b := range ts.Blocks() { msgs, err := lapi.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))) } fmt.Printf("\t%s: \t%d msgs, gasLimit: %d / %d (%0.2f%%), avgPremium: %s\n", b.Miner, len(msgs.BlsMessages)+len(msgs.SecpkMessages), limitSum, lotusbuild.BlockGasLimit, 100*float64(limitSum)/float64(lotusbuild.BlockGasLimit), avgpremium) } if i < len(tss)-1 { msgs, err := lapi.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 := lapi.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(lotusbuild.BlockGasLimit) fmt.Printf("\ttipset: \t%d msgs, %d (%0.2f%%) / %d (%0.2f%%)\n", len(msgs), gasUsed, gasEfficiency, limitSum, gasCapacity) } fmt.Println() } } else { for i := len(tss) - 1; i >= 0; i-- { printTipSet(cctx.String("format"), tss[i]) } } return nil }, }
View Source
var ChainReadObjCmd = &cli.Command{ Name: "read-obj", Usage: "Read the raw bytes of an object", ArgsUsage: "[objectCid]", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() c, err := cid.Decode(cctx.Args().First()) if err != nil { return fmt.Errorf("failed to parse cid input: %s", err) } obj, err := lapi.ChainReadObj(ctx, c) if err != nil { return err } fmt.Printf("%x\n", obj) 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: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.StringFlag{ Name: "base", Usage: "ignore links found in this obj", }, }), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() 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 := lapi.ChainStatObj(ctx, obj, base) if err != nil { return err } fmt.Printf("Links: %d\n", stats.Links) fmt.Printf("Size: %s (%d)\n", types.SizeStr(types.NewInt(stats.Size)), stats.Size) return nil }, }
View Source
var DaemonCmd = &cli.Command{ Name: "daemon", Usage: "Start a lily daemon process.", Description: `Starts lily in daemon mode with its own blockstore. In daemon mode lily synchronizes with the filecoin network and runs jobs such as walk and watch against its local blockstore. This gives better performance than operating against an external blockstore but requires more disk space and memory. Before starting the daemon for the first time the blockstore must be initialized and synchronized. Visor can use a copy of an already synchronized Lotus node repository or can initialize its own empty one and import a snapshot to catch up to the chain. To initialize an empty lily blockstore repository and import an initial snapshot of the chain: lily init --repo=<path> --import-snapshot=<url> This will initialize a blockstore repository at <path> and import chain state from the snapshot at <url>. The type of snapshot needed depends on the type of jobs expected to be run by the daemon. Watch jobs only require current actor state to be imported since incoming tipsets will be used to keep actor states up to date. The lightweight and full chain snapshots available at https://docs.filecoin.io/get-started/lotus/chain/ are suitable to initialize the daemon for watch jobs. Historic walks will require full actor states for the range of heights covered by the walk. These may be created from an existing Lotus node using the export command, provided receipts are also included in the export. See the Lotus documentation for full details. Once the repository is initialized, start the daemon: lily daemon --repo=<path> --config=<path>/config.toml Visor will connect to the filecoin network and begin synchronizing with the chain. To check the synchronization status use 'visor sync status' or 'visor sync wait'. Jobs may be started on the daemon at any time. A watch job will wait for the daemon to become synchronized before extracting data and will pause if the daemon falls out of sync. Start a watch using 'visor watch'. A walk job will start immediately. Start a walk using 'visor walk'. A walk may only be performed between heights that have been synchronized with the network. Note that jobs are not persisted between restarts of the daemon. See 'visor help job' for more information on managing jobs being run by the daemon. `, Flags: []cli.Flag{ clientAPIFlag, &cli.StringFlag{ Name: "repo", Usage: "Specify path where lily should store chain state.", EnvVars: []string{"LILY_REPO"}, Value: "~/.lily", Destination: &daemonFlags.repo, }, &cli.BoolFlag{ Name: "bootstrap", EnvVars: []string{"LILY_BOOTSTRAP"}, Value: true, Destination: &daemonFlags.bootstrap, Hidden: true, }, &cli.StringFlag{ Name: "config", Usage: "Specify path of config file to use.", EnvVars: []string{"LILY_CONFIG"}, Destination: &daemonFlags.config, }, &cli.StringFlag{ Name: "genesis", Usage: "Genesis file to use for first node run.", EnvVars: []string{"LILY_GENESIS"}, Destination: &daemonFlags.genesis, }, }, Action: func(c *cli.Context) error { lotuslog.SetupLogLevels() if err := setupLogging(VisorLogFlags); err != nil { return xerrors.Errorf("setup logging: %w", err) } if err := setupMetrics(VisorMetricFlags); err != nil { return xerrors.Errorf("setup metrics: %w", err) } tcloser, err := setupTracing(VisorTracingFlags) if err != nil { return xerrors.Errorf("setup tracing: %w", err) } defer tcloser() ctx := context.Background() repoDir, err := homedir.Expand(daemonFlags.repo) if err != nil { log.Warnw("could not expand repo location", "error", err) } else { log.Infof("visor repo: %s", repoDir) } r, err := repo.NewFS(daemonFlags.repo) if err != nil { return xerrors.Errorf("opening fs repo: %w", err) } if daemonFlags.config == "" { daemonFlags.config = filepath.Join(repoDir, "config.toml") } else { daemonFlags.config, err = homedir.Expand(daemonFlags.config) if err != nil { log.Warnw("could not expand repo location", "error", err) } else { log.Infof("visor config: %s", repoDir) } } if err := config.EnsureExists(daemonFlags.config); err != nil { return xerrors.Errorf("ensuring config is present at %q: %w", daemonFlags.config, err) } r.SetConfigPath(daemonFlags.config) err = r.Init(repo.FullNode) if err != nil && err != repo.ErrRepoExists { return xerrors.Errorf("repo init error: %w", err) } if err := paramfetch.GetParams(lcli.ReqContext(c), lotusbuild.ParametersJSON(), lotusbuild.SrsJSON(), 0); err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } var genBytes []byte if c.String("genesis") != "" { genBytes, err = ioutil.ReadFile(daemonFlags.genesis) if err != nil { return xerrors.Errorf("reading genesis: %w", err) } } else { genBytes = lotusbuild.MaybeGenesis() } genesis := node.Options() if len(genBytes) > 0 { genesis = node.Override(new(lotusmodules.Genesis), lotusmodules.LoadGenesis(genBytes)) } isBootstrapper := false shutdown := make(chan struct{}) liteModeDeps := node.Options() var api lily.LilyAPI stop, err := node.New(ctx, LilyNodeAPIOption(&api), node.Override(new(*config.Conf), modules.LoadConf(daemonFlags.config)), node.Override(new(*events.Events), modules.NewEvents), node.Override(new(*schedule.Scheduler), schedule.NewSchedulerDaemon), node.Override(new(*storage.Catalog), modules.NewStorageCatalog), node.Override(new(dtypes.Bootstrapper), isBootstrapper), node.Override(new(dtypes.ShutdownChan), shutdown), node.Online(), node.Repo(r), node.Override(new(*stmgr.StateManager), modules.StateManager), node.Override(new(stmgr.ExecMonitor), modules.NewBufferedExecMonitor), genesis, liteModeDeps, node.ApplyIf(func(s *node.Settings) bool { return c.IsSet("api") }, node.Override(node.SetApiEndpointKey, func(lr repo.LockedRepo) error { apima, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/" + c.String("api")) if err != nil { return err } return lr.SetAPIEndpoint(apima) })), node.ApplyIf(func(s *node.Settings) bool { return !daemonFlags.bootstrap }, node.Unset(node.RunPeerMgrKey), node.Unset(new(*peermgr.PeerMgr)), ), ) if err != nil { return xerrors.Errorf("initializing node: %w", err) } endpoint, err := r.APIEndpoint() if err != nil { return xerrors.Errorf("getting api endpoint: %w", err) } maxAPIRequestSize := int64(0) return util.ServeRPC(api, stop, endpoint, shutdown, maxAPIRequestSize) }, }
View Source
var GapCmd = &cli.Command{ Name: "gap", Usage: "Launch gap filling and finding jobs", Subcommands: []*cli.Command{ GapFillCmd, GapFindCmd, }, }
View Source
var GapFillCmd = &cli.Command{ Name: "fill", Usage: "Fill gaps in the database", Flags: []cli.Flag{ &cli.StringFlag{ Name: "api", Usage: "Address of lily api in multiaddr format.", EnvVars: []string{"LILY_API"}, Value: "/ip4/127.0.0.1/tcp/1234", Destination: &gapFlags.apiAddr, }, &cli.StringFlag{ Name: "api-token", Usage: "Authentication token for lily api.", EnvVars: []string{"LILY_API_TOKEN"}, Value: "", Destination: &gapFlags.apiToken, }, &cli.StringFlag{ Name: "storage", Usage: "Name of storage that results will be written to.", EnvVars: []string{"LILY_STORAGE"}, Value: "", Destination: &gapFlags.storage, }, &cli.StringFlag{ Name: "tasks", Usage: "Comma separated list of tasks to fill. Each task is reported separately in the database. If empty all task will be filled.", EnvVars: []string{"LILY_TASKS"}, Value: "", Destination: &gapFlags.tasks, }, &cli.StringFlag{ Name: "name", Usage: "Name of job for easy identification later.", EnvVars: []string{"LILY_JOB_NAME"}, Value: "", Destination: &gapFlags.name, }, &cli.Uint64Flag{ Name: "to", Usage: "to epoch to search for gaps in", EnvVars: []string{"LILY_TO"}, Destination: &gapFlags.to, Required: true, }, &cli.Uint64Flag{ Name: "from", Usage: "from epoch to search for gaps in", EnvVars: []string{"LILY_FROM"}, Destination: &gapFlags.from, Required: true, }, }, Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, gapFlags.apiAddr, gapFlags.apiToken) if err != nil { return err } defer closer() var tasks []string if gapFlags.tasks == "" { tasks = chain.AllTasks } else { tasks = strings.Split(gapFlags.tasks, ",") } fillName := fmt.Sprintf("fill_%d", time.Now().Unix()) if gapFlags.name != "" { fillName = gapFlags.name } gapFindID, err := api.LilyGapFill(ctx, &lily.LilyGapFillConfig{ RestartOnFailure: false, RestartOnCompletion: false, RestartDelay: 0, Storage: gapFlags.storage, Name: fillName, Tasks: tasks, To: gapFlags.to, From: gapFlags.from, }) if err != nil { return err } if _, err := fmt.Fprintf(os.Stdout, "Created Gap Job: %d\n", gapFindID); err != nil { return err } return nil }, }
View Source
var GapFindCmd = &cli.Command{ Name: "find", Usage: "find gaps in the database", Flags: []cli.Flag{ &cli.StringFlag{ Name: "api", Usage: "Address of lily api in multiaddr format.", EnvVars: []string{"LILY_API"}, Value: "/ip4/127.0.0.1/tcp/1234", Destination: &gapFlags.apiAddr, }, &cli.StringFlag{ Name: "api-token", Usage: "Authentication token for lily api.", EnvVars: []string{"LILY_API_TOKEN"}, Value: "", Destination: &gapFlags.apiToken, }, &cli.StringFlag{ Name: "storage", Usage: "Name of storage that results will be written to.", Value: "", Destination: &gapFlags.storage, }, &cli.StringFlag{ Name: "name", Usage: "Name of job for easy identification later.", Value: "", Destination: &gapFlags.name, }, &cli.StringFlag{ Name: "tasks", Usage: "Comma separated list of tasks to fill. Each task is reported separately in the database. If empty all task will be filled.", Value: "", Destination: &gapFlags.tasks, }, &cli.Uint64Flag{ Name: "to", Usage: "to epoch to search for gaps in", Destination: &gapFlags.to, Required: true, }, &cli.Uint64Flag{ Name: "from", Usage: "from epoch to search for gaps in", Destination: &gapFlags.from, Required: true, }, }, Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, gapFlags.apiAddr, gapFlags.apiToken) if err != nil { return err } defer closer() findName := fmt.Sprintf("find_%d", time.Now().Unix()) if gapFlags.name != "" { findName = gapFlags.name } var tasks []string if gapFlags.tasks == "" { tasks = chain.AllTasks } else { tasks = strings.Split(gapFlags.tasks, ",") } gapFindID, err := api.LilyGapFind(ctx, &lily.LilyGapFindConfig{ RestartOnFailure: false, RestartOnCompletion: false, RestartDelay: 0, Storage: gapFlags.storage, Tasks: tasks, Name: findName, To: gapFlags.to, From: gapFlags.from, }) if err != nil { return err } if _, err := fmt.Fprintf(os.Stdout, "Created Gap Job: %d\n", gapFindID); err != nil { return err } return nil }, }
View Source
var HelpCmd = &cli.Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(c *cli.Context) error { args := c.Args() if args.Present() { return ShowCommandHelp(c, args.First()) } _ = cli.ShowAppHelp(c) return nil }, }
View Source
var InitCmd = &cli.Command{ Name: "init", Usage: "Initialise a lily repository.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "repo", Usage: "Specify path where lily should store chain state.", EnvVars: []string{"LILY_REPO"}, Value: "~/.lily", Destination: &initFlags.repo, }, &cli.StringFlag{ Name: "config", Usage: "Specify path of config file to use.", EnvVars: []string{"LILY_CONFIG"}, Destination: &initFlags.config, }, &cli.StringFlag{ Name: "import-snapshot", Usage: "Import chain state from a given chain export file or url.", EnvVars: []string{"LILY_SNAPSHOT"}, Destination: &initFlags.importSnapshot, }, }, Action: func(c *cli.Context) error { lotuslog.SetupLogLevels() ctx := context.Background() { dir, err := homedir.Expand(initFlags.repo) if err != nil { log.Warnw("could not expand repo location", "error", err) } else { log.Infof("lotus repo: %s", dir) } } r, err := repo.NewFS(initFlags.repo) if err != nil { return xerrors.Errorf("opening fs repo: %w", err) } if initFlags.config != "" { if err := config.EnsureExists(initFlags.config); err != nil { return xerrors.Errorf("ensuring config is present at %q: %w", initFlags.config, err) } r.SetConfigPath(initFlags.config) } err = r.Init(repo.FullNode) if err != nil && err != repo.ErrRepoExists { return xerrors.Errorf("repo init error: %w", err) } if err := paramfetch.GetParams(lcli.ReqContext(c), lotusbuild.ParametersJSON(), lotusbuild.SrsJSON(), 0); err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } if initFlags.importSnapshot != "" { if err := util.ImportChain(ctx, r, initFlags.importSnapshot, true); err != nil { return err } } return nil }, }
View Source
var JobCmd = &cli.Command{ Name: "job", Usage: "Manage jobs being run by the daemon.", Subcommands: []*cli.Command{ JobStartCmd, JobStopCmd, JobListCmd, }, }
View Source
var JobListCmd = &cli.Command{ Name: "list", Usage: "list all jobs and their status", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() jobs, err := api.LilyJobList(ctx) if err != nil { return err } prettyJobs, err := json.MarshalIndent(jobs, "", "\t") if err != nil { return err } if _, err := fmt.Fprintf(os.Stdout, "List Jobs:\n%s\n", prettyJobs); err != nil { return err } return nil }, }
View Source
var JobStartCmd = &cli.Command{ Name: "start", Usage: "start a job.", Flags: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.IntFlag{ Name: "id", Usage: "Identifier of job to start", Required: true, Destination: &jobControlFlags.ID, }, }, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() return api.LilyJobStart(ctx, schedule.JobID(jobControlFlags.ID)) }, }
View Source
var JobStopCmd = &cli.Command{ Name: "stop", Usage: "stop a job.", Flags: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.IntFlag{ Name: "id", Usage: "Identifier of job to stop", Required: true, Destination: &jobControlFlags.ID, }, }, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() return api.LilyJobStop(ctx, schedule.JobID(jobControlFlags.ID)) }, }
View Source
var LogCmd = &cli.Command{ Name: "log", Usage: "Manage logging", Subcommands: []*cli.Command{ LogList, LogSetLevel, }, }
View Source
var LogList = &cli.Command{ Name: "list", Usage: "List log systems", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() systems, err := api.LogList(ctx) if err != nil { return err } sort.Strings(systems) 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: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.StringSliceFlag{ Name: "system", Usage: "limit to log system", Value: &cli.StringSlice{}, }, }, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) api, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() 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 MigrateCmd = &cli.Command{ Name: "migrate", Usage: "Manage the schema version installed in a database.", Flags: flagSet( dbConnectFlags, []cli.Flag{ &cli.StringFlag{ Name: "to", Usage: "Migrate the schema to specific `VERSION`.", Value: "", }, &cli.BoolFlag{ Name: "latest", Value: false, Usage: "Migrate the schema to the latest version.", }, }, ), Action: func(cctx *cli.Context) error { if err := setupLogging(VisorLogFlags); err != nil { return xerrors.Errorf("setup logging: %w", err) } ctx := cctx.Context db, err := storage.NewDatabase(ctx, VisorDBFlags.DB, VisorDBFlags.DBPoolSize, VisorDBFlags.Name, VisorDBFlags.DBSchema, false) if err != nil { return xerrors.Errorf("connect database: %w", err) } if cctx.IsSet("to") { targetVersion, err := model.ParseVersion(cctx.String("to")) if err != nil { return xerrors.Errorf("invalid schema version: %w", err) } return db.MigrateSchemaTo(ctx, targetVersion) } if cctx.Bool("latest") { return db.MigrateSchema(ctx) } dbVersion, latestVersion, err := db.GetSchemaVersions(ctx) if err != nil { return xerrors.Errorf("get schema versions: %w", err) } log.Infof("current database schema is version %s, latest is %s", dbVersion, latestVersion) if err := db.VerifyCurrentSchema(ctx); err != nil { return xerrors.Errorf("verify schema: %w", err) } log.Infof("database schema is supported by this version of visor") return nil }, }
View Source
var NetCmd = &cli.Command{ Name: "net", Usage: "Manage P2P Network", Subcommands: []*cli.Command{ NetID, NetListen, NetPeers, NetReachability, NetScores, }, }
View Source
var NetID = &cli.Command{ Name: "id", Usage: "Get peer ID of libp2p node used by daemon", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return xerrors.Errorf("get api: %w", err) } defer closer() pid, err := lapi.ID(ctx) if err != nil { return xerrors.Errorf("get id: %w", err) } fmt.Println(pid) return nil }, }
View Source
var NetListen = &cli.Command{ Name: "listen", Usage: "List libp2p addresses daemon is listening on", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return xerrors.Errorf("get api: %w", err) } defer closer() addrs, err := lapi.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: "List peers daemon is connected to", Flags: flagSet( clientAPIFlagSet, []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 { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return xerrors.Errorf("get api: %w", err) } defer closer() peers, err := lapi.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 := lapi.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 { w := tabwriter.NewWriter(os.Stdout, 4, 0, 1, ' ', 0) for _, peer := range peers { var agent string if cctx.Bool("agent") { agent, err = lapi.NetAgentVersion(ctx, peer.ID) if err != nil { log.Warnf("getting agent version: %s", err) } } fmt.Fprintf(w, "%s\t%s\t%s\n", peer.ID, peer.Addrs, agent) } w.Flush() } return nil }, }
View Source
var NetReachability = &cli.Command{ Name: "reachability", Usage: "Print information about reachability from the Internet", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return xerrors.Errorf("get api: %w", err) } defer closer() i, err := lapi.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: "List scores assigned to peers", Flags: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.BoolFlag{ Name: "extended", Aliases: []string{"x"}, Usage: "print extended peer scores in json", }, }, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return xerrors.Errorf("get api: %w", err) } defer closer() scores, err := lapi.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 { w := tabwriter.NewWriter(os.Stdout, 4, 0, 1, ' ', 0) for _, peer := range scores { fmt.Fprintf(w, "%s\t%f\n", peer.ID, peer.Score.Score) } w.Flush() } return nil }, }
View Source
var StopCmd = &cli.Command{ Name: "stop", Usage: "Stop a running lily daemon", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() err = lapi.Shutdown(ctx) if 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, }, }
View Source
var SyncStatusCmd = &cli.Command{ Name: "status", Usage: "Report sync status of a running lily daemon", Flags: flagSet( clientAPIFlagSet, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() state, err := lapi.SyncState(ctx) if err != nil { return err } fmt.Println("sync status:") for _, ss := range state.ActiveSyncs { fmt.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 } fmt.Printf("\tBase:\t%s\n", base) fmt.Printf("\tTarget:\t%s (%d)\n", target, theight) fmt.Printf("\tHeight diff:\t%d\n", heightDiff) fmt.Printf("\tStage: %s\n", ss.Stage) fmt.Printf("\tHeight: %d\n", ss.Height) if ss.End.IsZero() { if !ss.Start.IsZero() { fmt.Printf("\tElapsed: %s\n", time.Since(ss.Start)) } } else { fmt.Printf("\tElapsed: %s\n", ss.End.Sub(ss.Start)) } if ss.Stage == api.StageSyncErrored { fmt.Printf("\tError: %s\n", ss.Message) } } return nil }, }
View Source
var SyncWaitCmd = &cli.Command{ Name: "wait", Usage: "Wait for sync to be complete", Flags: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.BoolFlag{ Name: "watch", Usage: "don't exit after node is synced", }, }, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) lapi, closer, err := GetAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err != nil { return err } defer closer() return SyncWait(ctx, lapi, cctx.Bool("watch")) }, }
View Source
var WaitApiCmd = &cli.Command{ Name: "wait-api", Usage: "Wait for lily api to come online", Flags: flagSet( clientAPIFlagSet, []cli.Flag{ &cli.DurationFlag{ Name: "timeout", Usage: "Time to wait for API to become ready", Value: 30 * time.Second, }, }, ), Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) var timeout <-chan time.Time if cctx.Duration("timeout") > 0 { timeout = time.NewTimer(cctx.Duration("timeout")).C } for { err := checkAPI(ctx, clientAPIFlags.apiAddr, clientAPIFlags.apiToken) if err == nil { return nil } log.Warnf("API not online yet... (%s)", err) select { case <-ctx.Done(): return nil case <-timeout: return fmt.Errorf("timed out waiting for api to come online") case <-time.After(time.Second): } } }, }
View Source
var WalkCmd = &cli.Command{ Name: "walk", Usage: "Start a daemon job to walk a range of the filecoin blockchain.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "tasks", Usage: "Comma separated list of tasks to run. Each task is reported separately in the database.", EnvVars: []string{"LILY_TASKS"}, Value: strings.Join([]string{chain.BlocksTask, chain.MessagesTask, chain.ChainEconomicsTask, chain.ActorStatesRawTask}, ","), Destination: &walkFlags.tasks, }, &cli.DurationFlag{ Name: "window", Usage: "Duration after which any indexing work not completed will be marked incomplete", EnvVars: []string{"LILY_WINDOW"}, Value: builtin.EpochDurationSeconds * time.Second * 10, Destination: &walkFlags.window, }, &cli.Int64Flag{ Name: "from", Usage: "Limit actor and message processing to tipsets at or above `HEIGHT`", EnvVars: []string{"LILY_FROM"}, Destination: &walkFlags.from, Required: true, }, &cli.Int64Flag{ Name: "to", Usage: "Limit actor and message processing to tipsets at or below `HEIGHT`", EnvVars: []string{"LILY_TO"}, Destination: &walkFlags.to, Required: true, }, &cli.StringFlag{ Name: "storage", Usage: "Name of storage that results will be written to.", EnvVars: []string{"LILY_STORAGE"}, Value: "", Destination: &walkFlags.storage, }, &cli.StringFlag{ Name: "api", Usage: "Address of lily api in multiaddr format.", EnvVars: []string{"LILY_API"}, Value: "/ip4/127.0.0.1/tcp/1234", Destination: &walkFlags.apiAddr, }, &cli.StringFlag{ Name: "api-token", Usage: "Authentication token for lily api.", EnvVars: []string{"LILY_API_TOKEN"}, Value: "", Destination: &walkFlags.apiToken, }, &cli.StringFlag{ Name: "name", Usage: "Name of job for easy identification later.", EnvVars: []string{"LILY_JOB_NAME"}, Value: "", Destination: &walkFlags.name, }, }, Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) walkName := fmt.Sprintf("walk_%d", time.Now().Unix()) if walkFlags.name != "" { walkName = walkFlags.name } cfg := &lily.LilyWalkConfig{ Name: walkName, Tasks: strings.Split(walkFlags.tasks, ","), Window: walkFlags.window, From: walkFlags.from, To: walkFlags.to, RestartDelay: 0, RestartOnCompletion: false, RestartOnFailure: false, Storage: walkFlags.storage, } api, closer, err := GetAPI(ctx, walkFlags.apiAddr, walkFlags.apiToken) if err != nil { return err } defer closer() watchID, err := api.LilyWalk(ctx, cfg) if err != nil { return err } if _, err := fmt.Fprintf(os.Stdout, "Created walk job %d\n", watchID); err != nil { return err } return nil }, }
View Source
var WatchCmd = &cli.Command{ Name: "watch", Usage: "Start a daemon job to watch the head of the filecoin blockchain.", Flags: []cli.Flag{ &cli.IntFlag{ Name: "confidence", Usage: "Sets the size of the cache used to hold tipsets for possible reversion before being committed to the database", EnvVars: []string{"LILY_CONFIDENCE"}, Value: 2, Destination: &watchFlags.confidence, }, &cli.StringFlag{ Name: "tasks", Usage: "Comma separated list of tasks to run. Each task is reported separately in the database.", EnvVars: []string{"LILY_TASKS"}, Value: strings.Join([]string{chain.BlocksTask, chain.MessagesTask, chain.ChainEconomicsTask, chain.ActorStatesRawTask}, ","), Destination: &watchFlags.tasks, }, &cli.DurationFlag{ Name: "window", Usage: "Duration after which any indexing work not completed will be marked incomplete", EnvVars: []string{"LILY_WINDOW"}, Value: builtin.EpochDurationSeconds * time.Second, Destination: &watchFlags.window, }, &cli.StringFlag{ Name: "storage", Usage: "Name of storage that results will be written to.", EnvVars: []string{"LILY_STORAGE"}, Value: "", Destination: &watchFlags.storage, }, &cli.StringFlag{ Name: "api", Usage: "Address of lily api in multiaddr format.", EnvVars: []string{"LILY_API"}, Value: "/ip4/127.0.0.1/tcp/1234", Destination: &watchFlags.apiAddr, }, &cli.StringFlag{ Name: "api-token", Usage: "Authentication token for lily api.", EnvVars: []string{"LILY_API_TOKEN"}, Value: "", Destination: &watchFlags.apiToken, }, &cli.StringFlag{ Name: "name", Usage: "Name of job for easy identification later.", EnvVars: []string{"LILY_JOB_NAME"}, Value: "", Destination: &watchFlags.name, }, }, Action: func(cctx *cli.Context) error { ctx := lotuscli.ReqContext(cctx) watchName := fmt.Sprintf("watch_%d", time.Now().Unix()) if watchFlags.name != "" { watchName = watchFlags.name } cfg := &lily.LilyWatchConfig{ Name: watchName, Tasks: strings.Split(watchFlags.tasks, ","), Window: watchFlags.window, Confidence: watchFlags.confidence, RestartDelay: 0, RestartOnCompletion: false, RestartOnFailure: true, Storage: watchFlags.storage, } api, closer, err := GetAPI(ctx, watchFlags.apiAddr, watchFlags.apiToken) if err != nil { return err } defer closer() watchID, err := api.LilyWatch(ctx, cfg) if err != nil { return err } if _, err := fmt.Fprintf(os.Stdout, "Created watch job %d\n", watchID); err != nil { return err } return nil }, }
Functions ¶
func LilyNodeAPIOption ¶
Lily Node settings for injection into lotus node.
func NewSentinelNodeRPC ¶
func ShowCommandHelp ¶
Types ¶
type VisorDBOpts ¶
type VisorDBOpts struct { DB string Name string DBSchema string DBPoolSize int DBAllowUpsert bool DBAllowMigrations bool }
var VisorDBFlags VisorDBOpts
type VisorLogOpts ¶
var VisorLogFlags VisorLogOpts
type VisorMetricOpts ¶
type VisorMetricOpts struct {
PrometheusPort string
}
var VisorMetricFlags VisorMetricOpts
type VisorTracingOpts ¶
type VisorTracingOpts struct { Tracing bool JaegerHost string JaegerPort int JaegerName string JaegerSampleType string JaegerSamplerParam float64 }
var VisorTracingFlags VisorTracingOpts
Source Files
¶
Click to show internal directories.
Click to hide internal directories.