forked from TrueCloudLab/neoneo-go
Merge pull request #1915 from nspcc-dev/cli/deploy_refactoring
cli: unify 'contract deploy' and 'contract invoke*'
This commit is contained in:
commit
b0e8e37b99
4 changed files with 75 additions and 124 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue