Merge pull request #1915 from nspcc-dev/cli/deploy_refactoring

cli: unify 'contract deploy' and 'contract invoke*'
This commit is contained in:
Roman Khimov 2021-04-23 12:48:37 +03:00 committed by GitHub
commit b0e8e37b99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 124 deletions

View file

@ -194,12 +194,12 @@ func TestContractDeployWithData(t *testing.T) {
"--in", nefName, "--manifest", manifestName,
"[", "key1", "12", "key2", "take_me_to_church", "]")
e.checkTxPersisted(t, "Sent invocation transaction ")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
h, err := util.Uint160DecodeStringLE(line)
require.NoError(t, err)
e.checkTxPersisted(t)
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
@ -244,12 +244,12 @@ func deployVerifyContract(t *testing.T, e *executor) util.Uint160 {
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName)
e.checkTxPersisted(t, "Sent invocation transaction ")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
hVerify, err := util.Uint160DecodeStringLE(line)
require.NoError(t, err)
e.checkTxPersisted(t)
return hVerify
}
@ -288,12 +288,12 @@ func TestComlileAndInvokeFunction(t *testing.T) {
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName)
e.checkTxPersisted(t, "Sent invocation transaction ")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
h, err := util.Uint160DecodeStringLE(line)
require.NoError(t, err)
e.checkTxPersisted(t)
t.Run("check calc hash", func(t *testing.T) {
// missing sender

View file

@ -5,14 +5,12 @@ import (
"math/big"
"os"
"path"
"strings"
"testing"
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
@ -110,19 +108,7 @@ func TestSignMultisigTx(t *testing.T) {
require.Equal(t, big.NewInt(3), b)
t.Run("via invokefunction", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", "testdata/verify.nef", "--manifest", "testdata/verify.manifest.json")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
h, err := util.Uint160DecodeStringLE(line)
require.NoError(t, err)
e.checkTxPersisted(t)
h := deployVerifyContract(t, e)
e.In.WriteString("acc\rpass\rpass\r")
e.Run(t, "neo-go", "wallet", "import-deployed",

View file

@ -22,16 +22,13 @@ import (
"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/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
"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/smartcontract/callflag"
"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/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"gopkg.in/yaml.v2"
@ -98,21 +95,6 @@ func NewCommands() []cli.Command {
},
}
testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...)
deployFlags := []cli.Flag{
cli.StringFlag{
Name: "in, i",
Usage: "Input file for the smart contract (*.nef)",
},
cli.StringFlag{
Name: "manifest, m",
Usage: "Manifest input file (*.manifest.json)",
},
walletFlag,
addressFlag,
outFlag,
gasFlag,
}
deployFlags = append(deployFlags, options.RPC...)
invokeFunctionFlags := []cli.Flag{
walletFlag,
addressFlag,
@ -121,6 +103,16 @@ func NewCommands() []cli.Command {
forceFlag,
}
invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...)
deployFlags := append(invokeFunctionFlags, []cli.Flag{
cli.StringFlag{
Name: "in, i",
Usage: "Input file for the smart contract (*.nef)",
},
cli.StringFlag{
Name: "manifest, m",
Usage: "Manifest input file (*.manifest.json)",
},
}...)
return []cli.Command{{
Name: "contract",
Usage: "compile - debug - deploy smart contracts",
@ -167,7 +159,7 @@ func NewCommands() []cli.Command {
{
Name: "deploy",
Usage: "deploy a smart contract (.nef with description)",
UsageText: "neo-go contract deploy -r endpoint -w wallet [-a address] [-g gas] --in contract.nef --manifest contract.manifest.json [--out file] [data]",
UsageText: "neo-go contract deploy -r endpoint -w wallet [-a address] [-g gas] --in contract.nef --manifest contract.manifest.json [--out file] [--force] [data]",
Description: `Deploys given contract into the chain. The gas parameter is for additional
gas to be added as a network fee to prioritize the transaction. The data
parameter is an optional parameter to be passed to '_deploy' method.
@ -178,7 +170,7 @@ func NewCommands() []cli.Command {
{
Name: "invokefunction",
Usage: "invoke deployed contract on the blockchain",
UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] [--out file] scripthash [method] [arguments...] [--] [signers...]",
UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] [--out file] [--force] scripthash [method] [arguments...] [--] [signers...]",
Description: `Executes given (as a script hash) deployed script with the given method,
arguments and signers. Sender is included in the list of signers by default
with None witness scope. If you'd like to change default sender's scope,
@ -510,16 +502,11 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
var (
err error
exitErr *cli.ExitError
gas fixedn.Fixed8
operation string
params = make([]smartcontract.Parameter, 0)
paramsStart = 1
cosigners []transaction.Signer
cosignersAccounts []client.SignerAccount
cosignersOffset = 0
resp *result.Invoke
acc *wallet.Account
wall *wallet.Wallet
)
args := ctx.Args()
@ -549,15 +536,33 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
return exitErr
}
_, err = invokeWithArgs(ctx, signAndPush, script, operation, params, cosigners)
return err
}
func invokeWithArgs(ctx *cli.Context, signAndPush bool, script util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Signer) (util.Uint160, error) {
var (
err error
gas fixedn.Fixed8
cosignersAccounts []client.SignerAccount
resp *result.Invoke
acc *wallet.Account
wall *wallet.Wallet
sender util.Uint160
)
if signAndPush {
gas = flags.Fixed8FromContext(ctx, "gas")
acc, wall, err = getAccFromContext(ctx)
if err != nil {
return err
return sender, err
}
sender, err = address.StringToUint160(acc.Address)
if err != nil {
return sender, err
}
cosignersAccounts, err = cmdargs.GetSignersAccounts(wall, cosigners)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to calculate network fee: %w", err), 1)
return sender, cli.NewExitError(fmt.Errorf("failed to calculate network fee: %w", err), 1)
}
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -565,50 +570,50 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return err
return sender, err
}
resp, err = c.InvokeFunction(script, operation, params, cosigners)
if err != nil {
return cli.NewExitError(err, 1)
return sender, cli.NewExitError(err, 1)
}
if signAndPush && resp.State != "HALT" {
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s\n", resp.State, resp.FaultException)
if !ctx.Bool("force") {
return cli.NewExitError(errText+". Use --force flag to send the transaction anyway.", 1)
return sender, cli.NewExitError(errText+". Use --force flag to send the transaction anyway.", 1)
}
fmt.Fprintln(ctx.App.Writer, errText+". Sending transaction...")
}
if out := ctx.String("out"); out != "" {
tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed, int64(gas), cosignersAccounts)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
return sender, cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
if err := paramcontext.InitAndSave(c.GetNetwork(), tx, acc, out); err != nil {
return cli.NewExitError(err, 1)
return sender, cli.NewExitError(err, 1)
}
fmt.Fprintln(ctx.App.Writer, tx.Hash().StringLE())
return nil
return sender, nil
}
if signAndPush {
if len(resp.Script) == 0 {
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
return sender, cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
}
txHash, err := c.SignAndPushInvocationTx(resp.Script, acc, resp.GasConsumed, gas, cosignersAccounts)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
return sender, cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
}
fmt.Fprintf(ctx.App.Writer, "Sent invocation transaction %s\n", txHash.StringLE())
} else {
b, err := json.MarshalIndent(resp, "", " ")
if err != nil {
return cli.NewExitError(err, 1)
return sender, cli.NewExitError(err, 1)
}
fmt.Fprintln(ctx.App.Writer, string(b))
}
return nil
return sender, nil
}
func testInvokeScript(ctx *cli.Context) error {
@ -741,16 +746,7 @@ func contractDeploy(ctx *cli.Context) error {
if len(manifestFile) == 0 {
return cli.NewExitError(errNoManifestFile, 1)
}
gas := flags.Fixed8FromContext(ctx, "gas")
acc, _, err := getAccFromContext(ctx)
if err != nil {
return err
}
sender, err := address.StringToUint160(acc.Address)
if err != nil {
return cli.NewExitError(err, 1)
}
f, err := ioutil.ReadFile(in)
if err != nil {
return cli.NewExitError(err, 1)
@ -771,13 +767,25 @@ func contractDeploy(ctx *cli.Context) error {
return cli.NewExitError(fmt.Errorf("failed to restore manifest file: %w", err), 1)
}
_, data, extErr := cmdargs.GetDataFromContext(ctx)
if extErr != nil {
return extErr
appCallParams := []smartcontract.Parameter{
{
Type: smartcontract.ByteArrayType,
Value: f,
},
{
Type: smartcontract.ByteArrayType,
Value: manifestBytes,
},
}
appCallParams := []interface{}{f, manifestBytes}
if data != nil {
appCallParams = append(appCallParams, data)
_, data, err := cmdargs.ParseParams(ctx.Args(), true)
if err != nil {
return cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
}
if len(data) > 1 {
return cli.NewExitError("'data' should be represented as a single parameter", 1)
}
if len(data) != 0 {
appCallParams = append(appCallParams, data[0])
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -792,42 +800,13 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1)
}
buf := io.NewBufBinWriter()
emit.AppCall(buf.BinWriter, mgmtHash, "deploy",
callflag.ReadStates|callflag.WriteStates|callflag.AllowNotify,
appCallParams...)
if buf.Err != nil {
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %w", buf.Err), 1)
}
txScript := buf.Bytes()
// It doesn't require any signers.
invRes, err := c.InvokeScript(txScript, nil)
if err == nil && invRes.FaultException != "" {
err = errors.New(invRes.FaultException)
}
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to test-invoke deployment script: %w", err), 1)
sender, extErr := invokeWithArgs(ctx, true, mgmtHash, "deploy", appCallParams, nil)
if extErr != nil {
return extErr
}
var txHash util.Uint256
if out := ctx.String("out"); out != "" {
tx, err := c.CreateTxFromScript(txScript, acc, invRes.GasConsumed, int64(gas), nil)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
if err := paramcontext.InitAndSave(c.GetNetwork(), tx, acc, out); err != nil {
return cli.NewExitError(err, 1)
}
txHash = tx.Hash()
} else {
txHash, err = c.SignAndPushInvocationTx(txScript, acc, invRes.GasConsumed, gas, nil)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
}
}
hash := state.CreateContractHash(sender, nefFile.Checksum, m.Name)
fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", hash.StringLE())
fmt.Fprintln(ctx.App.Writer, txHash.StringLE())
return nil
}

View file

@ -14,7 +14,6 @@ import (
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
@ -243,20 +242,7 @@ func TestClaimGas(t *testing.T) {
func TestImportDeployed(t *testing.T) {
e := newExecutor(t, true)
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
"--in", "testdata/verify.nef", "--manifest", "testdata/verify.manifest.json")
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
h, err := util.Uint160DecodeStringLE(line)
require.NoError(t, err)
e.checkTxPersisted(t)
h := deployVerifyContract(t, e)
tmpDir := os.TempDir()
walletPath := path.Join(tmpDir, "wallet.json")
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)