diff --git a/cli/vm/cli.go b/cli/vm/cli.go index 58495944c..4b4845d99 100644 --- a/cli/vm/cli.go +++ b/cli/vm/cli.go @@ -39,6 +39,7 @@ import ( const ( chainKey = "chain" + chainCfgKey = "chainCfg" icKey = "ic" manifestKey = "manifest" exitFuncKey = "exitFunc" @@ -51,6 +52,11 @@ const ( stringType = "string" ) +// Various flag names. +const ( + verboseFlagFullName = "verbose" +) + var commands = []cli.Command{ { Name: "exit", @@ -231,6 +237,24 @@ example: Description: "Dump events emitted by the current loaded program", Action: handleEvents, }, + { + Name: "env", + Usage: "Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration)", + UsageText: `env [-v]`, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: verboseFlagFullName + ",v", + Usage: "Print the whole blockchain node configuration.", + }, + }, + Description: `env [-v] + +Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration). + +Example: +> env -v`, + Action: handleEnv, + }, } var completer *readline.PrefixCompleter @@ -318,6 +342,7 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg vmcli.shell.Metadata = map[string]interface{}{ chainKey: chain, + chainCfgKey: cfg, icKey: ic, manifestKey: new(manifest.Manifest), exitFuncKey: exitF, @@ -344,6 +369,10 @@ func getChainFromContext(app *cli.App) *core.Blockchain { return app.Metadata[chainKey].(*core.Blockchain) } +func getChainConfigFromContext(app *cli.App) config.Config { + return app.Metadata[chainCfgKey].(config.Config) +} + func getInteropContextFromContext(app *cli.App) *interop.Context { return app.Metadata[icKey].(*interop.Context) } @@ -764,6 +793,21 @@ func handleEvents(c *cli.Context) error { return nil } +func handleEnv(c *cli.Context) error { + bc := getChainFromContext(c.App) + cfg := getChainConfigFromContext(c.App) + message := fmt.Sprintf("Chain height: %d\nNetwork magic: %d\nDB type: %s\n", bc.BlockHeight(), bc.GetConfig().Magic, cfg.ApplicationConfiguration.DBConfiguration.Type) + if c.Bool(verboseFlagFullName) { + cfgBytes, err := json.MarshalIndent(cfg, "", "\t") + if err != nil { + return fmt.Errorf("failed to marshal node configuration: %w", err) + } + message += "Node config:\n" + string(cfgBytes) + "\n" + } + fmt.Fprint(c.App.Writer, message) + return nil +} + func dumpEvents(app *cli.App) (string, error) { ic := getInteropContextFromContext(app) if len(ic.Notifications) == 0 { diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 4c71304fe..241e54e1a 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -786,3 +786,28 @@ func TestEvents(t *testing.T) { e.checkEvents(t, true, expectedEvent) // automatically printed after `run` command e.checkEvents(t, false, expectedEvent) // printed after `events` command } + +func TestEnv(t *testing.T) { + t.Run("default setup", func(t *testing.T) { + e := newTestVMCLI(t) + e.runProg(t, "env") + e.checkNextLine(t, "Chain height: 0") + e.checkNextLine(t, "Network magic: 42") + e.checkNextLine(t, "DB type: inmemory") + }) + t.Run("setup with state", func(t *testing.T) { + e := newTestVMClIWithState(t) + e.runProg(t, "env") + e.checkNextLine(t, "Chain height: 5") + e.checkNextLine(t, "Network magic: 42") + e.checkNextLine(t, "DB type: leveldb") + }) + t.Run("verbose", func(t *testing.T) { + e := newTestVMClIWithState(t) + e.runProg(t, "env -v") + e.checkNextLine(t, "Chain height: 5") + e.checkNextLine(t, "Network magic: 42") + e.checkNextLine(t, "DB type: leveldb") + e.checkNextLine(t, "Node config:") // Do not check exact node config. + }) +}