diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 292c6649c..a1575755b 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -399,8 +399,10 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { if !args.Present() { return cli.NewExitError(errNoScriptHash, 1) } - script := args[0] - + script, err := util.Uint160DecodeStringLE(args[0]) + if err != nil { + return cli.NewExitError(fmt.Errorf("incorrect script hash: %v", err), 1) + } if len(args) <= 1 { return cli.NewExitError(errNoMethod, 1) } @@ -510,8 +512,7 @@ func testInvokeScript(ctx *cli.Context) error { return err } - scriptHex := hex.EncodeToString(nefFile.Script) - resp, err := c.InvokeScript(scriptHex, cosigners) + resp, err := c.InvokeScript(nefFile.Script, cosigners) if err != nil { return cli.NewExitError(err, 1) } @@ -654,12 +655,17 @@ func contractDeploy(ctx *cli.Context) error { return err } - txScript, sysfee, err := request.CreateDeploymentScript(nefFile.Script, m) + txScript, err := request.CreateDeploymentScript(nefFile.Script, m) if err != nil { return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1) } + // It doesn't require any cosigners. + invRes, err := c.InvokeScript(txScript, nil) + if err != nil { + return cli.NewExitError(fmt.Errorf("failed to test-invoke deployment script: %v", err), 1) + } - txHash, err := c.SignAndPushInvocationTx(txScript, acc, sysfee, gas) + txHash, err := c.SignAndPushInvocationTx(txScript, acc, invRes.GasConsumed, gas) if err != nil { return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1) } diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 651d4e12d..b8b84722d 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -1,7 +1,6 @@ package client import ( - "encoding/hex" "errors" "fmt" @@ -25,7 +24,7 @@ var ( // NEP5Decimals invokes `decimals` NEP5 method on a specified contract. func (c *Client) NEP5Decimals(tokenHash util.Uint160) (int64, error) { - result, err := c.InvokeFunction(tokenHash.StringLE(), "decimals", []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(tokenHash, "decimals", []smartcontract.Parameter{}, nil) if err != nil { return 0, err } else if result.State != "HALT" || len(result.Stack) == 0 { @@ -37,7 +36,7 @@ func (c *Client) NEP5Decimals(tokenHash util.Uint160) (int64, error) { // NEP5Name invokes `name` NEP5 method on a specified contract. func (c *Client) NEP5Name(tokenHash util.Uint160) (string, error) { - result, err := c.InvokeFunction(tokenHash.StringLE(), "name", []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(tokenHash, "name", []smartcontract.Parameter{}, nil) if err != nil { return "", err } else if result.State != "HALT" || len(result.Stack) == 0 { @@ -49,7 +48,7 @@ func (c *Client) NEP5Name(tokenHash util.Uint160) (string, error) { // NEP5Symbol invokes `symbol` NEP5 method on a specified contract. func (c *Client) NEP5Symbol(tokenHash util.Uint160) (string, error) { - result, err := c.InvokeFunction(tokenHash.StringLE(), "symbol", []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(tokenHash, "symbol", []smartcontract.Parameter{}, nil) if err != nil { return "", err } else if result.State != "HALT" || len(result.Stack) == 0 { @@ -61,7 +60,7 @@ func (c *Client) NEP5Symbol(tokenHash util.Uint160) (string, error) { // NEP5TotalSupply invokes `totalSupply` NEP5 method on a specified contract. func (c *Client) NEP5TotalSupply(tokenHash util.Uint160) (int64, error) { - result, err := c.InvokeFunction(tokenHash.StringLE(), "totalSupply", []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(tokenHash, "totalSupply", []smartcontract.Parameter{}, nil) if err != nil { return 0, err } else if result.State != "HALT" || len(result.Stack) == 0 { @@ -73,7 +72,7 @@ func (c *Client) NEP5TotalSupply(tokenHash util.Uint160) (int64, error) { // NEP5BalanceOf invokes `balanceOf` NEP5 method on a specified contract. func (c *Client) NEP5BalanceOf(tokenHash util.Uint160) (int64, error) { - result, err := c.InvokeFunction(tokenHash.StringLE(), "balanceOf", []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(tokenHash, "balanceOf", []smartcontract.Parameter{}, nil) if err != nil { return 0, err } else if result.State != "HALT" || len(result.Stack) == 0 { @@ -127,7 +126,7 @@ func (c *Client) CreateNEP5TransferTx(acc *wallet.Account, to util.Uint160, toke }, } - result, err := c.InvokeScript(hex.EncodeToString(script), []transaction.Cosigner{ + result, err := c.InvokeScript(script, []transaction.Cosigner{ { Account: from, Scopes: transaction.Global, diff --git a/pkg/rpc/client/policy.go b/pkg/rpc/client/policy.go index c366ce5f9..e0d9b46e6 100644 --- a/pkg/rpc/client/policy.go +++ b/pkg/rpc/client/policy.go @@ -29,7 +29,7 @@ func (c *Client) GetFeePerByte() (int64, error) { } func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) { - result, err := c.InvokeFunction(PolicyContractHash.StringLE(), operation, []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(PolicyContractHash, operation, []smartcontract.Parameter{}, nil) if err != nil { return 0, err } else if result.State != "HALT" || len(result.Stack) == 0 { @@ -41,7 +41,7 @@ func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) { // GetBlockedAccounts invokes `getBlockedAccounts` method on a native Policy contract. func (c *Client) GetBlockedAccounts() (native.BlockedAccounts, error) { - result, err := c.InvokeFunction(PolicyContractHash.StringLE(), "getBlockedAccounts", []smartcontract.Parameter{}, nil) + result, err := c.InvokeFunction(PolicyContractHash, "getBlockedAccounts", []smartcontract.Parameter{}, nil) if err != nil { return nil, err } else if result.State != "HALT" || len(result.Stack) == 0 { diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index dd342e6e5..7aac5ff41 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -349,36 +349,38 @@ func (c *Client) GetVersion() (*result.Version, error) { // InvokeScript returns the result of the given script after running it true the VM. // NOTE: This is a test invoke and will not affect the blockchain. -func (c *Client) InvokeScript(script string, cosigners []transaction.Cosigner) (*result.Invoke, error) { - var ( - params request.RawParams - resp = &result.Invoke{} - ) - if cosigners != nil { - params = request.NewRawParams(script, cosigners) - } else { - params = request.NewRawParams(script) - } - if err := c.performRequest("invokescript", params, resp); err != nil { - return nil, err - } - return resp, nil +func (c *Client) InvokeScript(script []byte, cosigners []transaction.Cosigner) (*result.Invoke, error) { + var p = request.NewRawParams(hex.EncodeToString(script)) + return c.invokeSomething("invokescript", p, cosigners) } // InvokeFunction returns the results after calling the smart contract scripthash // with the given operation and parameters. // NOTE: this is test invoke and will not affect the blockchain. -func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter, cosigners []transaction.Cosigner) (*result.Invoke, error) { - var ( - p request.RawParams - resp = &result.Invoke{} - ) +func (c *Client) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Cosigner) (*result.Invoke, error) { + var p = request.NewRawParams(contract.StringLE(), operation, params) + return c.invokeSomething("invokefunction", p, cosigners) +} + +// invokeSomething is an inner wrapper for Invoke* functions +func (c *Client) invokeSomething(method string, p request.RawParams, cosigners []transaction.Cosigner) (*result.Invoke, error) { + var resp = new(result.Invoke) if cosigners != nil { - p = request.NewRawParams(script, operation, params, cosigners) - } else { - p = request.NewRawParams(script, operation, params) + p.Values = append(p.Values, cosigners) } - if err := c.performRequest("invokefunction", p, resp); err != nil { + if err := c.performRequest(method, p, resp); err != nil { + // Retry with old-fashioned hashes (see neo/neo-modules#260). + if cosigners != nil { + var hashes = make([]util.Uint160, len(cosigners)) + for i := range cosigners { + hashes[i] = cosigners[i].Account + } + p.Values[len(p.Values)-1] = hashes + err = c.performRequest(method, p, resp) + if err == nil { + return resp, nil + } + } return nil, err } return resp, nil diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index d2d9c0693..93741e7b0 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -616,7 +616,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ if err != nil { panic(err) } - return c.InvokeFunction("af7c7328eee5a275a3bcaee2bf0cf662b5e739be", "balanceOf", []smartcontract.Parameter{ + contr, err := util.Uint160DecodeStringLE("af7c7328eee5a275a3bcaee2bf0cf662b5e739be") + if err != nil { + panic(err) + } + return c.InvokeFunction(contr, "balanceOf", []smartcontract.Parameter{ { Type: smartcontract.Hash160Type, Value: hash, @@ -649,7 +653,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ { name: "positive", invoke: func(c *Client) (interface{}, error) { - return c.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191", []transaction.Cosigner{{ + script, err := hex.DecodeString("00046e616d656724058e5e1b6008847cd662728549088a9ee82191") + if err != nil { + panic(err) + } + return c.InvokeScript(script, []transaction.Cosigner{{ Account: util.Uint160{1, 2, 3}, }}) }, @@ -952,13 +960,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ { name: "invokefunction_invalid_params_error", invoke: func(c *Client) (interface{}, error) { - return c.InvokeFunction("", "", []smartcontract.Parameter{}, nil) + return c.InvokeFunction(util.Uint160{}, "", []smartcontract.Parameter{}, nil) }, }, { name: "invokescript_invalid_params_error", invoke: func(c *Client) (interface{}, error) { - return c.InvokeScript("", nil) + return c.InvokeScript([]byte{}, nil) }, }, { @@ -1134,13 +1142,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ { name: "invokefunction_unmarshalling_error", invoke: func(c *Client) (interface{}, error) { - return c.InvokeFunction("", "", []smartcontract.Parameter{}, nil) + return c.InvokeFunction(util.Uint160{}, "", []smartcontract.Parameter{}, nil) }, }, { name: "invokescript_unmarshalling_error", invoke: func(c *Client) (interface{}, error) { - return c.InvokeScript("", nil) + return c.InvokeScript([]byte{}, nil) }, }, { diff --git a/pkg/rpc/request/txBuilder.go b/pkg/rpc/request/txBuilder.go index 953ef7287..f8db67de2 100644 --- a/pkg/rpc/request/txBuilder.go +++ b/pkg/rpc/request/txBuilder.go @@ -5,7 +5,6 @@ import ( "fmt" "strconv" - "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -16,18 +15,17 @@ import ( ) // CreateDeploymentScript returns a script that deploys given smart contract -// with its metadata and system fee require for this. -func CreateDeploymentScript(avm []byte, manif *manifest.Manifest) ([]byte, int64, error) { +// with its metadata. +func CreateDeploymentScript(avm []byte, manif *manifest.Manifest) ([]byte, error) { script := io.NewBufBinWriter() rawManifest, err := manif.MarshalJSON() if err != nil { - return nil, 0, err + return nil, err } emit.Bytes(script.BinWriter, rawManifest) emit.Bytes(script.BinWriter, avm) emit.Syscall(script.BinWriter, "System.Contract.Create") - sysfee := int64(core.StoragePrice * (len(avm) + len(rawManifest))) - return script.Bytes(), sysfee, nil + return script.Bytes(), nil } // expandArrayIntoScript pushes all FuncParam parameters from the given array