diff --git a/cli/util/convert.go b/cli/util/convert.go index 1fb910216..71a1c9547 100644 --- a/cli/util/convert.go +++ b/cli/util/convert.go @@ -1,10 +1,14 @@ package util import ( + "encoding/base64" + "encoding/hex" "fmt" + "os" "github.com/nspcc-dev/neo-go/cli/options" vmcli "github.com/nspcc-dev/neo-go/cli/vm" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/urfave/cli" ) @@ -44,6 +48,22 @@ func NewCommands() []cli.Command { Action: txDump, Flags: txDumpFlags, }, + { + Name: "ops", + Usage: "Pretty-print VM opcodes of the given base64- or hex- encoded script (base64 is checked first). If the input file is specified, then the script is taken from the file.", + UsageText: "ops [-i path-to-file] [--hex]", + Action: handleOps, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "in, i", + Usage: "input file containing base64- or hex- encoded script representation", + }, + cli.BoolFlag{ + Name: "hex", + Usage: "use hex encoding and do not check base64", + }, + }, + }, }, }, } @@ -57,3 +77,35 @@ func handleParse(ctx *cli.Context) error { fmt.Fprint(ctx.App.Writer, res) return nil } + +func handleOps(ctx *cli.Context) error { + var ( + s string + err error + b []byte + ) + in := ctx.String("in") + if len(in) != 0 { + b, err := os.ReadFile(in) + if err != nil { + return cli.NewExitError(fmt.Errorf("failed to read file: %w", err), 1) + } + s = string(b) + } else { + if !ctx.Args().Present() { + return cli.NewExitError("missing script", 1) + } + s = ctx.Args()[0] + } + b, err = base64.StdEncoding.DecodeString(s) + if err != nil || ctx.Bool("hex") { + b, err = hex.DecodeString(s) + } + if err != nil { + return cli.NewExitError("unknown encoding: base64 or hex are supported", 1) + } + v := vm.New() + v.LoadScript(b) + v.PrintOps(ctx.App.Writer) + return nil +} diff --git a/cli/util/util_test.go b/cli/util/util_test.go index e2f073465..609937fc9 100644 --- a/cli/util/util_test.go +++ b/cli/util/util_test.go @@ -1,10 +1,13 @@ package util_test import ( + "os" + "path/filepath" "testing" "github.com/nspcc-dev/neo-go/internal/testcli" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" ) func TestUtilConvert(t *testing.T) { @@ -24,3 +27,39 @@ func TestUtilConvert(t *testing.T) { e.CheckNextLine(t, "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzMDIwMQ==") // string to base64 e.CheckEOF(t) } + +func TestUtilOps(t *testing.T) { + e := testcli.NewExecutor(t, false) + base64Str := "EUA=" + hexStr := "1140" + + check := func(t *testing.T) { + e.CheckNextLine(t, "INDEX.*OPCODE.*PARAMETER") + e.CheckNextLine(t, "PUSH1") + e.CheckNextLine(t, "RET") + e.CheckEOF(t) + } + + e.Run(t, "neo-go", "util", "ops", base64Str) // base64 + check(t) + + e.Run(t, "neo-go", "util", "ops", hexStr) // base64 is checked firstly by default, but it's invalid script if decoded from base64 + e.CheckNextLine(t, "INDEX.*OPCODE.*PARAMETER") + e.CheckNextLine(t, ".*ERROR: incorrect opcode") + e.CheckEOF(t) + + e.Run(t, "neo-go", "util", "ops", "--hex", hexStr) // explicitly specify hex encoding + check(t) + + e.RunWithError(t, "neo-go", "util", "ops", "%&~*") // unknown encoding + + tmp := filepath.Join(t.TempDir(), "script_base64.txt") + require.NoError(t, os.WriteFile(tmp, []byte(base64Str), os.ModePerm)) + e.Run(t, "neo-go", "util", "ops", "--in", tmp) // base64 from file + check(t) + + tmp = filepath.Join(t.TempDir(), "script_hex.txt") + require.NoError(t, os.WriteFile(tmp, []byte(hexStr), os.ModePerm)) + e.Run(t, "neo-go", "util", "ops", "--hex", "--in", tmp) // hex from file + check(t) +}