From 6eb600de5ada550e470f75fed80d05ada8ddbcfe Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 14 Jun 2020 10:09:05 +0300 Subject: [PATCH 01/17] config: sync network magic numbers unit_testnet YAML was out of sync with Go code and Go code was out of sync with YAML for mainnet and testnet after 308243f36e86852409ccda4fb82061d1961374df. --- config/protocol.unit_testnet.yml | 2 +- pkg/config/protocol_config.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/protocol.unit_testnet.yml b/config/protocol.unit_testnet.yml index 855690854..3d68fd574 100644 --- a/config/protocol.unit_testnet.yml +++ b/config/protocol.unit_testnet.yml @@ -1,5 +1,5 @@ ProtocolConfiguration: - Magic: 56753 + Magic: 0 SecondsPerBlock: 15 LowPriorityThreshold: 0.000 MemPoolSize: 50000 diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index 06509dc0e..a0342c3a2 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -6,9 +6,9 @@ import ( const ( // ModeMainNet contains magic code used in the NEO main official network. - ModeMainNet NetMode = 0x00746e41 // 7630401 + ModeMainNet NetMode = 0x004f454e // 5195086 // ModeTestNet contains magic code used in the NEO testing network. - ModeTestNet NetMode = 0x74746e41 // 1953787457 + ModeTestNet NetMode = 0x744f454e // 1951352142 // ModePrivNet contains magic code usually used for NEO private networks. ModePrivNet NetMode = 56753 // docker privnet // ModeUnitTestNet is a stub magic code used for testing purposes. From 26f11a52d95e839ac1f7b4ba44216f10f5bc9f34 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 14 Jun 2020 10:34:50 +0300 Subject: [PATCH 02/17] config: move NetMode into its own micropackage It's going to be used a bit more and pulling whole config just for one type is a bit wrong. --- cli/server/server.go | 7 ++--- integration/performance_test.go | 3 ++- pkg/config/config.go | 3 ++- pkg/config/netmode/netmode.go | 31 ++++++++++++++++++++++ pkg/config/protocol_config.go | 39 ++++------------------------ pkg/consensus/consensus_test.go | 3 ++- pkg/core/helper_test.go | 3 ++- pkg/core/util_test.go | 5 ++-- pkg/network/payload/version.go | 8 +++--- pkg/network/payload/version_test.go | 4 +-- pkg/network/server_config.go | 3 ++- pkg/rpc/server/server_helper_test.go | 3 ++- 12 files changed, 61 insertions(+), 51 deletions(-) create mode 100644 pkg/config/netmode/netmode.go diff --git a/cli/server/server.go b/cli/server/server.go index 2e6aca218..e73c0c239 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -7,6 +7,7 @@ import ( "os/signal" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/storage" @@ -107,12 +108,12 @@ func newGraceContext() context.Context { // getConfigFromContext looks at path and mode flags in the given config and // returns appropriate config. func getConfigFromContext(ctx *cli.Context) (config.Config, error) { - var net = config.ModePrivNet + var net = netmode.PrivNet if ctx.Bool("testnet") { - net = config.ModeTestNet + net = netmode.TestNet } if ctx.Bool("mainnet") { - net = config.ModeMainNet + net = netmode.MainNet } configPath := "./config" if argCp := ctx.String("config-path"); argCp != "" { diff --git a/integration/performance_test.go b/integration/performance_test.go index 68592cc87..f139cd83a 100644 --- a/integration/performance_test.go +++ b/integration/performance_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -20,7 +21,7 @@ import ( // Benchmark test to measure number of processed TX. // Same benchmark made on reference C# node https://github.com/neo-project/neo/issues/1321. func BenchmarkTXPerformanceTest(t *testing.B) { - net := config.ModeUnitTestNet + net := netmode.UnitTestNet configPath := "../config" cfg, err := config.Load(configPath, net) require.NoError(t, err, "could not load config") diff --git a/pkg/config/config.go b/pkg/config/config.go index f434191bd..68b0bc8c9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,6 +6,7 @@ import ( "os" "github.com/go-yaml/yaml" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/pkg/errors" ) @@ -28,7 +29,7 @@ func (c Config) GenerateUserAgent() string { // Load attempts to load the config from the given // path for the given netMode. -func Load(path string, netMode NetMode) (Config, error) { +func Load(path string, netMode netmode.Magic) (Config, error) { configPath := fmt.Sprintf("%s/protocol.%s.yml", path, netMode) if _, err := os.Stat(configPath); os.IsNotExist(err) { return Config{}, errors.Wrap(err, "Unable to load config") diff --git a/pkg/config/netmode/netmode.go b/pkg/config/netmode/netmode.go new file mode 100644 index 000000000..8c3642d96 --- /dev/null +++ b/pkg/config/netmode/netmode.go @@ -0,0 +1,31 @@ +package netmode + +const ( + // MainNet contains magic code used in the NEO main official network. + MainNet Magic = 0x004f454e // 5195086 + // TestNet contains magic code used in the NEO testing network. + TestNet Magic = 0x744f454e // 1951352142 + // PrivNet contains magic code usually used for NEO private networks. + PrivNet Magic = 56753 // docker privnet + // UnitTestNet is a stub magic code used for testing purposes. + UnitTestNet Magic = 0 +) + +// Magic describes the network the blockchain will operate on. +type Magic uint32 + +// String implements the stringer interface. +func (n Magic) String() string { + switch n { + case PrivNet: + return "privnet" + case TestNet: + return "testnet" + case MainNet: + return "mainnet" + case UnitTestNet: + return "unit_testnet" + default: + return "net unknown" + } +} diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index a0342c3a2..151accf24 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -1,20 +1,10 @@ package config import ( + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/util" ) -const ( - // ModeMainNet contains magic code used in the NEO main official network. - ModeMainNet NetMode = 0x004f454e // 5195086 - // ModeTestNet contains magic code used in the NEO testing network. - ModeTestNet NetMode = 0x744f454e // 1951352142 - // ModePrivNet contains magic code usually used for NEO private networks. - ModePrivNet NetMode = 56753 // docker privnet - // ModeUnitTestNet is a stub magic code used for testing purposes. - ModeUnitTestNet NetMode = 0 -) - // ProtocolConfiguration represents the protocol config. type ( ProtocolConfiguration struct { @@ -22,10 +12,10 @@ type ( // transactions exceeding the MaxFreeTransactionSize. FeePerExtraByte float64 `yaml:"FeePerExtraByte"` // FreeGasLimit is an amount of GAS which can be spent for free. - FreeGasLimit util.Fixed8 `yaml:"FreeGasLimit"` - LowPriorityThreshold float64 `yaml:"LowPriorityThreshold"` - Magic NetMode `yaml:"Magic"` - MaxTransactionsPerBlock int `yaml:"MaxTransactionsPerBlock"` + FreeGasLimit util.Fixed8 `yaml:"FreeGasLimit"` + LowPriorityThreshold float64 `yaml:"LowPriorityThreshold"` + Magic netmode.Magic `yaml:"Magic"` + MaxTransactionsPerBlock int `yaml:"MaxTransactionsPerBlock"` // Maximum size of low priority transaction in bytes. MaxFreeTransactionSize int `yaml:"MaxFreeTransactionSize"` // Maximum number of low priority transactions accepted into block. @@ -41,23 +31,4 @@ type ( // Whether to verify transactions in received blocks. VerifyTransactions bool `yaml:"VerifyTransactions"` } - - // NetMode describes the mode the blockchain will operate on. - NetMode uint32 ) - -// String implements the stringer interface. -func (n NetMode) String() string { - switch n { - case ModePrivNet: - return "privnet" - case ModeTestNet: - return "testnet" - case ModeMainNet: - return "mainnet" - case ModeUnitTestNet: - return "unit_testnet" - default: - return "net unknown" - } -} diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 44ef14532..e03a60c55 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/dbft/block" "github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -217,7 +218,7 @@ func getTestValidator(i int) (*privateKey, *publicKey) { } func newTestChain(t *testing.T) *core.Blockchain { - unitTestNetCfg, err := config.Load("../../config", config.ModeUnitTestNet) + unitTestNetCfg, err := config.Load("../../config", netmode.UnitTestNet) require.NoError(t, err) chain, err := core.NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t)) diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 3c04e798e..414c480dd 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -34,7 +35,7 @@ var neoOwner = testchain.MultisigScriptHash() // newTestChain should be called before newBlock invocation to properly setup // global state. func newTestChain(t *testing.T) *Blockchain { - unitTestNetCfg, err := config.Load("../../config", config.ModeUnitTestNet) + unitTestNetCfg, err := config.Load("../../config", netmode.UnitTestNet) require.NoError(t, err) chain, err := NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t)) require.NoError(t, err) diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index c28752e55..c27d0c10e 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -4,13 +4,14 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGenesisBlockMainNet(t *testing.T) { - cfg, err := config.Load("../../config", config.ModeMainNet) + cfg, err := config.Load("../../config", netmode.MainNet) require.NoError(t, err) block, err := createGenesisBlock(cfg.ProtocolConfiguration) @@ -30,7 +31,7 @@ func TestGetConsensusAddressMainNet(t *testing.T) { consensusScript = "72c3d9b3bbf776698694cd2c73fa597a10c31294" ) - cfg, err := config.Load("../../config", config.ModeMainNet) + cfg, err := config.Load("../../config", netmode.MainNet) require.NoError(t, err) validators, err := getValidators(cfg.ProtocolConfiguration) diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index b0747284c..6bb3bcdc1 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -3,7 +3,7 @@ package payload import ( "time" - "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/network/capability" ) @@ -11,7 +11,7 @@ import ( // Version payload. type Version struct { // NetMode of the node - Magic config.NetMode + Magic netmode.Magic // currently the version of the protocol is 0 Version uint32 // timestamp @@ -25,7 +25,7 @@ type Version struct { } // NewVersion returns a pointer to a Version payload. -func NewVersion(magic config.NetMode, id uint32, ua string, c []capability.Capability) *Version { +func NewVersion(magic netmode.Magic, id uint32, ua string, c []capability.Capability) *Version { return &Version{ Magic: magic, Version: 0, @@ -38,7 +38,7 @@ func NewVersion(magic config.NetMode, id uint32, ua string, c []capability.Capab // DecodeBinary implements Serializable interface. func (p *Version) DecodeBinary(br *io.BinReader) { - p.Magic = config.NetMode(br.ReadU32LE()) + p.Magic = netmode.Magic(br.ReadU32LE()) p.Version = br.ReadU32LE() p.Timestamp = br.ReadU32LE() p.Nonce = br.ReadU32LE() diff --git a/pkg/network/payload/version_test.go b/pkg/network/payload/version_test.go index 09cd9430b..405b4cead 100644 --- a/pkg/network/payload/version_test.go +++ b/pkg/network/payload/version_test.go @@ -3,14 +3,14 @@ package payload import ( "testing" - "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/network/capability" "github.com/stretchr/testify/assert" ) func TestVersionEncodeDecode(t *testing.T) { - var magic config.NetMode = 56753 + var magic netmode.Magic = 56753 var tcpPort uint16 = 3000 var wsPort uint16 = 3001 var id uint32 = 13337 diff --git a/pkg/network/server_config.go b/pkg/network/server_config.go index edd6731b7..a84458e3a 100644 --- a/pkg/network/server_config.go +++ b/pkg/network/server_config.go @@ -4,6 +4,7 @@ import ( "time" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/wallet" "go.uber.org/zap/zapcore" ) @@ -38,7 +39,7 @@ type ( // ModePrivNet docker private network. // ModeTestNet NEO test network. // ModeMainNet NEO main network. - Net config.NetMode + Net netmode.Magic // Relay determines whether the server is forwarding its inventory. Relay bool diff --git a/pkg/rpc/server/server_helper_test.go b/pkg/rpc/server/server_helper_test.go index 5e31c4ae7..3f16f12cf 100644 --- a/pkg/rpc/server/server_helper_test.go +++ b/pkg/rpc/server/server_helper_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/storage" @@ -19,7 +20,7 @@ import ( ) func getUnitTestChain(t *testing.T) (*core.Blockchain, config.Config, *zap.Logger) { - net := config.ModeUnitTestNet + net := netmode.UnitTestNet configPath := "../../../config" cfg, err := config.Load(configPath, net) require.NoError(t, err, "could not load config") From 78ff11fe537f219c2e0ec9381ab01b7e92631c3b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 14 Jun 2020 22:38:43 +0300 Subject: [PATCH 03/17] netmode: make default Magic String() more useful --- pkg/config/netmode/netmode.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/config/netmode/netmode.go b/pkg/config/netmode/netmode.go index 8c3642d96..f7faa5ae3 100644 --- a/pkg/config/netmode/netmode.go +++ b/pkg/config/netmode/netmode.go @@ -1,5 +1,7 @@ package netmode +import "strconv" + const ( // MainNet contains magic code used in the NEO main official network. MainNet Magic = 0x004f454e // 5195086 @@ -26,6 +28,6 @@ func (n Magic) String() string { case UnitTestNet: return "unit_testnet" default: - return "net unknown" + return "net 0x" + strconv.FormatUint(uint64(n), 16) } } From 1c2318eed4009166e52f8c7f6605507e9ce9d5a8 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 14 Jun 2020 22:40:52 +0300 Subject: [PATCH 04/17] netmode: use non-zero unittest network Using zero is a bit dangerous as it's the default type's value, so we can miss some uninitialized variables when testing. --- config/protocol.unit_testnet.yml | 2 +- pkg/config/netmode/netmode.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/protocol.unit_testnet.yml b/config/protocol.unit_testnet.yml index 3d68fd574..7eccfffbc 100644 --- a/config/protocol.unit_testnet.yml +++ b/config/protocol.unit_testnet.yml @@ -1,5 +1,5 @@ ProtocolConfiguration: - Magic: 0 + Magic: 42 SecondsPerBlock: 15 LowPriorityThreshold: 0.000 MemPoolSize: 50000 diff --git a/pkg/config/netmode/netmode.go b/pkg/config/netmode/netmode.go index f7faa5ae3..5b77244a4 100644 --- a/pkg/config/netmode/netmode.go +++ b/pkg/config/netmode/netmode.go @@ -10,7 +10,7 @@ const ( // PrivNet contains magic code usually used for NEO private networks. PrivNet Magic = 56753 // docker privnet // UnitTestNet is a stub magic code used for testing purposes. - UnitTestNet Magic = 0 + UnitTestNet Magic = 42 ) // Magic describes the network the blockchain will operate on. From f2c4b9b1d9633380be47beed2409cf12b4345093 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 17 Jun 2020 21:13:37 +0300 Subject: [PATCH 05/17] cli: move network options to their own package They will be reused. --- cli/options/options.go | 30 ++++++++++++++++++++++++++++++ cli/server/server.go | 15 +++------------ 2 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 cli/options/options.go diff --git a/cli/options/options.go b/cli/options/options.go new file mode 100644 index 000000000..34cd31777 --- /dev/null +++ b/cli/options/options.go @@ -0,0 +1,30 @@ +/* +Package options contains a set of common CLI options and helper functions to use them. +*/ +package options + +import ( + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/urfave/cli" +) + +// Network is a set of flags for choosing the network to operate on +// (privnet/mainnet/testnet). +var Network = []cli.Flag{ + cli.BoolFlag{Name: "privnet, p"}, + cli.BoolFlag{Name: "mainnet, m"}, + cli.BoolFlag{Name: "testnet, t"}, +} + +// GetNetwork examines Context's flags and returns the appropriate network. It +// defaults to PrivNet if no flags are given. +func GetNetwork(ctx *cli.Context) netmode.Magic { + var net = netmode.PrivNet + if ctx.Bool("testnet") { + net = netmode.TestNet + } + if ctx.Bool("mainnet") { + net = netmode.MainNet + } + return net +} diff --git a/cli/server/server.go b/cli/server/server.go index e73c0c239..061dc0734 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -6,8 +6,8 @@ import ( "os" "os/signal" + "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/config" - "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/storage" @@ -25,11 +25,9 @@ import ( func NewCommands() []cli.Command { var cfgFlags = []cli.Flag{ cli.StringFlag{Name: "config-path"}, - cli.BoolFlag{Name: "privnet, p"}, - cli.BoolFlag{Name: "mainnet, m"}, - cli.BoolFlag{Name: "testnet, t"}, cli.BoolFlag{Name: "debug, d"}, } + cfgFlags = append(cfgFlags, options.Network...) var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags)) copy(cfgWithCountFlags, cfgFlags) cfgWithCountFlags = append(cfgWithCountFlags, @@ -108,18 +106,11 @@ func newGraceContext() context.Context { // getConfigFromContext looks at path and mode flags in the given config and // returns appropriate config. func getConfigFromContext(ctx *cli.Context) (config.Config, error) { - var net = netmode.PrivNet - if ctx.Bool("testnet") { - net = netmode.TestNet - } - if ctx.Bool("mainnet") { - net = netmode.MainNet - } configPath := "./config" if argCp := ctx.String("config-path"); argCp != "" { configPath = argCp } - return config.Load(configPath, net) + return config.Load(configPath, options.GetNetwork(ctx)) } // handleLoggingParams reads logging parameters. From 16ce63e653faae7d09c73177ebba9d5dc6a80c1d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 18 Jun 2020 00:15:13 +0300 Subject: [PATCH 06/17] cli: unify RPC endpoint flags under options package This makes rpc flags consistent across all commands, previously some commands used 'endpoint, e' and some 'rpc, r', some had ability to change timeout and some hadn't. Now 'rpc-endpoint, r' is used everywhere along with 'timeout, t'. --- cli/options/options.go | 48 ++++++++++++ cli/smartcontract/smart_contract.go | 109 +++++++++++++--------------- cli/wallet/multisig.go | 35 ++++----- cli/wallet/nep5.go | 109 ++++++++++++++-------------- cli/wallet/wallet.go | 42 ++++------- docs/compiler.md | 6 +- 6 files changed, 188 insertions(+), 161 deletions(-) diff --git a/cli/options/options.go b/cli/options/options.go index 34cd31777..568106e63 100644 --- a/cli/options/options.go +++ b/cli/options/options.go @@ -4,10 +4,22 @@ Package options contains a set of common CLI options and helper functions to use package options import ( + "context" + "errors" + "time" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/urfave/cli" ) +// DefaultTimeout is the default timeout used for RPC requests. +const DefaultTimeout = 10 * time.Second + +// RPCEndpointFlag is a long flag name for RPC endpoint. It can be used to +// check for flag presence in the context. +const RPCEndpointFlag = "rpc-endpoint" + // Network is a set of flags for choosing the network to operate on // (privnet/mainnet/testnet). var Network = []cli.Flag{ @@ -16,6 +28,20 @@ var Network = []cli.Flag{ cli.BoolFlag{Name: "testnet, t"}, } +// RPC is a set of flags used for RPC connections (endpoint and timeout). +var RPC = []cli.Flag{ + cli.StringFlag{ + Name: RPCEndpointFlag + ", r", + Usage: "RPC node address", + }, + cli.DurationFlag{ + Name: "timeout, t", + Usage: "Timeout for the operation (10 seconds by default)", + }, +} + +var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'") + // GetNetwork examines Context's flags and returns the appropriate network. It // defaults to PrivNet if no flags are given. func GetNetwork(ctx *cli.Context) netmode.Magic { @@ -28,3 +54,25 @@ func GetNetwork(ctx *cli.Context) netmode.Magic { } return net } + +// GetTimeoutContext returns a context.Context with default of user-set timeout. +func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) { + dur := ctx.Duration("timeout") + if dur == 0 { + dur = DefaultTimeout + } + return context.WithTimeout(context.Background(), dur) +} + +// GetRPCClient returns an RPC client instance for the given Context. +func GetRPCClient(gctx context.Context, ctx *cli.Context) (*client.Client, cli.ExitCoder) { + endpoint := ctx.String(RPCEndpointFlag) + if len(endpoint) == 0 { + return nil, cli.NewExitError(errNoEndpoint, 1) + } + c, err := client.New(gctx, endpoint, client.Options{}) + if err != nil { + return nil, cli.NewExitError(err, 1) + } + return c, nil +} diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index cc7be5a0d..a5a62e010 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -2,7 +2,6 @@ package smartcontract import ( "bytes" - "context" "encoding/hex" "encoding/json" "fmt" @@ -14,11 +13,11 @@ import ( "github.com/go-yaml/yaml" "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" - "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -32,7 +31,6 @@ import ( ) var ( - errNoEndpoint = errors.New("no RPC endpoint specified, use option '--endpoint' or '-e'") errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag") errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag") errNoMethod = errors.New("no method specified for function invocation command") @@ -41,10 +39,6 @@ var ( 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") - endpointFlag = cli.StringFlag{ - Name: "endpoint, e", - Usage: "trusted RPC endpoint address (like 'http://localhost:20331')", - } walletFlag = cli.StringFlag{ Name: "wallet, w", Usage: "wallet to use to get the key for transaction signing", @@ -76,6 +70,33 @@ func Main(op string, args []interface{}) { // NewCommands returns 'contract' command. func NewCommands() []cli.Command { + testInvokeScriptFlags := []cli.Flag{ + cli.StringFlag{ + Name: "in, i", + Usage: "Input location of the avm file that needs to be invoked", + }, + } + testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...) + deployFlags := []cli.Flag{ + cli.StringFlag{ + Name: "in, i", + Usage: "Input file for the smart contract (*.avm)", + }, + cli.StringFlag{ + Name: "config, c", + Usage: "configuration input file (*.yml)", + }, + walletFlag, + addressFlag, + gasFlag, + } + deployFlags = append(deployFlags, options.RPC...) + invokeFunctionFlags := []cli.Flag{ + walletFlag, + addressFlag, + gasFlag, + } + invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...) return []cli.Command{{ Name: "contract", Usage: "compile - debug - deploy smart contracts", @@ -121,42 +142,24 @@ func NewCommands() []cli.Command { to it). `, Action: contractDeploy, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "in, i", - Usage: "Input file for the smart contract (*.avm)", - }, - cli.StringFlag{ - Name: "config, c", - Usage: "configuration input file (*.yml)", - }, - endpointFlag, - walletFlag, - addressFlag, - gasFlag, - }, + Flags: deployFlags, }, { Name: "invokefunction", Usage: "invoke deployed contract on the blockchain", - UsageText: "neo-go contract invokefunction -e endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...] [--] [cosigners...]", + UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...] [--] [cosigners...]", Description: `Executes given (as a script hash) deployed script with the given method, arguments and cosigners. See testinvokefunction documentation for the details about parameters. It differs from testinvokefunction in that this command sends an invocation transaction to the network. `, Action: invokeFunction, - Flags: []cli.Flag{ - endpointFlag, - walletFlag, - addressFlag, - gasFlag, - }, + Flags: invokeFunctionFlags, }, { Name: "testinvokefunction", Usage: "invoke deployed contract on the blockchain (test mode)", - UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...] [--] [cosigners...]", + UsageText: "neo-go contract testinvokefunction -r endpoint scripthash [method] [arguments...] [--] [cosigners...]", Description: `Executes given (as a script hash) deployed script with the given method, arguments and cosigners. If no method is given "" is passed to the script, if no arguments are given, an empty array is passed, if no cosigners are given, @@ -247,25 +250,17 @@ func NewCommands() []cli.Command { * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,CustomGroups' `, Action: testInvokeFunction, - Flags: []cli.Flag{ - endpointFlag, - }, + Flags: options.RPC, }, { Name: "testinvokescript", Usage: "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)", - UsageText: "neo-go contract testinvokescript -e endpoint -i input.avm [cosigners...]", + UsageText: "neo-go contract testinvokescript -r endpoint -i input.avm [cosigners...]", Description: `Executes given compiled AVM instructions with the given set of cosigners. See testinvokefunction documentation for the details about parameters. `, Action: testInvokeScript, - Flags: []cli.Flag{ - endpointFlag, - cli.StringFlag{ - Name: "in, i", - Usage: "Input location of the avm file that needs to be invoked", - }, - }, + Flags: testInvokeScriptFlags, }, { Name: "init", @@ -407,11 +402,6 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { acc *wallet.Account ) - endpoint := ctx.String("endpoint") - if len(endpoint) == 0 { - return cli.NewExitError(errNoEndpoint, 1) - } - args := ctx.Args() if !args.Present() { return cli.NewExitError(errNoScriptHash, 1) @@ -455,9 +445,12 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { return err } } - c, err := client.New(context.TODO(), endpoint, client.Options{}) + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) if err != nil { - return cli.NewExitError(err, 1) + return err } resp, err = c.InvokeFunction(script, operation, params, cosigners) @@ -494,10 +487,6 @@ func testInvokeScript(ctx *cli.Context) error { if len(src) == 0 { return cli.NewExitError(errNoInput, 1) } - endpoint := ctx.String("endpoint") - if len(endpoint) == 0 { - return cli.NewExitError(errNoEndpoint, 1) - } b, err := ioutil.ReadFile(src) if err != nil { @@ -516,9 +505,12 @@ func testInvokeScript(ctx *cli.Context) error { } } - c, err := client.New(context.TODO(), endpoint, client.Options{}) + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) if err != nil { - return cli.NewExitError(err, 1) + return err } scriptHex := hex.EncodeToString(b) @@ -640,10 +632,6 @@ func contractDeploy(ctx *cli.Context) error { if len(confFile) == 0 { return cli.NewExitError(errNoConfFile, 1) } - endpoint := ctx.String("endpoint") - if len(endpoint) == 0 { - return cli.NewExitError(errNoEndpoint, 1) - } gas := flags.Fixed8FromContext(ctx, "gas") acc, err := getAccFromContext(ctx) @@ -659,9 +647,12 @@ func contractDeploy(ctx *cli.Context) error { return err } - c, err := client.New(context.TODO(), endpoint, client.Options{}) + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) if err != nil { - return cli.NewExitError(err, 1) + return err } m := conf.ToManifest(avm) diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index 639b77235..9f8176dd7 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -5,31 +5,31 @@ import ( "fmt" "io/ioutil" + "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" - "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/smartcontract/context" "github.com/urfave/cli" ) func newMultisigCommands() []cli.Command { + signFlags := []cli.Flag{ + walletPathFlag, + outFlag, + inFlag, + cli.StringFlag{ + Name: "addr", + Usage: "Address to use", + }, + } + signFlags = append(signFlags, options.RPC...) return []cli.Command{ { Name: "sign", Usage: "sign a transaction", UsageText: "multisig sign --path --addr --in --out ", Action: signMultisig, - Flags: []cli.Flag{ - walletPathFlag, - rpcFlag, - timeoutFlag, - outFlag, - inFlag, - cli.StringFlag{ - Name: "addr", - Usage: "Address to use", - }, - }, + Flags: signFlags, }, } } @@ -75,20 +75,21 @@ func signMultisig(ctx *cli.Context) error { } else if err := writeParameterContext(c, ctx.String("out")); err != nil { return cli.NewExitError(err, 1) } - if endpoint := ctx.String("rpc"); endpoint != "" { + if len(ctx.String(options.RPCEndpointFlag)) != 0 { w, err := c.GetWitness(acc.Contract) if err != nil { return cli.NewExitError(err, 1) } tx.Scripts = append(tx.Scripts, *w) - gctx, cancel := getGoContext(ctx) + gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() - c, err := client.New(gctx, ctx.String("rpc"), client.Options{}) + c, err := options.GetRPCClient(gctx, ctx) if err != nil { - return cli.NewExitError(err, 1) - } else if err := c.SendRawTransaction(tx); err != nil { + return err + } + if err := c.SendRawTransaction(tx); err != nil { return cli.NewExitError(err, 1) } } diff --git a/cli/wallet/nep5.go b/cli/wallet/nep5.go index cf61345ff..99e267620 100644 --- a/cli/wallet/nep5.go +++ b/cli/wallet/nep5.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/smartcontract/context" @@ -16,39 +17,59 @@ import ( ) func newNEP5Commands() []cli.Command { + balanceFlags := []cli.Flag{ + walletPathFlag, + cli.StringFlag{ + Name: "addr", + Usage: "Address to use", + }, + cli.StringFlag{ + Name: "token", + Usage: "Token to use", + }, + } + balanceFlags = append(balanceFlags, options.RPC...) + importFlags := []cli.Flag{ + walletPathFlag, + cli.StringFlag{ + Name: "token", + Usage: "Token contract hash in LE", + }, + } + importFlags = append(importFlags, options.RPC...) + transferFlags := []cli.Flag{ + walletPathFlag, + outFlag, + fromAddrFlag, + toAddrFlag, + cli.StringFlag{ + Name: "token", + Usage: "Token to use", + }, + cli.StringFlag{ + Name: "amount", + Usage: "Amount of asset to send", + }, + flags.Fixed8Flag{ + Name: "gas", + Usage: "Amount of GAS to attach to a tx", + }, + } + transferFlags = append(transferFlags, options.RPC...) return []cli.Command{ { Name: "balance", Usage: "get address balance", - UsageText: "balance --path --rpc --addr [--token ]", + UsageText: "balance --path --rpc-endpoint --timeout