package main import ( "fmt" "log" "os" "sort" "github.com/urfave/cli/v2" "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/lotus/api/v1api" lcli "github.com/filecoin-project/lotus/cli" ) // FullAPI is a JSON-RPC client targeting a full node. It's initialized in a // cli.BeforeFunc. var FullAPI v1api.FullNode // Closer is the closer for the JSON-RPC client, which must be called on // cli.AfterFunc. var Closer jsonrpc.ClientCloser // DefaultLotusRepoPath is where the fallback path where to look for a Lotus // client repo. It is expanded with mitchellh/go-homedir, so it'll work with all // OSes despite the Unix twiddle notation. const DefaultLotusRepoPath = "~/.lotus" var repoFlag = cli.StringFlag{ Name: "repo", EnvVars: []string{"LOTUS_PATH"}, Value: DefaultLotusRepoPath, TakesFile: true, } func main() { app := &cli.App{ Name: "tvx", Description: `tvx is a tool for extracting and executing test vectors. It has four subcommands. tvx extract extracts a test vector from a live network. It requires access to a Filecoin client that exposes the standard JSON-RPC API endpoint. Only message class test vectors are supported at this time. tvx exec executes test vectors against Lotus. Either you can supply one in a file, or many as an ndjson stdin stream. tvx extract-many performs a batch extraction of many messages, supplied in a CSV file. Refer to the help of that subcommand for more info. tvx simulate takes a raw message and simulates it on top of the supplied epoch, reporting the result on stderr and writing a test vector on stdout or into the specified file. SETTING THE JSON-RPC API ENDPOINT You can set the JSON-RPC API endpoint through one of the following methods. 1. Directly set the API endpoint on the FULLNODE_API_INFO env variable. The format is [token]:multiaddr, where token is optional for commands not accessing privileged operations. 2. If you're running tvx against a local Lotus client, you can set the REPO env variable to have the API endpoint and token extracted from the repo. Alternatively, you can pass the --repo CLI flag. 3. Rely on the default fallback, which inspects ~/.lotus and extracts the API endpoint string if the location is a Lotus repo. tvx will apply these methods in the same order of precedence they're listed. `, Usage: "tvx is a tool for extracting and executing test vectors", Commands: []*cli.Command{ extractCmd, execCmd, extractManyCmd, simulateCmd, }, } sort.Sort(cli.CommandsByName(app.Commands)) for _, c := range app.Commands { sort.Sort(cli.FlagsByName(c.Flags)) } if err := app.Run(os.Args); err != nil { log.Fatal(err) } } func initialize(c *cli.Context) error { // LOTUS_DISABLE_VM_BUF disables what's called "VM state tree buffering", // which stashes write operations in a BufferedBlockstore // (https://github.com/filecoin-project/lotus/blob/b7a4dbb07fd8332b4492313a617e3458f8003b2a/lib/bufbstore/buf_bstore.go#L21) // such that they're not written until the VM is actually flushed. // // For some reason, the standard behaviour was not working for me (raulk), // and disabling it (such that the state transformations are written immediately // to the blockstore) worked. _ = os.Setenv("LOTUS_DISABLE_VM_BUF", "iknowitsabadidea") // Make the API client. var err error if FullAPI, Closer, err = lcli.GetFullNodeAPIV1(c); err != nil { err = fmt.Errorf("failed to locate Lotus node; err: %w", err) } return err } func destroy(_ *cli.Context) error { if Closer != nil { Closer() } return nil } func ensureDir(path string) error { switch fi, err := os.Stat(path); { case os.IsNotExist(err): if err := os.MkdirAll(path, 0755); err != nil { return fmt.Errorf("failed to create directory %s: %w", path, err) } case err == nil: if !fi.IsDir() { return fmt.Errorf("path %s is not a directory: %w", path, err) } default: return fmt.Errorf("failed to stat directory %s: %w", path, err) } return nil }