cli: upgrade urfave lib to v2

Close #3097

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
Ekaterina Pavlova 2024-07-09 21:24:39 +03:00
parent b32e568d21
commit acde7bd0de
39 changed files with 980 additions and 693 deletions

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/cli/vm" "github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/cli/wallet" "github.com/nspcc-dev/neo-go/cli/wallet"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func versionPrinter(c *cli.Context) { func versionPrinter(c *cli.Context) {

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
const ( const (
@ -138,16 +138,16 @@ const (
// GetSignersFromContext returns signers parsed from context args starting // GetSignersFromContext returns signers parsed from context args starting
// from the specified offset. // from the specified offset.
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) { func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, cli.ExitCoder) {
args := ctx.Args() args := ctx.Args()
var ( var (
signers []transaction.Signer signers []transaction.Signer
err error err error
) )
if args.Present() && len(args) > offset { if args.Present() && args.Len() > offset {
signers, err = ParseSigners(args[offset:]) signers, err = ParseSigners(args.Slice()[offset:])
if err != nil { if err != nil {
return nil, cli.NewExitError(err, 1) return nil, cli.Exit(err, 1)
} }
} }
return signers, nil return signers, nil
@ -230,7 +230,7 @@ func parseCosigner(c string) (transaction.Signer, error) {
} }
// GetDataFromContext returns data parameter from context args. // GetDataFromContext returns data parameter from context args.
func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) { func GetDataFromContext(ctx *cli.Context) (int, any, cli.ExitCoder) {
var ( var (
data any data any
offset int offset int
@ -239,17 +239,17 @@ func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
) )
args := ctx.Args() args := ctx.Args()
if args.Present() { if args.Present() {
offset, params, err = ParseParams(args, true) offset, params, err = ParseParams(args.Slice(), true)
if err != nil { if err != nil {
return offset, nil, cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1) return offset, nil, cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
} }
if len(params) > 1 { if len(params) > 1 {
return offset, nil, cli.NewExitError("'data' should be represented as a single parameter", 1) return offset, nil, cli.Exit("'data' should be represented as a single parameter", 1)
} }
if len(params) != 0 { if len(params) != 0 {
data, err = smartcontract.ExpandParameterToEmitable(params[0]) data, err = smartcontract.ExpandParameterToEmitable(params[0])
if err != nil { if err != nil {
return offset, nil, cli.NewExitError(fmt.Sprintf("failed to convert 'data' to emitable type: %s", err.Error()), 1) return offset, nil, cli.Exit(fmt.Sprintf("failed to convert 'data' to emitable type: %s", err.Error()), 1)
} }
} }
} }
@ -258,9 +258,9 @@ func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
// EnsureNone returns an error if there are any positional arguments present. // EnsureNone returns an error if there are any positional arguments present.
// It can be used to check for them in commands that don't accept arguments. // It can be used to check for them in commands that don't accept arguments.
func EnsureNone(ctx *cli.Context) *cli.ExitError { func EnsureNone(ctx *cli.Context) cli.ExitCoder {
if ctx.Args().Present() { if ctx.Args().Present() {
return cli.NewExitError("additional arguments given while this command expects none", 1) return cli.Exit(fmt.Errorf("additional arguments given while this command expects none"), 1)
} }
return nil return nil
} }

View file

@ -7,7 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// Address is a wrapper for a Uint160 with flag.Value methods. // Address is a wrapper for a Uint160 with flag.Value methods.
@ -16,11 +16,15 @@ type Address struct {
Value util.Uint160 Value util.Uint160
} }
// AddressFlag is a flag with type string. // AddressFlag is a flag with type Uint160.
type AddressFlag struct { type AddressFlag struct {
Name string Name string
Usage string Usage string
Value Address Value Address
Aliases []string
Required bool
Hidden bool
Action func(*cli.Context, string) error
} }
var ( var (
@ -37,7 +41,7 @@ func (a Address) String() string {
func (a *Address) Set(s string) error { func (a *Address) Set(s string) error {
addr, err := ParseAddress(s) addr, err := ParseAddress(s)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
a.IsSet = true a.IsSet = true
a.Value = addr a.Value = addr
@ -63,9 +67,9 @@ func (f AddressFlag) IsSet() bool {
// (for usage defaults). // (for usage defaults).
func (f AddressFlag) String() string { func (f AddressFlag) String() string {
var names []string var names []string
eachName(f.Name, func(name string) { for _, name := range f.Names() {
names = append(names, getNameHelp(name)) names = append(names, getNameHelp(name))
}) }
return strings.Join(names, ", ") + "\t" + f.Usage return strings.Join(names, ", ") + "\t" + f.Usage
} }
@ -77,17 +81,57 @@ func getNameHelp(name string) string {
return fmt.Sprintf("--%s value", name) return fmt.Sprintf("--%s value", name)
} }
// GetName returns the name of the flag. // Names returns the names of the flag.
func (f AddressFlag) GetName() string { func (f AddressFlag) Names() []string {
return f.Name return cli.FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether the flag is required.
func (f AddressFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false.
func (f AddressFlag) IsVisible() bool {
return !f.Hidden
}
// TakesValue returns true of the flag takes a value, otherwise false.
func (f AddressFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag.
func (f AddressFlag) GetUsage() string {
return f.Usage
} }
// Apply populates the flag given the flag set and environment. // Apply populates the flag given the flag set and environment.
// Ignores errors. // Ignores errors.
func (f AddressFlag) Apply(set *flag.FlagSet) { func (f AddressFlag) Apply(set *flag.FlagSet) error {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
set.Var(&f.Value, name, f.Usage) set.Var(&f.Value, name, f.Usage)
}) }
return nil
}
// RunAction executes flag action if set.
func (f AddressFlag) RunAction(c *cli.Context) error {
if f.Action != nil {
return f.Action(c, address.Uint160ToString(f.Value.Value))
}
return nil
}
// GetValue returns the flags value as string representation.
func (f AddressFlag) GetValue() string {
return address.Uint160ToString(f.Value.Value)
}
// Get returns the flags value in the given Context.
func (f AddressFlag) Get(ctx *cli.Context) Address {
adr := ctx.Generic(f.Name).(*Address)
return *adr
} }
// ParseAddress parses a Uint160 from either an LE string or an address. // ParseAddress parses a Uint160 from either an LE string or an address.

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
) )
func TestParseAddress(t *testing.T) { func TestParseAddress(t *testing.T) {
@ -109,22 +110,102 @@ func TestAddress_getNameHelp(t *testing.T) {
require.Equal(t, "--flag value", getNameHelp("flag")) require.Equal(t, "--flag value", getNameHelp("flag"))
} }
func TestAddressFlag_GetName(t *testing.T) { func TestAddressFlag_Names(t *testing.T) {
flag := AddressFlag{ flag := AddressFlag{
Name: "my flag", Name: "flag",
Aliases: []string{"my"},
} }
require.Equal(t, "my flag", flag.GetName()) require.Equal(t, []string{"flag", "my"}, flag.Names())
} }
func TestAddress(t *testing.T) { func TestAddress(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError) f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(io.Discard) // don't pollute test output f.SetOutput(io.Discard) // don't pollute test output
addr := AddressFlag{Name: "addr, a"} addr := AddressFlag{Name: "addr", Aliases: []string{"a"}}
addr.Apply(f) err := addr.Apply(f)
require.NoError(t, err)
require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"})) require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String()) require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String())
require.NoError(t, f.Parse([]string{"-a", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"})) require.NoError(t, f.Parse([]string{"-a", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String()) require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String())
require.Error(t, f.Parse([]string{"--addr", "kek"})) require.Error(t, f.Parse([]string{"--addr", "kek"}))
} }
func TestAddressFlag_IsRequired(t *testing.T) {
flag := AddressFlag{Required: true}
require.True(t, flag.IsRequired())
flag.Required = false
require.False(t, flag.IsRequired())
}
func TestAddressFlag_IsVisible(t *testing.T) {
flag := AddressFlag{Hidden: false}
require.True(t, flag.IsVisible())
flag.Hidden = true
require.False(t, flag.IsVisible())
}
func TestAddressFlag_TakesValue(t *testing.T) {
flag := AddressFlag{}
require.True(t, flag.TakesValue())
}
func TestAddressFlag_GetUsage(t *testing.T) {
flag := AddressFlag{Usage: "Specify the address"}
require.Equal(t, "Specify the address", flag.GetUsage())
}
func TestAddressFlag_GetValue(t *testing.T) {
addrValue := util.Uint160{1, 2, 3}
flag := AddressFlag{Value: Address{IsSet: true, Value: addrValue}}
expectedStr := address.Uint160ToString(addrValue)
require.Equal(t, expectedStr, flag.GetValue())
}
func TestAddressFlag_Get(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
flag := AddressFlag{
Name: "testAddress",
Value: Address{Value: util.Uint160{1, 2, 3}, IsSet: false},
}
set.Var(&flag.Value, "testAddress", "test usage")
require.NoError(t, set.Set("testAddress", address.Uint160ToString(util.Uint160{3, 2, 1})))
expected := flag.Get(ctx)
require.True(t, expected.IsSet)
require.Equal(t, util.Uint160{3, 2, 1}, expected.Value)
}
func TestAddressFlag_RunAction(t *testing.T) {
called := false
action := func(ctx *cli.Context, s string) error {
called = true
require.Equal(t, address.Uint160ToString(util.Uint160{1, 2, 3}), s)
return nil
}
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
flag := AddressFlag{
Action: action,
Value: Address{IsSet: true, Value: util.Uint160{4, 5, 6}},
}
expected := address.Uint160ToString(util.Uint160{1, 2, 3})
set.Var(&flag.Value, "testAddress", "test usage")
require.NoError(t, set.Set("testAddress", expected))
require.Equal(t, expected, flag.GetValue())
err := flag.RunAction(ctx)
require.NoError(t, err)
require.True(t, called)
}

View file

@ -5,7 +5,7 @@ import (
"strings" "strings"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// Fixed8 is a wrapper for a Uint160 with flag.Value methods. // Fixed8 is a wrapper for a Uint160 with flag.Value methods.
@ -18,6 +18,10 @@ type Fixed8Flag struct {
Name string Name string
Usage string Usage string
Value Fixed8 Value Fixed8
Aliases []string
Required bool
Hidden bool
Action func(*cli.Context, string) error
} }
var ( var (
@ -34,7 +38,7 @@ func (a Fixed8) String() string {
func (a *Fixed8) Set(s string) error { func (a *Fixed8) Set(s string) error {
f, err := fixedn.Fixed8FromString(s) f, err := fixedn.Fixed8FromString(s)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
a.Value = f a.Value = f
return nil return nil
@ -45,31 +49,75 @@ func (a *Fixed8) Fixed8() fixedn.Fixed8 {
return a.Value return a.Value
} }
// IsSet checks if flag was set to a non-default value.
func (f Fixed8Flag) IsSet() bool {
return f.Value.Value != 0
}
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults). // (for usage defaults).
func (f Fixed8Flag) String() string { func (f Fixed8Flag) String() string {
var names []string var names []string
eachName(f.Name, func(name string) { for _, name := range f.Names() {
names = append(names, getNameHelp(name)) names = append(names, getNameHelp(name))
}) }
return strings.Join(names, ", ") + "\t" + f.Usage return strings.Join(names, ", ") + "\t" + f.Usage
} }
// GetName returns the name of the flag. // Names returns the names of the flag.
func (f Fixed8Flag) GetName() string { func (f Fixed8Flag) Names() []string {
return f.Name return cli.FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether the flag is required.
func (f Fixed8Flag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false.
func (f Fixed8Flag) IsVisible() bool {
return !f.Hidden
}
// TakesValue returns true if the flag takes a value, otherwise false.
func (f Fixed8Flag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag.
func (f Fixed8Flag) GetUsage() string {
return f.Usage
} }
// Apply populates the flag given the flag set and environment. // Apply populates the flag given the flag set and environment.
// Ignores errors. // Ignores errors.
func (f Fixed8Flag) Apply(set *flag.FlagSet) { func (f Fixed8Flag) Apply(set *flag.FlagSet) error {
eachName(f.Name, func(name string) { for _, name := range f.Names() {
set.Var(&f.Value, name, f.Usage) set.Var(&f.Value, name, f.Usage)
}) }
return nil
} }
// Fixed8FromContext returns a parsed util.Fixed8 value provided flag name. // Fixed8FromContext returns a parsed util.Fixed8 value provided flag name.
func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 { func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 {
return ctx.Generic(name).(*Fixed8).Value return ctx.Generic(name).(*Fixed8).Value
} }
// RunAction executes flag action if set.
func (f Fixed8Flag) RunAction(c *cli.Context) error {
if f.Action != nil {
return f.Action(c, f.Value.Value.String())
}
return nil
}
// GetValue returns the flags value as string representation.
func (f Fixed8Flag) GetValue() string {
return f.Value.Value.String()
}
// Get returns the flags value in the given Context.
func (f Fixed8Flag) Get(ctx *cli.Context) Fixed8 {
adr := ctx.Generic(f.Name).(*Fixed8)
return *adr
}

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
) )
func TestFixed8_String(t *testing.T) { func TestFixed8_String(t *testing.T) {
@ -45,22 +46,83 @@ func TestFixed8Flag_String(t *testing.T) {
require.Equal(t, "--myFlag value\tGas amount", flag.String()) require.Equal(t, "--myFlag value\tGas amount", flag.String())
} }
func TestFixed8Flag_GetName(t *testing.T) { func TestFixed8Flag_Names(t *testing.T) {
flag := Fixed8Flag{ flag := Fixed8Flag{
Name: "myFlag", Name: "myFlag",
} }
require.Equal(t, "myFlag", flag.GetName()) require.Equal(t, []string{"myFlag"}, flag.Names())
} }
func TestFixed8(t *testing.T) { func TestFixed8(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError) f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(io.Discard) // don't pollute test output f.SetOutput(io.Discard) // don't pollute test output
gas := Fixed8Flag{Name: "gas, g"} gas := Fixed8Flag{Name: "gas", Aliases: []string{"g"}, Usage: "Gas amount", Value: Fixed8{Value: 0}, Required: true, Hidden: false, Action: nil}
gas.Apply(f) err := gas.Apply(f)
require.NoError(t, err)
require.NoError(t, f.Parse([]string{"--gas", "0.123"})) require.NoError(t, f.Parse([]string{"--gas", "0.123"}))
require.Equal(t, "0.123", f.Lookup("g").Value.String()) require.Equal(t, "0.123", f.Lookup("g").Value.String())
require.NoError(t, f.Parse([]string{"-g", "0.456"})) require.NoError(t, f.Parse([]string{"-g", "0.456"}))
require.Equal(t, "0.456", f.Lookup("g").Value.String()) require.Equal(t, "0.456", f.Lookup("g").Value.String())
require.Error(t, f.Parse([]string{"--gas", "kek"})) require.Error(t, f.Parse([]string{"--gas", "kek"}))
} }
func TestFixed8Flag_Get(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
flag := Fixed8Flag{
Name: "testFlag",
}
fixedFlag := Fixed8{Value: fixedn.Fixed8(123)}
set.Var(&fixedFlag, "testFlag", "test usage")
require.NoError(t, set.Set("testFlag", "0.00000321"))
expected := flag.Get(ctx)
require.Equal(t, fixedn.Fixed8(321), expected.Value)
}
func TestFixed8Flag_GetValue(t *testing.T) {
f := Fixed8Flag{Value: Fixed8{Value: fixedn.Fixed8(123)}}
require.Equal(t, "0.00000123", f.GetValue())
require.True(t, f.TakesValue())
}
func TestFixed8Flag_RunAction(t *testing.T) {
called := false
action := func(ctx *cli.Context, s string) error {
called = true
require.Equal(t, "0.00000123", s)
return nil
}
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
f := Fixed8Flag{
Action: action,
Value: Fixed8{Value: fixedn.Fixed8(123)},
}
err := f.RunAction(ctx)
require.NoError(t, err)
require.True(t, called)
}
func TestFixed8Flag_GetUsage(t *testing.T) {
f := Fixed8Flag{Usage: "Use this flag to specify gas amount"}
require.Equal(t, "Use this flag to specify gas amount", f.GetUsage())
}
func TestFixed8Flag_IsVisible(t *testing.T) {
f := Fixed8Flag{Hidden: false}
require.True(t, f.IsVisible())
f.Hidden = true
require.False(t, f.IsVisible())
}
func TestFixed8Flag_IsRequired(t *testing.T) {
f := Fixed8Flag{Required: false}
require.False(t, f.IsRequired())
f.Required = true
require.True(t, f.IsRequired())
}

View file

@ -1,11 +0,0 @@
package flags
import "strings"
func eachName(longName string, fn func(string)) {
parts := strings.Split(longName, ",")
for _, name := range parts {
name = strings.Trim(name, " ")
fn(name)
}
}

View file

@ -1,17 +0,0 @@
package flags
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEachName(t *testing.T) {
expected := "*one*two*three"
actual := ""
eachName(" one,two ,three", func(s string) {
actual += "*" + s
})
require.Equal(t, expected, actual)
}

View file

@ -21,14 +21,13 @@ func TestNEP17Balance(t *testing.T) {
e := testcli.NewExecutor(t, true) e := testcli.NewExecutor(t, true)
args := []string{ args := []string{
"neo-go", "wallet", "nep17", "multitransfer", "neo-go", "wallet", "nep17", "multitransfer", "--force",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0], "--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--wallet", testcli.ValidatorWallet,
"--from", testcli.ValidatorAddr, "--from", testcli.ValidatorAddr,
"GAS:" + testcli.TestWalletMultiAccount1 + ":1", "GAS:" + testcli.TestWalletMultiAccount1 + ":1",
"NEO:" + testcli.TestWalletMultiAccount1 + ":10", "NEO:" + testcli.TestWalletMultiAccount1 + ":10",
"GAS:" + testcli.TestWalletMultiAccount3 + ":3", "GAS:" + testcli.TestWalletMultiAccount3 + ":3",
"--force",
} }
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, args...) e.Run(t, args...)

View file

@ -8,7 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/internal/testcli" "github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func TestGetRPCClient(t *testing.T) { func TestGetRPCClient(t *testing.T) {

View file

@ -26,7 +26,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"golang.org/x/term" "golang.org/x/term"
@ -47,65 +47,87 @@ const (
const RPCEndpointFlag = "rpc-endpoint" const RPCEndpointFlag = "rpc-endpoint"
// Wallet is a set of flags used for wallet operations. // Wallet is a set of flags used for wallet operations.
var Wallet = []cli.Flag{cli.StringFlag{ var Wallet = []cli.Flag{
Name: "wallet, w", &cli.StringFlag{
Name: "wallet",
Aliases: []string{"w"},
Usage: "Wallet to use to get the key for transaction signing; conflicts with --wallet-config flag", Usage: "Wallet to use to get the key for transaction signing; conflicts with --wallet-config flag",
}, cli.StringFlag{ },
&cli.StringFlag{
Name: "wallet-config", Name: "wallet-config",
Usage: "Path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag"}, Usage: "Path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag",
},
} }
// Network is a set of flags for choosing the network to operate on // Network is a set of flags for choosing the network to operate on
// (privnet/mainnet/testnet). // (privnet/mainnet/testnet).
var Network = []cli.Flag{ var Network = []cli.Flag{
cli.BoolFlag{Name: "privnet, p", Usage: "Use private network configuration (if --config-file option is not specified)"}, &cli.BoolFlag{
cli.BoolFlag{Name: "mainnet, m", Usage: "Use mainnet network configuration (if --config-file option is not specified)"}, Name: "privnet",
cli.BoolFlag{Name: "testnet, t", Usage: "Use testnet network configuration (if --config-file option is not specified)"}, Aliases: []string{"p"},
cli.BoolFlag{Name: "unittest", Hidden: true}, Usage: "Use private network configuration (if --config-file option is not specified)",
},
&cli.BoolFlag{
Name: "mainnet",
Aliases: []string{"m"},
Usage: "Use mainnet network configuration (if --config-file option is not specified)",
},
&cli.BoolFlag{
Name: "testnet",
Aliases: []string{"t"},
Usage: "Use testnet network configuration (if --config-file option is not specified)",
},
&cli.BoolFlag{
Name: "unittest",
Hidden: true,
},
} }
// RPC is a set of flags used for RPC connections (endpoint and timeout). // RPC is a set of flags used for RPC connections (endpoint and timeout).
var RPC = []cli.Flag{ var RPC = []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: RPCEndpointFlag + ", r", Name: RPCEndpointFlag,
Aliases: []string{"r"},
Usage: "RPC node address", Usage: "RPC node address",
}, },
cli.DurationFlag{ &cli.DurationFlag{
Name: "timeout, s", Name: "timeout",
Aliases: []string{"s"},
Value: DefaultTimeout, Value: DefaultTimeout,
Usage: "Timeout for the operation", Usage: "Timeout for the operation",
}, },
} }
// Historic is a flag for commands that can perform historic invocations. // Historic is a flag for commands that can perform historic invocations.
var Historic = cli.StringFlag{ var Historic = &cli.StringFlag{
Name: "historic", Name: "historic",
Usage: "Use historic state (height, block hash or state root hash)", Usage: "Use historic state (height, block hash or state root hash)",
} }
// Config is a flag for commands that use node configuration. // Config is a flag for commands that use node configuration.
var Config = cli.StringFlag{ var Config = &cli.StringFlag{
Name: "config-path", Name: "config-path",
Usage: "Path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)", Usage: "Path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)",
} }
// ConfigFile is a flag for commands that use node configuration and provide // ConfigFile is a flag for commands that use node configuration and provide
// path to the specific config file instead of config path. // path to the specific config file instead of config path.
var ConfigFile = cli.StringFlag{ var ConfigFile = &cli.StringFlag{
Name: "config-file", Name: "config-file",
Usage: "Path to the node configuration file (overrides --config-path option)", Usage: "Path to the node configuration file (overrides --config-path option)",
} }
// RelativePath is a flag for commands that use node configuration and provide // RelativePath is a flag for commands that use node configuration and provide
// a prefix to all relative paths in config files. // a prefix to all relative paths in config files.
var RelativePath = cli.StringFlag{ var RelativePath = &cli.StringFlag{
Name: "relative-path", Name: "relative-path",
Usage: "Prefix to all relative paths in the node configuration file", Usage: "Prefix to all relative paths in the node configuration file",
} }
// Debug is a flag for commands that allow node in debug mode usage. // Debug is a flag for commands that allow node in debug mode usage.
var Debug = cli.BoolFlag{ var Debug = &cli.BoolFlag{
Name: "debug, d", Name: "debug",
Aliases: []string{"d"},
Usage: "Enable debug logging (LOTS of output, overrides configuration)", Usage: "Enable debug logging (LOTS of output, overrides configuration)",
} }
@ -146,15 +168,15 @@ func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) {
func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) { func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) {
endpoint := ctx.String(RPCEndpointFlag) endpoint := ctx.String(RPCEndpointFlag)
if len(endpoint) == 0 { if len(endpoint) == 0 {
return nil, cli.NewExitError(errNoEndpoint, 1) return nil, cli.Exit(errNoEndpoint, 1)
} }
c, err := rpcclient.New(gctx, endpoint, rpcclient.Options{}) c, err := rpcclient.New(gctx, endpoint, rpcclient.Options{})
if err != nil { if err != nil {
return nil, cli.NewExitError(err, 1) return nil, cli.Exit(err, 1)
} }
err = c.Init() err = c.Init()
if err != nil { if err != nil {
return nil, cli.NewExitError(err, 1) return nil, cli.Exit(err, 1)
} }
return c, nil return c, nil
} }
@ -173,7 +195,7 @@ func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Sig
// Might as well be a block hash, but it makes no practical difference. // Might as well be a block hash, but it makes no practical difference.
return invoker.NewHistoricWithState(u256, c, signers), nil return invoker.NewHistoricWithState(u256, c, signers), nil
} }
return nil, cli.NewExitError(errInvalidHistoric, 1) return nil, cli.Exit(errInvalidHistoric, 1)
} }
// GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's // GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's
@ -314,7 +336,7 @@ func GetRPCWithActor(gctx context.Context, ctx *cli.Context, signers []actor.Sig
a, actorErr := actor.New(c, signers) a, actorErr := actor.New(c, signers)
if actorErr != nil { if actorErr != nil {
c.Close() c.Close()
return nil, nil, cli.NewExitError(fmt.Errorf("failed to create Actor: %w", actorErr), 1) return nil, nil, cli.Exit(fmt.Errorf("failed to create Actor: %w", actorErr), 1)
} }
return c, a, nil return c, a, nil
} }

View file

@ -8,7 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func TestGetNetwork(t *testing.T) { func TestGetNetwork(t *testing.T) {

View file

@ -22,21 +22,22 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// NewCommands returns 'query' command. // NewCommands returns 'query' command.
func NewCommands() []cli.Command { func NewCommands() []*cli.Command {
queryTxFlags := append([]cli.Flag{ queryTxFlags := append([]cli.Flag{
cli.BoolFlag{ &cli.BoolFlag{
Name: "verbose, v", Name: "verbose",
Aliases: []string{"v"},
Usage: "Output full tx info and execution logs", Usage: "Output full tx info and execution logs",
}, },
}, options.RPC...) }, options.RPC...)
return []cli.Command{{ return []*cli.Command{{
Name: "query", Name: "query",
Usage: "Query data from RPC node", Usage: "Query data from RPC node",
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "candidates", Name: "candidates",
Usage: "Get candidates and votes", Usage: "Get candidates and votes",
@ -61,14 +62,14 @@ func NewCommands() []cli.Command {
{ {
Name: "tx", Name: "tx",
Usage: "Query transaction status", Usage: "Query transaction status",
UsageText: "neo-go query tx <hash> -r endpoint [-s timeout] [-v]", UsageText: "neo-go query tx -r endpoint [-s timeout] [-v] <hash>",
Action: queryTx, Action: queryTx,
Flags: queryTxFlags, Flags: queryTxFlags,
}, },
{ {
Name: "voter", Name: "voter",
Usage: "Print NEO holder account state", Usage: "Print NEO holder account state",
UsageText: "neo-go query voter <address> -r endpoint [-s timeout]", UsageText: "neo-go query voter -r endpoint [-s timeout] <address>",
Action: queryVoter, Action: queryVoter,
Flags: options.RPC, Flags: options.RPC,
}, },
@ -77,16 +78,16 @@ func NewCommands() []cli.Command {
} }
func queryTx(ctx *cli.Context) error { func queryTx(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args().Slice()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("Transaction hash is missing", 1) return cli.Exit("Transaction hash is missing", 1)
} else if len(args) > 1 { } else if len(args) > 1 {
return cli.NewExitError("only one transaction hash is accepted", 1) return cli.Exit("only one transaction hash is accepted", 1)
} }
txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x")) txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("Invalid tx hash: %s", args[0]), 1) return cli.Exit(fmt.Sprintf("Invalid tx hash: %s", args[0]), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -94,25 +95,25 @@ func queryTx(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
txOut, err := c.GetRawTransactionVerbose(txHash) txOut, err := c.GetRawTransactionVerbose(txHash)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
var res *result.ApplicationLog var res *result.ApplicationLog
if !txOut.Blockhash.Equals(util.Uint256{}) { if !txOut.Blockhash.Equals(util.Uint256{}) {
res, err = c.GetApplicationLog(txHash, nil) res, err = c.GetApplicationLog(txHash, nil)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
} }
err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose")) err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
} }
@ -179,16 +180,16 @@ func queryCandidates(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
vals, err := c.GetCandidates() vals, err := c.GetCandidates()
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
comm, err := c.GetCommittee() comm, err := c.GetCommittee()
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
sort.Slice(vals, func(i, j int) bool { sort.Slice(vals, func(i, j int) bool {
@ -225,12 +226,12 @@ func queryCommittee(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
comm, err := c.GetCommittee() comm, err := c.GetCommittee()
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
for _, k := range comm { for _, k := range comm {
@ -251,12 +252,12 @@ func queryHeight(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
blockCount, err := c.GetBlockCount() blockCount, err := c.GetBlockCount()
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
blockHeight := blockCount - 1 // GetBlockCount returns block count (including 0), not the highest block index. blockHeight := blockCount - 1 // GetBlockCount returns block count (including 0), not the highest block index.
@ -271,16 +272,16 @@ func queryHeight(ctx *cli.Context) error {
} }
func queryVoter(ctx *cli.Context) error { func queryVoter(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args().Slice()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("No address specified", 1) return cli.Exit("No address specified", 1)
} else if len(args) > 1 { } else if len(args) > 1 {
return cli.NewExitError("this command only accepts one address", 1) return cli.Exit("this command only accepts one address", 1)
} }
addr, err := flags.ParseAddress(args[0]) addr, err := flags.ParseAddress(args[0])
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("wrong address: %s", args[0]), 1) return cli.Exit(fmt.Sprintf("wrong address: %s", args[0]), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -294,14 +295,14 @@ func queryVoter(ctx *cli.Context) error {
st, err := neoToken.GetAccountState(addr) st, err := neoToken.GetAccountState(addr)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if st == nil { if st == nil {
st = new(state.NEOBalance) st = new(state.NEOBalance)
} }
dec, err := neoToken.Decimals() dec, err := neoToken.Decimals()
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1) return cli.Exit(fmt.Errorf("failed to get decimals: %w", err), 1)
} }
voted := "null" voted := "null"
if st.VoteTo != nil { if st.VoteTo != nil {

View file

@ -27,13 +27,13 @@ import (
"github.com/nspcc-dev/neo-go/pkg/services/oracle" "github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv" "github.com/nspcc-dev/neo-go/pkg/services/rpcsrv"
"github.com/nspcc-dev/neo-go/pkg/services/stateroot" "github.com/nspcc-dev/neo-go/pkg/services/stateroot"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )
// NewCommands returns 'node' command. // NewCommands returns 'node' command.
func NewCommands() []cli.Command { func NewCommands() []*cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath} cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...) cfgFlags = append(cfgFlags, options.Network...)
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags)) var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
@ -41,47 +41,52 @@ func NewCommands() []cli.Command {
cfgFlags = append(cfgFlags, options.Debug) cfgFlags = append(cfgFlags, options.Debug)
cfgWithCountFlags = append(cfgWithCountFlags, cfgWithCountFlags = append(cfgWithCountFlags,
cli.UintFlag{ &cli.UintFlag{
Name: "count, c", Name: "count",
Aliases: []string{"c"},
Usage: "Number of blocks to be processed (default or 0: all chain)", Usage: "Number of blocks to be processed (default or 0: all chain)",
}, },
) )
var cfgCountOutFlags = make([]cli.Flag, len(cfgWithCountFlags)) var cfgCountOutFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountOutFlags, cfgWithCountFlags) copy(cfgCountOutFlags, cfgWithCountFlags)
cfgCountOutFlags = append(cfgCountOutFlags, cfgCountOutFlags = append(cfgCountOutFlags,
cli.UintFlag{ &cli.UintFlag{
Name: "start, s", Name: "start",
Usage: "Block number to start from (default: 0)", Aliases: []string{"s"},
Usage: "Block number to start from",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "out, o", Name: "out",
Aliases: []string{"o"},
Usage: "Output file (stdout if not given)", Usage: "Output file (stdout if not given)",
}, },
) )
var cfgCountInFlags = make([]cli.Flag, len(cfgWithCountFlags)) var cfgCountInFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountInFlags, cfgWithCountFlags) copy(cfgCountInFlags, cfgWithCountFlags)
cfgCountInFlags = append(cfgCountInFlags, cfgCountInFlags = append(cfgCountInFlags,
cli.StringFlag{ &cli.StringFlag{
Name: "in, i", Name: "in",
Aliases: []string{"i"},
Usage: "Input file (stdin if not given)", Usage: "Input file (stdin if not given)",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "dump", Name: "dump",
Usage: "Directory for storing JSON dumps", Usage: "Directory for storing JSON dumps",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "incremental, n", Name: "incremental",
Aliases: []string{"n"},
Usage: "Use if dump is incremental", Usage: "Use if dump is incremental",
}, },
) )
var cfgHeightFlags = make([]cli.Flag, len(cfgFlags)+1) var cfgHeightFlags = make([]cli.Flag, len(cfgFlags)+1)
copy(cfgHeightFlags, cfgFlags) copy(cfgHeightFlags, cfgFlags)
cfgHeightFlags[len(cfgHeightFlags)-1] = cli.UintFlag{ cfgHeightFlags[len(cfgHeightFlags)-1] = &cli.UintFlag{
Name: "height", Name: "height",
Usage: "Height of the state to reset DB to", Usage: "Height of the state to reset DB to",
Required: true, Required: true,
} }
return []cli.Command{ return []*cli.Command{
{ {
Name: "node", Name: "node",
Usage: "Start a NeoGo node", Usage: "Start a NeoGo node",
@ -92,7 +97,7 @@ func NewCommands() []cli.Command {
{ {
Name: "db", Name: "db",
Usage: "Database manipulations", Usage: "Database manipulations",
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "dump", Name: "dump",
Usage: "Dump blocks (starting with block #1) to the file", Usage: "Dump blocks (starting with block #1) to the file",
@ -134,7 +139,7 @@ func newGraceContext() context.Context {
func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) { func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) {
chain, _, err := initBlockChain(cfg, log) chain, _, err := initBlockChain(cfg, log)
if err != nil { if err != nil {
return nil, nil, nil, cli.NewExitError(err, 1) return nil, nil, nil, cli.Exit(err, 1)
} }
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log) prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log) pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
@ -142,11 +147,11 @@ func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *m
go chain.Run() go chain.Run()
err = prometheus.Start() err = prometheus.Start()
if err != nil { if err != nil {
return nil, nil, nil, cli.NewExitError(fmt.Errorf("failed to start Prometheus service: %w", err), 1) return nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Prometheus service: %w", err), 1)
} }
err = pprof.Start() err = pprof.Start()
if err != nil { if err != nil {
return nil, nil, nil, cli.NewExitError(fmt.Errorf("failed to start Pprof service: %w", err), 1) return nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Pprof service: %w", err), 1)
} }
return chain, prometheus, pprof, nil return chain, prometheus, pprof, nil
@ -158,11 +163,11 @@ func dumpDB(ctx *cli.Context) error {
} }
cfg, err := options.GetConfigFromContext(ctx) cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration) log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if logCloser != nil { if logCloser != nil {
defer func() { _ = logCloser() }() defer func() { _ = logCloser() }()
@ -174,7 +179,7 @@ func dumpDB(ctx *cli.Context) error {
if out := ctx.String("out"); out != "" { if out := ctx.String("out"); out != "" {
outStream, err = os.Create(out) outStream, err = os.Create(out)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
} }
defer outStream.Close() defer outStream.Close()
@ -192,7 +197,7 @@ func dumpDB(ctx *cli.Context) error {
chainCount := chain.BlockHeight() + 1 chainCount := chain.BlockHeight() + 1
if start+count > chainCount { if start+count > chainCount {
return cli.NewExitError(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", chainCount-1, count, start), 1) return cli.Exit(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", chainCount-1, count, start), 1)
} }
if count == 0 { if count == 0 {
count = chainCount - start count = chainCount - start
@ -203,7 +208,7 @@ func dumpDB(ctx *cli.Context) error {
writer.WriteU32LE(count) writer.WriteU32LE(count)
err = chaindump.Dump(chain, writer, start, count) err = chaindump.Dump(chain, writer, start, count)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
return nil return nil
} }
@ -218,7 +223,7 @@ func restoreDB(ctx *cli.Context) error {
} }
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration) log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if logCloser != nil { if logCloser != nil {
defer func() { _ = logCloser() }() defer func() { _ = logCloser() }()
@ -229,7 +234,7 @@ func restoreDB(ctx *cli.Context) error {
if in := ctx.String("in"); in != "" { if in := ctx.String("in"); in != "" {
inStream, err = os.Open(in) inStream, err = os.Open(in)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
} }
defer inStream.Close() defer inStream.Close()
@ -254,7 +259,7 @@ func restoreDB(ctx *cli.Context) error {
if ctx.Bool("incremental") { if ctx.Bool("incremental") {
start = reader.ReadU32LE() start = reader.ReadU32LE()
if chain.BlockHeight()+1 < start { if chain.BlockHeight()+1 < start {
return cli.NewExitError(fmt.Errorf("expected height: %d, dump starts at %d", return cli.Exit(fmt.Errorf("expected height: %d, dump starts at %d",
chain.BlockHeight()+1, start), 1) chain.BlockHeight()+1, start), 1)
} }
} }
@ -266,10 +271,10 @@ func restoreDB(ctx *cli.Context) error {
var allBlocks = reader.ReadU32LE() var allBlocks = reader.ReadU32LE()
if reader.Err != nil { if reader.Err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if skip+count > allBlocks { if skip+count > allBlocks {
return cli.NewExitError(fmt.Errorf("input file has only %d blocks, can't read %d starting from %d", allBlocks, count, skip), 1) return cli.Exit(fmt.Errorf("input file has only %d blocks, can't read %d starting from %d", allBlocks, count, skip), 1)
} }
if count == 0 { if count == 0 {
count = allBlocks - skip count = allBlocks - skip
@ -320,7 +325,7 @@ func restoreDB(ctx *cli.Context) error {
err = chaindump.Restore(chain, reader, skip, count, f) err = chaindump.Restore(chain, reader, skip, count, f)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
} }
@ -331,29 +336,29 @@ func resetDB(ctx *cli.Context) error {
} }
cfg, err := options.GetConfigFromContext(ctx) cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
h := uint32(ctx.Uint("height")) h := uint32(ctx.Uint("height"))
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration) log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if logCloser != nil { if logCloser != nil {
defer func() { _ = logCloser() }() defer func() { _ = logCloser() }()
} }
chain, store, err := initBlockChain(cfg, log) chain, store, err := initBlockChain(cfg, log)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create Blockchain instance: %w", err), 1) return cli.Exit(fmt.Errorf("failed to create Blockchain instance: %w", err), 1)
} }
err = chain.Reset(h) err = chain.Reset(h)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to reset chain state to height %d: %w", h, err), 1) return cli.Exit(fmt.Errorf("failed to reset chain state to height %d: %w", h, err), 1)
} }
err = store.Close() err = store.Close()
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to close the DB: %w", err), 1) return cli.Exit(fmt.Errorf("failed to close the DB: %w", err), 1)
} }
return nil return nil
} }
@ -442,12 +447,12 @@ func startServer(ctx *cli.Context) error {
cfg, err := options.GetConfigFromContext(ctx) cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
var logDebug = ctx.Bool("debug") var logDebug = ctx.Bool("debug")
log, logLevel, logCloser, err := options.HandleLoggingParams(logDebug, cfg.ApplicationConfiguration) log, logLevel, logCloser, err := options.HandleLoggingParams(logDebug, cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if logCloser != nil { if logCloser != nil {
defer func() { _ = logCloser() }() defer func() { _ = logCloser() }()
@ -458,12 +463,12 @@ func startServer(ctx *cli.Context) error {
serverConfig, err := network.NewServerConfig(cfg) serverConfig, err := network.NewServerConfig(cfg)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log) chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer func() { defer func() {
pprof.ShutDown() pprof.ShutDown()
@ -473,26 +478,26 @@ func startServer(ctx *cli.Context) error {
serv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), log) serv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), log)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create network server: %w", err), 1) return cli.Exit(fmt.Errorf("failed to create network server: %w", err), 1)
} }
srMod := chain.GetStateModule().(*corestate.Module) // Take full responsibility here. srMod := chain.GetStateModule().(*corestate.Module) // Take full responsibility here.
sr, err := stateroot.New(serverConfig.StateRootCfg, srMod, log, chain, serv.BroadcastExtensible) sr, err := stateroot.New(serverConfig.StateRootCfg, srMod, log, chain, serv.BroadcastExtensible)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't initialize StateRoot service: %w", err), 1) return cli.Exit(fmt.Errorf("can't initialize StateRoot service: %w", err), 1)
} }
serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload) serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload)
oracleSrv, err := mkOracle(cfg.ApplicationConfiguration.Oracle, cfg.ProtocolConfiguration.Magic, chain, serv, log) oracleSrv, err := mkOracle(cfg.ApplicationConfiguration.Oracle, cfg.ProtocolConfiguration.Magic, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log) dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log) p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
errChan := make(chan error) errChan := make(chan error)
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan) rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
@ -640,7 +645,7 @@ Main:
} }
if shutdownErr != nil { if shutdownErr != nil {
return cli.NewExitError(shutdownErr, 1) return cli.Exit(shutdownErr, 1)
} }
return nil return nil
@ -650,7 +655,7 @@ Main:
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) { func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) {
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration) store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
if err != nil { if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1) return nil, nil, cli.Exit(fmt.Errorf("could not initialize storage: %w", err), 1)
} }
chain, err := core.NewBlockchain(store, cfg.Blockchain(), log) chain, err := core.NewBlockchain(store, cfg.Blockchain(), log)
@ -663,7 +668,7 @@ func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, stora
errArgs = append(errArgs, closeErr) errArgs = append(errArgs, closeErr)
} }
return nil, nil, cli.NewExitError(fmt.Errorf(errText, errArgs...), 1) return nil, nil, cli.Exit(fmt.Errorf(errText, errArgs...), 1)
} }
return chain, store, nil return chain, store, nil
} }

View file

@ -15,7 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )

View file

@ -9,32 +9,35 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
var generatorFlags = []cli.Flag{ var generatorFlags = []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "config, c", Name: "config",
Aliases: []string{"c"},
Usage: "Configuration file to use", Usage: "Configuration file to use",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "manifest, m", Name: "manifest",
Aliases: []string{"m"},
Required: true, Required: true,
Usage: "Read contract manifest (*.manifest.json) file", Usage: "Read contract manifest (*.manifest.json) file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "out, o", Name: "out",
Aliases: []string{"o"},
Required: true, Required: true,
Usage: "Output of the compiled wrapper", Usage: "Output of the compiled wrapper",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "hash", Name: "hash",
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage", Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
}, },
} }
var generateWrapperCmd = cli.Command{ var generateWrapperCmd = &cli.Command{
Name: "generate-wrapper", Name: "generate-wrapper",
Usage: "Generate wrapper to use in other contracts", Usage: "Generate wrapper to use in other contracts",
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]", UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
@ -48,7 +51,7 @@ var generateWrapperCmd = cli.Command{
Flags: generatorFlags, Flags: generatorFlags,
} }
var generateRPCWrapperCmd = cli.Command{ var generateRPCWrapperCmd = &cli.Command{
Name: "generate-rpcwrapper", Name: "generate-rpcwrapper",
Usage: "Generate RPC wrapper to use for data reads", Usage: "Generate RPC wrapper to use for data reads",
UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]", UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
@ -76,23 +79,23 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
if hStr := ctx.String("hash"); len(hStr) != 0 { if hStr := ctx.String("hash"); len(hStr) != 0 {
h, err = util.Uint160DecodeStringLE(strings.TrimPrefix(hStr, "0x")) h, err = util.Uint160DecodeStringLE(strings.TrimPrefix(hStr, "0x"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1) return cli.Exit(fmt.Errorf("invalid contract hash: %w", err), 1)
} }
} }
m, _, err := readManifest(ctx.String("manifest"), h) m, _, err := readManifest(ctx.String("manifest"), h)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1) return cli.Exit(fmt.Errorf("can't read contract manifest: %w", err), 1)
} }
cfg := binding.NewConfig() cfg := binding.NewConfig()
if cfgPath := ctx.String("config"); cfgPath != "" { if cfgPath := ctx.String("config"); cfgPath != "" {
bs, err := os.ReadFile(cfgPath) bs, err := os.ReadFile(cfgPath)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read config file: %w", err), 1) return cli.Exit(fmt.Errorf("can't read config file: %w", err), 1)
} }
err = yaml.Unmarshal(bs, &cfg) err = yaml.Unmarshal(bs, &cfg)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't parse config file: %w", err), 1) return cli.Exit(fmt.Errorf("can't parse config file: %w", err), 1)
} }
} }
cfg.Manifest = m cfg.Manifest = m
@ -100,7 +103,7 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
f, err := os.Create(ctx.String("out")) f, err := os.Create(ctx.String("out"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't create output file: %w", err), 1) return cli.Exit(fmt.Errorf("can't create output file: %w", err), 1)
} }
defer f.Close() defer f.Close()
@ -108,7 +111,7 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
err = cb(cfg) err = cb(cfg)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1) return cli.Exit(fmt.Errorf("error during generation: %w", err), 1)
} }
return nil return nil
} }

View file

@ -13,7 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func TestGenerate(t *testing.T) { func TestGenerate(t *testing.T) {
@ -125,7 +125,7 @@ func TestGenerate(t *testing.T) {
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04, 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
} }
app := cli.NewApp() app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd} app.Commands = []*cli.Command{generateWrapperCmd}
rawCfg := `package: wrapper rawCfg := `package: wrapper
hash: ` + h.StringLE() + ` hash: ` + h.StringLE() + `
@ -351,7 +351,7 @@ func TestGenerateValidPackageName(t *testing.T) {
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04, 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
} }
app := cli.NewApp() app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd} app.Commands = []*cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
require.NoError(t, app.Run([]string{"", "generate-wrapper", require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile, "--manifest", manifestFile,
"--out", outFile, "--out", outFile,
@ -432,7 +432,7 @@ const rewriteExpectedOutputs = false
func TestGenerateRPCBindings(t *testing.T) { func TestGenerateRPCBindings(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
app := cli.NewApp() app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd} app.Commands = []*cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
var checkBinding = func(manifest string, hash string, good string) { var checkBinding = func(manifest string, hash string, good string) {
t.Run(manifest, func(t *testing.T) { t.Run(manifest, func(t *testing.T) {
@ -549,7 +549,7 @@ func TestAssistedRPCBindings(t *testing.T) {
func TestGenerate_Errors(t *testing.T) { func TestGenerate_Errors(t *testing.T) {
app := cli.NewApp() app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd} app.Commands = []*cli.Command{generateWrapperCmd}
app.ExitErrHandler = func(*cli.Context, error) {} app.ExitErrHandler = func(*cli.Context, error) {}
checkError := func(t *testing.T, msg string, args ...string) { checkError := func(t *testing.T, msg string, args ...string) {

View file

@ -13,7 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func manifestAddGroup(ctx *cli.Context) error { func manifestAddGroup(ctx *cli.Context) error {
@ -22,25 +22,25 @@ func manifestAddGroup(ctx *cli.Context) error {
} }
sender, err := flags.ParseAddress(ctx.String("sender")) sender, err := flags.ParseAddress(ctx.String("sender"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid sender: %w", err), 1) return cli.Exit(fmt.Errorf("invalid sender: %w", err), 1)
} }
nf, _, err := readNEFFile(ctx.String("nef")) nf, _, err := readNEFFile(ctx.String("nef"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read NEF file: %w", err), 1) return cli.Exit(fmt.Errorf("can't read NEF file: %w", err), 1)
} }
mPath := ctx.String("manifest") mPath := ctx.String("manifest")
m, _, err := readManifest(mPath, util.Uint160{}) m, _, err := readManifest(mPath, util.Uint160{})
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1) return cli.Exit(fmt.Errorf("can't read contract manifest: %w", err), 1)
} }
h := state.CreateContractHash(sender, nf.Checksum, m.Name) h := state.CreateContractHash(sender, nf.Checksum, m.Name)
gAcc, w, err := options.GetAccFromContext(ctx) gAcc, w, err := options.GetAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't get account to sign group with: %w", err), 1) return cli.Exit(fmt.Errorf("can't get account to sign group with: %w", err), 1)
} }
defer w.Close() defer w.Close()
@ -64,12 +64,12 @@ func manifestAddGroup(ctx *cli.Context) error {
rawM, err := json.Marshal(m) rawM, err := json.Marshal(m)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't marshal manifest: %w", err), 1) return cli.Exit(fmt.Errorf("can't marshal manifest: %w", err), 1)
} }
err = os.WriteFile(mPath, rawM, os.ModePerm) err = os.WriteFile(mPath, rawM, os.ModePerm)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't write manifest file: %w", err), 1) return cli.Exit(fmt.Errorf("can't write manifest file: %w", err), 1)
} }
return nil return nil
} }

View file

@ -27,13 +27,17 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// addressFlagName is a flag name used for address-related operations. It should be // addressFlagName and addressFlagAlias are a flag name and its alias
// the same within the smartcontract package, thus, use this constant. // used for address-related operations. It should be the same within
const addressFlagName = "address, a" // the smartcontract package, thus, use this constant.
const (
addressFlagName = "address"
addressFlagAlias = "a"
)
var ( var (
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag") errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
@ -43,8 +47,9 @@ var (
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument") 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") 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") errFileExist = errors.New("A file with given smart-contract name already exists")
addressFlag = flags.AddressFlag{ addressFlag = &flags.AddressFlag{
Name: addressFlagName, Name: addressFlagName,
Aliases: []string{addressFlagAlias},
Usage: "Address to use as transaction signee (and gas source)", Usage: "Address to use as transaction signee (and gas source)",
} }
) )
@ -74,10 +79,11 @@ func RuntimeNotify(args []any) {
) )
// NewCommands returns 'contract' command. // NewCommands returns 'contract' command.
func NewCommands() []cli.Command { func NewCommands() []*cli.Command {
testInvokeScriptFlags := []cli.Flag{ testInvokeScriptFlags := []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "in, i", Name: "in",
Aliases: []string{"i"},
Usage: "Input location of the .nef file that needs to be invoked", Usage: "Input location of the .nef file that needs to be invoked",
}, },
options.Historic, options.Historic,
@ -96,37 +102,43 @@ func NewCommands() []cli.Command {
invokeFunctionFlags = append(invokeFunctionFlags, options.Wallet...) invokeFunctionFlags = append(invokeFunctionFlags, options.Wallet...)
invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...) invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...)
deployFlags := append(invokeFunctionFlags, []cli.Flag{ deployFlags := append(invokeFunctionFlags, []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "in, i", Name: "in",
Aliases: []string{"i"},
Usage: "Input file for the smart contract (*.nef)", Usage: "Input file for the smart contract (*.nef)",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "manifest, m", Name: "manifest",
Aliases: []string{"m"},
Usage: "Manifest input file (*.manifest.json)", Usage: "Manifest input file (*.manifest.json)",
}, },
}...) }...)
manifestAddGroupFlags := append([]cli.Flag{ manifestAddGroupFlags := append([]cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "sender, s", Name: "sender",
Aliases: []string{"s"},
Usage: "Deploy transaction sender", Usage: "Deploy transaction sender",
}, },
flags.AddressFlag{ &flags.AddressFlag{
Name: addressFlagName, // use the same name for handler code unification. Name: addressFlagName, // use the same name for handler code unification.
Aliases: []string{addressFlagAlias},
Usage: "Account to sign group with", Usage: "Account to sign group with",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "nef, n", Name: "nef",
Aliases: []string{"n"},
Usage: "Path to the NEF file", Usage: "Path to the NEF file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "manifest, m", Name: "manifest",
Aliases: []string{"m"},
Usage: "Path to the manifest", Usage: "Path to the manifest",
}, },
}, options.Wallet...) }, options.Wallet...)
return []cli.Command{{ return []*cli.Command{{
Name: "contract", Name: "contract",
Usage: "Compile - debug - deploy smart contracts", Usage: "Compile - debug - deploy smart contracts",
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "compile", Name: "compile",
Usage: "Compile a smart contract to a .nef file", Usage: "Compile a smart contract to a .nef file",
@ -141,47 +153,53 @@ func NewCommands() []cli.Command {
`, `,
Action: contractCompile, Action: contractCompile,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "in, i", Name: "in",
Aliases: []string{"i"},
Usage: "Input file for the smart contract to be compiled (*.go file or directory)", Usage: "Input file for the smart contract to be compiled (*.go file or directory)",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "out, o", Name: "out",
Aliases: []string{"o"},
Usage: "Output of the compiled contract", Usage: "Output of the compiled contract",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "verbose, v", Name: "verbose",
Aliases: []string{"v"},
Usage: "Print out additional information after a compiling", Usage: "Print out additional information after a compiling",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "debug, d", Name: "debug",
Aliases: []string{"d"},
Usage: "Emit debug info in a separate file", Usage: "Emit debug info in a separate file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "manifest, m", Name: "manifest",
Aliases: []string{"m"},
Usage: "Emit contract manifest (*.manifest.json) file into separate file using configuration input file (*.yml)", Usage: "Emit contract manifest (*.manifest.json) file into separate file using configuration input file (*.yml)",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "config, c", Name: "config",
Aliases: []string{"c"},
Usage: "Configuration input file (*.yml)", Usage: "Configuration input file (*.yml)",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "no-standards", Name: "no-standards",
Usage: "Do not check compliance with supported standards", Usage: "Do not check compliance with supported standards",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "no-events", Name: "no-events",
Usage: "Do not check emitted events with the manifest", Usage: "Do not check emitted events with the manifest",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "no-permissions", Name: "no-permissions",
Usage: "Do not check if invoked contracts are allowed in manifest", Usage: "Do not check if invoked contracts are allowed in manifest",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "guess-eventtypes", Name: "guess-eventtypes",
Usage: "Guess event types for smart-contract bindings configuration from the code usages", Usage: "Guess event types for smart-contract bindings configuration from the code usages",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "bindings", Name: "bindings",
Usage: "Output file for smart-contract bindings configuration", Usage: "Output file for smart-contract bindings configuration",
}, },
@ -254,12 +272,14 @@ func NewCommands() []cli.Command {
UsageText: "neo-go contract init -n name [--skip-details]", UsageText: "neo-go contract init -n name [--skip-details]",
Action: initSmartContract, Action: initSmartContract,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "name, n", Name: "name",
Aliases: []string{"n"},
Usage: "Name of the smart-contract to be initialized", Usage: "Name of the smart-contract to be initialized",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "skip-details, skip", Name: "skip-details",
Aliases: []string{"skip"},
Usage: "Skip filling in the projects and contract details", Usage: "Skip filling in the projects and contract details",
}, },
}, },
@ -270,12 +290,14 @@ func NewCommands() []cli.Command {
UsageText: "neo-go contract inspect -i file [-c]", UsageText: "neo-go contract inspect -i file [-c]",
Action: inspect, Action: inspect,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ &cli.BoolFlag{
Name: "compile, c", Name: "compile",
Aliases: []string{"c"},
Usage: "Compile input file (it should be go code then)", Usage: "Compile input file (it should be go code then)",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "in, i", Name: "in",
Aliases: []string{"i"},
Usage: "Input file of the program (either .go or .nef)", Usage: "Input file of the program (either .go or .nef)",
}, },
}, },
@ -286,16 +308,18 @@ func NewCommands() []cli.Command {
UsageText: "neo-go contract calc-hash -i nef -m manifest -s address", UsageText: "neo-go contract calc-hash -i nef -m manifest -s address",
Action: calcHash, Action: calcHash,
Flags: []cli.Flag{ Flags: []cli.Flag{
flags.AddressFlag{ &flags.AddressFlag{
Name: "sender, s", Name: "sender",
Aliases: []string{"s"},
Usage: "Sender script hash or address", Usage: "Sender script hash or address",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "in", Name: "in",
Usage: "Path to NEF file", Usage: "Path to NEF file",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "manifest, m", Name: "manifest",
Aliases: []string{"m"},
Usage: "Path to manifest file", Usage: "Path to manifest file",
}, },
}, },
@ -303,7 +327,7 @@ func NewCommands() []cli.Command {
{ {
Name: "manifest", Name: "manifest",
Usage: "Manifest-related commands", Usage: "Manifest-related commands",
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "add-group", Name: "add-group",
Usage: "Adds group to the manifest", Usage: "Adds group to the manifest",
@ -324,12 +348,12 @@ func initSmartContract(ctx *cli.Context) error {
} }
contractName := ctx.String("name") contractName := ctx.String("name")
if contractName == "" { if contractName == "" {
return cli.NewExitError(errNoSmartContractName, 1) return cli.Exit(errNoSmartContractName, 1)
} }
// Check if the file already exists, if yes, exit // Check if the file already exists, if yes, exit
if _, err := os.Stat(contractName); err == nil { if _, err := os.Stat(contractName); err == nil {
return cli.NewExitError(errFileExist, 1) return cli.Exit(errFileExist, 1)
} }
basePath := contractName basePath := contractName
@ -338,7 +362,7 @@ func initSmartContract(ctx *cli.Context) error {
// create base directory // create base directory
if err := os.Mkdir(basePath, os.ModePerm); err != nil { if err := os.Mkdir(basePath, os.ModePerm); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
m := ProjectConfig{ m := ProjectConfig{
@ -363,10 +387,10 @@ func initSmartContract(ctx *cli.Context) error {
} }
b, err := yaml.Marshal(m) b, err := yaml.Marshal(m)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if err := os.WriteFile(filepath.Join(basePath, "neo-go.yml"), b, 0644); err != nil { if err := os.WriteFile(filepath.Join(basePath, "neo-go.yml"), b, 0644); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
ver := ModVersion ver := ModVersion
@ -382,12 +406,12 @@ require (
github.com/nspcc-dev/neo-go/pkg/interop ` + ver + ` github.com/nspcc-dev/neo-go/pkg/interop ` + ver + `
)`) )`)
if err := os.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil { if err := os.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
data := []byte(fmt.Sprintf(smartContractTmpl, contractName)) data := []byte(fmt.Sprintf(smartContractTmpl, contractName))
if err := os.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil { if err := os.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
fmt.Fprintf(ctx.App.Writer, "Successfully initialized smart contract [%s]\n", contractName) fmt.Fprintf(ctx.App.Writer, "Successfully initialized smart contract [%s]\n", contractName)
@ -401,7 +425,7 @@ func contractCompile(ctx *cli.Context) error {
} }
src := ctx.String("in") src := ctx.String("in")
if len(src) == 0 { if len(src) == 0 {
return cli.NewExitError(errNoInput, 1) return cli.Exit(errNoInput, 1)
} }
manifestFile := ctx.String("manifest") manifestFile := ctx.String("manifest")
confFile := ctx.String("config") confFile := ctx.String("config")
@ -409,7 +433,7 @@ func contractCompile(ctx *cli.Context) error {
out := ctx.String("out") out := ctx.String("out")
bindings := ctx.String("bindings") bindings := ctx.String("bindings")
if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) { if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) {
return cli.NewExitError(errNoConfFile, 1) return cli.Exit(errNoConfFile, 1)
} }
autocomplete := len(manifestFile) == 0 && autocomplete := len(manifestFile) == 0 &&
len(confFile) == 0 && len(confFile) == 0 &&
@ -419,7 +443,7 @@ func contractCompile(ctx *cli.Context) error {
var root string var root string
fileInfo, err := os.Stat(src) fileInfo, err := os.Stat(src)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to stat source file or directory: %w", err), 1) return cli.Exit(fmt.Errorf("failed to stat source file or directory: %w", err), 1)
} }
if fileInfo.IsDir() { if fileInfo.IsDir() {
base := filepath.Base(fileInfo.Name()) base := filepath.Base(fileInfo.Name())
@ -470,7 +494,7 @@ func contractCompile(ctx *cli.Context) error {
result, err := compiler.CompileAndSave(src, o) result, err := compiler.CompileAndSave(src, o)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if ctx.Bool("verbose") { if ctx.Bool("verbose") {
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(result)) fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(result))
@ -485,33 +509,33 @@ func calcHash(ctx *cli.Context) error {
} }
sender := ctx.Generic("sender").(*flags.Address) sender := ctx.Generic("sender").(*flags.Address)
if !sender.IsSet { if !sender.IsSet {
return cli.NewExitError("sender is not set", 1) return cli.Exit("sender is not set", 1)
} }
p := ctx.String("in") p := ctx.String("in")
if p == "" { if p == "" {
return cli.NewExitError(errors.New("no .nef file was provided"), 1) return cli.Exit(errors.New("no .nef file was provided"), 1)
} }
mpath := ctx.String("manifest") mpath := ctx.String("manifest")
if mpath == "" { if mpath == "" {
return cli.NewExitError(errors.New("no manifest file provided"), 1) return cli.Exit(errors.New("no manifest file provided"), 1)
} }
f, err := os.ReadFile(p) f, err := os.ReadFile(p)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't read .nef file: %w", err), 1) return cli.Exit(fmt.Errorf("can't read .nef file: %w", err), 1)
} }
nefFile, err := nef.FileFromBytes(f) nefFile, err := nef.FileFromBytes(f)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't unmarshal .nef file: %w", err), 1) return cli.Exit(fmt.Errorf("can't unmarshal .nef file: %w", err), 1)
} }
manifestBytes, err := os.ReadFile(mpath) manifestBytes, err := os.ReadFile(mpath)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1)
} }
m := &manifest.Manifest{} m := &manifest.Manifest{}
err = json.Unmarshal(manifestBytes, m) err = json.Unmarshal(manifestBytes, m)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore manifest file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to restore manifest file: %w", err), 1)
} }
fmt.Fprintln(ctx.App.Writer, "Contract hash:", state.CreateContractHash(sender.Uint160(), nefFile.Checksum, m.Name).StringLE()) fmt.Fprintln(ctx.App.Writer, "Contract hash:", state.CreateContractHash(sender.Uint160(), nefFile.Checksum, m.Name).StringLE())
return nil return nil
@ -528,7 +552,7 @@ func invokeFunction(ctx *cli.Context) error {
func invokeInternal(ctx *cli.Context, signAndPush bool) error { func invokeInternal(ctx *cli.Context, signAndPush bool) error {
var ( var (
err error err error
exitErr *cli.ExitError exitErr cli.ExitCoder
operation string operation string
params []any params []any
paramsStart = 1 paramsStart = 1
@ -539,22 +563,23 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
args := ctx.Args() args := ctx.Args()
if !args.Present() { if !args.Present() {
return cli.NewExitError(errNoScriptHash, 1) return cli.Exit(errNoScriptHash, 1)
} }
script, err := flags.ParseAddress(args[0]) argsSlice := args.Slice()
script, err := flags.ParseAddress(argsSlice[0])
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("incorrect script hash: %w", err), 1) return cli.Exit(fmt.Errorf("incorrect script hash: %w", err), 1)
} }
if len(args) <= 1 { if len(argsSlice) <= 1 {
return cli.NewExitError(errNoMethod, 1) return cli.Exit(errNoMethod, 1)
} }
operation = args[1] operation = argsSlice[1]
paramsStart++ paramsStart++
if len(args) > paramsStart { if len(argsSlice) > paramsStart {
cosignersOffset, scParams, err = cmdargs.ParseParams(args[paramsStart:], true) cosignersOffset, scParams, err = cmdargs.ParseParams(argsSlice[paramsStart:], true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
params = make([]any, len(scParams)) params = make([]any, len(scParams))
for i := range scParams { for i := range scParams {
@ -575,7 +600,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
if signAndPush { if signAndPush {
acc, w, err = options.GetAccFromContext(ctx) acc, w, err = options.GetAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer w.Close() defer w.Close()
} }
@ -595,7 +620,7 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
if signAndPush { if signAndPush {
signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None) signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
} }
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -615,12 +640,12 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
out := ctx.String("out") out := ctx.String("out")
resp, err = inv.Call(script, operation, params...) resp, err = inv.Call(script, operation, params...)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if resp.State != "HALT" { if resp.State != "HALT" {
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException) errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException)
if !signAndPush { if !signAndPush {
return cli.NewExitError(errText, 1) return cli.Exit(errText, 1)
} }
action := "send" action := "send"
@ -630,25 +655,25 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
process = "Saving" process = "Saving"
} }
if !ctx.Bool("force") { if !ctx.Bool("force") {
return cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1) return cli.Exit(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
} }
fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...") fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...")
} }
if !signAndPush { if !signAndPush {
b, err := json.MarshalIndent(resp, "", " ") b, err := json.MarshalIndent(resp, "", " ")
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
fmt.Fprintln(ctx.App.Writer, string(b)) fmt.Fprintln(ctx.App.Writer, string(b))
return nil return nil
} }
if len(resp.Script) == 0 { if len(resp.Script) == 0 {
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1) return cli.Exit(errors.New("no script returned from the RPC node"), 1)
} }
tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed, nil) tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed, nil)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1) return cli.Exit(fmt.Errorf("failed to create tx: %w", err), 1)
} }
return txctx.SignAndSend(ctx, act, acc, tx) return txctx.SignAndSend(ctx, act, acc, tx)
} }
@ -656,16 +681,16 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
func testInvokeScript(ctx *cli.Context) error { func testInvokeScript(ctx *cli.Context) error {
src := ctx.String("in") src := ctx.String("in")
if len(src) == 0 { if len(src) == 0 {
return cli.NewExitError(errNoInput, 1) return cli.Exit(errNoInput, 1)
} }
b, err := os.ReadFile(src) b, err := os.ReadFile(src)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
nefFile, err := nef.FileFromBytes(b) nefFile, err := nef.FileFromBytes(b)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore .nef file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to restore .nef file: %w", err), 1)
} }
signers, exitErr := cmdargs.GetSignersFromContext(ctx, 0) signers, exitErr := cmdargs.GetSignersFromContext(ctx, 0)
@ -683,12 +708,12 @@ func testInvokeScript(ctx *cli.Context) error {
resp, err := inv.Run(nefFile.Script) resp, err := inv.Run(nefFile.Script)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
b, err = json.MarshalIndent(resp, "", " ") b, err = json.MarshalIndent(resp, "", " ")
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
fmt.Fprintln(ctx.App.Writer, string(b)) fmt.Fprintln(ctx.App.Writer, string(b))
@ -715,7 +740,7 @@ func inspect(ctx *cli.Context) error {
in := ctx.String("in") in := ctx.String("in")
compile := ctx.Bool("compile") compile := ctx.Bool("compile")
if len(in) == 0 { if len(in) == 0 {
return cli.NewExitError(errNoInput, 1) return cli.Exit(errNoInput, 1)
} }
var ( var (
b []byte b []byte
@ -724,16 +749,16 @@ func inspect(ctx *cli.Context) error {
if compile { if compile {
b, err = compiler.Compile(in, nil) b, err = compiler.Compile(in, nil)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to compile: %w", err), 1) return cli.Exit(fmt.Errorf("failed to compile: %w", err), 1)
} }
} else { } else {
f, err := os.ReadFile(in) f, err := os.ReadFile(in)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to read .nef file: %w", err), 1)
} }
nefFile, err := nef.FileFromBytes(f) nefFile, err := nef.FileFromBytes(f)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore .nef file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to restore .nef file: %w", err), 1)
} }
b = nefFile.Script b = nefFile.Script
} }
@ -748,22 +773,22 @@ func inspect(ctx *cli.Context) error {
func contractDeploy(ctx *cli.Context) error { func contractDeploy(ctx *cli.Context) error {
nefFile, f, err := readNEFFile(ctx.String("in")) nefFile, f, err := readNEFFile(ctx.String("in"))
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
m, manifestBytes, err := readManifest(ctx.String("manifest"), util.Uint160{}) m, manifestBytes, err := readManifest(ctx.String("manifest"), util.Uint160{})
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1)
} }
var appCallParams = []any{f, manifestBytes} var appCallParams = []any{f, manifestBytes}
signOffset, data, err := cmdargs.ParseParams(ctx.Args(), true) signOffset, data, err := cmdargs.ParseParams(ctx.Args().Slice(), true)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1) return cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
} }
if len(data) > 1 { if len(data) > 1 {
return cli.NewExitError("'data' should be represented as a single parameter", 1) return cli.Exit("'data' should be represented as a single parameter", 1)
} }
if len(data) != 0 { if len(data) != 0 {
appCallParams = append(appCallParams, data[0]) appCallParams = append(appCallParams, data[0])
@ -771,7 +796,7 @@ func contractDeploy(ctx *cli.Context) error {
acc, w, err := options.GetAccFromContext(ctx) acc, w, err := options.GetAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1) return cli.Exit(fmt.Errorf("can't get sender address: %w", err), 1)
} }
defer w.Close() defer w.Close()
sender := acc.ScriptHash() sender := acc.ScriptHash()
@ -801,12 +826,12 @@ func ParseContractConfig(confFile string) (ProjectConfig, error) {
conf := ProjectConfig{} conf := ProjectConfig{}
confBytes, err := os.ReadFile(confFile) confBytes, err := os.ReadFile(confFile)
if err != nil { if err != nil {
return conf, cli.NewExitError(err, 1) return conf, cli.Exit(err, 1)
} }
err = yaml.Unmarshal(confBytes, &conf) err = yaml.Unmarshal(confBytes, &conf)
if err != nil { if err != nil {
return conf, cli.NewExitError(fmt.Errorf("bad config: %w", err), 1) return conf, cli.Exit(fmt.Errorf("bad config: %w", err), 1)
} }
return conf, nil return conf, nil
} }

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )

View file

@ -16,32 +16,34 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var ( var (
// GasFlag is a flag used for the additional network fee. // GasFlag is a flag used for the additional network fee.
GasFlag = flags.Fixed8Flag{ GasFlag = &flags.Fixed8Flag{
Name: "gas, g", Name: "gas",
Aliases: []string{"g"},
Usage: "Network fee to add to the transaction (prioritizing it)", Usage: "Network fee to add to the transaction (prioritizing it)",
} }
// SysGasFlag is a flag used for the additional system fee. // SysGasFlag is a flag used for the additional system fee.
SysGasFlag = flags.Fixed8Flag{ SysGasFlag = &flags.Fixed8Flag{
Name: "sysgas, e", Name: "sysgas",
Aliases: []string{"e"},
Usage: "System fee to add to the transaction (compensating for execution)", Usage: "System fee to add to the transaction (compensating for execution)",
} }
// OutFlag is a flag used for file output. // OutFlag is a flag used for file output.
OutFlag = cli.StringFlag{ OutFlag = &cli.StringFlag{
Name: "out", Name: "out",
Usage: "File (JSON) to put signature context with a transaction to", Usage: "File (JSON) to put signature context with a transaction to",
} }
// ForceFlag is a flag used to force transaction send. // ForceFlag is a flag used to force transaction send.
ForceFlag = cli.BoolFlag{ ForceFlag = &cli.BoolFlag{
Name: "force", Name: "force",
Usage: "Do not ask for a confirmation (and ignore errors)", Usage: "Do not ask for a confirmation (and ignore errors)",
} }
// AwaitFlag is a flag used to wait for the transaction to be included in a block. // AwaitFlag is a flag used to wait for the transaction to be included in a block.
AwaitFlag = cli.BoolFlag{ AwaitFlag = &cli.BoolFlag{
Name: "await", Name: "await",
Usage: "Wait for the transaction to be included in a block", Usage: "Wait for the transaction to be included in a block",
} }
@ -71,7 +73,7 @@ func SignAndSend(ctx *cli.Context, act *actor.Actor, acc *wallet.Account, tx *tr
promptTime := time.Now() promptTime := time.Now()
err := input.ConfirmTx(ctx.App.Writer, tx) err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
waitTime := time.Since(promptTime) waitTime := time.Since(promptTime)
// Compensate for confirmation waiting. // Compensate for confirmation waiting.
@ -83,17 +85,17 @@ func SignAndSend(ctx *cli.Context, act *actor.Actor, acc *wallet.Account, tx *tr
) )
resTx, vub, err = act.SignAndSend(tx) resTx, vub, err = act.SignAndSend(tx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if ctx.Bool("await") { if ctx.Bool("await") {
aer, err = act.Wait(resTx, vub, err) aer, err = act.Wait(resTx, vub, err)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", resTx.StringLE(), err), 1) return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", resTx.StringLE(), err), 1)
} }
} }
} }
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
DumpTransactionInfo(ctx.App.Writer, tx.Hash(), aer) DumpTransactionInfo(ctx.App.Writer, tx.Hash(), aer)

View file

@ -16,20 +16,20 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter" "github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func cancelTx(ctx *cli.Context) error { func cancelTx(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args().Slice()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("transaction hash is missing", 1) return cli.Exit("transaction hash is missing", 1)
} else if len(args) > 1 { } else if len(args) > 1 {
return cli.NewExitError("only one transaction hash is accepted", 1) return cli.Exit("only one transaction hash is accepted", 1)
} }
txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x")) txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x"))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("invalid tx hash: %s", args[0]), 1) return cli.Exit(fmt.Sprintf("invalid tx hash: %s", args[0]), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -37,13 +37,13 @@ func cancelTx(ctx *cli.Context) error {
acc, w, err := options.GetAccFromContext(ctx) acc, w, err := options.GetAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1) return cli.Exit(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1)
} }
defer w.Close() defer w.Close()
signers, err := cmdargs.GetSignersAccounts(acc, w, nil, transaction.CalledByEntry) signers, err := cmdargs.GetSignersAccounts(acc, w, nil, transaction.CalledByEntry)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
} }
c, a, exitErr := options.GetRPCWithActor(gctx, ctx, signers) c, a, exitErr := options.GetRPCWithActor(gctx, ctx, signers)
if exitErr != nil { if exitErr != nil {
@ -52,11 +52,11 @@ func cancelTx(ctx *cli.Context) error {
mainTx, _ := c.GetRawTransactionVerbose(txHash) mainTx, _ := c.GetRawTransactionVerbose(txHash)
if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) { if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) {
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1) return cli.Exit(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1)
} }
if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) { if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) {
return cli.NewExitError(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1) return cli.Exit(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1)
} }
resHash, resVub, err := a.SendTunedRun([]byte{byte(opcode.RET)}, []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: txHash}}}, func(r *result.Invoke, t *transaction.Transaction) error { resHash, resVub, err := a.SendTunedRun([]byte{byte(opcode.RET)}, []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: txHash}}}, func(r *result.Invoke, t *transaction.Transaction) error {
@ -74,7 +74,7 @@ func cancelTx(ctx *cli.Context) error {
return nil return nil
}) })
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to send conflicting transaction: %w", err), 1) return cli.Exit(fmt.Errorf("failed to send conflicting transaction: %w", err), 1)
} }
var res *state.AppExecResult var res *state.AppExecResult
if ctx.Bool("await") { if ctx.Bool("await") {
@ -82,19 +82,19 @@ func cancelTx(ctx *cli.Context) error {
if err != nil { if err != nil {
if errors.Is(err, waiter.ErrTxNotAccepted) { if errors.Is(err, waiter.ErrTxNotAccepted) {
if mainTx == nil { if mainTx == nil {
return cli.NewExitError(fmt.Errorf("neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of conlicting transaction). Main transaction is unknown to the provided RPC node, thus still has chances to be accepted, you may try cancellation again", resVub), 1) return cli.Exit(fmt.Errorf("neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of conlicting transaction). Main transaction is unknown to the provided RPC node, thus still has chances to be accepted, you may try cancellation again", resVub), 1)
} }
fmt.Fprintf(ctx.App.Writer, "Neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of both target and conflicting transactions). Main transaction is not valid anymore, cancellation is successful\n", resVub) fmt.Fprintf(ctx.App.Writer, "Neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of both target and conflicting transactions). Main transaction is not valid anymore, cancellation is successful\n", resVub)
return nil return nil
} }
return cli.NewExitError(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1) return cli.Exit(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1)
} }
if txHash.Equals(res.Container) { if txHash.Equals(res.Container) {
tx, err := c.GetRawTransactionVerbose(txHash) tx, err := c.GetRawTransactionVerbose(txHash)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted", txHash), 1) return cli.Exit(fmt.Errorf("target transaction %s is accepted", txHash), 1)
} }
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1) return cli.Exit(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1)
} }
fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted") fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted")
} }

View file

@ -11,27 +11,28 @@ import (
"github.com/nspcc-dev/neo-go/cli/txctx" "github.com/nspcc-dev/neo-go/cli/txctx"
vmcli "github.com/nspcc-dev/neo-go/cli/vm" vmcli "github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// NewCommands returns util commands for neo-go CLI. // NewCommands returns util commands for neo-go CLI.
func NewCommands() []cli.Command { func NewCommands() []*cli.Command {
txDumpFlags := append([]cli.Flag{}, options.RPC...) txDumpFlags := append([]cli.Flag{}, options.RPC...)
txSendFlags := append(txDumpFlags, txctx.AwaitFlag) txSendFlags := append(txDumpFlags, txctx.AwaitFlag)
txCancelFlags := append([]cli.Flag{ txCancelFlags := append([]cli.Flag{
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to use as conflicting transaction signee (and gas source)", Usage: "Address to use as conflicting transaction signee (and gas source)",
}, },
txctx.GasFlag, txctx.GasFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
}, options.RPC...) }, options.RPC...)
txCancelFlags = append(txCancelFlags, options.Wallet...) txCancelFlags = append(txCancelFlags, options.Wallet...)
return []cli.Command{ return []*cli.Command{
{ {
Name: "util", Name: "util",
Usage: "Various helper commands", Usage: "Various helper commands",
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "convert", Name: "convert",
Usage: "Convert provided argument into other possible formats", Usage: "Convert provided argument into other possible formats",
@ -44,7 +45,7 @@ func NewCommands() []cli.Command {
{ {
Name: "sendtx", Name: "sendtx",
Usage: "Send complete transaction stored in a context file", Usage: "Send complete transaction stored in a context file",
UsageText: "sendtx [-r <endpoint>] <file.in> [--await]", UsageText: "sendtx [-r <endpoint>] [--await] <file.in>",
Description: `Sends the transaction from the given context file to the given RPC node if it's Description: `Sends the transaction from the given context file to the given RPC node if it's
completely signed and ready. This command expects a ContractParametersContext completely signed and ready. This command expects a ContractParametersContext
JSON file for input, it can't handle binary (or hex- or base64-encoded) JSON file for input, it can't handle binary (or hex- or base64-encoded)
@ -57,7 +58,7 @@ func NewCommands() []cli.Command {
{ {
Name: "canceltx", Name: "canceltx",
Usage: "Cancel transaction by sending conflicting transaction", Usage: "Cancel transaction by sending conflicting transaction",
UsageText: "canceltx <txid> -r <endpoint> --wallet <wallet> [--account <account>] [--wallet-config <path>] [--gas <gas>] [--await]", UsageText: "canceltx -r <endpoint> --wallet <wallet> [--account <account>] [--wallet-config <path>] [--gas <gas>] [--await] <txid>",
Description: `Aims to prevent a transaction from being added to the blockchain by dispatching a more Description: `Aims to prevent a transaction from being added to the blockchain by dispatching a more
prioritized conflicting transaction to the specified RPC node. The input for this command should prioritized conflicting transaction to the specified RPC node. The input for this command should
be the transaction hash. If another account is not specified, the conflicting transaction is be the transaction hash. If another account is not specified, the conflicting transaction is
@ -90,14 +91,15 @@ func NewCommands() []cli.Command {
{ {
Name: "ops", 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.", 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 <base64/hex-encoded script> [-i path-to-file] [--hex]", UsageText: "ops [-i path-to-file] [--hex] <base64/hex-encoded script>",
Action: handleOps, Action: handleOps,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "in, i", Name: "in",
Aliases: []string{"i"},
Usage: "Input file containing base64- or hex- encoded script representation", Usage: "Input file containing base64- or hex- encoded script representation",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "hex", Name: "hex",
Usage: "Use hex encoding and do not check base64", Usage: "Use hex encoding and do not check base64",
}, },
@ -109,9 +111,9 @@ func NewCommands() []cli.Command {
} }
func handleParse(ctx *cli.Context) error { func handleParse(ctx *cli.Context) error {
res, err := vmcli.Parse(ctx.Args()) res, err := vmcli.Parse(ctx.Args().Slice())
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
fmt.Fprint(ctx.App.Writer, res) fmt.Fprint(ctx.App.Writer, res)
return nil return nil
@ -127,21 +129,21 @@ func handleOps(ctx *cli.Context) error {
if len(in) != 0 { if len(in) != 0 {
b, err := os.ReadFile(in) b, err := os.ReadFile(in)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read file: %w", err), 1) return cli.Exit(fmt.Errorf("failed to read file: %w", err), 1)
} }
s = string(b) s = string(b)
} else { } else {
if !ctx.Args().Present() { if !ctx.Args().Present() {
return cli.NewExitError("missing script", 1) return cli.Exit("missing script", 1)
} }
s = ctx.Args()[0] s = ctx.Args().Slice()[0]
} }
b, err = base64.StdEncoding.DecodeString(s) b, err = base64.StdEncoding.DecodeString(s)
if err != nil || ctx.Bool("hex") { if err != nil || ctx.Bool("hex") {
b, err = hex.DecodeString(s) b, err = hex.DecodeString(s)
} }
if err != nil { if err != nil {
return cli.NewExitError("unknown encoding: base64 or hex are supported", 1) return cli.Exit("unknown encoding: base64 or hex are supported", 1)
} }
v := vm.New() v := vm.New()
v.LoadScript(b) v.LoadScript(b)

View file

@ -8,30 +8,30 @@ import (
"github.com/nspcc-dev/neo-go/cli/paramcontext" "github.com/nspcc-dev/neo-go/cli/paramcontext"
"github.com/nspcc-dev/neo-go/cli/query" "github.com/nspcc-dev/neo-go/cli/query"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func txDump(ctx *cli.Context) error { func txDump(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args().Slice()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("missing input file", 1) return cli.Exit("missing input file", 1)
} else if len(args) > 1 { } else if len(args) > 1 {
return cli.NewExitError("only one input file is accepted", 1) return cli.Exit("only one input file is accepted", 1)
} }
c, err := paramcontext.Read(args[0]) c, err := paramcontext.Read(args[0])
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
tx, ok := c.Verifiable.(*transaction.Transaction) tx, ok := c.Verifiable.(*transaction.Transaction)
if !ok { if !ok {
return cli.NewExitError("verifiable item is not a transaction", 1) return cli.Exit("verifiable item is not a transaction", 1)
} }
err = query.DumpApplicationLog(ctx, nil, tx, nil, true) err = query.DumpApplicationLog(ctx, nil, tx, nil, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if ctx.String(options.RPCEndpointFlag) != "" { if ctx.String(options.RPCEndpointFlag) != "" {
@ -41,15 +41,15 @@ func txDump(ctx *cli.Context) error {
var err error var err error
cl, err := options.GetRPCClient(gctx, ctx) cl, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
res, err := cl.InvokeScript(tx.Script, tx.Signers) res, err := cl.InvokeScript(tx.Script, tx.Signers)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
resS, err := json.MarshalIndent(res, "", " ") resS, err := json.MarshalIndent(res, "", " ")
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
fmt.Fprintln(ctx.App.Writer, string(resS)) fmt.Fprintln(ctx.App.Writer, string(resS))
} }

View file

@ -8,25 +8,25 @@ import (
"github.com/nspcc-dev/neo-go/cli/txctx" "github.com/nspcc-dev/neo-go/cli/txctx"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter" "github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func sendTx(ctx *cli.Context) error { func sendTx(ctx *cli.Context) error {
args := ctx.Args() args := ctx.Args().Slice()
if len(args) == 0 { if len(args) == 0 {
return cli.NewExitError("missing input file", 1) return cli.Exit("missing input file", 1)
} else if len(args) > 1 { } else if len(args) > 1 {
return cli.NewExitError("only one input file is accepted", 1) return cli.Exit("only one input file is accepted", 1)
} }
pc, err := paramcontext.Read(args[0]) pc, err := paramcontext.Read(args[0])
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
tx, err := pc.GetCompleteTransaction() tx, err := pc.GetCompleteTransaction()
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to complete transaction: %w", err), 1) return cli.Exit(fmt.Errorf("failed to complete transaction: %w", err), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -34,18 +34,18 @@ func sendTx(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create RPC client: %w", err), 1) return cli.Exit(fmt.Errorf("failed to create RPC client: %w", err), 1)
} }
res, err := c.SendRawTransaction(tx) res, err := c.SendRawTransaction(tx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1) return cli.Exit(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1)
} }
var aer *state.AppExecResult var aer *state.AppExecResult
if ctx.Bool("await") { if ctx.Bool("await") {
version, err := c.GetVersion() version, err := c.GetVersion()
aer, err = waiter.New(c, version).Wait(res, tx.ValidUntilBlock, err) aer, err = waiter.New(c, version).Wait(res, tx.ValidUntilBlock, err)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1) return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1)
} }
} }
txctx.DumpTransactionInfo(ctx.App.Writer, res, aer) txctx.DumpTransactionInfo(ctx.App.Writer, res, aer)

View file

@ -44,7 +44,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )
@ -70,22 +70,22 @@ const (
) )
var ( var (
historicFlag = cli.IntFlag{ historicFlag = &cli.IntFlag{
Name: historicFlagFullName, Name: historicFlagFullName,
Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " + Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " +
"Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.", "Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.",
} }
gasFlag = cli.Int64Flag{ gasFlag = &cli.Int64Flag{
Name: gasFlagFullName, Name: gasFlagFullName,
Usage: "GAS limit for this execution (integer number, satoshi).", Usage: "GAS limit for this execution (integer number, satoshi).",
} }
hashFlag = cli.StringFlag{ hashFlag = &cli.StringFlag{
Name: hashFlagFullName, Name: hashFlagFullName,
Usage: "Smart-contract hash in LE form or address", Usage: "Smart-contract hash in LE form or address",
} }
) )
var commands = []cli.Command{ var commands = []*cli.Command{
{ {
Name: "exit", Name: "exit",
Usage: "Exit the VM prompt", Usage: "Exit the VM prompt",
@ -339,8 +339,9 @@ Example:
Usage: "Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration)", Usage: "Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration)",
UsageText: `env [-v]`, UsageText: `env [-v]`,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ &cli.BoolFlag{
Name: verboseFlagFullName + ",v", Name: verboseFlagFullName,
Aliases: []string{"v"},
Usage: "Print the whole blockchain node configuration.", Usage: "Print the whole blockchain node configuration.",
}, },
}, },
@ -353,14 +354,16 @@ Example:
{ {
Name: "storage", Name: "storage",
Usage: "Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation", Usage: "Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation",
UsageText: `storage <hash-or-address-or-id> [<prefix>] [--backwards] [--diff]`, UsageText: `storage [--backwards] [--diff] <hash-or-address-or-id> [<prefix>]`,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ &cli.BoolFlag{
Name: backwardsFlagFullName + ",b", Name: backwardsFlagFullName,
Aliases: []string{"b"},
Usage: "Backwards traversal direction", Usage: "Backwards traversal direction",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: diffFlagFullName + ",d", Name: diffFlagFullName,
Aliases: []string{"d"},
Usage: "Dump only those storage items that were added or changed during the current script invocation. Note that this call won't show removed storage items, use 'changes' command for that.", Usage: "Dump only those storage items that were added or changed during the current script invocation. Note that this call won't show removed storage items, use 'changes' command for that.",
}, },
}, },
@ -400,7 +403,7 @@ func init() {
if !c.Hidden { if !c.Hidden {
var flagsItems []readline.PrefixCompleterInterface var flagsItems []readline.PrefixCompleterInterface
for _, f := range c.Flags { for _, f := range c.Flags {
names := strings.SplitN(f.GetName(), ", ", 2) // only long name will be offered names := strings.SplitN(f.Names()[0], ", ", 2) // only long name will be offered
flagsItems = append(flagsItems, readline.PcItem("--"+names[0])) flagsItems = append(flagsItems, readline.PcItem("--"+names[0]))
} }
pcItems = append(pcItems, readline.PcItem(c.Name, flagsItems...)) pcItems = append(pcItems, readline.PcItem(c.Name, flagsItems...))
@ -459,7 +462,7 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg
log, _, logCloser, err := options.HandleLoggingParams(false, cfg.ApplicationConfiguration) log, _, logCloser, err := options.HandleLoggingParams(false, cfg.ApplicationConfiguration)
if err != nil { if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to init logger: %w", err), 1) return nil, cli.Exit(fmt.Errorf("failed to init logger: %w", err), 1)
} }
filter := zap.WrapCore(func(z zapcore.Core) zapcore.Core { filter := zap.WrapCore(func(z zapcore.Core) zapcore.Core {
return options.NewFilteringCore(z, func(entry zapcore.Entry) bool { return options.NewFilteringCore(z, func(entry zapcore.Entry) bool {
@ -479,12 +482,12 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg
chain, err := core.NewBlockchain(store, cfg.Blockchain(), fLog) chain, err := core.NewBlockchain(store, cfg.Blockchain(), fLog)
if err != nil { if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1) return nil, cli.Exit(fmt.Errorf("could not initialize blockchain: %w", err), 1)
} }
// Do not run chain, we need only state-related functionality from it. // Do not run chain, we need only state-related functionality from it.
ic, err := chain.GetTestVM(trigger.Application, nil, nil) ic, err := chain.GetTestVM(trigger.Application, nil, nil)
if err != nil { if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to create test VM: %w", err), 1) return nil, cli.Exit(fmt.Errorf("failed to create test VM: %w", err), 1)
} }
vmcli := CLI{ vmcli := CLI{
@ -610,7 +613,7 @@ func handleJump(c *cli.Context) error {
} }
func getInstructionParameter(c *cli.Context) (int, error) { func getInstructionParameter(c *cli.Context) (int, error) {
args := c.Args() args := c.Args().Slice()
if len(args) != 1 { if len(args) != 1 {
return 0, fmt.Errorf("%w: <ip>", ErrMissingParameter) return 0, fmt.Errorf("%w: <ip>", ErrMissingParameter)
} }
@ -690,7 +693,7 @@ func getHashFlag(c *cli.Context) (util.Uint160, error) {
} }
func handleLoadNEF(c *cli.Context) error { func handleLoadNEF(c *cli.Context) error {
args := c.Args() args := c.Args().Slice()
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("%w: <nef> is required", ErrMissingParameter) return fmt.Errorf("%w: <nef> is required", ErrMissingParameter)
} }
@ -734,7 +737,7 @@ func handleLoadNEF(c *cli.Context) error {
} }
var signers []transaction.Signer var signers []transaction.Signer
if signersStartOffset != 0 && len(args) > signersStartOffset { if signersStartOffset != 0 && len(args) > signersStartOffset {
signers, err = cmdargs.ParseSigners(c.Args()[signersStartOffset:]) signers, err = cmdargs.ParseSigners(args[signersStartOffset:])
if err != nil { if err != nil {
return fmt.Errorf("%w: failed to parse signers: %w", ErrInvalidParameter, err) return fmt.Errorf("%w: failed to parse signers: %w", ErrInvalidParameter, err)
} }
@ -761,7 +764,7 @@ func handleLoadNEF(c *cli.Context) error {
} }
func handleLoadBase64(c *cli.Context) error { func handleLoadBase64(c *cli.Context) error {
args := c.Args() args := c.Args().Slice()
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("%w: <string>", ErrMissingParameter) return fmt.Errorf("%w: <string>", ErrMissingParameter)
} }
@ -801,7 +804,7 @@ func createFakeTransaction(script []byte, signers []transaction.Signer) *transac
} }
func handleLoadHex(c *cli.Context) error { func handleLoadHex(c *cli.Context) error {
args := c.Args() args := c.Args().Slice()
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("%w: <string>", ErrMissingParameter) return fmt.Errorf("%w: <string>", ErrMissingParameter)
} }
@ -833,7 +836,7 @@ func handleLoadHex(c *cli.Context) error {
} }
func handleLoadGo(c *cli.Context) error { func handleLoadGo(c *cli.Context) error {
args := c.Args() args := c.Args().Slice()
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("%w: <file>", ErrMissingParameter) return fmt.Errorf("%w: <file>", ErrMissingParameter)
} }
@ -885,7 +888,7 @@ func handleLoadGo(c *cli.Context) error {
} }
func handleLoadTx(c *cli.Context) error { func handleLoadTx(c *cli.Context) error {
args := c.Args() args := c.Args().Slice()
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("%w: <file-or-hash>", ErrMissingParameter) return fmt.Errorf("%w: <file-or-hash>", ErrMissingParameter)
} }
@ -933,7 +936,7 @@ func handleLoadDeployed(c *cli.Context) error {
if !c.Args().Present() { if !c.Args().Present() {
return errors.New("contract hash, address or ID is mandatory argument") return errors.New("contract hash, address or ID is mandatory argument")
} }
args := c.Args() args := c.Args().Slice()
hashOrID := args[0] hashOrID := args[0]
ic := getInteropContextFromContext(c.App) ic := getInteropContextFromContext(c.App)
h, err := flags.ParseAddress(hashOrID) h, err := flags.ParseAddress(hashOrID)
@ -1062,7 +1065,7 @@ func getManifestFromFile(name string) (*manifest.Manifest, error) {
func handleRun(c *cli.Context) error { func handleRun(c *cli.Context) error {
v := getVMFromContext(c.App) v := getVMFromContext(c.App)
cs := getContractStateFromContext(c.App) cs := getContractStateFromContext(c.App)
args := c.Args() args := c.Args().Slice()
if len(args) != 0 { if len(args) != 0 {
var ( var (
params []stackitem.Item params []stackitem.Item
@ -1181,7 +1184,7 @@ func handleStep(c *cli.Context) error {
return nil return nil
} }
v := getVMFromContext(c.App) v := getVMFromContext(c.App)
args := c.Args() args := c.Args().Slice()
if len(args) > 0 { if len(args) > 0 {
n, err = strconv.Atoi(args[0]) n, err = strconv.Atoi(args[0])
if err != nil { if err != nil {
@ -1422,7 +1425,7 @@ func (c *CLI) Run() error {
} }
func handleParse(c *cli.Context) error { func handleParse(c *cli.Context) error {
res, err := Parse(c.Args()) res, err := Parse(c.Args().Slice())
if err != nil { if err != nil {
return err return err
} }

View file

@ -1188,7 +1188,7 @@ func TestDumpStorage(t *testing.T) {
"storage "+address.Uint160ToString(h), "storage "+address.Uint160ToString(h),
"storage 1", "storage 1",
"storage 1 "+hex.EncodeToString(expected[0].Key), "storage 1 "+hex.EncodeToString(expected[0].Key),
"storage 1 --backwards", "storage --backwards 1",
"exit", "exit",
) )
e.checkStorage(t, expected...) e.checkStorage(t, expected...)
@ -1214,11 +1214,11 @@ func TestDumpStorageDiff(t *testing.T) {
diff := storage.KeyValue{Key: []byte{3}, Value: []byte{3}} diff := storage.KeyValue{Key: []byte{3}, Value: []byte{3}}
e.runProg(t, e.runProg(t,
"storage 1", "storage 1",
"storage 1 --diff", "storage --diff 1",
"loadhex "+hex.EncodeToString(script.Bytes()), "loadhex "+hex.EncodeToString(script.Bytes()),
"run", "run",
"storage 1", "storage 1",
"storage 1 --diff", "storage --diff 1",
"exit", "exit",
) )

View file

@ -8,14 +8,14 @@ import (
"github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// NewCommands returns 'vm' command. // NewCommands returns 'vm' command.
func NewCommands() []cli.Command { func NewCommands() []*cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath} cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...) cfgFlags = append(cfgFlags, options.Network...)
return []cli.Command{{ return []*cli.Command{{
Name: "vm", Name: "vm",
Usage: "Start the virtual machine", Usage: "Start the virtual machine",
Action: startVMPrompt, Action: startVMPrompt,
@ -30,7 +30,7 @@ func startVMPrompt(ctx *cli.Context) error {
cfg, err := options.GetConfigFromContext(ctx) cfg, err := options.GetConfigFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if ctx.NumFlags() == 0 { if ctx.NumFlags() == 0 {
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
@ -42,7 +42,7 @@ func startVMPrompt(ctx *cli.Context) error {
p, err := NewWithConfig(true, os.Exit, &readline.Config{}, cfg) p, err := NewWithConfig(true, os.Exit, &readline.Config{}, cfg)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create VM CLI: %w", err), 1) return cli.Exit(fmt.Errorf("failed to create VM CLI: %w", err), 1)
} }
return p.Run() return p.Run()
} }

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter" "github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func signStoredTransaction(ctx *cli.Context) error { func signStoredTransaction(ctx *cli.Context) error {
@ -28,52 +28,52 @@ func signStoredTransaction(ctx *cli.Context) error {
pc, err := paramcontext.Read(ctx.String("in")) pc, err := paramcontext.Read(ctx.String("in"))
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if !addrFlag.IsSet { if !addrFlag.IsSet {
return cli.NewExitError("address was not provided", 1) return cli.Exit("address was not provided", 1)
} }
acc, _, err := options.GetAccFromContext(ctx) acc, _, err := options.GetAccFromContext(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
tx, ok := pc.Verifiable.(*transaction.Transaction) tx, ok := pc.Verifiable.(*transaction.Transaction)
if !ok { if !ok {
return cli.NewExitError("verifiable item is not a transaction", 1) return cli.Exit("verifiable item is not a transaction", 1)
} }
if !tx.HasSigner(acc.ScriptHash()) { if !tx.HasSigner(acc.ScriptHash()) {
return cli.NewExitError("tx signers don't contain provided account", 1) return cli.Exit("tx signers don't contain provided account", 1)
} }
if acc.CanSign() { if acc.CanSign() {
sign := acc.SignHashable(pc.Network, pc.Verifiable) sign := acc.SignHashable(pc.Network, pc.Verifiable)
if err := pc.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil { if err := pc.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil {
return cli.NewExitError(fmt.Errorf("can't add signature: %w", err), 1) return cli.Exit(fmt.Errorf("can't add signature: %w", err), 1)
} }
} else if rpcNode == "" { } else if rpcNode == "" {
return cli.NewExitError(fmt.Errorf("can't sign transactions with the given account and no RPC endpoing given to send anything signed"), 1) return cli.Exit(fmt.Errorf("can't sign transactions with the given account and no RPC endpoing given to send anything signed"), 1)
} }
// Not saving and not sending, print. // Not saving and not sending, print.
if out == "" && rpcNode == "" { if out == "" && rpcNode == "" {
txt, err := json.MarshalIndent(pc, " ", " ") txt, err := json.MarshalIndent(pc, " ", " ")
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't display resulting context: %w", err), 1) return cli.Exit(fmt.Errorf("can't display resulting context: %w", err), 1)
} }
fmt.Fprintln(ctx.App.Writer, string(txt)) fmt.Fprintln(ctx.App.Writer, string(txt))
return nil return nil
} }
if out != "" { if out != "" {
if err := paramcontext.Save(pc, out); err != nil { if err := paramcontext.Save(pc, out); err != nil {
return cli.NewExitError(fmt.Errorf("can't save resulting context: %w", err), 1) return cli.Exit(fmt.Errorf("can't save resulting context: %w", err), 1)
} }
} }
if rpcNode != "" { if rpcNode != "" {
tx, err = pc.GetCompleteTransaction() tx, err = pc.GetCompleteTransaction()
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to complete transaction: %w", err), 1) return cli.Exit(fmt.Errorf("failed to complete transaction: %w", err), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -82,17 +82,17 @@ func signStoredTransaction(ctx *cli.Context) error {
var err error // `GetRPCClient` returns specialized type. var err error // `GetRPCClient` returns specialized type.
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create RPC client: %w", err), 1) return cli.Exit(fmt.Errorf("failed to create RPC client: %w", err), 1)
} }
res, err := c.SendRawTransaction(tx) res, err := c.SendRawTransaction(tx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1) return cli.Exit(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1)
} }
if ctx.Bool("await") { if ctx.Bool("await") {
version, err := c.GetVersion() version, err := c.GetVersion()
aer, err = waiter.New(c, version).Wait(res, tx.ValidUntilBlock, err) aer, err = waiter.New(c, version).Wait(res, tx.ValidUntilBlock, err)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1) return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1)
} }
} }
} }

View file

@ -19,20 +19,20 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func newNEP11Commands() []cli.Command { func newNEP11Commands() []*cli.Command {
maxIters := strconv.Itoa(config.DefaultMaxIteratorResultItems) maxIters := strconv.Itoa(config.DefaultMaxIteratorResultItems)
tokenAddressFlag := flags.AddressFlag{ tokenAddressFlag := &flags.AddressFlag{
Name: "token", Name: "token",
Usage: "Token contract address or hash in LE", Usage: "Token contract address or hash in LE",
} }
ownerAddressFlag := flags.AddressFlag{ ownerAddressFlag := &flags.AddressFlag{
Name: "address", Name: "address",
Usage: "NFT owner address or hash in LE", Usage: "NFT owner address or hash in LE",
} }
tokenID := cli.StringFlag{ tokenID := &cli.StringFlag{
Name: "id", Name: "id",
Usage: "Hex-encoded token ID", Usage: "Hex-encoded token ID",
} }
@ -45,7 +45,7 @@ func newNEP11Commands() []cli.Command {
copy(transferFlags, baseTransferFlags) copy(transferFlags, baseTransferFlags)
transferFlags = append(transferFlags, tokenID) transferFlags = append(transferFlags, tokenID)
transferFlags = append(transferFlags, options.RPC...) transferFlags = append(transferFlags, options.RPC...)
return []cli.Command{ return []*cli.Command{
{ {
Name: "balance", Name: "balance",
Usage: "Get address balance", Usage: "Get address balance",
@ -247,16 +247,16 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
} }
tokenHash := ctx.Generic("token").(*flags.Address) tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet { if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1) return cli.Exit("token contract hash was not set", 1)
} }
tokenID := ctx.String("id") tokenID := ctx.String("id")
if tokenID == "" { if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1) return cli.Exit(errors.New("token ID should be specified"), 1)
} }
tokenIDBytes, err := hex.DecodeString(tokenID) tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1) return cli.Exit(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -271,7 +271,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
n11 := nep11.NewDivisibleReader(inv, tokenHash.Uint160()) n11 := nep11.NewDivisibleReader(inv, tokenHash.Uint160())
result, err := n11.OwnerOfExpanded(tokenIDBytes, config.DefaultMaxIteratorResultItems) result, err := n11.OwnerOfExpanded(tokenIDBytes, config.DefaultMaxIteratorResultItems)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 divisible `ownerOf` method: %s", err.Error()), 1) return cli.Exit(fmt.Sprintf("failed to call NEP-11 divisible `ownerOf` method: %s", err.Error()), 1)
} }
for _, h := range result { for _, h := range result {
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(h)) fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(h))
@ -280,7 +280,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
n11 := nep11.NewNonDivisibleReader(inv, tokenHash.Uint160()) n11 := nep11.NewNonDivisibleReader(inv, tokenHash.Uint160())
result, err := n11.OwnerOf(tokenIDBytes) result, err := n11.OwnerOf(tokenIDBytes)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 non-divisible `ownerOf` method: %s", err.Error()), 1) return cli.Exit(fmt.Sprintf("failed to call NEP-11 non-divisible `ownerOf` method: %s", err.Error()), 1)
} }
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(result)) fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(result))
} }
@ -292,12 +292,12 @@ func printNEP11TokensOf(ctx *cli.Context) error {
var err error var err error
tokenHash := ctx.Generic("token").(*flags.Address) tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet { if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1) return cli.Exit("token contract hash was not set", 1)
} }
acc := ctx.Generic("address").(*flags.Address) acc := ctx.Generic("address").(*flags.Address)
if !acc.IsSet { if !acc.IsSet {
return cli.NewExitError("owner address flag was not set", 1) return cli.Exit("owner address flag was not set", 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -311,7 +311,7 @@ func printNEP11TokensOf(ctx *cli.Context) error {
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160()) n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.TokensOfExpanded(acc.Uint160(), config.DefaultMaxIteratorResultItems) result, err := n11.TokensOfExpanded(acc.Uint160(), config.DefaultMaxIteratorResultItems)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `tokensOf` method: %s", err.Error()), 1) return cli.Exit(fmt.Sprintf("failed to call NEP-11 `tokensOf` method: %s", err.Error()), 1)
} }
for i := range result { for i := range result {
@ -327,7 +327,7 @@ func printNEP11Tokens(ctx *cli.Context) error {
} }
tokenHash := ctx.Generic("token").(*flags.Address) tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet { if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1) return cli.Exit("token contract hash was not set", 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -341,7 +341,7 @@ func printNEP11Tokens(ctx *cli.Context) error {
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160()) n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.TokensExpanded(config.DefaultMaxIteratorResultItems) result, err := n11.TokensExpanded(config.DefaultMaxIteratorResultItems)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call optional NEP-11 `tokens` method: %s", err.Error()), 1) return cli.Exit(fmt.Sprintf("failed to call optional NEP-11 `tokens` method: %s", err.Error()), 1)
} }
for i := range result { for i := range result {
@ -357,16 +357,16 @@ func printNEP11Properties(ctx *cli.Context) error {
} }
tokenHash := ctx.Generic("token").(*flags.Address) tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet { if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1) return cli.Exit("token contract hash was not set", 1)
} }
tokenID := ctx.String("id") tokenID := ctx.String("id")
if tokenID == "" { if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1) return cli.Exit(errors.New("token ID should be specified"), 1)
} }
tokenIDBytes, err := hex.DecodeString(tokenID) tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1) return cli.Exit(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -380,12 +380,12 @@ func printNEP11Properties(ctx *cli.Context) error {
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160()) n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.Properties(tokenIDBytes) result, err := n11.Properties(tokenIDBytes)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1) return cli.Exit(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1)
} }
bytes, err := stackitem.ToJSON(result) bytes, err := stackitem.ToJSON(result)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to convert result to JSON: %s", err), 1) return cli.Exit(fmt.Sprintf("failed to convert result to JSON: %s", err), 1)
} }
fmt.Fprintln(ctx.App.Writer, string(bytes)) fmt.Fprintln(ctx.App.Writer, string(bytes))
return nil return nil

View file

@ -27,7 +27,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// transferTarget represents target address, token amount and data for transfer. // transferTarget represents target address, token amount and data for transfer.
@ -39,7 +39,7 @@ type transferTarget struct {
} }
var ( var (
tokenFlag = cli.StringFlag{ tokenFlag = &cli.StringFlag{
Name: "token", Name: "token",
Usage: "Token to use (hash or name (for NEO/GAS or imported tokens))", Usage: "Token to use (hash or name (for NEO/GAS or imported tokens))",
} }
@ -47,15 +47,16 @@ var (
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
tokenFlag, tokenFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to use", Usage: "Address to use",
}, },
} }
importFlags = append([]cli.Flag{ importFlags = append([]cli.Flag{
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "token", Name: "token",
Usage: "Token contract address or hash in LE", Usage: "Token contract address or hash in LE",
}, },
@ -71,7 +72,7 @@ var (
txctx.SysGasFlag, txctx.SysGasFlag,
txctx.ForceFlag, txctx.ForceFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
cli.StringFlag{ &cli.StringFlag{
Name: "amount", Name: "amount",
Usage: "Amount of asset to send", Usage: "Amount of asset to send",
}, },
@ -88,14 +89,14 @@ var (
}, options.RPC...) }, options.RPC...)
) )
func newNEP17Commands() []cli.Command { func newNEP17Commands() []*cli.Command {
balanceFlags := make([]cli.Flag, len(baseBalanceFlags)) balanceFlags := make([]cli.Flag, len(baseBalanceFlags))
copy(balanceFlags, baseBalanceFlags) copy(balanceFlags, baseBalanceFlags)
balanceFlags = append(balanceFlags, options.RPC...) balanceFlags = append(balanceFlags, options.RPC...)
transferFlags := make([]cli.Flag, len(baseTransferFlags)) transferFlags := make([]cli.Flag, len(baseTransferFlags))
copy(transferFlags, baseTransferFlags) copy(transferFlags, baseTransferFlags)
transferFlags = append(transferFlags, options.RPC...) transferFlags = append(transferFlags, options.RPC...)
return []cli.Command{ return []*cli.Command{
{ {
Name: "balance", Name: "balance",
Usage: "Get address balance", Usage: "Get address balance",
@ -222,7 +223,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
} }
wall, _, err := readWallet(ctx) wall, _, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("bad wallet: %w", err), 1) return cli.Exit(fmt.Errorf("bad wallet: %w", err), 1)
} }
defer wall.Close() defer wall.Close()
@ -231,12 +232,12 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
addrHash := addrFlag.Uint160() addrHash := addrFlag.Uint160()
acc := wall.GetAccount(addrHash) acc := wall.GetAccount(addrHash)
if acc == nil { if acc == nil {
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1) return cli.Exit(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
} }
accounts = append(accounts, acc) accounts = append(accounts, acc)
} else { } else {
if len(wall.Accounts) == 0 { if len(wall.Accounts) == 0 {
return cli.NewExitError(errors.New("no accounts in the wallet"), 1) return cli.Exit(errors.New("no accounts in the wallet"), 1)
} }
accounts = wall.Accounts accounts = wall.Accounts
} }
@ -246,7 +247,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
name := ctx.String("token") name := ctx.String("token")
@ -274,7 +275,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
// But if we have an exact hash, it must be correct. // But if we have an exact hash, it must be correct.
token, err = getTokenWithStandard(c, h, standard) token, err = getTokenWithStandard(c, h, standard)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("%q is not a valid %s token: %w", name, standard, err), 1) return cli.Exit(fmt.Errorf("%q is not a valid %s token: %w", name, standard, err), 1)
} }
} }
} }
@ -284,7 +285,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
if len(tokenID) > 0 { if len(tokenID) > 0 {
_, err = hex.DecodeString(tokenID) _, err = hex.DecodeString(tokenID)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid token ID: %w", err), 1) return cli.Exit(fmt.Errorf("invalid token ID: %w", err), 1)
} }
} }
} }
@ -296,7 +297,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
err = accHandler(ctx, c, acc.ScriptHash(), name, token, tokenID) err = accHandler(ctx, c, acc.ScriptHash(), name, token, tokenID)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
} }
return nil return nil
@ -388,20 +389,20 @@ func importNEPToken(ctx *cli.Context, standard string) error {
} }
wall, _, err := openWallet(ctx, true) wall, _, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
tokenHashFlag := ctx.Generic("token").(*flags.Address) tokenHashFlag := ctx.Generic("token").(*flags.Address)
if !tokenHashFlag.IsSet { if !tokenHashFlag.IsSet {
return cli.NewExitError("token contract hash was not set", 1) return cli.Exit("token contract hash was not set", 1)
} }
tokenHash := tokenHashFlag.Uint160() tokenHash := tokenHashFlag.Uint160()
for _, t := range wall.Extra.Tokens { for _, t := range wall.Extra.Tokens {
if t.Hash.Equals(tokenHash) && t.Standard == standard { if t.Hash.Equals(tokenHash) && t.Standard == standard {
printTokenInfo(ctx, t) printTokenInfo(ctx, t)
return cli.NewExitError(fmt.Errorf("%s token already exists", standard), 1) return cli.Exit(fmt.Errorf("%s token already exists", standard), 1)
} }
} }
@ -410,17 +411,17 @@ func importNEPToken(ctx *cli.Context, standard string) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
tok, err := getTokenWithStandard(c, tokenHash, standard) tok, err := getTokenWithStandard(c, tokenHash, standard)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't receive token info: %w", err), 1) return cli.Exit(fmt.Errorf("can't receive token info: %w", err), 1)
} }
wall.AddToken(tok) wall.AddToken(tok)
if err := wall.Save(); err != nil { if err := wall.Save(); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
printTokenInfo(ctx, tok) printTokenInfo(ctx, tok)
return nil return nil
@ -457,14 +458,14 @@ func printNEPInfo(ctx *cli.Context, standard string) error {
} }
wall, _, err := readWallet(ctx) wall, _, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
if name := ctx.String("token"); name != "" { if name := ctx.String("token"); name != "" {
token, err := getMatchingToken(ctx, wall, name, standard) token, err := getMatchingToken(ctx, wall, name, standard)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
printTokenInfo(ctx, token) printTokenInfo(ctx, token)
return nil return nil
@ -493,13 +494,13 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
} }
wall, _, err := openWallet(ctx, true) wall, _, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard) token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if !ctx.Bool("force") { if !ctx.Bool("force") {
if ok := askForConsent(ctx.App.Writer); !ok { if ok := askForConsent(ctx.App.Writer); !ok {
@ -507,9 +508,9 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
} }
} }
if err := wall.RemoveToken(token.Hash); err != nil { if err := wall.RemoveToken(token.Hash); err != nil {
return cli.NewExitError(fmt.Errorf("can't remove token: %w", err), 1) return cli.Exit(fmt.Errorf("can't remove token: %w", err), 1)
} else if err := wall.Save(); err != nil { } else if err := wall.Save(); err != nil {
return cli.NewExitError(fmt.Errorf("error while saving wallet: %w", err), 1) return cli.Exit(fmt.Errorf("error while saving wallet: %w", err), 1)
} }
return nil return nil
} }
@ -517,25 +518,25 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
func multiTransferNEP17(ctx *cli.Context) error { func multiTransferNEP17(ctx *cli.Context) error {
wall, pass, err := readWallet(ctx) wall, pass, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
fromFlag := ctx.Generic("from").(*flags.Address) fromFlag := ctx.Generic("from").(*flags.Address)
from, err := getDefaultAddress(fromFlag, wall) from, err := getDefaultAddress(fromFlag, wall)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
acc, err := options.GetUnlockedAccount(wall, from, pass) acc, err := options.GetUnlockedAccount(wall, from, pass)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel() defer cancel()
if ctx.NArg() == 0 { if ctx.NArg() == 0 {
return cli.NewExitError("empty recipients list", 1) return cli.Exit("empty recipients list", 1)
} }
var ( var (
recipients []transferTarget recipients []transferTarget
@ -554,7 +555,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
} }
signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry) signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
} }
c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts) c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts)
if exitErr != nil { if exitErr != nil {
@ -566,7 +567,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
arg := ctx.Args().Get(i) arg := ctx.Args().Get(i)
ss := strings.SplitN(arg, ":", 3) ss := strings.SplitN(arg, ":", 3)
if len(ss) != 3 { if len(ss) != 3 {
return cli.NewExitError("send format must be '<token>:<addr>:<amount>", 1) return cli.Exit("send format must be '<token>:<addr>:<amount>", 1)
} }
token, ok := cache[ss[0]] token, ok := cache[ss[0]]
if !ok { if !ok {
@ -574,18 +575,18 @@ func multiTransferNEP17(ctx *cli.Context) error {
if err != nil { if err != nil {
token, err = getMatchingTokenRPC(ctx, c, from, ss[0], manifest.NEP17StandardName) token, err = getMatchingTokenRPC(ctx, c, from, ss[0], manifest.NEP17StandardName)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1) return cli.Exit(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
} }
} }
} }
cache[ss[0]] = token cache[ss[0]] = token
addr, err := address.StringToUint160(ss[1]) addr, err := address.StringToUint160(ss[1])
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid address: '%s'", ss[1]), 1) return cli.Exit(fmt.Errorf("invalid address: '%s'", ss[1]), 1)
} }
amount, err := fixedn.FromString(ss[2], int(token.Decimals)) amount, err := fixedn.FromString(ss[2], int(token.Decimals))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1) return cli.Exit(fmt.Errorf("invalid amount: %w", err), 1)
} }
recipients = append(recipients, transferTarget{ recipients = append(recipients, transferTarget{
Token: token.Hash, Token: token.Hash,
@ -597,7 +598,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
tx, err := makeMultiTransferNEP17(act, recipients) tx, err := makeMultiTransferNEP17(act, recipients)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't make transaction: %w", err), 1) return cli.Exit(fmt.Errorf("can't make transaction: %w", err), 1)
} }
return txctx.SignAndSend(ctx, act, acc, tx) return txctx.SignAndSend(ctx, act, acc, tx)
} }
@ -611,18 +612,18 @@ func transferNEP(ctx *cli.Context, standard string) error {
wall, pass, err := readWallet(ctx) wall, pass, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
fromFlag := ctx.Generic("from").(*flags.Address) fromFlag := ctx.Generic("from").(*flags.Address)
from, err := getDefaultAddress(fromFlag, wall) from, err := getDefaultAddress(fromFlag, wall)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
acc, err := options.GetUnlockedAccount(wall, from, pass) acc, err := options.GetUnlockedAccount(wall, from, pass)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -638,7 +639,7 @@ func transferNEP(ctx *cli.Context, standard string) error {
} }
signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry) signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
} }
c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts) c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts)
@ -648,14 +649,14 @@ func transferNEP(ctx *cli.Context, standard string) error {
toFlag := ctx.Generic("to").(*flags.Address) toFlag := ctx.Generic("to").(*flags.Address)
if !toFlag.IsSet { if !toFlag.IsSet {
return cli.NewExitError(errors.New("missing receiver address (--to)"), 1) return cli.Exit(errors.New("missing receiver address (--to)"), 1)
} }
to := toFlag.Uint160() to := toFlag.Uint160()
token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard) token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard)
if err != nil { if err != nil {
token, err = getMatchingTokenRPC(ctx, c, from, ctx.String("token"), standard) token, err = getMatchingTokenRPC(ctx, c, from, ctx.String("token"), standard)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1) return cli.Exit(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
} }
} }
@ -663,7 +664,7 @@ func transferNEP(ctx *cli.Context, standard string) error {
amount, err := fixedn.FromString(amountArg, int(token.Decimals)) amount, err := fixedn.FromString(amountArg, int(token.Decimals))
// It's OK for NEP-11 transfer to not have amount set. // It's OK for NEP-11 transfer to not have amount set.
if err != nil && (standard == manifest.NEP17StandardName || amountArg != "") { if err != nil && (standard == manifest.NEP17StandardName || amountArg != "") {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1) return cli.Exit(fmt.Errorf("invalid amount: %w", err), 1)
} }
switch standard { switch standard {
case manifest.NEP17StandardName: case manifest.NEP17StandardName:
@ -672,11 +673,11 @@ func transferNEP(ctx *cli.Context, standard string) error {
case manifest.NEP11StandardName: case manifest.NEP11StandardName:
tokenID := ctx.String("id") tokenID := ctx.String("id")
if tokenID == "" { if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1) return cli.Exit(errors.New("token ID should be specified"), 1)
} }
tokenIDBytes, terr := hex.DecodeString(tokenID) tokenIDBytes, terr := hex.DecodeString(tokenID)
if terr != nil { if terr != nil {
return cli.NewExitError(fmt.Errorf("invalid token ID: %w", terr), 1) return cli.Exit(fmt.Errorf("invalid token ID: %w", terr), 1)
} }
if amountArg == "" { if amountArg == "" {
n11 := nep11.NewNonDivisible(act, token.Hash) n11 := nep11.NewNonDivisible(act, token.Hash)
@ -686,10 +687,10 @@ func transferNEP(ctx *cli.Context, standard string) error {
tx, err = n11.TransferDUnsigned(act.Sender(), to, amount, tokenIDBytes, data) tx, err = n11.TransferDUnsigned(act.Sender(), to, amount, tokenIDBytes, data)
} }
default: default:
return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1) return cli.Exit(fmt.Errorf("unsupported token standard %s", standard), 1)
} }
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't make transaction: %w", err), 1) return cli.Exit(fmt.Errorf("can't make transaction: %w", err), 1)
} }
return txctx.SignAndSend(ctx, act, acc, tx) return txctx.SignAndSend(ctx, act, acc, tx)

View file

@ -12,11 +12,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
func newValidatorCommands() []cli.Command { func newValidatorCommands() []*cli.Command {
return []cli.Command{ return []*cli.Command{
{ {
Name: "register", Name: "register",
Usage: "Register as a new candidate", Usage: "Register as a new candidate",
@ -30,8 +30,9 @@ func newValidatorCommands() []cli.Command {
txctx.OutFlag, txctx.OutFlag,
txctx.ForceFlag, txctx.ForceFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to register", Usage: "Address to register",
}, },
}, options.RPC...), }, options.RPC...),
@ -49,8 +50,9 @@ func newValidatorCommands() []cli.Command {
txctx.OutFlag, txctx.OutFlag,
txctx.ForceFlag, txctx.ForceFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to unregister", Usage: "Address to unregister",
}, },
}, options.RPC...), }, options.RPC...),
@ -72,12 +74,14 @@ func newValidatorCommands() []cli.Command {
txctx.OutFlag, txctx.OutFlag,
txctx.ForceFlag, txctx.ForceFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to vote from", Usage: "Address to vote from",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "candidate, c", Name: "candidate",
Aliases: []string{"c"},
Usage: "Public key of candidate to vote for", Usage: "Public key of candidate to vote for",
}, },
}, options.RPC...), }, options.RPC...),
@ -103,18 +107,18 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
} }
wall, pass, err := readWallet(ctx) wall, pass, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address) addrFlag := ctx.Generic("address").(*flags.Address)
if !addrFlag.IsSet { if !addrFlag.IsSet {
return cli.NewExitError("address was not provided", 1) return cli.Exit("address was not provided", 1)
} }
addr := addrFlag.Uint160() addr := addrFlag.Uint160()
acc, err := options.GetUnlockedAccount(wall, addr, pass) acc, err := options.GetUnlockedAccount(wall, addr, pass)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -122,7 +126,7 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
signers, err := cmdargs.GetSignersAccounts(acc, wall, nil, transaction.CalledByEntry) signers, err := cmdargs.GetSignersAccounts(acc, wall, nil, transaction.CalledByEntry)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
} }
_, act, exitErr := options.GetRPCWithActor(gctx, ctx, signers) _, act, exitErr := options.GetRPCWithActor(gctx, ctx, signers)
if exitErr != nil { if exitErr != nil {
@ -132,7 +136,7 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
contract := neo.New(act) contract := neo.New(act)
tx, err := mkTx(contract, addr, acc) tx, err := mkTx(contract, addr, acc)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return txctx.SignAndSend(ctx, act, acc, tx) return txctx.SignAndSend(ctx, act, acc, tx)
} }

View file

@ -24,7 +24,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
const ( const (
@ -47,38 +47,40 @@ var (
) )
var ( var (
walletPathFlag = cli.StringFlag{ walletPathFlag = &cli.StringFlag{
Name: "wallet, w", Name: "wallet",
Aliases: []string{"w"},
Usage: "Path to the wallet file ('-' to read from stdin); conflicts with --wallet-config flag.", Usage: "Path to the wallet file ('-' to read from stdin); conflicts with --wallet-config flag.",
} }
walletConfigFlag = cli.StringFlag{ walletConfigFlag = &cli.StringFlag{
Name: "wallet-config", Name: "wallet-config",
Usage: "Path to the wallet config file; conflicts with --wallet flag.", Usage: "Path to the wallet config file; conflicts with --wallet flag.",
} }
wifFlag = cli.StringFlag{ wifFlag = &cli.StringFlag{
Name: "wif", Name: "wif",
Usage: "WIF to import", Usage: "WIF to import",
} }
decryptFlag = cli.BoolFlag{ decryptFlag = &cli.BoolFlag{
Name: "decrypt, d", Name: "decrypt",
Aliases: []string{"d"},
Usage: "Decrypt encrypted keys.", Usage: "Decrypt encrypted keys.",
} }
inFlag = cli.StringFlag{ inFlag = &cli.StringFlag{
Name: "in", Name: "in",
Usage: "File with JSON transaction", Usage: "File with JSON transaction",
} }
fromAddrFlag = flags.AddressFlag{ fromAddrFlag = &flags.AddressFlag{
Name: "from", Name: "from",
Usage: "Address to send an asset from", Usage: "Address to send an asset from",
} }
toAddrFlag = flags.AddressFlag{ toAddrFlag = &flags.AddressFlag{
Name: "to", Name: "to",
Usage: "Address to send an asset to", Usage: "Address to send an asset to",
} }
) )
// NewCommands returns 'wallet' command. // NewCommands returns 'wallet' command.
func NewCommands() []cli.Command { func NewCommands() []*cli.Command {
claimFlags := []cli.Flag{ claimFlags := []cli.Flag{
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
@ -87,8 +89,9 @@ func NewCommands() []cli.Command {
txctx.OutFlag, txctx.OutFlag,
txctx.ForceFlag, txctx.ForceFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to claim GAS for", Usage: "Address to claim GAS for",
}, },
} }
@ -99,16 +102,17 @@ func NewCommands() []cli.Command {
txctx.OutFlag, txctx.OutFlag,
txctx.AwaitFlag, txctx.AwaitFlag,
inFlag, inFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to use", Usage: "Address to use",
}, },
} }
signFlags = append(signFlags, options.RPC...) signFlags = append(signFlags, options.RPC...)
return []cli.Command{{ return []*cli.Command{{
Name: "wallet", Name: "wallet",
Usage: "Create, open and manage a Neo wallet", Usage: "Create, open and manage a Neo wallet",
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "claim", Name: "claim",
Usage: "Claim GAS", Usage: "Claim GAS",
@ -124,8 +128,9 @@ func NewCommands() []cli.Command {
Flags: []cli.Flag{ Flags: []cli.Flag{
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
cli.BoolFlag{ &cli.BoolFlag{
Name: "account, a", Name: "account",
Aliases: []string{"a"},
Usage: "Create a new account", Usage: "Create a new account",
}, },
}, },
@ -137,8 +142,9 @@ func NewCommands() []cli.Command {
Action: changePassword, Action: changePassword,
Flags: []cli.Flag{ Flags: []cli.Flag{
walletPathFlag, walletPathFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to change password for", Usage: "Address to change password for",
}, },
}, },
@ -151,8 +157,9 @@ func NewCommands() []cli.Command {
Flags: []cli.Flag{ Flags: []cli.Flag{
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
cli.StringFlag{ &cli.StringFlag{
Name: "out, o", Name: "out",
Aliases: []string{"o"},
Usage: "Where to write converted wallet", Usage: "Where to write converted wallet",
}, },
}, },
@ -191,8 +198,9 @@ func NewCommands() []cli.Command {
Flags: []cli.Flag{ Flags: []cli.Flag{
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Address to print public keys for", Usage: "Address to print public keys for",
}, },
}, },
@ -223,11 +231,12 @@ func NewCommands() []cli.Command {
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
wifFlag, wifFlag,
cli.StringFlag{ &cli.StringFlag{
Name: "name, n", Name: "name",
Aliases: []string{"n"},
Usage: "Optional account name", Usage: "Optional account name",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "contract", Name: "contract",
Usage: "Verification script for custom contracts", Usage: "Verification script for custom contracts",
}, },
@ -250,12 +259,14 @@ func NewCommands() []cli.Command {
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
wifFlag, wifFlag,
cli.StringFlag{ &cli.StringFlag{
Name: "name, n", Name: "name",
Aliases: []string{"n"},
Usage: "Optional account name", Usage: "Optional account name",
}, },
cli.IntFlag{ &cli.IntFlag{
Name: "min, m", Name: "min",
Aliases: []string{"m"},
Usage: "Minimal number of signatures", Usage: "Minimal number of signatures",
}, },
}, },
@ -269,12 +280,14 @@ func NewCommands() []cli.Command {
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
wifFlag, wifFlag,
cli.StringFlag{ &cli.StringFlag{
Name: "name, n", Name: "name",
Aliases: []string{"n"},
Usage: "Optional account name", Usage: "Optional account name",
}, },
flags.AddressFlag{ &flags.AddressFlag{
Name: "contract, c", Name: "contract",
Aliases: []string{"c"},
Usage: "Contract hash or address", Usage: "Contract hash or address",
}, },
}, options.RPC...), }, options.RPC...),
@ -288,8 +301,9 @@ func NewCommands() []cli.Command {
walletPathFlag, walletPathFlag,
walletConfigFlag, walletConfigFlag,
txctx.ForceFlag, txctx.ForceFlag,
flags.AddressFlag{ &flags.AddressFlag{
Name: "address, a", Name: "address",
Aliases: []string{"a"},
Usage: "Account address or hash in LE form to be removed", Usage: "Account address or hash in LE form to be removed",
}, },
}, },
@ -358,24 +372,24 @@ func changePassword(ctx *cli.Context) error {
} }
wall, _, err := openWallet(ctx, false) wall, _, err := openWallet(ctx, false)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
if len(wall.Accounts) == 0 { if len(wall.Accounts) == 0 {
return cli.NewExitError("wallet has no accounts", 1) return cli.Exit("wallet has no accounts", 1)
} }
addrFlag := ctx.Generic("address").(*flags.Address) addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet { if addrFlag.IsSet {
// Check for account presence first before asking for password. // Check for account presence first before asking for password.
acc := wall.GetAccount(addrFlag.Uint160()) acc := wall.GetAccount(addrFlag.Uint160())
if acc == nil { if acc == nil {
return cli.NewExitError("account is missing", 1) return cli.Exit("account is missing", 1)
} }
} }
oldPass, err := input.ReadPassword(EnterOldPasswordPrompt) oldPass, err := input.ReadPassword(EnterOldPasswordPrompt)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading old password: %w", err), 1) return cli.Exit(fmt.Errorf("Error reading old password: %w", err), 1)
} }
for i := range wall.Accounts { for i := range wall.Accounts {
@ -384,13 +398,13 @@ func changePassword(ctx *cli.Context) error {
} }
err := wall.Accounts[i].Decrypt(oldPass, wall.Scrypt) err := wall.Accounts[i].Decrypt(oldPass, wall.Scrypt)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("unable to decrypt account %s: %w", wall.Accounts[i].Address, err), 1) return cli.Exit(fmt.Errorf("unable to decrypt account %s: %w", wall.Accounts[i].Address, err), 1)
} }
} }
pass, err := readNewPassword() pass, err := readNewPassword()
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading new password: %w", err), 1) return cli.Exit(fmt.Errorf("Error reading new password: %w", err), 1)
} }
for i := range wall.Accounts { for i := range wall.Accounts {
if addrFlag.IsSet && wall.Accounts[i].Address != addrFlag.String() { if addrFlag.IsSet && wall.Accounts[i].Address != addrFlag.String() {
@ -398,12 +412,12 @@ func changePassword(ctx *cli.Context) error {
} }
err := wall.Accounts[i].Encrypt(pass, wall.Scrypt) err := wall.Accounts[i].Encrypt(pass, wall.Scrypt)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
} }
err = wall.Save() err = wall.Save()
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("Error saving the wallet: %w", err), 1) return cli.Exit(fmt.Errorf("Error saving the wallet: %w", err), 1)
} }
return nil return nil
} }
@ -414,16 +428,16 @@ func convertWallet(ctx *cli.Context) error {
} }
wall, pass, err := newWalletV2FromFile(ctx.String("wallet"), ctx.String("wallet-config")) wall, pass, err := newWalletV2FromFile(ctx.String("wallet"), ctx.String("wallet-config"))
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
out := ctx.String("out") out := ctx.String("out")
if len(out) == 0 { if len(out) == 0 {
return cli.NewExitError("missing out path", 1) return cli.Exit("missing out path", 1)
} }
newWallet, err := wallet.NewWallet(out) newWallet, err := wallet.NewWallet(out)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
newWallet.Scrypt = wall.Scrypt newWallet.Scrypt = wall.Scrypt
@ -431,19 +445,19 @@ func convertWallet(ctx *cli.Context) error {
if len(wall.Accounts) != 1 || pass == nil { if len(wall.Accounts) != 1 || pass == nil {
password, err := input.ReadPassword(fmt.Sprintf("Enter password for account %s (label '%s') > ", acc.Address, acc.Label)) password, err := input.ReadPassword(fmt.Sprintf("Enter password for account %s (label '%s') > ", acc.Address, acc.Label))
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1) return cli.Exit(fmt.Errorf("Error reading password: %w", err), 1)
} }
pass = &password pass = &password
} }
newAcc, err := acc.convert(*pass, wall.Scrypt) newAcc, err := acc.convert(*pass, wall.Scrypt)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
newWallet.AddAccount(newAcc) newWallet.AddAccount(newAcc)
} }
if err := newWallet.Save(); err != nil { if err := newWallet.Save(); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
} }
@ -454,12 +468,12 @@ func addAccount(ctx *cli.Context) error {
} }
wall, pass, err := openWallet(ctx, true) wall, pass, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
if err := createAccount(wall, pass); err != nil { if err := createAccount(wall, pass); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
@ -468,7 +482,7 @@ func addAccount(ctx *cli.Context) error {
func exportKeys(ctx *cli.Context) error { func exportKeys(ctx *cli.Context) error {
wall, pass, err := readWallet(ctx) wall, pass, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
@ -476,13 +490,13 @@ func exportKeys(ctx *cli.Context) error {
decrypt := ctx.Bool("decrypt") decrypt := ctx.Bool("decrypt")
if ctx.NArg() == 0 && decrypt { if ctx.NArg() == 0 && decrypt {
return cli.NewExitError(errors.New("address must be provided if '--decrypt' flag is used"), 1) return cli.Exit(errors.New("address must be provided if '--decrypt' flag is used"), 1)
} else if ctx.NArg() > 0 { } else if ctx.NArg() > 0 {
// check address format just to catch possible typos // check address format just to catch possible typos
addr = ctx.Args().First() addr = ctx.Args().First()
_, err := address.StringToUint160(addr) _, err := address.StringToUint160(addr)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't parse address: %w", err), 1) return cli.Exit(fmt.Errorf("can't parse address: %w", err), 1)
} }
} }
@ -508,14 +522,14 @@ loop:
if pass == nil { if pass == nil {
password, err := input.ReadPassword(EnterPasswordPrompt) password, err := input.ReadPassword(EnterPasswordPrompt)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1) return cli.Exit(fmt.Errorf("Error reading password: %w", err), 1)
} }
pass = &password pass = &password
} }
pk, err := keys.NEP2Decrypt(wif, *pass, wall.Scrypt) pk, err := keys.NEP2Decrypt(wif, *pass, wall.Scrypt)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
wif = pk.WIF() wif = pk.WIF()
@ -536,22 +550,22 @@ func importMultisig(ctx *cli.Context) error {
wall, pass, err := openWallet(ctx, true) wall, pass, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
m := ctx.Int("min") m := ctx.Int("min")
if ctx.NArg() < m { if ctx.NArg() < m {
return cli.NewExitError(errors.New("insufficient number of public keys"), 1) return cli.Exit(errors.New("insufficient number of public keys"), 1)
} }
args := []string(ctx.Args()) args := ctx.Args().Slice()
pubs := make([]*keys.PublicKey, len(args)) pubs := make([]*keys.PublicKey, len(args))
for i := range args { for i := range args {
pubs[i], err = keys.NewPublicKeyFromString(args[i]) pubs[i], err = keys.NewPublicKeyFromString(args[i])
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't decode public key %d: %w", i, err), 1) return cli.Exit(fmt.Errorf("can't decode public key %d: %w", i, err), 1)
} }
} }
@ -579,31 +593,31 @@ loop:
if acc != nil { if acc != nil {
err = acc.ConvertMultisigEncrypted(accPub, m, pubs) err = acc.ConvertMultisigEncrypted(accPub, m, pubs)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if label != nil { if label != nil {
acc.Label = *label acc.Label = *label
} }
if err := addAccountAndSave(wall, acc); err != nil { if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
} }
if !ctx.IsSet("wif") { if !ctx.IsSet("wif") {
return cli.NewExitError(errors.New("none of the provided public keys correspond to an existing key in the wallet or multiple matching accounts found in the wallet, and no WIF is provided"), 1) return cli.Exit(errors.New("none of the provided public keys correspond to an existing key in the wallet or multiple matching accounts found in the wallet, and no WIF is provided"), 1)
} }
acc, err = newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass) acc, err = newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if err := acc.ConvertMultisig(m, pubs); err != nil { if err := acc.ConvertMultisig(m, pubs); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if err := addAccountAndSave(wall, acc); err != nil { if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
@ -615,13 +629,13 @@ func importDeployed(ctx *cli.Context) error {
} }
wall, pass, err := openWallet(ctx, true) wall, pass, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
rawHash := ctx.Generic("contract").(*flags.Address) rawHash := ctx.Generic("contract").(*flags.Address)
if !rawHash.IsSet { if !rawHash.IsSet {
return cli.NewExitError("contract hash was not provided", 1) return cli.Exit("contract hash was not provided", 1)
} }
var label *string var label *string
@ -631,7 +645,7 @@ func importDeployed(ctx *cli.Context) error {
} }
acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass) acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
gctx, cancel := options.GetTimeoutContext(ctx) gctx, cancel := options.GetTimeoutContext(ctx)
@ -639,16 +653,16 @@ func importDeployed(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx) c, err := options.GetRPCClient(gctx, ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
cs, err := c.GetContractStateByHash(rawHash.Uint160()) cs, err := c.GetContractStateByHash(rawHash.Uint160())
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch contract info: %w", err), 1) return cli.Exit(fmt.Errorf("can't fetch contract info: %w", err), 1)
} }
md := cs.Manifest.ABI.GetMethod(manifest.MethodVerify, -1) md := cs.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
if md == nil || md.ReturnType != smartcontract.BoolType { if md == nil || md.ReturnType != smartcontract.BoolType {
return cli.NewExitError("contract has no `verify` method with boolean return", 1) return cli.Exit("contract has no `verify` method with boolean return", 1)
} }
acc.Address = address.Uint160ToString(cs.Hash) acc.Address = address.Uint160ToString(cs.Hash)
// Explicitly overwrite single signature script of the provided WIF since the contract is known to be deployed. // Explicitly overwrite single signature script of the provided WIF since the contract is known to be deployed.
@ -663,7 +677,7 @@ func importDeployed(ctx *cli.Context) error {
acc.Contract.Deployed = true acc.Contract.Deployed = true
if err := addAccountAndSave(wall, acc); err != nil { if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
@ -675,7 +689,7 @@ func importWallet(ctx *cli.Context) error {
} }
wall, pass, err := openWallet(ctx, true) wall, pass, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
@ -687,19 +701,19 @@ func importWallet(ctx *cli.Context) error {
acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass) acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if ctrFlag := ctx.String("contract"); ctrFlag != "" { if ctrFlag := ctx.String("contract"); ctrFlag != "" {
ctr, err := hex.DecodeString(ctrFlag) ctr, err := hex.DecodeString(ctrFlag)
if err != nil { if err != nil {
return cli.NewExitError("invalid contract", 1) return cli.Exit("invalid contract", 1)
} }
acc.Contract.Script = ctr acc.Contract.Script = ctr
} }
if err := addAccountAndSave(wall, acc); err != nil { if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
return nil return nil
@ -711,17 +725,17 @@ func removeAccount(ctx *cli.Context) error {
} }
wall, _, err := openWallet(ctx, true) wall, _, err := openWallet(ctx, true)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
addr := ctx.Generic("address").(*flags.Address) addr := ctx.Generic("address").(*flags.Address)
if !addr.IsSet { if !addr.IsSet {
return cli.NewExitError("valid account address must be provided", 1) return cli.Exit("valid account address must be provided", 1)
} }
acc := wall.GetAccount(addr.Uint160()) acc := wall.GetAccount(addr.Uint160())
if acc == nil { if acc == nil {
return cli.NewExitError("account wasn't found", 1) return cli.Exit("account wasn't found", 1)
} }
if !ctx.Bool("force") { if !ctx.Bool("force") {
@ -732,10 +746,10 @@ func removeAccount(ctx *cli.Context) error {
} }
if err := wall.RemoveAccount(acc.Address); err != nil { if err := wall.RemoveAccount(acc.Address); err != nil {
return cli.NewExitError(fmt.Errorf("error on remove: %w", err), 1) return cli.Exit(fmt.Errorf("error on remove: %w", err), 1)
} }
if err := wall.Save(); err != nil { if err := wall.Save(); err != nil {
return cli.NewExitError(fmt.Errorf("error while saving wallet: %w", err), 1) return cli.Exit(fmt.Errorf("error while saving wallet: %w", err), 1)
} }
return nil return nil
} }
@ -758,14 +772,14 @@ func dumpWallet(ctx *cli.Context) error {
} }
wall, pass, err := readWallet(ctx) wall, pass, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
if ctx.Bool("decrypt") { if ctx.Bool("decrypt") {
if pass == nil { if pass == nil {
password, err := input.ReadPassword(EnterPasswordPrompt) password, err := input.ReadPassword(EnterPasswordPrompt)
if err != nil { if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1) return cli.Exit(fmt.Errorf("Error reading password: %w", err), 1)
} }
pass = &password pass = &password
} }
@ -773,7 +787,7 @@ func dumpWallet(ctx *cli.Context) error {
// Just testing the decryption here. // Just testing the decryption here.
err := wall.Accounts[i].Decrypt(*pass, wall.Scrypt) err := wall.Accounts[i].Decrypt(*pass, wall.Scrypt)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
} }
} }
@ -787,7 +801,7 @@ func dumpKeys(ctx *cli.Context) error {
} }
wall, _, err := readWallet(ctx) wall, _, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
accounts := wall.Accounts accounts := wall.Accounts
@ -796,7 +810,7 @@ func dumpKeys(ctx *cli.Context) error {
if addrFlag.IsSet { if addrFlag.IsSet {
acc := wall.GetAccount(addrFlag.Uint160()) acc := wall.GetAccount(addrFlag.Uint160())
if acc == nil { if acc == nil {
return cli.NewExitError("account is missing", 1) return cli.Exit("account is missing", 1)
} }
accounts = []*wallet.Account{acc} accounts = []*wallet.Account{acc}
} }
@ -826,7 +840,7 @@ func dumpKeys(ctx *cli.Context) error {
continue continue
} }
if addrFlag.IsSet { if addrFlag.IsSet {
return cli.NewExitError(fmt.Errorf("unknown script type for address %s", address.Uint160ToString(addrFlag.Uint160())), 1) return cli.Exit(fmt.Errorf("unknown script type for address %s", address.Uint160ToString(addrFlag.Uint160())), 1)
} }
} }
return nil return nil
@ -838,7 +852,7 @@ func stripKeys(ctx *cli.Context) error {
} }
wall, _, err := readWallet(ctx) wall, _, err := readWallet(ctx)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
if !ctx.Bool("force") { if !ctx.Bool("force") {
@ -851,7 +865,7 @@ func stripKeys(ctx *cli.Context) error {
a.EncryptedWIF = "" a.EncryptedWIF = ""
} }
if err := wall.Save(); err != nil { if err := wall.Save(); err != nil {
return cli.NewExitError(fmt.Errorf("error while saving wallet: %w", err), 1) return cli.Exit(fmt.Errorf("error while saving wallet: %w", err), 1)
} }
return nil return nil
} }
@ -867,28 +881,28 @@ func createWallet(ctx *cli.Context) error {
return errConflictingWalletFlags return errConflictingWalletFlags
} }
if len(path) == 0 && len(configPath) == 0 { if len(path) == 0 && len(configPath) == 0 {
return cli.NewExitError(errNoPath, 1) return cli.Exit(errNoPath, 1)
} }
var pass *string var pass *string
if len(configPath) != 0 { if len(configPath) != 0 {
cfg, err := options.ReadWalletConfig(configPath) cfg, err := options.ReadWalletConfig(configPath)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
path = cfg.Path path = cfg.Path
pass = &cfg.Password pass = &cfg.Password
} }
wall, err := wallet.NewWallet(path) wall, err := wallet.NewWallet(path)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if err := wall.Save(); err != nil { if err := wall.Save(); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
if ctx.Bool("account") { if ctx.Bool("account") {
if err := createAccount(wall, pass); err != nil { if err := createAccount(wall, pass); err != nil {
return cli.NewExitError(err, 1) return cli.Exit(err, 1)
} }
defer wall.Close() defer wall.Close()
} }
@ -949,14 +963,14 @@ func createAccount(wall *wallet.Wallet, pass *string) error {
func openWallet(ctx *cli.Context, canUseWalletConfig bool) (*wallet.Wallet, *string, error) { func openWallet(ctx *cli.Context, canUseWalletConfig bool) (*wallet.Wallet, *string, error) {
path, pass, err := getWalletPathAndPass(ctx, canUseWalletConfig) path, pass, err := getWalletPathAndPass(ctx, canUseWalletConfig)
if err != nil { if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("failed to get wallet path or password: %w", err), 1) return nil, nil, cli.Exit(fmt.Errorf("failed to get wallet path or password: %w", err), 1)
} }
if path == "-" { if path == "-" {
return nil, nil, errNoStdin return nil, nil, errNoStdin
} }
w, err := wallet.NewWalletFromFile(path) w, err := wallet.NewWalletFromFile(path)
if err != nil { if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("failed to read wallet: %w", err), 1) return nil, nil, cli.Exit(fmt.Errorf("failed to read wallet: %w", err), 1)
} }
return w, pass, nil return w, pass, nil
} }

View file

@ -992,7 +992,7 @@ func TestOfflineSigning(t *testing.T) {
e.Run(t, "neo-go", "util", "sendtx", e.Run(t, "neo-go", "util", "sendtx",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
txPath, "--await") "--await", txPath)
e.CheckAwaitableTxPersisted(t) e.CheckAwaitableTxPersisted(t)
}) })
} }

5
go.mod
View file

@ -25,7 +25,7 @@ require (
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954
github.com/twmb/murmur3 v1.1.8 github.com/twmb/murmur3 v1.1.8
github.com/urfave/cli v1.22.5 github.com/urfave/cli/v2 v2.27.2
go.etcd.io/bbolt v1.3.9 go.etcd.io/bbolt v1.3.9
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/crypto v0.21.0 golang.org/x/crypto v0.21.0
@ -42,7 +42,7 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/bavard v0.1.13 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/frankban/quicktest v1.14.5 // indirect github.com/frankban/quicktest v1.14.5 // indirect
github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
@ -60,6 +60,7 @@ require (
github.com/rs/zerolog v1.30.0 // indirect github.com/rs/zerolog v1.30.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.16.0 // indirect golang.org/x/mod v0.16.0 // indirect

15
go.sum
View file

@ -1,4 +1,3 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -22,9 +21,8 @@ github.com/consensys/gnark v0.9.1/go.mod h1:udWvWGXnfBE7mn7BsNoGAvZDnUhcONBEtNij
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc=
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -131,20 +129,20 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@ -216,7 +214,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

View file

@ -34,7 +34,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"golang.org/x/term" "golang.org/x/term"

View file

@ -12,7 +12,7 @@ import (
"path/filepath" "path/filepath"
"sort" "sort"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var ledgerContractID = -4 var ledgerContractID = -4

View file

@ -10,7 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/pmezard/go-difflib/difflib" "github.com/pmezard/go-difflib/difflib"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
var errStateMatches = errors.New("state matches") var errStateMatches = errors.New("state matches")
@ -167,8 +167,9 @@ func main() {
ctl.Usage = "compare-states RPC_A RPC_B" ctl.Usage = "compare-states RPC_A RPC_B"
ctl.Action = cliMain ctl.Action = cliMain
ctl.Flags = []cli.Flag{ ctl.Flags = []cli.Flag{
cli.BoolFlag{ &cli.BoolFlag{
Name: "ignore-height, g", Name: "ignore-height",
Aliases: []string{"h"},
Usage: "Ignore height difference", Usage: "Ignore height difference",
}, },
} }