mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-04 19:19:44 +00:00
cli: use hashes for verifying in invoke*
calls
This commit is contained in:
parent
d20e54b725
commit
4b351f3123
4 changed files with 105 additions and 57 deletions
|
@ -68,6 +68,9 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
|||
func Main(op string, args []interface{}) {
|
||||
runtime.Notify("Hello world!")
|
||||
}`
|
||||
|
||||
// hashesForVerifyingSeparator delimits `invoke*` parameters from hashes for verifying
|
||||
hashesForVerifyingSeparator = "--"
|
||||
)
|
||||
|
||||
// NewCommands returns 'contract' command.
|
||||
|
@ -169,8 +172,9 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "testinvoke",
|
||||
Usage: "invoke deployed contract on the blockchain (test mode)",
|
||||
UsageText: "neo-go contract testinvoke -e endpoint scripthash [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given arguments.
|
||||
UsageText: "neo-go contract testinvoke -e endpoint scripthash [arguments...] [-- hashesForVerifying...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given arguments
|
||||
and hashes to verify using runtime.CheckWitness syscall.
|
||||
It's very similar to the tesinvokefunction command, but differs in the way
|
||||
arguments are being passed. This invoker does not accept method parameter
|
||||
and it passes all given parameters as plain values to the contract, not
|
||||
|
@ -189,13 +193,14 @@ func NewCommands() []cli.Command {
|
|||
{
|
||||
Name: "testinvokefunction",
|
||||
Usage: "invoke deployed contract on the blockchain (test mode)",
|
||||
UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method and
|
||||
arguments. If no method is given "" is passed to the script, if no arguments
|
||||
are given, an empty array is passed. All of the given arguments are
|
||||
encapsulated into array before invoking the script. The script thus should
|
||||
follow the regular convention of smart contract arguments (method string and
|
||||
an array of other arguments).
|
||||
UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...] [-- hashesForVerifying...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method,
|
||||
arguments and hashes to verify using System.Runtime.CheckWitness syscall.
|
||||
If no method is given "" is passed to the script, if no arguments
|
||||
are given, an empty array is passed, if no hashes are given, no array is
|
||||
passed. All of the given arguments are encapsulated into array before
|
||||
invoking the script. The script thus should follow the regular convention
|
||||
of smart contract arguments (method string and an array of other arguments).
|
||||
|
||||
Arguments always do have regular Neo smart contract parameter types, either
|
||||
specified explicitly or being inferred from the value. To specify the type
|
||||
|
@ -251,6 +256,15 @@ func NewCommands() []cli.Command {
|
|||
* 'string\:string' is a string with a value of 'string:string'
|
||||
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
|
||||
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
|
||||
|
||||
HashesForVerifying represent a set of Uint160 hashes which are used to verify
|
||||
hashes in System.Runtime.CheckWitness syscall. To specify hash use its
|
||||
hex-encoded 160 bit (20 byte) LE representation with optional '0x' prefix.
|
||||
If no hashes were specified, no array is passed.
|
||||
|
||||
Examples:
|
||||
* '0000000009070e030d0f0e020d0c06050e030c02'
|
||||
* '0x0000000009070e030d0f0e020d0c06050e030c02'
|
||||
`,
|
||||
Action: testInvokeFunction,
|
||||
Flags: []cli.Flag{
|
||||
|
@ -258,9 +272,10 @@ func NewCommands() []cli.Command {
|
|||
},
|
||||
},
|
||||
{
|
||||
Name: "testinvokescript",
|
||||
Usage: "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)",
|
||||
Action: testInvokeScript,
|
||||
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 testcontract.avm [-- hashesForVerifying...]",
|
||||
Action: testInvokeScript,
|
||||
Flags: []cli.Flag{
|
||||
endpointFlag,
|
||||
cli.StringFlag{
|
||||
|
@ -407,13 +422,15 @@ func invokeFunction(ctx *cli.Context) error {
|
|||
|
||||
func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||
var (
|
||||
err error
|
||||
gas util.Fixed8
|
||||
operation string
|
||||
params = make([]smartcontract.Parameter, 0)
|
||||
paramsStart = 1
|
||||
resp *result.Invoke
|
||||
acc *wallet.Account
|
||||
err error
|
||||
gas util.Fixed8
|
||||
operation string
|
||||
params = make([]smartcontract.Parameter, 0)
|
||||
paramsStart = 1
|
||||
hashesForVerifying []util.Uint160
|
||||
hashesForVerifyingStart = 0
|
||||
resp *result.Invoke
|
||||
acc *wallet.Account
|
||||
)
|
||||
|
||||
endpoint := ctx.String("endpoint")
|
||||
|
@ -435,6 +452,13 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
}
|
||||
if len(args) > paramsStart {
|
||||
for k, s := range args[paramsStart:] {
|
||||
if s == hashesForVerifyingSeparator {
|
||||
if signAndPush {
|
||||
return cli.NewExitError("adding hashes for verifying available for test invokes only", 1)
|
||||
}
|
||||
hashesForVerifyingStart = paramsStart + k + 1
|
||||
break
|
||||
}
|
||||
param, err := smartcontract.NewParameterFromString(s)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse argument #%d: %v", k+paramsStart+1, err), 1)
|
||||
|
@ -443,6 +467,16 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
if len(args) >= hashesForVerifyingStart && hashesForVerifyingStart > 0 {
|
||||
for i, c := range args[hashesForVerifyingStart:] {
|
||||
h, err := parseUint160(c)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse hash for verifying #%d: %v", i+1, err), 1)
|
||||
}
|
||||
hashesForVerifying = append(hashesForVerifying, h)
|
||||
}
|
||||
}
|
||||
|
||||
if signAndPush {
|
||||
gas = flags.Fixed8FromContext(ctx, "gas")
|
||||
acc, err = getAccFromContext(ctx)
|
||||
|
@ -456,9 +490,9 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
}
|
||||
|
||||
if withMethod {
|
||||
resp, err = c.InvokeFunction(script, operation, params)
|
||||
resp, err = c.InvokeFunction(script, operation, params, hashesForVerifying)
|
||||
} else {
|
||||
resp, err = c.Invoke(script, params)
|
||||
resp, err = c.Invoke(script, params, hashesForVerifying)
|
||||
}
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
|
@ -503,13 +537,25 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
args := ctx.Args()
|
||||
var hashesForVerifying []util.Uint160
|
||||
if args.Present() {
|
||||
for i, c := range args[:] {
|
||||
h, err := parseUint160(c)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse hash for verifying #%d: %v", i+1, err), 1)
|
||||
}
|
||||
hashesForVerifying = append(hashesForVerifying, h)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := client.New(context.TODO(), endpoint, client.Options{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
scriptHex := hex.EncodeToString(b)
|
||||
resp, err := c.InvokeScript(scriptHex)
|
||||
resp, err := c.InvokeScript(scriptHex, hashesForVerifying)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -524,6 +570,13 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseUint160(s string) (util.Uint160, error) {
|
||||
if len(s) == 2*util.Uint160Size+2 && s[0] == '0' && s[1] == 'x' {
|
||||
s = s[2:]
|
||||
}
|
||||
return util.Uint160DecodeStringLE(s)
|
||||
}
|
||||
|
||||
// ProjectConfig contains project metadata.
|
||||
type ProjectConfig struct {
|
||||
Version uint
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
// 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{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "decimals", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -30,7 +30,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{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "name", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -42,7 +42,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{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "symbol", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -54,7 +54,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{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "totalSupply", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
@ -66,7 +66,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{})
|
||||
result, err := c.InvokeFunction(tokenHash.StringLE(), "balanceOf", []smartcontract.Parameter{}, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if result.State != "HALT" || len(result.Stack) == 0 {
|
||||
|
|
|
@ -394,39 +394,34 @@ 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) (*result.Invoke, error) {
|
||||
var (
|
||||
params = request.NewRawParams(script)
|
||||
resp = &result.Invoke{}
|
||||
)
|
||||
if err := c.performRequest("invokescript", params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
func (c *Client) InvokeScript(script string, hashesForVerifying []util.Uint160) (*result.Invoke, error) {
|
||||
params := request.NewRawParams(script)
|
||||
return c.invokeSomething("invokescript", params, hashesForVerifying)
|
||||
}
|
||||
|
||||
// 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) (*result.Invoke, error) {
|
||||
var (
|
||||
p = request.NewRawParams(script, operation, params)
|
||||
resp = &result.Invoke{}
|
||||
)
|
||||
if err := c.performRequest("invokefunction", p, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter, hashesForVerifying []util.Uint160) (*result.Invoke, error) {
|
||||
p := request.NewRawParams(script, operation, params)
|
||||
return c.invokeSomething("invokefunction", p, hashesForVerifying)
|
||||
}
|
||||
|
||||
// Invoke returns the results after calling the smart contract scripthash
|
||||
// with the given parameters.
|
||||
func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*result.Invoke, error) {
|
||||
var (
|
||||
p = request.NewRawParams(script, params)
|
||||
resp = &result.Invoke{}
|
||||
)
|
||||
if err := c.performRequest("invoke", p, resp); err != nil {
|
||||
// NOTE: this is test invoke and will not affect the blockchain.
|
||||
func (c *Client) Invoke(script string, params []smartcontract.Parameter, hashesForVerifying []util.Uint160) (*result.Invoke, error) {
|
||||
p := request.NewRawParams(script, params)
|
||||
return c.invokeSomething("invoke", p, hashesForVerifying)
|
||||
}
|
||||
|
||||
// invokeSomething is an inner wrapper for Invoke* functions
|
||||
func (c *Client) invokeSomething(method string, p request.RawParams, hashesForVerifying []util.Uint160) (*result.Invoke, error) {
|
||||
var resp = new(result.Invoke)
|
||||
if hashesForVerifying != nil {
|
||||
p.Values = append(p.Values, hashesForVerifying)
|
||||
}
|
||||
if err := c.performRequest(method, p, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
|
|
|
@ -813,7 +813,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
Type: smartcontract.Hash160Type,
|
||||
Value: hash,
|
||||
},
|
||||
})
|
||||
}, nil)
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"HALT","gas_consumed":"0.311","stack":[{"type":"ByteArray","value":"262bec084432"}],"tx":"d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
|
@ -839,7 +839,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191")
|
||||
return c.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191", nil)
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"00046e616d656724058e5e1b6008847cd662728549088a9ee82191","state":"HALT","gas_consumed":"0.161","stack":[{"type":"ByteArray","value":"4e45503520474153"}],"tx":"d1011b00046e616d656724058e5e1b6008847cd662728549088a9ee82191000000000000000000000000"}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
|
@ -1186,13 +1186,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
{
|
||||
name: "invokefunction_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{})
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invokescript_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeScript("")
|
||||
return c.InvokeScript("", nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1392,13 +1392,13 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
{
|
||||
name: "invokefunction_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{})
|
||||
return c.InvokeFunction("", "", []smartcontract.Parameter{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invokescript_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.InvokeScript("")
|
||||
return c.InvokeScript("", nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue