cli: change deploy and invoke commands to use wallet

Passing WIF directly in the command line is not something we should be doing.

Also split netfee and sysfee in the RPC as they're different (and add a script
attribute for free transactions).
This commit is contained in:
Roman Khimov 2020-03-11 21:11:51 +03:00
parent bbd802681e
commit 25da5a30d8
2 changed files with 75 additions and 34 deletions

View file

@ -10,26 +10,31 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/go-yaml/yaml"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"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"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
)
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")
errNoWIF = errors.New("no WIF parameter found, specify it with the '--wif or -w' flag")
errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet or -w' flag")
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
errFileExist = errors.New("A file with given smart-contract name already exists")
@ -38,9 +43,13 @@ var (
Name: "endpoint, e",
Usage: "trusted RPC endpoint address (like 'http://localhost:20331')",
}
wifFlag = cli.StringFlag{
Name: "wif, w",
Usage: "key to sign deployed transaction (in wif format)",
walletFlag = cli.StringFlag{
Name: "wallet, w",
Usage: "wallet to use to get the key for transaction signing",
}
addressFlag = flags.AddressFlag{
Name: "address, a",
Usage: "address to use as transaction signee (and gas source)",
}
gasFlag = cli.Float64Flag{
Name: "gas, g",
@ -105,14 +114,15 @@ func NewCommands() []cli.Command {
Usage: "configuration input file (*.yml)",
},
endpointFlag,
wifFlag,
walletFlag,
addressFlag,
gasFlag,
},
},
{
Name: "invoke",
Usage: "invoke deployed contract on the blockchain",
UsageText: "neo-go contract invoke -e endpoint -w wif [-g gas] scripthash [arguments...]",
UsageText: "neo-go contract invoke -e endpoint -w wallet [-a address] [-g gas] scripthash [arguments...]",
Description: `Executes given (as a script hash) deployed script with the given arguments.
See testinvoke documentation for the details about parameters. It differs
from testinvoke in that this command sends an invocation transaction to
@ -121,14 +131,15 @@ func NewCommands() []cli.Command {
Action: invoke,
Flags: []cli.Flag{
endpointFlag,
wifFlag,
walletFlag,
addressFlag,
gasFlag,
},
},
{
Name: "invokefunction",
Usage: "invoke deployed contract on the blockchain",
UsageText: "neo-go contract invokefunction -e endpoint -w wif [-g gas] scripthash [method] [arguments...]",
UsageText: "neo-go contract invokefunction -e endpoint -w wallet [-a address] [-g gas] scripthash [method] [arguments...]",
Description: `Executes given (as a script hash) deployed script with the given method and
and arguments. See testinvokefunction documentation for the details about
parameters. It differs from testinvokefunction in that this command sends an
@ -137,7 +148,8 @@ func NewCommands() []cli.Command {
Action: invokeFunction,
Flags: []cli.Flag{
endpointFlag,
wifFlag,
walletFlag,
addressFlag,
gasFlag,
},
},
@ -371,7 +383,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
params = make([]smartcontract.Parameter, 0)
paramsStart = 1
resp *result.Invoke
wif *keys.WIF
acc *wallet.Account
)
endpoint := ctx.String("endpoint")
@ -400,8 +412,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
if signAndPush {
gas = util.Fixed8FromFloat(ctx.Float64("gas"))
wif, err = getWifFromContext(ctx)
acc, err = getAccFromContext(ctx)
if err != nil {
return err
}
@ -427,7 +438,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
if err != nil {
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %v", err), 1)
}
txHash, err := c.SignAndPushInvocationTx(script, wif, gas)
txHash, err := c.SignAndPushInvocationTx(script, acc, 0, gas)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
}
@ -531,17 +542,41 @@ func inspect(ctx *cli.Context) error {
return nil
}
func getWifFromContext(ctx *cli.Context) (*keys.WIF, error) {
wifStr := ctx.String("wif")
if len(wifStr) == 0 {
return nil, cli.NewExitError(errNoWIF, 1)
func getAccFromContext(ctx *cli.Context) (*wallet.Account, error) {
var addr util.Uint160
wPath := ctx.String("wallet")
if len(wPath) == 0 {
return nil, cli.NewExitError(errNoWallet, 1)
}
wif, err := keys.WIFDecode(wifStr, 0)
wall, err := wallet.NewWalletFromFile(wPath)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("bad wif: %v", err), 1)
return nil, cli.NewExitError(err, 1)
}
return wif, nil
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addr = addrFlag.Uint160()
} else {
addr = wall.GetChangeAddress()
}
acc := wall.GetAccount(addr)
if acc == nil {
return nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
}
fmt.Printf("Enter account %s password > ", address.Uint160ToString(addr))
rawPass, err := terminal.ReadPassword(syscall.Stdin)
fmt.Println()
if err != nil {
return nil, cli.NewExitError(err, 1)
}
pass := strings.TrimRight(string(rawPass), "\n")
err = acc.Decrypt(pass)
if err != nil {
return nil, cli.NewExitError(err, 1)
}
return acc, nil
}
// contractDeploy deploys contract.
@ -560,7 +595,7 @@ func contractDeploy(ctx *cli.Context) error {
}
gas := util.Fixed8FromFloat(ctx.Float64("gas"))
wif, err := getWifFromContext(ctx)
acc, err := getAccFromContext(ctx)
if err != nil {
return err
}
@ -589,9 +624,9 @@ func contractDeploy(ctx *cli.Context) error {
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1)
}
gas += smartcontract.GetDeploymentPrice(request.DetailsToSCProperties(&conf.Contract))
sysfee := smartcontract.GetDeploymentPrice(request.DetailsToSCProperties(&conf.Contract))
txHash, err := c.SignAndPushInvocationTx(txScript, wif, gas)
txHash, err := c.SignAndPushInvocationTx(txScript, acc, sysfee, gas)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
}

View file

@ -6,7 +6,7 @@ import (
"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/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
@ -249,24 +249,30 @@ func (c *Client) TransferAsset(asset util.Uint256, address string, amount util.F
// SignAndPushInvocationTx signs and pushes given script as an invocation
// transaction using given wif to sign it and spending the amount of gas
// specified. It returns a hash of the invocation transaction and an error.
func (c *Client) SignAndPushInvocationTx(script []byte, wif *keys.WIF, gas util.Fixed8) (util.Uint256, error) {
func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee util.Fixed8, netfee util.Fixed8) (util.Uint256, error) {
var txHash util.Uint256
var err error
tx := transaction.NewInvocationTX(script, gas)
fromAddress := wif.PrivateKey.Address()
tx := transaction.NewInvocationTX(script, sysfee)
gas := sysfee + netfee
if gas > 0 {
if err = request.AddInputsAndUnspentsToTx(tx, fromAddress, core.UtilityTokenID(), gas, c); err != nil {
if err = request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil {
return txHash, errors.Wrap(err, "failed to add inputs and unspents to transaction")
}
} else {
addr, err := address.StringToUint160(acc.Address)
if err != nil {
return txHash, errors.Wrap(err, "failed to get address")
}
tx.Attributes = append(tx.Attributes,
transaction.Attribute{
Usage: transaction.Script,
Data: addr.BytesBE(),
})
}
acc, err := wallet.NewAccountFromWIF(wif.S)
if err != nil {
return txHash, err
} else if err = acc.SignTx(tx); err != nil {
if err = acc.SignTx(tx); err != nil {
return txHash, errors.Wrap(err, "failed to sign tx")
}
txHash = tx.Hash()