cli: add Required field for flags

Close #2861

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
Ekaterina Pavlova 2024-07-05 01:34:40 +03:00
parent acde7bd0de
commit 39559b90e2
19 changed files with 261 additions and 276 deletions

View file

@ -348,3 +348,14 @@ func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers
}
return signersAccounts, nil
}
// EnsureNotEmpty returns a function that checks if the flag with the given name
// is not empty.
func EnsureNotEmpty(flagName string) func(*cli.Context, string) error {
return func(ctx *cli.Context, name string) error {
if ctx.String(flagName) == "" {
return cli.Exit(fmt.Errorf("required flag --%s is empty", flagName), 1)
}
return nil
}
}

View file

@ -55,11 +55,14 @@ func TestNEP11Import(t *testing.T) {
"--wallet", walletPath,
}
// missing token hash
e.RunWithError(t, args...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, args...)
// excessive parameters
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE(), "something")...)
// empty token hash
e.RunWithErrorCheck(t, `invalid value "" for flag -token: zero length string`, append(args, "--token", "")...)
// good: non-divisible
e.Run(t, append(args, "--token", nnsContractHash.StringLE())...)
@ -229,7 +232,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdOwnerOf...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOf: missing token ID
@ -244,11 +247,11 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flags "token, address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flag "address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", nftOwnerAddr)
// tokensOf: good
@ -260,7 +263,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdProperties...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
@ -286,7 +289,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokens...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: excessive parameters
@ -514,7 +517,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdOwnerOf...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOfD: missing token ID
@ -529,11 +532,11 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flags "token, address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flag "address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", testcli.ValidatorAddr)
// tokensOf: good
@ -547,7 +550,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdProperties...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
@ -580,7 +583,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokens...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: good, several tokens

View file

@ -113,7 +113,7 @@ func TestNEP17Balance(t *testing.T) {
e.CheckEOF(t)
})
t.Run("Bad wallet", func(t *testing.T) {
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null")...)
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null", "-r", "test")...)
})
}
@ -136,7 +136,7 @@ func TestNEP17Transfer(t *testing.T) {
as := append([]string{}, args[:8]...)
as = append(as, args[10:]...)
e.In.WriteString("one\r")
e.RunWithError(t, as...)
e.RunWithErrorCheck(t, `Required flag "to" not set`, as...)
e.In.Reset()
})
@ -326,7 +326,7 @@ func TestNEP17ImportToken(t *testing.T) {
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
// missing token hash
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
e.RunWithErrorCheck(t, `Required flag "token" not set`, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath)

View file

@ -14,6 +14,7 @@ import (
"strings"
"time"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/config"
@ -86,9 +87,11 @@ var Network = []cli.Flag{
// RPC is a set of flags used for RPC connections (endpoint and timeout).
var RPC = []cli.Flag{
&cli.StringFlag{
Name: RPCEndpointFlag,
Aliases: []string{"r"},
Usage: "RPC node address",
Name: RPCEndpointFlag,
Aliases: []string{"r"},
Usage: "RPC node address",
Required: true,
Action: cmdargs.EnsureNotEmpty("rpc-endpoint"),
},
&cli.DurationFlag{
Name: "timeout",
@ -131,7 +134,6 @@ var Debug = &cli.BoolFlag{
Usage: "Enable debug logging (LOTS of output, overrides configuration)",
}
var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'")
var errInvalidHistoric = errors.New("invalid 'historic' parameter, neither a block number, nor a block/state hash")
var errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet' or '-w' flag or specify wallet config file with the '--wallet-config' flag")
var errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location")
@ -167,9 +169,6 @@ func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) {
// GetRPCClient returns an RPC client instance for the given Context.
func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) {
endpoint := ctx.String(RPCEndpointFlag)
if len(endpoint) == 0 {
return nil, cli.Exit(errNoEndpoint, 1)
}
c, err := rpcclient.New(gctx, endpoint, rpcclient.Options{})
if err != nil {
return nil, cli.Exit(err, 1)

View file

@ -101,14 +101,14 @@ func NewCommands() []*cli.Command {
{
Name: "dump",
Usage: "Dump blocks (starting with block #1) to the file",
UsageText: "neo-go db dump -o file [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
UsageText: "neo-go db dump [-o file] [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Action: dumpDB,
Flags: cfgCountOutFlags,
},
{
Name: "restore",
Usage: "Restore blocks from the file",
UsageText: "neo-go db restore -i file [--dump] [-n] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
UsageText: "neo-go db restore [-i file] [--dump] [-n] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Action: restoreDB,
Flags: cfgCountInFlags,
},

View file

@ -56,13 +56,13 @@ func TestCalcHash(t *testing.T) {
cmd := []string{"neo-go", "contract", "calc-hash"}
t.Run("no sender", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
e.RunWithErrorCheck(t, `Required flag "sender" not set`, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
})
t.Run("no nef file", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
e.RunWithErrorCheck(t, `Required flag "in" not set`, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
})
t.Run("no manifest file", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
})
t.Run("invalid nef path", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
@ -289,7 +289,7 @@ func TestContractInitAndCompile(t *testing.T) {
e := testcli.NewExecutor(t, false)
t.Run("no path is provided", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "init")
e.RunWithErrorCheck(t, `Required flag "name" not set`, "neo-go", "contract", "init")
})
t.Run("invalid path", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "init", "--name", "\x00")
@ -313,7 +313,7 @@ func TestContractInitAndCompile(t *testing.T) {
manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json")
cmd := []string{"neo-go", "contract", "compile"}
t.Run("missing source", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...)
})
cmd = append(cmd, "--in", srcPath, "--out", nefPath, "--manifest", manifestPath)
@ -487,10 +487,10 @@ func TestDeployWithSigners(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--in", "", "--manifest", manifestName)
"--in", nefName, "--manifest", manifestName)
})
t.Run("missing manifest", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
e.RunWithErrorCheck(t, "required flag --manifest is empty", "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--in", nefName, "--manifest", "")
@ -517,7 +517,7 @@ func TestDeployWithSigners(t *testing.T) {
"[", "str1", "str2", "]")
})
t.Run("missing RPC", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "deploy",
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--in", nefName, "--manifest", manifestName,
"[", "str1", "str2", "]")
@ -548,28 +548,29 @@ func TestContractManifestGroups(t *testing.T) {
"--out", nefName, "--manifest", manifestName)
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group")
e.RunWithErrorCheck(t, `Required flags "sender, address, nef, manifest" not set`, "neo-go", "contract", "manifest", "add-group")
})
t.Run("invalid wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", t.TempDir())
"--wallet", t.TempDir(), "--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount,
"--nef", nefName, "--manifest", manifestName)
})
t.Run("invalid sender", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", "not-a-sender")
"--sender", "not-a-sender", "--nef", nefName, "--manifest", manifestName)
})
t.Run("invalid NEF file", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", tmpDir)
"--sender", testcli.TestWalletAccount, "--nef", tmpDir, "--manifest", manifestName)
})
t.Run("corrupted NEF file", func(t *testing.T) {
f := filepath.Join(tmpDir, "invalid.nef")
require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", f)
"--sender", testcli.TestWalletAccount, "--nef", f, "--manifest", manifestName)
})
t.Run("invalid manifest file", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
@ -630,9 +631,17 @@ func TestContract_TestInvokeScript(t *testing.T) {
"--out", goodNef, "--manifest", manifestName)
t.Run("missing in", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("empty in", func(t *testing.T) {
e.RunWithErrorCheck(t, "required flag --in is empty", "neo-go", "contract", "testinvokescript", "-i", "",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("empty rpc", func(t *testing.T) {
e.RunWithErrorCheck(t, "required flag --rpc-endpoint is empty", "neo-go", "contract", "testinvokescript", "-i", goodNef,
"--rpc-endpoint", "")
})
t.Run("unexisting in", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
@ -723,7 +732,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
t.Run("check calc hash", func(t *testing.T) {
// missing sender
e.RunWithError(t, "neo-go", "contract", "calc-hash",
e.RunWithErrorCheck(t, `Required flag "sender" not set`, "neo-go", "contract", "calc-hash",
"--in", nefName,
"--manifest", manifestName)
@ -755,7 +764,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.RunWithError(t, append(cmd, "--", "notahash")...)
})
t.Run("missing RPC address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "testinvokefunction",
h.StringLE(), "getValue")
})
@ -1038,7 +1047,7 @@ func TestContractInspect(t *testing.T) {
cmd := []string{"neo-go", "contract", "inspect"}
t.Run("missing input", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...)
})
t.Run("with raw '.go'", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--in", srcPath)...)

View file

@ -24,12 +24,14 @@ var generatorFlags = []cli.Flag{
Aliases: []string{"m"},
Required: true,
Usage: "Read contract manifest (*.manifest.json) file",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Required: true,
Usage: "Output of the compiled wrapper",
Action: cmdargs.EnsureNotEmpty("out"),
},
&cli.StringFlag{
Name: "hash",

View file

@ -1,4 +1,4 @@
package smartcontract
package smartcontract_test
import (
"bytes"
@ -6,14 +6,13 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
)
func TestGenerate(t *testing.T) {
@ -124,8 +123,7 @@ func TestGenerate(t *testing.T) {
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []*cli.Command{generateWrapperCmd}
e := testcli.NewExecutor(t, false)
rawCfg := `package: wrapper
hash: ` + h.StringLE() + `
@ -144,12 +142,12 @@ callflags:
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
require.NoError(t, app.Run([]string{"", "generate-wrapper",
e.Run(t, []string{"", "contract", "generate-wrapper",
"--manifest", manifestFile,
"--config", cfgPath,
"--out", outFile,
"--hash", h.StringLE(),
}))
}...)
const expected = `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
@ -234,11 +232,11 @@ func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
require.NoError(t, err)
require.Equal(t, expected, string(data))
require.NoError(t, app.Run([]string{"", "generate-wrapper",
e.Run(t, []string{"", "contract", "generate-wrapper",
"--manifest", manifestFile,
"--config", cfgPath,
"--out", outFile,
}))
}...)
expectedWithDynamicHash := `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package wrapper contains wrappers for MyContract contract.
@ -350,13 +348,12 @@ func TestGenerateValidPackageName(t *testing.T) {
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []*cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
require.NoError(t, app.Run([]string{"", "generate-wrapper",
e := testcli.NewExecutor(t, false)
e.Run(t, []string{"", "contract", "generate-wrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
}...)
data, err := os.ReadFile(outFile)
require.NoError(t, err)
@ -378,11 +375,11 @@ func Get() int {
return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int)
}
`, string(data))
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
e.Run(t, []string{"", "contract", "generate-rpcwrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
}...)
data, err = os.ReadFile(outFile)
require.NoError(t, err)
@ -431,17 +428,16 @@ const rewriteExpectedOutputs = false
func TestGenerateRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = []*cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
e := testcli.NewExecutor(t, false)
var checkBinding = func(manifest string, hash string, good string) {
t.Run(manifest, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
e.Run(t, []string{"", "contract", "generate-rpcwrapper",
"--manifest", manifest,
"--out", outFile,
"--hash", hash,
}))
}...)
data, err := os.ReadFile(outFile)
require.NoError(t, err)
@ -478,8 +474,7 @@ func TestGenerateRPCBindings(t *testing.T) {
func TestAssistedRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = NewCommands()
e := testcli.NewExecutor(t, false)
var checkBinding = func(source string, hasDefinedHash bool, guessEventTypes bool, suffix ...string) {
testName := source
@ -510,7 +505,7 @@ func TestAssistedRPCBindings(t *testing.T) {
if guessEventTypes {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
e.Run(t, cmd...)
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
@ -520,7 +515,7 @@ func TestAssistedRPCBindings(t *testing.T) {
if hasDefinedHash {
cmds = append(cmds, "--hash", "0x00112233445566778899aabbccddeeff00112233")
}
require.NoError(t, app.Run(cmds))
e.Run(t, cmds...)
data, err := os.ReadFile(outFile)
require.NoError(t, err)
@ -548,28 +543,22 @@ func TestAssistedRPCBindings(t *testing.T) {
}
func TestGenerate_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = []*cli.Command{generateWrapperCmd}
app.ExitErrHandler = func(*cli.Context, error) {}
e := testcli.NewExecutor(t, false)
args := []string{"neo-go", "contract", "generate-wrapper"}
checkError := func(t *testing.T, msg string, args ...string) {
// cli.ExitError doesn't implement wraping properly, so we check for an error message.
err := app.Run(append([]string{"", "generate-wrapper"}, args...))
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
}
t.Run("invalid hash", func(t *testing.T) {
checkError(t, "invalid contract hash", "--hash", "xxx", "--manifest", "yyy", "--out", "zzz")
e.RunWithErrorCheckExit(t, "invalid contract hash", append(args, "--hash", "xxx", "--manifest", "yyy", "--out", "zzz")...)
})
t.Run("missing manifest argument", func(t *testing.T) {
checkError(t, "Required flag \"manifest\" not set", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(args, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
t.Run("missing manifest file", func(t *testing.T) {
checkError(t, "can't read contract manifest", "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheckExit(t, "can't read contract manifest", append(args, "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
t.Run("empty manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
require.NoError(t, os.WriteFile(manifestFile, []byte("[]"), os.ModePerm))
checkError(t, "json: cannot unmarshal array into Go value of type manifest.Manifest", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheckExit(t, "json: cannot unmarshal array into Go value of type manifest.Manifest", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
t.Run("invalid manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
@ -577,7 +566,7 @@ func TestGenerate_Errors(t *testing.T) {
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
checkError(t, "ABI: no methods", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheckExit(t, "ABI: no methods", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
@ -593,9 +582,8 @@ func TestGenerate_Errors(t *testing.T) {
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
t.Run("missing config", func(t *testing.T) {
checkError(t, "can't read config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz")
e.RunWithErrorCheckExit(t, "can't read config file", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz")...)
})
t.Run("invalid config", func(t *testing.T) {
rawCfg := `package: wrapper
@ -605,23 +593,13 @@ callflags:
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
checkError(t, "can't parse config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", cfgPath, "--out", "zzz")
e.RunWithErrorCheckExit(t, "can't parse config file", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", cfgPath, "--out", "zzz")...)
})
}
func TestCompile_GuessEventTypes(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
checkError := func(t *testing.T, msg string, args ...string) {
// cli.ExitError doesn't implement wraping properly, so we check for an error message.
err := app.Run(args)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
}
e := testcli.NewExecutor(t, false)
check := func(t *testing.T, source string, expectedErrText string) {
tmpDir := t.TempDir()
configFile := filepath.Join(source, "invalid.yml")
@ -636,7 +614,7 @@ func TestCompile_GuessEventTypes(t *testing.T) {
"--out", nefF,
"--guess-eventtypes",
}
checkError(t, expectedErrText, cmd...)
e.RunWithErrorCheckExit(t, expectedErrText, cmd...)
}
t.Run("not declared in manifest", func(t *testing.T) {
@ -664,10 +642,7 @@ func TestCompile_GuessEventTypes(t *testing.T) {
}
func TestGenerateRPCBindings_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
e := testcli.NewExecutor(t, false)
t.Run("duplicating resulting fields", func(t *testing.T) {
check := func(t *testing.T, packageName string, autogen bool, expectedError string) {
tmpDir := t.TempDir()
@ -687,16 +662,14 @@ func TestGenerateRPCBindings_Errors(t *testing.T) {
if autogen {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
e.Run(t, cmd...)
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
"--manifest", manifestF,
"--out", out,
}
err := app.Run(cmds)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), expectedError), err.Error())
e.RunWithErrorCheckExit(t, expectedError, cmds...)
}
t.Run("event", func(t *testing.T) {

View file

@ -2,7 +2,6 @@ package smartcontract
import (
"encoding/json"
"errors"
"fmt"
"os"
@ -75,10 +74,6 @@ func manifestAddGroup(ctx *cli.Context) error {
}
func readNEFFile(filename string) (*nef.File, []byte, error) {
if len(filename) == 0 {
return nil, nil, errors.New("no nef file was provided")
}
f, err := os.ReadFile(filename)
if err != nil {
return nil, nil, err
@ -96,10 +91,6 @@ func readNEFFile(filename string) (*nef.File, []byte, error) {
// it for validness against the provided contract hash. If empty hash is specified
// then no hash-related manifest groups check is performed.
func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byte, error) {
if len(filename) == 0 {
return nil, nil, errNoManifestFile
}
manifestBytes, err := os.ReadFile(filename)
if err != nil {
return nil, nil, err

View file

@ -40,14 +40,11 @@ const (
)
var (
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag")
errNoMethod = errors.New("no method specified for function invocation command")
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
errFileExist = errors.New("A file with given smart-contract name already exists")
addressFlag = &flags.AddressFlag{
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
errNoMethod = errors.New("no method specified for function invocation command")
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
errFileExist = errors.New("A file with given smart-contract name already exists")
addressFlag = &flags.AddressFlag{
Name: addressFlagName,
Aliases: []string{addressFlagAlias},
Usage: "Address to use as transaction signee (and gas source)",
@ -82,9 +79,11 @@ func RuntimeNotify(args []any) {
func NewCommands() []*cli.Command {
testInvokeScriptFlags := []cli.Flag{
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Usage: "Input location of the .nef file that needs to be invoked",
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input location of the .nef file that needs to be invoked",
Action: cmdargs.EnsureNotEmpty("in"),
},
options.Historic,
}
@ -103,36 +102,47 @@ func NewCommands() []*cli.Command {
invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...)
deployFlags := append(invokeFunctionFlags, []cli.Flag{
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Usage: "Input file for the smart contract (*.nef)",
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input file for the smart contract (*.nef)",
Action: cmdargs.EnsureNotEmpty("in"),
},
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Usage: "Manifest input file (*.manifest.json)",
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Manifest input file (*.manifest.json)",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
}...)
manifestAddGroupFlags := append([]cli.Flag{
&cli.StringFlag{
Name: "sender",
Aliases: []string{"s"},
Usage: "Deploy transaction sender",
Name: "sender",
Aliases: []string{"s"},
Required: true,
Usage: "Deploy transaction sender",
Action: cmdargs.EnsureNotEmpty("sender"),
},
&flags.AddressFlag{
Name: addressFlagName, // use the same name for handler code unification.
Aliases: []string{addressFlagAlias},
Usage: "Account to sign group with",
Name: addressFlagName, // use the same name for handler code unification.
Aliases: []string{addressFlagAlias},
Required: true,
Usage: "Account to sign group with",
},
&cli.StringFlag{
Name: "nef",
Aliases: []string{"n"},
Usage: "Path to the NEF file",
Name: "nef",
Aliases: []string{"n"},
Required: true,
Usage: "Path to the NEF file",
Action: cmdargs.EnsureNotEmpty("nef"),
},
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Usage: "Path to the manifest",
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Path to the manifest",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
}, options.Wallet...)
return []*cli.Command{{
@ -154,9 +164,11 @@ func NewCommands() []*cli.Command {
Action: contractCompile,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Usage: "Input file for the smart contract to be compiled (*.go file or directory)",
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input file for the smart contract to be compiled (*.go file or directory)",
Action: cmdargs.EnsureNotEmpty("in"),
},
&cli.StringFlag{
Name: "out",
@ -273,9 +285,11 @@ func NewCommands() []*cli.Command {
Action: initSmartContract,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "Name of the smart-contract to be initialized",
Name: "name",
Aliases: []string{"n"},
Required: true,
Usage: "Name of the smart-contract to be initialized",
Action: cmdargs.EnsureNotEmpty("name"),
},
&cli.BoolFlag{
Name: "skip-details",
@ -296,9 +310,11 @@ func NewCommands() []*cli.Command {
Usage: "Compile input file (it should be go code then)",
},
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Usage: "Input file of the program (either .go or .nef)",
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input file of the program (either .go or .nef)",
Action: cmdargs.EnsureNotEmpty("in"),
},
},
},
@ -309,18 +325,23 @@ func NewCommands() []*cli.Command {
Action: calcHash,
Flags: []cli.Flag{
&flags.AddressFlag{
Name: "sender",
Aliases: []string{"s"},
Usage: "Sender script hash or address",
Name: "sender",
Aliases: []string{"s"},
Required: true,
Usage: "Sender script hash or address",
},
&cli.StringFlag{
Name: "in",
Usage: "Path to NEF file",
Name: "in",
Required: true,
Usage: "Path to NEF file",
Action: cmdargs.EnsureNotEmpty("in"),
},
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Usage: "Path to manifest file",
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Path to manifest file",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
},
},
@ -347,9 +368,6 @@ func initSmartContract(ctx *cli.Context) error {
return err
}
contractName := ctx.String("name")
if contractName == "" {
return cli.Exit(errNoSmartContractName, 1)
}
// Check if the file already exists, if yes, exit
if _, err := os.Stat(contractName); err == nil {
@ -424,9 +442,6 @@ func contractCompile(ctx *cli.Context) error {
return err
}
src := ctx.String("in")
if len(src) == 0 {
return cli.Exit(errNoInput, 1)
}
manifestFile := ctx.String("manifest")
confFile := ctx.String("config")
debugFile := ctx.String("debug")
@ -508,18 +523,9 @@ func calcHash(ctx *cli.Context) error {
return err
}
sender := ctx.Generic("sender").(*flags.Address)
if !sender.IsSet {
return cli.Exit("sender is not set", 1)
}
p := ctx.String("in")
if p == "" {
return cli.Exit(errors.New("no .nef file was provided"), 1)
}
mpath := ctx.String("manifest")
if mpath == "" {
return cli.Exit(errors.New("no manifest file provided"), 1)
}
f, err := os.ReadFile(p)
if err != nil {
return cli.Exit(fmt.Errorf("can't read .nef file: %w", err), 1)
@ -680,10 +686,6 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
func testInvokeScript(ctx *cli.Context) error {
src := ctx.String("in")
if len(src) == 0 {
return cli.Exit(errNoInput, 1)
}
b, err := os.ReadFile(src)
if err != nil {
return cli.Exit(err, 1)
@ -739,9 +741,6 @@ func inspect(ctx *cli.Context) error {
}
in := ctx.String("in")
compile := ctx.Bool("compile")
if len(in) == 0 {
return cli.Exit(errNoInput, 1)
}
var (
b []byte
err error

View file

@ -16,7 +16,11 @@ import (
// NewCommands returns util commands for neo-go CLI.
func NewCommands() []*cli.Command {
txDumpFlags := append([]cli.Flag{}, options.RPC...)
// By default, RPC flag is required. sendtx and txdump may be called without provided rpc-endpoint.
rpcFlagOriginal, _ := options.RPC[0].(*cli.StringFlag)
rpcFlag := *rpcFlagOriginal
rpcFlag.Required = false
txDumpFlags := append([]cli.Flag{&rpcFlag}, options.RPC[1:]...)
txSendFlags := append(txDumpFlags, txctx.AwaitFlag)
txCancelFlags := append([]cli.Flag{
&flags.AddressFlag{

View file

@ -39,7 +39,7 @@ func TestRegisterCandidate(t *testing.T) {
e.CheckEOF(t)
// missing address
e.RunWithError(t, "neo-go", "wallet", "candidate", "register",
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "candidate", "register",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet)
@ -131,7 +131,7 @@ func TestRegisterCandidate(t *testing.T) {
})
// missing address
e.RunWithError(t, "neo-go", "wallet", "candidate", "unregister",
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "candidate", "unregister",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet)
// additional argument
@ -153,7 +153,7 @@ func TestRegisterCandidate(t *testing.T) {
require.Equal(t, 0, len(vs))
// query voter: missing address
e.RunWithError(t, "neo-go", "query", "voter")
e.RunWithError(t, "neo-go", "query", "voter", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
// Excessive parameters.
e.RunWithError(t, "neo-go", "query", "voter", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], validatorAddress, validatorAddress)
e.RunWithError(t, "neo-go", "query", "committee", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "something")

View file

@ -79,10 +79,10 @@ func TestSignMultisigTx(t *testing.T) {
"--out", txPath)
// missing wallet
e.RunWithError(t, "neo-go", "wallet", "sign")
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "wallet", "sign")
// missing in
e.RunWithError(t, "neo-go", "wallet", "sign",
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "wallet", "sign",
"--wallet", wallet2Path)
// missing address

View file

@ -25,12 +25,14 @@ import (
func newNEP11Commands() []*cli.Command {
maxIters := strconv.Itoa(config.DefaultMaxIteratorResultItems)
tokenAddressFlag := &flags.AddressFlag{
Name: "token",
Usage: "Token contract address or hash in LE",
Name: "token",
Usage: "Token contract address or hash in LE",
Required: true,
}
ownerAddressFlag := &flags.AddressFlag{
Name: "address",
Usage: "NFT owner address or hash in LE",
Name: "address",
Usage: "NFT owner address or hash in LE",
Required: true,
}
tokenID := &cli.StringFlag{
Name: "id",
@ -71,7 +73,7 @@ func newNEP11Commands() []*cli.Command {
{
Name: "import",
Usage: "Import NEP-11 token to a wallet",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> --timeout <time> --token <hash>",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] --token <hash>",
Action: importNEP11Token,
Flags: importFlags,
},
@ -101,7 +103,7 @@ func newNEP11Commands() []*cli.Command {
{
Name: "transfer",
Usage: "Transfer NEP-11 tokens",
UsageText: "transfer -w wallet [--wallet-config path] --rpc-endpoint <node> --timeout <time> --from <addr> --to <addr> --token <hash-or-name> --id <token-id> [--amount string] [--await] [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
UsageText: "transfer -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] --from <addr> --to <addr> --token <hash-or-name> --id <token-id> [--amount string] [--await] [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Action: transferNEP11,
Flags: transferFlags,
Description: `Transfers specified NEP-11 token with optional cosigners list attached to
@ -246,10 +248,6 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
return err
}
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.Exit("token contract hash was not set", 1)
}
tokenID := ctx.String("id")
if tokenID == "" {
return cli.Exit(errors.New("token ID should be specified"), 1)
@ -291,15 +289,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
func printNEP11TokensOf(ctx *cli.Context) error {
var err error
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.Exit("token contract hash was not set", 1)
}
acc := ctx.Generic("address").(*flags.Address)
if !acc.IsSet {
return cli.Exit("owner address flag was not set", 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
@ -326,10 +316,6 @@ func printNEP11Tokens(ctx *cli.Context) error {
return err
}
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.Exit("token contract hash was not set", 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
@ -356,10 +342,6 @@ func printNEP11Properties(ctx *cli.Context) error {
return err
}
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.Exit("token contract hash was not set", 1)
}
tokenID := ctx.String("id")
if tokenID == "" {
return cli.Exit(errors.New("token ID should be specified"), 1)

View file

@ -57,8 +57,9 @@ var (
walletPathFlag,
walletConfigFlag,
&flags.AddressFlag{
Name: "token",
Usage: "Token contract address or hash in LE",
Name: "token",
Usage: "Token contract address or hash in LE",
Required: true,
},
}, options.RPC...)
baseTransferFlags = []cli.Flag{
@ -120,7 +121,7 @@ func newNEP17Commands() []*cli.Command {
{
Name: "import",
Usage: "Import NEP-17 token to a wallet",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> --timeout <time> --token <hash>",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] --token <hash>",
Action: importNEP17Token,
Flags: importFlags,
},
@ -150,7 +151,7 @@ func newNEP17Commands() []*cli.Command {
{
Name: "transfer",
Usage: "Transfer NEP-17 tokens",
UsageText: "transfer -w wallet [--wallet-config path] [--await] --rpc-endpoint <node> --timeout <time> --from <addr> --to <addr> --token <hash-or-name> --amount string [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
UsageText: "transfer -w wallet [--wallet-config path] [--await] --rpc-endpoint <node> [--timeout <time>] --from <addr> --to <addr> --token <hash-or-name> --amount string [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Action: transferNEP17,
Flags: transferFlags,
Description: `Transfers specified NEP-17 token amount with optional 'data' parameter and cosigners
@ -394,9 +395,6 @@ func importNEPToken(ctx *cli.Context, standard string) error {
defer wall.Close()
tokenHashFlag := ctx.Generic("token").(*flags.Address)
if !tokenHashFlag.IsSet {
return cli.Exit("token contract hash was not set", 1)
}
tokenHash := tokenHashFlag.Uint160()
for _, t := range wall.Extra.Tokens {
@ -648,9 +646,6 @@ func transferNEP(ctx *cli.Context, standard string) error {
}
toFlag := ctx.Generic("to").(*flags.Address)
if !toFlag.IsSet {
return cli.Exit(errors.New("missing receiver address (--to)"), 1)
}
to := toFlag.Uint160()
token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard)
if err != nil {

View file

@ -20,7 +20,7 @@ func newValidatorCommands() []*cli.Command {
{
Name: "register",
Usage: "Register as a new candidate",
UsageText: "register -w <path> -r <rpc> -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
UsageText: "register -w <path> -r <rpc> [-s timeout] -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
Action: handleRegister,
Flags: append([]cli.Flag{
walletPathFlag,
@ -31,16 +31,17 @@ func newValidatorCommands() []*cli.Command {
txctx.ForceFlag,
txctx.AwaitFlag,
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to register",
Name: "address",
Aliases: []string{"a"},
Required: true,
Usage: "Address to register",
},
}, options.RPC...),
},
{
Name: "unregister",
Usage: "Unregister self as a candidate",
UsageText: "unregister -w <path> -r <rpc> -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
UsageText: "unregister -w <path> -r <rpc> [-s timeout] -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
Action: handleUnregister,
Flags: append([]cli.Flag{
walletPathFlag,
@ -51,9 +52,10 @@ func newValidatorCommands() []*cli.Command {
txctx.ForceFlag,
txctx.AwaitFlag,
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to unregister",
Name: "address",
Required: true,
Aliases: []string{"a"},
Usage: "Address to unregister",
},
}, options.RPC...),
},
@ -75,9 +77,10 @@ func newValidatorCommands() []*cli.Command {
txctx.ForceFlag,
txctx.AwaitFlag,
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to vote from",
Name: "address",
Required: true,
Aliases: []string{"a"},
Usage: "Address to vote from",
},
&cli.StringFlag{
Name: "candidate",
@ -112,9 +115,6 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address)
if !addrFlag.IsSet {
return cli.Exit("address was not provided", 1)
}
addr := addrFlag.Uint160()
acc, err := options.GetUnlockedAccount(wall, addr, pass)
if err != nil {

View file

@ -66,16 +66,19 @@ var (
Usage: "Decrypt encrypted keys.",
}
inFlag = &cli.StringFlag{
Name: "in",
Usage: "File with JSON transaction",
Name: "in",
Required: true,
Usage: "File with JSON transaction",
Action: cmdargs.EnsureNotEmpty("in"),
}
fromAddrFlag = &flags.AddressFlag{
Name: "from",
Usage: "Address to send an asset from",
}
toAddrFlag = &flags.AddressFlag{
Name: "to",
Usage: "Address to send an asset to",
Name: "to",
Usage: "Address to send an asset to",
Required: true,
}
)
@ -90,9 +93,10 @@ func NewCommands() []*cli.Command {
txctx.ForceFlag,
txctx.AwaitFlag,
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to claim GAS for",
Name: "address",
Aliases: []string{"a"},
Required: true,
Usage: "Address to claim GAS for",
},
}
claimFlags = append(claimFlags, options.RPC...)
@ -108,7 +112,12 @@ func NewCommands() []*cli.Command {
Usage: "Address to use",
},
}
signFlags = append(signFlags, options.RPC...)
// By default, RPC flag is required. signtx may be called without provided rpc-endpoint.
rpcFlagOriginal, _ := options.RPC[0].(*cli.StringFlag)
rpcFlag := *rpcFlagOriginal
rpcFlag.Required = false
signFlags = append(signFlags, &rpcFlag)
signFlags = append(signFlags, options.RPC[1:]...)
return []*cli.Command{{
Name: "wallet",
Usage: "Create, open and manage a Neo wallet",
@ -138,7 +147,7 @@ func NewCommands() []*cli.Command {
{
Name: "change-password",
Usage: "Change password for accounts",
UsageText: "neo-go wallet change-password -w wallet -a address",
UsageText: "neo-go wallet change-password -w wallet [-a address]",
Action: changePassword,
Flags: []cli.Flag{
walletPathFlag,
@ -158,9 +167,11 @@ func NewCommands() []*cli.Command {
walletPathFlag,
walletConfigFlag,
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Usage: "Where to write converted wallet",
Name: "out",
Aliases: []string{"o"},
Required: true,
Usage: "Where to write converted wallet",
Action: cmdargs.EnsureNotEmpty("out"),
},
},
},
@ -274,7 +285,7 @@ func NewCommands() []*cli.Command {
{
Name: "import-deployed",
Usage: "Import deployed contract",
UsageText: "import-deployed -w wallet [--wallet-config path] --wif <wif> --contract <hash> [--name <account_name>]",
UsageText: "import-deployed -w wallet [--wallet-config path] --wif <wif> --contract <hash> --rpc-endpoint <endpoint> [-s <timeout>] [--name <account_name>]",
Action: importDeployed,
Flags: append([]cli.Flag{
walletPathFlag,
@ -286,9 +297,10 @@ func NewCommands() []*cli.Command {
Usage: "Optional account name",
},
&flags.AddressFlag{
Name: "contract",
Aliases: []string{"c"},
Usage: "Contract hash or address",
Name: "contract",
Aliases: []string{"c"},
Required: true,
Usage: "Contract hash or address",
},
}, options.RPC...),
},
@ -302,9 +314,10 @@ func NewCommands() []*cli.Command {
walletConfigFlag,
txctx.ForceFlag,
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Account address or hash in LE form to be removed",
Name: "address",
Aliases: []string{"a"},
Required: true,
Usage: "Account address or hash in LE form to be removed",
},
},
},
@ -432,9 +445,6 @@ func convertWallet(ctx *cli.Context) error {
}
out := ctx.String("out")
if len(out) == 0 {
return cli.Exit("missing out path", 1)
}
newWallet, err := wallet.NewWallet(out)
if err != nil {
return cli.Exit(err, 1)
@ -634,9 +644,6 @@ func importDeployed(ctx *cli.Context) error {
defer wall.Close()
rawHash := ctx.Generic("contract").(*flags.Address)
if !rawHash.IsSet {
return cli.Exit("contract hash was not provided", 1)
}
var label *string
if ctx.IsSet("name") {
@ -730,9 +737,6 @@ func removeAccount(ctx *cli.Context) error {
defer wall.Close()
addr := ctx.Generic("address").(*flags.Address)
if !addr.IsSet {
return cli.Exit("valid account address must be provided", 1)
}
acc := wall.GetAccount(addr.Uint160())
if acc == nil {
return cli.Exit("account wasn't found", 1)

View file

@ -40,10 +40,10 @@ func TestWalletAccountRemove(t *testing.T) {
require.NoError(t, err)
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "remove")
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "remove")
})
t.Run("missing address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "remove", "--wallet", walletPath)
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "remove", "--wallet", walletPath)
})
t.Run("invalid address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "remove", "--wallet", walletPath,
@ -109,7 +109,7 @@ func TestWalletChangePassword(t *testing.T) {
e.In.WriteString("pass\r")
e.In.WriteString("pass1\r")
e.In.WriteString("pass2\r")
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath, "--address", addr1)
})
t.Run("good, multiaccount", func(t *testing.T) {
e.In.WriteString("pass\r")
@ -593,7 +593,7 @@ func TestWalletClaimGas(t *testing.T) {
"--address", testcli.TestWalletAccount)
})
t.Run("missing address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "claim",
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "claim",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.TestWalletPath)
})
@ -605,7 +605,7 @@ func TestWalletClaimGas(t *testing.T) {
})
t.Run("missing endpoint", func(t *testing.T) {
e.In.WriteString("testpass\r")
e.RunWithError(t, "neo-go", "wallet", "claim",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "wallet", "claim",
"--wallet", testcli.TestWalletPath,
"--address", testcli.TestWalletAccount)
})
@ -711,19 +711,19 @@ func TestWalletImportDeployed(t *testing.T) {
require.NoError(t, err)
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "import-deployed")
e.RunWithErrorCheck(t, `Required flag "contract" not set`, "neo-go", "wallet", "import-deployed", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("missing contract sh", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath)
e.RunWithErrorCheck(t, `Required flag "contract" not set`, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath, "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("missing WIF", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath, "--contract", h.StringLE())
"--wallet", walletPath, "--contract", h.StringLE(), "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("missing endpoint", func(t *testing.T) {
e.In.WriteString("acc\rpass\rpass\r")
e.RunWithError(t, "neo-go", "wallet", "import-deployed",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath, "--contract", h.StringLE(),
"--wif", priv.WIF())
})
@ -1107,11 +1107,11 @@ func TestWalletConvert(t *testing.T) {
outPath := filepath.Join(tmpDir, "wallet.json")
cmd := []string{"neo-go", "wallet", "convert"}
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "out" not set`, cmd...)
})
cmd = append(cmd, "--wallet", "testdata/testwallet_NEO2.json")
t.Run("missing out path", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "out" not set`, cmd...)
})
t.Run("invalid out path", func(t *testing.T) {
dir := t.TempDir()

View file

@ -273,13 +273,26 @@ func checkExit(t *testing.T, ch <-chan int, code int) {
}
}
// RunWithError runs command and checks that is exits with error.
// RunWithError runs command and checks that is exits with error and exit code 1.
func (e *Executor) RunWithError(t *testing.T, args ...string) {
ch := setExitFunc()
require.Error(t, e.run(args...))
checkExit(t, ch, 1)
}
// RunWithErrorCheckExit runs command and checks that is exits with error and exit code 1.
func (e *Executor) RunWithErrorCheckExit(t *testing.T, msg string, args ...string) {
ch := setExitFunc()
require.ErrorContains(t, e.run(args...), msg)
checkExit(t, ch, 1)
}
// RunWithErrorCheck runs command and checks that there were errors with the specified message. Exit code is not checked.
func (e *Executor) RunWithErrorCheck(t *testing.T, msg string, args ...string) {
err := e.run(args...)
require.ErrorContains(t, err, msg)
}
// Run runs command and checks that there were no errors.
func (e *Executor) Run(t *testing.T, args ...string) {
ch := setExitFunc()